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.