text: import { feature, lineString } 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.
   * @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':
                  callback(coords, coordIndex, featureIndex, multiFeatureIndex, geometryIndex);
                  coordIndex++;
                  multiFeatureIndex++;
                  break;
              case 'LineString':
              case 'MultiPoint':
                  for (j = 0; j < coords.length; j++) {
                      callback(coords[j], coordIndex, featureIndex, multiFeatureIndex, geometryIndex);
                      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++) {
                          callback(coords[j][k], coordIndex, featureIndex, multiFeatureIndex, geometryIndex);
                          coordIndex++;
                      }
                      if (geomType === 'MultiLineString') multiFeatureIndex++;
                      if (geomType === 'Polygon') geometryIndex++;
                  }
                  if (geomType === 'Polygon') multiFeatureIndex++;
                  break;
              case 'MultiPolygon':
                  for (j = 0; j < coords.length; j++) {
                      if (geomType === 'MultiPolygon') geometryIndex = 0;
                      for (k = 0; k < coords[j].length; k++) {
                          for (l = 0; l < coords[j][k].length - wrapShrink; l++) {
                              callback(coords[j][k][l], coordIndex, featureIndex, multiFeatureIndex, geometryIndex);
                              coordIndex++;
                          }
                          geometryIndex++;
                      }
                      multiFeatureIndex++;
                  }
                  break;
              case 'GeometryCollection':
                  for (j = 0; j < geometry.geometries.length; j++)
                      coordEach(geometry.geometries[j], callback, excludeWrapCoord);
                  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)
   * @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++) {
              callback(geojson.features[i].properties, i);
          }
          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)
   * @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++) {
              callback(geojson.features[i], i);
          }
      }
  }
  
  /**
   * 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)
   * @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) {
                  callback(null, featureIndex, featureProperties, featureBBox, featureId);
                  continue;
              }
              switch (geometry.type) {
              case 'Point':
              case 'LineString':
              case 'MultiPoint':
              case 'Polygon':
              case 'MultiLineString':
              case 'MultiPolygon': {
                  callback(geometry, featureIndex, featureProperties, featureBBox, featureId);
                  break;
              }
              case 'GeometryCollection': {
                  for (j = 0; j < geometry.geometries.length; j++) {
                      callback(geometry.geometries[j], featureIndex, featureProperties, featureBBox, featureId);
                  }
                  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':
              callback(feature(geometry, properties, {bbox: bbox, id: id}), featureIndex, 0);
              return;
          }
  
          var geomType;
  
          // Callback for multi-geometry
          switch (type) {
          case 'MultiPoint':
              geomType = 'Point';
              break;
          case 'MultiLineString':
              geomType = 'LineString';
              break;
          case 'MultiPolygon':
              geomType = 'Polygon';
              break;
          }
  
          geometry.coordinates.forEach(function (coordinate, multiFeatureIndex) {
              var geom = {
                  type: geomType,
                  coordinates: coordinate
              };
              callback(feature(geom, properties), featureIndex, multiFeatureIndex);
          });
  
      });
  }
  
  /**
   * 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$$1, featureIndex, multiFeatureIndex) {
          var segmentIndex = 0;
  
          // Exclude null Geometries
          if (!feature$$1.geometry) return;
          // (Multi)Point geometries do not contain segments therefore they are ignored during this operation.
          var type = feature$$1.geometry.type;
          if (type === 'Point' || type === 'MultiPoint') return;
  
          // Generate 2-vertex line segments
          coordReduce(feature$$1, function (previousCoords, currentCoord, coordIndex, featureIndexCoord, mutliPartIndexCoord, geometryIndex) {
              var currentSegment = lineString([previousCoords, currentCoord], feature$$1.properties);
              callback(currentSegment, featureIndex, multiFeatureIndex, geometryIndex, segmentIndex);
              segmentIndex++;
              return currentCoord;
          });
      });
  }
  
  /**
   * 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
   *   //= segmentInex
   *   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$$1, featureIndex, multiFeatureIndex) {
          if (feature$$1.geometry === null) return;
          var type = feature$$1.geometry.type;
          var coords = feature$$1.geometry.coordinates;
          switch (type) {
          case 'LineString':
              callback(feature$$1, featureIndex, multiFeatureIndex, 0, 0);
              break;
          case 'Polygon':
              for (var geometryIndex = 0; geometryIndex < coords.length; geometryIndex++) {
                  callback(lineString(coords[geometryIndex], feature$$1.properties), featureIndex, multiFeatureIndex, geometryIndex);
              }
              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;
  }
  
  export { coordEach, coordReduce, propEach, propReduce, featureEach, featureReduce, coordAll, geomEach, geomReduce, flattenEach, flattenReduce, segmentEach, segmentReduce, lineEach, lineReduce };
  