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?
SnapTo feature--need help
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