index.js 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. import { geomEach, coordEach } from '@turf/meta';
  2. import { multiPolygon, polygon, featureCollection } from '@turf/helpers';
  3. /**
  4. * Smooths a {@link Polygon} or {@link MultiPolygon}. Based on [Chaikin's algorithm](http://graphics.cs.ucdavis.edu/education/CAGDNotes/Chaikins-Algorithm/Chaikins-Algorithm.html).
  5. * Warning: may create degenerate polygons.
  6. *
  7. * @name polygonSmooth
  8. * @param {FeatureCollection|Feature<Polygon|MultiPolygon>} inputPolys (Multi)Polygon(s) to smooth
  9. * @param {Object} [options={}] Optional parameters
  10. * @param {string} [options.iterations=1] THe number of times to smooth the polygon. A higher value means a smoother polygon.
  11. * @returns {FeatureCollection<Polygon>} FeatureCollection containing the smoothed polygon/poylgons
  12. * @example
  13. * var polygon = turf.polygon([[[11, 0], [22, 4], [31, 0], [31, 11], [21, 15], [11, 11], [11, 0]]]);
  14. *
  15. * var smoothed = turf.polygonSmooth(polygon, {iterations: 3})
  16. *
  17. * //addToMap
  18. * var addToMap = [smoothed, polygon];
  19. */
  20. function polygonSmooth(inputPolys, options) {
  21. var outPolys = [];
  22. // Optional parameters
  23. var iterations = options.iterations || 1;
  24. if (!inputPolys) throw new Error("inputPolys is required");
  25. geomEach(inputPolys, function (geom, geomIndex, properties) {
  26. var outCoords;
  27. var poly;
  28. var tempOutput;
  29. switch (geom.type) {
  30. case "Polygon":
  31. outCoords = [[]];
  32. for (var i = 0; i < iterations; i++) {
  33. tempOutput = [[]];
  34. poly = geom;
  35. if (i > 0) poly = polygon(outCoords).geometry;
  36. processPolygon(poly, tempOutput);
  37. outCoords = tempOutput.slice(0);
  38. }
  39. outPolys.push(polygon(outCoords, properties));
  40. break;
  41. case "MultiPolygon":
  42. outCoords = [[[]]];
  43. for (var y = 0; y < iterations; y++) {
  44. tempOutput = [[[]]];
  45. poly = geom;
  46. if (y > 0) poly = multiPolygon(outCoords).geometry;
  47. processMultiPolygon(poly, tempOutput);
  48. outCoords = tempOutput.slice(0);
  49. }
  50. outPolys.push(multiPolygon(outCoords, properties));
  51. break;
  52. default:
  53. throw new Error("geometry is invalid, must be Polygon or MultiPolygon");
  54. }
  55. });
  56. return featureCollection(outPolys);
  57. }
  58. /**
  59. * @param {poly} poly to process
  60. * @param {poly} tempOutput to place the results in
  61. * @private
  62. */
  63. function processPolygon(poly, tempOutput) {
  64. var prevGeomIndex = 0;
  65. var subtractCoordIndex = 0;
  66. coordEach(
  67. poly,
  68. function (
  69. currentCoord,
  70. coordIndex,
  71. featureIndex,
  72. multiFeatureIndex,
  73. geometryIndex
  74. ) {
  75. if (geometryIndex > prevGeomIndex) {
  76. prevGeomIndex = geometryIndex;
  77. subtractCoordIndex = coordIndex;
  78. tempOutput.push([]);
  79. }
  80. var realCoordIndex = coordIndex - subtractCoordIndex;
  81. var p1 = poly.coordinates[geometryIndex][realCoordIndex + 1];
  82. var p0x = currentCoord[0];
  83. var p0y = currentCoord[1];
  84. var p1x = p1[0];
  85. var p1y = p1[1];
  86. tempOutput[geometryIndex].push([
  87. 0.75 * p0x + 0.25 * p1x,
  88. 0.75 * p0y + 0.25 * p1y,
  89. ]);
  90. tempOutput[geometryIndex].push([
  91. 0.25 * p0x + 0.75 * p1x,
  92. 0.25 * p0y + 0.75 * p1y,
  93. ]);
  94. },
  95. true
  96. );
  97. tempOutput.forEach(function (ring) {
  98. ring.push(ring[0]);
  99. });
  100. }
  101. /**
  102. * @param {poly} poly to process
  103. * @param {poly} tempOutput to place the results in
  104. * @private
  105. */
  106. function processMultiPolygon(poly, tempOutput) {
  107. var prevGeomIndex = 0;
  108. var subtractCoordIndex = 0;
  109. var prevMultiIndex = 0;
  110. coordEach(
  111. poly,
  112. function (
  113. currentCoord,
  114. coordIndex,
  115. featureIndex,
  116. multiFeatureIndex,
  117. geometryIndex
  118. ) {
  119. if (multiFeatureIndex > prevMultiIndex) {
  120. prevMultiIndex = multiFeatureIndex;
  121. subtractCoordIndex = coordIndex;
  122. tempOutput.push([[]]);
  123. }
  124. if (geometryIndex > prevGeomIndex) {
  125. prevGeomIndex = geometryIndex;
  126. subtractCoordIndex = coordIndex;
  127. tempOutput[multiFeatureIndex].push([]);
  128. }
  129. var realCoordIndex = coordIndex - subtractCoordIndex;
  130. var p1 =
  131. poly.coordinates[multiFeatureIndex][geometryIndex][realCoordIndex + 1];
  132. var p0x = currentCoord[0];
  133. var p0y = currentCoord[1];
  134. var p1x = p1[0];
  135. var p1y = p1[1];
  136. tempOutput[multiFeatureIndex][geometryIndex].push([
  137. 0.75 * p0x + 0.25 * p1x,
  138. 0.75 * p0y + 0.25 * p1y,
  139. ]);
  140. tempOutput[multiFeatureIndex][geometryIndex].push([
  141. 0.25 * p0x + 0.75 * p1x,
  142. 0.25 * p0y + 0.75 * p1y,
  143. ]);
  144. },
  145. true
  146. );
  147. tempOutput.forEach(function (poly) {
  148. poly.forEach(function (ring) {
  149. ring.push(ring[0]);
  150. });
  151. });
  152. }
  153. export default polygonSmooth;