Adapting C# code to work indifferently with shapefile or PostGIS layers (c# mapscript tutorial, part 8)

Posted by 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.

Share and Enjoy:
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google
  • Furl
  • LinkedIn
  • Reddit
  • StumbleUpon
10 Comments on Adapting C# code to work indifferently with shapefile or PostGIS layers (c# mapscript tutorial, part 8)

Closed

  1. David Fawcett says:

    Paolo,

    Can you post all or some of this on the MapServer site?

    Thanks,

    David.

  2. pcorti says:

    Hello David
    yes of course, but how?
    Should I add a link in the mapserver’s wiki?
    let me know

    Paolo

  3. David Fawcett says:

    Paolo,

    If you create a user account at the MapServer site, you should be able to create and submit a HowTo or Tutorial.

    http://mapserver.gis.umn.edu/

    I know that C# MapScript documentation is sparse, so it would be great if you would contribute your work to the MapServer site.

    David.

  4. Ning says:

    My asp.net app runs under iis but map server requires apache. Do I have to have both web servers on the same machine to run the site?

    Ning

  5. pcorti says:

    David
    I tried to register an account at the MapServer site, but seems that “New accounts are currently closed, sorry for trouble”.
    I will check in the next days if it will be solved this.
    Paolo

  6. pcorti says:

    Ning,
    you just need iis to run c# mapscript.
    It has nothing to do with apache. You would install apache if you intend to use the cgi-mapserver (but you could do this also from iis), without using the mapscript API (then you would need to develope a connector if you want to use cgi-mode with c#).

    You could even use c# mapscript not in asp .net application, but for a console or windows application.
    Paolo

  7. newbie says:

    Hello all,

    This is great! I was able to get the tutorial working and posted on my remote IIS web server. Seems like the next steps are building a more user-friendly interface (e.g. a zoom tool that draws a rubberband). Does anyone know if I can use say, Chameleon with C# Mapscript? I believe these are user-interface tools. Just getting started and my general impression is that some of these technologies depend on if you are using cgi, C# MapScript, PHP MapScript, or Python MapScript.

  8. pcorti says:

    From an ESRI ArcIMS point of view-side, I would define Chamaleon for
    MapServer something like Esri’s HTML viewer for ArcIMS. An
    HTML/javascript software that can be easily customized for every
    ArcIMS web site.
    Chamaleon - I don’t know it, but I was taking a look at it at its
    website, seems to be expressely designed for working with php
    mapscript, but I believe It could be easily adapted for working with
    .NET framework as far as the core is javasript client side code that
    make request to the web server.
    Maybe someone could be interested in making this migration.
    Alternatively you could use a start from zero approach, but maybe
    adapting Chamaleon would be more usefull also for other developers.
    Another alternative is to use a Flash viewer, some months ago I
    developed a Flash viewer for an ArcIMS site, I think I could easily
    adapt it for MapServer, let me know if you think this can be usefull
    and I will put it in my website.

  9. newbie says:

    Yeah, I posted a question on the Chamaleon site and it is for PHP Mapscript.

    It also seems like the ka-Map Javascript API may also be an alternative for user-interface tools as well.

    Hey Paolo, definately would be interested in seeing your Flash Viewer and/or a MapServer adaptation.

  10. municipal water filtration and not home filtration

    Your post is on target. Keep it up.