Skip to content

Instantly share code, notes, and snippets.

@kristw
Last active April 27, 2024 07:28
Show Gist options
  • Save kristw/c7446464b887f3c112840c3766349846 to your computer and use it in GitHub Desktop.
Save kristw/c7446464b887f3c112840c3766349846 to your computer and use it in GitHub Desktop.
Bangkok Metropolitan map
license: mit

My original intention was to create a simple example of Thailand map using d3.js. This was modified to only show the Bangkok Metro area.

  • Hover each province to see name on top left.
  • Click on a province to zoom in. Click somewhere else to zoom out.

Credit Apisit Toompakdee for his GeoJSON file thailand.json

forked from kristw's block: Thailand map

<!DOCTYPE html>
<meta charset="utf-8">
<style>
.background {
fill: #eee;
pointer-events: all;
}
path {
stroke: none;
}
.map-layer {
fill: #fff;
stroke: #aaa;
}
text{
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
font-weight: 300;
}
text.big-text{
font-size: 30px;
font-weight: 400;
}
</style>
<body>
<svg></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
const width = 960;
const height = 500;
let centered;
// Define color scale
const color = d3.scaleOrdinal(d3.schemeCategory10)
// Set svg width & height
const svg = d3.select('svg')
.attr('width', width)
.attr('height', height);
// Add background
svg.append('rect')
.attr('class', 'background')
.attr('width', width)
.attr('height', height)
.on('click', clicked);
const g = svg.append('g');
const mapLayer = g.append('g')
.classed('map-layer', true);
const bigText = g.append('text')
.classed('big-text', true)
.attr('x', 20)
.attr('y', 45);
const path = d3.geoPath()
// Load map data
d3.json('thailand.json', function(error, mapData) {
const provinces = new RegExp('^(' + [
'Bangkok',
'SamutPrakan',
'Nonthaburi',
'SamutSakhon',
'PathumThani',
'NakhonPathom'
].join('|') + ')$');
mapData.features = mapData.features
.filter(d => provinces.test(d.properties.CHA_NE));
const features = mapData.features;
const projection = d3.geoMercator()
.rotate([-100.6331, -13.2])
.fitSize([width, height], mapData);
path.projection(projection);
// Draw each province as a path
const sEnter = mapLayer.selectAll('path')
.data(features)
.enter().append('g')
.on('mouseover', mouseover)
.on('mouseout', mouseout)
.on('click', clicked);
sEnter.append('path')
.attr('d', path)
.attr('vector-effect', 'non-scaling-stroke')
.style('fill', fillFn)
sEnter.append('text')
.attr('x', d => path.centroid(d)[0])
.attr('y', d => path.centroid(d)[1])
.style('text-anchor', 'middle')
.style('stroke', '#222')
.text(nameFn)
});
// Get province name
function nameFn(d){
return d && d.properties ? d.properties.CHA_NE : null;
}
// Get province color
function fillFn(d, i){
return color(i);
}
// When clicked, zoom in
function clicked(d) {
let x, y, k;
// Compute centroid of the selected path
if (d && centered !== d) {
let centroid = path.centroid(d);
x = centroid[0];
y = centroid[1];
k = 4;
centered = d;
} else {
x = width / 2;
y = height / 2;
k = 1;
centered = null;
}
// Highlight the clicked province
mapLayer.selectAll('path')
.style('fill', (d, i) => centered && d===centered ? '#D5708B' : fillFn(d, i));
// Zoom
g.transition()
.duration(750)
.attr('transform', 'translate(' + width / 2 + ',' + height / 2 + ')scale(' + k + ')translate(' + -x + ',' + -y + ')');
}
function mouseover(d){
// Highlight hovered province
d3.select(this).style('fill', 'orange');
bigText.text(nameFn(d))
}
function mouseout(d){
// Reset province color
mapLayer.selectAll('path')
.style('fill', (d, i) => centered && d===centered ? '#D5708B' : fillFn(d, i));
// Clear province name
bigText.text('');
}
</script>
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment