Created
March 24, 2012 21:26
-
-
Save dbouwman/2188210 to your computer and use it in GitHub Desktop.
Server Side Clustering Class in C#
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Web; | |
using SafeRoutes.Infrastructure.Storage.Domain; | |
namespace SafeRoutes.Infrastructure.Services | |
{ | |
/// <summary> | |
/// Service that clusters ProjectPoints | |
/// </summary> | |
/// <remarks>could be generalized via interfaces</remarks> | |
public static class PointClusterService | |
{ | |
/// <remarks>Originally From James O'Brien's Clustering Sample</remarks> | |
public static List<ProjectPoint> Cluster(List<ProjectPoint> pins, double resolution) | |
{ | |
//sort pins - must be ordered correctly by x,y | |
pins.Sort(); | |
List<ProjectPoint> clusteredPoints = new List<ProjectPoint>(); | |
for (int index = 0; index < pins.Count; index++) | |
{ | |
if (!pins[index].c) //skip already clustered pins | |
{ | |
ProjectPoint currentClusterPin = new ProjectPoint(); | |
//create our cluster object and add the first pin | |
currentClusterPin.AddPoint(pins[index]); | |
//the pin will represent this first point... | |
currentClusterPin.pid = pins[index].pid; | |
currentClusterPin.Amount = pins[index].Amount; | |
currentClusterPin.Name = pins[index].Name; | |
currentClusterPin.Src = pins[index].Src; | |
currentClusterPin.Year = pins[index].Year; | |
currentClusterPin.cs = pins[index].cs; | |
currentClusterPin.nsc = pins[index].nsc; | |
currentClusterPin.Location = pins[index].Location; | |
//Need to know what icon to show | |
if (pins[index].Src.ToLower() == "statewide") | |
{ | |
currentClusterPin.st = true; | |
} | |
//If any pin the the cluster is positive,the entire cluster needs to know | |
if (pins[index].Src.ToLower() == "district") | |
{ | |
currentClusterPin.sd =true; | |
} | |
//look backwards in the list for any points within the range that are not already grouped, | |
//as the points are in order we exit as soon as it exceeds the range. | |
PointClusterService.AddPinsWithinRange(pins, index, -1, currentClusterPin, resolution); | |
//look forwards in the list for any points within the range, again we short out. | |
PointClusterService.AddPinsWithinRange(pins, index, 1, currentClusterPin, resolution); | |
currentClusterPin.ico = GetPointIcon(currentClusterPin); | |
currentClusterPin.SortCluster(); | |
clusteredPoints.Add(currentClusterPin); | |
} | |
} | |
return clusteredPoints; | |
} | |
public static string GetPointIcon(ProjectPoint pt) | |
{ | |
//assume it's just a school | |
string icoName = "sa"; | |
//check if the st flag has been set (i.e. this is a state-wide award) | |
if (pt.st) | |
{ | |
//state award trumps everything... | |
icoName = "sta"; | |
} | |
else if(pt.sd) | |
{ | |
icoName = "sda"; | |
} | |
//if it's clustered... | |
if (pt.c) { icoName += "-c"; } | |
return icoName; | |
} | |
/// <summary> | |
/// | |
/// </summary> | |
/// <param name="pins"></param> | |
/// <param name="index"></param> | |
/// <param name="direction"></param> | |
/// <param name="currentClusterPin"></param> | |
/// <param name="zoomLevel"></param> | |
/// <remarks>From James O'Brien's Clustering Sample</remarks> | |
private static void AddPinsWithinRange(List<ProjectPoint> pins, int index, int direction, ProjectPoint currentClusterPin, double resolution) | |
{ | |
//Cluster width & heigth are in pixels. So any point within 20 pixels at the zoom level will be clustered. | |
int clusterwidth = 22; //Cluster region width, all pin within this area are clustered | |
bool finished = false; | |
int searchindex; | |
searchindex = index + direction; | |
while (!finished) | |
{ | |
if (searchindex >= pins.Count || searchindex < 0) | |
{ | |
finished = true; | |
} | |
else | |
{ | |
if (!pins[searchindex].c) | |
{ | |
//find distance between two points at specified indexes | |
if (Math.Abs(pins[searchindex].Location.X - pins[index].Location.X) / resolution < clusterwidth) //within the same x range | |
{ | |
if (Math.Abs(pins[searchindex].Location.Y - pins[index].Location.Y) / resolution < clusterwidth) //within the same y range = cluster needed | |
{ | |
//add to cluster | |
currentClusterPin.AddPoint(pins[searchindex]); | |
//this point represents a cluster... | |
currentClusterPin.c = true; | |
//================================================================================= | |
// Need to update the parent when we find a higher ranking point in the cluster | |
//basically transfer all the attributes "up" | |
//================================================================================= | |
if (currentClusterPin.Src.ToLower() == "school" && pins[searchindex].Src.ToLower() == "district") | |
{ | |
//currently it's a school, and the new pin is a district... copy | |
currentClusterPin.sd = true; | |
currentClusterPin.pid = pins[searchindex].pid; | |
currentClusterPin.Amount = pins[searchindex].Amount; | |
currentClusterPin.Name = pins[searchindex].Name; | |
currentClusterPin.Src = pins[searchindex].Src; | |
currentClusterPin.Year = pins[searchindex].Year; | |
currentClusterPin.cs = pins[searchindex].cs; | |
currentClusterPin.nsc = pins[searchindex].nsc; | |
} | |
if (currentClusterPin.Src.ToLower() == "school" && pins[searchindex].Src.ToLower() == "statewide") | |
{ | |
//clone up | |
currentClusterPin.st = true; | |
currentClusterPin.pid = pins[searchindex].pid; | |
currentClusterPin.Amount = pins[searchindex].Amount; | |
currentClusterPin.Name = pins[searchindex].Name; | |
currentClusterPin.Src = pins[searchindex].Src; | |
currentClusterPin.Year = pins[searchindex].Year; | |
currentClusterPin.cs = pins[searchindex].cs; | |
currentClusterPin.nsc = pins[searchindex].nsc; | |
} | |
if (currentClusterPin.Src.ToLower() == "district" && pins[searchindex].Src.ToLower() == "statewide") | |
{ | |
//clone up | |
currentClusterPin.st = true; | |
currentClusterPin.pid = pins[searchindex].pid; | |
currentClusterPin.Amount = pins[searchindex].Amount; | |
currentClusterPin.Name = pins[searchindex].Name; | |
currentClusterPin.Src = pins[searchindex].Src; | |
currentClusterPin.Year = pins[searchindex].Year; | |
currentClusterPin.cs = pins[searchindex].cs; | |
currentClusterPin.nsc = pins[searchindex].nsc; | |
} | |
//stop any further clustering actions on the pin we are comparing | |
//because it's already added to a cluster | |
pins[searchindex].c = true; | |
} | |
} | |
else | |
{ | |
finished = true; | |
} | |
} | |
searchindex += direction; | |
} | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment