ThinkGeo.com    |     Documentation    |     Premium Support

Equivalent to bing maps icon clustering?

is there an equivalent to BIng maps icon clustering based on zoom levels? When I zoom to a certain level and icons start stacking on top of each other, I'd like the display behavior to change...



Gregory,



  I whipped up some sample code that does the same thing.  The code is a little rough and for large number of points there may be some better ways to do this but I wanted to pass it along to you with a screen shot.  I am attaching the screenshot here and the code will come in the next post in a few minutes.  It works with Icons and other things as well.  it actually inherits from the PointStyle.  We added a TextStyle property so you can label on it.  Just refrence the FeatureCountColumn and it will have the total.



David





Greg,



Here is the code that goes with it.  I tried to comment it as best I could.  You can really go many ways with this as it inherits from PointStyle so you can add icons etc.  Also remember to use the text style to show the text. 



Also CB says hi. :-)



David



  
            // This is just a small sample on how to create the instance and some setting I used in the
            // screenshots
            ClusterPointStyle clusterPointStyle = new ClusterPointStyle(PointSymbolType.Circle, new GeoSolidBrush(GeoColor.FromArgb(100, GeoColor.StandardColors.Blue)), new GeoPen(GeoColor.StandardColors.Blue, 1), 25);
            clusterPointStyle.TextStyle = new TextStyle("FeatureCount", new GeoFont("Arial", 8), new GeoSolidBrush(GeoColor.StandardColors.Black));
            clusterPointStyle.TextStyle.DuplicateRule = LabelDuplicateRule.UnlimitedDuplicateLabels;
            clusterPointStyle.TextStyle.PointPlacement = PointPlacement.Center;


    public class ClusterPointStyle : PointStyle
    {
        private int cellSize = 100;
        private TextStyle textSytle = new TextStyle();

        // These are all of the constructors from the origional point style
        public ClusterPointStyle()
            : base()
        { }

        public ClusterPointStyle(GeoImage image)
            : base(image)
        { }

        public ClusterPointStyle(GeoFont characterFont, int characterIndex, GeoSolidBrush characterSolidBrush)
            : base(characterFont, characterIndex, characterSolidBrush)
        { }

        public ClusterPointStyle(PointSymbolType symbolType, GeoSolidBrush symbolSolidBrush, int symbolSize)
            : base(symbolType, symbolSolidBrush, symbolSize)
        { }

        public ClusterPointStyle(PointSymbolType symbolType, GeoSolidBrush symbolSolidBrush, GeoPen symbolPen, int symbolSize)
            : base(symbolType, symbolSolidBrush, symbolPen, symbolSize)
        { }

        // The TextStyle will be the label on the cluster.
        public TextStyle TextStyle
        {
            get { return textSytle; }
            set { textSytle = value; }
        }

        // This is in pixels and determines how the screen will be divided.  The small the slower it runs
        public int CellSize
        {
            get { return cellSize; }
            set { cellSize = value; }
        }

        // Here in the DrawCore we cluster the features
        protected override void DrawCore(IEnumerable<Feature> features, GeoCanvas canvas, Collection<SimpleCandidate> labelsInThisLayer, Collection<SimpleCandidate> labelsInAllLayers)
        {
            //  We get the scale to determine the grid.  This scale property should really be on the Canvas!
            double scale = ExtentHelper.GetScale(canvas.CurrentWorldExtent, canvas.Width, canvas.MapUnit);

            // Setup our grid for clustering the points.  This is where we specify our cell size in pixels
            MapSuiteTileMatrix mapSuiteTileMatrix = new MapSuiteTileMatrix(scale, cellSize, cellSize, canvas.MapUnit);

            // Pass in the current extent to get our grid cells.  All points in these cells will be consolidated
            IEnumerable<TileMatrixCell> tileMatricCells = mapSuiteTileMatrix.GetContainedCells(canvas.CurrentWorldExtent);

            // Create an unused features list, as we add them to clusters we will remove them from here
            // This is just for speed so we don't re-test lots of already associated features
            Dictionary<string, string> unusedFeatures = new Dictionary<string, string>();
            foreach (Feature feature in features)
            {
                unusedFeatures.Add(feature.Id, feature.Id);
            }

            // Loop through each cell and find the features that fit inside of it
            foreach (TileMatrixCell cell in tileMatricCells)
            {
                int featureCount = 0;                
                foreach (Feature feature in features)
                {
                    // Make sure the feature has not been used in another cluster                   
                    if (unusedFeatures.ContainsKey(feature.Id))
                    {
                        // Check if the cell contains the feature
                        if (cell.BoundingBox.Contains(feature.GetBoundingBox()))
                        {
                            featureCount++;
                            unusedFeatures.Remove(feature.Id);
                        }
                    }
                }
                if (featureCount > 0)
                {
                    // Add the feature count to the new feature we created.  The feature will be placed
                    // at the center of the cell we created
                    Dictionary<string, string> featureValues = new Dictionary<string, string>();
                    featureValues.Add("FeatureCount", featureCount.ToString());

                    //Draw the point shape
                    base.DrawCore(new Feature[] { new Feature(cell.BoundingBox.GetCenterPoint(), featureValues) }, canvas, labelsInThisLayer, labelsInAllLayers);
                    
                    // Draw the text style to show how many feaures are consolidated in the cluster
                    textSytle.Draw(new Feature[] { new Feature(cell.BoundingBox.GetCenterPoint(), featureValues) }, canvas, labelsInThisLayer, labelsInAllLayers);
                }
            }
        }
    }


 



Thanks, David - I will give it a try! 
  
 PS please pass on to CB what an outstanding job you guys are doing on the support forums! The first thing I do when I wake up in the morning is check to see what you guys have posted (well, after my coffee, naturally).

Gregory, 
  
   I hope you find it useful.  At least now you have the code and can tweak it to your needs.  Play around with icons and maybe even the doughnut style we have out on the code community.  I think there are a few ways to speed it up and we coudl do the labeling a bit better.  I think we are going to turn it into a code community piece and expand on it a bit and eventually it will make it into our core API.  It is pretty cool stuff. 
  
   I will pass on the info to CB.  I think we have lots of people subscribing to our forums.  Some days it’s like winning the super bowl and other days it’s like a soap opera! 
  
 David

Greg, 
  
   I wanted to update this as we have posted this project to the Code.ThinkGeo.Com.  The guys made some improvements over my original design.  I was drawing the icon representing the cluster at the center of the grid cell.  They enhanced it to draw the icon at the weighted center so it looks much better.  I am sure this might slow it down a bit but in many cases the position will be superior. 
  
 code.thinkgeo.com/projects/show/clusterpointstyle 
  
 David