GIS地图打点--点位坐标系要和地图坐标系一致(含坐标系转换)

594 阅读5分钟

这篇笔记主要记录,GIS地图打点--点位坐标系要和地图坐标系一致

用Cesium渲染一个地图实例,默认加载的必应地图,是WGS84坐标系。

  1. 如果我用高德地图坐标拾取工具, 拾取深圳市民中心坐标(GCJ-02坐标系),打一个点上图,那个点的位置会不会出现在,默认的必应地图, 深圳市民中心那个位置?

  2. 如果不出现在地图的市民中心位置,我把拾取的坐标转换成WGS84, 再看下打点是否会出现在深圳市民中心这个位置

验证WGS84坐标系地图和GCJ-02坐标系点位是否匹配

验证WGS84坐标系地图和GCJ-02坐标系点位是否匹配 ,就是说,一个GCJ-02坐标系的点,能否正确显示在WGS84坐标系地图的位置

先拾取实验点位置--深圳市民中心坐标 GCJ-02坐标系坐标

高德坐标拾取工具, 高德地图坐标用的是GCJ-02(火星坐标系),拾取到的深圳市民中心坐标是114.059614,22.543673

image.png

用Cesium的Entity加载下拾取的坐标,看下位置是否出现在市民中心
const viewer = new Cesium.Viewer('cesiumContainer');

// 深圳市民中心 gcj02坐标系坐标
const coordinates_gcj02 = [114.059614,22.543673];

// 加载一个文本展示到
viewer.entities.add({
    position: Cesium.Cartesian3.fromDegrees(coordinates_gcj02[0], coordinates_gcj02[1]),
    label: {
    id: 'gcj-02',
    text: '深圳市民中心-gcj02坐标系坐标'
  }
});
viewer.camera.flyTo({
    destination: Cesium.Cartesian3.fromDegrees(coordinates_gcj02[0],coordinates_gcj02[1], 600)
});

把上面代码放到cesium 实验平台运行下,

image.png 可以看到高德拾取的,不在我们想要的市民中心那个位置,现在把拾取的坐标,转换成WGS84坐标系看下,

验证WGS84坐标系地图和WGS84坐标系点位是否匹配

验证WGS84坐标系地图和WGS84坐标系点位是否匹配,看下WGS84坐标系点位能否正确显示在WGS84坐标系地图位置

坐标系转换 -- 把试验点GCJ-02坐标转成WGS84坐标

转换方法 这个转换方法网上找的,不是我写的

/**
 * @Author: Caven
 * @Date: 2021-01-31 20:40:25
 */

const BD_FACTOR = (3.14159265358979324 * 3000.0) / 180.0
const PI = 3.1415926535897932384626
const RADIUS = 6378245.0
const EE = 0.00669342162296594323

class CoordTransform {
    /**
     * BD-09 To GCJ-02
     * @param lng
     * @param lat
     * @returns {number[]}
     */
    static BD09ToGCJ02(lng, lat) {
        let x = +lng - 0.0065
        let y = +lat - 0.006
        let z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * BD_FACTOR)
        let theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * BD_FACTOR)
        let gg_lng = z * Math.cos(theta)
        let gg_lat = z * Math.sin(theta)
        return [gg_lng, gg_lat]
    }

    /**
     * GCJ-02 To BD-09
     * @param lng
     * @param lat
     * @returns {number[]}
     * @constructor
     */
    static GCJ02ToBD09(lng, lat) {
        lat = +lat
        lng = +lng
        let z =
            Math.sqrt(lng * lng + lat * lat) + 0.00002 * Math.sin(lat * BD_FACTOR)
        let theta = Math.atan2(lat, lng) + 0.000003 * Math.cos(lng * BD_FACTOR)
        let bd_lng = z * Math.cos(theta) + 0.0065
        let bd_lat = z * Math.sin(theta) + 0.006
        return [bd_lng, bd_lat]
    }

    /**
     * WGS-84 To GCJ-02
     * @param lng
     * @param lat
     * @returns {number[]}
     */
    static WGS84ToGCJ02(lng, lat) {
        lat = +lat
        lng = +lng
        if (this.out_of_china(lng, lat)) {
            return [lng, lat]
        } else {
            let d = this.delta(lng, lat)
            return [lng + d[0], lat + d[1]]
        }
    }

    /**
     * GCJ-02 To WGS-84
     * @param lng
     * @param lat
     * @returns {number[]}
     * @constructor
     */
    static GCJ02ToWGS84(lng, lat) {
        lat = +lat
        lng = +lng
        if (this.out_of_china(lng, lat)) {
            return [lng, lat]
        } else {
            let d = this.delta(lng, lat)
            let mgLng = lng + d[0]
            let mgLat = lat + d[1]
            return [lng * 2 - mgLng, lat * 2 - mgLat]
        }
    }

    /**
     *
     * @param lng
     * @param lat
     * @returns {number[]}
     */
    static delta(lng, lat) {
        let dLng = this.transformLng(lng - 105, lat - 35)
        let dLat = this.transformLat(lng - 105, lat - 35)
        const radLat = (lat / 180) * PI
        let magic = Math.sin(radLat)
        magic = 1 - EE * magic * magic
        const sqrtMagic = Math.sqrt(magic)
        dLng = (dLng * 180) / ((RADIUS / sqrtMagic) * Math.cos(radLat) * PI)
        dLat = (dLat * 180) / (((RADIUS * (1 - EE)) / (magic * sqrtMagic)) * PI)
        return [dLng, dLat]
    }

    /**
     *
     * @param lng
     * @param lat
     * @returns {number}
     */
    static transformLng(lng, lat) {
        lat = +lat
        lng = +lng
        let ret =
            300.0 +
            lng +
            2.0 * lat +
            0.1 * lng * lng +
            0.1 * lng * lat +
            0.1 * Math.sqrt(Math.abs(lng))
        ret +=
            ((20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) *
                2.0) /
            3.0
        ret +=
            ((20.0 * Math.sin(lng * PI) + 40.0 * Math.sin((lng / 3.0) * PI)) * 2.0) /
            3.0
        ret +=
            ((150.0 * Math.sin((lng / 12.0) * PI) +
                    300.0 * Math.sin((lng / 30.0) * PI)) *
                2.0) /
            3.0
        return ret
    }

    /**
     *
     * @param lng
     * @param lat
     * @returns {number}
     */
    static transformLat(lng, lat) {
        lat = +lat
        lng = +lng
        let ret = -100.0 +
            2.0 * lng +
            3.0 * lat +
            0.2 * lat * lat +
            0.1 * lng * lat +
            0.2 * Math.sqrt(Math.abs(lng))
        ret +=
            ((20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) *
                2.0) /
            3.0
        ret +=
            ((20.0 * Math.sin(lat * PI) + 40.0 * Math.sin((lat / 3.0) * PI)) * 2.0) /
            3.0
        ret +=
            ((160.0 * Math.sin((lat / 12.0) * PI) +
                    320 * Math.sin((lat * PI) / 30.0)) *
                2.0) /
            3.0
        return ret
    }

    /**
     *
     * @param lng
     * @param lat
     * @returns {boolean}
     */
    static out_of_china(lng, lat) {
        lat = +lat
        lng = +lng
        return !(lng > 73.66 && lng < 135.05 && lat > 3.86 && lat < 53.55)
    }
}


export default CoordTransform

引入转换算法文件,转换坐标

import CoordTransform from '../utils/CoordTransform.js'
const coordinates_gcj02 = [114.059614,22.543673];

const coordinates_wgs84 = CoordTransform.GCJ02ToWGS84(...coordinates_gcj02)

转换后的深圳市民中心坐标是114.05449802495899,22.54638718677114

加载WGS84坐标系点位
const viewer = new Cesium.Viewer('cesiumContainer');

// 深圳市民中心 gcj02坐标系坐标
const coordinates_gcj02 = [114.059614,22.543673];

// 加载一个文本展示
viewer.entities.add({
    position: Cesium.Cartesian3.fromDegrees(coordinates_gcj02[0], coordinates_gcj02[1]),
    label: {
    id: 'gcj-02',
    text: '深圳市民中心-gcj02坐标系坐标'
  }
});

// wgs84坐标展示文本
const coordinates_wgs84 = [114.05449802495899,22.54638718677114];
viewer.entities.add({
    position: Cesium.Cartesian3.fromDegrees(coordinates_wgs84[0], coordinates_wgs84[1]),
    label: {
    id: 'wgs84',
    text: '深圳市民中心-wgs84坐标系坐标'
  }
});
viewer.camera.flyTo({
    destination: Cesium.Cartesian3.fromDegrees(coordinates_gcj02[0],coordinates_gcj02[1], 1600)
});

把上面代码放到cesium 实验平台运行下,

image.png

只有WGS84才能显示在正确的位置上,也就是说:WGS84坐标系的地图需要WGS84位置的坐标点才能正确表述位置。

接着验证GCJ-02地图和GCJ-02坐标点是否匹配

加载高德地图(GCJ-02坐标系),再把上面2个坐标系点位上图试试,

const viewer = new Cesium.Viewer('cesiumContainer');

// 深圳市民中心 gcj02坐标系坐标
const coordinates_gcj02 = [114.059614,22.543673];

// 加载一个文本展示
viewer.entities.add({
    position: Cesium.Cartesian3.fromDegrees(coordinates_gcj02[0], coordinates_gcj02[1]),
    label: {
    id: 'gcj-02',
    text: 'gcj02',
    fillColor: Cesium.Color.BLUE,
    scale: 0.5
  }
});


const coordinates_wgs84 = [114.05449802495899,22.54638718677114];
viewer.entities.add({
    position: Cesium.Cartesian3.fromDegrees(coordinates_wgs84[0], coordinates_wgs84[1]),
    label: {
    id: 'wgs84',
    text: 'wgs84',
    fillColor: Cesium.Color.RED,
    scale: 0.5
  }
});

// 加载高德地图
function loadGaode() {
  const gaodeLayer = new Cesium.ImageryLayer(
    new Cesium.UrlTemplateImageryProvider({
      url: 'https://webrd02.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}', // 纯地标图(地名)
      minimumLevel: 1,
      maximumLevel: 18,
    })
  );
  viewer.imageryLayers.add(gaodeLayer);
}

loadGaode();

viewer.camera.flyTo({
    destination: Cesium.Cartesian3.fromDegrees(coordinates_gcj02[0],coordinates_gcj02[1], 1600)
});

image.png

可以看到高德地图能正确加载其坐标拾取工具的点,就是说GCJ-02坐标系的地图要加载GCJ-02坐标系的点。

结论: 我们的GIS应用常常加载多个地图,比如天地图,高德地图,百度地图,而我们地图常常需要展示一些点位位置,在应用内切换地图的时候,如果涉及到不同的坐标系,就要转换打点数据的坐标系,比如从高德地图切到百度地图,点位就要从GCJ-02 转到 BD-09坐标系, 不然点位就不能显示在正确的位置。