Author: Dudarev Roman, Ph.D. of Engineering
Introduction
Due to the wide spreading of computer technologies, which give an option to position objects on the Earth surface (i.e. GPS, GLONASS), there is a unique possibility to create computer systems that can be used in day-to-day functioning of an organization without considerable expenses. There are vehicle tracking systems of public transport and medical organizations and also navigators for private cars. All these systems have the same feature: there is a cartographical module on the basis. The module displays an interactive map and gives an option to track the set object. Our company has also developed the vehicle control system for organizations. The system consists of the several parts. Client module (developed in C# .Net 3.5 WPF) displays the map and the vehicles. Look&Feel of the Client Module is given in the picture below.
Look&Feel of the Enterra GIS Client Module.
In the article we will discuss only questions concerning the display of cartographical objects. The example of the article is a very simple geoinformation system with two levels of scaling without loading areas that are not within the visibility scope, without search, though with an option of moving the map and getting the information on the object. The questions related to tracking vehicles movements, their display on the map, etc. won’t be discussed here. They can be concluded from the given material without assistance.
Data Warehouse
At the present time, shp format (ESRI Shapefile) is one of the most widely spread formats for storing cartographical information. However, such files are not handy, because mechanisms of objects search and select by the criteria are not defined in the standard. Though the developers and third-party companies attempt to build special index files for such purposes. One of the freeware components is SharpMap. It enables you to fulfill all the necessary actions when working with geoinformation data. However, the tests showed that the capacity of the consumed memory and the performance leave much to be desired. Moreover the component is spread with GPL license which isn’t desirable to be used for commercial development, because it is necessary to deliver the source code when changes are to be made.
Geoinformation system supposes options of scaling the map, movement of the cartographical objects and search for them, which leads to the number of technical problems, i.e. download of yet not-loaded areas, upload of the irrelevant information, etc. Pre-tests showed unacceptable performance when using SharpMap library. It was decided to import cartographical information to a DBMS. On the primary step it was decided to use Microsoft SQL Server Compact Edition 3.5 as a DBMS. The DBMS is embedded into the application, is included into VisualStudio 2008 delivery package, is has no limitation for distribution, provides high performance, can be easily replaced to any other DBMS with a higher performance (i.e. Microsoft SQL Server 2008). This solution is very flexible, because it enables to segment the software product. It means that the version of the product that isn’t oriented to be used in the network can use local data bases with maps, whereas in the network version all the maps are stored on the central Server (that have a number of advantages, i.e. easy update). After the procedure of importing .shp files to DBMS has been completed and we got the first results of displaying the cartographic information, we desired to compare different embedded DBMS by the efficency criteria. It was rather an easy task, because all the requests for objects selection were made on standart SQL. We tested Firebird, SqLite, DBF, MSSQL. The results are given in the table 1.
Table 1. The comparasion of different DBMS by an average performance.
Position | DBMS |
---|---|
1 | MSSQL 2008 on the dedicated server |
2 | MSSQL 2008 on the one workstation with a client software |
3 | DBF files |
4 | MSSQL CE 3.5 |
5 | Firebird Embedded |
6 | SQLite |
Details of the tests and the discussion of results go beyond the article aim. Attentive readers can notice that we didn’t test DBMS Oracle and can test it themselves. Within the bounds of the article we will discuss the realization on MSSQL CE 3.5.
For realization of our system, one table with the shown below structure is enough.
Table 2. Database Table Structure.
Name | Data type | Description |
---|---|---|
Id | Int | Primary key |
Name | Nvarchar(255) | Object name |
Description | Nvarchar(255) | Object description |
Data | Image | Data |
ObjectType | TinyInt | Object type |
ElementType | TinyInt | Type of object elements |
Zoom | TinyInt | Scale |
ObjectId | Int | Object identifier |
ElementId | Int | Object element identifier |
Complex | Bit | Complex element feature |
Object selection for displaying the necessary zoom can be easily executed by the following request:
select ObjectId, ObjectType, Name as ObjectName, ElementType, Data, Description, Complex, ElementId from MapObject where @Zoom = Zoom order by ObjectType
Details on this functionality is implemented can be viewed in the attached example.
Additional functions can be easily implemented by changing this table and probably by adding a few new ones. In the real application we have 5 tables with the 3 of them designated for object search.
Choosing a drawing means
WPF uses vector graphics as a drawing format. It means that it is saved in a special way and sent the instruction kit to output subsystem. The kip describes how the drawing should be completed by using graphical primitives (i.e. line kit, curve kit, etc.). The method easily enables scaling without losing the quality.
There are two drawing means in WPF. The objects are Drawing and Shape. Shape objects are presented by Rectangle, Ellipse and other primitive kits. They can be easily used in xaml markup, supports antialiasing and events processing, however, the system performance, while drawing of a large number of such objects leaves much to be desired.
Drawing Visual objects provide maximum performance when drawing objects, pictures or texts. It is reached due to the fact that they don’t support arranging and events, additionally they can’t be described in the markup. It is necessary to create a container (inherited from FrameworkElement class) for their usage. VisualCollection class should be used for storing DrawingVisual objects. For guaranteeing container arranging it is necessary to overlay two simple functions.
public class BaseRenderer : FrameworkElement
{
//member for optimize
protected VisualCollection ObjectChildrenList;
protected override int VisualChildrenCount
{
get
{
return ObjectChildrenList.Count;
}
}
protected override Visual GetVisualChild(int index)
{
return ObjectChildrenList[index];
}
}
Created DrawingVisual objects should be placed into the ObjectChildrenList collection so that the WPF graphical subsystem can execute thier drawing. Note: to maximize the performance it is necessary to use the Add function and add the objects sequentially. Otherwise, for example, the Insert function recalculates the visual parent of all elements with the index higher then the index of insertion. The Remove function is analogous. That is why it is better to clear the collection completely. Make sure of this by using any profiler, for example, jetBrance or by reviewing the source code using, for example, Reflector. Thus, MapDrawingVisual object is created at first. The object is an adesendent from the DrawingVisual object and has a link to the business-object of the map for executing HitTesting procedure (hit check). Then there is a style for the object drawing, geometry is created and the drawing is executed.
private void CreateDrawing(MapElement element, bool closed)
{
MapDrawingVisual drawingVisual = new MapDrawingVisual();
drawingVisual.MapObject = element.MapObject;
DrawingContext drawingContext = drawingVisual.RenderOpen();
GeometryStyle style = StyleManager.GetStyle(element);
Geometry geometry = CreateBaseGeometry(element, closed);
drawingContext.DrawGeometry(style.Brush, style.Pen, geometry);
drawingContext.Close();AddDrawingVisual(drawingVisual);
}
private void AddDrawingVisual(DrawingVisual drawingVisual)
{
ObjectChildrenList.Add(drawingVisual);
}
Drawing of simple graphical primitives is executed with the help of StreamGeometry class, which gives the maximum performance. Besides the performance can be higher, if it is “freezed”. In most cases it makes sense, because our map objects are not expected to be modified.
private static Geometry CreateGeometry(Point[] points, bool closed, bool freeze)
{
Geometry geometry = new StreamGeometry();
using (StreamGeometryContext ctx = ((StreamGeometry)geometry).Open())
{ctx.BeginFigure(points[0], true, closed);
ctx.PolyLineTo(points, true, false);}
// Freeze the geometry (make it unmodifiable)
// for additional performance benefits.
if (freeze)
{geometry.Freeze();
}
return geometry;
}
Brushes, pens, text, object movements and other questions of optimization
Due to the application specific it is needed to execute the object drawing of different types: houses, rivers, roads, etc. They are great in number. Unfortunately, a simple solution which comes to mind (using one brush object for one object type), gives a significant slowdown to the system. And we had to think over the optimal way to create brushes and pens to increase performance. New brush creation right before it is used also didn’t lead to a significant performance increase. Maximum effect was reached by using a brush or pen as a template and getting copies from them to use with the help of GetCurrentValueAsFrozen() method. The code given below is simplified.
public GeometryStyle Clone()
{
return new GeometryStyle
{Brush = (Brush)Brush.GetCurrentValueAsFrozen(),
Pen = (Pen)Pen.GetCurrentValueAsFrozen()};
}
In the article’s example we didn’t give the realization object signatures, however, this question is worth taking about. In our application we signed objects on the background of semitransparent rectangle. Its size depends on the text size. But the logical solution to use width and height of the FormattedText object for rectangle drawing and then text drawing isn’t optimal. Recalculation happens repeatedly. To avoid it, it is first required to execute text drawing, rectangle drawing and then add the created DrawingVisual objects to the collection of the visual objects in the specified order.
Map movement with the mouse is quite an easy task and in fact consists of the correct calculation of the TranslateTransform class parameters executing objects shifts. By using transformations a number of sophisticated effects can be created, i.e. changing the viewing angle, map rotation, etc. But the performance leaves much to be desired.
Besides we noticed the substantial performance decrease when drawing lines. This is a serious problem, because there must be drawing of such cartographical objects like roads, rivers, region borders, etc. Task solving wasn’t successful. The only solution to make the drawing faster is a recommendation to use only integer number for pen thickness. Another obscure fact is that when antialiasing is disabled the performance decreases.
RenderOptions.SetEdgeMode(this, EdgeMode.Aliased);
I hope that there are readers who can explain such system behavior and ways to increase the performance. When creating WPF applications it is useful to download tools for performance profiling, i.e. WPF Perforator, and also get acquainted with Microsoft recommendations that can be found in MSDN.
Hit testing
Hit testing in the described event is quite an easy task and can be realized in few lines. Actually this simple class has been written for this purpose.
MapDrawingVisual:
public class MapDrawingVisual : DrawingVisual
{
public MapObject MapObject;
}
The whole code of the hit testing procedure is given below:
public MapInfo GetInfo(Point point)
{
MapInfo info = new MapInfo();
VisualTreeHelper.HitTest(Viewer, null,
delegate(HitTestResult result)
{
if (result.VisualHit is MapDrawingVisual
&& ((MapDrawingVisual)result.VisualHit).MapObject != null)
{MapObject mapObject =
((MapDrawingVisual)result.VisualHit).MapObject;
if (!String.IsNullOrEmpty(mapObject.ObjectName))
{
info.Name = mapObject.ObjectName;
info.Description = mapObject.Description;
return HitTestResultBehavior.Stop;
}}
// Stop the hit test enumeration of objects in the visual tree.
return HitTestResultBehavior.Continue;
}
, new PointHitTestParameters(point));
return info;
}
Summary
I would like to mention that WPF simplifies application development. Due to WPF, animations, compositing, hit testing and other rather complicated tasks are now realized with few lines. It is reasonable to use WPF if it isn’t needed to sort out a large amount of visual objects. Using WPF helps to reach rather interesting effects. When drawing rather big amount of the complex form objects and especially lines, it is necessary to think over the question of performance. There is an option to use WPF if computers where the application is supposed to be used are high-performance and it is necessary to think of another alternative if the computer isn’t high-end.
For example, the application can’t work on the following computer Celeron 2000, GeForce 4 MX400 video card, 1 Gb memory, though you can play computer games, i.e. Counter Strike, on it.
Do you like the article and want us to develop your project? Feel free to contact us here!
This entry was posted on Tuesday, January 13th, 2009 at 12:27 pm and is filed under Architecture, WPF.