ThinkGeo.com    |     Documentation    |     Premium Support

SnapTo feature--need help

Hello all.  



We are using the ThinkGeo WPF 7.0 version.



I need an ability to snap to an existing polygon.  I have seen the current samples.  That doesn’t handle our exact issue.  What we need is when the chooses to draw a line on the map, we need the cursor to snap to a given polygon.  We have it working after the user has already added one vertex.  What isn’t working is snapping for the first vertex.  



It seems the MouseMoved event of the Map.TrackOverlay object will not fire until after the shape has already started (ie: user has chosen the first vertex for the line).  



Is there a way for us to snap the cursor to the polygon before adding the first vertex?

Hi Scott, 
  
 Are you using Map1.TrackOverlay.MouseMoved event? I think Map1.TrackOverlay.MapMouseMove can works for you. 
  
 Regards, 
  
 Don

Yes, that is the event I am using.  I’ll try the MapMouseMove.

That is the event I needed.  Thanks.  The issue I have now is that the cursor is not moving to what I am trying to move it too. 



After I get the correct coordinates I try setting the ScreenX & ScreenY properties on the InteractionArguments object of MapMouseMoveInteractiveOverlayEventArgs.  However after I set that, no change appears to be made. 





private void MouseMoved(object sender, MapMouseMoveInteractiveOverlayEventArgs e)
        {
            try
            {
                if (Map.TrackOverlay.TrackMode == TrackMode.Circle || Map.TrackOverlay.TrackMode == TrackMode.Point)
                    return;
 
                if (!ViewModel.IsUsingTouch) CalculateDrawingDimensionsAndDisplay();
                if (!ViewModel.SnapOn || ViewModel.CurrentGeometryType == GeometryType.None || ViewModel.CurrentGeometryType == GeometryType.Circle) return;
 
                LayerOverlay overlay;
                InMemoryFeatureLayer layer;
                switch (ViewModel.CurrentEditor)
                {
                    case Editor.Design:
                        overlay = (LayerOverlay)Map.Overlays[PROPOSAL_OVERLAY];
                        layer = (InMemoryFeatureLayer)overlay.Layers[PROPOSAL_LAYER];
                        break;
                    default:
                        overlay = (LayerOverlay)Map.Overlays[LAYOUT_OVERLAY];
                        layer = (InMemoryFeatureLayer)overlay.Layers[LAYOUT_LAYER];
                        break;
                }
 
                layer.FeatureSource.Open();
                MapUtilities.SnapToFeature(e, layer.InternalFeatures, TOLERANCE, Map.MapUnit, Map);
                layer.FeatureSource.Close();
            }
            catch (Exception ex)
            {
                LogHost.Default.Error(ex.ToString());
            }
        }
public static void SnapToFeature(MapMouseMoveInteractiveOverlayEventArgs e, GeoCollection features, float tolerance, GeographyUnit mapUnit, WpfMap map)
        {
            try
            {
                var newMovedVertex = new Vertex();
                var lowestDistance = double.MaxValue;
 
                foreach (var feature in features)
                {
                    switch (feature.GetShape().GetWellKnownType())
                    {
                        case WellKnownType.Polygon:
                            FindSnapToPointOnPolygon(e, mapUnit, ref newMovedVertex, ref lowestDistance, feature.GetShape() as PolygonShape, map);
                            break;
                    }
                }
                if (!(lowestDistance <= tolerance)) return;
 
                var screenCoordinate = ExtentHelper.ToScreenCoordinate(map.CurrentExtent, new Feature(newMovedVertex), (float) map.CurrentExtent.Width, (float) map.CurrentExtent.Height);
 
                e.InteractionArguments.ScreenX = screenCoordinate.X;
                e.InteractionArguments.ScreenY = screenCoordinate.Y;
            }
            catch (Exception ex)
            {
                LogHost.Default.ErrorException(ex.ToString(), ex);
            }
        }
 
private static void FindSnapToPointOnPolygon(MapMouseMoveInteractiveOverlayEventArgs e, GeographyUnit mapUnit, ref Vertex newMovedVertex, ref double lowestDistance, PolygonShape polygon, WpfMap map)
        {
            var polygonDistance = double.MaxValue;
            var nearestPoint = new PointShape();
            for (var i = 0; i < polygon.OuterRing.Vertices.Count - 1; i++)
            {
                var line = new[] { polygon.OuterRing.Vertices, polygon.OuterRing.Vertices[i + 1] };
                var minx = polygon.OuterRing.Vertices.X > polygon.OuterRing.Vertices[i + 1].X ? polygon.OuterRing.Vertices[i + 1].X : polygon.OuterRing.Vertices.X;
                var maxx = polygon.OuterRing.Vertices.X < polygon.OuterRing.Vertices[i + 1].X ? polygon.OuterRing.Vertices[i + 1].X : polygon.OuterRing.Vertices.X;
                var miny = polygon.OuterRing.Vertices.Y > polygon.OuterRing.Vertices[i + 1].Y ? polygon.OuterRing.Vertices[i + 1].Y : polygon.OuterRing.Vertices.Y;
                var maxy = polygon.OuterRing.Vertices.Y < polygon.OuterRing.Vertices[i + 1].Y ? polygon.OuterRing.Vertices[i + 1].Y : polygon.OuterRing.Vertices.Y;
 
                var screenPointF = new ScreenPointF(e.InteractionArguments.ScreenX, e.InteractionArguments.ScreenY);
                var worldCoordinate = ExtentHelper.ToWorldCoordinate(map.CurrentExtent, screenPointF, (float)map.CurrentExtent.Width, (float)map.CurrentExtent.Height);
                var worldVertex = new Vertex(worldCoordinate);
                if (((!(minx < worldVertex.X)) || (!(maxx > worldVertex.X))) &&
                    ((!(miny < worldVertex.Y)) || (!(maxy > worldVertex.Y)))) continue;
 
                var xLine = new LineShape(line);
                var newDistance = xLine.GetDistanceTo(new PointShape(worldVertex), mapUnit, DistanceUnit.Meter);
                if (!(polygonDistance > newDistance)) continue;
 
                polygonDistance = newDistance;
                nearestPoint = xLine.GetClosestPointTo(new PointShape(worldVertex), mapUnit);
            }
            if (!(polygonDistance < lowestDistance)) return;
 
            lowestDistance = polygonDistance;
            newMovedVertex = new Vertex(nearestPoint.X, nearestPoint.Y);
        }













Hi Scott,  
  
 Sorry I miss some information about your question. I think maybe the new event cannot implement what you want, because you cannot set the mouse position via it. 
  
 Let us talk about this further, I think there are two possibilities about your scenario: 
  
 1. When you move mouse close to a polygon, the mouse cursor snap to the polygon. 
  
 2. When you move mouse close to a polygon, there is a point appear in the border of polygon for point out where can be the first snap point. 
  
 For item 1:  
 I think you have to call windows API SetCursorPos, because our API cannot move mouse cursor. 
  
 For item 2: 
 You can use currently MapMouseMove event and function, just need to draw a dynamic point on polygon when mouse move. 
  
 After this step, I think the customer still need to click on map for make sure where is the first point. 
  
 So now maybe you can call Map1.TrackOverlay.MouseClick to simulator click on target point and still use Map1.TrackOverlay.MouseMoved event for other points. 
  
 Please let me know whether it’s helpful for your scenario? 
  
 Regards, 
  
 Don