Dmitrij Holkin
Dmitrij Holkin

Reputation: 2055

AmCharts multiCSV dataloader with Worldmap

I have many CSV files,

I need create from them dataSetSelector

enter image description here

On select I need to get table

enter image description here

But im getting undefined on the end of my table.

And World map based on selected data enter image description here

And bar chart on maps bottom enter image description here

So need to get something like

enter image description here

  1. Also screen must be automatically adapted by user screen size
  2. And on mouse over each chart bar to highlight same value on map, the same on table items if possible
  3. On press bar on chart select maps area with this value, the same on table items if possible
  4. valueLegend min value must be min value from tables also as max value
  5. If there are duplicates countries fields in table, then in table show only one country and highlight this row or put * to country name (and be good to show popup on mouse over with all duplicates values), on map put all duplicates into description section splitting by newline and show value with *

What I do codepen.io

As I first day studing AmCharts, I cant get dataSetSelector and put data from multiple csv files into it. Also I cant find info how to join stock chart with map and table.

So please help to achieve my wysh.

Upvotes: 0

Views: 535

Answers (1)

xorspark
xorspark

Reputation: 16012

I'll focus on AmCharts-specific stuff in 2-4 since that's the main point of the question. I am also only going to provide a solution for AmCharts version 3.x. You should be able to handle 1 and 5 from there as you can tweak the CSS to more accommodate 1 and add whatever logic you need to satisfy 5.

To get the easy parts out of the way first

  • dataSetSelector is a stock chart property. The only way to reproduce this functionality for maps and serial charts is to write your own <select> with <option> tags and JavaScript to trigger the desired load action on change.
  • Stock charts only allow for date-based data. It cannot be used for charts where the category/x axis is just a string like in your screenshot, so it is not an option for you.

Taking the above into account, you need to set and position your dropdown, table and chart/map divs and add code to link everything together.

Some basic HTML and CSS for this layout

HTML:

<div id="container">
  <div id="selector-table-div">
    <select id="data-selector">
      <option value="path/to/csv-1">Dataset 1</option>
      <option value="path/to/csv-2">Dataset 2</option>
    </select>
    <div id="datatable"></div>
  </div>
  <div id="mapdiv"></div>
  <div id="chartdiv"></div>
</div>

CSS:

#mapdiv {
  width: 70%;
  height: 400px;
  float: left;
}
#chartdiv {
  width: 100%;
  height: 250px;
}
#selector-table-div {
  width: 20%;
  height: 450px;
  float: left;
}

You're on your own for making this more responsive for height. I omitted the datatable stuff and highlighted row for brevity.

In your JS, you'll want to attach a change event to trigger a page update when a different dropdown item is selected:

document
  .getElementById("data-selector")
  .addEventListener("change", function(e) {
    updatePage(e.target.value); //update page calls AmCharts.loadFile, updates/creates the table, map and chart with new data
  });

Since you're planning on using both charts and maps on the same page, you need to use amcharts.js, and ammap_amcharts_extension.js. Using amcharts.js and ammaps.js in the same page will cause bugs with both your charts and maps as both files override each other's methods. Your column chart will need serial.js:

<script src="https://www.amcharts.com/lib/3/amcharts.js"></script>
<script src="https://www.amcharts.com/lib/3/ammap_amcharts_extension.js"></script>
<script src="https://www.amcharts.com/lib/3/serial.js"></script>
<!-- other stuff omitted -->

Since you want your map to be tied to your data, you'll want to provide an easy way to map each row to a map area. Adding ISO 3166-2 country codes to your CSVs will simplify the process immensely:

country,visits,country_code
USA,2025,US
China,1882,CN
Japan,1809,JP
...

From there, you can set up your map's areas using your newly created country_code column as the MapArea id to activate the area on the map:

  var areas = data.map(function(row) {
      return {
        id: row.country_code, //use ISO code for area ids
        value: row.visits
      };
    });
  // ...
  AmCharts.makeChart("mapdiv", {
    // ..
    dataProvider: {
      map: "worldLow",
      areas: areas
    }
  });

To capture the min/max and assign it to the area, simply loop through the data and use Math.min/Math.max:

  var minValue = Number.MAX_VALUE;
  var maxValue = Number.MIN_VALUE;
  data.forEach(function(row) {
    minValue = Math.min(minValue, row.visits);
    maxValue = Math.max(maxValue, row.visits);
  });
  // ...
  AmCharts.makeChart("mapdiv", {
    // ..
    valueLegend: {
      minValue: minValue,
      maxValue: maxValue
      // ...
    }
  });      

You'll also want to adjust your map/chart creation code in separate functions that know when to either create a new map/chart or update an existing one:

var map, chart;
// ...
function updateMap(data) {
  // ...
  if (map) {
     //makeChart here
  }
  else {
    map.dataProvider.areas = areas;
    map.valueLegend.minValue = minValue;
    map.valueLegend.maxValue = maxValue;
    map.validateData(); // update map
  }

For the map, you'll also want to make sure that the map label placement code is called not only upon init, but also when the map is updated:

function updateMap(data) {
  // ...
  if (map) {
     //makeChart here
  }
  else {
     // data update here
  }
  updateLabel(); //update labels - same code as before

Creating your chart is pretty straightforward. You can add a clickGraphItem and rollOverGraphItem event to select the corresponding map area and highlight the table row on click/hover:

   chart = AmCharts.makeChart("chartdiv", {
      type: "serial",
      dataProvider: data,
      // ...
      listeners: [
        {
          event: "clickGraphItem",
          method: handleBarInteraction
        }, {
          event: "rollOverGraphItem",
          method: handleBarInteraction 
        }
      ]

function handleBarInteraction(e) {
  map.selectObject(map.getObjectById(e.item.dataContext.country_code));
  var selected = document.querySelector(".selected");

  if (selected) {
    selected.classList.remove("selected");
  }
  document
    .getElementById(e.item.dataContext.country_code)
    .classList.add("selected");
}

Your undefined line is likely coming from an extra newline in your CSV. You can simply check the last item and pop it out of the array before creating your table, map and chart:

var data = AmCharts.parseCSV(response, {
   // ...
});

if (data[data.length -1].country === undefined) {
  data.pop();
}

Here's a complete codepen with all of the above plus some restructured code. Note that the labels are placed in weird places. The example you pulled the label code from defines exception latitude and longitude variables for you to set up for specific areas. You'll need to figure out those values.

Upvotes: 2

Related Questions