SharpGIS

#GIS from a .NET developer's perspective

Open Source GIS can only be made using either C and Java...

...well at least according to this "white" paper from Refractions. Quote:

Open Source GIS software can be categorized into two largely independent development tribes. [...]

  • The ‘C’ tribe, consisting of developers working on UMN Mapserver, GRASS, GDAL/OGR, OSSIM, Proj4, GEOS, PostGIS, QGIS, MapGuide OS and OpenEV. The ‘C’ tribe also includes users of scripting languages that bind easily to C libraries, such as Python, Perl and PHP.
  • The ‘Java’ tribe, consisting of developers working on GeoTools, uDig, GeoServer, JTS, JUMP, and DeeGree.

Don't get me wrong - I think the above libraries are GREAT (I use many of them daily) but I think this is a strange way of categorizing software. First of all what about the .NET "tribe" and all the other tribes out there? I can think of several great .NET/Mono based open source GIS application and libraries (MonoGIS, Appomattox, NetTopologySuite, GeoTools.NET, SharpMap and many more). Secondly these libraries aren't even fixed to a specific language, merely a framework where you decide what .NET/Mono compatible language you decide to use when linking to these libraries. Actually you can even mix languages within the same application.

It's funny to see that people still can't see .NET as a language used for Open Source applications - probably because the big bad wolf (Microsoft) invented todays fastest growing language.

Ten ways NOT to ask a question in a developer forum:

Call me arrogant it you want, but you have probably seen some of this yourself. People sometimes seem exceptionally lazy when asking for help in a developer forum.

Here are some of the questions I often see in developer forums and that are rarely answered.

1.  "It does not work – Please help"
…if this is all you ask, how would you ever expect us to be able to help you?

2. Is this code correct? (followed by 100 lines of uncommented code snippets)
Do you really expect someone volunteer to set up a project, do what ever it takes to have your code run in our setup, fix it and send it back to you ready to implement in your project? Showing a bit of initiative, writing what this code does, where the error is, showing that you actually have been doing some debugging etc. always helps. Lots of code comments also help us understand your code without spending a lot of time analyzing it.

3. I get an error in this line: (followed by one line of code at most)
Ehm yeah so? Please state what you were trying to do, perhaps showing some previous lines of code, write WHAT error you received and preferably a full stack trace.

4. Could someone please write an application that does this and that?
Most developer forums are for helping out – not for doing YOUR job.

5. Reply to someone else’s thread, with a completely different question.
Did you ever wonder what the button "Create new thread" is for? (ex)

6. Create multiple new threads with the same question.
This tends annoy most people. Annoyed people tend not to be so helpful. Just because you haven’t received a reply within the last hour, it rarely helps asking over and over.

7. Make it clear that you never really put any effort into it to begin with.
Showing that you have actually tried working with your problem, that you have read the documentation and browsed the demos usually helps a lot to get people interested in helping you.

8. Follow-up on a question in a new thread, without referring to the previous thread.
How should we know what you are referring to? (ex)

9. Make a topic that could be about anything.
If you want people to read your post (and preferable someone who knows something about this), make sure the topic makes sense in relation to your question. It will also help people find your question later, when they run into the same problem. Topics like "Can I do this?" or "Problem" is not very appealing posts to read.

10. Ask a question in the wrong forum.
How would you expect to get help with printing from Microsoft Word in a forum about PostGreSQL?


Feel free to add some more in the comments section :-)

Using XAML for rendering maps

I was just reading a few articles on XAML - Microsoft new UI language for rendering vector content and user interfaces - when it hit me that this probably can get you much further in web mapping interaction than SVG can. XAML is part of the "Windows Presentation Foundation" a part of .NET Framework 3.0

 

My first attempt to generate XAML was done by creating a new renderer for SharpMap, and below you can see my first results in XamlPad. The data here has been created based on a SharpMap map by my quick'n'dirty XamlRenderer for SharpMap.

 

 

 

All it takes with the new renderer is initializing the new renderer with your map, and request a new map:

SharpMap.Xaml.Renderer XamlRenderer = new SharpMap.Xaml.Renderer(map);

string xaml = XamlRenderer.GetMap();

 

If you zip the generated XAML file you even get a 20% reduction over a PNG image.

 

Next step is to add interaction like zooming, querying etc…

 

Download XAML for the above worldmap (261,36 KB)

Applying on-the-fly transformation in SharpMap

I have received a lot of questions on how to transform data from one coordinatesystem to another on the fly in SharpMap. Usually the problem is that they have data in different coordinatesystems and want to match them. Although I would recommend applying transformations once-and-for-all to increase performance (you could use OGR for this), it is easy to setup in SharpMap. Below are some examples on how to accomplish this.

SharpMap gives you the full power to specify all the parameters in a projection. The following method demonstrates how to setup a UTM projection:

.csharpcode { font-size: small; color: black; font-family: Courier New , Courier, Monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }
/// <summary>
/// Creates a UTM projection for the northern
/// hemisphere based on the WGS84 datum
/// </summary>
/// <param name="utmZone">Utm Zone</param>
/// <returns>Projection</returns>
private IProjectedCoordinateSystem CreateUtmProjection(int utmZone)
{
CoordinateSystemFactory cFac = 
      new SharpMap.CoordinateSystems.CoordinateSystemFactory();
//Create geographic coordinate system based on the WGS84 datum
IEllipsoid ellipsoid = cFac.CreateFlattenedSphere("WGS 84", 
           6378137, 298.257223563, LinearUnit.Metre);
IHorizontalDatum datum = cFac.CreateHorizontalDatum("WGS_1984", 
                     DatumType.HD_Geocentric, ellipsoid, null);
IGeographicCoordinateSystem gcs = cFac.CreateGeographicCoordinateSystem(
                     "WGS 84", AngularUnit.Degrees, datum,
                     PrimeMeridian.Greenwich,
                     new AxisInfo("Lon", AxisOrientationEnum.East),
                     new AxisInfo("Lat", AxisOrientationEnum.North));
//Create UTM projection
List<ProjectionParameter> parameters = new List<ProjectionParameter>(5);
parameters.Add(new ProjectionParameter("latitude_of_origin", 0));
parameters.Add(new ProjectionParameter("central_meridian", -183+6*utmZone));
parameters.Add(new ProjectionParameter("scale_factor", 0.9996));
parameters.Add(new ProjectionParameter("false_easting", 500000));
parameters.Add(new ProjectionParameter("false_northing", 0.0));
IProjection projection = cFac.CreateProjection(
"Transverse Mercator", "Transverse_Mercator", parameters);
return cFac.CreateProjectedCoordinateSystem(
         "WGS 84 / UTM zone "+utmZone.ToString() +"N", gcs,
projection, LinearUnit.Metre,
new AxisInfo("East", AxisOrientationEnum.East),
new AxisInfo("North", AxisOrientationEnum.North));
}

If you have a well-known text-representation, you can also create a projection from this. A WKT for an UTM projection might look like this:

PROJCS["WGS 84 / UTM zone 32N",GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AUTHORITY["EPSG","32632"]]

SharpMap comes with WKT parsers for parsing a WKT to a coordinate system (note: the current v0.9RC1 has a few bug in its WKT parser, but if you get problems parsing the WKT, use the current source from the repository, where these issues have been resolved)

/// <summary>
/// Create coordinatesystem based on a Well-Known text
/// </summary>
/// <param name="wkt"></param>
/// <returns></returns>
private ICoordinateSystem CreateCoordinateSystemFromWKT(string wkt)
{
CoordinateSystemFactory cFac = new CoordinateSystemFactory();
return cFac.CreateFromWkt(strProj);
}

If your data is based on shapefile data and they have a .prj file defining the coordinatesystem, you can simply retrieve the CS from the shapefile instead:

((myMap.Layers[0] as VectorLayer).DataSource as ShapeFile).CoordinateSystem

The next step is to create a transformation between two coordinate systems. SharpMap currently supports transforming between a geographic coordinate system and one of the following projections:

  • Mercator 1-standard parallel (Mercator_1SP)
  • Mercator 1-standard parallels (Mercator_2SP)
  • Transverse mercator (Transverse_Mercator)
  • Lambert Conic Conformal 2-standard parallel (Lambert Conic Conformal (2SP))
  • Albers

Unfortunately datum-shifts and transformations between two projections are still down the pipeline, but the above will be sufficient in most cases. (for those interested full transformation between all supported projections as well as datum-shifts are almost done...)

The following shows how to create a transformation and apply it to a vectorlayer (only vector- and label-layers supports on-the-fly transformations):

//Create zone UTM 32N projection
IProjectedCoordinateSystem utmProj = CreateUtmProjection(32);
//Create geographic coordinate system (lets just reuse the CS from the projection)
IGeographicCoordinateSystem geoCS = utmProj.GeographicCoordinateSystem;
//Create transformation
CoordinateTransformationFactory ctFac = new CoordinateTransformationFactory();
ICoordinateTransformation transform = 
   ctFac.CreateFromCoordinateSystems(source, target);
//Apply transformation to a vectorlayer
(myMap.Layers[0] as VectorLayer).CoordinateTransformation = transform;

Happy transforming!

Constant variables

I've had the pleasure of working a lot with XSLT the last couple of weeks while implementing a site in Umbraco. Developing macros in XSLT is somewhat far from what I've been used to in other higher-level languages I've worked with.

The last couple of days I've been struggling a lot with the scope of variables as well as updating some variables in a loop, without much luck. It now turns out that this is not possible because "variables are constant" in XSLT. I wonder who came up with that idea...

OGC releases a "simplified" version of GML

I just read this days newsletter from OGC, containing some news on the release of a simple profile of the GML v3.1.1 standard. From the standard one can read the following in the introduction:

This profile defines a restricted but useful subset of XML-Schema and GML to lower the “implementation bar” of time and resources required for an organization to commit for developing software that supports GML. [...] this profile facilitates the ability to use WFS for interoperable feature data exchange with much less software development investment.

The GML standard is a big and complex standard, and I really think this is a great move by the OGC. This is a 30 page long specification (well 117 if you include the annex), and it's nothing compared to the 267 (601) pages full GML spec.

Hopefully we'll see more GML support in the future. At least this finally got me started with implementing GML/WFS support into SharpMap.

Well, while I'm at it, if you want to know more on the GML specification, I can recommend the book "Geography Markup Language, Foundation for the Geo-Web" by Ron Lake from Wiley.

Using OGR with SharpMap

Christian Graefe recently posted an update on his additions to the GDAL/OGR  C# wrapper and his OGR provider for SharpMap.

Apart from the data sources that SharpMap already supports, Christians OGR provider adds support for "MapInfo File", "TIGER", "S57", "DGN", "Memory", "CSV", "GML", "Interlis 1", "Interlis 2", "SQLite" and "ODBC". If I'm not completely wrong he is also working on a GDAL raster layer as well...

Look at http://www.volley-boy.de/home/sharpmap.aspx

You will need to install FWTools to use it.

Great work Christian !

Using NetTopologySuite in SharpMap

I've created a small demo showing how to utilize NetTopologySuite in SharpMap. The great thing about NetTopologySuite is that is has a bunch of geoprocessing methods that SharpMap doesn't have (yet anyway). The demo uses the NetTopologySuite library for the following:

  1. Apply some geoprocessing to the geometries (in this case it puts a buffer on all rivers)
  2. Uses NTS for making exact intersection queries. By clicking on an object the buffered feature(s) are highlighted and attribute data shown beneath.

The basics are:

Create a NTS datasource based on another datasource which takes a delegate to apply to all geometries:

NtsProvider ntsDataSource = new NtsProvider(myShapeFileDatasource, CreateBuffers);

Create the geometry-operation delegate method which should be applied to all features:

private void CreateBuffers(List<GisSharpBlog.NetTopologySuite.Features.Feature> features)
{
   foreach (GisSharpBlog.NetTopologySuite.Features.Feature feature in
features)
   feature.Geometry = feature.Geometry.Buffer(0.5);
}

The 'ntsDataSource' object can then be assigned as datasource to a vectorlayer.

Clicking and highlighting features are done by reacting to a click-event on an ImageButton (in ASP.NET) or a PictureBox (in Windows.Forms).

protected void imgMap_Click(object sender, ImageClickEventArgs e)
{
   //Convert click-point in image coordinates to world coordinates
   Point ClickPnt = myMap.ImageToWorld(new System.Drawing.Point(e.X, e.Y));
  
SharpMap.Data.FeatureDataSet ds = new SharpMap.Data.FeatureDataSet();
  
//Execute click-query on first layer in layers collection
  
(myMap.Layers[0] as SharpMap.Layers.VectorLayer).DataSource.ExecuteIntersectionQuery(ClickPnt, ds);
   if (ds.Tables.Count > 0) //We have a result
  
{
     
//Add clicked features to a new selection layer
     
SharpMap.Layers.VectorLayer laySelected = new SharpMap.Layers.VectorLayer("Selection");
      laySelected.DataSource =
new GeometryProvider(ds.Tables[0]);
      laySelected.Style.Fill =
new System.Drawing.SolidBrush(System.Drawing.Color.Yellow);
      myMap.Layers.Add(laySelected);
   }
   //Call method that renders a new map and displays it to the user
  
GenerateMap();
}

 

Download NetTopologySuite_Demo.zip (544,32 KB)