#30DayMapChallenge Ziua 1. Points
Locuri populate pe Pământ 🌍
Harta 3D a Globului în D3.js și Canvas este una dintre hărțile pe care le-am făcut pentru #30DayMapChallenge de pe Twitter în 2021. Puteți afla mai multe despre provocare și despre celelalte hărți ale mele în acest post.
Scop: O hartă cu puncte.
Soluție: Afișarea de orașe pe un glob pământesc.
Live: https://maptheclouds.com/playground/30-day-map-challenge/points/
Tweet: https://twitter.com/maptheclouds/status/1455203199716143121
Cod: https://github.com/alexaac/map-challenges/tree/master/points
Descriere:
Vizualizarea arată locurile cele mai populate de pe Pământ. Cu cât este mai mare cercul, cu atât este mai mare populația.
Orașele cu rank mai mare sunt colorate mai intens.
Date: Natural Earth
Tag-uri: #D3js #QGIS #mapshaper #canvas #interaction #globe #earth
Inspirație:
https://stackoverflow.com/questions/15907171/d3-canvas-globe-mouse-events
https://observablehq.com/@alainro/can-quadtree-speed-up-zoom-of-many-points-on-a-canvas
Harta 3D a Globului în D3.js și Canvas – procedeul
Provocări: Pentru harta 3D de tip glob în D3.js și Canvas, am descărcat țările și orașele de pe Natural Earth, deoarece era una dintre sursele gratuite de date pentru provocare. Mai întâi, le-am explorat în QGIS, apoi am început să le cartografiez pe un glob folosind D3.js
Utilizatorul poate interacționa cu harta prin zoom, pan și click pe orașe, totuși, dacă dai click la un zoom mai mic s-ar putea să nu vezi așa de bine orașul. Pentru a îmbunătăți interacțiunea pentru utilizator, am setat harta să se rotească și să se centreze pe orașul selectat.
Cea mai mare provocare a fost să vizualizez multe puncte pe glob și să mențin în același timp interacțiunea cu ele destul de rapidă. Pentru a adresa problema asta, am ales să desenez punctele folosind Canvas în loc să încarc datele ca selecții făcute cu D3.js în DOM.
// Draw the cities
citiesData.features.forEach((city) => {
context.beginPath();
cityPath(city);
context.lineWidth = 0.5 / (transform.k < 1 ? 1 : transform.k);
context.strokeStyle = '#000';
context.stroke();
context.fillStyle = colorScale(city.properties.pop_max);
context.fill();
});
Problema cu Canvas-ul este că nu mai poți interacționa direct cu punctele with the points, așa că a trebuit să fac o căutare inversă bazată pe click-ul cu mouse-ul pentru a găsi cel mai apropiat punct și a afișa o etichetă pentru el.
/* Get coordinates at mouse click, transform them into
geographic coordinates and search nearest cities */
function mapClick() {
// Can't apply transformations unless scale 1
if (transform.k !== 1) return;
var mousePos = d3.mouse(this);
var p = projection.invert(mousePos);
if (p == undefined || !p[0] || !p[1]) {
return false;
}
const city = findCityAtMapClick(mousePos, width, height, renderArgs);
if (city) {
selected = city;
// Mutates projection - TODO try to use pure functions
transition(city.geometry.coordinates, renderArgs);
}
}
Altă soluție care a ajutat a fost să folosesc d3.quadtree pentru a accelera căutarea.
const tree = d3
.quadtree()
.extent([
[-1, -1],
[width + 1, height + 1],
])
.x((d) => d.geometry.coordinates[0])
.y((d) => d.geometry.coordinates[1])
.addAll(citiesData.features);
const found = search(projection, tree, mousePos);
TODO:
Ar fi bine să îmbunătățesc căutarea, astfel încât orașul să fie ales mult mai corect la click-ul cu mouse-ul, și să fac globul să se rotească la click stânga, dar voi lăsa aceste îmbunătățiri pentru altă provocare. 🙂
Ca o concluzie, trebuie să spun că mi-a plăcut să fac această hartă și am învățat mai mult despre hărți 3D în D3.js și despre interacțiunile cu Canvas-ul în general.