By: Igor Ma.
16 FEB 2016 3689
About a year ago we were dealing with a project on data mapping in Chinese maps. In the process of development, we came across the pitfalls described in this article.
Initially, we used Google Maps. But later we discovered that if users were located in China, Google Maps could not be displayed because Google Services are disabled in China for reasons of national security. As a result, it was decided to use two different maps; one for Chinese users (Amap - AutoNavi Map) and another for users from other countries (Google Maps).
Encryption of geographical position in China
After implementing and testing the two maps, another interesting feature was identified – the coordinates that correspond to specific places in China point to completely different places. The solution was not obvious. In China no international system of coordinates is used.
The international coordinate system is WGS 84, but in China the GCJ-02 system is used. The main difference is that GCJ-02 adds random padding on both latitude and longitude.
Due to national security requirements, the use of geographic information in China is limited. In order to access it, one must receive special permission from the Surveying and Mapping Administrative Department under the State Council.
Despite the secrecy of GCJ-02 encryption, there are several open source projects, which convert the coordinates of GCJ-02 to the WGS-84 system. We used one of these in our application.
Difference between Apple Maps and Google Maps
It is worth paying attention to Apple Maps work with locations. During research it was found that, as opposed to Google Maps, Apple takes into account the features of the Chinese maps and displays converted coordinates.
If one of your potential software users is in China, it is worth using Amap instead of Google Maps and implementing coordinate decryption (sources of implementation and the description are given at the end of the article) to achieve maximum accuracy.
Google Maps
Apple Maps
Java source code has been taken from GitHub resource:
class WGStoGCJConverter {
final static double pi = 3.14159265358979324;
//
// Krasovsky 1940
//
// a = 6378245.0, 1/f = 298.3
// b = a * (1 - f)
// ee = (a^2 - b^2) / a^2;
final static double a = 6378245.0;
final static double ee = 0.00669342162296594323;
// World Geodetic System ==> Mars Geodetic System
public static Coordinates transform(double wgLat, double wgLon) {
if (outOfChina(wgLat, wgLon)) {
return new Coordinates(wgLat, wgLon);
}
double dLat = transformLat(wgLon - 105.0, wgLat - 35.0);
double dLon = transformLon(wgLon - 105.0, wgLat - 35.0);
double radLat = wgLat / 180.0 * pi;
double magic = Math.sin(radLat);
magic = 1 - ee * magic * magic;
double 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 new Coordinates(wgLat + dLat, wgLon + dLon);
}
public static boolean outOfChina(double lat, double lon) {
if (lon < 72.004 || lon > 137.8347)
return true;
if (lat < 0.8293 || lat > 55.8271)
return true;
return false;
}
public static double transformLat(double x, double y) {
double 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;
}
public static double transformLon(double x, double y) {
double 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;
}
public static class Coordinates {
private double latitude;
private double longitude;
public Coordinates(double latitude, double longitude){
this.latitude = latitude;
this.longitude = longitude;
}
public double getLatitude() {
return latitude;
}
public void setLatitude(double latitude) {
this.latitude = latitude;
}
public double getLongitude() {
return longitude;
}
public void setLongitude(double longitude) {
this.longitude = longitude;
}
}
}