Thinking in GIS

http://www.paolocorti.net/2006/09/20/mapserver-tutorial-for-c-mapscript-asp-net/

MapServer Tutorial for C# mapscript (ASP .NET)
Posted by Paolo Corti on September 20, 2006


This tutorial will try to guide you step by step in implementing a GIS (Geographic Information Systems) web solution, based on Open Source software (MapServer), within the .NET Framework.

The tutorial is designed to work with Visual Studio 2003 and .NET 1.1, but you will easily be able to perform all the necessary steps to complete it using another version of Visual Studio (ie: Visual Studio 2005) or other IDEs (like Visual Web Developer 2005 Express Edition or even the notepad). It will work also using .NET 2 as c# mapscript is .NET 2 compatible.

The tutorial is focused to people with knowledge of C# (but can be easily implemented with .NET Visual Basic) and the .NET framework, and with some background in GIS. If you are new to GIS, a good place to start is here.

This tutorial could be easily adapted for other languages and frameworks rather than C# (or VB) and Microsoft .NET Framewok. In fact the Open Source component we are going to use (MapServer) is also available for PHP, Java and Python, and other languages.

The whole tutorial is downloadable at this address, where you can find the data (shapefiles) necessary to complete the tutorial (but you can easily adapt your data), and a complete working Visual Studio 2003 solution with this tutorial’s code.

You can take a look at a working online demo of this tutorial here.

As we will implement a C# ASP .NET Application, take care about this issue (that should be solved in the future): MapServer thread safety and about plans to solve this issue.

Index of Tutorial
Introduction
1. Introduction to MapServer Web GIS development environment
2. Installing MapServer
3. Creating the MapFile and data configuration
4. Designing the tutorial user interface
5. Implementing the C# mapscript code
6. Migrating shapefiles to PostGIS
7. Connection MapFile layer to PostGIS
8. Adapting C# code to work indifferently with shapefile or PostGIS layers

And if the tutorial is not working for you (you get compilation errors, maps are not displayed, editing isIntroduction to MapServer Web GIS development environment (c# mapscript tutorial, part 1)
Posted by Paolo Corti on July 2, 2006


What is MapServer?

According to MapServer official site:

MapServer is an Open Source development environment for building spatially-enabled internet applications. MapServer is not a full-featured GIS system, nor does it aspire to be. Instead, MapServer excels at rendering spatial data (maps, images, and vector data) for the web.

MapServer was originally developed by the University of Minnesota (UMN) ForNet project in cooperation with NASA and the Minnesota Department of Natural Resources (MNDNR). Presently, the MapServer project is hosted by the TerraSIP project, a NASA sponsored project between the UMN and consortium of land management interests.
The software is maintained by a growing number of developers (nearing 20) from around the world and is supported by a diverse group of organizations that fund enhancements and maintenance.


MapServer main features

The MapServer’s main features are:
Advanced cartographic output

Scale dependent feature drawing and application execution
Feature labeling including label collision mediation
Fully customizable, template driven output
TrueType fonts
Map element automation (scalebar, reference map, and legend)
Thematic mapping using logical- or regular expression-based classes
Support for popular scripting and development environments

PHP, Python, Perl, Ruby, Java, and C#
Cross-platform support

Linux, Windows, Mac OS X, Solaris, and more
A multitude of raster and vector data formats
TIFF/GeoTIFF, EPPL7, and many others via GDAL
ESRI shapfiles, PostGIS, ESRI ArcSDE, Oracle Spatial, MySQL and many others via OGR
Open Geospatial Consortium (OGC) web specifications
WMS (client/server), non-transactional WFS (client/server), WMC, WCS, Filter Encoding, SLD, GML, SOS
Map projection support

On-the-fly map projection with 1000s of projections through the Proj.4 library

A good starting point to get other useful informations about MapServer is here.

How to develop with MapServer

MapServer can be used in two ways, with the CGI Interface or with the MapScript API.
Here I will discuss how to implement a MapServer site with MapScript API, accessed by C# MapScript.

MapScript provides a scripting interface for MapServer for the development of Web (ie: ASP :NET, J2EE, PHP, etc…) and stand-alone applications (ie: Windows .NET, Java, etc…). MapScript is used independently of CGI MapServer (in fact we won’t even install the CGI modality), it is a loadable module that adds MapServer capability to your favorite scripting language. MapScript currently exists in Php, Perl, Python, Ruby, Tcl, Java, and C# flavors.

All of these MapScript API where created by SWIG.

SWIG is a software development tool that connects programs written in C and C++ with a variety of high-level programming languages. SWIG is used with different types of languages including common scripting languages such as Perl, PHP, Python, Tcl, Ruby and PHP. The list of supported languages also includes non-scripting languages such as C#, Common Lisp (CLISP, Allegro CL, CFFI, UFFI), Java, Modula-3 and OCAML. Also several interpreted and compiled Scheme implementations (Guile, MzScheme, Chicken) are supported. SWIG is most commonly used to create high-level interpreted or compiled programming environments, user interfaces, and as a tool for testing and prototyping C/C++ software. SWIG can also export its parse tree in the form of XML and Lisp s-expressions. SWIG may be freely used, distributed, and modified for commercial and non-commercial use.

For more information about MapScript’s SWIG API independent language you should consult the Reference. not working…), please take a look here

Installing MapServer (c# mapscript tutorial, part 2)
Posted by Paolo Corti on July 3, 2006


Download MapServer

Download precompiled binaries here. For the purpose of this tutorial is indifferent if you download the MS4W for PHP4 or PHP5.

(As an alternative you could download an installation toolkit like this and compile yourself MapServer, but it is more difficult).

Installation

All you need to do is to extract the zip file, for example at: C:/ms4w.
Here we will not mention how to install CGI MapServer, because we are using the other modality (MapScript).

After doing that take a look at the structures of the directories created under C:/ms4w.

All the references we need in our C# ASP .NET (or Windows) application are the dll under:
C:/ms4w/Apache/cgi-bin/mapscript/csharp and the dll under C:/ms4w/Apache/cgi-bin.

The only .NET dll is C:/ms4w/Apache/cgi-bin/mapscript/csharp/mapscript_csharp.dll.

For all the other dlls is enough for them to be accessible for our application. To get this it is possible to operate in two ways:
for every .NET project manually copy these dll under bin
or simply put the path to all these dll under PATH Windows environment variable.

Note that not even a dll need to be registered on Windows (with REGSVR32), so when your application will be complete you could easily FTP it to your web server host.

Introduction to MapServer Web GIS development environment (c# mapscript tutorial, part 1)
Posted by Paolo Corti on July 2, 2006


What is MapServer?

According to MapServer official site:

MapServer is an Open Source development environment for building spatially-enabled internet applications. MapServer is not a full-featured GIS system, nor does it aspire to be. Instead, MapServer excels at rendering spatial data (maps, images, and vector data) for the web.

MapServer was originally developed by the University of Minnesota (UMN) ForNet project in cooperation with NASA and the Minnesota Department of Natural Resources (MNDNR). Presently, the MapServer project is hosted by the TerraSIP project, a NASA sponsored project between the UMN and consortium of land management interests.
The software is maintained by a growing number of developers (nearing 20) from around the world and is supported by a diverse group of organizations that fund enhancements and maintenance.


MapServer main features

The MapServer’s main features are:
Advanced cartographic output

Scale dependent feature drawing and application execution
Feature labeling including label collision mediation
Fully customizable, template driven output
TrueType fonts
Map element automation (scalebar, reference map, and legend)
Thematic mapping using logical- or regular expression-based classes
Support for popular scripting and development environments

PHP, Python, Perl, Ruby, Java, and C#
Cross-platform support

Linux, Windows, Mac OS X, Solaris, and more
A multitude of raster and vector data formats
TIFF/GeoTIFF, EPPL7, and many others via GDAL
ESRI shapfiles, PostGIS, ESRI ArcSDE, Oracle Spatial, MySQL and many others via OGR
Open Geospatial Consortium (OGC) web specifications
WMS (client/server), non-transactional WFS (client/server), WMC, WCS, Filter Encoding, SLD, GML, SOS
Map projection support

On-the-fly map projection with 1000s of projections through the Proj.4 library

A good starting point to get other useful informations about MapServer is here.

How to develop with MapServer

MapServer can be used in two ways, with the CGI Interface or with the MapScript API.
Here I will discuss how to implement a MapServer site with MapScript API, accessed by C# MapScript.

MapScript provides a scripting interface for MapServer for the development of Web (ie: ASP :NET, J2EE, PHP, etc…) and stand-alone applications (ie: Windows .NET, Java, etc…). MapScript is used independently of CGI MapServer (in fact we won’t even install the CGI modality), it is a loadable module that adds MapServer capability to your favorite scripting language. MapScript currently exists in Php, Perl, Python, Ruby, Tcl, Java, and C# flavors.

All of these MapScript API where created by SWIG.

SWIG is a software development tool that connects programs written in C and C++ with a variety of high-level programming languages. SWIG is used with different types of languages including common scripting languages such as Perl, PHP, Python, Tcl, Ruby and PHP. The list of supported languages also includes non-scripting languages such as C#, Common Lisp (CLISP, Allegro CL, CFFI, UFFI), Java, Modula-3 and OCAML. Also several interpreted and compiled Scheme implementations (Guile, MzScheme, Chicken) are supported. SWIG is most commonly used to create high-level interpreted or compiled programming environments, user interfaces, and as a tool for testing and prototyping C/C++ software. SWIG can also export its parse tree in the form of XML and Lisp s-expressions. SWIG may be freely used, distributed, and modified for commercial and non-commercial use.

For more information about MapScript’s SWIG API independent language you should consult the Reference.

Designing the tutorial user interface (c# mapscript tutorial, part 4)
Posted by Paolo Corti on July 26, 2006


Create an ASP .NET C# Project

Open Visual Studio, create a new ASP .NET C# Project called TutorialMapServer:



Insert application key value in web.config

Open web.config and add the following appSettings section under configuration section:
<configuration>
  <appSettings>
    <!--  Path to MapFile -->
    <add key="mapFilePath"
         value = " C:/training/mapServerTutorial/data/csharptutorial.map"
    />
  </appSettings>
  <!--  ........ -->
</configuration>

For the key mapFilePath be sure to put the right path to the map file (also a relative path is valid).


Create the main web form

Add a WebForm called Default.aspx:



On Default.aspx page add the following controls, like in the picture (use a table and flowlayout):


a button named butRefresh;
a button named butFullExtent;
a button named butClear;
a imageButton named ibMap;
a checkBoxList named cblLayers;
a dropdownlist named ddlLayers;
a label named lblInfo;
a literal named litIdentifyResult;
a radioButtonList named rblGisTools with the following items:

item with value 0, text “Zoom In” and selected true;
item with value 1, text “Zoom Out”
item with value 2, text “Identify”
item with value 3, text “Add Point”



For doing so you can copy and paste this HTML code in the HTML View of Default.aspx page in Visual Studio:
    <body>
        <form id="Form1" method="post" runat="server">
            <TABLE id="Table1" cellSpacing="1" cellPadding="1" width="500" border="0">
                <TR>
                    <TD colSpan="3">
                        <P align="center"><STRONG>C# <STRONG>MapServer </STRONG>Tutorial, by Paolo Corti
                                (26/07/2006)</STRONG></P>
                    </TD>
                </TR>
                <TR>
                    <TD colSpan="3">
                        <P align="center">User:
                            <asp:TextBox id="txtUser" runat="server">Paolo</asp:TextBox></P>
                    </TD>
                </TR>
                <TR>
                    <TD style="HEIGHT: 186px">Layer's visibility (check to make it visible)
                        <asp:checkboxlist id="cblLayers" runat="server"></asp:checkboxlist><asp:button id="butRefresh" runat="server" Text="Refresh Map"></asp:button></TD>
                    <TD style="HEIGHT: 186px"><asp:imagebutton id="ibMap" runat="server" BorderWidth="1px"></asp:imagebutton></TD>
                    <TD style="HEIGHT: 186px">Select a GIS action to perform on the Map:
                        <asp:radiobuttonlist id="rblGisTools" runat="server" Width="93px">
                            <asp:ListItem Value="0" Selected="True">Zoom In</asp:ListItem>
                            <asp:ListItem Value="1">Zoom Out</asp:ListItem>
                            <asp:ListItem Value="2">Identify</asp:ListItem>
                            <asp:ListItem Value="3">Add Point</asp:ListItem>
                        </asp:radiobuttonlist>
                        <asp:button id="butFullExtent" runat="server" Text="Full Extent"></asp:button><br>
                        <asp:Button id="butClear" runat="server" Text="Clear Active Point Layer"></asp:Button></TD>
                </TR>
                <TR>
                    <TD colSpan="3">Select the active layer (to identify):
                        <asp:dropdownlist id="ddlLayers" runat="server"></asp:dropdownlist></TD>
                </TR>
                <TR>
                    <TD colSpan="3"><asp:label id="lblInfo" runat="server" Font-Bold="True" ForeColor="Red"></asp:label></TD>
                </TR>
                <TR>
                    <TD colSpan="3">
                        <asp:Literal id="litIdentifyResult" runat="server"></asp:Literal></TD>
                </TR>
            </TABLE>
        </form>
    </body>

Create the MapStream web form

Add a second web form called MapStream.aspx (we will not use controls on this page, we will use it to send an image stream to ibMap, with the map produced by MapServer)

Implementing the C# mapscript code (c# mapscript tutorial, part 5)
Posted by Paolo Corti on July 26, 2006


Add reference to mapscript

Add the reference to mapscript_csharp.dll (browse to the mapscrip installation folder, for example C:/ms4w/Apache/cgi-bin/mapscript/csharp):

{{:mapserver:tutorial:add_reference.jpg|:mapserver:tutorial:add_reference.jpg}}

Remember that if you didn’t set PATH environment variable to the MapServer dlls you will need to manually copy them under the bin folder of this ASP .NET application.


The c# code for Default.aspx page

This is the complete code to add for the Default.aspx page:
using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Web;
using System.Web.SessionState;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
using System.Data.OleDb; //for dbf connection
using System.IO; //for copying the point shapefile
 
namespace TutorialMapServer
{
    /// <summary>
    /// User Interface for c# MapServer Tutorial
    /// </summary>
    public class _Default : System.Web.UI.Page
    {
        protected System.Web.UI.WebControls.Literal litIdentifyResult;
        protected System.Web.UI.WebControls.DropDownList ddlLayers;
        protected System.Web.UI.WebControls.Button butFullExtent;
        protected System.Web.UI.WebControls.RadioButtonList rblGisTools;
        protected System.Web.UI.WebControls.ImageButton ibMap;
        protected System.Web.UI.WebControls.Button butRefresh;
        protected System.Web.UI.WebControls.CheckBoxList cblLayers;
        protected System.Web.UI.WebControls.Label lblInfo;
        protected System.Web.UI.WebControls.TextBox txtUser;
        protected System.Web.UI.WebControls.Button butClear;
        //private variable for this class
        mapObj map;
 
        /// <summary>
        /// Page Load of Tutorial User Interface
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Page_Load(object sender, System.EventArgs e)
        {
            if(!Page.IsPostBack) //First access to the map
            {
                //send image stream from MapServer to ibMap
                ibMap.ImageUrl = "MapStream.aspx?ACTION=INITMAP";
                //initialize controls
                mapObj map = new mapObj(System.Configuration.ConfigurationSettings.AppSettings["mapFilePath"].ToString());
                //iterate the map layer to populate ddlLayer and cblLayer
                for(int i=0;i<map.numlayers;i++)
                {
                    layerObj layer = map.getLayer(i);
                    ddlLayers.Items.Add(layer.name);
                    cblLayers.Items.Add(layer.name);
                    //If this condition is true, the layer is visible
                    if(layer.status==(int)mapscript.MS_ON)
                    {
                        cblLayers.Items[i].Selected = true;
                    }
                }
            }
            else //Next accesses to the map, let's get it from session
            {
                map = (mapObj)Session["MAP"];
            }
        }
 
        /// <summary>
        /// Click Event on the Map button control
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ibMap_Click(object sender, System.Web.UI.ImageClickEventArgs e)
        {
            lblInfo.Text = "";
            String Action = "";
            String activeLayer=ddlLayers.SelectedItem.Text;
            //we have to check what GIS tool is needed
            switch(rblGisTools.SelectedItem.Text.ToUpper())
            {
                case "ZOOM IN":
                    Action = "ZOOMIN";
                    break;
                case "ZOOM OUT":
                    Action = "ZOOMOUT";
                    break;
                case "IDENTIFY":
                    Action = "IDENTIFY";
                    break;
                case "ADD POINT":
                    Action = "ADDPOINT";
                    break;
            }
            //For Identify let's call DoIdentify
            if(Action.Equals("IDENTIFY"))
            {
                DoIdentify(e.X,e.Y,activeLayer);
            }
            //For Add Point let's call AddPoint
            if(Action.Equals("ADDPOINT"))
            {
                String[,] fieldValues = new String[2,2];
                fieldValues[0,0]="POI_USER";
                fieldValues[0,1]=    txtUser.Text;
                fieldValues[1,0]="POI_TIME";
                fieldValues[1,1]= DateTime.Now.ToShortDateString() + ", " + System.DateTime.Now.ToLongTimeString();
                AddPoint(e.X,e.Y,activeLayer,fieldValues);
            }
            //Stream map image to ibMap according to the needed GIS Action
            ibMap.ImageUrl = "MapStream.aspx?ACTION=" + Action + "&X=" + e.X + "&Y=" + e.Y + "&ACTIVELAYER=" + activeLayer;
        }
 
        /// <summary>
        /// Create a full Extent Map
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void butFullExtent_Click(object sender, System.EventArgs e)
        {
            ibMap.ImageUrl = "MapStream.aspx?ACTION=FULLEXTENT";
        }
 
        /// <summary>
        /// Add a point feature to point shapefile with an array of values for dbf
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <param name="activeLayer"></param>
        /// <param name="fieldValues"></param>
        private void AddPoint(Double x, Double y, String activeLayer, String[,] fieldValues)
        {
            //check: this action is valid only for point shapefiles
            pointObj point = pixel2point(new pointObj(x,y,0,0)); //conver the image point in map point
            String shapeFullPath = map.shapepath + "//" + activeLayer + ".shp";
            shapefileObj shapefile = new shapefileObj(shapeFullPath,-2);
            if(shapefile.type!=(int)mapscript.MS_SHAPEFILE_POINT)
            {
                //notify action
                lblInfo.Text = "This action can be performed only on point shapefiles.";
            }
            else
            {
                /*Alternative way to insert a point in the shapefile using shapeObj:
                //create line to store the point
                lineObj line = new lineObj();
                line.add(point);
                //create shape
                shapeObj shape = new shapeObj((int)MS_SHAPE_TYPE.MS_SHAPE_POINT);
                shape.add(line);
                //add shape to shapefile
                shapefile.add(shape);
                */
                shapefile.addPoint(point);
                //add record for dbf table
                OleDbConnection cn = new OleDbConnection(@"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + map.shapepath + ";Extended Properties=dBASE IV;User ID=Admin;Password=");
                cn.Open();
                OleDbCommand com = cn.CreateCommand();
                //get field list and value list to use in the query on dbf
                String fieldList = "";
                String valueList = "";
                for(int i=0; i<(fieldValues.Length/2); i++)
                {
                    fieldList = fieldList + fieldValues[i,0];
                    valueList = valueList + "'" + fieldValues[i,1] + "'";
                    if(i<((fieldValues.Length/2)-1))
                    {
                        fieldList = fieldList + ", ";
                        valueList = valueList + ", ";
                    }
                }
                com.CommandText = "INSERT INTO " + activeLayer + " (" + fieldList + ") VALUES(" + valueList + ")";
                com.CommandType = CommandType.Text;
                com.ExecuteNonQuery();
                cn.Close();
                //notify action
                lblInfo.Text = "Point added (" + (shapefile.numshapes + 1) + " features in shapefile).";
            }
            shapefile.Dispose();
        }
 
        /// <summary>
        /// Let's do identify
        /// </summary>
        /// <param name="x">x image coordinate for the point to identify</param>
        /// <param name="y">y image coordinate for the point to identify</param>
        /// <param name="activeLayer">layer to identify</param>
        private void DoIdentify(Double x, Double y, String activeLayer)
        {
            litIdentifyResult.Text = "";
            //identify
            layerObj layer = map.getLayerByName(activeLayer);
            if(layer!=null)
            {
                layer.template = "dummy"; //for historical reasons
                pointObj point = pixel2point(new pointObj(x,y,0,0)); //conver the image point in map point
                double tolerance = map.width/100; //we use this tolerance
                if(layer.queryByPoint(map, point, mapscript.MS_SINGLE, tolerance)==(int)MS_RETURN_VALUE.MS_SUCCESS)
                {
                    //there is a feature to identify
                    resultCacheObj result = layer.getResults();
                    if(result.numresults>0)
                    {
                        int shapeInd = result.getResult(0).shapeindex;
                        //int tileInd = result.getResult(0).tileindex;
                        layer.open();
                        shapeObj shape=layer.getFeature(shapeInd, -1);
                        //iterate fields and getting values
                        for(int i=0; i<layer.numitems; i++)
                        {
                            litIdentifyResult.Text += "<BR><B>" + layer.getItem(i) + "</B>=" + shape.getValue(i);
                        }
                        layer.close();
                    }
                }
                else
                {
                    //there is nothing to identify
                    System.Diagnostics.Debug.WriteLine("Nothing to identify.");
                }
            }
        }
 
        /// <summary>
        /// Conver pixel point coordinates to map point coordinates
        /// </summary>
        /// <param name="pointPixel">pixel point (from map Image)</param>
        /// <returns></returns>
        private pointObj pixel2point(pointObj pointPixel)
        {
            rectObj extent = map.extent;
            double mapWidth = extent.maxx - extent.minx;
            double mapHeight = extent.maxy - extent.miny;
            double xperc;
            double yperc;
            xperc = pointPixel.x / map.width;
            yperc = (map.height-pointPixel.y) / map.height;
            double x=extent.minx + xperc*mapWidth;
            double y=extent.miny + yperc*mapHeight;
            pointObj pointMap = new pointObj(x,y,0,0);
            return pointMap;
        }
 
        /// <summary>
        /// Refresh the map
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void butRefresh_Click(object sender, System.EventArgs e)
        {
            //iterate layers and check visibility
            for(int i=0; i<cblLayers.Items.Count;i++)
            {
                layerObj layer = map.getLayerByName(cblLayers.Items[i].Text);
                if(cblLayers.Items[i].Selected)
                {
                    layer.status=(int)mapscript.MS_ON;
                }
                else
                {
                    layer.status=(int)mapscript.MS_OFF;
                }
            }
            //send image stream from MapServer to ibMap
            ibMap.ImageUrl = "MapStream.aspx?ACTION=REFRESHMAP";
        }
 
        /// <summary>
        /// Restore the original point shapefile (cleared)
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void butClear_Click(object sender, System.EventArgs e)
        {
            String shapeFullPath = map.shapepath + "//" + ddlLayers.SelectedItem.Text + ".shp";
            layerObj layer = map.getLayerByName(ddlLayers.SelectedItem.Text);
            if(layer.type!=MS_LAYER_TYPE.MS_LAYER_POINT || layer.connectiontype!=MS_CONNECTION_TYPE.MS_SHAPEFILE)
            {
                //notify action
                lblInfo.Text = "This action can be performed only on point shapefiles.";
            }
            else
            {
                //Clear the point shapefile by copying its copy
                //Create a DirectoryInfo object representing the specified directory.
                DirectoryInfo dir = new DirectoryInfo(map.shapepath);
                //Get the FileInfo objects for every file that belongs to shapefile in the directory.
                FileInfo[] files = dir.GetFiles(ddlLayers.SelectedItem.Text + "Copy.*");
                for(int i=0; i<files.Length; i++)
                {
                    //be sure to put a copy of the point shapefile under shapepath, the copy should be called as NameCopy (ie: for POI of this tutorial, we put a shapefile copy called POICopy)
                    File.Copy(files[i].FullName, map.shapepath + "//" + ddlLayers.SelectedItem.Text + files[i].Extension, true);
                }
                //notify action
                lblInfo.Text = "Shapefile cleared.";
            }
            ibMap.ImageUrl = "MapStream.aspx?ACTION=LAYERDELETE";
        }
 
 
        #region Web Form Designer generated code
        override protected void OnInit(EventArgs e)
        {
            //
            // CODEGEN: This call is required by the ASP.NET Web Form Designer.
            //
            InitializeComponent();
            base.OnInit(e);
        }
 
        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {   
            this.butRefresh.Click += new System.EventHandler(this.butRefresh_Click);
            this.ibMap.Click += new System.Web.UI.ImageClickEventHandler(this.ibMap_Click);
            this.butFullExtent.Click += new System.EventHandler(this.butFullExtent_Click);
            this.butClear.Click += new System.EventHandler(this.butClear_Click);
            this.Load += new System.EventHandler(this.Page_Load);
 
        }
        #endregion
 
    }
}

The c# code for MapStream.aspx page

This is the complete code to add for the MapStream.aspx page:
using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Web;
using System.Web.SessionState;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
using System.IO;
 
namespace TutorialMapServer
{
    /// <summary>
    /// MapStream produce an imagestream for the ibMap control at the Default.aspx page
    /// </summary>
    public class MapStream : System.Web.UI.Page
    {
        //private variable for this class
        mapObj map;
        rectObj originalExtent;
 
        /// <summary>
        /// Zoom Mode Enumerator
        /// </summary>
        private enum ZOOMMODE
        {
            ZoomIn = 0,
            ZoomOut = 1
        }
 
        /// <summary>
        /// Do a Map Action and send an image stream
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Page_Load(object sender, System.EventArgs e)
        {
            //read map if existing, otherwhise create a new one from map file
            map = (mapObj)Session["MAP"];
            if(map==null)
            {
                map = new mapObj(System.Configuration.ConfigurationSettings.AppSettings["mapFilePath"].ToString());
                originalExtent = new rectObj(map.extent.minx, map.extent.miny, map.extent.maxx, map.extent.maxy, 0);
                Session["ORIGINALEXTENT"]=originalExtent;
            }
            originalExtent = (rectObj)Session["ORIGINALEXTENT"];
            //read x,y
            Double x=0;
            Double y=0;
            if(Request.QueryString["X"]!=null && Request.QueryString["Y"]!=null)
            {
                x = Double.Parse(Request.QueryString["X"].ToString());
                y = Double.Parse(Request.QueryString["Y"].ToString());
            }
            //let's see which action is necessary
            String Action = Request.QueryString["ACTION"].ToString().ToUpper();
            switch(Action)
            {
                case "ZOOMIN":
                    DoZoom(ZOOMMODE.ZoomIn,x,y);
                    break;
                case "ZOOMOUT":
                    DoZoom(ZOOMMODE.ZoomOut,x,y);
                    break;
                case "FULLEXTENT":
                    DoZoomFullExtent();
                    break;
            }
            //refresh
            RefreshMap();
            //store in session
            Session["MAP"]=map;
        }
 
        /// <summary>
        /// Refresh MapServer map and send the image stream to output
        /// </summary>
        private void RefreshMap()
        {
            using(imageObj image = map.draw())
            {
                byte[] img = image.getBytes();
                using (MemoryStream ms = new MemoryStream(img))
                {
                    System.Drawing.Image mapimage = System.Drawing.Image.FromStream(ms);   
                    Bitmap bitmap = (Bitmap)mapimage;
                    bitmap.Save(Response.OutputStream, System.Drawing.Imaging.ImageFormat.Jpeg);
                }               
            }
        }
 
        /// <summary>
        /// Do a zoom in or zoom out
        /// </summary>
        /// <param name="zoomMode">zoomin or zoomout</param>
        /// <param name="x">x image coordinate</param>
        /// <param name="y">y image coordinate</param>
        private void DoZoom(ZOOMMODE zoomMode, Double x, Double y)
        {
            //Do Zoom In
            if(zoomMode==ZOOMMODE.ZoomIn)
            {
                map.zoomPoint(2, new pointObj(x,y,0,0), map.width, map.height, map.extent, null);
            }
            //Do Zoom Out
            if(zoomMode==ZOOMMODE.ZoomOut)
            {
                map.zoomPoint(-2, new pointObj(x,y,0,0), map.width, map.height, map.extent, null);
            }
        }
 
        /// <summary>
        /// Do a Full Extent (return to Origina Extent)
        /// </summary>
        private void DoZoomFullExtent()
        {
            map.extent = originalExtent;
        }
 
        #region Web Form Designer generated code
        override protected void OnInit(EventArgs e)
        {
            //
            // CODEGEN: This call is required by the ASP.NET Web Form Designer.
            //
            InitializeComponent();
            base.OnInit(e);
        }
 
        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {   
            this.Load += new System.EventHandler(this.Page_Load);
 
        }
        #endregion
    }
}

Code flow

The code is extensively commented, so you should have not difficulties to follow it.
the client from the browser request a map. If it is the first map the INITMAP action is performed and the first map from MapServer is generated
the MapServer map is generated from the mapObj from the mapscript library, by accessing to the data (shapefiles) by the Map File
the map is streamed to the image button control in the default.aspx page from the MapStream.aspx page
the mapObj is stored in session for following access from the client
all the GIS actions (Zoom In, Zoom Out…) from the default.aspx page are performed still by the MapStream.aspx page


Share and Enjoy:

Migrating shapefiles to PostGIS (c# mapscript tutorial, part 6)
Posted by Paolo Corti on July 27, 2006


What is PostGIS

PostGIS is an extension for PostgreSQL RDBMS that spatially enables it for storing GIS content.

It could be considered something similiar to Esri ArcSDE or Oracle Spatial.
In fact PostGIS is for PostgreSQL what is Esri ArcSDE for Oracle, MS SQL Server, Informix, DB2.

PostGIS is OGC compliant and is Open Source, released under the GNU General Public License.

For more info about PostGIS you can take a look here.

PostGIS Installation

First, if it is not on your RDBMS Server, you have to download and install PostgreSQL. Refer to the PostgreSQL’s Web Site for doing so.

After you have succesfully installed PostgreSQL, you can add the PostGIS module. For doing so refer to the PostGIS’s Web Site.


Create PostGIS Database

First create the PostgreSQL database, using PostgreSQL in the command window or the pgAdmin III SQL window, typing:
CREATE DATABASE "TUTORIAL"
  WITH OWNER = psqluser
       ENCODING = 'UTF8'
       TABLESPACE = pg_default;

After creating the database you have to enable it for storing, with PostGIS, the geographic information.
For doing so you have to enable PL/pgSQL procedural language extension.

You have to use the createlang command (under Windows use the command prompt, under the PostgreSQL’s bin folder):
createlang plpgsql -U psqluser -D TUTORIAL

Now you can succesfully load the PostGIS objects and functions by the lwpostgis.sql script.
psql -U psqluser -d TUTORIAL -f lwpostgis.sql

After doing so, you can see that a lot of object and functions are now loaded in TUTORIAL database.

This objects and functions are necessary for PostGIS to work.

Load data in PostGIS database

There are several ways in order to load geometry objects in PostGIS.

It is possible to load data using SQL, or using the Loader utility (shp2pgsql command, similiar to the shp2sde command for ArcSDE). In fact the loader utility is a command that will generate the SQL with the INSERT statment to load geometries in PostGIS.

Let’s see this two ways.

1. Load data in PostGIS using SQL

Create a table named TEST:
BEGIN;
CREATE TABLE "test" ("id" int4, "name" varchar(20));
SELECT AddGeometryColumn('','test','the_geom', '-1', 'LINESTRING',2);
END;

Let’s load the first linestring gis object in this table, using the GeomFromText function:
INSERT INTO "test" ("id","name","the_geom") VALUES (1, 'First Geometry', GeomFromText('LINESTRING(1 1,2 2,3 3,3 4)'));

Let’s retrieve this GIS object with the database:
SELECT id, name, the_geom FROM TEST;
 
> 1;"First Geometry";"01020000..."

As we can notice, the geometry information with a simple SELECT statment is retrieved as is stored in PostGIS database: as a binary string.

If we wan’t to retrieve the geometry information as a simple Open GIS Well Known Text Format string, we can use another function that was created in the TUTORIAL database by the lwpostgis.sql script: AsText
SELECT id, name, AsText(the_geom) FROM TEST;
 
> 1;"First Geometry";"LINESTRING(1 1,2 2,3 3,3 4)"

2. Load data (shapefiles) in PostGIS using the shp2pgsql command

To load shapefiles in PostGIS database you can use the Loader utility: the shp2pgsql command (under bin’s folder). This utility simply creates from any shapefile an sql file that can be used in PostGIS to load the shapefile in the database.

We will now load the tutorial’s shapefiles (compfun.shp, poi.shp, vestizioni.shp, zone.shp) into the TUTORIAL database we created with PostGIS.

First we will use the shp2pgsql command to produce the sql files:
shp2pgsql C:/training/mapServerTutorial/data/poi.shp poi > C:/training/mapServerTutorial/data/poi.sql
shp2pgsql C:/training/mapServerTutorial/data/compfun.shp compfun > C:/training/mapServerTutorial/data/compfun.sql
shp2pgsql C:/training/mapServerTutorial/data/vestizioni.shp vestizioni > C:/training/mapServerTutorial/data/vestizioni.sql
shp2pgsql C:/training/mapServerTutorial/data/zone.shp zone > C:/training/mapServerTutorial/data/zone.sql

Now we can use the 4 sql files produced with PostGIS: these sql files will phisically load the shapefile geometries in 4 PostGIS tables.

Executing the poi.sql script will create in TUTORIAL database the poi table:
BEGIN;
CREATE TABLE "poi" (gid serial PRIMARY KEY, "poi_time" varchar, "poi_user" varchar);
SELECT AddGeometryColumn('','poi','the_geom','-1','POINT',2);
INSERT INTO "poi" ("poi_time","poi_user",the_geom) VALUES ('04/08/2006, 16.23.41','Paolo','0101000000713D0A170A7A3141B81E853B33DB5041');
INSERT INTO "poi" ("poi_time","poi_user",the_geom) VALUES ('04/08/2006, 16.24.09','Paolo','01010000005C8FC2D5FB79314152B81E9971DB5041');
END;

Executing the compfun.sql script will create in TUTORIAL database the compfun table:
BEGIN;
CREATE TABLE "compfun" (gid serial PRIMARY KEY, "objectid" int8, "codarea" int8, "shape_area" numeric, "shape_len" numeric);
SELECT AddGeometryColumn('','compfun','the_geom','-1','MULTIPOLYGON',2);
INSERT INTO "compfun" ("objectid","codarea","shape_area","shape_len",the_geom) VALUES ('4052','2801','3.45349032455e+006','8.16732468815e+003','0106000000010000000103000000010000001D00000040996FAA397B3141E07CD98012DC50414016BF4B637B314120EDD71DF2DB5041C0C48863797C3141C026D7070CDC5041C08D9B964B7D3141C040084D8DDB504100750AE32F7B3141102BE15F19DB5041809E6B9AC87A3141906226D602DB504180B38C4A967931410077244BC0DA504100CCFE59B177314160708AEE51DA504180DD9DBA9F76314100C6939B17DA5041C04FAAF651763141706CA3D154DA504140EF3C1B8E7531411049C4A0EFDA5041003A43365D753141004DABBDECDA5041000172392C753141B0E26CBB34DB5041C080CC767F743141F057440D93DB5041402243CBBE74314110CE02459ADB5041003E9495AA743141603B417EA6DB5041C04DC6C64E753141E05D04A8BADB5041402243CBBE7431416060D6F125DC5041C080CC767F743141C0C546F137DC5041809068EA7374314150E422FE4EDC5041805FFBE4EC743141B00EB4615DDC5041403275FC62753141F05CE8A069DC504100C2B632047631414029A95177DC504140BA7EB6B97631419096E78A83DC5041C094C6072A773141C0A93C2E8CDC50410017E6B7897931416003F8D6A6DC504100DC11CE5E7A3141A0551333B0DC5041C0B399BBFB7A3141D0664F850DDC504140996FAA397B3141E07CD98012DC5041');
END;

Executing the vestizioni.sql script will create in TUTORIAL database the vestizioni table:
BEGIN;
CREATE TABLE "vestizioni" (gid serial PRIMARY KEY, "objectid" int8, "codarea" int8, "classe" int4, "tipo" int4, "cod" int4, "origine" int4, "shape_len" numeric);
SELECT AddGeometryColumn('','vestizioni','the_geom','-1','MULTILINESTRING',2);
INSERT INTO "vestizioni" ("objectid","codarea","classe","tipo","cod","origine","shape_len",the_geom) VALUES ('89833','2801','8','1','801','2','2.14449030864e+001','010500000001000000010200000002000000403738813D7C3141A08D27B907DC5041800BC76B457C314190779DBD02DC5041');
INSERT INTO "vestizioni" ("objectid","codarea","classe","tipo","cod","origine","shape_len",the_geom) VALUES ('89834','2801','8','1','801','2','5.51685811591e+000','01050000000100000001020000000200000040DFB2743B7C3141B0E4FA0009DC5041403738813D7C3141A08D27B907DC5041');
INSERT INTO "vestizioni" ("objectid","codarea","classe","tipo","cod","origine","shape_len",the_geom) VALUES ('89835','2801','8','1','801','2','9.23513291375e+000','01050000000100000001020000000200000040CB8C02387C3141C0C057250BDC504140DFB2743B7C3141B0E4FA0009DC5041');
/*
other omitted INSERT statments...
*/
END;

Executing the zone.sql script will create in TUTORIAL database the zone table:
BEGIN;
CREATE TABLE "zone" (gid serial PRIMARY KEY, "objectid" int8, "codarea" int8, "classe" int4, "tipo" int4, "cod" int4, "numlott" int8, "lottoid" numeric, "shape_area" numeric, "shape_len" numeric);
SELECT AddGeometryColumn('','zone','the_geom','-1','MULTIPOLYGON',2);
INSERT INTO "zone" ("objectid","codarea","classe","tipo","cod","numlott","lottoid","shape_area","shape_len",the_geom) VALUES ('46929','2801','1','7','107','117','2.80111700000e+006','5.68543477776e+003','3.01854931948e+002','0106000000010000000103000000010000000700000040900304607A3141900DF7EA83DB5041C0D4C76B7C7A3141205BC65C72DB5041C0E425CB7C7A314120791E9271DB5041C0E80CE8797A3141201B4EDF70DB5041803FC859407A3141F018096069DB5041C07A13F7207A314160903B487DDB504140900304607A3141900DF7EA83DB5041');
INSERT INTO "zone" ("objectid","codarea","classe","tipo","cod","numlott","lottoid","shape_area","shape_len",the_geom) VALUES ('46935','2801','1','7','107','110','2.80111000000e+006','6.65516688758e+003','3.94268253922e+002','01060000000100000001030000000100000007000000008FFB54307A314140C89894A1DB504180AF5AF5427A314100B92A0E96DB5041408C671FB9793141B0F4188D88DB50410008CE1BAD793141E063DA5990DB5041C03447E0D4793141F070036194DB5041C0C0EC31CF7931411030BA1498DB5041008FFB54307A314140C89894A1DB5041');
INSERT INTO "zone" ("objectid","codarea","classe","tipo","cod","numlott","lottoid","shape_area","shape_len",the_geom) VALUES ('46937','2801','1','7','107','116','2.80111600000e+006','7.15624610182e+003','3.39270025503e+002','01060000000100000001030000000100000005000000C07A13F7207A314160903B487DDB5041803FC859407A3141F018096069DB5041C02DDE73F8793141C0A7E30960DB50410009F5D4D679314140E3658775DB5041C07A13F7207A314160903B487DDB5041');
/*
other omitted INSERT statments...
*/
END;

Connection MapFile layer to PostGIS (c# mapscript tutorial, part 7)
Posted by Paolo Corti on August 1, 2006


First I am going to show the difference between connecting the MapFile to a shapefile layer and to a PostGIS layer, then I will show how to adapt your MapFile to work with PostGIS.

Shapefile Connection

Here is a sample connection from MapFile to a shapefile called “compfun”.

First you need to declare the path to the shape data (SHAPEPATH).

Then for each shapefile layer you have to set the shapefile’s data source (DATA) that is the name of the shapefile.
Here the shapefile is named compfun.shp so DATA is set to “compfun”.

Note that NAME can be anything, it means how in your MapFile want to call the shapefile layer.

MAP
...
SHAPEPATH "C:/training/mapServerTutorial/data"
...
LAYER
  NAME "compfun"
  TYPE POLYGON
  STATUS ON
  DATA "compfun"
  CLASS
    STYLE
      COLOR 255 235 190
      OUTLINECOLOR 0 0 0
      SYMBOL 0
    END
  END
END
...

PostGIS Connection

All the connection properties can be defined in the LAYER section of MapFile.

Obviously there is no need to set a SHAPEPATH attribute, unless you want to serve some shapefile layer togheter with PostGIS layers.

Basically in the LAYER section you need to set 3 attributes.
CONNECTIONTYPE has to be set to “postgis”
CONNECTION has to be set to the connection string needed for PostGIS
DATA has to be set with a SQL select to the geometry column from the layer table

The connection string needs some attribute to be set:
HOST hostname of the PostgreSQL server
DBNAME name of the database where your PostGIS data are stored
USER name of the user
PASSWORD
PORT you can omit this attribute if you installed PostgreSQL at the default port (5432)

Also for PostGIS layers, as for shapefile layers, NAME can be anything. It is how you want to call the layer in the MapServer context.
LAYER
  CONNECTIONTYPE postgis
  CONNECTION "host=localhost dbname=TUTORIAL user=psqluser password=psqluser port=5432"
  DATA "the_geom FROM compfun"
  NAME "compfun"
  TYPE POLYGON
    STATUS ON
    CLASS
    STYLE
      COLOR 255 235 190
      OUTLINECOLOR 0 0 0
      SYMBOL 0
    END
  END
END

Modify the MapFile tutorial’s file

You can use the csharptutorial_postgis.map MapFile included with the tutorial data.

Or if you prefer you can copy the following text in a new text file named csharptutorial_postgis.map.
MAP
NAME "Zone Samples"
SHAPEPATH "C:/training/mapServerTutorial/data"
SIZE 400 400
STATUS ON
EXTENT 1143759 4417539 1146436 4420390
UNITS METERS
FONTSET "fonts/fonts.list" 
WEB
  IMAGEPATH "C:/Inetpub/wwwroot/temp"
  IMAGEURL "C:/Inetpub/wwwroot/temp"
END
 
SYMBOL
  NAME "circle"
  TYPE ellipse
  FILLED true
  POINTS
    1 1
  END
END
 
LAYER
        CONNECTIONTYPE postgis
        CONNECTION "host=localhost dbname=TUTORIAL user=psqluser password=psqluser port=5432"
        DATA "the_geom FROM compfun"
    NAME "compfun"
    TYPE POLYGON
    STATUS ON
    CLASS
      STYLE
        COLOR 255 235 190
        OUTLINECOLOR 0 0 0
        SYMBOL 0
      END
    END
END
 
LAYER
    NAME "zone"
    CONNECTIONTYPE postgis
        CONNECTION "host=localhost dbname=TUTORIAL user=psqluser password=psqluser port=5432"
        DATA "the_geom FROM zone"
    TYPE POLYGON
    STATUS ON
    CLASSITEM "COD"
    LABELITEM "NUMLOTT"
    CLASS
                EXPRESSION ([COD]=103)
        STYLE
            COLOR 230 50 0
            OUTLINECOLOR 0 0 0
            SYMBOL 0
        END
    END
    CLASS
                EXPRESSION ([COD]=105 OR [COD]=106 OR [COD]=107 OR [COD]=108 OR [COD]=102)
        STYLE
            COLOR 255 235 230
            OUTLINECOLOR 0 0 0
            SYMBOL 0
        END
        LABEL
                    COLOR 0 0 0
                    FONT verdana
                    TYPE TRUETYPE
                    SIZE 7
                    POSITION CC
                END
    END
    CLASS
                EXPRESSION ([COD]=109 OR [COD]=101)
        STYLE
            COLOR 255 196 171
            OUTLINECOLOR 0 0 0
            SYMBOL 0
        END
    END
    CLASS
        STYLE
            COLOR 225 225 225
            OUTLINECOLOR 0 0 0
            SYMBOL 0
        END
    END
END
 
LAYER
        CONNECTIONTYPE postgis
        CONNECTION "host=localhost dbname=TUTORIAL user=psqluser password=psqluser port=5432"
        DATA "the_geom FROM vestizioni"
    NAME "vestizioni"
    TYPE LINE
    STATUS ON
    CLASS
        STYLE
            COLOR 0 0 0
            SYMBOL 0
        END
    END
END
 
LAYER
        CONNECTIONTYPE postgis
        CONNECTION "host=localhost dbname=TUTORIAL user=psqluser password=psqluser port=5432"
        DATA "the_geom FROM poi"
    NAME "POI"
    TYPE POINT
    STATUS ON
    LABELITEM "POI_TIME"
    CLASS
                SIZE 10
        STYLE
            COLOR 255 0 0
            OUTLINECOLOR 0 0 0
            SYMBOL "circle"
                END
                TEXT ([POI_USER], [POI_TIME])
                LABEL
                    COLOR 255 0 0
                    FONT verdana
                    TYPE TRUETYPE
                    SIZE 7
                    POSITION LC
                    WRAP " "
                END
        END
END
 
END

In the next step you will adapt the c# to work indifferently with shapefile or PostGIS data.

 

Adapting C# code to work indifferently with shapefile or PostGIS layers (c# mapscript tutorial, part 8)
Posted by Paolo Corti on September 14, 2006


In this section we will adapt the ASP .Net Tutorial to work both with shapefiles or with PostGIS layers.

The only c# code that needs to be modified is only the code that updates the point layer.

The tutorial is composed of basically 2 methods that update a point layer:
the first method adds a point and its attributes to the point layer (AddPoint method)
the second method delete all the points from the point layer (butClear_Click)

After you will terminate this step the tutorial will work both with shapefiles and PostGIS layers.


Adding a reference to PostgreSQL .NET dll to the Visual Studio solution

You will connect to PostGreSQL from your ASP .NET Project. To do so I have chosen to use the Npgsql .NET Data Provider. You should find it with your PostgreSQL distribution.

Basically you need the Npgsql.dll and the Mono.Securirty.dll to be added as .NET references for your Visual Studio Solution.

Modifying web.conf application key to use a MapFile with PostGIS connection

Update the sections of the web.config file.

First you need to replace the value for the key “mapFilePath” with the correct value: the full path to the MapFile that is connecting layers with PostGis.

Then you have to add a second key, named “postgreSQLConnectionString” with the value setted to the connection string for PostgreSQL. There parameters for the connection string are similiar to the ones that you used for connecting to PostgreSQL via the MapFile.
   <appSettings>
    <!--  Path to MapFile -->
    <add key="mapFilePath"
         value = "C:/training/mapServerTutorial/data/csharptutorial.map"
    />
    <add key="postgreSQLConnectionString"
         value = "Server=127.0.0.1;Port=5432;User Id=psqluser;password=psqluser;Database=TUTORIAL;"
    />
  </appSettings>

Modifying the c# method for adding points to the layer

Delete from the default.cs class the previously typed AddPoint method with these new code:
                /// <summary>
        /// Add a point feature to point shapefile with an array of values for dbf, or add a point and attributes to a point PostGIS feature class
        /// </summary>
        /// <param name="x">x image coordinate</param>
        /// <param name="y">y image coordinate</param>
        /// <param name="activeLayer">name of the active layer</param>
        /// <param name="fieldValues">array with field names and values</param>
        private void AddPoint(Double x, Double y, String activeLayer, String[,] fieldValues)
        {
      //check if the active layer is a point layer and if the point layer is from a shapefile or from PostGIS
      layerObj layer = map.getLayerByName(activeLayer);
      if(layer.type!=MS_LAYER_TYPE.MS_LAYER_POINT)
      {
        //notify action
        lblInfo.Text = "This action can be performed only on point layers.";
        return;
      }
      //convert the image point in map point
      pointObj point = pixel2point(new pointObj(x,y,0,0));
      //generate the sql INSERT statment
      //get field list and value list to use in the query on dbf
      String fieldList = "";
      String valueList = "";
      for(int i=0; i<(fieldValues.Length/2); i++)
      {
        fieldList = fieldList + fieldValues[i,0];
        valueList = valueList + "'" + fieldValues[i,1] + "'";
        if(i<((fieldValues.Length/2)-1))
        {
          fieldList = fieldList + ", ";
          valueList = valueList + ", ";
        }
      }
      //add the point to a shapefile
      if(layer.connectiontype==MS_CONNECTION_TYPE.MS_SHAPEFILE)
      {
        String shapeFullPath = map.shapepath + "//" + activeLayer + ".shp";
        shapefileObj shapefile = new shapefileObj(shapeFullPath,-2);
        /*Alternative way to insert a point in the shapefile using shapeObj:
        //create line to store the point
        lineObj line = new lineObj();
        line.add(point);
        //create shape
        shapeObj shape = new shapeObj((int)MS_SHAPE_TYPE.MS_SHAPE_POINT);
        shape.add(line);
        //add shape to shapefile
        shapefile.add(shape);
        */
        shapefile.addPoint(point);
        //add record for dbf table
        String sqlInsert = "INSERT INTO " + activeLayer + " (" + fieldList + ") VALUES(" + valueList + ")";
        OleDbConnection cn = new OleDbConnection(@"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + map.shapepath + ";Extended Properties=dBASE IV;User ID=Admin;Password=");
        cn.Open();
        OleDbCommand com = cn.CreateCommand();
        com.CommandText = sqlInsert;
        com.CommandType = CommandType.Text;
        com.ExecuteNonQuery();
        cn.Close();
        shapefile.Dispose();
      }
      //add the point to a PostGIS table
      if(layer.connectiontype==MS_CONNECTION_TYPE.MS_POSTGIS)
      {
        //set CurrentCulture according to PostgreSQL server
        CultureInfo newCultureInfo = new CultureInfo("en-US");
        newCultureInfo.NumberFormat.NaNSymbol = "";
        Thread.CurrentThread.CurrentCulture = newCultureInfo;
        //connect with PostgreSQL
        //the sqlInsert includes also the geometry (with shapefile we need to make 2 different steps)
        String sqlInsert = "INSERT INTO " + activeLayer + " (" + fieldList + ",the_geom) VALUES(" + valueList + ", GeomFromText('POINT(" + point.x.ToString() + " " + point.y.ToString() + ")',-1))";
        //reads connection string for PostgreSQL
        String connectionString = System.Configuration.ConfigurationSettings.AppSettings["postgreSQLConnectionString"].ToString();
        Npgsql.NpgsqlConnection cn = new Npgsql.NpgsqlConnection(connectionString);
        cn.Open();
        Npgsql.NpgsqlCommand com = cn.CreateCommand();
        com.CommandText = sqlInsert;
        com.ExecuteNonQuery();
        cn.Close();
      }
      //notify action
      lblInfo.Text = "Point added to " + activeLayer + " point layer.";
        }

This method is accepting 4 parameters: the x,y image coordinates of the point to be inserted in the layer, the active layer name, and an array with the field names and values.

The first thing to be performed is to check if the active layer is a point layer. If not we have to stop the execution.

If the active layer is a point layer, then we create the real point from the image coordinates.

Then we create a string for the INSERT INTO (field1,field2,…) VALUES (value1,value2,…) statment to be executed in the dbf or PostgreSQL database.

We have two differents way to make this INSERT, depending if the layer is a point shapefile or a point PostGIS layer.

Updating the shapefile

With a shapefile we need to do 2 consecutive steps (would be better to have a transaction just to be sure that both are performed):
First we need to generate a new geometry in the shp file using the shapefileObj MapScript class. To do so we can use the shapefileObj .add(shape) or the .addPoint(point) method
Then we need to insert a new record in the dbf table. To do so I use an OleDBCommand

Updating the PostGIS layer

With a PostGIS layer is much easier and powerfull than with a shapefile.

We can just use SQL to perform the INSERT of both geometric and textual information. And to do so we just need to perform an unique SQL statment.

To do so I use a NpgsqlCommand object.

Modifying the c# method for clearing all teh points from the layer

Delete from the default.cs class the previously typed butClear_Click method with these new code:
        /// <summary>
        /// Restore the original point shapefile or delete all the records from the PostGIS layer
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void butClear_Click(object sender, System.EventArgs e)
        {
            String shapeFullPath = map.shapepath + "//" + ddlLayers.SelectedItem.Text + ".shp";
            layerObj layer = map.getLayerByName(ddlLayers.SelectedItem.Text);
      //action allowed only for point layer
      if(layer.type!=MS_LAYER_TYPE.MS_LAYER_POINT)
      {
        //notify action
        lblInfo.Text = "This action can be performed only on point layers.";
        return;
      }
      //different delete action if layer is shapefile or PostGIS
      //shapefile layer
            if(layer.connectiontype==MS_CONNECTION_TYPE.MS_SHAPEFILE)
            {
                //Clear the point shapefile by restoring its copy
                //Create a DirectoryInfo object representing the specified directory.
                DirectoryInfo dir = new DirectoryInfo(map.shapepath);
                //Get the FileInfo objects for every file that belongs to shapefile in the directory.
                FileInfo[] files = dir.GetFiles(ddlLayers.SelectedItem.Text + "Copy.*");
                for(int i=0; i<files.Length; i++)
                {
                    //be sure to put a copy of the point shapefile under shapepath, the copy should be called as NameCopy (ie: for POI of this tutorial, we put a shapefile copy called POICopy)
                    File.Copy(files[i].FullName, map.shapepath + "//" + ddlLayers.SelectedItem.Text + files[i].Extension, true);
                }
                //notify action
                lblInfo.Text = "Shapefile cleared.";
            }
      //PostGIS layer
      if(layer.connectiontype==MS_CONNECTION_TYPE.MS_POSTGIS)
      {
        String connectionString = "Server=127.0.0.1;Port=5432;User Id=psqluser;password=psqluser;Database=TUTORIAL;";
        Npgsql.NpgsqlConnection cn = new Npgsql.NpgsqlConnection(connectionString);
        cn.Open();
        Npgsql.NpgsqlCommand com = cn.CreateCommand();
        com.CommandText = "DELETE FROM " + ddlLayers.SelectedItem.Text;
        com.ExecuteNonQuery();
        cn.Close();
        //notify action
        lblInfo.Text = "PostGIS layer cleared.";
      }
            ibMap.ImageUrl = "MapStream.aspx?ACTION=LAYERDELETE";
        }

The first thing to be performed is to check if the active layer is a point layer. If not we have to stop the execution.

If the active layer is a point layer, then we are going to clear all the point features in this layer.

We have two differents way to make this deletion, depending if the layer is a point shapefile or a point PostGIS layer.

Deleting features from the shapefile

With a shapefile we will copy the original shapefile with 0 features, replacing the actual shapefile, served by MapServer, with n features.

Deleting features from a PostGIS layer

With a PostGIS layer is much easier and powerfull than with a shapefile.

We can just use SQL to perform the DELETE of every feature in the PostGIS layer.

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值