Public
Edited
May 5, 2024
Importers
Insert cell
Insert cell
largeDownload = d3.json('https://raw.githubusercontent.com/rapomon/geojson-places/master/data/build.json')
Insert cell
catastrophes = FileAttachment("CatastrophesFilled.json").json()
Insert cell
world = FileAttachment("countries-50m (1).json").json()
Insert cell
tempCountries = topojson.feature(world, world.objects.countries)
Insert cell
toChange = [
//["Netherland Antilles", "ANT", "Netherland Antilles", "NLD", "Netherland Antilles"], //Netherlands
["Netherland Antilles", "ANT", "Netherlands", "NLD", "Netherlands"], //Netherlands
["Syrian Arab Republic", "SYR", "Syria", "SYX", "Syria"],
["Syrian Arab Republic", "SYR", "Syria", "SYU", "Syria"],
["Palestine, State of", "PSE", "Israel", "ISR", "Israel"],
["Germany Fed Rep", "DFR", "Germany", "GER", "Germany"],
["Yugoslavia", "YUG", "Russia", "RUS", "Russia"],
["Serbia Montenegro", "SCG", "Russia", "RUS", "Russia"],
//["Azores Islands", "AZO", "Portugal", "PAZ", "Azores Islands"], //Portugal
["Azores Islands", "AZO", "Portugal", "PRT", "Portugal"], //Portugal
["Canary Is", "SPI", "Spain", "ESP", "Spain"],
["United Kingdom of Great Britain and Northern Ireland (the)", "GBR", "United Kingdom", "GBR", "United Kingdom"],
/*["United Kingdom of Great Britain and Northern Ireland (the)", "GBR", "United Kingdom", "GBR", "North Ireland"],
["United Kingdom of Great Britain and Northern Ireland (the)", "GBR", "United Kingdom", "GBR", "Wales"],
["United Kingdom of Great Britain and Northern Ireland (the)", "GBR", "United Kingdom", "GBR", "England"],
["United Kingdom of Great Britain and Northern Ireland (the)", "GBR", "United Kingdom", "GBR", "Scotland"],*/
["Soviet Union", "SUN", "Russia", "RUS", "Russia"],
["South Sudan", "SSD", "S. Sudan", "SDS", "South Sudan"],
["Bolivia (Plurinational State of)", "BOL", "Bolivia", "BOL", "Bolivia"],
["Tanzania, United Republic of", "TZA", "United Republic of Tanzania", "TZA", "United Republic of Tanzania"],
//["French Guiana", "GUF", "France", "GUF", "French Guiana"], //France
["French Guiana", "GUF", "France", "FRA", "France"],
["Korea (the Republic of)", "KOR", "South Korea", "KOR", "South Korea"],
["Korea (the Democratic People's Republic of)", "PRK", "North Korea", "PRK", "North Korea"],
["Congo (the)", "COG", "Republic of the Congo", "COG", "Republic of the Congo"],
["Congo (the Democratic Republic of the)", "COD", "Democratic Republic of the Congo", "COD", "Democratic Republic of the Congo"],
["Namibia", "NAM", "Namibia", "NAM", "Namibia"],
["South Africa", "ZAF", "South Africa", "ZAF", "South Africa"],
["United Arab Emirates (the)", "ARE", "United Arab Emirates", "ARE", "United Arab Emirates"],
["Russian Federation (the)", "RUS", "Russia", "RUS", "Russia"],
["Czech Republic (the)", "CZE", "Czech Republic", "CZE", "Czech Republic"],
["Viet Nam", "VNM", "Vietnam", "VNM", "Vietnam"],
["Macedonia (the former Yugoslav Republic of)", "MKD", "Macedonia", "MKD", "Macedonia"],
["Lao People's Democratic Republic (the)", "LAO", "Laos", "LAO", "Laos"],
["Central African Republic", "CAF", "Central African Republic", "CAF", "Central African Republic"],
["Sudan (the)", "SDN", "Sudan", "SDN", "Sudan"],
["Iran (Islamic Republic of)", "IRN", "Iran", "IRN", "Iran"],
["Netherlands (the)", "NLD", "Netherlands", "NLD", "Netherlands"],
["Côte d’Ivoire", "CIV", "Ivory Coast", "CIV", "Ivory Coast"],
["Botswana", "BWA", "Botswana", "BWA", "Botswana"],
["Dominican Republic (the)", "DOM", "Dominican Republic", "DOM", "Dominican Republic"],
["Swaziland", "SWZ", "Swaziland", "SWZ", "Swaziland"],
["Bosnia and Herzegovina", "BIH", "Bosnia and Herzegovina", "BHF", "Bosnia and Herzegovina"],
["Moldova (the Republic of)", "MDA", "Moldova", "MDA", "Moldova"],
["Lesotho", "LSO", "Lesotho", "LSO", "Lesotho"],
["Niger (the)", "NER", "Niger", "NER", "Niger"],
["United States of America (the)", "USA", "United States of America", "USA", "United States of America"],
["Venezuela (Bolivarian Republic of)", "VEN", "Venezuela", "VEN", "Venezuela"],
["Equatorial Guinea", "GNQ", "Equatorial Guinea", "GNQ", "Equatorial Guinea"],
["Gambia (the)", "GMB", "Gambia", "GMB", "Gambia"],
["Philippines (the)", "PHL", "Philippines", "PHL", "Philippines"],
["Bahamas (the)", "BHS", "The Bahamas", "BHS", "The Bahamas"],
["Turks and Caicos Islands (the)", "TCA", "Turks and Caicos Islands", "TCA", "Turks and Caicos Islands"],
["Taiwan (Province of China)", "TWN", "Taiwan", "TWN", "Taiwan"],
["French Polynesia", "PYF", "French Polynesia", "PYF", "French Polynesia"],
["Marshall Islands (the)", "MHL", "Marshall Islands", "MHL", "Marshall Islands"],
["Saint Vincent and the Grenadines", "VCT", "Saint Vincent and the Grenadines", "VCT", "Saint Vincent and the Grenadines"],
//["Martinique", "MTQ", "France", "MTQ", "Martinique"], //France
["Martinique", "MTQ", "France", "FRA", "France"], //France
//["Guadeloupe", "GLP", "France", "GLP", "Guadeloupe"], //France
["Guadeloupe", "GLP", "France", "FRA", "France"], //France
["Antigua and Barbuda", "ATG", "Antigua and Barbuda", "ACA", "Antigua and Barbuda"],
["Virgin Island (U.S.)", "VIR", "United States Virgin Islands", "VIR", "United States Virgin Islands"],
["Virgin Island (British)", "VGB", "British Virgin Islands", "VGB", "British Virgin Islands"],
["Cayman Islands (the)", "CYM", "Cayman Islands", "CYM", "Cayman Islands"],
["Saint Helena, Ascension and Tristan da Cunha", "SHN", "Saint Helena", "SHN", "Saint Helena"],
//["Réunion", "REU", "France", "REU", "Réunion"], //France
["Réunion", "REU", "France", "FRA", "France"], //France
["Comoros (the)", "COM", "Comoros", "COM", "Comoros"],
["Sao Tome and Principe", "STP", "Sao Tome and Principe", "STP", "Sao Tome and Principe"],
["Cook Islands (the)", "COK", "Cook Islands", "COK", "Cook Islands"],
["Wallis and Futuna", "WLF", "Wallis and Futuna", "WLF", "Wallis and Futuna"],
["Solomon Islands", "SLB", "Solomon Islands", "SLB", "Solomon Islands"],
//["Tuvalu", "TUV", "Tuvalu", "TUV", "Tuvalu"], //Solomon Islands (not correct!)
["Tuvalu", "TUV", "Tuvalu", "SLB", "Solomon Islands"], //Solomon Islands (not correct!)
["Micronesia (Federated States of)", "FSM", "Federated States of Micronesia", "FSM", "Federated States of Micronesia"],
["Northern Mariana Islands (the)", "MNP", "Northern Mariana Islands", "MNP", "Northern Mariana Islands"],
//["Tokelau", "TKL", "New Zealand", "TKL", "Tokelau"] //New Zealand
["Tokelau", "TKL", "New Zealand", "NZL", "New Zealand"] //New Zealand
]
Insert cell
countriesToChange = [
["Dominican Rep.", "Dominican Republic"],
["Bahamas", "The Bahamas"],
["St. Vin. and Gren.", "Saint Vincent and the Grenadines"],
["U.S. Virgin Is.", "United States Virgin Islands"],
["Antigua and Barb.", "Antigua and Barbuda"],
["Cayman Is.", "Cayman Islands"],
["Turks and Caicos Is.", "Turks and Caicos Islands"],
["British Virgin Is.", "British Virgin Islands"],
["Cook Is.", "Cook Islands"],
["Wallis and Futuna Is.", "Wallis and Futuna"],
["Fr. Polynesia", "French Polynesia"],
["Solomon Is.", "Solomon Islands"],
["Micronesia", "Federated States of Micronesia"],
["Marshall Is.", "Marshall Islands"],
["N. Mariana Is.", "Northern Mariana Islands"],
["Czechia", "Czech Republic"],
["Bosnia and Herz.", "Bosnia and Herzegovina"],
["Central African Rep.", "Central African Republic"],
["Dem. Rep. Congo", "Democratic Republic of the Congo"],
["Congo", "Republic of the Congo"],
["Eq. Guinea", "Equatorial Guinea"],
["São Tomé and Principe", "Sao Tome and Principe"],
["Côte d'Ivoire", "Ivory Coast"],
["Tanzania", "United Republic of Tanzania"],
["S. Sudan", "South Sudan"],
["eSwatini", "Swaziland"]
]
Insert cell
//testResult[0]
Insert cell
/*function changeMissing(){
let toFix = new Map()
toChange.forEach(country => {
catastrophesByYear.forEach(entry => {
if(entry.ISO === country[2]){
if(entry.ISO !== "FRA" && country[1] === "France"){
toFix.set(country[0], [entry.Country, entry.ISO, country[1], country[3], country[0]])
}else if(entry.ISO !== "NZL" && country[1] === "New Zealand"){
toFix.set(country[0], [entry.Country, entry.ISO, country[1], country[3], country[0]])
}else{
toFix.set(country[1], [entry.Country, entry.ISO, country[1], country[3], country[1]])
}
}
})
})
console.log(Array.from(toFix));
return toFix;
}*/
Insert cell
function changeCountries(){
let large = cloneObject(largeDownload);
let catastrophesByYear = cloneObject(catastrophes);
let tempCountryStructure = cloneObject(tempCountries);
let changedCountries = cloneObject(tempCountries);
toChange.forEach(change => {
let changed = false;
large.features.forEach(country => {
if(country.properties.gu_a3 === change[3] || (country.properties.admin === change[2] && country.properties.admin !== "France" && country.properties.admin !== "Portugal" && country.properties.admin !== "New Zealand")){
country.properties.gu_a3 = change[3];
country.properties.admin = change[4];
//console.log("Changed Country: " + change[4])
catastrophesByYear.forEach(entry => {
if(entry.ISO === change[1] || entry.Country === change[0]){
entry.ISO = change[3];
entry.Country = change[4];
}
})
countriesToChange.forEach(countryToChange => {
tempCountryStructure.features.forEach(countryStruct => {
if(countryStruct.properties.name === countryToChange[0]){
countryStruct.properties.name = countryToChange[1];
}
})
changedCountries.features.forEach(countryStruct => {
if(countryStruct.properties.name === countryToChange[0]){
countryStruct.properties.name = countryToChange[1];
}
})
})
if((change[3] === "TUV" || change[3] === "GUF" || change[3] === "GLP" || change[3] === "MTQ" || change[3] === "TKL" || change[3] === "PAZ" || change[3] === "REU") && !changed){
let countryObject = {
type: "Feature",
properties: {name: change[4]},
geometry: country.geometry
}
changed = true;
tempCountryStructure.features.push(countryObject)
}
}
})
})
return [large, catastrophesByYear, tempCountryStructure, changedCountries];
}
Insert cell
temp = changeCountries()
Insert cell
large = temp[0]
Insert cell
catastrophesByYear = temp[1]
Insert cell
countries = temp[2]
Insert cell
changedCountries = temp[3]
Insert cell
checkUpISO = nest(large.features, d => d.properties.gu_a3)
Insert cell
checkUpName = nest(large.features, d => d.properties.admin)
Insert cell
checkUpName.get("Netherland Antilles")
Insert cell
/*
Netherland Antilles, ANT = Caribbean Netherlands, NLD (Netherland Antilles)
Syrian Arab Republic, SYR = Syria, (SYX, SYU) (Syria)
Palestine, State of, PSE = Israel, ISR (Israel)
Germany Fed Rep, DFR = Germany, GER (Germany)
Yugoslavia, YUG = Russia, RUS (Russia)
Serbia Montenegro, SCG = Serbia, SRB (or better RUS) (Russia)
Azores Islands, AZO = Portugal, PAZ (Azores Islands)
Canary Is, SPI = "", Spain (should be ESP) (Canary Islands)
United Kingdom of Great Britain and Northern Ireland (the), GBR = United Kingdom, (NIR, WLS, ENG, SCT) (North Ireland, Wales, England, Scotland)
Soviet Union, SUN = Russia, RUS (Russia)
South Sudan, SSD = S. Sudan, SDS (South Sudan)
*/
Insert cell
catastrophesByYear
Type Table, then Shift-Enter. Ctrl-space for more options.

Insert cell
function checkIfCoordinatesAreUnique(latitudeToCheck, longitudeToCheck, latitude, longitude) {
if (Math.abs(latitudeToCheck - latitude) < Number.EPSILON && Math.abs(longitudeToCheck - longitude) < Number.EPSILON) {
return false
}
return true;
}
Insert cell
function groupDoubledLocations() {
let temp = []
let tempCatastrophes = cloneObject(catastrophes)
catastrophes.forEach(entry => {
let foundEntryIndex = -1;
for (let index = 0; index < temp.length; index++) {
if (!checkIfCoordinatesAreUnique(parseFloat(temp[index][0].Latitude), parseFloat(temp[index][0].Longitude), parseFloat(entry.Latitude), parseFloat(entry.Longitude), 0.00001)){
foundEntryIndex = index;
break;
}
}
if(foundEntryIndex == -1){
temp.push([entry])
}else{
temp[foundEntryIndex].push(entry)
}
})

return temp
}
Insert cell
entriesOnSameLocation = groupDoubledLocations()
Insert cell
function generateCirclePoints(latitudeCenter, longitudeCenter, n, radius, padding) {
const circles = d3.packSiblings(d3.range(n).map(() => ({r: radius + padding})));
return circles;
}
Insert cell
function changeCoordinatesForDoubledEntries(){
let temp = cloneObject(entriesOnSameLocation)
temp.forEach(entry => {
let calculatedPoints = generateCirclePoints(entry[0].Latitude, entry[0].Longitude, entry.length, 0.01, 0.001)
entry.forEach((doubledEntry, pos) => {
doubledEntry.Latitude = parseFloat(entry[0].Latitude) + calculatedPoints[pos].x
doubledEntry.Longitude = parseFloat(entry[0].Longitude) + calculatedPoints[pos].y
})
})
return temp;
}
Insert cell
changedCoordinates = changeCoordinatesForDoubledEntries()
Insert cell
function updateCatastrophes(){
let temp = cloneObject(catastrophes)
changedCoordinates.forEach(entry => {
entry.forEach(child => {
const index = temp.findIndex(catastrophe => {
return catastrophe.Year === child.Year && catastrophe.Seq === child.Seq;
})
if (index !== -1) {
temp[index].Latitude = child.Latitude;
temp[index].Longitude = child.Longitude;
}
})
})
return temp;
}
Insert cell
updatedCatastrophes = updateCatastrophes()
Insert cell
updatedCatastrophes
Type Table, then Shift-Enter. Ctrl-space for more options.

Insert cell
function createYearsFromLocationArrays() {
let temp = cloneObject(filledCatastrophes)
let yearsArray = Array.from({ length: 2021 - 1990 }, (_, i) => [])
temp.forEach(entry => {
yearsArray[entry.Year-1990].push(entry)
})
return yearsArray;
}
Insert cell
yearsFromLocation = createYearsFromLocationArrays()
Insert cell
regions = FileAttachment("regions1.json").json()
Insert cell
countriesDownload = d3.json('https://gist.githubusercontent.com/bumbeishvili/20a70b32e172c58d9ada27f88adae54f/raw/32d8cd06362a673add0d7371f91c0a9ab3ecd01b/jsonDownload.json')
Insert cell
continentsDownload = d3.json('https://gist.githubusercontent.com/bumbeishvili/d2680078bb5232d11993d36030c5770c/raw/f19267222668edd504f739bb3c82c55f00283112/continents.json')
Insert cell
function createContinents() {
let tempContinents = cloneObject(continentsDownload);
tempContinents.objects.continents.geometries.forEach(continent => {
let continentObject = {name: continent.properties.CONTINENT};
continent.properties = continentObject;
})
return tempContinents;
}
Insert cell
tempContinents = createContinents()
Insert cell
countriesTopo = topojson.feature(countriesDownload, countriesDownload.objects.jsonDownload)
Insert cell
continentTopo = topojson.feature(tempContinents, tempContinents.objects.continents)
Insert cell
countrymesh = topojson.mesh(world, world.objects.countries, (a, b) => a !== b);
Insert cell
/*regionsGeoJson = [australiaAndNewZealand, caribbeans, centralAmerica, centralAsia, easternAfrica, easternAsia, easternEurope, melanesia, micronesia, middleAfrica, northernAfrica, northernAmerica, northernEurope, polynesia, russianFederation, southAmerica, southEasternAsia, southernAfrica, southernAsia, southernEurope, westernAfrica, westernAsia, westernEurope]*/
Insert cell
/*function createGeoObjectFromGeoJsonArray(array, name){
let object = {
type: "FeatureCollection",
name: name,
features: []
}

array.forEach(geoJsonObject => {
let feature = geoJsonObject.features[0];
let tempObject = {
type: "Feature",
properties: {name: feature.properties.ADMIN},
geometry: feature.geometry
}
object.features.push(tempObject);
})
return object;
}*/
Insert cell
function cloneObject(obj) {
if (typeof obj !== 'object' || obj === null) {
// If the value is not an object, return it directly
return obj;
}

const clonedObj = Array.isArray(obj) ? [] : {}; // Determine if obj is an array or an object
for (const key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
// Recursively clone nested objects
clonedObj[key] = cloneObject(obj[key]);
}
}
return clonedObj;
}
Insert cell
nodeStructure = nest(catastrophesByYear, d => d.Continent, d => d.Region, d => d.Country, d => d.ISO)
Insert cell
function checkIfUnique(list, iso, name, mode) {
if(mode === "Country"){
for (const country of list) {
if ((name === country.name && iso === country.iso3)) {
console.log(name, country.name, iso, country.iso3)
return false;
}
}
} else if (mode === "Province"){
iso = iso.join("-");
for (const province of list) {
let provinceIso = province.country + "-" + province.state;
if (name === province.name || iso === provinceIso) {
console.log(name, province.name, provinceIso)
return false;
}
}
}
return true;
}
Insert cell
function createZoomableObject(){
let tempObject = {
name: "World",
children: []
}
for(var i = 0; i < continentTopo.features.length; i++){
let continent = continentTopo.features[i];
let continentName = continent.properties.name
//Rename to find in nodeStructure
if(continentName === "North America" || continentName === "South America"){
continentName = "Americas"
} else if (continentName === "Australia" || continentName === "Oceania"){
continentName = "Oceania"
}
let regionObjects = []
let regionsList = nodeStructure.get(continentName)
if(regionsList !== undefined){
Array.from(regionsList.keys()).forEach(region => {
regions.features.forEach(tempRegion => {
if(region === tempRegion.properties.name){
let countryList = []
let countryNames = regionsList.get(region).keys()
Array.from(countryNames).forEach(countryName => {
let iso = Array.from(regionsList.get(region).get(countryName).keys())[0]
let provinces = []
large.features.forEach(province => {
if(province.properties.admin === countryName || iso === province.properties.gu_a3 || (province.properties.admin.includes("Serbia") && countryName === "Serbia")){
let iso3166 = province.properties.iso_3166_2.split("-");
let properties = {
countryName: province.properties.admin,
iso3: province.properties.gu_a3,
country: iso3166[0],
state: iso3166[1],
name: province.properties.name,
type: province.properties.type_en,
latitude: province.properties.latitude,
longitude: province.properties.longitude
}
let provinceObject = {
name: province.properties.name + " (" + province.properties.iso_3166_2 + ")",
properties: properties
}
provinces.push(provinceObject)
}
})
if(iso === "SRS" || iso === "SCG"){iso = "SRB"}
countries.features.forEach(country => {
if(country.properties.name == countryName && checkIfUnique(countryList, iso, countryName, "Country")){
let countryObject = {
name: country.properties.name,
iso3: iso,
children: provinces
}
countryList.push(countryObject);
}
})
})
let regionObject = {
name: region,
children: countryList
}
regionObjects.push(regionObject);
}
})
})
}
let continentObject = {
name: continent.properties.name,
children: regionObjects
}
tempObject.children.push(continentObject)
}
return tempObject;
}
Insert cell
zoomAbleWorld = createZoomableObject()
Insert cell
function checkForCorrectLength(){
let correct = true;
let list = []
zoomAbleWorld.children.forEach(continent => {
continent.children.forEach(region => {
region.children.forEach(country => {
list.push([country.name, country.iso3])
let iso = country.iso3;
let countryName = country.name;
if(iso === "SRB"){countryName = "Republic of Serbia"}
if(checkUpISO.get(iso) || checkUpName.get(countryName)){
if(checkUpName.get(countryName)){
if(country.children.length !== Array.from(checkUpName.get(countryName)).length){
//console.log(country.name, country.children.length, checkUp.get(country.name))
correct = false;
console.log("Wrong amount of children: " + country.name + ", " + country.iso3 + ", children found: " + country.children.length + ", expected: " + Array.from(checkUpName.get(country.name)).length)
}
}else{
if(country.children.length !== Array.from(checkUpISO.get(iso)).length){
//console.log(country.name, country.children.length, checkUp.get(country.name))
correct = false;
console.log("Wrong amount of children: " + country.name + ", " + country.iso3 + ", children found: " + country.children.length + ", expected: " + Array.from(checkUpISO.get(iso)).length)
}
}
}else{
console.log("Missing country: " + country.name + ", " + country.iso3 + " in zoomAbleWorld")
}
})
})
})
return [correct, list];
}
Insert cell
check = checkForCorrectLength()
Insert cell
function checkForTableCompletion(){
let complete = true;
let missing = []
Array.from(nodeStructure.keys()).forEach(continent => {
Array.from(nodeStructure.get(continent).keys()).forEach(region => {
Array.from(nodeStructure.get(continent).get(region).keys()).forEach(country => {
Array.from(nodeStructure.get(continent).get(region).get(country).keys()).forEach(iso => {
let found = false;
for (const countryStructure of check[1]) {
if(country === countryStructure[0] || iso === (countryStructure[1])){
found = true;
break;
}
}
if(!found){missing.push([country, iso]); console.log("Missing: " + country + ", " + iso); complete = false;}
})
})
})
})
return missing;
}
Insert cell
test = checkForTableCompletion()
Insert cell
testFind = nest(countries.features, d => d.properties.name)
Insert cell
testFind.get("Tokelau")
Insert cell
function checkLarge(test){
let missing = JSON.parse(JSON.stringify(test));
let found = []
large.features.forEach(province => {
missing.forEach((missingCountry, pos) => {
if(province.properties.admin.includes(missingCountry[0]) || missingCountry[1] === province.properties.gu_a3){
found.push([missingCountry[0], province.properties.admin, missingCountry[1], province.properties.gu_a3])
missing.splice(pos, 1);
}
})
})
return [found, missing];
}
Insert cell
testResult = checkLarge(test)
Insert cell
function createTest(){
let features = []
for(let i = 0; i < 1; i++){
features.push(largeDownload.features[i]);
}
return {type: "FeatureCollection",
features: largeDownload.features[0]};
}
Insert cell
testMap = createTest()
Insert cell
map = createZoomAbleMap(900, 500, 0.5, 100, d3.geoNaturalEarth1(), zoomAbleMapObject, "red", "white", "#444", false, "orange", 12)
Insert cell
function createTooltip(d){
const location = d.Location ? `Location: ${d.Location} ` : "";
const country = d.Country ? `(${d.Country.replace(" (the)", "")})` : "";
const seq = d.Seq ? `Nr. ${d.Seq}` : "";
const disasterSubgroup = d.DisasterSubgroup ? `${d.DisasterSubgroup} Catastrophe` : "";
const disasterSubtype = d.DisasterSubtype ? `(${d.DisasterSubtype})` : "";
const disasterSubsubtype = d.DisasterSubsubtype ? `-${d.DisasterSubsubtype}` : "";
const startMonth = d.StartMonth ? `Time: ${d.StartMonth}/` : "";
const startYear = d.StartYear ? `${d.StartYear}` : "";
const endMonth = d.EndMonth ? `-${d.EndMonth}/` : "";
const endYear = d.EndYear ? `${d.EndYear}` : "";
const totalAffected = d.TotalAffected ? `Total Affected: ${d.TotalAffected}` : "";

return `${location} ${country}\n${seq} ${disasterSubgroup} ${disasterSubtype} ${disasterSubsubtype}\n${startMonth}${startYear}${endMonth}${endYear}\n${totalAffected}`
}
Insert cell
function createZoomAbleMap(chosenWidth=900, chosenHeight=500, minZoom=0.5, maxZoom=100, projection=d3.geoMercator(), data=zoomAbleWorld, fillColor="red", strokeColor="white", fillColorNone="#444", names = false, textColor = "grey", fontSize = 1, markersData=catastrophes, radius=1, markersFillColor="orange") {
const width = chosenWidth;
const height = chosenHeight;

const zoom = d3.zoom()
.scaleExtent([minZoom, maxZoom])
.on("zoom", zoomed);

const svg = d3.create("svg")
.attr("viewBox", [0, 0, width, height])
.attr("width", width)
.attr("height", height)
.attr("style", "max-width: 100%; height: 100%;")
.on("click", reset);

const path = d3.geoPath(projection);

const g = svg.append("g");

const states = g.append("g")
.attr("fill", fillColorNone)
.attr("cursor", "pointer")
.selectAll("path")
.data(data.features)
.join("path")
.attr("d", path)
.on("click", clicked);

let textLabels = "";

if (names) {
textLabels = g.append("g")
.attr("pointer-events", "none") // Ensure text labels don't interfere with zooming and clicking
.selectAll("text")
.data(data.features)
.join("text")
.attr("text-anchor", "middle")
.style("font-size", "12px")
.text(d => d.properties.name);
} else {
states.append("title").text(d => d.properties.name);
}

const markers = g.append("g")
.attr("fill-opacity", 0.3)
.selectAll("circle")
.attr("cursor", "pointer")
.data(markersData)
.join("circle")
.attr("cx", d => projection([d.Longitude, d.Latitude])[0])
.attr("cy", d => projection([d.Longitude, d.Latitude])[1])
.attr("r", radius)
.attr("fill", markersFillColor)
.append("title")
.text(d => createTooltip(d));

g.append("path")
.attr("fill", "none")
.attr("stroke", strokeColor)
.attr("stroke-linejoin", "round")
.attr("d", path(countrymesh.features));

svg.call(zoom);

function reset() {
states.transition().style("fill", null);
svg.transition().duration(750).call(
zoom.transform,
d3.zoomIdentity,
d3.zoomTransform(svg.node()).invert([width, height])
);
}

function clicked(event, d) {
if (!d.children) return; // Do nothing if no children

const [[x0, y0], [x1, y1]] = path.bounds(d);
event.stopPropagation();
states.transition().style("fill", null);
d3.select(this).transition().style("fill", fillColor);
svg.transition().duration(750).call(
zoom.transform,
d3.zoomIdentity
.translate(width / 2, height / 2)
.scale(Math.min(100, 0.9 / Math.max((x1 - x0) / width, (y1 - y0) / height)))
.translate(-(x0 + x1) / 2, -(y0 + y1) / 2),
d3.pointer(event, svg.node()),
);
console.log(d.properties.name);
if (!d._children) {
d._children = d.children;
d.children = null;
} else {
d.children = d._children;
d._children = null;
}
}

function zoomed(event) {
const {transform} = event;
g.attr("transform", transform);
g.attr("stroke-width", 1 / transform.k);
if (names) {
textLabels.attr("transform", d => `translate(${path.centroid(d)}) scale(${Math.min(transform.k / 10, 5)})`);
textLabels.style("font-size", fontSize); // Adjust font size based on zoom scale
textLabels.attr("fill", textColor);
}
// Update marker positions on zoom
markers.attr("x", d => projection([d.Longitude, d.Latitude])[0])
.attr("y", d => projection([d.Longitude, d.Latitude])[1]);
}

return svg.node();
}
Insert cell
Insert cell
function createZoomableMapObject(){
let tempObject = {
name: "World",
features: [],
}
for(var i = 0; i < continentTopo.features.length; i++){
let continent = continentTopo.features[i];
let continentName = continent.properties.name
//Rename to find in nodeStructure
if(continentName === "North America" || continentName === "South America"){
continentName = "Americas"
} else if (continentName === "Australia" || continentName === "Oceania"){
continentName = "Oceania"
}
let regionObjects = []
let regionsList = nodeStructure.get(continentName)
if(regionsList !== undefined){
Array.from(regionsList.keys()).forEach(region => {
regions.features.forEach(tempRegion => {
if(region === tempRegion.properties.name){
let countryList = []
let countryNames = regionsList.get(region).keys()
Array.from(countryNames).forEach(countryName => {
let iso = Array.from(regionsList.get(region).get(countryName).keys())[0]
let provinces = []
large.features.forEach(province => {
if(province.properties.admin === countryName || iso === province.properties.gu_a3 || (province.properties.admin.includes("Serbia") && countryName === "Serbia")){
let iso3166 = province.properties.iso_3166_2.split("-");
let properties = {
countryName: province.properties.admin,
iso3: province.properties.gu_a3,
country: iso3166[0],
state: iso3166[1],
name: province.properties.name,
type: province.properties.type_en,
latitude: province.properties.latitude,
longitude: province.properties.longitude,
}
let provinceObject = {
type: "Feature",
territoryType: "Province",
properties: properties,
geometry: province.geometry
}
provinces.push(provinceObject)
}
})
if(iso === "SRS" || iso === "SCG"){iso = "SRB"}
countries.features.forEach(country => {
if(country.properties.name == countryName && checkIfUnique(countryList, iso, countryName, "Country")){
let countryObject = {
type: "Feature",
territoryType: "Country",
properties: country.properties,
geometry: country.geometry,
features: provinces,
}
countryList.push(countryObject);
}
})
})
let regionObject = {
type: "Feature",
territoryType: "Region",
properties: tempRegion.properties,
geometry: tempRegion.geometry,
features: countryList
}
regionObjects.push(regionObject);
}
})
})
}
let continentObject = {
type: "Feature",
territoryType: "Continent",
properties: continent.properties,
geometry: continent.geometry,
features: regionObjects,
}
tempObject.features.push(continentObject)
}
return tempObject;
}
Insert cell
zoomAbleMapObject = createZoomableMapObject()
Insert cell
function nest(values, ...keys) {
return (function regroup(values, i) {
if (i >= keys.length) return values;
const map = group(values, keys[i]);
return new Map(Array.from(map, ([k, v]) => [k, regroup(v, i + 1)]));
})(values, 0);
}
Insert cell
function groupReduce(values, keyof = identity, reduce, init = noop) {
const map = new Map();
let index = -1;
for (const value of values) {
const key = keyof(value, ++index, values);
map.set(key, reduce(map.has(key) ? map.get(key) : init(key), value, index, values));
}
return map;
}
Insert cell
group = {
const reduce = (p, v) => (p.push(v), p);
const init = () => [];
return function group(values, keyof) {
return groupReduce(values, keyof, reduce, init);
};
}
Insert cell
function identity(x) { return x; }
Insert cell
//Whole section from: https://observablehq.com/@mbostock/group & https://observablehq.com/@mbostock/nested-groups
function noop() {}
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