• Open in:

Map with dynamic Pie charts

Since any amCharts instance, bet it map or chart of any type, can be shown on the same page without any conflicts, we can use all of them to create a nice infographics.

This demo shows, how we can place various Pie charts on a map.

First of all, we’re going to “disguise” pie charts as map images. A map image is any image that can be placed at specific latitude/longitude coordinate. A regular circle image would go into images array in dataProvider and would look something like this:

"images": [ {
  "title": "North America",
  "latitude": 39.563353,
  "longitude": -99.316406,
  "width": 150,
  "height": 150,
  "type": "circle"
} ]

Since we’re not aiming for simple circles, but would rather display a nice interactive pie chart, we’re going to replace the type part with a proprietary pie block, which as you probably already guessed, will contain Pie chart’s config:

"images": [ {
  "title": "North America",
  "latitude": 39.563353,
  "longitude": -99.316406,
  "width": 150,
  "height": 150,
  "pie": {
    "type": "pie",
    "pullOutRadius": 0,
    "labelRadius": 0,
    "dataProvider": [ {
      "category": "Category #1",
      "value": 1200
    }, {
      "category": "Category #2",
      "value": 500
    }, {
      "category": "Category #3",
      "value": 765
    } ],
    "labelText": "[[value]]%",
    "valueField": "value",
    "titleField": "category"
  }
} ]

Since JavaScript Maps does not support this kind of config out-of-the-box, we’re going to use an event handler positionChanged to:

  1. Create custom HTML container for each image in dataProvider;
  2. Position those containers over the map, right where the map image would be;
  3. Use the Pie chart config container in image data to create an actual live chart;
  4. Update position of the chart containers when the map is zoomed or moved.

Demo source

<!-- Styles -->
<style>
#chartdiv {
  width: 100%;
  height: 500px;
}
</style>

<!-- Resources -->
<script src="https://www.amcharts.com/lib/3/amcharts.js"></script>
<script src="https://www.amcharts.com/lib/3/pie.js"></script>
<script src="https://www.amcharts.com/lib/3/ammap_amcharts_extension.js"></script>
<script src="https://www.amcharts.com/lib/3/maps/js/continentsLow.js"></script>
<script src="https://www.amcharts.com/lib/3/plugins/export/export.min.js"></script>
<link rel="stylesheet" href="https://www.amcharts.com/lib/3/plugins/export/export.css" type="text/css" media="all" />

<!-- Chart code -->
<script>
/**
 * Create a map
 */
var map = AmCharts.makeChart("chartdiv", {
  "type": "map",
  "theme": "none",
  "projection": "winkel3",

  /**
   * Data Provider
   * The images contains pie chart information
   * The handler for `positionChanged` event will take care
   * of creating external elements, position them and create
   * Pie chart instances in them
   */
  "dataProvider": {
    "map": "continentsLow",
    "images": [{
      "title": "North America",
      "latitude": 39.563353,
      "longitude": -99.316406,
      "width": 150,
      "height": 150,
      "pie": {
        "type": "pie",
        "pullOutRadius": 0,
        "labelRadius": 0,
        "dataProvider": [{
          "category": "Category #1",
          "value": 1200
        }, {
          "category": "Category #2",
          "value": 500
        }, {
          "category": "Category #3",
          "value": 765
        }, {
          "category": "Category #4",
          "value": 260
        }],
        "labelText": "[[value]]%",
        "valueField": "value",
        "titleField": "category"
      }
    }, {
      "title": "Europe",
      "latitude": 50.896104,
      "longitude": 19.160156,
      "width": 200,
      "height": 200,
      "pie": {
        "type": "pie",
        "pullOutRadius": 0,
        "labelRadius": 0,
        "radius": "10%",
        "dataProvider": [{
          "category": "Category #1",
          "value": 200
        }, {
          "category": "Category #2",
          "value": 600
        }, {
          "category": "Category #3",
          "value": 350
        }],
        "labelText": "",
        "valueField": "value",
        "titleField": "category"
      }
    }, {
      "title": "Asia",
      "latitude": 47.212106,
      "longitude": 103.183594,
      "width": 200,
      "height": 200,
      "pie": {
        "type": "pie",
        "pullOutRadius": 0,
        "labelRadius": 0,
        "radius": "10%",
        "dataProvider": [{
          "category": "Category #1",
          "value": 352
        }, {
          "category": "Category #2",
          "value": 266
        }, {
          "category": "Category #3",
          "value": 512
        }, {
          "category": "Category #4",
          "value": 199
        }],
        "labelText": "",
        "valueField": "value",
        "titleField": "category"
      }
    }, {
      "title": "Africa",
      "latitude": 11.081385,
      "longitude": 21.621094,
      "width": 200,
      "height": 200,
      "pie": {
        "type": "pie",
        "pullOutRadius": 0,
        "labelRadius": 0,
        "radius": "10%",
        "dataProvider": [{
          "category": "Category #1",
          "value": 200
        }, {
          "category": "Category #2",
          "value": 300
        }, {
          "category": "Category #3",
          "value": 599
        }, {
          "category": "Category #4",
          "value": 512
        }],
        "labelText": "",
        "valueField": "value",
        "titleField": "category"
      }
    }]
  },
  
  /**
   * Add event to execute when the map is zoomed/moved
   * It also is executed when the map first loads
   */
  "listeners": [{
    "event": "positionChanged",
    "method": updateCustomMarkers
  }]
});

/**
 * Creates and positions custom markers (pie charts)
 */
function updateCustomMarkers(event) {
  // get map object
  var map = event.chart;

  // go through all of the images
  for (var x = 0; x < map.dataProvider.images.length; x++) {

    // get MapImage object
    var image = map.dataProvider.images[x];

    // Is it a Pie?
    if (image.pie === undefined) {
      continue;
    }

    // create id
    if (image.id === undefined) {
      image.id = "amcharts_pie_" + x;
    }
    // Add theme
    if ("undefined" == typeof image.pie.theme) {
      image.pie.theme = map.theme;
    }

    // check if it has corresponding HTML element
    if ("undefined" == typeof image.externalElement) {
      image.externalElement = createCustomMarker(image);
    }

    // reposition the element accoridng to coordinates
    var xy = map.coordinatesToStageXY(image.longitude, image.latitude);
    image.externalElement.style.top = xy.y + "px";
    image.externalElement.style.left = xy.x + "px";
    image.externalElement.style.marginTop = Math.round(image.height / -2) + "px";
    image.externalElement.style.marginLeft = Math.round(image.width / -2) + "px";
  }
}

/**
 * Creates a custom map marker - a div for container and a
 * pie chart in it
 */
function createCustomMarker(image) {

  // Create chart container
  var holder = document.createElement("div");
  holder.id = image.id;
  holder.title = image.title;
  holder.style.position = "absolute";
  holder.style.width = image.width + "px";
  holder.style.height = image.height + "px";

  // Append the chart container to the map container
  image.chart.chartDiv.appendChild(holder);

  // Create a pie chart
  var chart = AmCharts.makeChart(image.id, image.pie);

  return holder;
}
</script>

<!-- HTML -->
<div id="chartdiv"></div>