This project is read-only.

Share Code

Nov 21, 2013 at 3:47 PM
I think there needs to be an additional tab in Dotspatial website, just for sharing code. That way users, who are/are not developers, can share tools, code, etc. with other users who may find the code useful. It can also act as a tutorial to help some people create other tools and ideas. For example, the code below allows users to draw a rectangle that will clip an image (say the basemap) from the screen, add a projection and worldfile, then add it to the legend. Enjoy!

Imports DotSpatial.Controls
Imports DotSpatial.Controls.Header
Imports DotSpatial.Data
Imports DotSpatial.Topology
Imports DotSpatial.Symbology
Imports System.IO

Namespace Tools
Public Class ClipScreenFromRect
    Inherits Extension
    Private _ClipButtonClicked As Boolean = False
    Private selectBox As Rectangle
    Private selectBoxPt As System.Drawing.Point
    Private mouseDownClicked As Boolean = False
    Private _myMap As Map

    Public Overrides Sub Activate()

        Try
            'To add the "Clip" button to the main toolbar
            Dim mainButton As New SimpleActionItem(HeaderControl.HomeRootItemKey, "Clip Screen", AddressOf MainButton_Click) With { _
             .GroupCaption = "Online Basemap", _
             .LargeImage = My.Resources.clip32x32, _
             .SmallImage = My.Resources.clip16x16, _
             .ToolTipText = "Clip Tool - click on the map to clip the selected area"
           }

            App.HeaderControl.Add(mainButton)

        Catch
            MessageBox.Show("There was an error adding the Screen Clip button.")
            Return
        End Try

        Dim mm As Map = DirectCast(App.Map, Map)
        _myMap = mm

        AddHandler mm.MouseDown, AddressOf Map_MouseDown
        AddHandler mm.MouseUp, AddressOf Map_Mouseup
        AddHandler mm.MouseMove, AddressOf Map_MouseMove
        MyBase.Activate()
    End Sub

    ''' <summary>
    ''' When the plugin is deactivated
    ''' </summary>
    Public Overrides Sub Deactivate()
        App.HeaderControl.RemoveAll()
        Dim map As Map = DirectCast(App.Map, Map)
        RemoveHandler map.MouseDown, AddressOf Map_MouseDown
        MyBase.Deactivate()
    End Sub

    ''' <summary>
    ''' When the 'Clip' button on the main toolbar is clicked
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    Private Sub MainButton_Click(ByVal sender As Object, ByVal e As EventArgs)
        App.Map.FunctionMode = FunctionMode.None
        If Not _ClipButtonClicked Then
            _ClipButtonClicked = True
            App.Map.Cursor = Cursors.Cross
        End If
    End Sub

    ''' <summary>
    ''' When the Clip is active and the user clicks on the map
    ''' </summary>
    ''' <param name="sender">the map object</param>
    ''' <param name="e">mouse event</param>
    Public Sub Map_MouseDown(ByVal sender As Object, ByVal e As MouseEventArgs)
        If Not _ClipButtonClicked Then
            Return
        End If
        selectBoxPt = e.Location
        mouseDownClicked = True
    End Sub

    ''' <summary>
    ''' When the Clip is active and the user clicks on the map
    ''' </summary>
    ''' <param name="sender">the map object</param>
    ''' <param name="e">mouse event</param>
    Public Sub Map_Mouseup(ByVal sender As Object, ByVal e As MouseEventArgs)
        If Not _ClipButtonClicked Then
            Return
        End If
        Dim width As Double = e.X - selectBoxPt.X
        Dim height As Double = e.Y - selectBoxPt.Y
        Dim rectSize As Size = New Size(width, height)
        selectBox = New Rectangle(selectBoxPt, rectSize)
        If e.X = selectBoxPt.X AndAlso e.Y = selectBoxPt.Y Then
            Exit Sub
        Else
            ClipScreen(selectBox)
        End If
        mouseDownClicked = False
        _ClipButtonClicked = False
        App.Map.Cursor = Cursors.Arrow
        App.Map.FunctionMode = FunctionMode.None
    End Sub

    ''' <summary>
    ''' Adds a special 'Clip area' layer to the map. This layer show the area, which is displayed
    ''' by the Clip window.
    ''' TL is top left coordinate of the Clip area in map coordinates
    ''' mySize is the size of the clip rectangle
    ''' </summary>
    Public Sub CreateSnapshotBoundary(ByVal TL As System.Drawing.Point, ByVal mySize As Size)
        Dim mm As Map = DirectCast(App.Map, Map)
        Dim g As Graphics = mm.CreateGraphics()
        Dim fill As Brush = New SolidBrush(Color.Black)
        Dim pt As Coordinate = mm.PixelToProj(New System.Drawing.Point(TL.X, TL.Y))
        g.DrawRectangle(Pens.Black, TL.X, TL.Y, mySize.Width, mySize.Height)
        mm.Refresh()
    End Sub

    Private Sub Map_MouseMove(ByVal sender As Object, ByVal e As MouseEventArgs)
        If Not _ClipButtonClicked Then
            Return
        End If
        If mouseDownClicked = True Then
            Dim width As Integer = e.X - selectBoxPt.X
            Dim height As Integer = e.Y - selectBoxPt.Y
            Dim mySize As Size = New Size(width, height)
            CreateSnapshotBoundary(selectBoxPt, mySize)
        End If
    End Sub

    Private Sub ClipScreen(ByVal selectbox As Rectangle)
        Dim MainMap As Map = DirectCast(App.Map, Map)

        Dim mapLayers As IMapLayerCollection = MainMap.Layers()
        Dim mapRasterLayers As IMapLayerCollection = New MapLayerCollection()
        For i = 0 To mapLayers.Count - 1
            If TypeOf mapLayers(i) Is MapImageLayer Then
                mapRasterLayers.Add(mapLayers.Item(i))
            End If
        Next
        Dim mapWebLay As MapImageLayer = Nothing
        If mapRasterLayers.Count > 0 Then
            For Each Layer In mapRasterLayers
                If Layer.LegendText = "Online Basemap" Then
                    mapWebLay = Layer
                End If
            Next
        End If

        If IsNothing(mapWebLay) Then Exit Sub

        Dim imgext As Extent = mapWebLay.Image.Extent
        Dim H As Integer = mapWebLay.Image.Height
        Dim W As Integer = mapWebLay.Image.Width
        Dim vals() As Byte = mapWebLay.Image.Values

        'Directory for storing temporary file: snapshot 
        Dim tempDir As String = Path.Combine(Path.GetTempPath(), "ImageClips")

        If Not Directory.Exists(tempDir) Then
            Try
                Directory.CreateDirectory(tempDir)
            Catch
                tempDir = Path.GetTempPath()
            End Try
        End If

        Try
            'this is the name of the required temporary files. 
            Dim imgFilename = tempDir & Path.DirectorySeparatorChar & "snapshot.tif"
            If File.Exists(imgFilename) Then
                File.Delete(imgFilename)
            End If

            'convert the mouse coordinates to map projected coordinates
            Dim TL As Coordinate = MainMap.MapFrame.PixelToProj(New System.Drawing.Point(selectbox.Left, selectbox.Top))
            Dim BR As Coordinate = MainMap.MapFrame.PixelToProj(New System.Drawing.Point(selectbox.Right, selectbox.Bottom))

            Dim mapRect As New Rectangle(0, 0, MainMap.Width, MainMap.Height)

            Dim Cwidth As Double = Math.Abs(TL.X - BR.X) / selectbox.Width
            Dim Cheight As Double = Math.Abs(TL.Y - BR.Y) / selectbox.Height
            Dim rect As New Rectangle(0, 0, selectbox.Width, selectbox.Height)
            Dim bm As Bitmap = New Bitmap(mapRect.Width, mapRect.Height)
            Dim gr As Graphics = Graphics.FromImage(bm)

            MainMap.DrawToBitmap(bm, mapRect)

            Dim CropImage = New Bitmap(selectbox.Width, selectbox.Height)
            Using grp = Graphics.FromImage(CropImage)
                grp.DrawImage(bm, rect, selectbox, GraphicsUnit.Pixel)
                bm.Dispose()
                CropImage.Save(imgFilename)
            End Using

            Dim dst_wkt As String = MainMap.MapFrame.Projection.ToEsriString
            Dim Geotransform(5) As Double
            Geotransform(0) = TL.X + (Cwidth / 2)
            Geotransform(1) = Cwidth
            Geotransform(2) = 0
            Geotransform(3) = TL.Y - (Cheight / 2)
            Geotransform(4) = 0
            Geotransform(5) = -Cheight

            Dim prj As String = System.IO.Path.ChangeExtension(imgFilename, ".prj")

            Dim prjfi As FileInfo = New FileInfo(prj)
            Dim prjtw As TextWriter = prjfi.CreateText()
            prjtw.WriteLine(dst_wkt)
            prjtw.Close()

            Dim tfw As String = System.IO.Path.ChangeExtension(imgFilename, ".tfw")

            Dim tfwfi As FileInfo = New FileInfo(tfw)
            Dim tfwtw As TextWriter = tfwfi.CreateText()
            tfwtw.WriteLine(Geotransform(1))
            tfwtw.WriteLine(Geotransform(2))
            tfwtw.WriteLine(Geotransform(4))
            tfwtw.WriteLine(Geotransform(5))
            tfwtw.WriteLine(Geotransform(0))
            tfwtw.WriteLine(Geotransform(3))
            tfwtw.Close()

            bm.Dispose()
            CropImage.Dispose()

            MainMap.AddLayer(imgFilename)
        Catch ex As Exception
            MsgBox(ex.ToString)
        End Try
    End Sub

End Class
End Namespace
Nov 21, 2013 at 7:49 PM
I agree the community is so small we need code examples like i posted many months ago....
Nov 22, 2013 at 7:29 AM
You can ask to be Editors on the project, that way you can add/modify/correct paages in the documentation section.
Nov 22, 2013 at 12:36 PM
Yes I think this is a very good idea that can boost the usability of dotspatial, alhough in my opinion it could erroneously become a clone of the discussion section.

What I mean is that every code proposed by one user should be tested or at least checked by a dotspatial expert to assess if it is correct or not. Furthermore shall this eventual new section give standard users the possibility to comment on posts? This would lead in my opinion to a clone of the discussion section. What do you think?

The main problem is that there are so many methods and properties within any dotspatial class that are not documented at all and I found myself many times using some of these methods and properties without really knowing what they mean and why one needs to use them (say e.g. the fastdrawnstate...what is it? where and why is it necessary to call it?). Many things I know about dotspatial didn't come from reading the documentation section, neither from the discussions, but I just tested them in so many ways that at one point I could make them work...but I don't know if my way of making them working is the optimum, neither if it is completely correct.

Oscar
Nov 22, 2013 at 2:41 PM
That is a good point, and I had wondered if it would become too much like the discussion page as well. I'm not sure if we can create rules (or just general guidelines), for people to post (i.e., the first person to start a thread must post a general introduction of what their code does, why it's useful, what other code must be implemented to their project to make this code work, and then they must post valid, working code below that. Additional replies on the page may be amendments or changes to the code that improved the speed or performance of the original code posted.

There obviously would have to be more organization to this section also. For instance, where should the code posted be placed: is it a tool, an addition or change to the previous dotspatial code (i.e., dotspatial.symbology.forms), or even categorized as general or miscellaneous? What I don't want to see in this section are posts asking people for help with a tool they want, or help with code they are working on, or ideas for additional dotspatial work, etc. Those topics should be left to the Discussions section.

Maybe if someone starts thread that does not follow the rules of the section and it gets moved by the editor to the Discussion section, that person gets a slap on the hand or check mark on their account. Thoughts?
Dec 11, 2013 at 11:50 AM
What do you think if in this "new" section users are able to post specific example code for some purpose, and other users just give suggestion on how to change the code to gain better performance? Then the post proposer is in charge (and the only one that can do it... a part from editors) of updating the example with the latest suggestions. It shall look like the discussion section, but the first post, which is the main post and where all updates are made, will serve to show discussion result.
The example code should be placed on top of the post and maybe downloadble as a doc file. Discussion follows below the code windows.
Let's think about something like wikipedia, where there is a main window for a definition and a discussion section where users exchange opinions and information aiming of better defining the main window definition.
Do you understand my idea?