Purpose of this tutorial: Finding an elevation distance profile for any hiking path on any DEM.
This tutorial has 5 important steps.
Step 1: Download the DotSpatial class library
Step 2: Add the DotSpatial reference and change the compile option.
Step 3: Add the DotSpatial Controls into the Visual Studio Toolbox.
Step 4: Copy the Data Extensions folder to the debugs folder of the current project
Step 5: Design the GUI. (GUI – Graphical User Interface)
Step 6: Write the code for implementing the map operations.
Step 1: Download the DotSpatial class library
This step is similar to the Tutorial #1 step 1.
Step 2: Add the DotSpatial reference and change the compile option.
1.1) Adding the references.
DotSpatial.Data.Forms.dll, DotSpatial.Symbology.dll, DotSpatial.Controls.dll, DotSpatial.Projections.dll, DotSpatial.Data.dll, DotSpatial.Topology.dll
Fig. 1 Required references.
2.2) Change the compile option.
Change the compile option from .Net Framework 4 Client Profile to .Net Framework4. This step is as same as the Tutorial # 1 step 2.2.
Step 2: Add the DotSpatial controls on the Visual Studio Toolbox window.
Create a new tab on the toolbox and add the DotSpatial controls on it, the same as tutorial (1) step 1.
Step 3: Add the DotSpatial Controls into the Visual Studio Toolbox.
This step is as same as the Tutorial # 1 step 3.
Step 4: Copy the Data Extension folder from your downloaded folder to your current project debug folder. The .dlls from this folder is necessary for GDAL data provider access.
Fig. 2 Data Extensions folder from DotSpatial downloaded unzip folder
Step 5: Design the GUI : GUI has 2 different parts such as Main interface form and Graph interface form.
5.1 ) Design the main interface form.
Fig. 2 Final GUI. (frmMain form)
Interface design considerations.
1. Add three panel controls. Panel control’s properties should be as follows:
Properties | Panel1 | Panel2 | Panel3 |
Name | pnlOperations | pnlLegend | pnlMap |
Dock | Top | Left | Fill |
Optional step : Panel 1 background color : ActiveCaption
2. Add three buttons. Button properties should be as follows:
Properties | Button1 | Button2 | Button3 |
Name | btnLoadDEM | btnDrawPath | btnViewElevation |
Text | &Load DEM | &Draw Hiking Path | &View Elevation |
3. Add a label control and set its property as follows:
Name : lbltile Text: Hiking Path Finder.
4. Drag a “Legend” control from the DotSpatial tab under toolbox and drop it on pnlLegend. Legend properties should be as follows:
Name: Legend1 Dock: Fill
5. Drag a “Map” control from the DotSpatial tab under toolbox and drop it on pnlMap. Map properties should be as follows:
Name: Map1, Dock: Fill, Legend: Legend1 , Back color: Silver , ProjectionModeDefine: Never ProjectionModeReproject: Never
6. Drag an “AppManager” control from DotSpatial tab under toolbox and drop it on the form.
Note: This control is necessary for loading different formats of raster data.
Fig. 3 AppManager.
7. Set the properties of AppManager1 as follows:
Map: Map1 Legend: Legend1
9. Rename the form1 as frmTutorial6.
5.2 ) Design the graph interface form.
1. Download the ZedGraph.dll from the class form website or from the following URL: http://sourceforge.net/projects/zedgraph/files/
2. from the tool box, right click on the DotSpatial tab OR all windows forms tab and select the choose items from the context menu.
Fig. 6 Choose Item context menu.
Select the ZedGraph.dll from the downloaded folder.
3. Design the interface as follows:
4. Add a second form and name it as frmGraph.
Fig. 7 frmGraph form.
5. Add a panel control and set its properties as follows:
name : pnlGraph Dock= fill
6. Drag the ZedGraph control from the toolbox and drop into the pnlGraph control. Set its properties as follows:
Dock : fill
Step 6: Code implementation
6.1) Following code is related to frmMain form.
Import the following namespaces
VB
#Region “NameSpaces”
Imports DotSpatial.Controls
Imports DotSpatial.Data
Imports DotSpatial.Symbology
Imports DotSpatial.Topology
Imports System.Collections.Generic
#End Region
C#
#region “NameSpaces”
using DotSpatial.Controls;
using DotSpatial.Data;
using DotSpatial.Symbology;
using DotSpatial.Topology;
#endregion
Declare the following class level variables.
VB
#Region “Class level varibales”
‘the line layer
Dim lineLayer As MapLineLayer
‘the line feature set
Dim lineF As New FeatureSet(FeatureType.Line)
Dim lineID As Integer = 0
‘boolean variable for first time mouse click
Dim firstClick As Boolean = False
‘boolean variable for ski path drawing finished
Dim hikingpathPathFinished As Boolean = False
#End Region
C#
#region “Class level varibales”
//the line layer
MapLineLayer lineLayer = default(MapLineLayer);
//the line feature set
FeatureSet lineF = new FeatureSet(FeatureType.Line);
int lineID = 0;
//boolean variable for first time mouse click
bool firstClick = false;
//boolean variable for ski path drawing finished
bool hikingpathPathFinished = false;
#endregion
Create a PathPoint class as follows below the frmTutorial6 class.
VB
Public Class PathPoint
Public X As Double
Public Y As Double
Public Distance As Double
Public Elevation As Double
End Class
C#
public class PathPoint
{
public double X;
public double Y;
public double Distance;
public double Elevation;
}
Crate an ExtractElevation function as follows: This function is used to get the elevation from the DEM along with the line segment.
VB
”’ <summary>
”’ This function is used to get the elevation.
”’ Based on the given line segment’s start and endpoint, 100 points will be divided and based on the points elevation will be claculated.
”’ </summary>
”’ <param name=”startX”>Line segement’s start X point</param>
”’ <param name=”startY”>Line segement’s start Y point</param>
”’ <param name=”endX”>Line segement’s end X point</param>
”’ <param name=”endY”>Line segement’s end Y point</param>
”’ <param name=”raster”>Raster DEM</param>
”’ <returns>List of elevation</returns>
”’ <remarks></remarks>
Public Function ExtractElevation(ByVal startX As Double, ByVal startY As Double, ByVal endX As Double, ByVal endY As Double, ByVal raster As IMapRasterLayer) As List(Of PathPoint)
Dim curX As Double = startX
Dim curY As Double = startY
Dim curElevation As Double = 0
Dim pathPointList As New List(Of PathPoint)
Dim numberofpoints As Integer = 100
Dim constXdif = ((endX – startX) / numberofpoints)
Dim constYdif = ((endY – startY) / numberofpoints)
For i As Integer = 0 To numberofpoints
Dim newPathPoint As New PathPoint
If (i = 0) Then
curX = startX
curY = startY
Else
curX = curX + constXdif
curY = curY + constYdif
End If
Dim coordinate As New Coordinate(curX, curY)
Dim rowColumn As RcIndex = raster.DataSet.Bounds.ProjToCell(coordinate)
curElevation = raster.DataSet.Value(rowColumn.Row, rowColumn.Column)
‘set the properties of new PathPoint
newPathPoint.X = curX
newPathPoint.Y = curY
newPathPoint.Elevation = curElevation
pathPointList.Add(newPathPoint)
Next
Return pathPointList
End Function
C#
/// <summary>
/// This function is used to get the elevation.
/// Based on the given line segment’s start and endpoint, 100 points will be divided and based on the points elevation will be claculated.
/// </summary>
/// <param name=”startX”>Line segement’s start X point</param>
/// <param name=”startY”>Line segement’s start Y point</param>
/// <param name=”endX”>Line segement’s end X point</param>
/// <param name=”endY”>Line segement’s end Y point</param>
/// <param name=”raster”>Raster DEM</param>
/// <returns>List of elevation</returns>
/// <remarks></remarks>
public List<PathPoint> ExtractElevation(double startX, double startY, double endX, double endY, IMapRasterLayer raster)
{
double curX = startX;
double curY = startY;
double curElevation = 0;
List<PathPoint> pathPointList = new List<PathPoint>();
int numberofpoints = 100;
double constXdif = ((endX – startX) / numberofpoints);
double constYdif = ((endY – startY) / numberofpoints);
for (int i = 0; i <= numberofpoints; i++)
{
PathPoint newPathPoint = new PathPoint();
if ((i == 0))
{
curX = startX;
curY = startY;
}
else
{
curX = curX + constXdif;
curY = curY + constYdif;
}
Coordinate coordinate = new Coordinate(curX, curY);
RcIndex rowColumn = raster.DataSet.Bounds.ProjToCell(coordinate);
curElevation = raster.DataSet.Value[rowColumn.Row, rowColumn.Column];
//set the properties of new PathPoint
newPathPoint.X = curX;
newPathPoint.Y = curY;
newPathPoint.Elevation = curElevation;
pathPointList.Add(newPathPoint);
}
return pathPointList;
}
Add the following code in the Load DEM button click event.
VB
Private Sub btnLoadDEM_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnLoadDEM.Click
Map1.AddRasterLayer()
Map1.ZoomToMaxExtent()
End Sub
C#
private void btnLoadDEM_Click(object sender, EventArgs e)
{
map1.AddRasterLayer();
map1.ZoomToMaxExtent();
}
Add the following code in the Drawing hiking path button click event.
VB
Private Sub btnDrawPath_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnDrawPath.Click
‘remove any existing path
Dim existingPathList = Map1.GetLineLayers()
For Each existingPath As IMapLineLayer In existingPathList
Map1.Layers.Remove(existingPath)
Next
lineF = New FeatureSet(FeatureType.Line)
‘ski path is not finished
hikingpathPathFinished = False
‘initialize polyline feature set
Map1.Cursor = Cursors.Cross
‘set projection
lineF.Projection = Map1.Projection
‘initialize the featureSet attribute table
Dim column As New DataColumn(“ID”)
lineF.DataTable.Columns.Add(column)
‘add the featureSet as map layer
lineLayer = Map1.Layers.Add(lineF)
Dim symbol As New LineSymbolizer(Color.Blue, 2)
lineLayer.Symbolizer = symbol
lineLayer.LegendText = “Hiking path”
firstClick = True
End Sub
C#
private void btnDrawPath_Click(System.Object sender, System.EventArgs e)
{
//remove any existing path
foreach (IMapLineLayer existingPath in map1.GetLineLayers())
{
map1.Layers.Remove(existingPath);
}
lineF = new FeatureSet(FeatureType.Line);
//ski path is not finished
hikingpathPathFinished = false;
//initialize polyline feature set
map1.Cursor = Cursors.Cross;
//set projection
lineF.Projection = map1.Projection;
//initialize the featureSet attribute table
DataColumn column = new DataColumn(“ID”);
lineF.DataTable.Columns.Add(column);
//add the featureSet as map layer
lineLayer = (MapLineLayer)map1.Layers.Add(lineF);
LineSymbolizer symbol = new LineSymbolizer(Color.Blue, 2);
lineLayer.Symbolizer = symbol;
lineLayer.LegendText = “Hiking path”;
firstClick = true;
}
Add the following code in the Map1_MouseDown event.
VB
Private Sub Map1_MouseDown(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Map1.MouseDown
‘if ski path is fininshed, don’t draw any line
If hikingpathPathFinished = True Then Exit Sub
If e.Button = MouseButtons.Left Then
‘left click – fill array of coordinates
‘coordinate of clicked point
Dim coord As Coordinate = Map1.PixelToProj(e.Location)
‘first time left click – create empty line feature
If firstClick Then
‘Create a new List called lineArray.
‘In List we need not define the size and also
‘Here this list will store the Coordinates
‘We are going to store the mouse click coordinates into this array.
Dim lineArray As New List(Of Coordinate)
‘Create an instance for LineString class.
‘We need to pass collection of list coordinates
Dim lineGeometry As New LineString(lineArray)
‘Add the linegeometry to line feature
Dim lineFeature As IFeature = lineF.AddFeature(lineGeometry)
‘add first coordinate to the line feature
lineFeature.Coordinates.Add(coord)
‘set the line feature attribute
lineID = lineID + 1
lineFeature.DataRow(“ID”) = lineID
firstClick = False
Else
‘second or more clicks – add points to the existing feature
Dim existingFeature As IFeature = lineF.Features(lineF.Features.Count – 1)
existingFeature.Coordinates.Add(coord)
‘refresh the map if line has 2 or more points
If existingFeature.Coordinates.Count >= 2 Then
lineF.InitializeVertices()
Map1.ResetBuffer()
End If
End If
Else
‘right click – reset first mouse click
firstClick = True
Map1.ResetBuffer()
lineF.SaveAs(“c:\2009 Falls\linepath.shp”, True)
MsgBox(“The line shapefile has been saved.”)
Map1.Cursor = Cursors.Arrow
‘the ski path is finished
hikingpathPathFinished = True
End If
End Sub
C#
private void map1_MouseDown(object sender, MouseEventArgs e)
{
//if ski path is fininshed, don’t draw any line
if (hikingpathPathFinished == true)
return;
if (e.Button == MouseButtons.Left)
{
//left click – fill array of coordinates
//coordinate of clicked point
Coordinate coord = map1.PixelToProj(e.Location);
//first time left click – create empty line feature
if (firstClick)
{
//Create a new List called lineArray.
//In List we need not define the size and also
//Here this list will store the Coordinates
//We are going to store the mouse click coordinates into this array.
List<Coordinate> lineArray = new List<Coordinate>();
//Create an instance for LineString class.
//We need to pass collection of list coordinates
LineString lineGeometry = new LineString(lineArray);
//Add the linegeometry to line feature
IFeature lineFeature = lineF.AddFeature(lineGeometry);
//add first coordinate to the line feature
lineFeature.Coordinates.Add(coord);
//set the line feature attribute
lineID = lineID + 1;
lineFeature.DataRow[“ID”] = lineID;
firstClick = false;
}
else
{
//second or more clicks – add points to the existing feature
IFeature existingFeature = lineF.Features[lineF.Features.Count – 1];
existingFeature.Coordinates.Add(coord);
//refresh the map if line has 2 or more points
if (existingFeature.Coordinates.Count >= 2)
{
lineF.InitializeVertices();
map1.ResetBuffer();
}
}
}
else
{
//right click – reset first mouse click
firstClick = true;
map1.ResetBuffer();
lineF.SaveAs(“c:\\2009 Falls\\linepath.shp”, true);
MessageBox.Show(“The line shapefile has been saved.”);
map1.Cursor = Cursors.Arrow;
//the ski path is finished
hikingpathPathFinished = true;
}
}
Add the following code in the frmGraph.vb form.
VB
Public Class frmGraph
Public Sub New(ByVal pathList As List(Of PathPoint))
InitializeComponent()
‘populate the graph
‘create the distance and elevation arrays..
Dim distanceArray(pathList.Count – 1) As Double
Dim elevationArray(pathList.Count – 1) As Double
For i As Integer = 0 To pathList.Count – 1
distanceArray(i) = pathList(i).Distance
elevationArray(i) = pathList(i).Elevation
Next
ZedGraphControl1.GraphPane.CurveList.Clear()
Dim myCurve As ZedGraph.LineItem = ZedGraphControl1.GraphPane.AddCurve(“Elevation Profile”, distanceArray, elevationArray, Color.Blue)
myCurve.Line.Width = 2.0F
myCurve.Symbol.Type = ZedGraph.SymbolType.None
myCurve.Line.Fill.Color = Color.LightBlue
myCurve.Line.Fill.Color = Color.FromArgb(100, 0, 0, 255)
myCurve.Line.Fill.IsVisible = True
ZedGraphControl1.GraphPane.XAxis.Title.Text = “Distance (meters)”
ZedGraphControl1.GraphPane.YAxis.Title.Text = “Elevation (meters)”
‘refresh the graph
ZedGraphControl1.AxisChange()
‘set the graph title
ZedGraphControl1.GraphPane.Title.Text = “Hiking Path Graph”
End Sub
End Class
C#
public frmGraph(List<PathPoint> pathList)
{
InitializeComponent();
//populate the graph
//create the distance and elevation arrays..
double[] distanceArray = new double[pathList.Count];
double[] elevationArray = new double[pathList.Count];
for (int i = 0; i <= pathList.Count – 1; i++)
{
distanceArray[i] = pathList[i].Distance;
elevationArray[i] = pathList[i].Elevation;
}
zedGraphControl1.GraphPane.CurveList.Clear();
ZedGraph.LineItem myCurve = zedGraphControl1.GraphPane.AddCurve(“Elevation Profile”, distanceArray, elevationArray, Color.Blue);
myCurve.Line.Width = 2f;
myCurve.Symbol.Type = ZedGraph.SymbolType.None;
myCurve.Line.Fill.Color = Color.LightBlue;
myCurve.Line.Fill.Color = Color.FromArgb(100, 0, 0, 255);
myCurve.Line.Fill.IsVisible = true;
zedGraphControl1.GraphPane.XAxis.Title.Text = “Distance (meters)”;
zedGraphControl1.GraphPane.YAxis.Title.Text = “Elevation (meters)”;
//refresh the graph
zedGraphControl1.AxisChange();
//set the graph title
zedGraphControl1.GraphPane.Title.Text = “Hiking Path Graph”;
}
Add the following code in the frmMain.vb form’s btnViewElevation_Click event
VB
Private Sub btnViewElevation_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnViewElevation.Click
Try
‘extract the complete elevation
‘get the raster layer
Dim rasterLayer As IMapRasterLayer
If Map1.GetRasterLayers().Count = 0 Then
MsgBox(“Please add a raster layer”)
Exit Sub
End If
‘use the first raster layer in the map
rasterLayer = Map1.GetRasterLayers(0)
‘get the ski path line layer
Dim pathLayer As IMapLineLayer
If Map1.GetLineLayers().Count = 0 Then
MsgBox(“Please add the ski path”)
Exit Sub
End If
pathLayer = Map1.GetLineLayers(0)
Dim featureSet As IFeatureSet = pathLayer.DataSet
‘get the coordinates of the ski path. this is the first feature of
‘the feature set.
Dim coordinateList = featureSet.Features(0).Coordinates
‘get elevation of all segments of the path
Dim fullPathList As New List(Of PathPoint)
For i As Integer = 1 To coordinateList.Count – 1
‘for each line segment
Dim startCoord = coordinateList(i – 1)
Dim endCoord = coordinateList(i)
Dim segmentPointList = ExtractElevation(startCoord.X, startCoord.Y, endCoord.X, endCoord.Y, rasterLayer)
‘add list of points from this line segment to the complete list
fullPathList.AddRange(segmentPointList)
Next
‘calculate the distance
Dim distanceFromStart As Double = 0
For i As Integer = 1 To fullPathList.Count – 1
‘distance between two neighbouring points
Dim x1 = fullPathList(i – 1).X
Dim y1 = fullPathList(i – 1).Y
Dim x2 = fullPathList(i).X
Dim y2 = fullPathList(i).Y
Dim distance12 = Math.Sqrt(((x2 – x1) * (x2 – x1)) + ((y2 – y1) * (y2 – y1)))
distanceFromStart += distance12
fullPathList(i).Distance = distanceFromStart
Next
Dim graphForm As New frmGraph(fullPathList)
graphForm.Show()
Catch ex As Exception
MessageBox.Show(“Error calculating elevation. the whole path should be inside the DEM area”)
End Try
End Sub
C#
private void btnViewElevation_Click(System.Object sender, System.EventArgs e)
{
try
{
//extract the complete elevation
//get the raster layer
IMapRasterLayer rasterLayer = default(IMapRasterLayer);
if (map1.GetRasterLayers().Count() == 0)
{
MessageBox.Show(“Please add a raster layer”);
return;
}
//use the first raster layer in the map
rasterLayer = map1.GetRasterLayers()[0];
//get the ski path line layer
IMapLineLayer pathLayer = default(IMapLineLayer);
if (map1.GetLineLayers().Count() == 0)
{
MessageBox.Show(“Please add the ski path”);
return;
}
pathLayer = map1.GetLineLayers()[0];
IFeatureSet featureSet = pathLayer.DataSet;
//get the coordinates of the ski path. this is the first feature of
//the feature set.
IList<Coordinate> coordinateList = featureSet.Features[0].Coordinates;
//get elevation of all segments of the path
List<PathPoint> fullPathList = new List<PathPoint>();
for (int i = 0; i < coordinateList.Count – 1; i++)
{
//for each line segment
Coordinate startCoord = coordinateList[i];
Coordinate endCoord = coordinateList[i + 1];
List<PathPoint> segmentPointList = ExtractElevation(startCoord.X, startCoord.Y, endCoord.X, endCoord.Y, rasterLayer);
//add list of points from this line segment to the complete list
fullPathList.AddRange(segmentPointList);
}
//calculate the distance
double distanceFromStart = 0;
for (int i = 1; i <= fullPathList.Count – 1; i++)
{
//distance between two neighbouring points
double x1 = fullPathList[i – 1].X;
double y1 = fullPathList[i – 1].Y;
double x2 = fullPathList[i].X;
double y2 = fullPathList[i].Y;
double distance12 = Math.Sqrt(((x2 – x1) * (x2 – x1)) + ((y2 – y1) * (y2 – y1)));
distanceFromStart += distance12;
fullPathList[i].Distance = distanceFromStart;
}
frmGraph graphForm = new frmGraph(fullPathList);
graphForm.Show();
}
catch (Exception ex)
{
MessageBox.Show(“Error calculating elevation. the whole path should be inside the DEM area”);
}
}
Output screen shots
Fig. 8 HikingMain form output screen.
Fig. 9 Elevation distance profile for the drawn path.