mapbox / mapbox-gl-js

Interactive, thoroughly customizable maps in the browser, powered by vector tiles and WebGL
https://docs.mapbox.com/mapbox-gl-js/
Other
11.17k stars 2.22k forks source link

Aggregate function for the property heatmap-color #9762

Open fifiDesPlaines opened 4 years ago

fifiDesPlaines commented 4 years ago

Hi,

mapbox-gl-js version: 1.10.1

Question

I would like to construct a kind of temperature-like map. I would like to fill the map depending on the value of my dots and not their densities. The closest kind of layer that I can use is the Heatmap... Is there a way to display a heatmap whose color depends on an aggregation of the point's properties composing it instead of their densities?

Something like that : https://github.com/optimisme/javascript-temperatureMap where dots will be kind of cluster's centers of other points.

Links to related documentation

https://docs.mapbox.com/mapbox-gl-js/style-spec/layers/#heatmap The doc says : "Defines the color of each pixel based on its density value in a heatmap. Should be an expression that uses ["heatmap-density"] as input."

https://docs.mapbox.com/help/tutorials/make-a-heatmap-with-mapbox-gl-js/ Here, you've constructed the heatmap from the density, Is it possible to construct an expression based on an average of property of the geojson ?

mourner commented 4 years ago

Currently this is not possible with the heatmap layer — the way it is technically implemented makes the rendering dependent on the density of points. We might want to explore adding an option for an alternative rendering. For now, you could try e.g. using blurred circles to represent the temperatures, kind of like a scatterplot.

jdelga commented 4 years ago

It would be perfect to show temperature or internet speed from user doesn't matter the amount or density of users that we have.

Using the explained heatmap it doesn't make any sense as "temperature" can't be added depending on the amount of dots.

From the docs:

It is even mentioned the second kind of heatmap, in which the value is the average of the points, instead of the density. However, only one is explained.

https://docs.mapbox.com/help/tutorials/make-a-heatmap-with-mapbox-gl-js/#what-is-the-purpose-of-a-heatmap

Among maps you'll find on the web, there are two common categories of heatmaps: those that encourage the user to explore dense point data, and those that interpolate discrete values over a continuous surface, creating a smooth gradient between those points. The latter is less common and most often used in scientific publications or when a phenomenon is distributed over an area in a predictable way. For example, your town may only have a few weather stations, but your favorite weather app displays a smooth gradient of temperatures across the entire area of your town. For your local weather service, it is reasonable to assume that, if two adjacent stations report different temperatures, the temperature between them will transition gradually from one to the next.

More info about these kind of maps:

https://mgimond.github.io/Spatial/spatial-interpolation.html

Sample:

image

Possible result: image

fifiDesPlaines commented 4 years ago

Currently this is not possible with the heatmap layer — the way it is technically implemented makes the rendering dependent on the density of points. We might want to explore adding an option for an alternative rendering. For now, you could try e.g. using blurred circles to represent the temperatures, kind of like a scatterplot.

@mourner , I tried this approach, but the result is not satisfying for the moment. I'm trying to cut the map in tiles with their size depending on the zoom, and to calculate the fill color from an aggregation function.

Rylern commented 3 years ago

Hi, if anyone still needs this feature I used a custom Mapbox layer to create what you're describing, take a look here: https://github.com/Rylern/InterpolateHeatmapLayer

royyeah commented 2 years ago

@Rylern We tried your approach, but the rendering is not customizable (no custom colors) and rendering fast enough for our use case. We currently went for the approach suggested by @mourner (blurred circles to represent the temperatures), but we keep our eyes open for something better in the future!

dson commented 2 years ago

Any update on this? The heatmap layer looks amazing! It's a shame we can't use it for this other common use case as well. :)

Rylern commented 2 years ago

@Rylern We tried your approach, but the rendering is not customizable (no custom colors) and rendering fast enough for our use case. We currently went for the approach suggested by @mourner (blurred circles to represent the temperatures), but we keep our eyes open for something better in the future!

Actually you can customize the colors, take a look at the valueToColor parameter. It allows you to define the function that maps a value to a color. Tell me the color scale you want, and I can help you define this parameter. When you say it's not rendering fast enough, what do you mean exactly? Is it when you first load the map, or after when you change the map location?

royyeah commented 2 years ago

@Rylern Oh, you’re right, my bad. I confused it with another approach we tried. I think your solution works pretty well for visualizing value distribution on a global scale. For our use case however we ran into some issues we couldn’t solve:

Screenshot 2022-03-25 at 13 30 46
Rylern commented 2 years ago
* It covers the entire map. There is an option to limit it to a specific area, but we would prefer something more like the Mapbox heatmap, where the effect only applies where the points are.

* The points have to be passed at layer creation, which means we’ll have to recreate the layer every time we want to update the values. It would be nice if we could use a datasource for the points.
Rylern commented 2 years ago

@Rylern Oh, you’re right, my bad. I confused it with another approach we tried. I think your solution works pretty well for visualizing value distribution on a global scale. For our use case however we ran into some issues we couldn’t solve:

* It covers the entire map. There is an option to limit it to a specific area, but we would prefer something more like the Mapbox heatmap, where the effect only applies where the points are.

* The points have to be passed at layer creation, which means we’ll have to recreate the layer every time we want to update the values. It would be nice if we could use a datasource for the points.
Screenshot 2022-03-25 at 13 30 46

For future use, features addressing these two comments have been added in version 1.3.0 of the InterpolateHeatmapLayer.

mattbbc commented 1 year ago

Just to add my vote to a native feature to be able to use data expressions with the Mapbox GL heatmap layer - we have some Elasticsearch aggregations plotted on a map, each as a point with some additional metadata concerning the value of the relevant aggregation. It would be great to be able to hook into this info to determine the heatmap colour, rather than the density of the points.

SimonMeissner commented 7 months ago

@Rylern We tried your approach, but the rendering is not customizable (no custom colors) and rendering fast enough for our use case. We currently went for the approach suggested by @mourner (blurred circles to represent the temperatures), but we keep our eyes open for something better in the future!

Actually you can customize the colors, take a look at the valueToColor parameter. It allows you to define the function that maps a value to a color. Tell me the color scale you want, and I can help you define this parameter. When you say it's not rendering fast enough, what do you mean exactly? Is it when you first load the map, or after when you change the map location?

@Rylern How can I customize the colors? Can I use a custom color scale (from black to red to yellow to green) or can I only adjust the available color scale? I would be happy to receive an answer :)

Rylern commented 7 months ago

@Rylern How can I customize the colors? Can I use a custom color scale (from black to red to yellow to green) or can I only adjust the available color scale? I would be happy to receive an answer :)

Here is an example of using the valueToColor parameter (written without checking so not 100% sure):

vec3 valueToColor(float value) {
  if (value < 0.3333) {
    return vec3(value/0.3333, 0.0, 0.0);
  } else if (value < 0.6666) {
    return vec3(1., (value-0.3333) / 0.3333, 0.0);
  } else {
    return vec3(1. - (value-0.6666) / 0.3333, 1.0, 0.0);
  }
}

value belongs to [0; 1], so this function returns: vec3(0 0 0) with value=0 (black) vec3(1 0 0) with value=0.33 (red) vec3(1 1 0) with value=0.66 (yellow) vec3(0 1 0) with value=1 (green)

And an interpolated color between these values.

cscetbon commented 3 weeks ago

@SimonMeissner did it work for you ? I wasn't happy with the result I got... If yes, can you show some code ?