This project is read-only.

DotSpatial "TrackingLayer"?

Mar 22, 2012 at 5:56 PM

Is there existing capability in DotSpatial like the TrackingLayer in MapObjects?  The MapObjects TrackingLayer allows temporary, but persistent, shapes and labels to be applied to the map and is used for things like showing gps position.  Additionally, individual tracking event features can be added/removed/modified/managed to the “collection” of all events in the TrackingLayer.

I've browsed through and searched through the help file for all the synonyms I could think of for "tracking" and "event" and "temporary" without success.

Mar 22, 2012 at 8:28 PM

In our app, we had to manage much of it ourselves.  It's been a while since I looked at this, but a quick look refreshed my brain a little.  We took this approach:

- Hooked on the Map.BufferChanged event.  I think this event gets called at the end of the map draw cycle before actually displaying the in-memory bitmap to the window.  But I see a comment that says it gets called twice....  Whatever you draw here will stay in the map until the next draw (it will persist between map refreshes just responding to paint events (i.e. window uncover events...))

- Created a Graphics object (var bufferDevice = Graphics.FromImage(Map.BufferedImage);) which allows you to draw to the in-memory bitmap

- Drew to the Graphics object to our hearts content.

We created our own instances of DotSpatial Symbolizers to use in the drawing code.  As part of that, we had a need to create our own MapArgs...

MapArgs myMapArgs = new MapArgs(Map.MapFrame.ClientRectangle, Map.MapFrame.ViewExtents, bufferDevice);

We ended up implementing our own little DSDrawingUtil that I have included below...

First a snippet that uses the DSDrawingUtil to draw some points inside the BufferChanged event:

                DSDrawingUtil _dsDrawUtil = new DSDrawingUtil();
                DotSpatial.Symbology.IPointSymbolizer myPointSymbolizer = DSDrawingUtil.CreatePointSymbolizer(Convert.ToChar(241), "WingDings", myArrowColor, dArrowSize, 0.0);

                Graphics bufferDevice = Graphics.FromImage(Globals.GeoCue.dsMap.BufferedImage);
                MapArgs myMapArgs = new MapArgs(Globals.GeoCue.dsMap.MapFrame.ClientRectangle, Globals.GeoCue.dsMap.MapFrame.ViewExtents, bufferDevice);

                _dsDrawUtil.BeginDrawingPoints(myMapArgs);

                for (int i = 0; i < GeoAnalysis.m_alArrowInfo.Count; i++)
                {
                    myPointSymbolizer.Symbols[0].Angle = -((ArrowInfo)GeoAnalysis.m_alArrowInfo[i]).m_dAzimuth;
                    _dsDrawUtil.Symbolizer = myPointSymbolizer;
                    _dsDrawUtil.DrawPoint(((ArrowInfo)GeoAnalysis.m_alArrowInfo[i]).m_dX, ((ArrowInfo)GeoAnalysis.m_alArrowInfo[i]).m_dY, myMapArgs);
                }

                _dsDrawUtil.EndDrawingPoints();

When drawing lines or polygons, you don't have to have an EndDrawingxxx() method since that method was added for performance reasons regarding the GDI transforms to rotate bitmaps and what not.

HTH,

Kyle

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Text;
using DotSpatial.Controls;
using DotSpatial.Data;
using DotSpatial.Symbology;
using DotSpatial.Topology;
using Point = System.Drawing.Point;
using PointShape = DotSpatial.Data.PointShape;

namespace NIIRS10.GeoCueDotSpatial
{
    /// <summary>
    /// Class for drawing various Dot Spatial geometries with symbology
    /// </summary>
    public class DSDrawingUtil
    {
        const int X = 0;
        const int Y = 1;

        private IFeatureSymbolizer _symbolizer = null;

        #region PointDrawingStuff
        private Matrix _origTransform = null;
        private MapArgs _mapArgsForPointsOnly;
        private Bitmap _pointSymbolBitmap;
        #endregion
        private Brush _brush = null;
        private Pen _pen = null;
        private double _opacity = 1.0;

        /// <summary>
        /// Set or get the pen used to draw lines and polygon outlines.  This value is computed when setting the Symbolizer.  You may override the Symbolizer by setting this value directly. 
        /// </summary>
        public Pen Pen
        {
            get { return _pen; }
            set
            {
                if(null != _pen)
                    _pen.Dispose();
                _pen = value;
            }
        }

        /// <summary>
        /// Set or get the brush used to fill polygons.  This value is computed when setting the Symbolizer.  You may override the Symbolizer by setting this value directly.
        /// </summary>
        public Brush Brush
        {
            get { return _brush; }
            set
            {
                if(null != _brush)
                    _brush.Dispose();
                _brush = value;
            }
        }

        /// <summary>
        /// Set or get the symbolizer.
        /// If drawing polygons, this should be a PolygonSymbolizer.  
        /// If drawing linestrings, this should be a LineSymbolizer.  
        /// If drawing points, this should be a PointSymbolizer.
        /// </summary>
        public IFeatureSymbolizer Symbolizer
        {
            get { return _symbolizer; }
            set
            {
                _symbolizer = value;
                IPolygonSymbolizer pSym = _symbolizer as IPolygonSymbolizer;
                if (null != pSym)
                {
                    Pen pen = null;
                    if(pSym.OutlineSymbolizer.Strokes.Count == 1)
                    {
                        pen = pSym.OutlineSymbolizer.Strokes[0].ToPen(1);
                        if (_opacity != 1.0)
                        {
                            pen.Color = ColorWithOpacity(pen.Color);
                        }
                    }

                    if(null == pen)
                    {
                        pen = new Pen(ColorWithOpacity(pSym.OutlineSymbolizer.GetFillColor()), (float)pSym.GetOutlineWidth());
                    }
                    Pen = pen;
                    Brush brush = null;
                    if(pSym.Patterns.Count == 1)
                    {
                        if(pSym.Patterns[0] is HatchPattern)
                        {
                            IHatchPattern hatchPattern = pSym.Patterns[0] as IHatchPattern;
                            brush = new HatchBrush(hatchPattern.HatchStyle, ColorWithOpacity(hatchPattern.ForeColor),
                                                   ColorWithOpacity(hatchPattern.BackColor));
                        }
                    }

                    if (pSym.GetFillColor().A != 0)
                    {
                        Brush = brush ?? new SolidBrush(ColorWithOpacity(pSym.GetFillColor()));
                    }
                    else
                    {
                        Brush = null;
                    }
                    return;
                }

                ILineSymbolizer lSym = _symbolizer as ILineSymbolizer;
                if(null != lSym)
                {
                    Pen pen = null;
                    if(lSym.Strokes.Count == 1)
                    {
                        pen = lSym.Strokes[0].ToPen(1);
                        if (_opacity != 1.0)
                            pen.Color = ColorWithOpacity(pen.Color);
                    }
                    Pen = pen ?? new Pen(ColorWithOpacity(lSym.GetFillColor()), (float)lSym.GetWidth());
                    return;
                }

                // Point Symbolizer Setup
                IPointSymbolizer ptSym = _symbolizer as IPointSymbolizer;
                double scaleSize = 1;
                if (ptSym.ScaleMode == ScaleMode.Geographic)
                    scaleSize = _mapArgsForPointsOnly.ImageRectangle.Width/_mapArgsForPointsOnly.GeographicExtents.Width;
                Size2D size = ptSym.GetSize();
                if(null != _pointSymbolBitmap)
                    _pointSymbolBitmap.Dispose();

                if (size.Width * scaleSize < 1 || size.Height * scaleSize < 1)
                {
                    _pointSymbolBitmap = null;
                    return;
                }

                _pointSymbolBitmap = new Bitmap((int)(size.Width * scaleSize) + 1, (int)(size.Height * scaleSize) + 1);
                Graphics bg = Graphics.FromImage(_pointSymbolBitmap);
                bg.SmoothingMode = ptSym.Smoothing ? SmoothingMode.AntiAlias : SmoothingMode.None;
                Matrix trans = bg.Transform;
                trans.Translate(((float)(size.Width * scaleSize) / 2 - 1), (float)(size.Height * scaleSize) / 2 - 1);
                bg.Transform = trans;
                Color symColor = ptSym.GetFillColor();
                if(_opacity != 1.0)
                    ptSym.SetFillColor(ColorWithOpacity(symColor));
                ptSym.Draw(bg, 1);
                ptSym.SetFillColor(symColor);
                bg.Dispose();
            }
        }

        /// <summary>
        /// Opacity to use when drawing
        /// </summary>
        public double Opacity
        {
            get { return _opacity; }
            set
            {
                if (_opacity != value)
                {
                    _opacity = value;
                    if(null != _symbolizer)
                        Symbolizer = _symbolizer;
                }
            }
        }

        private Color ColorWithOpacity(Color baseColor)
        {
            if (1.0 == _opacity)
                return baseColor;
            return Color.FromArgb((int)(baseColor.A * _opacity), baseColor);
        }

        /// <summary>
        /// Draw a polygon shape.  If the Graphics device has a clip region and the polygon extends outside the clip region, clip should be true.
        /// </summary>
        /// <param name="shape"></param>
        /// <param name="args"></param>
        /// <param name="shClip"></param>
        public void DrawPolygon(Shape shape, MapArgs args, SoutherlandHodgman shClip)
        {
            double[] vertices = shape.Vertices;
            ShapeRange shpx = shape.Range;
            double minX = args.MinX;
            double maxY = args.MaxY;
            double dx = args.Dx;
            double dy = args.Dy;
            
            for (int prt = 0; prt < shpx.Parts.Count; prt++)
            {
                PartRange prtx = shpx.Parts[prt];
                int start = prtx.StartIndex;
                int end = prtx.EndIndex;
                List<double[]> points = new List<double[]>();

                for (int i = start; i <= end; i++)
                {
                    double[] pt = new double[2];
                    pt[X] = (vertices[i * 2] - minX) * dx;
                    pt[Y] = (maxY - vertices[i * 2 + 1]) * dy;
                    points.Add(pt);
                }
                if (null != shClip)
                {
                    points = shClip.Clip(points);
                }
                List<Point> intPoints = DuplicationPreventer.Clean(points);
                if (intPoints.Count < 2)
                {
                    points.Clear();
                    continue;
                }
                Point[] pointArray = intPoints.ToArray();
                if(null != _brush)
                    args.Device.FillPolygon(_brush, pointArray, FillMode.Winding);

                args.Device.DrawPolygon(_pen, pointArray);
            }
        }

        /// <summary>
        /// Draw a linestring shape
        /// </summary>
        /// <param name="shape"></param>
        /// <param name="args"></param>
        public void DrawLinestring(Shape shape, MapArgs args)
        {
            DrawLinestring(shape, args, Rectangle.Empty);
        }

        /// <summary>
        /// Draw a clipped linestring
        /// </summary>
        /// <param name="shape"></param>
        /// <param name="args"></param>
        /// <param name="clipRect"></param>
        public void DrawLinestring(Shape shape, MapArgs args, Rectangle clipRect)
        {
            double minX = args.MinX;
            double maxY = args.MaxY;
            double dx = args.Dx;
            double dy = args.Dy;
            double[] vertices = shape.Vertices;

            bool clip = !clipRect.IsEmpty;
            ShapeRange shpx = shape.Range;
            for (int prt = 0; prt < shpx.Parts.Count; prt++)
            {
                PartRange prtx = shpx.Parts[prt];
                int start = prtx.StartIndex;
                int end = prtx.EndIndex;
                List<double[]> points = new List<double[]>();
                //Point previousPoint = new Point();
                for (int i = start; i <= end; i++)
                {
                    double[] pt = new double[2];
                    pt[X] = (vertices[i * 2] - minX) * dx;
                    pt[Y] = (maxY - vertices[i * 2 + 1]) * dy;
                    points.Add(pt);
                }
                List<List<double[]>> multiLinestrings;
                if (clip && !shpx.Extent.Within(args.GeographicExtents))
                {
                    multiLinestrings = CohenSutherland.ClipLinestring(points, clipRect.Left, clipRect.Top,
                                                            clipRect.Right, clipRect.Bottom);
                }
                else
                {
                    multiLinestrings = new List<List<double[]>> {points};
                }

                foreach (List<double[]> linestring in multiLinestrings)
                {
                    List<Point> intPoints = DuplicationPreventer.Clean(linestring);
                    if (intPoints.Count < 2)
                    {
                        points.Clear();
                        continue;
                    }
                    args.Device.DrawLines(_pen, intPoints.ToArray());
                }
            }
        }

        /// <summary>
        /// Draw a single point shape.  You must bracket DrawPoint calls with Begin/End DrawingPoints
        /// </summary>
        /// <param name="shape"></param>
        /// <param name="args"></param>
        public void DrawPoint(Shape shape, MapArgs args)
        {
            double[] vertices = shape.Vertices;
            DrawPoint(vertices[0], vertices[1], args);
        }
        /// <summary>
        /// Draw a multi-point shape. You must bracket DrawPoint calls with Begin/End DrawingPoints
        /// </summary>
        /// <param name="shape"></param>
        /// <param name="args"></param>
        public void DrawMultiPoint(Shape shape, MapArgs args)
        {
            double[] vertices = shape.Vertices;
            ShapeRange range = shape.Range;
            int start = range.StartIndex;
            int end = range.EndIndex();
            for(int i = start; i<end; i++)
            {
                DrawPoint(vertices[i * 2], vertices[i * 2 + 1], args);
            }
        }

        /// <summary>
        /// Draw a point at x,y. You must bracket DrawPoint calls with Begin/End DrawingPoints
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <param name="args"></param>
        public void DrawPoint(double x, double y, MapArgs args)
        {
            // Could be the point was too small to be drawn or we just have not been setup with a point symbolizer
            if (null == _pointSymbolBitmap)
                return;

            Point pt = new Point();
            pt.X = Convert.ToInt32((x - args.MinX) * args.Dx);
            pt.Y = Convert.ToInt32((args.MaxY - y) * args.Dy);
            Matrix shift = _origTransform.Clone();
            shift.Translate(pt.X, pt.Y);
            args.Device.Transform = shift;
            args.Device.DrawImageUnscaled(_pointSymbolBitmap, -_pointSymbolBitmap.Width / 2, -_pointSymbolBitmap.Height / 2);
        }

        /// <summary>
        /// Setup before drawing points
        /// </summary>
        /// <param name="args"></param>
        public void BeginDrawingPoints(MapArgs args)
        {
            _mapArgsForPointsOnly = args;
            _origTransform = args.Device.Transform;
        }

        /// <summary>
        /// Restore settings when finished drawing points.
        /// </summary>
        public void EndDrawingPoints()
        {
            RestorePointDrawingTransform();
        }

        /// <summary>
        /// Restore the transform on the graphics device
        /// </summary>
        public void RestorePointDrawingTransform()
        {
            _mapArgsForPointsOnly.Device.Transform = _origTransform;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="color"></param>
        /// <param name="width"></param>
        /// <param name="dashStyle"></param>
        /// <returns></returns>
        public static ILineSymbolizer CreateLineSymbolizer(Color color, double width, DashStyle dashStyle)
        {
            SimpleStroke stroke = new SimpleStroke(width, color);
            stroke.DashStyle = dashStyle;
            LineSymbolizer ls = new LineSymbolizer(new SimpleStroke[]{stroke});
            return ls;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="fillColor"></param>
        /// <param name="outlineColor"></param>
        /// <param name="outlineWidth"></param>
        /// <returns></returns>
        public static IPolygonSymbolizer CreatePolygonSymbolizer(Color fillColor, Color outlineColor, double outlineWidth)
        {
            return new PolygonSymbolizer(fillColor, outlineColor, outlineWidth);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="patternBackgroundColor"></param>
        /// <param name="patternForegroundColor"></param>
        /// <param name="hatchStyle"></param>
        /// <param name="outlineColor"></param>
        /// <param name="outlineWidth"></param>
        /// <returns></returns>
        public static IPolygonSymbolizer CreatePolygonSymbolizer(Color patternBackgroundColor, Color patternForegroundColor, HatchStyle hatchStyle, Color outlineColor, double outlineWidth)
        {
            HatchPattern hatchPattern = new HatchPattern(hatchStyle, patternForegroundColor, patternBackgroundColor);
            IPattern[] iPatterns = new IPattern[]{hatchPattern};

            PolygonSymbolizer pSym = new PolygonSymbolizer(iPatterns);
            pSym.SetOutline(outlineColor, outlineWidth);
            return pSym;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="character"></param>
        /// <param name="fontFamily"></param>
        /// <param name="color"></param>
        /// <param name="size"></param>
        /// <param name="degrees"></param>
        /// <returns></returns>
        public static IPointSymbolizer CreatePointSymbolizer(char character, string fontFamily, Color color, double size, double degrees)
        {
            PointSymbolizer pSym = new PointSymbolizer(character, fontFamily, color, size);
            pSym.Symbols[0].Angle = degrees;
            return pSym;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="color"></param>
        /// <param name="pointShape"></param>
        /// <param name="size"></param>
        /// <returns></returns>
        public static IPointSymbolizer CreatePointSymbolizer(Color color, DotSpatial.Symbology.PointShape pointShape, double size)
        {
            return new PointSymbolizer(color, pointShape, size);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="mapFrame"></param>
        /// <returns></returns>
        public static MapArgs CreateMapArgsFromMapBuffer(MapFrame mapFrame)
        {
            return new MapArgs(mapFrame.ClientRectangle, mapFrame.ViewExtents, Graphics.FromImage(mapFrame.BufferImage));
        }
    }
}

Mar 23, 2012 at 1:50 AM

Thanks - I think this will definitely help me get going with implementing this.

Mike

Jan 4, 2014 at 6:22 PM
 DSDrawingUtil _dsDrawUtil = new DSDrawingUtil();
                DotSpatial.Symbology.IPointSymbolizer myPointSymbolizer = DSDrawingUtil.CreatePointSymbolizer(Convert.ToChar(241), "WingDings", myArrowColor, dArrowSize, 0.0);

                Graphics bufferDevice = Graphics.FromImage(Globals.GeoCue.dsMap.BufferedImage);
                MapArgs myMapArgs = new MapArgs(Globals.GeoCue.dsMap.MapFrame.ClientRectangle, Globals.GeoCue.dsMap.MapFrame.ViewExtents, bufferDevice);

                _dsDrawUtil.BeginDrawingPoints(myMapArgs);

                for (int i = 0; i < GeoAnalysis.m_alArrowInfo.Count; i++)
                {
                    myPointSymbolizer.Symbols[0].Angle = -((ArrowInfo)GeoAnalysis.m_alArrowInfo[i]).m_dAzimuth;
                    _dsDrawUtil.Symbolizer = myPointSymbolizer;
                    _dsDrawUtil.DrawPoint(((ArrowInfo)GeoAnalysis.m_alArrowInfo[i]).m_dX, ((ArrowInfo)GeoAnalysis.m_alArrowInfo[i]).m_dY, myMapArgs);
                }

                _dsDrawUtil.EndDrawingPoints();
hi guys i codding on VB but i dont nkow m_alarrorinfo and m_dAzimuth
kellison can u post full code ? (calling code)
thanks
Jan 4, 2014 at 6:24 PM
or anyone know this how to start draw polygon?
Jan 6, 2014 at 3:53 PM
The point drawing code I supplied was just for informational purposes. You will need to come up with your own data to draw. The arrowInfo was very application specific to drawing direction arrows.

To draw polygons, you should do something like this:

_dsDrawUtil.Symbolizer = DSDrawingUtil.CreatePolygonSymbolizer(....);

_dsDrawUtil.DrawPolygon(...);

You should be able to pass null for the shClip argument to DrawPolygon if you do not need to clip. I vaguely recall that I had to add clipping when I had data that extended WAY outside the normal window extent. I think it was something to do with GDI+ not liking window coordinates greater than a 16-bit number or something along those lines.

HTH