Using PointSymbolizer on a DrawingLayer

Introduction

DotSpatial is an open-source project that contains controls which can be used to manipulate and display geographic information. This article explains how to create a DotSpatial extension by using the online template. If you are not familiar with creating a simple DotSpatial-based extension, please consider the introductory article. The extension we are creating will show a star georeferenced to each point where the user right clicks.

Getting Started

Create a new project using the DotSpatial Plugin Template. You may delete the Readme.txt and modify the name of the MyPlugin1 class (to reflect the functionality provided by the extension). I named mine PointSymbolizerPlugin. It is also reasonable to rename the template SimpleActionItem from “My Button Caption” to “Create Stars On Right-Click”

Creating a MapPointLayer

We’ll use the ButtonClick event to start tracking the mouse so that we can capture coordinates and draw points on the map surface. The MapPointLayer will hold the collection of points and a PointSymbolizer will be responsible for drawing them.

public void ButtonClick(object sender, EventArgs e)
{
    map = App.Map as Map;

    // Enable left click panning and mouse wheel zooming
    map.FunctionMode = FunctionMode.Pan;

    // Handle mouse up event on the map
    map.MouseUp += map_MouseUp;

    // The FeatureSet starts with no data; be sure to set it to the point featuretype
    _markers = new FeatureSet(FeatureType.Point);

    // The MapPointLayer controls the drawing of the marker features
    _markerLayer = new MapPointLayer(_markers);

    // The Symbolizer controls what the points look like
    _markerLayer.Symbolizer = new PointSymbolizer(Color.Blue, Symbology.PointShape.Star, 15);

    // A drawing layer draws on top of data layers, but is still georeferenced.
    map.MapFrame.DrawingLayers.Add(_markerLayer);
}

You will notice that we use a few class level variables (fields). Add them to class by placing this code inside the class and outside of any methods, preferably at the top of the class.

    private Map map;
    private FeatureSet _markers;
    private MapPointLayer _markerLayer;

To keep the code succinct, add these statements beneath the other using statements, which are near the top of the file.

    using System.Drawing;
    using System.Windows.Forms;
    using DotSpatial.Data;
    using DotSpatial.Symbology;
    using DotSpatial.Topology;

References should be added (Project, Add Reference) to System.Windows.Forms and System.Drawing. These assemblies contain code related to MouseUp event and to the Color Blue, respectively.

Adding Features to a FeatureSet

We will use the Mouse up event to intercept right clicks and add features (points) to the feature set and layer we created earlier. These points will be symbolized (drawn) by the PointSymbolizer when the MapFrame is invalidated. This happens when the window is resized or when we programatically invoke the Invalidate() method.

void map_MouseUp(object sender, MouseEventArgs e)
{
    // Intercept only the right click for adding markers
    if (e.Button != MouseButtons.Right) return;

    // Get the geographic location that was clicked
    Coordinate c = map.PixelToProj(e.Location);

    // Add the new coordinate as a "point" to the point featureset
    _markers.AddFeature(new DotSpatial.Topology.Point(c));

    // Drawing will take place from a bitmap buffer, so if data is updated,
    // we need to tell the map to refresh the buffer 
    map.MapFrame.Invalidate();
}

It is important that we convert the mouse location to the proper map coordinate based on the current map projection, by calling PixelToProj() with the mouse coordinates.

Cleaning Up on Deactivate()

Though the template doesn’t include an extension manager that would let us deactivate or activate the extension, let’s consider how we should deal with the case where the extension was uninstalled while running or where it was deactivated by the user.

The Deactivate method already has a call to HeaderControl.RemoveAll(). This means the menu item will be removed. We need to also remove our layer and redraw the map. Add this code to the Deactivate method under the RemoveAll() method call.

if (map != null && map.MapFrame.DrawingLayers.Contains(_markerLayer))
{
    // Remove our drawing layer from the map.
    map.MapFrame.DrawingLayers.Remove(_markerLayer);

    // Request a redraw
    map.MapFrame.Invalidate();
}

Conclusion

Build and run your project. Add a layer (e.g., bgd file) to the map for reference by dragging and dropping the file onto the map control. Click your extension menu item and add a few points to the map. When you zoom in or out using the mouse wheel, any stars will redrawn so that their size remains constant.

image

Points Of Interest

You can drop the DotSpatial.Plugins.ExtensionManager.dll and NuGet.Core in your output Plugins folder if you would like test deactivating your extension. I noticed that the current DemoMap implementation will not add the menu item back in if you reactivate the extension. You’ll have to run the project again.

Create an Extension to Analyze Vector and Raster Data

Introduction

DotSpatial is an open-source project that contains controls which can be used to manipulate and display geographic information. This article explains how to create a DotSpatial extension by using the online template. The extension we are creating will allow the user to draw a line and create a line layer and show elevation of a path on a Digital Elevation Model (DEM).

Getting Started

If you are not familiar with creating a simple DotSpatial-based extension, please consider the introductory article. For practical purposes, we assume you are coming to this article after having completed the previous one.

Creating a New Project

Create a new project using the DotSpatial Plugin Template. You may delete the Readme.txt and modify the name of the MyPlugin1 class (to reflect the functionality provided by the extension). I named mine PlotPathElevationPlugin.

Creating the Chart

We’ll be using a standard Windows Forms chart to plot the elevation. Create a new Form (Project, Add Windows Form…) named ChartForm.

Add a Chart control to your form. This can be done by double clicking the Chart item in the Data tab of the Toolbox (View, Toolbox). In the Properties Window (View, Properties Window) set Dock to Fill.

We only need to add one method to the form class to create a visual plot of the data that will be passed in (View, Code).

public void Plot(double[] data)
{
    chart1.Series.Clear();
    var series = chart1.Series.Add("Elevation (meters)");
    series.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Line;
    series.Points.DataBindY(data);
}

When we call the Plot method with an array of elevation data, it will make a simple line chart out of it.

Getting Raster Data

A DEM raster is essentially a rectangular grid with an elevation value for each cell. The elevation values are often represented visually by a color gradient. DotSpatial allows us to retrieve these elevation values by reaching into the DataSet for a particular row and column.

We’ll add the rest of our code to the PlotPathElevationPlugin class. Add these statements beneath the other using statements, which are near the top of the file.

    using System.Data;
    using System.Drawing;
    using System.Windows.Forms;
    using DotSpatial.Data;
    using DotSpatial.Symbology;
    using DotSpatial.Topology;

Create a method that returns the elevation for a given coordinate.

private static double GetElevation(IMapRasterLayer raster, Coordinate coordinate)
{
    RcIndex rowColumn = raster.DataSet.Bounds.ProjToCell(coordinate);
    double elevation = raster.DataSet.Value[rowColumn.Row, rowColumn.Column];
    return elevation;
}

We also create a method to get the linear distance between two points.

private static double GetDistance(double x1, double y1, double x2, double y2)
{
    return Math.Sqrt(((x2 - x1) * (x2 - x1)) + ((y2 - y1) * (y2 - y1)));
}

Coordinates

Much of the work of this extension is in collecting a list of coordinates provided by the user (via mouse clicks on the map) and expanding those coordinates into a larger set of points whose elevation will be plotted. The next two methods demonstrate how we can get a list of coordinates from a line layer. Later, we’ll cover how to create this line feature using LineString.

private List<Coordinate> GetCoordinatesFromLine(IMapLineLayer lineLayer)
{
    IFeatureSet featureSet = lineLayer.DataSet;

    // The coordinates should be the first feature of the feature set.
    IList<Coordinate> lineCoordinates = featureSet.Features[0].Coordinates;

    // Though the original line may only have a few points, we split
    // each line segment into many points
    List<Coordinate> pathCoordinates = new List<Coordinate>();

    for (int i = 0; i < lineCoordinates.Count - 1; i++)
    {
        Coordinate startCoord = lineCoordinates[i];
        Coordinate endCoord = lineCoordinates[i + 1];
        List<Coordinate> segmentCoordinates = SplitSegment(startCoord.X, startCoord.Y, endCoord.X, endCoord.Y);

        //add list of points from this line segment to the complete list
        pathCoordinates.AddRange(segmentCoordinates);
    }
    return pathCoordinates;
}

private static List<Coordinate> SplitSegment(double startX, double startY, double endX, double endY)
{
    const int MinimumDistanceBetweenPoints = 15;

    double points = Math.Floor(GetDistance(startX, startY, endX, endY) / MinimumDistanceBetweenPoints);
    int PointsPerSegment = (int)Math.Max(points, 1);

    double curX = startX;
    double curY = startY;
    double constXdif = ((endX - startX) / PointsPerSegment);
    double constYdif = ((endY - startY) / PointsPerSegment);

    List<Coordinate> pathPointList = new List<Coordinate>(PointsPerSegment);
    for (int i = 0; i <= PointsPerSegment; i++)
    {
        if (i == 0)
        {
            curX = startX;
            curY = startY;
        }
        else
        {
            curX = curX + constXdif;
            curY = curY + constYdif;
        }
        Coordinate coordinate = new Coordinate(curX, curY);
        pathPointList.Add(coordinate);
    }
    return pathPointList;
}

Class Level Variables

We will need several class level variables: an IFeature will represent our line while the user is drawing it, a MapLineLayer reference will be stored so that we can remove the user drawn path if they wish to start over, and a Map, which will point to the Application Map, though it will be cast to the Windows Forms type of map.

Add these fields to your class as shown.

    private IFeature _LineFeature;
    private MapLineLayer _PathLineLayer;
    Map map;

image

Converting Coordinates to Elevation

With a little code to glue things together, we’ll have the ability to take a line (path) and a raster (DEM) and plot the elevation of the path over the DEM. In the following sections we will work on allowing the user to create the line.

private void ShowElevation()
{
    if (!map.GetRasterLayers().Any())
    {
        MessageBox.Show("Please add a DEM raster layer to the map.");
        return;
    }

    if (!map.GetLineLayers().Any())
    {
        MessageBox.Show("Please create a path by left clicking to add points and right-clicking to complete the path.");
        return;
    }

    try
    {
        IMapRasterLayer rasterLayer = map.GetRasterLayers().First();
        IMapLineLayer pathLayer = map.GetLineLayers().First();
        var coords = GetCoordinatesFromLine(pathLayer);

        double[] elevation = new double[coords.Count];
        for (int i = 0; i < coords.Count; i++)
        {
            elevation[i] = GetElevation(rasterLayer, coords[i]);
        }

        ChartForm chart = new ChartForm();
        chart.Plot(elevation);
        chart.Show();
    }
    catch (Exception ex)
    {
        MessageBox.Show("Error calculating elevation. The whole path should be inside the DEM area. " + ex.Message);
    }
}

Using the SimpleActionItem (menu item)

Change the name of the SimpleActionItem from “My Button Caption” to “Plot Elevation of Path”.  We’ll use the ButtonClick event to start tracking the mouse so that we can capture coordinates and draw a line on the map surface. We’ll assume that the user probably already loaded a DEM, and we’ll prompt them if they try to draw a path without a DEM under it.

Replace the ButtonClick event handler.

public void ButtonClick(object sender, EventArgs e)
{
    // We're expecting this extension to only be run in a Windows Forms application.
    // We'll depend on a few Windows Forms (Map) features like MouseDown, so we cast
    // the App.Map as a Map and store a reference to it.
    map = App.Map as Map;

    // remove any existing path if needed.
    if (_PathLineLayer != null)
        map.Layers.Remove(_PathLineLayer);

    _PathLineLayer = null;
    _LineFeature = null;

    // Let the user know we are ready for them to set points by changing the cursor.
    map.Cursor = Cursors.Cross;
    map.MouseDown += map_MouseDown;
}

Creating a Vector Layer

You’ll notice the previous code block references the map_MouseDown method, which hasn’t yet been added. You can find the find the remaining code below. We use the MouseDown event to start of continue the process of adding points to the line as well as showing a progress method to provide the user with a hint on how to continue.

The current extension template DemoMap overwrites the status frequently with the current mouse coordinate, so you may see this message only briefly when using the application.

When the user signals they are done drawing the path, we show them the elevation plot.

We add several columns to the attribute table for our feature, and update these as the user creates the path.

private void map_MouseDown(object sender, MouseEventArgs e)
{
    if (e.Button == MouseButtons.Left)
    {
        // Encourage the user to select a raster, if they haven't done so.
        if (!map.GetRasterLayers().Any())
        {
            map.AddRasterLayer();
            map.ZoomToMaxExtent();
            return;
        }

        StartOrContinueDrawingPath(e.Location);
        App.ProgressHandler.Progress(null, 0, "Point registered. Click again to add line segment. Right-click to finish.");
    }
    else if (e.Button == MouseButtons.Right)
    {
        EndDrawingPath();
        ShowElevation();
        App.ProgressHandler.Progress(null, 0, "Ready.");
    }
}

private IFeature AddLineFeatureSetToMap()
{
    FeatureSet lineFeatureSet = new FeatureSet(FeatureType.Line);
    lineFeatureSet.Projection = map.Projection;

    // Initialize the featureSet attribute table by creating columns
    DataColumn column = new DataColumn("ID", typeof(short));
    lineFeatureSet.DataTable.Columns.Add(column);
    DataColumn column2 = new DataColumn("Number of Points", typeof(int));
    lineFeatureSet.DataTable.Columns.Add(column2);
    DataColumn column3 = new DataColumn("Description");
    lineFeatureSet.DataTable.Columns.Add(column3);

    // Add the featureSet as map layer
    _PathLineLayer = (MapLineLayer)map.Layers.Add(lineFeatureSet);
    _PathLineLayer.Symbolizer = new LineSymbolizer(Color.Blue, 2);
    _PathLineLayer.LegendText = "Path Layer";

    var newList = new List<Coordinate>();
    LineString lineGeometry = new LineString(newList);

    // AddFeature creates the point and a row in the DataTable
    return lineFeatureSet.AddFeature(lineGeometry);
}

private void StartOrContinueDrawingPath(System.Drawing.Point mouseLocation)
{
    Coordinate coord = map.PixelToProj(mouseLocation);

    if (_LineFeature == null)
    {
        // This is the first time we see a left click; create empty line feature.
        _LineFeature = AddLineFeatureSetToMap();

        // Add first coordinate to the line feature.
        _LineFeature.Coordinates.Add(coord);

        // Set the line feature attribute. This line may have multiple points,
        // but there is only one row in the attribute table for the entire feature (line).
        _LineFeature.DataRow["ID"] = 0;
        _LineFeature.DataRow["Description"] = "Path (line)";
    }
    else
    {
        // Second or later click - add points to the existing feature
        _LineFeature.BasicGeometry.Coordinates.Add(coord);
        _LineFeature.ParentFeatureSet.InitializeVertices();

        // Draw the line.
        map.ResetBuffer();

        // Update the attribute table.
        _LineFeature.DataRow["Number of Points"] = _LineFeature.BasicGeometry.Coordinates.Count;
    }
}

private void EndDrawingPath()
{
    // The path is complete.
    map.ResetBuffer();
    map.Cursor = Cursors.Arrow;
    map.MouseDown -= map_MouseDown;
    _LineFeature = null;
}

Conclusion

Build and run you application, and click away!

image

Points of Interest

DotSpatial only supports .bgd rasters by default. You’ll need the GDAL extension, for example if you want to open .tif files.

We kept things simple, so the X axis on your plot will only roughly represent relative distance.

Create an Extension to Import an Excel Worksheet

Introduction

DotSpatial is an open-source project that contains controls which can be used to manipulate and display geographic information. This article explains how to create a DotSpatial extension by using the online template. The extension we are creating will allow the user to import points from an Excel file. It will also demonstrate how to programmatically create a FeatureSet.

Getting Started

If you are not familiar with creating a simple DotSpatial-based extension, please consider the introductory article. For practical purposes, we assume you are coming to this article after having completed the previous one.

Creating a New Project

Create a new project using the DotSpatial Plugin Template. You may delete the Readme.txt and modify the name of the MyPlugin1 class (to reflect the functionality provided by the extension). I named mine ImportFromExcelPlugin.

Add a new class file to the project (Project, Add New Item…) named ExcelHelper. We will place our logic in this class and call it from the button in our plugin class. This separation of concerns makes maintaining the code easier.

Using Windows Forms

We are going to prompt the user to select a file using the OpenFileDialog. This requires adding a reference to our project. Using the Add Reference dialog (Project, Add Reference…) find, and add a reference to System.Windows.Forms.

We also want to add a using statement to our project so that we can access the OpenFileDialog class without using its fully qualified namespace (System.Windows.Forms.OpenFileDialog) each time. This will also mean that we can use other classes in the same namespace, like the MessageBox class, without specifying the full namespace.

Add the statement beneath the other using statements, which are near the top of the file.

using System.Windows.Forms;

Getting the Selected File

Using the OpenFileDialog is fairly simple. We set the type of files we want the user to be able to select as the Filter. We then call ShowDialog() to freeze our extension while the user selects a file. If they don’t click cancel, the FileName property will have the file they wish to open. Add this public method to your class, which will do this task.

public static FeatureSet OpenExcelFile()
{
    OpenFileDialog openDialog = new OpenFileDialog();
    openDialog.Filter = "Excel Files|*.xlsx";
    if (openDialog.ShowDialog() == DialogResult.OK)
    {
        DataTable excelTable = ConvertExcelFileToDataTable(openDialog.FileName);
        return ConvertDataTableToFeatureSet(excelTable);
    }
    return null;
}

We call two methods here that we haven’t yet created. We’ll create them next.

Converting the Excel File to a DataTable

Working with files can pose a number of issues. For example, perhaps a process deletes the file after the user selects it and right before we open it. We’ll ignore most of these cases to keep things simple.

We also assume that the Excel file has a sheet named Sheet1 with a lat and a long column. We’re only working with xlsx files, but you could use another connection string to connect to other Excel file formats. You may want to go ahead an create a sample Excel file that you can import.

image

To convert the Excel file to a DataTable, we establish a connection to the file, execute a select all command and use a OleDbDataAdapter to fill a DataTable with the results.

You’ll want to add a few more using statements to your class file.

    using System.Data.OleDb;
    using System.Data;
    using DotSpatial.Projections;
    using DotSpatial.Data;
    using DotSpatial.Topology;

And the following static method.

private static DataTable ConvertExcelFileToDataTable(string excelFileName)
{
    string connectionString =
        String.Format("Provider=Microsoft.ACE.OLEDB.12.0;Data Source={0};Extended Properties=\"Excel 12.0 Xml;HDR=YES;IMEX=1\"", excelFileName);

    using (OleDbConnection connection = new OleDbConnection(connectionString))
    {
        string query = "SELECT * FROM [Sheet1$]";
        connection.Open();
        OleDbCommand command = new OleDbCommand(query, connection);
        OleDbDataAdapter adapter = new OleDbDataAdapter(command);

        DataTable excelTable = new DataTable();
        adapter.Fill(excelTable);
        return excelTable;
    }
}

You’ll notice that we’ve used static methods, because our code follows a functional style and there aren’t any objects to which our class needs to hold a reference.

The using () block above ensures that the connection object is properly disposed when our method returns, even if there is an exception.

Converting the DataTable to a FeatureSet

Creating a FeatureSet from a DataTable is fairly straightforward. I’ll let you examine the commented code below.

private static FeatureSet ConvertDataTableToFeatureSet(DataTable excelTable)
{
    // See if table has the lat, long columns
    if (excelTable.Columns.Contains("lat") & excelTable.Columns.Contains("long"))
    {
        FeatureSet fs = new FeatureSet(FeatureType.Point);
        fs.Projection = KnownCoordinateSystems.Geographic.World.WGS1984;

        // Set columns of attribute table
        fs.DataTable = excelTable.Clone();

        foreach (DataRow excelRow in excelTable.Rows)
        {
            double lat = Double.Parse(excelRow["lat"].ToString());
            double lon = Double.Parse(excelRow["long"].ToString());

            // Add the point to the FeatureSet
            Coordinate coord = new Coordinate(lon, lat);
            Point point = new Point(coord);
            IFeature feature = fs.AddFeature(point);

            // Bring over all of the data as attribute data.
            for (int i = 0; i <= excelTable.Columns.Count - 1; i++)
            {
                feature.DataRow[i] = excelRow[i];
            }
        }
        return fs;
    }
    else
    {
        MessageBox.Show("The excel table must have lat and long columns.");
        return null;
    }
}

Wire up the Plugin to access our helper class

Our helper class is complete and we can reference it from our plugin class. Open ImportFromExcelPlugin. Change the name of the SimpleActionItem from “My Button Caption” to “Add Layer from Excel…”

Replace the ButtonClick event handler. Here, we add the FeatureSet created by our ExcelHelper to the map and assign it LegendText.

public void ButtonClick(object sender, EventArgs e)
{
    var featureSet = ExcelHelper.OpenExcelFile();

    if (featureSet != null)
    {
        //add feature set to map
        var layer = App.Map.Layers.Add(featureSet);
        layer.LegendText = "Points From Excel";
    }
}

We are done! Build and run your project.

image

Getting Started with Attribute Data Explorer

Introduction

DotSpatial is an open-source project that contains controls which can be used to manipulate and display geographic information. This article provides an overview of the Attribute Data Explorer extension. Once you have selected a feature layer, there are many options that will help you pare down your data.

Sorting

To sort records by a column’s values and replace existing sort conditions that are applied to the current or other columns, click the target column’s header, until an Up or Down Arrow icon is displayed within the header. The Up and Down Arrows indicate ascending and descending sort orders respectively.

To sort records by a column’s values while preserving existing sort conditions (to sort by more than one column), do one of the following:

  • Click a column header while holding the SHIFT key down, until an Up or Down Arrow icon is displayed within the header.
  • Right-click a column header and select Sort Ascending or Sort Descending from the context menu that appears:

To remove sorting by a column, click a column header while holding the CTRL key down. You can also select Clear Sorting from the column header context menu.

Grouping

To group by a specific column, do one of the following:

  • Drag a column header from the column header panel to the group panel: You can display this panel by right-clicking in the column header and selecting Group By Box.

  • Right-click a column header and select Group By This Column from the context menu:

 

To ungroup data by a grouping column, do one of the following:

  • Drag a column header from the group panel to the column header panel:

  • Right-click a grouping column’s header and select UnGroup from the context menu:

To remove grouping by all columns, right click the group panel and select Clear Grouping from the context menu:

Hiding Columns (Fields)

Do one of the following:

  • Click a column header/band header and drag it onto the grid control’s cell area, until the cursor changes its image to a big ‘X’. Then drop the header.
  • Drag and drop a column/band header onto the Customization Form if it’s open.

     

Applying Summaries

To change the type of summary for a specific column or apply a summary, do the following:

  1. Right-click a region within a group footer or grid footer under a specific column:

    A context menu displaying a list of supported summary types will be displayed:

  2. Select the required option from the context menu.

Filtering by Unique Value

Invoke the Filter Dropdown List

  • Hover over the column header. Click the filter button () within the column header that appears.

Create a Simple Filter Condition

To select records that contain a specific value in a specific column/card field, do the following:

  1. Invoke the filter dropdown list containing available filter values.

    By default, if filtering is applied, the filter dropdown will only display the values which match the current filter criteria. If the SHIFT key is pressed while opening the filter dropdown, all values will be listed (not only those that match the current filter criteria).

  2. Select the required filter value in the filter dropdown list:

    The filter dropdown list will be immediately closed, and the control will display the records which contain the specified value in the specified column/card field.

You can sequentially filter data against multiple columns using the method described above.

Use Microsoft Excel Style Custom Filter Dialog

To construct filter criteria involving up to two conditions, do the following:

  • Invoke the filter dropdown list (see above) and click Custom. This will invoke the Custom Filter Dialog, allowing you to compare a column with one or two values:

Use Advanced Filter Editor Dialog

To invoke an advanced Filter Editor dialog, do one of the following:

  • Right-click any column’s header and select Filter Editor:

  • If the filter panel at the bottom of the grid control is visible, click the Edit Filter button:

The Filter Editor is also invoked when choosing the (Custom) item in a column’s filter dropdown list, if the current filter criteria applied to the column consists of three or more simple filter conditions, or if the filter criteria contain advanced comparison operators such as “Is between” and “Is any of”.

To learn how to work with the Filter Editor, refer to Filter Data via the Filter Editor.

Clear the Filter

To clear the filter applied to a specific column, do one of the following:

  • Invoke the filter dropdown list (see below) and click (All).
  • In Grid Views, right-click the column header and select Clear Filter:

To clear all filter criteria, click the Close Filter button within the Filter Panel:

Disable/Enable the Filter

Click the Enable Filter button within the Filter Panel:

Points of Interest

If you are not familiar with loading extensions or don’t know where to find the Attribute Data Explorer, check out Finding and Installing the WebMap Extension.

Getting Started with DotSpatial 1.0 Desktop Mapping

Introduction

DotSpatial is an open-source project that contains controls which can be used to manipulate and display geographic information. This article provides a quick overview and functional code that will help you get started with DotSpatial. Some of the basic operations that can be performed include: loading a shape file, panning and zooming.

Downloading DotSpatial

This article is based on the DotSpatial 1.0 Release — Minimal located on the downloads tab at http://dotspatial.codeplex.com

image

Note: Be aware that your browser may add an identifier to downloaded files which results in “blocked” dll files. You can click the following link to learn how to “Unblock” files. Right click on the zip file before unzipping, choose properties, go to the general tab and click the unblock button.

image

Once you have extracted the downloaded archive, you may run DemoMap.exe to get a sense of the capabilities in DotSpatial.

Creating a project

Start Visual Studio 2010 and create a new project. By using the Windows Forms Application template, a form is added to our project. Later, we will place visual elements on this form.

image

Adding Toolbox items

The toolbox provides a way to drag and drop controls onto the design surface of a form. Generally, when a control is dropped onto a form, Visual Studio will generate code necessary to instantiate the control, and add any required references to the project. You could instantiate the control without using the designer, by writing the appropriate code and making any necessary references.

Make sure the Solution Explorer is visible (View, Solution Explorer). Double-click Form1 to view it in Design view. Make the Toolbox visible (View, Toolbox). Optionally, create a new tab to contain the DotSpatial controls by right-clicking in the toolbox and clicking Add Tab in the context menu.

image

Right click in the tab you just created or in the General tab and click Choose Items…

image

Click Browse… and navigate to the location where you downloaded the DotSpatial files. Select DotSpatial.Controls and click Open, then OK.

image

You should see a number of DotSpatial control fill the Toolbox.

image

Laying out the user interface

Drop four Button controls onto the design surface of Form1. You can find the button in the Common Controls tab of the Toolbox. Also, drop a Map control onto the form. You will probably want to resize the form so that there is more space for placing the controls.

image

Adding DotSpatial references

You will notice that Visual Studio adds a reference to DotSpatial.Controls when you drop the Map control onto Form1. If we try to build the project at this point, however, we are notified that we need to add additional references to the project.

Add references to key DotSpatial libraries by clicking Project, Add Reference.…Browse to the location where you downloaded the files. Select DotSpatial.Data and DotSpatial.Symbology. You can select multiple files by holding the CTRL key while individually clicking on them.

clip_image001

Setting control properties

We return to the form designer to set properties of the various controls we have added. Again, this could be done in code, but properties may be easier to discover in the Properties Window. Select button1 and look at the Properties Window (View, Properties Window). Notice the Text property is “button1.” Change it to “Open File.” It is wise to change the name of the button as well to represent its function. Find (Name) and change it to “uxOpenFile.” Prefixing the button name with ux is simply a convention. You won’t be able to include spaces in a control name.

image

Similarly, update buttons 2-4 to represent Zoom In, Zoom Wide, and Pan. Remember to rename them. Rename the map from “map1” to “uxMap.”

image

If you were to run the project at this point, you would find you could drop a file onto the map control, but we haven’t yet wired up the buttons and they won’t do anything if you depress them. The designer in Visual Studio has a shortcut that will create a button event handler and take us to it so we can add our own code. Double click the Open File button. You are taken to the appropriate event handler. Type the following:

uxMap.AddLayer();

The AddLayer method will show the user a dialog so that they can select a relevant file to display on the map control.

Return to the form designer by double clicking on Form1 and create an event handler for Zoom In. Type the following code into the event handler.

uxMap.ZoomIn();

Now, when a user clicks the Zoom In button, the map view will lurch forward. You’ll similarly add the following code to deal with the situation when the user clicks the Zoom Wide button.

uxMap.ZoomToMaxExtent();

For the final button, instead of going back to the designer, just paste this code below the event handler for Zoom Wide. You need to make sure you paste it just after the event handler and above the two trailing braces }.

private void uxPan_Click(object sender, EventArgs e)
{
     uxMap.FunctionMode = DotSpatial.Controls.FunctionMode.Pan;
}

Now we need to attach this method to the uxPan Button. Double Click Form1 to return to the form designer and select the uxPan Button. In the Properties Window, click the lightning bolt icon to view Events. Type uxPan_Click into the Click Action.

When a user clicks the pan button, the cursor will change to a Pan (hand) symbol. In this mode, the map can be dragged from right to left, etc. While our previous event handlers execute methods, this event handler changes the FunctionMode of the map. The map will remain in pan mode until the user closes the application. Alternately, you could add buttons that would toggle the map into other modes.

Build and run your project and open a data file (see resource section for samples).

Points of Interest

You should be able to easily expand this project to include buttons which toggle between the Info, Measure, and Select tools.

This 1.0 release version supports the .Net 4 Client Profile.

Resources

dotspatial.codeplex.comAdditional information and documentation about DotSpatial

mapwindow6.codeplex.comMapWindow 6 – an extensible DotSpatial-based application

naturalearthdata.com Sample data files

www.diva-gis.org/gdata Sample data files

How to Load DotSpatial Extensions Into My Toolbar

Introduction

DotSpatial is an open-source project that contains controls which can be used to manipulate and display geographic information. As DotSpatial is extensible, developers may find they want to load extensions create by others. This article describes the requirements for loading extensions that interact with the user interface.

Downloading DotSpatial and Creating a Project

This article is based on the DotSpatial 1.0 Release located on the downloads tab at http://dotspatial.codeplex.com

If you are not familiar with creating a simple DotSpatial-based application, please consider the introductory article and basic extension loading. For practical purposes, we assume you are coming to this article after having completed the previous ones.

Extension Interfaces

In order to provide extension developers (those writing a class that inherits from Extension or IExtension) with a simpler experience, we guarantee that they will have access to a Docking Manager, Header Control, and Status Control. This means they can create docking panels, ribbon buttons or menu items, and progress messages.

We don’t want these developers to worry about checking first to see if a header control or progress bar is available, so we categorically require that the application developer implement IDockManager, IHeaderControl, and IStatusControl. If you’re feeling like implementing these is going to be too much work, you might also ask “should I create an extension or an application?” You don’t need to implement these interfaces to support ITool or IDataProvider based extensions.

We’ve provided a few implementations which are available in the feed, such as DotSpatial.Plugins.Ribbon, which implements IHeaderControl and IStatusControl and DotSpatial.Plugins.DockManager, which implements IDockManager.

These can be consumed as nuget packages, or you can download them as zip files. Locate the zip url as the tag that begins with

content type="application/zip"

The aforementioned assemblies presently have the requirement that your main form export a Shell (see DemoMap), so that they understand to what form they should attach themselves.

Implementing IHeaderControl

Let’s examine the DemoMap project for a sample implementation of IHeaderControl.

[Export(typeof(IHeaderControl))]
public class SimpleHeaderControl : MenuBarHeaderControl, IPartImportsSatisfiedNotification
{
    private ToolStripContainer toolStripContainer1;

    [Import("Shell", typeof(ContainerControl))]
    private ContainerControl Shell { get; set; }

    ///

    /// Called when a part's imports have been satisfied and it is safe to use. (Shell will have a value)
    ///

public void OnImportsSatisfied() { this.toolStripContainer1 = new ToolStripContainer(); this.toolStripContainer1.ContentPanel.SuspendLayout(); this.toolStripContainer1.SuspendLayout(); this.toolStripContainer1.Dock = DockStyle.Fill; this.toolStripContainer1.Name = “toolStripContainer1”; // place all of the controls that were on the form originally inside of our content panel. while (Shell.Controls.Count > 0) { foreach (Control control in Shell.Controls) { this.toolStripContainer1.ContentPanel.Controls.Add(control); } } Shell.Controls.Add(this.toolStripContainer1); this.toolStripContainer1.ContentPanel.ResumeLayout(false); this.toolStripContainer1.ResumeLayout(false); this.toolStripContainer1.PerformLayout(); Initialize(toolStripContainer1); } }

Note the attribute decorating the SimpleHeaderControl class.

[Export(typeof(IHeaderControl))]

This attribute declares that this is the header control that we are exporting. We can only export one header control, one status control, and  one dock manager; otherwise when extensions are loaded, it is not clear which one should be used.

SimpleHeaderControl provides a menu and toolbar implementation and is derived from DotSpatial.Controls.Header.MenuBarHeaderControl which does the bulk of the work and requires only a ToolStripContainer where it can place menu items and toolbar buttons.

In the listed code you can see we nest any controls that were on the shell form inside of the content panel so that the menu and toolbar are docked outside (above) them.

If you are not able to derive from MenuBarHeaderControl, you will need to provide similar functionality but should be able to derive from HeaderControl, which keeps track of which items are added by what extension and implements the RemoveAll() method.

Points of Interest

You can test your implementation, by loading the DotSpatial.Plugins.MenuBar assembly.

The DemoMap.exe contains code that meets the requirements for loading extensions. Instead of creating a separate sample in this article, please reference that code.

DotSpatial 1.7 requires and extra line of code to display the default buttons in the header control. InstantiateSnippet

new DotSpatial.Controls.DefaultMenuBars(appManager1).Initialize(appManager1.HeaderControl);

Finding and Installing the WebMap Extension

Introduction

DotSpatial is an open-source project that contains controls which can be used to manipulate and display geographic information. MapWindow 6 is a thin wrapper around DotSpatial. This article explains how to locate, download, and use the WebMap extension. Some extensions are included by default with DotSpatial builds while others can be found in the Extension Manager.

Downloading DotSpatial

This article is based on DotSpatial 1.0 Release — Minimal located on the downloads tab at http://dotspatial.codeplex.com

Using the Extension Manager

Launch the Extension Manager (from the File menu).

image

The Extension Manager will probably be greatly improved at some point and include a search feature. Presently, there is a short list of extensions we can view on the Online tab. Select the DotSpatial.Plugins.WebMap extension and click Install.

image

The application status bar informs us that the extension is downloading. Once the installation completes, we can see it appear on the Installed Extensions tab. You will also note the addition of two drop down controls on the toolstrip:

image

If you want to see the files that were created on disk, click Show Extensions Folder. This will open explorer to a location similar to %appdata%\DemoMap.exe\Extensions\Packages

Using WebMap

Select the Bing Street Map from the first drop down and click the zoom tool.

image

As we zoom in, the extension displays a properly resized basemap from the nearest available zoom level. Feel free to contribute code that creates an option to snap to zoom levels!