/*
 * Prune points from a polyline
 *
 * Requires leafletjs
 */

(function(factory, window) {
    // define an AMD module that relies on 'leaflet'
    if (typeof define === 'function' && define.amd) {
        define(['leaflet'], function(L) {
            factory(L);
        });
    } else if (typeof module === 'object' && module.exports) {
        // require('LIBRARY') returns a factory that requires window to
        // build a LIBRARY instance, we normalize how we use modules
        // that require this pattern but the window provided is a noop
        // if it's defined
        module.exports = function(root, L) {
            if (L === undefined) {
                if (typeof window !== 'undefined') {
                    L = require('leaflet');
                } else {
                    L = require('leaflet')(root);
                }
            }
            factory(L);
            return L;
        };
    } else if (typeof window !== 'undefined' && window.L) {
        factory(window.L);
    }
}(function polypruneFactory(L) {

    var PolyPrune = L.Class.extend({

        statics: {
            /**
             * Pruning function
             * Removes points located less than a given distance from the track between its adjacent points.
             * Possible options:
             *  - tolerance: distance in meters (default is 5)
             *  - useAlt: use altitude to compute 3D distance (default is true)
             */
            prune : function(latlngs, options) {

                var tolerance = 5;
                var useAlt = true;
                if (options) {
                  if (!isNaN(options.tolerance)) {
                    tolerance = options.tolerance;
                  }
                  if (typeof(options.useAlt) == typeof(true)) {
                    useAlt = options.useAlt;
                  }
                }

                var initlen = latlngs.length; // initial number of points
                var pruned = [];

                /**
                 * Returns the closest distance (2D) of a point to a segment defined by 2 points
                 *
                 * Adapted from Pieter Iserbyt & Paul Bourke http://paulbourke.net/geometry/pointlineplane/
                 *
                 * @param startLine  First point of the segment
                 * @param endLine    Second point of the segment
                 * @return The distance
                 */
                function distanceFromLine(latlng, startLine, endLine) {

                    var xDelta = endLine.lng - startLine.lng;
                    var yDelta = endLine.lat - startLine.lat;
                    // we need all points to have altitude to compute 3D distance
                    var zDelta = !useAlt || isNaN(endLine.alt) ||
                      isNaN(startLine.alt) || isNaN(latlng.alt) ?
                      undefined : (endLine.alt - startLine.alt);
                    var closestPoint;

                    if ((xDelta === 0) && (yDelta === 0)) {
                        // startLine and endLine are the same point, return distance from this point
                        closestPoint = L.latLng(startLine.lat, startLine.lng);
                        if (!isNaN(zDelta)) {
                          closestPoint.alt = startLine.alt + zDelta / 2;
                        }
                    }

                    var u = ((latlng.lng - startLine.lng) * xDelta + (latlng.lat - startLine.lat) * yDelta) / (xDelta * xDelta + yDelta * yDelta);

                    if (u < 0) {
                        closestPoint = startLine;
                    } else if (u > 1) {
                        closestPoint = endLine;
                    } else {
                        closestPoint = L.latLng(startLine.lat + u * yDelta, startLine.lng + u * xDelta);
                        if (!isNaN(zDelta)) {
                          closestPoint.alt = startLine.alt + u * zDelta;
                        }
                    }

                    var dist2d = latlng.distanceTo(closestPoint);
                    var dist3d;
                    if (!isNaN(zDelta)) {
                      var zDeltaClosest = closestPoint.alt - latlng.alt;
                      dist3d = Math.sqrt(Math.pow(dist2d,2) + Math.pow(zDeltaClosest,2));
                    }

                    // return 3D distance if it could be computed, otherwise 2D
                    //console.log("2D: " + dist2d);
                    //console.log("3D: " + dist3d);
                    return isNaN(dist3d) ? dist2d : dist3d;
                }


                if (initlen > 2) { // no pruning required when 0, 1 or 2 points
                    var mindeleted = initlen; // mindeleted tracks the smallest deleted point index

                    // we always keep first point
                    pruned.push(latlngs[0]);

                    var ptmax = initlen - 1; // max point index
                    var ptlast = 0; // mast inserted point index

                    for (var i = 1; i < ptmax; i++) {

                        var prev = pruned[pruned.length - 1];
                        var next = latlngs[i + 1];

                        for (var j = i; j > ptlast; j--) {
                            var pt = latlngs[j];
                            var delta = distanceFromLine(pt, prev, next);
                            if (delta > tolerance) {
                                // removing i loses this pt, keep this trkpt[i]
                                latlngs[i].i = pruned.length;
                                ptlast = i;
                                pruned.push(latlngs[i]);
                                break;
                            }
                        }
                        // did we keep i?
                        if (ptlast != i) {
                            // discarded
                            mindeleted = Math.min(i, mindeleted);
                        }
                    }

                    // we always keep last point
                    latlngs[initlen - 1].i = pruned.length;
                    pruned.push(latlngs[latlngs.length - 1]);

                    return pruned;
                } else {
                    return latlngs;
                }

            }
        },

        initialize: function() {
        },
    });

    L.PolyPrune = PolyPrune;
    L.polyPrune = function() {
        return new L.PolyPrune();
    };

}, window));

@@ -3342,34 +3342,43 @@
                %7D
-%0A%0A
+ else %7B%0A%0A  
                 
@@ -3525,24 +3525,26 @@
             
+  
 if (u %3C 0) %7B
@@ -3540,24 +3540,26 @@
 f (u %3C 0) %7B%0A
+  
             
@@ -3612,24 +3612,26 @@
             
+ 
+ 
 %7D else if (u
@@ -3634,24 +3634,26 @@
 f (u %3E 1) %7B%0A
+  
             
@@ -3700,32 +3700,34 @@
                 
+  
 %7D else %7B%0A       
@@ -3711,32 +3711,34 @@
        %7D else %7B%0A
+  
                 
@@ -3842,32 +3842,34 @@
                 
+  
 if (!isNaN(zDelt
@@ -3866,32 +3866,34 @@
 sNaN(zDelta)) %7B%0A
+  
                 
@@ -3949,16 +3949,18 @@
 zDelta;%0A
+  
         
@@ -3989,33 +3989,59 @@
                 
-%7D
+  %7D%0A%0A                    %7D%0A
 %0A%0A              

fix when two points are co-located