index.js 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. import clone from '@turf/clone';
  2. import booleanClockwise from '@turf/boolean-clockwise';
  3. import { featureEach, geomEach } from '@turf/meta';
  4. import { getCoords } from '@turf/invariant';
  5. import { isObject, featureCollection } from '@turf/helpers';
  6. /**
  7. * Rewind {@link LineString|(Multi)LineString} or {@link Polygon|(Multi)Polygon} outer ring counterclockwise and inner rings clockwise (Uses {@link http://en.wikipedia.org/wiki/Shoelace_formula|Shoelace Formula}).
  8. *
  9. * @name rewind
  10. * @param {GeoJSON} geojson input GeoJSON Polygon
  11. * @param {Object} [options={}] Optional parameters
  12. * @param {boolean} [options.reverse=false] enable reverse winding
  13. * @param {boolean} [options.mutate=false] allows GeoJSON input to be mutated (significant performance increase if true)
  14. * @returns {GeoJSON} rewind Polygon
  15. * @example
  16. * var polygon = turf.polygon([[[121, -29], [138, -29], [138, -18], [121, -18], [121, -29]]]);
  17. *
  18. * var rewind = turf.rewind(polygon);
  19. *
  20. * //addToMap
  21. * var addToMap = [rewind];
  22. */
  23. function rewind(geojson, options) {
  24. // Optional parameters
  25. options = options || {};
  26. if (!isObject(options)) throw new Error("options is invalid");
  27. var reverse = options.reverse || false;
  28. var mutate = options.mutate || false;
  29. // validation
  30. if (!geojson) throw new Error("<geojson> is required");
  31. if (typeof reverse !== "boolean")
  32. throw new Error("<reverse> must be a boolean");
  33. if (typeof mutate !== "boolean")
  34. throw new Error("<mutate> must be a boolean");
  35. // prevent input mutation
  36. if (mutate === false) geojson = clone(geojson);
  37. // Support Feature Collection or Geometry Collection
  38. var results = [];
  39. switch (geojson.type) {
  40. case "GeometryCollection":
  41. geomEach(geojson, function (geometry) {
  42. rewindFeature(geometry, reverse);
  43. });
  44. return geojson;
  45. case "FeatureCollection":
  46. featureEach(geojson, function (feature) {
  47. featureEach(rewindFeature(feature, reverse), function (result) {
  48. results.push(result);
  49. });
  50. });
  51. return featureCollection(results);
  52. }
  53. // Support Feature or Geometry Objects
  54. return rewindFeature(geojson, reverse);
  55. }
  56. /**
  57. * Rewind
  58. *
  59. * @private
  60. * @param {Geometry|Feature<any>} geojson Geometry or Feature
  61. * @param {Boolean} [reverse=false] enable reverse winding
  62. * @returns {Geometry|Feature<any>} rewind Geometry or Feature
  63. */
  64. function rewindFeature(geojson, reverse) {
  65. var type = geojson.type === "Feature" ? geojson.geometry.type : geojson.type;
  66. // Support all GeoJSON Geometry Objects
  67. switch (type) {
  68. case "GeometryCollection":
  69. geomEach(geojson, function (geometry) {
  70. rewindFeature(geometry, reverse);
  71. });
  72. return geojson;
  73. case "LineString":
  74. rewindLineString(getCoords(geojson), reverse);
  75. return geojson;
  76. case "Polygon":
  77. rewindPolygon(getCoords(geojson), reverse);
  78. return geojson;
  79. case "MultiLineString":
  80. getCoords(geojson).forEach(function (lineCoords) {
  81. rewindLineString(lineCoords, reverse);
  82. });
  83. return geojson;
  84. case "MultiPolygon":
  85. getCoords(geojson).forEach(function (lineCoords) {
  86. rewindPolygon(lineCoords, reverse);
  87. });
  88. return geojson;
  89. case "Point":
  90. case "MultiPoint":
  91. return geojson;
  92. }
  93. }
  94. /**
  95. * Rewind LineString - outer ring clockwise
  96. *
  97. * @private
  98. * @param {Array<Array<number>>} coords GeoJSON LineString geometry coordinates
  99. * @param {Boolean} [reverse=false] enable reverse winding
  100. * @returns {void} mutates coordinates
  101. */
  102. function rewindLineString(coords, reverse) {
  103. if (booleanClockwise(coords) === reverse) coords.reverse();
  104. }
  105. /**
  106. * Rewind Polygon - outer ring counterclockwise and inner rings clockwise.
  107. *
  108. * @private
  109. * @param {Array<Array<Array<number>>>} coords GeoJSON Polygon geometry coordinates
  110. * @param {Boolean} [reverse=false] enable reverse winding
  111. * @returns {void} mutates coordinates
  112. */
  113. function rewindPolygon(coords, reverse) {
  114. // outer ring
  115. if (booleanClockwise(coords[0]) !== reverse) {
  116. coords[0].reverse();
  117. }
  118. // inner rings
  119. for (var i = 1; i < coords.length; i++) {
  120. if (booleanClockwise(coords[i]) === reverse) {
  121. coords[i].reverse();
  122. }
  123. }
  124. }
  125. export default rewind;