Larger hitTest intersection area?

774
6
Jump to solution
10-23-2017 08:18 AM
BenDouglas
New Contributor II

Hi all, I've just started using the ArcGIS Javascript API over the past month or two and it's been great. Most issues I've managed to solve myself or with the help of this forum but I'm stuck on this one:

I'm developing an iPad application for my company that requires users to click on polyline graphics, but it's really difficult for people to do this on the iPad screen because fingers are not mouse pointers, and selecting a precise polyline graphic out of a haystack of them is challenging for some.

Is there any way of adjusting how the hitTest function works to look a bit wider for any intersecting graphics? Such that if someone tapped on a x,y coordinate that was just 5 pixels away from another graphic, it could return that graphic in the array?

Or maybe somehow trick the polyline graphics to think they are larger than they are, without giving them an ugly large size?

Or maybe there is another solution using Query... but I haven't yet explored that function.

I'm using a FeatureLayer for the polylines and a view.on "click" listener which does the hitTest.

Attached is a preview of what I'm dealing with:

Tags (3)
0 Kudos
1 Solution

Accepted Solutions
ThomasSolow
Regular Contributor

It looks to me like features is an array of graphics when you need to compare geometries with intersects.

Maybe you could change it to this:

for (i = 0, il = features.length; i < il; i++) {
  console.log("comparing:", features[i].attributes.ZIP_CRID);
  console.log("intersects:", geometryEngine.intersects(features[i].geometry, touchPoint));
}

// this is probably preferred

selectedFeatures = features.filter(function(feature) {
  return geometryEngine.intersects(feature.geometry, touchPoint);
});

This approach is fine, but you may run into issues if there are a large number of polylines (ie thousands).  You can also query the client-side features using featureLayerView.queryFeatures, but this only lets you pass in an extent (which will be faster) but differs a little from a circle.

View solution in original post

6 Replies
ThomasSolow
Regular Contributor

I don't think there's an easy way to do this with hitTest.  The only option I know of with hitTest is to symbolize the polylines with wider lines, but looking at your data that doesn't seem like a possibility for you.

Aside from hitTest, you have some other options.  Assuming these polylines are backed by a feature service, you could query the feature service with an extent or a polygon or a circle centered at where the user clicked with a radius equal to 5 pixels, then the server will respond with a list of polylines that intersect that circle.

Another option is to do all this in the client.  Depending on how many features you have, it's possible to test each feature against the extent/polygon/circle in the client and not worry about the server at all.

Here's a sample that shows the server option: https://codepen.io/solowt/pen/rGgYQb?editors=1000 

That's a point feature layer but it should work for polyline feature layers as well.

0 Kudos
ThomasSolow
Regular Contributor

Actually I thought of another option with hitTest.  You could programmatically fire off a bunch of hitTests in a circle around where the user clicked.  I had some code to rasterize a circle lying around so here's a sample showing that as well: https://codepen.io/solowt/pen/MEdrgx?editors=1000 

The basic idea here is just to get all the pixels in a filled circle with radius of X pixels and fire a hitTest for each pixel.  Then take the results and eliminate duplicate graphics.

0 Kudos
BenDouglas
New Contributor II

These both sound like great solutions! I'll do some testing on it tomorrow. Thanks!

0 Kudos
BenDouglas
New Contributor II

So after some frustrating testing without errors, I think I've realized that the Query functions only work for FeatureLayers that are not client-side -only services on a server- and the graphics I am working with are all stored local inside the source attribute of the FeatureLayer (in my code it's named displayRoutesLayer). So I tried to use the second option of your first post and came up with something like this:

var touchCircle = new Circle({
     center: event.mapPoint,
     radius: 10 * view.extent.width / view.width
});

var touchPoint = new Graphic({
     geometry: touchCircle,
     symbol: polygonStyle
});

view.graphics.add(touchPoint);

var features = displayRoutesLayer.source.items;
console.log("features", features);

for (i = 0, il = features.length; i < il; i++) {
     console.log("comparing:", features[i].attributes.ZIP_CRID);
     console.log("intersects:", geometryEngine.intersects(features[i], touchPoint));
}‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

I was trying to use the geometryEngine's intersects() function using each of the polyline graphics and the circle polygon, but its giving me back "Uncaught TypeError: d.getCacheValue is not a function" when it tries to run that.

Am I using this the right way? I can't immediately see where the issue is...

I like the idea of your rasterized hitTest calls, but I'd prefer to use this intersects function because I may be using it later as I develop the application for improved processes.

0 Kudos
ThomasSolow
Regular Contributor

It looks to me like features is an array of graphics when you need to compare geometries with intersects.

Maybe you could change it to this:

for (i = 0, il = features.length; i < il; i++) {
  console.log("comparing:", features[i].attributes.ZIP_CRID);
  console.log("intersects:", geometryEngine.intersects(features[i].geometry, touchPoint));
}

// this is probably preferred

selectedFeatures = features.filter(function(feature) {
  return geometryEngine.intersects(feature.geometry, touchPoint);
});

This approach is fine, but you may run into issues if there are a large number of polylines (ie thousands).  You can also query the client-side features using featureLayerView.queryFeatures, but this only lets you pass in an extent (which will be faster) but differs a little from a circle.

BenDouglas
New Contributor II

YES! I was comparing graphics instead of comparing geometries. Had to change both feature.geometry and also touchPoint to touchCircle and it worked beautifully. This should resolve my issue. Thanks Thomas!