-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 5898bd9
Showing
4 changed files
with
281 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
# Hack Club Map | ||
|
||
Map of our global network of clubs. Built with D3. | ||
|
||
[**hackclub.com/map**](https://hackclub.com/map/) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
<head> | ||
<meta name="viewport" content="width=device-width" /> | ||
<meta charset="utf-8" /> | ||
<meta property="og:type" content="website" /> | ||
<meta property="og:site_name" content="Hack Club" /> | ||
<meta name="twitter:site" content="@hackclub" /> | ||
<title>Clubs Map – Hack Club</title> | ||
<meta property="og:title" content="Clubs Map – Hack Club" /> | ||
<meta name="twitter:title" content="Clubs Map – Hack Club" /> | ||
<meta | ||
name="description" | ||
content="Browse all the high school coding clubs in the global Hack Club network." | ||
/> | ||
<meta | ||
property="og:description" | ||
content="Browse all the high school coding clubs in the global Hack Club network." | ||
/> | ||
<meta | ||
name="twitter:description" | ||
content="Browse all the high school coding clubs in the global Hack Club network." | ||
/> | ||
<meta | ||
property="og:image" | ||
content="https://hackclub.com/map/card.png" | ||
/> | ||
<meta name="twitter:card" content="summary_large_image" /> | ||
<meta | ||
name="twitter:image" | ||
content="https://hackclub.com/map/card.png" | ||
/> | ||
<meta name="theme-color" content="#ec3750" /> | ||
<meta name="msapplication-TileColor" content="#ec3750" /> | ||
<link rel="stylesheet" href="style.css" /> | ||
</head> | ||
<body> | ||
<a href="https://hackclub.com/"> | ||
<img | ||
style="position: absolute; top: 0; left: 10px; border: 0; width: 192px;" | ||
src="https://assets.hackclub.com/flag-orpheus-top.png" | ||
alt="Hack Club flag" /> | ||
</a> | ||
|
||
<svg></svg> | ||
<section id="banner"></section> | ||
|
||
<script src="https://d3js.org/d3.v5.min.js"></script> | ||
<script src="https://d3js.org/topojson.v1.min.js"></script> | ||
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.min.js"></script> | ||
<script src="script.js"></script> | ||
</body> | ||
</html> | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,172 @@ | ||
const width = window.innerWidth | ||
const height = window.innerHeight | ||
const size = [width / 2, height / 2] | ||
|
||
const config = { | ||
speed: -0.008, | ||
verticalTilt: -24, | ||
horizontalTilt: 0, | ||
} | ||
|
||
let locations = [] | ||
|
||
const svg = d3.select('svg').attr('width', width).attr('height', height) | ||
const projection = d3.geoOrthographic().translate(size) | ||
const initialScale = projection.scale() | ||
const path = d3.geoPath().projection(projection) | ||
|
||
const oceanFill = svg | ||
.append('defs') | ||
.append('radialGradient') | ||
.attr('id', 'ocean-fill') | ||
.attr('cx', '75%') | ||
.attr('cy', '25%') | ||
oceanFill.append('stop').attr('offset', '5%').attr('stop-color', '#ddf') | ||
oceanFill.append('stop').attr('offset', '100%').attr('stop-color', '#9ab') | ||
svg | ||
.append('circle') | ||
.attr('cx', size[0]) | ||
.attr('cy', size[1]) | ||
.attr('r', initialScale) | ||
.style('pointer-events', 'none') | ||
.style('fill', 'url(#ocean-fill)') | ||
|
||
const featureGroup = svg.append('g') | ||
const render = () => svg.selectAll('.segment').attr('d', path) | ||
|
||
const globeHighlight = svg | ||
.append('defs') | ||
.append('radialGradient') | ||
.attr('id', 'globe-highlight') | ||
.attr('cx', '75%') | ||
.attr('cy', '25%') | ||
globeHighlight | ||
.append('stop') | ||
.attr('offset', '5%') | ||
.attr('stop-color', '#ffd') | ||
.attr('stop-opacity', '0.6') | ||
globeHighlight | ||
.append('stop') | ||
.attr('offset', '100%') | ||
.attr('stop-color', '#ba9') | ||
.attr('stop-opacity', '0.2') | ||
svg | ||
.append('circle') | ||
.attr('cx', size[0]) | ||
.attr('cy', size[1]) | ||
.attr('r', initialScale) | ||
.style('pointer-events', 'none') | ||
.style('fill', 'url(#globe-highlight)') | ||
|
||
const globeShading = svg | ||
.append('defs') | ||
.append('radialGradient') | ||
.attr('id', 'globe-shading') | ||
.attr('cx', '50%') | ||
.attr('cy', '40%') | ||
globeShading | ||
.append('stop') | ||
.attr('offset', '50%') | ||
.attr('stop-color', '#9ab') | ||
.attr('stop-opacity', '0') | ||
globeShading | ||
.append('stop') | ||
.attr('offset', '100%') | ||
.attr('stop-color', '#3e6184') | ||
.attr('stop-opacity', '0.3') | ||
svg | ||
.append('circle') | ||
.attr('cx', size[0]) | ||
.attr('cy', size[1]) | ||
.attr('r', initialScale) | ||
.style('pointer-events', 'none') | ||
.style('fill', 'url(#globe-shading)') | ||
|
||
const markerGroup = svg.append('g') | ||
|
||
// Import geographies | ||
d3.json( | ||
'https://gist.githubusercontent.com/mbostock/4090846/raw/d534aba169207548a8a3d670c9c2cc719ff05c47/world-110m.json' | ||
).then((worldData) => { | ||
featureGroup | ||
.selectAll('.segment') | ||
.data(topojson.feature(worldData, worldData.objects.countries).features) | ||
.enter() | ||
.append('path') | ||
.attr('class', 'segment') | ||
.attr('d', path) | ||
.style('stroke', '#aaa') | ||
.style('stroke-width', '1px') | ||
.style('fill', '#e5e5e5') | ||
|
||
svg.call( | ||
d3.zoom().on('zoom', () => { | ||
const newScale = initialScale * d3.event.transform.k | ||
projection.scale(newScale) | ||
d3.selectAll('circle').attr('r', newScale) | ||
render() | ||
}) | ||
) | ||
}) | ||
|
||
d3.json( | ||
'https://api2.hackclub.com/v0/Operations/Clubs/?select=%7B%22fields%22:%5B%22Name%22,%22Latitude%22,%22Longitude%22,%22Customized%20Name%22%5D,%22filterByFormula%22:%22AND(%7BRejected%7D=0,%7BDummy%7D=0,%7BDropped%7D=0)%22%7D' | ||
) | ||
.then((data) => _.filter(data, (c) => !_.isEmpty(c.fields['Latitude']))) | ||
.then((clubs) => { | ||
console.log(clubs.length) | ||
clubs.forEach(({ fields }) => { | ||
locations.push({ | ||
name: fields['Name'], | ||
lat: fields['Latitude'][0], | ||
lng: fields['Longitude'][0], | ||
}) | ||
}) | ||
return locations | ||
}) | ||
.then(() => { | ||
drawMarkers() | ||
}) | ||
|
||
// Spinning animation | ||
// /* | ||
d3.timer((elapsed) => { | ||
projection.rotate([ | ||
config.speed * elapsed - 256, | ||
config.verticalTilt, | ||
config.horizontalTilt, | ||
]) | ||
|
||
render() | ||
drawMarkers() | ||
}) | ||
// */ | ||
|
||
// City markers | ||
function drawMarkers() { | ||
const markers = markerGroup.selectAll('circle').data(locations) | ||
markers | ||
.enter() | ||
.append('circle') | ||
.merge(markers) | ||
.attr('cx', ({ lng, lat }) => projection([lng, lat])[0]) | ||
.attr('cy', ({ lng, lat }) => projection([lng, lat])[1]) | ||
.attr('fill', ({ lng, lat }) => { | ||
const coordinate = [lng, lat] | ||
gdistance = d3.geoDistance(coordinate, projection.invert(size)) | ||
return gdistance > 1.625 ? 'none' : '#ec3750' | ||
}) | ||
.attr('r', (projection.scale() / initialScale) * 6) | ||
.on('mouseenter', (club) => { | ||
d3.select('#banner').text(club.name).style('opacity', 1) | ||
}) | ||
.on('click', (club) => { | ||
d3.select('#banner').text(club.name).style('opacity', 1) | ||
}) | ||
// .on('mouseleave', () => { | ||
// d3.select('#banner').text('').style('opacity', 0) | ||
// }) | ||
svg.append(markers) | ||
} | ||
|
||
drawMarkers() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
font-face { | ||
font-family: 'Phantom Sans'; | ||
src: url('https://hackclub.com/fonts/Phantom_Sans_0.6/Regular.woff') | ||
format('woff'), | ||
url('https://hackclub.com/fonts/Phantom_Sans_0.6/Regular.woff2') | ||
format('woff2'); | ||
font-weight: normal; | ||
font-style: normal; | ||
font-display: swap; | ||
} | ||
@font-face { | ||
font-family: 'Phantom Sans'; | ||
src: url('https://hackclub.com/fonts/Phantom_Sans_0.6/Bold.woff') | ||
format('woff'), | ||
url('https://hackclub.com/fonts/Phantom_Sans_0.6/Bold.woff2') | ||
format('woff2'); | ||
font-weight: bold; | ||
font-style: normal; | ||
font-display: swap; | ||
} | ||
|
||
body { | ||
margin: 0; | ||
font-family: "Phantom Sans", system-ui, Roboto, sans-serif; | ||
} | ||
|
||
@media (prefers-color-scheme: dark) { | ||
body { | ||
background-color: #1e1e1e; | ||
} | ||
} | ||
|
||
#banner { | ||
background-color: #ec3750; | ||
color: #fff; | ||
pointer-events: none; | ||
padding: 1rem 1.5rem; | ||
border-radius: 999px; | ||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.25); | ||
max-width: calc(100% - 1.5rem); | ||
text-align: center; | ||
position: absolute; | ||
top: 1rem; | ||
left: 50%; | ||
-webkit-transform: translateX(-50%); | ||
transform: translateX(-50%); | ||
opacity: 0; | ||
-webkit-transition: opacity .25s ease-out; | ||
transition: opacity .25s ease-out; | ||
} |