Skip to content

Commit

Permalink
Fix google map offsets in china (#68)
Browse files Browse the repository at this point in the history
* Modify "China" boundary for offset fix plugin
iitc-project/ingress-intel-total-conversion#1188 #2

* Update Fix Google Map offsets in China to GoogleMutant. But not working =(
The plugin changes coordinates, but the change is not applied

* An example of a working solution. Described in more detail in a task
#64

* Smooth operation with map, fixed get map options, fixed download of map tiles (except Google maps)

* fix-googlemap-china-offset: fix style issues

lint:
* `let` is not in es5
* 'HK_LENGTH' is assigned a value but never used
* 'original' is defined but never used

style:
* == vs ===
* Mixed spaces and tabs
* fix indents, missed/extra/trailing spaces, missed semi

* Fix offset in Google maps

* Rename to fix-china-offset.user.js

* Edited the documentation, because now plugin works not only in Google maps

* fix-googlemap-china-offset: refactoring

divide PRCoords into 2 distinct functions:
* plugin.fixChinaOffset.isInGoogle
* plugin.fixChinaOffset.gcjObfs

added stub for old fix-googlemap-china-offset plugin

* fixup! fix-googlemap-china-offset: refactoring

* Revert changes in Leaflet.GoogleMutant.js

* Rename fix-china-offset -> fix-china-map-offset

* Previous indent returned

* Update plugins/fix-china-map-offset.js

Co-Authored-By: modos189 <[email protected]>

* Update plugins/fix-china-map-offset.js

Co-Authored-By: modos189 <[email protected]>

* Update plugins/fix-china-map-offset.js

Co-Authored-By: modos189 <[email protected]>

* fix-china-map-offset: Baidu support

* Sources refactored to be closer to upstream https://github.com/Artoria2e5/PRCoords
* Baidu support
* Clarify comment about usage

* fix-china-map-offset: appied to googleMutant

* fix-china-map-offset: fix filename

* fix-china-map-offset: fixme: options for HongKong and map type

* fixup! fix-china-map-offset: fixme: options for HongKong and map type

* fix-china-map-offset: refactor (intermediate function eliminated)

* fix-china-map-offset: refactor (redefine leaflet methods cleaner)

* fix-china-map-offset: cosmetics (restore original source indents)

* fix-china-map-offset: change leaflet extending

do not touch L.GridLayer
extend L.TileLayer and L.GridLayer.GoogleMutant

* fix-china-map-offset: set 'needFixChinaOffset' option in boot.js

* fix-china-map-offset: fix: put actions into setup function

* fix-china-map-offset: patch GoogleMutant in plugin completely (not in boot.js)

* fixup! fix-china-map-offset: cosmetics (restore original source indents)

* fix-china-map-offset: remove Baidu-stuff

In current state it doesn't function.
Maybe someone reimplement it in future.
More info here: #64.

* fix-china-map-offset: shorten 'namespace' access

* fix-china-map-offset: fix: L.Util may not be available yet

* fix-china-map-offset: fix: namespace conflict
  • Loading branch information
modos189 committed Mar 13, 2019
1 parent c65895d commit 8e8c2bc
Show file tree
Hide file tree
Showing 3 changed files with 339 additions and 186 deletions.
3 changes: 2 additions & 1 deletion code/boot.js
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,8 @@ function createDefaultBaseMapLayers() {
stylers: [{visibility:"on"}, {hue:"#005eff"}, {invert_lightness:true}] },
{ featureType:"poi", stylers:[{visibility:"off"}]},
{ featureType:"transit", elementType:"all", stylers:[{visibility:"off"}] }
]});
],
});
baseLayers['Google Roads'] = L.gridLayer.googleMutant({type:'roadmap', maxZoom: 21});
var trafficMutant = L.gridLayer.googleMutant({type:'roadmap', maxZoom: 21});
trafficMutant.addGoogleLayer('TrafficLayer');
Expand Down
335 changes: 335 additions & 0 deletions plugins/fix-china-map-offset.user.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,335 @@
// ==UserScript==
// @id iitc-plugin-fix-china-map-offset
// @name IITC plugin: Fix maps offsets in China
// @category Tweaks
// @version 0.2.0.@@DATETIMEVERSION@@
// @description [@@BUILDNAME@@-@@BUILDDATE@@] Show correct maps for China user by applying offset tweaks.
@@METAINFO@@
// ==/UserScript==

@@PLUGINSTART@@

// PLUGIN START ////////////////////////////////////////////////////////

// use own namespace for plugin
var fixChinaMapOffset = {};
window.plugin.fixChinaMapOffset = fixChinaMapOffset;

// This plugin is intended to fix offset problem of Google maps in China.
//
// When the plugin is active then the same correction method is applying
// to any map that has custom TileLayer option `needFixChinaOffset: true`.
//
// Example: basemap-gaode.user.js


// Before understanding how this plugin works, you should know 3 points:
//
// Point1.
// The coordinate system of Ingress is WGS-84.
// However, the tiles of Google maps (except satellite map) and some other maps
// in China have offsets (base on GCJ-02 coordinate system) by China policy.
// That means, if you request map tiles by giving GCJ-02 position, you
// will get the correct map.
//
// Point2.
// Currently there are no easy algorithm to transform from GCJ-02 to WGS-84,
// but we can easily transform data from WGS-84 to GCJ-02.
//
// Point3.
// When using, for example, Google maps in IITC, the layer structure looks like this:
// --------------------------
// | Other Leaflet layers | (Including portals, links, fields, and so on)
// --------------------------
// | L.GridLayer.GoogleMutant | (Only for controling)
// --------------------------
// | Google Map layer | (Generated by Google Map APIs, for rendering maps)
// --------------------------
//
// When users are interacting with L.GridLayer.GoogleMutant (for example, dragging, zooming),
// L.GridLayer.GoogleMutant will perform the same action on the Google Map layer using Google
// Map APIs.
//
// So, here is the internal of the plugin:
//
// The plugin overwrites behaviours of L.GridLayer.GoogleMutant. When users are dragging the map,
// L.GridLayer.GoogleMutant will pass offseted positions to Google Map APIs (WGS-84 to GCJ-02).
// So Google Map APIs will render a correct map.
//
// The offset between Google maps and Ingress objects can also be fixed by applying
// WGS-84 to GCJ-02 transformation on Ingress objects. However we cannot simply know
// the requesting bounds of Ingress objects because we cannot transform GCJ-02 to
// WGS-84. As a result, the Ingress objects on maps would be incomplete.
//
// The algorithm of transforming WGS-84 to GCJ-02 comes from:
// https://on4wp7.codeplex.com/SourceControl/changeset/view/21483#353936
// There is no official algorithm because it is classified information.
//
// Here we use the PRCoords implementation of this algorithm, which contains
// a more careful yet still rough "China area" check in "insane_is_in_china.js".
// Comments and code style have been adapted, mainly to remove profanity.
// https://github.com/Artoria2e5/PRCoords
// (more info: https://github.com/iitc-project/ingress-intel-total-conversion/pull/1188)

var insane_is_in_china = (function () { // adapted from https://github.com/Artoria2e5/PRCoords/blob/master/js/misc/insane_is_in_china.js
/* eslint-disable */
'use strict';

/// *** pnpoly *** ///
// Wm. Franklin's 8-line point-in-polygon C program
// Copyright (c) 1970-2003, Wm. Randolph Franklin
// Copyright (c) 2017, Mingye Wang (js translation)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimers.
// 2. Redistributions in binary form must reproduce the above
// copyright notice in the documentation and/or other materials
// provided with the distribution.
// 3. The name of W. Randolph Franklin may not be used to endorse or
// promote products derived from this Software without specific
// prior written permission.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
var pnpoly = function (xs, ys, x, y) {
if (!(xs.length === ys.length)) { throw new Error('pnpoly: assert(xs.length === ys.length)'); }
var inside = 0;
// j records previous value. Also handles wrapping around.
for (var i = 0, j = xs.length - 1; i < xs.length; j = i++) {
inside ^= ys[i] > y !== ys[j] > y &&
x < (xs[j] - xs[i]) * (y - ys[i]) / (ys[j] - ys[i]) + xs[i];
}
// Let's make js as magical as C. Yay.
return !!inside;
};
/// ^^^ pnpoly ^^^ ///

// This set of points roughly illustrates the scope of Google's
// distortion. It has nothing to do with national borders etc.
// Points around Hong Kong/Shenzhen are mapped with a little more precision,
// in hope that it will make agents work a bit more smoothly there.
//
// Edits around these points are welcome.

// lon, lat
var POINTS = [
// start hkmo
114.433722, 22.064310,
114.009458, 22.182105,
113.599275, 22.121763,
113.583463, 22.176002,
113.530900, 22.175318,
113.529542, 22.210608,
113.613377, 22.227435,
113.938514, 22.483714,
114.043449, 22.500274,
114.138506, 22.550640,
114.222984, 22.550960,
114.366803, 22.524255,
// end hkmo
115.254019, 20.235733,
121.456316, 26.504442,
123.417261, 30.355685,
124.289197, 39.761103,
126.880509, 41.774504,
127.887261, 41.370015,
128.214602, 41.965359,
129.698745, 42.452788,
130.766139, 42.668534,
131.282487, 45.037051,
133.142361, 44.842986,
134.882453, 48.370596,
132.235531, 47.785403,
130.980075, 47.804860,
130.659026, 48.968383,
127.860252, 50.043973,
125.284310, 53.667091,
120.619316, 53.100485,
119.403751, 50.105903,
117.070862, 49.690388,
115.586019, 47.995542,
118.599613, 47.927785,
118.260771, 46.707335,
113.534759, 44.735134,
112.093739, 45.001999,
111.431259, 43.489381,
105.206324, 41.809510,
96.485703, 42.778692,
94.167961, 44.991668,
91.130430, 45.192938,
90.694601, 47.754437,
87.356293, 49.232005,
85.375791, 48.263928,
85.876055, 47.109272,
82.935423, 47.285727,
81.929808, 45.506317,
79.919457, 45.108122,
79.841455, 42.178752,
73.334917, 40.076332,
73.241805, 39.062331,
79.031902, 34.206413,
78.738395, 31.578004,
80.715812, 30.453822,
81.821692, 30.585965,
85.501663, 28.208463,
92.096061, 27.754241,
94.699781, 29.357171,
96.079442, 29.429559,
98.910308, 27.140660,
97.404057, 24.494701,
99.400021, 23.168966,
100.697449, 21.475914,
102.976870, 22.616482,
105.476997, 23.244292,
108.565621, 20.907735,
107.730505, 18.193406,
110.669856, 17.754550,
];

var lats = POINTS.filter(function (_, idx) { return idx % 2 === 1; });
var lons = POINTS.filter(function (_, idx) { return idx % 2 === 0; });

function isInChina (lat, lon) {
// Yank out South China Sea as it's not distorted.
if (lat >= 17.754 && lat <= 55.8271 &&
lon >= 72.004 && lon <= 137.8347) {
return pnpoly(lats, lons, lat, lon);
}
}

return { isInChina: isInChina };
/* eslint-enable */
})();

fixChinaMapOffset.isInChina = insane_is_in_china.isInChina;

var PRCoords = (function () { // adapted from https://github.com/Artoria2e5/PRCoords/blob/master/js/PRCoords.js
/* eslint-disable */
'use strict';

/// Krasovsky 1940 ellipsoid
/// @const
var GCJ_A = 6378245;
var GCJ_EE = 0.00669342162296594323; // f = 1/298.3; e^2 = 2*f - f**2

function wgs_gcj (wgs) {

var x = wgs.lng - 105, // mod: lon->lng
y = wgs.lat - 35;

// These distortion functions accept (x = lon - 105, y = lat - 35).
// They return distortions in terms of arc lengths, in meters.
var dLat_m = -100 + 2 * x + 3 * y + 0.2 * y * y + 0.1 * x * y +
0.2 * Math.sqrt(Math.abs(x)) + (
2 * Math.sin(x * 6 * Math.PI) + 2 * Math.sin(x * 2 * Math.PI) +
2 * Math.sin(y * Math.PI) + 4 * Math.sin(y / 3 * Math.PI) +
16 * Math.sin(y / 12 * Math.PI) + 32 * Math.sin(y / 30 * Math.PI)
) * 20 / 3;
var dLon_m = 300 + x + 2 * y + 0.1 * x * x + 0.1 * x * y +
0.1 * Math.sqrt(Math.abs(x)) + (
2 * Math.sin(x * 6 * Math.PI) + 2 * Math.sin(x * 2 * Math.PI) +
2 * Math.sin(x * Math.PI) + 4 * Math.sin(x / 3 * Math.PI) +
15 * Math.sin(x / 12 * Math.PI) + 30 * Math.sin(x / 30 * Math.PI)
) * 20 / 3;

var radLat = wgs.lat / 180 * Math.PI;
var magic = 1 - GCJ_EE * Math.pow(Math.sin(radLat), 2); // just a common expr

// Arc lengths per degree, on the wrong ellipsoid
var lat_deg_arclen = Math.PI / 180 * (GCJ_A * (1 - GCJ_EE)) * Math.pow(magic, 1.5);
var lon_deg_arclen = Math.PI / 180 * (GCJ_A * Math.cos(radLat) / Math.sqrt(magic));

return {
lat: wgs.lat + dLat_m / lat_deg_arclen,
lng: wgs.lng + dLon_m / lon_deg_arclen, // mod: lon->lng
};
}

return { wgs_gcj: wgs_gcj };
/* eslint-enable */
})();

fixChinaMapOffset.wgs_gcj = PRCoords.wgs_gcj;

fixChinaMapOffset.transform = function (wgs, options) {
if (options.needFixChinaOffset && fixChinaMapOffset.isInChina(wgs.lat, wgs.lng)) {
return fixChinaMapOffset.wgs_gcj(wgs);
}
return wgs;
};

// redefine L.TileLayer methods
var fixChinaOffset = {
_getTiledPixelBounds: function (center) {
center = fixChinaMapOffset.transform(center, this.options);
return L.GridLayer.prototype._getTiledPixelBounds.call(this, center);
},
_setZoomTransform: function (level, center, zoom) {
center = fixChinaMapOffset.transform(center, this.options);
return L.GridLayer.prototype._setZoomTransform.call(this, level, center, zoom);
}
};

// redefine L.GridLayer.GoogleMutant methods
var fixGoogleMutant = {
/* eslint-disable */
_update: function () {
// zoom level check needs to happen before super's implementation (tile addition/creation)
// otherwise tiles may be missed if maxNativeZoom is not yet correctly determined
if (this._mutant) {
var center = this._map.getCenter();
var _center = new google.maps.LatLng(center.lat, center.lng);
/// modified here ///
center = fixChinaMapOffset.transform(center, this.options);
/////////////////////

this._mutant.setCenter(_center);
var zoom = this._map.getZoom();
var fractionalLevel = zoom !== Math.round(zoom);
var mutantZoom = this._mutant.getZoom();

//ignore fractional zoom levels
if (!fractionalLevel && (zoom != mutantZoom)) {
this._mutant.setZoom(zoom);

if (this._mutantIsReady) this._checkZoomLevels();
//else zoom level check will be done later by 'idle' handler
}
}

L.GridLayer.prototype._update.call(this);
},
/* eslint-enable */
};

function setup () {
// add support of `needFixChinaOffset` property to any TileLayer
L.TileLayer.include(fixChinaOffset);

// GoogleMutant needs additional support
L.GridLayer.GoogleMutant.include(fixChinaOffset);
L.GridLayer.GoogleMutant.include(fixGoogleMutant);
layerChooser._layers.forEach(function (item) {
if (item.layer._GAPIPromise) { // Google layer
var o = item.layer.options;
o.needFixChinaOffset = o.type !== 'satellite' && o.type !== 'hybride';
}
});
}

// PLUGIN END //////////////////////////////////////////////////////////

@@PLUGINEND@@
Loading

0 comments on commit 8e8c2bc

Please sign in to comment.