与地图经纬度计算有关的一些积累

网上有关这些的东西很多,但是试过之后有的计算偏差大一点,有的小一点。下面这几个相关计算我自己试过之后相对来说计算的误差小一点。

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;
    }
}
原文地址:https://www.cnblogs.com/yangms/p/9946399.html