Permalink
Browse files

Stereographic & orthographic projections!

These now have arbitrary origins, as well. Still need to clip, though.
  • Loading branch information...
1 parent 987755b commit b9eff658a3244a92489ba60f725aaefc50034141 @mbostock mbostock committed May 28, 2011
Showing with 137 additions and 134 deletions.
  1. +31 −13 d3.geo.js
  2. +1 −1 d3.geo.min.js
  3. +1 −12 examples/azimuthal/azimuthal.css
  4. +62 −34 examples/azimuthal/azimuthal.html
  5. +11 −61 examples/azimuthal/azimuthal.js
  6. +31 −13 src/geo/azimuthal.js
View
@@ -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
View

Some generated files are not rendered by default. Learn more.

Oops, something went wrong.
@@ -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")
View
@@ -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]);
};

0 comments on commit b9eff65

Please sign in to comment.