| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160 | 'use strict';var meta = require('@turf/meta');var invariant = require('@turf/invariant');var helpers = require('@turf/helpers');var centerMean = require('@turf/center-mean');var pointsWithinPolygon = require('@turf/points-within-polygon');var ellipse = require('@turf/ellipse');function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }var centerMean__default = /*#__PURE__*/_interopDefaultLegacy(centerMean);var pointsWithinPolygon__default = /*#__PURE__*/_interopDefaultLegacy(pointsWithinPolygon);var ellipse__default = /*#__PURE__*/_interopDefaultLegacy(ellipse);/** * Takes a {@link FeatureCollection} and returns a standard deviational ellipse, * also known as a “directional distribution.” The standard deviational ellipse * aims to show the direction and the distribution of a dataset by drawing * an ellipse that contains about one standard deviation’s worth (~ 70%) of the * data. * * This module mirrors the functionality of [Directional Distribution](http://desktop.arcgis.com/en/arcmap/10.3/tools/spatial-statistics-toolbox/directional-distribution.htm) * in ArcGIS and the [QGIS Standard Deviational Ellipse Plugin](http://arken.nmbu.no/~havatv/gis/qgisplugins/SDEllipse/) * * **Bibliography** * * • Robert S. Yuill, “The Standard Deviational Ellipse; An Updated Tool for * Spatial Description,” _Geografiska Annaler_ 53, no. 1 (1971): 28–39, * doi:{@link https://doi.org/10.2307/490885|10.2307/490885}. * * • Paul Hanly Furfey, “A Note on Lefever’s “Standard Deviational Ellipse,” * _American Journal of Sociology_ 33, no. 1 (1927): 94—98, * doi:{@link https://doi.org/10.1086/214336|10.1086/214336}. * * * @name standardDeviationalEllipse * @param {FeatureCollection<Point>} points GeoJSON points * @param {Object} [options={}] Optional parameters * @param {string} [options.weight] the property name used to weight the center * @param {number} [options.steps=64] number of steps for the polygon * @param {Object} [options.properties={}] properties to pass to the resulting ellipse * @returns {Feature<Polygon>} an elliptical Polygon that includes approximately 1 SD of the dataset within it. * @example * * var bbox = [-74, 40.72, -73.98, 40.74]; * var points = turf.randomPoint(400, {bbox: bbox}); * var sdEllipse = turf.standardDeviationalEllipse(points); * * //addToMap * var addToMap = [points, sdEllipse]; * */function standardDeviationalEllipse(points, options) {  // Optional params  options = options || {};  if (!helpers.isObject(options)) throw new Error("options is invalid");  var steps = options.steps || 64;  var weightTerm = options.weight;  var properties = options.properties || {};  // Validation:  if (!helpers.isNumber(steps)) throw new Error("steps must be a number");  if (!helpers.isObject(properties)) throw new Error("properties must be a number");  // Calculate mean center & number of features:  var numberOfFeatures = meta.coordAll(points).length;  var meanCenter = centerMean__default['default'](points, { weight: weightTerm });  // Calculate angle of rotation:  // [X, Y] = mean center of all [x, y].  // theta = arctan( (A + B) / C )  // A = sum((x - X)^2) - sum((y - Y)^2)  // B = sqrt(A^2 + 4(sum((x - X)(y - Y))^2))  // C = 2(sum((x - X)(y - Y)))  var xDeviationSquaredSum = 0;  var yDeviationSquaredSum = 0;  var xyDeviationSum = 0;  meta.featureEach(points, function (point) {    var weight = point.properties[weightTerm] || 1;    var deviation = getDeviations(invariant.getCoords(point), invariant.getCoords(meanCenter));    xDeviationSquaredSum += Math.pow(deviation.x, 2) * weight;    yDeviationSquaredSum += Math.pow(deviation.y, 2) * weight;    xyDeviationSum += deviation.x * deviation.y * weight;  });  var bigA = xDeviationSquaredSum - yDeviationSquaredSum;  var bigB = Math.sqrt(Math.pow(bigA, 2) + 4 * Math.pow(xyDeviationSum, 2));  var bigC = 2 * xyDeviationSum;  var theta = Math.atan((bigA + bigB) / bigC);  var thetaDeg = (theta * 180) / Math.PI;  // Calculate axes:  // sigmaX = sqrt((1 / n - 2) * sum((((x - X) * cos(theta)) - ((y - Y) * sin(theta)))^2))  // sigmaY = sqrt((1 / n - 2) * sum((((x - X) * sin(theta)) - ((y - Y) * cos(theta)))^2))  var sigmaXsum = 0;  var sigmaYsum = 0;  var weightsum = 0;  meta.featureEach(points, function (point) {    var weight = point.properties[weightTerm] || 1;    var deviation = getDeviations(invariant.getCoords(point), invariant.getCoords(meanCenter));    sigmaXsum +=      Math.pow(        deviation.x * Math.cos(theta) - deviation.y * Math.sin(theta),        2      ) * weight;    sigmaYsum +=      Math.pow(        deviation.x * Math.sin(theta) + deviation.y * Math.cos(theta),        2      ) * weight;    weightsum += weight;  });  var sigmaX = Math.sqrt((2 * sigmaXsum) / weightsum);  var sigmaY = Math.sqrt((2 * sigmaYsum) / weightsum);  var theEllipse = ellipse__default['default'](meanCenter, sigmaX, sigmaY, {    units: "degrees",    angle: thetaDeg,    steps: steps,    properties: properties,  });  var pointsWithinEllipse = pointsWithinPolygon__default['default'](    points,    helpers.featureCollection([theEllipse])  );  var standardDeviationalEllipseProperties = {    meanCenterCoordinates: invariant.getCoords(meanCenter),    semiMajorAxis: sigmaX,    semiMinorAxis: sigmaY,    numberOfFeatures: numberOfFeatures,    angle: thetaDeg,    percentageWithinEllipse:      (100 * meta.coordAll(pointsWithinEllipse).length) / numberOfFeatures,  };  theEllipse.properties.standardDeviationalEllipse = standardDeviationalEllipseProperties;  return theEllipse;}/** * Get x_i - X and y_i - Y * * @private * @param {Array} coordinates Array of [x_i, y_i] * @param {Array} center Array of [X, Y] * @returns {Object} { x: n, y: m } */function getDeviations(coordinates, center) {  return {    x: coordinates[0] - center[0],    y: coordinates[1] - center[1],  };}module.exports = standardDeviationalEllipse;module.exports.default = standardDeviationalEllipse;
 |