Skip to content

Commit d023979

Browse files
committed
create simple topojson albers usa recipe
1 parent 1f5c5ae commit d023979

File tree

2 files changed

+338
-0
lines changed

2 files changed

+338
-0
lines changed

lib/topojson.js

+280
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,280 @@
1+
topojson = (function() {
2+
3+
function merge(topology, arcs) {
4+
var fragmentByStart = {},
5+
fragmentByEnd = {};
6+
7+
arcs.forEach(function(i) {
8+
var e = ends(i),
9+
start = e[0],
10+
end = e[1],
11+
f, g;
12+
13+
if (f = fragmentByEnd[start]) {
14+
delete fragmentByEnd[f.end];
15+
f.push(i);
16+
f.end = end;
17+
if (g = fragmentByStart[end]) {
18+
delete fragmentByStart[g.start];
19+
var fg = g === f ? f : f.concat(g);
20+
fragmentByStart[fg.start = f.start] = fragmentByEnd[fg.end = g.end] = fg;
21+
} else if (g = fragmentByEnd[end]) {
22+
delete fragmentByStart[g.start];
23+
delete fragmentByEnd[g.end];
24+
var fg = f.concat(g.map(function(i) { return ~i; }).reverse());
25+
fragmentByStart[fg.start = f.start] = fragmentByEnd[fg.end = g.start] = fg;
26+
} else {
27+
fragmentByStart[f.start] = fragmentByEnd[f.end] = f;
28+
}
29+
} else if (f = fragmentByStart[end]) {
30+
delete fragmentByStart[f.start];
31+
f.unshift(i);
32+
f.start = start;
33+
if (g = fragmentByEnd[start]) {
34+
delete fragmentByEnd[g.end];
35+
var gf = g === f ? f : g.concat(f);
36+
fragmentByStart[gf.start = g.start] = fragmentByEnd[gf.end = f.end] = gf;
37+
} else if (g = fragmentByStart[start]) {
38+
delete fragmentByStart[g.start];
39+
delete fragmentByEnd[g.end];
40+
var gf = g.map(function(i) { return ~i; }).reverse().concat(f);
41+
fragmentByStart[gf.start = g.end] = fragmentByEnd[gf.end = f.end] = gf;
42+
} else {
43+
fragmentByStart[f.start] = fragmentByEnd[f.end] = f;
44+
}
45+
} else if (f = fragmentByStart[start]) {
46+
delete fragmentByStart[f.start];
47+
f.unshift(~i);
48+
f.start = end;
49+
if (g = fragmentByEnd[end]) {
50+
delete fragmentByEnd[g.end];
51+
var gf = g === f ? f : g.concat(f);
52+
fragmentByStart[gf.start = g.start] = fragmentByEnd[gf.end = f.end] = gf;
53+
} else if (g = fragmentByStart[end]) {
54+
delete fragmentByStart[g.start];
55+
delete fragmentByEnd[g.end];
56+
var gf = g.map(function(i) { return ~i; }).reverse().concat(f);
57+
fragmentByStart[gf.start = g.end] = fragmentByEnd[gf.end = f.end] = gf;
58+
} else {
59+
fragmentByStart[f.start] = fragmentByEnd[f.end] = f;
60+
}
61+
} else if (f = fragmentByEnd[end]) {
62+
delete fragmentByEnd[f.end];
63+
f.push(~i);
64+
f.end = start;
65+
if (g = fragmentByEnd[start]) {
66+
delete fragmentByStart[g.start];
67+
var fg = g === f ? f : f.concat(g);
68+
fragmentByStart[fg.start = f.start] = fragmentByEnd[fg.end = g.end] = fg;
69+
} else if (g = fragmentByStart[start]) {
70+
delete fragmentByStart[g.start];
71+
delete fragmentByEnd[g.end];
72+
var fg = f.concat(g.map(function(i) { return ~i; }).reverse());
73+
fragmentByStart[fg.start = f.start] = fragmentByEnd[fg.end = g.start] = fg;
74+
} else {
75+
fragmentByStart[f.start] = fragmentByEnd[f.end] = f;
76+
}
77+
} else {
78+
f = [i];
79+
fragmentByStart[f.start = start] = fragmentByEnd[f.end = end] = f;
80+
}
81+
});
82+
83+
function ends(i) {
84+
var arc = topology.arcs[i], p0 = arc[0], p1 = [0, 0];
85+
arc.forEach(function(dp) { p1[0] += dp[0], p1[1] += dp[1]; });
86+
return [p0, p1];
87+
}
88+
89+
var fragments = [];
90+
for (var k in fragmentByEnd) fragments.push(fragmentByEnd[k]);
91+
return fragments;
92+
}
93+
94+
function mesh(topology, o, filter) {
95+
var arcs = [];
96+
97+
if (arguments.length > 1) {
98+
var geomsByArc = [],
99+
geom;
100+
101+
function arc(i) {
102+
if (i < 0) i = ~i;
103+
(geomsByArc[i] || (geomsByArc[i] = [])).push(geom);
104+
}
105+
106+
function line(arcs) {
107+
arcs.forEach(arc);
108+
}
109+
110+
function polygon(arcs) {
111+
arcs.forEach(line);
112+
}
113+
114+
function geometry(o) {
115+
if (o.type === "GeometryCollection") o.geometries.forEach(geometry);
116+
else if (o.type in geometryType) {
117+
geom = o;
118+
geometryType[o.type](o.arcs);
119+
}
120+
}
121+
122+
var geometryType = {
123+
LineString: line,
124+
MultiLineString: polygon,
125+
Polygon: polygon,
126+
MultiPolygon: function(arcs) { arcs.forEach(polygon); }
127+
};
128+
129+
geometry(o);
130+
131+
geomsByArc.forEach(arguments.length < 3
132+
? function(geoms, i) { arcs.push(i); }
133+
: function(geoms, i) { if (filter(geoms[0], geoms[geoms.length - 1])) arcs.push(i); });
134+
} else {
135+
for (var i = 0, n = topology.arcs.length; i < n; ++i) arcs.push(i);
136+
}
137+
138+
return object(topology, {type: "MultiLineString", arcs: merge(topology, arcs)});
139+
}
140+
141+
function featureOrCollection(topology, o) {
142+
return o.type === "GeometryCollection" ? {
143+
type: "FeatureCollection",
144+
features: o.geometries.map(function(o) { return feature(topology, o); })
145+
} : feature(topology, o);
146+
}
147+
148+
function feature(topology, o) {
149+
var f = {
150+
type: "Feature",
151+
id: o.id,
152+
properties: o.properties || {},
153+
geometry: object(topology, o)
154+
};
155+
if (o.id == null) delete f.id;
156+
return f;
157+
}
158+
159+
function object(topology, o) {
160+
var tf = topology.transform,
161+
kx = tf.scale[0],
162+
ky = tf.scale[1],
163+
dx = tf.translate[0],
164+
dy = tf.translate[1],
165+
arcs = topology.arcs;
166+
167+
function arc(i, points) {
168+
if (points.length) points.pop();
169+
for (var a = arcs[i < 0 ? ~i : i], k = 0, n = a.length, x = 0, y = 0, p; k < n; ++k) points.push([
170+
(x += (p = a[k])[0]) * kx + dx,
171+
(y += p[1]) * ky + dy
172+
]);
173+
if (i < 0) reverse(points, n);
174+
}
175+
176+
function point(coordinates) {
177+
return [coordinates[0] * kx + dx, coordinates[1] * ky + dy];
178+
}
179+
180+
function line(arcs) {
181+
var points = [];
182+
for (var i = 0, n = arcs.length; i < n; ++i) arc(arcs[i], points);
183+
if (points.length < 2) points.push(points[0].slice());
184+
return points;
185+
}
186+
187+
function ring(arcs) {
188+
var points = line(arcs);
189+
while (points.length < 4) points.push(points[0].slice());
190+
return points;
191+
}
192+
193+
function polygon(arcs) {
194+
return arcs.map(ring);
195+
}
196+
197+
function geometry(o) {
198+
var t = o.type;
199+
return t === "GeometryCollection" ? {type: t, geometries: o.geometries.map(geometry)}
200+
: t in geometryType ? {type: t, coordinates: geometryType[t](o)}
201+
: null;
202+
}
203+
204+
var geometryType = {
205+
Point: function(o) { return point(o.coordinates); },
206+
MultiPoint: function(o) { return o.coordinates.map(point); },
207+
LineString: function(o) { return line(o.arcs); },
208+
MultiLineString: function(o) { return o.arcs.map(line); },
209+
Polygon: function(o) { return polygon(o.arcs); },
210+
MultiPolygon: function(o) { return o.arcs.map(polygon); }
211+
};
212+
213+
return geometry(o);
214+
}
215+
216+
function reverse(array, n) {
217+
var t, j = array.length, i = j - n; while (i < --j) t = array[i], array[i++] = array[j], array[j] = t;
218+
}
219+
220+
function bisect(a, x) {
221+
var lo = 0, hi = a.length;
222+
while (lo < hi) {
223+
var mid = lo + hi >>> 1;
224+
if (a[mid] < x) lo = mid + 1;
225+
else hi = mid;
226+
}
227+
return lo;
228+
}
229+
230+
function neighbors(objects) {
231+
var indexesByArc = {}, // arc index -> array of object indexes
232+
neighbors = objects.map(function() { return []; });
233+
234+
function line(arcs, i) {
235+
arcs.forEach(function(a) {
236+
if (a < 0) a = ~a;
237+
var o = indexesByArc[a];
238+
if (o) o.push(i);
239+
else indexesByArc[a] = [i];
240+
});
241+
}
242+
243+
function polygon(arcs, i) {
244+
arcs.forEach(function(arc) { line(arc, i); });
245+
}
246+
247+
function geometry(o, i) {
248+
if (o.type === "GeometryCollection") o.geometries.forEach(function(o) { geometry(o, i); });
249+
else if (o.type in geometryType) geometryType[o.type](o.arcs, i);
250+
}
251+
252+
var geometryType = {
253+
LineString: line,
254+
MultiLineString: polygon,
255+
Polygon: polygon,
256+
MultiPolygon: function(arcs, i) { arcs.forEach(function(arc) { polygon(arc, i); }); }
257+
};
258+
259+
objects.forEach(geometry);
260+
261+
for (var i in indexesByArc) {
262+
for (var indexes = indexesByArc[i], m = indexes.length, j = 0; j < m; ++j) {
263+
for (var k = j + 1; k < m; ++k) {
264+
var ij = indexes[j], ik = indexes[k], n;
265+
if ((n = neighbors[ij])[i = bisect(n, ik)] !== ik) n.splice(i, 0, ik);
266+
if ((n = neighbors[ik])[i = bisect(n, ij)] !== ij) n.splice(i, 0, ij);
267+
}
268+
}
269+
}
270+
271+
return neighbors;
272+
}
273+
274+
return {
275+
version: "1.2.3",
276+
mesh: mesh,
277+
feature: featureOrCollection,
278+
neighbors: neighbors
279+
};
280+
})();

src/chapter12/usa.html

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<meta charset="utf-8">
5+
<meta name="viewport" content="initial-scale=1,maximum-scale=1"/>
6+
<title>Albers USA with TopoJSON</title>
7+
<link rel="stylesheet" type="text/css" href="../../css/styles.css"/>
8+
<script type="text/javascript" src="../../lib/d3.js"></script>
9+
<script type="text/javascript" src="../../lib/topojson.js"></script>
10+
11+
<style>
12+
path {
13+
fill: #ccc;
14+
stroke: #fff;
15+
stroke-width: .5px;
16+
}
17+
18+
path:hover {
19+
fill: red;
20+
}
21+
</style>
22+
</head>
23+
24+
<body>
25+
26+
<script type="text/javascript">
27+
var width = 960,
28+
height = 500;
29+
30+
var path = d3.geo.path();
31+
32+
var svg = d3.select("body").append("svg")
33+
.attr("width", width)
34+
.attr("height", height)
35+
.append('g')
36+
.call(
37+
d3.behavior.zoom()
38+
.scaleExtent([1, 10])
39+
.on("zoom", zoom)
40+
);
41+
42+
d3.json("/data/us.json", function (error, topology) {
43+
svg.selectAll("path")
44+
.data(topojson.feature(topology, topology.objects.counties).features)
45+
.enter().append("path")
46+
.attr("d", path);
47+
});
48+
49+
function zoom() {
50+
svg.attr("transform", "translate("
51+
+ d3.event.translate
52+
+ ")scale(" + d3.event.scale + ")");
53+
}
54+
</script>
55+
56+
</body>
57+
58+
</html>

0 commit comments

Comments
 (0)