This article refer to
- Several common online map (Heaven and Earth map, Baidu map, Gaode map) coordinate system conversion algorithm
Introduction to coordinate system
Common coordinate system
WGS84 coordinate system (standard GPS coordinates)
GPS, WGS-84, original coordinate system. Generally, the coordinates recorded by the international standard GPS recorder are the coordinates of GPS.
What is EPSG:4326’s relationship to WGS84? Internationally, each coordinate system is assigned an EPSG code, and EPSG:4326 is the code for WGS84
WGS84 Web Mercator (Flat map)
EPSG:3857 (WGS 84 / pseudo-Mercator) is the official code name for the Web Mercator
GCJ02 Latitude and Longitude Projection (Mars coordinate system)
Gcj-02 is the coordinate system of geographic information system (GIS) formulated by China State Bureau of Surveying and Mapping (G stands for Guojia Country, C stands for Cehui Surveying and mapping, J stands for Ju Bureau).
Gcj-02 is the coordinate system released by the National Survey bureau in 2002. Also known as “Mars coordinates”
The GCJ02 longitude and latitude projection, which is based on the WGS84 longitude and latitude projection, is gCJ-02 plus offset. The addition bias processing is a special algorithm to encrypt the real coordinates into false coordinates, and this addition bias is not linear addition bias, so the offset situation will be different from place to place.
The coordinate values in this coordinate system are in the format of latitude and longitude in degrees.
GCJ02 Web Mercator Projection
The coordinate values are in Web Mercator format in meters.
For reasons of domestic policy, domestic maps will have encryption requirements, generally there are two situations:
- One is the Web Mercator based on the national standard encryption of the national 02 coordinate system, known as the “Mars coordinate system”;
- The other is further encryption under the 02 coordinate system of national standard, such as the BD09 coordinate system of Baidu map
BD09 longitude and latitude projection
BD09 longitude and latitude projection belongs to baidu coordinate system,
It is on the basis of the standard latitude and longitude of gCJ-02 after the addition of the offset, plus Baidu’s own algorithm, that is, on the basis of the standard latitude and longitude of the two add offset.
The coordinate values in this coordinate system are in the format of latitude and longitude in degrees.
Common coordinate system, right?
-
EPSG:4326, equivalent to THE WGS84 coordinate system
-
CGCS2000, the world map coordinate system, is the same as GPS, the deviation is smaller
-
GCJ02, Mars coordinate system, offset GPS coordinates
-
EPSG:3857, equivalent to 900913, projected by the Mercator (originally the Web Mercator was refused to assign EPSG code. The unofficial code EPSG:900913 (Google’s digital variant) is widely used to represent it.)
-
Bd-09:, Baidu map uses coordinate system
Map migration
Why map offset?
In a word: national security. Can imagine, if the map is not encrypted, in Baidu to find the key (military, scientific research institute, etc.) targets, missile air strikes, the loss is incalculable
Where is the offset?
The equipment is generally standard GPS information – and it is difficult to ask foreign manufacturers to make GPS devices to Chinese requirements
The Chinese government encrypts maps, not GPS signals
The map is offset, why the map positioning is very accurate (Baidu map explanation)
-
Baidu map supplier is Mind Map New. Before the map is provided to Baidu, it needs to be submitted to the Bureau of Surveying and Mapping. The bureau of Surveying and Mapping does offset encryption on the map, and the coordinate system of the encrypted map becomes GCJ-02(Mars coordinate system).
-
When Baidu received mind Map’s new maps, it found that GPS locations were far away from the map
-
Baidu map software to the bureau of surveying and mapping, ask them to join a “secret plug-in”, do the same GPS offset GPS offset
Summary: Map offset and GPS offset ensure that map and GPS overlap
Coordinate transformation
Why do coordinate transformations
In the work, the project developed uses OpenLayers or Leaflet open source map engine, and the business logic of using the map has been written. In order to improve the reusability of business components, the best way to switch map layer is to change the map layer, and the API about business should not be changed. So all that’s left to do is transform the frame;
For example, if you want to use Baidu map and Baidu map engine (MAP.js), then the code of business logic should be implemented using Baidu API, which is equivalent to re-development, reducing efficiency and not conducive to code reuse
By making the map available with WMTS, you can change the layer and not the API
Map services (capabilities provided by the background map engine)
TMS — Tile Map Service
Cut the map into multiple level image pyramids (quadtree segmentation),It usually requires a REST-compliant URI structure to identify each slice.
- TMS is purely RESTful;
- TMS tiles are square
WMTS — Web Map Tile Service
It is a service for pre-rendering or real-time map tiling on the Internet.
For example, type in a browserMaponline0.bdimg.com/tile/?qt=vt…, you can view the picture
WMTS can have three protocols: KVP, SOAP, RESTful;
WMTS tiles are rectangular;
Tiles of different scales corresponding in WMTS can vary in size
In addition, WMTS can obtain the following information
- Map tiles can be obtained;
- WMTS service metadata can be obtained.
- Get FeatureInfo about single tiles;
- Map legends can be obtained;
WMS — Web Map Service
When the client requests the WMS service, the client is returned with a complete picture, it can not let the client know the tile layout, the client gets the direct display, so WMS is only focused on flexibility.
However, the probability of reuse is very low, and as concurrency increases, server performance deteriorates.
There are different request types:
- GetCapabilities: Returns metadata and accessible layers for the WMS service
- GetMap: Returns a map picture
- GetFeatureInfo: If the layer is marked “queryable”, the map image data can be requested through the coordinates
- DescribeLayer: Returns the DescribeLayer element type for the specified layer
- GetLegendGraphic: Returns a map legendgraphic
Common coordinate system conversion tools (Baidu Map, GCJ, WGS84)
const mapTools = (function () {
const x_pi = (3.14159265358979324 * 3000.0) / 180.0;
var pi = 3.14159265358979324;
var a = 6378245.0;
var ee = 0.00669342162296594323;
var LLBAND = [75.60.45.30.15.0];
var LL2MC = [
[
-0.0015702102444.111320.7020616939.1704480524535203, -10338987376042340.26112667856603880, -35149669176653700.26595700718403920,
-10725012454188240.1800819912950474.82.5,], [0.0008277824516172526.111320.7020463578.647795574.6671607,
-4082003173.641316.10774905663.51142, -15171875531.51559.12053065338.62167, -5124939663.577472.913311935.9512032.67.5,], [0.00337398766765.111320.7020202162.4481351.045890365,
-23393751.19931662.79682215.47186455, -115964993.2797253.97236711.15602145, -43661946.33752821.8477230.501135234.52.5,], [0.00220636496208.111320.7020209128.51751.86112841131.3796837.749470245.992013.7397791013, -1221952.21711287.1340652.697009075,
-620943.6990984312.144416.9293806241.37.5,], [...0.0003441963504368392.111320.7020576856.278.2353980772752.2485758.690035394.6070.750963243378.54821.18345352118.9540.606633304236, -2710.55326746645.1405.483844121726.22.5,], [...0.0003218135878613132.111320.7020701615.0.00369383431289.823725.6402795718.0.46104986909093.2351.343141331292.1.58060784298199.8.77738589078284.0.37238884252424.7.45,]];var MCBAND = [12890594.86.8362377.87.5591021.3481989.83.1678043.12.0];
var MC2LL = [
[
1.410526172116255 e-8.0.00000898305509648872, -1.9939833816331.200.9824383106796, -187.2403703815547.91.6087516669843,
-23.38765649603339.2.57121317296198, -0.03801003308653.17337981.2,], [...7.435856389565537 e-9.0.000008983055097726239, -0.78625201886289.96.32687599759846, -1.85204757529826, -59.36935905485877.47.40033549296737, -16.50741931063887.2.28786674699375.10260144.86,], [...3.030883460898826 e-8.0.00000898305509983578.0.30071316287616.59.74293618442277.7.357984074871, -25.38371002664745.13.45380521110908,
-3.29883767235584.0.32710905363475.6856817.37,], [...1.981981304930552 e-8.0.000008983055099779535.0.03278182852591.40.31678527705744.0.65659298677277, -4.44255534477492.0.85341911805263.0.12923347998204, -0.04625736007561.4482777.06,], [3.09191371068437 e-9.0.000008983055096812155.0.00006995724062.23.10934304144901, -0.00023663490511, -0.6321817810242, -0.00663494467273.0.03430082397953, -0.00466043876332.2555164.4,], [2.890871144776878 e-9.0.000008983055095805407, -3.068298 e-8.7.47137025468032, -0.00000353937994, -0.02145144861037, -0.00001234426596.0.00010322952773, -0.00000323890364.826088.5,]];function getRange(cC, cB, T) {
if(cB ! =null) {
cC = Math.max(cC, cB);
}
if(T ! =null) {
cC = Math.min(cC, T);
}
return cC;
}
function getLoop(cC, cB, T) {
while (cC > T) {
cC -= T - cB;
}
while (cC < cB) {
cC += T - cB;
}
return cC;
}
function convertor(cC, cD) {
if(! cC || ! cD) {return null;
}
let T = cD[0] + cD[1] * Math.abs(cC.x);
const cB = Math.abs(cC.y) / cD[9];
let cE =
cD[2] +
cD[3] * cB +
cD[4] * cB * cB +
cD[5] * cB * cB * cB +
cD[6] * cB * cB * cB * cB +
cD[7] * cB * cB * cB * cB * cB +
cD[8] * cB * cB * cB * cB * cB * cB;
T *= cC.x < 0 ? -1 : 1;
cE *= cC.y < 0 ? -1 : 1;
return [T, cE];
}
/** ** ** ** ** *@param {*} cB
* @returns* /
function convertBdMC2LL(lnglat) {
const cB = {
x: lnglat.lng,
y: lnglat.lat,
};
const cC = {
x: Math.abs(cB.x),
y: Math.abs(cB.y),
};
let cE;
for (let cD = 0, len = MCBAND.length; cD < len; cD++) {
if (cC.y >= MCBAND[cD]) {
cE = MC2LL[cD];
break; }}const T = convertor(cB, cE);
return T;
}
/** ** ** ** ** ** **@param {*} T
* @returns* /
function convertBdLL2MC(lnglat) {
const T = {
x: lnglat.lng,
y: lnglat.lat,
};
let cD, cC, len;
T.x = getLoop(T.x, -180.180);
T.y = getRange(T.y, -74.74);
const cB = T;
for (cC = 0, len = LLBAND.length; cC < len; cC++) {
if (cB.y >= LLBAND[cC]) {
cD = LL2MC[cC];
break; }}if(! cD) {for (cC = LLBAND.length - 1; cC >= 0; cC--) {
if (cB.y <= -LLBAND[cC]) {
cD = LL2MC[cC];
break; }}}const cE = convertor(T, cD);
// return cE;
return {
lng: cE[0].lat: cE[1]}; }/* If you are not in China */
function outOfChina(lon, lat) {
if ((lon < 72.004 || lon > 137.8347) && (lat < 0.8293 || lat > 55.8271)) {
return true;
} else {
return false; }}function transformLat(x, y) {
var 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) {
var 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;
}
function delta(lat, lon) {
let a = 6378245.0; // a: the projection factor of the ellipsoid coordinates of the satellite into the plane map coordinate system.
let ee = 0.00669342162296594323; // ee: eccentricity of ellipsoid.
let dLat = transformLat(lon - 105.0, lat - 35.0);
let dLon = transformLon(lon - 105.0, lat - 35.0);
let radLat = (lat / 180.0) * pi;
let magic = Math.sin(radLat);
magic = 1 - ee * magic * magic;
let sqrtMagic = Math.sqrt(magic);
dLat = (dLat * 180.0) / (((a * (1 - ee)) / (magic * sqrtMagic)) * pi);
dLon = (dLon * 180.0) / ((a / sqrtMagic) * Math.cos(radLat) * pi);
return {
lat: dLat,
lon: dLon,
};
}
// Earth coordinate system (WGS-84) to Mars coordinate system (GCJ) :
function transformWGS2GCJ(lnglat) {
const wgLat = lnglat.lat;
const wgLon = lnglat.lng;
var mars_point = {};
if (outOfChina(wgLon, wgLat)) {
mars_point.lat = wgLat;
mars_point.lon = wgLon;
return;
}
var dLat = transformLat(wgLon - 105.0, wgLat - 35.0);
var dLon = transformLon(wgLon - 105.0, wgLat - 35.0);
var radLat = (wgLat / 180.0) * pi;
var magic = Math.sin(radLat);
magic = 1 - ee * magic * magic;
var sqrtMagic = Math.sqrt(magic);
dLat = (dLat * 180.0) / (((a * (1 - ee)) / (magic * sqrtMagic)) * pi);
dLon = (dLon * 180.0) / ((a / sqrtMagic) * Math.cos(radLat) * pi);
mars_point.lat = wgLat + dLat;
mars_point.lng = wgLon + dLon;
return mars_point;
}
// Mars coordinate GCJ02 to Earth coordinate WGS84:
function transformGCJ2WGS(lnglat) {
const gcjLat = lnglat.lat;
const gcjLon = lnglat.lng;
let d = delta(gcjLat, gcjLon);
return {
lat: gcjLat - d.lat,
lng: gcjLon - d.lon,
};
}
/** *@param {*} baidu_point
* @returns* /
function baiduTomars(baidu_point) {
var mars_point = { lng: 0.lat: 0 };
var x = baidu_point.lng - 0.0065;
var y = baidu_point.lat - 0.006;
var z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * x_pi);
var theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * x_pi);
mars_point.lng = z * Math.cos(theta);
mars_point.lat = z * Math.sin(theta);
return mars_point;
}
/** *@param {*} mars_point
* @returns* /
function marsTobaidu(mars_point) {
var baidu_point = { lng: 0.lat: 0 };
var x = mars_point.lng;
var y = mars_point.lat;
var z = Math.sqrt(x * x + y * y) + 0.00002 * Math.sin(y * x_pi);
var theta = Math.atan2(y, x) + 0.000003 * Math.cos(x * x_pi);
baidu_point.lng = z * Math.cos(theta) + 0.0065;
baidu_point.lat = z * Math.sin(theta) + 0.006;
return baidu_point;
}
// Test the transformation between Baidu and Mars coordinates
function testBaiduMars() {
const lng = 116.01965;
const lat = 40.043425;
const lnglat = {
lng,
lat,
};
let result;
result = mapTools.baiduTomars(lnglat);
console.log(result); // {LNG: 116.01318467451765, LAT: 40.037373017166615}
result = mapTools.marsTobaidu(result);
console.log(result);
}
// Test the Mars coordinate system and WGS interconversion
function testWGSMars() {
const lng = 116.01965;
const lat = 40.043425;
const lnglat = {
lng,
lat,
};
let result;
result = mapTools.transformWGS2GCJ(lnglat); // Earth coordinate system (WGS-84) to Mars coordinate system (GCJ) :
console.log(result); // {LNG: 116.01318467451765, LAT: 40.037373017166615}
result = mapTools.transformGCJ2WGS(result);
console.log(result);
}
// Test the conversion between baidu latitude and Longitude and Mercator coordinates
function testBdMC2LL() {
const lng = 116.01965;
const lat = 40.043425;
const lnglat = {
lng,
lat,
};
let result;
result = mapTools.convertBdLL2MC(lnglat); // Earth coordinate system (WGS-84) to Mars coordinate system (GCJ) :
console.log(result); // {LNG: 116.01318467451765, LAT: 40.037373017166615}
result = mapTools.convertBdMC2LL(result);
console.log(result);
}
return {
test: {
testBaiduMars, // Baidu latitude and longitude coordinates and GCJ coordinate system conversion
testWGSMars, // Convert the WGS84 and GCJ coordinates
testBdMC2LL // Test the conversion between baidu latitude and Longitude and Mercator coordinates
},
convertBdMC2LL, // Convert the Mercator coordinates to the longitude and latitude coordinates:
convertBdLL2MC, // Select * from BD09;
transformWGS2GCJ, // Earth coordinate system (WGS-84) to Mars coordinate system (GCJ) :
transformGCJ2WGS, // Mars coordinate GCJ02 to Earth coordinate WGS84:
baiduTomars, // Transfer from Baidu to Mars:
marsTobaidu, // Convert Mars coordinates to Baidu coordinates:}; }) ();// mapTools.test.testBaiduMars()
// mapTools.test.testWGSMars()
// mapTools.test.testBdMC2LL()
export default mapTools
Copy the code