Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
Tissot's Indicatrix

An attempt at Tissot's Indicatrix in d3.js v4.

It uses a spherical earth for ease of calculation, but as the earth is a slight ellipsoid, there could be a small amount of error. But this is small enough it shouldn't distort the graphics.

Each indicator circle/ellipse (depending on projection) represents a circular area with a 500 km radius. The number of indicators can be changed with the across/high variables.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<style>
svg {
background: #9ecae1;
}
.boundary {
fill:none;
stroke: white;
stroke-width: 0.5px;
}
.land {
fill: #41ab5d;
}
.indicator {
stroke: steelblue;
stroke-width: 1px;
fill: white;
fill-opacity: 0.4;
}
</style>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/d3-geo-projection.v1.min.js"></script>
<script src="https://d3js.org/topojson.v1.min.js"></script>
</head>
<body>
<script type="text/javascript">
var width = 960,
height = 735;
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var projection = d3.geoMercator().scale(155).center([0,40]);
var path = d3.geoPath().projection(projection);
var g = svg.append("g");
d3.json("world.json", function(error, world) {
// Add land masses from world.json:
g.insert("path", ".land")
.datum(topojson.feature(world, world.objects.land))
.attr("class", "land")
.attr("d", path);
g.insert("path", ".boundary")
.datum(topojson.mesh(world, world.objects.countries, function(a, b) { return a !== b; }))
.attr("class", "boundary")
.attr("d", path);
// Create some points to build the indicatrix around
var across = 8;
var high = 5;
var points = [];
for (i=0;i<across;i++) {
for (j=0;j<high;j++) {
if (j == 0) { points.push([ i * 360/across + 180/across -180, 0 ]) }
else {
points.push([ i * 360/across + 180/across - 180 , 0 + j/(high-1) * 72 ])
points.push([ i * 360/across + 180/across - 180, 0 - j/(high-1) * 72 ])
}
}
}
// Build some features around each point
var features = [];
for (j=0; j < points.length; j++) {
var point = points[j];
var r = 500000; // radius in m for each indicator
var data = [];
for (i=0;i<37;i++) {
var p = getPoint(point, i, r);
data.push ([ p[0],p[1] ]);
}
features.push(
{ "type":"Feature", "geometry": { "type": "Polygon", "coordinates": [data] } }
);
}
var geoJSON = { "type": "FeatureCollection", "features": features }
// Append those features
g.append("path").attr("d",path(geoJSON)).attr("class","indicator");
});
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* Latitude/longitude spherical geodesy tools (c) Chris Veness 2002-2016 */
/* MIT Licence */
/* www.movable-type.co.uk/scripts/latlong.html */
/* www.movable-type.co.uk/scripts/geodesy/docs/module-latlon-spherical.html */
function getPoint(p1,i,d) {
var bearing = i * 10 * Math.PI / 180
var R = 6371e3; // metres
var λ1 = p1[0] * Math.PI/180 ;
var φ1 = p1[1] * Math.PI/180 ;
var φ2 = Math.asin( Math.sin1)*Math.cos(d/R) + Math.cos1)*Math.sin(d/R)*Math.cos(bearing) );
var λ2 = λ1 + Math.atan2(Math.sin(bearing)*Math.sin(d/R)*Math.cos1), Math.cos(d/R)-Math.sin1)*Math.sin2));
φ2 = φ2 * 180/Math.PI;
λ2 = λ2 * 180/Math.PI;
λ2 =2+540)%360-180;
return22];
}
</script>
</body>
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