Skip to content

Instantly share code, notes, and snippets.

@Fil
Last active February 23, 2018 16:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Fil/137b06fcdb6f3ab8cc12e39d7d95e987 to your computer and use it in GitHub Desktop.
Save Fil/137b06fcdb6f3ab8cc12e39d7d95e987 to your computer and use it in GitHub Desktop.
Loximuthal projection & rhumb line
license: gpl-3.0

The loximuthal projection is available as d3.geoLoximuthal in the extended geographic projections plugin.

Projection introduced by Karl Siemon in 1935, it was published and forgotten -- then independently recreated by Waldo R. Tobler in 1966, who named it. Implemented in d3-geo-projection by @JasonDavies.

With the loximuthal projection, rhumb lines (aka loxodromes, in green) are straight lines. Formula adapted from http://www.mathpages.com/home/kmath502/kmath502.htm

Mouse over the map to change the reference parallel.

forked from mbostock's block: Loximuthal

<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
background: white;
}
.stroke {
fill: none;
stroke: #000;
stroke-width: 3px;
}
.fill {
fill: #f6f9fd;
}
.graticule {
fill: none;
stroke: #777;
stroke-width: .5px;
stroke-opacity: .5;
}
.land {
fill: #162d73;
}
#rhumb {
stroke: #dd4f98;
stroke-opacity: 0.5;
stroke-width: 1.5;
fill: none;
}
</style>
<body>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/d3-geo-projection.v2.min.js"></script>
<script src="https://d3js.org/topojson.v2.min.js"></script>
<script>
var width = 960,
height = 530;
var projection = d3.geoLoximuthal()
.rotate([-10,0]);
var path = d3.geoPath()
.projection(projection);
var graticule = d3.geoGraticule();
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
svg.append("defs").append("path")
.datum({type: "Sphere"})
.attr("id", "sphere");
svg.append("use")
.attr("class", "stroke")
.attr("xlink:href", "#sphere");
svg.append("use")
.attr("class", "fill")
.attr("xlink:href", "#sphere");
svg.append("path")
.datum(graticule)
.attr("class", "graticule");
d3.json("https://unpkg.com/world-atlas/world/110m.json", function(error, world) {
if (error) throw error;
svg.insert("path", ".graticule")
.datum(topojson.feature(world, world.objects.land))
.attr("class", "land");
draw();
});
var rhumb = [], lat0 = -85, lon0 = 0, K = 0.001, k = 0.06 / Math.sqrt(1 + K*K), radians = Math.PI / 180;
for(var i = 0; i < 3000; i++) {
var s = i,
lat = lat0 + k * s,
lon = lon0 + 1/K * Math.log(
(1 - Math.sin(lat * radians))
* Math.cos(lat0 * radians)
/ (1 - Math.sin(lat0 * radians))
/ Math.cos(lat * radians)
);
if (Math.abs(lat) < 85) rhumb.push([lon % 360, lat]);
}
var rhumbline = {type: "LineString", coordinates: rhumb };
svg.append('path').datum(rhumbline)
.attr('id', 'rhumb');
draw();
svg.on('mousemove click', function() {
var p = projection.invert(d3.mouse(this));
if (p && Math.abs(p[1]) < 88) projection.parallel(p[1]);
draw();
})
function draw() {
const margin = 10;
const width = Math.min(960, window.innerWidth),
height = Math.min(width / 2, window.innerHeight);
svg.attr('height', height).attr('width', width)
projection.fitExtent([[margin, margin], [width - margin, height - margin]], {
type: "Sphere"
});
svg.selectAll('path').attr('d', path)
}
d3.select(window).on('resize', draw);
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment