OpenLayers 高德地图偏移问题

本文详细阐述了OpenLayers在处理高德地图时遇到的偏移问题,解释了坐标系差异导致的偏移原理,并提供了使用GCJ-02坐标系进行转换的JavaScript代码示例,包括注册坐标系和定义坐标转换方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

OpenLayers 高德地图偏移问题

1. 偏移产生的原因

地球上同一个地理位置的经纬度,在不同的坐标系中,会有少许偏移,国内目前常见的坐标系主要分为三种:

  1. 地球坐标系——WGS84:常见于 GPS 设备,Google 地图等国际标准的坐标体系。
  2. 火星坐标系——GCJ-02:中国国内使用的被强制加密后的坐标体系,高德坐标就属于该种坐标体系。
  3. 百度坐标系——BD-09:百度地图所使用的坐标体系,是在火星坐标系的基础上又进行了一次加密处理。

2. 解决思路

web地图坐标系常用的一般是:WGS 84(EPSG:4326)WGS 84 / Pseudo-Mercator(EPSG:3857)China Geodetic Coordinate System 2000(EPSG:4490) 等。
既然高德地图偏移产生的原因是由于坐标系不一致,那么将坐标系统一一下即可解决。

3. 代码

OpenLayers地图坐标以EPSG:3857为例:

  1. 注册坐标系,定义坐标转换方法,gcj02Mecator.js
// 导入proj控件,使用其方法注入gcj02坐标系
import {
  Projection,
  addProjection,
  addCoordinateTransforms,
  getTransform,
} from 'ol/proj'

const PI = Math.PI
const AXIS = 6378245.0
const OFFSET = 0.00669342162296594323 // (a^2 - b^2) / a^2

function delta(wgLon, wgLat){
  let dLat = transformLat(wgLon - 105.0, wgLat - 35.0)
  let dLon = transformLon(wgLon - 105.0, wgLat - 35.0)
  const radLat = (wgLat / 180.0) * PI
  let magic = Math.sin(radLat)
  magic = 1 - OFFSET * magic * magic
  const sqrtMagic = Math.sqrt(magic)
  dLat = (dLat * 180.0) / (((AXIS * (1 - OFFSET)) / (magic * sqrtMagic)) * PI)
  dLon = (dLon * 180.0) / ((AXIS / sqrtMagic) * Math.cos(radLat) * PI)
  return [dLon, dLat]
}

function outOfChina(lon, lat){
  if (lon < 72.004 || lon > 137.8347) {
    return true
  }
  if (lat < 0.8293 || lat > 55.8271) {
    return true
  }
  return false
}

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 * 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
}

function transformLon(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 * 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
}

const forEachPoint = function(func){
  return function(input, opt_output, opt_dimension){
    const len = input.length
    const dimension = opt_dimension || 2
    let output
    if (opt_output) {
      output = opt_output
    } else if (dimension !== 2) {
      output = input.slice()
    } else {
      output = new Array(len)
    }
    for (let offset = 0; offset < len; offset += dimension) {
      func(input, output, offset)
    }
    return output
  }
}

const gcj02 = {
  toWGS84: forEachPoint(function(input, output, offset){
    let lng = input[offset]
    let lat = input[offset + 1]
    if (!outOfChina(lng, lat)) {
      const deltaD = delta(lng, lat)
      lng = lng - deltaD[0]
      lat = lat - deltaD[1]
    }
    output[offset] = lng
    output[offset + 1] = lat
  }),
  fromWGS84: forEachPoint(function(input, output, offset){
    let lng = input[offset]
    let lat = input[offset + 1]
    if (!outOfChina(lng, lat)) {
      const deltaD = delta(lng, lat)
      lng = lng + deltaD[0]
      lat = lat + deltaD[1]
    }
    output[offset] = lng
    output[offset + 1] = lat
  }),
}

const projzh = {
  ll2smerc: getTransform('EPSG:4326', 'EPSG:3857'),
  smerc2ll: getTransform('EPSG:3857', 'EPSG:4326'),
}

// wgs84 -> gcj02
projzh.ll2gmerc = function(input, opt_output, opt_dimension){
  const output = gcj02.fromWGS84(input, opt_output, opt_dimension)
  return projzh.ll2smerc(output, output, opt_dimension)
}
// gcj02 -> wgs84
projzh.gmerc2ll = function(input, opt_output, opt_dimension){
  const output = projzh.smerc2ll(input, input, opt_dimension)
  return gcj02.toWGS84(output, opt_output, opt_dimension)
}
// 3857 -> gcj02
projzh.smerc2gmerc = function(input, opt_output, opt_dimension){
  let output = projzh.smerc2ll(input, input, opt_dimension)
  output = gcj02.fromWGS84(output, output, opt_dimension)
  return projzh.ll2smerc(output, output, opt_dimension)
}
// gcj02 -> 3857
projzh.gmerc2smerc = function(input, opt_output, opt_dimension){
  let output = projzh.smerc2ll(input, input, opt_dimension)
  output = gcj02.toWGS84(output, output, opt_dimension)
  return projzh.ll2smerc(output, output, opt_dimension)
}

// 定义GCJ02墨卡托投影坐标系
const gcj02Mecator = new Projection({
  code: 'GCJ-02',
  // extent不能缺少,OpenLayers frame 渲染的时候需要
  extent: [-20037508.342789244, -20037508.342789244, 20037508.342789244, 20037508.342789244],
  units: 'm',
})

// 将GCJ02墨卡托投影坐标系注册进OpenLayers
addProjection(gcj02Mecator)

// 覆盖默认的转换方法
addCoordinateTransforms('EPSG:4326', gcj02Mecator, projzh.ll2gmerc, projzh.gmerc2ll)
addCoordinateTransforms('EPSG:3857', gcj02Mecator, projzh.smerc2gmerc, projzh.gmerc2smerc)

export default gcj02Mecator
  1. 创建Layer
import { Map, View } from 'ol'
import { Tile as TileLayer } from 'ol/layer'
import { XYZ as XYZSource } from 'ol/source'
import gcj02Mecator from './gcj02Mecator'

const amapLayer = new TileLayer({
  source: new XYZSource({
    projection: gcj02Mecator,
    url: 'http://webst0{1-4}.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}&key=你的key',
  }),
})

const map = new Map({
  target: 'mapContainerId',
  view: new View({
    center: [0, 0],
    zoom: 1,
  }), // projection 默认是 'EPSG:3857
  layers: [amapLayer],
})
请问在openlayers中如何删除Layer中的marker 3 在层中加了一个标记怎么永久的存到地图上 3 OpenLayers 如何设死地图边界 3 谁写过openlayers解析xml 3 openlayer.popup 3 OpenLayers 怎么做字段标注 4 字段标注不是用OL做的,而是你在SLD中定义用数据表的哪个字段做标签就可以了 4 OpenLayers 中画最短轨迹 4 使用TileCache配合OpenLayers 4 openlayers如何准确测距 9 openlayers简单实用例子 9 关于TileCache函数 13 openlayers 有函数说明的类图 14 go 14 Maker 一多,客户端就死掉 15 OpenLayers 怎么实现 AJAX 16 openlayer 要怎么研究才会应用得比较自如 16 改良OpenLayers的Popup 16 在openlayers的地图中要添加地图搜索功能一般采用什么方法 17 openlayers中如何访问geoserver发布的图片 18 什么是TMS? 18 怎样设置瓦片的大小 18 画一条线,数据量太大,怎么办? 18 怎么在图层上动态画点 20 GeoExt集成google map ,地图偏移 21 用JavaScriptOpenLayers.Marker的加上单击显示详细信息的功能 21 改良OpenLayers的MousePostion 25 使用OpenLayers实现一个在鼠标点击处添加标记的效果 27 openlayers 利用google maps的卫星地图 29 openLayers集成google map ,点标注错位 30 openlayers可以做出谷歌效果的地图吗? 31 用OpenLayers API和类来画点、线、面 32 OGC标准术语介绍WMS、WFS、WCS 35 如何控制地图的放缩 37 openLayers链接WMS的代码探讨 37 openlayers 2.5 矢量层在ie下闪烁的问题 38 openLayers 在地图上添加一个点并保存 39 openLayers 各个参数的意义 42 geoserver能搞出这种风格的图来吗? 43 关于SLD的线切割后的设置 43 GEOSERVE 标注铁路,使用 SLD 44 geoserver 发布.shp 中文乱码问题 怎么解决啊 45 Geoserver怎么连postGreSql 数据库 48 Geoserver连Oracle Spatial 52 GeoServer架构浅谈 53 Geoserver发布地图无法显示问题 57 WebGIS相关的OpenGIS规范 58 geoserver中地图以外的区域以自定义图片填充 62 怎样修改 geoServer 的用户名密码 65 GeoServer中的WMS服务生成的png图片背景色为透明 65 比例尺 65 需不需要一个layer对应一个store 66 如何部署shp 66 用GeoWebCache后Marker错位 66 标签太大导致不能显示? 67 geoserver把两个shapefile格式的图叠 67 GeoServer 能够集成 Google Map? 68 gwc地图切片的缓存路径在web.xml中设置么 68 如何实现地图查询? 68 sld文件怎么用 69 在sld中怎么控制路名的间隔? 69
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

漠诽

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值