Public
Edited
Sep 22, 2023
1 fork
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
countriesMap2 = countriesMap.map((x) => x.replace(/ +/g, ""))
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
worldMap = {

// define colors
var waterCol = '#deebf7'
var noDataCol = '#9DC2B3'
var unselectedCol = '#4E997B'
var selectedCol = "#3C755E"

var maxCol = 'rgb(218,218,235)'
var minCol = 'rgb(106,81,163)'

var myColor = d3.scaleLinear()
.domain([0,Math.sqrt(max_distance)])
.range([minCol,maxCol])

// set size of the map
var width = 900
var height = 600

// initialize svg and group
const svg = d3.create('svg')
.attr('width', width)
.attr('height',height)
const g = svg.append('g')

// add rectangle to the group as background
g.append('rect')
.attr('class', 'background')
.attr('x', 0)
.attr('y', 0)
.attr('width', width)
.attr('height', height)
.attr('fill', waterCol)
.on("click", clicked)
// draw spherical Mercator projection
const projection = geo.geoMercator()
.scale(140)
.translate([width/2, height/1.4])
// generate an SVG path data string
const path = geo.geoPath(projection)

// initialize tooltip
const tooltip = d3.select("body").append("div")
.attr("class", "tooltip")
.style("position", "absolute")
.style("visibility", "hidden")

// get the countries and draw them
const countriesTemp = topojson.feature(topoData, topoData.objects.countries)
// exclude the Antarctica
const countriesSelected = countriesTemp.features
.filter((feature) => feature.properties.name != 'Antarctica')
// draw the countries
g.selectAll('path')
.data(countriesSelected)
.enter().append('path')
.attr('class', function(d) {return d.properties.name.replace(/ +/g, "")})
.attr('d', path)
.attr("fill", function(d) {
if (selection_mode=="Compare" && selectedCountry) {
return colorCountriesByDistance(d.properties.name.replace(/ +/g, ""))
} else {
if (selectedCountry) {
return colorCountriesByDistance(d.properties.name.replace(/ +/g, ""))
} else {
if (countriesMap2.includes(d.properties.name.replace(/ +/g, ""))) {return unselectedCol}
else {return noDataCol}
}
}
})
.attr("stroke", function(d) {
if (selectedCountries.has(d.properties.name.replace(/ +/g, ""))){return '#4a1486'}
else {
if (countriesMap2.includes(d.properties.name.replace(/ +/g, ""))) {return selectedCol}
else {return unselectedCol}
}
})
.attr("stroke-width", (d) => selectedCountries.has(d.properties.name.replace(/ +/g, "")) ? 2.5 : 0.7)
// mouse events
.on("click", clicked)
.on("mouseover", function(event, d) {
let text = d.properties.name
showToolTip(text, [event.pageX, event.pageY]).on("mousemove", function(event) {
d3.select(".tooltip")
.style("top", event.pageY - 10 + "px")
.style("left", event.pageX + 10 + "px")
})
})
.on("mousemove", function(event) {
d3.select(".tooltip")
.style("top", event.pageY - 10 + "px")
.style("left", event.pageX + 10 + "px")
})
.on("mouseout", function() {
d3.select(".tooltip").style("visibility", "hidden")
})

// mouse event handling
function clicked(event, d) {
if (event.target.className.baseVal == "background") {
// unselect everything when user clicks on background
if (selection_mode == 'Compare') {
// selection mode is on compare
// -> set selectedCountry to undefined
mutable selectedCountry = ''
} else {
// selection mode is on selection
// -> clear the selection
mutable selectedCountries = new Set()
}
} else if (countriesMap2.includes(d.properties.name.replace(/ +/g, ""))) {
// click on country with data
var countryName = d.properties.name.replace(/ +/g, "")
if (selection_mode == 'Compare') {
// selection mode is on compare
// -> set selected Country to the country name
mutable selectedCountry = d.properties.name.replace(/ +/g, "")
} else {
// selection mode is on selection
if (!selectedCountries.has(countryName)) {
// selected country is not in the selection -> select
let tempSelectedCountries = structuredClone(selectedCountries)
tempSelectedCountries.add(countryName)
mutable selectedCountries = tempSelectedCountries
} else {
// selected country is not in the selection -> unselect
let tempSelectedCountries = structuredClone(selectedCountries)
tempSelectedCountries.delete(countryName)
mutable selectedCountries = tempSelectedCountries
}
}
}

if (selection_mode == 'Compare') {
// update coloring
if (selectedCountry == '') {
// no country is selected -> color all green
for (let i = 0; i < countryCodes.length; i++) {
let unselCountry = countryCodes[i].mapName.replace(/ +/g, "")
d3.select("." + unselCountry).attr("fill", unselectedCol)
}
} else {
// one country is selected -> color according to similarity
colorCountriesByDistance(selectedCountry)
}
} else {
// update stroke for all countries
for (let i = 0; i < countryCodes.length; i++) {
let unselCountry = countryCodes[i].mapName.replace(/ +/g, "")
d3.select("." + unselCountry).attr("stroke-width", 0.7).attr("stroke", selectedCol)
}
// and selected countries
selectedCountries.forEach(function (selCountry) {
var path = d3.select("." + selCountry)._groups[0][0]
d3.select(path.parentNode.appendChild(path))
.attr("stroke-width", 2.5).attr("stroke", "#4a1486")})
}
}

// color all countries by the relationship to one selected country
function colorCountriesByDistance(countryName) {

if (!countryCodes.map((x) => x.noWhitespace).includes(countryName)) return noDataCol
var firstID = parseInt(countryCodes.filter((country) => country.noWhitespace == selectedCountry)[0].index)
let secondID = parseInt(countryCodes.filter((country) => country.noWhitespace == countryName)[0].index)
let secondCountry = countryCodes.filter((country) => country.noWhitespace == countryName)[0].noWhitespace

let distance = distance_mat_clone[firstID][secondID]
let distanceTrans = Math.sqrt(distance)
return myColor(distanceTrans)
}

// zoom
svg.call(d3.zoom()
.extent([[0, 0], [width, height]])
.translateExtent([[0, 0], [width, height]])
.scaleExtent([1, 8])
.on("zoom", zoomed))

function zoomed({transform}) {
g.attr("transform", transform);
}

// legend
svg.append('g')
.attr("transform", "translate(565,550)")
.append(() => legend)
return svg.node();
}
Insert cell
Insert cell
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more