Stereographic & orthographic projections!
These now have arbitrary origins, as well. Still need to clip, though.
This commit is contained in:
Родитель
987755b01e
Коммит
b9eff658a3
44
d3.geo.js
44
d3.geo.js
|
@ -1,26 +1,44 @@
|
|||
(function(){d3.geo = {};
|
||||
// TODO clip input coordinates on opposite hemisphere
|
||||
d3.geo.azimuthal = function() {
|
||||
var scale = 200,
|
||||
var mode = "orthographic", // or stereographic
|
||||
origin,
|
||||
scale = 200,
|
||||
translate = [480, 250],
|
||||
pole = 1;
|
||||
x0,
|
||||
y0,
|
||||
cy0,
|
||||
sy0;
|
||||
|
||||
function azimuthal(coordinates) {
|
||||
var p = p < 0 ? 0 :
|
||||
p > 1 ? 1 :
|
||||
1 - pole * coordinates[1] / 90,
|
||||
t = coordinates[0] * d3_radians,
|
||||
x = p * Math.cos(t),
|
||||
y = p * Math.sin(t);
|
||||
var x1 = coordinates[0] * d3_radians - x0,
|
||||
y1 = coordinates[1] * d3_radians,
|
||||
cx1 = Math.cos(x1),
|
||||
sx1 = Math.sin(x1),
|
||||
cy1 = Math.cos(y1),
|
||||
sy1 = Math.sin(y1),
|
||||
k = mode == "stereographic" ? 1 / (1 + sy0 * sy1 + cy0 * cy1 * cx1) : 1,
|
||||
x = k * cy1 * sx1,
|
||||
y = k * (sy0 * cy1 * cx1 - cy0 * sy1);
|
||||
return [
|
||||
scale * x + translate[0],
|
||||
scale * y + translate[1]
|
||||
];
|
||||
}
|
||||
|
||||
// 1 or -1 means N or S hemisphere
|
||||
azimuthal.hemisphere = function(x) {
|
||||
if (!arguments.length) return pole;
|
||||
pole = +x;
|
||||
azimuthal.mode = function(x) {
|
||||
if (!arguments.length) return mode;
|
||||
mode = x;
|
||||
return azimuthal;
|
||||
};
|
||||
|
||||
azimuthal.origin = function(x) {
|
||||
if (!arguments.length) return origin;
|
||||
origin = x;
|
||||
x0 = origin[0] * d3_radians;
|
||||
y0 = origin[1] * d3_radians;
|
||||
cy0 = Math.cos(y0);
|
||||
sy0 = Math.sin(y0);
|
||||
return azimuthal;
|
||||
};
|
||||
|
||||
|
@ -36,7 +54,7 @@ d3.geo.azimuthal = function() {
|
|||
return azimuthal;
|
||||
};
|
||||
|
||||
return azimuthal;
|
||||
return azimuthal.origin([0, 0]);
|
||||
};
|
||||
// Derived from Tom Carden's Albers implementation for Protovis.
|
||||
// http://gist.github.com/476238
|
||||
|
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -11,22 +11,11 @@ svg {
|
|||
background: #eee;
|
||||
}
|
||||
|
||||
line {
|
||||
stroke: brown;
|
||||
stroke-dasharray: 4,2;
|
||||
}
|
||||
|
||||
path {
|
||||
fill: none;
|
||||
fill: #ccc;
|
||||
stroke: #fff;
|
||||
}
|
||||
|
||||
path.grid {
|
||||
fill: none;
|
||||
stroke: #eee;
|
||||
stroke-width: .5;
|
||||
}
|
||||
|
||||
div {
|
||||
width: 960px;
|
||||
}
|
||||
|
|
|
@ -10,46 +10,74 @@
|
|||
<link type="text/css" rel="stylesheet" href="azimuthal.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<h3>Polar Stereographic Projection</h3>
|
||||
<h3>Azimuthal Projection</h3>
|
||||
<script type="text/javascript" src="azimuthal.js"></script><p>
|
||||
<div id="scale">scale: <span>200</span></div><p>
|
||||
<div id="translate-x">translate.x: <span>480</span></div><p>
|
||||
<div id="lon">origin.longitude: <span>0</span></div>
|
||||
<div id="lat">origin.latitude: <span>0</span></div><p>
|
||||
<div id="scale">scale: <span>240</span></div><p>
|
||||
<div id="translate-x">translate.x: <span>480</span></div>
|
||||
<div id="translate-y">translate.y: <span>250</span></div>
|
||||
<script type="text/javascript">
|
||||
|
||||
$("#scale").slider({
|
||||
min: 0,
|
||||
max: 3000,
|
||||
value: 200,
|
||||
slide: function(event, ui) {
|
||||
xy.scale(ui.value);
|
||||
refresh();
|
||||
}
|
||||
});
|
||||
$("#lon").slider({
|
||||
min: -180,
|
||||
max: 180,
|
||||
step: 1e-1,
|
||||
value: 0,
|
||||
slide: function(event, ui) {
|
||||
var origin = xy.origin();
|
||||
origin[0] = ui.value;
|
||||
xy.origin(origin);
|
||||
refresh();
|
||||
}
|
||||
});
|
||||
|
||||
$("#translate-x").slider({
|
||||
min: -2000,
|
||||
max: 2000,
|
||||
value: 480,
|
||||
slide: function(event, ui) {
|
||||
var translate = xy.translate();
|
||||
translate[0] = ui.value;
|
||||
xy.translate(translate);
|
||||
refresh();
|
||||
}
|
||||
});
|
||||
$("#lat").slider({
|
||||
min: -90,
|
||||
max: 90,
|
||||
step: 1e-1,
|
||||
value: 0,
|
||||
slide: function(event, ui) {
|
||||
var origin = xy.origin();
|
||||
origin[1] = ui.value;
|
||||
xy.origin(origin);
|
||||
refresh();
|
||||
}
|
||||
});
|
||||
|
||||
$("#translate-y").slider({
|
||||
min: -2000,
|
||||
max: 2000,
|
||||
value: 250,
|
||||
slide: function(event, ui) {
|
||||
var translate = xy.translate();
|
||||
translate[1] = ui.value;
|
||||
xy.translate(translate);
|
||||
refresh();
|
||||
}
|
||||
});
|
||||
$("#scale").slider({
|
||||
min: 0,
|
||||
max: 3000,
|
||||
value: 240,
|
||||
slide: function(event, ui) {
|
||||
xy.scale(ui.value);
|
||||
refresh();
|
||||
}
|
||||
});
|
||||
|
||||
$("#translate-x").slider({
|
||||
min: -2000,
|
||||
max: 2000,
|
||||
value: 480,
|
||||
slide: function(event, ui) {
|
||||
var translate = xy.translate();
|
||||
translate[0] = ui.value;
|
||||
xy.translate(translate);
|
||||
refresh();
|
||||
}
|
||||
});
|
||||
|
||||
$("#translate-y").slider({
|
||||
min: -2000,
|
||||
max: 2000,
|
||||
value: 250,
|
||||
slide: function(event, ui) {
|
||||
var translate = xy.translate();
|
||||
translate[1] = ui.value;
|
||||
xy.translate(translate);
|
||||
refresh();
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
|
|
|
@ -1,73 +1,23 @@
|
|||
// Our projection.
|
||||
var xy = d3.geo.azimuthal(),
|
||||
path = d3.geo.path().projection(xy);
|
||||
|
||||
var geopath = function(d) {
|
||||
var c0 = xy([0, 270]), c1 = xy([180, 270]),
|
||||
r = Math.abs(c0[0] - c1[0]) / 2,
|
||||
arc = " A " + r + "," + r + " 0 0,1 ",
|
||||
useArc = false,
|
||||
min = 360,
|
||||
max = 0,
|
||||
i = -1,
|
||||
n = d.length;
|
||||
while (++i < n) {
|
||||
var lon = d[i][0];
|
||||
if (lon < min) min = lon;
|
||||
if (lon > max) max = lon;
|
||||
if (max - min >= 180) {
|
||||
useArc = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return path(d) + (useArc ? " M " + c0.join(",") + arc + c1.join(",") + arc + c0.join(",") + " z" : "");
|
||||
};
|
||||
|
||||
var states = d3.select("body")
|
||||
.append("svg:svg")
|
||||
.append("svg:g")
|
||||
//.attr("fill-rule", "evenodd")
|
||||
.attr("id", "states");
|
||||
|
||||
var grid = d3.select("svg")
|
||||
.append('svg:g');
|
||||
|
||||
grid.selectAll('path')
|
||||
.data(
|
||||
d3.range(0, 90, 10).map(function(lat) { return d3.range(0, 370, 10).map(function(lon) { return [lon, lat] }) }).concat(
|
||||
d3.range(0, 360, 10).map(function(lon) { return d3.range(0, 100, 10).map(function(lat) { return [lon, lat] }) }))
|
||||
)
|
||||
.enter().append("svg:path")
|
||||
.attr('class', 'grid')
|
||||
.attr("d", d3.svg.line()
|
||||
.x(function(d) { return xy(d)[0] })
|
||||
.y(function(d) { return xy(d)[1] })
|
||||
);
|
||||
var xy = d3.geo.azimuthal().scale(240).mode("stereographic"),
|
||||
path = d3.geo.path().projection(xy),
|
||||
svg = d3.select("body").append("svg:svg");
|
||||
|
||||
d3.json("../../data/world-countries.json", function(collection) {
|
||||
|
||||
states
|
||||
.selectAll("path")
|
||||
svg.selectAll("path")
|
||||
.data(collection.features)
|
||||
.enter().append("svg:path")
|
||||
.attr("d", geopath)
|
||||
.attr("d", path)
|
||||
.append("svg:title")
|
||||
.text(function(d) { return d.properties.name; });
|
||||
|
||||
});
|
||||
|
||||
function refresh() {
|
||||
states
|
||||
.selectAll("path")
|
||||
.attr("d", geopath);
|
||||
|
||||
grid
|
||||
.selectAll('path')
|
||||
.attr("d", d3.svg.line()
|
||||
.x(function(d) { return xy(d)[0] })
|
||||
.y(function(d) { return xy(d)[1] })
|
||||
);
|
||||
|
||||
svg.selectAll("path")
|
||||
.attr("d", path);
|
||||
d3.select("#lon span")
|
||||
.text(xy.origin()[0]);
|
||||
d3.select("#lat span")
|
||||
.text(xy.origin()[1]);
|
||||
d3.select("#scale span")
|
||||
.text(xy.scale());
|
||||
d3.select("#translate-x span")
|
||||
|
|
|
@ -1,25 +1,43 @@
|
|||
// TODO clip input coordinates on opposite hemisphere
|
||||
d3.geo.azimuthal = function() {
|
||||
var scale = 200,
|
||||
var mode = "orthographic", // or stereographic
|
||||
origin,
|
||||
scale = 200,
|
||||
translate = [480, 250],
|
||||
pole = 1;
|
||||
x0,
|
||||
y0,
|
||||
cy0,
|
||||
sy0;
|
||||
|
||||
function azimuthal(coordinates) {
|
||||
var p = p < 0 ? 0 :
|
||||
p > 1 ? 1 :
|
||||
1 - pole * coordinates[1] / 90,
|
||||
t = coordinates[0] * d3_radians,
|
||||
x = p * Math.cos(t),
|
||||
y = p * Math.sin(t);
|
||||
var x1 = coordinates[0] * d3_radians - x0,
|
||||
y1 = coordinates[1] * d3_radians,
|
||||
cx1 = Math.cos(x1),
|
||||
sx1 = Math.sin(x1),
|
||||
cy1 = Math.cos(y1),
|
||||
sy1 = Math.sin(y1),
|
||||
k = mode == "stereographic" ? 1 / (1 + sy0 * sy1 + cy0 * cy1 * cx1) : 1,
|
||||
x = k * cy1 * sx1,
|
||||
y = k * (sy0 * cy1 * cx1 - cy0 * sy1);
|
||||
return [
|
||||
scale * x + translate[0],
|
||||
scale * y + translate[1]
|
||||
];
|
||||
}
|
||||
|
||||
// 1 or -1 means N or S hemisphere
|
||||
azimuthal.hemisphere = function(x) {
|
||||
if (!arguments.length) return pole;
|
||||
pole = +x;
|
||||
azimuthal.mode = function(x) {
|
||||
if (!arguments.length) return mode;
|
||||
mode = x;
|
||||
return azimuthal;
|
||||
};
|
||||
|
||||
azimuthal.origin = function(x) {
|
||||
if (!arguments.length) return origin;
|
||||
origin = x;
|
||||
x0 = origin[0] * d3_radians;
|
||||
y0 = origin[1] * d3_radians;
|
||||
cy0 = Math.cos(y0);
|
||||
sy0 = Math.sin(y0);
|
||||
return azimuthal;
|
||||
};
|
||||
|
||||
|
@ -35,5 +53,5 @@ d3.geo.azimuthal = function() {
|
|||
return azimuthal;
|
||||
};
|
||||
|
||||
return azimuthal;
|
||||
return azimuthal.origin([0, 0]);
|
||||
};
|
||||
|
|
Загрузка…
Ссылка в новой задаче