Edited at

大円軌跡とそれを囲む矩形(円筒投影法)

More than 1 year has passed since last update.

こんにちは。

大円の軌跡(部分)とそれを囲む円筒投影法矩形を d3.js を使って描いてみました。geojson の LineString を与えると大円軌跡を描きます。矩形に必要な緯線を作るなどの各種処理は「Geographic Bounding Boxes」から持ってきました1。投影法はBraun投影です。

braun.jpg


boundingBox.html

<!DOCTYPE html>

<meta charset="utf-8">
<title>Braun projection</title>
<style>
.frame {
fill: none;
stroke: #000;
stroke-width: 3px;
}
.fill {
fill: #fff;
}
.arc {
fill: none;
stroke: #0fc;
stroke-width: 1.5px;
}
.graticule {
fill: none;
stroke: #777;
stroke-width: 0.5px;
stroke-opacity: 0.5;
}
.land {
fill: #888;
}
.boundary {
fill: none;
stroke: #fff;
stroke-width: 0.5px;
}
.bounds {
fill: #f00;
fill-opacity: .1;
stroke: #f00;
stroke-width: 0.2px;
pointer-events: none;
}
</style>
<svg width="600" height="400"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://unpkg.com/topojson-client@3"></script>
<script>
var geojson_geometry = {type: "LineString", coordinates: [[110, 60], [-110, 60]]};

var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height");

var projection = d3.geoProjection(braunRaw)
.scale((width - 3) / (2 * Math.PI))
.translate([width/2, height/2])
.precision(0.1);

function braunRaw(lambda, phi) {
return [lambda, Math.tan(phi/2)*2];
}

var path = d3.geoPath()
.projection(projection);

var graticule = d3.geoGraticule();

svg.append("defs").append("path")
.datum(graticule.outline())
.attr("id", "sphere")
.attr("d", path);

svg.append("use")
.attr("class", "frame")
.attr("xlink:href", "#sphere");

svg.append("use")
.attr("class", "fill")
.attr("xlink:href", "#sphere");

svg.append("path")
.datum(graticule)
.attr("class", "graticule")
.attr("d", path);

svg.append("path")
.datum(geojson_geometry)
.attr("class", "arc")
.attr("d", path);

svg.append("path")
.datum(boundRect(geojson_geometry))
.attr("class", "bounds")
.attr("d", path);

d3.json("https://unpkg.com/world-atlas@1/world/50m.json", function(error, world) {
svg.insert("path", ".graticule")
.datum(topojson.feature(world, world.objects.land))
.attr("class", "land")
.attr("d", path);

svg.insert("path", ".graticule")
.datum(topojson.mesh(world, world.objects.countries, function(a, b) { return a !== b; }))
.attr("class", "boundary")
.attr("d", path);
});

function boundRect(pnts) {
var coords = [[pnts[0][0],pnts[0][1]]]
.concat(parallel(pnts[0][0], pnts[1][0], pnts[0][1]))
.concat(parallel(pnts[0][0], pnts[1][0], pnts[1][1]).reverse());
return rfc7946tod3({type: "Polygon", coordinates: [coords]})
}

function parallel(λ0, λ1, φ) {
if (λ0 > λ1) λ1 += 360;
var dλ = λ1 - λ0,
step = dλ / Math.ceil(dλ);
return d3.range(λ0, λ1 + 0.5 * step, step).map(function(λ) { return [normalise(λ), φ]; });
}

function normalise(x) {
return (x + 180) % 360 - 180;
}

// https://github.com/tyrasd/rfc7946-to-d3
function rfc7946tod3(geojson) {
switch ((geojson && geojson.type) || null) {
case 'FeatureCollection':
geojson.features.forEach(rfc7946tod3)
break
case 'GeometryCollection':
geojson.geometries.forEach(rfc7946tod3)
break
case 'Feature':
rfc7946tod3(geojson.geometry)
break
case 'MultiPolygon':
geojson.coordinates.forEach(function(polygon) {
polygon.forEach(function(ring) {
ring.reverse()
})
})
break
case 'Polygon':
geojson.coordinates.forEach(function(ring) {
ring.reverse()
})
break
}
return geojson
}
</script>







  1. 内部で gojson の polygon 処理に、rfc7946-to-d3.js を使っています。