melowntech / vts-browser-js

JavaScript WebGL 3D map rendering engine
BSD 2-Clause "Simplified" License
220 stars 42 forks source link

geo-feature-click multiple features #194

Open jrjdavidson opened 4 years ago

jrjdavidson commented 4 years ago

Hi again, I'm become a regular here :)

I was playing around with click event on freelayers using browser.on('geo-feature-click',...) . It occurred to me that it would be useful to return all features that are present under the click location - is that possible?

See example here, where the 'geo-feature-click', event is logged to the console, it seems only one of the polygons is returned (perhaps the one on 'top'?):

https://www.holoceneadventures.com/test/test1.html

davidmtech commented 4 years ago

You are right only one polygon is returned in click event. Which one depends on several factors. All features with some click/hover event are rendered into special texture. Where each feature has special color representing feature id. Which feature is present at specific screen coordinates is determined by looking into this texture. Advantage of this approach is that is fast. Disadvantage of this approach is that only one feature can be stored in one pixel. So there is no easy way how to get all features. But you can do it manually. Function map.getScreenRay(screenX, screenY) returns position and direction of the screen ray and you can use that for ray-triangle intersection.

https://github.com/melowntech/vts-browser-js/blob/a0d80a516fe82a8e626f96009b4b27ba250a16d3/src/core/renderer/octree.js#L424

You can take inspiration from octree raycating library or provide input for your data and use that library

https://github.com/melowntech/vts-browser-js/blob/master/src/core/renderer/octree.js

function geodata.makeGeodata() returns polygons in tessellated form.

Another option is to live with one feature per pixel and take more control over rendered result.

When you use "polygon-use-stencil": true in geodata styles, then pixel accepts only first polygon and all other polygons are ignored. This is good for rendering onion like structures, when you render inner layer first and outer layer later. You can affect rendering order by sorting input data.

jrjdavidson commented 4 years ago

Thanks for the detailed explanantion @davidmtech, I'll have a think about this and see if I can come up with something. It's a really interesting problem, but it's pushing the limit of my capibilites! when you say "where each feature has special color representing feature id", would it be possible to generate 'special colors' that represent an array of ids? I guess I would have to have a look the code that generates the special texture, but I assume that there is some sort of mapping function that could be played with.

davidmtech commented 4 years ago

It is possible to have special color representing multiple ids. But there is limitation given by bit width of rendered color which is usually 32 bit. So for example you can encode only 4 ids in one color with total 256 features in the map (4 * 8bit = 32bit). More serious problem is that you have limited read access to texture into which you are rendering. So composing final id color is not so trivial. This part is very tricky in WebGL.