Stereographic & orthographic projections!

These now have arbitrary origins, as well. Still need to clip, though.
This commit is contained in:
Mike Bostock 2011-05-28 00:13:53 -07:00
Родитель 987755b01e
Коммит b9eff658a3
6 изменённых файлов: 137 добавлений и 134 удалений

Просмотреть файл

@ -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

2
d3.geo.min.js поставляемый

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Просмотреть файл

@ -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]);
};