PostGIS Spatial Database Engine UMN Mapserver Boston Geographic Information Systems    Checkout our PostGIS in Action book.  First chapter is a free download   PostGreSQL Object Relational Database Management System
GIS Books  Home   Consulting Services  About Boston GIS   Boston GIS Blog  Postgres OnLine Journal
PostGIS in Action is out in hard-copy,
download the first chapter
and SQL Primer for free. Tips and Tricks for PostGIS
  GIS Article comments Comments Rss
Part 2 - PostGIS and SharpMap in ASP.NET 2.0 using VB.NET: Displaying the Maps

The Fun Part: Mapping the Data

There are 5 key things we need to do when presenting data on web maps. They are

  • Plotting the data
  • Maintaining view state - where was the user last - if this is there first visit - initialize the state.
  • Figuring out where a user clicked on an image map and converting these coordinates to spatial coordinates relative to view state.
  • Taking additional inputs such as request for specific layers or filtering of layers.
  • Doing something with these coordinates and inputs - e.g. do we Zoom in, Zoom Out, Pan, filter data


Everthing beyond those 5 basic items is presentational sugar.

Initializing Properties

For this exercise, we will use arrays to keep track of the 4 maps we have. This makes it easy to extend for more maps.

    Protected justmaps_string() As String = {"2002", "2003", "2004", "2005"}
    Protected justmaps_maps(3) As SharpMap.Map 'its a zeroth based array

The Presentation Layer

The key parts of our aspx is shown below. Note how we have named the image controls to be in line with our array variables. See the Demo in action

        <table>
            <tr>
                <td nowrap colspan="2">
                    <asp:RadioButtonList ID="rblMapTools" runat="server" RepeatDirection="Horizontal">
                        <asp:ListItem Value="0">Zoom in</asp:ListItem>
                        <asp:ListItem Value="1">Zoom out</asp:ListItem>
                        <asp:ListItem Value="2" Selected="True">Pan</asp:ListItem>
                    </asp:RadioButtonList>
                    <asp:Button ID="cmdReset" Text="Reset" runat="server"/>
                </td>
             </tr>
             <tr>
                <td nowrap>Unit Type: 
                    <asp:DropDownList ID="ddlLU" runat="server" AutoPostBack="true">
                        <asp:ListItem Value="ALL">ALL</asp:ListItem>
                        <asp:ListItem Value="A">A</asp:ListItem>
                        <asp:ListItem Value="C">C</asp:ListItem>
                        <asp:ListItem Value="CM">CM</asp:ListItem>
                        <asp:ListItem Value="I">I</asp:ListItem>
                        <asp:ListItem Value="R1">R1</asp:ListItem>
                        <asp:ListItem Value="R2">R2</asp:ListItem>
                        <asp:ListItem Value="R3">R3</asp:ListItem>
                        <asp:ListItem Value="RC">RC</asp:ListItem>
                    </asp:DropDownList>
                </td>
                <td>
                    Abandoned Type: 
                    <asp:DropDownList ID="ddlAbandtype" runat="server" AutoPostBack="true">
                        <asp:ListItem Value="ALL">ALL</asp:ListItem>
                        <asp:ListItem Value="A">Abandoned</asp:ListItem>
                        <asp:ListItem Value="B">Burned</asp:ListItem>
                        <asp:ListItem Value="D">Boarded</asp:ListItem>
                    </asp:DropDownList>
               </td>
            </tr>
            <tr>
                <td><b>Survey 2002</b><br />
                        <asp:ImageButton Width="300" Height="300" ID="imgMap2002" runat="server" style="border: 1px solid #000;" />
                </td>
                <td>
                    <b>Survey 2003</b><br />
                    <asp:ImageButton Width="300" Height="300" ID="imgMap2003" runat="server" style="border: 1px solid #000;" />
                </td>
            </tr>
            <tr>
                <td>
                    <b>Survey 2004</b><br />
                    <asp:ImageButton Width="300" Height="300" ID="imgMap2004" runat="server" style="border: 1px solid #000;" />
                </td>
                <td>
                    <b>Survey 2005</b><br />
                    <asp:ImageButton Width="300" Height="300" ID="imgMap2005" runat="server" style="border: 1px solid #000;" />
                </td>
            </tr>
        </table>

Initializing the Data

In ASP.NET the basic state is initialized in the Page Load event. Below is what our page load looks like

    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        Dim i As Integer
        Dim img As System.Web.UI.WebControls.Image

        For i = 0 To justmaps_string.GetUpperBound(0)
            img = Me.FindControl("imgMap" & justmaps_string(i))
            justmaps_maps(i) = Me.InitializeMap(New System.Drawing.Size(img.Width.Value, img.Height.Value), justmaps_string(i))
        Next

        If Page.IsPostBack Then
            'Page is post back. Restore center and zoom-values from viewstate
            For i = 0 To justmaps_string.GetUpperBound(0)
                justmaps_maps(i).Center = ViewState("mapCenter")
                justmaps_maps(i).Zoom = ViewState("mapZoom")
            Next
        Else
            'Save the current mapcenter and zoom in the viewstate by just picking the first map
            ViewState.Add("mapCenter", justmaps_maps(0).Center)
            ViewState.Add("mapZoom", justmaps_maps(0).Zoom)
            For i = 0 To justmaps_string.GetUpperBound(0)
                GenerateMap(justmaps_maps(i), justmaps_string(i))
            Next
        End If
    End Sub

Observe in the above, we create an instance of a sharpmap object to correspond with each .NET Image control. Below is what our initialize map code looks like.

    Public Function InitializeMap(ByVal size As System.Drawing.Size, ByVal maptype As String) As SharpMap.Map
        HttpContext.Current.Trace.Write("Initializing map...")
        'Initialize a new map of size 'imagesize'
        Dim map As SharpMap.Map = New SharpMap.Map(size)
        Dim ConnStr As String = ConfigurationManager.AppSettings("DSN")

        Dim dtItem As New SharpMap.Data.Providers.PostGIS(ConnStr, "abansurveys", "the_pointft")
        Dim dtNeighborhoods As New SharpMap.Data.Providers.PostGIS(ConnStr, "neighborhoods", "the_geom")
        Dim strWhere As String = "(yr = " & maptype & " AND abandtype <> 'N') "

        Dim lyrNeighborhoods As SharpMap.Layers.VectorLayer = New SharpMap.Layers.VectorLayer("Neighborhoods")
        Dim lyrNeighborhoodNames As SharpMap.Layers.LabelLayer = New SharpMap.Layers.LabelLayer("NeighborhoodNames")
        Dim lyrItem As SharpMap.Layers.VectorLayer = New SharpMap.Layers.VectorLayer("Item")

        If Not IsNumeric(maptype) Then 'map type should be an integer otherwise the request is a potential hack
            Return map
        End If
        With lyrNeighborhoods
            .DataSource = dtNeighborhoods
            .Style.Fill = Brushes.White
            .Style.Outline = System.Drawing.Pens.Black
            .Style.EnableOutline = True
        End With
        map.Layers.Add(lyrNeighborhoods)

        If Me.ddlLU.SelectedValue <> "ALL" Then
            strWhere &= "AND lu = '" & Me.ddlLU.SelectedValue & "' "
        End If

        If Me.ddlAbandtype.SelectedValue <> "ALL" Then
            strWhere &= "AND abandtype = '" & Me.ddlAbandtype.SelectedValue & "' "
        End If

        dtItem.DefinitionQuery = strWhere

        With lyrItem
            .DataSource = dtItem
            .Enabled = True
            With .Style
                .SymbolScale = 0.8F
            End With
        End With
        map.Layers.Add(lyrItem)

        With lyrNeighborhoodNames
            .DataSource = dtNeighborhoods
            .Enabled = True
            .LabelColumn = "name"
            .Style = New SharpMap.Styles.LabelStyle
            With .Style
                .ForeColor = Color.Black
                .Font = New Font(FontFamily.GenericMonospace, 11, FontStyle.Bold)
                .HorizontalAlignment = SharpMap.Styles.LabelStyle.HorizontalAlignmentEnum.Center
                .VerticalAlignment = SharpMap.Styles.LabelStyle.VerticalAlignmentEnum.Middle
                .Halo = New Pen(Color.Yellow, 2)
                .CollisionBuffer = New System.Drawing.SizeF(30, 30)
                .CollisionDetection = True
            End With
            .TextRenderingHint = Text.TextRenderingHint.AntiAlias
        End With
        map.Layers.Add(lyrNeighborhoodNames)

        map.BackColor = Color.White
        map.ZoomToExtents()
        HttpContext.Current.Trace.Write("Map initialized")
        Return map
    End Function

In the page load for each map we call a GenerateMap which renders the image of the map to the browser. The code looks like the below

    '<summary>
    'Grabs the map corresponding to a given image control, inserts it into the cache and sets the Image control Url to the cached image
    '</summary>
    Private Sub GenerateMap(ByVal aMap As SharpMap.Map, ByVal maptype As String)
        Dim img As System.Drawing.Image = aMap.GetMap()
        Dim imgID As String = SharpMap.Web.Caching.InsertIntoCache(1, img)
        CType(Me.FindControl("imgMap" & maptype), System.Web.UI.WebControls.Image).ImageUrl = "Getmap.aspx?ID=" + HttpUtility.UrlEncode(imgID)
    End Sub

Handling user requests

In this particular example, we've got a couple of actions that a user can perform.

  1. Zoom In
  2. Zoom Out
  3. Pan
  4. Filter by Unit Type
  5. Filter by Abandoned Type


To handle the first 3 actions, we define a single event handler that covers clicking on any of the maps. That code looks like

    Protected Sub imgMap_Click(ByVal sender As Object, ByVal e As System.Web.UI.ImageClickEventArgs) Handles imgMap2002.Click, imgMap2003.Click, imgMap2004.Click, imgMap2005.Click
        Dim i As Integer
        '-- Set center of the map to where the client clicked
        For i = 0 To justmaps_string.GetUpperBound(0)
            justmaps_maps(i).Center = justmaps_maps(i).ImageToWorld(New System.Drawing.Point(e.X, e.Y))
            '-- Set zoom value if any of the zoom tools were selected
            If rblMapTools.SelectedValue = "0" Then '//Zoom in
                justmaps_maps(i).Zoom = justmaps_maps(i).Zoom * 0.5
            ElseIf rblMapTools.SelectedValue = "1" Then '//Zoom out
                justmaps_maps(i).Zoom = justmaps_maps(i).Zoom * 2
            End If
            '--//Save the new map's zoom and center in the viewstate
            ViewState.Add("mapCenter", justmaps_maps(i).Center)
            ViewState.Add("mapZoom", justmaps_maps(i).Zoom)
            '--//Create the map
            GenerateMap(justmaps_maps(i), justmaps_string(i))
        Next
    End Sub


To handle query filter requests, again we use a single event handler. Note that most of the logic for filtering is in our InitializeMap routine.

    Protected Sub ddl_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles ddlLU.SelectedIndexChanged, ddlAbandtype.SelectedIndexChanged
        Dim i As Integer
        For i = 0 To justmaps_string.GetUpperBound(0)
            '-- Initialize the zoom and center to what user had last
            justmaps_maps(i).Zoom = ViewState("mapZoom")
            justmaps_maps(i).Center = ViewState("mapCenter")
            '--//Create the map
            GenerateMap(justmaps_maps(i), justmaps_string(i))
        Next
    End Sub

There are other ways of tying the event handler to a presentation object. We could have just as easily and perhaps more extensibly defined an onclick event for each of our controls which would have looked something like

<asp:ImageButton Width="300" Height="300" ID="imgMap2002" runat="server" style="border: 1px solid #000;" onclick="imgMap_Click"/>

The benefit of that approach over the one we chose is that it would be easier to implement a dynamic number of maps say if you have a map for each data item in a datalist.



Post Comments About Part 2 - PostGIS and SharpMap in ASP.NET 2.0 using VB.NET: Displaying the Maps




This Document is available under the GNU Free Documentation License 1.2 http://www.gnu.org/copyleft/fdl.html & for download at the BostonGIS site http://www.bostongis.com

Boston GIS      Copyright 2024      Paragon Corporation