index.js 47 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401
  1. import { feature, lineString, isObject, point } from '@turf/helpers';
  2. /**
  3. * Callback for coordEach
  4. *
  5. * @callback coordEachCallback
  6. * @param {Array<number>} currentCoord The current coordinate being processed.
  7. * @param {number} coordIndex The current index of the coordinate being processed.
  8. * @param {number} featureIndex The current index of the Feature being processed.
  9. * @param {number} multiFeatureIndex The current index of the Multi-Feature being processed.
  10. * @param {number} geometryIndex The current index of the Geometry being processed.
  11. */
  12. /**
  13. * Iterate over coordinates in any GeoJSON object, similar to Array.forEach()
  14. *
  15. * @name coordEach
  16. * @param {FeatureCollection|Feature|Geometry} geojson any GeoJSON object
  17. * @param {Function} callback a method that takes (currentCoord, coordIndex, featureIndex, multiFeatureIndex)
  18. * @param {boolean} [excludeWrapCoord=false] whether or not to include the final coordinate of LinearRings that wraps the ring in its iteration.
  19. * @returns {void}
  20. * @example
  21. * var features = turf.featureCollection([
  22. * turf.point([26, 37], {"foo": "bar"}),
  23. * turf.point([36, 53], {"hello": "world"})
  24. * ]);
  25. *
  26. * turf.coordEach(features, function (currentCoord, coordIndex, featureIndex, multiFeatureIndex, geometryIndex) {
  27. * //=currentCoord
  28. * //=coordIndex
  29. * //=featureIndex
  30. * //=multiFeatureIndex
  31. * //=geometryIndex
  32. * });
  33. */
  34. function coordEach(geojson, callback, excludeWrapCoord) {
  35. // Handles null Geometry -- Skips this GeoJSON
  36. if (geojson === null) return;
  37. var j,
  38. k,
  39. l,
  40. geometry,
  41. stopG,
  42. coords,
  43. geometryMaybeCollection,
  44. wrapShrink = 0,
  45. coordIndex = 0,
  46. isGeometryCollection,
  47. type = geojson.type,
  48. isFeatureCollection = type === "FeatureCollection",
  49. isFeature = type === "Feature",
  50. stop = isFeatureCollection ? geojson.features.length : 1;
  51. // This logic may look a little weird. The reason why it is that way
  52. // is because it's trying to be fast. GeoJSON supports multiple kinds
  53. // of objects at its root: FeatureCollection, Features, Geometries.
  54. // This function has the responsibility of handling all of them, and that
  55. // means that some of the `for` loops you see below actually just don't apply
  56. // to certain inputs. For instance, if you give this just a
  57. // Point geometry, then both loops are short-circuited and all we do
  58. // is gradually rename the input until it's called 'geometry'.
  59. //
  60. // This also aims to allocate as few resources as possible: just a
  61. // few numbers and booleans, rather than any temporary arrays as would
  62. // be required with the normalization approach.
  63. for (var featureIndex = 0; featureIndex < stop; featureIndex++) {
  64. geometryMaybeCollection = isFeatureCollection
  65. ? geojson.features[featureIndex].geometry
  66. : isFeature
  67. ? geojson.geometry
  68. : geojson;
  69. isGeometryCollection = geometryMaybeCollection
  70. ? geometryMaybeCollection.type === "GeometryCollection"
  71. : false;
  72. stopG = isGeometryCollection
  73. ? geometryMaybeCollection.geometries.length
  74. : 1;
  75. for (var geomIndex = 0; geomIndex < stopG; geomIndex++) {
  76. var multiFeatureIndex = 0;
  77. var geometryIndex = 0;
  78. geometry = isGeometryCollection
  79. ? geometryMaybeCollection.geometries[geomIndex]
  80. : geometryMaybeCollection;
  81. // Handles null Geometry -- Skips this geometry
  82. if (geometry === null) continue;
  83. coords = geometry.coordinates;
  84. var geomType = geometry.type;
  85. wrapShrink =
  86. excludeWrapCoord &&
  87. (geomType === "Polygon" || geomType === "MultiPolygon")
  88. ? 1
  89. : 0;
  90. switch (geomType) {
  91. case null:
  92. break;
  93. case "Point":
  94. if (
  95. callback(
  96. coords,
  97. coordIndex,
  98. featureIndex,
  99. multiFeatureIndex,
  100. geometryIndex
  101. ) === false
  102. )
  103. return false;
  104. coordIndex++;
  105. multiFeatureIndex++;
  106. break;
  107. case "LineString":
  108. case "MultiPoint":
  109. for (j = 0; j < coords.length; j++) {
  110. if (
  111. callback(
  112. coords[j],
  113. coordIndex,
  114. featureIndex,
  115. multiFeatureIndex,
  116. geometryIndex
  117. ) === false
  118. )
  119. return false;
  120. coordIndex++;
  121. if (geomType === "MultiPoint") multiFeatureIndex++;
  122. }
  123. if (geomType === "LineString") multiFeatureIndex++;
  124. break;
  125. case "Polygon":
  126. case "MultiLineString":
  127. for (j = 0; j < coords.length; j++) {
  128. for (k = 0; k < coords[j].length - wrapShrink; k++) {
  129. if (
  130. callback(
  131. coords[j][k],
  132. coordIndex,
  133. featureIndex,
  134. multiFeatureIndex,
  135. geometryIndex
  136. ) === false
  137. )
  138. return false;
  139. coordIndex++;
  140. }
  141. if (geomType === "MultiLineString") multiFeatureIndex++;
  142. if (geomType === "Polygon") geometryIndex++;
  143. }
  144. if (geomType === "Polygon") multiFeatureIndex++;
  145. break;
  146. case "MultiPolygon":
  147. for (j = 0; j < coords.length; j++) {
  148. geometryIndex = 0;
  149. for (k = 0; k < coords[j].length; k++) {
  150. for (l = 0; l < coords[j][k].length - wrapShrink; l++) {
  151. if (
  152. callback(
  153. coords[j][k][l],
  154. coordIndex,
  155. featureIndex,
  156. multiFeatureIndex,
  157. geometryIndex
  158. ) === false
  159. )
  160. return false;
  161. coordIndex++;
  162. }
  163. geometryIndex++;
  164. }
  165. multiFeatureIndex++;
  166. }
  167. break;
  168. case "GeometryCollection":
  169. for (j = 0; j < geometry.geometries.length; j++)
  170. if (
  171. coordEach(geometry.geometries[j], callback, excludeWrapCoord) ===
  172. false
  173. )
  174. return false;
  175. break;
  176. default:
  177. throw new Error("Unknown Geometry Type");
  178. }
  179. }
  180. }
  181. }
  182. /**
  183. * Callback for coordReduce
  184. *
  185. * The first time the callback function is called, the values provided as arguments depend
  186. * on whether the reduce method has an initialValue argument.
  187. *
  188. * If an initialValue is provided to the reduce method:
  189. * - The previousValue argument is initialValue.
  190. * - The currentValue argument is the value of the first element present in the array.
  191. *
  192. * If an initialValue is not provided:
  193. * - The previousValue argument is the value of the first element present in the array.
  194. * - The currentValue argument is the value of the second element present in the array.
  195. *
  196. * @callback coordReduceCallback
  197. * @param {*} previousValue The accumulated value previously returned in the last invocation
  198. * of the callback, or initialValue, if supplied.
  199. * @param {Array<number>} currentCoord The current coordinate being processed.
  200. * @param {number} coordIndex The current index of the coordinate being processed.
  201. * Starts at index 0, if an initialValue is provided, and at index 1 otherwise.
  202. * @param {number} featureIndex The current index of the Feature being processed.
  203. * @param {number} multiFeatureIndex The current index of the Multi-Feature being processed.
  204. * @param {number} geometryIndex The current index of the Geometry being processed.
  205. */
  206. /**
  207. * Reduce coordinates in any GeoJSON object, similar to Array.reduce()
  208. *
  209. * @name coordReduce
  210. * @param {FeatureCollection|Geometry|Feature} geojson any GeoJSON object
  211. * @param {Function} callback a method that takes (previousValue, currentCoord, coordIndex)
  212. * @param {*} [initialValue] Value to use as the first argument to the first call of the callback.
  213. * @param {boolean} [excludeWrapCoord=false] whether or not to include the final coordinate of LinearRings that wraps the ring in its iteration.
  214. * @returns {*} The value that results from the reduction.
  215. * @example
  216. * var features = turf.featureCollection([
  217. * turf.point([26, 37], {"foo": "bar"}),
  218. * turf.point([36, 53], {"hello": "world"})
  219. * ]);
  220. *
  221. * turf.coordReduce(features, function (previousValue, currentCoord, coordIndex, featureIndex, multiFeatureIndex, geometryIndex) {
  222. * //=previousValue
  223. * //=currentCoord
  224. * //=coordIndex
  225. * //=featureIndex
  226. * //=multiFeatureIndex
  227. * //=geometryIndex
  228. * return currentCoord;
  229. * });
  230. */
  231. function coordReduce(geojson, callback, initialValue, excludeWrapCoord) {
  232. var previousValue = initialValue;
  233. coordEach(
  234. geojson,
  235. function (
  236. currentCoord,
  237. coordIndex,
  238. featureIndex,
  239. multiFeatureIndex,
  240. geometryIndex
  241. ) {
  242. if (coordIndex === 0 && initialValue === undefined)
  243. previousValue = currentCoord;
  244. else
  245. previousValue = callback(
  246. previousValue,
  247. currentCoord,
  248. coordIndex,
  249. featureIndex,
  250. multiFeatureIndex,
  251. geometryIndex
  252. );
  253. },
  254. excludeWrapCoord
  255. );
  256. return previousValue;
  257. }
  258. /**
  259. * Callback for propEach
  260. *
  261. * @callback propEachCallback
  262. * @param {Object} currentProperties The current Properties being processed.
  263. * @param {number} featureIndex The current index of the Feature being processed.
  264. */
  265. /**
  266. * Iterate over properties in any GeoJSON object, similar to Array.forEach()
  267. *
  268. * @name propEach
  269. * @param {FeatureCollection|Feature} geojson any GeoJSON object
  270. * @param {Function} callback a method that takes (currentProperties, featureIndex)
  271. * @returns {void}
  272. * @example
  273. * var features = turf.featureCollection([
  274. * turf.point([26, 37], {foo: 'bar'}),
  275. * turf.point([36, 53], {hello: 'world'})
  276. * ]);
  277. *
  278. * turf.propEach(features, function (currentProperties, featureIndex) {
  279. * //=currentProperties
  280. * //=featureIndex
  281. * });
  282. */
  283. function propEach(geojson, callback) {
  284. var i;
  285. switch (geojson.type) {
  286. case "FeatureCollection":
  287. for (i = 0; i < geojson.features.length; i++) {
  288. if (callback(geojson.features[i].properties, i) === false) break;
  289. }
  290. break;
  291. case "Feature":
  292. callback(geojson.properties, 0);
  293. break;
  294. }
  295. }
  296. /**
  297. * Callback for propReduce
  298. *
  299. * The first time the callback function is called, the values provided as arguments depend
  300. * on whether the reduce method has an initialValue argument.
  301. *
  302. * If an initialValue is provided to the reduce method:
  303. * - The previousValue argument is initialValue.
  304. * - The currentValue argument is the value of the first element present in the array.
  305. *
  306. * If an initialValue is not provided:
  307. * - The previousValue argument is the value of the first element present in the array.
  308. * - The currentValue argument is the value of the second element present in the array.
  309. *
  310. * @callback propReduceCallback
  311. * @param {*} previousValue The accumulated value previously returned in the last invocation
  312. * of the callback, or initialValue, if supplied.
  313. * @param {*} currentProperties The current Properties being processed.
  314. * @param {number} featureIndex The current index of the Feature being processed.
  315. */
  316. /**
  317. * Reduce properties in any GeoJSON object into a single value,
  318. * similar to how Array.reduce works. However, in this case we lazily run
  319. * the reduction, so an array of all properties is unnecessary.
  320. *
  321. * @name propReduce
  322. * @param {FeatureCollection|Feature} geojson any GeoJSON object
  323. * @param {Function} callback a method that takes (previousValue, currentProperties, featureIndex)
  324. * @param {*} [initialValue] Value to use as the first argument to the first call of the callback.
  325. * @returns {*} The value that results from the reduction.
  326. * @example
  327. * var features = turf.featureCollection([
  328. * turf.point([26, 37], {foo: 'bar'}),
  329. * turf.point([36, 53], {hello: 'world'})
  330. * ]);
  331. *
  332. * turf.propReduce(features, function (previousValue, currentProperties, featureIndex) {
  333. * //=previousValue
  334. * //=currentProperties
  335. * //=featureIndex
  336. * return currentProperties
  337. * });
  338. */
  339. function propReduce(geojson, callback, initialValue) {
  340. var previousValue = initialValue;
  341. propEach(geojson, function (currentProperties, featureIndex) {
  342. if (featureIndex === 0 && initialValue === undefined)
  343. previousValue = currentProperties;
  344. else
  345. previousValue = callback(previousValue, currentProperties, featureIndex);
  346. });
  347. return previousValue;
  348. }
  349. /**
  350. * Callback for featureEach
  351. *
  352. * @callback featureEachCallback
  353. * @param {Feature<any>} currentFeature The current Feature being processed.
  354. * @param {number} featureIndex The current index of the Feature being processed.
  355. */
  356. /**
  357. * Iterate over features in any GeoJSON object, similar to
  358. * Array.forEach.
  359. *
  360. * @name featureEach
  361. * @param {FeatureCollection|Feature|Geometry} geojson any GeoJSON object
  362. * @param {Function} callback a method that takes (currentFeature, featureIndex)
  363. * @returns {void}
  364. * @example
  365. * var features = turf.featureCollection([
  366. * turf.point([26, 37], {foo: 'bar'}),
  367. * turf.point([36, 53], {hello: 'world'})
  368. * ]);
  369. *
  370. * turf.featureEach(features, function (currentFeature, featureIndex) {
  371. * //=currentFeature
  372. * //=featureIndex
  373. * });
  374. */
  375. function featureEach(geojson, callback) {
  376. if (geojson.type === "Feature") {
  377. callback(geojson, 0);
  378. } else if (geojson.type === "FeatureCollection") {
  379. for (var i = 0; i < geojson.features.length; i++) {
  380. if (callback(geojson.features[i], i) === false) break;
  381. }
  382. }
  383. }
  384. /**
  385. * Callback for featureReduce
  386. *
  387. * The first time the callback function is called, the values provided as arguments depend
  388. * on whether the reduce method has an initialValue argument.
  389. *
  390. * If an initialValue is provided to the reduce method:
  391. * - The previousValue argument is initialValue.
  392. * - The currentValue argument is the value of the first element present in the array.
  393. *
  394. * If an initialValue is not provided:
  395. * - The previousValue argument is the value of the first element present in the array.
  396. * - The currentValue argument is the value of the second element present in the array.
  397. *
  398. * @callback featureReduceCallback
  399. * @param {*} previousValue The accumulated value previously returned in the last invocation
  400. * of the callback, or initialValue, if supplied.
  401. * @param {Feature} currentFeature The current Feature being processed.
  402. * @param {number} featureIndex The current index of the Feature being processed.
  403. */
  404. /**
  405. * Reduce features in any GeoJSON object, similar to Array.reduce().
  406. *
  407. * @name featureReduce
  408. * @param {FeatureCollection|Feature|Geometry} geojson any GeoJSON object
  409. * @param {Function} callback a method that takes (previousValue, currentFeature, featureIndex)
  410. * @param {*} [initialValue] Value to use as the first argument to the first call of the callback.
  411. * @returns {*} The value that results from the reduction.
  412. * @example
  413. * var features = turf.featureCollection([
  414. * turf.point([26, 37], {"foo": "bar"}),
  415. * turf.point([36, 53], {"hello": "world"})
  416. * ]);
  417. *
  418. * turf.featureReduce(features, function (previousValue, currentFeature, featureIndex) {
  419. * //=previousValue
  420. * //=currentFeature
  421. * //=featureIndex
  422. * return currentFeature
  423. * });
  424. */
  425. function featureReduce(geojson, callback, initialValue) {
  426. var previousValue = initialValue;
  427. featureEach(geojson, function (currentFeature, featureIndex) {
  428. if (featureIndex === 0 && initialValue === undefined)
  429. previousValue = currentFeature;
  430. else previousValue = callback(previousValue, currentFeature, featureIndex);
  431. });
  432. return previousValue;
  433. }
  434. /**
  435. * Get all coordinates from any GeoJSON object.
  436. *
  437. * @name coordAll
  438. * @param {FeatureCollection|Feature|Geometry} geojson any GeoJSON object
  439. * @returns {Array<Array<number>>} coordinate position array
  440. * @example
  441. * var features = turf.featureCollection([
  442. * turf.point([26, 37], {foo: 'bar'}),
  443. * turf.point([36, 53], {hello: 'world'})
  444. * ]);
  445. *
  446. * var coords = turf.coordAll(features);
  447. * //= [[26, 37], [36, 53]]
  448. */
  449. function coordAll(geojson) {
  450. var coords = [];
  451. coordEach(geojson, function (coord) {
  452. coords.push(coord);
  453. });
  454. return coords;
  455. }
  456. /**
  457. * Callback for geomEach
  458. *
  459. * @callback geomEachCallback
  460. * @param {Geometry} currentGeometry The current Geometry being processed.
  461. * @param {number} featureIndex The current index of the Feature being processed.
  462. * @param {Object} featureProperties The current Feature Properties being processed.
  463. * @param {Array<number>} featureBBox The current Feature BBox being processed.
  464. * @param {number|string} featureId The current Feature Id being processed.
  465. */
  466. /**
  467. * Iterate over each geometry in any GeoJSON object, similar to Array.forEach()
  468. *
  469. * @name geomEach
  470. * @param {FeatureCollection|Feature|Geometry} geojson any GeoJSON object
  471. * @param {Function} callback a method that takes (currentGeometry, featureIndex, featureProperties, featureBBox, featureId)
  472. * @returns {void}
  473. * @example
  474. * var features = turf.featureCollection([
  475. * turf.point([26, 37], {foo: 'bar'}),
  476. * turf.point([36, 53], {hello: 'world'})
  477. * ]);
  478. *
  479. * turf.geomEach(features, function (currentGeometry, featureIndex, featureProperties, featureBBox, featureId) {
  480. * //=currentGeometry
  481. * //=featureIndex
  482. * //=featureProperties
  483. * //=featureBBox
  484. * //=featureId
  485. * });
  486. */
  487. function geomEach(geojson, callback) {
  488. var i,
  489. j,
  490. g,
  491. geometry,
  492. stopG,
  493. geometryMaybeCollection,
  494. isGeometryCollection,
  495. featureProperties,
  496. featureBBox,
  497. featureId,
  498. featureIndex = 0,
  499. isFeatureCollection = geojson.type === "FeatureCollection",
  500. isFeature = geojson.type === "Feature",
  501. stop = isFeatureCollection ? geojson.features.length : 1;
  502. // This logic may look a little weird. The reason why it is that way
  503. // is because it's trying to be fast. GeoJSON supports multiple kinds
  504. // of objects at its root: FeatureCollection, Features, Geometries.
  505. // This function has the responsibility of handling all of them, and that
  506. // means that some of the `for` loops you see below actually just don't apply
  507. // to certain inputs. For instance, if you give this just a
  508. // Point geometry, then both loops are short-circuited and all we do
  509. // is gradually rename the input until it's called 'geometry'.
  510. //
  511. // This also aims to allocate as few resources as possible: just a
  512. // few numbers and booleans, rather than any temporary arrays as would
  513. // be required with the normalization approach.
  514. for (i = 0; i < stop; i++) {
  515. geometryMaybeCollection = isFeatureCollection
  516. ? geojson.features[i].geometry
  517. : isFeature
  518. ? geojson.geometry
  519. : geojson;
  520. featureProperties = isFeatureCollection
  521. ? geojson.features[i].properties
  522. : isFeature
  523. ? geojson.properties
  524. : {};
  525. featureBBox = isFeatureCollection
  526. ? geojson.features[i].bbox
  527. : isFeature
  528. ? geojson.bbox
  529. : undefined;
  530. featureId = isFeatureCollection
  531. ? geojson.features[i].id
  532. : isFeature
  533. ? geojson.id
  534. : undefined;
  535. isGeometryCollection = geometryMaybeCollection
  536. ? geometryMaybeCollection.type === "GeometryCollection"
  537. : false;
  538. stopG = isGeometryCollection
  539. ? geometryMaybeCollection.geometries.length
  540. : 1;
  541. for (g = 0; g < stopG; g++) {
  542. geometry = isGeometryCollection
  543. ? geometryMaybeCollection.geometries[g]
  544. : geometryMaybeCollection;
  545. // Handle null Geometry
  546. if (geometry === null) {
  547. if (
  548. callback(
  549. null,
  550. featureIndex,
  551. featureProperties,
  552. featureBBox,
  553. featureId
  554. ) === false
  555. )
  556. return false;
  557. continue;
  558. }
  559. switch (geometry.type) {
  560. case "Point":
  561. case "LineString":
  562. case "MultiPoint":
  563. case "Polygon":
  564. case "MultiLineString":
  565. case "MultiPolygon": {
  566. if (
  567. callback(
  568. geometry,
  569. featureIndex,
  570. featureProperties,
  571. featureBBox,
  572. featureId
  573. ) === false
  574. )
  575. return false;
  576. break;
  577. }
  578. case "GeometryCollection": {
  579. for (j = 0; j < geometry.geometries.length; j++) {
  580. if (
  581. callback(
  582. geometry.geometries[j],
  583. featureIndex,
  584. featureProperties,
  585. featureBBox,
  586. featureId
  587. ) === false
  588. )
  589. return false;
  590. }
  591. break;
  592. }
  593. default:
  594. throw new Error("Unknown Geometry Type");
  595. }
  596. }
  597. // Only increase `featureIndex` per each feature
  598. featureIndex++;
  599. }
  600. }
  601. /**
  602. * Callback for geomReduce
  603. *
  604. * The first time the callback function is called, the values provided as arguments depend
  605. * on whether the reduce method has an initialValue argument.
  606. *
  607. * If an initialValue is provided to the reduce method:
  608. * - The previousValue argument is initialValue.
  609. * - The currentValue argument is the value of the first element present in the array.
  610. *
  611. * If an initialValue is not provided:
  612. * - The previousValue argument is the value of the first element present in the array.
  613. * - The currentValue argument is the value of the second element present in the array.
  614. *
  615. * @callback geomReduceCallback
  616. * @param {*} previousValue The accumulated value previously returned in the last invocation
  617. * of the callback, or initialValue, if supplied.
  618. * @param {Geometry} currentGeometry The current Geometry being processed.
  619. * @param {number} featureIndex The current index of the Feature being processed.
  620. * @param {Object} featureProperties The current Feature Properties being processed.
  621. * @param {Array<number>} featureBBox The current Feature BBox being processed.
  622. * @param {number|string} featureId The current Feature Id being processed.
  623. */
  624. /**
  625. * Reduce geometry in any GeoJSON object, similar to Array.reduce().
  626. *
  627. * @name geomReduce
  628. * @param {FeatureCollection|Feature|Geometry} geojson any GeoJSON object
  629. * @param {Function} callback a method that takes (previousValue, currentGeometry, featureIndex, featureProperties, featureBBox, featureId)
  630. * @param {*} [initialValue] Value to use as the first argument to the first call of the callback.
  631. * @returns {*} The value that results from the reduction.
  632. * @example
  633. * var features = turf.featureCollection([
  634. * turf.point([26, 37], {foo: 'bar'}),
  635. * turf.point([36, 53], {hello: 'world'})
  636. * ]);
  637. *
  638. * turf.geomReduce(features, function (previousValue, currentGeometry, featureIndex, featureProperties, featureBBox, featureId) {
  639. * //=previousValue
  640. * //=currentGeometry
  641. * //=featureIndex
  642. * //=featureProperties
  643. * //=featureBBox
  644. * //=featureId
  645. * return currentGeometry
  646. * });
  647. */
  648. function geomReduce(geojson, callback, initialValue) {
  649. var previousValue = initialValue;
  650. geomEach(
  651. geojson,
  652. function (
  653. currentGeometry,
  654. featureIndex,
  655. featureProperties,
  656. featureBBox,
  657. featureId
  658. ) {
  659. if (featureIndex === 0 && initialValue === undefined)
  660. previousValue = currentGeometry;
  661. else
  662. previousValue = callback(
  663. previousValue,
  664. currentGeometry,
  665. featureIndex,
  666. featureProperties,
  667. featureBBox,
  668. featureId
  669. );
  670. }
  671. );
  672. return previousValue;
  673. }
  674. /**
  675. * Callback for flattenEach
  676. *
  677. * @callback flattenEachCallback
  678. * @param {Feature} currentFeature The current flattened feature being processed.
  679. * @param {number} featureIndex The current index of the Feature being processed.
  680. * @param {number} multiFeatureIndex The current index of the Multi-Feature being processed.
  681. */
  682. /**
  683. * Iterate over flattened features in any GeoJSON object, similar to
  684. * Array.forEach.
  685. *
  686. * @name flattenEach
  687. * @param {FeatureCollection|Feature|Geometry} geojson any GeoJSON object
  688. * @param {Function} callback a method that takes (currentFeature, featureIndex, multiFeatureIndex)
  689. * @example
  690. * var features = turf.featureCollection([
  691. * turf.point([26, 37], {foo: 'bar'}),
  692. * turf.multiPoint([[40, 30], [36, 53]], {hello: 'world'})
  693. * ]);
  694. *
  695. * turf.flattenEach(features, function (currentFeature, featureIndex, multiFeatureIndex) {
  696. * //=currentFeature
  697. * //=featureIndex
  698. * //=multiFeatureIndex
  699. * });
  700. */
  701. function flattenEach(geojson, callback) {
  702. geomEach(geojson, function (geometry, featureIndex, properties, bbox, id) {
  703. // Callback for single geometry
  704. var type = geometry === null ? null : geometry.type;
  705. switch (type) {
  706. case null:
  707. case "Point":
  708. case "LineString":
  709. case "Polygon":
  710. if (
  711. callback(
  712. feature(geometry, properties, { bbox: bbox, id: id }),
  713. featureIndex,
  714. 0
  715. ) === false
  716. )
  717. return false;
  718. return;
  719. }
  720. var geomType;
  721. // Callback for multi-geometry
  722. switch (type) {
  723. case "MultiPoint":
  724. geomType = "Point";
  725. break;
  726. case "MultiLineString":
  727. geomType = "LineString";
  728. break;
  729. case "MultiPolygon":
  730. geomType = "Polygon";
  731. break;
  732. }
  733. for (
  734. var multiFeatureIndex = 0;
  735. multiFeatureIndex < geometry.coordinates.length;
  736. multiFeatureIndex++
  737. ) {
  738. var coordinate = geometry.coordinates[multiFeatureIndex];
  739. var geom = {
  740. type: geomType,
  741. coordinates: coordinate,
  742. };
  743. if (
  744. callback(feature(geom, properties), featureIndex, multiFeatureIndex) ===
  745. false
  746. )
  747. return false;
  748. }
  749. });
  750. }
  751. /**
  752. * Callback for flattenReduce
  753. *
  754. * The first time the callback function is called, the values provided as arguments depend
  755. * on whether the reduce method has an initialValue argument.
  756. *
  757. * If an initialValue is provided to the reduce method:
  758. * - The previousValue argument is initialValue.
  759. * - The currentValue argument is the value of the first element present in the array.
  760. *
  761. * If an initialValue is not provided:
  762. * - The previousValue argument is the value of the first element present in the array.
  763. * - The currentValue argument is the value of the second element present in the array.
  764. *
  765. * @callback flattenReduceCallback
  766. * @param {*} previousValue The accumulated value previously returned in the last invocation
  767. * of the callback, or initialValue, if supplied.
  768. * @param {Feature} currentFeature The current Feature being processed.
  769. * @param {number} featureIndex The current index of the Feature being processed.
  770. * @param {number} multiFeatureIndex The current index of the Multi-Feature being processed.
  771. */
  772. /**
  773. * Reduce flattened features in any GeoJSON object, similar to Array.reduce().
  774. *
  775. * @name flattenReduce
  776. * @param {FeatureCollection|Feature|Geometry} geojson any GeoJSON object
  777. * @param {Function} callback a method that takes (previousValue, currentFeature, featureIndex, multiFeatureIndex)
  778. * @param {*} [initialValue] Value to use as the first argument to the first call of the callback.
  779. * @returns {*} The value that results from the reduction.
  780. * @example
  781. * var features = turf.featureCollection([
  782. * turf.point([26, 37], {foo: 'bar'}),
  783. * turf.multiPoint([[40, 30], [36, 53]], {hello: 'world'})
  784. * ]);
  785. *
  786. * turf.flattenReduce(features, function (previousValue, currentFeature, featureIndex, multiFeatureIndex) {
  787. * //=previousValue
  788. * //=currentFeature
  789. * //=featureIndex
  790. * //=multiFeatureIndex
  791. * return currentFeature
  792. * });
  793. */
  794. function flattenReduce(geojson, callback, initialValue) {
  795. var previousValue = initialValue;
  796. flattenEach(
  797. geojson,
  798. function (currentFeature, featureIndex, multiFeatureIndex) {
  799. if (
  800. featureIndex === 0 &&
  801. multiFeatureIndex === 0 &&
  802. initialValue === undefined
  803. )
  804. previousValue = currentFeature;
  805. else
  806. previousValue = callback(
  807. previousValue,
  808. currentFeature,
  809. featureIndex,
  810. multiFeatureIndex
  811. );
  812. }
  813. );
  814. return previousValue;
  815. }
  816. /**
  817. * Callback for segmentEach
  818. *
  819. * @callback segmentEachCallback
  820. * @param {Feature<LineString>} currentSegment The current Segment being processed.
  821. * @param {number} featureIndex The current index of the Feature being processed.
  822. * @param {number} multiFeatureIndex The current index of the Multi-Feature being processed.
  823. * @param {number} geometryIndex The current index of the Geometry being processed.
  824. * @param {number} segmentIndex The current index of the Segment being processed.
  825. * @returns {void}
  826. */
  827. /**
  828. * Iterate over 2-vertex line segment in any GeoJSON object, similar to Array.forEach()
  829. * (Multi)Point geometries do not contain segments therefore they are ignored during this operation.
  830. *
  831. * @param {FeatureCollection|Feature|Geometry} geojson any GeoJSON
  832. * @param {Function} callback a method that takes (currentSegment, featureIndex, multiFeatureIndex, geometryIndex, segmentIndex)
  833. * @returns {void}
  834. * @example
  835. * var polygon = turf.polygon([[[-50, 5], [-40, -10], [-50, -10], [-40, 5], [-50, 5]]]);
  836. *
  837. * // Iterate over GeoJSON by 2-vertex segments
  838. * turf.segmentEach(polygon, function (currentSegment, featureIndex, multiFeatureIndex, geometryIndex, segmentIndex) {
  839. * //=currentSegment
  840. * //=featureIndex
  841. * //=multiFeatureIndex
  842. * //=geometryIndex
  843. * //=segmentIndex
  844. * });
  845. *
  846. * // Calculate the total number of segments
  847. * var total = 0;
  848. * turf.segmentEach(polygon, function () {
  849. * total++;
  850. * });
  851. */
  852. function segmentEach(geojson, callback) {
  853. flattenEach(geojson, function (feature, featureIndex, multiFeatureIndex) {
  854. var segmentIndex = 0;
  855. // Exclude null Geometries
  856. if (!feature.geometry) return;
  857. // (Multi)Point geometries do not contain segments therefore they are ignored during this operation.
  858. var type = feature.geometry.type;
  859. if (type === "Point" || type === "MultiPoint") return;
  860. // Generate 2-vertex line segments
  861. var previousCoords;
  862. var previousFeatureIndex = 0;
  863. var previousMultiIndex = 0;
  864. var prevGeomIndex = 0;
  865. if (
  866. coordEach(
  867. feature,
  868. function (
  869. currentCoord,
  870. coordIndex,
  871. featureIndexCoord,
  872. multiPartIndexCoord,
  873. geometryIndex
  874. ) {
  875. // Simulating a meta.coordReduce() since `reduce` operations cannot be stopped by returning `false`
  876. if (
  877. previousCoords === undefined ||
  878. featureIndex > previousFeatureIndex ||
  879. multiPartIndexCoord > previousMultiIndex ||
  880. geometryIndex > prevGeomIndex
  881. ) {
  882. previousCoords = currentCoord;
  883. previousFeatureIndex = featureIndex;
  884. previousMultiIndex = multiPartIndexCoord;
  885. prevGeomIndex = geometryIndex;
  886. segmentIndex = 0;
  887. return;
  888. }
  889. var currentSegment = lineString(
  890. [previousCoords, currentCoord],
  891. feature.properties
  892. );
  893. if (
  894. callback(
  895. currentSegment,
  896. featureIndex,
  897. multiFeatureIndex,
  898. geometryIndex,
  899. segmentIndex
  900. ) === false
  901. )
  902. return false;
  903. segmentIndex++;
  904. previousCoords = currentCoord;
  905. }
  906. ) === false
  907. )
  908. return false;
  909. });
  910. }
  911. /**
  912. * Callback for segmentReduce
  913. *
  914. * The first time the callback function is called, the values provided as arguments depend
  915. * on whether the reduce method has an initialValue argument.
  916. *
  917. * If an initialValue is provided to the reduce method:
  918. * - The previousValue argument is initialValue.
  919. * - The currentValue argument is the value of the first element present in the array.
  920. *
  921. * If an initialValue is not provided:
  922. * - The previousValue argument is the value of the first element present in the array.
  923. * - The currentValue argument is the value of the second element present in the array.
  924. *
  925. * @callback segmentReduceCallback
  926. * @param {*} previousValue The accumulated value previously returned in the last invocation
  927. * of the callback, or initialValue, if supplied.
  928. * @param {Feature<LineString>} currentSegment The current Segment being processed.
  929. * @param {number} featureIndex The current index of the Feature being processed.
  930. * @param {number} multiFeatureIndex The current index of the Multi-Feature being processed.
  931. * @param {number} geometryIndex The current index of the Geometry being processed.
  932. * @param {number} segmentIndex The current index of the Segment being processed.
  933. */
  934. /**
  935. * Reduce 2-vertex line segment in any GeoJSON object, similar to Array.reduce()
  936. * (Multi)Point geometries do not contain segments therefore they are ignored during this operation.
  937. *
  938. * @param {FeatureCollection|Feature|Geometry} geojson any GeoJSON
  939. * @param {Function} callback a method that takes (previousValue, currentSegment, currentIndex)
  940. * @param {*} [initialValue] Value to use as the first argument to the first call of the callback.
  941. * @returns {void}
  942. * @example
  943. * var polygon = turf.polygon([[[-50, 5], [-40, -10], [-50, -10], [-40, 5], [-50, 5]]]);
  944. *
  945. * // Iterate over GeoJSON by 2-vertex segments
  946. * turf.segmentReduce(polygon, function (previousSegment, currentSegment, featureIndex, multiFeatureIndex, geometryIndex, segmentIndex) {
  947. * //= previousSegment
  948. * //= currentSegment
  949. * //= featureIndex
  950. * //= multiFeatureIndex
  951. * //= geometryIndex
  952. * //= segmentIndex
  953. * return currentSegment
  954. * });
  955. *
  956. * // Calculate the total number of segments
  957. * var initialValue = 0
  958. * var total = turf.segmentReduce(polygon, function (previousValue) {
  959. * previousValue++;
  960. * return previousValue;
  961. * }, initialValue);
  962. */
  963. function segmentReduce(geojson, callback, initialValue) {
  964. var previousValue = initialValue;
  965. var started = false;
  966. segmentEach(
  967. geojson,
  968. function (
  969. currentSegment,
  970. featureIndex,
  971. multiFeatureIndex,
  972. geometryIndex,
  973. segmentIndex
  974. ) {
  975. if (started === false && initialValue === undefined)
  976. previousValue = currentSegment;
  977. else
  978. previousValue = callback(
  979. previousValue,
  980. currentSegment,
  981. featureIndex,
  982. multiFeatureIndex,
  983. geometryIndex,
  984. segmentIndex
  985. );
  986. started = true;
  987. }
  988. );
  989. return previousValue;
  990. }
  991. /**
  992. * Callback for lineEach
  993. *
  994. * @callback lineEachCallback
  995. * @param {Feature<LineString>} currentLine The current LineString|LinearRing being processed
  996. * @param {number} featureIndex The current index of the Feature being processed
  997. * @param {number} multiFeatureIndex The current index of the Multi-Feature being processed
  998. * @param {number} geometryIndex The current index of the Geometry being processed
  999. */
  1000. /**
  1001. * Iterate over line or ring coordinates in LineString, Polygon, MultiLineString, MultiPolygon Features or Geometries,
  1002. * similar to Array.forEach.
  1003. *
  1004. * @name lineEach
  1005. * @param {Geometry|Feature<LineString|Polygon|MultiLineString|MultiPolygon>} geojson object
  1006. * @param {Function} callback a method that takes (currentLine, featureIndex, multiFeatureIndex, geometryIndex)
  1007. * @example
  1008. * var multiLine = turf.multiLineString([
  1009. * [[26, 37], [35, 45]],
  1010. * [[36, 53], [38, 50], [41, 55]]
  1011. * ]);
  1012. *
  1013. * turf.lineEach(multiLine, function (currentLine, featureIndex, multiFeatureIndex, geometryIndex) {
  1014. * //=currentLine
  1015. * //=featureIndex
  1016. * //=multiFeatureIndex
  1017. * //=geometryIndex
  1018. * });
  1019. */
  1020. function lineEach(geojson, callback) {
  1021. // validation
  1022. if (!geojson) throw new Error("geojson is required");
  1023. flattenEach(geojson, function (feature, featureIndex, multiFeatureIndex) {
  1024. if (feature.geometry === null) return;
  1025. var type = feature.geometry.type;
  1026. var coords = feature.geometry.coordinates;
  1027. switch (type) {
  1028. case "LineString":
  1029. if (callback(feature, featureIndex, multiFeatureIndex, 0, 0) === false)
  1030. return false;
  1031. break;
  1032. case "Polygon":
  1033. for (
  1034. var geometryIndex = 0;
  1035. geometryIndex < coords.length;
  1036. geometryIndex++
  1037. ) {
  1038. if (
  1039. callback(
  1040. lineString(coords[geometryIndex], feature.properties),
  1041. featureIndex,
  1042. multiFeatureIndex,
  1043. geometryIndex
  1044. ) === false
  1045. )
  1046. return false;
  1047. }
  1048. break;
  1049. }
  1050. });
  1051. }
  1052. /**
  1053. * Callback for lineReduce
  1054. *
  1055. * The first time the callback function is called, the values provided as arguments depend
  1056. * on whether the reduce method has an initialValue argument.
  1057. *
  1058. * If an initialValue is provided to the reduce method:
  1059. * - The previousValue argument is initialValue.
  1060. * - The currentValue argument is the value of the first element present in the array.
  1061. *
  1062. * If an initialValue is not provided:
  1063. * - The previousValue argument is the value of the first element present in the array.
  1064. * - The currentValue argument is the value of the second element present in the array.
  1065. *
  1066. * @callback lineReduceCallback
  1067. * @param {*} previousValue The accumulated value previously returned in the last invocation
  1068. * of the callback, or initialValue, if supplied.
  1069. * @param {Feature<LineString>} currentLine The current LineString|LinearRing being processed.
  1070. * @param {number} featureIndex The current index of the Feature being processed
  1071. * @param {number} multiFeatureIndex The current index of the Multi-Feature being processed
  1072. * @param {number} geometryIndex The current index of the Geometry being processed
  1073. */
  1074. /**
  1075. * Reduce features in any GeoJSON object, similar to Array.reduce().
  1076. *
  1077. * @name lineReduce
  1078. * @param {Geometry|Feature<LineString|Polygon|MultiLineString|MultiPolygon>} geojson object
  1079. * @param {Function} callback a method that takes (previousValue, currentLine, featureIndex, multiFeatureIndex, geometryIndex)
  1080. * @param {*} [initialValue] Value to use as the first argument to the first call of the callback.
  1081. * @returns {*} The value that results from the reduction.
  1082. * @example
  1083. * var multiPoly = turf.multiPolygon([
  1084. * turf.polygon([[[12,48],[2,41],[24,38],[12,48]], [[9,44],[13,41],[13,45],[9,44]]]),
  1085. * turf.polygon([[[5, 5], [0, 0], [2, 2], [4, 4], [5, 5]]])
  1086. * ]);
  1087. *
  1088. * turf.lineReduce(multiPoly, function (previousValue, currentLine, featureIndex, multiFeatureIndex, geometryIndex) {
  1089. * //=previousValue
  1090. * //=currentLine
  1091. * //=featureIndex
  1092. * //=multiFeatureIndex
  1093. * //=geometryIndex
  1094. * return currentLine
  1095. * });
  1096. */
  1097. function lineReduce(geojson, callback, initialValue) {
  1098. var previousValue = initialValue;
  1099. lineEach(
  1100. geojson,
  1101. function (currentLine, featureIndex, multiFeatureIndex, geometryIndex) {
  1102. if (featureIndex === 0 && initialValue === undefined)
  1103. previousValue = currentLine;
  1104. else
  1105. previousValue = callback(
  1106. previousValue,
  1107. currentLine,
  1108. featureIndex,
  1109. multiFeatureIndex,
  1110. geometryIndex
  1111. );
  1112. }
  1113. );
  1114. return previousValue;
  1115. }
  1116. /**
  1117. * Finds a particular 2-vertex LineString Segment from a GeoJSON using `@turf/meta` indexes.
  1118. *
  1119. * Negative indexes are permitted.
  1120. * Point & MultiPoint will always return null.
  1121. *
  1122. * @param {FeatureCollection|Feature|Geometry} geojson Any GeoJSON Feature or Geometry
  1123. * @param {Object} [options={}] Optional parameters
  1124. * @param {number} [options.featureIndex=0] Feature Index
  1125. * @param {number} [options.multiFeatureIndex=0] Multi-Feature Index
  1126. * @param {number} [options.geometryIndex=0] Geometry Index
  1127. * @param {number} [options.segmentIndex=0] Segment Index
  1128. * @param {Object} [options.properties={}] Translate Properties to output LineString
  1129. * @param {BBox} [options.bbox={}] Translate BBox to output LineString
  1130. * @param {number|string} [options.id={}] Translate Id to output LineString
  1131. * @returns {Feature<LineString>} 2-vertex GeoJSON Feature LineString
  1132. * @example
  1133. * var multiLine = turf.multiLineString([
  1134. * [[10, 10], [50, 30], [30, 40]],
  1135. * [[-10, -10], [-50, -30], [-30, -40]]
  1136. * ]);
  1137. *
  1138. * // First Segment (defaults are 0)
  1139. * turf.findSegment(multiLine);
  1140. * // => Feature<LineString<[[10, 10], [50, 30]]>>
  1141. *
  1142. * // First Segment of 2nd Multi Feature
  1143. * turf.findSegment(multiLine, {multiFeatureIndex: 1});
  1144. * // => Feature<LineString<[[-10, -10], [-50, -30]]>>
  1145. *
  1146. * // Last Segment of Last Multi Feature
  1147. * turf.findSegment(multiLine, {multiFeatureIndex: -1, segmentIndex: -1});
  1148. * // => Feature<LineString<[[-50, -30], [-30, -40]]>>
  1149. */
  1150. function findSegment(geojson, options) {
  1151. // Optional Parameters
  1152. options = options || {};
  1153. if (!isObject(options)) throw new Error("options is invalid");
  1154. var featureIndex = options.featureIndex || 0;
  1155. var multiFeatureIndex = options.multiFeatureIndex || 0;
  1156. var geometryIndex = options.geometryIndex || 0;
  1157. var segmentIndex = options.segmentIndex || 0;
  1158. // Find FeatureIndex
  1159. var properties = options.properties;
  1160. var geometry;
  1161. switch (geojson.type) {
  1162. case "FeatureCollection":
  1163. if (featureIndex < 0)
  1164. featureIndex = geojson.features.length + featureIndex;
  1165. properties = properties || geojson.features[featureIndex].properties;
  1166. geometry = geojson.features[featureIndex].geometry;
  1167. break;
  1168. case "Feature":
  1169. properties = properties || geojson.properties;
  1170. geometry = geojson.geometry;
  1171. break;
  1172. case "Point":
  1173. case "MultiPoint":
  1174. return null;
  1175. case "LineString":
  1176. case "Polygon":
  1177. case "MultiLineString":
  1178. case "MultiPolygon":
  1179. geometry = geojson;
  1180. break;
  1181. default:
  1182. throw new Error("geojson is invalid");
  1183. }
  1184. // Find SegmentIndex
  1185. if (geometry === null) return null;
  1186. var coords = geometry.coordinates;
  1187. switch (geometry.type) {
  1188. case "Point":
  1189. case "MultiPoint":
  1190. return null;
  1191. case "LineString":
  1192. if (segmentIndex < 0) segmentIndex = coords.length + segmentIndex - 1;
  1193. return lineString(
  1194. [coords[segmentIndex], coords[segmentIndex + 1]],
  1195. properties,
  1196. options
  1197. );
  1198. case "Polygon":
  1199. if (geometryIndex < 0) geometryIndex = coords.length + geometryIndex;
  1200. if (segmentIndex < 0)
  1201. segmentIndex = coords[geometryIndex].length + segmentIndex - 1;
  1202. return lineString(
  1203. [
  1204. coords[geometryIndex][segmentIndex],
  1205. coords[geometryIndex][segmentIndex + 1],
  1206. ],
  1207. properties,
  1208. options
  1209. );
  1210. case "MultiLineString":
  1211. if (multiFeatureIndex < 0)
  1212. multiFeatureIndex = coords.length + multiFeatureIndex;
  1213. if (segmentIndex < 0)
  1214. segmentIndex = coords[multiFeatureIndex].length + segmentIndex - 1;
  1215. return lineString(
  1216. [
  1217. coords[multiFeatureIndex][segmentIndex],
  1218. coords[multiFeatureIndex][segmentIndex + 1],
  1219. ],
  1220. properties,
  1221. options
  1222. );
  1223. case "MultiPolygon":
  1224. if (multiFeatureIndex < 0)
  1225. multiFeatureIndex = coords.length + multiFeatureIndex;
  1226. if (geometryIndex < 0)
  1227. geometryIndex = coords[multiFeatureIndex].length + geometryIndex;
  1228. if (segmentIndex < 0)
  1229. segmentIndex =
  1230. coords[multiFeatureIndex][geometryIndex].length - segmentIndex - 1;
  1231. return lineString(
  1232. [
  1233. coords[multiFeatureIndex][geometryIndex][segmentIndex],
  1234. coords[multiFeatureIndex][geometryIndex][segmentIndex + 1],
  1235. ],
  1236. properties,
  1237. options
  1238. );
  1239. }
  1240. throw new Error("geojson is invalid");
  1241. }
  1242. /**
  1243. * Finds a particular Point from a GeoJSON using `@turf/meta` indexes.
  1244. *
  1245. * Negative indexes are permitted.
  1246. *
  1247. * @param {FeatureCollection|Feature|Geometry} geojson Any GeoJSON Feature or Geometry
  1248. * @param {Object} [options={}] Optional parameters
  1249. * @param {number} [options.featureIndex=0] Feature Index
  1250. * @param {number} [options.multiFeatureIndex=0] Multi-Feature Index
  1251. * @param {number} [options.geometryIndex=0] Geometry Index
  1252. * @param {number} [options.coordIndex=0] Coord Index
  1253. * @param {Object} [options.properties={}] Translate Properties to output Point
  1254. * @param {BBox} [options.bbox={}] Translate BBox to output Point
  1255. * @param {number|string} [options.id={}] Translate Id to output Point
  1256. * @returns {Feature<Point>} 2-vertex GeoJSON Feature Point
  1257. * @example
  1258. * var multiLine = turf.multiLineString([
  1259. * [[10, 10], [50, 30], [30, 40]],
  1260. * [[-10, -10], [-50, -30], [-30, -40]]
  1261. * ]);
  1262. *
  1263. * // First Segment (defaults are 0)
  1264. * turf.findPoint(multiLine);
  1265. * // => Feature<Point<[10, 10]>>
  1266. *
  1267. * // First Segment of the 2nd Multi-Feature
  1268. * turf.findPoint(multiLine, {multiFeatureIndex: 1});
  1269. * // => Feature<Point<[-10, -10]>>
  1270. *
  1271. * // Last Segment of last Multi-Feature
  1272. * turf.findPoint(multiLine, {multiFeatureIndex: -1, coordIndex: -1});
  1273. * // => Feature<Point<[-30, -40]>>
  1274. */
  1275. function findPoint(geojson, options) {
  1276. // Optional Parameters
  1277. options = options || {};
  1278. if (!isObject(options)) throw new Error("options is invalid");
  1279. var featureIndex = options.featureIndex || 0;
  1280. var multiFeatureIndex = options.multiFeatureIndex || 0;
  1281. var geometryIndex = options.geometryIndex || 0;
  1282. var coordIndex = options.coordIndex || 0;
  1283. // Find FeatureIndex
  1284. var properties = options.properties;
  1285. var geometry;
  1286. switch (geojson.type) {
  1287. case "FeatureCollection":
  1288. if (featureIndex < 0)
  1289. featureIndex = geojson.features.length + featureIndex;
  1290. properties = properties || geojson.features[featureIndex].properties;
  1291. geometry = geojson.features[featureIndex].geometry;
  1292. break;
  1293. case "Feature":
  1294. properties = properties || geojson.properties;
  1295. geometry = geojson.geometry;
  1296. break;
  1297. case "Point":
  1298. case "MultiPoint":
  1299. return null;
  1300. case "LineString":
  1301. case "Polygon":
  1302. case "MultiLineString":
  1303. case "MultiPolygon":
  1304. geometry = geojson;
  1305. break;
  1306. default:
  1307. throw new Error("geojson is invalid");
  1308. }
  1309. // Find Coord Index
  1310. if (geometry === null) return null;
  1311. var coords = geometry.coordinates;
  1312. switch (geometry.type) {
  1313. case "Point":
  1314. return point(coords, properties, options);
  1315. case "MultiPoint":
  1316. if (multiFeatureIndex < 0)
  1317. multiFeatureIndex = coords.length + multiFeatureIndex;
  1318. return point(coords[multiFeatureIndex], properties, options);
  1319. case "LineString":
  1320. if (coordIndex < 0) coordIndex = coords.length + coordIndex;
  1321. return point(coords[coordIndex], properties, options);
  1322. case "Polygon":
  1323. if (geometryIndex < 0) geometryIndex = coords.length + geometryIndex;
  1324. if (coordIndex < 0)
  1325. coordIndex = coords[geometryIndex].length + coordIndex;
  1326. return point(coords[geometryIndex][coordIndex], properties, options);
  1327. case "MultiLineString":
  1328. if (multiFeatureIndex < 0)
  1329. multiFeatureIndex = coords.length + multiFeatureIndex;
  1330. if (coordIndex < 0)
  1331. coordIndex = coords[multiFeatureIndex].length + coordIndex;
  1332. return point(coords[multiFeatureIndex][coordIndex], properties, options);
  1333. case "MultiPolygon":
  1334. if (multiFeatureIndex < 0)
  1335. multiFeatureIndex = coords.length + multiFeatureIndex;
  1336. if (geometryIndex < 0)
  1337. geometryIndex = coords[multiFeatureIndex].length + geometryIndex;
  1338. if (coordIndex < 0)
  1339. coordIndex =
  1340. coords[multiFeatureIndex][geometryIndex].length - coordIndex;
  1341. return point(
  1342. coords[multiFeatureIndex][geometryIndex][coordIndex],
  1343. properties,
  1344. options
  1345. );
  1346. }
  1347. throw new Error("geojson is invalid");
  1348. }
  1349. export { coordAll, coordEach, coordReduce, featureEach, featureReduce, findPoint, findSegment, flattenEach, flattenReduce, geomEach, geomReduce, lineEach, lineReduce, propEach, propReduce, segmentEach, segmentReduce };