一个基于经纬度操作的地图的工具类分享:区域重叠、点是否在区域内、不同坐标系的经纬度转换、线段是否穿过区域、两点计算中心点坐标、根据圆心坐标和半径得到圆弧坐标

最近在公司做的项目需要用到地图经纬度的一些操作,只能手动的去写一些算法,期间参考了网上很多朋友的博客,现在分享出来,希望对搞这块的朋友有所帮助。

一、高德地图、奥维地图、百度地图之前的互转,WGS-84坐标系、GCJ-02坐标系、BD-09坐标系之间的互转。(转)

高德地图、奥维地图、百度地图他们之间的坐标系不一样,所以把相同的经纬度坐标放在不同的地图上展示的位置也不一样,详细说明见下引用。工具类代码如下所示。

1.WGS-84原始坐标系,一般用国际GPS纪录仪记录下来的经纬度,通过GPS定位拿到的原始经纬度,Google和高德地图定位的的经纬度(国外)都是基于WGS-84坐标系的;但是在国内是不允许直接用WGS84坐标系标注的,必须经过加密后才能使用;
2.GCJ-02坐标系,又名“火星坐标系”,是我国国测局独创的坐标体系,由WGS-84加密而成,在国内,必须至少使用GCJ-02坐标系,或者使用在GCJ-02加密后再进行加密的坐标系,如百度坐标系。高德和Google在国内都是使用GCJ-02坐标系,可以说,GCJ-02是国内最广泛使用的坐标系;
3.百度坐标系:bd-09,百度坐标系是在GCJ-02坐标系的基础上再次加密偏移后形成的坐标系,只适用于百度地图。(目前百度API提供了从其它坐标系转换为百度坐标系的API,但却没有从百度坐标系转为其他坐标系的API)
作者:青檬可乐
链接:https://www.jianshu.com/p/d3dd4149bb0b
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

/**
 * 坐标转换工具
 * 奥维地图:WGS-84坐标系
 * 高德地图、Google地图:GCJ-02坐标系
 * 百度坐标系:BD-09坐标系  百度坐标系是在GCJ-02坐标系的基础上再次加密偏移后形成的坐标系
 */
public class CoordinateTransitionUtils {
    public static double pi = 3.1415926535897932384626;  
    public static double x_pi = 3.14159265358979324 * 3000.0 / 180.0;  
    public static double a = 6378245.0;  
    public static double ee = 0.00669342162296594323;  
  
    public static double transformLat(double x, double y) {  
        double ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y  
                + 0.2 * Math.sqrt(Math.abs(x));  
        ret += (20.0 * Math.sin(6.0 * x * pi) + 20.0 * Math.sin(2.0 * x * pi)) * 2.0 / 3.0;  
        ret += (20.0 * Math.sin(y * pi) + 40.0 * Math.sin(y / 3.0 * pi)) * 2.0 / 3.0;  
        ret += (160.0 * Math.sin(y / 12.0 * pi) + 320 * Math.sin(y * pi / 30.0)) * 2.0 / 3.0;  
        return ret;  
    }  
  
    public static double transformLon(double x, double y) {  
        double ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1  
                * Math.sqrt(Math.abs(x));  
        ret += (20.0 * Math.sin(6.0 * x * pi) + 20.0 * Math.sin(2.0 * x * pi)) * 2.0 / 3.0;  
        ret += (20.0 * Math.sin(x * pi) + 40.0 * Math.sin(x / 3.0 * pi)) * 2.0 / 3.0;  
        ret += (150.0 * Math.sin(x / 12.0 * pi) + 300.0 * Math.sin(x / 30.0  
                * pi)) * 2.0 / 3.0;  
        return ret;  
    }  
    public static double[] transform(double lat, double lon) {  
        if (outOfChina(lat, lon)) {  
            return new double[]{lat,lon};  
        }  
        double dLat = transformLat(lon - 105.0, lat - 35.0);  
        double dLon = transformLon(lon - 105.0, lat - 35.0);  
        double radLat = lat / 180.0 * pi;  
        double magic = Math.sin(radLat);  
        magic = 1 - ee * magic * magic;  
        double sqrtMagic = Math.sqrt(magic);  
        dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * pi);  
        dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos(radLat) * pi);  
        double mgLat = lat + dLat;  
        double mgLon = lon + dLon;  
        return new double[]{mgLat,mgLon};  
    }  
    public static boolean outOfChina(double lat, double lon) {  
        if (lon < 72.004 || lon > 137.8347)  
            return true;  
        if (lat < 0.8293 || lat > 55.8271)  
            return true;  
        return false;  
    }  
    /** 
     * 84 to 火星坐标系 (GCJ-02) World Geodetic System ==> Mars Geodetic System
     * 奥维 ----> 高德、Google
     * @param initAxis -->"经度,纬度"这样形式的坐标
     * @return 火星坐标系坐标
     */  
    public static String gps84_To_Gcj02(String initAxis) {
        String[] split = initAxis.split(",");
        double lon = Double.parseDouble(split[0]);
        double lat = Double.parseDouble(split[1]);
        if (outOfChina(lat, lon)) {
            String resultAxis = lon+","+lat;
            return resultAxis;
        }  
        double dLat = transformLat(lon - 105.0, lat - 35.0);  
        double dLon = transformLon(lon - 105.0, lat - 35.0);  
        double radLat = lat / 180.0 * pi;  
        double magic = Math.sin(radLat);  
        magic = 1 - ee * magic * magic;  
        double sqrtMagic = Math.sqrt(magic);  
        dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * pi);  
        dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos(radLat) * pi);  
        double mgLat = lat + dLat;  
        double mgLon = lon + dLon;
        String resultAxis = mgLon+","+mgLat;
        return resultAxis;
    }  
  
    /** 
     *  火星坐标系 (GCJ-02) to 84 坐标系
     *  Google、高德 --> 奥维
     *  @param initAxis -->"经度,纬度"这样形式的坐标
     * */  
    public static String gcj02_To_Gps84(String initAxis) {
        String[] split = initAxis.split(",");
        double lon = Double.parseDouble(split[0]);
        double lat = Double.parseDouble(split[1]);
        double[] gps = transform(lat, lon);  
        double lontitude = lon * 2 - gps[1];  
        double latitude = lat * 2 - gps[0];
        String resultAxis = lontitude+","+latitude;
        return resultAxis;
    }  
    /** 
     * 火星坐标系 (GCJ-02) 与百度坐标系 (BD-09) 的转换算法 将 GCJ-02 坐标转换成 BD-09 坐标
     * Google、高德 --> 百度坐标
     * @param initAxis -->"经度,纬度"这样形式的坐标
     */  
    public static String gcj02_To_Bd09(String initAxis) {
        String[] split = initAxis.split(",");
        double lon = Double.parseDouble(split[0]);
        double lat = Double.parseDouble(split[1]);
        double x = lon, y = lat;  
        double z = Math.sqrt(x * x + y * y) + 0.00002 * Math.sin(y * x_pi);  
        double theta = Math.atan2(y, x) + 0.000003 * Math.cos(x * x_pi);  
        double tempLon = z * Math.cos(theta) + 0.0065;  
        double tempLat = z * Math.sin(theta) + 0.006;
        String resultAxis = tempLon+","+tempLat;
        return resultAxis;
    }  
  
    /** 
     * 火星坐标系 (GCJ-02)<----百度坐标系 (BD-09)
     * BD-09 坐标转换成GCJ-02 坐标
     * @param initAxis -->"经度,纬度"这样形式的坐标
     */  
    public static String bd09_To_Gcj02(String initAxis) {
        String[] split = initAxis.split(",");
        double lon = Double.parseDouble(split[0]);
        double lat = Double.parseDouble(split[1]);
        double x = lon - 0.0065, y = lat - 0.006;  
        double z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * x_pi);  
        double theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * x_pi);  
        double tempLon = z * Math.cos(theta);  
        double tempLat = z * Math.sin(theta);
        String resultAxis = tempLon+","+tempLat;
        return resultAxis;
    }  
  
    /**
     * 将gps84转为bd09
     * @param initAxis -->"经度,纬度"这样形式的坐标
     * @return  /
     */  
    public static String gps84_To_bd09(String initAxis){
        String gcj02 = gps84_To_Gcj02(initAxis);
        String bd09 = gcj02_To_Bd09(gcj02);
        return bd09;  
    }
    /**
     * 将bd09转为gps84
     * @param initAxis -->"经度,纬度"这样形式的坐标
     * @return /
     */
    public static String bd09_To_gps84(String initAxis){
        String gcj02 = bd09_To_Gcj02(initAxis);
        String gps84 = gcj02_To_Gps84(gcj02);
        //保留小数点后六位
        String[] split = gps84.split(",");
        double lon = Double.parseDouble(split[0]);
        double lat = Double.parseDouble(split[1]);
        lon = retain6(lon);
        lat = retain6(lat);
        return lon+","+lat;
    }  
  
    /**
     * 保留小数点后六位
     * @param num 位数
     * @return /
     */  
    private static double retain6(double num){  
        String result = String .format("%.6f", num);  
        return Double.valueOf(result);  
    }  
} 

二、根据经纬度坐标判断两个区域是否重叠的工具类

这个工具类里面包含了根据经纬度坐标判断两个区域是否重叠、根据经纬度判断坐标点是否在区域内、判断根据两点的经纬度坐标连成的线段是否穿过某个区域、根据两个经纬度坐标点计算其连线的中心点坐标等功能,不多BB,附上代码。

/**
 * Author: oldou
 * Date: 2020/12/23
 * Description:判断两个区域是否重叠、根据两个坐标的经纬度连线判断该线段是否穿过某个区域
 */
public class AreaOverlapCheckUtils {
    /**
     * 判断线是否穿过某个区域
     * @param lineAxisList 两点确定一条直线的坐标集合 {"经度1,纬度1","经度2,纬度2","经度3,纬度3".....}
     * @param AreaAxisList 区域的坐标集合 {"经度1,纬度1","经度2,纬度2","经度3,纬度3".....}
     * @return /
     */
    public static boolean isLineAcrossArea(List<String> lineAxisList,List<String> AreaAxisList){
        List<Location> locationList1 = new ArrayList<>();
        List<Location> locationList2 = new ArrayList<>();
        /* 线的坐标转换处理*/
        for (String s : lineAxisList) {
            String[] split = s.split(",");
            Location location = new Location(Double.parseDouble(split[0]),Double.parseDouble(split[1]));
            locationList1.add(location);
        }
        /* 区域的坐标转换处理*/
        for (String s : AreaAxisList) {
            String[] split = s.split(",");
            Location location = new Location(Double.parseDouble(split[0]),Double.parseDouble(split[1]));
            locationList2.add(location);
        }
        //获取线的坐标线段
        List<Line> lineList = getLines(locationList1);
        //获取区域的线段
        List<Line> lineList2 = getLines(locationList2);
        //判断是否相交
        boolean isIntersect = isIntersect(lineList, lineList2);
        if (isIntersect) return true;
        //如果不相交判断是否包含-由于没有相交线段只要存在点在多边形内就说明包含
        boolean isPolygonInPolygon = isPolygonInPolygon(locationList1, locationList2);
        if (isPolygonInPolygon) return true;
        return false;
    }

    /**
     * 判断根据给定的区域经纬度判断两个区域是否有重叠地方  A
     * @param AreaList1 区域一坐标的字符串集合 {"经度1,纬度1","经度2,纬度2","经度3,纬度3".....}
     * @param AreaList2 区域二坐标的字符串集合 {"经度1,纬度1","经度2,纬度2","经度3,纬度3".....}
     * @return 是否相交的标识
     */
    public static boolean isCoinCide(List<String> AreaList1, List<String> AreaList2) {
        List<Location> locationList1 = new ArrayList<>();
        List<Location> locationList2 = new ArrayList<>();
        /* 区域一转换*/
        for (String s : AreaList1) {
            String[] split = s.split(",");
            Location location = new Location(Double.parseDouble(split[0]),Double.parseDouble(split[1]));
            locationList1.add(location);
        }
        /* 区域二转换*/
        for (String s : AreaList2) {
            String[] split = s.split(",");
            Location location = new Location(Double.parseDouble(split[0]),Double.parseDouble(split[1]));
            locationList2.add(location);
        }
        //获取四边形的线段
        List<Line> lineList = getLines(locationList1);
        //获取三角形的线段
        List<Line> lineList2 = getLines(locationList2);
        //判断是否相交
        boolean isIntersect = isIntersect(lineList, lineList2);
        if (isIntersect) return true;
        //如果不相交判断是否包含-由于没有相交线段只要存在点在多边形内就说明包含
        boolean isPolygonInPolygon = isPolygonInPolygon(locationList1, locationList2);
        if (isPolygonInPolygon) return true;
        return false;
    }

    /**
     * 判断线段是否相交,A的附属方法 B
     * @param locationList /
     * @param locationList2 /
     * @return /
     */
    private static boolean isPolygonInPolygon(List<Location> locationList, List<Location> locationList2) {
        //判断第一个多边形是否在第二个多边形内
        for (Location location : locationList) {
            boolean isPointInPolygon = isPointInPolygon(location, locationList2);
            if (isPointInPolygon) return true;
        }
        //判断第二个多边形是否在第一个多边形内
        for (Location location : locationList2) {
            boolean isPointInPolygon = isPointInPolygon(location, locationList);
            if (isPointInPolygon) return true;
        }
        return false;
    }

    /**
     * 判断点是否在区域内,B的附属方法
     * @param location
     * @param locationList2
     * @return
     */
    private static boolean isPointInPolygon(Location location, List<Location> locationList2) {
        //点是否在多边形内
        GeneralPath path = new GeneralPath();
        //设定多边形起始点
        path.moveTo(locationList2.get(0).getLongitude(), locationList2.get(0).getLatitude());
        for (Location l : locationList2) {
            path.lineTo(l.getLongitude(), l.getLatitude());
        }
        //图像完成,封闭
        path.moveTo(locationList2.get(0).getLongitude(), locationList2.get(0).getLatitude());
        //多边形结束
        path.closePath();
        return path.contains(location.getLongitude(), location.getLatitude());
    }

    /**
     * 判断线段是否相交 A的附属方法
     * @param lineList /
     * @param lineList2 /
     * @return /
     */
    private static boolean isIntersect(List<Line> lineList, List<Line> lineList2) {
        for (Line line : lineList) {
            for (Line line1 : lineList2) {
                //两条线段是否相交
                boolean b = Line2D.linesIntersect(line.location1.longitude, line.location1.latitude, line.location2.longitude, line.location2.latitude,
                        line1.location1.longitude, line1.location1.latitude, line1.location2.longitude, line1.location2.latitude);
                if (b) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 根据坐标经纬度获取线的方法
     * @param locationList /
     * @return /
     */
    private static List<Line> getLines(List<Location> locationList) {
        List<Line> lineList = new ArrayList();
        for (int i = 0; i < locationList.size(); i++) {
            if (i < locationList.size() - 1) {
                Location l = locationList.get(i);
                Location l2 = locationList.get(i + 1);
                Line line = new Line(l, l2);
                lineList.add(line);
            } else {
                Location l = locationList.get(i);
                Location l2 = locationList.get(0);
                Line line = new Line(l, l2);
                lineList.add(line);
            }
        }
        return lineList;
    }

    /**
     * 辅助静态类-线
     */
    public static class Line {
        private Location location1;//起点
        private Location location2;//终点

        public Line(Location location1, Location location2) {
            this.location1 = location1;
            this.location2 = location2;
        }

        public Location getLocation1() {
            return location1;
        }

        public void setLocation1(Location location1) {
            this.location1 = location1;
        }

        public Location getLocation2() {
            return location2;
        }

        public void setLocation2(Location location2) {
            this.location2 = location2;
        }

        @Override
        public String toString() {
            return "Line{" +
                    "location1=" + location1 +
                    ", location2=" + location2 +
                    '}';
        }
    }

    /**
     * 辅助静态类-坐标
     */
    public static class Location {

        private double longitude;//经度
        private double latitude;//纬度

        public Location(double longitude, double latitude) {
            this.longitude = longitude;
            this.latitude = latitude;
        }

        public double getLongitude() {
            return longitude;
        }

        public void setLongitude(double longitude) {
            this.longitude = longitude;
        }

        public double getLatitude() {
            return latitude;
        }

        public void setLatitude(double latitude) {
            this.latitude = latitude;
        }

        @Override
        public String toString() {
            return "Location{" +
                    "longitude=" + longitude +
                    ", latitude=" + latitude +
                    '}';
        }
    }
}

根据两个坐标的经纬度,计算两点之间的中心点经纬度

/**
     * 根据输入的地点坐标计算中心点
     * @param startAxis 起点坐标 "经度,纬度"
     * @param endAxis 终点坐标 "经度,纬度"
     * @return 返回中心点坐标 "经度,纬度"
     */
    public static String getCenterPoint(String startAxis,String endAxis) {
        String[] arr = {startAxis,endAxis};
        int total = arr.length;
        double X = 0, Y = 0, Z = 0;
        for (int i = 0; i < arr.length; i++) {
            double lat, lon, x, y, z;
            lon = Double.parseDouble(arr[i].split(",")[0]) * Math.PI / 180;
            lat = Double.parseDouble(arr[i].split(",")[1]) * Math.PI / 180;
            x = Math.cos(lat) * Math.cos(lon);
            y = Math.cos(lat) * Math.sin(lon);
            z = Math.sin(lat);
            X += x;
            Y += y;
            Z += z;
        }
        X = X / total;
        Y = Y / total;
        Z = Z / total;
        double Lon = Math.atan2(Y, X);
        double Hyp = Math.sqrt(X * X + Y * Y);
        double Lat = Math.atan2(Z, Hyp);
        String centreAxis = (Lon * 180 / Math.PI) + "," + (Lat * 180 / Math.PI);
        return centreAxis;
    }

根据圆心坐标经纬度和半径,得到圆上的坐标

/**
     * 给定一个圆心的经纬度和半径,求圆弧上的坐标
     * @param centerPoint 经纬度坐标
     * @param radius 半径
     */
    public static List<String> getCircleAxis(Double[] centerPoint, int radius) {
        Double X; //经度
        Double Y; //纬度
        double r = 6371000.79;
        List<String> optionsAxis = new ArrayList<>();
        int numpoints = 360; // 角度
        double phase = 2 * Math.PI / numpoints;

        //画图
        for (int i = 0; i < numpoints; i++) {
            /**
             * 计算坐标点
             */
            double dx = (radius * Math.cos(i * phase));
            double dy = (radius * Math.sin(i * phase)); //乘以1.6 椭圆比例

            /**
             * 转换成经纬度
             */
            double dlng = dx / (r * Math.cos(centerPoint[1] * Math.PI / 180) * Math.PI / 180); //纬度的差值
            double dlat = dy / (r * Math.PI / 180);  // 经度的差值

            X = centerPoint[1] + dlat;
            Y = centerPoint[0] + dlng;
            String optAxis = Y+","+X;
            optionsAxis.add(optAxis);
        }
        return optionsAxis;
    }

注意

以上代码中的经纬度坐标,基本上都是 “经度,纬度” 这样的格式,请自行改动。对你有帮助的话记得点赞支持哦。

相关推荐
©️2020 CSDN 皮肤主题: 书香水墨 设计师:CSDN官方博客 返回首页