// ==UserScript== // @id iitc-plugin-fix-googlemap-china-offset@breezewish // @name IITC plugin: Fix Google Map offsets in China // @category Tweaks // @version 0.0.1.@@DATETIMEVERSION@@ // @namespace https://github.com/jonatkins/ingress-intel-total-conversion // @updateURL @@UPDATEURL@@ // @downloadURL @@DOWNLOADURL@@ // @description [@@BUILDNAME@@-@@BUILDDATE@@] Show correct Google Map for China user by applying offset tweaks. // @include https://*.ingress.com/intel* // @include http://*.ingress.com/intel* // @match https://*.ingress.com/intel* // @match http://*.ingress.com/intel* // @include https://*.ingress.com/mission/* // @include http://*.ingress.com/mission/* // @match https://*.ingress.com/mission/* // @match http://*.ingress.com/mission/* // @grant none // ==/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) 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 Google maps in IITC, the layer structure looks like this: // ---------------------- // | Other Leaflet layers | (Including portals, links, fields, and so on) // ---------------------- // | L.Google | (Only for controling) // ---------------------- // | Google Map layer | (Generated by Google Map APIs, for rendering maps) // ---------------------- // // When users are interacting with L.Google (for example, dragging, zooming), // L.Google 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.Google. When users are dragging the map, // L.Google 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. /////////// begin WGS84 to GCJ-02 transformer ///////// var WGS84transformer = window.plugin.fixChinaOffset.WGS84transformer = function() {}; // Krasovsky 1940 // // a = 6378245.0, 1/f = 298.3 // b = a * (1 - f) // ee = (a^2 - b^2) / a^2; WGS84transformer.prototype.a = 6378245.0; WGS84transformer.prototype.ee = 0.00669342162296594323; WGS84transformer.prototype.transform = function(wgLat, wgLng) { if(this.isOutOfChina(wgLat, wgLng)) return {lat: wgLat, lng: wgLng}; dLat = this.transformLat(wgLng - 105.0, wgLat - 35.0); dLng = this.transformLng(wgLng - 105.0, wgLat - 35.0); radLat = wgLat / 180.0 * Math.PI; magic = Math.sin(radLat); magic = 1 - this.ee * magic * magic; sqrtMagic = Math.sqrt(magic); dLat = (dLat * 180.0) / ((this.a * (1 - this.ee)) / (magic * sqrtMagic) * Math.PI); dLng = (dLng * 180.0) / (this.a / sqrtMagic * Math.cos(radLat) * Math.PI); mgLat = wgLat + dLat; mgLng = wgLng + dLng; return {lat: mgLat, lng: mgLng}; }; WGS84transformer.prototype.isOutOfChina = function(lat, lng) { if(lng < 72.004 || lng > 137.8347) return true; if(lat < 0.8293 || lat > 55.8271) return true; return false; }; WGS84transformer.prototype.transformLat = function(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 * Math.PI) + 20.0 * Math.sin(2.0 * x * Math.PI)) * 2.0 / 3.0; ret += (20.0 * Math.sin(y * Math.PI) + 40.0 * Math.sin(y / 3.0 * Math.PI)) * 2.0 / 3.0; ret += (160.0 * Math.sin(y / 12.0 * Math.PI) + 320 * Math.sin(y * Math.PI / 30.0)) * 2.0 / 3.0; return ret; }; WGS84transformer.prototype.transformLng = function(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 * Math.PI) + 20.0 * Math.sin(2.0 * x * Math.PI)) * 2.0 / 3.0; ret += (20.0 * Math.sin(x * Math.PI) + 40.0 * Math.sin(x / 3.0 * Math.PI)) * 2.0 / 3.0; ret += (150.0 * Math.sin(x / 12.0 * Math.PI) + 300.0 * Math.sin(x / 30.0 * Math.PI)) * 2.0 / 3.0; return ret; }; /////////// end WGS84 to GCJ-02 transformer ///////// var WGS84toGCJ02 = new WGS84transformer(); /////////// begin overwrited L.Google ///////// window.plugin.fixChinaOffset.L = {}; window.plugin.fixChinaOffset.L.Google = { _update: function(e) { if(!this._google) return; this._resize(); var center = e && e.latlng ? e.latlng : this._map.getCenter(); ///// modified here /// var _center = window.plugin.fixChinaOffset.getLatLng(center, this._type); /////////////////////// this._google.setCenter(_center); this._google.setZoom(this._map.getZoom()); this._checkZoomLevels(); }, _handleZoomAnim: function (e) { var center = e.center; ///// modified here /// var _center = window.plugin.fixChinaOffset.getLatLng(center, this._type); /////////////////////// this._google.setCenter(_center); this._google.setZoom(e.zoom); } } /////////// end overwrited L.Google ///////// window.plugin.fixChinaOffset.getLatLng = function(pos, type) { // No offsets in satellite and hybrid maps if(type !== 'SATELLITE' && type !== 'HYBRID') { var newPos = WGS84toGCJ02.transform(pos.lat, pos.lng); return new google.maps.LatLng(newPos.lat, newPos.lng); } else { return new google.maps.LatLng(pos.lat, pos.lng); } }; window.plugin.fixChinaOffset.overwrite = function(dest, src) { for(var key in src) { if(src.hasOwnProperty(key)) { dest[key] = src[key]; } } } var setup = function() { window.plugin.fixChinaOffset.overwrite(L.Google.prototype, window.plugin.fixChinaOffset.L.Google); } // PLUGIN END ////////////////////////////////////////////////////////// @@PLUGINEND@@