Select to view content in your preferred language

Buffering a click in screen space

530
5
Jump to solution
09-12-2023 08:26 AM
MBambino_ATS
Occasional Contributor

I have a Map Tool that listens for a click on the map to then search for shapes (mainly lines and points). I already have the settings enabled to use the visual shape to increase the click area, but is it possible to extend that even further? I know that you can use a circle to increase the search area, but that makes zoomed-in clicks much wider than zoomed out clicks. Is there a way to scale the circle to the screen size, so for example, I would search and extra 4 pixels in all directions?

0 Kudos
2 Solutions

Accepted Solutions
MBambino_ATS
Occasional Contributor
protected override async Task<bool> OnSketchCompleteAsync(Geometry geometry) {
    return await QueuedTask.Run<bool>(async () => {
        // geometry is a point
        var clickedPnt = geometry as MapPoint;
        if (clickedPnt == null)
            return false;

        float tolerance = 4.0f; // Buffer size

        // Take the screen-space point and buffer it by the tolerance
        var topLeft = new Coordinate2D(clickedPnt.X - tolerance, clickedPnt.Y + tolerance);
        var bottomRight = new Coordinate2D(clickedPnt.X + tolerance, clickedPnt.Y - tolerance);

        Geometry envelopeGeometry = EnvelopeBuilderEx.CreateEnvelope(topLeft, bottomRight);
        if (envelopeGeometry == null)
            return false;

        var result = ActiveMapView.GetFeatures(envelopeGeometry);

        foreach (var kvp in result.ToDictionary()){
            // Do Stuff
        }

        return true; // Return T/F based on the above loop
    });
}

Thanks for pointing me to that!
I found a mistake in that example, though. The envelope created is never used, and if it were used, it wouldn't find the correct target due to a differing spatial reference id.
Here is the correct code:

View solution in original post

0 Kudos
Wolf
by Esri Regular Contributor
Esri Regular Contributor

Thanks for pointing out the issue with the sample, i will correct the issue for the upcoming 3.2 release.   

This code uses a circle around the click point using the pixel tolerance as the radius:

 

// pixel tolerance
var toleranceInScreenUnits = 10;
// Use bottomRight to popup the dynamic menu result
bottomRight = new Point(clickedPnt.X + toleranceInScreenUnits, clickedPnt.Y + toleranceInScreenUnits);
var toleranceInMapUnits = GetScreenPointsInMapUnits(toleranceInScreenUnits, MapView.Active);

// Create a search circle around the click point using the pixel tolerance as a radius
var pnt = MapView.Active.ClientToMap(new Point(clickedPnt.X, clickedPnt.Y));
var arcSegment = EllipticArcBuilderEx.CreateCircle(pnt.Coordinate2D, toleranceInMapUnits, ArcOrientation.ArcClockwise, pnt.SpatialReference);
var circlePolygon = PolygonBuilderEx.CreatePolygon(new[] { arcSegment });

//Get the features that intersect the search circle polygon
var result = ActiveMapView.GetFeatures(circlePolygon);

Here is a screenshot that show the search polygon on the map:

Wolf_0-1694641934894.png

 

 

View solution in original post

0 Kudos
5 Replies
GKmieliauskas
Esri Regular Contributor

Hi,

Look at ArcGIS Pro SDK community sample

It uses MapTool with SketchOutputMode equal SketchOutputMode.Screen. 

In OnToolActivateAsync method it expands clicked point extent by 3 pixels.

MBambino_ATS
Occasional Contributor
protected override async Task<bool> OnSketchCompleteAsync(Geometry geometry) {
    return await QueuedTask.Run<bool>(async () => {
        // geometry is a point
        var clickedPnt = geometry as MapPoint;
        if (clickedPnt == null)
            return false;

        float tolerance = 4.0f; // Buffer size

        // Take the screen-space point and buffer it by the tolerance
        var topLeft = new Coordinate2D(clickedPnt.X - tolerance, clickedPnt.Y + tolerance);
        var bottomRight = new Coordinate2D(clickedPnt.X + tolerance, clickedPnt.Y - tolerance);

        Geometry envelopeGeometry = EnvelopeBuilderEx.CreateEnvelope(topLeft, bottomRight);
        if (envelopeGeometry == null)
            return false;

        var result = ActiveMapView.GetFeatures(envelopeGeometry);

        foreach (var kvp in result.ToDictionary()){
            // Do Stuff
        }

        return true; // Return T/F based on the above loop
    });
}

Thanks for pointing me to that!
I found a mistake in that example, though. The envelope created is never used, and if it were used, it wouldn't find the correct target due to a differing spatial reference id.
Here is the correct code:

0 Kudos
Wolf
by Esri Regular Contributor
Esri Regular Contributor

Thanks for pointing out the issue with the sample, i will correct the issue for the upcoming 3.2 release.   

This code uses a circle around the click point using the pixel tolerance as the radius:

 

// pixel tolerance
var toleranceInScreenUnits = 10;
// Use bottomRight to popup the dynamic menu result
bottomRight = new Point(clickedPnt.X + toleranceInScreenUnits, clickedPnt.Y + toleranceInScreenUnits);
var toleranceInMapUnits = GetScreenPointsInMapUnits(toleranceInScreenUnits, MapView.Active);

// Create a search circle around the click point using the pixel tolerance as a radius
var pnt = MapView.Active.ClientToMap(new Point(clickedPnt.X, clickedPnt.Y));
var arcSegment = EllipticArcBuilderEx.CreateCircle(pnt.Coordinate2D, toleranceInMapUnits, ArcOrientation.ArcClockwise, pnt.SpatialReference);
var circlePolygon = PolygonBuilderEx.CreatePolygon(new[] { arcSegment });

//Get the features that intersect the search circle polygon
var result = ActiveMapView.GetFeatures(circlePolygon);

Here is a screenshot that show the search polygon on the map:

Wolf_0-1694641934894.png

 

 

0 Kudos
MBambino_ATS
Occasional Contributor

Thank you for that!
Where does GetScreenPointsInMapUnits come from?

0 Kudos
Wolf
by Esri Regular Contributor
Esri Regular Contributor

Sorry i forgot to include the GetScreenPointsInMapUnits method in my snippet above.  

private double GetScreenPointsInMapUnits (double pixels, MapView mapView)
{
  var left = new Point(0, 0);
  var right = new Point(0, pixels);
  var pntLeft = mapView.ClientToMap(left);
  var pntRight = mapView.ClientToMap(right);
  var line = LineBuilderEx.CreateLineSegment (pntLeft, pntRight);
  return line.Length;
}

In essence the method takes the tolerance in screen units (i.e. pixels) and returns the tolerance in units for the current active map's spatial reference.  So when i generate the circle geometry for the search i can then use toleranceInMapUnits as the radius for the circle.

0 Kudos