ThinkGeo.com    |     Documentation    |     Premium Support

Drawing a ruler

Hi


I am trying to draw a ruler on the map to allow users to measure distances.


I want to do 2 things:


1) Only allow 2 vertexes - I don't want the user to have to double click to end the line, I want first click to start the line and second click to end the line. 


2) Display the distance as a label on the line as I am drawing the line ie the label changes as the line gets longer then is set as the last click hapens.


In v2 I used TrackShapesDraw to do a lot of this. 


At the moment I can switch to drawing mode and draw lines on the map but can't do items 1 and 2 above. I could possible use VertexAdded and count each vertex to determine when the second vertex has been added but that doesn't feel correct. Also, how do you manually stop the line drawing?


I have searched the forums and found a number of similar how do I posts but no answers.


Cheers


Steve



TrackMode.StraightLine nearly covers item 1 but unfortunately the user has to keep the mouse button pressed while they draw the line. My users are using tablets with a stylus which makes it hard to do this. I really need to have click start, click end. 
  
 Steve

Getting close to a solution for item 1.


If I do this:



        private void TrackOverlayVertexAdded(object sender, VertexAddedTrackInteractiveOverlayEventArgs e)
        {
            if (gisMap.TrackOverlay.GetTrackingShape() != null)
            {
                //gisMap.TrackOverlay.TrackMode = TrackMode.None;
            }

        }

I can determine if a shape is being drawn or not.


I can stop drawing by setting TrackMode to TrackMode.None but this feels clumsy and does not allow the user to draw a second line.


There must be a better way to stop the line being drawn?


Steve



Steve,


Thanks for your post and question.
 
The requirements you mentioned is somehow complex, we may need to create a brand new TrackInterativeOverlay to make it. I took a try and hope following code is what we are going to achieve.
 

 private void TrackAndEditShapes_Load(object sender, EventArgs e)
        {
            winformsMap1.MapUnit = GeographyUnit.DecimalDegree;
            winformsMap1.BackgroundOverlay.BackgroundBrush = new GeoSolidBrush(GeoColor.GeographicColors.ShallowOcean);

            WorldMapKitWmsDesktopOverlay worldMapKitDesktopOverlay = new WorldMapKitWmsDesktopOverlay();
            winformsMap1.Overlays.Add("WorldOverlay", worldMapKitDesktopOverlay);

            RulerInteractiveOverlay ruleinterativeOverlay = new RulerInteractiveOverlay();
            winformsMap1.TrackOverlay = ruleinterativeOverlay;
            winformsMap1.TrackOverlay.TrackMode = TrackMode.Line;

            winformsMap1.CurrentExtent = new RectangleShape(-139.2, 92.4, 120.9, -93.2);
            winformsMap1.Refresh();
        }

public class RulerInteractiveOverlay : TrackInteractiveOverlay
    {
        private const string currentFeatureKey = "CurrentFeature";
        private LineShape rulerLineShape;
        private int mouseDown;

        public RulerInteractiveOverlay()
            : base()
        {
            TrackShapeLayer.Open();
            TrackShapeLayer.Columns.Add(new FeatureSourceColumn("length"));

            TrackShapeLayer.ZoomLevelSet.ZoomLevel01.DefaultLineStyle = LineStyles.CreateSimpleLineStyle(GeoColor.FromArgb(100, 255, 0, 0), 1, true);
            TrackShapeLayer.ZoomLevelSet.ZoomLevel01.DefaultLineStyle.OuterPen.LineJoin = DrawingLineJoin.Round;
            TrackShapeLayer.ZoomLevelSet.ZoomLevel01.DefaultLineStyle.InnerPen.LineJoin = DrawingLineJoin.Round;
            TrackShapeLayer.ZoomLevelSet.ZoomLevel01.DefaultLineStyle.CenterPen.LineJoin = DrawingLineJoin.Round;
            TrackShapeLayer.ZoomLevelSet.ZoomLevel01.DefaultTextStyle = new TextStyle("length", new GeoFont("Verdana", 10, DrawingFontStyles.Bold), new GeoSolidBrush(GeoColor.SimpleColors.DarkGreen));
            TrackShapeLayer.ZoomLevelSet.ZoomLevel01.DefaultTextStyle.YOffsetInPixel = 5;
            TrackShapeLayer.ZoomLevelSet.ZoomLevel01.ApplyUntilZoomLevel = ApplyUntilZoomLevel.Level20;
        }

        protected override InteractiveResult MouseDownCore(InteractionArguments interactionArguments)
        {
            InteractiveResult interactiveResult = new InteractiveResult();
            interactiveResult.DrawThisOverlay = InteractiveOverlayDrawType.Draw;
            interactiveResult.ProcessOtherOverlaysMode  = ProcessOtherOverlaysMode.DoNotProcessOtherOverlays;

            rulerLineShape = new LineShape(new Collection<Vertex>() { new Vertex(interactionArguments.WorldX, interactionArguments.WorldY), new Vertex(interactionArguments.WorldX, interactionArguments.WorldY) });

            if (!TrackShapeLayer.InternalFeatures.Contains(currentFeatureKey))
            {
                TrackShapeLayer.InternalFeatures.Add(currentFeatureKey, new Feature(rulerLineShape));
            }
            mouseDown++;
            return interactiveResult;
        }

        protected override InteractiveResult MouseMoveCore(InteractionArguments interactionArguments)
        {
            InteractiveResult interactiveResult = new InteractiveResult();

            if (mouseDown > 0)
            {
                Lock.EnterWriteLock();
                rulerLineShape.Vertices[rulerLineShape.Vertices.Count - 1] = new Vertex(interactionArguments.WorldX, interactionArguments.WorldY);
                Lock.ExitWriteLock();

                interactiveResult.DrawThisOverlay = InteractiveOverlayDrawType.Draw;
                interactiveResult.ProcessOtherOverlaysMode = ProcessOtherOverlaysMode.DoNotProcessOtherOverlays;
            }

            return interactiveResult;
        }

        protected override InteractiveResult MouseUpCore(InteractionArguments interactionArguments)
        {
            InteractiveResult interactiveResult = new InteractiveResult();

            interactiveResult.DrawThisOverlay = InteractiveOverlayDrawType.Draw;
            interactiveResult.ProcessOtherOverlaysMode = ProcessOtherOverlaysMode.DoNotProcessOtherOverlays;

            if (mouseDown == 2)
            {
                mouseDown = 0;
                Feature feature =new Feature( TrackShapeLayer.InternalFeatures[currentFeatureKey].GetShape());
                double length = ((LineShape)feature.GetShape()).GetLength(GeographyUnit.DecimalDegree, DistanceUnit.Kilometer);
                feature.ColumnValues.Add("length", ((int)length).ToString() + " km");

                Lock.EnterWriteLock();
                TrackShapeLayer.InternalFeatures.Add(feature.Id, feature);
                TrackShapeLayer.InternalFeatures.Remove(currentFeatureKey);
                Lock.ExitWriteLock();
            }
            return interactiveResult;
        }

        protected override void DrawCore(GeoCanvas canvas)
        {
            Collection<SimpleCandidate> labelingInAllLayers = new Collection<SimpleCandidate>();

            try
            {
                if (rulerLineShape != null)
                {
                    Feature feature = new Feature(rulerLineShape);
                    double length = rulerLineShape.GetLength(GeographyUnit.DecimalDegree, DistanceUnit.Kilometer);
                    feature.ColumnValues.Add("length", ((int)length).ToString() + " km");
                    
                    Lock.EnterWriteLock();
                    {
                        if (TrackShapeLayer.InternalFeatures.Contains(currentFeatureKey))
                        {
                            TrackShapeLayer.InternalFeatures[currentFeatureKey] = feature;
                        }
                        else
                        {
                            TrackShapeLayer.InternalFeatures.Add(currentFeatureKey, feature);
                        }
                    }
                    Lock.ExitWriteLock();
                }

                TrackShapeLayer.Open();
                TrackShapeLayer.Draw(canvas, labelingInAllLayers);
                canvas.Flush();
            }
            finally
            {
                TrackShapeLayer.Close();
            }
        }
    }


 
Thanks.
 
Yale

Steven,


I also attached a screenshot for the result.


Any more questions just feel free to let us know.



Thanks.


Yale



Wow! Thanks for all the code Yale! 
  
 Looks good and we are nearly there. Just one small problem.  
  
 The override of mouse down, up and move core seem to work whatever mode I’m in. I only want to draw a ruler when I’m in track mode. I fixed this by checking for the mode and returning if TrackMode == TrackMode.None. 
  
 Is there a better way? 
  
 Cheers 
  
 Steve 


Steve, 
  
 That is it, you got it.  
  
 I am not exactly sure what cases we want to achieve, if we use the TrackMode to see if it is None can only deal with pan, we cannot make any difference between TrackPolygon, TrackRectangle etc. If we want, we can use the same logic with TrackMode information used. 
  
 Any more questions just feel free to let me know. 
  
 Thanks. 
  
 Yale 


Thanks Yale. Works fine now. Hopefully anyone else trying to do the same thing will find this post as the code you wrote pretty much solves the problem. Maybe worth posting in the newsletter or code community as it provides examples of a number of techniques. 
  
 Cheers 
  
 Steve

You are right, Steve. We added this idea for the Code Community and we will work on publishing a project on that this week. Thank you for the idea.

Steve,


 I let you know that we published a new project, Ruler Overlay, in the Code Community on that subject. code.thinkgeo.com/projects/show/ruleroverlay


Thank you.