import centerMean from '@turf/center-mean';
import distance from '@turf/distance';
import centroid from '@turf/centroid';
import { point, isObject, featureCollection } from '@turf/helpers';
import { featureEach } from '@turf/meta';
import { getCoord } from '@turf/invariant';

/**
 * Takes a {@link FeatureCollection} of points and calculates the median center,
 * algorithimically. It uses the algorithm described in:
 *
 * Harold W. Kuhn and Robert E. Kuenne, “An Efficient Algorithm for the
 * Numerical Solution of the Generalized Weber Problem in Spatial
 * Economics,” _Journal of Regional Science_ 4, no. 2 (1962): 21–33,
 * doi:{@link https://doi.org/10.1111/j.1467-9787.1962.tb00902.x}.
 *
 * James E. Burt, Gerald M. Barber, and David L. Rigby, _Elementary
 * Statistics for Geographers_, 3rd ed., New York: The Guilford
 * Press, 2009, 150–151.
 *
 * @name centerMedian
 * @param {FeatureCollection<Point>} features GeoJSON Feature or Geometry
 * @param {Object} [options={}] Optional parameters
 * @param {string} [options.weight] the property name used to weight the center
 * @param {number} [options.tolerance=0.001] the difference in distance between candidate medians at which point the algorighim stops iterating.
 * @returns {Feature<Point>} The median center of the collection
 * @example
 * var points = turf.featureCollection([turf.point([0, 0], turf.point([1, 0]), turf.point([0, 1]), turf.point([5, 8])]);
 * var medianCenter = turf.centerMedian(points);
 *
 * //addToMap
 * var addToMap = [points, medianCenter]
 */
function centerMedian(features, options) {
    // Optional params
    options = options || {};
    if (!isObject(options)) throw new Error('options is invalid');
    var weightTerm = options.weight;

    // Calculate mean center:
    var meanCenter = centerMean(features, {weight: options.weight});
    
    // Calculate center of every feature:
    var centroids = [];
    featureEach(features, function (feature) { centroids.push(centroid(feature, {weight: feature.properties[weightTerm]})); });
    centroids = featureCollection(centroids);
    centroids.properties = {
        tolerance: options.tolerance,
        medianCandidates: []
    };
    return findMedian(meanCenter, [0, 0], centroids);
}

/**
 * Recursive function to find new candidate medians.
 *
 * @private
 * @param {Feature<Position>} candidateMedian current candidate median
 * @param {Feature<Position>} previousCandidate the previous candidate median
 * @param {FeatureCollection<Point>} centroids the collection of centroids whose median we are determining
 * @returns {Feature<Point>} the median center of the dataset.
 */
function findMedian(candidateMedian, previousCandidate, centroids) {
    var tolerance = centroids.properties.tolerance || 0.001;
    var candidateXsum = 0;
    var candidateYsum = 0;
    var kSum = 0;
    featureEach(centroids, function (theCentroid) {
        var weight = theCentroid.properties.weight || 1;
        var distanceFromCandidate = weight * distance(theCentroid, candidateMedian);
        var k = weight / distanceFromCandidate;
        candidateXsum += getCoord(theCentroid)[0] * k;
        candidateYsum += getCoord(theCentroid)[1] * k;
        kSum += k;
    });
    var candidateX = candidateXsum / kSum;
    var candidateY = candidateYsum / kSum;
    if (Math.abs(candidateX - previousCandidate[0]) < tolerance && Math.abs(candidateY - previousCandidate[1]) < tolerance) {
        return point([candidateX, candidateY], {medianCandidates: centroids.properties.medianCandidates});
    } else {
        centroids.properties.medianCandidates.push([candidateX, candidateY]);
        return findMedian([candidateX, candidateY], candidateMedian, centroids);
    }
}

export default centerMedian;


@@ -123,16 +123,26 @@
 import %7B
+ isNumber,
  point, 
@@ -193,16 +193,16 @@
 lpers';%0A
-
 import %7B
@@ -913,21 +913,19 @@
 lection%3C
-Point
+Any
 %3E%7D featu
@@ -1364,17 +1364,18 @@
 oint
-(
+s%5B
 %5B0, 0%5D, 
 turf
@@ -1374,66 +1374,30 @@
 0%5D, 
-turf.point(
 %5B1, 0%5D
-)
 , 
-turf.point(
 %5B0, 1%5D
-)
 , 
-turf.point(
 %5B5, 8%5D
-)
 %5D);%0A
@@ -2917,16 +2917,160 @@
 t %7C%7C 1;%0A
+        if (!isNumber(weight)) throw new Error('weight value must be a number');%0A        weight = Number(weight);%0A        if (weight %3E 0) %7B%0A    
         
@@ -3150,24 +3150,28 @@
 n);%0A        
+    
 var k = weig
@@ -3198,32 +3198,36 @@
 didate;%0A        
+ 
+   
 candidateXsum +=
@@ -3257,32 +3257,36 @@
 0%5D * k;%0A        
+   
+ 
 candidateYsum +=
@@ -3312,24 +3312,28 @@
 id)%5B1%5D * k;%0A
+    
         kSum
@@ -3335,24 +3335,34 @@
  kSum += k;%0A
+        %7D%0A
     %7D);%0A    

Handle 0 or negative weights like in @turf/center-mean