| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401 | import { feature, lineString, isObject, point } from '@turf/helpers';/** * Callback for coordEach * * @callback coordEachCallback * @param {Array<number>} currentCoord The current coordinate being processed. * @param {number} coordIndex The current index of the coordinate being processed. * @param {number} featureIndex The current index of the Feature being processed. * @param {number} multiFeatureIndex The current index of the Multi-Feature being processed. * @param {number} geometryIndex The current index of the Geometry being processed. *//** * Iterate over coordinates in any GeoJSON object, similar to Array.forEach() * * @name coordEach * @param {FeatureCollection|Feature|Geometry} geojson any GeoJSON object * @param {Function} callback a method that takes (currentCoord, coordIndex, featureIndex, multiFeatureIndex) * @param {boolean} [excludeWrapCoord=false] whether or not to include the final coordinate of LinearRings that wraps the ring in its iteration. * @returns {void} * @example * var features = turf.featureCollection([ *   turf.point([26, 37], {"foo": "bar"}), *   turf.point([36, 53], {"hello": "world"}) * ]); * * turf.coordEach(features, function (currentCoord, coordIndex, featureIndex, multiFeatureIndex, geometryIndex) { *   //=currentCoord *   //=coordIndex *   //=featureIndex *   //=multiFeatureIndex *   //=geometryIndex * }); */function coordEach(geojson, callback, excludeWrapCoord) {  // Handles null Geometry -- Skips this GeoJSON  if (geojson === null) return;  var j,    k,    l,    geometry,    stopG,    coords,    geometryMaybeCollection,    wrapShrink = 0,    coordIndex = 0,    isGeometryCollection,    type = geojson.type,    isFeatureCollection = type === "FeatureCollection",    isFeature = type === "Feature",    stop = isFeatureCollection ? geojson.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 (var featureIndex = 0; featureIndex < stop; featureIndex++) {    geometryMaybeCollection = isFeatureCollection      ? geojson.features[featureIndex].geometry      : isFeature      ? geojson.geometry      : geojson;    isGeometryCollection = geometryMaybeCollection      ? geometryMaybeCollection.type === "GeometryCollection"      : false;    stopG = isGeometryCollection      ? geometryMaybeCollection.geometries.length      : 1;    for (var geomIndex = 0; geomIndex < stopG; geomIndex++) {      var multiFeatureIndex = 0;      var geometryIndex = 0;      geometry = isGeometryCollection        ? geometryMaybeCollection.geometries[geomIndex]        : geometryMaybeCollection;      // Handles null Geometry -- Skips this geometry      if (geometry === null) continue;      coords = geometry.coordinates;      var geomType = geometry.type;      wrapShrink =        excludeWrapCoord &&        (geomType === "Polygon" || geomType === "MultiPolygon")          ? 1          : 0;      switch (geomType) {        case null:          break;        case "Point":          if (            callback(              coords,              coordIndex,              featureIndex,              multiFeatureIndex,              geometryIndex            ) === false          )            return false;          coordIndex++;          multiFeatureIndex++;          break;        case "LineString":        case "MultiPoint":          for (j = 0; j < coords.length; j++) {            if (              callback(                coords[j],                coordIndex,                featureIndex,                multiFeatureIndex,                geometryIndex              ) === false            )              return false;            coordIndex++;            if (geomType === "MultiPoint") multiFeatureIndex++;          }          if (geomType === "LineString") multiFeatureIndex++;          break;        case "Polygon":        case "MultiLineString":          for (j = 0; j < coords.length; j++) {            for (k = 0; k < coords[j].length - wrapShrink; k++) {              if (                callback(                  coords[j][k],                  coordIndex,                  featureIndex,                  multiFeatureIndex,                  geometryIndex                ) === false              )                return false;              coordIndex++;            }            if (geomType === "MultiLineString") multiFeatureIndex++;            if (geomType === "Polygon") geometryIndex++;          }          if (geomType === "Polygon") multiFeatureIndex++;          break;        case "MultiPolygon":          for (j = 0; j < coords.length; j++) {            geometryIndex = 0;            for (k = 0; k < coords[j].length; k++) {              for (l = 0; l < coords[j][k].length - wrapShrink; l++) {                if (                  callback(                    coords[j][k][l],                    coordIndex,                    featureIndex,                    multiFeatureIndex,                    geometryIndex                  ) === false                )                  return false;                coordIndex++;              }              geometryIndex++;            }            multiFeatureIndex++;          }          break;        case "GeometryCollection":          for (j = 0; j < geometry.geometries.length; j++)            if (              coordEach(geometry.geometries[j], callback, excludeWrapCoord) ===              false            )              return false;          break;        default:          throw new Error("Unknown Geometry Type");      }    }  }}/** * 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. * * @callback coordReduceCallback * @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 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} multiFeatureIndex The current index of the Multi-Feature being processed. * @param {number} geometryIndex The current index of the Geometry being processed. *//** * Reduce coordinates in any GeoJSON object, similar to Array.reduce() * * @name coordReduce * @param {FeatureCollection|Geometry|Feature} geojson any GeoJSON object * @param {Function} callback a method that takes (previousValue, currentCoord, coordIndex) * @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 = turf.featureCollection([ *   turf.point([26, 37], {"foo": "bar"}), *   turf.point([36, 53], {"hello": "world"}) * ]); * * turf.coordReduce(features, function (previousValue, currentCoord, coordIndex, featureIndex, multiFeatureIndex, geometryIndex) { *   //=previousValue *   //=currentCoord *   //=coordIndex *   //=featureIndex *   //=multiFeatureIndex *   //=geometryIndex *   return currentCoord; * }); */function coordReduce(geojson, callback, initialValue, excludeWrapCoord) {  var previousValue = initialValue;  coordEach(    geojson,    function (      currentCoord,      coordIndex,      featureIndex,      multiFeatureIndex,      geometryIndex    ) {      if (coordIndex === 0 && initialValue === undefined)        previousValue = currentCoord;      else        previousValue = callback(          previousValue,          currentCoord,          coordIndex,          featureIndex,          multiFeatureIndex,          geometryIndex        );    },    excludeWrapCoord  );  return previousValue;}/** * Callback for propEach * * @callback propEachCallback * @param {Object} currentProperties The current Properties being processed. * @param {number} featureIndex The current index of the Feature being processed. *//** * Iterate over properties in any GeoJSON object, similar to Array.forEach() * * @name propEach * @param {FeatureCollection|Feature} geojson any GeoJSON object * @param {Function} callback a method that takes (currentProperties, featureIndex) * @returns {void} * @example * var features = turf.featureCollection([ *     turf.point([26, 37], {foo: 'bar'}), *     turf.point([36, 53], {hello: 'world'}) * ]); * * turf.propEach(features, function (currentProperties, featureIndex) { *   //=currentProperties *   //=featureIndex * }); */function propEach(geojson, callback) {  var i;  switch (geojson.type) {    case "FeatureCollection":      for (i = 0; i < geojson.features.length; i++) {        if (callback(geojson.features[i].properties, i) === false) break;      }      break;    case "Feature":      callback(geojson.properties, 0);      break;  }}/** * 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. * * @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} featureIndex The current index of the Feature being processed. *//** * 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 {FeatureCollection|Feature} geojson any GeoJSON object * @param {Function} callback a method that takes (previousValue, currentProperties, featureIndex) * @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 = turf.featureCollection([ *     turf.point([26, 37], {foo: 'bar'}), *     turf.point([36, 53], {hello: 'world'}) * ]); * * turf.propReduce(features, function (previousValue, currentProperties, featureIndex) { *   //=previousValue *   //=currentProperties *   //=featureIndex *   return currentProperties * }); */function propReduce(geojson, callback, initialValue) {  var previousValue = initialValue;  propEach(geojson, function (currentProperties, featureIndex) {    if (featureIndex === 0 && initialValue === undefined)      previousValue = currentProperties;    else      previousValue = callback(previousValue, currentProperties, featureIndex);  });  return previousValue;}/** * Callback for featureEach * * @callback featureEachCallback * @param {Feature<any>} currentFeature The current Feature being processed. * @param {number} featureIndex The current index of the Feature being processed. *//** * Iterate over features in any GeoJSON object, similar to * Array.forEach. * * @name featureEach * @param {FeatureCollection|Feature|Geometry} geojson any GeoJSON object * @param {Function} callback a method that takes (currentFeature, featureIndex) * @returns {void} * @example * var features = turf.featureCollection([ *   turf.point([26, 37], {foo: 'bar'}), *   turf.point([36, 53], {hello: 'world'}) * ]); * * turf.featureEach(features, function (currentFeature, featureIndex) { *   //=currentFeature *   //=featureIndex * }); */function featureEach(geojson, callback) {  if (geojson.type === "Feature") {    callback(geojson, 0);  } else if (geojson.type === "FeatureCollection") {    for (var i = 0; i < geojson.features.length; i++) {      if (callback(geojson.features[i], i) === false) break;    }  }}/** * 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. * * @callback featureReduceCallback * @param {*} previousValue The accumulated value previously returned in the last invocation * of the callback, or initialValue, if supplied. * @param {Feature} currentFeature The current Feature being processed. * @param {number} featureIndex The current index of the Feature being processed. *//** * Reduce features in any GeoJSON object, similar to Array.reduce(). * * @name featureReduce * @param {FeatureCollection|Feature|Geometry} geojson any GeoJSON object * @param {Function} callback a method that takes (previousValue, currentFeature, featureIndex) * @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 = turf.featureCollection([ *   turf.point([26, 37], {"foo": "bar"}), *   turf.point([36, 53], {"hello": "world"}) * ]); * * turf.featureReduce(features, function (previousValue, currentFeature, featureIndex) { *   //=previousValue *   //=currentFeature *   //=featureIndex *   return currentFeature * }); */function featureReduce(geojson, callback, initialValue) {  var previousValue = initialValue;  featureEach(geojson, function (currentFeature, featureIndex) {    if (featureIndex === 0 && initialValue === undefined)      previousValue = currentFeature;    else previousValue = callback(previousValue, currentFeature, featureIndex);  });  return previousValue;}/** * Get all coordinates from any GeoJSON object. * * @name coordAll * @param {FeatureCollection|Feature|Geometry} geojson any GeoJSON object * @returns {Array<Array<number>>} coordinate position array * @example * var features = turf.featureCollection([ *   turf.point([26, 37], {foo: 'bar'}), *   turf.point([36, 53], {hello: 'world'}) * ]); * * var coords = turf.coordAll(features); * //= [[26, 37], [36, 53]] */function coordAll(geojson) {  var coords = [];  coordEach(geojson, function (coord) {    coords.push(coord);  });  return coords;}/** * Callback for geomEach * * @callback geomEachCallback * @param {Geometry} currentGeometry The current Geometry being processed. * @param {number} featureIndex The current index of the Feature being processed. * @param {Object} featureProperties The current Feature Properties being processed. * @param {Array<number>} featureBBox The current Feature BBox being processed. * @param {number|string} featureId The current Feature Id being processed. *//** * Iterate over each geometry in any GeoJSON object, similar to Array.forEach() * * @name geomEach * @param {FeatureCollection|Feature|Geometry} geojson any GeoJSON object * @param {Function} callback a method that takes (currentGeometry, featureIndex, featureProperties, featureBBox, featureId) * @returns {void} * @example * var features = turf.featureCollection([ *     turf.point([26, 37], {foo: 'bar'}), *     turf.point([36, 53], {hello: 'world'}) * ]); * * turf.geomEach(features, function (currentGeometry, featureIndex, featureProperties, featureBBox, featureId) { *   //=currentGeometry *   //=featureIndex *   //=featureProperties *   //=featureBBox *   //=featureId * }); */function geomEach(geojson, callback) {  var i,    j,    g,    geometry,    stopG,    geometryMaybeCollection,    isGeometryCollection,    featureProperties,    featureBBox,    featureId,    featureIndex = 0,    isFeatureCollection = geojson.type === "FeatureCollection",    isFeature = geojson.type === "Feature",    stop = isFeatureCollection ? geojson.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      ? geojson.features[i].geometry      : isFeature      ? geojson.geometry      : geojson;    featureProperties = isFeatureCollection      ? geojson.features[i].properties      : isFeature      ? geojson.properties      : {};    featureBBox = isFeatureCollection      ? geojson.features[i].bbox      : isFeature      ? geojson.bbox      : undefined;    featureId = isFeatureCollection      ? geojson.features[i].id      : isFeature      ? geojson.id      : undefined;    isGeometryCollection = geometryMaybeCollection      ? geometryMaybeCollection.type === "GeometryCollection"      : false;    stopG = isGeometryCollection      ? geometryMaybeCollection.geometries.length      : 1;    for (g = 0; g < stopG; g++) {      geometry = isGeometryCollection        ? geometryMaybeCollection.geometries[g]        : geometryMaybeCollection;      // Handle null Geometry      if (geometry === null) {        if (          callback(            null,            featureIndex,            featureProperties,            featureBBox,            featureId          ) === false        )          return false;        continue;      }      switch (geometry.type) {        case "Point":        case "LineString":        case "MultiPoint":        case "Polygon":        case "MultiLineString":        case "MultiPolygon": {          if (            callback(              geometry,              featureIndex,              featureProperties,              featureBBox,              featureId            ) === false          )            return false;          break;        }        case "GeometryCollection": {          for (j = 0; j < geometry.geometries.length; j++) {            if (              callback(                geometry.geometries[j],                featureIndex,                featureProperties,                featureBBox,                featureId              ) === false            )              return false;          }          break;        }        default:          throw new Error("Unknown Geometry Type");      }    }    // Only increase `featureIndex` per each feature    featureIndex++;  }}/** * 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. * * @callback geomReduceCallback * @param {*} previousValue The accumulated value previously returned in the last invocation * of the callback, or initialValue, if supplied. * @param {Geometry} currentGeometry The current Geometry being processed. * @param {number} featureIndex The current index of the Feature being processed. * @param {Object} featureProperties The current Feature Properties being processed. * @param {Array<number>} featureBBox The current Feature BBox being processed. * @param {number|string} featureId The current Feature Id being processed. *//** * Reduce geometry in any GeoJSON object, similar to Array.reduce(). * * @name geomReduce * @param {FeatureCollection|Feature|Geometry} geojson any GeoJSON object * @param {Function} callback a method that takes (previousValue, currentGeometry, featureIndex, featureProperties, featureBBox, featureId) * @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 = turf.featureCollection([ *     turf.point([26, 37], {foo: 'bar'}), *     turf.point([36, 53], {hello: 'world'}) * ]); * * turf.geomReduce(features, function (previousValue, currentGeometry, featureIndex, featureProperties, featureBBox, featureId) { *   //=previousValue *   //=currentGeometry *   //=featureIndex *   //=featureProperties *   //=featureBBox *   //=featureId *   return currentGeometry * }); */function geomReduce(geojson, callback, initialValue) {  var previousValue = initialValue;  geomEach(    geojson,    function (      currentGeometry,      featureIndex,      featureProperties,      featureBBox,      featureId    ) {      if (featureIndex === 0 && initialValue === undefined)        previousValue = currentGeometry;      else        previousValue = callback(          previousValue,          currentGeometry,          featureIndex,          featureProperties,          featureBBox,          featureId        );    }  );  return previousValue;}/** * Callback for flattenEach * * @callback flattenEachCallback * @param {Feature} currentFeature The current flattened feature being processed. * @param {number} featureIndex The current index of the Feature being processed. * @param {number} multiFeatureIndex The current index of the Multi-Feature being processed. *//** * Iterate over flattened features in any GeoJSON object, similar to * Array.forEach. * * @name flattenEach * @param {FeatureCollection|Feature|Geometry} geojson any GeoJSON object * @param {Function} callback a method that takes (currentFeature, featureIndex, multiFeatureIndex) * @example * var features = turf.featureCollection([ *     turf.point([26, 37], {foo: 'bar'}), *     turf.multiPoint([[40, 30], [36, 53]], {hello: 'world'}) * ]); * * turf.flattenEach(features, function (currentFeature, featureIndex, multiFeatureIndex) { *   //=currentFeature *   //=featureIndex *   //=multiFeatureIndex * }); */function flattenEach(geojson, callback) {  geomEach(geojson, function (geometry, featureIndex, properties, bbox, id) {    // Callback for single geometry    var type = geometry === null ? null : geometry.type;    switch (type) {      case null:      case "Point":      case "LineString":      case "Polygon":        if (          callback(            feature(geometry, properties, { bbox: bbox, id: id }),            featureIndex,            0          ) === false        )          return false;        return;    }    var geomType;    // Callback for multi-geometry    switch (type) {      case "MultiPoint":        geomType = "Point";        break;      case "MultiLineString":        geomType = "LineString";        break;      case "MultiPolygon":        geomType = "Polygon";        break;    }    for (      var multiFeatureIndex = 0;      multiFeatureIndex < geometry.coordinates.length;      multiFeatureIndex++    ) {      var coordinate = geometry.coordinates[multiFeatureIndex];      var geom = {        type: geomType,        coordinates: coordinate,      };      if (        callback(feature(geom, properties), featureIndex, multiFeatureIndex) ===        false      )        return false;    }  });}/** * Callback for flattenReduce * * 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. * * @callback flattenReduceCallback * @param {*} previousValue The accumulated value previously returned in the last invocation * of the callback, or initialValue, if supplied. * @param {Feature} currentFeature The current Feature being processed. * @param {number} featureIndex The current index of the Feature being processed. * @param {number} multiFeatureIndex The current index of the Multi-Feature being processed. *//** * Reduce flattened features in any GeoJSON object, similar to Array.reduce(). * * @name flattenReduce * @param {FeatureCollection|Feature|Geometry} geojson any GeoJSON object * @param {Function} callback a method that takes (previousValue, currentFeature, featureIndex, multiFeatureIndex) * @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 = turf.featureCollection([ *     turf.point([26, 37], {foo: 'bar'}), *     turf.multiPoint([[40, 30], [36, 53]], {hello: 'world'}) * ]); * * turf.flattenReduce(features, function (previousValue, currentFeature, featureIndex, multiFeatureIndex) { *   //=previousValue *   //=currentFeature *   //=featureIndex *   //=multiFeatureIndex *   return currentFeature * }); */function flattenReduce(geojson, callback, initialValue) {  var previousValue = initialValue;  flattenEach(    geojson,    function (currentFeature, featureIndex, multiFeatureIndex) {      if (        featureIndex === 0 &&        multiFeatureIndex === 0 &&        initialValue === undefined      )        previousValue = currentFeature;      else        previousValue = callback(          previousValue,          currentFeature,          featureIndex,          multiFeatureIndex        );    }  );  return previousValue;}/** * Callback for segmentEach * * @callback segmentEachCallback * @param {Feature<LineString>} currentSegment The current Segment being processed. * @param {number} featureIndex The current index of the Feature being processed. * @param {number} multiFeatureIndex The current index of the Multi-Feature being processed. * @param {number} geometryIndex The current index of the Geometry being processed. * @param {number} segmentIndex The current index of the Segment being processed. * @returns {void} *//** * Iterate over 2-vertex line segment in any GeoJSON object, similar to Array.forEach() * (Multi)Point geometries do not contain segments therefore they are ignored during this operation. * * @param {FeatureCollection|Feature|Geometry} geojson any GeoJSON * @param {Function} callback a method that takes (currentSegment, featureIndex, multiFeatureIndex, geometryIndex, segmentIndex) * @returns {void} * @example * var polygon = turf.polygon([[[-50, 5], [-40, -10], [-50, -10], [-40, 5], [-50, 5]]]); * * // Iterate over GeoJSON by 2-vertex segments * turf.segmentEach(polygon, function (currentSegment, featureIndex, multiFeatureIndex, geometryIndex, segmentIndex) { *   //=currentSegment *   //=featureIndex *   //=multiFeatureIndex *   //=geometryIndex *   //=segmentIndex * }); * * // Calculate the total number of segments * var total = 0; * turf.segmentEach(polygon, function () { *     total++; * }); */function segmentEach(geojson, callback) {  flattenEach(geojson, function (feature, featureIndex, multiFeatureIndex) {    var segmentIndex = 0;    // Exclude null Geometries    if (!feature.geometry) return;    // (Multi)Point geometries do not contain segments therefore they are ignored during this operation.    var type = feature.geometry.type;    if (type === "Point" || type === "MultiPoint") return;    // Generate 2-vertex line segments    var previousCoords;    var previousFeatureIndex = 0;    var previousMultiIndex = 0;    var prevGeomIndex = 0;    if (      coordEach(        feature,        function (          currentCoord,          coordIndex,          featureIndexCoord,          multiPartIndexCoord,          geometryIndex        ) {          // Simulating a meta.coordReduce() since `reduce` operations cannot be stopped by returning `false`          if (            previousCoords === undefined ||            featureIndex > previousFeatureIndex ||            multiPartIndexCoord > previousMultiIndex ||            geometryIndex > prevGeomIndex          ) {            previousCoords = currentCoord;            previousFeatureIndex = featureIndex;            previousMultiIndex = multiPartIndexCoord;            prevGeomIndex = geometryIndex;            segmentIndex = 0;            return;          }          var currentSegment = lineString(            [previousCoords, currentCoord],            feature.properties          );          if (            callback(              currentSegment,              featureIndex,              multiFeatureIndex,              geometryIndex,              segmentIndex            ) === false          )            return false;          segmentIndex++;          previousCoords = currentCoord;        }      ) === false    )      return false;  });}/** * Callback for segmentReduce * * 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. * * @callback segmentReduceCallback * @param {*} previousValue The accumulated value previously returned in the last invocation * of the callback, or initialValue, if supplied. * @param {Feature<LineString>} currentSegment The current Segment being processed. * @param {number} featureIndex The current index of the Feature being processed. * @param {number} multiFeatureIndex The current index of the Multi-Feature being processed. * @param {number} geometryIndex The current index of the Geometry being processed. * @param {number} segmentIndex The current index of the Segment being processed. *//** * Reduce 2-vertex line segment in any GeoJSON object, similar to Array.reduce() * (Multi)Point geometries do not contain segments therefore they are ignored during this operation. * * @param {FeatureCollection|Feature|Geometry} geojson any GeoJSON * @param {Function} callback a method that takes (previousValue, currentSegment, currentIndex) * @param {*} [initialValue] Value to use as the first argument to the first call of the callback. * @returns {void} * @example * var polygon = turf.polygon([[[-50, 5], [-40, -10], [-50, -10], [-40, 5], [-50, 5]]]); * * // Iterate over GeoJSON by 2-vertex segments * turf.segmentReduce(polygon, function (previousSegment, currentSegment, featureIndex, multiFeatureIndex, geometryIndex, segmentIndex) { *   //= previousSegment *   //= currentSegment *   //= featureIndex *   //= multiFeatureIndex *   //= geometryIndex *   //= segmentIndex *   return currentSegment * }); * * // Calculate the total number of segments * var initialValue = 0 * var total = turf.segmentReduce(polygon, function (previousValue) { *     previousValue++; *     return previousValue; * }, initialValue); */function segmentReduce(geojson, callback, initialValue) {  var previousValue = initialValue;  var started = false;  segmentEach(    geojson,    function (      currentSegment,      featureIndex,      multiFeatureIndex,      geometryIndex,      segmentIndex    ) {      if (started === false && initialValue === undefined)        previousValue = currentSegment;      else        previousValue = callback(          previousValue,          currentSegment,          featureIndex,          multiFeatureIndex,          geometryIndex,          segmentIndex        );      started = true;    }  );  return previousValue;}/** * Callback for lineEach * * @callback lineEachCallback * @param {Feature<LineString>} currentLine The current LineString|LinearRing being processed * @param {number} featureIndex The current index of the Feature being processed * @param {number} multiFeatureIndex The current index of the Multi-Feature being processed * @param {number} geometryIndex The current index of the Geometry being processed *//** * Iterate over line or ring coordinates in LineString, Polygon, MultiLineString, MultiPolygon Features or Geometries, * similar to Array.forEach. * * @name lineEach * @param {Geometry|Feature<LineString|Polygon|MultiLineString|MultiPolygon>} geojson object * @param {Function} callback a method that takes (currentLine, featureIndex, multiFeatureIndex, geometryIndex) * @example * var multiLine = turf.multiLineString([ *   [[26, 37], [35, 45]], *   [[36, 53], [38, 50], [41, 55]] * ]); * * turf.lineEach(multiLine, function (currentLine, featureIndex, multiFeatureIndex, geometryIndex) { *   //=currentLine *   //=featureIndex *   //=multiFeatureIndex *   //=geometryIndex * }); */function lineEach(geojson, callback) {  // validation  if (!geojson) throw new Error("geojson is required");  flattenEach(geojson, function (feature, featureIndex, multiFeatureIndex) {    if (feature.geometry === null) return;    var type = feature.geometry.type;    var coords = feature.geometry.coordinates;    switch (type) {      case "LineString":        if (callback(feature, featureIndex, multiFeatureIndex, 0, 0) === false)          return false;        break;      case "Polygon":        for (          var geometryIndex = 0;          geometryIndex < coords.length;          geometryIndex++        ) {          if (            callback(              lineString(coords[geometryIndex], feature.properties),              featureIndex,              multiFeatureIndex,              geometryIndex            ) === false          )            return false;        }        break;    }  });}/** * Callback for lineReduce * * 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. * * @callback lineReduceCallback * @param {*} previousValue The accumulated value previously returned in the last invocation * of the callback, or initialValue, if supplied. * @param {Feature<LineString>} currentLine The current LineString|LinearRing being processed. * @param {number} featureIndex The current index of the Feature being processed * @param {number} multiFeatureIndex The current index of the Multi-Feature being processed * @param {number} geometryIndex The current index of the Geometry being processed *//** * Reduce features in any GeoJSON object, similar to Array.reduce(). * * @name lineReduce * @param {Geometry|Feature<LineString|Polygon|MultiLineString|MultiPolygon>} geojson object * @param {Function} callback a method that takes (previousValue, currentLine, featureIndex, multiFeatureIndex, geometryIndex) * @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 multiPoly = turf.multiPolygon([ *   turf.polygon([[[12,48],[2,41],[24,38],[12,48]], [[9,44],[13,41],[13,45],[9,44]]]), *   turf.polygon([[[5, 5], [0, 0], [2, 2], [4, 4], [5, 5]]]) * ]); * * turf.lineReduce(multiPoly, function (previousValue, currentLine, featureIndex, multiFeatureIndex, geometryIndex) { *   //=previousValue *   //=currentLine *   //=featureIndex *   //=multiFeatureIndex *   //=geometryIndex *   return currentLine * }); */function lineReduce(geojson, callback, initialValue) {  var previousValue = initialValue;  lineEach(    geojson,    function (currentLine, featureIndex, multiFeatureIndex, geometryIndex) {      if (featureIndex === 0 && initialValue === undefined)        previousValue = currentLine;      else        previousValue = callback(          previousValue,          currentLine,          featureIndex,          multiFeatureIndex,          geometryIndex        );    }  );  return previousValue;}/** * Finds a particular 2-vertex LineString Segment from a GeoJSON using `@turf/meta` indexes. * * Negative indexes are permitted. * Point & MultiPoint will always return null. * * @param {FeatureCollection|Feature|Geometry} geojson Any GeoJSON Feature or Geometry * @param {Object} [options={}] Optional parameters * @param {number} [options.featureIndex=0] Feature Index * @param {number} [options.multiFeatureIndex=0] Multi-Feature Index * @param {number} [options.geometryIndex=0] Geometry Index * @param {number} [options.segmentIndex=0] Segment Index * @param {Object} [options.properties={}] Translate Properties to output LineString * @param {BBox} [options.bbox={}] Translate BBox to output LineString * @param {number|string} [options.id={}] Translate Id to output LineString * @returns {Feature<LineString>} 2-vertex GeoJSON Feature LineString * @example * var multiLine = turf.multiLineString([ *     [[10, 10], [50, 30], [30, 40]], *     [[-10, -10], [-50, -30], [-30, -40]] * ]); * * // First Segment (defaults are 0) * turf.findSegment(multiLine); * // => Feature<LineString<[[10, 10], [50, 30]]>> * * // First Segment of 2nd Multi Feature * turf.findSegment(multiLine, {multiFeatureIndex: 1}); * // => Feature<LineString<[[-10, -10], [-50, -30]]>> * * // Last Segment of Last Multi Feature * turf.findSegment(multiLine, {multiFeatureIndex: -1, segmentIndex: -1}); * // => Feature<LineString<[[-50, -30], [-30, -40]]>> */function findSegment(geojson, options) {  // Optional Parameters  options = options || {};  if (!isObject(options)) throw new Error("options is invalid");  var featureIndex = options.featureIndex || 0;  var multiFeatureIndex = options.multiFeatureIndex || 0;  var geometryIndex = options.geometryIndex || 0;  var segmentIndex = options.segmentIndex || 0;  // Find FeatureIndex  var properties = options.properties;  var geometry;  switch (geojson.type) {    case "FeatureCollection":      if (featureIndex < 0)        featureIndex = geojson.features.length + featureIndex;      properties = properties || geojson.features[featureIndex].properties;      geometry = geojson.features[featureIndex].geometry;      break;    case "Feature":      properties = properties || geojson.properties;      geometry = geojson.geometry;      break;    case "Point":    case "MultiPoint":      return null;    case "LineString":    case "Polygon":    case "MultiLineString":    case "MultiPolygon":      geometry = geojson;      break;    default:      throw new Error("geojson is invalid");  }  // Find SegmentIndex  if (geometry === null) return null;  var coords = geometry.coordinates;  switch (geometry.type) {    case "Point":    case "MultiPoint":      return null;    case "LineString":      if (segmentIndex < 0) segmentIndex = coords.length + segmentIndex - 1;      return lineString(        [coords[segmentIndex], coords[segmentIndex + 1]],        properties,        options      );    case "Polygon":      if (geometryIndex < 0) geometryIndex = coords.length + geometryIndex;      if (segmentIndex < 0)        segmentIndex = coords[geometryIndex].length + segmentIndex - 1;      return lineString(        [          coords[geometryIndex][segmentIndex],          coords[geometryIndex][segmentIndex + 1],        ],        properties,        options      );    case "MultiLineString":      if (multiFeatureIndex < 0)        multiFeatureIndex = coords.length + multiFeatureIndex;      if (segmentIndex < 0)        segmentIndex = coords[multiFeatureIndex].length + segmentIndex - 1;      return lineString(        [          coords[multiFeatureIndex][segmentIndex],          coords[multiFeatureIndex][segmentIndex + 1],        ],        properties,        options      );    case "MultiPolygon":      if (multiFeatureIndex < 0)        multiFeatureIndex = coords.length + multiFeatureIndex;      if (geometryIndex < 0)        geometryIndex = coords[multiFeatureIndex].length + geometryIndex;      if (segmentIndex < 0)        segmentIndex =          coords[multiFeatureIndex][geometryIndex].length - segmentIndex - 1;      return lineString(        [          coords[multiFeatureIndex][geometryIndex][segmentIndex],          coords[multiFeatureIndex][geometryIndex][segmentIndex + 1],        ],        properties,        options      );  }  throw new Error("geojson is invalid");}/** * Finds a particular Point from a GeoJSON using `@turf/meta` indexes. * * Negative indexes are permitted. * * @param {FeatureCollection|Feature|Geometry} geojson Any GeoJSON Feature or Geometry * @param {Object} [options={}] Optional parameters * @param {number} [options.featureIndex=0] Feature Index * @param {number} [options.multiFeatureIndex=0] Multi-Feature Index * @param {number} [options.geometryIndex=0] Geometry Index * @param {number} [options.coordIndex=0] Coord Index * @param {Object} [options.properties={}] Translate Properties to output Point * @param {BBox} [options.bbox={}] Translate BBox to output Point * @param {number|string} [options.id={}] Translate Id to output Point * @returns {Feature<Point>} 2-vertex GeoJSON Feature Point * @example * var multiLine = turf.multiLineString([ *     [[10, 10], [50, 30], [30, 40]], *     [[-10, -10], [-50, -30], [-30, -40]] * ]); * * // First Segment (defaults are 0) * turf.findPoint(multiLine); * // => Feature<Point<[10, 10]>> * * // First Segment of the 2nd Multi-Feature * turf.findPoint(multiLine, {multiFeatureIndex: 1}); * // => Feature<Point<[-10, -10]>> * * // Last Segment of last Multi-Feature * turf.findPoint(multiLine, {multiFeatureIndex: -1, coordIndex: -1}); * // => Feature<Point<[-30, -40]>> */function findPoint(geojson, options) {  // Optional Parameters  options = options || {};  if (!isObject(options)) throw new Error("options is invalid");  var featureIndex = options.featureIndex || 0;  var multiFeatureIndex = options.multiFeatureIndex || 0;  var geometryIndex = options.geometryIndex || 0;  var coordIndex = options.coordIndex || 0;  // Find FeatureIndex  var properties = options.properties;  var geometry;  switch (geojson.type) {    case "FeatureCollection":      if (featureIndex < 0)        featureIndex = geojson.features.length + featureIndex;      properties = properties || geojson.features[featureIndex].properties;      geometry = geojson.features[featureIndex].geometry;      break;    case "Feature":      properties = properties || geojson.properties;      geometry = geojson.geometry;      break;    case "Point":    case "MultiPoint":      return null;    case "LineString":    case "Polygon":    case "MultiLineString":    case "MultiPolygon":      geometry = geojson;      break;    default:      throw new Error("geojson is invalid");  }  // Find Coord Index  if (geometry === null) return null;  var coords = geometry.coordinates;  switch (geometry.type) {    case "Point":      return point(coords, properties, options);    case "MultiPoint":      if (multiFeatureIndex < 0)        multiFeatureIndex = coords.length + multiFeatureIndex;      return point(coords[multiFeatureIndex], properties, options);    case "LineString":      if (coordIndex < 0) coordIndex = coords.length + coordIndex;      return point(coords[coordIndex], properties, options);    case "Polygon":      if (geometryIndex < 0) geometryIndex = coords.length + geometryIndex;      if (coordIndex < 0)        coordIndex = coords[geometryIndex].length + coordIndex;      return point(coords[geometryIndex][coordIndex], properties, options);    case "MultiLineString":      if (multiFeatureIndex < 0)        multiFeatureIndex = coords.length + multiFeatureIndex;      if (coordIndex < 0)        coordIndex = coords[multiFeatureIndex].length + coordIndex;      return point(coords[multiFeatureIndex][coordIndex], properties, options);    case "MultiPolygon":      if (multiFeatureIndex < 0)        multiFeatureIndex = coords.length + multiFeatureIndex;      if (geometryIndex < 0)        geometryIndex = coords[multiFeatureIndex].length + geometryIndex;      if (coordIndex < 0)        coordIndex =          coords[multiFeatureIndex][geometryIndex].length - coordIndex;      return point(        coords[multiFeatureIndex][geometryIndex][coordIndex],        properties,        options      );  }  throw new Error("geojson is invalid");}export { coordAll, coordEach, coordReduce, featureEach, featureReduce, findPoint, findSegment, flattenEach, flattenReduce, geomEach, geomReduce, lineEach, lineReduce, propEach, propReduce, segmentEach, segmentReduce };
 |