Removed CRS and Project components and updated tests

master
Robin Hawkes 2016-06-16 14:15:02 +01:00
rodzic 5a79c45bfd
commit 3064d70496
23 zmienionych plików z 250 dodań i 1935 usunięć

103
dist/vizicities.js vendored
Wyświetl plik

@ -193,8 +193,6 @@ return /******/ (function(modules) { // webpackBootstrap
var _lodashAssign2 = _interopRequireDefault(_lodashAssign);
// import CRS from './geo/crs/index';
var _geoGeo = __webpack_require__(6);
var _geoGeo2 = _interopRequireDefault(_geoGeo);
@ -225,7 +223,6 @@ return /******/ (function(modules) { // webpackBootstrap
_get(Object.getPrototypeOf(World.prototype), 'constructor', this).call(this);
var defaults = {
// crs: CRS.EPSG3857,
skybox: false,
postProcessing: false
};
@ -2138,7 +2135,7 @@ return /******/ (function(modules) { // webpackBootstrap
// Converts pixel / WebGL coords to geo coords
// This just reverses the Y axis to match WebGL
Geo.pointToLatLon = function (point) {
var _point = VIZI.point(point.x, point.y * -1);
var _point = (0, _Point.point)(point.x, point.y * -1);
return Geo.unproject(_point);
};
@ -2203,47 +2200,27 @@ return /******/ (function(modules) { // webpackBootstrap
};
// Convert real metres to a value in world (WebGL) units
Geo.metresToWorld = function (metres, pointScale, zoom) {
Geo.metresToWorld = function (metres, pointScale) {
// Transform metres to projected metres using the latitude point scale
//
// Latitude scale is chosen because it fluctuates more than longitude
var projectedMetres = Geo.metresToProjected(metres, pointScale);
var scale = Geo.scale(zoom);
// Half scale if using zoom as WebGL origin is in the centre, not top left
if (zoom) {
scale /= 2;
}
var scale = Geo.scale();
// Scale projected metres
var scaledMetres = scale * projectedMetres;
// Not entirely sure why this is neccessary
if (zoom) {
scaledMetres /= pointScale[1];
}
return scaledMetres;
};
// Convert world (WebGL) units to a value in real metres
Geo.worldToMetres = function (worldUnits, pointScale, zoom) {
var scale = Geo.scale(zoom);
// Half scale if using zoom as WebGL origin is in the centre, not top left
if (zoom) {
scale /= 2;
}
Geo.worldToMetres = function (worldUnits, pointScale) {
var scale = Geo.scale();
var projectedUnits = worldUnits / scale;
var realMetres = Geo.projectedToMetres(projectedUnits, pointScale);
// Not entirely sure why this is neccessary
if (zoom) {
realMetres *= pointScale[1];
}
return realMetres;
};
@ -18578,11 +18555,7 @@ return /******/ (function(modules) { // webpackBootstrap
// TODO: A lot of these utils don't need to be in separate, tiny files
var _Transformation = __webpack_require__(78);
var _Transformation2 = _interopRequireDefault(_Transformation);
var _wrapNum = __webpack_require__(79);
var _wrapNum = __webpack_require__(78);
var _wrapNum2 = _interopRequireDefault(_wrapNum);
@ -18600,7 +18573,6 @@ return /******/ (function(modules) { // webpackBootstrap
var Util = {};
Util.Transformation = _Transformation2['default'];
Util.wrapNum = _wrapNum2['default'];
Util.extrudePolygon = _extrudePolygon2['default'];
Util.GeoJSON = _GeoJSON2['default'];
@ -18611,69 +18583,6 @@ return /******/ (function(modules) { // webpackBootstrap
/***/ },
/* 78 */
/***/ function(module, exports, __webpack_require__) {
Object.defineProperty(exports, '__esModule', {
value: true
});
var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
/*
* Transformation is an utility class to perform simple point transformations
* through a 2d-matrix.
*
* Based on:
* https://github.com/Leaflet/Leaflet/blob/master/src/geometry/Transformation.js
*/
var _geoPoint = __webpack_require__(8);
var Transformation = (function () {
function Transformation(a, b, c, d) {
_classCallCheck(this, Transformation);
this._a = a;
this._b = b;
this._c = c;
this._d = d;
}
_createClass(Transformation, [{
key: 'transform',
value: function transform(point, scale) {
// Copy input point as to not destroy the original data
return this._transform(point.clone(), scale);
}
// Destructive transform (faster)
}, {
key: '_transform',
value: function _transform(point, scale) {
scale = scale || 1;
point.x = scale * (this._a * point.x + this._b);
point.y = scale * (this._c * point.y + this._d);
return point;
}
}, {
key: 'untransform',
value: function untransform(point, scale) {
scale = scale || 1;
return (0, _geoPoint.point)((point.x / scale - this._b) / this._a, (point.y / scale - this._d) / this._c);
}
}]);
return Transformation;
})();
exports['default'] = Transformation;
module.exports = exports['default'];
/***/ },
/* 79 */
/***/ function(module, exports) {
Object.defineProperty(exports, "__esModule", {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Wyświetl plik

@ -44,7 +44,7 @@ Geo.latLonToPoint = function(latlon) {
// Converts pixel / WebGL coords to geo coords
// This just reverses the Y axis to match WebGL
Geo.pointToLatLon = function(point) {
var _point = VIZI.point(point.x, point.y * -1);
var _point = Point(point.x, point.y * -1);
return Geo.unproject(_point);
};
@ -109,47 +109,27 @@ Geo.projectedToMetres = function(projectedUnits, pointScale) {
};
// Convert real metres to a value in world (WebGL) units
Geo.metresToWorld = function(metres, pointScale, zoom) {
Geo.metresToWorld = function(metres, pointScale) {
// Transform metres to projected metres using the latitude point scale
//
// Latitude scale is chosen because it fluctuates more than longitude
var projectedMetres = Geo.metresToProjected(metres, pointScale);
var scale = Geo.scale(zoom);
// Half scale if using zoom as WebGL origin is in the centre, not top left
if (zoom) {
scale /= 2;
}
var scale = Geo.scale();
// Scale projected metres
var scaledMetres = (scale * projectedMetres);
// Not entirely sure why this is neccessary
if (zoom) {
scaledMetres /= pointScale[1];
}
return scaledMetres;
};
// Convert world (WebGL) units to a value in real metres
Geo.worldToMetres = function(worldUnits, pointScale, zoom) {
var scale = Geo.scale(zoom);
// Half scale if using zoom as WebGL origin is in the centre, not top left
if (zoom) {
scale /= 2;
}
Geo.worldToMetres = function(worldUnits, pointScale) {
var scale = Geo.scale();
var projectedUnits = worldUnits / scale;
var realMetres = Geo.projectedToMetres(projectedUnits, pointScale);
// Not entirely sure why this is neccessary
if (zoom) {
realMetres *= pointScale[1];
}
return realMetres;
};

Wyświetl plik

@ -1,32 +0,0 @@
/*
* CRS.EPSG3395 (WGS 84 / World Mercator) CRS implementation.
*
* Based on:
* https://github.com/Leaflet/Leaflet/blob/master/src/geo/crs/CRS.EPSG3395.js
*/
import extend from 'lodash.assign';
import Earth from './CRS.Earth';
import Mercator from '../projection/Projection.Mercator';
import Transformation from '../../util/Transformation';
var _EPSG3395 = {
code: 'EPSG:3395',
projection: Mercator,
// Work out how to de-dupe this (scoping issue)
transformScale: 1 / (Math.PI * Mercator.R),
// Scale and transformation inputs changed to account for central origin in
// WebGL, instead of top-left origin used in Leaflet
transformation: (function() {
// TODO: Cannot use this.transformScale due to scope
var scale = 1 / (Math.PI * Mercator.R);
return new Transformation(scale, 0, -scale, 0);
}())
};
const EPSG3395 = extend({}, Earth, _EPSG3395);
export default EPSG3395;

Wyświetl plik

@ -1,38 +0,0 @@
/*
* CRS.EPSG3857 (WGS 84 / Pseudo-Mercator) CRS implementation.
*
* Based on:
* https://github.com/Leaflet/Leaflet/blob/master/src/geo/crs/CRS.EPSG3857.js
*/
import extend from 'lodash.assign';
import Earth from './CRS.Earth';
import SphericalMercator from '../projection/Projection.SphericalMercator';
import Transformation from '../../util/Transformation';
var _EPSG3857 = {
code: 'EPSG:3857',
projection: SphericalMercator,
// Work out how to de-dupe this (scoping issue)
transformScale: 1 / (Math.PI * SphericalMercator.R),
// Scale and transformation inputs changed to account for central origin in
// WebGL, instead of top-left origin used in Leaflet
transformation: (function() {
// TODO: Cannot use this.transformScale due to scope
var scale = 1 / (Math.PI * SphericalMercator.R);
return new Transformation(scale, 0, -scale, 0);
}())
};
const EPSG3857 = extend({}, Earth, _EPSG3857);
const EPSG900913 = extend({}, EPSG3857, {
code: 'EPSG:900913'
});
export {EPSG900913};
export default EPSG3857;

Wyświetl plik

@ -1,29 +0,0 @@
/*
* CRS.EPSG4326 is a CRS popular among advanced GIS specialists.
*
* Based on:
* https://github.com/Leaflet/Leaflet/blob/master/src/geo/crs/CRS.EPSG4326.js
*/
import extend from 'lodash.assign';
import Earth from './CRS.Earth';
import LatLonProjection from '../projection/Projection.LatLon';
import Transformation from '../../util/Transformation';
var _EPSG4326 = {
code: 'EPSG:4326',
projection: LatLonProjection,
// Work out how to de-dupe this (scoping issue)
transformScale: 1 / 180,
// Scale and transformation inputs changed to account for central origin in
// WebGL, instead of top-left origin used in Leaflet
//
// TODO: Cannot use this.transformScale due to scope
transformation: new Transformation(1 / 180, 0, -1 / 180, 0)
};
const EPSG4326 = extend({}, Earth, _EPSG4326);
export default EPSG4326;

Wyświetl plik

@ -1,132 +0,0 @@
/*
* CRS.Earth is the base class for all CRS representing Earth.
*
* Based on:
* https://github.com/Leaflet/Leaflet/blob/master/src/geo/crs/CRS.Earth.js
*/
import extend from 'lodash.assign';
import CRS from './CRS';
import {latLon as LatLon} from '../LatLon';
const Earth = {
wrapLon: [-180, 180],
R: 6378137,
// Distance between two geographical points using spherical law of cosines
// approximation or Haversine
//
// See: http://www.movable-type.co.uk/scripts/latlong.html
distance: function(latlon1, latlon2, accurate) {
var rad = Math.PI / 180;
var lat1;
var lat2;
var a;
if (!accurate) {
lat1 = latlon1.lat * rad;
lat2 = latlon2.lat * rad;
a = Math.sin(lat1) * Math.sin(lat2) + Math.cos(lat1) * Math.cos(lat2) * Math.cos((latlon2.lon - latlon1.lon) * rad);
return this.R * Math.acos(Math.min(a, 1));
} else {
lat1 = latlon1.lat * rad;
lat2 = latlon2.lat * rad;
var lon1 = latlon1.lon * rad;
var lon2 = latlon2.lon * rad;
var deltaLat = lat2 - lat1;
var deltaLon = lon2 - lon1;
var halfDeltaLat = deltaLat / 2;
var halfDeltaLon = deltaLon / 2;
a = Math.sin(halfDeltaLat) * Math.sin(halfDeltaLat) + Math.cos(lat1) * Math.cos(lat2) * Math.sin(halfDeltaLon) * Math.sin(halfDeltaLon);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
return this.R * c;
}
},
// Scale factor for converting between real metres and projected metres
//
// projectedMetres = realMetres * pointScale
// realMetres = projectedMetres / pointScale
//
// Defaults to a scale factor of 1 if no calculation method exists
//
// Probably need to run this through the CRS transformation or similar so the
// resulting scale is relative to the dimensions of the world space
// Eg. 1 metre in projected space is likly scaled up or down to some other
// number
pointScale: function(latlon, accurate) {
return (this.projection.pointScale) ? this.projection.pointScale(latlon, accurate) : [1, 1];
},
// Convert real metres to projected units
//
// Latitude scale is chosen because it fluctuates more than longitude
metresToProjected: function(metres, pointScale) {
return metres * pointScale[1];
},
// Convert projected units to real metres
//
// Latitude scale is chosen because it fluctuates more than longitude
projectedToMetres: function(projectedUnits, pointScale) {
return projectedUnits / pointScale[1];
},
// Convert real metres to a value in world (WebGL) units
metresToWorld: function(metres, pointScale, zoom) {
// Transform metres to projected metres using the latitude point scale
//
// Latitude scale is chosen because it fluctuates more than longitude
var projectedMetres = this.metresToProjected(metres, pointScale);
var scale = this.scale(zoom);
// Half scale if using zoom as WebGL origin is in the centre, not top left
if (zoom) {
scale /= 2;
}
// Scale projected metres
var scaledMetres = (scale * (this.transformScale * projectedMetres));
// Not entirely sure why this is neccessary
if (zoom) {
scaledMetres /= pointScale[1];
}
return scaledMetres;
},
// Convert world (WebGL) units to a value in real metres
worldToMetres: function(worldUnits, pointScale, zoom) {
var scale = this.scale(zoom);
// Half scale if using zoom as WebGL origin is in the centre, not top left
if (zoom) {
scale /= 2;
}
var projectedUnits = ((worldUnits / scale) / this.transformScale);
var realMetres = this.projectedToMetres(projectedUnits, pointScale);
// Not entirely sure why this is neccessary
if (zoom) {
realMetres *= pointScale[1];
}
return realMetres;
}
};
export default extend({}, CRS, Earth);

Wyświetl plik

@ -1,50 +0,0 @@
/*
* CRS.Proj4 for any Proj4-supported CRS.
*/
import extend from 'lodash.assign';
import Earth from './CRS.Earth';
import Proj4Projection from '../projection/Projection.Proj4';
import Transformation from '../../util/Transformation';
var _Proj4 = function(code, def, bounds) {
var projection = Proj4Projection(def, bounds);
// Transformation calcuations
var diffX = projection.bounds[1][0] - projection.bounds[0][0];
var diffY = projection.bounds[1][1] - projection.bounds[0][1];
var halfX = diffX / 2;
var halfY = diffY / 2;
// This is the raw scale factor
var scaleX = 1 / halfX;
var scaleY = 1 / halfY;
// Find the minimum scale factor
//
// The minimum scale factor comes from the largest side and is the one
// you want to use for both axis so they stay relative in dimension
var scale = Math.min(scaleX, scaleY);
// Find amount to offset each axis by to make the central point lie on
// the [0,0] origin
var offsetX = scale * (projection.bounds[0][0] + halfX);
var offsetY = scale * (projection.bounds[0][1] + halfY);
return {
code: code,
projection: projection,
transformScale: scale,
// Map the input to a [-1,1] range with [0,0] in the centre
transformation: new Transformation(scale, -offsetX, -scale, offsetY)
};
};
const Proj4 = function(code, def, bounds) {
return extend({}, Earth, _Proj4(code, def, bounds));
};
export default Proj4;

Wyświetl plik

@ -1,47 +0,0 @@
/*
* A simple CRS that can be used for flat non-Earth maps like panoramas or game
* maps.
*
* Based on:
* https://github.com/Leaflet/Leaflet/blob/master/src/geo/crs/CRS.Simple.js
*/
import extend from 'lodash.assign';
import CRS from './CRS';
import LatLonProjection from '../projection/Projection.LatLon';
import Transformation from '../../util/Transformation';
var _Simple = {
projection: LatLonProjection,
// Straight 1:1 mapping (-1, -1 would be top-left)
transformation: new Transformation(1, 0, 1, 0),
scale: function(zoom) {
// If zoom is provided then return scale based on map tile zoom
if (zoom) {
return Math.pow(2, zoom);
// Else, make no change to scale – may need to increase this or make it a
// user-definable variable
} else {
return 1;
}
},
zoom: function(scale) {
return Math.log(scale) / Math.LN2;
},
distance: function(latlon1, latlon2) {
var dx = latlon2.lon - latlon1.lon;
var dy = latlon2.lat - latlon1.lat;
return Math.sqrt(dx * dx + dy * dy);
},
infinite: true
};
const Simple = extend({}, CRS, _Simple);
export default Simple;

Wyświetl plik

@ -1,120 +0,0 @@
/*
* CRS is the base object for all defined CRS (Coordinate Reference Systems)
*
* Based on:
* https://github.com/Leaflet/Leaflet/blob/master/src/geo/crs/CRS.js
*/
import {latLon as LatLon} from '../LatLon';
import {point as Point} from '../Point';
import wrapNum from '../../util/wrapNum';
const CRS = {
// Scale factor determines final dimensions of world space
//
// Projection transformation in range -1 to 1 is multiplied by scale factor to
// find final world coordinates
//
// Scale factor can be considered as half the amount of the desired dimension
// for the largest side when transformation is equal to 1 or -1, or as the
// distance between 0 and 1 on the largest side
//
// For example, if you want the world dimensions to be between -1000 and 1000
// then the scale factor will be 1000
scaleFactor: 1000000,
// Converts geo coords to pixel / WebGL ones
latLonToPoint: function(latlon, zoom) {
var projectedPoint = this.projection.project(latlon);
var scale = this.scale(zoom);
// Half scale if using zoom as WebGL origin is in the centre, not top left
if (zoom) {
scale /= 2;
}
return this.transformation._transform(projectedPoint, scale);
},
// Converts pixel / WebGL coords to geo coords
pointToLatLon: function(point, zoom) {
var scale = this.scale(zoom);
// Half scale if using zoom as WebGL origin is in the centre, not top left
if (zoom) {
scale /= 2;
}
var untransformedPoint = this.transformation.untransform(point, scale);
return this.projection.unproject(untransformedPoint);
},
// Converts geo coords to projection-specific coords (e.g. in meters)
project: function(latlon) {
return this.projection.project(latlon);
},
// Converts projected coords to geo coords
unproject: function(point) {
return this.projection.unproject(point);
},
// If zoom is provided, returns the map width in pixels for a given zoom
// Else, provides fixed scale value
scale: function(zoom) {
// If zoom is provided then return scale based on map tile zoom
if (zoom >= 0) {
return 256 * Math.pow(2, zoom);
// Else, return fixed scale value to expand projected coordinates from
// their 0 to 1 range into something more practical
} else {
return this.scaleFactor;
}
},
// Returns zoom level for a given scale value
// This only works with a scale value that is based on map pixel width
zoom: function(scale) {
return Math.log(scale / 256) / Math.LN2;
},
// Returns the bounds of the world in projected coords if applicable
getProjectedBounds: function(zoom) {
if (this.infinite) { return null; }
var b = this.projection.bounds;
var s = this.scale(zoom);
// Half scale if using zoom as WebGL origin is in the centre, not top left
if (zoom) {
s /= 2;
}
// Bottom left
var min = this.transformation.transform(Point(b[0]), s);
// Top right
var max = this.transformation.transform(Point(b[1]), s);
return [min, max];
},
// Whether a coordinate axis wraps in a given range (e.g. longitude from -180 to 180); depends on CRS
// wrapLon: [min, max],
// wrapLat: [min, max],
// If true, the coordinate space will be unbounded (infinite in all directions)
// infinite: false,
// Wraps geo coords in certain ranges if applicable
wrapLatLon: function(latlon) {
var lat = this.wrapLat ? wrapNum(latlon.lat, this.wrapLat, true) : latlon.lat;
var lon = this.wrapLon ? wrapNum(latlon.lon, this.wrapLon, true) : latlon.lon;
var alt = latlon.alt;
return LatLon(lat, lon, alt);
}
};
export default CRS;

Wyświetl plik

@ -1,17 +0,0 @@
import EPSG3857 from './CRS.EPSG3857';
import {EPSG900913} from './CRS.EPSG3857';
import EPSG3395 from './CRS.EPSG3395';
import EPSG4326 from './CRS.EPSG4326';
import Simple from './CRS.Simple';
import Proj4 from './CRS.Proj4';
const CRS = {};
CRS.EPSG3857 = EPSG3857;
CRS.EPSG900913 = EPSG900913;
CRS.EPSG3395 = EPSG3395;
CRS.EPSG4326 = EPSG4326;
CRS.Simple = Simple;
CRS.Proj4 = Proj4;
export default CRS;

Wyświetl plik

@ -1,49 +0,0 @@
/*
* Simple equirectangular (Plate Carree) projection, used by CRS like EPSG:4326
* and Simple.
*
* Based on:
* https://github.com/Leaflet/Leaflet/blob/master/src/geo/projection/Projection.LonLat.js
*/
import {latLon as LatLon} from '../LatLon';
import {point as Point} from '../Point';
const ProjectionLatLon = {
project: function(latlon) {
return Point(latlon.lon, latlon.lat);
},
unproject: function(point) {
return LatLon(point.y, point.x);
},
// Scale factor for converting between real metres and degrees
//
// degrees = realMetres * pointScale
// realMetres = degrees / pointScale
//
// See: http://stackoverflow.com/questions/639695/how-to-convert-latitude-or-longitude-to-meters
// See: http://gis.stackexchange.com/questions/75528/length-of-a-degree-where-do-the-terms-in-this-formula-come-from
pointScale: function(latlon) {
var m1 = 111132.92;
var m2 = -559.82;
var m3 = 1.175;
var m4 = -0.0023;
var p1 = 111412.84;
var p2 = -93.5;
var p3 = 0.118;
var rad = Math.PI / 180;
var lat = latlon.lat * rad;
var latlen = m1 + m2 * Math.cos(2 * lat) + m3 * Math.cos(4 * lat) + m4 * Math.cos(6 * lat);
var lonlen = p1 * Math.cos(lat) + p2 * Math.cos(3 * lat) + p3 * Math.cos(5 * lat);
return [1 / latlen, 1 / lonlen];
},
bounds: [[-180, -90], [180, 90]]
};
export default ProjectionLatLon;

Wyświetl plik

@ -1,76 +0,0 @@
/*
* Mercator projection that takes into account that the Earth is not a perfect
* sphere. Less popular than spherical mercator; used by projections like
* EPSG:3395.
*
* Based on:
* https://github.com/Leaflet/Leaflet/blob/master/src/geo/projection/Projection.Mercator.js
*/
import {latLon as LatLon} from '../LatLon';
import {point as Point} from '../Point';
const Mercator = {
// Radius / WGS84 semi-major axis
R: 6378137,
R_MINOR: 6356752.314245179,
// WGS84 eccentricity
ECC: 0.081819191,
ECC2: 0.081819191 * 0.081819191,
project: function(latlon) {
var d = Math.PI / 180;
var r = this.R;
var y = latlon.lat * d;
var tmp = this.R_MINOR / r;
var e = Math.sqrt(1 - tmp * tmp);
var con = e * Math.sin(y);
var ts = Math.tan(Math.PI / 4 - y / 2) / Math.pow((1 - con) / (1 + con), e / 2);
y = -r * Math.log(Math.max(ts, 1E-10));
return Point(latlon.lon * d * r, y);
},
unproject: function(point) {
var d = 180 / Math.PI;
var r = this.R;
var tmp = this.R_MINOR / r;
var e = Math.sqrt(1 - tmp * tmp);
var ts = Math.exp(-point.y / r);
var phi = Math.PI / 2 - 2 * Math.atan(ts);
for (var i = 0, dphi = 0.1, con; i < 15 && Math.abs(dphi) > 1e-7; i++) {
con = e * Math.sin(phi);
con = Math.pow((1 - con) / (1 + con), e / 2);
dphi = Math.PI / 2 - 2 * Math.atan(ts * con) - phi;
phi += dphi;
}
return LatLon(phi * d, point.x * d / r);
},
// Scale factor for converting between real metres and projected metres
//
// projectedMetres = realMetres * pointScale
// realMetres = projectedMetres / pointScale
//
// See pg.8: http://www.hydrometronics.com/downloads/Web%20Mercator%20-%20Non-Conformal,%20Non-Mercator%20(notes).pdf
pointScale: function(latlon) {
var rad = Math.PI / 180;
var lat = latlon.lat * rad;
var sinLat = Math.sin(lat);
var sinLat2 = sinLat * sinLat;
var cosLat = Math.cos(lat);
var k = Math.sqrt(1 - this.ECC2 * sinLat2) / cosLat;
// [scaleX, scaleY]
return [k, k];
},
bounds: [[-20037508.34279, -15496570.73972], [20037508.34279, 18764656.23138]]
};
export default Mercator;

Wyświetl plik

@ -1,56 +0,0 @@
/*
* Proj4 support for any projection.
*/
import proj4 from 'proj4';
import {latLon as LatLon} from '../LatLon';
import {point as Point} from '../Point';
const Proj4 = function(def, bounds) {
var proj = proj4(def);
var project = function(latlon) {
return Point(proj.forward([latlon.lon, latlon.lat]));
};
var unproject = function(point) {
var inverse = proj.inverse([point.x, point.y]);
return LatLon(inverse[1], inverse[0]);
};
return {
project: project,
unproject: unproject,
// Scale factor for converting between real metres and projected metres\
//
// Need to work out the best way to provide the pointScale calculations
// for custom, unknown projections (if wanting to override default)
//
// For now, user can manually override crs.pointScale or
// crs.projection.pointScale
//
// projectedMetres = realMetres * pointScale
// realMetres = projectedMetres / pointScale
pointScale: function(latlon, accurate) {
return [1, 1];
},
// Try and calculate bounds if none are provided
//
// This will provide incorrect bounds for some projections, so perhaps make
// bounds a required input instead
bounds: (function() {
if (bounds) {
return bounds;
} else {
var bottomLeft = project([-90, -180]);
var topRight = project([90, 180]);
return [bottomLeft, topRight];
}
})()
};
};
export default Proj4;

Wyświetl plik

@ -1,95 +0,0 @@
/*
* Spherical Mercator is the most popular map projection, used by EPSG:3857 CRS
* used by default.
*
* Based on:
* https://github.com/Leaflet/Leaflet/blob/master/src/geo/projection/Projection.SphericalMercator.js
*/
import {latLon as LatLon} from '../LatLon';
import {point as Point} from '../Point';
const SphericalMercator = {
// Radius / WGS84 semi-major axis
R: 6378137,
MAX_LATITUDE: 85.0511287798,
// WGS84 eccentricity
ECC: 0.081819191,
ECC2: 0.081819191 * 0.081819191,
project: function(latlon) {
var d = Math.PI / 180;
var max = this.MAX_LATITUDE;
var lat = Math.max(Math.min(max, latlon.lat), -max);
var sin = Math.sin(lat * d);
return Point(
this.R * latlon.lon * d,
this.R * Math.log((1 + sin) / (1 - sin)) / 2
);
},
unproject: function(point) {
var d = 180 / Math.PI;
return LatLon(
(2 * Math.atan(Math.exp(point.y / this.R)) - (Math.PI / 2)) * d,
point.x * d / this.R
);
},
// Scale factor for converting between real metres and projected metres
//
// projectedMetres = realMetres * pointScale
// realMetres = projectedMetres / pointScale
//
// Accurate scale factor uses proper Web Mercator scaling
// See pg.9: http://www.hydrometronics.com/downloads/Web%20Mercator%20-%20Non-Conformal,%20Non-Mercator%20(notes).pdf
// See: http://jsfiddle.net/robhawkes/yws924cf/
pointScale: function(latlon, accurate) {
var rad = Math.PI / 180;
var k;
if (!accurate) {
k = 1 / Math.cos(latlon.lat * rad);
// [scaleX, scaleY]
return [k, k];
} else {
var lat = latlon.lat * rad;
var lon = latlon.lon * rad;
var a = this.R;
var sinLat = Math.sin(lat);
var sinLat2 = sinLat * sinLat;
var cosLat = Math.cos(lat);
// Radius meridian
var p = a * (1 - this.ECC2) / Math.pow(1 - this.ECC2 * sinLat2, 3 / 2);
// Radius prime meridian
var v = a / Math.sqrt(1 - this.ECC2 * sinLat2);
// Scale N/S
var h = (a / p) / cosLat;
// Scale E/W
k = (a / v) / cosLat;
// [scaleX, scaleY]
return [k, h];
}
},
// Not using this.R due to scoping
bounds: (function() {
var d = 6378137 * Math.PI;
return [[-d, -d], [d, d]];
})()
};
export default SphericalMercator;

Wyświetl plik

@ -1,13 +0,0 @@
import LatLon from './Projection.LatLon';
import SphericalMercator from './Projection.SphericalMercator';
import Mercator from './Projection.Mercator';
import Proj4 from './Projection.Proj4';
const Projection = {};
Projection.LatLon = LatLon;
Projection.SphericalMercator = SphericalMercator;
Projection.Mercator = Mercator;
Projection.Proj4 = Proj4;
export default Projection;

Wyświetl plik

@ -1,42 +0,0 @@
/*
* Transformation is an utility class to perform simple point transformations
* through a 2d-matrix.
*
* Based on:
* https://github.com/Leaflet/Leaflet/blob/master/src/geometry/Transformation.js
*/
import {point as Point} from '../geo/Point';
class Transformation {
constructor(a, b, c, d) {
this._a = a;
this._b = b;
this._c = c;
this._d = d;
}
transform(point, scale) {
// Copy input point as to not destroy the original data
return this._transform(point.clone(), scale);
}
// Destructive transform (faster)
_transform(point, scale) {
scale = scale || 1;
point.x = scale * (this._a * point.x + this._b);
point.y = scale * (this._c * point.y + this._d);
return point;
}
untransform(point, scale) {
scale = scale || 1;
return Point(
(point.x / scale - this._b) / this._a,
(point.y / scale - this._d) / this._c
);
}
}
export default Transformation;

Wyświetl plik

@ -1,6 +1,5 @@
// TODO: A lot of these utils don't need to be in separate, tiny files
import Transformation from './Transformation';
import wrapNum from './wrapNum';
import extrudePolygon from './extrudePolygon';
import GeoJSON from './GeoJSON';
@ -8,7 +7,6 @@ import Buffer from './Buffer';
const Util = {};
Util.Transformation = Transformation;
Util.wrapNum = wrapNum;
Util.extrudePolygon = extrudePolygon;
Util.GeoJSON = GeoJSON;

Wyświetl plik

@ -1,634 +0,0 @@
import extend from 'lodash.assign';
import CRS from '../../src/geo/crs/index';
import {latLon as LatLon} from '../../src/geo/LatLon';
import {point as Point} from '../../src/geo/Point';
describe('CRS.EPSG3857', () => {
var crs = CRS.EPSG3857;
describe('#code', () => {
it('returns the correct code', () => {
expect(crs.code).to.equal('EPSG:3857');
});
});
describe('#latLonToPoint', () => {
it('projects the center', () => {
var point = crs.latLonToPoint(LatLon(0, 0));
expect(point.x).to.be.closeTo(0, 0.01);
expect(point.y).to.be.closeTo(0, 0.01);
});
it('projects the North-West corner', () => {
var scaleFactor = crs.scaleFactor;
var point = crs.latLonToPoint(LatLon(85.0511287798, -180));
expect(point.x).to.be.closeTo(-scaleFactor, 0.01);
expect(point.y).to.be.closeTo(-scaleFactor, 0.01);
});
it('projects the South-East corner', () => {
var scaleFactor = crs.scaleFactor;
var point = crs.latLonToPoint(LatLon(-85.0511287798, 180));
expect(point.x).to.be.closeTo(scaleFactor, 0.01);
expect(point.x).to.be.closeTo(scaleFactor, 0.01);
});
it('projects using pixels if zoom is given', () => {
var point = crs.latLonToPoint(LatLon(-85.0511287798, 180), 1);
expect(point.x).to.be.closeTo(256, 0.01);
expect(point.y).to.be.closeTo(256, 0.01);
});
});
describe('#pointToLatLon', () => {
it('unprojects the center', () => {
var latlon = crs.pointToLatLon(Point(0, 0));
expect(latlon.lat).to.be.closeTo(0, 0.01);
expect(latlon.lon).to.be.closeTo(0, 0.01);
});
it('unprojects the North-West corner', () => {
var scaleFactor = crs.scaleFactor;
var latlon = crs.pointToLatLon(Point(-scaleFactor, -scaleFactor));
expect(latlon.lat).to.be.closeTo(85.0511287798, 0.01);
expect(latlon.lon).to.be.closeTo(-180, 0.01);
});
it('unprojects the South-East corner', () => {
var scaleFactor = crs.scaleFactor;
var latlon = crs.pointToLatLon(Point(scaleFactor, scaleFactor));
expect(latlon.lat).to.be.closeTo(-85.0511287798, 0.01);
expect(latlon.lon).to.be.closeTo(180, 0.01);
});
it('unprojects using pixels if zoom is given', () => {
var latlon = crs.pointToLatLon(Point(256, 256), 1);
expect(latlon.lat).to.be.closeTo(-85.0511287798, 0.01);
expect(latlon.lon).to.be.closeTo(180, 0.01);
latlon = crs.pointToLatLon(Point(0, 0), 1);
expect(latlon.lat).to.be.closeTo(0, 0.01);
expect(latlon.lon).to.be.closeTo(0, 0.01);
});
});
describe('#project', () => {
it('projects the center', () => {
var point = crs.project(LatLon(0, 0));
expect(point.x).to.be.closeTo(0, 0.01);
expect(point.y).to.be.closeTo(0, 0.01);
});
it('projects the North-West corner', () => {
var point = crs.project(LatLon(85.0511287798, -180));
expect(point.x).to.be.closeTo(-20037508.34279, 0.01);
expect(point.y).to.be.closeTo(20037508.34278, 0.01);
});
it('projects the South-East corner', () => {
var point = crs.project(LatLon(-85.0511287798, 180));
expect(point.x).to.be.closeTo(20037508.34278, 0.01);
expect(point.y).to.be.closeTo(-20037508.34278, 0.01);
});
it('caps the maximum latitude', () => {
var point = crs.project(LatLon(-90, 180));
expect(point.x).to.be.closeTo(20037508.34278, 0.01);
expect(point.y).to.be.closeTo(-20037508.34278, 0.01);
});
});
describe('#unproject', () => {
it('unprojects the center', () => {
var latlon = crs.unproject(Point(0, 0));
expect(latlon.lat).to.be.closeTo(0, 0.01);
expect(latlon.lon).to.be.closeTo(0, 0.01);
});
it('unprojects the North-West corner', () => {
var latlon = crs.unproject(Point(-20037508.34278, 20037508.34278));
expect(latlon.lat).to.be.closeTo(85.0511287798, 0.01);
expect(latlon.lon).to.be.closeTo(-180, 0.01);
});
it('unprojects the South-East corner', () => {
var latlon = crs.unproject(Point(20037508.34278, -20037508.34278));
expect(latlon.lat).to.be.closeTo(-85.0511287798, 0.01);
expect(latlon.lon).to.be.closeTo(180, 0.01);
});
});
describe('#scale', () => {
it('uses the scale factor by default', () => {
var scale = crs.scale();
expect(scale).to.equal(crs.scaleFactor);
});
it('uses the zoom level if provided', () => {
var scale = crs.scale(1);
expect(scale).to.equal(512);
});
});
describe('#zoom', () => {
it('returns zoom level for given scale', () => {
var scale = 512;
var zoom = crs.zoom(scale);
expect(zoom).to.equal(1);
});
});
describe('#getProjectedBounds', () => {
it('returns correct bounds without zoom', () => {
var scaleFactor2 = crs.scaleFactor * 2;
var bounds = crs.getProjectedBounds();
expect(Math.abs(bounds[1].x - bounds[0].x)).to.be.closeTo(scaleFactor2, 0.1);
expect(Math.abs(bounds[1].y - bounds[0].y)).to.be.closeTo(scaleFactor2, 0.1);
});
it('returns correct bounds with zoom', () => {
var bounds = crs.getProjectedBounds(1);
expect(Math.abs(bounds[1].x - bounds[0].x)).to.be.closeTo(512, 0.1);
expect(Math.abs(bounds[1].y - bounds[0].y)).to.be.closeTo(512, 0.1);
});
});
describe('#wrapLatLon', () => {
it('wraps longitude between -180 and 180 by default', () => {
expect(crs.wrapLatLon(LatLon(0, 190)).lon).to.equal(-170);
expect(crs.wrapLatLon(LatLon(0, 360)).lon).to.equal(0);
expect(crs.wrapLatLon(LatLon(0, -190)).lon).to.equal(170);
expect(crs.wrapLatLon(LatLon(0, -360)).lon).to.equal(0);
expect(crs.wrapLatLon(LatLon(0, 0)).lon).to.equal(0);
expect(crs.wrapLatLon(LatLon(0, 180)).lon).to.equal(180);
});
it('keeps altitude value', () => {
expect(crs.wrapLatLon(LatLon(0, 190, 100)).lon).to.equal(-170);
expect(crs.wrapLatLon(LatLon(0, 190, 100)).alt).to.equal(100);
});
});
describe('#distance', () => {
it('returns correct distance using cosine law approximation', () => {
expect(crs.distance(LatLon(0, 0), LatLon(0.001, 0))).to.be.closeTo(111.31949492321543, 0.1);
});
it('returns correct distance using Haversine', () => {
expect(crs.distance(LatLon(0, 0), LatLon(0.001, 0), true)).to.be.closeTo(111.3194907932736, 0.1);
});
});
describe('#pointScale', () => {
var pointScale;
it('returns approximate point scale factor', () => {
pointScale = crs.pointScale(LatLon(0, 0));
expect(pointScale[0]).to.be.closeTo(1, 0.1);
expect(pointScale[1]).to.be.closeTo(1, 0.1);
pointScale = crs.pointScale(LatLon(60, 0));
expect(pointScale[0]).to.be.closeTo(1.9999999999999996, 0.1);
expect(pointScale[1]).to.be.closeTo(1.9999999999999996, 0.1);
});
it('returns accurate point scale factor', () => {
pointScale = crs.pointScale(LatLon(0, 0), true);
expect(pointScale[0]).to.be.closeTo(1, 0.1);
expect(pointScale[1]).to.be.closeTo(1.0067394967683778, 0.1);
pointScale = crs.pointScale(LatLon(60, 0), true);
expect(pointScale[0]).to.be.closeTo(1.994972897047054, 0.1);
expect(pointScale[1]).to.be.closeTo(1.9983341753952164, 0.1);
});
});
describe('#metresToProjected', () => {
var pointScale;
it('returns correct projected units', () => {
pointScale = crs.pointScale(LatLon(0, 0));
expect(crs.metresToProjected(1, pointScale)).to.be.closeTo(1, 0.1);
pointScale = crs.pointScale(LatLon(60, 0));
expect(crs.metresToProjected(1, pointScale)).to.be.closeTo(1.9999999999999996, 0.1);
});
});
describe('#projectedToMetres', () => {
var pointScale;
it('returns correct real metres', () => {
pointScale = crs.pointScale(LatLon(0, 0));
expect(crs.projectedToMetres(1, pointScale)).to.be.closeTo(1, 0.1);
pointScale = crs.pointScale(LatLon(60, 0));
expect(crs.projectedToMetres(1.9999999999999996, pointScale)).to.be.closeTo(1, 0.1);
});
});
// These two are combined as it is hard to write an invidual test that can
// be specified without knowing or redifining crs.scaleFactor
describe('#metresToWorld & #worldToMetres', () => {
var pointScale;
var worldUnits;
var metres;
it('returns correct world units', () => {
pointScale = crs.pointScale(LatLon(0, 0));
worldUnits = crs.metresToWorld(1, pointScale);
metres = crs.worldToMetres(worldUnits, pointScale);
expect(metres).to.be.closeTo(1, 0.1);
pointScale = crs.pointScale(LatLon(60, 0));
worldUnits = crs.metresToWorld(1, pointScale);
metres = crs.worldToMetres(worldUnits, pointScale);
expect(metres).to.be.closeTo(1, 0.1);
});
it('returns correct world units using zoom', () => {
pointScale = crs.pointScale(LatLon(0, 0));
worldUnits = crs.metresToWorld(40075016.68556, pointScale, 1);
expect(worldUnits).to.be.closeTo(512, 0.1);
pointScale = crs.pointScale(LatLon(60, 0));
worldUnits = crs.metresToWorld(40075016.68556, pointScale, 1);
expect(worldUnits).to.be.closeTo(512, 0.1);
});
it('returns correct metres using zoom', () => {
pointScale = crs.pointScale(LatLon(0, 0));
worldUnits = crs.worldToMetres(512, pointScale, 1);
expect(worldUnits).to.be.closeTo(40075016.68556, 0.1);
pointScale = crs.pointScale(LatLon(60, 0));
worldUnits = crs.worldToMetres(512, pointScale, 1);
expect(worldUnits).to.be.closeTo(40075016.68556, 0.1);
});
});
});
// Only test for code as the rest is identical to CRS.EPSG3857
describe('CRS.EPSG900913', () => {
var crs = CRS.EPSG900913;
describe('#code', () => {
it('returns the correct code', () => {
expect(crs.code).to.equal('EPSG:900913');
});
});
});
// Basic tests for projection-specific stuff as the rest is likely ok given
// correct projected / unprojected values
describe('CRS.3395', () => {
var crs = CRS.EPSG3395;
describe('#code', () => {
it('returns the correct code', () => {
expect(crs.code).to.equal('EPSG:3395');
});
});
describe('#latLonToPoint', () => {
it('projects the center', () => {
var point = crs.latLonToPoint(LatLon(0, 0));
expect(point.x).to.be.closeTo(0, 0.01);
expect(point.y).to.be.closeTo(0, 0.01);
});
it('projects the North-West corner', () => {
var scaleFactor = crs.scaleFactor;
var point = crs.latLonToPoint(LatLon(85.0840591556, -180));
expect(point.x).to.be.closeTo(-scaleFactor, 0.01);
expect(point.y).to.be.closeTo(-scaleFactor, 0.01);
});
it('projects the South-East corner', () => {
var scaleFactor = crs.scaleFactor;
var point = crs.latLonToPoint(LatLon(-85.0840591556, 180));
expect(point.x).to.be.closeTo(scaleFactor, 0.01);
expect(point.y).to.be.closeTo(scaleFactor, 0.01);
});
it('projects using pixels if zoom is given', () => {
var point = crs.latLonToPoint(LatLon(-85.0840591556, 180), 1);
expect(point.x).to.be.closeTo(256, 0.01);
expect(point.y).to.be.closeTo(256, 0.01);
});
});
describe('#pointToLatLon', () => {
it('unprojects the center', () => {
var latlon = crs.pointToLatLon(Point(0, 0));
expect(latlon.lat).to.be.closeTo(0, 0.01);
expect(latlon.lon).to.be.closeTo(0, 0.01);
});
it('unprojects the North-West corner', () => {
var scaleFactor = crs.scaleFactor;
var latlon = crs.pointToLatLon(Point(-scaleFactor, -scaleFactor));
expect(latlon.lat).to.be.closeTo(85.0840591556, 0.01);
expect(latlon.lon).to.be.closeTo(-180, 0.01);
});
it('unprojects the South-East corner', () => {
var scaleFactor = crs.scaleFactor;
var latlon = crs.pointToLatLon(Point(scaleFactor, scaleFactor));
expect(latlon.lat).to.be.closeTo(-85.0840591556, 0.01);
expect(latlon.lon).to.be.closeTo(180, 0.01);
});
it('unprojects using pixels if zoom is given', () => {
var latlon = crs.pointToLatLon(Point(256, 256), 1);
expect(latlon.lat).to.be.closeTo(-85.0840591556, 0.01);
expect(latlon.lon).to.be.closeTo(180, 0.01);
latlon = crs.pointToLatLon(Point(0, 0), 1);
expect(latlon.lat).to.be.closeTo(0, 0.01);
expect(latlon.lon).to.be.closeTo(0, 0.01);
});
});
});
// Basic tests for projection-specific stuff as the rest is likely ok given
// correct projected / unprojected values
describe('CRS.4326', () => {
var crs = CRS.EPSG4326;
describe('#code', () => {
it('returns the correct code', () => {
expect(crs.code).to.equal('EPSG:4326');
});
});
describe('#latLonToPoint', () => {
it('projects the center', () => {
var point = crs.latLonToPoint(LatLon(0, 0));
expect(point.x).to.be.closeTo(0, 0.01);
expect(point.y).to.be.closeTo(0, 0.01);
});
it('projects the North-West corner', () => {
var scaleFactor = crs.scaleFactor;
var point = crs.latLonToPoint(LatLon(90, -180));
expect(point.x).to.be.closeTo(-scaleFactor, 0.01);
// Half scale factor as projection is not square
expect(point.y).to.be.closeTo(-scaleFactor / 2, 0.01);
});
it('projects the South-East corner', () => {
var scaleFactor = crs.scaleFactor;
var point = crs.latLonToPoint(LatLon(-90, 180));
expect(point.x).to.be.closeTo(scaleFactor, 0.01);
// Half scale factor as projection is not square
expect(point.y).to.be.closeTo(scaleFactor / 2, 0.01);
});
it('projects using pixels if zoom is given', () => {
var point = crs.latLonToPoint(LatLon(-90, 180), 1);
expect(point.x).to.be.closeTo(256, 0.01);
// Half width as projection is not square
expect(point.y).to.be.closeTo(128, 0.01);
});
});
describe('#pointToLatLon', () => {
it('unprojects the center', () => {
var latlon = crs.pointToLatLon(Point(0, 0));
expect(latlon.lat).to.be.closeTo(0, 0.01);
expect(latlon.lon).to.be.closeTo(0, 0.01);
});
it('unprojects the North-West corner', () => {
var scaleFactor = crs.scaleFactor;
var latlon = crs.pointToLatLon(Point(-scaleFactor, -scaleFactor / 2));
expect(latlon.lat).to.be.closeTo(90, 0.01);
expect(latlon.lon).to.be.closeTo(-180, 0.01);
});
it('unprojects the South-East corner', () => {
var scaleFactor = crs.scaleFactor;
var latlon = crs.pointToLatLon(Point(scaleFactor, scaleFactor / 2));
expect(latlon.lat).to.be.closeTo(-90, 0.01);
expect(latlon.lon).to.be.closeTo(180, 0.01);
});
it('unprojects using pixels if zoom is given', () => {
var latlon = crs.pointToLatLon(Point(256, 128), 1);
expect(latlon.lat).to.be.closeTo(-90, 0.01);
expect(latlon.lon).to.be.closeTo(180, 0.01);
latlon = crs.pointToLatLon(Point(0, 0), 1);
expect(latlon.lat).to.be.closeTo(0, 0.01);
expect(latlon.lon).to.be.closeTo(0, 0.01);
});
});
});
// Basic tests for projection-specific stuff as the rest is likely ok given
// correct projected / unprojected values
describe('CRS.Simple', () => {
var crs = CRS.Simple;
describe('#latLonToPoint', () => {
it('projects the center', () => {
var point = crs.latLonToPoint(LatLon(0, 0));
expect(point.x).to.be.closeTo(0, 0.01);
expect(point.y).to.be.closeTo(0, 0.01);
});
it('projects the North-West corner', () => {
var scaleFactor = crs.scaleFactor;
var point = crs.latLonToPoint(LatLon(-500, -500));
expect(point.x).to.be.closeTo(-500, 0.01);
expect(point.y).to.be.closeTo(-500, 0.01);
});
it('projects the South-East corner', () => {
var scaleFactor = crs.scaleFactor;
var point = crs.latLonToPoint(LatLon(500, 500));
expect(point.x).to.be.closeTo(500, 0.01);
expect(point.y).to.be.closeTo(500, 0.01);
});
});
describe('#pointToLatLon', () => {
it('unprojects the center', () => {
var latlon = crs.pointToLatLon(Point(0, 0));
expect(latlon.lat).to.be.closeTo(0, 0.01);
expect(latlon.lon).to.be.closeTo(0, 0.01);
});
it('unprojects the North-West corner', () => {
var latlon = crs.pointToLatLon(Point(-500, -500));
expect(latlon.lat).to.be.closeTo(-500, 0.01);
expect(latlon.lon).to.be.closeTo(-500, 0.01);
});
it('unprojects the South-East corner', () => {
var latlon = crs.pointToLatLon(Point(500, 500));
expect(latlon.lat).to.be.closeTo(500, 0.01);
expect(latlon.lon).to.be.closeTo(500, 0.01);
});
});
describe('#wrapLatLon', () => {
it('no wrapping by default', () => {
expect(crs.wrapLatLon(LatLon(0, 190)).lon).to.equal(190);
expect(crs.wrapLatLon(LatLon(0, 360)).lon).to.equal(360);
expect(crs.wrapLatLon(LatLon(0, -190)).lon).to.equal(-190);
expect(crs.wrapLatLon(LatLon(0, -360)).lon).to.equal(-360);
expect(crs.wrapLatLon(LatLon(0, 0)).lon).to.equal(0);
expect(crs.wrapLatLon(LatLon(0, 180)).lon).to.equal(180);
});
it('wraps if defined', () => {
crs = extend({}, CRS.Simple, {
wrapLat: [-90, 90],
wrapLon: [-180, 180],
});
expect(crs.wrapLatLon(LatLon(0, 190)).lon).to.equal(-170);
expect(crs.wrapLatLon(LatLon(0, 360)).lon).to.equal(0);
expect(crs.wrapLatLon(LatLon(100, 0)).lat).to.equal(-80);
expect(crs.wrapLatLon(LatLon(180, 0)).lat).to.equal(0);
});
});
describe('#distance', () => {
it('returns correct distance using trigonometry', () => {
expect(crs.distance(LatLon(0, 0), LatLon(100, 0))).to.be.closeTo(100, 0.1);
expect(crs.distance(LatLon(0, 0), LatLon(0, 100))).to.be.closeTo(100, 0.1);
expect(crs.distance(LatLon(0, 0), LatLon(100, 100))).to.be.closeTo(141.4213562373095, 0.1);
});
});
});
// Basic tests for projection-specific stuff as the rest is likely ok given
// correct projected / unprojected values
describe('CRS.Proj4', () => {
// British National Grid
var crs = CRS.Proj4('EPSG:27700', '+proj=tmerc +lat_0=49 +lon_0=-2 +k=0.9996012717 +x_0=400000 +y_0=-100000 +ellps=airy +towgs84=446.448,-125.157,542.06,0.15,0.247,0.842,-20.489 +units=m +no_defs', [[-84667.14, 11795.97], [608366.68, 1230247.30]]);
describe('#code', () => {
it('returns the correct code', () => {
expect(crs.code).to.equal('EPSG:27700');
});
});
describe('#latLonToPoint', () => {
it('projects the center', () => {
var point = crs.latLonToPoint(LatLon(55.46347028885475, -4.186594751907175));
expect(point.x).to.be.closeTo(0, 0.01);
expect(point.y).to.be.closeTo(0, 0.01);
});
it('projects the North-West corner', () => {
var scaleFactor = crs.scaleFactor;
var point = crs.latLonToPoint(LatLon(60.6596573635804, -10.8865578798635));
// Only test y-axis as we know this is the largest size for 27700
expect(point.y).to.be.closeTo(-scaleFactor, 0.01);
});
it('projects the South-East corner', () => {
var scaleFactor = crs.scaleFactor;
var point = crs.latLonToPoint(LatLon(49.9699671340061, 0.904406143400599));
// Only test y-axis as we know this is the largest size for 27700
expect(point.y).to.be.closeTo(scaleFactor, 0.01);
});
});
describe('#pointToLatLon', () => {
it('unprojects the center', () => {
var latlon = crs.pointToLatLon(Point(0, 0));
expect(latlon.lat).to.be.closeTo(55.46347028885475, 0.1);
expect(latlon.lon).to.be.closeTo(-4.186594751907175, 0.1);
});
it('unprojects the North edge', () => {
var scaleFactor = crs.scaleFactor;
// Only test y-axis as we know this is the largest size for 27700
//
// Increased delta due to inaccuracy so far from 27700 origin
var latlon = crs.pointToLatLon(Point(0, -scaleFactor));
expect(latlon.lat).to.be.closeTo(60.6596573635804, 1);
});
it('unprojects the South edge', () => {
var scaleFactor = crs.scaleFactor;
// Only test y-axis as we know this is the largest size for 27700
//
// Increased delta due to inaccuracy so far from 27700 origin
var latlon = crs.pointToLatLon(Point(0, scaleFactor));
expect(latlon.lat).to.be.closeTo(49.9699671340061, 1);
});
});
});

232
test/unit/Geo.js 100644
Wyświetl plik

@ -0,0 +1,232 @@
import extend from 'lodash.assign';
import Geo from '../../src/geo/Geo';
import {latLon as LatLon} from '../../src/geo/LatLon';
import {point as Point} from '../../src/geo/Point';
describe('Geo', () => {
describe('#latLonToPoint', () => {
it('projects the center', () => {
var point = Geo.latLonToPoint(LatLon(0, 0));
expect(point.x).to.be.closeTo(0, 0.01);
expect(point.y).to.be.closeTo(0, 0.01);
});
it('projects the North-West corner', () => {
var bounds = Geo.bounds;
var point = Geo.latLonToPoint(LatLon(85.0511287798, -180));
expect(point.x).to.be.closeTo(bounds[0][0], 0.01);
expect(point.y).to.be.closeTo(bounds[0][1], 0.01);
});
it('projects the South-East corner', () => {
var bounds = Geo.bounds;
var point = Geo.latLonToPoint(LatLon(-85.0511287798, 180));
expect(point.x).to.be.closeTo(bounds[1][0], 0.01);
expect(point.x).to.be.closeTo(bounds[1][1], 0.01);
});
});
describe('#pointToLatLon', () => {
it('unprojects the center', () => {
var latlon = Geo.pointToLatLon(Point(0, 0));
expect(latlon.lat).to.be.closeTo(0, 0.01);
expect(latlon.lon).to.be.closeTo(0, 0.01);
});
it('unprojects the North-West corner', () => {
var bounds = Geo.bounds;
var latlon = Geo.pointToLatLon(Point(bounds[0][0], bounds[0][1]));
expect(latlon.lat).to.be.closeTo(85.0511287798, 0.01);
expect(latlon.lon).to.be.closeTo(-180, 0.01);
});
it('unprojects the South-East corner', () => {
var bounds = Geo.bounds;
var latlon = Geo.pointToLatLon(Point(bounds[1][0], bounds[1][1]));
expect(latlon.lat).to.be.closeTo(-85.0511287798, 0.01);
expect(latlon.lon).to.be.closeTo(180, 0.01);
});
});
describe('#project', () => {
it('projects the center', () => {
var point = Geo.project(LatLon(0, 0));
expect(point.x).to.be.closeTo(0, 0.01);
expect(point.y).to.be.closeTo(0, 0.01);
});
it('projects the North-West corner', () => {
var point = Geo.project(LatLon(85.0511287798, -180));
expect(point.x).to.be.closeTo(-20037508.34279, 0.01);
expect(point.y).to.be.closeTo(20037508.34278, 0.01);
});
it('projects the South-East corner', () => {
var point = Geo.project(LatLon(-85.0511287798, 180));
expect(point.x).to.be.closeTo(20037508.34278, 0.01);
expect(point.y).to.be.closeTo(-20037508.34278, 0.01);
});
it('caps the maximum latitude', () => {
var point = Geo.project(LatLon(-90, 180));
expect(point.x).to.be.closeTo(20037508.34278, 0.01);
expect(point.y).to.be.closeTo(-20037508.34278, 0.01);
});
});
describe('#unproject', () => {
it('unprojects the center', () => {
var latlon = Geo.unproject(Point(0, 0));
expect(latlon.lat).to.be.closeTo(0, 0.01);
expect(latlon.lon).to.be.closeTo(0, 0.01);
});
it('unprojects the North-West corner', () => {
var latlon = Geo.unproject(Point(-20037508.34278, 20037508.34278));
expect(latlon.lat).to.be.closeTo(85.0511287798, 0.01);
expect(latlon.lon).to.be.closeTo(-180, 0.01);
});
it('unprojects the South-East corner', () => {
var latlon = Geo.unproject(Point(20037508.34278, -20037508.34278));
expect(latlon.lat).to.be.closeTo(-85.0511287798, 0.01);
expect(latlon.lon).to.be.closeTo(180, 0.01);
});
});
describe('#scale', () => {
it('defaults to 1', () => {
var scale = Geo.scale();
expect(scale).to.equal(1);
});
it('uses the zoom level if provided', () => {
var scale = Geo.scale(1);
expect(scale).to.equal(512);
});
});
describe('#zoom', () => {
it('returns zoom level for given scale', () => {
var scale = 512;
var zoom = Geo.zoom(scale);
expect(zoom).to.equal(1);
});
});
// describe('#wrapLatLon', () => {
// it('wraps longitude between -180 and 180 by default', () => {
// expect(Geo.wrapLatLon(LatLon(0, 190)).lon).to.equal(-170);
// expect(Geo.wrapLatLon(LatLon(0, 360)).lon).to.equal(0);
//
// expect(Geo.wrapLatLon(LatLon(0, -190)).lon).to.equal(170);
// expect(Geo.wrapLatLon(LatLon(0, -360)).lon).to.equal(0);
//
// expect(Geo.wrapLatLon(LatLon(0, 0)).lon).to.equal(0);
// expect(Geo.wrapLatLon(LatLon(0, 180)).lon).to.equal(180);
// });
//
// it('keeps altitude value', () => {
// expect(Geo.wrapLatLon(LatLon(0, 190, 100)).lon).to.equal(-170);
// expect(Geo.wrapLatLon(LatLon(0, 190, 100)).alt).to.equal(100);
// });
// });
describe('#distance', () => {
it('returns correct distance using cosine law approximation', () => {
expect(Geo.distance(LatLon(0, 0), LatLon(0.001, 0))).to.be.closeTo(111.31949492321543, 0.1);
});
it('returns correct distance using Haversine', () => {
expect(Geo.distance(LatLon(0, 0), LatLon(0.001, 0), true)).to.be.closeTo(111.3194907932736, 0.1);
});
});
describe('#pointScale', () => {
var pointScale;
it('returns approximate point scale factor', () => {
pointScale = Geo.pointScale(LatLon(0, 0));
expect(pointScale[0]).to.be.closeTo(1, 0.1);
expect(pointScale[1]).to.be.closeTo(1, 0.1);
pointScale = Geo.pointScale(LatLon(60, 0));
expect(pointScale[0]).to.be.closeTo(1.9999999999999996, 0.1);
expect(pointScale[1]).to.be.closeTo(1.9999999999999996, 0.1);
});
it('returns accurate point scale factor', () => {
pointScale = Geo.pointScale(LatLon(0, 0), true);
expect(pointScale[0]).to.be.closeTo(1, 0.1);
expect(pointScale[1]).to.be.closeTo(1.0067394967683778, 0.1);
pointScale = Geo.pointScale(LatLon(60, 0), true);
expect(pointScale[0]).to.be.closeTo(1.994972897047054, 0.1);
expect(pointScale[1]).to.be.closeTo(1.9983341753952164, 0.1);
});
});
describe('#metresToProjected', () => {
var pointScale;
it('returns correct projected units', () => {
pointScale = Geo.pointScale(LatLon(0, 0));
expect(Geo.metresToProjected(1, pointScale)).to.be.closeTo(1, 0.1);
pointScale = Geo.pointScale(LatLon(60, 0));
expect(Geo.metresToProjected(1, pointScale)).to.be.closeTo(1.9999999999999996, 0.1);
});
});
describe('#projectedToMetres', () => {
var pointScale;
it('returns correct real metres', () => {
pointScale = Geo.pointScale(LatLon(0, 0));
expect(Geo.projectedToMetres(1, pointScale)).to.be.closeTo(1, 0.1);
pointScale = Geo.pointScale(LatLon(60, 0));
expect(Geo.projectedToMetres(1.9999999999999996, pointScale)).to.be.closeTo(1, 0.1);
});
});
// These two are combined as it is hard to write an invidual test that can
// be specified without knowing or redifining Geo.scaleFactor
describe('#metresToWorld & #worldToMetres', () => {
var pointScale;
var worldUnits;
var metres;
it('returns correct world units', () => {
pointScale = Geo.pointScale(LatLon(0, 0));
worldUnits = Geo.metresToWorld(1, pointScale);
metres = Geo.worldToMetres(worldUnits, pointScale);
expect(metres).to.be.closeTo(1, 0.1);
pointScale = Geo.pointScale(LatLon(60, 0));
worldUnits = Geo.metresToWorld(1, pointScale);
metres = Geo.worldToMetres(worldUnits, pointScale);
expect(metres).to.be.closeTo(1, 0.1);
});
});
});

Wyświetl plik

@ -1,327 +0,0 @@
import Projection from '../../src/geo/projection/index';
import {latLon as LatLon} from '../../src/geo/LatLon';
import {point as Point} from '../../src/geo/Point';
describe('Projection.SphericalMercator', () => {
var projection = Projection.SphericalMercator;
describe('#project', () => {
it('projects the center', () => {
var point = projection.project(LatLon(0, 0));
expect(point.x).to.be.closeTo(0, 0.01);
expect(point.y).to.be.closeTo(0, 0.01);
});
it('projects the North-West corner', () => {
var point = projection.project(LatLon(85.0511287798, -180));
expect(point.x).to.be.closeTo(-20037508.34279, 0.01);
expect(point.y).to.be.closeTo(20037508.34278, 0.01);
});
it('projects the South-East corner', () => {
var point = projection.project(LatLon(-85.0511287798, 180));
expect(point.x).to.be.closeTo(20037508.34278, 0.01);
expect(point.y).to.be.closeTo(-20037508.34278, 0.01);
});
it('caps the maximum latitude', () => {
var point = projection.project(LatLon(-90, 180));
expect(point.x).to.be.closeTo(20037508.34278, 0.01);
expect(point.y).to.be.closeTo(-20037508.34278, 0.01);
});
});
describe('#unproject', () => {
it('unprojects the center', () => {
var latlon = projection.unproject(Point(0, 0));
expect(latlon.lat).to.be.closeTo(0, 0.01);
expect(latlon.lon).to.be.closeTo(0, 0.01);
});
it('unprojects the North-West corner', () => {
var latlon = projection.unproject(Point(-20037508.34278, 20037508.34278));
expect(latlon.lat).to.be.closeTo(85.0511287798, 0.01);
expect(latlon.lon).to.be.closeTo(-180, 0.01);
});
it('unprojects the South-East corner', () => {
var latlon = projection.unproject(Point(20037508.34278, -20037508.34278));
expect(latlon.lat).to.be.closeTo(-85.0511287798, 0.01);
expect(latlon.lon).to.be.closeTo(180, 0.01);
});
});
describe('#pointScale', () => {
var pointScale;
it('returns approximate point scale factor', () => {
pointScale = projection.pointScale(LatLon(0, 0));
expect(pointScale[0]).to.be.closeTo(1, 0.1);
expect(pointScale[1]).to.be.closeTo(1, 0.1);
pointScale = projection.pointScale(LatLon(60, 0));
expect(pointScale[0]).to.be.closeTo(1.9999999999999996, 0.1);
expect(pointScale[1]).to.be.closeTo(1.9999999999999996, 0.1);
});
it('returns accurate point scale factor', () => {
pointScale = projection.pointScale(LatLon(0, 0), true);
expect(pointScale[0]).to.be.closeTo(1, 0.1);
expect(pointScale[1]).to.be.closeTo(1.0067394967683778, 0.1);
pointScale = projection.pointScale(LatLon(60, 0), true);
expect(pointScale[0]).to.be.closeTo(1.994972897047054, 0.1);
expect(pointScale[1]).to.be.closeTo(1.9983341753952164, 0.1);
});
});
describe('#bounds', () => {
it('returns correct bounds', () => {
var dimensions = 40075016.6855785;
var bounds = projection.bounds;
expect(Math.abs(bounds[1][0] - bounds[0][0])).to.be.closeTo(dimensions, 0.1);
expect(Math.abs(bounds[1][1] - bounds[0][1])).to.be.closeTo(dimensions, 0.1);
});
});
});
describe('Projection.Mercator', () => {
var projection = Projection.Mercator;
describe('#project', () => {
it('projects the center', () => {
var point = projection.project(LatLon(0, 0));
expect(point.x).to.be.closeTo(0, 0.01);
expect(point.y).to.be.closeTo(0, 0.01);
});
it('projects the North-West corner', () => {
var point = projection.project(LatLon(85.0840591556, -180));
expect(point.x).to.be.closeTo(-20037508.34279, 0.01);
expect(point.y).to.be.closeTo(20037508.4798169, 0.01);
});
it('projects the South-East corner', () => {
var point = projection.project(LatLon(-85.0840591556, 180));
expect(point.x).to.be.closeTo(20037508.34279, 0.01);
expect(point.y).to.be.closeTo(-20037508.4798169, 0.01);
});
});
describe('#unproject', () => {
it('unprojects the center', () => {
var latlon = projection.unproject(Point(0, 0));
expect(latlon.lat).to.be.closeTo(0, 0.01);
expect(latlon.lon).to.be.closeTo(0, 0.01);
});
it('unprojects the North-West corner', () => {
var latlon = projection.unproject(Point(-20037508.34279, 20037508.4798169));
expect(latlon.lat).to.be.closeTo(85.0840591556, 0.01);
expect(latlon.lon).to.be.closeTo(-180, 0.01);
});
it('unprojects the South-East corner', () => {
var latlon = projection.unproject(Point(20037508.34279, -20037508.4798169));
expect(latlon.lat).to.be.closeTo(-85.0840591556, 0.01);
expect(latlon.lon).to.be.closeTo(180, 0.01);
});
});
describe('#pointScale', () => {
var pointScale;
it('returns point scale factor', () => {
pointScale = projection.pointScale(LatLon(0, 0));
expect(pointScale[0]).to.be.closeTo(1, 0.1);
expect(pointScale[1]).to.be.closeTo(1, 0.1);
pointScale = projection.pointScale(LatLon(60, 0));
expect(pointScale[0]).to.be.closeTo(1.9999999999999996, 0.1);
expect(pointScale[1]).to.be.closeTo(1.9999999999999996, 0.1);
});
});
describe('#bounds', () => {
it('returns correct bounds', () => {
var dimensions = [40075016.68558, 34261226.9711];
var bounds = projection.bounds;
expect(Math.abs(bounds[1][0] - bounds[0][0])).to.be.closeTo(dimensions[0], 0.1);
expect(Math.abs(bounds[1][1] - bounds[0][1])).to.be.closeTo(dimensions[1], 0.1);
});
});
});
describe('Projection.LatLon', () => {
var projection = Projection.LatLon;
describe('#project', () => {
it('projects the center', () => {
var point = projection.project(LatLon(0, 0));
expect(point.x).to.be.closeTo(0, 0.01);
expect(point.y).to.be.closeTo(0, 0.01);
});
it('projects the North-West corner', () => {
var point = projection.project(LatLon(90, -180));
expect(point.x).to.be.closeTo(-180, 0.01);
expect(point.y).to.be.closeTo(90, 0.01);
});
it('projects the South-East corner', () => {
var point = projection.project(LatLon(-90, 180));
expect(point.x).to.be.closeTo(180, 0.01);
expect(point.y).to.be.closeTo(-90, 0.01);
});
});
describe('#unproject', () => {
it('unprojects the center', () => {
var latlon = projection.unproject(Point(0, 0));
expect(latlon.lat).to.be.closeTo(0, 0.01);
expect(latlon.lon).to.be.closeTo(0, 0.01);
});
it('unprojects the North-West corner', () => {
var latlon = projection.unproject(Point(-180, 90));
expect(latlon.lat).to.be.closeTo(90, 0.01);
expect(latlon.lon).to.be.closeTo(-180, 0.01);
});
it('unprojects the South-East corner', () => {
var latlon = projection.unproject(Point(180, -90));
expect(latlon.lat).to.be.closeTo(-90, 0.01);
expect(latlon.lon).to.be.closeTo(180, 0.01);
});
});
describe('#pointScale', () => {
var pointScale;
it('returns point scale factor', () => {
pointScale = projection.pointScale(LatLon(0, 0));
expect(pointScale[0]).to.be.closeTo(0.000009043695025814084, 0.1);
expect(pointScale[1]).to.be.closeTo(0.000009043695025814084, 0.1);
pointScale = projection.pointScale(LatLon(60, 0));
expect(pointScale[0]).to.be.closeTo(0.000009043695025814084, 0.1);
expect(pointScale[1]).to.be.closeTo(0.000009043695025814084, 0.1);
});
});
describe('#bounds', () => {
it('returns correct bounds', () => {
var dimensions = [360, 180];
var bounds = projection.bounds;
expect(Math.abs(bounds[1][0] - bounds[0][0])).to.be.closeTo(dimensions[0], 0.1);
expect(Math.abs(bounds[1][1] - bounds[0][1])).to.be.closeTo(dimensions[1], 0.1);
});
});
});
describe('Projection.Proj4', () => {
// British National Grid
var projection = Projection.Proj4('+proj=tmerc +lat_0=49 +lon_0=-2 +k=0.9996012717 +x_0=400000 +y_0=-100000 +ellps=airy +towgs84=446.448,-125.157,542.06,0.15,0.247,0.842,-20.489 +units=m +no_defs', [[-84667.14, 11795.97], [608366.68, 1230247.30]]);
describe('#project', () => {
it('projects the center', () => {
var point = projection.project(LatLon(55.46347028885475, -4.186594751907175));
expect(point.x).to.be.closeTo(261849.77, 0.1);
expect(point.y).to.be.closeTo(621021.635, 0.1);
});
it('projects the North-West corner', () => {
var point = projection.project(LatLon(60.6596573635804, -10.8865578798635));
expect(point.x).to.be.closeTo(-84667.14, 0.1);
expect(point.y).to.be.closeTo(1230247.30, 0.1);
});
it('projects the South-East corner', () => {
var point = projection.project(LatLon(49.9699671340061, 0.904406143400599));
expect(point.x).to.be.closeTo(608366.68, 0.1);
expect(point.y).to.be.closeTo(11795.97, 0.1);
});
});
describe('#unproject', () => {
it('unprojects the center', () => {
var latlon = projection.unproject(Point(261849.77, 621021.635));
expect(latlon.lat).to.be.closeTo(55.46347028885475, 0.01);
expect(latlon.lon).to.be.closeTo(-4.186594751907175, 0.01);
});
it('unprojects the North-West corner', () => {
var latlon = projection.unproject(Point(-84667.14, 1230247.30));
expect(latlon.lat).to.be.closeTo(60.6596573635804, 0.01);
expect(latlon.lon).to.be.closeTo(-10.8865578798635, 0.01);
});
it('unprojects the South-East corner', () => {
var latlon = projection.unproject(Point(608366.68, 11795.97));
expect(latlon.lat).to.be.closeTo(49.9699671340061, 0.01);
expect(latlon.lon).to.be.closeTo(0.904406143400599, 0.01);
});
});
describe('#pointScale', () => {
var pointScale;
it('returns point scale factor', () => {
pointScale = projection.pointScale(LatLon(0, 0));
expect(pointScale[0]).to.be.closeTo(1, 0.1);
expect(pointScale[1]).to.be.closeTo(1, 0.1);
pointScale = projection.pointScale(LatLon(60, 0));
expect(pointScale[0]).to.be.closeTo(1, 0.1);
expect(pointScale[1]).to.be.closeTo(1, 0.1);
});
});
describe('#bounds', () => {
it('returns correct bounds', () => {
var dimensions = [693033.82, 1218451.33];
var bounds = projection.bounds;
expect(Math.abs(bounds[1][0] - bounds[0][0])).to.be.closeTo(dimensions[0], 0.1);
expect(Math.abs(bounds[1][1] - bounds[0][1])).to.be.closeTo(dimensions[1], 0.1);
});
});
});

Wyświetl plik

@ -1,47 +0,0 @@
import Transformation from '../../src/util/Transformation';
import {point as Point} from '../../src/geo/Point';
describe('Transformation', () => {
var transformation;
var point;
var scale;
// Probably superflous to use spies here but they serve as an example of how
// to use them in this way at least
beforeEach(() => {
transformation = new Transformation(1, 0.5, 1, 0.5);
spy(transformation, 'transform');
spy(transformation, 'untransform');
point = Point(5, 5);
scale = 2;
});
describe('#transform', () => {
it('can transform', () => {
var point2 = transformation.transform(point, scale);
expect(transformation.transform).to.have.returned(Point(11, 11));
});
it('uses a scale of 1 by default', () => {
transformation.transform(point);
expect(transformation.transform).to.have.returned(Point(5.5, 5.5));
});
});
describe('#untransform', () => {
it('can untransform', () => {
var point2 = transformation.transform(point, scale);
var point3 = transformation.untransform(point2, scale);
expect(transformation.untransform).to.have.returned(point);
});
it('uses a scale of 1 by default', () => {
var point2 = transformation.transform(point);
var point3 = transformation.untransform(point2);
expect(transformation.untransform).to.have.returned(point);
});
});
});