Creating chart thematic

Dec 29, 2010 at 3:37 PM

Any clues as to how start creating a chart thematic layer ?

Developer
Dec 29, 2010 at 8:17 PM

Meaning that you want to show pie charts or something as symbols for something?  Two steps.  The first step is to create a point shapefile with points at the center of each of the pie charts, and with attributes that are required.  The second step is to write a bit of code that actually creates a separate bitmap for each pie chart based on the attributes, creating a new PictureSymbol for each specific case you need to work with.  If these are really complex, then you might just assume that each symbol is unique, and make one bitmap per shape.  Finally you can create separate catagories based on the [FID] or some other characteristic to tie it to the bitmap you drew.  In future we might look at tweaking the ISymbol drawing code to support an overload that can receive the FID or something.  Currently we don't do that though, so you more or less have to stage your charts in advance.

Ted

 

Dec 30, 2010 at 1:59 PM

Hi thanks for your sujestions.

The code below tries to do what you sujested.

The only thing i don't really understand is  how to link each feature with the appropriate chart/symbol or category. Can you please again point me to the right direction?

Oh and Merry Crisis .... from Greece!

 

   Dim SourceFeats As FeatureSet = CType(SourceLayer.DataSet, FeatureSet)

        'create the new featureset  and add some columns in the table
        Dim PointFeatureset As New FeatureSet(FeatureType.Point)
        PointFeatureset.DataTable.Columns.Add(New DataColumn("FID", GetType(Integer)))
        PointFeatureset.DataTable.Columns.Add(New DataColumn("NAME", GetType(String)))
        PointFeatureset.DataTable.Columns.Add(New DataColumn("Value1", GetType(Double)))
        PointFeatureset.DataTable.Columns.Add(New DataColumn("Value2", GetType(Double)))

        'get center coordinate and get values
        Dim max As Double = Double.MinValue
        Dim min As Double = Double.MaxValue
        For Each feat As Feature In SourceFeats.Features
            Dim id As Integer = feat.DataRow.Table.Rows.IndexOf(feat.DataRow)
            Dim name As String = CStr(feat.DataRow.ItemArray(0))
            Dim center As Coordinate = feat.BasicGeometry.Envelope.Center

            Dim pointFeat As New Feature(center)

            Dim addedFEAT As IFeature = PointFeatureset.AddFeature(pointFeat)
            addedFEAT.DataRow.Item(0) = id
            addedFEAT.DataRow.Item(1) = name
            Dim value1 As Double = CDbl(feat.DataRow.Item("POP91"))
            Dim value2 As Double = CDbl(feat.DataRow.Item("POP81"))
            addedFEAT.DataRow.Item(2) = value1
            addedFEAT.DataRow.Item(3) = value2

            If min > value1 Then min = value1
            If max < value1 Then max = value1
        Next
        '////////////////////////////////////////////////////////
        ' create the new point layer
        _chartLayer = New MapPointLayer(PointFeatureset)
        Dim s As PointScheme = New PointScheme()
        s = CType(_chartLayer.Symbology, PointScheme)
        s.Categories.Clear()

        For Each feat As Feature In _chartLayer.DataSet.Features
            Dim NormalSymbolizer As New PointSymbolizer()
            NormalSymbolizer.Symbols.Clear()
            Dim PictureSymbol As New PictureSymbol()
            Dim myPane As New GraphPane
            myPane.Title.Text = ""
            myPane.XAxis.Title.Text = ""
            myPane.YAxis.Title.Text = ""
            Dim listValues1 As New PointPairList
            Dim listValues2 As New PointPairList
            listValues1.Add(0, CDbl(feat.DataRow.Item(2)))
            listValues2.Add(0, CDbl(feat.DataRow.Item(3)))
            Dim myCurve1 As BarItem = myPane.AddBar("POP81", listValues1, Color.Blue)
            Dim myCurve2 As BarItem = myPane.AddBar("POP91", listValues2, Color.Red)
            myPane.Chart.Fill = New Fill(Color.Transparent, Color.Transparent, 45.0F)
            myPane.YAxis.Scale.Max = max + 10
            myPane.YAxis.Scale.Min = 0
            PictureSymbol.Image = myPane.GetImage
            NormalSymbolizer.Symbols.Add(PictureSymbol)
            Dim pc As New PointCategory(NormalSymbolizer)
            s.Categories.Add(pc)
        Next

        _chartLayer.Symbology = s

        _chartLayer.SelectionEnabled = False
        _chartLayer.IsSelected = False

        theMap.Layers.Add(_chartLayer)
Developer
Dec 30, 2010 at 3:04 PM

So it looks like you have create a new category for each feature.  One way to define which category goes with which feature is to set the FilterExpression on the category.  So each category can not only have a certain appearance, it can be associated with attributes.  If you don't have any attributes to speak of you can also use the FID, as if it were a field like [FID] = 0 for instance.  For large layers this may take a bit of time to parse all the filter expressions.  An alternative is to hard code it using the "DrawnStates" array.  A Drawn state basically defines for each feature index (determined by the array offset) whether the feature is selected and what category to draw.  In most cases, this is an object reference to basically just one or two categories over and over again.  in your case, however, it looks like you are creating the features from scratch rather than loading them.  You can still control which feature goes with which item, but it is a little more complex since it involves setting up a dictionary that links a feature with the drawn state instead.  If I remember correctly there is something called a DrawingFilter on each layer that stores this dictionary.  For points, this might not even be used though, I can't remember.  Anyway, for your first crack at it, I'd add a line that specifies the FilterExpression on each of the Categories as you create them.

Ted

 

Dec 30, 2010 at 9:31 PM

implementing what you sujested any idea .. why this doesn't seem to work ?

 

   Dim SourceLayer As PolygonLayer = CType(theMap.Layers(0), PolygonLayer)
        Dim SourceFeats As FeatureSet = CType(SourceLayer.DataSet, FeatureSet)

        'create the new featureset  and add some columns in the table
        Dim PointFeatureset As New FeatureSet(FeatureType.Point)
        PointFeatureset.DataTable.Columns.Add(New DataColumn("FID", GetType(Integer)))
        PointFeatureset.DataTable.Columns.Add(New DataColumn("NAME", GetType(String)))
        PointFeatureset.DataTable.Columns.Add(New DataColumn("Value1", GetType(Double)))
        PointFeatureset.DataTable.Columns.Add(New DataColumn("Value2", GetType(Double)))

        'get center coordinate and get values
        Dim max As Double = Double.MinValue
        Dim min As Double = Double.MaxValue
        For Each feat As Feature In SourceFeats.Features
            Dim FID As Integer = feat.DataRow.Table.Rows.IndexOf(feat.DataRow)
            'Dim FID1 As Integer = feat.DataRow.Table.Rows.IndexOf(feat.DataRow)
            Dim name As String = CStr(feat.DataRow.ItemArray(0))
            Dim center As Coordinate = feat.BasicGeometry.Envelope.Center

            Dim pointFeat As New Feature(center)

            Dim addedFEAT As IFeature = PointFeatureset.AddFeature(pointFeat)
            addedFEAT.DataRow.Item(0) = FID
            addedFEAT.DataRow.Item(1) = name
            Dim value1 As Double = CDbl(feat.DataRow.Item("POP81"))
            Dim value2 As Double = CDbl(feat.DataRow.Item("POP91"))
            addedFEAT.DataRow.Item(2) = value1
            addedFEAT.DataRow.Item(3) = value2

            If min > value1 Then min = value1
            If max < value1 Then max = value1

            If min > value2 Then min = value2
            If max < value2 Then max = value2
        Next
        '////////////////////////////////////////////////////////
        ' create the new point layer
        _chartLayer = New MapPointLayer(PointFeatureset)
        'Dim ps As PointScheme = CType(_chartLayer.Symbology, PointScheme)
        Dim ps As New PointScheme
        Dim commonSymboliser As New PointSymbolizer()
        commonSymboliser.IsVisible = True
        commonSymboliser.Smoothing = True
        ps.AppearsInLegend = True

        ps.ClearCategories()
        ps.Categories.Clear()

        For Each feat As Feature In _chartLayer.DataSet.Features
            Dim FID As Integer = feat.DataRow.Table.Rows.IndexOf(feat.DataRow)
            Dim name As String = CStr(feat.DataRow.Item("NAME"))

            Dim myPane As New GraphPane
            myPane.Title.Text = ""
            myPane.XAxis.Title.Text = ""
            myPane.YAxis.Title.Text = ""
            Dim years() As String = New String() {"1981", "1991"}
            Dim listValues1(0) As Double
            Dim listValues2(0) As Double
            listValues1(0) = CDbl(feat.DataRow.Item("Value1"))
            listValues2(0) = CDbl(feat.DataRow.Item("Value2"))
            Dim myCurve1 As BarItem = myPane.AddBar("POP81", Nothing, listValues1, Color.Blue)
            myCurve1.Bar.Fill = New Fill(Color.Blue)
            Dim myCurve2 As BarItem = myPane.AddBar("POP91", Nothing, listValues2, Color.Red)
            myCurve2.Bar.Fill = New Fill(Color.Red)
            myPane.Chart.Fill = New Fill(Color.Transparent)
            myPane.YAxis.Color = Color.Transparent
            myPane.XAxis.Color = Color.Transparent
            myPane.YAxis.IsVisible = False
            myPane.XAxis.IsVisible = False
            myPane.XAxis.Type = AxisType.Text
            myPane.XAxis.Scale.TextLabels = years
            myPane.Fill = New Fill(Color.Transparent)
            myPane.YAxis.Scale.Max = max
            myPane.YAxis.Scale.Min = min
            myPane.Border.IsVisible = False
            myPane.AxisChange()
            myPane.Legend.IsVisible = False

            Dim PictureSymbol As New PictureSymbol()
            Dim fn As String = "c:\TEMP\" & feat.DataRow.Item("NAME").ToString & ".png"
            myPane.GetImage.Save(fn, Imaging.ImageFormat.Png)
            'PictureSymbol.ImageFilename = fn
            PictureSymbol.Image = myPane.GetImage()
            PictureSymbol.Scale(3)
            PictureSymbol.Opacity = 0
            PictureSymbol.UseOutline = False

            Dim NONcommonSymboliser As New PointSymbolizer
            NONcommonSymboliser.Smoothing = True
            NONcommonSymboliser.IsVisible = True
            NONcommonSymboliser.Symbols.Clear()
            NONcommonSymboliser.Symbols.Add(PictureSymbol)
            Dim pc1 As New PointCategory(NONcommonSymboliser)
            pc1.FilterExpression = "[FID] = '" & FID & "'"
            pc1.LegendText = name
            pc1.DisplayExpression()
            ps.Categories.Add(pc1)
        Next
        _chartLayer.Symbology = ps

        _chartLayer.SelectionEnabled = True
        _chartLayer.IsSelected = True
        '_chartLayer.Projection = MapHelper.CommonCoordSys
        theMap.Layers.Add(_chartLayer)
Developer
Dec 31, 2010 at 1:11 PM

For numbers you don't use a single quote.  So its [FID] = 1 but [StateName] = 'Texas'

Ted

 

Jan 3, 2011 at 12:17 PM

Thanks again Ted,

Another  issue was the use of  PictureSymbol.Opacity = 0

 


Happy new Year!

Feb 3, 2011 at 11:39 PM

Thanks a lot agelospanagiotaki and Ted,

Now I can draw pie charts on my map !

However I have an issue with the zoom. The size of my pies already depend on the scale of the map, so this part is done, however I don't know how to catch the fact that the zoom has changed on the map.

I am using the predefined zooming tool included into the spatialtoolstrip.

I thought about different possibilities : 

  1. Use regular map event. Pb with the mouse wheel. The click on the map using zoomIn and zoomOut button is working fine (I am catching the mouse up event on the map and check if one of these buttons is selected, then I redraw the layer with bigger/smaller bitmaps). However this method is not working for the mousewheel event. If I catch the mousewheel event to redraw the layer, I actually delete it and recreate it before the zoom even happens. I would need a mousewheeled event :)
  2. Use the map event ViewExtentsChanged. Pb with performance while panning. This solution works for using both mousewheel and zoom buttons, but the process of creating the bitmap is time consuming, so I need to be able to pan (not zoom) through the layer without having to re-build it everytime. And that's what is happening with this event. I could put a test to see if the scale is different, then delete-recreate the layer.
  3. Use a post zoom changed event. Unfortunately I could not find such event. Do you know if it exists and where ?

Thanks for your help

Bruno