apache / echarts

Apache ECharts is a powerful, interactive charting and data visualization library for browser
https://echarts.apache.org
Apache License 2.0
60.2k stars 19.61k forks source link

[Feature] displaying a heatmap on a map within polygons, similar to a height level map #20205

Open Algorush opened 1 month ago

Algorush commented 1 month ago

What problem does this feature solve?

Is there a way to display points as a heatmap on the world map, but not on top of the geoJSON map, as in the example on heatmap-bmap example, but to draw them on the surface of the earth (map polygons) so that the radius and blur do not go beyond the boundaries of the territory. That is, like a height map, perhaps there are already examples with a height map in echarts? It would be great if someone could share or advise how to do this

What does the proposed API look like?

Perhaps it would be possible to draw directly on polygons in renderItem? I'm not very familiar with the API yet

Ovilia commented 1 month ago

Do you mean a 3D bar series on the globe or a contour map as #13909 ?

Algorush commented 1 month ago

Do you mean a 3D bar series on the globe or a contour map as #13909 ?

Thanks for these links! I mean contour maps. They are more similar to what I want. My question was essentially, is it possible to draw directly onto a polygon? If it were possible to draw a heatmap on a polygon, then it would be possible to draw a contour map

helgasoft commented 1 month ago

Check official example to see if it works for you.. If it doesn't, please explain why.

Algorush commented 1 month ago

Check official example to see if it works for you.. If it doesn't, please explain why.

this is not exactly what I need. But if it would be possible to make a smooth color transition between the map polygons in this example, then that would be good too. But I dont know if it's possible. I have points on a map with Float numeric values, and I wanted to make a heatmap for them, like in this example (https://echarts.apache.org/examples/zh/editor.html?c=heatmap-bmap) but so that this heatmap does not extend beyond the ground. That is, it is similar to contour map.

Ovilia commented 1 month ago

@Algorush There are several map extensions for Apache ECharts. You may check which one works for you. test/custom-bmap-polygon.html is an example using Baidu Map and Apache ECharts custom series to draw the polygon. It should be similar if you are using other extensions.

image
Algorush commented 1 month ago

Thanks for help! I made three series on a map with geo component, using geoJson data: 'Heatmap', 'Scatter', and a Custom Series that fills the polygons in the renderItem functon. Everything is working good. Only I can’t use api.visual('color') in renderItem if there are three series. If I leave only custom series, then api.visual('color') works. Cant understand why. Another issue. I added legend to enable and disable series. Heatmap and Scatter switch well, but Custom does not disappear. It only disappears if I select Scatter instead, but not Heatmap... What could be the reason? I will be grateful for help.

My code with options data:

            option = {
              dataset: [
                {
                  id: 'mapdata',
                  source: transformedData
                }
              ],
              animation: false,
              geo: {
                  map: 'world-with-states-provinces',
                  roam: true,
                  label: { 
                      show: false
                  },
                  itemStyle: {
                      normal: { areaColor: '#f5f5f5', borderColor: '#111' },
                      emphasis: { areaColor: '#ddd' }
                  }
              },
              legend: {
                  data: ['Heatmap', 'Scatter', 'AverageDistance'],
                  zlevel: 10,
                  z: 2,
                  selectedMode: 'multiple',
                  selected: {
                    'Heatmap': false,
                    'Scatter': false
                  }
              },
              visualMap: {
                  text: ['High', 'Low'],
                  min: 0,
                  max: 20,
                  calculable: true,
                  inRange: { color: ['green', 'yellow', 'orange', 'red'] },
                  textStyle: { color: '#000' }
              },
              series: [
                {
                  name: 'Heatmap',
                  type: 'heatmap',
                  seriesIndex: 0,
                  coordinateSystem: 'geo',
                  zlevel: 1,
                  data: transformedData,
                  pointSize: 10,
                  blurSize: 10,
                  geoIndex: 0,
                  minOpacity: 0.2,
                  maxOpacity: 0.8,
                  symbolSize: 5,
                  encode: {
                    value: 2,
                    tooltip: [2]
                  }
                },
                {
                  name: 'Scatter',
                  type: 'scatter',
                  seriesIndex: 1,
                  zlevel: 2,
                  coordinateSystem: 'geo',
                  data: transformedData,
                  geoIndex: 0,
                  pointSize: 1,
                  itemStyle: {
                    color: function (params) {
                        return getColorForDistance(params.value[2])
                    }
                  }
                },
                {
                  name: 'AverageDistance',
                  type: 'custom',
                  zlevel: 0,
                  encode: {
                      value: 'avgDist', // Map the third element (avgDist) in the value array to the visualMap
                      tooltip: 'avgDist' // Show avgDist in the tooltip
                  },
                  seriesIndex: 2,
                  coordinateSystem: 'geo',
                  renderItem: function (params, api) {
                  const item = customSeriesData[params.dataIndex];
                  const fillColor = getColorForDistance(item.avgDist);
                  if (item && item.geometry) {
                      return {
                        type: 'group',
                        children:
                          item.geometry.coordinates.map(coords => {
                            if (coords.length === 1) {
                              coords = coords[0];
                            }

                            const points = [];
                            for (let i = 0; i < coords.length; i++) {
                              points.push(api.coord(coords[i]));
                            }
                            return {
                              type: 'polygon',
                              shape: {
                                points: points
                              },
                              style: api.style({stroke: 'white', lineWidth: 0.5, shadowBlur: 40, opacity: 0.9, fill: fillColor, shadowColor: fillColor})
                            }
                          })
                      }
                    }
                  },
                  data: customSeriesData
                }
              ]
            };