第14章 - 投影与坐标转换

第14章 - 投影与坐标转换

14.1 投影基础

14.1.1 常用投影

import { get as getProjection, transform, fromLonLat, toLonLat } from 'ol/proj';

// 内置投影
const wgs84 = getProjection('EPSG:4326');      // WGS84 经纬度
const webMercator = getProjection('EPSG:3857'); // Web Mercator

// 投影信息
console.log('代码:', webMercator.getCode());
console.log('单位:', webMercator.getUnits());        // 'm'
console.log('范围:', webMercator.getExtent());
console.log('是否全球:', webMercator.isGlobal());

14.1.2 坐标转换

// 经纬度 → Web Mercator
const mercatorCoord = fromLonLat([116.4074, 39.9042]);

// Web Mercator → 经纬度
const lonLatCoord = toLonLat([12958752.49, 4853167.35]);

// 通用转换
const coord = transform(
  [116.4074, 39.9042],
  'EPSG:4326',
  'EPSG:3857'
);

// 范围转换
import { transformExtent } from 'ol/proj';
const extent = transformExtent(
  [115, 39, 118, 41],
  'EPSG:4326',
  'EPSG:3857'
);

// 几何转换
geometry.transform('EPSG:4326', 'EPSG:3857');

14.2 自定义投影

14.2.1 使用 Proj4

import { register } from 'ol/proj/proj4';
import proj4 from 'proj4';

// 定义 CGCS2000
proj4.defs('EPSG:4490', '+proj=longlat +ellps=GRS80 +no_defs');

// 定义高斯投影
proj4.defs('EPSG:4527', 
  '+proj=tmerc +lat_0=0 +lon_0=117 +k=1 +x_0=500000 +y_0=0 +ellps=GRS80 +units=m +no_defs'
);

// 注册到 OpenLayers
register(proj4);

// 使用
const cgcs2000 = getProjection('EPSG:4490');

14.2.2 中国常用投影

// CGCS2000 经纬度
proj4.defs('EPSG:4490', '+proj=longlat +ellps=GRS80 +no_defs');

// CGCS2000 高斯投影(3度带)
// 中央经线 117°(北京区域)
proj4.defs('EPSG:4527', 
  '+proj=tmerc +lat_0=0 +lon_0=117 +k=1 +x_0=500000 +y_0=0 +ellps=GRS80 +units=m +no_defs'
);

// 中央经线 120°(上海区域)
proj4.defs('EPSG:4528',
  '+proj=tmerc +lat_0=0 +lon_0=120 +k=1 +x_0=500000 +y_0=0 +ellps=GRS80 +units=m +no_defs'
);

// 北京54
proj4.defs('EPSG:4214', 
  '+proj=longlat +ellps=krass +towgs84=15.8,-154.4,-82.3,0,0,0,0 +no_defs'
);

// 西安80
proj4.defs('EPSG:4610', '+proj=longlat +ellps=IAU76 +no_defs');

register(proj4);

14.3 坐标偏移处理

14.3.1 火星坐标(GCJ-02)

// WGS84 → GCJ-02
function wgs84ToGcj02(lng, lat) {
  const a = 6378245.0;
  const ee = 0.00669342162296594323;
  
  let dLat = transformLat(lng - 105.0, lat - 35.0);
  let dLng = transformLng(lng - 105.0, lat - 35.0);
  
  const radLat = lat / 180.0 * Math.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) * Math.PI);
  dLng = (dLng * 180.0) / (a / sqrtMagic * Math.cos(radLat) * Math.PI);
  
  return [lng + dLng, lat + dLat];
}

// GCJ-02 → WGS84
function gcj02ToWgs84(lng, lat) {
  const [mLng, mLat] = wgs84ToGcj02(lng, lat);
  return [lng * 2 - mLng, lat * 2 - mLat];
}

function transformLat(x, y) {
  let 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 * Math.PI) + 20.0 * Math.sin(2.0 * x * Math.PI)) * 2.0 / 3.0;
  ret += (20.0 * Math.sin(y * Math.PI) + 40.0 * Math.sin(y / 3.0 * Math.PI)) * 2.0 / 3.0;
  ret += (160.0 * Math.sin(y / 12.0 * Math.PI) + 320 * Math.sin(y * Math.PI / 30.0)) * 2.0 / 3.0;
  return ret;
}

function transformLng(x, y) {
  let 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 * Math.PI) + 20.0 * Math.sin(2.0 * x * Math.PI)) * 2.0 / 3.0;
  ret += (20.0 * Math.sin(x * Math.PI) + 40.0 * Math.sin(x / 3.0 * Math.PI)) * 2.0 / 3.0;
  ret += (150.0 * Math.sin(x / 12.0 * Math.PI) + 300.0 * Math.sin(x / 30.0 * Math.PI)) * 2.0 / 3.0;
  return ret;
}

14.3.2 百度坐标(BD-09)

// GCJ-02 → BD-09
function gcj02ToBd09(lng, lat) {
  const x = lng;
  const y = lat;
  const z = Math.sqrt(x * x + y * y) + 0.00002 * Math.sin(y * Math.PI * 3000.0 / 180.0);
  const theta = Math.atan2(y, x) + 0.000003 * Math.cos(x * Math.PI * 3000.0 / 180.0);
  
  return [z * Math.cos(theta) + 0.0065, z * Math.sin(theta) + 0.006];
}

// BD-09 → GCJ-02
function bd09ToGcj02(lng, lat) {
  const x = lng - 0.0065;
  const y = lat - 0.006;
  const z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * Math.PI * 3000.0 / 180.0);
  const theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * Math.PI * 3000.0 / 180.0);
  
  return [z * Math.cos(theta), z * Math.sin(theta)];
}

// WGS84 → BD-09
function wgs84ToBd09(lng, lat) {
  const gcj = wgs84ToGcj02(lng, lat);
  return gcj02ToBd09(gcj[0], gcj[1]);
}

// BD-09 → WGS84
function bd09ToWgs84(lng, lat) {
  const gcj = bd09ToGcj02(lng, lat);
  return gcj02ToWgs84(gcj[0], gcj[1]);
}

14.4 投影工具类

class CoordinateTransformer {
  static wgs84ToMercator(lon, lat) {
    return fromLonLat([lon, lat]);
  }
  
  static mercatorToWgs84(x, y) {
    return toLonLat([x, y]);
  }
  
  static wgs84ToGcj02(lon, lat) {
    return wgs84ToGcj02(lon, lat);
  }
  
  static gcj02ToWgs84(lon, lat) {
    return gcj02ToWgs84(lon, lat);
  }
  
  static wgs84ToBd09(lon, lat) {
    return wgs84ToBd09(lon, lat);
  }
  
  static bd09ToWgs84(lon, lat) {
    return bd09ToWgs84(lon, lat);
  }
  
  static gcj02ToMercator(lon, lat) {
    const wgs84 = this.gcj02ToWgs84(lon, lat);
    return fromLonLat(wgs84);
  }
  
  static bd09ToMercator(lon, lat) {
    const wgs84 = this.bd09ToWgs84(lon, lat);
    return fromLonLat(wgs84);
  }
  
  static transformFeatures(features, fromProj, toProj) {
    return features.map(feature => {
      const cloned = feature.clone();
      cloned.getGeometry().transform(fromProj, toProj);
      return cloned;
    });
  }
}

// 使用示例
const mercator = CoordinateTransformer.gcj02ToMercator(116.4074, 39.9042);

14.5 本章小结

本章介绍了投影与坐标转换:

  1. 投影基础:WGS84、Web Mercator
  2. 坐标转换:fromLonLat、toLonLat、transform
  3. 自定义投影:Proj4 注册
  4. 坐标偏移:GCJ-02、BD-09 转换
  5. 工具类:统一转换接口

关键要点

  • OpenLayers 默认使用 EPSG:3857
  • 使用 Proj4 注册自定义投影
  • 处理国内地图坐标偏移问题

← 上一章:地图事件与动画 | 返回目录 | 下一章:要素编辑与绘制 →

posted @ 2026-01-08 11:37  我才是银古  阅读(10)  评论(0)    收藏  举报