$ ogr2ogr -f GeoJSON land.json 110m_cultural/ne_110m_admin_0_countries.shp $ ogr2ogr -f GeoJSON river.json 110m_physical/ne_110m_rivers_lake_centerlines.shp $ ogr2ogr -f GeoJSON lake.json 110m_physical/ne_110m_lakes.shp $ topojson -o world.json land.json river.json lake.json quantization: bounds -180 -85.60903777459767 180 83.64512999999998 (spherical) quantization: maximum error 2.181km (0.0196°) prune: retained 689 / 689 arcs (100%)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>D3 JP Map</title>
<style type="text/css">
.land {
fill : lime;
stroke : none;
}
.land-boundary {
fill: none;
stroke: white;
stroke-dasharray: 2,2;
stroke-linejoin: round;
}
.land-sea-boundary {
fill: none;
stroke: gray;
stroke-width : 0.3;
}
.river {
fill: none;
stroke: blue;
stroke-width : 0.3;
}
.lake {
fill: blue;
stroke: none;
}
.graticule {
fill: none;
stroke: #777;
stroke-width: .5px;
stroke-opacity: .5;
}
</style>
<script type="text/javascript" src="https://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script type="text/javascript" src="https://d3js.org/topojson.v1.min.js" charset="utf-8"></script>
</head>
<body>
<div id="unitName" class="hidden"></div><script type="text/javascript">
var width = 640;
var height = 320;
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
svg.append("rect").attr({x:0, y:0, width:width, height:height, stroke:"black", fill:"lavender"});
var projection = d3.geo.equirectangular()
.scale(110)
.center([0, 0])
.translate([width / 2, height / 2]);
var path = d3.geo.path()
.projection(projection);
var graticule = d3.geo.graticule();
//var url = "index.php?plugin=attach&pcmd=open&file=world.json&refer=HTML%20D3.js%20Globe";
var url = "world.json";
d3.json(url, function(error, data){
var land = topojson.feature(data, data.objects.land);
var river = topojson.feature(data, data.objects.river);
var lake = topojson.feature(data, data.objects.lake);
// 経線、緯線
svg.append("path")
.datum(graticule)
.attr("class", "graticule")
.attr("d", path);
// 国
svg.selectAll(".lands")
.data(land.features)
.enter()
.append("path")
.attr("class", "land")
.attr("d", path);
// 国境 (他のポリゴンと共有)
svg.append("path")
.datum(topojson.mesh(data, data.objects.land, function(a, b) { return a !== b; }))
.attr("d", path)
.attr("class", "land-boundary");
// 海岸 (他のポリゴンと共有しない)
svg.append("path")
.datum(topojson.mesh(data, data.objects.land, function(a, b) { return a === b; }))
.attr("d", path)
.attr("class", "land-sea-boundary");
// 河川
svg.selectAll(".river")
.data(river.features)
.enter()
.append("path")
.attr("class", "river")
.attr("d", path);
// 湖沼
svg.selectAll(".lake")
.data(lake.features)
.enter()
.append("path")
.attr("class", "lake")
.attr("d", path);
}); // end of d3.json
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>D3 Globe</title>
<style type="text/css">
.land {
fill : lime;
stroke : none;
}
.land-boundary {
fill: none;
stroke: gray;
stroke-dasharray: 2,2;
stroke-linejoin: round;
}
.land-sea-boundary {
fill: none;
stroke: gray;
stroke-width : 0.3;
}
.river {
fill: none;
stroke: blue;
stroke-width : 0.3;
}
.lake {
fill: blue;
stroke: none;
}
.graticule {
fill: none;
stroke: white;
stroke-width: .5;
stroke-opacity: .5;
}
.equator {
fill: none;
stroke: red;
stroke-width : 1;
}
</style>
<script type="text/javascript" src="https://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script type="text/javascript" src="https://d3js.org/topojson.v1.min.js" charset="utf-8"></script>
</head>
<body bgcolor="black">
<script type="text/javascript">
var width = 480;
var height = 480;
var projection = d3.geo.orthographic()
.scale(200)
.center([0, 0])
.translate([width / 2, height / 2])
.clipAngle(90);
var path = d3.geo.path()
.projection(projection);
var graticule = d3.geo.graticule();
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
// グラデーション定義
var defs = svg.append("defs");
var grad1 = defs.append("linearGradient")
.attr("id", "g1")
.attr("x1", "0")
.attr("y1", "0")
.attr("x2", "1")
.attr("y2", "1");
grad1.append("stop")
.attr("offset", "0.0")
.attr("stop-color", "blue");
grad1.append("stop")
.attr("offset", "1.0")
.attr("stop-color", "darkblue");
// 球 (の見た目の円) をグラデーションで塗りつぶす
svg.append("path")
.datum({type: "Sphere"})
.attr("id", "sphere")
.attr("d", path)
.attr("fill", "url(#g1)");
//var url = "index.php?plugin=attach&pcmd=open&file=world.json&refer=HTML%20D3.js%20Globe";
var url = "world.json";
d3.json(url, function(error, data){
var land = topojson.feature(data, data.objects.land);
var river = topojson.feature(data, data.objects.river);
var lake = topojson.feature(data, data.objects.lake);
// 経線、緯線
svg.append("path")
.datum(graticule)
.attr("class", "graticule")
.attr("d", path);
// 国
svg.selectAll(".lands")
.data(land.features)
.enter()
.append("path")
.attr("class", "land")
.attr("d", path);
// 国境 (他のポリゴンと共有)
svg.append("path")
.datum(topojson.mesh(data, data.objects.land, function(a, b) { return a !== b; }))
.attr("d", path)
.attr("class", "land-boundary");
// 海岸 (他のポリゴンと共有しない)
svg.append("path")
.datum(topojson.mesh(data, data.objects.land, function(a, b) { return a === b; }))
.attr("d", path)
.attr("class", "land-sea-boundary");
// 河川
svg.selectAll(".river")
.data(river.features)
.enter()
.append("path")
.attr("class", "river")
.attr("d", path);
// 湖沼
svg.selectAll(".lake")
.data(lake.features)
.enter()
.append("path")
.attr("class", "lake")
.attr("d", path);
// 赤道
svg.insert("path")
.datum(equatorGeoJson())
.attr("class", "equator")
.attr("d", path);
// アニメーション開始
rotate();
}); // end of d3.json
var vLat = 0;
var vLon = 0;
function rotate() {
vLat = Math.sin(Math.PI * vLon / 180) * 25;
vLat = vLat > 90 ? 90 : vLat < -90 ? -90 : vLat;
vLon = (vLon + 3) % 360;
projection.rotate([vLon, vLat, 0]);
svg.selectAll("path")
.attr("d", path);
setTimeout("rotate()", 100);
}
function equatorGeoJson() {
var path = new Array();
for (var lon = -180; lon <= 180; lon += 5) {
path.push([lon, 0]);
}
return {
"type": "Feature",
"geometry": {
"type": "LineString",
"coordinates": path
}
};
}
</script>
</body>
</html>
<svg> <defs> <linearGradient id="g1" x1="0" y1="0" x2="1" y2="1"> <stop offset="0.0" stop-color="blue"/> <stop offset="1.0" stop-color="darkblue"/> </linearGradiane> </defs> </svg>
svg.append("path").datum({type: "Sphere"}).attr("d",path)
projection.rotate([λ, Φ, γ]);
svg.selectAll("path").attr("d", path);
{ "type" : "Feature". "geometry" : { "type" : "LineString", "coordinates" : [[lon,lat],[lon,lat],[lon,lat],...] } }
回転の応用。球に地図を投影する path() と 平面に投影する path2() を交互に SVG の全 <path> に適用しているだけ
1990 年代の CG みたいで懐かしい
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>D3 Globe</title>
<style type="text/css">
.land {
fill : lime;
stroke : none;
}
.land-boundary {
fill: none;
stroke: gray;
stroke-dasharray: 2,2;
stroke-linejoin: round;
}
.land-sea-boundary {
fill: none;
stroke: gray;
stroke-width : 0.3;
}
.river {
fill: none;
stroke: blue;
stroke-width : 0.3;
}
.lake {
fill: blue;
stroke: none;
}
.graticule {
fill: none;
stroke: white;
stroke-width: .5;
stroke-opacity: .5;
}
.equator {
fill: none;
stroke: red;
stroke-width : 1;
}
</style>
<script type="text/javascript" src="https://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script type="text/javascript" src="https://d3js.org/topojson.v1.min.js" charset="utf-8"></script>
</head>
<body bgcolor="black">
<svg></svg>
<button>flat</button>
<script type="text/javascript">
var width = 480;
var height = 480;
var projection = d3.geo.orthographic()
.scale(200)
.center([0, 0])
.translate([width / 2, height / 2])
.clipAngle(90);
var path = d3.geo.path()
.projection(projection);
var graticule = d3.geo.graticule();
var svg = d3.select("svg")
.attr("width", width)
.attr("height", height);
// グラデーション定義
var defs = svg.append("defs");
var grad1 = defs.append("linearGradient")
.attr("id", "g1")
.attr("x1", "0")
.attr("y1", "0")
.attr("x2", "1")
.attr("y2", "1");
grad1.append("stop")
.attr("offset", "0.0")
.attr("stop-color", "blue");
grad1.append("stop")
.attr("offset", "1.0")
.attr("stop-color", "darkblue");
// 球 (の見た目の円) をグラデーションで塗りつぶす
svg.append("path")
.datum({type: "Sphere"})
.attr("id", "sphere")
.attr("d", path)
.attr("fill", "url(#g1)");
//var url = "index.php?plugin=attach&pcmd=open&file=world.json&refer=HTML%20D3.js%20Globe";
var url = "world.json";
d3.json(url, function(error, data){
var land = topojson.feature(data, data.objects.land);
var river = topojson.feature(data, data.objects.river);
var lake = topojson.feature(data, data.objects.lake);
// 経線、緯線
svg.append("path")
.datum(graticule)
.attr("class", "graticule")
.attr("d", path);
// 国
svg.selectAll(".lands")
.data(land.features)
.enter()
.append("path")
.attr("class", "land")
.attr("d", path);
// 国境 (他のポリゴンと共有)
svg.append("path")
.datum(topojson.mesh(data, data.objects.land, function(a, b) { return a !== b; }))
.attr("d", path)
.attr("class", "land-boundary");
// 海岸 (他のポリゴンと共有しない)
svg.append("path")
.datum(topojson.mesh(data, data.objects.land, function(a, b) { return a === b; }))
.attr("d", path)
.attr("class", "land-sea-boundary");
// 河川
svg.selectAll(".river")
.data(river.features)
.enter()
.append("path")
.attr("class", "river")
.attr("d", path);
// 湖沼
svg.selectAll(".lake")
.data(lake.features)
.enter()
.append("path")
.attr("class", "lake")
.attr("d", path);
// 赤道
svg.insert("path")
.datum(equatorGeoJson())
.attr("class", "equator")
.attr("d", path);
}); // end of d3.json
var projection2 = d3.geo.equirectangular()
.scale(200)
.center([0,0])
.translate([width / 2, height / 2]);
var path2 = d3.geo.path().projection(projection2);
d3.select("button").on("click", function() {
var $this = d3.select(this);
switch ($this.text()) {
case "flat" :
svg.selectAll("path")
.transition()
.ease("sin")
.duration(1000)
.attr("d", path2);
$this.text("globe");
break;
case "globe" :
default :
svg.selectAll("path")
.transition()
.ease("sin")
.duration(500)
.attr("d", path);
$this.text("flat");
break;
}
})
function equatorGeoJson() {
var path = new Array();
for (var lon = -180; lon <= 180; lon += 5) {
path.push([lon, 0]);
}
return {
"type": "Feature",
"geometry": {
"type": "LineString",
"coordinates": path
}
};
}
</script>
</body>
</html>
回転の応用。球に地図を投影する path() と 平面に投影する path2() を交互に SVG の全 <path> に適用しているだけ
1990 年代の CG みたいで懐かしい
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>D3 Globe</title>
<link rel="stylesheet" type="text/css" href="https://code.jquery.com/ui/1.10.3/themes/smoothness/jquery-ui.css">
<style type="text/css">
.land {
fill : lime;
stroke : none;
}
.land-boundary {
fill: none;
stroke: gray;
stroke-dasharray: 2,2;
stroke-linejoin: round;
}
.land-sea-boundary {
fill: none;
stroke: gray;
stroke-width : 0.3;
}
.river {
fill: none;
stroke: blue;
stroke-width : 0.3;
}
.lake {
fill: blue;
stroke: none;
}
.graticule {
fill: none;
stroke: black;
stroke-width: .5;
stroke-opacity: .5;
}
td {
padding-left: 20px;
}
</style>
<script type="text/javascript" src="https://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script type="text/javascript" src="https://d3js.org/topojson.v1.min.js" charset="utf-8"></script>
<script type="text/javascript" src="https://d3js.org/d3.geo.projection.v0.min.js" charset="utf-8"></script>
<script type="text/javascript" src="https://code.jquery.com/jquery-2.0.3.min.js" charset="utf-8"></script>
<script type="text/javascript" src="https://code.jquery.com/ui/1.10.3/jquery-ui.js" charset="utf-8"></script>
</head>
<body>
<div style="position:absolute;color:black;background:orange;opacity:0.6">
<table border="0">
<tbody>
<tr>
<td>distance</td>
<td width="300px"><div id="slider_distance"></div></td>
<td><span id="value_distance"></span></td>
</tr>
<tr>
<td>scale</td>
<td width="300px"><div id="slider_scale"></div></td>
<td><span id="value_scale"></span></td>
</tr>
<tr>
<td>rotate</td>
<td width="300px"><div id="slider_beta"></div></td>
<td><span id="value_rotate"></span></td>
</tr>
<tr>
<td>tilt</td>
<td width="300px"><div id="slider_tilt"></div></td>
<td><span id="value_tilt"></span></td>
</tr>
<tr>
<td>clip angle</td>
<td width="300px"><div id="slider_clipangle"></div></td>
<td><span id="value_clipangle"></span></td>
</tr>
<tr>
<td>precision</td>
<td width="300px"><div id="slider_precision"></div></td>
<td><span id="value_precision"></span></td>
</tr>
</tbody>
</table>
</div>
<script type="text/javascript">
var width = 800;
var height = 480;
var lon = 0;
var lat = 0;
var dlon = 5/180;
var dlat = 1;
var beta = 180;
var distance = 1.5;
var scale = 500;
var tilt = 40;
var clipangle = 60;
var precision = 0.1;
$("#slider_distance").slider({
min: 1.0,
max: 10.0,
step: 0.1,
value: distance,
change : function(event, ui) {
distance = ui.value;
$("#value_distance").text(distance);
}
});
$("#slider_scale").slider({
min: 100,
max: 2000,
step: 100,
value: scale,
change : function(event, ui) {
scale = ui.value;
$("#value_scale").text(scale);
}
});
$("#slider_beta").slider({
min: 0,
max: 360,
step: 10,
value: beta,
change : function(event, ui) {
beta = ui.value;
}
});
$("#slider_tilt").slider({
min: 0,
max: 100,
step: 10,
value: tilt,
change : function(event, ui) {
tilt = ui.value;
$("#value_tilt").text(tilt);
}
});
$("#slider_clipangle").slider({
min: 0,
max: 180,
step: 10,
value: clipangle,
change : function(event, ui) {
clipangle = ui.value;
$("#value_clipangle").text(clipangle);
}
});
$("#slider_precision").slider({
min: 0,
max: 1,
step: 0.1,
value: precision,
change : function(event, ui) {
precision = ui.value;
$("#value_precision").text(precision);
}
});
$("#value_distance").text(distance);
$("#value_scale").text(scale);
$("#value_rotate").text("[" + lon + "," + lat + "," + beta + "]");
$("#value_tilt").text(tilt);
$("#value_clipangle").text(clipangle);
$("#value_precision").text(precision);
var projection = d3.geo.satellite()
.distance(distance)
.scale(scale)
.rotate([lon, lat, beta])
.center([0,0])
.tilt(tilt)
.clipAngle(clipangle)
.precision(precision)
.translate([width / 2, height / 2]);
var path = d3.geo.path()
.projection(projection);
var graticule = d3.geo.graticule();
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
//var url = "index.php?plugin=attach&pcmd=open&file=world.json&refer=HTML%20D3.js%20Globe";
var url = "world.json";
d3.json(url, function(error, data){
var ocean = topojson.feature(data, data.objects.ocean);
var land = topojson.feature(data, data.objects.land);
var river = topojson.feature(data, data.objects.river);
var lake = topojson.feature(data, data.objects.lake);
// 経線、緯線
svg.append("path")
.datum(graticule)
.attr("class", "graticule")
.attr("d", path);
// 国
svg.selectAll(".lands")
.data(land.features)
.enter()
.append("path")
.attr("class", "land")
.attr("d", path);
// 国境 (他のポリゴンと共有)
svg.append("path")
.datum(topojson.mesh(data, data.objects.land, function(a, b) { return a !== b; }))
.attr("d", path)
.attr("class", "land-boundary");
// 海岸 (他のポリゴンと共有しない)
svg.append("path")
.datum(topojson.mesh(data, data.objects.land, function(a, b) { return a === b; }))
.attr("d", path)
.attr("class", "land-sea-boundary");
// 河川
svg.selectAll(".river")
.data(river.features)
.enter()
.append("path")
.attr("class", "river")
.attr("d", path);
// 湖沼
svg.selectAll(".lake")
.data(lake.features)
.enter()
.append("path")
.attr("class", "lake")
.attr("d", path);
// アニメーション開始
rotate();
}); // end of d3.json
function rotate() {
lon += dlon;
lat += dlat;
if (lat > 90) {
lat = 90;
dlat *= -1;
dlon *= -1;
lon = (lon + 180) % 360;
beta = (beta + 180) % 360;
$("#slider_beta").slider("value", beta);
} else if (lat < -90) {
lat = -90;
dlat *= -1;
dlon *= -1;
lon = (lon + 180) % 360;
beta = (beta + 180) % 360;
$("#slider_beta").slider("value", beta);
}
$("#value_rotate").text("[" + Math.floor(lon) + "," + Math.floor(lat) + "," + beta + "]");
projection
.distance(distance)
.scale(scale)
.rotate([lon, lat, beta])
.center([0,0])
.tilt(tilt)
.clipAngle(clipangle)
.precision(precision);
svg.selectAll("path")
.attr("d", path);
setTimeout("rotate()", 100);
}
</script>
</body>
</html>
var projection = d3.geo.satellite() .distance(distance) .scale(scale) .rotate([lon, lat, beta]) .center([0,0]) .tilt(tilt) .clipAngle(clipangle) .precision(precision) .translate([width / 2, height / 2]);