This sample demonstrates implementing a custom clusterer. The
clusterer in this sample aggregates the values of a specified field
for each feautre in a cluster and displays that value on the
cluster. In the sample XAML, the custom clusterer is used to
display aggregate population for each cluster.
Download Sample Application
< UserControl x:Class = " ArcGISWPFSDK.LocalCustomClusterer "
xmlns = " http://schemas.microsoft.com/winfx/2006/xaml/presentation "
xmlns:x = " http://schemas.microsoft.com/winfx/2006/xaml "
xmlns:esri = " http://schemas.esri.com/arcgis/client/2009 "
xmlns:samples = " clr-namespace:ArcGISWPFSDK " >
< Grid x:Name = " LayoutRoot " Background = " White " >
< Grid.Resources >
< esri : SimpleRenderer x:Key = " MySimpleRenderer " >
< esri : SimpleRenderer.Symbol >
< esri : SimpleMarkerSymbol Color = " #FF00BB00 " />
</ esri : SimpleRenderer.Symbol >
</ esri : SimpleRenderer >
< LinearGradientBrush x:Key = " PanelGradient " EndPoint = " 0.5,1 " StartPoint = " 0.5,0 " >
< LinearGradientBrush.RelativeTransform >
< TransformGroup >
< ScaleTransform CenterY = " 0.5 " CenterX = " 0.5 " />
< SkewTransform CenterY = " 0.5 " CenterX = " 0.5 " />
< RotateTransform Angle = " 176 " CenterY = " 0.5 " CenterX = " 0.5 " />
< TranslateTransform />
</ TransformGroup >
</ LinearGradientBrush.RelativeTransform >
< GradientStop Color = " #FF145787 " Offset = " 0.16 " />
< GradientStop Color = " #FF3D7FAC " Offset = " 0.502 " />
< GradientStop Color = " #FF88C5EF " Offset = " 0.984 " />
</ LinearGradientBrush >
</ Grid.Resources >
< esri : Map x:Name = " MyMap " Extent = " -15000000,2000000,-7000000,8000000 " Background = " #FFE3E3E3 " MinimumResolution = " 2445.98490512499 " >
< esri : ArcGISLocalTiledLayer ID = " myBaseMap " Path = " ..\\Data\\TPKs\\Topographic.tpk " />
< esri : ArcGISLocalFeatureLayer ID = " MyFeatureLayer " LayerName = " Cities "
Path = " ..\\Data\\MPKS\\USCitiesStates.mpk "
Where = " POP2000 > 75000 "
OutFields = " POP2000 " Renderer = " {StaticResource MySimpleRenderer} " >
< esri : ArcGISLocalFeatureLayer.Clusterer >
< samples : SumClusterer AggregateColumn = " POP2000 " SymbolScale = " 0.001 " />
</ esri : ArcGISLocalFeatureLayer.Clusterer >
< esri : ArcGISLocalFeatureLayer.MapTip >
< Grid Background = " LightYellow " >
< StackPanel Margin = " 5 " >
< StackPanel Orientation = " Horizontal " >
< TextBlock Foreground = " Black " Text = " Population (2000): " />
< TextBlock Foreground = " Black " Text = " {Binding Path=.[POP2000]} " />
</ StackPanel >
</ StackPanel >
< Border BorderBrush = " Black " BorderThickness = " 1 " />
</ Grid >
</ esri : ArcGISLocalFeatureLayer.MapTip >
</ esri : ArcGISLocalFeatureLayer >
</ esri : Map >
< Grid Margin = " 10 " HorizontalAlignment = " Right " VerticalAlignment = " Top " >
< Border Background = " {StaticResource PanelGradient} " CornerRadius = " 3 " Padding = " 5 " >
< StackPanel Orientation = " Vertical " Margin = " 5 " >
< TextBlock Text = " Population density " Foreground = " White " FontWeight = " Bold " FontSize = " 12 " Margin = " 3 " />
< CheckBox Checked = " CheckBox_Checked " Unchecked = " CheckBox_Unchecked " IsChecked = " True "
HorizontalAlignment = " Center " Foreground = " Black " Margin = " 0,0,5,0 " >
< TextBlock Foreground = " White " Text = " Enable Clustering " > </ TextBlock >
</ CheckBox >
</ StackPanel >
</ Border >
</ Grid >
</ Grid >
</ UserControl >
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using ESRI.ArcGIS.Client;
using ESRI.ArcGIS.Client.Geometry;
namespace ArcGISWPFSDK
{
public partial class LocalCustomClusterer : UserControl
{
public LocalCustomClusterer()
{
InitializeComponent();
}
private void CheckBox_Checked(object sender, RoutedEventArgs e)
{
if (MyMap == null ) return ;
GraphicsLayer layer = MyMap.Layers["MyFeatureLayer" ] as GraphicsLayer;
layer.Clusterer = new SumClusterer2() { AggregateColumn = "POP2000" , SymbolScale = 0.001 };
}
private void CheckBox_Unchecked(object sender, RoutedEventArgs e)
{
if (MyMap == null ) return ;
GraphicsLayer layer = MyMap.Layers["MyFeatureLayer" ] as GraphicsLayer;
layer.Clusterer = null ;
}
}
public class SumClusterer2 : GraphicsClusterer
{
public SumClusterer2()
{
MinimumColor = Colors.Red;
MaximumColor = Colors.Yellow;
SymbolScale = 1;
base .Radius = 50;
}
public string AggregateColumn { get ; set ; }
public double SymbolScale { get ; set ; }
public Color MinimumColor { get ; set ; }
public Color MaximumColor { get ; set ; }
protected override Graphic OnCreateGraphic(GraphicCollection cluster, MapPoint point, int maxClusterCount)
{
if (cluster.Count == 1) return cluster[0];
Graphic graphic = null ;
double sum = 0;
foreach (Graphic g in cluster)
{
if (g.Attributes.ContainsKey(AggregateColumn))
{
try
{
sum += Convert.ToDouble(g.Attributes[AggregateColumn]);
}
catch { }
}
}
double size = (sum + 450) / 30;
size = (Math.Log(sum * SymbolScale / 10) * 10 + 20);
if (size < 12) size = 12;
graphic = new Graphic() { Symbol = new ClusterSymbol2() { Size = size }, Geometry = point };
graphic.Attributes.Add("Count" , sum);
graphic.Attributes.Add("Size" , size);
graphic.Attributes.Add("Color" , InterpolateColor(size - 12, 100));
return graphic;
}
private static Brush InterpolateColor(double value, double max)
{
value = (int )Math.Round(value * 255.0 / max);
if (value > 255) value = 255;
else if (value < 0) value = 0;
return new SolidColorBrush(Color.FromArgb(127, 255, (byte )value, 0));
}
}
internal class ClusterSymbol2 : ESRI.ArcGIS.Client.Symbols.MarkerSymbol
{
public ClusterSymbol2()
{
string template = "<ControlTemplate " +
"xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\" xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"" + ">" +
@"<Grid IsHitTestVisible=""False"">
<Ellipse
Fill=""{Binding Attributes[Color]}""
Width=""{Binding Attributes[Size]}""
Height=""{Binding Attributes[Size]}"" />
<Grid HorizontalAlignment=""Center"" VerticalAlignment=""Center"">
<TextBlock
Text=""{Binding Attributes[Count]}""
FontSize=""9"" Margin=""1,1,0,0"" FontWeight=""Bold""
Foreground=""#99000000"" />
<TextBlock
Text=""{Binding Attributes[Count]}""
FontSize=""9"" Margin=""0,0,1,1"" FontWeight=""Bold""
Foreground=""White"" />
</Grid>
</Grid>
</ControlTemplate>" ;
System.IO.MemoryStream templateStream = new System.IO.MemoryStream(System.Text.UTF8Encoding.Default.GetBytes(template));
ControlTemplate = System.Windows.Markup.XamlReader.Load(templateStream) as ControlTemplate;
}
public double Size { get ; set ; }
public override double OffsetX
{
get
{
return Size / 2;
}
set
{
throw new NotSupportedException();
}
}
public override double OffsetY
{
get
{
return Size / 2;
}
set
{
throw new NotSupportedException();
}
}
}
}
Imports System.Windows
Imports System.Windows.Controls
Imports System.Windows.Media
Imports ESRI.ArcGIS.Client
Imports ESRI.ArcGIS.Client.Geometry
Namespace ArcGISWPFSDKVB
Partial Public Class LocalCustomClusterer
Inherits UserControl
Public Sub New ()
InitializeComponent()
End Sub
Private Sub CheckBox_Checked(sender As Object , e As RoutedEventArgs)
If MyMap Is Nothing Then
Return
End If
Dim layer As GraphicsLayer = TryCast(MyMap.Layers("MyFeatureLayer" ), GraphicsLayer)
layer.Clusterer = New SumClusterer2() With { _
.AggregateColumn = "POP2000" , _
.SymbolScale = 0.001 _
}
End Sub
Private Sub CheckBox_Unchecked(sender As Object , e As RoutedEventArgs)
If MyMap Is Nothing Then
Return
End If
Dim layer As GraphicsLayer = TryCast(MyMap.Layers("MyFeatureLayer" ), GraphicsLayer)
layer.Clusterer = Nothing
End Sub
End Class
Public Class SumClusterer2
Inherits GraphicsClusterer
Public Sub New ()
MinimumColor = Colors.Red
MaximumColor = Colors.Yellow
SymbolScale = 1
MyBase .Radius = 50
End Sub
Public Property AggregateColumn() As String
Get
Return m_AggregateColumn
End Get
Set (value As String )
m_AggregateColumn = value
End Set
End Property
Private m_AggregateColumn As String
Public Property SymbolScale() As Double
Get
Return m_SymbolScale
End Get
Set (value As Double )
m_SymbolScale = value
End Set
End Property
Private m_SymbolScale As Double
Public Property MinimumColor() As Color
Get
Return m_MinimumColor
End Get
Set (value As Color)
m_MinimumColor = value
End Set
End Property
Private m_MinimumColor As Color
Public Property MaximumColor() As Color
Get
Return m_MaximumColor
End Get
Set (value As Color)
m_MaximumColor = value
End Set
End Property
Private m_MaximumColor As Color
Protected Overrides Function OnCreateGraphic(cluster As GraphicCollection, point As MapPoint, maxClusterCount As Integer ) As Graphic
If cluster.Count = 1 Then
Return cluster(0)
End If
Dim graphic As Graphic = Nothing
Dim sum As Double = 0
For Each g As Graphic In cluster
If g.Attributes.ContainsKey(AggregateColumn) Then
Try
sum += Convert.ToDouble(g.Attributes(AggregateColumn))
Catch
End Try
End If
Next
Dim size As Double = (sum + 450) / 30
size = (Math.Log(sum * SymbolScale / 10) * 10 + 20)
If size < 12 Then
size = 12
End If
graphic = New Graphic() With { _
.Symbol = New ClusterSymbol2() With { _
.Size = size _
}, _
.Geometry = point _
}
graphic.Attributes.Add("Count" , sum )
graphic.Attributes.Add("Size" , size)
graphic.Attributes.Add("Color" , InterpolateColor(size - 12, 100))
Return graphic
End Function
Private Shared Function InterpolateColor(value As Double , max As Double ) As Brush
value = CInt (Math.Truncate(Math.Round(value * 255.0 / max )))
If value > 255 Then
value = 255
ElseIf value < 0 Then
value = 0
End If
Return New SolidColorBrush(Color.FromArgb(127, 255, CByte (Math.Truncate(value)), 0))
End Function
End Class
Friend Class ClusterSymbol2
Inherits ESRI.ArcGIS.Client.Symbols.MarkerSymbol
Public Sub New ()
Dim template As String = "<ControlTemplate " & "xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation"" xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml""" & ">" & "<Grid IsHitTestVisible=""False"">" & vbCr & vbLf & " <Ellipse" & vbCr & vbLf & " Fill=""{Binding Attributes[Color]}"" " & vbCr & vbLf & " Width=""{Binding Attributes[Size]}""" & vbCr & vbLf & " Height=""{Binding Attributes[Size]}"" />" & vbCr & vbLf & " <Grid HorizontalAlignment=""Center"" VerticalAlignment=""Center"">" & vbCr & vbLf & " <TextBlock " & vbCr & vbLf & " Text=""{Binding Attributes[Count]}"" " & vbCr & vbLf & " FontSize=""9"" Margin=""1,1,0,0"" FontWeight=""Bold""" & vbCr & vbLf & " Foreground=""#99000000"" />" & vbCr & vbLf & " <TextBlock" & vbCr & vbLf & " Text=""{Binding Attributes[Count]}"" " & vbCr & vbLf & " FontSize=""9"" Margin=""0,0,1,1"" FontWeight=""Bold""" & vbCr & vbLf & " Foreground=""White"" />" & vbCr & vbLf & " </Grid>" & vbCr & vbLf & " </Grid>" & vbCr & vbLf & " </ControlTemplate>"
Dim templateStream As New System.IO.MemoryStream(System.Text.UTF8Encoding.[Default ].GetBytes(template))
ControlTemplate = TryCast(System.Windows.Markup.XamlReader.Load(templateStream), ControlTemplate)
End Sub
Public Property Size() As Double
Get
Return m_Size
End Get
Set (value As Double )
m_Size = value
End Set
End Property
Private m_Size As Double
Public Overrides Property OffsetX() As Double
Get
Return Size / 2
End Get
Set (value As Double )
Throw New NotSupportedException()
End Set
End Property
Public Overrides Property OffsetY() As Double
Get
Return Size / 2
End Get
Set (value As Double )
Throw New NotSupportedException()
End Set
End Property
End Class
End Namespace
5/16/2014