ThinkGeo Cloud
ThinkGeo UI Controls
ThinkGeo Open Source
Help and Support
External Resources
ThinkGeo Cloud
ThinkGeo UI Controls
ThinkGeo Open Source
Help and Support
External Resources
using System.Collections.Generic; using System.Collections.ObjectModel; using System.Globalization; using System.Windows; using ThinkGeo.MapSuite.Core; using ThinkGeo.MapSuite.WpfDesktopEdition; namespace ClusterPointSample { public partial class MainWindow : Window { private const string ThemeName = "Default"; private const string assetCollectionType = "Value"; private const string withAssetCollection = "1"; public MainWindow() { InitializeComponent(); } private void WpfMap_Loaded(object sender, RoutedEventArgs e) { wpfMap.MapUnit = GeographyUnit.DecimalDegree; // Set up the value style. ValueStyle valueStyle = GetValueStyle(assetCollectionType, "green.png", "grey.png"); // Set up the cluster style. ClusterPointStyle clusterPointStyle = new ClusterPointStyle(); clusterPointStyle.DrawingClusteredFeature += ClusterPointStyle_DrawingClusteredFeature; clusterPointStyle.Styles.Add(valueStyle); clusterPointStyle.LowerScale = 2000; clusterPointStyle.TextStyle = new TextStyle("FeatureCount", new GeoFont("Arial", 10), GeoBrushes.Black); clusterPointStyle.TextStyle.OverlappingRule = LabelOverlappingRule.AllowOverlapping; // Set the minimum features in per cell. If the count is less than the value it will invisible. clusterPointStyle.MinimumFeaturesPerCellToCluster = 2; ShapeFileFeatureLayer markerLayer = new ShapeFileFeatureLayer("Data/random.shp"); markerLayer.ZoomLevelSet.ZoomLevel06.CustomStyles.Add(clusterPointStyle); markerLayer.ZoomLevelSet.ZoomLevel06.ApplyUntilZoomLevel = ApplyUntilZoomLevel.Level20; markerLayer.Open(); // Set batch limit to number of features in the featuresource to avoid overlapping clusterpoints markerLayer.ProgressiveDrawingRecordsCount = markerLayer.FeatureSource.GetCount(); wpfMap.Overlays.Add(new WorldMapKitWmsWpfOverlay() { Projection = WorldMapKitProjection.DecimalDegrees }); LayerOverlay markerOverlay = new LayerOverlay(); markerOverlay.Layers.Add("markerLayer", markerLayer); // Set up cached InMemoryFeatureLayers for zoom levels 1-5 Collection<ZoomLevel> zoomLevels = markerLayer.ZoomLevelSet.GetZoomLevels(); Collection<Feature> features = markerLayer.QueryTools.GetAllFeatures(ReturningColumnsType.AllColumns); // Iterate over first 5 zoom layers for (int i = 1; i <= 5; i++) { InMemoryFeatureLayer zoomLevelLayer = GetInMemoryFeatureLayerForZoomLevel(markerLayer, features, i); markerOverlay.Layers.Add("layerForZoomLevel" + i, zoomLevelLayer); } wpfMap.Overlays.Add(markerOverlay); wpfMap.CurrentExtent = new RectangleShape(-130, 60, -60, 15); } private InMemoryFeatureLayer GetInMemoryFeatureLayerForZoomLevel(ShapeFileFeatureLayer featureLayer, Collection<Feature> features, int zoomLevelNumber) { InMemoryFeatureLayer layerForZoomLevel = new InMemoryFeatureLayer(); ZoomLevel zoomLevel = layerForZoomLevel.ZoomLevelSet.GetZoomLevels()[zoomLevelNumber-1]; // Get features in zoom level and add them to the FeatureLayer List<Feature> featuresForZoomLevel = GetFeatures(features, zoomLevel.Scale, featureLayer.GetBoundingBox(), GeographyUnit.DecimalDegree, 2, 100); layerForZoomLevel.Open(); layerForZoomLevel.Columns.Add(new FeatureSourceColumn("FeatureCount")); foreach (Feature feature in featuresForZoomLevel) { layerForZoomLevel.InternalFeatures.Add(feature); } // Add drawing styles zoomLevel.CustomStyles.Add(GetClassBreakStyle(layerForZoomLevel.Columns[0].ColumnName)); zoomLevel.CustomStyles.Add(GetValueStyle(assetCollectionType, "green.png", "grey.png")); return layerForZoomLevel; } private void ClusterPointStyle_DrawingClusteredFeature(object sender, DrawingClusteredFeatureClusterPointStyleEventArgs e) { bool useGreenIcon = false; foreach (Feature clusteringFeature in e.ClusteringFeatures) { if (clusteringFeature.ColumnValues[assetCollectionType] == withAssetCollection) { useGreenIcon = true; break; } } // Get the feature count and based on the count to render the different style. int featureCount = int.Parse(e.ClusteredFeature.ColumnValues["FeatureCount"]); TextStyle textStyle = (TextStyle)e.Styles[1]; GeoImage geoImage = new GeoImage(); if (featureCount > 99) { textStyle.XOffsetInPixel = -13; textStyle.YOffsetInPixel = 13; geoImage = GetGeoImage(useGreenIcon ? "largegreen.png": "largegrey.png", ThemeName); } else if (featureCount > 9) { textStyle.XOffsetInPixel = -10; textStyle.YOffsetInPixel = 8; geoImage = GetGeoImage(useGreenIcon ? "green.png" : "grey.png", ThemeName); } else if (featureCount > 1) { textStyle.XOffsetInPixel = -6; textStyle.YOffsetInPixel = 6; geoImage = GetGeoImage(useGreenIcon ? "smallgreen.png" : "smallgrey.png", ThemeName); } PointStyle pointStyle = new PointStyle(geoImage); pointStyle.DrawingLevel = DrawingLevel.LabelLevel; e.Styles.RemoveAt(0); e.Styles.Insert(0, pointStyle); } private ClassBreakStyle GetClassBreakStyle(string columnName) { ClassBreakStyle classBreakStyle = new ClassBreakStyle(); classBreakStyle.ColumnName = columnName; ValueStyle valueStyle = GetValueStyle("WithValue", "largegreen.png", "largegrey.png"); TextStyle textStyle = GetTextStyle(-15, 12, 7); Collection<ThinkGeo.MapSuite.Core.Style> styles = new Collection<ThinkGeo.MapSuite.Core.Style>() { valueStyle, textStyle }; classBreakStyle.ClassBreaks.Add(new ClassBreak(9999, styles)); textStyle = GetTextStyle(-14, 12, 8); styles = new Collection<ThinkGeo.MapSuite.Core.Style>() { valueStyle, textStyle }; classBreakStyle.ClassBreaks.Add(new ClassBreak(999, styles)); textStyle = GetTextStyle(-13, 12, 10); styles = new Collection<ThinkGeo.MapSuite.Core.Style>() { valueStyle, textStyle }; classBreakStyle.ClassBreaks.Add(new ClassBreak(99, styles)); valueStyle = GetValueStyle("WithValue", "green.png", "grey.png"); textStyle = GetTextStyle(-10, 8, 10); styles = new Collection<ThinkGeo.MapSuite.Core.Style>() { valueStyle, textStyle }; classBreakStyle.ClassBreaks.Add(new ClassBreak(9, styles)); valueStyle = GetValueStyle("WithValue", "smallgreen.png", "smallgrey.png"); textStyle = GetTextStyle(-6, 6, 10); styles = new Collection<ThinkGeo.MapSuite.Core.Style>() { valueStyle, textStyle }; classBreakStyle.ClassBreaks.Add(new ClassBreak(2, styles)); return classBreakStyle; } private ValueStyle GetValueStyle(string columnName, string withImageStyleFilename, string withoutImageStyleFilename) { ValueStyle valueStyle = new ValueStyle(); valueStyle.ColumnName = columnName; PointStyle withImageStyle = new PointStyle(GetGeoImage(withImageStyleFilename, ThemeName)); PointStyle withoutImageStyle = new PointStyle(GetGeoImage(withoutImageStyleFilename, ThemeName)); valueStyle.ValueItems.Add(new ValueItem("1", withImageStyle)); valueStyle.ValueItems.Add(new ValueItem("0", withoutImageStyle)); return valueStyle; } private TextStyle GetTextStyle(int xOffsetInPixel, int yOffsetInPixel, int fontSize) { TextStyle textStyle = new TextStyle("FeatureCount", new GeoFont("Arial", fontSize), GeoBrushes.Black); textStyle.OverlappingRule = LabelOverlappingRule.AllowOverlapping; textStyle.XOffsetInPixel = xOffsetInPixel; textStyle.YOffsetInPixel = yOffsetInPixel; return textStyle; } private GeoImage GetGeoImage(string imageName, string themeName) { GeoImage image = new GeoImage(string.Format("Theme/{0}/{1}", themeName, imageName)); return image; } public static List<Feature> GetFeatures(Collection<Feature> features, double scale, RectangleShape currentWorldExtent, GeographyUnit mapUnit, int minimumFeaturesPerCellToCluster, int cellSize) { List<Feature> results = new List<Feature>(); // Make sure that we set the feature threashold greater than 0 otherwise we will never draw the cluster styles // Also ensure that the total number of features is greater than the threshold otherwise we will never draw a cluster style // Check that the current extent is between the upper and lower bounds MapSuiteTileMatrix mapSuiteTileMatrix = new MapSuiteTileMatrix(scale, cellSize, cellSize, mapUnit); IEnumerable<TileMatrixCell> tileMatrixCells = mapSuiteTileMatrix.GetIntersectingCells(currentWorldExtent); List<Feature> validFeatures = new List<Feature>(); Dictionary<string, PointShape> validPoints = new Dictionary<string, PointShape>(); foreach (Feature feature in features) { WellKnownType wellKnownType = feature.GetWellKnownType(); if (wellKnownType == WellKnownType.Point) { validFeatures.Add(feature); validPoints.Add(feature.Id, (PointShape)feature.GetShape()); } else if (wellKnownType == WellKnownType.Multipoint) { validFeatures.Add(feature); validPoints.Add(feature.Id, ((MultipointShape)feature.GetShape()).GetCenterPoint()); } } // Loop through each cell and find the features that fit inside of it foreach (TileMatrixCell cell in tileMatrixCells) { Collection<Feature> featuresInCell = new Collection<Feature>(); int featureCount = 0; MultipointShape tempMultiPointShape = new MultipointShape(); for (int i = validFeatures.Count - 1; i >= 0; i--) { Feature currentFeature = validFeatures[i]; PointShape currentShape = validPoints[currentFeature.Id];// currentFeature.GetShape(); // Check if the cell contains the feature if (IsContained(cell.BoundingBox, currentShape)) { featuresInCell.Add(currentFeature); featureCount++; validFeatures.RemoveAt(i); tempMultiPointShape.Points.Add(currentShape); } } if (featuresInCell.Count >= minimumFeaturesPerCellToCluster) { // Add the feature count to the new feature we created. The feature will be placed // at the center of gravity of all the clustered features of the cell we created. Dictionary<string, string> featureValues = new Dictionary<string, string>(); featureValues.Add("FeatureCount", featureCount.ToString(CultureInfo.InvariantCulture)); featureValues.Add("WithValue", "0"); Feature clusteredFeature = new Feature(tempMultiPointShape.GetCenterPoint(), featureValues); foreach (Feature clusteringFeature in featuresInCell) { //if (clusteringFeature.ColumnValues[_assetCollectionType] == _withAssetCollection) if (clusteringFeature.ColumnValues["Value"] == "1") { clusteredFeature.ColumnValues["WithValue"] = "1"; break; } } results.Add(clusteredFeature); } else { results.AddRange(featuresInCell); } } return results; } private static bool IsContained(RectangleShape boundingBox, PointShape point) { return (boundingBox.LowerLeftPoint.X < point.X && boundingBox.UpperRightPoint.X > point.X) && (boundingBox.UpperLeftPoint.Y > point.Y && boundingBox.LowerRightPoint.Y < point.Y); } } }