Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix google map offsets in china #68

Merged
merged 35 commits into from
Mar 13, 2019
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
6d3e284
Modify "China" boundary for offset fix plugin
modos189 Jan 6, 2019
ce0673b
Update Fix Google Map offsets in China to GoogleMutant. But not worki…
modos189 Jan 6, 2019
64060c4
An example of a working solution. Described in more detail in a task
modos189 Jan 16, 2019
67d0fdd
Smooth operation with map, fixed get map options, fixed download of m…
modos189 Jan 17, 2019
ad23110
fix-googlemap-china-offset: fix style issues
Jan 17, 2019
b07f133
Fix offset in Google maps
modos189 Jan 17, 2019
555f025
Merge branch 'fix-google-map-offsets-in-china' into fix-google-map-of…
Jan 17, 2019
057bad6
Rename to fix-china-offset.user.js
modos189 Jan 17, 2019
109aeee
Edited the documentation, because now plugin works not only in Google…
modos189 Jan 17, 2019
a5b2dbc
fix-googlemap-china-offset: refactoring
Jan 17, 2019
c1e4cf0
fixup! fix-googlemap-china-offset: refactoring
Jan 17, 2019
a6667d0
Revert changes in Leaflet.GoogleMutant.js
modos189 Jan 17, 2019
670343c
Rename fix-china-offset -> fix-china-map-offset
modos189 Jan 17, 2019
5951855
Previous indent returned
modos189 Jan 17, 2019
c1d8510
Update plugins/fix-china-map-offset.js
Jan 18, 2019
16ed429
Update plugins/fix-china-map-offset.js
Jan 18, 2019
c7d0b37
Update plugins/fix-china-map-offset.js
Jan 18, 2019
62000e6
fix-china-map-offset: Baidu support
Jan 19, 2019
0861f45
fix-china-map-offset: appied to googleMutant
Jan 19, 2019
306fee2
fix-china-map-offset: fix filename
Jan 19, 2019
54a76f4
fix-china-map-offset: fixme: options for HongKong and map type
Jan 19, 2019
9e074cb
fixup! fix-china-map-offset: fixme: options for HongKong and map type
Jan 20, 2019
328e2a6
fix-china-map-offset: refactor (intermediate function eliminated)
Jan 20, 2019
ea4efcb
fix-china-map-offset: refactor (redefine leaflet methods cleaner)
Jan 20, 2019
8c6360b
fix-china-map-offset: cosmetics (restore original source indents)
Jan 24, 2019
9e01a7c
fix-china-map-offset: change leaflet extending
Jan 24, 2019
29f1543
fix-china-map-offset: set 'needFixChinaOffset' option in boot.js
Jan 24, 2019
da28970
fix-china-map-offset: fix: put actions into setup function
Jan 25, 2019
d483b17
fix-china-map-offset: patch GoogleMutant in plugin completely (not in…
Jan 25, 2019
993cefa
fixup! fix-china-map-offset: cosmetics (restore original source indents)
Jan 26, 2019
423e66d
fix-china-map-offset: remove Baidu-stuff
Jan 28, 2019
2cb1bfc
fix-china-map-offset: shorten 'namespace' access
Feb 28, 2019
e4e2f96
fix-china-map-offset: fix: L.Util may not be available yet
Feb 28, 2019
2d6aca1
fix-china-map-offset: fix: namespace conflict
Feb 28, 2019
667ecea
Merge pull request #72 from johnd0e/fix-google-map-offsets-in-china
Mar 10, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
313 changes: 313 additions & 0 deletions plugins/fix-china-map-offset.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,313 @@
// ==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
window.plugin.fixChinaOffset = {};

// 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
//
// Correction is made for maps that have the parameter
// needFixChinaOffset: true
// in options. For an example of work, see Leaflet.GoogleMutant.js

plugin.fixChinaOffset.isInGoogle = (function () { // insane_is_in_china
// 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.
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 lngs = POINTS.filter(function (_, idx) { return idx % 2 === 0; });
POINTS = null; // not needed anyway...

/// *** 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;
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];
}
return !!inside;
};
/// ^^^ pnpoly ^^^ ///

var isInGoogle = function (coords) {
// Yank out South China Sea as it's not distorted.
return coords.lat >= 17.754 && coords.lat <= 55.8271 &&
coords.lng >= 72.004 && coords.lng <= 137.8347 &&
pnpoly(lats, lngs, coords.lat, coords.lng);
};

return isInGoogle;
})();

plugin.fixChinaOffset.gcjObfs = (function () {
/// Krasovsky 1940 ellipsoid
/// @const
var GCJ_A = 6378245;
var GCJ_EE = 0.00669342162296594323; // f = 1/298.3; e^2 = 2*f - f**2

var gcjObfs = function (wgs) {

var x = wgs.lng - 105,
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)) +
20 / 3 * (
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));
var dLon_m = 300 + x + 2 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x)) +
20 / 3 * (
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));

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,
};
};

return gcjObfs;
})();

plugin.fixChinaOffset.process = function (wgs) {
return plugin.fixChinaOffset.isInGoogle(wgs)
? plugin.fixChinaOffset.gcjObfs(wgs)
: wgs;
};

///////// begin overwrited L.GridLayer /////////

L.GridLayer.prototype._getTiledPixelBounds = (function () {
return function (center) {
/// modified here ///
center = window.plugin.fixChinaOffset.getLatLng(center, this.options);
/////////////////////
var map = this._map,
mapZoom = map._animatingZoom ? Math.max(map._animateToZoom, map.getZoom()) : map.getZoom(),
scale = map.getZoomScale(mapZoom, this._tileZoom),
pixelCenter = map.project(center, this._tileZoom).floor(),
halfSize = map.getSize().divideBy(scale * 2);

return new L.Bounds(pixelCenter.subtract(halfSize), pixelCenter.add(halfSize));
};
})(L.GridLayer.prototype._getTiledPixelBounds);

L.GridLayer.prototype._setZoomTransform = (function (original) {
return function (level, center, zoom) {
center = window.plugin.fixChinaOffset.getLatLng(center, this.options);
original.apply(this, [level, center, zoom]);
};
})(L.GridLayer.prototype._setZoomTransform);

L.GridLayer.GoogleMutant.prototype._update = (function (original) {
modos189 marked this conversation as resolved.
Show resolved Hide resolved
return function () {
this.options.needFixChinaOffset = true;
// 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();
/// modified here ///
center = window.plugin.fixChinaOffset.getLatLng(center, this.options);
/////////////////////
modos189 marked this conversation as resolved.
Show resolved Hide resolved
var _center = new google.maps.LatLng(center.lat, center.lng);

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
}
modos189 marked this conversation as resolved.
Show resolved Hide resolved
}

L.GridLayer.prototype._update.call(this);
};
})(L.GridLayer.GoogleMutant.prototype._update);

window.plugin.fixChinaOffset.getLatLng = function (pos, options) {
// No offsets in satellite and hybrid maps
johnd0e marked this conversation as resolved.
Show resolved Hide resolved
if (options.needFixChinaOffset === true && options.type !== 'satellite' && options.type !== 'hybrid') {
return plugin.fixChinaOffset.process(pos);
}
return pos;
};

var setup = function () {};

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

@@PLUGINEND@@
Loading