UNPKG

@progress/kendo-charts

Version:

Kendo UI platform-independent Charts library

229 lines (202 loc) 6.56 kB
import { geometry as g } from '@progress/kendo-drawing'; import { setDefaultOptions, limitValue, rad, deg, deepExtend } from '../common'; import { Location } from './location'; import { datums } from './datums'; let math = Math, atan = math.atan, exp = math.exp, pow = math.pow, sin = math.sin, log = math.log, tan = math.tan, Point = g.Point; let PI = math.PI, PI_DIV_2 = PI / 2, PI_DIV_4 = PI / 4, DEG_TO_RAD = PI / 180; let WGS84 = datums.WGS84; // WGS 84 / World Mercator export class Mercator { constructor(options) { this.initProperties(); this._initOptions(options); } _initOptions(options) { this.options = deepExtend({}, this.options, options); } initProperties() { // super.initProperties(); deepExtend(this, { MAX_LNG: 180, MAX_LAT: 85.0840590501, INVERSE_ITERATIONS: 15, INVERSE_CONVERGENCE: 1e-12 }); } forward(loc, clamp) { let proj = this, options = proj.options, datum = options.datum, r = datum.a, lng0 = options.centralMeridian, lat = limitValue(loc.lat, -proj.MAX_LAT, proj.MAX_LAT), lng = clamp ? limitValue(loc.lng, -proj.MAX_LNG, proj.MAX_LNG) : loc.lng, x = rad(lng - lng0) * r, y = proj._projectLat(lat); return new Point(x, y); } _projectLat(lat) { let datum = this.options.datum, ecc = datum.e, r = datum.a, y = rad(lat), ts = tan(PI_DIV_4 + y / 2), con = ecc * sin(y), p = pow((1 - con) / (1 + con), ecc / 2); // See: http://en.wikipedia.org/wiki/Mercator_projection#Generalization_to_the_ellipsoid return r * log(ts * p); } inverse(point, clamp) { let proj = this, options = proj.options, datum = options.datum, r = datum.a, lng0 = options.centralMeridian, lng = point.x / (DEG_TO_RAD * r) + lng0, lat = limitValue(proj._inverseY(point.y), -proj.MAX_LAT, proj.MAX_LAT); if (clamp) { lng = limitValue(lng, -proj.MAX_LNG, proj.MAX_LNG); } return new Location(lat, lng); } _inverseY(y) { let proj = this, datum = proj.options.datum, r = datum.a, ecc = datum.e, ecch = ecc / 2, ts = exp(-y / r), phi = PI_DIV_2 - 2 * atan(ts), i; for (i = 0; i <= proj.INVERSE_ITERATIONS; i++) { let con = ecc * sin(phi), p = pow((1 - con) / (1 + con), ecch), dphi = PI_DIV_2 - 2 * atan(ts * p) - phi; phi += dphi; if (math.abs(dphi) <= proj.INVERSE_CONVERGENCE) { break; } } return deg(phi); } } setDefaultOptions(Mercator, { centralMeridian: 0, datum: WGS84 }); // WGS 84 / Pseudo-Mercator // Used by Google Maps, Bing, OSM, etc. // Spherical projection of ellipsoidal coordinates. export class SphericalMercator extends Mercator { initProperties() { super.initProperties(); deepExtend(this, { MAX_LAT: 85.0511287798 }); } _projectLat(lat) { let r = this.options.datum.a, y = rad(lat), ts = tan(PI_DIV_4 + y / 2); return r * log(ts); } _inverseY(y) { let r = this.options.datum.a, ts = exp(-y / r); return deg(PI_DIV_2 - 2 * atan(ts)); } } export class Equirectangular { forward(loc) { return new Point(loc.lng, loc.lat); } inverse(point) { return new Location(point.y, point.x); } } // This is the projected coordinate system used for rendering maps in Google Maps, OpenStreetMap, etc // Unit: metre // Geodetic CRS: WGS 84 // Scope: Certain Web mapping and visualisation applications. It is not a recognised geodetic system: for that see ellipsoidal Mercator CRS code 3395 (WGS 84 / World Mercator). // Remarks: Uses spherical development of ellipsoidal coordinates. Relative to WGS 84 / World Mercator (CRS code 3395) errors of 0.7 percent in scale and differences in northing of up to 43km in the map (equivalent to 21km on the ground) may arise. // Area of use: World between 85.06°S and 85.06°N. // Coordinate system: Cartesian 2D CS. Axes: easting, northing (X,Y). Orientations: east, north. UoM: m. // https://epsg.io/3857 export class EPSG3857 { constructor() { let crs = this, proj = crs._proj = new SphericalMercator(); let c = this.c = 2 * PI * proj.options.datum.a; // transfrom matrix // Scale circumference to 1, mirror Y and shift origin to top left this._tm = g.transform().translate(0.5, 0.5).scale(1 / c, -1 / c); // Inverse transform matrix this._itm = g.transform().scale(c, -c).translate(-0.5, -0.5); } // Location <-> Point (screen coordinates for a given scale) toPoint(loc, scale, clamp) { let point = this._proj.forward(loc, clamp); return point.transform(this._tm).scale(scale || 1); } toLocation(point, scale, clamp) { let newPoint = point.clone().scale(1 / (scale || 1)).transform(this._itm); return this._proj.inverse(newPoint, clamp); } } // Unit: metre // Geodetic CRS: WGS 84 // Scope: Very small scale mapping. // Remarks: Euro-centric view of world excluding polar areas. // Area of use: World between 80°S and 84°N. // Coordinate system: Cartesian 2D CS. Axes: easting, northing (E,N). Orientations: east, north. UoM: m. // https://epsg.io/3395 export class EPSG3395 { constructor() { this._proj = new Mercator(); } toPoint(loc) { return this._proj.forward(loc); } toLocation(point) { return this._proj.inverse(point); } } // Unit: degree // Geodetic CRS: WGS 84 // Scope: Horizontal component of 3D system. Used by the GPS satellite navigation system and for NATO military geodetic surveying. // Area of use: World. // Coordinate system: Ellipsoidal 2D CS. Axes: latitude, longitude. Orientations: north, east. UoM: degree // https://epsg.io/4326 export class EPSG4326 { constructor() { this._proj = new Equirectangular(); } toPoint(loc) { return this._proj.forward(loc); } toLocation(point) { return this._proj.inverse(point); } }