Skip to content

Commit

Permalink
Implement featureIndex in coordEach & coordReduce (#872)
Browse files Browse the repository at this point in the history
  • Loading branch information
DenisCarriere authored Jul 31, 2017
1 parent 9595296 commit 60000d1
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 26 deletions.
4 changes: 2 additions & 2 deletions packages/turf-meta/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ export type AllGeoJSON = Feature<any> | Features<any> | GeometryObject | Geometr
/**
* http://turfjs.org/docs/#coordreduce
*/
export function coordReduce(geojson: AllGeoJSON, callback: (previousValue: any, currentCoord: number[], coordIndex: number) => void, initialValue?: any): void;
export function coordReduce(geojson: AllGeoJSON, callback: (previousValue: any, currentCoord: number[], coordIndex: number, featureIndex: number, featureSubIndex: number) => void, initialValue?: any): void;

/**
* http://turfjs.org/docs/#coordeach
*/
export function coordEach(geojson: AllGeoJSON, callback: (currentCoord: number[], coordIndex: number) => void): void;
export function coordEach(geojson: AllGeoJSON, callback: (currentCoord: number[], coordIndex: number, featureIndex: number, featureSubIndex: number) => void): void;

/**
* http://turfjs.org/docs/#propeach
Expand Down
51 changes: 32 additions & 19 deletions packages/turf-meta/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,37 @@
* Callback for coordEach
*
* @callback coordEachCallback
* @param {Array<number>} currentCoord The current coordinates being processed.
* @param {number} coordIndex 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.
* @param {Array<number>} currentCoord The current coordinate being processed.
* @param {number} coordIndex The current index of the coordinate being processed.
* Starts at index 0.
* @param {number} featureIndex The current index of the feature being processed.
* @param {number} featureSubIndex The current subIndex of the feature being processed.
*/

/**
* Iterate over coordinates in any GeoJSON object, similar to Array.forEach()
*
* @name coordEach
* @param {FeatureCollection|Geometry|Feature} geojson any GeoJSON object
* @param {Function} callback a method that takes (currentCoord, coordIndex)
* @param {Function} callback a method that takes (currentCoord, coordIndex, featureIndex, featureSubIndex)
* @param {boolean} [excludeWrapCoord=false] whether or not to include the final coordinate of LinearRings that wraps the ring in its iteration.
* @example
* var features = turf.featureCollection([
* turf.point([26, 37], {"foo": "bar"}),
* turf.point([36, 53], {"hello": "world"})
* ]);
*
* turf.coordEach(features, function (currentCoord, coordIndex) {
* turf.coordEach(features, function (currentCoord, coordIndex, featureIndex, featureSubIndex) {
* //=currentCoord
* //=coordIndex
* //=featureIndex
* //=featureSubIndex
* });
*/
function coordEach(geojson, callback, excludeWrapCoord) {
// Handles null Geometry -- Skips this GeoJSON
if (geojson === null) return;
var i, j, k, g, l, geometry, stopG, coords,
var featureIndex, geometryIndex, j, k, l, geometry, stopG, coords,
geometryMaybeCollection,
wrapShrink = 0,
coordIndex = 0,
Expand All @@ -50,16 +54,17 @@ function coordEach(geojson, callback, excludeWrapCoord) {
// 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++) {
for (featureIndex = 0; featureIndex < stop; featureIndex++) {
var featureSubIndex = 0;

geometryMaybeCollection = (isFeatureCollection ? geojson.features[i].geometry :
geometryMaybeCollection = (isFeatureCollection ? geojson.features[featureIndex].geometry :
(isFeature ? geojson.geometry : geojson));
isGeometryCollection = (geometryMaybeCollection) ? geometryMaybeCollection.type === 'GeometryCollection' : false;
stopG = isGeometryCollection ? geometryMaybeCollection.geometries.length : 1;

for (g = 0; g < stopG; g++) {
for (geometryIndex = 0; geometryIndex < stopG; geometryIndex++) {
geometry = isGeometryCollection ?
geometryMaybeCollection.geometries[g] : geometryMaybeCollection;
geometryMaybeCollection.geometries[geometryIndex] : geometryMaybeCollection;

// Handles null Geometry -- Skips this geometry
if (geometry === null) continue;
Expand All @@ -72,30 +77,34 @@ function coordEach(geojson, callback, excludeWrapCoord) {
case null:
break;
case 'Point':
callback(coords, coordIndex);
callback(coords, coordIndex, featureIndex, featureSubIndex);
coordIndex++;
featureSubIndex++;
break;
case 'LineString':
case 'MultiPoint':
for (j = 0; j < coords.length; j++) {
callback(coords[j], coordIndex);
callback(coords[j], coordIndex, featureIndex, featureSubIndex);
coordIndex++;
featureSubIndex++;
}
break;
case 'Polygon':
case 'MultiLineString':
for (j = 0; j < coords.length; j++)
for (k = 0; k < coords[j].length - wrapShrink; k++) {
callback(coords[j][k], coordIndex);
callback(coords[j][k], coordIndex, featureIndex, featureSubIndex);
coordIndex++;
featureSubIndex++;
}
break;
case '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], coordIndex);
callback(coords[j][k][l], coordIndex, featureIndex, featureSubIndex);
coordIndex++;
featureSubIndex++;
}
break;
case 'GeometryCollection':
Expand Down Expand Up @@ -126,8 +135,10 @@ function coordEach(geojson, callback, excludeWrapCoord) {
* @param {*} previousValue The accumulated value previously returned in the last invocation
* of the callback, or initialValue, if supplied.
* @param {Array<number>} currentCoord The current coordinate being processed.
* @param {number} coordIndex 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.
* @param {number} coordIndex The current index of the coordinate being processed.
* Starts at index 0, if an initialValue is provided, and at index 1 otherwise.
* @param {number} featureIndex The current index of the feature being processed.
* @param {number} featureSubIndex The current subIndex of the feature being processed.
*/

/**
Expand All @@ -145,18 +156,20 @@ function coordEach(geojson, callback, excludeWrapCoord) {
* turf.point([36, 53], {"hello": "world"})
* ]);
*
* turf.coordReduce(features, function (previousValue, currentCoord, coordIndex) {
* turf.coordReduce(features, function (previousValue, currentCoord, coordIndex, featureIndex, featureSubIndex) {
* //=previousValue
* //=currentCoord
* //=coordIndex
* //=featureIndex
* //=featureSubIndex
* return currentCoord;
* });
*/
function coordReduce(geojson, callback, initialValue, excludeWrapCoord) {
var previousValue = initialValue;
coordEach(geojson, function (currentCoord, coordIndex) {
coordEach(geojson, function (currentCoord, coordIndex, featureIndex, featureSubIndex) {
if (coordIndex === 0 && initialValue === undefined) previousValue = currentCoord;
else previousValue = callback(previousValue, currentCoord, coordIndex);
else previousValue = callback(previousValue, currentCoord, coordIndex, featureIndex, featureSubIndex);
}, excludeWrapCoord);
return previousValue;
}
Expand Down
62 changes: 57 additions & 5 deletions packages/turf-meta/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -499,11 +499,14 @@ const geojsonSegments = featureCollection([
test('segmentEach -- index & subIndex', t => {
const index = [];
const subIndex = [];
// Segment Each
let total = 0;

meta.segmentEach(geojsonSegments, (segment, featureIndex, featureSubIndex) => {
index.push(featureIndex);
subIndex.push(featureSubIndex);
total++;
});
t.equal(total, 8, 'total');
t.deepEqual(index, [1, 1, 2, 2, 2, 2, 4, 4], 'index');
t.deepEqual(subIndex, [0, 1, 0, 1, 2, 3, 0, 1], 'subIndex');
t.end();
Expand All @@ -512,12 +515,61 @@ test('segmentEach -- index & subIndex', t => {
test('segmentReduce -- index & subIndex', t => {
const index = [];
const subIndex = [];
// Segment Each
meta.segmentReduce(geojsonSegments, (previousValue, segment, featureIndex, featureSubIndex) => {

const total = meta.segmentReduce(geojsonSegments, (previousValue, segment, featureIndex, featureSubIndex) => {
index.push(featureIndex);
subIndex.push(featureSubIndex);
});
previousValue++;
return previousValue;
}, 0);

t.equal(total, 8, 'total');
t.deepEqual(index, [1, 1, 2, 2, 2, 2, 4, 4], 'index');
t.deepEqual(subIndex, [0, 1, 0, 1, 2, 3, 0, 1], 'subIndex');
t.end();
});
});

const geojsonCoords = featureCollection([
point([0, 1]),
lineString([[0, 0], [2, 2], [4, 4]]),
point([2, 2])
]);

test('coordEach -- index & subIndex', t => {
const coordIndexes = [];
const featureIndexes = [];
const featureSubIndexes = [];
let total = 0;

meta.coordEach(geojsonCoords, (coord, coordIndex, featureIndex, featureSubIndex) => {
coordIndexes.push(coordIndex);
featureIndexes.push(featureIndex);
featureSubIndexes.push(featureSubIndex);
total++;
});
t.equal(total, 5, 'total');
t.deepEqual(coordIndexes, [0, 1, 2, 3, 4], 'coordIndex');
t.deepEqual(featureIndexes, [0, 1, 1, 1, 2], 'featureIndex');
t.deepEqual(featureSubIndexes, [0, 0, 1, 2, 0], 'featureSubIndex');
t.end();
});

test('coordEach -- index & subIndex', t => {
const coordIndexes = [];
const featureIndexes = [];
const featureSubIndexes = [];

const total = meta.coordReduce(geojsonCoords, (previousValue, coord, coordIndex, featureIndex, featureSubIndex) => {
coordIndexes.push(coordIndex);
featureIndexes.push(featureIndex);
featureSubIndexes.push(featureSubIndex);
previousValue++;
return previousValue;
}, 0);

t.equal(total, 5);
t.deepEqual(coordIndexes, [0, 1, 2, 3, 4], 'coordIndex');
t.deepEqual(featureIndexes, [0, 1, 1, 1, 2], 'featureIndex');
t.deepEqual(featureSubIndexes, [0, 0, 1, 2, 0], 'featureSubIndex');
t.end();
});

0 comments on commit 60000d1

Please sign in to comment.