Blog from February, 2008

Working with some map controls

Introduction
An important part of the new GUI will be the Mapping library/control used. Gena has already looked at several available options. There is also an overview "The State of Open Source GIS 2007-09-survey-open-source.pdf" available at:
http://wiki.wldelft.nl/display/TOOLS/Overview+of+available+controls+and+libraries

There is strong preference for using open source projects. Some points that must be taking into consideration are:

  • well designed; what effort is necessary to extend functionality, are standard libraries used.
  • performance
  • portablility, is it available at Mono, can we port it
  • userbase, stability
  • Is it native .NET. This makes debugging/ modification easier.

If a commercial product will be used it should have at least the make the source available for better debugging/understanding.

In detail MapWindow vs SharpMap
At a more detailed level both MapWindow and SharpMap have been analysed in some testprojects. Both are at availbale as .NET object. although MapWindow still uses an OCX control.
MapWindow is used for the Sobek RR Prototype in DelftSHell. Sharpmap is used in a separate testproject.

Sharpmap versie 0.9 en 2.0 http://www.codeplex.com/SharpMap
Sharpmap has been in development for several years. It appears to be well designed and makes extensive support of external libraries (NTS, Proj.Net, GDAL/OGR). It is completely written in c#. The currenlty 'release' version is 0.9. There is beta 1 of version 2.0 available at http://www.codeplex.com/SharpMap/Release/ProjectReleases.aspx?ReleaseId=5035.
Sharpmap 2.0 promises some very nice new functionality as support for WPF and updateble features.
I did not manage to get 2.0 running due to different version of used libraries and modified interfaces so used 0.9 for this test.

The supplied DemoWinForm project was used as a starting point to try to add some very simple network editing functionality; add and move nodes and straight branches. Sharpmap used the NTS supplied interfaces what makes it, when getting used to, very easy to work with.

To implement a GIS editor speed is very important. Most GIS component/application support shape editing but it is often not very intuitive or fast. In the testproject the following approach was used.

1 Add an extra layer "testje". This layer will be used to store all the geometry objects of the network. In this test this layer is not connected to a shapefile.
2 Add an extra layer "tracking". Set this layer to invisible; see OnMouseMove. The tracking layer is used to a

OnMouseDown

  • Get MapImage as bitmap. Note that this will not include the invisible tracker layer.
  • Store trackers (= handles of selected objects). For now a node has 1 tracker, a branch 2, a start and an end tracker. In a final user interface you want trackers to be visible. These are often small black squares. To move an object update all trackers, to resize an object update a subset of trackers. Fortunately trackers can be 1 to 1 connected to a ICoordinate pair inside the IGeometry interface making moving objects very easy.

OnMouseMove

  • Update trackers with a coordinate shift
  • Draw MapImage
  • Draw trackerLayer

OnMouseUp

  • Update trackers and other administration such as links to other objects
  • Clear trackers
  • Refresh MapImage

Image 1 shows a screen dump of the test program. The user has added a number of nodes and branches to the map.

Image 2 shows a screen dump of the test program. The user has selected a node (note the light blue color) and is moving it around the map. The objects are also still visible at their original location. Connected branches a also updated. The performance is good since only the objects that are in de tracker layer (1 node trackers and 4 branch tracker; see the 5 in the status bar) are rendered. The country background color are changed because of another test.

Conclusion:
Working with sharpmap 0.9 proved to be very simple. For this test I did not have to implement my own drawing logic but this is easily possible -for at least IPoint derived objects - since the custom feature renderer supports GDI+ bitmaps and transparency.
Even if the number of good samples is limited the implementation is clear enough to make is easy to understand. Performance is notable worse than MapWindow, but version 2.0 might show improvement.
This is Sharpmap has potential but their project is poorly maintained.

MapWindow http://www.mapwindow.com/
MapWindow is now based on an OCX control. It has a large user base. The MapWindow desktop application is written in VB.NET but the internal engine used is an OCX control. They are working on a native .NET implementation in C# (see project MapWindow6Dev, seems to be in a very early stage).

The usage of MapWindow in the Sobek RR Prototype consists of the loading of a shape file, coloring layer features and on selection of features connect to a schematisation object (UrbanNode).

coloring of features
The coloring of features works different from SharpMap. In SharpMap you set a custom renderer for layer and a delegate is called when a feature has to be rendered. In MapWindow (at least in the DelftShell implementation) display properties (color, size) are set to the shapes in the shapefile after the shapefile is loaded:

for (int i = 0; i < shp.NumShapes; i++)
{
  string featureCode = shp.get_CellValue(shapeKeyIndex, i).ToString();
  Color  color = Color.FromArgb(0, featureRenderer.GetColor(featureCode));
  map.AxMapControl.OcxMap.set_ShapePointColor(layerIndex, i, (uint)(color.ToArgb()));
  map.AxMapControl.OcxMap.set_ShapePointSize(layerIndex, i, 15);
}

In sharpmap this is not possible, the IGeometry objects in a layer do not have properties like color or pointsize. I did not found a method in MapWindow to override the rendering; not in the docs or in the source MapWinGIS4Dev\Map.cpp (see CMap::OnDraw). There is event fired as part of the drawing process:
if (m_sendOnDrawBackBuffer)
FireOnDrawBackBuffer((long)m_backbuffer->m_hDC);
that you can use to draw on the map using GDI+.

Both methods have their pros and cons. The MapWindow method is optionally faster when rerendering. The SharpMap version gives more control to add custom made objects such as diagrams. With MapWindow you have more control, in addition it is always possible to grab a Graphics (Control.CreateGraphics) object from a control in .NET.

selection of features
The selection of features in a layer in MapWindow and SharpMap are much alike.

MapWindow
MapWinGIS.Extents ex = new MapWinGIS.Extents();
.. add points to ex object
object result = null;
bool success = shp.SelectShapes(ex, 0, MapWinGIS.SelectMode.INCLUSION, ref result);

SharpMap
IEnvelope Envelope = GetEnvelope(WorldPos, ImagePos);
.. add points to Envelope object
Collection<IGeometry> selection = userLayer.DataSource.GetGeometriesInView(Envelope);

MapWindow returns an array of shape indices in result and SharpMap returns an collection of IGeometry objects. Thus although almost similar Sharpmap is nicer to use.

conclusion
When considering the points named in the introduction the following table shows were SharpMap versus MapWindow scores better/worse.

head1

SharpMap

MapWindow

well designed

1

0

performance

0

1

portablility

1

0

userbase

0

1

native .NET

1

0

Both products claim to release a major new version in the near future. For development now I would prefer SharpMap but both its performance and small userbase/project management (working version 2.0) are serious problems.

Multi Object Property Editor

In order to support Multi Object editing in Delft Shell an extended property editor called FilteredPropertyGrid has been added to the DelftShell UI.

Multi Object Editing in Visual Studio
The standard System.Windows.Forms.PropertyGrid supports editing of multiple objects by exposing a SelectedObjects property. This functionality is also found in Visual Studio 2005. In the image below you can see 3 different views of the grid when editing a form using the form editor.

figure 1 - Some sample views from the multi object property editor in Microsoft Visual Studio

PropertyGrid analyses the objects set to SelectedObjects and makes the following adjustments:

  • Show empty property value if the values of the different objects are different (See blue boxes in image)
  • Remove all properties that are not found in all selected objects (see column 2 and 3 in image)
  • It removes the Name property if more than 1 object is selected. This is probably an addition from Visual Studio.

In Visual Studio an extra combo box is added that shows all objects on the form (see green boxes). A user can also select a control via this combo box (as opposed to clicking on the form). This is almost the desired behaviour that we want in DelftShell but instead of indivual objects we want categories of objects. When editing a schematisation the collection of all individual objects is probably too large.

FilteredPropertyGrid
FilteredPropertyGrid extends the functionality of the PropertyGrid by extra support for objects of different types. The default behaviour of the PropertyGrid is to show only the common properties of the objects in the selectedObjects array. In some cases it is desirable to add an extra filter for objects types.
An example were this behaviour is requested is the 1d schematisation editor. The user selects the objects in the GIS oriented view by tracking a rectangle. The objects inside the rectangle are set as selectedObjects in a propertygrid. In many cases the objects in the selection rectangle will be of different types; this results in a very limited subset of shared properties. Typically a user is only interested in river profiles or culverts. This FilteredPropertyGrid user control automatically makes a subdivision of the different types in the selectedObjects array and offers the user the chance to make a selection via a combobox.

The combobox at the top of the FilteredPropertyGrid tries to mimic the behaviour of the combobox in Visual Studio. If 1 object is selected the Id is shown bold followed by the type description.

figure 2 - Some sample views from the multi object property editor in DelftShell and a RR schematisation. Note that this model only has object of type string or UrbanNode.

To Do/ Discussion

  • Add option to add indivual objects to the combobox like visual studio.
  • Remove the Id property if more than 1 object is selected.
  • The functionality of the original PropertyGrid class in Delft Shell (keyboard support) has not yet been copied to FilteredPropertyGrid.
  • Add a icon/symbol for each type of object in the combobox to enable the user to respond/select more quickly.

Notes:

  • This is not a replacement for the Multiple Data Editor found in Sobek. This editor uses a table to show multiple objects. This editor allows individual editing of object and multi-editing by selecting ranges in the grid. Some objects are subdivided for to keep it comprehensible (e.g. Flow - Cross Section is split in Cross Section, Friction and Initial Values) This functionality - if needed - can possibly be best implemented by using (custom generated) DataTables bound to a DataGrid.
Undo-redo

I've been looking for a nice way to implement the undo redo functionality. To do a simple undo redo you need to use the command pattern. We already have this defined in the interface. See Command pattern and ICommand interface.
To give users more room for mistakes you will want to use at least a buffered or multilevel undo-redo functionality. Therefor we also will use the memento pattern. See pattern and examples .

I also found a nice example of a tree based undo-redo functionality in the editor e. This is the windows version of textmate. See also attached screenshot.