(function (root, factory) {
// Node.
if(typeof module === 'object' && typeof module.exports === 'object') {
exports = module.exports = factory();
}
// Browser Global.
if(typeof window === "object") {
root.Terraformer = factory();
}
}(this, function(){
var exports = {},
EarthRadius = 6378137,
DegreesPerRadian = 57.295779513082320,
RadiansPerDegree = 0.017453292519943,
MercatorCRS = {
"type": "link",
"properties": {
"href": "http://spatialreference.org/ref/sr-org/6928/ogcwkt/",
"type": "ogcwkt"
}
},
GeographicCRS = {
"type": "link",
"properties": {
"href": "http://spatialreference.org/ref/epsg/4326/ogcwkt/",
"type": "ogcwkt"
}
};
/*
Internal: isArray function
*/
function isArray(obj) {
return Object.prototype.toString.call(obj) === "[object Array]";
}
/*
Internal: safe warning
*/
function warn() {
var args = Array.prototype.slice.apply(arguments);
if (typeof console !== undefined && console.warn) {
console.warn.apply(console, args);
}
}
/*
Internal: Extend one object with another.
*/
function extend(destination, source) {
for (var k in source) {
if (source.hasOwnProperty(k)) {
destination[k] = source[k];
}
}
return destination;
}
/*
Public: Calculate an bounding box for a geojson object
*/
function calculateBounds (geojson) {
if(geojson.type){
switch (geojson.type) {
case 'Point':
return [ geojson.coordinates[0], geojson.coordinates[1], geojson.coordinates[0], geojson.coordinates[1]];
case 'MultiPoint':
return calculateBoundsFromArray(geojson.coordinates);
case 'LineString':
return calculateBoundsFromArray(geojson.coordinates);
case 'MultiLineString':
return calculateBoundsFromNestedArrays(geojson.coordinates);
case 'Polygon':
return calculateBoundsFromNestedArrays(geojson.coordinates);
case 'MultiPolygon':
return calculateBoundsFromNestedArrayOfArrays(geojson.coordinates);
case 'Feature':
return geojson.geometry? calculateBounds(geojson.geometry) : null;
case 'FeatureCollection':
return calculateBoundsForFeatureCollection(geojson);
case 'GeometryCollection':
return calculateBoundsForGeometryCollection(geojson);
default:
throw new Error("Unknown type: " + geojson.type);
}
}
return null;
}
/*
Internal: Calculate an bounding box from an nested array of positions
[
[
[ [lng, lat],[lng, lat],[lng, lat] ]
]
[
[lng, lat],[lng, lat],[lng, lat]
]
[
[lng, lat],[lng, lat],[lng, lat]
]
]
*/
function calculateBoundsFromNestedArrays (array) {
var x1 = null, x2 = null, y1 = null, y2 = null;
for (var i = 0; i < array.length; i++) {
var inner = array[i];
for (var j = 0; j < inner.length; j++) {
var lonlat = inner[j];
var lon = lonlat[0];
var lat = lonlat[1];
if (x1 === null) {
x1 = lon;
} else if (lon < x1) {
x1 = lon;
}
if (x2 === null) {
x2 = lon;
} else if (lon > x2) {
x2 = lon;
}
if (y1 === null) {
y1 = lat;
} else if (lat < y1) {
y1 = lat;
}
if (y2 === null) {
y2 = lat;
} else if (lat > y2) {
y2 = lat;
}
}
}
return [x1, y1, x2, y2 ];
}
/*
Internal: Calculate a bounding box from an array of arrays of arrays
[
[ [lng, lat],[lng, lat],[lng, lat] ]
[ [lng, lat],[lng, lat],[lng, lat] ]
[ [lng, lat],[lng, lat],[lng, lat] ]
]
*/
function calculateBoundsFromNestedArrayOfArrays (array) {
var x1 = null, x2 = null, y1 = null, y2 = null;
for (var i = 0; i < array.length; i++) {
var inner = array[i];
for (var j = 0; j < inner.length; j++) {
var innerinner = inner[j];
for (var k = 0; k < innerinner.length; k++) {
var lonlat = innerinner[k];
var lon = lonlat[0];
var lat = lonlat[1];
if (x1 === null) {
x1 = lon;
} else if (lon < x1) {
x1 = lon;
}
if (x2 === null) {
x2 = lon;
} else if (lon > x2) {
x2 = lon;
}
if (y1 === null) {
y1 = lat;
} else if (lat < y1) {
y1 = lat;
}
if (y2 === null) {
y2 = lat;
} else if (lat > y2) {
y2 = lat;
}
}
}
}
return [x1, y1, x2, y2];
}
/*
Internal: Calculate a bounding box from an array of positions
[
[lng, lat],[lng, lat],[lng, lat]
]
*/
function calculateBoundsFromArray (array) {
var x1 = null, x2 = null, y1 = null, y2 = null;
for (var i = 0; i < array.length; i++) {
var lonlat = array[i];
var lon = lonlat[0];
var lat = lonlat[1];
if (x1 === null) {
x1 = lon;
} else if (lon < x1) {
x1 = lon;
}
if (x2 === null) {
x2 = lon;
} else if (lon > x2) {
x2 = lon;
}
if (y1 === null) {
y1 = lat;
} else if (lat < y1) {
y1 = lat;
}
if (y2 === null) {
y2 = lat;
} else if (lat > y2) {
y2 = lat;
}
}
return [x1, y1, x2, y2 ];
}
/*
Internal: Calculate an bounding box for a feature collection
*/
function calculateBoundsForFeatureCollection(featureCollection){
var extents = [], extent;
for (var i = featureCollection.features.length - 1; i >= 0; i--) {
extent = calculateBounds(featureCollection.features[i].geometry);
extents.push([extent[0],extent[1]]);
extents.push([extent[2],extent[3]]);
}
return calculateBoundsFromArray(extents);
}
/*
Internal: Calculate an bounding box for a geometry collection
*/
function calculateBoundsForGeometryCollection(geometryCollection){
var extents = [], extent;
for (var i = geometryCollection.geometries.length - 1; i >= 0; i--) {
extent = calculateBounds(geometryCollection.geometries[i]);
extents.push([extent[0],extent[1]]);
extents.push([extent[2],extent[3]]);
}
return calculateBoundsFromArray(extents);
}
function calculateEnvelope(geojson){
var bounds = calculateBounds(geojson);
return {
x: bounds[0],
y: bounds[1],
w: Math.abs(bounds[0] - bounds[2]),
h: Math.abs(bounds[1] - bounds[3])
};
}
/*
Internal: Convert radians to degrees. Used by spatial reference converters.
*/
function radToDeg(rad) {
return rad * DegreesPerRadian;
}
/*
Internal: Convert degrees to radians. Used by spatial reference converters.
*/
function degToRad(deg) {
return deg * RadiansPerDegree;
}
/*
Internal: Loop over each array in a geojson object and apply a function to it. Used by spatial reference converters.
*/
function eachPosition(coordinates, func) {
for (var i = 0; i < coordinates.length; i++) {
// we found a number so lets convert this pair
if(typeof coordinates[i][0] === "number"){
coordinates[i] = func(coordinates[i]);
}
// we found an coordinates array it again and run THIS function against it
if(typeof coordinates[i] === "object"){
coordinates[i] = eachPosition(coordinates[i], func);
}
}
return coordinates;
}
/*
Public: Convert a GeoJSON Position object to Geographic (4326)
*/
function positionToGeographic(position) {
var x = position[0];
var y = position[1];
return [radToDeg(x / EarthRadius) - (Math.floor((radToDeg(x / EarthRadius) + 180) / 360) * 360), radToDeg((Math.PI / 2) - (2 * Math.atan(Math.exp(-1.0 * y / Earth