650 lines
22 KiB
JavaScript
650 lines
22 KiB
JavaScript
/**
|
|
* Callback for coordEach
|
|
*
|
|
* @private
|
|
* @callback coordEachCallback
|
|
* @param {[number, number]} currentCoords The current coordinates being processed.
|
|
* @param {number} currentIndex The index of the current element being processed in the
|
|
* array.Starts at index 0, if an initialValue is provided, and at index 1 otherwise.
|
|
*/
|
|
|
|
/**
|
|
* Iterate over coordinates in any GeoJSON object, similar to Array.forEach()
|
|
*
|
|
* @name coordEach
|
|
* @param {Object} layer any GeoJSON object
|
|
* @param {Function} callback a method that takes (currentCoords, currentIndex)
|
|
* @param {boolean} [excludeWrapCoord=false] whether or not to include
|
|
* the final coordinate of LinearRings that wraps the ring in its iteration.
|
|
* @example
|
|
* var features = {
|
|
* "type": "FeatureCollection",
|
|
* "features": [
|
|
* {
|
|
* "type": "Feature",
|
|
* "properties": {},
|
|
* "geometry": {
|
|
* "type": "Point",
|
|
* "coordinates": [26, 37]
|
|
* }
|
|
* },
|
|
* {
|
|
* "type": "Feature",
|
|
* "properties": {},
|
|
* "geometry": {
|
|
* "type": "Point",
|
|
* "coordinates": [36, 53]
|
|
* }
|
|
* }
|
|
* ]
|
|
* };
|
|
* turf.coordEach(features, function (currentCoords, currentIndex) {
|
|
* //=currentCoords
|
|
* //=currentIndex
|
|
* });
|
|
*/
|
|
function coordEach(layer, callback, excludeWrapCoord) {
|
|
var i, j, k, g, l, geometry, stopG, coords,
|
|
geometryMaybeCollection,
|
|
wrapShrink = 0,
|
|
currentIndex = 0,
|
|
isGeometryCollection,
|
|
isFeatureCollection = layer.type === 'FeatureCollection',
|
|
isFeature = layer.type === 'Feature',
|
|
stop = isFeatureCollection ? layer.features.length : 1;
|
|
|
|
// This logic may look a little weird. The reason why it is that way
|
|
// is because it's trying to be fast. GeoJSON supports multiple kinds
|
|
// of objects at its root: FeatureCollection, Features, Geometries.
|
|
// This function has the responsibility of handling all of them, and that
|
|
// means that some of the `for` loops you see below actually just don't apply
|
|
// to certain inputs. For instance, if you give this just a
|
|
// Point geometry, then both loops are short-circuited and all we do
|
|
// is gradually rename the input until it's called 'geometry'.
|
|
//
|
|
// This also aims to allocate as few resources as possible: just a
|
|
// few numbers and booleans, rather than any temporary arrays as would
|
|
// be required with the normalization approach.
|
|
for (i = 0; i < stop; i++) {
|
|
|
|
geometryMaybeCollection = (isFeatureCollection ? layer.features[i].geometry :
|
|
(isFeature ? layer.geometry : layer));
|
|
isGeometryCollection = geometryMaybeCollection.type === 'GeometryCollection';
|
|
stopG = isGeometryCollection ? geometryMaybeCollection.geometries.length : 1;
|
|
|
|
for (g = 0; g < stopG; g++) {
|
|
geometry = isGeometryCollection ?
|
|
geometryMaybeCollection.geometries[g] : geometryMaybeCollection;
|
|
coords = geometry.coordinates;
|
|
|
|
wrapShrink = (excludeWrapCoord &&
|
|
(geometry.type === 'Polygon' || geometry.type === 'MultiPolygon')) ?
|
|
1 : 0;
|
|
|
|
if (geometry.type === 'Point') {
|
|
callback(coords, currentIndex);
|
|
currentIndex++;
|
|
} else if (geometry.type === 'LineString' || geometry.type === 'MultiPoint') {
|
|
for (j = 0; j < coords.length; j++) {
|
|
callback(coords[j], currentIndex);
|
|
currentIndex++;
|
|
}
|
|
} else if (geometry.type === 'Polygon' || geometry.type === 'MultiLineString') {
|
|
for (j = 0; j < coords.length; j++)
|
|
for (k = 0; k < coords[j].length - wrapShrink; k++) {
|
|
callback(coords[j][k], currentIndex);
|
|
currentIndex++;
|
|
}
|
|
} else if (geometry.type === 'MultiPolygon') {
|
|
for (j = 0; j < coords.length; j++)
|
|
for (k = 0; k < coords[j].length; k++)
|
|
for (l = 0; l < coords[j][k].length - wrapShrink; l++) {
|
|
callback(coords[j][k][l], currentIndex);
|
|
currentIndex++;
|
|
}
|
|
} else if (geometry.type === 'GeometryCollection') {
|
|
for (j = 0; j < geometry.geometries.length; j++)
|
|
coordEach(geometry.geometries[j], callback, excludeWrapCoord);
|
|
} else {
|
|
throw new Error('Unknown Geometry Type');
|
|
}
|
|
}
|
|
}
|
|
}
|
|
module.exports.coordEach = coordEach;
|
|
|
|
/**
|
|
* Callback for coordReduce
|
|
*
|
|
* The first time the callback function is called, the values provided as arguments depend
|
|
* on whether the reduce method has an initialValue argument.
|
|
*
|
|
* If an initialValue is provided to the reduce method:
|
|
* - The previousValue argument is initialValue.
|
|
* - The currentValue argument is the value of the first element present in the array.
|
|
*
|
|
* If an initialValue is not provided:
|
|
* - The previousValue argument is the value of the first element present in the array.
|
|
* - The currentValue argument is the value of the second element present in the array.
|
|
*
|
|
* @private
|
|
* @callback coordReduceCallback
|
|
* @param {*} previousValue The accumulated value previously returned in the last invocation
|
|
* of the callback, or initialValue, if supplied.
|
|
* @param {[number, number]} currentCoords The current coordinate being processed.
|
|
* @param {number} currentIndex The index of the current element being processed in the
|
|
* array.Starts at index 0, if an initialValue is provided, and at index 1 otherwise.
|
|
*/
|
|
|
|
/**
|
|
* Reduce coordinates in any GeoJSON object, similar to Array.reduce()
|
|
*
|
|
* @name coordReduce
|
|
* @param {Object} layer any GeoJSON object
|
|
* @param {Function} callback a method that takes (previousValue, currentCoords, currentIndex)
|
|
* @param {*} [initialValue] Value to use as the first argument to the first call of the callback.
|
|
* @param {boolean} [excludeWrapCoord=false] whether or not to include
|
|
* the final coordinate of LinearRings that wraps the ring in its iteration.
|
|
* @returns {*} The value that results from the reduction.
|
|
* @example
|
|
* var features = {
|
|
* "type": "FeatureCollection",
|
|
* "features": [
|
|
* {
|
|
* "type": "Feature",
|
|
* "properties": {},
|
|
* "geometry": {
|
|
* "type": "Point",
|
|
* "coordinates": [26, 37]
|
|
* }
|
|
* },
|
|
* {
|
|
* "type": "Feature",
|
|
* "properties": {},
|
|
* "geometry": {
|
|
* "type": "Point",
|
|
* "coordinates": [36, 53]
|
|
* }
|
|
* }
|
|
* ]
|
|
* };
|
|
* turf.coordReduce(features, function (previousValue, currentCoords, currentIndex) {
|
|
* //=previousValue
|
|
* //=currentCoords
|
|
* //=currentIndex
|
|
* return currentCoords;
|
|
* });
|
|
*/
|
|
function coordReduce(layer, callback, initialValue, excludeWrapCoord) {
|
|
var previousValue = initialValue;
|
|
coordEach(layer, function (currentCoords, currentIndex) {
|
|
if (currentIndex === 0 && initialValue === undefined) {
|
|
previousValue = currentCoords;
|
|
} else {
|
|
previousValue = callback(previousValue, currentCoords, currentIndex);
|
|
}
|
|
}, excludeWrapCoord);
|
|
return previousValue;
|
|
}
|
|
module.exports.coordReduce = coordReduce;
|
|
|
|
/**
|
|
* Callback for propEach
|
|
*
|
|
* @private
|
|
* @callback propEachCallback
|
|
* @param {*} currentProperties The current properties being processed.
|
|
* @param {number} currentIndex The index of the current element being processed in the
|
|
* array.Starts at index 0, if an initialValue is provided, and at index 1 otherwise.
|
|
*/
|
|
|
|
/**
|
|
* Iterate over properties in any GeoJSON object, similar to Array.forEach()
|
|
*
|
|
* @name propEach
|
|
* @param {Object} layer any GeoJSON object
|
|
* @param {Function} callback a method that takes (currentProperties, currentIndex)
|
|
* @example
|
|
* var features = {
|
|
* "type": "FeatureCollection",
|
|
* "features": [
|
|
* {
|
|
* "type": "Feature",
|
|
* "properties": {"foo": "bar"},
|
|
* "geometry": {
|
|
* "type": "Point",
|
|
* "coordinates": [26, 37]
|
|
* }
|
|
* },
|
|
* {
|
|
* "type": "Feature",
|
|
* "properties": {"hello": "world"},
|
|
* "geometry": {
|
|
* "type": "Point",
|
|
* "coordinates": [36, 53]
|
|
* }
|
|
* }
|
|
* ]
|
|
* };
|
|
* turf.propEach(features, function (currentProperties, currentIndex) {
|
|
* //=currentProperties
|
|
* //=currentIndex
|
|
* });
|
|
*/
|
|
function propEach(layer, callback) {
|
|
var i;
|
|
switch (layer.type) {
|
|
case 'FeatureCollection':
|
|
for (i = 0; i < layer.features.length; i++) {
|
|
callback(layer.features[i].properties, i);
|
|
}
|
|
break;
|
|
case 'Feature':
|
|
callback(layer.properties, 0);
|
|
break;
|
|
}
|
|
}
|
|
module.exports.propEach = propEach;
|
|
|
|
|
|
/**
|
|
* Callback for propReduce
|
|
*
|
|
* The first time the callback function is called, the values provided as arguments depend
|
|
* on whether the reduce method has an initialValue argument.
|
|
*
|
|
* If an initialValue is provided to the reduce method:
|
|
* - The previousValue argument is initialValue.
|
|
* - The currentValue argument is the value of the first element present in the array.
|
|
*
|
|
* If an initialValue is not provided:
|
|
* - The previousValue argument is the value of the first element present in the array.
|
|
* - The currentValue argument is the value of the second element present in the array.
|
|
*
|
|
* @private
|
|
* @callback propReduceCallback
|
|
* @param {*} previousValue The accumulated value previously returned in the last invocation
|
|
* of the callback, or initialValue, if supplied.
|
|
* @param {*} currentProperties The current properties being processed.
|
|
* @param {number} currentIndex The index of the current element being processed in the
|
|
* array.Starts at index 0, if an initialValue is provided, and at index 1 otherwise.
|
|
*/
|
|
|
|
/**
|
|
* Reduce properties in any GeoJSON object into a single value,
|
|
* similar to how Array.reduce works. However, in this case we lazily run
|
|
* the reduction, so an array of all properties is unnecessary.
|
|
*
|
|
* @name propReduce
|
|
* @param {Object} layer any GeoJSON object
|
|
* @param {Function} callback a method that takes (previousValue, currentProperties, currentIndex)
|
|
* @param {*} [initialValue] Value to use as the first argument to the first call of the callback.
|
|
* @returns {*} The value that results from the reduction.
|
|
* @example
|
|
* var features = {
|
|
* "type": "FeatureCollection",
|
|
* "features": [
|
|
* {
|
|
* "type": "Feature",
|
|
* "properties": {"foo": "bar"},
|
|
* "geometry": {
|
|
* "type": "Point",
|
|
* "coordinates": [26, 37]
|
|
* }
|
|
* },
|
|
* {
|
|
* "type": "Feature",
|
|
* "properties": {"hello": "world"},
|
|
* "geometry": {
|
|
* "type": "Point",
|
|
* "coordinates": [36, 53]
|
|
* }
|
|
* }
|
|
* ]
|
|
* };
|
|
* turf.propReduce(features, function (previousValue, currentProperties, currentIndex) {
|
|
* //=previousValue
|
|
* //=currentProperties
|
|
* //=currentIndex
|
|
* return currentProperties
|
|
* });
|
|
*/
|
|
function propReduce(layer, callback, initialValue) {
|
|
var previousValue = initialValue;
|
|
propEach(layer, function (currentProperties, currentIndex) {
|
|
if (currentIndex === 0 && initialValue === undefined) {
|
|
previousValue = currentProperties;
|
|
} else {
|
|
previousValue = callback(previousValue, currentProperties, currentIndex);
|
|
}
|
|
});
|
|
return previousValue;
|
|
}
|
|
module.exports.propReduce = propReduce;
|
|
|
|
/**
|
|
* Callback for featureEach
|
|
*
|
|
* @private
|
|
* @callback featureEachCallback
|
|
* @param {Feature<any>} currentFeature The current feature being processed.
|
|
* @param {number} currentIndex The index of the current element being processed in the
|
|
* array.Starts at index 0, if an initialValue is provided, and at index 1 otherwise.
|
|
*/
|
|
|
|
/**
|
|
* Iterate over features in any GeoJSON object, similar to
|
|
* Array.forEach.
|
|
*
|
|
* @name featureEach
|
|
* @param {Object} layer any GeoJSON object
|
|
* @param {Function} callback a method that takes (currentFeature, currentIndex)
|
|
* @example
|
|
* var features = {
|
|
* "type": "FeatureCollection",
|
|
* "features": [
|
|
* {
|
|
* "type": "Feature",
|
|
* "properties": {},
|
|
* "geometry": {
|
|
* "type": "Point",
|
|
* "coordinates": [26, 37]
|
|
* }
|
|
* },
|
|
* {
|
|
* "type": "Feature",
|
|
* "properties": {},
|
|
* "geometry": {
|
|
* "type": "Point",
|
|
* "coordinates": [36, 53]
|
|
* }
|
|
* }
|
|
* ]
|
|
* };
|
|
* turf.featureEach(features, function (currentFeature, currentIndex) {
|
|
* //=currentFeature
|
|
* //=currentIndex
|
|
* });
|
|
*/
|
|
function featureEach(layer, callback) {
|
|
if (layer.type === 'Feature') {
|
|
callback(layer, 0);
|
|
} else if (layer.type === 'FeatureCollection') {
|
|
for (var i = 0; i < layer.features.length; i++) {
|
|
callback(layer.features[i], i);
|
|
}
|
|
}
|
|
}
|
|
module.exports.featureEach = featureEach;
|
|
|
|
/**
|
|
* Callback for featureReduce
|
|
*
|
|
* The first time the callback function is called, the values provided as arguments depend
|
|
* on whether the reduce method has an initialValue argument.
|
|
*
|
|
* If an initialValue is provided to the reduce method:
|
|
* - The previousValue argument is initialValue.
|
|
* - The currentValue argument is the value of the first element present in the array.
|
|
*
|
|
* If an initialValue is not provided:
|
|
* - The previousValue argument is the value of the first element present in the array.
|
|
* - The currentValue argument is the value of the second element present in the array.
|
|
*
|
|
* @private
|
|
* @callback featureReduceCallback
|
|
* @param {*} previousValue The accumulated value previously returned in the last invocation
|
|
* of the callback, or initialValue, if supplied.
|
|
* @param {Feature<any>} currentFeature The current Feature being processed.
|
|
* @param {number} currentIndex The index of the current element being processed in the
|
|
* array.Starts at index 0, if an initialValue is provided, and at index 1 otherwise.
|
|
*/
|
|
|
|
/**
|
|
* Reduce features in any GeoJSON object, similar to Array.reduce().
|
|
*
|
|
* @name featureReduce
|
|
* @param {Object} layer any GeoJSON object
|
|
* @param {Function} callback a method that takes (previousValue, currentFeature, currentIndex)
|
|
* @param {*} [initialValue] Value to use as the first argument to the first call of the callback.
|
|
* @returns {*} The value that results from the reduction.
|
|
* @example
|
|
* var features = {
|
|
* "type": "FeatureCollection",
|
|
* "features": [
|
|
* {
|
|
* "type": "Feature",
|
|
* "properties": {"foo": "bar"},
|
|
* "geometry": {
|
|
* "type": "Point",
|
|
* "coordinates": [26, 37]
|
|
* }
|
|
* },
|
|
* {
|
|
* "type": "Feature",
|
|
* "properties": {"hello": "world"},
|
|
* "geometry": {
|
|
* "type": "Point",
|
|
* "coordinates": [36, 53]
|
|
* }
|
|
* }
|
|
* ]
|
|
* };
|
|
* turf.featureReduce(features, function (previousValue, currentFeature, currentIndex) {
|
|
* //=previousValue
|
|
* //=currentFeature
|
|
* //=currentIndex
|
|
* return currentFeature
|
|
* });
|
|
*/
|
|
function featureReduce(layer, callback, initialValue) {
|
|
var previousValue = initialValue;
|
|
featureEach(layer, function (currentFeature, currentIndex) {
|
|
if (currentIndex === 0 && initialValue === undefined) {
|
|
previousValue = currentFeature;
|
|
} else {
|
|
previousValue = callback(previousValue, currentFeature, currentIndex);
|
|
}
|
|
});
|
|
return previousValue;
|
|
}
|
|
module.exports.featureReduce = featureReduce;
|
|
|
|
/**
|
|
* Get all coordinates from any GeoJSON object.
|
|
*
|
|
* @name coordAll
|
|
* @param {Object} layer any GeoJSON object
|
|
* @returns {Array<Array<number>>} coordinate position array
|
|
* @example
|
|
* var features = {
|
|
* "type": "FeatureCollection",
|
|
* "features": [
|
|
* {
|
|
* "type": "Feature",
|
|
* "properties": {},
|
|
* "geometry": {
|
|
* "type": "Point",
|
|
* "coordinates": [26, 37]
|
|
* }
|
|
* },
|
|
* {
|
|
* "type": "Feature",
|
|
* "properties": {},
|
|
* "geometry": {
|
|
* "type": "Point",
|
|
* "coordinates": [36, 53]
|
|
* }
|
|
* }
|
|
* ]
|
|
* };
|
|
* var coords = turf.coordAll(features);
|
|
* //=coords
|
|
*/
|
|
function coordAll(layer) {
|
|
var coords = [];
|
|
coordEach(layer, function (coord) {
|
|
coords.push(coord);
|
|
});
|
|
return coords;
|
|
}
|
|
module.exports.coordAll = coordAll;
|
|
|
|
/**
|
|
* Iterate over each geometry in any GeoJSON object, similar to Array.forEach()
|
|
*
|
|
* @name geomEach
|
|
* @param {Object} layer any GeoJSON object
|
|
* @param {Function} callback a method that takes (currentGeometry, currentIndex)
|
|
* @example
|
|
* var features = {
|
|
* "type": "FeatureCollection",
|
|
* "features": [
|
|
* {
|
|
* "type": "Feature",
|
|
* "properties": {},
|
|
* "geometry": {
|
|
* "type": "Point",
|
|
* "coordinates": [26, 37]
|
|
* }
|
|
* },
|
|
* {
|
|
* "type": "Feature",
|
|
* "properties": {},
|
|
* "geometry": {
|
|
* "type": "Point",
|
|
* "coordinates": [36, 53]
|
|
* }
|
|
* }
|
|
* ]
|
|
* };
|
|
* turf.geomEach(features, function (currentGeometry, currentIndex) {
|
|
* //=currentGeometry
|
|
* //=currentIndex
|
|
* });
|
|
*/
|
|
function geomEach(layer, callback) {
|
|
var i, j, g, geometry, stopG,
|
|
geometryMaybeCollection,
|
|
isGeometryCollection,
|
|
currentIndex = 0,
|
|
isFeatureCollection = layer.type === 'FeatureCollection',
|
|
isFeature = layer.type === 'Feature',
|
|
stop = isFeatureCollection ? layer.features.length : 1;
|
|
|
|
// This logic may look a little weird. The reason why it is that way
|
|
// is because it's trying to be fast. GeoJSON supports multiple kinds
|
|
// of objects at its root: FeatureCollection, Features, Geometries.
|
|
// This function has the responsibility of handling all of them, and that
|
|
// means that some of the `for` loops you see below actually just don't apply
|
|
// to certain inputs. For instance, if you give this just a
|
|
// Point geometry, then both loops are short-circuited and all we do
|
|
// is gradually rename the input until it's called 'geometry'.
|
|
//
|
|
// This also aims to allocate as few resources as possible: just a
|
|
// few numbers and booleans, rather than any temporary arrays as would
|
|
// be required with the normalization approach.
|
|
for (i = 0; i < stop; i++) {
|
|
|
|
geometryMaybeCollection = (isFeatureCollection ? layer.features[i].geometry :
|
|
(isFeature ? layer.geometry : layer));
|
|
isGeometryCollection = geometryMaybeCollection.type === 'GeometryCollection';
|
|
stopG = isGeometryCollection ? geometryMaybeCollection.geometries.length : 1;
|
|
|
|
for (g = 0; g < stopG; g++) {
|
|
geometry = isGeometryCollection ?
|
|
geometryMaybeCollection.geometries[g] : geometryMaybeCollection;
|
|
|
|
if (geometry.type === 'Point' ||
|
|
geometry.type === 'LineString' ||
|
|
geometry.type === 'MultiPoint' ||
|
|
geometry.type === 'Polygon' ||
|
|
geometry.type === 'MultiLineString' ||
|
|
geometry.type === 'MultiPolygon') {
|
|
callback(geometry, currentIndex);
|
|
currentIndex++;
|
|
} else if (geometry.type === 'GeometryCollection') {
|
|
for (j = 0; j < geometry.geometries.length; j++) {
|
|
callback(geometry.geometries[j], currentIndex);
|
|
currentIndex++;
|
|
}
|
|
} else {
|
|
throw new Error('Unknown Geometry Type');
|
|
}
|
|
}
|
|
}
|
|
}
|
|
module.exports.geomEach = geomEach;
|
|
|
|
/**
|
|
* Callback for geomReduce
|
|
*
|
|
* The first time the callback function is called, the values provided as arguments depend
|
|
* on whether the reduce method has an initialValue argument.
|
|
*
|
|
* If an initialValue is provided to the reduce method:
|
|
* - The previousValue argument is initialValue.
|
|
* - The currentValue argument is the value of the first element present in the array.
|
|
*
|
|
* If an initialValue is not provided:
|
|
* - The previousValue argument is the value of the first element present in the array.
|
|
* - The currentValue argument is the value of the second element present in the array.
|
|
*
|
|
* @private
|
|
* @callback geomReduceCallback
|
|
* @param {*} previousValue The accumulated value previously returned in the last invocation
|
|
* of the callback, or initialValue, if supplied.
|
|
* @param {*} currentGeometry The current Feature being processed.
|
|
* @param {number} currentIndex The index of the current element being processed in the
|
|
* array.Starts at index 0, if an initialValue is provided, and at index 1 otherwise.
|
|
*/
|
|
|
|
/**
|
|
* Reduce geometry in any GeoJSON object, similar to Array.reduce().
|
|
*
|
|
* @name geomReduce
|
|
* @param {Object} layer any GeoJSON object
|
|
* @param {Function} callback a method that takes (previousValue, currentGeometry, currentIndex)
|
|
* @param {*} [initialValue] Value to use as the first argument to the first call of the callback.
|
|
* @returns {*} The value that results from the reduction.
|
|
* @example
|
|
* var features = {
|
|
* "type": "FeatureCollection",
|
|
* "features": [
|
|
* {
|
|
* "type": "Feature",
|
|
* "properties": {"foo": "bar"},
|
|
* "geometry": {
|
|
* "type": "Point",
|
|
* "coordinates": [26, 37]
|
|
* }
|
|
* },
|
|
* {
|
|
* "type": "Feature",
|
|
* "properties": {"hello": "world"},
|
|
* "geometry": {
|
|
* "type": "Point",
|
|
* "coordinates": [36, 53]
|
|
* }
|
|
* }
|
|
* ]
|
|
* };
|
|
* turf.geomReduce(features, function (previousValue, currentGeometry, currentIndex) {
|
|
* //=previousValue
|
|
* //=currentGeometry
|
|
* //=currentIndex
|
|
* return currentGeometry
|
|
* });
|
|
*/
|
|
function geomReduce(layer, callback, initialValue) {
|
|
var previousValue = initialValue;
|
|
geomEach(layer, function (currentGeometry, currentIndex) {
|
|
if (currentIndex === 0 && initialValue === undefined) {
|
|
previousValue = currentGeometry;
|
|
} else {
|
|
previousValue = callback(previousValue, currentGeometry, currentIndex);
|
|
}
|
|
});
|
|
return previousValue;
|
|
}
|
|
module.exports.geomReduce = geomReduce;
|