|
<!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.sin(φ1)*Math.cos(d/R) + Math.cos(φ1)*Math.sin(d/R)*Math.cos(bearing) ); |
|
var λ2 = λ1 + Math.atan2(Math.sin(bearing)*Math.sin(d/R)*Math.cos(φ1), Math.cos(d/R)-Math.sin(φ1)*Math.sin(φ2)); |
|
|
|
φ2 = φ2 * 180/Math.PI; |
|
λ2 = λ2 * 180/Math.PI; |
|
|
|
λ2 = (λ2+540)%360-180; |
|
|
|
return [λ2,φ2]; |
|
} |
|
|
|
|
|
|
|
|
|
</script> |
|
</body> |