public Point2(Point2 P)
 {
     cluster = P.cluster;
     X = P.X;
     Y = P.Y;
     count = P.count;
 }
        private int cluster(ref Point2[] data, ref Point2[] Means)
        {
            Random r = new Random((int)DateTime.Now.TimeOfDay.Ticks);

            int index = r.Next(data.Length);
            Means[0].cluster = 0;
            Means[0].X = data[index].X;
            Means[0].Y = data[index].Y;

            for (int j = 1; j < Means.Length; j++)
            {
                double[] tempDist = new double[data.Length];
                double tempMin = double.MaxValue;

                for (int i = 0; i < data.Length; i++)
                {
                    tempDist[i] = double.MaxValue;
                    for (int k = 0; k < j; k++)
                    {
                        tempMin = Distance(data[i], Means[k]);
                        if (tempMin < tempDist[i])
                            tempDist[i] = tempMin;
                    }
                }

                double randomD = r.NextDouble() * (tempDist.Sum());

                for (int i = 0; i < data.Length; i++)
                {
                    if (randomD < tempDist[i])
                    {
                        Means[j].cluster = j;
                        Means[j].X = data[i].X;
                        Means[j].Y = data[i].Y;
                        break;
                    }

                    randomD = randomD - tempDist[i];
                }
            }

            // Find K Means
            double[] Dist = new double[Means.Length];
            Point2[] sums = new Point2[Means.Length];
            bool reiterate = true;

            int iterations = 0;
            while (reiterate)
            {
                iterations++;
                if (iterations > 100) break;
                reiterate = false;

                for (int j = 0; j < Means.Length; j++)
                {
                    sums[j].X = 0.0;
                    sums[j].Y = 0.0;
                    sums[j].count = 0;
                }

                int i;
                for (i = 0; i < data.Length; i++)
                {
                    for (int j = 0; j < Means.Length; j++)
                    {
                        Dist[j] = Distance(data[i], Means[j]);
                    }

                    for (int j = 0; j < Means.Length; j++)
                    {
                        if (Dist[j].Equals((double)Dist.Min()) && data[i].cluster != j)
                        {
                            reiterate = true;
                            data[i].cluster = j;
                            break;
                        }
                    }

                    sums[data[i].cluster].X += data[i].X;
                    sums[data[i].cluster].Y += data[i].Y;
                    sums[data[i].cluster].count++;
                }

                for (int j = 0; j < Means.Length; j++)
                {
                    if (sums[j].count > 0)
                    {
                        Means[j].X = sums[j].X / sums[j].count;
                        Means[j].Y = sums[j].Y / sums[j].count;
                        Means[j].count = sums[j].count;
                    }
                }
            }
            return iterations;
        }
 private double Distance(Point2 P1, Point2 P2)
 {
     return Math.Sqrt(Math.Pow((P2.Y - P1.Y), 2) + Math.Pow((P2.X - P1.X), 2));
 }
        public void Execute(params object[] args)
        {
            string statusMessage = "Clustering orders.";
            App.Current.Messenger.AddInfo(statusMessage);

            /////////////////////////////////
            // Find Current Schedule, Routes and Orders
            /////////////////////////////////
            ESRI.ArcLogistics.DomainObjects.Schedule schedule = new ESRI.ArcLogistics.DomainObjects.Schedule();

            int numSchedules = App.Current.Project.Schedules.Count;

            foreach (ESRI.ArcLogistics.DomainObjects.Schedule s in App.Current.Project.Schedules.Search(App.Current.CurrentDate))
            {
                if (s.Name == "Current")
                {
                    schedule = s;
                    break;
                }
            }

            //Get number of Routes on current Schedule
            int numRoutes = schedule.Routes.Count;

            //Get number of Orders for current Date
            int numOrders = App.Current.Project.Orders.GetCount(App.Current.CurrentDate);

            if (numRoutes > 0 && numOrders > numRoutes)
            {
                /////////////////////////////////
                // Create Dataset
                /////////////////////////////////
                Point2[] data = new Point2[numOrders];
                Point2[] centres = new Point2[numRoutes];

                List<ESRI.ArcLogistics.DomainObjects.Order> oList = App.Current.Project.Orders.Search(App.Current.CurrentDate).ToList();
                for (int i = 0; i < numOrders; i++)
                {
                    ESRI.ArcLogistics.DomainObjects.Order o = oList.ElementAt(i);
                    Point2 p2 = new Point2(0, o.GeoLocation.Value.X, o.GeoLocation.Value.Y);
                    data[i] = p2;
                }

                /////////////////////////////////
                // Perform Clustering
                /////////////////////////////////

                int iterations = cluster(ref data, ref centres);

                /////////////////////////////////
                // Delete all previous zones from the project
                /////////////////////////////////

                for (int c = App.Current.Project.Zones.Count - 1; c >= 0; c--)
                {
                    ESRI.ArcLogistics.DomainObjects.Zone z = App.Current.Project.Zones.ElementAt(c);
                    App.Current.Project.Zones.Remove(z);
                }

                /////////////////////////////////
                // Delete all previous zones from the current day's routes
                /////////////////////////////////

                for (int j = 0; j < numRoutes; j++)
                {
                    for (int c = schedule.Routes[j].Zones.Count - 1; c >= 0; c--)
                    {
                        ESRI.ArcLogistics.DomainObjects.Zone z = schedule.Routes[j].Zones.ElementAt(c);
                        schedule.Routes[j].Zones.Remove(z);
                    }
                }

                /////////////////////////////////
                // Find Convex Hull, create Soft Zones and assign them to routes
                /////////////////////////////////

                List<Point3>[] DataList = new List<Point3>[numRoutes];
                for (int j = 0; j < numRoutes; j++)
                    DataList[j] = new List<Point3>();

                for (int i = 0; i < numOrders; i++)
                {
                    Point3 p3 = new Point3(data[i].X, data[i].Y);
                    DataList[data[i].cluster].Add(p3);
                }

                for (int j = 0; j < numRoutes; j++)
                {
                    // Dont create zones for less than 3 orders
                    if (DataList[j].Count <= 2)
                        continue;

                    // Get Convex Hull
                    Point3[] chpts = ConvexPolygon.getConvexPolygon(DataList[j].ToArray());

                    // Convert Point3 array to Geometry Point array
                    ESRI.ArcLogistics.Geometry.Point[] polyPoints = new ESRI.ArcLogistics.Geometry.Point[chpts.Length];
                    for (int k = 0; k < chpts.Length; k++)
                    {
                        Point3 p3 = chpts[k];

                        ESRI.ArcLogistics.Geometry.Point geoP = new ESRI.ArcLogistics.Geometry.Point();
                        geoP.X = p3.x;
                        geoP.Y = p3.y;

                        polyPoints[k] = geoP;
                    }

                    // Create soft zone, and add to Project and to a Route
                    ESRI.ArcLogistics.Geometry.Polygon p = new ESRI.ArcLogistics.Geometry.Polygon(polyPoints);
                    ESRI.ArcLogistics.DomainObjects.Zone z = new ESRI.ArcLogistics.DomainObjects.Zone();
                    z.CreationTime = DateTime.Now.Ticks;
                    z.Geometry = p;
                    z.Name = String.Format("Zone {0}", j+1);

                    App.Current.Project.Zones.Add(z);
                    schedule.Routes[j].Zones.Add(z);
                    schedule.Routes[j].HardZones = false;

                }
                App.Current.Project.Save();
                statusMessage = "Finished clustering orders.";
                App.Current.Messenger.AddInfo(statusMessage);
            }
            else
            {
                statusMessage="";

                if(numOrders==0)
                    statusMessage = "No Orders found.";
                else if(numRoutes==0)
                    statusMessage = "No Routes found.";
                else if (numRoutes > numOrders)
                    statusMessage = "Insufficient number of orders.";

                App.Current.Messenger.AddError(statusMessage);
            }
        }