ThinkGeo.com    |     Documentation    |     Premium Support

Utilizing cascading LineStyle's?

Hi,


I'm trying to build classes to handle complex, data-driven line styles.


The issue is that the final line style will depend on multiple factors, and so I want to wrap the logic up in multiple levels of complexity.


1st level assigns a custom linestyle that depends on a table field value. This class implements a large Select statement, that assigns a proper linestyle based on the field value.


2nd level assigns a custom line style based on a pre-defined set of known "line styles" using a "pattern number" (and width and color) as arguments. This class again implements a Select statement, assigning the relevant linestyle based on the pattern number.


3rd level assigns a complex line style, e.g. a line style with symbols spaced along the line shape.


I.e., Default.aspx => CustomLineStyleLevel1 => CustomLineStyleLevel2 => CustomLineStyleLevel3


The available samples all revolve around a single level of custom linestyle, but how must multi-level linestyle assignments as described be handled ?


The samples all have the custom linestyle utilize the DrawCore routine to issue Draw calls to the supplied canvas, but I assume that in a multi-level setup that oughtn't be the case. I presume that only the lowest level applied should issue the relevant draw calls.


Can you provide samples and insight on how to accomplish the above multi-level line style setup ?


 



Hi Lars I.:


In MapSuiteCore, we have lots of predefined styles to handle sub-styles according to values, for example: ValueStyle, RegexStyle, ClassBreakStyle. In my opinion, ValueStyle should meet your requirement.


Level 1: use ValueStyle to assign a proper LineStyle based on the field value


Level 2: use your own logic to assign relevant LineStyle


Level 3: implement a complex LineStyle


Please let us know if you have more questions.


Regards,


Ivan



Hi Ivan,


Thanks for the reply, but it doesn't really help me along.


To clarify my need, this is an example of what I want:


Level 1: In my layer setup, I assign a LineStyle dependent on two database (varchar) columns. Each distinct combination of values in these columns set a distinct symbology of my choosing (i.e. the large Select statement). These symbologies may be simple, or may be derived from a "symbology enumeration" that I implement (mimicking MapInfo pen styles in this case), hence level 2.


Level 2: This is the implementation of the "symbology enumeration" (MapInfo pen styles). Each pen is defined by a pattern number, a width and a color. Again this is implemented as a Select statement (per pattern #). Some of the pattern # refer to simple lines, but most refer to complex LineStyles, hence level 3.


Level 3: This implements some/multiple complex LineStyles (one class for each), e.g. lines with various symbols spaced along the line shape.


Please have a look at this thread (link below) to see examples of the line styles I'm trying to build. The built-in creator functions in Map Suite are not sufficient for this, as I see it.


gis.thinkgeo.com/Support/DiscussionForums/tabid/143/aff/12/aft/8935/afv/topic/Default.aspx


I'm not asking whether this is possible or feasable, it is.


My question was really just, how to implement such a multi-level symbology. Must I (in DrawCore) call the Draw function on each level, or should I only call Draw at the lowest level ?


And I would also like to know, whether there are any pitfalls in this approach I'm not aware of.


TIA


 



Lars,


 I responded to your Post "Building complex line styles?" gis.thinkgeo.com/Support/Discussion...fault.aspx and I hope I gave you some good direction on how to implements the different types of line styles based on your document. If you need more help on that, let us know. I actually will improve the example I sent you, make it presentable and I will publish a sample in the Code Community. Thank you for the idea.


 For this post, I read it from the beginning and I am still not very clear on what you mean  by "multi-level symbology". You describe Level 1, Level 2 and Level 3 and I think I understand every one of those concepts taken separately but taken together within the concept of Levels I get confuse. Can you give us a clear example of how that would work using some concrete example? You can also point us out to some web site where this technique is explained. Sorry for taking long to get the concept right. Thank you.


 



Hi Val,


Well, I have my answer by now, since it seems DrawCore must be called on all levels. Which makes sense.


As for the code, I've trimmed it down to highlight what I'm attempting to do. Here goes:



'--- Default.aspx.vb

myLayer.ZoomLevelSet.ZoomLevel01.CustomStyles.Clear()
myLayer.ZoomLevelSet.ZoomLevel01.CustomStyles.Add(New ColumnSymbologyLineStyle("element_code", "element_subcode"))
myLayer.ZoomLevelSet.ZoomLevel01.ApplyUntilZoomLevel = ApplyUntilZoomLevel.Level20

'--- Level 1 - database driven selection:

Public Class ColumnSymbologyLineStyle
    Inherits LineStyle

    Private m_symColumnName1 As String
    Private m_symColumnName2 As String

    Public Sub New(ByVal symColumnName1 As String, ByVal symColumnName2 As String)
        m_symColumnName1 = symColumnName1
        m_symColumnName2 = symColumnName2
    End Sub

    Protected Overrides Sub DrawCore(...)
        Dim useLineStyle As LineStyle

        For Each ft As Feature In features
            useLineStyle = Nothing
            Dim symColumnValue As String = ft.ColumnValues(m_symColumnName1).ToString().Trim()

            Select Case symColumnValue
                Case "1a"
                    useLineStyle = New MapInfoPenLineStyle(1, 2, 0)
                Case "1b"
                    useLineStyle = New MapInfoPenLineStyle(2, 4, 255)
                Case "1c"
                    useLineStyle = New MapInfoPenLineStyle(2, 81, 255)
                'etc.
            End Select

            If useLineStyle IsNot Nothing Then
                Dim featureCollection As Collection(Of Feature) = New Collection(Of Feature)()
                featureCollection.Add(ft)
                useLineStyle.Draw(featureCollection, canvas, labelsInThisLayer, labelsInAllLayers)
            End If
        Next
    End Sub
End Class

'--- Level 2 - reusable "enumeration":

Public Class MapInfoPenLineStyle
    Inherits LineStyle

    Private m_PenPattern As Integer
    Private m_PenColor As GeoColor
    Private m_PenWidth As Integer

    Public Sub New(ByVal penWidth As Integer, ByVal penPattern As Integer, ByVal penColor As Long)
        Dim r As Integer, g As Integer, b As Integer
        b = CInt(penColor Mod 256L)
        g = CInt((penColor \ 256L) Mod 256L)
        r = CInt(penColor \ 65536L) '65536 = 256*256

        m_PenPattern = penPattern
        m_PenColor = New GeoColor(r, g, b)
        m_PenWidth = penWidth
    End Sub

    Protected Overloads Overrides Sub DrawCore(...)
        Dim useLineStyle As LineStyle = Nothing
        Select Case m_PenPattern
            Case 1
                'invisible = leave undefined
            Case 2
                useLineStyle = New LineStyle(New GeoPen(m_PenColor, m_PenWidth))
            Case 3
                LineStyles.CreateSimpleLineStyle(m_PenColor, m_PenWidth, LineDashStyle.Dot, True)
            'etc. until 118
            'example of complex line style:
            Case 81
                useLineStyle = New MapInfoPen_81_LineStyle(m_PenColor, m_PenWidth)
        End Select

        For Each ft As Feature In features
            If useLineStyle IsNot Nothing Then
                Dim featureCollection As Collection(Of Feature) = New Collection(Of Feature)()
                featureCollection.Add(ft)
                useLineStyle.Draw(featureCollection, canvas, labelsInThisLayer, labelsInAllLayers)
            End If
        Next
    End Sub
End Class

'--- Level 3:

Public Class MapInfoPen_81_LineStyle
    Inherits LineStyle

    Private m_SymbolColor As GeoColor
    Private m_LineWidth As Integer = 1

    Public Sub New(ByVal rgbColor As Long, ByVal lineWidth As Integer)
        Dim r As Integer, g As Integer, b As Integer
        b = CInt(rgbColor Mod 256L)
        g = CInt((rgbColor \ 256L) Mod 256L)
        r = CInt(rgbColor \ 65536L) '65536 = 256*256

        m_SymbolColor = New GeoColor(r, g, b)
        m_LineWidth = lineWidth
    End Sub

    Protected Overloads Overrides Sub DrawCore(...)
        Const DistBetweenSymbolsPx As Integer = 16
        Dim worldIncrDist As Double = ExtentHelper.GetWorldDistanceBetweenTwoScreenPoints(canvas.CurrentWorldExtent, 0, 0, DistBetweenSymbolsPx, 0, canvas.Width, canvas.Height, GeographyUnit.Meter, DistanceUnit.Meter)
        Dim screenRadius As Double = 3
        Dim useLineStyle = LineStyles.CreateSimpleLineStyle(GeoColor.StandardColors.Black, m_LineWidth, False)

        For Each ft In features
            Dim ftCollection As Collection(Of Feature) = New Collection(Of Feature)()
            ftCollection.Add(ft)
            useLineStyle.Draw(ftCollection, canvas, labelsInThisLayer, labelsInAllLayers)

            Dim curShape As LineShape = DirectCast(ft.GetShape, LineShape)
            Dim worldSumDist As Double = worldIncrDist / 2.0
            Dim worldMaxDist As Double = curShape.GetLength(GeographyUnit.Meter, DistanceUnit.Meter)

            While worldSumDist < worldMaxDist 'curShape.GetLength(GeographyUnit.Meter, DistanceUnit.Meter)
                Dim insPt = curShape.GetPointOnALine(StartingPoint.FirstPoint, worldSumDist, GeographyUnit.Meter, DistanceUnit.Meter)
                canvas.DrawEllipse(insPt, screenRadius * 2, screenRadius * 2, New GeoSolidBrush(m_SymbolColor), DrawingLevel.LevelFour)
                worldSumDist += worldIncrDist
            End While
        Next 'ft
    End Sub
End Class


Lars, 
  
  I am glad you figure it out on your own. If you still have some doubts, do not hesitate to let us know. If you don’t mind, could you attach some screenshots so that we can see how the symbols look like, if this is not confidential, of course. Thank you.