geo.cpp
上传用户:lbr_007
上传日期:2019-05-31
资源大小:282k
文件大小:18k
- // geo.cpp
- //
- #include "stdafx.h"
- #pragma warning (disable:4786)
- #include "geo.h"
- #include <math.h>
- #include <UtilityParser.h>
- #include <StringUtil.h>
- double GeoCalc::EllipseArcLength(double lat1, double lat2, double a, double f)
- {
- double result = 0.0;
- // if this path crosses the equator (i.e. lat1 & lat2 are different signs)
- // then we have to break the path up into 2 parts, from the southern point
- // up to the equator, then from the equator to the northern point.
- // if this path is entirely on one side of the equator we can do it in one
- // part.
- UINT32 numParts = 1;
- double multLat = lat1 * lat2;
- if (multLat < 0.0)
- {
- numParts = 2;
- }
- INT32 steps;
- double snLat1, snLat2, twoFF, x1, x2, dx, x, y1, y2, dy;
- double aOneF, dydx, adx, a2, oneF;
- double dlat1, dlat2;
- a2 = a * a;
- oneF = 1 - f;
- aOneF = a * oneF;
- for (UINT32 part = 0; part < numParts; part++){
- if (numParts == 2)
- {
- if (part == 0)
- {
- dlat1 = (lat1 < lat2) ? lat1 : lat2;
- dlat2 = 0.0;
- }
- else
- {
- dlat1 = 0.0;
- dlat2 = (lat1 > lat2) ? lat1 : lat2;
- }
- }
- else
- {
- dlat1 = (lat1 < lat2) ? lat1 : lat2;
- dlat2 = (lat1 > lat2) ? lat1 : lat2;
- }
- steps = 100;
- steps += 100 * (INT32)(0.50 + (dlat2-dlat1));
- steps = (steps > 4000) ? 4000 : steps;
- snLat1 = sin(GEO::DE2RA*dlat1);
- snLat2 = sin(GEO::DE2RA*dlat2);
- twoFF = 2 * f - f * f;
- x1 = a * cos(GEO::DE2RA * dlat1) / sqrt(1-twoFF*snLat1*snLat1);
- x2 = a * cos(GEO::DE2RA * dlat2) / sqrt(1-twoFF*snLat2*snLat2);
- dx = (x2 - x1) / (double)(steps - 1);
- x, y1, y2, dy, dydx;
- adx = (dx < 0.0) ? -dx : dx; // absolute value of dx
- for (INT32 i = 0; i < (steps - 1); i++){
- x = x1 + dx * i;
- dydx = ((aOneF * sqrt((1.0 - ((x+dx)*(x+dx))/a2))) -
- (aOneF * sqrt((1.0 - (x*x)/a2)))) / dx;
- result += adx * sqrt(1.0 + dydx*dydx);
- }
- }
- return result;
- }
- double GeoCalc::GCDistance(double lat1, double lon1, double lat2, double lon2)
- {
- lat1 *= GEO::DE2RA;
- lon1 *= GEO::DE2RA;
- lat2 *= GEO::DE2RA;
- lon2 *= GEO::DE2RA;
- double d = sin(lat1)*sin(lat2) + cos(lat1)*cos(lat2)*cos(lon1 - lon2);
- return (GEO::AVG_ERAD * acos(d));
- }
- void GeoCalc::GCDistanceAzimuth(double lat1, double lon1, double dist, double az,
- double& lat2, double& lon2)
- {
- double b = dist / GEO::AVG_ERAD;
- double sinb = sin(b);
- double cosb = cos(b);
- double sinc = sin(GEO::DE2RA * (90.0 - lat1));
- double cosc = cos(GEO::DE2RA * (90.0 - lat1));
- double azrad = GEO::DE2RA * az;
- double a = acos(cosb*cosc + sinc*sinb*cos(azrad));
- double B = asin(sinb*sin(azrad)/sin(a));
- lat2 = GEO::RA2DE * ((GEO::PIE/2.0) - a);
- lon2 = GEO::RA2DE * B + lon1;
- }
- double GeoCalc::GCAzimuth(double lat1, double lon1, double lat2, double lon2)
- {
- double result = 0.0;
- INT32 ilat1 = (INT32)(0.50 + lat1 * 360000.0);
- INT32 ilat2 = (INT32)(0.50 + lat2 * 360000.0);
- INT32 ilon1 = (INT32)(0.50 + lon1 * 360000.0);
- INT32 ilon2 = (INT32)(0.50 + lon2 * 360000.0);
- lat1 *= GEO::DE2RA;
- lon1 *= GEO::DE2RA;
- lat2 *= GEO::DE2RA;
- lon2 *= GEO::DE2RA;
- if ((ilat1 == ilat2) && (ilon1 == ilon2))
- {
- return result;
- }
- else if (ilon1 == ilon2)
- {
- if (ilat1 > ilat2)
- result = 180.0;
- }
- else
- {
- double c = acos(sin(lat2)*sin(lat1) + cos(lat2)*cos(lat1)*cos((lon2-lon1)));
- double A = asin(cos(lat2)*sin((lon2-lon1))/sin(c));
- result = (A * GEO::RA2DE);
- if ((ilat2 > ilat1) && (ilon2 > ilon1))
- {
- }
- else if ((ilat2 < ilat1) && (ilon2 < ilon1))
- {
- result = 180.0 - result;
- }
- else if ((ilat2 < ilat1) && (ilon2 > ilon1))
- {
- result = 180.0 - result;
- }
- else if ((ilat2 > ilat1) && (ilon2 < ilon1))
- {
- result += 360.0;
- }
- }
- return result;
- }
- double GeoCalc::ApproxDistance(double lat1, double lon1, double lat2, double lon2)
- {
- lat1 = GEO::DE2RA * lat1;
- lon1 = -GEO::DE2RA * lon1;
- lat2 = GEO::DE2RA * lat2;
- lon2 = -GEO::DE2RA * lon2;
- double F = (lat1 + lat2) / 2.0;
- double G = (lat1 - lat2) / 2.0;
- double L = (lon1 - lon2) / 2.0;
- double sing = sin(G);
- double cosl = cos(L);
- double cosf = cos(F);
- double sinl = sin(L);
- double sinf = sin(F);
- double cosg = cos(G);
- double S = sing*sing*cosl*cosl + cosf*cosf*sinl*sinl;
- double C = cosg*cosg*cosl*cosl + sinf*sinf*sinl*sinl;
- double W = atan2(sqrt(S),sqrt(C));
- double R = sqrt((S*C))/W;
- double H1 = (3 * R - 1.0) / (2.0 * C);
- double H2 = (3 * R + 1.0) / (2.0 * S);
- double D = 2 * W * GEO::ERAD;
- return (D * (1 + GEO::FLATTENING * H1 * sinf*sinf*cosg*cosg -
- GEO::FLATTENING*H2*cosf*cosf*sing*sing));
- }
- double GeoCalc::EllipsoidDistance(double lat1, double lon1, double lat2, double lon2)
- {
- double distance = 0.0;
- double faz, baz;
- double r = 1.0 - GEO::FLATTENING;
- double tu1, tu2, cu1, su1, cu2, x, sx, cx, sy, cy, y, sa, c2a, cz, e, c, d;
- double cosy1, cosy2;
- distance = 0.0;
- if((lon1 == lon2) && (lat1 == lat2)) return distance;
- lon1 *= GEO::DE2RA;
- lon2 *= GEO::DE2RA;
- lat1 *= GEO::DE2RA;
- lat2 *= GEO::DE2RA;
- cosy1 = cos(lat1);
- cosy2 = cos(lat2);
- if(cosy1 == 0.0) cosy1 = 0.0000000001;
- if(cosy2 == 0.0) cosy2 = 0.0000000001;
- tu1 = r * sin(lat1) / cosy1;
- tu2 = r * sin(lat2) / cosy2;
- cu1 = 1.0 / sqrt(tu1 * tu1 + 1.0);
- su1 = cu1 * tu1;
- cu2 = 1.0 / sqrt(tu2 * tu2 + 1.0);
- x = lon2 - lon1;
- distance = cu1 * cu2;
- baz = distance * tu2;
- faz = baz * tu1;
- do {
- sx = sin(x);
- cx = cos(x);
- tu1 = cu2 * sx;
- tu2 = baz - su1 * cu2 * cx;
- sy = sqrt(tu1 * tu1 + tu2 * tu2);
- cy = distance * cx + faz;
- y = atan2(sy, cy);
- sa = distance * sx / sy;
- c2a = -sa * sa + 1.0;
- cz = faz + faz;
- if(c2a > 0.0) cz = -cz / c2a + cy;
- e = cz * cz * 2. - 1.0;
- c = ((-3.0 * c2a + 4.0) * GEO::FLATTENING + 4.0) * c2a * GEO::FLATTENING / 16.0;
- d = x;
- x = ((e * cy * c + cz) * sy * c + y) * sa;
- x = (1.0 - c) * x * GEO::FLATTENING + lon2 - lon1;
- } while(fabs(d - x) > GEO::EPS);
- x = sqrt((1.0 / r / r - 1.0) * c2a + 1.0) + 1.0;
- x = (x - 2.0) / x;
- c = 1.0 - x;
- c = (x * x / 4.0 + 1.0) / c;
- d = (0.375 * x * x - 1.0) * x;
- x = e * cy;
- distance = 1.0 - e - e;
- distance = ((((sy * sy * 4.0 - 3.0) *
- distance * cz * d / 6.0 - x) * d / 4.0 + cz) * sy * d + y) * c * GEO::ERAD * r;
- return distance;
- }
- // the first path corresponds to { lat1A, lon1A, lat1B, lon1B }
- // second path is: { lat2A, lon2A, lat2B, lon2B }
- //
- // Any 3 non-coincident points form a plane, so the first plane is formed
- // by lat1A, lon1A, lat1B, lon1B, and the center of the earth, the second
- // plane is formed by lat2A, lon2A, lat2B, lon2B, and the earth's center.
- // You can get the normal to each plane by taking the cross product of
- // the vector from the Earth's center to the first path point with the
- // vector from the Earth's center to the second path point. The cross
- // product guarantees a vector that is normal (perpendicular) to the
- // first two, and any vector normal to either of the respective normals
- // must lie in the corresponding plane, so by taking the cross product of
- // the two normals (normals to each path plane) you get the vector of the
- // line of intersection of the planes. From this vector its easy to deduce
- // if the intersection point of the planes is on either of the paths.
- //
- bool GeoCalc::GCIntersectSegment(double lat1A, double lon1A, double lat1B, double lon1B,
- double lat2A, double lon2A, double lat2B, double lon2B,
- double& lat3A, double& lon3A, double& lat3B, double& lon3B)
- {
- bool isInside = false;
- double v1[3], v2[3], v3a[3], v3b[3], n1[3], n2[3];
- double m;
- double d1 = ApproxDistance(lat1A, lon1A, lat1B, lon1B);
- double d2 = ApproxDistance(lat2A, lon2A, lat2B, lon2B);
- //
- // for path 1, setting up my 2 vectors, v1 is vector
- // from center of the Earth to point A, v2 is vector
- // from center of the Earth to point B.
- //
- v1[0] = cos(lat1A * GEO::DE2RA) * cos(lon1A * GEO::DE2RA);
- v1[1] = sin(lat1A * GEO::DE2RA);
- v1[2] = cos(lat1A * GEO::DE2RA) * sin(lon1A * GEO::DE2RA);
- v2[0] = cos(lat1B * GEO::DE2RA) * cos(lon1B * GEO::DE2RA);
- v2[1] = sin(lat1B * GEO::DE2RA);
- v2[2] = cos(lat1B * GEO::DE2RA) * sin(lon1B * GEO::DE2RA);
- //
- // n1 is the normal to the plane formed by the three points:
- // center of the Earth, point 1A, and point 1B
- //
- n1[0] = (v1[1]*v2[2]) - (v1[2]*v2[1]);
- n1[1] = (v1[2]*v2[0]) - (v1[0]*v2[2]);
- n1[2] = (v1[0]*v2[1]) - (v1[1]*v2[0]);
- //
- // for path 2, setting up my 2 vectors, v1 is vector
- // from center of the Earth to point A, v2 is vector
- // from center of the Earth to point B.
- //
- v1[0] = cos(lat2A * GEO::DE2RA) * cos(lon2A * GEO::DE2RA);
- v1[1] = sin(lat2A * GEO::DE2RA);
- v1[2] = cos(lat2A * GEO::DE2RA) * sin(lon2A * GEO::DE2RA);
- v2[0] = cos(lat2B * GEO::DE2RA) * cos(lon2B * GEO::DE2RA);
- v2[1] = sin(lat2B * GEO::DE2RA);
- v2[2] = cos(lat2B * GEO::DE2RA) * sin(lon2B * GEO::DE2RA);
- //
- // n1 is the normal to the plane formed by the three points:
- // center of the Earth, point 2A, and point 2B
- //
- n2[0] = (v1[1]*v2[2]) - (v1[2]*v2[1]);
- n2[1] = (v1[2]*v2[0]) - (v1[0]*v2[2]);
- n2[2] = (v1[0]*v2[1]) - (v1[1]*v2[0]);
- //
- // v3 is perpendicular to both normal 1 and normal 2, so
- // it lies in both planes, so it must be the line of
- // intersection of the planes. The question is: does it
- // go towards the correct intersection point or away from
- // it.
- //
- v3a[0] = (n1[1]*n2[2]) - (n1[2]*n2[1]);
- v3a[1] = (n1[2]*n2[0]) - (n1[0]*n2[2]);
- v3a[2] = (n1[0]*n2[1]) - (n1[1]*n2[0]);
- //
- // want to make v3a a unit vector, so first have to get
- // magnitude, then each component by magnitude
- //
- m = sqrt(v3a[0]*v3a[0] + v3a[1]*v3a[1] + v3a[2]*v3a[2]);
- v3a[0] /= m;
- v3a[1] /= m;
- v3a[2] /= m;
- //
- // calculating intersection points 3A & 3B. A & B are
- // arbitrary designations right now, later we make A
- // the one close to, or within, the paths.
- //
- lat3A = asin(v3a[1]);
- if ((lat3A > GEO::EPS) || (-lat3A > GEO::EPS))
- lon3A = asin(v3a[2]/cos(lat3A));
- else
- lon3A = 0.0;
- v3b[0] = (n2[1]*n1[2]) - (n2[2]*n1[1]);
- v3b[1] = (n2[2]*n1[0]) - (n2[0]*n1[2]);
- v3b[2] = (n2[0]*n1[1]) - (n2[1]*n1[0]);
- m = sqrt(v3b[0]*v3b[0] + v3b[1]*v3b[1] + v3b[2]*v3b[2]);
- v3b[0] /= m;
- v3b[1] /= m;
- v3b[2] /= m;
- lat3B = asin(v3b[1]);
- if ((lat3B > GEO::EPS) || (-lat3B > GEO::EPS))
- lon3B = asin(v3b[2]/cos(lat3B));
- else
- lon3B = 0.0;
- //
- // get the distances from the path endpoints to the two
- // intersection points. these values will be used to determine
- // which intersection point lies on the paths, or which one
- // is closer.
- //
- double d1a3a = ApproxDistance(lat1A, lon1A, lat3A, lon3A);
- double d1b3a = ApproxDistance(lat1B, lon1B, lat3A, lon3A);
- double d2a3a = ApproxDistance(lat2A, lon2A, lat3A, lon3A);
- double d2b3a = ApproxDistance(lat2B, lon2B, lat3A, lon3A);
- double d1a3b = ApproxDistance(lat1A, lon1A, lat3B, lon3B);
- double d1b3b = ApproxDistance(lat1B, lon1B, lat3B, lon3B);
- double d2a3b = ApproxDistance(lat2A, lon2A, lat3B, lon3B);
- double d2b3b = ApproxDistance(lat2B, lon2B, lat3B, lon3B);
- if ((d1a3a < d1) && (d1b3a < d1) && (d2a3a < d2) && (d2b3a < d2))
- {
- isInside = true;
- }
- else if ((d1a3b < d1) && (d1b3b < d1) && (d2a3b < d2) && (d2b3b < d2))
- {
- //
- // 3b is inside the two paths, so swap 3a & 3b
- //
- isInside = true;
- m = lat3A;
- lat3A = lat3B;
- lat3B = m;
- m = lon3A;
- lon3A = lon3B;
- lon3B = m;
- }
- else
- {
- //
- // figure out which one is closer to the path
- //
- d1 = d1a3a + d1b3a + d2a3a + d2b3a;
- d2 = d1a3b + d1b3b + d2a3b + d2b3b;
- if (d1 > d2)
- {
- //
- // ok, we are here because 3b {lat3B,lon3B} is closer to the
- // paths, so we need to swap 3a & 3b. the other case is already
- // the way 3a & 3b are organized, so no need to swap
- //
- m = lat3A;
- lat3A = lat3B;
- lat3B = m;
- m = lon3A;
- lon3A = lon3B;
- lon3B = m;
- }
- }
- //
- // convert the intersection points to degrees
- //
- lat3A *= GEO::RA2DE;
- lon3A *= GEO::RA2DE;
- lat3B *= GEO::RA2DE;
- lon3B *= GEO::RA2DE;
- return isInside;
- }
- void GeoCalc::GCSatellite(double lat1, double lon1, double satellite_lon,
- double& az, double& elev)
- {
- double lat2 = 0.0;
- double lon2 = satellite_lon;
- // before we convert to radians, lets just get the azimuth
- az = GCAzimuth(lat1,lon1,lat2,lon2);
- lat1 *= GEO::DE2RA;
- lon1 *= GEO::DE2RA;
- lon2 *= GEO::DE2RA;
- double R = GEO::AVG_ERAD;
- double h = GEO::GEOSTATIONARY_ALT;
- // calculate "b" just like we did in GCDistance
- double b = acos(cos(lon2-lon1)*cos(lat1));//sin(lat1)*sin(lat2) + cos(lat1)*cos(lat2)*cos(lon1 - lon2);
- // calculate "d", the distance to the satellite in kilometers
- double d = sqrt(R*R + (R+h)*(R+h) - 2*R*(R+h)*cos(b));
- // now calculate the elevation angle above the horizon
- elev = GEO::RA2DE * acos((R+h)*sin(b)/d);
- }
- double GeoCalc::DecodeLatitude(const char * txt)
- {
- double result = 0.0;
- UtilityParser p;
- StringUtil su;
- std::string arg1, arg2, arg3, arg4;
- bool is_negative = false;
- std::string tmp = txt;
- su.StripSpaces(tmp);
- if (su.StartsWith(tmp, "-"))
- {
- is_negative = true;
- }
- p.SetDelimiters(" -:,");
- p.ParseString(txt);
- int size = p.GetSize();
- if (size > 0)
- {
- if (size == 1)
- {
- if (p.GetArgument(0,arg1))
- {
- if (p.IsNumber(arg1))
- {
- result = atof(arg1.c_str());
- }
- }
- }
- else if (size == 2)
- {
- if (p.GetArgument(0,arg1) && p.GetArgument(1,arg2))
- {
- if (p.IsNumber(arg1))
- {
- result = atof(arg1.c_str());
- if (p.IsNumber(arg2))
- {
- result += atof(arg2.c_str())/60.0;
- }
- else
- {
- su.Uppercase(arg2);
- su.StripControlCharacters(arg2);
- su.StripSpaces(arg2);
- if (arg2 == "N")
- {
- result = fabs(result);
- }
- else if (arg2 == "S")
- {
- result = -1.0000000 * fabs(result);
- }
- else if (arg2.length() > 0)
- {
- result = 0;
- }
- }
- }
- }
- }
- else if (size > 2)
- {
- p.GetArgument(0,arg1);
- p.GetArgument(1,arg2);
- p.GetArgument(2,arg3);
- p.GetArgument(3,arg4);
- if (p.IsNumber(arg1) && p.IsNumber(arg2) && p.IsNumber(arg3))
- {
- result = atof(arg1.c_str()) + atof(arg2.c_str())/60.0 + atof(arg3.c_str())/3600.0;
- su.Uppercase(arg4);
- su.StripControlCharacters(arg4);
- su.StripSpaces(arg4);
- if (arg4 == "N")
- {
- result = fabs(result);
- }
- else if (arg4 == "S")
- {
- result = -1.0000000 * fabs(result);
- }
- }
- else if (p.IsNumber(arg1) && p.IsNumber(arg2))
- {
- result = atof(arg1.c_str()) + atof(arg2.c_str())/60.0;
- su.Uppercase(arg3);
- su.StripControlCharacters(arg3);
- su.StripSpaces(arg3);
- if (arg3 == "N")
- {
- result = fabs(result);
- }
- else if (arg3 == "S")
- {
- result = -1.0000000 * fabs(result);
- }
- }
- }
- }
- if (is_negative)
- {
- result = -fabs(result);
- }
- return result;
- }
- double GeoCalc::DecodeLongitude(const char * txt)
- {
- double result = 0.0;
- UtilityParser p;
- StringUtil su;
- std::string arg1, arg2, arg3, arg4;
- bool is_negative = false;
- std::string tmp = txt;
- su.StripSpaces(tmp);
- if (su.StartsWith(tmp, "-"))
- {
- is_negative = true;
- }
- p.SetDelimiters(" -:,");
- p.ParseString(txt);
- int size = p.GetSize();
- if (size > 0)
- {
- if (size == 1)
- {
- if (p.GetArgument(0,arg1))
- {
- if (p.IsNumber(arg1))
- {
- result = atof(arg1.c_str());
- }
- }
- }
- else if (size == 2)
- {
- if (p.GetArgument(0,arg1) && p.GetArgument(1,arg2))
- {
- if (p.IsNumber(arg1))
- {
- result = atof(arg1.c_str());
- if (p.IsNumber(arg2))
- {
- result += atof(arg2.c_str())/60.0;
- }
- else
- {
- su.Uppercase(arg2);
- su.StripControlCharacters(arg2);
- su.StripSpaces(arg2);
- if (arg2 == "E")
- {
- result = fabs(result);
- }
- else if (arg2 == "W")
- {
- result = -1.0000000 * fabs(result);
- }
- else if (arg2.length() > 0)
- {
- result = 0;
- }
- }
- }
- }
- }
- else if (size > 2)
- {
- p.GetArgument(0,arg1);
- p.GetArgument(1,arg2);
- p.GetArgument(2,arg3);
- p.GetArgument(3,arg4);
- if (p.IsNumber(arg1) && p.IsNumber(arg2) && p.IsNumber(arg3))
- {
- result = atof(arg1.c_str()) + atof(arg2.c_str())/60.0 + atof(arg3.c_str())/3600.0;
- su.Uppercase(arg4);
- su.StripControlCharacters(arg4);
- su.StripSpaces(arg4);
- if (arg4 == "E")
- {
- result = fabs(result);
- }
- else if (arg4 == "W")
- {
- result = -1.0000000 * fabs(result);
- }
- }
- else if (p.IsNumber(arg1) && p.IsNumber(arg2))
- {
- result = atof(arg1.c_str()) + atof(arg2.c_str())/60.0;
- su.Uppercase(arg3);
- su.StripControlCharacters(arg3);
- su.StripSpaces(arg3);
- if (arg3 == "E")
- {
- result = fabs(result);
- }
- else if (arg3 == "W")
- {
- result = -1.0000000 * fabs(result);
- }
- }
- }
- }
- if (is_negative)
- {
- result = -fabs(result);
- }
- return result;
- }
- void GeoCalc::CoordinateToDMS(double coord, INT32& deg, INT32& minutes, double& secs)
- {
- double dlat, d, m, s;
- dlat = coord + (double)0.000001;
-
- d = (double)(long)dlat;
- m = (double)((dlat - d)*60.0);
- s = (double)((m - (double)(long)(m))*60.0);
- if (s >= 60.00)
- {
- if (m == 59)
- {
- d += 1;
- if (d < 0.0)
- d -= 1;
- else
- d += 1;
- m = 0;
- s -= 60.0;
- }
- else
- {
- s -= 60.00;
- s += 1;
- }
- }
- deg = (int)d;
- minutes = (int)m;
- secs = s;
- }
- #define IABS(a) ((a>0) ? a : -a)
- void GeoCalc::EncodeLatitude(double lat, std::string& s)
- {
- char txt[24];
- INT32 d, m;
- double seconds;
- CoordinateToDMS(lat,d,m,seconds);
- sprintf(txt,"%d-%.2d-%2.1f %c",IABS(d),IABS(m),fabs(seconds),(lat>0.0)?'N':'S');
- s = txt;
- }
- void GeoCalc::EncodeLongitude(double lon, std::string& s)
- {
- char txt[24];
- INT32 d, m;
- double seconds;
- CoordinateToDMS(lon,d,m,seconds);
- sprintf(txt,"%d-%.2d-%2.1f %c",IABS(d),IABS(m),fabs(seconds),(lon>0.0)?'E':'W');
- s = txt;
- }