@progress/kendo-charts
Version:
Kendo UI platform-independent Charts library
229 lines (202 loc) • 6.56 kB
JavaScript
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);
}
}