-
Notifications
You must be signed in to change notification settings - Fork 2.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Proposal: Heatmap layer type #4756
Comments
@mourner This sounds awesome. Your mock-up interface looks 💯 I played around with this a while back using a GLSL heatmap plugin in a I came across two ways of thinking about zoom functions and heatmaps:
|
That second one sounds good. On the one map you can visualise a heat map at a continental scale, country scale, city scale, neighbourhood scale having the right amount of detail at each zoom. In the first approach you have not enough detail in the neighbourhood views and too much at the continental views. There might be use cases for the 1st too, so ideally it would support both approaches? |
One use case for the first approach would be a radar map: you wouldn’t want the appearance of showers scattering as you zoom in. |
Mathematically, I guess the first approach is equivalent to using a bandwidth parameter for the KDE that's independent of zoom level, while the second varies the bandwidth by zoom? Wouldn't the second be a misleading visualization? A heatmap implies that you are visualizing a sampled estimate of some continuous variable. Is there a situation where that estimate would vary depending on zoom level (other than making cool looking demos that don't ultimately have any meaning)? |
@jfirebaugh Agreed-- heatmaps already have a lot of potential to mislead depending on how you choose parameters. Keeping the visualization constant with view changes seems more "honest" to me.
@andrewharvey When it comes to visualizing a dataset at a wide range of zoom levels, my conclusion from experimenting with heatmaps is that this is an area where they are not the best tool for the job. Clustering points is much better for this. Even better would be a heatmap at low zoom levels that fades into clustered or individual points after a certain threshold zoom. This layer type could be configurable to go either way, but I would suggest we go with a constant geographic radius (option 1 above) as the default. |
Maybe: If the input is a loaded as a tiled dataset the resolution of the data will be different at different zoom levels. The higher the resolution of the input data, the smaller a kernel you can use. Another maybe: You may want to generalize the estimate when visualizing it. A lower resolution version might be clearer than a high resolution one if it hides details that are unnecessary at that scale. Using a larger kernel to oversmooth the estimate would get you something in that direction, even if it isn't the most statistically valid way to do it. questionswhat would the input data for heatmaps look like?
how would the data be simplified/aggregated for low zoom levels?
|
For point data visualizations what is really needed is the ability to
However this visualization does not help if you want to identify areas below a
This approach generalizes well with point data sets in other domains as well.
Most applications do this processing on the server side at the moment. It |
@jfirebaugh I don't think heatmap visualization has to be perceived solely as a geospatial analysis tool, which would mean showing mathematically correct results that depend on physical distances between points. The other purpose of a heatmap (which pretty much all mapping heatmap plugins adhere to) is being a purely visual cue to help navigate a crowded dataset, which has to adapt to zoom to remain useful. Clustering is similar in that regard — it's mathematically meaningless, but you won't be able to browse a million points without it with good performance, and it makes browsing the data much easier. So I'd like to support both cases by allowing the user to pick either a constant value or a zoom function (exponential base-2 for constant bandwidth). It would help if the latter had some kind of an easy alias in the new expressions API, rather than having to do something unintuitive like @mb12 what you're describing is a different feature — not kernel density estimation (heatmap), but multivariate interpolation. It requires a very different family of algorithms (such as Inverse Distane Weighting), and is computationally much more expensive and technically more difficult to do on the client. Let's keep this thread focused on heatmaps. |
What if at the rendering stage heatmaps were just a regular raster layer that gets colourized? (and interpolated between zooms before colorization if you want to be fancy). Heatmap creation could be handled at the source level just like marker clustering is. I haven't thought through this, but it might be worth considering. I think @mb12 was hinting at supporting different kinds of data-level transforms before reaching the rendering stage. |
@ansis this might be a good approach for interpolation, which is static, but probably not the best fit for heatmaps because we wouldn't be able to change heatmap radius / max weight / etc. dynamically. |
@mourner why not? GeoJSONSource has |
The raster tiles produced for different zoom levels could have different radiuses, max weights which means they don't have to be zoom-constant. Changing the values via the runtime api would be slower than with your proposed implementation but this isn't a common thing. It would be similar to changing the cluster radius. Rendering might be faster since with the raster approach each pixel gets shaded only once. |
@anandthakker @ansis I mean 60-fps dynamic changes, animation — just like we can do for circle radius and color. Also, the colorizing part is not the bottleneck — drawing kernels is (it's O(n * radius^2) rather than O(pixels)), and CPU-based calculation will simply be way too slow in this case compared to GPU rendering. So I don't think your proposed approach will work. |
What's the case dynamically animating kernel size? I'm not sure this is the right thing to optimize for. Or are you thinking of animating the point data underlying the heatmap?
Yep, in the proposal this is a per-frame cost. If it's moved to where tiles are loaded and updated, it becomes a less frequent cost. The webgl approach to generating a heatmap you describe could be used to create these rasters. |
@ansis to support smooth zooming in the screen-based kernel use case (no. 2 in @kronick's comment). Otherwise user experience will be noticeably worse — equivalent to using raster map tiles compared to vector maps. Another reason is being able to animate DDS points smoothly after we introduce DDS animations (which are in the roadmap) — tying ourselves to CPU-based raster approach will make this impossible. |
I would like to see support for something ala .PNG raster tiles as a data source for the heat map. Each pixel value in the matrix containing the "count" for the "area" covered by the pixel. The .png could contain 8-bit "grayscale" (0-255) values or up to 32-bit values if required. The vector (array of coordinates / geojson) variant might not be feasible for huge datasets, and the raster approach might work nicely in a shader program? |
@anandthakker @ansis to address your code/spec bloat concerns: my current plan is to finish a proof of concept PR with a new core type to have something to play and experiment with; once we have this, we'll be able to see what works and what doesn't in terms of the behavior and customization, and see if we can design an extension API for a potential plugin around it instead if we decide not to pursue a core type. |
@mourner sounds good! I agree that this seems like a good thing to have in the core mapbox gl. |
@mourner I don’t want to interfere in your technical discussion, but from an end-user perspective I am very excited to read that you are going to make a proof a concept with a chance that this will end up in your core code or made available as a plugin. Like you said, heatmap is a very common and nice way to present clustering and we get many requests for this feature from our customers (we provide a viewer solution to the utility market via GE as reseller and we based it on the mapbox technology). So sounds very good and keep up the good work, you all make very cool stuff! |
For anyone wondering, here's a little preview of heatmaps in Mapbox GL JS: https://www.mapbox.com/blog/heatmaps-preview/ |
@mourner This is the last piece to our implementation of mapbox gl replacing Google maps. Is there a roadmap to a release date of adding the heatmap feature? |
@mourner Is there a branch where I can try out this new layer? Thanks for your work on this, it looks great! |
@stevewillard should be up in a few days, stay tuned for new pull requests! |
Hey everyone, sorry for the delay! Just open a work in progress PR here: #5253. Still a lot of small things to handle (captured in the checklist), but generally it's looking good. |
@mourner The heatmap layer is working great. One that that I couldn't figure out from the PR was changing the color stops - is this configurable? |
@stevewillard yes, with "heatmap-color": {
"stops": [
[0, "blue"],
[0.5, "yellow"],
[1, "red"]
],
"default": "rgba(0, 0, 0, 0)" // temporary hack, will not be needed when PR is ready
} |
I don't have this issue. The stops should range from 0 to 1 I'd imagine.
I have observed this too, just by accident though :/ |
Yeah, I'm aware of the resizing bug — on my list. Another issue you may also notice is that sometimes heatmaps will flicker. |
Why doesn't my heatmap show?My circle point can. But the type:heatmap can't. |
@wwangchen can you make a minimal JSFiddle to demonstrate your problem? Have you looked at the official example? |
Yes. the error is : Error: layers.heat0.type: expected one of [fill, line, symbol, circle, fill-extrusion, raster, background], heatmap found |
@wwangchen are you sure you're using the latest GL JS release (0.41.0)? |
Thank you. I am not using the latest GL JS. |
Hi @mourner, any plans to add heatmap controls to the style editor? |
@mourner, in your original proposal (first post above) you suggested the possibility of a I'm wondering if this was subsequently considered any further or if there are any suggested workflows for achieving this sort of behaviour with the current implementation? Thanks. |
@songololo I was looking for exactly that feature when I found that thread. @mourner We're also wondering if there is a feature in progress to use heatmap based on value, instead of density. We tried mapping our value to the 'weight' property, but the density always take over, with no way to cancel it out. |
@sanchexx pending the heatmap discussion: in the interim it works quite well to use a circle with blurring enabled (assuming a large radius). If pinning the radius to zoom and the colour to the magnitude, then it will give a heatmap-like effect. |
Thanks @songololo . That's actually what we started playing around with, which seem to be giving results. Started experimenting with a mix of blur & opacity, and adding varying radius according to the amount of points. The only thing I don't like is that it still seem like a bunch of circles interlacing a bit, not as smooth a as a heatmap. But better than nothing! Can you explain what you meant by pinning the radius to the zoom? Like having a step function that define circle radius on zoom level? Any example of what worked well for you? Might be something worth considering :) |
@sanchexx this is the pattern I'm using, hope it is helpful:
PS: I'm intentionally reducing and fading-out the points below a zoom level of 14, so in most cases the zoom should probably extend lower and with less fading...! |
Dear developers, I tried to make the data of MultiPolygon into a heatMap, but I found that the heatmapWeight doesn't work. I can be sure that the data contains the values of the relevant Settings. |
@songyuyang0918 yes, only points are supported. |
@mourner Thank you for your reply. My current data is the WFS service of geoserver, which may be of various types such as point,Polygon and line. Do you have any good ideas based on this? |
Motivation
Heatmap support is one of the most requested features for Mapbox GL, and an extremely common map visualization type, especially prevalent in weather, travel, fitness and social apps.
Heatmaps are typically used to visualize density of a point dataset. It's in a way similar to clustering, but with a very different look that shows areas points are crowded in very well.
Design Alternatives
Option A: Implement it as an external plugin
We could use the heatmaps use case to drive the design of custom layers API and custom sources API, both of which are not fleshed out yet, and then package it as an external plugin. The drawbacks of this approach are:
Option B:
colorize-alpha
propertyPrior discussion in #3182. The idea is to introduce a
colorize-alpha
property for existing layers likecircle
, then render the circles with blur as is and recolorize according to the specified color ramp that matches each pixel alpha value to a color. Drawbacks:ONE, ONE
blend mode), and KDEs usually use a different sprite for each point — a Gaussian kernel, which may also look like a blurred circle but is still different.colorize-alpha
on —circle-stroke-*
,circle- pitch-scale
,circle-translate-*
, andcircle-blur
should be always1.0
. Having these options makes heatmaps harder to use and easier to screw up.This also feels very similar to the case of extrusion-related properties as a part of the
fill
type vs a separatefill-extrusion
type. We chose the latter option, and I'm inclined towards the same in the choise of additionalcircle
properties vs newheatmap
layer type.Design
Given considerations above, I propose to implement a new
heatmap
layer type in the core. It will have its own set of shaders and drawing code along other types. We'll be able to render either vector tile points, GeoJSON points or GeoJSON clusters as heatmaps, just like we do with circles.The main drawback of this approach are code and specification bloat, since we are unlikely to remove a feature once we add it. I think the feature is important enough to justify being in the core, but not yet fully convinced — let's discuss.
Mock-Up
alpha
values, notzoom
, so this would be an outlier in the spec. Is this OK? And how would an equivalent function look in the arbitrary expressions proposal?EXT_blend_minmax.MAX
rather thanGL_FUNC_ADD
).Implementation
The only way I've seen heatmaps implemented in the GPU is the same — draw a kernel ("blurred circle") for each point of the dataset with additive blending (storing the result in a temporary framebuffer), then colorize each pixel using a color ramp (based on alpha values). We'll have to use the same approach — I haven't found a more efficient one for the GPU.
As for performance, we have the advantage of being able to cluster points (which is already implemented), which enables us to render huge datasets as heatmaps on the client.
cc @kkaefer @anandthakker @ansis @1ec5 @ryanbaumann @peterqliu @mapbox/gl
Previous tickets: #3994 #3182
The text was updated successfully, but these errors were encountered: