Skip to content

Commit

Permalink
Let there be map
Browse files Browse the repository at this point in the history
  • Loading branch information
lachlanjc committed Apr 19, 2020
0 parents commit 5898bd9
Show file tree
Hide file tree
Showing 4 changed files with 281 additions and 0 deletions.
5 changes: 5 additions & 0 deletions README.md
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/)
54 changes: 54 additions & 0 deletions index.html
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>

172 changes: 172 additions & 0 deletions script.js
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()
50 changes: 50 additions & 0 deletions style.css
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;
}

0 comments on commit 5898bd9

Please sign in to comment.