Skip to content

Commit 44f902e

Browse files
committed
add live map
1 parent c0edf31 commit 44f902e

3 files changed

Lines changed: 99 additions & 0 deletions

File tree

_includes/livemap.html

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
<p id="map-text">
2+
Loading...
3+
</p>
4+
<div id="map-container"></div>
5+
<div id="map-tooltip" class="tooltip"></div>
6+
7+
<script type="module">
8+
import * as d3 from "https://cdn.jsdelivr.net/npm/d3@v7/+esm";
9+
import escape from "https://cdn.jsdelivr.net/npm/escape-html/+esm";
10+
11+
const container = document.getElementById("map-container");
12+
const text = document.getElementById("map-text");
13+
const tooltip = d3.select("#map-tooltip");
14+
15+
const width = 640;
16+
const height = 400;
17+
18+
// create a new svg for d3.js to draw on
19+
const svg = d3.create("svg").attr("width", width).attr("height", height);
20+
container.append(svg.node());
21+
22+
// grab the data
23+
Promise.all([
24+
d3.json("http://localhost:8787/locations.geojson"),
25+
d3.json("https://raw.githubusercontent.com/holtzy/D3-graph-gallery/master/DATA/world.geojson")
26+
]).then(([pointData, worldData]) => {
27+
// update map text
28+
text.innerText = `${pointData.features.length} hackers online!`;
29+
30+
// get the bounds of locations (min & max coordinates)
31+
const bounds = d3.geoBounds(pointData);
32+
const [[x0, y0], [x1, y1]] = bounds;
33+
34+
const dx = x1 - x0;
35+
const dy = y1 - y0;
36+
const x = ((x0 + x1) / 2) || 0;
37+
const y = ((y0 + y1) / 2) || 0;
38+
39+
// calculate scale and translation based on the bounds
40+
const scale = Math.max(10, Math.min(dx / width, dy / height)) * 100;
41+
const translate = [width / 2 - scale * x, height / 2 - scale * y];
42+
const padding = 100;
43+
44+
// create a new projection, centered at the middle of the locations
45+
let projection = d3.geoNaturalEarth1()
46+
.center([x, y])
47+
.translate([width / 2, height / 2]);
48+
49+
// if there's more than 1 location, fit the map to show all locations nicely
50+
if (pointData.features.length > 1) {
51+
projection = projection.fitExtent([[0 + padding, 0 + padding], [width - padding, height - padding]], pointData);
52+
}
53+
54+
// clamp the map scaling to 2000 so countries can be seen still
55+
projection = projection.scale(Math.min(projection.scale(), 2000));
56+
57+
const pathGenerator = d3.geoPath(projection).pointRadius(4);
58+
59+
// draw countries
60+
svg.append("path")
61+
.datum(worldData)
62+
.attr("d", pathGenerator)
63+
.attr("fill", "#e6e7e8")
64+
.attr("stroke", "white");
65+
66+
// draw points for each hacker location
67+
svg.selectAll("circle")
68+
.data(pointData.features)
69+
.enter()
70+
.append("circle")
71+
.attr("cx", (d) => projection(d.geometry.coordinates)[0])
72+
.attr("cy", (d) => projection(d.geometry.coordinates)[1])
73+
.attr("r", 4)
74+
.attr("fill", "blue")
75+
.on("mouseover", (event, d) => {
76+
tooltip.style("display", "block")
77+
.html(`<b>${escape(d.properties.name || "Anonymous")}</b><br>${escape(d.properties.locationName)}`)
78+
.style("left", (event.pageX + 10) + "px")
79+
.style("top", (event.pageY - 10) + "px");
80+
}).on("mouseout", () => {
81+
tooltip.style("display", "none");
82+
});
83+
});
84+
</script>

_sass/_style.scss

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,3 +353,11 @@ ol {
353353
}
354354
}
355355
}
356+
357+
.tooltip {
358+
position: absolute;
359+
background: var(--background-light);
360+
color: var(--foreground);
361+
padding: 5px;
362+
display: none;
363+
}

pages/index.html

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,13 @@
1111
An online event where you can meet friends and work on something new
1212
</p>
1313

14+
<section class="map">
15+
<div>
16+
<h2>Live Map</h2>
17+
{% include livemap.html %}
18+
</div>
19+
</section>
20+
1421
<section class="📅">
1522
<div>
1623
<h2>Next event</h2>

0 commit comments

Comments
 (0)