Thinking in GIS
a blog about GIS from a urban geogeek living at the countryside
Implementing the C# mapscript code (c# mapscript tutorial, part 5)
Posted: July 26, 2006Categories: GIS, .NET, PostGIS, devs, MapServer, Tutorials
Feedback: View Comments
Add reference to mapscript
Add the reference to mapscript_csharp.dll (browse to the mapscrip installation folder, for example C:ms4wApachecgi-binmapscriptcsharp):
{{: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
{
///
/// User Interface for c# MapServer Tutorial
///
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;
///
/// Page Load of Tutorial User Interface
///
///
///
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
/// Click Event on the Map button control
///
///
///
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;
}
///
/// Create a full Extent Map
///
///
///
private void butFullExtent_Click(object sender, System.EventArgs e)
{
ibMap.ImageUrl = "MapStream.aspx?ACTION=FULLEXTENT";
}
///
/// Add a point feature to point shapefile with an array of values for dbf
///
///
///
///
///
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();
}
///
/// Let's do identify
///
/// x image coordinate for the point to identify
/// y image coordinate for the point to identify
/// layer to identify
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.getItem(i) + "=" + shape.getValue(i);
}
layer.close();
}
}
else
{
//there is nothing to identify
System.Diagnostics.Debug.WriteLine("Nothing to identify.");
}
}
}
///
/// Conver pixel point coordinates to map point coordinates
///
/// pixel point (from map Image)
///
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;
}
///
/// Refresh the map
///
///
///
private void butRefresh_Click(object sender, System.EventArgs e)
{
//iterate layers and check visibility
for(int i=0; i
/// Restore the original point shapefile (cleared)
///
///
///
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
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
///
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
{
///
/// MapStream produce an imagestream for the ibMap control at the Default.aspx page
///
public class MapStream : System.Web.UI.Page
{
//private variable for this class
mapObj map;
rectObj originalExtent;
///
/// Zoom Mode Enumerator
///
private enum ZOOMMODE
{
ZoomIn = 0,
ZoomOut = 1
}
///
/// Do a Map Action and send an image stream
///
///
///
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;
}
///
/// Refresh MapServer map and send the image stream to output
///
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);
}
}
}
///
/// Do a zoom in or zoom out
///
/// zoomin or zoomout
/// x image coordinate
/// y image coordinate
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);
}
}
///
/// Do a Full Extent (return to Origina Extent)
///
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);
}
///
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
///
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