Projection explorer.
From D3 in Depth book by Peter Cook.
forked from d3indepth‘s block: Projection explorer
<!DOCTYPE html>
<meta charset="utf-8">
<head>
<title>Geo (projection configuration)</title>
</head>
<style>
body {
font-family: "Helvetica Neue", Helvetica, sans-serif;
font-size: 12px;
color: #333;
margin: 10px;
}
#description {
margin: 20px 0;
font-size: 16px;
line-height: 24px;
color: #555;
}
#menu {
position: absolute;
top: 20px;
left: 30px;
}
#menu .item {
margin-bottom: 12px;
}
#menu .item input {
width: 100px;
}
#menu select {
margin-top: 4px;
}
#menu .item .value {
font-weight: bold;
}
#menu .item span, #menu .item input {
vertical-align: middle;
}
#menu .item .low {
display: inline-block;
width: 30px;
text-align: right;
}
svg {
border: 1px solid #eee;
}
.map path {
fill: #45716a;
stroke: #777;
}
.projection-center {
fill: red;
}
.graticule path {
fill: none;
stroke: #eee;
}
.circles path {
fill: none;
stroke: #aaa;
}
</style>
<body>
<div id="menu">
<div class="projection-type item">
<div><select name="type" value="150"></select></div>
</div>
<div class="slider item">
<div class="label">scale (<span class="value">120</span>)</div>
<div><span class="low">0</span> <input type="range" name="scale" min="0" max="400" value="120"> <span>400</span></div>
</div>
<div class="slider item">
<div class="label">translate (x) (<span class="value">480</span>)</div>
<div><span class="low">0</span> <input type="range" name="translateX" min="0" max="960" value="480"> <span>960</span></div>
</div>
<div class="slider item">
<div class="label">translate (y) (<span class="value">250</span>)</div>
<div><span class="low">0</span> <input type="range" name="translateY" min="0" max="500" value="250"> <span>500</span></div>
</div>
<div class="slider item">
<div class="label">center (lon) (<span class="value">0</span>)</div>
<div><span class="low">-180</span> <input type="range" name="centerLon" min="-180" max="180" value="0"> <span>180</span></div>
</div>
<div class="slider item">
<div class="label">center (lat) (<span class="value">0</span>)</div>
<div><span class="low">-90</span> <input type="range" name="centerLat" min="-90" max="90" value="0"> <span>90</span></div>
</div>
<div class="slider item">
<div class="label">rotate (λ) (<span class="value">0</span>)</div>
<div><span class="low">-180</span> <input type="range" name="rotateLambda" min="-180" max="180" value="0"> <span>180</span></div>
</div>
<div class="slider item">
<div class="label">rotate (φ) (<span class="value">0</span>)</div>
<div><span class="low">-180</span> <input type="range" name="rotatePhi" min="-180" max="180" value="0"> <span>180</span></div>
</div>
<div class="slider item">
<div class="label">rotate (γ) (<span class="value">0</span>)</div>
<div><span class="low">-180</span> <input type="range" name="rotateGamma" min="-180" max="180" value="0"> <span>180</span></div>
</div>
</div>
<svg width="960px" height="500px">
<g class="graticule"><path></path></g>
<g class="circles"></g>
<g class="map"></g>
<circle class="projection-center" r="4"></circle>
</svg>
<div id="description">
<div>Explore <a href="https://d3js.org/" target="_blank">D3</a>'s geographic projections and their <a href="https://github.com/d3/d3-geo/blob/master/README.md#projection_scale" target="_blank">scale</a>, <a href="https://github.com/d3/d3-geo/blob/master/README.md#projection_translate" target="_blank">translate</a>, <a href="https://github.com/d3/d3-geo/blob/master/README.md#projection_center" target="_blank">center</a> and <a href="https://github.com/d3/d3-geo/blob/master/README.md#projection_rotate" target="_blank">rotate</a> parameters. The red dot indicates the <a href="https://github.com/d3/d3-geo/blob/master/README.md#projection_center" target="_blank">projection center</a>.</div>
<div>To demonstrate area distortion (e.g. Mercator) grey circles with the same radius have also been added.</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.2.2/d3.min.js"></script>
<script>
var geojson;
var projectionTypes = [
'AzimuthalEqualArea',
'AzimuthalEquidistant',
'Gnomonic',
'Orthographic',
'Stereographic',
'Albers',
'ConicConformal',
'ConicEqualArea',
'ConicEquidistant',
'Equirectangular',
'Mercator',
'TransverseMercator'
];
var projection;
var geoGenerator = d3.geoPath()
.projection(projection);
var graticule = d3.geoGraticule();
var circles = [
[-135, 0], [-90, 0], [-45, 0], [0, 0], [45, 0], [90, 0], [135, 0], [180, 0],
[0, -70], [0, -35], [0, 35], [0, 70],
[180, -70], [180, -35], [180, 35], [180, 70],
];
var geoCircle = d3.geoCircle().radius(10).precision(1);
var state = {
type: 'AzimuthalEqualArea',
scale: 120,
translateX: 480,
translateY: 250,
centerLon: 0,
centerLat: 0,
rotateLambda: 0.1,
rotatePhi: 0,
rotateGamma: 0
}
function initMenu() {
d3.select('#menu')
.selectAll('.slider.item input')
.on('input', function(d) {
var attr = d3.select(this).attr('name');
state[attr] = this.value;
d3.select(this.parentNode.parentNode).select('.value').text(this.value);
update()
});
d3.select('#menu .projection-type select')
.on('change', function(d) {
state.type = this.options[this.selectedIndex].value;
update()
})
.selectAll('option')
.data(projectionTypes)
.enter()
.append('option')
.attr('value', function(d) {return d;})
.text(function(d) {return d;});
}
function update() {
// Update projection
projection = d3['geo' + state.type]()
geoGenerator.projection(projection);
projection
.scale(state.scale)
.translate([state.translateX, state.translateY])
.center([state.centerLon, state.centerLat])
.rotate([state.rotateLambda, state.rotatePhi, state.rotateGamma])
// Update world map
var u = d3.select('g.map')
.selectAll('path')
.data(geojson.features)
u.enter()
.append('path')
.merge(u)
.attr('d', geoGenerator)
// Update projection center
var projectedCenter = projection([state.centerLon, state.centerLat]);
d3.select('.projection-center')
.attr('cx', projectedCenter[0])
.attr('cy', projectedCenter[1]);
// Update graticule
d3.select('.graticule path')
.datum(graticule())
.attr('d', geoGenerator);
// Update circles
u = d3.select('.circles')
.selectAll('path')
.data(circles.map(function(d) {
geoCircle.center(d);
return geoCircle();
}));
u.enter()
.append('path')
.merge(u)
.attr('d', geoGenerator);
}
d3.json('ne_110m_land.json', function(err, json) {
geojson = json;
initMenu();
update();
})
</script>
</body>
</html>