Skip to content

Commit 6a65451

Browse files
committed
Merge branch 'bonne' of https://github.com/jasondavies/d3 into release
2 parents a70975b + 11c1104 commit 6a65451

6 files changed

Lines changed: 340 additions & 1 deletion

File tree

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ d3.geo.js: \
184184
src/geo/geo.js \
185185
src/geo/azimuthal.js \
186186
src/geo/albers.js \
187+
src/geo/bonne.js \
187188
src/geo/equirectangular.js \
188189
src/geo/mercator.js \
189190
src/geo/type.js \

d3.geo.js

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,70 @@ d3.geo.albersUsa = function() {
207207

208208
return albersUsa.scale(lower48.scale());
209209
};
210+
d3.geo.bonne = function() {
211+
var origin,
212+
scale = 200,
213+
translate = [480, 250],
214+
parallel, // 90 for Werner, 0 for Sinusoidal
215+
p0,
216+
ctp0,
217+
x0,
218+
y0;
219+
220+
function bonne(coordinates) {
221+
var x1 = coordinates[0] * d3_geo_radians - x0,
222+
y1 = coordinates[1] * d3_geo_radians - y0,
223+
p = ctp0 + p0 - y1,
224+
E = x1 * Math.cos(y1) / p,
225+
x = p * Math.sin(E),
226+
y = p * Math.cos(E) - ctp0;
227+
return [
228+
scale * x + translate[0],
229+
scale * y + translate[1]
230+
];
231+
}
232+
233+
bonne.invert = function(coordinates) {
234+
var x = (coordinates[0] - translate[0]) / scale,
235+
y = (coordinates[1] - translate[1]) / scale,
236+
c = ctp0 + y,
237+
p = Math.sqrt(x * x + c * c),
238+
y1 = (ctp0 + p0 - p);
239+
return [
240+
(x0 + p * Math.atan2(x, c) / Math.cos(y1)) / d3_geo_radians,
241+
y1 / d3_geo_radians
242+
];
243+
};
244+
245+
bonne.parallel = function(x) {
246+
if (!arguments.length) return parallel;
247+
parallel = +x;
248+
ctp0 = 1 / Math.tan(p0 = parallel * d3_geo_radians);
249+
return bonne;
250+
};
251+
252+
bonne.origin = function(x) {
253+
if (!arguments.length) return origin;
254+
origin = x;
255+
x0 = origin[0] * d3_geo_radians;
256+
y0 = origin[1] * d3_geo_radians;
257+
return bonne;
258+
};
259+
260+
bonne.scale = function(x) {
261+
if (!arguments.length) return scale;
262+
scale = +x;
263+
return bonne;
264+
};
265+
266+
bonne.translate = function(x) {
267+
if (!arguments.length) return translate;
268+
translate = [+x[0], +x[1]];
269+
return bonne;
270+
};
271+
272+
return bonne.origin([0, 0]).parallel(40);
273+
};
210274
d3.geo.equirectangular = function() {
211275
var scale = 500,
212276
translate = [480, 250];

d3.geo.min.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

examples/bonne/bonne.html

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
5+
<title>Bonne Projection</title>
6+
<script type="text/javascript" src="../../d3.js"></script>
7+
<script type="text/javascript" src="../../d3.geo.js"></script>
8+
<script type="text/javascript" src="../../lib/jquery/jquery.min.js"></script>
9+
<script type="text/javascript" src="../../lib/jquery-ui/jquery-ui.min.js"></script>
10+
<style type="text/css">
11+
12+
@import url("../../lib/jquery-ui/jquery-ui.css");
13+
14+
body, .ui-widget {
15+
font: 14px Helvetica Neue;
16+
}
17+
18+
svg {
19+
width: 960px;
20+
height: 500px;
21+
border: solid 1px #ccc;
22+
background: #eee;
23+
}
24+
25+
line {
26+
stroke: brown;
27+
stroke-dasharray: 4,2;
28+
}
29+
30+
path {
31+
fill: #ccc;
32+
stroke: #fff;
33+
}
34+
35+
div {
36+
width: 960px;
37+
}
38+
39+
</style>
40+
</head>
41+
<body>
42+
<h3>Bonne Projection</h3>
43+
<script type="text/javascript">
44+
45+
// Our projection.
46+
var xy = d3.geo.bonne().origin([0, 0]),
47+
path = d3.geo.path().projection(xy);
48+
49+
var states = d3.select("body")
50+
.append("svg:svg")
51+
.append("svg:g")
52+
.attr("id", "states");
53+
54+
var equator = d3.select("svg")
55+
.append("svg:line")
56+
.attr("x1", "0%")
57+
.attr("x2", "100%");
58+
59+
d3.json("../data/world-countries.json", function(collection) {
60+
61+
states
62+
.selectAll("path")
63+
.data(collection.features)
64+
.enter().append("svg:path")
65+
.attr("d", path)
66+
.append("svg:title")
67+
.text(function(d) { return d.properties.name; });
68+
69+
equator
70+
.attr("y1", xy([0, 0])[1])
71+
.attr("y2", xy([0, 0])[1])
72+
});
73+
74+
function refresh() {
75+
states
76+
.selectAll("path")
77+
.attr("d", path);
78+
79+
equator
80+
.attr("y1", xy([0, 0])[1])
81+
.attr("y2", xy([0, 0])[1])
82+
83+
d3.select("#scale span")
84+
.text(xy.scale());
85+
d3.select("#translate-x span")
86+
.text(xy.translate()[0]);
87+
d3.select("#translate-y span")
88+
.text(xy.translate()[1]);
89+
}
90+
91+
</script><p>
92+
<div id="lon">origin.longitude: <span>0</span></div>
93+
<div id="lat">origin.latitude: <span>0</span></div><p>
94+
<div id="scale">scale: <span>500</span></div><p>
95+
<div id="translate-x">translate.x: <span>480</span></div>
96+
<div id="translate-y">translate.y: <span>250</span></div>
97+
<script type="text/javascript">
98+
99+
$("#lon").slider({
100+
min: -180,
101+
max: 180,
102+
step: 1e-1,
103+
value: 0,
104+
slide: function(event, ui) {
105+
var origin = xy.origin();
106+
origin[0] = ui.value;
107+
xy.origin(origin);
108+
refresh();
109+
}
110+
});
111+
112+
$("#lat").slider({
113+
min: -90,
114+
max: 90,
115+
step: 1e-1,
116+
value: 0,
117+
slide: function(event, ui) {
118+
var origin = xy.origin();
119+
origin[1] = ui.value;
120+
xy.origin(origin);
121+
refresh();
122+
}
123+
});
124+
125+
$("#scale").slider({
126+
min: 0,
127+
max: 3000,
128+
value: 500,
129+
slide: function(event, ui) {
130+
xy.scale(ui.value);
131+
refresh();
132+
}
133+
});
134+
135+
$("#translate-x").slider({
136+
min: -2000,
137+
max: 2000,
138+
value: 480,
139+
slide: function(event, ui) {
140+
var translate = xy.translate();
141+
translate[0] = ui.value;
142+
xy.translate(translate);
143+
refresh();
144+
}
145+
});
146+
147+
$("#translate-y").slider({
148+
min: -2000,
149+
max: 2000,
150+
value: 250,
151+
slide: function(event, ui) {
152+
var translate = xy.translate();
153+
translate[1] = ui.value;
154+
xy.translate(translate);
155+
refresh();
156+
}
157+
});
158+
159+
</script>
160+
</body>
161+
</html>

src/geo/bonne.js

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
d3.geo.bonne = function() {
2+
var origin,
3+
scale = 200,
4+
translate = [480, 250],
5+
parallel, // 90 for Werner, 0 for Sinusoidal
6+
p0,
7+
ctp0,
8+
x0,
9+
y0;
10+
11+
function bonne(coordinates) {
12+
var x1 = coordinates[0] * d3_geo_radians - x0,
13+
y1 = coordinates[1] * d3_geo_radians - y0,
14+
p = ctp0 + p0 - y1,
15+
E = x1 * Math.cos(y1) / p,
16+
x = p * Math.sin(E),
17+
y = p * Math.cos(E) - ctp0;
18+
return [
19+
scale * x + translate[0],
20+
scale * y + translate[1]
21+
];
22+
}
23+
24+
bonne.invert = function(coordinates) {
25+
var x = (coordinates[0] - translate[0]) / scale,
26+
y = (coordinates[1] - translate[1]) / scale,
27+
c = ctp0 + y,
28+
p = Math.sqrt(x * x + c * c),
29+
y1 = (ctp0 + p0 - p);
30+
return [
31+
(x0 + p * Math.atan2(x, c) / Math.cos(y1)) / d3_geo_radians,
32+
y1 / d3_geo_radians
33+
];
34+
};
35+
36+
bonne.parallel = function(x) {
37+
if (!arguments.length) return parallel;
38+
parallel = +x;
39+
ctp0 = 1 / Math.tan(p0 = parallel * d3_geo_radians);
40+
return bonne;
41+
};
42+
43+
bonne.origin = function(x) {
44+
if (!arguments.length) return origin;
45+
origin = x;
46+
x0 = origin[0] * d3_geo_radians;
47+
y0 = origin[1] * d3_geo_radians;
48+
return bonne;
49+
};
50+
51+
bonne.scale = function(x) {
52+
if (!arguments.length) return scale;
53+
scale = +x;
54+
return bonne;
55+
};
56+
57+
bonne.translate = function(x) {
58+
if (!arguments.length) return translate;
59+
translate = [+x[0], +x[1]];
60+
return bonne;
61+
};
62+
63+
return bonne.origin([0, 0]).parallel(40);
64+
};

test/geo/bonne-test.js

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
require("../env");
2+
require("../../d3");
3+
require("../../d3.geo");
4+
5+
var vows = require("vows"),
6+
assert = require("assert");
7+
8+
var suite = vows.describe("d3.geo.bonne");
9+
10+
suite.addBatch({
11+
"bonne": {
12+
topic: function() {
13+
return d3.geo.bonne();
14+
},
15+
"Arctic": function(bonne) {
16+
var lonlat = [0, 85],
17+
coords = bonne(lonlat);
18+
assert.inDelta(coords, [480, 92.920367], 1e-6);
19+
assert.inDelta(bonne.invert(coords), lonlat, 1e-6);
20+
},
21+
"Antarctic": function(bonne) {
22+
var lonlat = [0, -85],
23+
coords = bonne(lonlat);
24+
assert.inDelta(coords, [480, 686.332312], 1e-6);
25+
assert.inDelta(bonne.invert(coords), lonlat, 1e-6);
26+
},
27+
"Hawaii": function(bonne) {
28+
var lonlat = [-180, 0],
29+
coords = bonne(lonlat);
30+
assert.inDelta(coords, [103.604887, -22.895998], 1e-6);
31+
assert.inDelta(bonne.invert(coords), lonlat, 1e-6);
32+
},
33+
"Phillipines": function(bonne) {
34+
var lonlat = [180, 0],
35+
coords = bonne(lonlat);
36+
assert.inDelta(coords, [856.395112, -22.895998], 1e-6);
37+
assert.inDelta(bonne.invert(coords), lonlat, 1e-6);
38+
},
39+
"Inversion works for non-zero translation": function() {
40+
var bonne = d3.geo.bonne().translate([123, 99]).scale(100),
41+
coords = bonne([0, 85]),
42+
lonlat = bonne.invert(coords);
43+
assert.inDelta(lonlat[0], 0, 1e-6);
44+
assert.inDelta(lonlat[1], 85, 1e-6);
45+
}
46+
}
47+
});
48+
49+
suite.export(module);

0 commit comments

Comments
 (0)