index.js 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. 'use strict';
  2. var center = require('@turf/center');
  3. var turfJsts = require('turf-jsts');
  4. var meta = require('@turf/meta');
  5. var d3Geo = require('d3-geo');
  6. var helpers = require('@turf/helpers');
  7. function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
  8. var center__default = /*#__PURE__*/_interopDefaultLegacy(center);
  9. /**
  10. * Calculates a buffer for input features for a given radius. Units supported are miles, kilometers, and degrees.
  11. *
  12. * When using a negative radius, the resulting geometry may be invalid if
  13. * it's too small compared to the radius magnitude. If the input is a
  14. * FeatureCollection, only valid members will be returned in the output
  15. * FeatureCollection - i.e., the output collection may have fewer members than
  16. * the input, or even be empty.
  17. *
  18. * @name buffer
  19. * @param {FeatureCollection|Geometry|Feature<any>} geojson input to be buffered
  20. * @param {number} radius distance to draw the buffer (negative values are allowed)
  21. * @param {Object} [options={}] Optional parameters
  22. * @param {string} [options.units="kilometers"] any of the options supported by turf units
  23. * @param {number} [options.steps=8] number of steps
  24. * @returns {FeatureCollection|Feature<Polygon|MultiPolygon>|undefined} buffered features
  25. * @example
  26. * var point = turf.point([-90.548630, 14.616599]);
  27. * var buffered = turf.buffer(point, 500, {units: 'miles'});
  28. *
  29. * //addToMap
  30. * var addToMap = [point, buffered]
  31. */
  32. function buffer(geojson, radius, options) {
  33. // Optional params
  34. options = options || {};
  35. // use user supplied options or default values
  36. var units = options.units || "kilometers";
  37. var steps = options.steps || 8;
  38. // validation
  39. if (!geojson) throw new Error("geojson is required");
  40. if (typeof options !== "object") throw new Error("options must be an object");
  41. if (typeof steps !== "number") throw new Error("steps must be an number");
  42. // Allow negative buffers ("erosion") or zero-sized buffers ("repair geometry")
  43. if (radius === undefined) throw new Error("radius is required");
  44. if (steps <= 0) throw new Error("steps must be greater than 0");
  45. var results = [];
  46. switch (geojson.type) {
  47. case "GeometryCollection":
  48. meta.geomEach(geojson, function (geometry) {
  49. var buffered = bufferFeature(geometry, radius, units, steps);
  50. if (buffered) results.push(buffered);
  51. });
  52. return helpers.featureCollection(results);
  53. case "FeatureCollection":
  54. meta.featureEach(geojson, function (feature) {
  55. var multiBuffered = bufferFeature(feature, radius, units, steps);
  56. if (multiBuffered) {
  57. meta.featureEach(multiBuffered, function (buffered) {
  58. if (buffered) results.push(buffered);
  59. });
  60. }
  61. });
  62. return helpers.featureCollection(results);
  63. }
  64. return bufferFeature(geojson, radius, units, steps);
  65. }
  66. /**
  67. * Buffer single Feature/Geometry
  68. *
  69. * @private
  70. * @param {Feature<any>} geojson input to be buffered
  71. * @param {number} radius distance to draw the buffer
  72. * @param {string} [units='kilometers'] any of the options supported by turf units
  73. * @param {number} [steps=8] number of steps
  74. * @returns {Feature<Polygon|MultiPolygon>} buffered feature
  75. */
  76. function bufferFeature(geojson, radius, units, steps) {
  77. var properties = geojson.properties || {};
  78. var geometry = geojson.type === "Feature" ? geojson.geometry : geojson;
  79. // Geometry Types faster than jsts
  80. if (geometry.type === "GeometryCollection") {
  81. var results = [];
  82. meta.geomEach(geojson, function (geometry) {
  83. var buffered = bufferFeature(geometry, radius, units, steps);
  84. if (buffered) results.push(buffered);
  85. });
  86. return helpers.featureCollection(results);
  87. }
  88. // Project GeoJSON to Azimuthal Equidistant projection (convert to Meters)
  89. var projection = defineProjection(geometry);
  90. var projected = {
  91. type: geometry.type,
  92. coordinates: projectCoords(geometry.coordinates, projection),
  93. };
  94. // JSTS buffer operation
  95. var reader = new turfJsts.GeoJSONReader();
  96. var geom = reader.read(projected);
  97. var distance = helpers.radiansToLength(helpers.lengthToRadians(radius, units), "meters");
  98. var buffered = turfJsts.BufferOp.bufferOp(geom, distance, steps);
  99. var writer = new turfJsts.GeoJSONWriter();
  100. buffered = writer.write(buffered);
  101. // Detect if empty geometries
  102. if (coordsIsNaN(buffered.coordinates)) return undefined;
  103. // Unproject coordinates (convert to Degrees)
  104. var result = {
  105. type: buffered.type,
  106. coordinates: unprojectCoords(buffered.coordinates, projection),
  107. };
  108. return helpers.feature(result, properties);
  109. }
  110. /**
  111. * Coordinates isNaN
  112. *
  113. * @private
  114. * @param {Array<any>} coords GeoJSON Coordinates
  115. * @returns {boolean} if NaN exists
  116. */
  117. function coordsIsNaN(coords) {
  118. if (Array.isArray(coords[0])) return coordsIsNaN(coords[0]);
  119. return isNaN(coords[0]);
  120. }
  121. /**
  122. * Project coordinates to projection
  123. *
  124. * @private
  125. * @param {Array<any>} coords to project
  126. * @param {GeoProjection} proj D3 Geo Projection
  127. * @returns {Array<any>} projected coordinates
  128. */
  129. function projectCoords(coords, proj) {
  130. if (typeof coords[0] !== "object") return proj(coords);
  131. return coords.map(function (coord) {
  132. return projectCoords(coord, proj);
  133. });
  134. }
  135. /**
  136. * Un-Project coordinates to projection
  137. *
  138. * @private
  139. * @param {Array<any>} coords to un-project
  140. * @param {GeoProjection} proj D3 Geo Projection
  141. * @returns {Array<any>} un-projected coordinates
  142. */
  143. function unprojectCoords(coords, proj) {
  144. if (typeof coords[0] !== "object") return proj.invert(coords);
  145. return coords.map(function (coord) {
  146. return unprojectCoords(coord, proj);
  147. });
  148. }
  149. /**
  150. * Define Azimuthal Equidistant projection
  151. *
  152. * @private
  153. * @param {Geometry|Feature<any>} geojson Base projection on center of GeoJSON
  154. * @returns {GeoProjection} D3 Geo Azimuthal Equidistant Projection
  155. */
  156. function defineProjection(geojson) {
  157. var coords = center__default['default'](geojson).geometry.coordinates;
  158. var rotation = [-coords[0], -coords[1]];
  159. return d3Geo.geoAzimuthalEquidistant().rotate(rotation).scale(helpers.earthRadius);
  160. }
  161. module.exports = buffer;
  162. module.exports.default = buffer;