DotSpatial in a beginning GIS programming class

Coordinator
Nov 17, 2010 at 12:53 AM

Hi all. I'm planning to use DotSpatial in my beginning GIS programming class next semester (starting in Jan). I will be using VB.NET in the class. Before I get started building exercises, do any of you current users have any suggestions on things to focus on in the cflass? Or soft areas of the project I should avoid?- Dan

Developer
Nov 17, 2010 at 2:51 AM

The DotSpatial.Positioning libraries are newly plugged in and I think still have some errors that would come up if you tried connecting with actual positioning gear.  I would recommend avoiding that whole area until it has had time for someone with a device to test it or else until we can get a simulator online and working.  The easier classes for beginners to work with are the FeatureSet, Raster, and ImageData classes for data access.  Be advised that the FeatureSet class has two modes of operation.  Index mode, which takes up less memory, and edit mode, where the "Features" list gives access to individual Features.  The Features list lets you add, remove, insert and re-arrange features easily, and the FeatureSet generates these features in a lazy fashion the first time you access the Features property on the FeatureSet.  If you don't want to create the features list, you can safely use the GetFeature(index) method, which returns the same feature, but only for one shape and it does not track anything internally.

DotSpatial.Projections is important for GIS, and should probably feature a role.  Currently, the x-y vertices are stored in an array called Vertex.  For reprojecting, you simply need to pass that Vertex array into the Reproject method if you want to use the library directly.  The FeatureSet class has a "Reproject(myProjectionInfo)" method on it to simplify things though, so you don't really need to know how the vertices are passed through reprojection to be able to implement it.

Topology is another important aspect of GIS.  Being able to invoke a Buffer, test for intersections, or calculate the vector overlay from two separate geometries.  There are many ways you can demonstrate this in DotSpatial.  Either down at the DotSpatial.Topology level, working with geometries like the Point, LineString, Polygon, MultiPoint, MultiLineString and MultiPolygon.  However, there are extension methods directly on the FeatureSet and Feature classes.  So if you have two features, you should be able to perform the basic intersect and overlay testing without having to worry about geometry interfaces, casting or technical details involving geometry factories and other complications.  In the words of the Framework Design Guidelines, simple things should be easy, and complex things should be possible.  IF you care about the geometry factory etc, you can control that, but if you are working in the context of our framework, there is only one geometry factory anyway.

The real action, however, is being able to interact with the map.  The most important thing for them to be able to do can be done in two separate ways.  The first is to add an event handler to the various Map mouse events and do something with it.  The Map supports PixelToProj and ProjToPixel methods, but the geographic coordinates should be provided in the event arguments that are handed to you in your event handler.  You can also handle the Map.Paint event.  Technically you can doodle on the map using the old standby of Graphics g = Map.CreateGraphics(), and then use the g in order to draw stuff.  However this will be flickery since you are drawing independently from the map, and people will see a fraction of a second without your doodle.  Instead, if you use the graphics object handed to you in the event argument of the Map.Paint event, you can easily draw any content that you want on top of the map, and have it look smooth because you are drawing to the same backbuffer that the underlying map is using to draw.

Symbology is important, and should be covered more because our model is not as simple as MapWindow was.  With mapWindow, you only supported a few rendering options, and all options that you could do were immediately listed, even if a large portion of those became confusing because they might not apply to the case you were working with.  This model is more sensitive to casting.  If you walk them through casting an ILayer into an IMapPolygonLayer, for instance, then you are gold.  From that point on, all the polygon symbology etc is fully fleshed out with all the options you can do, your DataSet is an IFeatureSet, so you can do myMapPolygonLayer.DataSet.Features[2].Coordinates[1].X and it will know what you want.  If you don't cast it, it's like MapWinGIS handing you an "object" for whatever the object being rendered is.  That object could be a MapWinGIS.Shapefile, a MapWinGIS.Image, or a MapWinGIS.Grid.  Until you cast it appropriately, you can't do much of anything.  ILayer.DataSet has the exact same ambiguity.  The sample code below gives some basics that should at least get you started.  The most confusing thing for me was that if you don't add enough references to your project, things aren't available.  You might not realize that you need a reference to DotSpatial.Symbology to access the map because there are public members on the map that are classes defined in DotSpatial.Symbology.  This is a recently discovered pain, since we were previously a monolithic dll.  Anyway, pay attention to the "Add Reference" comments at the top and your sample code should work fine.  I added references to the x86 libraries, since I think on my computer the projects default to x86.  On yours you might want to look at your configuration manager and make sure your configuration will match the library you add a reference to.

Imports DotSpatial.Controls
Imports DotSpatial.Data
Imports DotSpatial.Symbology

' Reference DotSpatial.Controls
' Reference DotSpatial.Symbology
' Reference DotSpatial.Data
' Reference DotSpatial.Projections
' Reference DotSpatial.Topology
Public Class Form1

    ' A Reference To DotSpatial.Symbology is necessary or else the Map is not Happy.
    ' Map1 will show up like a class and not like a variable until you add
    ' references to DotSpatial.Controls and DotSpatial.Symbology
    Public Sub AddLayer()
        ' Use a dialog to allow the user to add any old content as a new layer.
        Map1.AddLayer()
    End Sub

    Public Sub AddFeatureSet()
        ' FeatureSet needs a reference to DotSpatial.Data
        Dim fs As New FeatureSet
        ' Open a featureset for a specific shapefile
        fs.Open("filename")
        ' Add the content to the map
        Map1.Layers.Add(fs)
    End Sub

    Public Sub AddRaster()
        Dim r As New Raster
        r.Open("filename")
        Map1.Layers.Add(r)
    End Sub

    Public Sub AddImage()
        Dim img As New ImageData
        img.Open("filename")
        Map1.Layers.Add(img)
    End Sub

    Public Sub GetProjection()
        Dim fs As New FeatureSet
        fs.Open("filename")
        Dim s As String
        s = fs.Projection.ToEsriString()
        Dim eccentricity As Double
        ' This gives a good picture of the nested objects in a projection
        eccentricity = fs.Projection.GeographicInfo.Datum.Spheroid.Eccentricity()
    End Sub

    Public Sub Buffer()
        Dim fs As New FeatureSet
        fs.Open("filename")
        Dim result As FeatureSet

        ' First value is distance, second value is to copy attributes to the new featureset
        result = fs.Buffer(1.231, True)

        ' True is to over write.  .shp extension is CRITICAL.  Without it you will get an exception.
        ' This might be a good time to introduce them to Try Catch.
        Try
            result.SaveAs("BufferFilename.shp", True)
        Catch ex As Exception
            MessageBox.Show("An invalid format was specified.")
        End Try

    End Sub

    Public Sub IntersectTest()
        Dim fsA As New FeatureSet
        fsA.Open("filenameA")
        Dim fsB As New FeatureSet
        fsB.Open("filenameB")
        ' Feature isn't currently happy without a reference to DotSpatial.Topology,
        ' even if you aren't instantiating Topology classes directly.  A Feature
        ' has a "BasicGeometry" property and that is what needs the reference.
        For Each fA As Feature In fsA.Features
            For Each fB As Feature In fsB.Features
                If (fA.Intersects(fB)) Then
                    MessageBox.Show("An intersection was found.")
                    Exit Sub
                End If
            Next
        Next
    End Sub

    Public Sub SumRaster()
        Dim r As New Raster
        r.Open("filename")
        Dim sum As Double
        For Row As Integer = 0 To r.NumRows
            For col As Integer = 0 To r.NumColumns
                Dim value As Double
                value = r.Value(Row, col)
                sum = sum + value;
            Next
        Next
    End Sub

    Public Sub TryCastPolygonLayer()
        For Each lyr As IMapLayer In Map1.Layers
            Dim pgl As IMapPolygonLayer

            ' Try cast will return null if it is not a polygon layer.
            pgl = TryCast(lyr, IMapPolygonLayer)
            If (Not pgl Is Nothing) Then
                pgl.Symbolizer = New PolygonSymbolizer(Color.Red)
                ' or alternately
                pgl.Symbolizer.SetFillColor(Color.Red)
                ' The above is fancy because it will do its best to color 
                ' something red, hiding the fact that different kinds of 
                ' Pattern classes are actually involved.
            End If
            Dim ptl As IMapPointLayer
            ptl = TryCast(lyr, IMapPointLayer)
            If (Not ptl Is Nothing) Then
                ' Control a few things that are universal directly
                ptl.Symbolizer.SetSize(New Size2D(10, 10))
                ptl.Symbolizer.SetFillColor(Color.Yellow)

                ' more specific things can use more specific classes
                ptl.Symbolizer = New PointSymbolizer(Color.Blue, DotSpatial.Symbology.PointShape.Star, 10)
                ' alternately
                Dim s As New SimpleSymbol(Color.Blue, DotSpatial.Symbology.PointShape.Star, 10)
                s.OutlineColor = Color.Black
                ptl.Symbolizer.Symbols(0) = s
                ' Or stack with more complicated symbols.  The c denotes a "char" data type, instead of string.
                ptl.Symbolizer.Symbols.Add(New CharacterSymbol("T"c))

                ' Create a custom bitmap
                Dim bmp As New Bitmap(16, 16)

                ' A Using statement helps remind you to dispose your graphics object
                Using g As Graphics = Graphics.FromImage(bmp)
                    g.FillEllipse(Brushes.Blue, New Rectangle(0, 0, 16, 16))
                End Using

                ptl.Symbolizer.Symbols(0) = New PictureSymbol(bmp)

                ' Handle different categories using filter expressions
                Dim pc As New PointCategory
                pc.FilterExpression = "[StateName] = 'Texas'"
                pc.Symbolizer = New PointSymbolizer(Color.Green, DotSpatial.Symbology.PointShape.Hexagon, 16)
                ptl.Symbology.AddCategory(pc)



            End If
        Next

    End Sub

End Class

 

Coordinator
Nov 19, 2010 at 3:09 PM
Wow, great feedback Ted! Thanks. And thanks for the sample code in my native tongue of VB.NET! - Dan

On Tue, Nov 16, 2010 at 10:51 PM, shade1974 <notifications@codeplex.com> wrote:

From: shade1974

The DotSpatial.Positioning libraries are newly plugged in and I think still have some errors that would come up if you tried connecting with actual positioning gear. I would recommend avoiding that whole area until it has had time for someone with a device to test it or else until we can get a simulator online and working. The easier classes for beginners to work with are the FeatureSet, Raster, and ImageData classes for data access. Be advised that the FeatureSet class has two modes of operation. Index mode, which takes up less memory, and edit mode, where the "Features" list gives access to individual Features. The Features list lets you add, remove, insert and re-arrange features easily, and the FeatureSet generates these features in a lazy fashion the first time you access the Features property on the FeatureSet. If you don't want to create the features list, you can safely use the GetFeature(index) method, which returns the same feature, but only for one shape and it does not track anything internally.

DotSpatial.Projections is important for GIS, and should probably feature a role. Currently, the x-y vertices are stored in an array called Vertex. For reprojecting, you simply need to pass that Vertex array into the Reproject method if you want to use the library directly. The FeatureSet class has a "Reproject(myProjectionInfo)" method on it to simplify things though, so you don't really need to know how the vertices are passed through reprojection to be able to implement it.

Topology is another important aspect of GIS. Being able to invoke a Buffer, test for intersections, or calculate the vector overlay from two separate geometries. There are many ways you can demonstrate this in DotSpatial. Either down at the DotSpatial.Topology level, working with geometries like the Point, LineString, Polygon, MultiPoint, MultiLineString and MultiPolygon. However, there are extension methods directly on the FeatureSet and Feature classes. So if you have two features, you should be able to perform the basic intersect and overlay testing without having to worry about geometry interfaces, casting or technical details involving geometry factories and other complications. In the words of the Framework Design Guidelines, simple things should be easy, and complex things should be possible. IF you care about the geometry factory etc, you can control that, but if you are working in the context of our framework, there is only one geometry factory anyway.

The real action, however, is being able to interact with the map. The most important thing for them to be able to do can be done in two separate ways. The first is to add an event handler to the various Map mouse events and do something with it. The Map supports PixelToProj and ProjToPixel methods, but the geographic coordinates should be provided in the event arguments that are handed to you in your event handler. You can also handle the Map.Paint event. Technically you can doodle on the map using the old standby of Graphics g = Map.CreateGraphics(), and then use the g in order to draw stuff. However this will be flickery since you are drawing independently from the map, and people will see a fraction of a second without your doodle. Instead, if you use the graphics object handed to you in the event argument of the Map.Paint event, you can easily draw any content that you want on top of the map, and have it look smooth because you are drawing to the same backbuffer that the underlying map is using to draw.

Symbology is important, and should be covered more because our model is not as simple as MapWindow was. With mapWindow, you only supported a few rendering options, and all options that you could do were immediately listed, even if a large portion of those became confusing because they might not apply to the case you were working with. This model is more sensitive to casting. If you walk them through casting an ILayer into an IMapPolygonLayer, for instance, then you are gold. From that point on, all the polygon symbology etc is fully fleshed out with all the options you can do, your DataSet is an IFeatureSet, so you can do myMapPolygonLayer.DataSet.Features[2].Coordinates[1].X and it will know what you want. If you don't cast it, it's like MapWinGIS handing you an "object" for whatever the object being rendered is. That object could be a MapWinGIS.Shapefile, a MapWinGIS.Image, or a MapWinGIS.Grid. Until you cast it appropriately, you can't do much of anything. ILayer.DataSet has the exact same ambiguity. The sample code below gives some basics that should at least get you started. The most confusing thing for me was that if you don't add enough references to your project, things aren't available. You might not realize that you need a reference to DotSpatial.Symbology to access the map because there are public members on the map that are classes defined in DotSpatial.Symbology. This is a recently discovered pain, since we were previously a monolithic dll. Anyway, pay attention to the "Add Reference" comments at the top and your sample code should work fine. I added references to the x86 libraries, since I think on my computer the projects default to x86. On yours you might want to look at your configuration manager and make sure your configuration will match the library you add a reference to.

Imports DotSpatial.Controls
Imports DotSpatial.Data
Imports DotSpatial.Symbology

' Reference DotSpatial.Controls
' Reference DotSpatial.Symbology
' Reference DotSpatial.Data
' Reference DotSpatial.Projections
' Reference DotSpatial.Topology
Public Class Form1

    ' A Reference To DotSpatial.Symbology is necessary or else the Map is not Happy.
    ' Map1 will show up like a class and not like a variable until you add
    ' references to DotSpatial.Controls and DotSpatial.Symbology
    Public Sub AddLayer()
        ' Use a dialog to allow the user to add any old content as a new layer.
        Map1.AddLayer()
    End Sub

    Public Sub AddFeatureSet()
        ' FeatureSet needs a reference to DotSpatial.Data
        Dim fs As New FeatureSet
        ' Open a featureset for a specific shapefile
        fs.Open("filename")
        ' Add the content to the map
        Map1.Layers.Add(fs)
    End Sub

    Public Sub AddRaster()
        Dim r As New Raster
        r.Open("filename")
        Map1.Layers.Add(r)
    End Sub

    Public Sub AddImage()
        Dim img As New ImageData
        img.Open("filename")
        Map1.Layers.Add(img)
    End Sub

    Public Sub GetProjection()
        Dim fs As New FeatureSet
        fs.Open("filename")
        Dim s As String
        s = fs.Projection.ToEsriString()
        Dim eccentricity As Double
        ' This gives a good picture of the nested objects in a projection
        eccentricity = fs.Projection.GeographicInfo.Datum.Spheroid.Eccentricity()
    End Sub

    Public Sub Buffer()
        Dim fs As New FeatureSet
        fs.Open("filename")
        Dim result As FeatureSet

        ' First value is distance, second value is to copy attributes to the new featureset
        result = fs.Buffer(1.231, True)

        ' True is to over write.  .shp extension is CRITICAL.  Without it you will get an exception.
        ' This might be a good time to introduce them to Try Catch.
        Try
            result.SaveAs("BufferFilename.shp", True)
        Catch ex As Exception
            MessageBox.Show("An invalid format was specified.")
        End Try

    End Sub

    Public Sub IntersectTest()
        Dim fsA As New FeatureSet
        fsA.Open("filenameA")
        Dim fsB As New FeatureSet
        fsB.Open("filenameB")
        ' Feature isn't currently happy without a reference to DotSpatial.Topology,
        ' even if you aren't instantiating Topology classes directly.  A Feature
        ' has a "BasicGeometry" property and that is what needs the reference.
        For Each fA As Feature In fsA.Features
            For Each fB As Feature In fsB.Features
                If (fA.Intersects(fB)) Then
                    MessageBox.Show("An intersection was found.")
                    Exit Sub
                End If
            Next
        Next
    End Sub

    Public Sub SumRaster()
        Dim r As New Raster
        r.Open("filename")
        Dim sum As Double
        For Row As Integer = 0 To r.NumRows
            For col As Integer = 0 To r.NumColumns
                Dim value As Double
                value = r.Value(Row, col)
                sum = sum + value;
            Next
        Next
    End Sub

    Public Sub TryCastPolygonLayer()
        For Each lyr As IMapLayer In Map1.Layers
            Dim pgl As IMapPolygonLayer

            ' Try cast will return null if it is not a polygon layer.
            pgl = TryCast(lyr, IMapPolygonLayer)
            If (Not pgl Is Nothing) Then
                pgl.Symbolizer = New PolygonSymbolizer(Color.Red)
                ' or alternately
                pgl.Symbolizer.SetFillColor(Color.Red)
                ' The above is fancy because it will do its best to color 
                ' something red, hiding the fact that different kinds of 
                ' Pattern classes are actually involved.
            End If
            Dim ptl As IMapPointLayer
            ptl = TryCast(lyr, IMapPointLayer)
            If (Not ptl Is Nothing) Then
                ' Control a few things that are universal directly
                ptl.Symbolizer.SetSize(New Size2D(10, 10))
                ptl.Symbolizer.SetFillColor(Color.Yellow)

                ' more specific things can use more specific classes
                ptl.Symbolizer = New PointSymbolizer(Color.Blue, DotSpatial.Symbology.PointShape.Star, 10)
                ' alternately
                Dim s As New SimpleSymbol(Color.Blue, DotSpatial.Symbology.PointShape.Star, 10)
                s.OutlineColor = Color.Black
                ptl.Symbolizer.Symbols(0) = s
                ' Or stack with more complicated symbols.  The c denotes a "char" data type, instead of string.
                ptl.Symbolizer.Symbols.Add(New CharacterSymbol("T"c))

                ' Create a custom bitmap
                Dim bmp As New Bitmap(16, 16)

                ' A Using statement helps remind you to dispose your graphics object
                Using g As Graphics = Graphics.FromImage(bmp)
                    g.FillEllipse(Brushes.Blue, New Rectangle(0, 0, 16, 16))
                End Using

                ptl.Symbolizer.Symbols(0) = New PictureSymbol(bmp)

                ' Handle different categories using filter expressions
                Dim pc As New PointCategory
                pc.FilterExpression = "[StateName] = 'Texas'"
                pc.Symbolizer = New PointSymbolizer(Color.Green, DotSpatial.Symbology.PointShape.Hexagon, 16)
                ptl.Symbology.AddCategory(pc)



            End If
        Next

    End Sub

End Class

Read the full discussion online.

To add a post to this discussion, reply to this email (DotSpatial@discussions.codeplex.com@discussions.codeplex.com)

To start a new discussion for this project, email DotSpatial@discussions.codeplex.com@discussions.codeplex.com

You are receiving this email because you subscribed to this discussion on CodePlex. You can unsubscribe or change your settings on codePlex.com.

Please note: Images and attachments will be removed from emails. Any posts to this discussion will also be available online at codeplex.com




--
Daniel P. Ames, Ph.D. PE
Associate Professor, Geosciences
Idaho State University - Idaho Falls
amesdani@isu.edu
geology.isu.edu
www.mapwindow.org