/// <summary> /// Return the result of adding a number to sum (but don't change sum). /// </summary> /// <param name="y">the number to be added to the sum.</param> /// <returns><i>sum</i> + <i>y</i>.</returns> public double Sum(double y) { Accumulator a = this; a.Add(y); return(a._s); }
/// <summary> /// Clear current instance of <see cref="PolygonArea{T}"/>, allowing a new polygon to be started. /// </summary> public void Clear() { _num = 0; _crossings = 0; _areasum = 0; _perimetersum = 0; _lat0 = _lon0 = _lat1 = _lon1 = double.NaN; }
/** * Return the results so far. * <p> * @param reverse if true then clockwise (instead of counter-clockwise) * traversal counts as a positive area. * @param sign if true then return a signed result for the area if * the polygon is traversed in the "wrong" direction instead of returning * the area for the rest of the earth. * @return PolygonResult(<i>num</i>, <i>perimeter</i>, <i>area</i>) where * <i>num</i> is the number of vertices, <i>perimeter</i> is the perimeter * of the polygon or the length of the polyline (meters), and <i>area</i> * is the area of the polygon (meters<sup>2</sup>) or Double.NaN of * <i>polyline</i> is true in the constructor. * <p> * More points can be added to the polygon after this call. **********************************************************************/ public PolygonResult Compute(bool reverse, bool sign) { if (_num < 2) { return(new PolygonResult(_num, 0, _polyline ? Double.NaN : 0)); } if (_polyline) { return(new PolygonResult(_num, _perimetersum.Sum(), Double.NaN)); } GeodesicData g = _earth.Inverse(_lat1, _lon1, _lat0, _lon0, _mask); Accumulator tempsum = new Accumulator(_areasum); tempsum.Add(g.S12); int crossings = _crossings + transit(_lon1, _lon0); if ((crossings & 1) != 0) { tempsum.Add((tempsum.Sum() < 0 ? 1 : -1) * _area0 / 2); } // area is with the clockwise sense. If !reverse convert to // counter-clockwise convention. if (!reverse) { tempsum.Negate(); } // If sign put area in (-area0/2, area0/2], else put area in [0, area0) if (sign) { if (tempsum.Sum() > _area0 / 2) { tempsum.Add(-_area0); } else if (tempsum.Sum() <= -_area0 / 2) { tempsum.Add(+_area0); } } else { if (tempsum.Sum() >= _area0) { tempsum.Add(-_area0); } else if (tempsum.Sum() < 0) { tempsum.Add(+_area0); } } return (new PolygonResult(_num, _perimetersum.Sum(g.s12), 0 + tempsum.Sum())); }
/** * Constructor for PolygonArea. * <p> * @param earth the Geodesic object to use for geodesic calculations. * @param polyline if true that treat the points as defining a polyline * instead of a polygon. **********************************************************************/ public PolygonArea(Geodesic earth, bool polyline) { _earth = earth; _area0 = _earth.EllipsoidArea(); _polyline = polyline; _mask = GeodesicMask.LATITUDE | GeodesicMask.LONGITUDE | GeodesicMask.DISTANCE | (_polyline ? GeodesicMask.NONE : GeodesicMask.AREA); _perimetersum = new Accumulator(0); if (!_polyline) { _areasum = new Accumulator(0); } Clear(); }
/// <summary> /// Add an edge to the polygon or polyline. /// </summary> /// <param name="azi">azimuth at current point (degrees).</param> /// <param name="s">distance from current point to next point (meters).</param> /// <remarks> /// This does nothing if no points have been added yet. /// Use <see cref="CurrentPoint"/> to determine the position of the new vertex. /// </remarks> public void AddEdge(double azi, double s) { if (_num != 0) { // Do nothing if _num is zero _earth.GenDirect(_lat1, _lon1, azi, false, s, _mask, out var lat, out var lon, out _, out _, out _, out _, out _, out var S12); _perimetersum += s; if (!_polyline) { _areasum += S12; _crossings += TransitDirect(_lon1, lon); lon = AngNormalize(lon); } _lat1 = lat; _lon1 = lon; ++_num; } }
/// <summary> /// Add a point to the polygon or polyline. /// </summary> /// <param name="lat">the latitude of the point (degrees).</param> /// <param name="lon">the longitude of the point (degrees).</param> /// <remarks> /// <paramref name="lat"/> should be in the range [−90°, 90°]. /// </remarks> public void AddPoint(double lat, double lon) { lat = LatFix(lat); lon = AngNormalize(lon); if (_num == 0) { _lat0 = _lat1 = lat; _lon0 = _lon1 = lon; } else { _earth.GenInverse(_lat1, _lon1, lat, lon, _mask, out var s12, out _, out _, out _, out _, out _, out var S12); _perimetersum += s12; if (!_polyline) { _areasum += S12; _crossings += Transit(_lon1, lon); } _lat1 = lat; _lon1 = lon; } ++_num; }
private void AreaReduce(ref Accumulator area, int crossings, bool reverse, bool sign) { Remainder(ref area); if ((crossings & 1) != 0) { area += (area < 0 ? 1 : -1) * _area0 / 2; } // area is with the clockwise sense. If !reverse convert to // counter-clockwise convention. if (!reverse) { area *= -1; } // If sign put area in (-_area0/2, _area0/2], else put area in [0, _area0) if (sign) { if (area > _area0 / 2) { area -= _area0; } else if (area <= -_area0 / 2) { area += _area0; } } else { if (area >= _area0) { area -= _area0; } else if (area < 0) { area += _area0; } } }
private void Remainder(ref Accumulator a) => a.Remainder(_area0);
/// <summary> /// Construct from another Accumulator. /// </summary> /// <param name="a">set <i>sum</i> = <i>a</i>.</param> public Accumulator(Accumulator a) { _s = a._s; _t = a._t; }