/// <summary> /// Add a point to the polygon or polyline. /// </summary> /// <param name="g">The geod_geodesic object specifying the ellipsoid.</param> /// <param name="p">The geod_polygon object specifying the polygon.</param> /// <param name="lat">The latitude of the point (degrees).</param> /// <param name="lon">The longitude of the point (degrees).</param> /// <remarks> /// g and p must have been initialized with calls to geod_init() and /// geod_polygon_init(), respectively. The same g must be used for all the /// points and edges in a polygon. lat should be in the range /// [-90deg, 90deg] and lon should be in the range [-540deg, 540deg). /// </remarks> public void geod_polygon_addpoint(geod_geodesic g, double lat, double lon) { lon = AngNormalize(lon); if (num == 0) { lat0 = this.lat = lat; lon0 = this.lon = lon; } else { double s12, S12 = 0, dummy; if (polyline) { g.geod_geninverse(this.lat, this.lon, lat, lon, GEOD.DISTANCE, out s12, out dummy, out dummy, out dummy, out dummy, out dummy, out dummy); } else { g.geod_geninverse(this.lat, this.lon, lat, lon, GEOD.DISTANCE | GEOD.AREA, out s12, out dummy, out dummy, out dummy, out dummy, out dummy, out S12); } accadd(P, s12); if (!polyline) { accadd(A, S12); crossings += transit(this.lon, lon); } this.lat = lat; this.lon = lon; } ++num; }
/// <summary> /// A simple interface for computing the area of a geodesic polygon. /// </summary> /// <param name="g">The geod_geodesic object specifying the ellipsoid.</param> /// <param name="lats">An array of latitudes of the polygon vertices (degrees).</param> /// <param name="lons">An array of longitudes of the polygon vertices (degrees).</param> /// <param name="n">The number of vertices.</param> /// <param name="pA">The area of the polygon (square meters).</param> /// <param name="pP">The perimeter of the polygon (meters).</param> /// <remarks> /// lats should be in the range [-90deg, 90deg]; lons should be in the range [-540deg, 540deg). /// /// Only simple polygons (which are not self-intersecting) are allowed. /// There's no need to "close" the polygon by repeating the first vertex. The /// area returned is signed with counter-clockwise traversal being treated as /// positive. /// </remarks> public static void geod_polygonarea(geod_geodesic g, double[] lats, double[] lons, int n, out double pA, out double pP) { geod_polygon p = new geod_polygon(false); for (int i = 0; i < n; ++i) { p.geod_polygon_addpoint(g, lats[i], lons[i]); } p.geod_polygon_compute(g, false, true, out pA, out pP); }
/// <summary> /// Add an edge to the polygon or polyline. /// </summary> /// <param name="g">The geod_geodesic object specifying the ellipsoid.</param> /// <param name="p">The geod_polygon object specifying the polygon.</param> /// <param name="azi">The azimuth at current point (degrees).</param> /// <param name="s">The distance from current point to next point (meters).</param> /// <remarks> /// g and p must have been initialized with calls to geod_init() and /// geod_polygon_init(), respectively. The same g must be used for all the /// points and edges in a polygon. azi should be in the range /// [-540deg, 540deg). This does nothing if no points have been /// added yet. The lat and lon fields of p give the location of /// the new vertex. /// </remarks> public void geod_polygon_addedge(geod_geodesic g, double azi, double s) { if (num != 0) { // Do nothing is num is zero double lat, lon, S12 = 0, dummy; if (polyline) { g.geod_gendirect(this.lat, this.lon, azi, GEOD.LONG_UNROLL, s, GEOD.LATITUDE | GEOD.LONGITUDE, out lat, out lon, out dummy, out dummy, out dummy, out dummy, out dummy, out dummy); } else { g.geod_gendirect(this.lat, this.lon, azi, GEOD.LONG_UNROLL, s, GEOD.LATITUDE | GEOD.LONGITUDE | GEOD.AREA, out lat, out lon, out dummy, out dummy, out dummy, out dummy, out dummy, out S12); } accadd(P, s); if (!polyline) { accadd(A, S12); crossings += transitdirect(this.lon, lon); } this.lat = lat; this.lon = lon; ++num; } }
/// <summary> /// Return the results assuming a tentative final test point is added via an /// azimuth and distance; however, the data for the test point is not saved. /// This lets you report a running result for the perimeter and area as the /// user moves the mouse cursor. Ordinary floating point arithmetic is used /// to accumulate the data for the test point; thus the area and perimeter /// returned are less accurate than if geod_polygon_addedge() and /// geod_polygon_compute() are used. /// </summary> /// <param name="g">The geod_geodesic object specifying the ellipsoid.</param> /// <param name="p">The geod_polygon object specifying the polygon.</param> /// <param name="azi">The azimuth at current point (degrees).</param> /// <param name="s">The distance from current point to final test point (meters).</param> /// <param name="reverse">If set <b>true</b> then clockwise (instead of counter-clockwise) /// traversal counts as a positive area.</param> /// <param name="sign">If set <b>true</b> 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.</param> /// <param name="pA">The area of the polygon (square meters); only set if polyline is set <b>true</b> /// in the call to geod_polygon_init().</param> /// <param name="pP">The perimeter of the polygon or length of the polyline (meters).</param> /// <returns>The number of points.</returns> /// <remarks> /// azi should be in the range [-540deg, 540deg). /// </remarks> public int geod_polygon_testedge(geod_geodesic g, double azi, double s, bool reverse, bool sign, out double pA, out double pP) { pP = pA = NaN; int num = this.num + 1; if (num == 1) { return(0); // we don't have a starting point! } double perimeter = P[0] + s; if (polyline) { pP = perimeter; return(num); } double tempsum = A[0]; int crossings = this.crossings; { double lat, lon, s12, S12, dummy; g.geod_gendirect(this.lat, this.lon, azi, GEOD.LONG_UNROLL, s, GEOD.LATITUDE | GEOD.LONGITUDE | GEOD.AREA, out lat, out lon, out dummy, out dummy, out dummy, out dummy, out dummy, out S12); tempsum += S12; crossings += transitdirect(this.lon, lon); g.geod_geninverse(lat, lon, lat0, lon0, GEOD.DISTANCE | GEOD.AREA, out s12, out dummy, out dummy, out dummy, out dummy, out dummy, out S12); perimeter += s12; tempsum += S12; crossings += transit(lon, lon0); } double area0 = 4 * pi * g.c2; if ((crossings & 1) != 0) { tempsum += (tempsum < 0?1:-1) * area0 / 2; } // area is with the clockwise sense. If !reverse convert to counter-clockwise convention. if (!reverse) { tempsum *= -1; } // If sign put area in (-area0/2, area0/2], else put area in [0, area0) if (sign) { if (tempsum > area0 / 2) { tempsum -= area0; } else if (tempsum <= -area0 / 2) { tempsum += area0; } } else { if (tempsum >= area0) { tempsum -= area0; } else if (tempsum < 0) { tempsum += area0; } } pP = perimeter; pA = 0 + tempsum; return(num); }
/// <summary> /// Return the results assuming a tentative final test point is added; /// however, the data for the test point is not saved. This lets you report a /// running result for the perimeter and area as the user moves the mouse /// cursor. Ordinary floating point arithmetic is used to accumulate the data /// for the test point; thus the area and perimeter returned are less accurate /// than if geod_polygon_addpoint() and geod_polygon_compute() are used. /// </summary> /// <param name="g">The geod_geodesic object specifying the ellipsoid.</param> /// <param name="p">The geod_polygon object specifying the polygon.</param> /// <param name="lat">The latitude of the test point (degrees).</param> /// <param name="lon">The longitude of the test point (degrees).</param> /// <param name="reverse">If set <b>true</b> then clockwise (instead of counter-clockwise) /// traversal counts as a positive area.</param> /// <param name="sign">If set <b>true</b> 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.</param> /// <param name="pA">The area of the polygon (square meters); only set if polyline is set <b>true</b> /// in the call to geod_polygon_init().</param> /// <param name="pP">The perimeter of the polygon or length of the polyline (meters).</param> /// <returns>The number of points.</returns> /// <remarks> /// lat should be in the range [-90deg, 90deg] and lon should be in the range [-540deg, 540deg). /// </remarks> public int geod_polygon_testpoint(geod_geodesic g, double lat, double lon, bool reverse, bool sign, out double pA, out double pP) { pP = pA = 0; int num = this.num + 1; if (num == 1) { return(num); } double perimeter = P[0]; double tempsum = polyline?0:A[0]; int crossings = this.crossings; for (int i = 0; i < (polyline?1:2); ++i) { double s12, dummy; if (!polyline) { double S12; g.geod_geninverse(i == 0?this.lat:lat, i == 0?this.lon:lon, i != 0?lat0:lat, i != 0?lon0:lon, GEOD.DISTANCE | GEOD.AREA, out s12, out dummy, out dummy, out dummy, out dummy, out dummy, out S12); tempsum += S12; crossings += transit(i == 0?this.lon:lon, i != 0?this.lon0:lon); } else { g.geod_geninverse(i == 0?this.lat:lat, i == 0?this.lon:lon, i != 0?lat0:lat, i != 0?lon0:lon, GEOD.DISTANCE, out s12, out dummy, out dummy, out dummy, out dummy, out dummy, out dummy); } perimeter += s12; } pP = perimeter; if (polyline) { return(num); } double area0 = 4 * pi * g.c2; if ((crossings & 1) != 0) { tempsum += (tempsum < 0?1:-1) * area0 / 2; } // area is with the clockwise sense. If !reverse convert to counter-clockwise convention. if (!reverse) { tempsum *= -1; } // If sign put area in (-area0/2, area0/2], else put area in [0, area0) if (sign) { if (tempsum > area0 / 2) { tempsum -= area0; } else if (tempsum <= -area0 / 2) { tempsum += area0; } } else { if (tempsum >= area0) { tempsum -= area0; } else if (tempsum < 0) { tempsum += area0; } } pA = 0 + tempsum; return(num); }
/// <summary> /// Return the results for a polygon. /// </summary> /// <param name="g">The geod_geodesic object specifying the ellipsoid.</param> /// <param name="p">The geod_polygon object specifying the polygon.</param> /// <param name="reverse">If set <b>true</b> then clockwise (instead of counter-clockwise) /// traversal counts as a positive area.</param> /// <param name="sign">If set <b>true</b> 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.</param> /// <param name="pA">The area of the polygon (square meters); only set if polyline is set <b>true</b> /// in the call to geod_polygon_init().</param> /// <param name="pP">The perimeter of the polygon or length of the polyline (meters).</param> /// <returns>The number of points.</returns> /// <remarks> /// The area and perimeter are accumulated at two times the standard floating /// point precision to guard against the loss of accuracy with many-sided /// polygons. Only simple polygons (which are not self-intersecting) are /// allowed. There's no need to "close" the polygon by repeating the first vertex. /// </remarks> public int geod_polygon_compute(geod_geodesic g, bool reverse, bool sign, out double pA, out double pP) { pP = pA = 0; if (num < 2) { return(num); } if (polyline) { pP = P[0]; return(num); } double s12, S12, dummy; g.geod_geninverse(lat, lon, lat0, lon0, GEOD.DISTANCE | GEOD.AREA, out s12, out dummy, out dummy, out dummy, out dummy, out dummy, out S12); pP = accsum(P, s12); double[] t = new double[2]; acccopy(A, t); accadd(t, S12); int crossings = this.crossings + transit(lon, lon0); double area0 = 4 * pi * g.c2; if ((crossings & 1) != 0) { accadd(t, (t[0] < 0?1:-1) * area0 / 2); } // area is with the clockwise sense. If !reverse convert to // counter-clockwise convention. if (!reverse) { accneg(t); } // If sign put area in (-area0/2, area0/2], else put area in [0, area0) if (sign) { if (t[0] > area0 / 2) { accadd(t, -area0); } else if (t[0] <= -area0 / 2) { accadd(t, +area0); } } else { if (t[0] >= area0) { accadd(t, -area0); } else if (t[0] < 0) { accadd(t, +area0); } } pA = 0 + t[0]; return(num); }
public override PJ Init() { g = new geod_geodesic(a, es / (1 + Math.Sqrt(one_es))); phi0 = Proj.pj_param_r(ctx, parameters, "lat_0"); if (Math.Abs(Math.Abs(phi0) - Proj.HALFPI) < EPS10) { mode = phi0 < 0.0?aeqd_mode.S_POLE:aeqd_mode.N_POLE; sinph0 = phi0 < 0.0?-1.0:1.0; cosph0 = 0.0; } else if (Math.Abs(phi0) < EPS10) { mode = aeqd_mode.EQUIT; sinph0 = 0.0; cosph0 = 1.0; } else { mode = aeqd_mode.OBLIQ; sinph0 = Math.Sin(phi0); cosph0 = Math.Cos(phi0); } if (es == 0) { inv = s_inverse; fwd = s_forward; } else { en = Proj.pj_enfn(es); if (en == null) { return(null); } if (Proj.pj_param_b(ctx, parameters, "guam")) { M1 = Proj.pj_mlfn(phi0, sinph0, cosph0, en); inv = e_guam_inv; fwd = e_guam_fwd; } else { switch (mode) { case aeqd_mode.N_POLE: Mp = Proj.pj_mlfn(Proj.HALFPI, 1.0, 0.0, en); break; case aeqd_mode.S_POLE: Mp = Proj.pj_mlfn(-Proj.HALFPI, -1.0, 0.0, en); break; case aeqd_mode.EQUIT: case aeqd_mode.OBLIQ: inv = e_inverse; fwd = e_forward; N1 = 1.0 / Math.Sqrt(1.0 - es * sinph0 * sinph0); He = e / Math.Sqrt(one_es); G = sinph0 * He; He *= cosph0; break; } inv = e_inverse; fwd = e_forward; } } return(this); }
public void Ini() { GlobalGeodesic = new geod_geodesic(geod_a, geod_f); }
/// <summary> /// Return the results assuming a tentative final test point is added; /// however, the data for the test point is not saved. This lets you report a /// running result for the perimeter and area as the user moves the mouse /// cursor. Ordinary floating point arithmetic is used to accumulate the data /// for the test point; thus the area and perimeter returned are less accurate /// than if geod_polygon_addpoint() and geod_polygon_compute() are used. /// </summary> /// <param name="g">The geod_geodesic object specifying the ellipsoid.</param> /// <param name="p">The geod_polygon object specifying the polygon.</param> /// <param name="lat">The latitude of the test point (degrees).</param> /// <param name="lon">The longitude of the test point (degrees).</param> /// <param name="reverse">If set <b>true</b> then clockwise (instead of counter-clockwise) /// traversal counts as a positive area.</param> /// <param name="sign">If set <b>true</b> 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.</param> /// <param name="pA">The area of the polygon (square meters); only set if polyline is set <b>true</b> /// in the call to geod_polygon_init().</param> /// <param name="pP">The perimeter of the polygon or length of the polyline (meters).</param> /// <returns>The number of points.</returns> /// <remarks> /// lat should be in the range [-90deg, 90deg] and lon should be in the range [-540deg, 540deg). /// </remarks> public int geod_polygon_testpoint(geod_geodesic g, double lat, double lon, bool reverse, bool sign, out double pA, out double pP) { pP=pA=0; int num=this.num+1; if(num==1) return num; double perimeter=P[0]; double tempsum=polyline?0:A[0]; int crossings=this.crossings; for(int i=0; i<(polyline?1:2); ++i) { double s12, dummy; if(!polyline) { double S12; g.geod_geninverse(i==0?this.lat:lat, i==0?this.lon:lon, i!=0?lat0:lat, i!=0?lon0:lon, GEOD.DISTANCE|GEOD.AREA, out s12, out dummy, out dummy, out dummy, out dummy, out dummy, out S12); tempsum+=S12; crossings+=transit(i==0?this.lon:lon, i!=0?this.lon0:lon); } else g.geod_geninverse(i==0?this.lat:lat, i==0?this.lon:lon, i!=0?lat0:lat, i!=0?lon0:lon, GEOD.DISTANCE, out s12, out dummy, out dummy, out dummy, out dummy, out dummy, out dummy); perimeter+=s12; } pP=perimeter; if(polyline) return num; double area0=4*pi*g.c2; if((crossings&1)!=0) tempsum+=(tempsum<0?1:-1)*area0/2; // area is with the clockwise sense. If !reverse convert to counter-clockwise convention. if(!reverse) tempsum*=-1; // If sign put area in (-area0/2, area0/2], else put area in [0, area0) if(sign) { if(tempsum>area0/2) tempsum-=area0; else if(tempsum<=-area0/2) tempsum+=area0; } else { if(tempsum>=area0) tempsum-=area0; else if(tempsum<0) tempsum+=area0; } pA=0+tempsum; return num; }
/// <summary> /// A simple interface for computing the area of a geodesic polygon. /// </summary> /// <param name="g">The geod_geodesic object specifying the ellipsoid.</param> /// <param name="lats">An array of latitudes of the polygon vertices (degrees).</param> /// <param name="lons">An array of longitudes of the polygon vertices (degrees).</param> /// <param name="n">The number of vertices.</param> /// <param name="pA">The area of the polygon (square meters).</param> /// <param name="pP">The perimeter of the polygon (meters).</param> /// <remarks> /// lats should be in the range [-90deg, 90deg]; lons should be in the range [-540deg, 540deg). /// /// Only simple polygons (which are not self-intersecting) are allowed. /// There's no need to "close" the polygon by repeating the first vertex. The /// area returned is signed with counter-clockwise traversal being treated as /// positive. /// </remarks> public static void geod_polygonarea(geod_geodesic g, double[] lats, double[] lons, int n, out double pA, out double pP) { geod_polygon p=new geod_polygon(false); for(int i=0; i<n; ++i) p.geod_polygon_addpoint(g, lats[i], lons[i]); p.geod_polygon_compute(g, false, true, out pA, out pP); }
/// <summary> /// Return the results assuming a tentative final test point is added via an /// azimuth and distance; however, the data for the test point is not saved. /// This lets you report a running result for the perimeter and area as the /// user moves the mouse cursor. Ordinary floating point arithmetic is used /// to accumulate the data for the test point; thus the area and perimeter /// returned are less accurate than if geod_polygon_addedge() and /// geod_polygon_compute() are used. /// </summary> /// <param name="g">The geod_geodesic object specifying the ellipsoid.</param> /// <param name="p">The geod_polygon object specifying the polygon.</param> /// <param name="azi">The azimuth at current point (degrees).</param> /// <param name="s">The distance from current point to final test point (meters).</param> /// <param name="reverse">If set <b>true</b> then clockwise (instead of counter-clockwise) /// traversal counts as a positive area.</param> /// <param name="sign">If set <b>true</b> 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.</param> /// <param name="pA">The area of the polygon (square meters); only set if polyline is set <b>true</b> /// in the call to geod_polygon_init().</param> /// <param name="pP">The perimeter of the polygon or length of the polyline (meters).</param> /// <returns>The number of points.</returns> /// <remarks> /// azi should be in the range [-540deg, 540deg). /// </remarks> public int geod_polygon_testedge(geod_geodesic g, double azi, double s, bool reverse, bool sign, out double pA, out double pP) { pP=pA=NaN; int num=this.num+1; if(num==1) return 0; // we don't have a starting point! double perimeter=P[0]+s; if(polyline) { pP=perimeter; return num; } double tempsum=A[0]; int crossings=this.crossings; { double lat, lon, s12, S12, dummy; g.geod_gendirect(this.lat, this.lon, azi, GEOD.LONG_UNROLL, s, GEOD.LATITUDE|GEOD.LONGITUDE|GEOD.AREA, out lat, out lon, out dummy, out dummy, out dummy, out dummy, out dummy, out S12); tempsum+=S12; crossings+=transitdirect(this.lon, lon); g.geod_geninverse(lat, lon, lat0, lon0, GEOD.DISTANCE|GEOD.AREA, out s12, out dummy, out dummy, out dummy, out dummy, out dummy, out S12); perimeter+=s12; tempsum+=S12; crossings+=transit(lon, lon0); } double area0=4*pi*g.c2; if((crossings&1)!=0) tempsum+=(tempsum<0?1:-1)*area0/2; // area is with the clockwise sense. If !reverse convert to counter-clockwise convention. if(!reverse) tempsum*=-1; // If sign put area in (-area0/2, area0/2], else put area in [0, area0) if(sign) { if(tempsum>area0/2) tempsum-=area0; else if(tempsum<=-area0/2) tempsum+=area0; } else { if(tempsum>=area0) tempsum-=area0; else if(tempsum<0) tempsum+=area0; } pP=perimeter; pA=0+tempsum; return num; }
/// <summary> /// Return the results for a polygon. /// </summary> /// <param name="g">The geod_geodesic object specifying the ellipsoid.</param> /// <param name="p">The geod_polygon object specifying the polygon.</param> /// <param name="reverse">If set <b>true</b> then clockwise (instead of counter-clockwise) /// traversal counts as a positive area.</param> /// <param name="sign">If set <b>true</b> 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.</param> /// <param name="pA">The area of the polygon (square meters); only set if polyline is set <b>true</b> /// in the call to geod_polygon_init().</param> /// <param name="pP">The perimeter of the polygon or length of the polyline (meters).</param> /// <returns>The number of points.</returns> /// <remarks> /// The area and perimeter are accumulated at two times the standard floating /// point precision to guard against the loss of accuracy with many-sided /// polygons. Only simple polygons (which are not self-intersecting) are /// allowed. There's no need to "close" the polygon by repeating the first vertex. /// </remarks> public int geod_polygon_compute(geod_geodesic g, bool reverse, bool sign, out double pA, out double pP) { pP=pA=0; if(num<2) return num; if(polyline) { pP=P[0]; return num; } double s12, S12, dummy; g.geod_geninverse(lat, lon, lat0, lon0, GEOD.DISTANCE|GEOD.AREA, out s12, out dummy, out dummy, out dummy, out dummy, out dummy, out S12); pP=accsum(P, s12); double[] t=new double[2]; acccopy(A, t); accadd(t, S12); int crossings=this.crossings+transit(lon, lon0); double area0=4*pi*g.c2; if((crossings&1)!=0) accadd(t, (t[0]<0?1:-1)*area0/2); // area is with the clockwise sense. If !reverse convert to // counter-clockwise convention. if(!reverse) accneg(t); // If sign put area in (-area0/2, area0/2], else put area in [0, area0) if(sign) { if(t[0]>area0/2) accadd(t, -area0); else if(t[0]<=-area0/2) accadd(t, +area0); } else { if(t[0]>=area0) accadd(t, -area0); else if(t[0]<0) accadd(t, +area0); } pA=0+t[0]; return num; }
/// <summary> /// Add a point to the polygon or polyline. /// </summary> /// <param name="g">The geod_geodesic object specifying the ellipsoid.</param> /// <param name="p">The geod_polygon object specifying the polygon.</param> /// <param name="lat">The latitude of the point (degrees).</param> /// <param name="lon">The longitude of the point (degrees).</param> /// <remarks> /// g and p must have been initialized with calls to geod_init() and /// geod_polygon_init(), respectively. The same g must be used for all the /// points and edges in a polygon. lat should be in the range /// [-90deg, 90deg] and lon should be in the range [-540deg, 540deg). /// </remarks> public void geod_polygon_addpoint(geod_geodesic g, double lat, double lon) { lon=AngNormalize(lon); if(num==0) { lat0=this.lat=lat; lon0=this.lon=lon; } else { double s12, S12=0, dummy; if(polyline) g.geod_geninverse(this.lat, this.lon, lat, lon, GEOD.DISTANCE, out s12, out dummy, out dummy, out dummy, out dummy, out dummy, out dummy); else g.geod_geninverse(this.lat, this.lon, lat, lon, GEOD.DISTANCE|GEOD.AREA, out s12, out dummy, out dummy, out dummy, out dummy, out dummy, out S12); accadd(P, s12); if(!polyline) { accadd(A, S12); crossings+=transit(this.lon, lon); } this.lat=lat; this.lon=lon; } ++num; }
/// <summary> /// Add an edge to the polygon or polyline. /// </summary> /// <param name="g">The geod_geodesic object specifying the ellipsoid.</param> /// <param name="p">The geod_polygon object specifying the polygon.</param> /// <param name="azi">The azimuth at current point (degrees).</param> /// <param name="s">The distance from current point to next point (meters).</param> /// <remarks> /// g and p must have been initialized with calls to geod_init() and /// geod_polygon_init(), respectively. The same g must be used for all the /// points and edges in a polygon. azi should be in the range /// [-540deg, 540deg). This does nothing if no points have been /// added yet. The lat and lon fields of p give the location of /// the new vertex. /// </remarks> public void geod_polygon_addedge(geod_geodesic g, double azi, double s) { if(num!=0) { // Do nothing is num is zero double lat, lon, S12=0, dummy; if(polyline) g.geod_gendirect(this.lat, this.lon, azi, GEOD.LONG_UNROLL, s, GEOD.LATITUDE|GEOD.LONGITUDE, out lat, out lon, out dummy, out dummy, out dummy, out dummy, out dummy, out dummy); else g.geod_gendirect(this.lat, this.lon, azi, GEOD.LONG_UNROLL, s, GEOD.LATITUDE|GEOD.LONGITUDE|GEOD.AREA, out lat, out lon, out dummy, out dummy, out dummy, out dummy, out dummy, out S12); accadd(P, s); if(!polyline) { accadd(A, S12); crossings+=transitdirect(this.lon, lon); } this.lat=lat; this.lon=lon; ++num; } }
/// <summary> /// Initialize a geod_geodesicline object. /// </summary> /// <param name="g">The <see cref="geod_geodesic"/> object specifying the ellipsoid.</param> /// <param name="lat1">Latitude of point 1 (degrees).</param> /// <param name="lon1">Longitude of point 1 (degrees).</param> /// <param name="azi1">Azimuth at point 1 (degrees).</param> /// <param name="caps">Bitor'ed combination of <see cref="geod_mask"/> values. See remarks for more informations.</param> /// <remarks> /// <paramref name="caps"/> bitor'ed combination of geod_mask() values specifying /// the capabilities the geod_geodesicline object should possess, i.e., which /// quantities can be returned in calls to geod_position() and /// geod_genposition(). /// /// <paramref name="lat1"/> should be in the range [-90deg, 90deg]. /// <paramref name="lon1"/> and <paramref name="azi1"/> should be in the range [-540deg, 540deg). /// /// The geod_mask values are: /// - caps|=GEOD_LATITUDE for the latitude lat2; this is added automatically, /// - caps|=GEOD_LONGITUDE for the latitude lon2, /// - caps|=GEOD_AZIMUTH for the latitude azi2; this is added automatically, /// - caps|=GEOD_DISTANCE for the distance s12, /// - caps|=GEOD_REDUCEDLENGTH for the reduced length m12, /// - caps|=GEOD_GEODESICSCALE for the geodesic scales M12 and M21, /// - caps|=GEOD_AREA for the area S12, /// - caps|=GEOD_DISTANCE_IN permits the length of the /// geodesic to be given in terms of s12; without this capability the /// length can only be specified in terms of arc length. /// /// A value of caps=0 is treated as GEOD.LATITUDE|GEOD.LONGITUDE|GEOD.AZIMUTH|GEOD.DISTANCE_IN /// (to support the solution of the "standard" direct problem). /// </remarks> public geod_geodesicline(geod_geodesic g, double lat1, double lon1, double azi1, GEOD caps) { a = g.a; f = g.f; b = g.b; c2 = g.c2; f1 = g.f1; // If caps is 0 assume the standard direct calculation this.caps = (caps != 0?caps:GEOD.DISTANCE_IN | GEOD.LONGITUDE) | GEOD.LATITUDE | GEOD.AZIMUTH | GEOD.LONG_UNROLL; // always allow latitude and azimuth and unrolling of longitude this.lat1 = lat1; this.lon1 = lon1; // Guard against underflow in salp0 this.azi1 = AngRound(AngNormalize(azi1)); // alp1 is in [0, pi] double alp1 = this.azi1 * degree; // Enforce sin(pi)==0 and cos(pi/2)==0. Better to face the ensuing // problems directly than to skirt them. salp1 = this.azi1 == -180?0:Math.Sin(alp1); calp1 = Math.Abs(this.azi1) == 90?0:Math.Cos(alp1); double phi = lat1 * degree; // Ensure cbet1=+epsilon at poles double sbet1 = f1 * Math.Sin(phi); double cbet1 = Math.Abs(lat1) == 90?tiny:Math.Cos(phi); norm2(ref sbet1, ref cbet1); dn1 = Math.Sqrt(1 + g.ep2 * sq(sbet1)); // Evaluate alp0 from sin(alp1)*cos(bet1)=sin(alp0), salp0 = salp1 * cbet1; // alp0 in [0, pi/2-|bet1|] // Alt: calp0=hypot(sbet1, calp1*cbet1). The following // is slightly better (consider the case salp1=0). calp0 = hypotx(calp1, salp1 * sbet1); // Evaluate sig with tan(bet1)=tan(sig1)*cos(alp1). // sig = 0 is nearest northward crossing of equator. // With bet1=0, alp1=pi/2, we have sig1=0 (equatorial line). // With bet1=pi/2, alp1=-pi, sig1=pi/2 // With bet1=-pi/2, alp1=0, sig1=-pi/2 // Evaluate omg1 with tan(omg1)=sin(alp0)*tan(sig1). // With alp0 in (0, pi/2], quadrants for sig and omg coincide. // No atan2(0, 0) ambiguity at poles since cbet1=+epsilon. // With alp0=0, omg1=0 for alp1=0, omg1=pi for alp1=pi. ssig1 = sbet1; somg1 = salp0 * sbet1; csig1 = comg1 = sbet1 != 0 || calp1 != 0?cbet1 * calp1:1; norm2(ref ssig1, ref csig1); // sig1 in (-pi, pi] // norm2(ref somg1, ref comg1); -- don't need to normalize! k2 = sq(calp0) * g.ep2; double eps = k2 / (2 * (1 + Math.Sqrt(1 + k2)) + k2); if ((this.caps & GEOD.CAP_C1) != 0) { double s, c; A1m1 = geod_geodesic.A1m1f(eps); geod_geodesic.C1f(eps, C1a); B11 = SinCosSeries(true, ssig1, csig1, C1a, nC1); s = Math.Sin(B11); c = Math.Cos(B11); // tau1=sig1+B11 stau1 = ssig1 * c + csig1 * s; ctau1 = csig1 * c - ssig1 * s; // Not necessary because C1pa reverts C1a // B11=-SinCosSeries(TRUE, stau1, ctau1, C1pa, nC1p); } if ((this.caps & GEOD.CAP_C1p) != 0) { geod_geodesic.C1pf(eps, C1pa); } if ((this.caps & GEOD.CAP_C2) != 0) { A2m1 = geod_geodesic.A2m1f(eps); geod_geodesic.C2f(eps, C2a); B21 = SinCosSeries(true, ssig1, csig1, C2a, nC2); } if ((this.caps & GEOD.CAP_C3) != 0) { g.C3f(eps, C3a); A3c = -f *salp0 *g.A3f(eps); B31 = SinCosSeries(true, ssig1, csig1, C3a, nC3 - 1); } if ((this.caps & GEOD.CAP_C4) != 0) { g.C4f(eps, C4a); // Multiplier=a^2*e^2*cos(alpha0)*sin(alpha0) A4 = sq(a) * calp0 * salp0 * g.e2; B41 = SinCosSeries(false, ssig1, csig1, C4a, nC4); } }