5.0 The process - GiovanniKaaijk/functional-programming GitHub Wiki

Getting to know SVG

To start working with SVG elements, I started making a 'drawing' in SVG elements. Hereby I tried to make a smiley, I did this to understand how SVG elements work.

Rendering my world map

First I wanted to render a world map in my app. To achieve this, I went looking for examples of D3 maps, I analyzed the code of these examples until I understood it, then I started projecting my own world map.

Voor de wereldkaart heb ik het volgende gebruikt:

  • Topojson
  • geoPath
  • World-atlas
const rendermap = function (d3, topojson) {
    const worldMap = d3.geoNaturalEarth1(); //comes in different styles on d3
    const pathCreator = d3.geoPath().projection(worldMap);

    svg.append('path')
        .attr('class', 'sphere')
        .attr('d', pathCreator({type: 'Sphere'}));

    d3.json('https://unpkg.com/[email protected]/world/110m.json')
        .then(json => {
        console.log(json, json.objects.countries);
            const countries = topojson.feature(json, json.objects.countries);
            svg.selectAll('path')
            .data(countries.features)
                .enter()
                    .append('path')
                    .attr('d', pathCreator)
                    .attr('class', 'country')
        });
}(d3, topojson);

Filtering my data

Many of the results included place names instead of countries. To replace these place names with country names, I started looking for a file that contains all capitals of all countries. I found a JSON file while searching. This file had the following format:

{country: "Afghanistan", city: "Kabul"}
{country: "Albania", city: "Tirana"}
{country: "Algeria", city: "Alger"}
etc.

After fetching this file, I went to see if a result matches a country from these data. If this was the case, I replace the city name with the country name. I do this in the following way:

// Resultaten uit de query
results.forEach((result) => {
// Als het resultaat niet overeenkomt met een land uit de array met landen.
   if(!countryArray.includes(result.placeName.value)){
// Loop over alle steden om te kijken of een stad gelijk is aan het resultaat
         cities.forEach((city) => {
         if(city.city == result.placeName.value){
// Vervang het resultaat met het land van de stad
            result.placeName.value = city.country;
         }
      });
    }
});

Counting the objects per country

After this I wanted to see how many objects were found per country so that I could make a heat map of it. To keep track of how many objects come from a certain country I have written a loop that goes over all objects, with each object he checks which country the object comes from and then with this country a count is kept of how many objects there are in that country.

I managed to do this with the following code:

results.forEach(result => {
// Als het resultaat overeen komt met een land uit de array
   if(countryArray.includes(result.placeName.value)) {
         dataCount.forEach((counter) => {
// Zoek het land dat bij het resultaat hoort
         if(counter.properties.name == result.placeName.value){
// Tel 1 op bij de count van dit land
               counter.properties.count = counter.properties.count += 1;
// Als de counter van dit land hoger is dan de hoogste count van een land tot nu toe
               if(counter.properties.count > highestCount) {
// highestCount wordt vervangen door de huidige count, deze wordt later gebruikt voor het domain van de color scale in D3
                   highestCount = counter.properties.count;
               }
         }
     });
  }
})

Then my map looked as desired, I now wanted to add zooming and highlighting to the map.

Zooming and highlighting

I used D3 zoom to be able to zoom on my map. D3 zoom is a function that ensures that you can zoom in on your SVG element. I managed to do this with the following code:

svg.call(zoom.on('zoom', () => {
    g.attr('transform', d3.event.transform);
}))

After this I wanted to make sure that the user could highlight countries. I have added that a user can hover over a country, this makes the border of the country thicker and therefore clearer. Furthermore, a user can click on a country and then the name of the country appears, so that the user can see which countries he is viewing.

Hover:

.style('stroke-opacity', 0.2)
.on('mouseover', function() {
    d3.select(this)
      .style('stroke-opacity', 1)
})
.on('mouseout', function() {
    d3.select(this)
      .style('stroke-opacity', 0.2)
}

Tooltip:

// zorgt ervoor dat de tooltip verdwijnt wanneer je niet meer over hetzelfde land hovert
.on('mouseout', function() {
   tooltip.style("visibility", "hidden")
})
// laat de tooltip verschijnen met de land titel samen met het aantal objecten gevonden.
.on("click", (d) => { tooltip.style("visibility", "visible").text(d.properties.name + ' = ' + d.properties.count)})
// dit houdt de positie van de muis bij.
.on("mousemove", () => { tooltip.style("top", (event.pageY-40)+"px").style("left",(event.pageX-35)+"px")})

Upcoming features

If I had had more time I would have made the following with the app:

  • A working timeline, where you can select certain years to subsequently display the objects from this period.
  • Display historical moments on the timeline, so that you can better see discoveries and colonialism.