ThinkGeo.com    |     Documentation    |     Premium Support

The need for Speed!

Hi Guys,


Here’s a little app that plots a couple of GreatCircles to a common point and allows you to drag the common point around in real time.  Basically click and then drag the point between the two GreatCircles.


The version 2 is infinitely faster than the Version 3, how do we get the same speed of drawing from version 3?

Version 2



using System;
using System.Windows.Forms;
using MapSuite;
using MapSuite.DesktopEdition;
using MapSuite.Geometry;

namespace TG2
{
    public partial class Form1 : Form
    {
        private PointShape gcOneStart;
        private PointShape gcTwoEnd;
        private PointMapShape target;
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            map1.MapUnit = MapLengthUnits.DecimalDegrees;
            Layer worldLayer = new Layer(@"..\..\SampleData\SampleData\World\cntry02.shp", true); worldLayer.ZoomLevel01.GeoStyle = GeoAreaStyles.Country1; worldLayer.ZoomLevel01.ApplyUntilZoomLevel = ApplyUntilZoomLevel.ZoomLevel18; map1.Layers.Add(worldLayer);
            gcOneStart = new PointShape(0, 60.0);
            gcTwoEnd = new PointShape(-80.0, 11.0);
            target = new PointMapShape(new PointShape(-40, 20.0));
            target.ZoomLevel01.GeoStyle = GeoPointStyles.City1;
            target.ZoomLevel01.ApplyUntilZoomLevel = ApplyUntilZoomLevel.ZoomLevel18;
            map1.MapShapes.Add(target);
            PlotGreatCircles();
            map1.Mode = ModeType.Selection;
            map1.CurrentExtent = worldLayer.Extent;
        }

        private void PlotGreatCircles()
        {
            if (map1.MapShapes.Count > 1)
            {
                map1.MapShapes.RemoveAt(1);
                map1.MapShapes.RemoveAt(1);
            }
            LineMapShape gcFrom = new LineMapShape();
            gcFrom.Shape = gcOneStart.GetShortestLineTo(map1.MapUnit, target.BaseShape);
            gcFrom.Symbols.Add(new LineSymbol(new GeoPen(GeoColor.KnownColors.Black)));
            gcFrom.Dragable = false;
            map1.MapShapes.Add(gcFrom);
            LineMapShape gcTo = new LineMapShape();
            gcTo.Shape = gcTwoEnd.GetShortestLineTo(map1.MapUnit, target.BaseShape);
            gcTo.Symbols.Add(new LineSymbol(new GeoPen(GeoColor.KnownColors.Black)));
            gcTo.Dragable = false;
            map1.MapShapes.Add(gcTo);
        }

        private void map1_MapShapesDrag(BaseMapShapeCollection MapShapes)
        {
            PlotGreatCircles();
        }
    }
}

Version 3



using System;
using System.Windows.Forms;
using ThinkGeo.MapSuite.Core;
using ThinkGeo.MapSuite.DesktopEdition;

namespace TG3
{
    public partial class Form1 : Form
    {
        private readonly PointShape gcOneStart;
        private readonly PointShape gcTwoEnd;
        private PointShape targetPoint;
        private readonly InMemoryFeatureLayer lineLayer;

        public Form1()
        {
            InitializeComponent();
            winformsMap1.MapUnit = GeographyUnit.DecimalDegree;
            ShapeFileFeatureLayer worldLayer = new ShapeFileFeatureLayer(@"..\..\SampleData\Data\Countries02.shp");
            worldLayer.ZoomLevelSet.ZoomLevel01.DefaultAreaStyle = AreaStyles.Country1;
            worldLayer.ZoomLevelSet.ZoomLevel01.ApplyUntilZoomLevel = ApplyUntilZoomLevel.Level20;
            lineLayer = new InMemoryFeatureLayer();
            lineLayer.ZoomLevelSet.ZoomLevel01.DefaultLineStyle = LineStyles.Equator1;
            lineLayer.ZoomLevelSet.ZoomLevel01.DefaultPointStyle = PointStyles.City1;
            lineLayer.ZoomLevelSet.ZoomLevel01.ApplyUntilZoomLevel = ApplyUntilZoomLevel.Level20;
            targetPoint = new PointShape("POINT(-40 20)");
            gcOneStart = new PointShape("POINT(0 60)");
            gcTwoEnd = new PointShape("POINT(-80 11)");
            lineLayer.InternalFeatures.Add("Target", new Feature(targetPoint));
            lineLayer.InternalFeatures.Add("GCOne", new Feature(gcOneStart.GetShortestLineTo(targetPoint, GeographyUnit.DecimalDegree)));
            lineLayer.InternalFeatures.Add("GCTwo", new Feature(targetPoint.GetShortestLineTo(gcTwoEnd, GeographyUnit.DecimalDegree)));
            winformsMap1.StaticOverlay.Layers.Add("WorldLayer", worldLayer); winformsMap1.DynamicOverlay.Layers.Add("LineLayer", lineLayer); winformsMap1.MapBackground.BackgroundBrush = new GeoSolidBrush(GeoColor.GeographicColors.ShallowOcean);
            winformsMap1.CurrentExtent = new RectangleShape(-180.0, 83.0, 180.0, -90.0);
            winformsMap1.Refresh();
        }

        private void winformsMap1_MouseMove(object sender, MouseEventArgs e)
        {
            if (winformsMap1.EditOverlay.TrackMode == TrackMode.Edit && (e.Button & MouseButtons.Left) == MouseButtons.Left)
            {
                lineLayer.InternalFeatures.Clear();
                targetPoint = ExtentHelper.ToWorldCoordinate(winformsMap1.CurrentExtent, e.X, e.Y, winformsMap1.Width, winformsMap1.Height);
                lineLayer.InternalFeatures.Add("Target", new Feature(targetPoint)); lineLayer.InternalFeatures.Add("GCOne", new Feature(gcOneStart.GetShortestLineTo(targetPoint, GeographyUnit.DecimalDegree)));
                lineLayer.InternalFeatures.Add("GCTwo", new Feature(targetPoint.GetShortestLineTo(gcTwoEnd, GeographyUnit.DecimalDegree)));
                winformsMap1.RefreshDynamic();
            }
        }

        private void winformsMap1_MapClick(object sender, MapClickEventArgs e)
        {
            winformsMap1.EditOverlay.EditLayer.InternalFeatures.Add(new Feature(targetPoint));
            winformsMap1.EditOverlay.EditSettings.IsDraggable = true;
            winformsMap1.EditOverlay.EditSettings.IsReshapable = false;
            winformsMap1.EditOverlay.EditSettings.IsResizable = false;
            winformsMap1.EditOverlay.EditSettings.IsRotatable = false;
            winformsMap1.EditOverlay.TrackMode = TrackMode.Edit;
        }
    }
}

Regards


John



Hi Guys,


And some timings that may help:-


V3

Timings average for 200 hits

AVG(ms)

        private void winformsMap1_MouseMove(object sender, MouseEventArgs e)

        {

 0.001    if (winformsMap1.EditOverlay.TrackMode == TrackMode.Edit && (e.Button & MouseButtons.Left)  == MouseButtons.Left)

          {

 0.004      lineLayer.InternalFeatures.Clear();

 0.006      targetPoint = ExtentHelper.ToWorldCoordinate(winformsMap1.CurrentExtent, e.X, e.Y, winformsMap1.Width, winformsMap1.Height);

 0.016      lineLayer.InternalFeatures.Add("Target", new Feature(targetPoint));

 2.327      lineLayer.InternalFeatures.Add("GCOne",new Feature(gcOneStart.GetShortestLineTo(targetPoint, GeographyUnit.DecimalDegree)));

 2.301      lineLayer.InternalFeatures.Add("GCTwo",new Feature(targetPoint.GetShortestLineTo(gcTwoEnd, GeographyUnit.DecimalDegree)));

27.340      winformsMap1.RefreshDynamic();

          }

        }



V2

Timings average for 200 hits

AVG(ms)

        private void PlotGreatCircles()

        {

 0.000    if (map1.MapShapes.Count > 1)

          {

 0.000      map1.MapShapes.RemoveAt(1);

 0.009      map1.MapShapes.RemoveAt(1);

           }

 0.000    LineMapShape gcFrom = new LineMapShape();

 0.002    gcFrom.Shape = gcOneStart.GetShortestLineTo(map1.MapUnit, target.BaseShape);

 1.555    gcFrom.Symbols.Add(new LineSymbol(new GeoPen(GeoColor.KnownColors.Black)));

 0.011    gcFrom.Dragable = false;

 0.000    map1.MapShapes.Add(gcFrom);

 0.002    LineMapShape gcTo = new LineMapShape();

 0.001    gcTo.Shape = gcTwoEnd.GetShortestLineTo(map1.MapUnit, target.BaseShape);

 1.543    gcTo.Symbols.Add(new LineSymbol(new GeoPen(GeoColor.KnownColors.Black)));

 0.009    gcTo.Dragable = false;

 0.000    map1.MapShapes.Add(gcTo);

        }


It does seem that the bottleneck is in RefreshDynamic, just the one layer and 3 features...


John




 John, 
  
 You didn’t count the real drawing time for V2.  I suggest you to raise up the AfterMapDraw event and check the elapse time there, that will be accurate.  
  
 Thanks, 
  
 Ben

Hi Ben, 
  
 Whenever I see something that is apparently slower than it should be I generally run a performance profiler on the app to try and isolate where the time is going, overall elapsed time is more difficult to measure accurately if you are just waggling the mouse around IMHO. 
  
 You just have to run the two apps side by side to see the difference, the purpose of posting the timings was to try and show what the performance profiler thinks the bottleneck in V3.   
  
 This appears to be the 27.34ms that it takes each time to refresh the one and only dynamic overlay that just contains the 3 features, a point and two lines. 
  
 Regards 
  
 John 


You are right John. After a test and review the codes, we found some performance issues in the source. I’ve report it as a bug in our Tracking system and sorry for the inconvenience for now. We will review the performance before release the next version.  
  
 Ben

Hi Ben, 
  
 Thanks for that, I thought there was something going on somewhere. 
  
 Incidentally whilst I was waiting from an answer I thought I would have a bash with the GdiPlusGeoCanvas and do the drawing on a bitmap.  Speed is very good until you try using the DrawEllipse methods which seem very slow compared to DrawLine, DrawArea etc.  
  
 Don’t know if that’s related to the other problems or not, any thoughts? 
  
 Regards 
  
 John 


John, 
  
 We did some test with GdiPlusGeoCanvas and found that compared with drawing area, the time of drawing ellipse is doubled. I am not sure whether you have the similar results but if yes, I think that makes sense as Gdi+ takes more time to draw an ellipse than a rectangle. If the difference is much more than that, could you please provide a concrete sample so we can recreate it? 
  
 Thanks, 
  
 Ben 
  
  
  
   
  
 Thanks, 
  
 Ben 


Hi Ben,


Here are some timings for drawing a 'target indicator', averaged over 450 passes.  Two methods of drawing the same target object one using the GeoCanvas and the other using direct GDI+ :-


AvgMs

0.006   Graphics nG = Graphics.FromImage(nBmp);

0.016   gc.BeginDrawing(nBmp, winformsMap1.CurrentExtent, GeographyUnit.DecimalDegree);

0.972   gc.DrawEllipse(targetOut, 12, 12,aPtOutlinePen, aPtBrush,DrawingLevel.LevelTwo);

0.023   gc.DrawEllipse(targetIn, 4, 4, aPtFill,DrawingLevel.LevelTwo);

3.469   gc.EndDrawing();

0.001   nG.SmoothingMode = SmoothingMode.AntiAlias;

0.001   nG.CompositingQuality = CompositingQuality.HighQuality;

0.080   nG.DrawEllipse(Pens.Red, e.X - 6, e.Y - 6, 12, 12);

0.030   nG.FillEllipse(aFillBrush, e.X - 6, e.Y - 6, 12, 12);

0.020   nG.DrawEllipse(Pens.Red, e.X - 2, e.Y - 2, 4, 4);

0.018   nG.FillEllipse(Brushes.Red, e.X - 2, e.Y - 2, 4, 4);

0.003   nG.Dispose();



Adding the timimgs up:-


DrawEllipse = 0.995 against GDI+ of 0.148, DrawEllipse does seem alot slower...


Regards


John


 




John, 
  
 Thanks very much for your codes, we will work on it and let you know what we find out. 
  
 Ben

John, 
  
 We found this speed issue is caused by the Quality / Speed settings. 
  
 In Graphic the default settings are: 
 SmoothingMode.None 
 CompositingQuality.Default; 
  
 But the default values in the GdiPluseGeoCanvas are: 
 SmoothingMode.HighQuality 
 CompositingQuality.HighSpeed 
  
 Once you change the GdiPluseGeoCanvas’ settings same as GDI+, the drawing time are almost the same. 
 
canvas.DrawingQuality = DrawingQuality.CanvasSettings;
canvas.SmoothingMode = SmoothingMode.None;
canvas.CompositingQuality = CompositingQuality.Default;
 
 Time different: DrawEllipse in Canvas: 0.195s; DrawEllipse with GDI+: 0.175s; DrawEllipse in Canvas with previous default settings: 0.705s 
  
 It makes sense that  GdiPluseGeoCanvas spends a little more time than GDI+ as it does some extra calculation. 
  
 Thanks, 
  
 Ben