Thursday, December 4, 2008

OpenStreetMap rendering in Silverlight part VI

I've spent some time looking at performance, using some of the tricks from Seema's blog - she also very helpfully sent me some helpful tips when trying to build applications with many complex shapes.

Changes to the version now include:
  • Speed/Memory improvement using feature clipping.
  • Tooltips tags for features, (mouse over a line or area to see)

The neeed for speed: performance investigation
This is where the ability to swap drawers has been helpful, I was able to create try out new ideas without having to worry about breaking the existing drawing system. One of the technique's I investigated was drawing using Silverlight's mini-language, this allows you to draw complex shapes using a LOGO like definition:

<Path Stroke="Black" Fill="Gray"Data="M 10,100 C 10,300 300,-200 300,100" />

Since I need to draw this dynamically I need to load the Path.Data from a string:

StringBuilder sb = new StringBuilder();
sb.Append("<Path xmlns=\"\"");
sb.Append(" xmlns:x=\"\"");
sb.Append(" Data=\"");

ShowWay(sb, tileBorder);
foreach (var gpp in quadrant.GeoPolyPoints)
DrawFeature(sb, gpp);


GeometryGroup gg = new GeometryGroup();
Path uiPath = (Path)System.Windows.Markup.XamlReader.Load(sb.ToString());

using a helper function to turn a set of points into a set of draw instructions:

private void DrawFeature(StringBuilder sb, GeoPolyPoint gpp)

   bool isFirst = true;
   foreach (GeoPoint geoPoint in gpp.GeoPoints)
    int y = geoPoint.GetScaledX(viewport);
    int x = geoPoint.GetScaledY(viewport);

    if (isFirst)
     isFirst = false;
     sb.Append(" M ");
     sb.Append("L ");

   if (gpp.PolyConstruct == PolyConstruct.Polygon)
    sb.Append("Z ");

   return count;

I then timed this against my previous attempt, and found hardly any difference - and I also tried create PathFigures, Geometries etc - these were painfully slow (mainly due to the need to create lots of LineSegment objects). So I am sticking with creating Shapes (Polygon and Polyline) as both of these take a collection of points, so no need to create lots of lines.

I then had a look at Seema's blog again and had a play with
<param name="enableRedrawRegions" value="true" />

This showed that I was updating a vast region of the screen, this seems to be related to clipping not behaving as expected - I've asked Seema for some clarification on this. However I also knew my draw routine was pretty lazy - it did not check when drawing a feature if it would even be visible on the tile (for example to the left of the tile).

I was relying on Silverlight clipping to do all the work. So I put in a simple check to only draw features that actually appear in the tile. This reduced the New Malden draw set from 26,038 features to 7,979. This means my code is drawing 3 times less shapes - a massive memory and speed improvement (10% faster), but more importantly ~20,000 shapes that Silverlight does not have to calculate, rotate, clip for no visible effect.

Once I get an answer from Seema on the clipping issue things should get even quicker!

1 comment:

Nguyễn Ngọc Tú said...


to cover String To PathGeometry