GIS坐标转换工具

支持WGS84、大地2000、墨卡托投影、火星坐标(GCJ02)和百度坐标(BD09)之间的相互转换

坐标转换器

批量转换

每行一个坐标,格式:经度,纬度 或 经度 纬度

坐标系说明

  • WGS84:世界大地测量系统1984,GPS使用的标准坐标系,国际通用
  • CGCS2000(大地2000):中国国家大地坐标系2000,与WGS84非常接近,差异在厘米级
  • Web墨卡托投影:EPSG:3857,Google Maps、高德地图等使用的投影坐标系,单位为米
  • GCJ02(火星坐标):中国国家测绘局制定的加密坐标系,高德地图、腾讯地图使用
  • BD09(百度坐标):百度在GCJ02基础上再次加密的坐标系,百度地图专用

代码实现

JavaScript

// GIS坐标转换工具
const PI = Math.PI;
const X_PI = PI * 3000.0 / 180.0;
const A = 6378245.0;
const EE = 0.006693421622965943;
const EARTH_RADIUS = 6378137.0;

function outOfChina(lng, lat) {
  return lng < 72.004 || lng > 137.8347 || lat < 0.8293 || lat > 55.8271;
}

function transformLat(lng, lat) {
  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;
}

function transformLng(lng, lat) {
  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;
}

// WGS84 -> GCJ02
function wgs84ToGcj02(lng, lat) {
  if (outOfChina(lng, lat)) return { lng, lat };
  let dLat = transformLat(lng - 105.0, lat - 35.0);
  let dLng = transformLng(lng - 105.0, lat - 35.0);
  const radLat = lat / 180.0 * PI;
  let magic = Math.sin(radLat);
  magic = 1 - EE * magic * magic;
  const sqrtMagic = Math.sqrt(magic);
  dLat = (dLat * 180.0) / ((A * (1 - EE)) / (magic * sqrtMagic) * PI);
  dLng = (dLng * 180.0) / (A / sqrtMagic * Math.cos(radLat) * PI);
  return { lng: lng + dLng, lat: lat + dLat };
}

// GCJ02 -> WGS84
function gcj02ToWgs84(lng, lat) {
  if (outOfChina(lng, lat)) return { lng, lat };
  let dLat = transformLat(lng - 105.0, lat - 35.0);
  let dLng = transformLng(lng - 105.0, lat - 35.0);
  const radLat = lat / 180.0 * PI;
  let magic = Math.sin(radLat);
  magic = 1 - EE * magic * magic;
  const sqrtMagic = Math.sqrt(magic);
  dLat = (dLat * 180.0) / ((A * (1 - EE)) / (magic * sqrtMagic) * PI);
  dLng = (dLng * 180.0) / (A / sqrtMagic * Math.cos(radLat) * PI);
  return { lng: lng - dLng, lat: lat - dLat };
}

// GCJ02 <-> BD09
function gcj02ToBd09(lng, lat) {
  const z = Math.sqrt(lng * lng + lat * lat) + 0.00002 * Math.sin(lat * X_PI);
  const theta = Math.atan2(lat, lng) + 0.000003 * Math.cos(lng * X_PI);
  return { lng: z * Math.cos(theta) + 0.0065, lat: z * Math.sin(theta) + 0.006 };
}

function bd09ToGcj02(lng, lat) {
  const x = lng - 0.0065, y = lat - 0.006;
  const z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * X_PI);
  const theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * X_PI);
  return { lng: z * Math.cos(theta), lat: z * Math.sin(theta) };
}

// WGS84 <-> Web墨卡托
function wgs84ToMercator(lng, lat) {
  const x = lng * EARTH_RADIUS * PI / 180;
  const y = EARTH_RADIUS * Math.log(Math.tan((90 + lat) * PI / 360));
  return { lng: x, lat: y };
}

function mercatorToWgs84(x, y) {
  const lng = x / EARTH_RADIUS * (180 / PI);
  const lat = Math.atan(Math.exp(y / EARTH_RADIUS)) * 360 / PI - 90;
  return { lng, lat };
}

// 使用示例
const wgs84 = { lng: 116.397428, lat: 39.90923 }; // 北京天安门
console.log('WGS84:', wgs84);
console.log('GCJ02:', wgs84ToGcj02(wgs84.lng, wgs84.lat));

Python

import math

PI = math.pi
X_PI = PI * 3000.0 / 180.0
A = 6378245.0
EE = 0.006693421622965943
EARTH_RADIUS = 6378137.0

def out_of_china(lng: float, lat: float) -> bool:
    return lng < 72.004 or lng > 137.8347 or lat < 0.8293 or lat > 55.8271

def transform_lat(lng: float, lat: float) -> float:
    ret = -100.0 + 2.0 * lng + 3.0 * lat + 0.2 * lat * lat + 0.1 * lng * lat + 0.2 * math.sqrt(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

def transform_lng(lng: float, lat: float) -> float:
    ret = 300.0 + lng + 2.0 * lat + 0.1 * lng * lng + 0.1 * lng * lat + 0.1 * math.sqrt(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

def wgs84_to_gcj02(lng: float, lat: float) -> tuple[float, float]:
    """WGS84 -> GCJ02"""
    if out_of_china(lng, lat):
        return lng, lat
    d_lat = transform_lat(lng - 105.0, lat - 35.0)
    d_lng = transform_lng(lng - 105.0, lat - 35.0)
    rad_lat = lat / 180.0 * PI
    magic = math.sin(rad_lat)
    magic = 1 - EE * magic * magic
    sqrt_magic = math.sqrt(magic)
    d_lat = (d_lat * 180.0) / ((A * (1 - EE)) / (magic * sqrt_magic) * PI)
    d_lng = (d_lng * 180.0) / (A / sqrt_magic * math.cos(rad_lat) * PI)
    return lng + d_lng, lat + d_lat

def gcj02_to_wgs84(lng: float, lat: float) -> tuple[float, float]:
    """GCJ02 -> WGS84"""
    if out_of_china(lng, lat):
        return lng, lat
    d_lat = transform_lat(lng - 105.0, lat - 35.0)
    d_lng = transform_lng(lng - 105.0, lat - 35.0)
    rad_lat = lat / 180.0 * PI
    magic = math.sin(rad_lat)
    magic = 1 - EE * magic * magic
    sqrt_magic = math.sqrt(magic)
    d_lat = (d_lat * 180.0) / ((A * (1 - EE)) / (magic * sqrt_magic) * PI)
    d_lng = (d_lng * 180.0) / (A / sqrt_magic * math.cos(rad_lat) * PI)
    return lng - d_lng, lat - d_lat

def gcj02_to_bd09(lng: float, lat: float) -> tuple[float, float]:
    """GCJ02 -> BD09"""
    z = math.sqrt(lng * lng + lat * lat) + 0.00002 * math.sin(lat * X_PI)
    theta = math.atan2(lat, lng) + 0.000003 * math.cos(lng * X_PI)
    return z * math.cos(theta) + 0.0065, z * math.sin(theta) + 0.006

def bd09_to_gcj02(lng: float, lat: float) -> tuple[float, float]:
    """BD09 -> GCJ02"""
    x, y = lng - 0.0065, lat - 0.006
    z = math.sqrt(x * x + y * y) - 0.00002 * math.sin(y * X_PI)
    theta = math.atan2(y, x) - 0.000003 * math.cos(x * X_PI)
    return z * math.cos(theta), z * math.sin(theta)

def wgs84_to_mercator(lng: float, lat: float) -> tuple[float, float]:
    """WGS84 -> Web墨卡托"""
    x = lng * EARTH_RADIUS * PI / 180
    y = EARTH_RADIUS * math.log(math.tan((90 + lat) * PI / 360))
    return x, y

def mercator_to_wgs84(x: float, y: float) -> tuple[float, float]:
    """Web墨卡托 -> WGS84"""
    lng = x / EARTH_RADIUS * (180 / PI)
    lat = math.atan(math.exp(y / EARTH_RADIUS)) * 360 / PI - 90
    return lng, lat

# 使用示例
if __name__ == "__main__":
    wgs84 = (116.397428, 39.90923)  # 北京天安门
    print(f"WGS84: {wgs84}")
    print(f"GCJ02: {wgs84_to_gcj02(*wgs84)}")