recent
أخبار ساخنة

اضافه عداد فيروس كورونا (كوفيد 19) و خريطه انتشاره والحالات الجديده والوفيات في جميع البلدان لمدونات بلوجر

اضافه عداد فيروس كورونا (كوفيد 19) عافانا الله و اياكم من السوء  و خريطه انتشاره والحالات الجديده والوفيات في جميع البلدان لمدونات بلوجر

Corona Virus Counter (Covid 19), may God bless us and you, may God protect us from badness, its spread map, new cases and deaths in all countries

يمكنك اضافه الاداه لمدونه بلوجر عن طريق وضع الاكواد التاليه في المشاركه او في صفحه او من خلال التخطيط


<div dir="rtl" style="text-align: right;" trbidi="on">
يعرض هذا النمودج الجدول الزمني لأوبئة COVID-19.
<br />
<!-- Styles -->
<style>
#chartdiv {
  max-width: 100%;
  height: 800px;
  background-color:#212327;
}
</style>

<!-- Resources -->
<script src="https://www.amcharts.com/lib/4/core.js"></script>
<script src="https://www.amcharts.com/lib/4/charts.js"></script>
<script src="https://www.amcharts.com/lib/4/maps.js"></script>
<script src="https://www.amcharts.com/lib/4/geodata/worldLow.js"></script>
<script src="https://covid.amCharts.com/data/js/world_timeline.js"></script>
<script src="https://covid.amCharts.com/data/js/total_timeline.js"></script>
<script src="https://www.amcharts.com/lib/4/themes/animated.js"></script>

<!-- Chart code -->
<script>
am4core.ready(function() {

// Themes begin
am4core.useTheme(am4themes_animated);
// Themes end

var backgroundColor = am4core.color("#1e2128");
  var activeColor = am4core.color("#ff8726");
  var confirmedColor = am4core.color("#d21a1a");
  var recoveredColor = am4core.color("#45d21a");
  var deathsColor = am4core.color("#1c5fe5");

  // for an easier access by key
  var colors = { active: activeColor, confirmed: confirmedColor, recovered: recoveredColor, deaths: deathsColor };

  var countryColor = am4core.color("#3b3b3b");
  var countryStrokeColor = am4core.color("#000000");
  var buttonStrokeColor = am4core.color("#ffffff");
  var countryHoverColor = am4core.color("#1b1b1b");
  var activeCountryColor = am4core.color("#0f0f0f");

  var currentIndex;
  var currentCountry = "World";

  // last date of the data
  var lastDate = new Date(covid_total_timeline[covid_total_timeline.length - 1].date);
  var currentDate = lastDate;

  var currentPolygon;

  var countryDataTimeout;

  var currentType;

  var sliderAnimation;
  //////////////////////////////////////////////////////////////////////////////
  // PREPARE DATA
  //////////////////////////////////////////////////////////////////////////////

  // make a map of country indexes for later use
  var countryIndexMap = {};
  var list = covid_world_timeline[covid_world_timeline.length - 1].list;
  for (var i = 0; i < list.length; i++) {
    var country = list[i]
    countryIndexMap[country.id] = i;
  }

  // calculated active cases in world data (active = confirmed - recovered)
  for (var i = 0; i < covid_total_timeline.length; i++) {
    var di = covid_total_timeline[i];
    di.active = di.confirmed - di.recovered;
  }

  // function that returns current slide
  // if index is not set, get last slide
  function getSlideData(index) {
    if (index == undefined) {
      index = covid_world_timeline.length - 1;
    }

    var data = covid_world_timeline[index];

    return data;
  }

  // get slide data
  var slideData = getSlideData();

  // as we will be modifying raw data, make a copy
  var mapData = JSON.parse(JSON.stringify(slideData.list));
  var max = { confirmed: 0, recovered: 0, deaths: 0 };

  // the last day will have most
  for (var i = 0; i < mapData.length; i++) {
    var di = mapData[i];
    if (di.confirmed > max.confirmed) {
      max.confirmed = di.confirmed;
    }
    if (di.recovered > max.recovered) {
      max.recovered = di.recovered;
    }
    if (di.deaths > max.deaths) {
      max.deaths = di.deaths
    }
    max.active = max.confirmed;
  }

  // END OF DATA

  //////////////////////////////////////////////////////////////////////////////
  // LAYOUT & CHARTS
  //////////////////////////////////////////////////////////////////////////////

  // main container
  // https://www.amcharts.com/docs/v4/concepts/svg-engine/containers/
  var container = am4core.create("chartdiv", am4core.Container);
  container.width = am4core.percent(100);
  container.height = am4core.percent(100);
container.fontSize = "0.9em";

  // MAP CHART 
  // https://www.amcharts.com/docs/v4/chart-types/map/
  var mapChart = container.createChild(am4maps.MapChart);
  mapChart.height = am4core.percent(80);
  mapChart.zoomControl = new am4maps.ZoomControl();
  mapChart.zoomControl.align = "right";
  mapChart.zoomControl.marginRight = 15;
  mapChart.zoomControl.valign = "middle";

  // by default minus button zooms out by one step, but we modify the behavior so when user clicks on minus, the map would fully zoom-out and show world data
  mapChart.zoomControl.minusButton.events.on("hit", showWorld);
  // clicking on a "sea" will also result a full zoom-out
  mapChart.seriesContainer.background.events.on("hit", showWorld);
  mapChart.seriesContainer.background.events.on("over", resetHover);
  mapChart.seriesContainer.background.fillOpacity = 0;
  mapChart.zoomEasing = am4core.ease.sinOut;

  // https://www.amcharts.com/docs/v4/chart-types/map/#Map_data
  // you can use more accurate world map or map of any other country - a wide selection of maps available at: https://github.com/amcharts/amcharts4-geodata
  mapChart.geodata = am4geodata_worldLow;

  // Set projection
  // https://www.amcharts.com/docs/v4/chart-types/map/#Setting_projection
  // instead of Miller, you can use Mercator or many other projections available: https://www.amcharts.com/demos/map-using-d3-projections/
  mapChart.projection = new am4maps.projections.Miller();
  mapChart.panBehavior = "move";

  // when map is globe, beackground is made visible
  mapChart.backgroundSeries.mapPolygons.template.polygon.fillOpacity = 0.05;
  mapChart.backgroundSeries.mapPolygons.template.polygon.fill = am4core.color("#ffffff");
  mapChart.backgroundSeries.hidden = true;


  // Map polygon series (defines how country areas look and behave)
  var polygonSeries = mapChart.series.push(new am4maps.MapPolygonSeries());
  polygonSeries.dataFields.id = "id";
  polygonSeries.exclude = ["AQ"]; // Antarctica is excluded in non-globe projection
  polygonSeries.useGeodata = true;
  polygonSeries.nonScalingStroke = true;
  polygonSeries.strokeWidth = 0.5;
  // this helps to place bubbles in the visual middle of the area
  polygonSeries.calculateVisualCenter = true;

  var polygonTemplate = polygonSeries.mapPolygons.template;
  polygonTemplate.fill = countryColor;
  polygonTemplate.fillOpacity = 1
  polygonTemplate.stroke = countryStrokeColor;
  polygonTemplate.strokeOpacity = 0.15
  polygonTemplate.setStateOnChildren = true;

  polygonTemplate.events.on("hit", handleCountryHit);
  polygonTemplate.events.on("over", handleCountryOver);
  polygonTemplate.events.on("out", handleCountryOut);

  // you can have pacific - centered map if you set this to -154.8
  mapChart.deltaLongitude = -10;

  // polygon states
  var polygonHoverState = polygonTemplate.states.create("hover");
  polygonHoverState.properties.fill = countryHoverColor;

  var polygonActiveState = polygonTemplate.states.create("active")
  polygonActiveState.properties.fill = activeCountryColor;

  // Bubble series
  var bubbleSeries = mapChart.series.push(new am4maps.MapImageSeries());
  bubbleSeries.data = mapData;
  bubbleSeries.dataFields.value = "confirmed";
  bubbleSeries.dataFields.id = "id";

  // adjust tooltip
  bubbleSeries.tooltip.animationDuration = 0;
  bubbleSeries.tooltip.showInViewport = false;
  bubbleSeries.tooltip.background.fillOpacity = 0.2;
  bubbleSeries.tooltip.getStrokeFromObject = true;
  bubbleSeries.tooltip.getFillFromObject = false;
  bubbleSeries.tooltip.background.fillOpacity = 0.2;
  bubbleSeries.tooltip.background.fill = am4core.color("#000000");

  var imageTemplate = bubbleSeries.mapImages.template;
  // if you want bubbles to become bigger when zoomed, set this to false
  imageTemplate.nonScaling = true;
  imageTemplate.strokeOpacity = 0;
  imageTemplate.fillOpacity = 0.4;
  imageTemplate.tooltipText = "{name}: [bold]{value}[/]";
  // this is needed for the tooltip to point to the top of the circle instead of the middle
  //imageTemplate.adapter.add("tooltipY", function(tooltipY, target) {
  //  return -target.children.getIndex(0).radius;
  //})


  imageTemplate.events.on("over", handleImageOver);
  imageTemplate.events.on("out", handleImageOut);
  imageTemplate.events.on("hit", handleImageHit);

  // When hovered, circles become non-opaque  
  var imageHoverState = imageTemplate.states.create("hover");
  imageHoverState.properties.fillOpacity = 1;

  // add circle inside the image
  var circle = imageTemplate.createChild(am4core.Circle);
  // this makes the circle to pulsate a bit when showing it
  circle.hiddenState.properties.scale = 0.0001;
  circle.hiddenState.transitionDuration = 2000;
  circle.defaultState.transitionDuration = 2000;
  circle.defaultState.transitionEasing = am4core.ease.elasticOut;
  // later we set fill color on template (when changing what type of data the map should show) and all the clones get the color because of this
  circle.applyOnClones = true;

  // heat rule makes the bubbles to be of a different width. Adjust min/max for smaller/bigger radius of a bubble
  bubbleSeries.heatRules.push({
    "target": circle,
    "property": "radius",
    "min": 3,
    "max": 30,
    "dataField": "value"
  })

  // when data items validated, hide 0 value bubbles (because min size is set)
  bubbleSeries.events.on("dataitemsvalidated", function() {
    bubbleSeries.dataItems.each((dataItem) => {
      var mapImage = dataItem.mapImage;
      var circle = mapImage.children.getIndex(0);
      if (mapImage.dataItem.value == 0) {
        circle.hide(0);
      }
      else if (circle.isHidden || circle.isHiding) {
        circle.show();
      }
    })
  })

  // this places bubbles at the visual center of a country
  imageTemplate.adapter.add("latitude", function(latitude, target) {
    var polygon = polygonSeries.getPolygonById(target.dataItem.id);
    if (polygon) {
      target.disabled = false;
      return polygon.visualLatitude;
    }
    else {
      target.disabled = true;
    }
    return latitude;
  })

  imageTemplate.adapter.add("longitude", function(longitude, target) {
    var polygon = polygonSeries.getPolygonById(target.dataItem.id);
    if (polygon) {
      target.disabled = false;
      return polygon.visualLongitude;
    }
    else {
      target.disabled = true;
    }
    return longitude;
  })

  // END OF MAP  

  // top title
  var title = mapChart.titles.create();
  title.fontSize = "1.5em";
  title.text = "COVID-19 Spread Data";
  title.align = "left";
  title.horizontalCenter = "left";
  title.marginLeft = 20;
  title.paddingBottom = 10;
  title.fill = am4core.color("#ffffff");
  title.y = 20;

  // switch between map and globe
  var mapGlobeSwitch = mapChart.createChild(am4core.SwitchButton);
  mapGlobeSwitch.align = "right"
  mapGlobeSwitch.y = 15;
  mapGlobeSwitch.leftLabel.text = "Map";
  mapGlobeSwitch.leftLabel.fill = am4core.color("#ffffff");
  mapGlobeSwitch.rightLabel.fill = am4core.color("#ffffff");
  mapGlobeSwitch.rightLabel.text = "Globe";
  mapGlobeSwitch.verticalCenter = "top";


  mapGlobeSwitch.events.on("toggled", function() {
    if (mapGlobeSwitch.isActive) {
      mapChart.projection = new am4maps.projections.Orthographic;
      mapChart.backgroundSeries.show();
      mapChart.panBehavior = "rotateLongLat";
      polygonSeries.exclude = [];
    } else {
      mapChart.projection = new am4maps.projections.Miller;
      mapChart.backgroundSeries.hide();
      mapChart.panBehavior = "move";
      polygonSeries.data = [];
      polygonSeries.exclude = ["AQ"];
    }
  })

  // buttons & chart container
  var buttonsAndChartContainer = container.createChild(am4core.Container);
  buttonsAndChartContainer.layout = "vertical";
  buttonsAndChartContainer.height = am4core.percent(40); // make this bigger if you want more space for the chart
  buttonsAndChartContainer.width = am4core.percent(100);
  buttonsAndChartContainer.valign = "bottom";

  // country name and buttons container
  var nameAndButtonsContainer = buttonsAndChartContainer.createChild(am4core.Container)
  nameAndButtonsContainer.width = am4core.percent(100);
  nameAndButtonsContainer.padding(0, 10, 5, 20);
  nameAndButtonsContainer.layout = "horizontal";

  // name of a country and date label
  var countryName = nameAndButtonsContainer.createChild(am4core.Label);
  countryName.fontSize = "1.1em";
  countryName.fill = am4core.color("#ffffff");
  countryName.valign = "middle";

  // buttons container (active/confirmed/recovered/deaths)
  var buttonsContainer = nameAndButtonsContainer.createChild(am4core.Container);
  buttonsContainer.layout = "grid";
  buttonsContainer.width = am4core.percent(100);
  buttonsContainer.x = 10;
  buttonsContainer.contentAlign = "right";

  // Chart & slider container
  var chartAndSliderContainer = buttonsAndChartContainer.createChild(am4core.Container);
  chartAndSliderContainer.layout = "vertical";
  chartAndSliderContainer.height = am4core.percent(100);
  chartAndSliderContainer.width = am4core.percent(100);  
  chartAndSliderContainer.background = new am4core.RoundedRectangle();
  chartAndSliderContainer.background.fill = am4core.color("#000000");
  chartAndSliderContainer.background.cornerRadius(30, 30, 0, 0)
  chartAndSliderContainer.background.fillOpacity = 0.25;
  chartAndSliderContainer.paddingTop = 12;
  chartAndSliderContainer.paddingBottom = 0;

  // Slider container
  var sliderContainer = chartAndSliderContainer.createChild(am4core.Container);
  sliderContainer.width = am4core.percent(100);
  sliderContainer.padding(0, 15, 15, 10);
  sliderContainer.layout = "horizontal";

  var slider = sliderContainer.createChild(am4core.Slider);
  slider.width = am4core.percent(100);
  slider.valign = "middle";
  slider.background.opacity = 0.4;
  slider.opacity = 0.7;
  slider.background.fill = am4core.color("#ffffff");
  slider.marginLeft = 20;
  slider.marginRight = 35;
  slider.height = 15;
  slider.start = 1;


  // what to do when slider is dragged
  slider.events.on("rangechanged", function(event) {
    var index = Math.round((covid_world_timeline.length - 1) * slider.start);
    updateMapData(getSlideData(index).list);
    updateTotals(index);
  })
  // stop animation if dragged
  slider.startGrip.events.on("drag", () => {
    stop();
    if (sliderAnimation) {
      sliderAnimation.setProgress(slider.start);
    }
  });

  // play button
  var playButton = sliderContainer.createChild(am4core.PlayButton);
  playButton.valign = "middle";
  // play button behavior
  playButton.events.on("toggled", function(event) {
    if (event.target.isActive) {
      play();
    } else {
      stop();
    }
  })
  // make slider grip look like play button
  slider.startGrip.background.fill = playButton.background.fill;
  slider.startGrip.background.strokeOpacity = 0;
  slider.startGrip.icon.stroke = am4core.color("#ffffff");
  slider.startGrip.background.states.copyFrom(playButton.background.states)


  // bubble size slider
  var sizeSlider = container.createChild(am4core.Slider);
  sizeSlider.orientation = "vertical";
  sizeSlider.height = am4core.percent(12);
  sizeSlider.marginLeft = 25;
  sizeSlider.align = "left";
  sizeSlider.valign = "top";
  sizeSlider.verticalCenter = "middle";
  sizeSlider.opacity = 0.7;
  sizeSlider.background.fill = am4core.color("#ffffff");
  sizeSlider.adapter.add("y", function(y, target) {
    return container.pixelHeight * (1 - buttonsAndChartContainer.percentHeight / 100) * 0.25;
  })

  sizeSlider.startGrip.background.fill = activeColor;
  sizeSlider.startGrip.background.fillOpacity = 0.8;
  sizeSlider.startGrip.background.strokeOpacity = 0;
  sizeSlider.startGrip.icon.stroke = am4core.color("#ffffff");
  sizeSlider.startGrip.background.states.getKey("hover").properties.fill = activeColor;
  sizeSlider.startGrip.background.states.getKey("down").properties.fill = activeColor;
  sizeSlider.horizontalCenter = "middle";


  sizeSlider.events.on("rangechanged", function() {
    sizeSlider.startGrip.scale = 0.75 + sizeSlider.start;
    bubbleSeries.heatRules.getIndex(0).max = 30 + sizeSlider.start * 100;
    circle.clones.each(function(clone) {
      clone.radius = clone.radius;
    })
  })


  var sizeLabel = container.createChild(am4core.Label);
  sizeLabel.text = "max bubble size *";
  sizeLabel.fill = am4core.color("#ffffff");
  sizeLabel.rotation = 90;
  sizeLabel.fontSize = "0.8em";
  sizeLabel.fillOpacity = 0.5;
  sizeLabel.horizontalCenter = "middle";
  sizeLabel.align = "left"
  sizeLabel.paddingBottom = 40;
  sizeLabel.tooltip = new am4core.Tooltip();
  sizeLabel.tooltip.setBounds({ x: 0, y: 0, width: 200000, height: 200000 })
  sizeLabel.tooltip.label.wrap = true;
  sizeLabel.tooltip.label.maxWidth = 300;
  sizeLabel.tooltipText = "Some countries have so many cases that bubbles for countries with smaller values often look the same even if there is a significant difference between them. This slider can be used to increase maximum size of a bubble so that when you zoom in to a region with relatively small values you could compare them anyway."
  sizeLabel.fill = am4core.color("#ffffff");

  sizeLabel.adapter.add("y", function(y, target) {
    return container.pixelHeight * (1 - buttonsAndChartContainer.percentHeight / 100) * 0.25;
  })

  // filter slider

  // bubble size slider
  var filterSlider = container.createChild(am4core.Slider);
  filterSlider.orientation = "vertical";
  filterSlider.height = am4core.percent(28);
  filterSlider.marginLeft = 25;
  filterSlider.align = "left";
  filterSlider.valign = "top";
  filterSlider.verticalCenter = "middle";
  filterSlider.opacity = 0.7;
  filterSlider.background.fill = am4core.color("#ffffff");
  filterSlider.adapter.add("y", function(y, target) {
    return container.pixelHeight * (1 - buttonsAndChartContainer.percentHeight / 100) * 0.7;
  })

  filterSlider.startGrip.background.fill = activeColor;
  filterSlider.startGrip.background.fillOpacity = 0.8;
  filterSlider.startGrip.background.strokeOpacity = 0;
  filterSlider.startGrip.icon.stroke = am4core.color("#ffffff");
  filterSlider.startGrip.background.states.getKey("hover").properties.fill = activeColor;
  filterSlider.startGrip.background.states.getKey("down").properties.fill = activeColor;
  filterSlider.horizontalCenter = "middle";
  filterSlider.start = 1;


  filterSlider.events.on("rangechanged", function() {
    var maxValue = max[currentType] * filterSlider.start + 1;
    if (!isNaN(maxValue) && bubbleSeries.inited) {
      bubbleSeries.heatRules.getIndex(0).maxValue = maxValue;
      circle.clones.each(function(clone) {
        if (clone.dataItem.value > maxValue) {
          clone.dataItem.hide();
        }
        else {
          clone.dataItem.show();
        }
        clone.radius = clone.radius;
      })
    }
  })


  var filterLabel = container.createChild(am4core.Label);
  filterLabel.text = "filter max values *";
  filterLabel.rotation = 90;
  filterLabel.fill = am4core.color("#ffffff");
  filterLabel.fontSize = "0.8em";
  filterLabel.fillOpacity = 0.5;
  filterLabel.horizontalCenter = "middle";
  filterLabel.align = "left"
  filterLabel.paddingBottom = 40;
  filterLabel.tooltip = new am4core.Tooltip();
  filterLabel.tooltip.setBounds({ x: 0, y: 0, width: 200000, height: 200000 })
  filterLabel.tooltip.label.wrap = true;
  filterLabel.tooltip.label.maxWidth = 300;
  filterLabel.tooltipText = "This filter allows to remove countries with many cases from the map so that it would be possible to compare countries with smaller number of cases."
  filterLabel.fill = am4core.color("#ffffff");

  filterLabel.adapter.add("y", function(y, target) {
    return container.pixelHeight * (1 - buttonsAndChartContainer.percentHeight / 100) * 0.7;
  })



  // play behavior
  function play() {
    if (!sliderAnimation) {
      sliderAnimation = slider.animate({ property: "start", to: 1, from: 0 }, 50000, am4core.ease.linear).pause();
      sliderAnimation.events.on("animationended", () => {
        playButton.isActive = false;
      })
    }

    if (slider.start >= 1) {
      slider.start = 0;
      sliderAnimation.start();
    }
    sliderAnimation.resume();
    playButton.isActive = true;
  }

  // stop behavior
  function stop() {
    if (sliderAnimation) {
      sliderAnimation.pause();
    }
    playButton.isActive = false;
  }

  // BOTTOM CHART
  // https://www.amcharts.com/docs/v4/chart-types/xy-chart/
  var lineChart = chartAndSliderContainer.createChild(am4charts.XYChart);
  lineChart.fontSize = "0.8em";
  lineChart.paddingRight = 30;
  lineChart.paddingLeft = 30;
  lineChart.maskBullets = false;
  lineChart.zoomOutButton.disabled = true;
  lineChart.paddingBottom = 5;
  lineChart.paddingTop = 3;

  // make a copy of data as we will be modifying it
  lineChart.data = JSON.parse(JSON.stringify(covid_total_timeline));

  // date axis
  // https://www.amcharts.com/docs/v4/concepts/axes/date-axis/
  var dateAxis = lineChart.xAxes.push(new am4charts.DateAxis());
  dateAxis.renderer.minGridDistance = 50;
  dateAxis.renderer.grid.template.stroke = am4core.color("#000000");
  dateAxis.renderer.grid.template.strokeOpacity = 0.25;
  dateAxis.max = lastDate.getTime() + am4core.time.getDuration("day", 3);
  dateAxis.tooltip.label.fontSize = "0.8em";
  dateAxis.tooltip.background.fill = activeColor;
  dateAxis.tooltip.background.stroke = activeColor;
  dateAxis.renderer.labels.template.fill = am4core.color("#ffffff");

  // value axis
  // https://www.amcharts.com/docs/v4/concepts/axes/value-axis/
  var valueAxis = lineChart.yAxes.push(new am4charts.ValueAxis());
  valueAxis.interpolationDuration = 3000;
  valueAxis.renderer.grid.template.stroke = am4core.color("#000000");
  valueAxis.renderer.grid.template.strokeOpacity = 0.25;
  valueAxis.renderer.baseGrid.disabled = true;
  valueAxis.tooltip.disabled = true;
  valueAxis.renderer.minGridDistance = 20;
  valueAxis.extraMax = 0.05;
  valueAxis.maxPrecision = 0;
  valueAxis.renderer.inside = true;
  valueAxis.renderer.labels.template.verticalCenter = "bottom";
  valueAxis.renderer.labels.template.fill = am4core.color("#ffffff");
  valueAxis.renderer.labels.template.padding(2, 2, 2, 2);  
  valueAxis.adapter.add("max", function(max, target) {
    if (max < 5) {
      max = 5
    }
    return max;
  })

  valueAxis.adapter.add("min", function(min, target) {
    if (min < 0) {
      min = 0;
    }
    return min;
  })

  // cursor
  // https://www.amcharts.com/docs/v4/concepts/chart-cursor/
  lineChart.cursor = new am4charts.XYCursor();
  lineChart.cursor.behavior = "none"; // set zoomX for a zooming possibility
  lineChart.cursor.lineY.disabled = true;
  lineChart.cursor.lineX.stroke = activeColor;
  lineChart.cursor.xAxis = dateAxis;
  // this prevents cursor to move to the clicked location while map is dragged
  am4core.getInteraction().body.events.off("down", lineChart.cursor.handleCursorDown, lineChart.cursor)
  am4core.getInteraction().body.events.off("up", lineChart.cursor.handleCursorUp, lineChart.cursor)

  // legend
  // https://www.amcharts.com/docs/v4/concepts/legend/  
  lineChart.legend = new am4charts.Legend();
  lineChart.legend.parent = lineChart.plotContainer;
  lineChart.legend.labels.template.fill = am4core.color("#ffffff");

  // create series
  var activeSeries = addSeries("active", activeColor);
  // active series is visible initially
  activeSeries.tooltip.disabled = true;
  activeSeries.hidden = false;

  var confirmedSeries = addSeries("confirmed", confirmedColor);
  var recoveredSeries = addSeries("recovered", recoveredColor);
  var deathsSeries = addSeries("deaths", deathsColor);

  var series = { active: activeSeries, confirmed: confirmedSeries, recovered: recoveredSeries, deaths: deathsSeries };
  // add series
  function addSeries(name, color) {
    var series = lineChart.series.push(new am4charts.LineSeries())
    series.dataFields.valueY = name;
    series.dataFields.dateX = "date";
    series.name = capitalizeFirstLetter(name);
    series.strokeOpacity = 0.6;
    series.stroke = color;
    series.maskBullets = false;
    series.minBulletDistance = 10;
    series.hidden = true;
    series.hideTooltipWhileZooming = true;
    // series bullet
    var bullet = series.bullets.push(new am4charts.CircleBullet());

    // only needed to pass it to circle
    var bulletHoverState = bullet.states.create("hover");
    bullet.setStateOnChildren = true;

    bullet.circle.fillOpacity = 1;
    bullet.circle.fill = backgroundColor;
    bullet.circle.radius = 2;

    var circleHoverState = bullet.circle.states.create("hover");
    circleHoverState.properties.fillOpacity = 1;
    circleHoverState.properties.fill = color;
    circleHoverState.properties.scale = 1.4;

    // tooltip setup
    series.tooltip.pointerOrientation = "down";
    series.tooltip.getStrokeFromObject = true;
    series.tooltip.getFillFromObject = false;
    series.tooltip.background.fillOpacity = 0.2;
    series.tooltip.background.fill = am4core.color("#000000");
    series.tooltip.dy = -4;
    series.tooltip.fontSize = "0.8em";
    series.tooltipText = "{valueY}";

    return series;
  }

  lineChart.plotContainer.events.on("up", function(){
    slider.start = lineChart.cursor.xPosition *  ((dateAxis.max - dateAxis.min) / (lastDate.getTime() - dateAxis.min));
  })


  // data warning label
  var label = lineChart.plotContainer.createChild(am4core.Label);
  label.text = "Current day stats may be incomplete until countries submit their data.";
  label.fill = am4core.color("#ffffff");
  label.fontSize = "0.7em";
  label.opacity = 0.5;
  label.align = "right";
  label.horizontalCenter = "right";
  label.verticalCenter = "bottom";

  // BUTTONS
  // create buttons
  var activeButton = addButton("active", activeColor);
  var confirmedButton = addButton("confirmed", confirmedColor);
  var recoveredButton = addButton("recovered", recoveredColor);
  var deathsButton = addButton("deaths", deathsColor);

  var buttons = { active: activeButton, confirmed: confirmedButton, recovered: recoveredButton, deaths: deathsButton };

  // add button
  function addButton(name, color) {
    var button = buttonsContainer.createChild(am4core.Button)
    button.label.valign = "middle"
    button.label.fill = am4core.color("#ffffff");
    button.fontSize = 12;
    button.background.cornerRadius(30, 30, 30, 30);
    button.background.strokeOpacity = 0.3
    button.background.fillOpacity = 0;
    button.background.stroke = buttonStrokeColor;
    button.background.padding(2, 3, 2, 3);
    button.states.create("active");
    button.setStateOnChildren = true;

    var activeHoverState = button.background.states.create("hoverActive");
    activeHoverState.properties.fillOpacity = 0;

    var circle = new am4core.Circle();
    circle.radius = 8;
    circle.fillOpacity = 0.3;
    circle.fill = buttonStrokeColor;
    circle.strokeOpacity = 0;
    circle.valign = "middle";
    circle.marginRight = 5;
    button.icon = circle;

    // save name to dummy data for later use
    button.dummyData = name;

    var circleActiveState = circle.states.create("active");
    circleActiveState.properties.fill = color;
    circleActiveState.properties.fillOpacity = 0.5;

    button.events.on("hit", handleButtonClick);

    return button;
  }

  // handle button clikc
  function handleButtonClick(event) {
    // we saved name to dummy data
    changeDataType(event.target.dummyData);
  }

  // change data type (active/confirmed/recovered/deaths)
  function changeDataType(name) {
    currentType = name;
    // make button active
    var activeButton = buttons[name];
    activeButton.isActive = true;
    // make other buttons inactive
    for (var key in buttons) {
      if (buttons[key] != activeButton) {
        buttons[key].isActive = false;
      }
    }
    // tell series new field name
    bubbleSeries.dataFields.value = name;
    bubbleSeries.invalidateData();
    // change color of bubbles
    // setting colors on mapImage for tooltip colors
    bubbleSeries.mapImages.template.fill = colors[name];
    bubbleSeries.mapImages.template.stroke = colors[name];
    // first child is circle
    bubbleSeries.mapImages.template.children.getIndex(0).fill = colors[name];

    dateAxis.tooltip.background.fill = colors[name];
    dateAxis.tooltip.background.stroke = colors[name];
    lineChart.cursor.lineX.stroke = colors[name];

    // show series
    var activeSeries = series[name];
    activeSeries.show();
    // hide other series
    for (var key in series) {
      if (series[key] != activeSeries) {
        series[key].hide();
      }
    }
    // update heat rule's maxValue
    bubbleSeries.heatRules.getIndex(0).maxValue = max[name];
  }

  // select a country
  function selectCountry(mapPolygon) {
    resetHover();
    polygonSeries.hideTooltip();

    // if the same country is clicked show world
    if (currentPolygon == mapPolygon) {
      currentPolygon.isActive = false;
      currentPolygon = undefined;
      showWorld();
      return;
    }
    // save current polygon
    currentPolygon = mapPolygon;
    var countryIndex = countryIndexMap[mapPolygon.dataItem.id];
    currentCountry = mapPolygon.dataItem.dataContext.name;

    // make others inactive
    polygonSeries.mapPolygons.each(function(polygon) {
      polygon.isActive = false;
    })

    // clear timeout if there is one
    if (countryDataTimeout) {
      clearTimeout(countryDataTimeout);
    }
    // we delay change of data for better performance (so that data is not changed whil zooming)
    countryDataTimeout = setTimeout(function() {
      setCountryData(countryIndex);
    }, 1000); // you can adjust number, 1000 is one second

    updateTotals(currentIndex);
    updateCountryName();

    mapPolygon.isActive = true;
    // meaning it's globe
    if (mapGlobeSwitch.isActive) {
      // animate deltas (results the map to be rotated to the selected country)
      if (mapChart.zoomLevel != 1) {
        mapChart.goHome();
        rotateAndZoom(mapPolygon);
      }
      else {
        rotateAndZoom(mapPolygon);
      }
    }
    // if it's not a globe, simply zoom to the country
    else {
      mapChart.zoomToMapObject(mapPolygon, getZoomLevel(mapPolygon));
    }
  }

  // change line chart data to the selected countries  
  function setCountryData(countryIndex) {
    // instead of setting whole data array, we modify current raw data so that a nice animation would happen
    for (var i = 0; i < lineChart.data.length; i++) {
      var di = covid_world_timeline[i].list;

      var countryData = di[countryIndex];
      var dataContext = lineChart.data[i];
      if (countryData) {
        dataContext.recovered = countryData.recovered;
        dataContext.confirmed = countryData.confirmed;
        dataContext.deaths = countryData.deaths;
        dataContext.active = countryData.confirmed - countryData.recovered - countryData.deaths;
        valueAxis.min = undefined;
        valueAxis.max = undefined;
      }
      else {
        dataContext.recovered = 0;
        dataContext.confirmed = 0;
        dataContext.deaths = 0;
        dataContext.active = 0;
        valueAxis.min = 0;
        valueAxis.max = 10;
      }
    }

    lineChart.invalidateRawData();
    updateTotals(currentIndex);
    setTimeout(updateSeriesTooltip, 1000);
  }

  function updateSeriesTooltip() {

    var position = dateAxis.dateToPosition(currentDate);
    position = dateAxis.toGlobalPosition(position);
    var x = dateAxis.positionToCoordinate(position);

    lineChart.cursor.triggerMove({ x: x, y: 0 }, "soft", true);
    lineChart.series.each(function(series) {
      if (!series.isHidden) {
        series.tooltip.disabled = false;
        series.showTooltipAtDataItem(series.tooltipDataItem);
      }
    })
  }

  // what happens when a country is rolled-over
  function rollOverCountry(mapPolygon) {

    resetHover();
    if (mapPolygon) {
      mapPolygon.isHover = true;

      // make bubble hovered too
      var image = bubbleSeries.getImageById(mapPolygon.dataItem.id);
      if (image) {
        image.dataItem.dataContext.name = mapPolygon.dataItem.dataContext.name;
        image.isHover = true;
      }
    }
  }
  // what happens when a country is rolled-out
  function rollOutCountry(mapPolygon) {
    var image = bubbleSeries.getImageById(mapPolygon.dataItem.id)
    resetHover();
    if (image) {
      image.isHover = false;
    }
  }

  // rotate and zoom
  function rotateAndZoom(mapPolygon) {
    polygonSeries.hideTooltip();
    var animation = mapChart.animate([{ property: "deltaLongitude", to: -mapPolygon.visualLongitude }, { property: "deltaLatitude", to: -mapPolygon.visualLatitude }], 1000)
    animation.events.on("animationended", function() {
      mapChart.zoomToMapObject(mapPolygon, getZoomLevel(mapPolygon));
    })
  }

  // calculate zoom level (default is too close)
  function getZoomLevel(mapPolygon) {
    var w = mapPolygon.polygon.bbox.width;
    var h = mapPolygon.polygon.bbox.width;
    // change 2 to smaller walue for a more close zoom
    return Math.min(mapChart.seriesWidth / (w * 2), mapChart.seriesHeight / (h * 2))
  }

  // show world data
  function showWorld() {
    currentCountry = "World";
    currentPolygon = undefined;
    resetHover();

    if (countryDataTimeout) {
      clearTimeout(countryDataTimeout);
    }

    // make all inactive
    polygonSeries.mapPolygons.each(function(polygon) {
      polygon.isActive = false;
    })

    updateCountryName();

    // update line chart data (again, modifying instead of setting new data for a nice animation)
    for (var i = 0; i < lineChart.data.length; i++) {
      var di = covid_total_timeline[i];
      var dataContext = lineChart.data[i];

      dataContext.recovered = di.recovered;
      dataContext.confirmed = di.confirmed;
      dataContext.deaths = di.deaths;
      dataContext.active = di.confirmed - di.recovered;
      valueAxis.min = undefined;
      valueAxis.max = undefined;
    }

    lineChart.invalidateRawData();

    updateTotals(currentIndex);
    mapChart.goHome();
  }

  // updates country name and date
  function updateCountryName() {
    countryName.text = currentCountry + ", " + mapChart.dateFormatter.format(currentDate, "MMM dd, yyyy");
  }

  // update total values in buttons
  function updateTotals(index) {
    if (!isNaN(index)) {
      var di = covid_total_timeline[index];
      var date = new Date(di.date);
      currentDate = date;

      updateCountryName();

      var position = dateAxis.dateToPosition(date);
      position = dateAxis.toGlobalPosition(position);
      var x = dateAxis.positionToCoordinate(position);

      if (lineChart.cursor) {
        lineChart.cursor.triggerMove({ x: x, y: 0 }, "soft", true);
      }
      for (var key in buttons) {
        buttons[key].label.text = capitalizeFirstLetter(key) + ": " + lineChart.data[index][key];
      }
      currentIndex = index;
    }
  }

  // update map data
  function updateMapData(data) {
    //modifying instead of setting new data for a nice animation
    bubbleSeries.dataItems.each(function(dataItem) {
      dataItem.dataContext.confirmed = 0;
      dataItem.dataContext.deaths = 0;
      dataItem.dataContext.recovered = 0;
      dataItem.dataContext.active = 0;
    })

    for (var i = 0; i < data.length; i++) {
      var di = data[i];
      var image = bubbleSeries.getImageById(di.id);
      if (image) {
        image.dataItem.dataContext.confirmed = di.confirmed;
        image.dataItem.dataContext.deaths = di.deaths;
        image.dataItem.dataContext.recovered = di.recovered;
        image.dataItem.dataContext.active = di.confirmed - di.recovered - di.deaths;
      }
    }
    bubbleSeries.invalidateRawData();
  }

  // capitalize first letter
  function capitalizeFirstLetter(string) {
    return string.charAt(0).toUpperCase() + string.slice(1);
  }


  function handleImageOver(event) {
    rollOverCountry(polygonSeries.getPolygonById(event.target.dataItem.id));
  }

  function handleImageOut(event) {
    rollOutCountry(polygonSeries.getPolygonById(event.target.dataItem.id));
  }

  function handleImageHit(event) {
    selectCountry(polygonSeries.getPolygonById(event.target.dataItem.id));
  }

  function handleCountryHit(event) {
    selectCountry(event.target);
  }

  function handleCountryOver(event) {
    rollOverCountry(event.target);
  }

  function handleCountryOut(event) {
    rollOutCountry(event.target);
  }

  function resetHover() {
    polygonSeries.mapPolygons.each(function(polygon) {
      polygon.isHover = false;
    })

    bubbleSeries.mapImages.each(function(image) {
      image.isHover = false;
    })
  }

  container.events.on("layoutvalidated", function() {
    dateAxis.tooltip.hide();
    lineChart.cursor.hide();
    updateTotals(currentIndex);
  });

  // set initial data and names
  updateCountryName();
  changeDataType("active");

  setTimeout(updateSeriesTooltip, 3000);

}); // end am4core.ready()
</script>

<!-- HTML -->
<br />
<div dir="ltr" id="chartdiv">
</div>
</div>
google-playkhamsatmostaqltradent