网上有关这些的东西很多,但是试过之后有的计算偏差大一点,有的小一点。下面这几个相关计算我自己试过之后相对来说计算的误差小一点。
1、已知两点经纬度计算方位角
/** * 以真北为0度起点,由东向南向西顺时针旋转360度,主要是用于控制象限。 * 根据2点经纬度,计算方位角 * 给定2点,获得经纬度 * 起点经纬度,都是以度为单位 * 终点经纬度,都是以度为单位 */ public static double computeAzimuth(LatLng la1, LatLng la2) { double lat1 = la1.getLatitude(), lon1 = la1.getLongitude(), lat2 = la2.getLatitude(), lon2 = la2.getLongitude(); double result = 0.0; int ilat1 = (int) (0.50 + lat1 * 360000.0); int ilat2 = (int) (0.50 + lat2 * 360000.0); int ilon1 = (int) (0.50 + lon1 * 360000.0); int ilon2 = (int) (0.50 + lon2 * 360000.0); lat1 = Math.toRadians(lat1); lon1 = Math.toRadians(lon1); lat2 = Math.toRadians(lat2); lon2 = Math.toRadians(lon2); if ((ilat1 == ilat2) && (ilon1 == ilon2)) { return result; } else if (ilon1 == ilon2) { if (ilat1 > ilat2) result = 180.0; } else { double c = Math .acos(Math.sin(lat2) * Math.sin(lat1) + Math.cos(lat2) * Math.cos(lat1) * Math.cos((lon2 - lon1))); double A = Math.asin(Math.cos(lat2) * Math.sin((lon2 - lon1)) / Math.sin(c)); result = Math.toDegrees(A); 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; }
2、已知一点经纬度、方位角和距离,求另一点经纬度
/* * 大地坐标系资料WGS-84 长半径a=6378137 短半径b=6356752.3142 扁率f=1/298.2572236 */ /** * 长半径a=6378137 */ private static double a = 6378137; /** * 短半径b=6356752.3142 */ private static double b = 6356752.3142; /** * 扁率f=1/298.2572236 */ private static double f = 1 / 298.2572236; /** * 计算另一点经纬度 * * @param, lon * 经度 * @param, lat * 维度 * @param, lonlat * 已知点经纬度 * @param, brng * 方位角 度 * @param, dist * 距离(米) * 已知一点经纬度,方位角,距离,求另一点经纬度 */ public static LatLng getLatlngByFixedPointAziDistance(double brng, double dist, LatLng fixedPoint) { double lon = fixedPoint.getLongitude(); double lat = fixedPoint.getLatitude(); double alpha1 = rad(brng); double sinAlpha1 = Math.sin(alpha1); double cosAlpha1 = Math.cos(alpha1); double tanU1 = (1 - f) * Math.tan(rad(lat)); double cosU1 = 1 / Math.sqrt((1 + tanU1 * tanU1)); double sinU1 = tanU1 * cosU1; double sigma1 = Math.atan2(tanU1, cosAlpha1); double sinAlpha = cosU1 * sinAlpha1; double cosSqAlpha = 1 - sinAlpha * sinAlpha; double uSq = cosSqAlpha * (a * a - b * b) / (b * b); double A = 1 + uSq / 16384 * (4096 + uSq * (-768 + uSq * (320 - 175 * uSq))); double B = uSq / 1024 * (256 + uSq * (-128 + uSq * (74 - 47 * uSq))); double cos2SigmaM = 0; double sinSigma = 0; double cosSigma = 0; double sigma = dist / (b * A), sigmaP = 2 * Math.PI; while (Math.abs(sigma - sigmaP) > 1e-12) { cos2SigmaM = Math.cos(2 * sigma1 + sigma); sinSigma = Math.sin(sigma); cosSigma = Math.cos(sigma); double deltaSigma = B * sinSigma * (cos2SigmaM + B / 4 * (cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM) - B / 6 * cos2SigmaM * (-3 + 4 * sinSigma * sinSigma) * (-3 + 4 * cos2SigmaM * cos2SigmaM))); sigmaP = sigma; sigma = dist / (b * A) + deltaSigma; } double tmp = sinU1 * sinSigma - cosU1 * cosSigma * cosAlpha1; double lat2 = Math.atan2(sinU1 * cosSigma + cosU1 * sinSigma * cosAlpha1, (1 - f) * Math.sqrt(sinAlpha * sinAlpha + tmp * tmp)); double lambda = Math.atan2(sinSigma * sinAlpha1, cosU1 * cosSigma - sinU1 * sinSigma * cosAlpha1); double C = f / 16 * cosSqAlpha * (4 + f * (4 - 3 * cosSqAlpha)); double L = lambda - (1 - C) * f * sinAlpha * (sigma + C * sinSigma * (cos2SigmaM + C * cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM))); double revAz = Math.atan2(sinAlpha, -tmp); // final bearing //System.out.println(revAz); //Log.e("方位角",String.valueOf(revAz)); //Log.e("经纬度",String.valueOf(deg(lat2))+"经度"+String.valueOf(lon+deg(L))); //System.out.println(+","+); return new LatLng(deg(lat2), lon + deg(L)); }
3、已知两个坐标和其对应的方位角,求另一点坐标
public static LatLng getLatlngByTwoFixedPointAziIntersect(double aziOne, double aziTwo, LatLng fixedPointOne, LatLng fixedPointTwo) { double azi; LatLng latLng; if (fixedPointOne.getLongitude() > fixedPointTwo.getLongitude()) { azi = aziOne; latLng = fixedPointOne; aziOne = aziTwo; fixedPointOne = fixedPointTwo; aziTwo = azi; fixedPointTwo = latLng; } Double aziAB = computeAzimuth(fixedPointOne, fixedPointTwo); Double aziBA = 180 + aziAB; if (0 <= aziOne && aziOne < 180) { if (aziOne <= aziAB) { if (aziOne <= aziTwo && aziTwo < aziBA) { Toast.makeText(MyApplication.getContextObject(), "不能相交,请重新输入方位角", Toast.LENGTH_SHORT).show(); return new LatLng(); } } if (aziOne > aziAB) { if ((0 <= aziTwo && aziTwo <= aziOne) || (aziBA < aziTwo && aziTwo <= 360)) { Toast.makeText(MyApplication.getContextObject(), "不能相交,请重新输入方位角", Toast.LENGTH_SHORT).show(); return new LatLng(); } } } if (180 <= aziOne && aziOne < 270) { if ((0 <= aziTwo && aziTwo <= aziOne) || (aziBA < aziTwo && aziTwo < 360)) { Toast.makeText(MyApplication.getContextObject(), "不能相交,请重新输入方位角", Toast.LENGTH_SHORT).show(); return new LatLng(); } } if (270 <= aziOne && aziOne < 360) { if ((0 <= aziTwo && aziTwo < aziBA) || (aziOne < aziTwo && aziTwo < 360)) { Toast.makeText(MyApplication.getContextObject(), "不能相交,请重新输入方位角", Toast.LENGTH_SHORT).show(); return new LatLng(); } } double k1 = Math.tan(rad(90 - aziOne)); double k2 = Math.tan(rad(90 - aziTwo)); double y1 = fixedPointOne.getLatitude(); double x1 = fixedPointOne.getLongitude(); double y2 = fixedPointTwo.getLatitude(); double x2 = fixedPointTwo.getLongitude(); double lon = (y2 - y1 + k1 * x1 - k2 * x2) / (k1 - k2); double lat = (k1 * y2 - k2 * y1 + k1 * k2 * x1 - k1 * k2 * x2) / (k1 - k2); /* if (0 <= aziOne && aziOne < 180) { if (aziOne < aziAB) { if (lat < y1) { Toast.makeText(MyApplication.getContextObject(), "不能相交,请重新输入方位角", Toast.LENGTH_SHORT).show(); return new LatLng(); } } if (aziOne > aziAB) { if (lat > y2) { Toast.makeText(MyApplication.getContextObject(), "不能相交,请重新输入方位角", Toast.LENGTH_SHORT).show(); return new LatLng(); } } }*/ if (lat > 90 || lat < -90) { Toast.makeText(MyApplication.getContextObject(), "不能相交,请重新输入方位角", Toast.LENGTH_SHORT).show(); return new LatLng(); } return new LatLng(lat, lon); }
度与弧度的转换关系
/** * 度换成弧度 * * @param d 度 * @return 弧度 */ private static double rad(double d) { return d * Math.PI / 180.0; } /** * 弧度换成度 * * @param x 弧度 * @return 度 */ private static double deg(double x) { return x * 180 / Math.PI; }
4、拓扑关系的相关运算
public class Topology { /** * 判断点是否在面内 * * @param polygon 组成多边形的坐标串 多变形节点不能少于3 * @param point 测试的点 * @return 是否在多边形内 * @throws IllegalArgumentException */ public static boolean isPointInPolygon(List<LatLng> polygon, LatLng point) throws IllegalArgumentException { if (polygon.size() < 3) { throw new IllegalArgumentException("polygon 节点少于3"); } LatLngBounds.Builder builder = new LatLngBounds.Builder(); builder.includes(polygon); LatLngBounds bounds = builder.build(); if (!bounds.contains(point)) return false; LatLng far = new LatLng(point.getLatitude(), -180.0); //向正西方向做一条射线 LineSegment ray = new LineSegment(point, far); int intersectionCount = 0; int count = polygon.size(); for (int i = 0; i < count; i++) { LatLng latLng1 = polygon.get(i); LatLng latLng2 = polygon.get((i + 1) % count); LineSegment lineSeg = new LineSegment(latLng1, latLng2); if (isPointOnLineSegment(point, lineSeg)) return true; if (isSameDouble(latLng2.getLatitude(), latLng1.getLatitude())) continue; if (isSameDouble(latLng1.getLatitude(), point.getLatitude()) && (latLng1.getLatitude() > latLng2.getLatitude())) { intersectionCount++; } else if (isSameDouble(latLng2.getLatitude(), point.getLatitude()) && (latLng2.getLatitude() > latLng1.getLatitude())) { intersectionCount++; } else { if (isLineSegmentIntersect(lineSeg, ray)) { intersectionCount++; } } } return (intersectionCount & 1) == 1; } public static class LineSegment { public LatLng start; public LatLng end; private LatLngBounds bounds; public LineSegment(LatLng start, LatLng end) { this.start = start; this.end = end; } public LatLngBounds getBounds() { if (bounds == null) { LatLngBounds.Builder builder = new LatLngBounds.Builder(); builder.include(start).include(end); bounds = builder.build(); } return bounds; } } /** * 线段是否相交 * * @param l1 * @param l2 * @return */ public static boolean isLineSegmentIntersect(LineSegment l1, LineSegment l2) { if (isLineSegmentExclusive(l1, l2)) return false; //矩形不相交 double slat1 = l1.start.getLatitude(); double slon1 = l1.start.getLongitude(); double elat1 = l1.end.getLatitude(); double elon1 = l1.end.getLongitude(); double slat2 = l2.start.getLatitude(); double slon2 = l2.start.getLongitude(); double elat2 = l2.end.getLatitude(); double elon2 = l2.end.getLongitude(); //跨立试验 double p1xq = crossProduct(slon1 - slon2, slat1 - slat2, elon2 - slon2, elat2 - slat2); double p2xq = crossProduct(elon1 - slon2, elat1 - slat2, elon2 - slon2, elat2 - slat2); double q1xp = crossProduct(slon2 - slon1, slat2 - slat1, elon1 - slon1, elat1 - slat1); double q2xp = crossProduct(elon2 - slon1, elat2 - slat1, elon1 - slon1, elat1 - slat1); return (p1xq * p2xq <= 0) && (q1xp * q2xp <= 0); } /** * 线段 包围矩形是否 不相交 * * @param l1 * @param l2 * @return */ public static boolean isLineSegmentExclusive(LineSegment l1, LineSegment l2) { double W1 = Math.min(l1.start.getLongitude(), l1.end.getLongitude()); double E1 = Math.max(l1.start.getLongitude(), l1.end.getLongitude()); double N1 = Math.max(l1.start.getLatitude(), l1.end.getLatitude()); double S1 = Math.min(l1.start.getLatitude(), l1.end.getLatitude()); double W2 = Math.min(l2.start.getLongitude(), l2.end.getLongitude()); double E2 = Math.max(l2.start.getLongitude(), l2.end.getLongitude()); double N2 = Math.max(l2.start.getLatitude(), l2.end.getLatitude()); double S2 = Math.min(l2.start.getLatitude(), l2.end.getLatitude()); if (N1 < S2) return true; if (S1 > N2) return true; if (E1 < W2) return true; if (W1 > E2) return true; return false; } /** * 点是否在线段上 * * @param point * @param line * @return */ public static boolean isPointOnLineSegment(LatLng point, LineSegment line) { LatLngBounds.Builder builder = new LatLngBounds.Builder(); LatLngBounds bounds = builder.include(line.start).include(line.end).build(); if (!bounds.contains(point)) { return false; } LatLng start = line.start; LatLng end = line.end; double cp = crossProduct(end.getLongitude() - start.getLongitude(), end.getLatitude() - start.getLongitude(), point.getLongitude() - start.getLongitude(), point.getLatitude() - start.getLatitude()); return Math.abs(cp) < 0.000000001; } /** * 点到线段的距离 * * @param point * @param lineSegment * @return */ public static double point2LineSegment(LatLng point, LineSegment lineSegment) { double dis1 = point2Point(point, lineSegment.start); double dis2 = point2Point(point, lineSegment.end); double dis3 = point2Line(point, lineSegment); return Math.min(dis1, Math.min(dis2, dis3)); } /** * 点到直线的距离 * * @param point * @param line * @return */ public static double point2Line(LatLng point, LineSegment line) { //先求过点 point 与 line垂直的直线与line 的交点 double x1 = line.start.getLongitude(); double x2 = line.end.getLongitude(); double y1 = line.start.getLatitude(); double y2 = line.end.getLatitude(); double xp = point.getLongitude(); double yp = point.getLatitude(); double x, y; if (y1 == y2) { y = y1; x = xp; return point.distanceTo(new LatLng(y, x)); } else if (x1 == x2) { x = x1; y = yp; return point.distanceTo(new LatLng(y, x)); } else { double k = (x2 - x1) / (y1 - y2); double b = yp - (k * xp); double k1 = (y2 - y1) / (x2 - x1); double b1 = y1 - (k1 * x1); x = (b1 - b) / (k - k1); y = k * x + b; return point.distanceTo(new LatLng(y, x)); } } /** * 线段到面的距离 * * @param lineSegment * @param polygon * @return */ public static double line2Polygon(LineSegment lineSegment, List<LatLng> polygon) { //先判断线是否与面相交 double minDistance = Double.MAX_VALUE; int count = polygon.size(); for (int i = 0; i < count; i++) { LatLng latLng1 = polygon.get(i); double dis = point2Line(latLng1, lineSegment); minDistance = Math.min(dis, minDistance); LatLng latLng2 = polygon.get((i + 1) % count); LineSegment lineSeg = new LineSegment(latLng1, latLng2); //如果相交,返回0 if (isLineSegmentIntersect(lineSeg, lineSegment)) { return 0; } } return minDistance; } /** * 取模运算 * * @param lineSegment * @return */ private static double mod(LineSegment lineSegment) { double x = lineSegment.end.getLongitude() - lineSegment.start.getLongitude(); double y = lineSegment.end.getLatitude() - lineSegment.start.getLatitude(); return Math.sqrt(x * x + y * y); } /** * 点到点的距离 * * @param latLng1 * @param latLng2 * @return */ public static double point2Point(LatLng latLng1, LatLng latLng2) { return latLng1.distanceTo(latLng2); } /** * 点和线段是否接近 * * @param buffer 容差 * @param point 被判断的点 * @param lienStart 线段起点 * @param lineEnd 线段终点 * @return */ public static boolean isNearBy(LatLng point, LatLng lienStart, LatLng lineEnd, double buffer) { //先使用经纬度筛选一遍 LineSegment lineSegment = new LineSegment(lienStart, lineEnd); double dis = buffer / 111000; //换算为度 LatLngBounds bounds = lineSegment.getBounds(); if (bounds.getLatNorth() + dis < point.getLatitude()) return false; if (bounds.getLatSouth() - dis > point.getLatitude()) return false; if (bounds.getLonEast() + dis < point.getLongitude()) return false; if (bounds.getLonWest() - dis > point.getLongitude()) return false; double distance = Topology.point2LineSegment(point, lineSegment); return distance <= buffer; } /** * 求两条直线的交点 * 没有考虑重合的情况,重合的情况,这里返回不相交 * * @param start1 * @param end1 * @param start2 * @param end2 * @return */ public static LatLng lineInterSection(LatLng start1, LatLng end1, LatLng start2, LatLng end2) { //先判断是否相交 double deltaX1 = end1.getLongitude() - start1.getLongitude(); double deltaX2 = end2.getLongitude() - start2.getLongitude(); double deltaY1 = end1.getLatitude() - start1.getLatitude(); double deltaY2 = end2.getLatitude() - start2.getLatitude(); if (deltaX1 == 0.0 && deltaX2 == 0.0) return null; double k1 = deltaY1 / deltaX1; double k2 = deltaY2 / deltaX2; double b1 = start1.getLatitude() - k1 * start1.getLongitude(); double b2 = start2.getLatitude() - k2 * start2.getLongitude(); double x = (b2 - b1) / (k1 - k2); double y = k1 * x + b1; return new LatLng(y, x); } /** * 计算总路程 * * @param latLngList * @return 总里程(公里) */ public static double computeDistance(final List<LatLng> latLngList) { if (latLngList.size() < 1) return 0.0; double distance = 0.0; int count = latLngList.size(); for (int i = 1; i < count; i++) { distance += latLngList.get(i - 1).distanceTo(latLngList.get(i)); } return distance / 1000; } /** * 叉积 * 矢量积 * * @param x1 * @param y1 * @param x2 * @param y2 * @return */ public static double crossProduct(double x1, double y1, double x2, double y2) { return x1 * y2 - x2 * y1; } /** * 盘点点在线的左边还是右边 * @param x1 * @param y1 * @param x2 * @param y2 * @param x3 * @param y3 * @return 左边 >0 右边 < 0 */ public static int leftRight(double x1,double y1,double x2,double y2,double x3,double y3){ double f = (x2 - x1) * (y3 - y1) - (x3 - x1) * (y2 - y1); if(f > 0) return 1; if(f < 0) return -1; return 0; } /** * 点积 ,数量积 * * @param x1 * @param y1 * @param x2 * @param y2 * @return */ public static double dopProduct(double x1, double y1, double x2, double y2) { return x1 * x2 + y1 * y2; } public static boolean isSameDouble(double var1, double var2) { return Math.abs(var2 - var1) < 0.000000000001; } }