index.js 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. import { isObject, isNumber, degreesToRadians, polygon } from '@turf/helpers';
  2. import rhumbDestination from '@turf/rhumb-destination';
  3. import transformRotate from '@turf/transform-rotate';
  4. import { getCoord } from '@turf/invariant';
  5. /**
  6. * Takes a {@link Point} and calculates the ellipse polygon given two semi-axes expressed in variable units and steps for precision.
  7. *
  8. * @param {Coord} center center point
  9. * @param {number} xSemiAxis semi (major) axis of the ellipse along the x-axis
  10. * @param {number} ySemiAxis semi (minor) axis of the ellipse along the y-axis
  11. * @param {Object} [options={}] Optional parameters
  12. * @param {number} [options.angle=0] angle of rotation in decimal degrees, positive clockwise
  13. * @param {Coord} [options.pivot='origin'] point around which the rotation will be performed
  14. * @param {number} [options.steps=64] number of steps
  15. * @param {string} [options.units='kilometers'] unit of measurement for axes
  16. * @param {Object} [options.properties={}] properties
  17. * @returns {Feature<Polygon>} ellipse polygon
  18. * @example
  19. * var center = [-75, 40];
  20. * var xSemiAxis = 5;
  21. * var ySemiAxis = 2;
  22. * var ellipse = turf.ellipse(center, xSemiAxis, ySemiAxis);
  23. *
  24. * //addToMap
  25. * var addToMap = [turf.point(center), ellipse]
  26. */
  27. function ellipse(center, xSemiAxis, ySemiAxis, options) {
  28. // Optional params
  29. options = options || {};
  30. var steps = options.steps || 64;
  31. var units = options.units || "kilometers";
  32. var angle = options.angle || 0;
  33. var pivot = options.pivot || center;
  34. var properties = options.properties || center.properties || {};
  35. // validation
  36. if (!center) throw new Error("center is required");
  37. if (!xSemiAxis) throw new Error("xSemiAxis is required");
  38. if (!ySemiAxis) throw new Error("ySemiAxis is required");
  39. if (!isObject(options)) throw new Error("options must be an object");
  40. if (!isNumber(steps)) throw new Error("steps must be a number");
  41. if (!isNumber(angle)) throw new Error("angle must be a number");
  42. var centerCoords = getCoord(center);
  43. if (units === "degrees") {
  44. var angleRad = degreesToRadians(angle);
  45. } else {
  46. xSemiAxis = rhumbDestination(center, xSemiAxis, 90, { units: units });
  47. ySemiAxis = rhumbDestination(center, ySemiAxis, 0, { units: units });
  48. xSemiAxis = getCoord(xSemiAxis)[0] - centerCoords[0];
  49. ySemiAxis = getCoord(ySemiAxis)[1] - centerCoords[1];
  50. }
  51. var coordinates = [];
  52. for (var i = 0; i < steps; i += 1) {
  53. var stepAngle = (i * -360) / steps;
  54. var x =
  55. (xSemiAxis * ySemiAxis) /
  56. Math.sqrt(
  57. Math.pow(ySemiAxis, 2) +
  58. Math.pow(xSemiAxis, 2) * Math.pow(getTanDeg(stepAngle), 2)
  59. );
  60. var y =
  61. (xSemiAxis * ySemiAxis) /
  62. Math.sqrt(
  63. Math.pow(xSemiAxis, 2) +
  64. Math.pow(ySemiAxis, 2) / Math.pow(getTanDeg(stepAngle), 2)
  65. );
  66. if (stepAngle < -90 && stepAngle >= -270) x = -x;
  67. if (stepAngle < -180 && stepAngle >= -360) y = -y;
  68. if (units === "degrees") {
  69. var newx = x * Math.cos(angleRad) + y * Math.sin(angleRad);
  70. var newy = y * Math.cos(angleRad) - x * Math.sin(angleRad);
  71. x = newx;
  72. y = newy;
  73. }
  74. coordinates.push([x + centerCoords[0], y + centerCoords[1]]);
  75. }
  76. coordinates.push(coordinates[0]);
  77. if (units === "degrees") {
  78. return polygon([coordinates], properties);
  79. } else {
  80. return transformRotate(polygon([coordinates], properties), angle, {
  81. pivot: pivot,
  82. });
  83. }
  84. }
  85. /**
  86. * Get Tan Degrees
  87. *
  88. * @private
  89. * @param {number} deg Degrees
  90. * @returns {number} Tan Degrees
  91. */
  92. function getTanDeg(deg) {
  93. var rad = (deg * Math.PI) / 180;
  94. return Math.tan(rad);
  95. }
  96. export default ellipse;