harunpehlivan
4/11/2018 - 7:51 AM

amCharts V4: Countries morphing to pie chart

amCharts V4: Countries morphing to pie chart

@import url("https://fonts.googleapis.com/css?family=Archivo+Narrow");

body {
  font-family: "Archivo Narrow";
  margin:0;
}
#chartdiv {
  width: 100%;
  height: 98vh;  
}
/**
 * --------------------------------------------------------
 * This demo was created using amCharts V4 preview release.
 *
 * V4 is the latest installement in amCharts data viz
 * library family, to be released in the first half of
 * 2018.
 *
 * For more information and documentation visit:
 * https://www.amcharts.com/docs/v4/
 * --------------------------------------------------------
 */
amcharts4.ready(function() {
  amcharts4.useTheme(amcharts4.themes.animated);

  var chart = amcharts4.create("chartdiv", amcharts4.map.MapChart);
  chart.geoJSON = amcharts4.maps.worldLow;
  chart.projection = new amcharts4.map.projections.Mercator();

  // zoomout on background click
  chart.chartContainer.background.events.on("hit", () => {
    zoomOut();
  });

  var colorSet = new amcharts4.ColorSet();
  var morphedPolygon;

  // map polygon series (countries)
  var polygonSeries = chart.series.push(new amcharts4.map.MapPolygonSeries());
  polygonSeries.getDataFromJSON = true;
  // specify which countries to include
  polygonSeries.include = [
    "IT",
    "CH",
    "FR",
    "DE",
    "GB",
    "ES",
    "PT",
    "IE",
    "NL",
    "LU",
    "BE",
    "AT",
    "DK"
  ];

  // country area look and behavior
  var polygonTemplate = polygonSeries.mapPolygons.template;
  polygonTemplate.strokeOpacity = 1;
  polygonTemplate.stroke = amcharts4.color("#ffffff");
  polygonTemplate.fillOpacity = 0.5;
  polygonTemplate.tooltipText = "{name}";
  polygonTemplate.cursorOverStyle = amcharts4.MouseCursorStyle.pointer

  // desaturate filter for countries
  var desaturateFilter = new amcharts4.DesaturateFilter();
  desaturateFilter.saturation = 0.25;
  polygonTemplate.filters.push(desaturateFilter);

  // take a color from color set
  polygonTemplate.adapter.add("fill", (fill, target) => {
    return colorSet.getIndex(target.dataItem.index + 1);
  });

  // set fillOpacity to 1 when hovered
  var hoverState = polygonTemplate.states.create("hover");
  hoverState.properties.fillOpacity = 1;

  // what to do when country is clicked
  polygonTemplate.events.on("hit", event => {
    selectPolygon(event.target);
  });

  // Pie chart
  var pieChart = chart.seriesContainer.createChild(amcharts4.pie.PieChart);
  // Set width/heigh of a pie chart for easier positioning only
  pieChart.width = 100;
  pieChart.height = 100;
  pieChart.visible = false;

  var pieSeries = pieChart.series.push(new amcharts4.pie.PieSeries());
  pieSeries.dataFields.value = "value";
  pieSeries.dataFields.category = "category";
  pieSeries.data = [
    { value: 100, category: "First" },
    { value: 20, category: "Second" },
    { value: 10, category: "Third" }
  ];

  var dropShadowFilter = new amcharts4.DropShadowFilter();
  dropShadowFilter.blur = 4;
  pieSeries.filters.push(dropShadowFilter);

  var sliceTemplate = pieSeries.slices.template;
  sliceTemplate.fillOpacity = 1;
  sliceTemplate.strokeOpacity = 0;

  var activeState = sliceTemplate.states.getKey("active");
  activeState.properties.shiftRadius = 0; // no need to pull on click, as country circle under the pie won't make it good

  var sliceHoverState = sliceTemplate.states.getKey("hover");
  sliceHoverState.properties.shiftRadius = 0; // no need to pull on click, as country circle under the pie won't make it good

  // we don't need default pie chart animation, so change defaults
  var hiddenState = pieSeries.hiddenState;
  hiddenState.properties.startAngle = pieSeries.startAngle;
  hiddenState.properties.endAngle = pieSeries.endAngle;
  hiddenState.properties.opacity = 0;
  hiddenState.properties.visible = false;

  // series labels
  var labelTemplate = pieSeries.labels.template;
  labelTemplate.fill = amcharts4.color("#FFFFFF");
  labelTemplate.background = new amcharts4.RoundedRectangle();
  labelTemplate.background.fillOpacity = 0.9;
  labelTemplate.padding(4, 9, 4, 9);
  labelTemplate.background.fill = amcharts4.color("#7678a0");

  // we need pie series to hide faster to avoid strange pause after country is clicked
  pieSeries.hiddenState.transitionDuration = 200;

  // country label
  var countryLabel = chart.chartContainer.createChild(amcharts4.Label);
  countryLabel.text = "Select a country";
  countryLabel.fill = amcharts4.color("#7678a0");
  countryLabel.fontSize = 40;

  countryLabel.hiddenState.properties.dy = 1000;
  countryLabel.defaultState.properties.dy = 0;
  countryLabel.valign = "middle";
  countryLabel.align = "right";
  countryLabel.paddingRight = 50;
  countryLabel.hide(0);
  countryLabel.show();

  // select polygon
  function selectPolygon(polygon) {
    if (morphedPolygon != polygon) {
      var animation = pieSeries.hide();
      if (animation) {
        animation.events.on("animationend", () => {
          morphToCircle(polygon);
        });
      } else {
        morphToCircle(polygon);
      }
    }
  }

  // fade out all countries except selected
  function fadeOut(exceptPolygon) {
    for (var i = 0; i < polygonSeries.mapPolygons.length; i++) {
      var polygon = polygonSeries.mapPolygons.getIndex(i);
      if (polygon != exceptPolygon) {
        polygon.defaultState.properties.fillOpacity = 0.5;
        polygon.animate(
          [
            { property: "fillOpacity", to: 0.5 },
            { property: "strokeOpacity", to: 1 }
          ],
          polygon.polygon.morpher.morphDuration
        );
      }
    }
  }

  function zoomOut() {
    if (morphedPolygon) {
      pieSeries.hide();
      morphBack();
      fadeOut();
      countryLabel.hide();
      morphedPolygon = undefined;
    }
  }

  function morphBack() {
    if (morphedPolygon) {
      morphedPolygon.polygon.morpher.morphBack();
      var dsf = morphedPolygon.filters.getIndex(0);
      dsf.animate(
        { property: "saturation", to: 0.25 },
        morphedPolygon.polygon.morpher.morphDuration
      );
    }
  }

  function morphToCircle(polygon) {
    var animationDuration = polygon.polygon.morpher.morphDuration;
    // if there is a country already morphed to circle, morph it back
    morphBack();
    // morph polygon to circle
    polygon.toFront();
    polygon.polygon.morpher.morphToSingle = true;
    var morphAnimation = polygon.polygon.morpher.morphToCircle();

    polygon.strokeOpacity = 0; // hide stroke for lines not to cross countries

    polygon.defaultState.properties.fillOpacity = 1;
    polygon.animate({ property: "fillOpacity", to: 1 }, animationDuration);

    // animate desaturate filter
    var filter = polygon.filters.getIndex(0);
    filter.animate({ property: "saturation", to: 1 }, animationDuration);

    // save currently morphed polygon
    morphedPolygon = polygon;

    // fade out all other
    fadeOut(polygon);

    // hide country label
    countryLabel.hide();

    if (morphAnimation) {
      morphAnimation.events.on("animationend", () => {
        zoomToCountry(polygon);
      });
    } else {
      zoomToCountry(polygon);
    }
  }

  function zoomToCountry(polygon) {
    var zoomAnimation = chart.zoomToMapObject(polygon, 0.5, true);
    if (zoomAnimation) {
      zoomAnimation.events.on("animationend", () => {
        showPieChart(polygon);
      });
    } else {
      showPieChart(polygon);
    }
  }

  function showPieChart(polygon) {
    polygon.polygon.measure();
    pieChart.radius =
      polygon.pixelWidth /
      2 *
      polygon.globalScale /
      chart.seriesContainer.scale;
    var centerPoint = amcharts4.utils.spritePointToSvg(
      polygon.polygon.centerPoint,
      polygon.polygon
    );
    centerPoint = amcharts4.utils.svgPointToSprite(
      centerPoint,
      chart.seriesContainer
    );

    pieChart.x = centerPoint.x - 50;
    pieChart.y = centerPoint.y - 50;

    var fill = polygon.fill;
    var desaturated = fill.saturate(0.3);

    for (var i = 0; i < pieSeries.dataItems.length; i++) {
      var dataItem = pieSeries.dataItems.getIndex(i);
      dataItem.value = Math.round(Math.random() * 100);
      dataItem.slice.fill = amcharts4.colors.interpolate(
        fill,
        amcharts4.color("#ffffff"),
        0.2 * i
      );

      dataItem.label.background.fill = desaturated;
      dataItem.tick.stroke = fill;
    }

    pieSeries.show();
    pieChart.show();

    countryLabel.text = "{name}";
    countryLabel.dataItem = polygon.dataItem;
    countryLabel.fill = desaturated;
    countryLabel.show();
  }
});
<script src="https://www.amcharts.com/lib/4/amcharts.js"></script>
<script src="https://www.amcharts.com/lib/4/pie.js"></script>
<script src="https://www.amcharts.com/lib/4/map.js"></script>
<script src="https://www.amcharts.com/lib/4/maps/worldLow.js"></script>
<script src="https://www.amcharts.com/lib/4/themes/animated.js"></script>
<div id="chartdiv"></div>

amCharts V4: Countries morphing to pie chart

A Pen by HARUN PEHLİVAN on CodePen.

License.