const double Exp = 2; // 2=euclid, 1=manhatten
        
        // Minkowski dist
        // if lat lon precise dist is needed, use Haversine or similar formulas
        // this is approx calc for clustering, no precise dist is needed
        public static double Distance(MapPoint a, MapPoint b)
        {
            // lat lon wrap, values don't seem needed to be normalized to [0;1] for better distance calc
            var absx = LatLonDiff(a.X, b.X);
            var absy = LatLonDiff(a.Y, b.Y);

            return Math.Pow(Math.Pow(absx, Exp) +
                Math.Pow(Math.Abs(absy), Exp), 1.0 / Exp);
        }
 public int CompareTo(MapPoint other, int dimension)
 {
     switch (dimension)
     {
         case 0:
             return this.X.CompareTo(other.X);
         case 1:
             return this.Y.CompareTo(other.Y);
         default:
             throw new ArgumentException("Invalid dimension.");
     }
 }
        public void BuildContent(MapPoint point)
        {
            if (point == null)
            {
                Content = "Marker could not be found";
                return;
            }

            Id = point.MarkerId.ToString();
            Type = point.MarkerType;
            Lat = point.Lat;
            Long = point.Long;

            var stringBuilder = new StringBuilder();
            stringBuilder.AppendLine("<div class='gmcKN-marker-info'>");
            stringBuilder.AppendFormat("Time: {0}<br/>",DateTime.Now.ToString("HH:mm:ss"));
            stringBuilder.AppendFormat("Id: {0}<br /> Type: {1}<br />", Id, Type);
            stringBuilder.AppendFormat("Lat: {0} Lon: {1}", point.Lat, point.Long);
            stringBuilder.AppendLine("</div>");

            Content = stringBuilder.ToString();
        }        
        // To work properly it requires the p is already normalized
        protected static int[] GetPointMappedIds(MapPoint point, Boundary grid, double deltaX, double deltaY)
        {
            #region Naive
            // Naive version, lon points near 180 and lat points near 90 are not clustered together
            //idx = (int)(relativeX / deltax);
            //idy = (int)(relativeY / deltay);
            //var relativeX = p.X - grid.Minx;
            #endregion Naive

            /*
            You have to draw a line with longitude values 180, -180 on papir to understand this            
                
             e.g. _deltaX = 20
longitude        150   170  180  -170   -150
                 |      |          |     |
                 
       
   idx =         7      8    9    -9    -8
                            -10    
                                  
here we want idx 8, 9, -10 and -9 be equal to each other, we set them to idx=8
then the longitudes from 170 to -170 will be clustered together
             */

            var relativeY = point.Y - grid.MinY;

            var overlapMapMinX = (int)(LatLongInfo.MinLonValue / deltaX) - 1;
            var overlapMapMaxX = (int)(LatLongInfo.MaxLonValue / deltaX);

            // The deltaX = 20 example scenario, then set the value 9 to 8 and -10 to -9

            // Similar to if (LatLonInfo.MaxLonValue % deltax == 0) without floating presicion issue
            if (Math.Abs(LatLongInfo.MaxLonValue % deltaX - 0) < Numbers.Epsilon)
            {
                overlapMapMaxX--;
                overlapMapMinX++;
            }

            var idxx = (int)(point.X / deltaX);
            if (point.X < 0) idxx--;

            if (Math.Abs(LatLongInfo.MaxLonValue % point.X - 0) < Numbers.Epsilon)
            {
                if (point.X < 0) idxx++;
                else idxx--;
            }
            if (idxx == overlapMapMinX) idxx = overlapMapMaxX;

            var idx = idxx;

            // Latitude never wraps around with Google Maps, ignore 90, -90 wrap-around for latitude
            var idy = (int)(relativeY / deltaY);

            return new[] { idx, idy };
        }
 public static double Haversine(MapPoint p1, MapPoint p2)
 {
     return Haversine(p1.Y, p1.X, p2.Y, p2.X);
 }
 public static bool IsInside(Boundary b, MapPoint p)
 {
     return IsInside(b.MinX, b.MinY, b.MaxX, b.MaxY, p.X, p.Y, false, false);
 }
        // Circular mean, very relevant for points around New Zealand, where lon -180 to 180 overlap
        // Adapted Centroid Calculation of N Points for Google Maps usage
        // If you are working with points in a specific country only such as India or USA then a simpler centroid calc can be used
        // http://en.wikipedia.org/wiki/Centroid
        public static MapPoint GetCentroidFromClusterLatLon(List<MapPoint> list) //O(n)
        {
            int count;
            if (list == null || (count = list.Count) == 0) return null;

            if (count == 1)
            {
                return list.First();
            }

            // http://en.wikipedia.org/wiki/Circular_mean
            // http://stackoverflow.com/questions/491738/how-do-you-calculate-the-average-of-a-set-of-angles
            /*
                                  1/N*  sum_i_from_1_to_N sin(a[i])
                a = atan2      ---------------------------
                                  1/N*  sum_i_from_1_to_N cos(a[i])
             */

            double longSin = 0;
            double longCos = 0;
            double latSin = 0;
            double latCos = 0;
            foreach (var p in list)
            {
                longSin += Math.Sin(p.X.LatLongToRadian());
                longCos += Math.Cos(p.X.LatLongToRadian());
                latSin += Math.Sin(p.Y.LatLongToRadian());
                latCos += Math.Cos(p.Y.LatLongToRadian());
            }

            longSin /= count;
            longCos /= count;

            double radx = 0;
            double rady = 0;

            // if both are zero-ish
            if (Math.Abs(longSin - 0) > Numbers.Epsilon && Math.Abs(longCos - 0) > Numbers.Epsilon)
            {
                radx = Math.Atan2(longSin, longCos);
                rady = Math.Atan2(latSin, latCos);
            }
            var x = radx.RadianToLatLong();
            var y = rady.RadianToLatLong();

            var centroid = new MapPoint { X = x, Y = y, Count = count };
            return centroid;
        }
        public MapPoint GetClosestPoint(MapPoint from, List<MapPoint> list) // O(n)
        {
            var min = double.MaxValue;
            MapPoint closests = null;
            foreach (var p in list)
            {
                var dist = MathTool.Distance(from, p);
                if (dist >= min) continue;

                // update
                min = dist;
                closests = p;
            }
            return closests;
        }