manubb / Leaflet.PixiOverlay

Bring Pixi.js power to Leaflet maps
MIT License
463 stars 85 forks source link

Interactivity with Markers using ParticleContainer #36

Closed tabiodun closed 4 years ago

tabiodun commented 4 years ago

Hi. Is there a way to add click handlers to the markers drawn with the particle container (like the 1 million markers demo?) or if not to the us cities simple example where the markers dont change size on zoom?

manubb commented 4 years ago

Hi. Apparently, you cannot use pixi interactivity with particleContainer: https://www.html5gamedevs.com/topic/35348-pixiparticlesparticlecontainer-children-particle-hitareas-dont-seem-to-follow-the-particles-as-they-move/ You have two options:

tabiodun commented 4 years ago

Thanks. I'll look into those options

mirko77 commented 4 years ago

@tabiodun have you find a way? I am looking for the best way to add a click callback to each marker.

acycliq commented 4 years ago

At the deepest zoom levels (where you dont have that many points to render) you could switch your code to plain leaflet (probably with a canvas renderer) and hide the pixi containers.

Otherwise a spatial index like kdbush looks like a possible way. If your marker has a "regular" shape, like rectangle or circle then with some tweaking, I imagine, one would be able to find when the mouse pointer collides with a marker and which marker it is. If your marker is star-shaped, a cross or some other unusual shape then (I think) it will be trickier to detect exactly when the mouse collides with the marker (and hence when exactly to call the clickcallback)

mirko77 commented 4 years ago

@acycliq I tried to implement the French cities example using my dataset but it crashes the browser due to the window.solveCollsion() function pushing the cpu to over 100% for a while (until I kill the tab). I guess that happens because there are many overlapping markers.

If I remove that function (and the whole interactivity), I am able to display up to 100.000 markers (circles) with good responsiveness, but I miss the click event on each of them.

I ended up using your first solution (canvas renderer) which works up to 10.000 points. For bigger datasets, I keep using clusters.

acycliq commented 4 years ago

@mirko77 Yes, it will not work with a quadtree. I was thinking to use kdbush. Then register a callback like:

utils.getMap().on('mousemove', L.Util.throttle(function (e) {
      onMousemove(e); 
}, 32));

and in the onMousemove callback:

function onMousemove(e) {
    mouseTarget = findFeature(e.latlng);
}

the findFeature function will be grabbing all the markers around a tight area of my mouse cursor, e.latlng (using something similar to one of the two lines below, taken from the KDBush readme:)

const ids1 = index.range(10, 10, 20, 20); // bbox search - minX, minY, maxX, maxY
const ids2 = index.within(10, 10, 5);     // radius search - x, y, radius

In my case I was thinking to have something like: const ids2 = index.within(e.lat, e.lng, radius);

Then given that I know the size of the markers I should be able to identify whether I am hovering over the background or a marker and which marker it is. Once you got a target marker you can use plain leaflet to draw a highlight over that marker.

I didnt end up writing it (hence I dont know if it eventually works buy I think it will) because it was not a requirement and I am also using standard leaflet on a canvas renderer for the deep zoom levels to trigger my mouse events.

One of the demos here is in this direction (use of a spatial index I mean) but it employs RBush which is more suitable for polygons. I think it is one of the French elections demos, search for RBush() and see if you can use this logic for your project

mirko77 commented 4 years ago

Thanks for this. How would you handle overlapping markers?

acycliq commented 4 years ago

I would always raise an event on the closest marker to my mouse. If I have a group of equidistant markers I would raise an event on whichever comes at position [0] of the set. If they exactly overlap (exactly the same coords and same size) then again raise an event on the marker at position [0]. Otherwise the smallest would take priority. But perfectly overlapping markers is always going to be a problem anyway.

mirko77 commented 4 years ago

Thanks again I will give it a try

acycliq commented 4 years ago

No worries, I hope It works for you. Let me know how it goes

mirko77 commented 4 years ago

Ok, here is what I have so far https://pixi-overlay.bitbucket.io/

code: https://bitbucket.org/pixi-overlay/pixi-overlay.bitbucket.io/src/master/

I get false hits at closer zoom levels, I suppose the radius must be re-calculated based on the marker scale? I guess the radius in index.within is in meters as well, could not find anything in the kdbush docs

acycliq commented 4 years ago

This is my take on this so far...I have oversimplified your example, I have only two points and they are miles and miles away. I tried to draw a highlight when the mouse hovers over the marker. There is some scaling that has to be taken into account, but I am not sure if I got it. With regards to your question about the radius, I believe it is in pixels. (which make more sense, since kdbush and the other algorithms are generic methodologies, they are not specific to GIS). (Btw I also noticed there is geokdbush which maybe suits you better)

here is the demo (I dont think it is finished). Code here

mirko77 commented 4 years ago

Thanks, nice one.

I tried to use it with my datasets.

It works well for markers far away from each other, but again the issues start when there are many overlapping markers.