index.js 4.7 KB

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