Search code examples
javascriptdictionarychartsamchartsamcharts4

amCharts Map with Bubbles on lat log coordinates


It's possible to create exactly this kind of map:

https://www.amcharts.com/demos/map-bubbles/

but instead of positioning bubbles using the coutry ID doing it via latitude and longitude coordinates? I need to place a bubble for each city that i have data so, probably, more than one bubble per country.

I have a data structure like this:

Array (
        "name" => $row["city"],
        "latitude" => $row["latitude"],
        "longitude" => $row["longitude"],
        "value" => $row["count"],
    );

Solution

  • I was facing the same struggle, managed to solve it.

    So the data you assign to the image series need the properties latitude, longitude and value so that the bubble can be placed and the size can be determined.

    imageSeries.dataFields.value will be assigned the value property from your data, while imageTemplate.propertyFields.latitude and imageTemplate.propertyFields.longitude will pick up the longitude and latitude from your data.

    The function is written in JavaScript, looks a bit like below:

    import * as am4core from "@amcharts/amcharts4/core";
    import * as am4maps from "@amcharts/amcharts4/maps";
    import am4geodata_worldLow from "@amcharts/amcharts4-geodata/worldLow";
    
    /* This is what the data looks like  
    [
      {
        latitude: 48.856614,
        longitude: 2.352222,
        value: 100,
        title: "Paris",
      },
      {
        latitude: 40.712775,
        longitude: -74.005973,
        value: 200,
        title: "New York",
      },
      {
        latitude: 49.282729,
        longitude: -123.120738,
        value: 1000,
        title: "Vancouver",
      },
    ];
    */
    
    const setupBubbleMap = ({ elementId }) => {
      let amChart = am4core.create(elementId, am4maps.MapChart);
    
      let title = amChart.titles.create();
      title.text = "[bold font-size: 20]Hotspots[/]";
      title.textAlign = "middle";
    
      amChart.geodata = am4geodata_worldLow;
      amChart.projection = new am4maps.projections.Miller();
    
      let polygonSeries = amChart.series.push(new am4maps.MapPolygonSeries());
      polygonSeries.exclude = ["AQ"];
      polygonSeries.useGeodata = true;
      polygonSeries.nonScalingStroke = true;
      polygonSeries.strokeWidth = 0.5;
    
      const polygonTemplate = polygonSeries.mapPolygons.template;
      polygonTemplate.tooltipText = "{title}";
      polygonSeries.calculateVisualCenter = true;
    
      let imageSeries = amChart.series.push(new am4maps.MapImageSeries());
      imageSeries.dataFields.value = "value";
    
      let imageTemplate = imageSeries.mapImages.template;
      imageTemplate.nonScaling = true;
      imageTemplate.propertyFields.latitude = "latitude";
      imageTemplate.propertyFields.longitude = "longitude";
    
      imageSeries.data = data;
    
      let circle = imageTemplate.createChild(am4core.Circle);
      circle.fillOpacity = 0.7;
      circle.fill = am4core.color("#B27799");
      circle.tooltipText = "{title}: [bold]{value}[/]";
    
      imageSeries.heatRules.push({
        target: circle,
        property: "radius",
        min: 4,
        max: 30,
        dataField: "value",
      });
    
      imageTemplate.adapter.add("latitude", function (latitude, target) {
        let polygon = polygonSeries.getPolygonById(target.dataItem.dataContext.id);
        if (polygon) {
          return polygon.visualLatitude;
        }
        return latitude;
      });
    
      imageTemplate.adapter.add("longitude", function (longitude, target) {
        let polygon = polygonSeries.getPolygonById(target.dataItem.dataContext.id);
        if (polygon) {
          return polygon.visualLongitude;
        }
        return longitude;
      });
    
      return amChart;
    };
    
    export default setupBubbleMap;