Python中将GCJ02转换为WGS84坐标系

Python中将GCJ02转换为WGS84坐标系

2024-08-18
#分享 , #编码

前文 用workflow转换Google地图kmz数据为geojson数据 中提到了我是如何获取Google Map里MyMap的数据,并自动转换为GeoJSON数据应用在博客上的。

但是最近在查看我更新的地点时,发现有部分数据实际应用在Mapbox中会存在偏差,导致很多定位在陆地上的地点漂移到了江河之上。比如这个 重庆来福士广场 就漂移到了长江上。

定位偏移

其实这个问题我发现已经满久的了,之前也咨询了一下 昱行大发哥,不过他们的给的方案我没太弄明白,当时碍于技术问题一直处于搁置状态,最近因为重新更新了地点,又把这个问题给勾了起来。

不过这次这个问题让我联想起之前在将华为的数据导入到Strava时好像也遇到过,同样也是坐标偏移的问题,当时同步软件里有个「使用XX坐标系」的选项,启用之后重新同步的数据就可以在Strava正常显示了。

所以我尝试找了个坐标系在线转换工具 EasyMap ,这个工具可以很方便的进行多种坐标系的互相转换,我试着将转换好的GeoJSON数据复制进去,并转换成WGS84坐标系后在贴到 Mapbox 的在线 GeoJSON 工具 https://geojson.io/ 中预览,发现定位已经正确显示,应该可以确定就是这个问题。

奇怪的是我之前查过GoogleMap的坐标系标准就是WGS84呀?为什么实际上是GCJ02呢?
定位恢复正常

接下来就是修改上次的转换代码,在获取定位坐标的时候想办法将坐标进一步转换成WGS84坐标系即可。

幸运的是Github有大佬分享了一个基于python的互转脚本 coordTransform_py
,该脚本提供了以下特性,同时还提供了一个js版本:

此模块用于百度坐标系(bd-09)、火星坐标系(国测局坐标系、gcj02)、WGS84坐标系的相互转换,并提供中文地址到坐标的转换功能,仅使用Python标准模块,无其他依赖。

中文地址到坐标转换使用高德地图API,需要申请API KEY。

需要js版本可以移步coordtransform

所以我们在之前 convert_kmz_to_geojson.py 文件中引入该库,并对坐标进行处理:

import os
import sys
import json
import zipfile
import xml.etree.ElementTree as ET
from shapely.geometry import shape, Point, LineString, Polygon, mapping
from coordTransform_utils import gcj02_to_wgs84 # 引入gcj02 to wgs84的转入函数

def convert_kmz_to_geojson(kmz_file_path, geojson_file_path):
    with zipfile.ZipFile(kmz_file_path, 'r') as kmz:
        kml_content = kmz.read('doc.kml')
        
        root = ET.fromstring(kml_content)
        geojson_content = kml_to_geojson(root)
        
        with open(geojson_file_path, 'w') as geojson_file:
            json.dump(geojson_content, geojson_file, indent=4)

def kml_to_geojson(element):
    geojson = {
        "type": "FeatureCollection",
        "features": []
    }

    for placemark in element.iterfind('.//{http://www.opengis.net/kml/2.2}Placemark'):
        feature = {
            "type": "Feature",
            "properties": {},
            "geometry": None
        }

        for name in placemark.iterfind('.//{http://www.opengis.net/kml/2.2}name'):
            feature["properties"]["name"] = name.text
        
        for description in placemark.iterfind('.//{http://www.opengis.net/kml/2.2}description'):
            feature["properties"]["description"] = description.text

        for point in placemark.iterfind('.//{http://www.opengis.net/kml/2.2}Point'):
            coords = point.find('{http://www.opengis.net/kml/2.2}coordinates').text.strip()
            lon, lat, _ = map(float, coords.split(','))
            # 在获取坐标时进行转换
            feature["geometry"] = mapping(Point(gcj02_to_wgs84(lon, lat)[0], gcj02_to_wgs84(lon, lat)[1]))

        
        for linestring in placemark.iterfind('.//{http://www.opengis.net/kml/2.2}LineString'):
            coords = linestring.find('{http://www.opengis.net/kml/2.2}coordinates').text.strip()
            points = [tuple(map(float, coord.split(',')))[:2] for coord in coords.split()]
            feature["geometry"] = mapping(LineString(points))
        
        for polygon in placemark.iterfind('.//{http://www.opengis.net/kml/2.2}Polygon'):
            outer_boundary = polygon.find('.//{http://www.opengis.net/kml/2.2}outerBoundaryIs')
            if outer_boundary is not None:
                coords = outer_boundary.find('.//{http://www.opengis.net/kml/2.2}coordinates').text.strip()
                points = [tuple(map(float, coord.split(',')))[:2] for coord in coords.split()]
                feature["geometry"] = mapping(Polygon([points]))
        
        if feature["geometry"] is not None:
            geojson["features"].append(feature)
    
    return geojson

if __name__ == "__main__":
    # kmz_file_path = "sys.argv[1]"
    # geojson_file_path = sys.argv[2]
    kmz_file_path = "downloaded_file.kmz"
    geojson_file_path = "downloaded_file.json"
    convert_kmz_to_geojson(kmz_file_path, geojson_file_path)

目前足迹功能还会出现比较卡的情况,但是目前还没处理头绪,幸运的卡顿只是略微影响使用,就留待下次处理把。

加入评论