├── .editorconfig ├── .gitignore ├── .jshintrc ├── LICENSE ├── README.md ├── d3.geo ├── README.md ├── d3f.js ├── makefile └── package.json ├── dist └── async-cartogram.js ├── example ├── Gruntfile.js ├── README.md ├── capture.png ├── data │ ├── .~lock.metriques-paris.csv# │ ├── arrondissements.geojson │ ├── arrondissements.json │ ├── metriques-paris.csv │ └── source.md ├── dist │ ├── assets │ │ └── main.js │ ├── favicon.ico │ ├── images │ │ ├── latoureiffel.svg │ │ └── yeoman.png │ └── index.html ├── package.json ├── src │ ├── components │ │ └── AsyncCartogramApp.js │ ├── favicon.ico │ ├── images │ │ ├── latoureiffel.svg │ │ └── yeoman.png │ ├── index.html │ └── styles │ │ └── main.css ├── webpack.config.js └── webpack.dist.config.js ├── interestingPapers ├── Dougeniketal1985.pdf ├── IDEAS00.pdf └── TD-54FMRT.pdf ├── package.json ├── src ├── cartogram-helpers.js ├── cartogram-worker.js └── cartogramaster.js └── webpack.config.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 4 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### SublimeText ### 2 | *.sublime-workspace 3 | 4 | ### OSX ### 5 | .DS_Store 6 | .AppleDouble 7 | .LSOverride 8 | Icon 9 | 10 | # Thumbnails 11 | ._* 12 | 13 | # Files that might appear on external disk 14 | .Spotlight-V100 15 | .Trashes 16 | 17 | ### Windows ### 18 | # Windows image file caches 19 | Thumbs.db 20 | ehthumbs.db 21 | 22 | # Folder config file 23 | Desktop.ini 24 | 25 | # Recycle Bin used on file shares 26 | $RECYCLE.BIN/ 27 | 28 | # App specific 29 | 30 | node_modules/ 31 | .tmp 32 | /src/main.js 33 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": true, 3 | "browser": true, 4 | "esnext": true, 5 | "bitwise": true, 6 | "camelcase": false, 7 | "curly": true, 8 | "eqeqeq": true, 9 | "immed": true, 10 | "indent": 2, 11 | "latedef": true, 12 | "newcap": true, 13 | "noarg": true, 14 | "quotmark": "false", 15 | "regexp": true, 16 | "undef": true, 17 | "unused": false, 18 | "strict": true, 19 | "trailing": true, 20 | "smarttabs": true, 21 | "white": true, 22 | "newcap": false, 23 | "globals": { 24 | "React": true, 25 | "d3": true 26 | }, 27 | "asi": true 28 | } 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2012 Shawn Allen 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | What ? 2 | ----------- 3 | 4 | A **js cartogram library** that does not block the browser, using **web workers**, forked from the original [d3 cartogram](http://prag.ma/code/d3-cartogram/). It can also be run with **node**, which is nice for preprocessing geometries offline. 5 | 6 | - Designed to handle a series of cartograms (called tasks) : compute all the maps at once then display them smoothly. 7 | 8 | - Using only a geo subset of d3 (you'll probably use d3 again to display the map in the browser, add colors, transitions, hovers...). 9 | 10 | - Also attempts to add the effective range tip from Algorithms for Cartogram Animation, 11 | by Ouyang et al., hopefully making it slightly faster. 12 | 13 | Usage : 14 | -------- 15 | 16 | The library is compiled in `dist/` as [UMD](https://github.com/umdjs/umd), and has been tested using `require("dist/async-cartogram.js")` 17 | 18 | ```js 19 | var promiseOfGeometries = AsyncCartogram( 20 | { 21 | topology: topojsonData, 22 | // geometries to reshape: 23 | geometries: topojsonData.objects.OBJECTNAME.geometries, 24 | projection: { 25 | name: 'mercator', 26 | translation: [X,Y], 27 | scaling: scalingFactor, //e.g. 2000 28 | center: [long, lat] 29 | } 30 | }, 31 | values, // { taskId => { geoJsonFeatureValue => newArea} } 32 | featureProperty // geoJsonFeatureIdKey to link geojson features to values 33 | ); 34 | ``` 35 | 36 | See the [example](https://github.com/laem/async-cartogram/tree/master/example) app (cartogram of Paris) for detailed usage. 37 | 38 | 39 | ![L'escargot](https://raw.githubusercontent.com/laem/async-cartogram/master/example/capture.png) 40 | 41 | Check the project for which this lib was created, [a 90-years cartogram of europe](https://github.com/laem/eurpop). 42 | -------------------------------------------------------------------------------- /d3.geo/README.md: -------------------------------------------------------------------------------- 1 | This is a [partial d3 build](https://github.com/mbostock/smash/wiki). 2 | -------------------------------------------------------------------------------- /d3.geo/d3f.js: -------------------------------------------------------------------------------- 1 | !function(){ 2 | var d3f = {version: "3.5.5"}; // semver 3 | function d3f_identity(d) { 4 | return d; 5 | } 6 | var ε = 1e-6, 7 | ε2 = ε * ε, 8 | π = Math.PI, 9 | τ = 2 * π, 10 | τε = τ - ε, 11 | halfπ = π / 2, 12 | d3f_radians = π / 180, 13 | d3f_degrees = 180 / π; 14 | 15 | function d3f_sgn(x) { 16 | return x > 0 ? 1 : x < 0 ? -1 : 0; 17 | } 18 | 19 | // Returns the 2D cross product of AB and AC vectors, i.e., the z-component of 20 | // the 3D cross product in a quadrant I Cartesian coordinate system (+x is 21 | // right, +y is up). Returns a positive value if ABC is counter-clockwise, 22 | // negative if clockwise, and zero if the points are collinear. 23 | function d3f_cross2d(a, b, c) { 24 | return (b[0] - a[0]) * (c[1] - a[1]) - (b[1] - a[1]) * (c[0] - a[0]); 25 | } 26 | 27 | function d3f_acos(x) { 28 | return x > 1 ? 0 : x < -1 ? π : Math.acos(x); 29 | } 30 | 31 | function d3f_asin(x) { 32 | return x > 1 ? halfπ : x < -1 ? -halfπ : Math.asin(x); 33 | } 34 | 35 | function d3f_sinh(x) { 36 | return ((x = Math.exp(x)) - 1 / x) / 2; 37 | } 38 | 39 | function d3f_cosh(x) { 40 | return ((x = Math.exp(x)) + 1 / x) / 2; 41 | } 42 | 43 | function d3f_tanh(x) { 44 | return ((x = Math.exp(2 * x)) - 1) / (x + 1); 45 | } 46 | 47 | function d3f_haversin(x) { 48 | return (x = Math.sin(x / 2)) * x; 49 | } 50 | d3f.geo = {}; 51 | // Copies a variable number of methods from source to target. 52 | d3f.rebind = function(target, source) { 53 | var i = 1, n = arguments.length, method; 54 | while (++i < n) target[method = arguments[i]] = d3f_rebind(target, source, source[method]); 55 | return target; 56 | }; 57 | 58 | // Method is assumed to be a standard D3 getter-setter: 59 | // If passed with no arguments, gets the value. 60 | // If passed with arguments, sets the value and returns the target. 61 | function d3f_rebind(target, source, method) { 62 | return function() { 63 | var value = method.apply(source, arguments); 64 | return value === source ? target : value; 65 | }; 66 | } 67 | function d3f_true() { 68 | return true; 69 | } 70 | var abs = Math.abs; 71 | d3f.merge = function(arrays) { 72 | var n = arrays.length, 73 | m, 74 | i = -1, 75 | j = 0, 76 | merged, 77 | array; 78 | 79 | while (++i < n) j += arrays[i].length; 80 | merged = new Array(j); 81 | 82 | while (--n >= 0) { 83 | array = arrays[n]; 84 | m = array.length; 85 | while (--m >= 0) { 86 | merged[--j] = array[m]; 87 | } 88 | } 89 | 90 | return merged; 91 | }; 92 | function d3f_noop() {} 93 | 94 | function d3f_geo_spherical(cartesian) { 95 | return [ 96 | Math.atan2(cartesian[1], cartesian[0]), 97 | d3f_asin(cartesian[2]) 98 | ]; 99 | } 100 | 101 | function d3f_geo_sphericalEqual(a, b) { 102 | return abs(a[0] - b[0]) < ε && abs(a[1] - b[1]) < ε; 103 | } 104 | 105 | // General spherical polygon clipping algorithm: takes a polygon, cuts it into 106 | // visible line segments and rejoins the segments by interpolating along the 107 | // clip edge. 108 | function d3f_geo_clipPolygon(segments, compare, clipStartInside, interpolate, listener) { 109 | var subject = [], 110 | clip = []; 111 | 112 | segments.forEach(function(segment) { 113 | if ((n = segment.length - 1) <= 0) return; 114 | var n, p0 = segment[0], p1 = segment[n]; 115 | 116 | // If the first and last points of a segment are coincident, then treat as 117 | // a closed ring. 118 | // TODO if all rings are closed, then the winding order of the exterior 119 | // ring should be checked. 120 | if (d3f_geo_sphericalEqual(p0, p1)) { 121 | listener.lineStart(); 122 | for (var i = 0; i < n; ++i) listener.point((p0 = segment[i])[0], p0[1]); 123 | listener.lineEnd(); 124 | return; 125 | } 126 | 127 | var a = new d3f_geo_clipPolygonIntersection(p0, segment, null, true), 128 | b = new d3f_geo_clipPolygonIntersection(p0, null, a, false); 129 | a.o = b; 130 | subject.push(a); 131 | clip.push(b); 132 | a = new d3f_geo_clipPolygonIntersection(p1, segment, null, false); 133 | b = new d3f_geo_clipPolygonIntersection(p1, null, a, true); 134 | a.o = b; 135 | subject.push(a); 136 | clip.push(b); 137 | }); 138 | clip.sort(compare); 139 | d3f_geo_clipPolygonLinkCircular(subject); 140 | d3f_geo_clipPolygonLinkCircular(clip); 141 | if (!subject.length) return; 142 | 143 | for (var i = 0, entry = clipStartInside, n = clip.length; i < n; ++i) { 144 | clip[i].e = entry = !entry; 145 | } 146 | 147 | var start = subject[0], 148 | points, 149 | point; 150 | while (1) { 151 | // Find first unvisited intersection. 152 | var current = start, 153 | isSubject = true; 154 | while (current.v) if ((current = current.n) === start) return; 155 | points = current.z; 156 | listener.lineStart(); 157 | do { 158 | current.v = current.o.v = true; 159 | if (current.e) { 160 | if (isSubject) { 161 | for (var i = 0, n = points.length; i < n; ++i) listener.point((point = points[i])[0], point[1]); 162 | } else { 163 | interpolate(current.x, current.n.x, 1, listener); 164 | } 165 | current = current.n; 166 | } else { 167 | if (isSubject) { 168 | points = current.p.z; 169 | for (var i = points.length - 1; i >= 0; --i) listener.point((point = points[i])[0], point[1]); 170 | } else { 171 | interpolate(current.x, current.p.x, -1, listener); 172 | } 173 | current = current.p; 174 | } 175 | current = current.o; 176 | points = current.z; 177 | isSubject = !isSubject; 178 | } while (!current.v); 179 | listener.lineEnd(); 180 | } 181 | } 182 | 183 | function d3f_geo_clipPolygonLinkCircular(array) { 184 | if (!(n = array.length)) return; 185 | var n, 186 | i = 0, 187 | a = array[0], 188 | b; 189 | while (++i < n) { 190 | a.n = b = array[i]; 191 | b.p = a; 192 | a = b; 193 | } 194 | a.n = b = array[0]; 195 | b.p = a; 196 | } 197 | 198 | function d3f_geo_clipPolygonIntersection(point, points, other, entry) { 199 | this.x = point; 200 | this.z = points; 201 | this.o = other; // another intersection 202 | this.e = entry; // is an entry? 203 | this.v = false; // visited 204 | this.n = this.p = null; // next & previous 205 | } 206 | 207 | function d3f_geo_clip(pointVisible, clipLine, interpolate, clipStart) { 208 | return function(rotate, listener) { 209 | var line = clipLine(listener), 210 | rotatedClipStart = rotate.invert(clipStart[0], clipStart[1]); 211 | 212 | var clip = { 213 | point: point, 214 | lineStart: lineStart, 215 | lineEnd: lineEnd, 216 | polygonStart: function() { 217 | clip.point = pointRing; 218 | clip.lineStart = ringStart; 219 | clip.lineEnd = ringEnd; 220 | segments = []; 221 | polygon = []; 222 | }, 223 | polygonEnd: function() { 224 | clip.point = point; 225 | clip.lineStart = lineStart; 226 | clip.lineEnd = lineEnd; 227 | 228 | segments = d3f.merge(segments); 229 | var clipStartInside = d3f_geo_pointInPolygon(rotatedClipStart, polygon); 230 | if (segments.length) { 231 | if (!polygonStarted) listener.polygonStart(), polygonStarted = true; 232 | d3f_geo_clipPolygon(segments, d3f_geo_clipSort, clipStartInside, interpolate, listener); 233 | } else if (clipStartInside) { 234 | if (!polygonStarted) listener.polygonStart(), polygonStarted = true; 235 | listener.lineStart(); 236 | interpolate(null, null, 1, listener); 237 | listener.lineEnd(); 238 | } 239 | if (polygonStarted) listener.polygonEnd(), polygonStarted = false; 240 | segments = polygon = null; 241 | }, 242 | sphere: function() { 243 | listener.polygonStart(); 244 | listener.lineStart(); 245 | interpolate(null, null, 1, listener); 246 | listener.lineEnd(); 247 | listener.polygonEnd(); 248 | } 249 | }; 250 | 251 | function point(λ, φ) { 252 | var point = rotate(λ, φ); 253 | if (pointVisible(λ = point[0], φ = point[1])) listener.point(λ, φ); 254 | } 255 | function pointLine(λ, φ) { 256 | var point = rotate(λ, φ); 257 | line.point(point[0], point[1]); 258 | } 259 | function lineStart() { clip.point = pointLine; line.lineStart(); } 260 | function lineEnd() { clip.point = point; line.lineEnd(); } 261 | 262 | var segments; 263 | 264 | var buffer = d3f_geo_clipBufferListener(), 265 | ringListener = clipLine(buffer), 266 | polygonStarted = false, 267 | polygon, 268 | ring; 269 | 270 | function pointRing(λ, φ) { 271 | ring.push([λ, φ]); 272 | var point = rotate(λ, φ); 273 | ringListener.point(point[0], point[1]); 274 | } 275 | 276 | function ringStart() { 277 | ringListener.lineStart(); 278 | ring = []; 279 | } 280 | 281 | function ringEnd() { 282 | pointRing(ring[0][0], ring[0][1]); 283 | ringListener.lineEnd(); 284 | 285 | var clean = ringListener.clean(), 286 | ringSegments = buffer.buffer(), 287 | segment, 288 | n = ringSegments.length; 289 | 290 | ring.pop(); 291 | polygon.push(ring); 292 | ring = null; 293 | 294 | if (!n) return; 295 | 296 | // No intersections. 297 | if (clean & 1) { 298 | segment = ringSegments[0]; 299 | var n = segment.length - 1, 300 | i = -1, 301 | point; 302 | if (n > 0) { 303 | if (!polygonStarted) listener.polygonStart(), polygonStarted = true; 304 | listener.lineStart(); 305 | while (++i < n) listener.point((point = segment[i])[0], point[1]); 306 | listener.lineEnd(); 307 | } 308 | return; 309 | } 310 | 311 | // Rejoin connected segments. 312 | // TODO reuse bufferListener.rejoin()? 313 | if (n > 1 && clean & 2) ringSegments.push(ringSegments.pop().concat(ringSegments.shift())); 314 | 315 | segments.push(ringSegments.filter(d3f_geo_clipSegmentLength1)); 316 | } 317 | 318 | return clip; 319 | }; 320 | } 321 | 322 | function d3f_geo_clipSegmentLength1(segment) { 323 | return segment.length > 1; 324 | } 325 | 326 | function d3f_geo_clipBufferListener() { 327 | var lines = [], 328 | line; 329 | return { 330 | lineStart: function() { lines.push(line = []); }, 331 | point: function(λ, φ) { line.push([λ, φ]); }, 332 | lineEnd: d3f_noop, 333 | buffer: function() { 334 | var buffer = lines; 335 | lines = []; 336 | line = null; 337 | return buffer; 338 | }, 339 | rejoin: function() { 340 | if (lines.length > 1) lines.push(lines.pop().concat(lines.shift())); 341 | } 342 | }; 343 | } 344 | 345 | // Intersection points are sorted along the clip edge. For both antimeridian 346 | // cutting and circle clipping, the same comparison is used. 347 | function d3f_geo_clipSort(a, b) { 348 | return ((a = a.x)[0] < 0 ? a[1] - halfπ - ε : halfπ - a[1]) 349 | - ((b = b.x)[0] < 0 ? b[1] - halfπ - ε : halfπ - b[1]); 350 | } 351 | 352 | var d3f_geo_clipAntimeridian = d3f_geo_clip( 353 | d3f_true, 354 | d3f_geo_clipAntimeridianLine, 355 | d3f_geo_clipAntimeridianInterpolate, 356 | [-π, -π / 2]); 357 | 358 | // Takes a line and cuts into visible segments. Return values: 359 | // 0: there were intersections or the line was empty. 360 | // 1: no intersections. 361 | // 2: there were intersections, and the first and last segments should be 362 | // rejoined. 363 | function d3f_geo_clipAntimeridianLine(listener) { 364 | var λ0 = NaN, 365 | φ0 = NaN, 366 | sλ0 = NaN, 367 | clean; // no intersections 368 | 369 | return { 370 | lineStart: function() { 371 | listener.lineStart(); 372 | clean = 1; 373 | }, 374 | point: function(λ1, φ1) { 375 | var sλ1 = λ1 > 0 ? π : -π, 376 | dλ = abs(λ1 - λ0); 377 | if (abs(dλ - π) < ε) { // line crosses a pole 378 | listener.point(λ0, φ0 = (φ0 + φ1) / 2 > 0 ? halfπ : -halfπ); 379 | listener.point(sλ0, φ0); 380 | listener.lineEnd(); 381 | listener.lineStart(); 382 | listener.point(sλ1, φ0); 383 | listener.point(λ1, φ0); 384 | clean = 0; 385 | } else if (sλ0 !== sλ1 && dλ >= π) { // line crosses antimeridian 386 | // handle degeneracies 387 | if (abs(λ0 - sλ0) < ε) λ0 -= sλ0 * ε; 388 | if (abs(λ1 - sλ1) < ε) λ1 -= sλ1 * ε; 389 | φ0 = d3f_geo_clipAntimeridianIntersect(λ0, φ0, λ1, φ1); 390 | listener.point(sλ0, φ0); 391 | listener.lineEnd(); 392 | listener.lineStart(); 393 | listener.point(sλ1, φ0); 394 | clean = 0; 395 | } 396 | listener.point(λ0 = λ1, φ0 = φ1); 397 | sλ0 = sλ1; 398 | }, 399 | lineEnd: function() { 400 | listener.lineEnd(); 401 | λ0 = φ0 = NaN; 402 | }, 403 | // if there are intersections, we always rejoin the first and last segments. 404 | clean: function() { return 2 - clean; } 405 | }; 406 | } 407 | 408 | function d3f_geo_clipAntimeridianIntersect(λ0, φ0, λ1, φ1) { 409 | var cosφ0, 410 | cosφ1, 411 | sinλ0_λ1 = Math.sin(λ0 - λ1); 412 | return abs(sinλ0_λ1) > ε 413 | ? Math.atan((Math.sin(φ0) * (cosφ1 = Math.cos(φ1)) * Math.sin(λ1) 414 | - Math.sin(φ1) * (cosφ0 = Math.cos(φ0)) * Math.sin(λ0)) 415 | / (cosφ0 * cosφ1 * sinλ0_λ1)) 416 | : (φ0 + φ1) / 2; 417 | } 418 | 419 | function d3f_geo_clipAntimeridianInterpolate(from, to, direction, listener) { 420 | var φ; 421 | if (from == null) { 422 | φ = direction * halfπ; 423 | listener.point(-π, φ); 424 | listener.point( 0, φ); 425 | listener.point( π, φ); 426 | listener.point( π, 0); 427 | listener.point( π, -φ); 428 | listener.point( 0, -φ); 429 | listener.point(-π, -φ); 430 | listener.point(-π, 0); 431 | listener.point(-π, φ); 432 | } else if (abs(from[0] - to[0]) > ε) { 433 | var s = from[0] < to[0] ? π : -π; 434 | φ = direction * s / 2; 435 | listener.point(-s, φ); 436 | listener.point( 0, φ); 437 | listener.point( s, φ); 438 | } else { 439 | listener.point(to[0], to[1]); 440 | } 441 | } 442 | // TODO 443 | // cross and scale return new vectors, 444 | // whereas add and normalize operate in-place 445 | 446 | function d3f_geo_cartesian(spherical) { 447 | var λ = spherical[0], 448 | φ = spherical[1], 449 | cosφ = Math.cos(φ); 450 | return [ 451 | cosφ * Math.cos(λ), 452 | cosφ * Math.sin(λ), 453 | Math.sin(φ) 454 | ]; 455 | } 456 | 457 | function d3f_geo_cartesianDot(a, b) { 458 | return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; 459 | } 460 | 461 | function d3f_geo_cartesianCross(a, b) { 462 | return [ 463 | a[1] * b[2] - a[2] * b[1], 464 | a[2] * b[0] - a[0] * b[2], 465 | a[0] * b[1] - a[1] * b[0] 466 | ]; 467 | } 468 | 469 | function d3f_geo_cartesianAdd(a, b) { 470 | a[0] += b[0]; 471 | a[1] += b[1]; 472 | a[2] += b[2]; 473 | } 474 | 475 | function d3f_geo_cartesianScale(vector, k) { 476 | return [ 477 | vector[0] * k, 478 | vector[1] * k, 479 | vector[2] * k 480 | ]; 481 | } 482 | 483 | function d3f_geo_cartesianNormalize(d) { 484 | var l = Math.sqrt(d[0] * d[0] + d[1] * d[1] + d[2] * d[2]); 485 | d[0] /= l; 486 | d[1] /= l; 487 | d[2] /= l; 488 | } 489 | function d3f_geo_compose(a, b) { 490 | 491 | function compose(x, y) { 492 | return x = a(x, y), b(x[0], x[1]); 493 | } 494 | 495 | if (a.invert && b.invert) compose.invert = function(x, y) { 496 | return x = b.invert(x, y), x && a.invert(x[0], x[1]); 497 | }; 498 | 499 | return compose; 500 | } 501 | 502 | function d3f_geo_equirectangular(λ, φ) { 503 | return [λ, φ]; 504 | } 505 | 506 | (d3f.geo.equirectangular = function() { 507 | return d3f_geo_projection(d3f_geo_equirectangular); 508 | }).raw = d3f_geo_equirectangular.invert = d3f_geo_equirectangular; 509 | 510 | d3f.geo.rotation = function(rotate) { 511 | rotate = d3f_geo_rotation(rotate[0] % 360 * d3f_radians, rotate[1] * d3f_radians, rotate.length > 2 ? rotate[2] * d3f_radians : 0); 512 | 513 | function forward(coordinates) { 514 | coordinates = rotate(coordinates[0] * d3f_radians, coordinates[1] * d3f_radians); 515 | return coordinates[0] *= d3f_degrees, coordinates[1] *= d3f_degrees, coordinates; 516 | } 517 | 518 | forward.invert = function(coordinates) { 519 | coordinates = rotate.invert(coordinates[0] * d3f_radians, coordinates[1] * d3f_radians); 520 | return coordinates[0] *= d3f_degrees, coordinates[1] *= d3f_degrees, coordinates; 521 | }; 522 | 523 | return forward; 524 | }; 525 | 526 | function d3f_geo_identityRotation(λ, φ) { 527 | return [λ > π ? λ - τ : λ < -π ? λ + τ : λ, φ]; 528 | } 529 | 530 | d3f_geo_identityRotation.invert = d3f_geo_equirectangular; 531 | 532 | // Note: |δλ| must be < 2π 533 | function d3f_geo_rotation(δλ, δφ, δγ) { 534 | return δλ ? (δφ || δγ ? d3f_geo_compose(d3f_geo_rotationλ(δλ), d3f_geo_rotationφγ(δφ, δγ)) 535 | : d3f_geo_rotationλ(δλ)) 536 | : (δφ || δγ ? d3f_geo_rotationφγ(δφ, δγ) 537 | : d3f_geo_identityRotation); 538 | } 539 | 540 | function d3f_geo_forwardRotationλ(δλ) { 541 | return function(λ, φ) { 542 | return λ += δλ, [λ > π ? λ - τ : λ < -π ? λ + τ : λ, φ]; 543 | }; 544 | } 545 | 546 | function d3f_geo_rotationλ(δλ) { 547 | var rotation = d3f_geo_forwardRotationλ(δλ); 548 | rotation.invert = d3f_geo_forwardRotationλ(-δλ); 549 | return rotation; 550 | } 551 | 552 | function d3f_geo_rotationφγ(δφ, δγ) { 553 | var cosδφ = Math.cos(δφ), 554 | sinδφ = Math.sin(δφ), 555 | cosδγ = Math.cos(δγ), 556 | sinδγ = Math.sin(δγ); 557 | 558 | function rotation(λ, φ) { 559 | var cosφ = Math.cos(φ), 560 | x = Math.cos(λ) * cosφ, 561 | y = Math.sin(λ) * cosφ, 562 | z = Math.sin(φ), 563 | k = z * cosδφ + x * sinδφ; 564 | return [ 565 | Math.atan2(y * cosδγ - k * sinδγ, x * cosδφ - z * sinδφ), 566 | d3f_asin(k * cosδγ + y * sinδγ) 567 | ]; 568 | } 569 | 570 | rotation.invert = function(λ, φ) { 571 | var cosφ = Math.cos(φ), 572 | x = Math.cos(λ) * cosφ, 573 | y = Math.sin(λ) * cosφ, 574 | z = Math.sin(φ), 575 | k = z * cosδγ - y * sinδγ; 576 | return [ 577 | Math.atan2(y * cosδγ + z * sinδγ, x * cosδφ + k * sinδφ), 578 | d3f_asin(k * cosδφ - x * sinδφ) 579 | ]; 580 | }; 581 | 582 | return rotation; 583 | } 584 | 585 | d3f.geo.circle = function() { 586 | var origin = [0, 0], 587 | angle, 588 | precision = 6, 589 | interpolate; 590 | 591 | function circle() { 592 | var center = typeof origin === "function" ? origin.apply(this, arguments) : origin, 593 | rotate = d3f_geo_rotation(-center[0] * d3f_radians, -center[1] * d3f_radians, 0).invert, 594 | ring = []; 595 | 596 | interpolate(null, null, 1, { 597 | point: function(x, y) { 598 | ring.push(x = rotate(x, y)); 599 | x[0] *= d3f_degrees, x[1] *= d3f_degrees; 600 | } 601 | }); 602 | 603 | return {type: "Polygon", coordinates: [ring]}; 604 | } 605 | 606 | circle.origin = function(x) { 607 | if (!arguments.length) return origin; 608 | origin = x; 609 | return circle; 610 | }; 611 | 612 | circle.angle = function(x) { 613 | if (!arguments.length) return angle; 614 | interpolate = d3f_geo_circleInterpolate((angle = +x) * d3f_radians, precision * d3f_radians); 615 | return circle; 616 | }; 617 | 618 | circle.precision = function(_) { 619 | if (!arguments.length) return precision; 620 | interpolate = d3f_geo_circleInterpolate(angle * d3f_radians, (precision = +_) * d3f_radians); 621 | return circle; 622 | }; 623 | 624 | return circle.angle(90); 625 | }; 626 | 627 | // Interpolates along a circle centered at [0°, 0°], with a given radius and 628 | // precision. 629 | function d3f_geo_circleInterpolate(radius, precision) { 630 | var cr = Math.cos(radius), 631 | sr = Math.sin(radius); 632 | return function(from, to, direction, listener) { 633 | var step = direction * precision; 634 | if (from != null) { 635 | from = d3f_geo_circleAngle(cr, from); 636 | to = d3f_geo_circleAngle(cr, to); 637 | if (direction > 0 ? from < to: from > to) from += direction * τ; 638 | } else { 639 | from = radius + direction * τ; 640 | to = radius - .5 * step; 641 | } 642 | for (var point, t = from; direction > 0 ? t > to : t < to; t -= step) { 643 | listener.point((point = d3f_geo_spherical([ 644 | cr, 645 | -sr * Math.cos(t), 646 | -sr * Math.sin(t) 647 | ]))[0], point[1]); 648 | } 649 | }; 650 | } 651 | 652 | // Signed angle of a cartesian point relative to [cr, 0, 0]. 653 | function d3f_geo_circleAngle(cr, point) { 654 | var a = d3f_geo_cartesian(point); 655 | a[0] -= cr; 656 | d3f_geo_cartesianNormalize(a); 657 | var angle = d3f_acos(-a[1]); 658 | return ((-a[2] < 0 ? -angle : angle) + 2 * Math.PI - ε) % (2 * Math.PI); 659 | } 660 | // Adds floating point numbers with twice the normal precision. 661 | // Reference: J. R. Shewchuk, Adaptive Precision Floating-Point Arithmetic and 662 | // Fast Robust Geometric Predicates, Discrete & Computational Geometry 18(3) 663 | // 305–363 (1997). 664 | // Code adapted from GeographicLib by Charles F. F. Karney, 665 | // http://geographiclib.sourceforge.net/ 666 | // See lib/geographiclib/LICENSE for details. 667 | 668 | function d3f_adder() {} 669 | 670 | d3f_adder.prototype = { 671 | s: 0, // rounded value 672 | t: 0, // exact error 673 | add: function(y) { 674 | d3f_adderSum(y, this.t, d3f_adderTemp); 675 | d3f_adderSum(d3f_adderTemp.s, this.s, this); 676 | if (this.s) this.t += d3f_adderTemp.t; 677 | else this.s = d3f_adderTemp.t; 678 | }, 679 | reset: function() { 680 | this.s = this.t = 0; 681 | }, 682 | valueOf: function() { 683 | return this.s; 684 | } 685 | }; 686 | 687 | var d3f_adderTemp = new d3f_adder; 688 | 689 | function d3f_adderSum(a, b, o) { 690 | var x = o.s = a + b, // a + b 691 | bv = x - a, av = x - bv; // b_virtual & a_virtual 692 | o.t = (a - av) + (b - bv); // a_roundoff + b_roundoff 693 | } 694 | 695 | d3f.geo.stream = function(object, listener) { 696 | if (object && d3f_geo_streamObjectType.hasOwnProperty(object.type)) { 697 | d3f_geo_streamObjectType[object.type](object, listener); 698 | } else { 699 | d3f_geo_streamGeometry(object, listener); 700 | } 701 | }; 702 | 703 | function d3f_geo_streamGeometry(geometry, listener) { 704 | if (geometry && d3f_geo_streamGeometryType.hasOwnProperty(geometry.type)) { 705 | d3f_geo_streamGeometryType[geometry.type](geometry, listener); 706 | } 707 | } 708 | 709 | var d3f_geo_streamObjectType = { 710 | Feature: function(feature, listener) { 711 | d3f_geo_streamGeometry(feature.geometry, listener); 712 | }, 713 | FeatureCollection: function(object, listener) { 714 | var features = object.features, i = -1, n = features.length; 715 | while (++i < n) d3f_geo_streamGeometry(features[i].geometry, listener); 716 | } 717 | }; 718 | 719 | var d3f_geo_streamGeometryType = { 720 | Sphere: function(object, listener) { 721 | listener.sphere(); 722 | }, 723 | Point: function(object, listener) { 724 | object = object.coordinates; 725 | listener.point(object[0], object[1], object[2]); 726 | }, 727 | MultiPoint: function(object, listener) { 728 | var coordinates = object.coordinates, i = -1, n = coordinates.length; 729 | while (++i < n) object = coordinates[i], listener.point(object[0], object[1], object[2]); 730 | }, 731 | LineString: function(object, listener) { 732 | d3f_geo_streamLine(object.coordinates, listener, 0); 733 | }, 734 | MultiLineString: function(object, listener) { 735 | var coordinates = object.coordinates, i = -1, n = coordinates.length; 736 | while (++i < n) d3f_geo_streamLine(coordinates[i], listener, 0); 737 | }, 738 | Polygon: function(object, listener) { 739 | d3f_geo_streamPolygon(object.coordinates, listener); 740 | }, 741 | MultiPolygon: function(object, listener) { 742 | var coordinates = object.coordinates, i = -1, n = coordinates.length; 743 | while (++i < n) d3f_geo_streamPolygon(coordinates[i], listener); 744 | }, 745 | GeometryCollection: function(object, listener) { 746 | var geometries = object.geometries, i = -1, n = geometries.length; 747 | while (++i < n) d3f_geo_streamGeometry(geometries[i], listener); 748 | } 749 | }; 750 | 751 | function d3f_geo_streamLine(coordinates, listener, closed) { 752 | var i = -1, n = coordinates.length - closed, coordinate; 753 | listener.lineStart(); 754 | while (++i < n) coordinate = coordinates[i], listener.point(coordinate[0], coordinate[1], coordinate[2]); 755 | listener.lineEnd(); 756 | } 757 | 758 | function d3f_geo_streamPolygon(coordinates, listener) { 759 | var i = -1, n = coordinates.length; 760 | listener.polygonStart(); 761 | while (++i < n) d3f_geo_streamLine(coordinates[i], listener, 1); 762 | listener.polygonEnd(); 763 | } 764 | 765 | d3f.geo.area = function(object) { 766 | d3f_geo_areaSum = 0; 767 | d3f.geo.stream(object, d3f_geo_area); 768 | return d3f_geo_areaSum; 769 | }; 770 | 771 | var d3f_geo_areaSum, 772 | d3f_geo_areaRingSum = new d3f_adder; 773 | 774 | var d3f_geo_area = { 775 | sphere: function() { d3f_geo_areaSum += 4 * π; }, 776 | point: d3f_noop, 777 | lineStart: d3f_noop, 778 | lineEnd: d3f_noop, 779 | 780 | // Only count area for polygon rings. 781 | polygonStart: function() { 782 | d3f_geo_areaRingSum.reset(); 783 | d3f_geo_area.lineStart = d3f_geo_areaRingStart; 784 | }, 785 | polygonEnd: function() { 786 | var area = 2 * d3f_geo_areaRingSum; 787 | d3f_geo_areaSum += area < 0 ? 4 * π + area : area; 788 | d3f_geo_area.lineStart = d3f_geo_area.lineEnd = d3f_geo_area.point = d3f_noop; 789 | } 790 | }; 791 | 792 | function d3f_geo_areaRingStart() { 793 | var λ00, φ00, λ0, cosφ0, sinφ0; // start point and previous point 794 | 795 | // For the first point, … 796 | d3f_geo_area.point = function(λ, φ) { 797 | d3f_geo_area.point = nextPoint; 798 | λ0 = (λ00 = λ) * d3f_radians, cosφ0 = Math.cos(φ = (φ00 = φ) * d3f_radians / 2 + π / 4), sinφ0 = Math.sin(φ); 799 | }; 800 | 801 | // For subsequent points, … 802 | function nextPoint(λ, φ) { 803 | λ *= d3f_radians; 804 | φ = φ * d3f_radians / 2 + π / 4; // half the angular distance from south pole 805 | 806 | // Spherical excess E for a spherical triangle with vertices: south pole, 807 | // previous point, current point. Uses a formula derived from Cagnoli’s 808 | // theorem. See Todhunter, Spherical Trig. (1871), Sec. 103, Eq. (2). 809 | var dλ = λ - λ0, 810 | sdλ = dλ >= 0 ? 1 : -1, 811 | adλ = sdλ * dλ, 812 | cosφ = Math.cos(φ), 813 | sinφ = Math.sin(φ), 814 | k = sinφ0 * sinφ, 815 | u = cosφ0 * cosφ + k * Math.cos(adλ), 816 | v = k * sdλ * Math.sin(adλ); 817 | d3f_geo_areaRingSum.add(Math.atan2(v, u)); 818 | 819 | // Advance the previous points. 820 | λ0 = λ, cosφ0 = cosφ, sinφ0 = sinφ; 821 | } 822 | 823 | // For the last point, return to the start. 824 | d3f_geo_area.lineEnd = function() { 825 | nextPoint(λ00, φ00); 826 | }; 827 | } 828 | 829 | function d3f_geo_pointInPolygon(point, polygon) { 830 | var meridian = point[0], 831 | parallel = point[1], 832 | meridianNormal = [Math.sin(meridian), -Math.cos(meridian), 0], 833 | polarAngle = 0, 834 | winding = 0; 835 | d3f_geo_areaRingSum.reset(); 836 | 837 | for (var i = 0, n = polygon.length; i < n; ++i) { 838 | var ring = polygon[i], 839 | m = ring.length; 840 | if (!m) continue; 841 | var point0 = ring[0], 842 | λ0 = point0[0], 843 | φ0 = point0[1] / 2 + π / 4, 844 | sinφ0 = Math.sin(φ0), 845 | cosφ0 = Math.cos(φ0), 846 | j = 1; 847 | 848 | while (true) { 849 | if (j === m) j = 0; 850 | point = ring[j]; 851 | var λ = point[0], 852 | φ = point[1] / 2 + π / 4, 853 | sinφ = Math.sin(φ), 854 | cosφ = Math.cos(φ), 855 | dλ = λ - λ0, 856 | sdλ = dλ >= 0 ? 1 : -1, 857 | adλ = sdλ * dλ, 858 | antimeridian = adλ > π, 859 | k = sinφ0 * sinφ; 860 | d3f_geo_areaRingSum.add(Math.atan2(k * sdλ * Math.sin(adλ), cosφ0 * cosφ + k * Math.cos(adλ))); 861 | 862 | polarAngle += antimeridian ? dλ + sdλ * τ : dλ; 863 | 864 | // Are the longitudes either side of the point's meridian, and are the 865 | // latitudes smaller than the parallel? 866 | if (antimeridian ^ λ0 >= meridian ^ λ >= meridian) { 867 | var arc = d3f_geo_cartesianCross(d3f_geo_cartesian(point0), d3f_geo_cartesian(point)); 868 | d3f_geo_cartesianNormalize(arc); 869 | var intersection = d3f_geo_cartesianCross(meridianNormal, arc); 870 | d3f_geo_cartesianNormalize(intersection); 871 | var φarc = (antimeridian ^ dλ >= 0 ? -1 : 1) * d3f_asin(intersection[2]); 872 | if (parallel > φarc || parallel === φarc && (arc[0] || arc[1])) { 873 | winding += antimeridian ^ dλ >= 0 ? 1 : -1; 874 | } 875 | } 876 | if (!j++) break; 877 | λ0 = λ, sinφ0 = sinφ, cosφ0 = cosφ, point0 = point; 878 | } 879 | } 880 | 881 | // First, determine whether the South pole is inside or outside: 882 | // 883 | // It is inside if: 884 | // * the polygon winds around it in a clockwise direction. 885 | // * the polygon does not (cumulatively) wind around it, but has a negative 886 | // (counter-clockwise) area. 887 | // 888 | // Second, count the (signed) number of times a segment crosses a meridian 889 | // from the point to the South pole. If it is zero, then the point is the 890 | // same side as the South pole. 891 | 892 | return (polarAngle < -ε || polarAngle < ε && d3f_geo_areaRingSum < 0) ^ (winding & 1); 893 | } 894 | 895 | // Clip features against a small circle centered at [0°, 0°]. 896 | function d3f_geo_clipCircle(radius) { 897 | var cr = Math.cos(radius), 898 | smallRadius = cr > 0, 899 | notHemisphere = abs(cr) > ε, // TODO optimise for this common case 900 | interpolate = d3f_geo_circleInterpolate(radius, 6 * d3f_radians); 901 | 902 | return d3f_geo_clip(visible, clipLine, interpolate, smallRadius ? [0, -radius] : [-π, radius - π]); 903 | 904 | function visible(λ, φ) { 905 | return Math.cos(λ) * Math.cos(φ) > cr; 906 | } 907 | 908 | // Takes a line and cuts into visible segments. Return values used for 909 | // polygon clipping: 910 | // 0: there were intersections or the line was empty. 911 | // 1: no intersections. 912 | // 2: there were intersections, and the first and last segments should be 913 | // rejoined. 914 | function clipLine(listener) { 915 | var point0, // previous point 916 | c0, // code for previous point 917 | v0, // visibility of previous point 918 | v00, // visibility of first point 919 | clean; // no intersections 920 | return { 921 | lineStart: function() { 922 | v00 = v0 = false; 923 | clean = 1; 924 | }, 925 | point: function(λ, φ) { 926 | var point1 = [λ, φ], 927 | point2, 928 | v = visible(λ, φ), 929 | c = smallRadius 930 | ? v ? 0 : code(λ, φ) 931 | : v ? code(λ + (λ < 0 ? π : -π), φ) : 0; 932 | if (!point0 && (v00 = v0 = v)) listener.lineStart(); 933 | // Handle degeneracies. 934 | // TODO ignore if not clipping polygons. 935 | if (v !== v0) { 936 | point2 = intersect(point0, point1); 937 | if (d3f_geo_sphericalEqual(point0, point2) || d3f_geo_sphericalEqual(point1, point2)) { 938 | point1[0] += ε; 939 | point1[1] += ε; 940 | v = visible(point1[0], point1[1]); 941 | } 942 | } 943 | if (v !== v0) { 944 | clean = 0; 945 | if (v) { 946 | // outside going in 947 | listener.lineStart(); 948 | point2 = intersect(point1, point0); 949 | listener.point(point2[0], point2[1]); 950 | } else { 951 | // inside going out 952 | point2 = intersect(point0, point1); 953 | listener.point(point2[0], point2[1]); 954 | listener.lineEnd(); 955 | } 956 | point0 = point2; 957 | } else if (notHemisphere && point0 && smallRadius ^ v) { 958 | var t; 959 | // If the codes for two points are different, or are both zero, 960 | // and there this segment intersects with the small circle. 961 | if (!(c & c0) && (t = intersect(point1, point0, true))) { 962 | clean = 0; 963 | if (smallRadius) { 964 | listener.lineStart(); 965 | listener.point(t[0][0], t[0][1]); 966 | listener.point(t[1][0], t[1][1]); 967 | listener.lineEnd(); 968 | } else { 969 | listener.point(t[1][0], t[1][1]); 970 | listener.lineEnd(); 971 | listener.lineStart(); 972 | listener.point(t[0][0], t[0][1]); 973 | } 974 | } 975 | } 976 | if (v && (!point0 || !d3f_geo_sphericalEqual(point0, point1))) { 977 | listener.point(point1[0], point1[1]); 978 | } 979 | point0 = point1, v0 = v, c0 = c; 980 | }, 981 | lineEnd: function() { 982 | if (v0) listener.lineEnd(); 983 | point0 = null; 984 | }, 985 | // Rejoin first and last segments if there were intersections and the first 986 | // and last points were visible. 987 | clean: function() { return clean | ((v00 && v0) << 1); } 988 | }; 989 | } 990 | 991 | // Intersects the great circle between a and b with the clip circle. 992 | function intersect(a, b, two) { 993 | var pa = d3f_geo_cartesian(a), 994 | pb = d3f_geo_cartesian(b); 995 | 996 | // We have two planes, n1.p = d1 and n2.p = d2. 997 | // Find intersection line p(t) = c1 n1 + c2 n2 + t (n1 ⨯ n2). 998 | var n1 = [1, 0, 0], // normal 999 | n2 = d3f_geo_cartesianCross(pa, pb), 1000 | n2n2 = d3f_geo_cartesianDot(n2, n2), 1001 | n1n2 = n2[0], // d3f_geo_cartesianDot(n1, n2), 1002 | determinant = n2n2 - n1n2 * n1n2; 1003 | 1004 | // Two polar points. 1005 | if (!determinant) return !two && a; 1006 | 1007 | var c1 = cr * n2n2 / determinant, 1008 | c2 = -cr * n1n2 / determinant, 1009 | n1xn2 = d3f_geo_cartesianCross(n1, n2), 1010 | A = d3f_geo_cartesianScale(n1, c1), 1011 | B = d3f_geo_cartesianScale(n2, c2); 1012 | d3f_geo_cartesianAdd(A, B); 1013 | 1014 | // Solve |p(t)|^2 = 1. 1015 | var u = n1xn2, 1016 | w = d3f_geo_cartesianDot(A, u), 1017 | uu = d3f_geo_cartesianDot(u, u), 1018 | t2 = w * w - uu * (d3f_geo_cartesianDot(A, A) - 1); 1019 | 1020 | if (t2 < 0) return; 1021 | 1022 | var t = Math.sqrt(t2), 1023 | q = d3f_geo_cartesianScale(u, (-w - t) / uu); 1024 | d3f_geo_cartesianAdd(q, A); 1025 | q = d3f_geo_spherical(q); 1026 | if (!two) return q; 1027 | 1028 | // Two intersection points. 1029 | var λ0 = a[0], 1030 | λ1 = b[0], 1031 | φ0 = a[1], 1032 | φ1 = b[1], 1033 | z; 1034 | if (λ1 < λ0) z = λ0, λ0 = λ1, λ1 = z; 1035 | var δλ = λ1 - λ0, 1036 | polar = abs(δλ - π) < ε, 1037 | meridian = polar || δλ < ε; 1038 | 1039 | if (!polar && φ1 < φ0) z = φ0, φ0 = φ1, φ1 = z; 1040 | 1041 | // Check that the first point is between a and b. 1042 | if (meridian 1043 | ? polar 1044 | ? φ0 + φ1 > 0 ^ q[1] < (abs(q[0] - λ0) < ε ? φ0 : φ1) 1045 | : φ0 <= q[1] && q[1] <= φ1 1046 | : δλ > π ^ (λ0 <= q[0] && q[0] <= λ1)) { 1047 | var q1 = d3f_geo_cartesianScale(u, (-w + t) / uu); 1048 | d3f_geo_cartesianAdd(q1, A); 1049 | return [q, d3f_geo_spherical(q1)]; 1050 | } 1051 | } 1052 | 1053 | // Generates a 4-bit vector representing the location of a point relative to 1054 | // the small circle's bounding box. 1055 | function code(λ, φ) { 1056 | var r = smallRadius ? radius : π - radius, 1057 | code = 0; 1058 | if (λ < -r) code |= 1; // left 1059 | else if (λ > r) code |= 2; // right 1060 | if (φ < -r) code |= 4; // below 1061 | else if (φ > r) code |= 8; // above 1062 | return code; 1063 | } 1064 | } 1065 | 1066 | // Liang–Barsky line clipping. 1067 | function d3f_geom_clipLine(x0, y0, x1, y1) { 1068 | return function(line) { 1069 | var a = line.a, 1070 | b = line.b, 1071 | ax = a.x, 1072 | ay = a.y, 1073 | bx = b.x, 1074 | by = b.y, 1075 | t0 = 0, 1076 | t1 = 1, 1077 | dx = bx - ax, 1078 | dy = by - ay, 1079 | r; 1080 | 1081 | r = x0 - ax; 1082 | if (!dx && r > 0) return; 1083 | r /= dx; 1084 | if (dx < 0) { 1085 | if (r < t0) return; 1086 | if (r < t1) t1 = r; 1087 | } else if (dx > 0) { 1088 | if (r > t1) return; 1089 | if (r > t0) t0 = r; 1090 | } 1091 | 1092 | r = x1 - ax; 1093 | if (!dx && r < 0) return; 1094 | r /= dx; 1095 | if (dx < 0) { 1096 | if (r > t1) return; 1097 | if (r > t0) t0 = r; 1098 | } else if (dx > 0) { 1099 | if (r < t0) return; 1100 | if (r < t1) t1 = r; 1101 | } 1102 | 1103 | r = y0 - ay; 1104 | if (!dy && r > 0) return; 1105 | r /= dy; 1106 | if (dy < 0) { 1107 | if (r < t0) return; 1108 | if (r < t1) t1 = r; 1109 | } else if (dy > 0) { 1110 | if (r > t1) return; 1111 | if (r > t0) t0 = r; 1112 | } 1113 | 1114 | r = y1 - ay; 1115 | if (!dy && r < 0) return; 1116 | r /= dy; 1117 | if (dy < 0) { 1118 | if (r > t1) return; 1119 | if (r > t0) t0 = r; 1120 | } else if (dy > 0) { 1121 | if (r < t0) return; 1122 | if (r < t1) t1 = r; 1123 | } 1124 | 1125 | if (t0 > 0) line.a = {x: ax + t0 * dx, y: ay + t0 * dy}; 1126 | if (t1 < 1) line.b = {x: ax + t1 * dx, y: ay + t1 * dy}; 1127 | return line; 1128 | }; 1129 | } 1130 | 1131 | var d3f_geo_clipExtentMAX = 1e9; 1132 | 1133 | d3f.geo.clipExtent = function() { 1134 | var x0, y0, x1, y1, 1135 | stream, 1136 | clip, 1137 | clipExtent = { 1138 | stream: function(output) { 1139 | if (stream) stream.valid = false; 1140 | stream = clip(output); 1141 | stream.valid = true; // allow caching by d3f.geo.path 1142 | return stream; 1143 | }, 1144 | extent: function(_) { 1145 | if (!arguments.length) return [[x0, y0], [x1, y1]]; 1146 | clip = d3f_geo_clipExtent(x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1]); 1147 | if (stream) stream.valid = false, stream = null; 1148 | return clipExtent; 1149 | } 1150 | }; 1151 | return clipExtent.extent([[0, 0], [960, 500]]); 1152 | }; 1153 | 1154 | function d3f_geo_clipExtent(x0, y0, x1, y1) { 1155 | return function(listener) { 1156 | var listener_ = listener, 1157 | bufferListener = d3f_geo_clipBufferListener(), 1158 | clipLine = d3f_geom_clipLine(x0, y0, x1, y1), 1159 | segments, 1160 | polygon, 1161 | ring; 1162 | 1163 | var clip = { 1164 | point: point, 1165 | lineStart: lineStart, 1166 | lineEnd: lineEnd, 1167 | polygonStart: function() { 1168 | listener = bufferListener; 1169 | segments = []; 1170 | polygon = []; 1171 | clean = true; 1172 | }, 1173 | polygonEnd: function() { 1174 | listener = listener_; 1175 | segments = d3f.merge(segments); 1176 | var clipStartInside = insidePolygon([x0, y1]), 1177 | inside = clean && clipStartInside, 1178 | visible = segments.length; 1179 | if (inside || visible) { 1180 | listener.polygonStart(); 1181 | if (inside) { 1182 | listener.lineStart(); 1183 | interpolate(null, null, 1, listener); 1184 | listener.lineEnd(); 1185 | } 1186 | if (visible) { 1187 | d3f_geo_clipPolygon(segments, compare, clipStartInside, interpolate, listener); 1188 | } 1189 | listener.polygonEnd(); 1190 | } 1191 | segments = polygon = ring = null; 1192 | } 1193 | }; 1194 | 1195 | function insidePolygon(p) { 1196 | var wn = 0, // the winding number counter 1197 | n = polygon.length, 1198 | y = p[1]; 1199 | 1200 | for (var i = 0; i < n; ++i) { 1201 | for (var j = 1, v = polygon[i], m = v.length, a = v[0], b; j < m; ++j) { 1202 | b = v[j]; 1203 | if (a[1] <= y) { 1204 | if (b[1] > y && d3f_cross2d(a, b, p) > 0) ++wn; 1205 | } else { 1206 | if (b[1] <= y && d3f_cross2d(a, b, p) < 0) --wn; 1207 | } 1208 | a = b; 1209 | } 1210 | } 1211 | return wn !== 0; 1212 | } 1213 | 1214 | function interpolate(from, to, direction, listener) { 1215 | var a = 0, a1 = 0; 1216 | if (from == null || 1217 | (a = corner(from, direction)) !== (a1 = corner(to, direction)) || 1218 | comparePoints(from, to) < 0 ^ direction > 0) { 1219 | do { 1220 | listener.point(a === 0 || a === 3 ? x0 : x1, a > 1 ? y1 : y0); 1221 | } while ((a = (a + direction + 4) % 4) !== a1); 1222 | } else { 1223 | listener.point(to[0], to[1]); 1224 | } 1225 | } 1226 | 1227 | function pointVisible(x, y) { 1228 | return x0 <= x && x <= x1 && y0 <= y && y <= y1; 1229 | } 1230 | 1231 | function point(x, y) { 1232 | if (pointVisible(x, y)) listener.point(x, y); 1233 | } 1234 | 1235 | var x__, y__, v__, // first point 1236 | x_, y_, v_, // previous point 1237 | first, 1238 | clean; 1239 | 1240 | function lineStart() { 1241 | clip.point = linePoint; 1242 | if (polygon) polygon.push(ring = []); 1243 | first = true; 1244 | v_ = false; 1245 | x_ = y_ = NaN; 1246 | } 1247 | 1248 | function lineEnd() { 1249 | // TODO rather than special-case polygons, simply handle them separately. 1250 | // Ideally, coincident intersection points should be jittered to avoid 1251 | // clipping issues. 1252 | if (segments) { 1253 | linePoint(x__, y__); 1254 | if (v__ && v_) bufferListener.rejoin(); 1255 | segments.push(bufferListener.buffer()); 1256 | } 1257 | clip.point = point; 1258 | if (v_) listener.lineEnd(); 1259 | } 1260 | 1261 | function linePoint(x, y) { 1262 | x = Math.max(-d3f_geo_clipExtentMAX, Math.min(d3f_geo_clipExtentMAX, x)); 1263 | y = Math.max(-d3f_geo_clipExtentMAX, Math.min(d3f_geo_clipExtentMAX, y)); 1264 | var v = pointVisible(x, y); 1265 | if (polygon) ring.push([x, y]); 1266 | if (first) { 1267 | x__ = x, y__ = y, v__ = v; 1268 | first = false; 1269 | if (v) { 1270 | listener.lineStart(); 1271 | listener.point(x, y); 1272 | } 1273 | } else { 1274 | if (v && v_) listener.point(x, y); 1275 | else { 1276 | var l = {a: {x: x_, y: y_}, b: {x: x, y: y}}; 1277 | if (clipLine(l)) { 1278 | if (!v_) { 1279 | listener.lineStart(); 1280 | listener.point(l.a.x, l.a.y); 1281 | } 1282 | listener.point(l.b.x, l.b.y); 1283 | if (!v) listener.lineEnd(); 1284 | clean = false; 1285 | } else if (v) { 1286 | listener.lineStart(); 1287 | listener.point(x, y); 1288 | clean = false; 1289 | } 1290 | } 1291 | } 1292 | x_ = x, y_ = y, v_ = v; 1293 | } 1294 | 1295 | return clip; 1296 | }; 1297 | 1298 | function corner(p, direction) { 1299 | return abs(p[0] - x0) < ε ? direction > 0 ? 0 : 3 1300 | : abs(p[0] - x1) < ε ? direction > 0 ? 2 : 1 1301 | : abs(p[1] - y0) < ε ? direction > 0 ? 1 : 0 1302 | : direction > 0 ? 3 : 2; // abs(p[1] - y1) < ε 1303 | } 1304 | 1305 | function compare(a, b) { 1306 | return comparePoints(a.x, b.x); 1307 | } 1308 | 1309 | function comparePoints(a, b) { 1310 | var ca = corner(a, 1), 1311 | cb = corner(b, 1); 1312 | return ca !== cb ? ca - cb 1313 | : ca === 0 ? b[1] - a[1] 1314 | : ca === 1 ? a[0] - b[0] 1315 | : ca === 2 ? a[1] - b[1] 1316 | : b[0] - a[0]; 1317 | } 1318 | } 1319 | 1320 | function d3f_geo_resample(project) { 1321 | var δ2 = .5, // precision, px² 1322 | cosMinDistance = Math.cos(30 * d3f_radians), // cos(minimum angular distance) 1323 | maxDepth = 16; 1324 | 1325 | function resample(stream) { 1326 | return (maxDepth ? resampleRecursive : resampleNone)(stream); 1327 | } 1328 | 1329 | function resampleNone(stream) { 1330 | return d3f_geo_transformPoint(stream, function(x, y) { 1331 | x = project(x, y); 1332 | stream.point(x[0], x[1]); 1333 | }); 1334 | } 1335 | 1336 | function resampleRecursive(stream) { 1337 | var λ00, φ00, x00, y00, a00, b00, c00, // first point 1338 | λ0, x0, y0, a0, b0, c0; // previous point 1339 | 1340 | var resample = { 1341 | point: point, 1342 | lineStart: lineStart, 1343 | lineEnd: lineEnd, 1344 | polygonStart: function() { stream.polygonStart(); resample.lineStart = ringStart; }, 1345 | polygonEnd: function() { stream.polygonEnd(); resample.lineStart = lineStart; } 1346 | }; 1347 | 1348 | function point(x, y) { 1349 | x = project(x, y); 1350 | stream.point(x[0], x[1]); 1351 | } 1352 | 1353 | function lineStart() { 1354 | x0 = NaN; 1355 | resample.point = linePoint; 1356 | stream.lineStart(); 1357 | } 1358 | 1359 | function linePoint(λ, φ) { 1360 | var c = d3f_geo_cartesian([λ, φ]), p = project(λ, φ); 1361 | resampleLineTo(x0, y0, λ0, a0, b0, c0, x0 = p[0], y0 = p[1], λ0 = λ, a0 = c[0], b0 = c[1], c0 = c[2], maxDepth, stream); 1362 | stream.point(x0, y0); 1363 | } 1364 | 1365 | function lineEnd() { 1366 | resample.point = point; 1367 | stream.lineEnd(); 1368 | } 1369 | 1370 | function ringStart() { 1371 | lineStart(); 1372 | resample.point = ringPoint; 1373 | resample.lineEnd = ringEnd; 1374 | } 1375 | 1376 | function ringPoint(λ, φ) { 1377 | linePoint(λ00 = λ, φ00 = φ), x00 = x0, y00 = y0, a00 = a0, b00 = b0, c00 = c0; 1378 | resample.point = linePoint; 1379 | } 1380 | 1381 | function ringEnd() { 1382 | resampleLineTo(x0, y0, λ0, a0, b0, c0, x00, y00, λ00, a00, b00, c00, maxDepth, stream); 1383 | resample.lineEnd = lineEnd; 1384 | lineEnd(); 1385 | } 1386 | 1387 | return resample; 1388 | } 1389 | 1390 | function resampleLineTo(x0, y0, λ0, a0, b0, c0, x1, y1, λ1, a1, b1, c1, depth, stream) { 1391 | var dx = x1 - x0, 1392 | dy = y1 - y0, 1393 | d2 = dx * dx + dy * dy; 1394 | if (d2 > 4 * δ2 && depth--) { 1395 | var a = a0 + a1, 1396 | b = b0 + b1, 1397 | c = c0 + c1, 1398 | m = Math.sqrt(a * a + b * b + c * c), 1399 | φ2 = Math.asin(c /= m), 1400 | λ2 = abs(abs(c) - 1) < ε || abs(λ0 - λ1) < ε ? (λ0 + λ1) / 2 : Math.atan2(b, a), 1401 | p = project(λ2, φ2), 1402 | x2 = p[0], 1403 | y2 = p[1], 1404 | dx2 = x2 - x0, 1405 | dy2 = y2 - y0, 1406 | dz = dy * dx2 - dx * dy2; 1407 | if (dz * dz / d2 > δ2 // perpendicular projected distance 1408 | || abs((dx * dx2 + dy * dy2) / d2 - .5) > .3 // midpoint close to an end 1409 | || a0 * a1 + b0 * b1 + c0 * c1 < cosMinDistance) { // angular distance 1410 | resampleLineTo(x0, y0, λ0, a0, b0, c0, x2, y2, λ2, a /= m, b /= m, c, depth, stream); 1411 | stream.point(x2, y2); 1412 | resampleLineTo(x2, y2, λ2, a, b, c, x1, y1, λ1, a1, b1, c1, depth, stream); 1413 | } 1414 | } 1415 | } 1416 | 1417 | resample.precision = function(_) { 1418 | if (!arguments.length) return Math.sqrt(δ2); 1419 | maxDepth = (δ2 = _ * _) > 0 && 16; 1420 | return resample; 1421 | }; 1422 | 1423 | return resample; 1424 | } 1425 | var d3f_arraySlice = [].slice, 1426 | d3f_array = function(list) { return d3f_arraySlice.call(list); }; // conversion for NodeLists 1427 | 1428 | d3f.geo.transform = function(methods) { 1429 | return { 1430 | stream: function(stream) { 1431 | var transform = new d3f_geo_transform(stream); 1432 | for (var k in methods) transform[k] = methods[k]; 1433 | return transform; 1434 | } 1435 | }; 1436 | }; 1437 | 1438 | function d3f_geo_transform(stream) { 1439 | this.stream = stream; 1440 | } 1441 | 1442 | d3f_geo_transform.prototype = { 1443 | point: function(x, y) { this.stream.point(x, y); }, 1444 | sphere: function() { this.stream.sphere(); }, 1445 | lineStart: function() { this.stream.lineStart(); }, 1446 | lineEnd: function() { this.stream.lineEnd(); }, 1447 | polygonStart: function() { this.stream.polygonStart(); }, 1448 | polygonEnd: function() { this.stream.polygonEnd(); } 1449 | }; 1450 | 1451 | function d3f_geo_transformPoint(stream, point) { 1452 | return { 1453 | point: point, 1454 | sphere: function() { stream.sphere(); }, 1455 | lineStart: function() { stream.lineStart(); }, 1456 | lineEnd: function() { stream.lineEnd(); }, 1457 | polygonStart: function() { stream.polygonStart(); }, 1458 | polygonEnd: function() { stream.polygonEnd(); }, 1459 | }; 1460 | } 1461 | 1462 | d3f.geo.projection = d3f_geo_projection; 1463 | d3f.geo.projectionMutator = d3f_geo_projectionMutator; 1464 | 1465 | function d3f_geo_projection(project) { 1466 | return d3f_geo_projectionMutator(function() { return project; })(); 1467 | } 1468 | 1469 | function d3f_geo_projectionMutator(projectAt) { 1470 | var project, 1471 | rotate, 1472 | projectRotate, 1473 | projectResample = d3f_geo_resample(function(x, y) { x = project(x, y); return [x[0] * k + δx, δy - x[1] * k]; }), 1474 | k = 150, // scale 1475 | x = 480, y = 250, // translate 1476 | λ = 0, φ = 0, // center 1477 | δλ = 0, δφ = 0, δγ = 0, // rotate 1478 | δx, δy, // center 1479 | preclip = d3f_geo_clipAntimeridian, 1480 | postclip = d3f_identity, 1481 | clipAngle = null, 1482 | clipExtent = null, 1483 | stream; 1484 | 1485 | function projection(point) { 1486 | point = projectRotate(point[0] * d3f_radians, point[1] * d3f_radians); 1487 | return [point[0] * k + δx, δy - point[1] * k]; 1488 | } 1489 | 1490 | function invert(point) { 1491 | point = projectRotate.invert((point[0] - δx) / k, (δy - point[1]) / k); 1492 | return point && [point[0] * d3f_degrees, point[1] * d3f_degrees]; 1493 | } 1494 | 1495 | projection.stream = function(output) { 1496 | if (stream) stream.valid = false; 1497 | stream = d3f_geo_projectionRadians(preclip(rotate, projectResample(postclip(output)))); 1498 | stream.valid = true; // allow caching by d3f.geo.path 1499 | return stream; 1500 | }; 1501 | 1502 | projection.clipAngle = function(_) { 1503 | if (!arguments.length) return clipAngle; 1504 | preclip = _ == null ? (clipAngle = _, d3f_geo_clipAntimeridian) : d3f_geo_clipCircle((clipAngle = +_) * d3f_radians); 1505 | return invalidate(); 1506 | }; 1507 | 1508 | projection.clipExtent = function(_) { 1509 | if (!arguments.length) return clipExtent; 1510 | clipExtent = _; 1511 | postclip = _ ? d3f_geo_clipExtent(_[0][0], _[0][1], _[1][0], _[1][1]) : d3f_identity; 1512 | return invalidate(); 1513 | }; 1514 | 1515 | projection.scale = function(_) { 1516 | if (!arguments.length) return k; 1517 | k = +_; 1518 | return reset(); 1519 | }; 1520 | 1521 | projection.translate = function(_) { 1522 | if (!arguments.length) return [x, y]; 1523 | x = +_[0]; 1524 | y = +_[1]; 1525 | return reset(); 1526 | }; 1527 | 1528 | projection.center = function(_) { 1529 | if (!arguments.length) return [λ * d3f_degrees, φ * d3f_degrees]; 1530 | λ = _[0] % 360 * d3f_radians; 1531 | φ = _[1] % 360 * d3f_radians; 1532 | return reset(); 1533 | }; 1534 | 1535 | projection.rotate = function(_) { 1536 | if (!arguments.length) return [δλ * d3f_degrees, δφ * d3f_degrees, δγ * d3f_degrees]; 1537 | δλ = _[0] % 360 * d3f_radians; 1538 | δφ = _[1] % 360 * d3f_radians; 1539 | δγ = _.length > 2 ? _[2] % 360 * d3f_radians : 0; 1540 | return reset(); 1541 | }; 1542 | 1543 | d3f.rebind(projection, projectResample, "precision"); 1544 | 1545 | function reset() { 1546 | projectRotate = d3f_geo_compose(rotate = d3f_geo_rotation(δλ, δφ, δγ), project); 1547 | var center = project(λ, φ); 1548 | δx = x - center[0] * k; 1549 | δy = y + center[1] * k; 1550 | return invalidate(); 1551 | } 1552 | 1553 | function invalidate() { 1554 | if (stream) stream.valid = false, stream = null; 1555 | return projection; 1556 | } 1557 | 1558 | return function() { 1559 | project = projectAt.apply(this, arguments); 1560 | projection.invert = project.invert && invert; 1561 | return reset(); 1562 | }; 1563 | } 1564 | 1565 | function d3f_geo_projectionRadians(stream) { 1566 | return d3f_geo_transformPoint(stream, function(x, y) { 1567 | stream.point(x * d3f_radians, y * d3f_radians); 1568 | }); 1569 | } 1570 | 1571 | function d3f_geo_conic(projectAt) { 1572 | var φ0 = 0, 1573 | φ1 = π / 3, 1574 | m = d3f_geo_projectionMutator(projectAt), 1575 | p = m(φ0, φ1); 1576 | 1577 | p.parallels = function(_) { 1578 | if (!arguments.length) return [φ0 / π * 180, φ1 / π * 180]; 1579 | return m(φ0 = _[0] * π / 180, φ1 = _[1] * π / 180); 1580 | }; 1581 | 1582 | return p; 1583 | } 1584 | 1585 | function d3f_geo_conicEqualArea(φ0, φ1) { 1586 | var sinφ0 = Math.sin(φ0), 1587 | n = (sinφ0 + Math.sin(φ1)) / 2, 1588 | C = 1 + sinφ0 * (2 * n - sinφ0), 1589 | ρ0 = Math.sqrt(C) / n; 1590 | 1591 | function forward(λ, φ) { 1592 | var ρ = Math.sqrt(C - 2 * n * Math.sin(φ)) / n; 1593 | return [ 1594 | ρ * Math.sin(λ *= n), 1595 | ρ0 - ρ * Math.cos(λ) 1596 | ]; 1597 | } 1598 | 1599 | forward.invert = function(x, y) { 1600 | var ρ0_y = ρ0 - y; 1601 | return [ 1602 | Math.atan2(x, ρ0_y) / n, 1603 | d3f_asin((C - (x * x + ρ0_y * ρ0_y) * n * n) / (2 * n)) 1604 | ]; 1605 | }; 1606 | 1607 | return forward; 1608 | } 1609 | 1610 | (d3f.geo.conicEqualArea = function() { 1611 | return d3f_geo_conic(d3f_geo_conicEqualArea); 1612 | }).raw = d3f_geo_conicEqualArea; 1613 | 1614 | // ESRI:102003 1615 | d3f.geo.albers = function() { 1616 | return d3f.geo.conicEqualArea() 1617 | .rotate([96, 0]) 1618 | .center([-.6, 38.7]) 1619 | .parallels([29.5, 45.5]) 1620 | .scale(1070); 1621 | }; 1622 | 1623 | // A composite projection for the United States, configured by default for 1624 | // 960×500. Also works quite well at 960×600 with scale 1285. The set of 1625 | // standard parallels for each region comes from USGS, which is published here: 1626 | // http://egsc.usgs.gov/isb/pubs/MapProjections/projections.html#albers 1627 | d3f.geo.albersUsa = function() { 1628 | var lower48 = d3f.geo.albers(); 1629 | 1630 | // EPSG:3338 1631 | var alaska = d3f.geo.conicEqualArea() 1632 | .rotate([154, 0]) 1633 | .center([-2, 58.5]) 1634 | .parallels([55, 65]); 1635 | 1636 | // ESRI:102007 1637 | var hawaii = d3f.geo.conicEqualArea() 1638 | .rotate([157, 0]) 1639 | .center([-3, 19.9]) 1640 | .parallels([8, 18]); 1641 | 1642 | var point, 1643 | pointStream = {point: function(x, y) { point = [x, y]; }}, 1644 | lower48Point, 1645 | alaskaPoint, 1646 | hawaiiPoint; 1647 | 1648 | function albersUsa(coordinates) { 1649 | var x = coordinates[0], y = coordinates[1]; 1650 | point = null; 1651 | (lower48Point(x, y), point) 1652 | || (alaskaPoint(x, y), point) 1653 | || hawaiiPoint(x, y); 1654 | return point; 1655 | } 1656 | 1657 | albersUsa.invert = function(coordinates) { 1658 | var k = lower48.scale(), 1659 | t = lower48.translate(), 1660 | x = (coordinates[0] - t[0]) / k, 1661 | y = (coordinates[1] - t[1]) / k; 1662 | return (y >= .120 && y < .234 && x >= -.425 && x < -.214 ? alaska 1663 | : y >= .166 && y < .234 && x >= -.214 && x < -.115 ? hawaii 1664 | : lower48).invert(coordinates); 1665 | }; 1666 | 1667 | // A naïve multi-projection stream. 1668 | // The projections must have mutually exclusive clip regions on the sphere, 1669 | // as this will avoid emitting interleaving lines and polygons. 1670 | albersUsa.stream = function(stream) { 1671 | var lower48Stream = lower48.stream(stream), 1672 | alaskaStream = alaska.stream(stream), 1673 | hawaiiStream = hawaii.stream(stream); 1674 | return { 1675 | point: function(x, y) { 1676 | lower48Stream.point(x, y); 1677 | alaskaStream.point(x, y); 1678 | hawaiiStream.point(x, y); 1679 | }, 1680 | sphere: function() { 1681 | lower48Stream.sphere(); 1682 | alaskaStream.sphere(); 1683 | hawaiiStream.sphere(); 1684 | }, 1685 | lineStart: function() { 1686 | lower48Stream.lineStart(); 1687 | alaskaStream.lineStart(); 1688 | hawaiiStream.lineStart(); 1689 | }, 1690 | lineEnd: function() { 1691 | lower48Stream.lineEnd(); 1692 | alaskaStream.lineEnd(); 1693 | hawaiiStream.lineEnd(); 1694 | }, 1695 | polygonStart: function() { 1696 | lower48Stream.polygonStart(); 1697 | alaskaStream.polygonStart(); 1698 | hawaiiStream.polygonStart(); 1699 | }, 1700 | polygonEnd: function() { 1701 | lower48Stream.polygonEnd(); 1702 | alaskaStream.polygonEnd(); 1703 | hawaiiStream.polygonEnd(); 1704 | } 1705 | }; 1706 | }; 1707 | 1708 | albersUsa.precision = function(_) { 1709 | if (!arguments.length) return lower48.precision(); 1710 | lower48.precision(_); 1711 | alaska.precision(_); 1712 | hawaii.precision(_); 1713 | return albersUsa; 1714 | }; 1715 | 1716 | albersUsa.scale = function(_) { 1717 | if (!arguments.length) return lower48.scale(); 1718 | lower48.scale(_); 1719 | alaska.scale(_ * .35); 1720 | hawaii.scale(_); 1721 | return albersUsa.translate(lower48.translate()); 1722 | }; 1723 | 1724 | albersUsa.translate = function(_) { 1725 | if (!arguments.length) return lower48.translate(); 1726 | var k = lower48.scale(), x = +_[0], y = +_[1]; 1727 | 1728 | lower48Point = lower48 1729 | .translate(_) 1730 | .clipExtent([[x - .455 * k, y - .238 * k], [x + .455 * k, y + .238 * k]]) 1731 | .stream(pointStream).point; 1732 | 1733 | alaskaPoint = alaska 1734 | .translate([x - .307 * k, y + .201 * k]) 1735 | .clipExtent([[x - .425 * k + ε, y + .120 * k + ε], [x - .214 * k - ε, y + .234 * k - ε]]) 1736 | .stream(pointStream).point; 1737 | 1738 | hawaiiPoint = hawaii 1739 | .translate([x - .205 * k, y + .212 * k]) 1740 | .clipExtent([[x - .214 * k + ε, y + .166 * k + ε], [x - .115 * k - ε, y + .234 * k - ε]]) 1741 | .stream(pointStream).point; 1742 | 1743 | return albersUsa; 1744 | }; 1745 | 1746 | return albersUsa.scale(1070); 1747 | }; 1748 | 1749 | d3f.geo.bounds = (function() { 1750 | var λ0, φ0, λ1, φ1, // bounds 1751 | λ_, // previous λ-coordinate 1752 | λ__, φ__, // first point 1753 | p0, // previous 3D point 1754 | dλSum, 1755 | ranges, 1756 | range; 1757 | 1758 | var bound = { 1759 | point: point, 1760 | lineStart: lineStart, 1761 | lineEnd: lineEnd, 1762 | 1763 | polygonStart: function() { 1764 | bound.point = ringPoint; 1765 | bound.lineStart = ringStart; 1766 | bound.lineEnd = ringEnd; 1767 | dλSum = 0; 1768 | d3f_geo_area.polygonStart(); 1769 | }, 1770 | polygonEnd: function() { 1771 | d3f_geo_area.polygonEnd(); 1772 | bound.point = point; 1773 | bound.lineStart = lineStart; 1774 | bound.lineEnd = lineEnd; 1775 | if (d3f_geo_areaRingSum < 0) λ0 = -(λ1 = 180), φ0 = -(φ1 = 90); 1776 | else if (dλSum > ε) φ1 = 90; 1777 | else if (dλSum < -ε) φ0 = -90; 1778 | range[0] = λ0, range[1] = λ1; 1779 | } 1780 | }; 1781 | 1782 | function point(λ, φ) { 1783 | ranges.push(range = [λ0 = λ, λ1 = λ]); 1784 | if (φ < φ0) φ0 = φ; 1785 | if (φ > φ1) φ1 = φ; 1786 | } 1787 | 1788 | function linePoint(λ, φ) { 1789 | var p = d3f_geo_cartesian([λ * d3f_radians, φ * d3f_radians]); 1790 | if (p0) { 1791 | var normal = d3f_geo_cartesianCross(p0, p), 1792 | equatorial = [normal[1], -normal[0], 0], 1793 | inflection = d3f_geo_cartesianCross(equatorial, normal); 1794 | d3f_geo_cartesianNormalize(inflection); 1795 | inflection = d3f_geo_spherical(inflection); 1796 | var dλ = λ - λ_, 1797 | s = dλ > 0 ? 1 : -1, 1798 | λi = inflection[0] * d3f_degrees * s, 1799 | antimeridian = abs(dλ) > 180; 1800 | if (antimeridian ^ (s * λ_ < λi && λi < s * λ)) { 1801 | var φi = inflection[1] * d3f_degrees; 1802 | if (φi > φ1) φ1 = φi; 1803 | } else if (λi = (λi + 360) % 360 - 180, antimeridian ^ (s * λ_ < λi && λi < s * λ)) { 1804 | var φi = -inflection[1] * d3f_degrees; 1805 | if (φi < φ0) φ0 = φi; 1806 | } else { 1807 | if (φ < φ0) φ0 = φ; 1808 | if (φ > φ1) φ1 = φ; 1809 | } 1810 | if (antimeridian) { 1811 | if (λ < λ_) { 1812 | if (angle(λ0, λ) > angle(λ0, λ1)) λ1 = λ; 1813 | } else { 1814 | if (angle(λ, λ1) > angle(λ0, λ1)) λ0 = λ; 1815 | } 1816 | } else { 1817 | if (λ1 >= λ0) { 1818 | if (λ < λ0) λ0 = λ; 1819 | if (λ > λ1) λ1 = λ; 1820 | } else { 1821 | if (λ > λ_) { 1822 | if (angle(λ0, λ) > angle(λ0, λ1)) λ1 = λ; 1823 | } else { 1824 | if (angle(λ, λ1) > angle(λ0, λ1)) λ0 = λ; 1825 | } 1826 | } 1827 | } 1828 | } else { 1829 | point(λ, φ); 1830 | } 1831 | p0 = p, λ_ = λ; 1832 | } 1833 | 1834 | function lineStart() { bound.point = linePoint; } 1835 | function lineEnd() { 1836 | range[0] = λ0, range[1] = λ1; 1837 | bound.point = point; 1838 | p0 = null; 1839 | } 1840 | 1841 | function ringPoint(λ, φ) { 1842 | if (p0) { 1843 | var dλ = λ - λ_; 1844 | dλSum += abs(dλ) > 180 ? dλ + (dλ > 0 ? 360 : -360) : dλ; 1845 | } else λ__ = λ, φ__ = φ; 1846 | d3f_geo_area.point(λ, φ); 1847 | linePoint(λ, φ); 1848 | } 1849 | 1850 | function ringStart() { 1851 | d3f_geo_area.lineStart(); 1852 | } 1853 | 1854 | function ringEnd() { 1855 | ringPoint(λ__, φ__); 1856 | d3f_geo_area.lineEnd(); 1857 | if (abs(dλSum) > ε) λ0 = -(λ1 = 180); 1858 | range[0] = λ0, range[1] = λ1; 1859 | p0 = null; 1860 | } 1861 | 1862 | // Finds the left-right distance between two longitudes. 1863 | // This is almost the same as (λ1 - λ0 + 360°) % 360°, except that we want 1864 | // the distance between ±180° to be 360°. 1865 | function angle(λ0, λ1) { return (λ1 -= λ0) < 0 ? λ1 + 360 : λ1; } 1866 | 1867 | function compareRanges(a, b) { return a[0] - b[0]; } 1868 | 1869 | function withinRange(x, range) { 1870 | return range[0] <= range[1] ? range[0] <= x && x <= range[1] : x < range[0] || range[1] < x; 1871 | } 1872 | 1873 | return function(feature) { 1874 | φ1 = λ1 = -(λ0 = φ0 = Infinity); 1875 | ranges = []; 1876 | 1877 | d3f.geo.stream(feature, bound); 1878 | 1879 | var n = ranges.length; 1880 | if (n) { 1881 | // First, sort ranges by their minimum longitudes. 1882 | ranges.sort(compareRanges); 1883 | 1884 | // Then, merge any ranges that overlap. 1885 | for (var i = 1, a = ranges[0], b, merged = [a]; i < n; ++i) { 1886 | b = ranges[i]; 1887 | if (withinRange(b[0], a) || withinRange(b[1], a)) { 1888 | if (angle(a[0], b[1]) > angle(a[0], a[1])) a[1] = b[1]; 1889 | if (angle(b[0], a[1]) > angle(a[0], a[1])) a[0] = b[0]; 1890 | } else { 1891 | merged.push(a = b); 1892 | } 1893 | } 1894 | 1895 | // Finally, find the largest gap between the merged ranges. 1896 | // The final bounding box will be the inverse of this gap. 1897 | var best = -Infinity, dλ; 1898 | for (var n = merged.length - 1, i = 0, a = merged[n], b; i <= n; a = b, ++i) { 1899 | b = merged[i]; 1900 | if ((dλ = angle(a[1], b[0])) > best) best = dλ, λ0 = b[0], λ1 = a[1]; 1901 | } 1902 | } 1903 | ranges = range = null; 1904 | 1905 | return λ0 === Infinity || φ0 === Infinity 1906 | ? [[NaN, NaN], [NaN, NaN]] 1907 | : [[λ0, φ0], [λ1, φ1]]; 1908 | }; 1909 | })(); 1910 | 1911 | d3f.geo.centroid = function(object) { 1912 | d3f_geo_centroidW0 = d3f_geo_centroidW1 = 1913 | d3f_geo_centroidX0 = d3f_geo_centroidY0 = d3f_geo_centroidZ0 = 1914 | d3f_geo_centroidX1 = d3f_geo_centroidY1 = d3f_geo_centroidZ1 = 1915 | d3f_geo_centroidX2 = d3f_geo_centroidY2 = d3f_geo_centroidZ2 = 0; 1916 | d3f.geo.stream(object, d3f_geo_centroid); 1917 | 1918 | var x = d3f_geo_centroidX2, 1919 | y = d3f_geo_centroidY2, 1920 | z = d3f_geo_centroidZ2, 1921 | m = x * x + y * y + z * z; 1922 | 1923 | // If the area-weighted centroid is undefined, fall back to length-weighted centroid. 1924 | if (m < ε2) { 1925 | x = d3f_geo_centroidX1, y = d3f_geo_centroidY1, z = d3f_geo_centroidZ1; 1926 | // If the feature has zero length, fall back to arithmetic mean of point vectors. 1927 | if (d3f_geo_centroidW1 < ε) x = d3f_geo_centroidX0, y = d3f_geo_centroidY0, z = d3f_geo_centroidZ0; 1928 | m = x * x + y * y + z * z; 1929 | // If the feature still has an undefined centroid, then return. 1930 | if (m < ε2) return [NaN, NaN]; 1931 | } 1932 | 1933 | return [Math.atan2(y, x) * d3f_degrees, d3f_asin(z / Math.sqrt(m)) * d3f_degrees]; 1934 | }; 1935 | 1936 | var d3f_geo_centroidW0, 1937 | d3f_geo_centroidW1, 1938 | d3f_geo_centroidX0, 1939 | d3f_geo_centroidY0, 1940 | d3f_geo_centroidZ0, 1941 | d3f_geo_centroidX1, 1942 | d3f_geo_centroidY1, 1943 | d3f_geo_centroidZ1, 1944 | d3f_geo_centroidX2, 1945 | d3f_geo_centroidY2, 1946 | d3f_geo_centroidZ2; 1947 | 1948 | var d3f_geo_centroid = { 1949 | sphere: d3f_noop, 1950 | point: d3f_geo_centroidPoint, 1951 | lineStart: d3f_geo_centroidLineStart, 1952 | lineEnd: d3f_geo_centroidLineEnd, 1953 | polygonStart: function() { 1954 | d3f_geo_centroid.lineStart = d3f_geo_centroidRingStart; 1955 | }, 1956 | polygonEnd: function() { 1957 | d3f_geo_centroid.lineStart = d3f_geo_centroidLineStart; 1958 | } 1959 | }; 1960 | 1961 | // Arithmetic mean of Cartesian vectors. 1962 | function d3f_geo_centroidPoint(λ, φ) { 1963 | λ *= d3f_radians; 1964 | var cosφ = Math.cos(φ *= d3f_radians); 1965 | d3f_geo_centroidPointXYZ(cosφ * Math.cos(λ), cosφ * Math.sin(λ), Math.sin(φ)); 1966 | } 1967 | 1968 | function d3f_geo_centroidPointXYZ(x, y, z) { 1969 | ++d3f_geo_centroidW0; 1970 | d3f_geo_centroidX0 += (x - d3f_geo_centroidX0) / d3f_geo_centroidW0; 1971 | d3f_geo_centroidY0 += (y - d3f_geo_centroidY0) / d3f_geo_centroidW0; 1972 | d3f_geo_centroidZ0 += (z - d3f_geo_centroidZ0) / d3f_geo_centroidW0; 1973 | } 1974 | 1975 | function d3f_geo_centroidLineStart() { 1976 | var x0, y0, z0; // previous point 1977 | 1978 | d3f_geo_centroid.point = function(λ, φ) { 1979 | λ *= d3f_radians; 1980 | var cosφ = Math.cos(φ *= d3f_radians); 1981 | x0 = cosφ * Math.cos(λ); 1982 | y0 = cosφ * Math.sin(λ); 1983 | z0 = Math.sin(φ); 1984 | d3f_geo_centroid.point = nextPoint; 1985 | d3f_geo_centroidPointXYZ(x0, y0, z0); 1986 | }; 1987 | 1988 | function nextPoint(λ, φ) { 1989 | λ *= d3f_radians; 1990 | var cosφ = Math.cos(φ *= d3f_radians), 1991 | x = cosφ * Math.cos(λ), 1992 | y = cosφ * Math.sin(λ), 1993 | z = Math.sin(φ), 1994 | w = Math.atan2( 1995 | Math.sqrt((w = y0 * z - z0 * y) * w + (w = z0 * x - x0 * z) * w + (w = x0 * y - y0 * x) * w), 1996 | x0 * x + y0 * y + z0 * z); 1997 | d3f_geo_centroidW1 += w; 1998 | d3f_geo_centroidX1 += w * (x0 + (x0 = x)); 1999 | d3f_geo_centroidY1 += w * (y0 + (y0 = y)); 2000 | d3f_geo_centroidZ1 += w * (z0 + (z0 = z)); 2001 | d3f_geo_centroidPointXYZ(x0, y0, z0); 2002 | } 2003 | } 2004 | 2005 | function d3f_geo_centroidLineEnd() { 2006 | d3f_geo_centroid.point = d3f_geo_centroidPoint; 2007 | } 2008 | 2009 | // See J. E. Brock, The Inertia Tensor for a Spherical Triangle, 2010 | // J. Applied Mechanics 42, 239 (1975). 2011 | function d3f_geo_centroidRingStart() { 2012 | var λ00, φ00, // first point 2013 | x0, y0, z0; // previous point 2014 | 2015 | d3f_geo_centroid.point = function(λ, φ) { 2016 | λ00 = λ, φ00 = φ; 2017 | d3f_geo_centroid.point = nextPoint; 2018 | λ *= d3f_radians; 2019 | var cosφ = Math.cos(φ *= d3f_radians); 2020 | x0 = cosφ * Math.cos(λ); 2021 | y0 = cosφ * Math.sin(λ); 2022 | z0 = Math.sin(φ); 2023 | d3f_geo_centroidPointXYZ(x0, y0, z0); 2024 | }; 2025 | 2026 | d3f_geo_centroid.lineEnd = function() { 2027 | nextPoint(λ00, φ00); 2028 | d3f_geo_centroid.lineEnd = d3f_geo_centroidLineEnd; 2029 | d3f_geo_centroid.point = d3f_geo_centroidPoint; 2030 | }; 2031 | 2032 | function nextPoint(λ, φ) { 2033 | λ *= d3f_radians; 2034 | var cosφ = Math.cos(φ *= d3f_radians), 2035 | x = cosφ * Math.cos(λ), 2036 | y = cosφ * Math.sin(λ), 2037 | z = Math.sin(φ), 2038 | cx = y0 * z - z0 * y, 2039 | cy = z0 * x - x0 * z, 2040 | cz = x0 * y - y0 * x, 2041 | m = Math.sqrt(cx * cx + cy * cy + cz * cz), 2042 | u = x0 * x + y0 * y + z0 * z, 2043 | v = m && -d3f_acos(u) / m, // area weight 2044 | w = Math.atan2(m, u); // line weight 2045 | d3f_geo_centroidX2 += v * cx; 2046 | d3f_geo_centroidY2 += v * cy; 2047 | d3f_geo_centroidZ2 += v * cz; 2048 | d3f_geo_centroidW1 += w; 2049 | d3f_geo_centroidX1 += w * (x0 + (x0 = x)); 2050 | d3f_geo_centroidY1 += w * (y0 + (y0 = y)); 2051 | d3f_geo_centroidZ1 += w * (z0 + (z0 = z)); 2052 | d3f_geo_centroidPointXYZ(x0, y0, z0); 2053 | } 2054 | } 2055 | 2056 | // TODO Unify this code with d3f.geom.polygon area? 2057 | 2058 | var d3f_geo_pathAreaSum, d3f_geo_pathAreaPolygon, d3f_geo_pathArea = { 2059 | point: d3f_noop, 2060 | lineStart: d3f_noop, 2061 | lineEnd: d3f_noop, 2062 | 2063 | // Only count area for polygon rings. 2064 | polygonStart: function() { 2065 | d3f_geo_pathAreaPolygon = 0; 2066 | d3f_geo_pathArea.lineStart = d3f_geo_pathAreaRingStart; 2067 | }, 2068 | polygonEnd: function() { 2069 | d3f_geo_pathArea.lineStart = d3f_geo_pathArea.lineEnd = d3f_geo_pathArea.point = d3f_noop; 2070 | d3f_geo_pathAreaSum += abs(d3f_geo_pathAreaPolygon / 2); 2071 | } 2072 | }; 2073 | 2074 | function d3f_geo_pathAreaRingStart() { 2075 | var x00, y00, x0, y0; 2076 | 2077 | // For the first point, … 2078 | d3f_geo_pathArea.point = function(x, y) { 2079 | d3f_geo_pathArea.point = nextPoint; 2080 | x00 = x0 = x, y00 = y0 = y; 2081 | }; 2082 | 2083 | // For subsequent points, … 2084 | function nextPoint(x, y) { 2085 | d3f_geo_pathAreaPolygon += y0 * x - x0 * y; 2086 | x0 = x, y0 = y; 2087 | } 2088 | 2089 | // For the last point, return to the start. 2090 | d3f_geo_pathArea.lineEnd = function() { 2091 | nextPoint(x00, y00); 2092 | }; 2093 | } 2094 | 2095 | var d3f_geo_pathBoundsX0, 2096 | d3f_geo_pathBoundsY0, 2097 | d3f_geo_pathBoundsX1, 2098 | d3f_geo_pathBoundsY1; 2099 | 2100 | var d3f_geo_pathBounds = { 2101 | point: d3f_geo_pathBoundsPoint, 2102 | lineStart: d3f_noop, 2103 | lineEnd: d3f_noop, 2104 | polygonStart: d3f_noop, 2105 | polygonEnd: d3f_noop 2106 | }; 2107 | 2108 | function d3f_geo_pathBoundsPoint(x, y) { 2109 | if (x < d3f_geo_pathBoundsX0) d3f_geo_pathBoundsX0 = x; 2110 | if (x > d3f_geo_pathBoundsX1) d3f_geo_pathBoundsX1 = x; 2111 | if (y < d3f_geo_pathBoundsY0) d3f_geo_pathBoundsY0 = y; 2112 | if (y > d3f_geo_pathBoundsY1) d3f_geo_pathBoundsY1 = y; 2113 | } 2114 | function d3f_geo_pathBuffer() { 2115 | var pointCircle = d3f_geo_pathBufferCircle(4.5), 2116 | buffer = []; 2117 | 2118 | var stream = { 2119 | point: point, 2120 | 2121 | // While inside a line, override point to moveTo then lineTo. 2122 | lineStart: function() { stream.point = pointLineStart; }, 2123 | lineEnd: lineEnd, 2124 | 2125 | // While inside a polygon, override lineEnd to closePath. 2126 | polygonStart: function() { stream.lineEnd = lineEndPolygon; }, 2127 | polygonEnd: function() { stream.lineEnd = lineEnd; stream.point = point; }, 2128 | 2129 | pointRadius: function(_) { 2130 | pointCircle = d3f_geo_pathBufferCircle(_); 2131 | return stream; 2132 | }, 2133 | 2134 | result: function() { 2135 | if (buffer.length) { 2136 | var result = buffer.join(""); 2137 | buffer = []; 2138 | return result; 2139 | } 2140 | } 2141 | }; 2142 | 2143 | function point(x, y) { 2144 | buffer.push("M", x, ",", y, pointCircle); 2145 | } 2146 | 2147 | function pointLineStart(x, y) { 2148 | buffer.push("M", x, ",", y); 2149 | stream.point = pointLine; 2150 | } 2151 | 2152 | function pointLine(x, y) { 2153 | buffer.push("L", x, ",", y); 2154 | } 2155 | 2156 | function lineEnd() { 2157 | stream.point = point; 2158 | } 2159 | 2160 | function lineEndPolygon() { 2161 | buffer.push("Z"); 2162 | } 2163 | 2164 | return stream; 2165 | } 2166 | 2167 | function d3f_geo_pathBufferCircle(radius) { 2168 | return "m0," + radius 2169 | + "a" + radius + "," + radius + " 0 1,1 0," + -2 * radius 2170 | + "a" + radius + "," + radius + " 0 1,1 0," + 2 * radius 2171 | + "z"; 2172 | } 2173 | 2174 | // TODO Unify this code with d3f.geom.polygon centroid? 2175 | // TODO Enforce positive area for exterior, negative area for interior? 2176 | 2177 | var d3f_geo_pathCentroid = { 2178 | point: d3f_geo_pathCentroidPoint, 2179 | 2180 | // For lines, weight by length. 2181 | lineStart: d3f_geo_pathCentroidLineStart, 2182 | lineEnd: d3f_geo_pathCentroidLineEnd, 2183 | 2184 | // For polygons, weight by area. 2185 | polygonStart: function() { 2186 | d3f_geo_pathCentroid.lineStart = d3f_geo_pathCentroidRingStart; 2187 | }, 2188 | polygonEnd: function() { 2189 | d3f_geo_pathCentroid.point = d3f_geo_pathCentroidPoint; 2190 | d3f_geo_pathCentroid.lineStart = d3f_geo_pathCentroidLineStart; 2191 | d3f_geo_pathCentroid.lineEnd = d3f_geo_pathCentroidLineEnd; 2192 | } 2193 | }; 2194 | 2195 | function d3f_geo_pathCentroidPoint(x, y) { 2196 | d3f_geo_centroidX0 += x; 2197 | d3f_geo_centroidY0 += y; 2198 | ++d3f_geo_centroidZ0; 2199 | } 2200 | 2201 | function d3f_geo_pathCentroidLineStart() { 2202 | var x0, y0; 2203 | 2204 | d3f_geo_pathCentroid.point = function(x, y) { 2205 | d3f_geo_pathCentroid.point = nextPoint; 2206 | d3f_geo_pathCentroidPoint(x0 = x, y0 = y); 2207 | }; 2208 | 2209 | function nextPoint(x, y) { 2210 | var dx = x - x0, dy = y - y0, z = Math.sqrt(dx * dx + dy * dy); 2211 | d3f_geo_centroidX1 += z * (x0 + x) / 2; 2212 | d3f_geo_centroidY1 += z * (y0 + y) / 2; 2213 | d3f_geo_centroidZ1 += z; 2214 | d3f_geo_pathCentroidPoint(x0 = x, y0 = y); 2215 | } 2216 | } 2217 | 2218 | function d3f_geo_pathCentroidLineEnd() { 2219 | d3f_geo_pathCentroid.point = d3f_geo_pathCentroidPoint; 2220 | } 2221 | 2222 | function d3f_geo_pathCentroidRingStart() { 2223 | var x00, y00, x0, y0; 2224 | 2225 | // For the first point, … 2226 | d3f_geo_pathCentroid.point = function(x, y) { 2227 | d3f_geo_pathCentroid.point = nextPoint; 2228 | d3f_geo_pathCentroidPoint(x00 = x0 = x, y00 = y0 = y); 2229 | }; 2230 | 2231 | // For subsequent points, … 2232 | function nextPoint(x, y) { 2233 | var dx = x - x0, dy = y - y0, z = Math.sqrt(dx * dx + dy * dy); 2234 | d3f_geo_centroidX1 += z * (x0 + x) / 2; 2235 | d3f_geo_centroidY1 += z * (y0 + y) / 2; 2236 | d3f_geo_centroidZ1 += z; 2237 | 2238 | z = y0 * x - x0 * y; 2239 | d3f_geo_centroidX2 += z * (x0 + x); 2240 | d3f_geo_centroidY2 += z * (y0 + y); 2241 | d3f_geo_centroidZ2 += z * 3; 2242 | d3f_geo_pathCentroidPoint(x0 = x, y0 = y); 2243 | } 2244 | 2245 | // For the last point, return to the start. 2246 | d3f_geo_pathCentroid.lineEnd = function() { 2247 | nextPoint(x00, y00); 2248 | }; 2249 | } 2250 | 2251 | function d3f_geo_pathContext(context) { 2252 | var pointRadius = 4.5; 2253 | 2254 | var stream = { 2255 | point: point, 2256 | 2257 | // While inside a line, override point to moveTo then lineTo. 2258 | lineStart: function() { stream.point = pointLineStart; }, 2259 | lineEnd: lineEnd, 2260 | 2261 | // While inside a polygon, override lineEnd to closePath. 2262 | polygonStart: function() { stream.lineEnd = lineEndPolygon; }, 2263 | polygonEnd: function() { stream.lineEnd = lineEnd; stream.point = point; }, 2264 | 2265 | pointRadius: function(_) { 2266 | pointRadius = _; 2267 | return stream; 2268 | }, 2269 | 2270 | result: d3f_noop 2271 | }; 2272 | 2273 | function point(x, y) { 2274 | context.moveTo(x + pointRadius, y); 2275 | context.arc(x, y, pointRadius, 0, τ); 2276 | } 2277 | 2278 | function pointLineStart(x, y) { 2279 | context.moveTo(x, y); 2280 | stream.point = pointLine; 2281 | } 2282 | 2283 | function pointLine(x, y) { 2284 | context.lineTo(x, y); 2285 | } 2286 | 2287 | function lineEnd() { 2288 | stream.point = point; 2289 | } 2290 | 2291 | function lineEndPolygon() { 2292 | context.closePath(); 2293 | } 2294 | 2295 | return stream; 2296 | } 2297 | 2298 | d3f.geo.path = function() { 2299 | var pointRadius = 4.5, 2300 | projection, 2301 | context, 2302 | projectStream, 2303 | contextStream, 2304 | cacheStream; 2305 | 2306 | function path(object) { 2307 | if (object) { 2308 | if (typeof pointRadius === "function") contextStream.pointRadius(+pointRadius.apply(this, arguments)); 2309 | if (!cacheStream || !cacheStream.valid) cacheStream = projectStream(contextStream); 2310 | d3f.geo.stream(object, cacheStream); 2311 | } 2312 | return contextStream.result(); 2313 | } 2314 | 2315 | path.area = function(object) { 2316 | d3f_geo_pathAreaSum = 0; 2317 | d3f.geo.stream(object, projectStream(d3f_geo_pathArea)); 2318 | return d3f_geo_pathAreaSum; 2319 | }; 2320 | 2321 | path.centroid = function(object) { 2322 | d3f_geo_centroidX0 = d3f_geo_centroidY0 = d3f_geo_centroidZ0 = 2323 | d3f_geo_centroidX1 = d3f_geo_centroidY1 = d3f_geo_centroidZ1 = 2324 | d3f_geo_centroidX2 = d3f_geo_centroidY2 = d3f_geo_centroidZ2 = 0; 2325 | d3f.geo.stream(object, projectStream(d3f_geo_pathCentroid)); 2326 | return d3f_geo_centroidZ2 ? [d3f_geo_centroidX2 / d3f_geo_centroidZ2, d3f_geo_centroidY2 / d3f_geo_centroidZ2] 2327 | : d3f_geo_centroidZ1 ? [d3f_geo_centroidX1 / d3f_geo_centroidZ1, d3f_geo_centroidY1 / d3f_geo_centroidZ1] 2328 | : d3f_geo_centroidZ0 ? [d3f_geo_centroidX0 / d3f_geo_centroidZ0, d3f_geo_centroidY0 / d3f_geo_centroidZ0] 2329 | : [NaN, NaN]; 2330 | }; 2331 | 2332 | path.bounds = function(object) { 2333 | d3f_geo_pathBoundsX1 = d3f_geo_pathBoundsY1 = -(d3f_geo_pathBoundsX0 = d3f_geo_pathBoundsY0 = Infinity); 2334 | d3f.geo.stream(object, projectStream(d3f_geo_pathBounds)); 2335 | return [[d3f_geo_pathBoundsX0, d3f_geo_pathBoundsY0], [d3f_geo_pathBoundsX1, d3f_geo_pathBoundsY1]]; 2336 | }; 2337 | 2338 | path.projection = function(_) { 2339 | if (!arguments.length) return projection; 2340 | projectStream = (projection = _) ? _.stream || d3f_geo_pathProjectStream(_) : d3f_identity; 2341 | return reset(); 2342 | }; 2343 | 2344 | path.context = function(_) { 2345 | if (!arguments.length) return context; 2346 | contextStream = (context = _) == null ? new d3f_geo_pathBuffer : new d3f_geo_pathContext(_); 2347 | if (typeof pointRadius !== "function") contextStream.pointRadius(pointRadius); 2348 | return reset(); 2349 | }; 2350 | 2351 | path.pointRadius = function(_) { 2352 | if (!arguments.length) return pointRadius; 2353 | pointRadius = typeof _ === "function" ? _ : (contextStream.pointRadius(+_), +_); 2354 | return path; 2355 | }; 2356 | 2357 | function reset() { 2358 | cacheStream = null; 2359 | return path; 2360 | } 2361 | 2362 | return path.projection(d3f.geo.albersUsa()).context(null); 2363 | }; 2364 | 2365 | function d3f_geo_pathProjectStream(project) { 2366 | var resample = d3f_geo_resample(function(x, y) { return project([x * d3f_degrees, y * d3f_degrees]); }); 2367 | return function(stream) { return d3f_geo_projectionRadians(resample(stream)); }; 2368 | } 2369 | 2370 | function d3f_geo_mercator(λ, φ) { 2371 | return [λ, Math.log(Math.tan(π / 4 + φ / 2))]; 2372 | } 2373 | 2374 | d3f_geo_mercator.invert = function(x, y) { 2375 | return [x, 2 * Math.atan(Math.exp(y)) - halfπ]; 2376 | }; 2377 | 2378 | function d3f_geo_mercatorProjection(project) { 2379 | var m = d3f_geo_projection(project), 2380 | scale = m.scale, 2381 | translate = m.translate, 2382 | clipExtent = m.clipExtent, 2383 | clipAuto; 2384 | 2385 | m.scale = function() { 2386 | var v = scale.apply(m, arguments); 2387 | return v === m ? (clipAuto ? m.clipExtent(null) : m) : v; 2388 | }; 2389 | 2390 | m.translate = function() { 2391 | var v = translate.apply(m, arguments); 2392 | return v === m ? (clipAuto ? m.clipExtent(null) : m) : v; 2393 | }; 2394 | 2395 | m.clipExtent = function(_) { 2396 | var v = clipExtent.apply(m, arguments); 2397 | if (v === m) { 2398 | if (clipAuto = _ == null) { 2399 | var k = π * scale(), t = translate(); 2400 | clipExtent([[t[0] - k, t[1] - k], [t[0] + k, t[1] + k]]); 2401 | } 2402 | } else if (clipAuto) { 2403 | v = null; 2404 | } 2405 | return v; 2406 | }; 2407 | 2408 | return m.clipExtent(null); 2409 | } 2410 | 2411 | (d3f.geo.mercator = function() { 2412 | return d3f_geo_mercatorProjection(d3f_geo_mercator); 2413 | }).raw = d3f_geo_mercator; 2414 | if (typeof define === "function" && define.amd) define(d3f); 2415 | else if (typeof module === "object" && module.exports) module.exports = d3f; 2416 | this.d3f = d3f; 2417 | }(); 2418 | -------------------------------------------------------------------------------- /d3.geo/makefile: -------------------------------------------------------------------------------- 1 | LIBRARY_FILES = \ 2 | node_modules/d3/src/start.js \ 3 | node_modules/d3/src/geo/path.js \ 4 | node_modules/d3/src/geo/geo.js \ 5 | node_modules/d3/src/geo/mercator.js \ 6 | node_modules/d3/src/end.js 7 | 8 | 9 | 10 | lib.js: $(LIBRARY_FILES) 11 | node_modules/.bin/smash $(LIBRARY_FILES) | sed -e "s/d3/d3f/g" > d3f.js 12 | -------------------------------------------------------------------------------- /d3.geo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "anonymous", 3 | "version": "0.0.1", 4 | "dependencies": { 5 | "d3": "3", 6 | "queue-async": "1", 7 | "smash": "0", 8 | "topojson": "1", 9 | "uglify-js": "2" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /dist/async-cartogram.js: -------------------------------------------------------------------------------- 1 | !function(n,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):"object"==typeof exports?exports.AsyncCartogram=t():n.AsyncCartogram=t()}(this,function(){return function(n){function t(i){if(r[i])return r[i].exports;var o=r[i]={exports:{},id:i,loaded:!1};return n[i].call(o.exports,o,o.exports,t),o.loaded=!0,o.exports}var r={};return t.m=n,t.c=r,t.p="",t(0)}([function(n,t,r){function i(){var n;return new(n=r("undefined"!=typeof window?1:3))}function o(n,t,r){function o(i,o){i.postMessage({"do":"carto",geo:n,values:t[o],featureProperty:r,task:o})}function a(n,t){n.onmessage=function(t){var r=t;if("undefined"!=typeof t.data&&(r=t.data),"processing"===r.done){if(u.notify(Object.keys(s).length/l),s[r.task]=r,Object.keys(s).length===l)return u.resolve(s),void f.forEach(function(n){n.terminate()});if(c.length>0){var i=c.pop();o(n,i)}}},o(n,t)}for(var u=new e.Deferred,c=Object.keys(t),l=c.length,f=[],s={};f.length<9&&c.length>0;){var p=i();f.push(p),a(p,c.pop())}return u}var e=r(6);n.exports=o},function(n,t,r){n.exports=function(){return r(2)('!function(n){function t(i){if(r[i])return r[i].exports;var o=r[i]={exports:{},id:i,loaded:!1};return n[i].call(o.exports,o,o.exports,t),o.loaded=!0,o.exports}var r={};return t.m=n,t.c=r,t.p="",t(0)}([function(n,t,r){function i(){}function o(n){function t(n){return h[n.properties[g]]}var r=n;if("undefined"!=typeof n.data&&(r=n.data),"carto"===r["do"]){var i=r.geo,o=i.topology,u=i.geometries,c=i.path,l=i.translation,f=i.projection.name,s=i.projection.scaling,p=i.projection.center,l=i.projection.translation,h=r.values,g=r.featureProperty,v=r.task;o=a.copy(o);var d=e.geo[f]().scale(s);null!=p&&d.center(p),null!=l&&d.translate(l);for(var M,y,E,m,S,x=a.transformer(o.transform),N=o.arcs.length,b=0,q=new Array(N);N>b;){for(M=0,y=0,E=o.arcs[b].length,m=0,S=new Array(E);E>m;)o.arcs[b][m][0]=M+=o.arcs[b][m][0],o.arcs[b][m][1]=y+=o.arcs[b][m][1],S[m]=d(x(o.arcs[b][m])),m++;q[b++]=S}for(var c=e.geo.path().projection(null),w=a.object(q,{type:"GeometryCollection",geometries:u}).geometries.map(function(n){return{type:"Feature",id:n.id,properties:a.properties.call(null,n,o),geometry:n}}),P=w.map(t),j=P.reduce(function(n,t){return n+t}),A=8,z=0;z++b;){for(E=q[b].length,m=0;E>m;){for(J=[0,0],K=F.length,Q=0;K>Q;){if(V=F[Q].centroid,D=F[Q].mass,B=F[Q].radius,W=B*B,X=q[b][m][0]-V[0],Y=q[b][m][1]-V[1],$=X*X+Y*Y,_=Math.sqrt($),_B?D*B/_:D*($/W)*(4-3*_/B);var rn=a.arctans(Y,X);J[0]+=nn*rn.cos,J[1]+=nn*rn.sin}Q++}q[b][m][0]+=J[0]*tn,q[b][m][1]+=J[1]*tn,m++}b++}if(1>=H)break}var on={done:"processing",features:w,task:v};"undefined"!=typeof self?self.postMessage(on):this.onmessage(on)}}var e=r(1),a=r(2);"undefined"!=typeof onmessage?onmessage=o:(i.prototype.postMessage=o,i.prototype.terminate=function(){},n.exports=i)},function(n,t,r){var i,o;!function(){function e(n){return n}function a(n,t,r){return(t[0]-n[0])*(r[1]-n[1])-(t[1]-n[1])*(r[0]-n[0])}function u(n){return n>1?0:-1>n?bn:Math.acos(n)}function c(n){return n>1?wn:-1>n?-wn:Math.asin(n)}function l(n,t,r){return function(){var i=r.apply(t,arguments);return i===t?n:i}}function f(){return!0}function s(){}function p(n){return[Math.atan2(n[1],n[0]),c(n[2])]}function h(n,t){return An(n[0]-t[0])u;++u)o.point((r=n[u])[0],r[1]);return void o.lineEnd()}var c=new d(r,n,null,!0),l=new d(r,null,c,!1);c.o=l,e.push(c),a.push(l),c=new d(i,n,null,!1),l=new d(i,null,c,!0),c.o=l,e.push(c),a.push(l)}}),a.sort(t),v(e),v(a),e.length){for(var u=0,c=r,l=a.length;l>u;++u)a[u].e=c=!c;for(var f,s,p=e[0];;){for(var g=p,M=!0;g.v;)if((g=g.n)===p)return;f=g.z,o.lineStart();do{if(g.v=g.o.v=!0,g.e){if(M)for(var u=0,l=f.length;l>u;++u)o.point((s=f[u])[0],s[1]);else i(g.x,g.n.x,1,o);g=g.n}else{if(M){f=g.p.z;for(var u=f.length-1;u>=0;--u)o.point((s=f[u])[0],s[1])}else i(g.x,g.p.x,-1,o);g=g.p}g=g.o,f=g.z,M=!M}while(!g.v);o.lineEnd()}}}function v(n){if(t=n.length){for(var t,r,i=0,o=n[0];++i0){for(q||(e.polygonStart(),q=!0),e.lineStart();++a1&&2&t&&r.push(r.pop().concat(r.shift())),h.push(r.filter(y))}var h,v,d,M=t(e),S=o.invert(i[0],i[1]),x={point:a,lineStart:c,lineEnd:l,polygonStart:function(){x.point=f,x.lineStart=s,x.lineEnd=p,h=[],v=[]},polygonEnd:function(){x.point=a,x.lineStart=c,x.lineEnd=l,h=Sn.merge(h);var n=J(S,v);h.length?(q||(e.polygonStart(),q=!0),g(h,m,n,r,e)):n&&(q||(e.polygonStart(),q=!0),e.lineStart(),r(null,null,1,e),e.lineEnd()),q&&(e.polygonEnd(),q=!1),h=v=null},sphere:function(){e.polygonStart(),e.lineStart(),r(null,null,1,e),e.lineEnd(),e.polygonEnd()}},N=E(),b=t(N),q=!1;return x}}function y(n){return n.length>1}function E(){var n,t=[];return{lineStart:function(){t.push(n=[])},point:function(t,r){n.push([t,r])},lineEnd:s,buffer:function(){var r=t;return t=[],n=null,r},rejoin:function(){t.length>1&&t.push(t.pop().concat(t.shift()))}}}function m(n,t){return((n=n.x)[0]<0?n[1]-wn-xn:wn-n[1])-((t=t.x)[0]<0?t[1]-wn-xn:wn-t[1])}function S(n){var t,r=NaN,i=NaN,o=NaN;return{lineStart:function(){n.lineStart(),t=1},point:function(e,a){var u=e>0?bn:-bn,c=An(e-r);An(c-bn)0?wn:-wn),n.point(o,i),n.lineEnd(),n.lineStart(),n.point(u,i),n.point(e,i),t=0):o!==u&&c>=bn&&(An(r-o)xn?Math.atan((Math.sin(t)*(e=Math.cos(i))*Math.sin(r)-Math.sin(i)*(o=Math.cos(t))*Math.sin(n))/(o*e*a)):(t+i)/2}function N(n,t,r,i){var o;if(null==n)o=r*wn,i.point(-bn,o),i.point(0,o),i.point(bn,o),i.point(bn,0),i.point(bn,-o),i.point(0,-o),i.point(-bn,-o),i.point(-bn,0),i.point(-bn,o);else if(An(n[0]-t[0])>xn){var e=n[0]bn?n-qn:-bn>n?n+qn:n,t]}function L(n,t,r){return n?t||r?z(k(n),C(t,r)):k(n):t||r?C(t,r):R}function O(n){return function(t,r){return t+=n,[t>bn?t-qn:-bn>t?t+qn:t,r]}}function k(n){var t=O(n);return t.invert=O(-n),t}function C(n,t){function r(n,t){var r=Math.cos(t),u=Math.cos(n)*r,l=Math.sin(n)*r,f=Math.sin(t),s=f*i+u*o;return[Math.atan2(l*e-s*a,u*i-f*o),c(s*e+l*a)]}var i=Math.cos(n),o=Math.sin(n),e=Math.cos(t),a=Math.sin(t);return r.invert=function(n,t){var r=Math.cos(t),u=Math.cos(n)*r,l=Math.sin(n)*r,f=Math.sin(t),s=f*e-l*a;return[Math.atan2(l*e+f*a,u*i+s*o),c(s*i-u*o)]},r}function F(n,t){var r=Math.cos(n),i=Math.sin(n);return function(o,e,a,u){var c=a*t;null!=o?(o=G(r,o),e=G(r,e),(a>0?e>o:o>e)&&(o+=a*qn)):(o=n+a*qn,e=n-.5*c);for(var l,f=o;a>0?f>e:e>f;f-=c)u.point((l=p([r,-i*Math.cos(f),-i*Math.sin(f)]))[0],l[1])}}function G(n,t){var r=b(t);r[0]-=n,A(r);var i=u(-r[1]);return((-r[2]<0?-i:i)+2*Math.PI-xn)%(2*Math.PI)}function T(){}function U(n,t,r){var i=r.s=n+t,o=i-n,e=i-o;r.t=n-e+(t-o)}function Z(n,t){n&&Ln.hasOwnProperty(n.type)&&Ln[n.type](n,t)}function B(n,t,r){var i,o=-1,e=n.length-r;for(t.lineStart();++o=0?1:-1,u=a*r,c=Math.cos(t),l=Math.sin(t),f=e*l,s=o*c+f*Math.cos(u),p=f*a*Math.sin(u);kn.add(Math.atan2(p,s)),i=n,o=c,e=l}var t,r,i,o,e;Cn.point=function(a,u){Cn.point=n,i=(t=a)*Pn,o=Math.cos(u=(r=u)*Pn/2+bn/4),e=Math.sin(u)},Cn.lineEnd=function(){n(t,r)}}function J(n,t){var r=n[0],i=n[1],o=[Math.sin(r),-Math.cos(r),0],e=0,a=0;kn.reset();for(var u=0,l=t.length;l>u;++u){var f=t[u],s=f.length;if(s)for(var p=f[0],h=p[0],g=p[1]/2+bn/4,v=Math.sin(g),d=Math.cos(g),M=1;;){M===s&&(M=0),n=f[M];var y=n[0],E=n[1]/2+bn/4,m=Math.sin(E),S=Math.cos(E),x=y-h,N=x>=0?1:-1,q=N*x,P=q>bn,j=v*m;if(kn.add(Math.atan2(j*N*Math.sin(q),d*S+j*Math.cos(q))),e+=P?x+N*qn:x,P^h>=r^y>=r){var z=w(b(p),b(n));A(z);var I=w(o,z);A(I);var R=(P^x>=0?-1:1)*c(I[2]);(i>R||i===R&&(z[0]||z[1]))&&(a+=P^x>=0?1:-1)}if(!M++)break;h=y,v=m,d=S,p=n}}return(-xn>e||xn>e&&0>kn)^1&a}function K(n){function t(n,t){return Math.cos(n)*Math.cos(t)>e}function r(n){var r,e,c,l,f;return{lineStart:function(){l=c=!1,f=1},point:function(s,p){var g,v=[s,p],d=t(s,p),M=a?d?0:o(s,p):d?o(s+(0>s?bn:-bn),p):0;if(!r&&(l=c=d)&&n.lineStart(),d!==c&&(g=i(r,v),(h(r,g)||h(v,g))&&(v[0]+=xn,v[1]+=xn,d=t(v[0],v[1]))),d!==c)f=0,d?(n.lineStart(),g=i(v,r),n.point(g[0],g[1])):(g=i(r,v),n.point(g[0],g[1]),n.lineEnd()),r=g;else if(u&&r&&a^d){var y;M&e||!(y=i(v,r,!0))||(f=0,a?(n.lineStart(),n.point(y[0][0],y[0][1]),n.point(y[1][0],y[1][1]),n.lineEnd()):(n.point(y[1][0],y[1][1]),n.lineEnd(),n.lineStart(),n.point(y[0][0],y[0][1])))}!d||r&&h(r,v)||n.point(v[0],v[1]),r=v,c=d,e=M},lineEnd:function(){c&&n.lineEnd(),r=null},clean:function(){return f|(l&&c)<<1}}}function i(n,t,r){var i=b(n),o=b(t),a=[1,0,0],u=w(i,o),c=q(u,u),l=u[0],f=c-l*l;if(!f)return!r&&n;var s=e*c/f,h=-e*l/f,g=w(a,u),v=j(a,s),d=j(u,h);P(v,d);var M=g,y=q(v,M),E=q(M,M),m=y*y-E*(q(v,v)-1);if(!(0>m)){var S=Math.sqrt(m),x=j(M,(-y-S)/E);if(P(x,v),x=p(x),!r)return x;var N,A=n[0],z=t[0],I=n[1],R=t[1];A>z&&(N=A,A=z,z=N);var L=z-A,O=An(L-bn)L;if(!O&&I>R&&(N=I,I=R,R=N),k?O?I+R>0^x[1]<(An(x[0]-A)bn^(A<=x[0]&&x[0]<=z)){var C=j(M,(-y+S)/E);return P(C,v),[x,p(C)]}}}function o(t,r){var i=a?n:bn-n,o=0;return-i>t?o|=1:t>i&&(o|=2),-i>r?o|=4:r>i&&(o|=8),o}var e=Math.cos(n),a=e>0,u=An(e)>xn,c=F(n,6*Pn);return M(t,r,c,a?[0,-n]:[-bn,n-bn])}function Q(n,t,r,i){return function(o){var e,a=o.a,u=o.b,c=a.x,l=a.y,f=u.x,s=u.y,p=0,h=1,g=f-c,v=s-l;if(e=n-c,g||!(e>0)){if(e/=g,0>g){if(p>e)return;h>e&&(h=e)}else if(g>0){if(e>h)return;e>p&&(p=e)}if(e=r-c,g||!(0>e)){if(e/=g,0>g){if(e>h)return;e>p&&(p=e)}else if(g>0){if(p>e)return;h>e&&(h=e)}if(e=t-l,v||!(e>0)){if(e/=v,0>v){if(p>e)return;h>e&&(h=e)}else if(v>0){if(e>h)return;e>p&&(p=e)}if(e=i-l,v||!(0>e)){if(e/=v,0>v){if(e>h)return;e>p&&(p=e)}else if(v>0){if(p>e)return;h>e&&(h=e)}return p>0&&(o.a={x:c+p*g,y:l+p*v}),1>h&&(o.b={x:c+h*g,y:l+h*v}),o}}}}}}function V(n,t,r,i){function o(i,o){return An(i[0]-n)0?0:3:An(i[0]-r)0?2:1:An(i[1]-t)0?1:0:o>0?3:2}function e(n,t){return u(n.x,t.x)}function u(n,t){var r=o(n,1),i=o(t,1);return r!==i?r-i:0===r?t[1]-n[1]:1===r?n[0]-t[0]:2===r?n[1]-t[1]:t[0]-n[0]}return function(c){function l(n){for(var t=0,r=y.length,i=n[1],o=0;r>o;++o)for(var e,u=1,c=y[o],l=c.length,f=c[0];l>u;++u)e=c[u],f[1]<=i?e[1]>i&&a(f,e,n)>0&&++t:e[1]<=i&&a(f,e,n)<0&&--t,f=e;return 0!==t}function f(e,a,c,l){var f=0,s=0;if(null==e||(f=o(e,c))!==(s=o(a,c))||u(e,a)<0^c>0){do l.point(0===f||3===f?n:r,f>1?i:t);while((f=(f+c+4)%4)!==s)}else l.point(a[0],a[1])}function s(o,e){return o>=n&&r>=o&&e>=t&&i>=e}function p(n,t){s(n,t)&&c.point(n,t)}function h(){R.point=d,y&&y.push(m=[]),P=!0,w=!1,b=q=NaN}function v(){M&&(d(S,x),N&&w&&z.rejoin(),M.push(z.buffer())),R.point=p,w&&c.lineEnd()}function d(n,t){n=Math.max(-Fn,Math.min(Fn,n)),t=Math.max(-Fn,Math.min(Fn,t));var r=s(n,t);if(y&&m.push([n,t]),P)S=n,x=t,N=r,P=!1,r&&(c.lineStart(),c.point(n,t));else if(r&&w)c.point(n,t);else{var i={a:{x:b,y:q},b:{x:n,y:t}};I(i)?(w||(c.lineStart(),c.point(i.a.x,i.a.y)),c.point(i.b.x,i.b.y),r||c.lineEnd(),j=!1):r&&(c.lineStart(),c.point(n,t),j=!1)}b=n,q=t,w=r}var M,y,m,S,x,N,b,q,w,P,j,A=c,z=E(),I=Q(n,t,r,i),R={point:p,lineStart:h,lineEnd:v,polygonStart:function(){c=z,M=[],y=[],j=!0},polygonEnd:function(){c=A,M=Sn.merge(M);var t=l([n,i]),r=j&&t,o=M.length;(r||o)&&(c.polygonStart(),r&&(c.lineStart(),f(null,null,1,c),c.lineEnd()),o&&g(M,e,t,f,c),c.polygonEnd()),M=y=m=null}};return R}}function W(n){function t(n){return(u?i:r)(n)}function r(t){return Y(t,function(r,i){r=n(r,i),t.point(r[0],r[1])})}function i(t){function r(r,i){r=n(r,i),t.point(r[0],r[1])}function i(){E=NaN,q.point=e,t.lineStart()}function e(r,i){var e=b([r,i]),a=n(r,i);o(E,m,y,S,x,N,E=a[0],m=a[1],y=r,S=e[0],x=e[1],N=e[2],u,t),t.point(E,m)}function a(){q.point=r,t.lineEnd()}function c(){i(),q.point=l,q.lineEnd=f}function l(n,t){e(s=n,p=t),h=E,g=m,v=S,d=x,M=N,q.point=e}function f(){o(E,m,y,S,x,N,h,g,s,v,d,M,u,t),q.lineEnd=a,a()}var s,p,h,g,v,d,M,y,E,m,S,x,N,q={point:r,lineStart:i,lineEnd:a,polygonStart:function(){t.polygonStart(),q.lineStart=c},polygonEnd:function(){t.polygonEnd(),q.lineStart=i}};return q}function o(t,r,i,u,c,l,f,s,p,h,g,v,d,M){var y=f-t,E=s-r,m=y*y+E*E;if(m>4*e&&d--){var S=u+h,x=c+g,N=l+v,b=Math.sqrt(S*S+x*x+N*N),q=Math.asin(N/=b),w=An(An(N)-1)e||An((y*z+E*I)/m-.5)>.3||a>u*h+c*g+l*v)&&(o(t,r,i,u,c,l,j,A,w,S/=b,x/=b,N,d,M),M.point(j,A),o(j,A,w,S,x,N,f,s,p,h,g,v,d,M))}}var e=.5,a=Math.cos(30*Pn),u=16;return t.precision=function(n){return arguments.length?(u=(e=n*n)>0&&16,t):Math.sqrt(e)},t}function X(n){this.stream=n}function Y(n,t){return{point:t,sphere:function(){n.sphere()},lineStart:function(){n.lineStart()},lineEnd:function(){n.lineEnd()},polygonStart:function(){n.polygonStart()},polygonEnd:function(){n.polygonEnd()}}}function $(n){return _(function(){return n})()}function _(n){function t(n){return n=c(n[0]*Pn,n[1]*Pn),[n[0]*h+l,f-n[1]*h]}function r(n){return n=c.invert((n[0]-l)/h,(f-n[1])/h),n&&[n[0]*jn,n[1]*jn]}function i(){c=z(u=L(y,E,m),a);var n=a(d,M);return l=g-n[0]*h,f=v+n[1]*h,o()}function o(){return s&&(s.valid=!1,s=null),t}var a,u,c,l,f,s,p=W(function(n,t){return n=a(n,t),[n[0]*h+l,f-n[1]*h]}),h=150,g=480,v=250,d=0,M=0,y=0,E=0,m=0,S=zn,x=e,N=null,b=null;return t.stream=function(n){return s&&(s.valid=!1),s=nn(S(u,p(x(n)))),s.valid=!0,s},t.clipAngle=function(n){return arguments.length?(S=null==n?(N=n,zn):K((N=+n)*Pn),o()):N},t.clipExtent=function(n){return arguments.length?(b=n,x=n?V(n[0][0],n[0][1],n[1][0],n[1][1]):e,o()):b},t.scale=function(n){return arguments.length?(h=+n,i()):h},t.translate=function(n){return arguments.length?(g=+n[0],v=+n[1],i()):[g,v]},t.center=function(n){return arguments.length?(d=n[0]%360*Pn,M=n[1]%360*Pn,i()):[d*jn,M*jn]},t.rotate=function(n){return arguments.length?(y=n[0]%360*Pn,E=n[1]%360*Pn,m=n.length>2?n[2]%360*Pn:0,i()):[y*jn,E*jn,m*jn]},Sn.rebind(t,p,"precision"),function(){return a=n.apply(this,arguments),t.invert=a.invert&&r,i()}}function nn(n){return Y(n,function(t,r){n.point(t*Pn,r*Pn)})}function tn(n){var t=0,r=bn/3,i=_(n),o=i(t,r);return o.parallels=function(n){return arguments.length?i(t=n[0]*bn/180,r=n[1]*bn/180):[t/bn*180,r/bn*180]},o}function rn(n,t){function r(n,t){var r=Math.sqrt(e-2*o*Math.sin(t))/o;return[r*Math.sin(n*=o),a-r*Math.cos(n)]}var i=Math.sin(n),o=(i+Math.sin(t))/2,e=1+i*(2*o-i),a=Math.sqrt(e)/o;return r.invert=function(n,t){var r=a-t;return[Math.atan2(n,r)/o,c((e-(n*n+r*r)*o*o)/(2*o))]},r}function on(n,t){n*=Pn;var r=Math.cos(t*=Pn);en(r*Math.cos(n),r*Math.sin(n),Math.sin(t))}function en(n,t,r){++Gn,Un+=(n-Un)/Gn,Zn+=(t-Zn)/Gn,Bn+=(r-Bn)/Gn}function an(){function n(n,o){n*=Pn;var e=Math.cos(o*=Pn),a=e*Math.cos(n),u=e*Math.sin(n),c=Math.sin(o),l=Math.atan2(Math.sqrt((l=r*c-i*u)*l+(l=i*a-t*c)*l+(l=t*u-r*a)*l),t*a+r*u+i*c);Tn+=l,Dn+=l*(t+(t=a)),Hn+=l*(r+(r=u)),Jn+=l*(i+(i=c)),en(t,r,i)}var t,r,i;tt.point=function(o,e){o*=Pn;var a=Math.cos(e*=Pn);t=a*Math.cos(o),r=a*Math.sin(o),i=Math.sin(e),tt.point=n,en(t,r,i)}}function un(){tt.point=on}function cn(){function n(n,t){n*=Pn;var r=Math.cos(t*=Pn),a=r*Math.cos(n),c=r*Math.sin(n),l=Math.sin(t),f=o*l-e*c,s=e*a-i*l,p=i*c-o*a,h=Math.sqrt(f*f+s*s+p*p),g=i*a+o*c+e*l,v=h&&-u(g)/h,d=Math.atan2(h,g);Kn+=v*f,Qn+=v*s,Vn+=v*p,Tn+=d,Dn+=d*(i+(i=a)),Hn+=d*(o+(o=c)),Jn+=d*(e+(e=l)),en(i,o,e)}var t,r,i,o,e;tt.point=function(a,u){t=a,r=u,tt.point=n,a*=Pn;var c=Math.cos(u*=Pn);i=c*Math.cos(a),o=c*Math.sin(a),e=Math.sin(u),en(i,o,e)},tt.lineEnd=function(){n(t,r),tt.lineEnd=un,tt.point=on}}function ln(){function n(n,t){Xn+=o*n-i*t,i=n,o=t}var t,r,i,o;rt.point=function(e,a){rt.point=n,t=i=e,r=o=a},rt.lineEnd=function(){n(t,r)}}function fn(n,t){Yn>n&&(Yn=n),n>_n&&(_n=n),$n>t&&($n=t),t>nt&&(nt=t)}function sn(){function n(n,t){a.push("M",n,",",t,e)}function t(n,t){a.push("M",n,",",t),u.point=r}function r(n,t){a.push("L",n,",",t)}function i(){u.point=n}function o(){a.push("Z")}var e=pn(4.5),a=[],u={point:n,lineStart:function(){u.point=t},lineEnd:i,polygonStart:function(){u.lineEnd=o},polygonEnd:function(){u.lineEnd=i,u.point=n},pointRadius:function(n){return e=pn(n),u},result:function(){if(a.length){var n=a.join("");return a=[],n}}};return u}function pn(n){return"m0,"+n+"a"+n+","+n+" 0 1,1 0,"+-2*n+"a"+n+","+n+" 0 1,1 0,"+2*n+"z"}function hn(n,t){Un+=n,Zn+=t,++Bn}function gn(){function n(n,i){var o=n-t,e=i-r,a=Math.sqrt(o*o+e*e);Dn+=a*(t+n)/2,Hn+=a*(r+i)/2,Jn+=a,hn(t=n,r=i)}var t,r;ot.point=function(i,o){ot.point=n,hn(t=i,r=o)}}function vn(){ot.point=hn}function dn(){function n(n,t){var r=n-i,e=t-o,a=Math.sqrt(r*r+e*e);Dn+=a*(i+n)/2,Hn+=a*(o+t)/2,Jn+=a,a=o*n-i*t,Kn+=a*(i+n),Qn+=a*(o+t),Vn+=3*a,hn(i=n,o=t)}var t,r,i,o;ot.point=function(e,a){ot.point=n,hn(t=i=e,r=o=a)},ot.lineEnd=function(){n(t,r)}}function Mn(n){function t(t,r){n.moveTo(t+a,r),n.arc(t,r,a,0,qn)}function r(t,r){n.moveTo(t,r),u.point=i}function i(t,r){n.lineTo(t,r)}function o(){u.point=t}function e(){n.closePath()}var a=4.5,u={point:t,lineStart:function(){u.point=r},lineEnd:o,polygonStart:function(){u.lineEnd=e},polygonEnd:function(){u.lineEnd=o,u.point=t},pointRadius:function(n){return a=n,u},result:s};return u}function yn(n){var t=W(function(t,r){return n([t*jn,r*jn])});return function(n){return nn(t(n))}}function En(n,t){return[n,Math.log(Math.tan(bn/4+t/2))]}function mn(n){var t,r=$(n),i=r.scale,o=r.translate,e=r.clipExtent;return r.scale=function(){var n=i.apply(r,arguments);return n===r?t?r.clipExtent(null):r:n},r.translate=function(){var n=o.apply(r,arguments);return n===r?t?r.clipExtent(null):r:n},r.clipExtent=function(n){var a=e.apply(r,arguments);if(a===r){if(t=null==n){var u=bn*i(),c=o();e([[c[0]-u,c[1]-u],[c[0]+u,c[1]+u]])}}else t&&(a=null);return a},r.clipExtent(null)}var Sn={version:"3.5.5"},xn=1e-6,Nn=xn*xn,bn=Math.PI,qn=2*bn,wn=bn/2,Pn=bn/180,jn=180/bn;Sn.geo={},Sn.rebind=function(n,t){for(var r,i=1,o=arguments.length;++i=0;)for(i=n[o],t=i.length;--t>=0;)r[--a]=i[t];return r};var zn=M(f,S,N,[-bn,-bn/2]);(Sn.geo.equirectangular=function(){return $(I)}).raw=I.invert=I,Sn.geo.rotation=function(n){function t(t){return t=n(t[0]*Pn,t[1]*Pn),t[0]*=jn,t[1]*=jn,t}return n=L(n[0]%360*Pn,n[1]*Pn,n.length>2?n[2]*Pn:0),t.invert=function(t){return t=n.invert(t[0]*Pn,t[1]*Pn),t[0]*=jn,t[1]*=jn,t},t},R.invert=I,Sn.geo.circle=function(){function n(){var n="function"==typeof i?i.apply(this,arguments):i,t=L(-n[0]*Pn,-n[1]*Pn,0).invert,o=[];return r(null,null,1,{point:function(n,r){o.push(n=t(n,r)),n[0]*=jn,n[1]*=jn}}),{type:"Polygon",coordinates:[o]}}var t,r,i=[0,0],o=6;return n.origin=function(t){return arguments.length?(i=t,n):i},n.angle=function(i){return arguments.length?(r=F((t=+i)*Pn,o*Pn),n):t},n.precision=function(i){return arguments.length?(r=F(t*Pn,(o=+i)*Pn),n):o},n.angle(90)},T.prototype={s:0,t:0,add:function(n){U(n,this.t,In),U(In.s,this.s,this),this.s?this.t+=In.t:this.s=In.t},reset:function(){this.s=this.t=0},valueOf:function(){return this.s}};var In=new T;Sn.geo.stream=function(n,t){n&&Rn.hasOwnProperty(n.type)?Rn[n.type](n,t):Z(n,t)};var Rn={Feature:function(n,t){Z(n.geometry,t)},FeatureCollection:function(n,t){for(var r=n.features,i=-1,o=r.length;++in?4*bn+n:n,Cn.lineStart=Cn.lineEnd=Cn.point=s}},Fn=1e9;Sn.geo.clipExtent=function(){var n,t,r,i,o,e,a={stream:function(n){return o&&(o.valid=!1),o=e(n),o.valid=!0,o},extent:function(u){return arguments.length?(e=V(n=+u[0][0],t=+u[0][1],r=+u[1][0],i=+u[1][1]),o&&(o.valid=!1,o=null),a):[[n,t],[r,i]]}};return a.extent([[0,0],[960,500]])};[].slice;Sn.geo.transform=function(n){return{stream:function(t){var r=new X(t);for(var i in n)r[i]=n[i];return r}}},X.prototype={point:function(n,t){this.stream.point(n,t)},sphere:function(){this.stream.sphere()},lineStart:function(){this.stream.lineStart()},lineEnd:function(){this.stream.lineEnd()},polygonStart:function(){this.stream.polygonStart()},polygonEnd:function(){this.stream.polygonEnd()}},Sn.geo.projection=$,Sn.geo.projectionMutator=_,(Sn.geo.conicEqualArea=function(){return tn(rn)}).raw=rn,Sn.geo.albers=function(){return Sn.geo.conicEqualArea().rotate([96,0]).center([-.6,38.7]).parallels([29.5,45.5]).scale(1070)},Sn.geo.albersUsa=function(){function n(n){var e=n[0],a=n[1];return t=null,r(e,a),t||(i(e,a),t)||o(e,a),t}var t,r,i,o,e=Sn.geo.albers(),a=Sn.geo.conicEqualArea().rotate([154,0]).center([-2,58.5]).parallels([55,65]),u=Sn.geo.conicEqualArea().rotate([157,0]).center([-3,19.9]).parallels([8,18]),c={point:function(n,r){t=[n,r]}};return n.invert=function(n){var t=e.scale(),r=e.translate(),i=(n[0]-r[0])/t,o=(n[1]-r[1])/t;return(o>=.12&&.234>o&&i>=-.425&&-.214>i?a:o>=.166&&.234>o&&i>=-.214&&-.115>i?u:e).invert(n)},n.stream=function(n){var t=e.stream(n),r=a.stream(n),i=u.stream(n);return{point:function(n,o){t.point(n,o),r.point(n,o),i.point(n,o)},sphere:function(){t.sphere(),r.sphere(),i.sphere()},lineStart:function(){t.lineStart(),r.lineStart(),i.lineStart()},lineEnd:function(){t.lineEnd(),r.lineEnd(),i.lineEnd()},polygonStart:function(){t.polygonStart(),r.polygonStart(),i.polygonStart()},polygonEnd:function(){t.polygonEnd(),r.polygonEnd(),i.polygonEnd()}}},n.precision=function(t){return arguments.length?(e.precision(t),a.precision(t),u.precision(t),n):e.precision()},n.scale=function(t){return arguments.length?(e.scale(t),a.scale(.35*t),u.scale(t),n.translate(e.translate())):e.scale()},n.translate=function(t){if(!arguments.length)return e.translate();var l=e.scale(),f=+t[0],s=+t[1];return r=e.translate(t).clipExtent([[f-.455*l,s-.238*l],[f+.455*l,s+.238*l]]).stream(c).point,i=a.translate([f-.307*l,s+.201*l]).clipExtent([[f-.425*l+xn,s+.12*l+xn],[f-.214*l-xn,s+.234*l-xn]]).stream(c).point,o=u.translate([f-.205*l,s+.212*l]).clipExtent([[f-.214*l+xn,s+.166*l+xn],[f-.115*l-xn,s+.234*l-xn]]).stream(c).point,n},n.scale(1070)},Sn.geo.bounds=function(){function n(n,t){m.push(S=[f=n,h=n]),s>t&&(s=t),t>g&&(g=t)}function t(t,r){var i=b([t*Pn,r*Pn]);if(y){var o=w(y,i),e=[o[1],-o[0],0],a=w(e,o);A(a),a=p(a);var c=t-v,l=c>0?1:-1,d=a[0]*jn*l,M=An(c)>180;if(M^(d>l*v&&l*t>d)){var E=a[1]*jn;E>g&&(g=E)}else if(d=(d+360)%360-180,M^(d>l*v&&l*t>d)){var E=-a[1]*jn;s>E&&(s=E)}else s>r&&(s=r),r>g&&(g=r);M?v>t?u(f,t)>u(f,h)&&(h=t):u(t,h)>u(f,h)&&(f=t):h>=f?(f>t&&(f=t),t>h&&(h=t)):t>v?u(f,t)>u(f,h)&&(h=t):u(t,h)>u(f,h)&&(f=t)}else n(t,r);y=i,v=t}function r(){x.point=t}function i(){S[0]=f,S[1]=h,x.point=n,y=null}function o(n,r){if(y){var i=n-v;E+=An(i)>180?i+(i>0?360:-360):i}else d=n,M=r;Cn.point(n,r),t(n,r)}function e(){Cn.lineStart()}function a(){o(d,M),Cn.lineEnd(),An(E)>xn&&(f=-(h=180)),S[0]=f,S[1]=h,y=null}function u(n,t){return(t-=n)<0?t+360:t}function c(n,t){return n[0]-t[0]}function l(n,t){return t[0]<=t[1]?t[0]<=n&&n<=t[1]:nkn?(f=-(h=180),s=-(g=90)):E>xn?g=90:-xn>E&&(s=-90),S[0]=f,S[1]=h}};return function(n){g=h=-(f=s=1/0),m=[],Sn.geo.stream(n,x);var t=m.length;if(t){m.sort(c);for(var r,i=1,o=m[0],e=[o];t>i;++i)r=m[i],l(r[0],o)||l(r[1],o)?(u(o[0],r[1])>u(o[0],o[1])&&(o[1]=r[1]),u(r[0],o[1])>u(o[0],o[1])&&(o[0]=r[0])):e.push(o=r);for(var a,r,p=-(1/0),t=e.length-1,i=0,o=e[t];t>=i;o=r,++i)r=e[i],(a=u(o[1],r[0]))>p&&(p=a,f=r[0],h=o[1])}return m=S=null,f===1/0||s===1/0?[[NaN,NaN],[NaN,NaN]]:[[f,s],[h,g]]}}(),Sn.geo.centroid=function(n){Gn=Tn=Un=Zn=Bn=Dn=Hn=Jn=Kn=Qn=Vn=0,Sn.geo.stream(n,tt);var t=Kn,r=Qn,i=Vn,o=t*t+r*r+i*i;return Nn>o&&(t=Dn,r=Hn,i=Jn,xn>Tn&&(t=Un,r=Zn,i=Bn),o=t*t+r*r+i*i,Nn>o)?[NaN,NaN]:[Math.atan2(r,t)*jn,c(i/Math.sqrt(o))*jn]};var Gn,Tn,Un,Zn,Bn,Dn,Hn,Jn,Kn,Qn,Vn,Wn,Xn,Yn,$n,_n,nt,tt={sphere:s,point:on,lineStart:an,lineEnd:un,polygonStart:function(){tt.lineStart=cn},polygonEnd:function(){tt.lineStart=an}},rt={point:s,lineStart:s,lineEnd:s,polygonStart:function(){Xn=0,rt.lineStart=ln},polygonEnd:function(){rt.lineStart=rt.lineEnd=rt.point=s,Wn+=An(Xn/2)}},it={point:fn,lineStart:s,lineEnd:s,polygonStart:s,polygonEnd:s},ot={point:hn,lineStart:gn,lineEnd:vn,polygonStart:function(){ot.lineStart=dn},polygonEnd:function(){ot.point=hn,ot.lineStart=gn,ot.lineEnd=vn}};Sn.geo.path=function(){function n(n){return n&&("function"==typeof c&&a.pointRadius(+c.apply(this,arguments)),u&&u.valid||(u=o(a)),Sn.geo.stream(n,u)),a.result()}function t(){return u=null,n}var r,i,o,a,u,c=4.5;return n.area=function(n){return Wn=0,Sn.geo.stream(n,o(rt)),Wn},n.centroid=function(n){return Un=Zn=Bn=Dn=Hn=Jn=Kn=Qn=Vn=0,Sn.geo.stream(n,o(ot)),Vn?[Kn/Vn,Qn/Vn]:Jn?[Dn/Jn,Hn/Jn]:Bn?[Un/Bn,Zn/Bn]:[NaN,NaN]},n.bounds=function(n){return _n=nt=-(Yn=$n=1/0),Sn.geo.stream(n,o(it)),[[Yn,$n],[_n,nt]]},n.projection=function(n){return arguments.length?(o=(r=n)?n.stream||yn(n):e,t()):r},n.context=function(n){return arguments.length?(a=null==(i=n)?new sn:new Mn(n),"function"!=typeof c&&a.pointRadius(c),t()):i},n.pointRadius=function(t){return arguments.length?(c="function"==typeof t?t:(a.pointRadius(+t),+t),n):c},n.projection(Sn.geo.albersUsa()).context(null)},En.invert=function(n,t){return[n,2*Math.atan(Math.exp(t))-wn]},(Sn.geo.mercator=function(){return mn(En)}).raw=En,i=Sn,o="function"==typeof i?i.call(t,r,t,n):i,!(void 0!==o&&(n.exports=o)),this.d3f=Sn}()},function(n,t){function r(n){var t={};for(var r in n)t[r]=o.copy(n[r]);return t}function i(n,t){for(var r,i=n.length,o=i-t;o<--i;)r=n[o],n[o++]=n[i],n[i]=r}var o={};o.copy=function(n){return n instanceof Array?n.map(o.copy):"string"==typeof n||"number"==typeof n?n:r(n)},o.arctans=function(n,t){var r=n/t,i=Math.sqrt(1+r*r),o=t>0?i:-i,e=1/o,a=r*e;return{cos:e,sin:a}},o.object=function(n,t){function r(t,r){r.length&&r.pop();for(var o=n[0>t?~t:t],e=0,a=o.length;a>e;++e)r.push(o[e]);0>t&&i(r,a)}function o(n){for(var t=[],i=0,o=n.length;o>i;++i)r(n[i],t);return t}function e(n){return n.map(o)}function a(n){return n=Object.create(n),n.properties=n.properties,n.coordinates=u[n.type](n.arcs),n.type=n.type,n}var u={LineString:o,MultiLineString:e,Polygon:e,MultiPolygon:function(n){return n.map(e)}};return"GeometryCollection"===t.type?(t=Object.create(t),t.geometries=t.geometries.map(a),t):a(t)},o.properties=function(n){return n.properties||{}},o.transformer=function(n){function t(n){return[n[0]*r+o,n[1]*i+e]}var r=n.scale[0],i=n.scale[1],o=n.translate[0],e=n.translate[1];return t.invert=function(n){return[(n[0]-o)/r,(n[1]-e)/i]},t},n.exports=o}]);',r.p+"2623f394d64d470ff1d8.worker.js")}},function(n,t){var r=window.URL||window.webkitURL;n.exports=function(n,t){try{try{var i;try{var o=window.BlobBuilder||window.WebKitBlobBuilder||window.MozBlobBuilder||window.MSBlobBuilder;i=new o,i.append(n),i=i.getBlob()}catch(e){i=new Blob([n])}return new Worker(r.createObjectURL(i))}catch(e){return new Worker("data:application/javascript,"+encodeURIComponent(n))}}catch(e){return new Worker(t)}}},function(n,t,r){function i(){}function o(n){function t(n){return h[n.properties[g]]}var r=n;if("undefined"!=typeof n.data&&(r=n.data),"carto"===r["do"]){var i=r.geo,o=i.topology,u=i.geometries,c=i.path,l=i.translation,f=i.projection.name,s=i.projection.scaling,p=i.projection.center,l=i.projection.translation,h=r.values,g=r.featureProperty,v=r.task;o=a.copy(o);var d=e.geo[f]().scale(s);null!=p&&d.center(p),null!=l&&d.translate(l);for(var M,y,m,E,S,x=a.transformer(o.transform),b=o.arcs.length,N=0,w=new Array(b);b>N;){for(M=0,y=0,m=o.arcs[N].length,E=0,S=new Array(m);m>E;)o.arcs[N][E][0]=M+=o.arcs[N][E][0],o.arcs[N][E][1]=y+=o.arcs[N][E][1],S[E]=d(x(o.arcs[N][E])),E++;w[N++]=S}for(var c=e.geo.path().projection(null),q=a.object(w,{type:"GeometryCollection",geometries:u}).geometries.map(function(n){return{type:"Feature",id:n.id,properties:a.properties.call(null,n,o),geometry:n}}),P=q.map(t),j=P.reduce(function(n,t){return n+t}),A=8,R=0;R++N;){for(m=w[N].length,E=0;m>E;){for(J=[0,0],_=L.length,H=0;_>H;){if(Q=L[H].centroid,Z=L[H].mass,W=L[H].radius,V=W*W,K=w[N][E][0]-Q[0],Y=w[N][E][1]-Q[1],$=K*K+Y*Y,X=Math.sqrt($),XW?Z*W/X:Z*($/V)*(4-3*X/W);var rn=a.arctans(Y,K);J[0]+=nn*rn.cos,J[1]+=nn*rn.sin}H++}w[N][E][0]+=J[0]*tn,w[N][E][1]+=J[1]*tn,E++}N++}if(1>=T)break}var on={done:"processing",features:q,task:v};"undefined"!=typeof self?self.postMessage(on):this.onmessage(on)}}var e=r(4),a=r(5);"undefined"!=typeof onmessage?onmessage=o:(i.prototype.postMessage=o,i.prototype.terminate=function(){},n.exports=i)},function(n,t,r){var i,o;!function(){function e(n){return n}function a(n,t,r){return(t[0]-n[0])*(r[1]-n[1])-(t[1]-n[1])*(r[0]-n[0]); 2 | }function u(n){return n>1?0:-1>n?Nn:Math.acos(n)}function c(n){return n>1?qn:-1>n?-qn:Math.asin(n)}function l(n,t,r){return function(){var i=r.apply(t,arguments);return i===t?n:i}}function f(){return!0}function s(){}function p(n){return[Math.atan2(n[1],n[0]),c(n[2])]}function h(n,t){return An(n[0]-t[0])u;++u)o.point((r=n[u])[0],r[1]);return void o.lineEnd()}var c=new d(r,n,null,!0),l=new d(r,null,c,!1);c.o=l,e.push(c),a.push(l),c=new d(i,n,null,!1),l=new d(i,null,c,!0),c.o=l,e.push(c),a.push(l)}}),a.sort(t),v(e),v(a),e.length){for(var u=0,c=r,l=a.length;l>u;++u)a[u].e=c=!c;for(var f,s,p=e[0];;){for(var g=p,M=!0;g.v;)if((g=g.n)===p)return;f=g.z,o.lineStart();do{if(g.v=g.o.v=!0,g.e){if(M)for(var u=0,l=f.length;l>u;++u)o.point((s=f[u])[0],s[1]);else i(g.x,g.n.x,1,o);g=g.n}else{if(M){f=g.p.z;for(var u=f.length-1;u>=0;--u)o.point((s=f[u])[0],s[1])}else i(g.x,g.p.x,-1,o);g=g.p}g=g.o,f=g.z,M=!M}while(!g.v);o.lineEnd()}}}function v(n){if(t=n.length){for(var t,r,i=0,o=n[0];++i0){for(w||(e.polygonStart(),w=!0),e.lineStart();++a1&&2&t&&r.push(r.pop().concat(r.shift())),h.push(r.filter(y))}var h,v,d,M=t(e),S=o.invert(i[0],i[1]),x={point:a,lineStart:c,lineEnd:l,polygonStart:function(){x.point=f,x.lineStart=s,x.lineEnd=p,h=[],v=[]},polygonEnd:function(){x.point=a,x.lineStart=c,x.lineEnd=l,h=Sn.merge(h);var n=J(S,v);h.length?(w||(e.polygonStart(),w=!0),g(h,E,n,r,e)):n&&(w||(e.polygonStart(),w=!0),e.lineStart(),r(null,null,1,e),e.lineEnd()),w&&(e.polygonEnd(),w=!1),h=v=null},sphere:function(){e.polygonStart(),e.lineStart(),r(null,null,1,e),e.lineEnd(),e.polygonEnd()}},b=m(),N=t(b),w=!1;return x}}function y(n){return n.length>1}function m(){var n,t=[];return{lineStart:function(){t.push(n=[])},point:function(t,r){n.push([t,r])},lineEnd:s,buffer:function(){var r=t;return t=[],n=null,r},rejoin:function(){t.length>1&&t.push(t.pop().concat(t.shift()))}}}function E(n,t){return((n=n.x)[0]<0?n[1]-qn-xn:qn-n[1])-((t=t.x)[0]<0?t[1]-qn-xn:qn-t[1])}function S(n){var t,r=NaN,i=NaN,o=NaN;return{lineStart:function(){n.lineStart(),t=1},point:function(e,a){var u=e>0?Nn:-Nn,c=An(e-r);An(c-Nn)0?qn:-qn),n.point(o,i),n.lineEnd(),n.lineStart(),n.point(u,i),n.point(e,i),t=0):o!==u&&c>=Nn&&(An(r-o)xn?Math.atan((Math.sin(t)*(e=Math.cos(i))*Math.sin(r)-Math.sin(i)*(o=Math.cos(t))*Math.sin(n))/(o*e*a)):(t+i)/2}function b(n,t,r,i){var o;if(null==n)o=r*qn,i.point(-Nn,o),i.point(0,o),i.point(Nn,o),i.point(Nn,0),i.point(Nn,-o),i.point(0,-o),i.point(-Nn,-o),i.point(-Nn,0),i.point(-Nn,o);else if(An(n[0]-t[0])>xn){var e=n[0]Nn?n-wn:-Nn>n?n+wn:n,t]}function k(n,t,r){return n?t||r?R(C(n),B(t,r)):C(n):t||r?B(t,r):O}function z(n){return function(t,r){return t+=n,[t>Nn?t-wn:-Nn>t?t+wn:t,r]}}function C(n){var t=z(n);return t.invert=z(-n),t}function B(n,t){function r(n,t){var r=Math.cos(t),u=Math.cos(n)*r,l=Math.sin(n)*r,f=Math.sin(t),s=f*i+u*o;return[Math.atan2(l*e-s*a,u*i-f*o),c(s*e+l*a)]}var i=Math.cos(n),o=Math.sin(n),e=Math.cos(t),a=Math.sin(t);return r.invert=function(n,t){var r=Math.cos(t),u=Math.cos(n)*r,l=Math.sin(n)*r,f=Math.sin(t),s=f*e-l*a;return[Math.atan2(l*e+f*a,u*i+s*o),c(s*i-u*o)]},r}function L(n,t){var r=Math.cos(n),i=Math.sin(n);return function(o,e,a,u){var c=a*t;null!=o?(o=F(r,o),e=F(r,e),(a>0?e>o:o>e)&&(o+=a*wn)):(o=n+a*wn,e=n-.5*c);for(var l,f=o;a>0?f>e:e>f;f-=c)u.point((l=p([r,-i*Math.cos(f),-i*Math.sin(f)]))[0],l[1])}}function F(n,t){var r=N(t);r[0]-=n,A(r);var i=u(-r[1]);return((-r[2]<0?-i:i)+2*Math.PI-xn)%(2*Math.PI)}function G(){}function U(n,t,r){var i=r.s=n+t,o=i-n,e=i-o;r.t=n-e+(t-o)}function D(n,t){n&&kn.hasOwnProperty(n.type)&&kn[n.type](n,t)}function W(n,t,r){var i,o=-1,e=n.length-r;for(t.lineStart();++o=0?1:-1,u=a*r,c=Math.cos(t),l=Math.sin(t),f=e*l,s=o*c+f*Math.cos(u),p=f*a*Math.sin(u);Cn.add(Math.atan2(p,s)),i=n,o=c,e=l}var t,r,i,o,e;Bn.point=function(a,u){Bn.point=n,i=(t=a)*Pn,o=Math.cos(u=(r=u)*Pn/2+Nn/4),e=Math.sin(u)},Bn.lineEnd=function(){n(t,r)}}function J(n,t){var r=n[0],i=n[1],o=[Math.sin(r),-Math.cos(r),0],e=0,a=0;Cn.reset();for(var u=0,l=t.length;l>u;++u){var f=t[u],s=f.length;if(s)for(var p=f[0],h=p[0],g=p[1]/2+Nn/4,v=Math.sin(g),d=Math.cos(g),M=1;;){M===s&&(M=0),n=f[M];var y=n[0],m=n[1]/2+Nn/4,E=Math.sin(m),S=Math.cos(m),x=y-h,b=x>=0?1:-1,w=b*x,P=w>Nn,j=v*E;if(Cn.add(Math.atan2(j*b*Math.sin(w),d*S+j*Math.cos(w))),e+=P?x+b*wn:x,P^h>=r^y>=r){var R=q(N(p),N(n));A(R);var I=q(o,R);A(I);var O=(P^x>=0?-1:1)*c(I[2]);(i>O||i===O&&(R[0]||R[1]))&&(a+=P^x>=0?1:-1)}if(!M++)break;h=y,v=E,d=S,p=n}}return(-xn>e||xn>e&&0>Cn)^1&a}function _(n){function t(n,t){return Math.cos(n)*Math.cos(t)>e}function r(n){var r,e,c,l,f;return{lineStart:function(){l=c=!1,f=1},point:function(s,p){var g,v=[s,p],d=t(s,p),M=a?d?0:o(s,p):d?o(s+(0>s?Nn:-Nn),p):0;if(!r&&(l=c=d)&&n.lineStart(),d!==c&&(g=i(r,v),(h(r,g)||h(v,g))&&(v[0]+=xn,v[1]+=xn,d=t(v[0],v[1]))),d!==c)f=0,d?(n.lineStart(),g=i(v,r),n.point(g[0],g[1])):(g=i(r,v),n.point(g[0],g[1]),n.lineEnd()),r=g;else if(u&&r&&a^d){var y;M&e||!(y=i(v,r,!0))||(f=0,a?(n.lineStart(),n.point(y[0][0],y[0][1]),n.point(y[1][0],y[1][1]),n.lineEnd()):(n.point(y[1][0],y[1][1]),n.lineEnd(),n.lineStart(),n.point(y[0][0],y[0][1])))}!d||r&&h(r,v)||n.point(v[0],v[1]),r=v,c=d,e=M},lineEnd:function(){c&&n.lineEnd(),r=null},clean:function(){return f|(l&&c)<<1}}}function i(n,t,r){var i=N(n),o=N(t),a=[1,0,0],u=q(i,o),c=w(u,u),l=u[0],f=c-l*l;if(!f)return!r&&n;var s=e*c/f,h=-e*l/f,g=q(a,u),v=j(a,s),d=j(u,h);P(v,d);var M=g,y=w(v,M),m=w(M,M),E=y*y-m*(w(v,v)-1);if(!(0>E)){var S=Math.sqrt(E),x=j(M,(-y-S)/m);if(P(x,v),x=p(x),!r)return x;var b,A=n[0],R=t[0],I=n[1],O=t[1];A>R&&(b=A,A=R,R=b);var k=R-A,z=An(k-Nn)k;if(!z&&I>O&&(b=I,I=O,O=b),C?z?I+O>0^x[1]<(An(x[0]-A)Nn^(A<=x[0]&&x[0]<=R)){var B=j(M,(-y+S)/m);return P(B,v),[x,p(B)]}}}function o(t,r){var i=a?n:Nn-n,o=0;return-i>t?o|=1:t>i&&(o|=2),-i>r?o|=4:r>i&&(o|=8),o}var e=Math.cos(n),a=e>0,u=An(e)>xn,c=L(n,6*Pn);return M(t,r,c,a?[0,-n]:[-Nn,n-Nn])}function H(n,t,r,i){return function(o){var e,a=o.a,u=o.b,c=a.x,l=a.y,f=u.x,s=u.y,p=0,h=1,g=f-c,v=s-l;if(e=n-c,g||!(e>0)){if(e/=g,0>g){if(p>e)return;h>e&&(h=e)}else if(g>0){if(e>h)return;e>p&&(p=e)}if(e=r-c,g||!(0>e)){if(e/=g,0>g){if(e>h)return;e>p&&(p=e)}else if(g>0){if(p>e)return;h>e&&(h=e)}if(e=t-l,v||!(e>0)){if(e/=v,0>v){if(p>e)return;h>e&&(h=e)}else if(v>0){if(e>h)return;e>p&&(p=e)}if(e=i-l,v||!(0>e)){if(e/=v,0>v){if(e>h)return;e>p&&(p=e)}else if(v>0){if(p>e)return;h>e&&(h=e)}return p>0&&(o.a={x:c+p*g,y:l+p*v}),1>h&&(o.b={x:c+h*g,y:l+h*v}),o}}}}}}function Q(n,t,r,i){function o(i,o){return An(i[0]-n)0?0:3:An(i[0]-r)0?2:1:An(i[1]-t)0?1:0:o>0?3:2}function e(n,t){return u(n.x,t.x)}function u(n,t){var r=o(n,1),i=o(t,1);return r!==i?r-i:0===r?t[1]-n[1]:1===r?n[0]-t[0]:2===r?n[1]-t[1]:t[0]-n[0]}return function(c){function l(n){for(var t=0,r=y.length,i=n[1],o=0;r>o;++o)for(var e,u=1,c=y[o],l=c.length,f=c[0];l>u;++u)e=c[u],f[1]<=i?e[1]>i&&a(f,e,n)>0&&++t:e[1]<=i&&a(f,e,n)<0&&--t,f=e;return 0!==t}function f(e,a,c,l){var f=0,s=0;if(null==e||(f=o(e,c))!==(s=o(a,c))||u(e,a)<0^c>0){do l.point(0===f||3===f?n:r,f>1?i:t);while((f=(f+c+4)%4)!==s)}else l.point(a[0],a[1])}function s(o,e){return o>=n&&r>=o&&e>=t&&i>=e}function p(n,t){s(n,t)&&c.point(n,t)}function h(){O.point=d,y&&y.push(E=[]),P=!0,q=!1,N=w=NaN}function v(){M&&(d(S,x),b&&q&&R.rejoin(),M.push(R.buffer())),O.point=p,q&&c.lineEnd()}function d(n,t){n=Math.max(-Ln,Math.min(Ln,n)),t=Math.max(-Ln,Math.min(Ln,t));var r=s(n,t);if(y&&E.push([n,t]),P)S=n,x=t,b=r,P=!1,r&&(c.lineStart(),c.point(n,t));else if(r&&q)c.point(n,t);else{var i={a:{x:N,y:w},b:{x:n,y:t}};I(i)?(q||(c.lineStart(),c.point(i.a.x,i.a.y)),c.point(i.b.x,i.b.y),r||c.lineEnd(),j=!1):r&&(c.lineStart(),c.point(n,t),j=!1)}N=n,w=t,q=r}var M,y,E,S,x,b,N,w,q,P,j,A=c,R=m(),I=H(n,t,r,i),O={point:p,lineStart:h,lineEnd:v,polygonStart:function(){c=R,M=[],y=[],j=!0},polygonEnd:function(){c=A,M=Sn.merge(M);var t=l([n,i]),r=j&&t,o=M.length;(r||o)&&(c.polygonStart(),r&&(c.lineStart(),f(null,null,1,c),c.lineEnd()),o&&g(M,e,t,f,c),c.polygonEnd()),M=y=E=null}};return O}}function V(n){function t(n){return(u?i:r)(n)}function r(t){return Y(t,function(r,i){r=n(r,i),t.point(r[0],r[1])})}function i(t){function r(r,i){r=n(r,i),t.point(r[0],r[1])}function i(){m=NaN,w.point=e,t.lineStart()}function e(r,i){var e=N([r,i]),a=n(r,i);o(m,E,y,S,x,b,m=a[0],E=a[1],y=r,S=e[0],x=e[1],b=e[2],u,t),t.point(m,E)}function a(){w.point=r,t.lineEnd()}function c(){i(),w.point=l,w.lineEnd=f}function l(n,t){e(s=n,p=t),h=m,g=E,v=S,d=x,M=b,w.point=e}function f(){o(m,E,y,S,x,b,h,g,s,v,d,M,u,t),w.lineEnd=a,a()}var s,p,h,g,v,d,M,y,m,E,S,x,b,w={point:r,lineStart:i,lineEnd:a,polygonStart:function(){t.polygonStart(),w.lineStart=c},polygonEnd:function(){t.polygonEnd(),w.lineStart=i}};return w}function o(t,r,i,u,c,l,f,s,p,h,g,v,d,M){var y=f-t,m=s-r,E=y*y+m*m;if(E>4*e&&d--){var S=u+h,x=c+g,b=l+v,N=Math.sqrt(S*S+x*x+b*b),w=Math.asin(b/=N),q=An(An(b)-1)e||An((y*R+m*I)/E-.5)>.3||a>u*h+c*g+l*v)&&(o(t,r,i,u,c,l,j,A,q,S/=N,x/=N,b,d,M),M.point(j,A),o(j,A,q,S,x,b,f,s,p,h,g,v,d,M))}}var e=.5,a=Math.cos(30*Pn),u=16;return t.precision=function(n){return arguments.length?(u=(e=n*n)>0&&16,t):Math.sqrt(e)},t}function K(n){this.stream=n}function Y(n,t){return{point:t,sphere:function(){n.sphere()},lineStart:function(){n.lineStart()},lineEnd:function(){n.lineEnd()},polygonStart:function(){n.polygonStart()},polygonEnd:function(){n.polygonEnd()}}}function $(n){return X(function(){return n})()}function X(n){function t(n){return n=c(n[0]*Pn,n[1]*Pn),[n[0]*h+l,f-n[1]*h]}function r(n){return n=c.invert((n[0]-l)/h,(f-n[1])/h),n&&[n[0]*jn,n[1]*jn]}function i(){c=R(u=k(y,m,E),a);var n=a(d,M);return l=g-n[0]*h,f=v+n[1]*h,o()}function o(){return s&&(s.valid=!1,s=null),t}var a,u,c,l,f,s,p=V(function(n,t){return n=a(n,t),[n[0]*h+l,f-n[1]*h]}),h=150,g=480,v=250,d=0,M=0,y=0,m=0,E=0,S=Rn,x=e,b=null,N=null;return t.stream=function(n){return s&&(s.valid=!1),s=nn(S(u,p(x(n)))),s.valid=!0,s},t.clipAngle=function(n){return arguments.length?(S=null==n?(b=n,Rn):_((b=+n)*Pn),o()):b},t.clipExtent=function(n){return arguments.length?(N=n,x=n?Q(n[0][0],n[0][1],n[1][0],n[1][1]):e,o()):N},t.scale=function(n){return arguments.length?(h=+n,i()):h},t.translate=function(n){return arguments.length?(g=+n[0],v=+n[1],i()):[g,v]},t.center=function(n){return arguments.length?(d=n[0]%360*Pn,M=n[1]%360*Pn,i()):[d*jn,M*jn]},t.rotate=function(n){return arguments.length?(y=n[0]%360*Pn,m=n[1]%360*Pn,E=n.length>2?n[2]%360*Pn:0,i()):[y*jn,m*jn,E*jn]},Sn.rebind(t,p,"precision"),function(){return a=n.apply(this,arguments),t.invert=a.invert&&r,i()}}function nn(n){return Y(n,function(t,r){n.point(t*Pn,r*Pn)})}function tn(n){var t=0,r=Nn/3,i=X(n),o=i(t,r);return o.parallels=function(n){return arguments.length?i(t=n[0]*Nn/180,r=n[1]*Nn/180):[t/Nn*180,r/Nn*180]},o}function rn(n,t){function r(n,t){var r=Math.sqrt(e-2*o*Math.sin(t))/o;return[r*Math.sin(n*=o),a-r*Math.cos(n)]}var i=Math.sin(n),o=(i+Math.sin(t))/2,e=1+i*(2*o-i),a=Math.sqrt(e)/o;return r.invert=function(n,t){var r=a-t;return[Math.atan2(n,r)/o,c((e-(n*n+r*r)*o*o)/(2*o))]},r}function on(n,t){n*=Pn;var r=Math.cos(t*=Pn);en(r*Math.cos(n),r*Math.sin(n),Math.sin(t))}function en(n,t,r){++Fn,Un+=(n-Un)/Fn,Dn+=(t-Dn)/Fn,Wn+=(r-Wn)/Fn}function an(){function n(n,o){n*=Pn;var e=Math.cos(o*=Pn),a=e*Math.cos(n),u=e*Math.sin(n),c=Math.sin(o),l=Math.atan2(Math.sqrt((l=r*c-i*u)*l+(l=i*a-t*c)*l+(l=t*u-r*a)*l),t*a+r*u+i*c);Gn+=l,Zn+=l*(t+(t=a)),Tn+=l*(r+(r=u)),Jn+=l*(i+(i=c)),en(t,r,i)}var t,r,i;tt.point=function(o,e){o*=Pn;var a=Math.cos(e*=Pn);t=a*Math.cos(o),r=a*Math.sin(o),i=Math.sin(e),tt.point=n,en(t,r,i)}}function un(){tt.point=on}function cn(){function n(n,t){n*=Pn;var r=Math.cos(t*=Pn),a=r*Math.cos(n),c=r*Math.sin(n),l=Math.sin(t),f=o*l-e*c,s=e*a-i*l,p=i*c-o*a,h=Math.sqrt(f*f+s*s+p*p),g=i*a+o*c+e*l,v=h&&-u(g)/h,d=Math.atan2(h,g);_n+=v*f,Hn+=v*s,Qn+=v*p,Gn+=d,Zn+=d*(i+(i=a)),Tn+=d*(o+(o=c)),Jn+=d*(e+(e=l)),en(i,o,e)}var t,r,i,o,e;tt.point=function(a,u){t=a,r=u,tt.point=n,a*=Pn;var c=Math.cos(u*=Pn);i=c*Math.cos(a),o=c*Math.sin(a),e=Math.sin(u),en(i,o,e)},tt.lineEnd=function(){n(t,r),tt.lineEnd=un,tt.point=on}}function ln(){function n(n,t){Kn+=o*n-i*t,i=n,o=t}var t,r,i,o;rt.point=function(e,a){rt.point=n,t=i=e,r=o=a},rt.lineEnd=function(){n(t,r)}}function fn(n,t){Yn>n&&(Yn=n),n>Xn&&(Xn=n),$n>t&&($n=t),t>nt&&(nt=t)}function sn(){function n(n,t){a.push("M",n,",",t,e)}function t(n,t){a.push("M",n,",",t),u.point=r}function r(n,t){a.push("L",n,",",t)}function i(){u.point=n}function o(){a.push("Z")}var e=pn(4.5),a=[],u={point:n,lineStart:function(){u.point=t},lineEnd:i,polygonStart:function(){u.lineEnd=o},polygonEnd:function(){u.lineEnd=i,u.point=n},pointRadius:function(n){return e=pn(n),u},result:function(){if(a.length){var n=a.join("");return a=[],n}}};return u}function pn(n){return"m0,"+n+"a"+n+","+n+" 0 1,1 0,"+-2*n+"a"+n+","+n+" 0 1,1 0,"+2*n+"z"}function hn(n,t){Un+=n,Dn+=t,++Wn}function gn(){function n(n,i){var o=n-t,e=i-r,a=Math.sqrt(o*o+e*e);Zn+=a*(t+n)/2,Tn+=a*(r+i)/2,Jn+=a,hn(t=n,r=i)}var t,r;ot.point=function(i,o){ot.point=n,hn(t=i,r=o)}}function vn(){ot.point=hn}function dn(){function n(n,t){var r=n-i,e=t-o,a=Math.sqrt(r*r+e*e);Zn+=a*(i+n)/2,Tn+=a*(o+t)/2,Jn+=a,a=o*n-i*t,_n+=a*(i+n),Hn+=a*(o+t),Qn+=3*a,hn(i=n,o=t)}var t,r,i,o;ot.point=function(e,a){ot.point=n,hn(t=i=e,r=o=a)},ot.lineEnd=function(){n(t,r)}}function Mn(n){function t(t,r){n.moveTo(t+a,r),n.arc(t,r,a,0,wn)}function r(t,r){n.moveTo(t,r),u.point=i}function i(t,r){n.lineTo(t,r)}function o(){u.point=t}function e(){n.closePath()}var a=4.5,u={point:t,lineStart:function(){u.point=r},lineEnd:o,polygonStart:function(){u.lineEnd=e},polygonEnd:function(){u.lineEnd=o,u.point=t},pointRadius:function(n){return a=n,u},result:s};return u}function yn(n){var t=V(function(t,r){return n([t*jn,r*jn])});return function(n){return nn(t(n))}}function mn(n,t){return[n,Math.log(Math.tan(Nn/4+t/2))]}function En(n){var t,r=$(n),i=r.scale,o=r.translate,e=r.clipExtent;return r.scale=function(){var n=i.apply(r,arguments);return n===r?t?r.clipExtent(null):r:n},r.translate=function(){var n=o.apply(r,arguments);return n===r?t?r.clipExtent(null):r:n},r.clipExtent=function(n){var a=e.apply(r,arguments);if(a===r){if(t=null==n){var u=Nn*i(),c=o();e([[c[0]-u,c[1]-u],[c[0]+u,c[1]+u]])}}else t&&(a=null);return a},r.clipExtent(null)}var Sn={version:"3.5.5"},xn=1e-6,bn=xn*xn,Nn=Math.PI,wn=2*Nn,qn=Nn/2,Pn=Nn/180,jn=180/Nn;Sn.geo={},Sn.rebind=function(n,t){for(var r,i=1,o=arguments.length;++i=0;)for(i=n[o],t=i.length;--t>=0;)r[--a]=i[t];return r};var Rn=M(f,S,b,[-Nn,-Nn/2]);(Sn.geo.equirectangular=function(){return $(I)}).raw=I.invert=I,Sn.geo.rotation=function(n){function t(t){return t=n(t[0]*Pn,t[1]*Pn),t[0]*=jn,t[1]*=jn,t}return n=k(n[0]%360*Pn,n[1]*Pn,n.length>2?n[2]*Pn:0),t.invert=function(t){return t=n.invert(t[0]*Pn,t[1]*Pn),t[0]*=jn,t[1]*=jn,t},t},O.invert=I,Sn.geo.circle=function(){function n(){var n="function"==typeof i?i.apply(this,arguments):i,t=k(-n[0]*Pn,-n[1]*Pn,0).invert,o=[];return r(null,null,1,{point:function(n,r){o.push(n=t(n,r)),n[0]*=jn,n[1]*=jn}}),{type:"Polygon",coordinates:[o]}}var t,r,i=[0,0],o=6;return n.origin=function(t){return arguments.length?(i=t,n):i},n.angle=function(i){return arguments.length?(r=L((t=+i)*Pn,o*Pn),n):t},n.precision=function(i){return arguments.length?(r=L(t*Pn,(o=+i)*Pn),n):o},n.angle(90)},G.prototype={s:0,t:0,add:function(n){U(n,this.t,In),U(In.s,this.s,this),this.s?this.t+=In.t:this.s=In.t},reset:function(){this.s=this.t=0},valueOf:function(){return this.s}};var In=new G;Sn.geo.stream=function(n,t){n&&On.hasOwnProperty(n.type)?On[n.type](n,t):D(n,t)};var On={Feature:function(n,t){D(n.geometry,t)},FeatureCollection:function(n,t){for(var r=n.features,i=-1,o=r.length;++in?4*Nn+n:n,Bn.lineStart=Bn.lineEnd=Bn.point=s}},Ln=1e9;Sn.geo.clipExtent=function(){var n,t,r,i,o,e,a={stream:function(n){return o&&(o.valid=!1),o=e(n),o.valid=!0,o},extent:function(u){return arguments.length?(e=Q(n=+u[0][0],t=+u[0][1],r=+u[1][0],i=+u[1][1]),o&&(o.valid=!1,o=null),a):[[n,t],[r,i]]}};return a.extent([[0,0],[960,500]])};[].slice;Sn.geo.transform=function(n){return{stream:function(t){var r=new K(t);for(var i in n)r[i]=n[i];return r}}},K.prototype={point:function(n,t){this.stream.point(n,t)},sphere:function(){this.stream.sphere()},lineStart:function(){this.stream.lineStart()},lineEnd:function(){this.stream.lineEnd()},polygonStart:function(){this.stream.polygonStart()},polygonEnd:function(){this.stream.polygonEnd()}},Sn.geo.projection=$,Sn.geo.projectionMutator=X,(Sn.geo.conicEqualArea=function(){return tn(rn)}).raw=rn,Sn.geo.albers=function(){return Sn.geo.conicEqualArea().rotate([96,0]).center([-.6,38.7]).parallels([29.5,45.5]).scale(1070)},Sn.geo.albersUsa=function(){function n(n){var e=n[0],a=n[1];return t=null,r(e,a),t||(i(e,a),t)||o(e,a),t}var t,r,i,o,e=Sn.geo.albers(),a=Sn.geo.conicEqualArea().rotate([154,0]).center([-2,58.5]).parallels([55,65]),u=Sn.geo.conicEqualArea().rotate([157,0]).center([-3,19.9]).parallels([8,18]),c={point:function(n,r){t=[n,r]}};return n.invert=function(n){var t=e.scale(),r=e.translate(),i=(n[0]-r[0])/t,o=(n[1]-r[1])/t;return(o>=.12&&.234>o&&i>=-.425&&-.214>i?a:o>=.166&&.234>o&&i>=-.214&&-.115>i?u:e).invert(n)},n.stream=function(n){var t=e.stream(n),r=a.stream(n),i=u.stream(n);return{point:function(n,o){t.point(n,o),r.point(n,o),i.point(n,o)},sphere:function(){t.sphere(),r.sphere(),i.sphere()},lineStart:function(){t.lineStart(),r.lineStart(),i.lineStart()},lineEnd:function(){t.lineEnd(),r.lineEnd(),i.lineEnd()},polygonStart:function(){t.polygonStart(),r.polygonStart(),i.polygonStart()},polygonEnd:function(){t.polygonEnd(),r.polygonEnd(),i.polygonEnd()}}},n.precision=function(t){return arguments.length?(e.precision(t),a.precision(t),u.precision(t),n):e.precision()},n.scale=function(t){return arguments.length?(e.scale(t),a.scale(.35*t),u.scale(t),n.translate(e.translate())):e.scale()},n.translate=function(t){if(!arguments.length)return e.translate();var l=e.scale(),f=+t[0],s=+t[1];return r=e.translate(t).clipExtent([[f-.455*l,s-.238*l],[f+.455*l,s+.238*l]]).stream(c).point,i=a.translate([f-.307*l,s+.201*l]).clipExtent([[f-.425*l+xn,s+.12*l+xn],[f-.214*l-xn,s+.234*l-xn]]).stream(c).point,o=u.translate([f-.205*l,s+.212*l]).clipExtent([[f-.214*l+xn,s+.166*l+xn],[f-.115*l-xn,s+.234*l-xn]]).stream(c).point,n},n.scale(1070)},Sn.geo.bounds=function(){function n(n,t){E.push(S=[f=n,h=n]),s>t&&(s=t),t>g&&(g=t)}function t(t,r){var i=N([t*Pn,r*Pn]);if(y){var o=q(y,i),e=[o[1],-o[0],0],a=q(e,o);A(a),a=p(a);var c=t-v,l=c>0?1:-1,d=a[0]*jn*l,M=An(c)>180;if(M^(d>l*v&&l*t>d)){var m=a[1]*jn;m>g&&(g=m)}else if(d=(d+360)%360-180,M^(d>l*v&&l*t>d)){var m=-a[1]*jn;s>m&&(s=m)}else s>r&&(s=r),r>g&&(g=r);M?v>t?u(f,t)>u(f,h)&&(h=t):u(t,h)>u(f,h)&&(f=t):h>=f?(f>t&&(f=t),t>h&&(h=t)):t>v?u(f,t)>u(f,h)&&(h=t):u(t,h)>u(f,h)&&(f=t)}else n(t,r);y=i,v=t}function r(){x.point=t}function i(){S[0]=f,S[1]=h,x.point=n,y=null}function o(n,r){if(y){var i=n-v;m+=An(i)>180?i+(i>0?360:-360):i}else d=n,M=r;Bn.point(n,r),t(n,r)}function e(){Bn.lineStart()}function a(){o(d,M),Bn.lineEnd(),An(m)>xn&&(f=-(h=180)),S[0]=f,S[1]=h,y=null}function u(n,t){return(t-=n)<0?t+360:t}function c(n,t){return n[0]-t[0]}function l(n,t){return t[0]<=t[1]?t[0]<=n&&n<=t[1]:nCn?(f=-(h=180),s=-(g=90)):m>xn?g=90:-xn>m&&(s=-90),S[0]=f,S[1]=h}};return function(n){g=h=-(f=s=1/0),E=[],Sn.geo.stream(n,x);var t=E.length;if(t){E.sort(c);for(var r,i=1,o=E[0],e=[o];t>i;++i)r=E[i],l(r[0],o)||l(r[1],o)?(u(o[0],r[1])>u(o[0],o[1])&&(o[1]=r[1]),u(r[0],o[1])>u(o[0],o[1])&&(o[0]=r[0])):e.push(o=r);for(var a,r,p=-(1/0),t=e.length-1,i=0,o=e[t];t>=i;o=r,++i)r=e[i],(a=u(o[1],r[0]))>p&&(p=a,f=r[0],h=o[1])}return E=S=null,f===1/0||s===1/0?[[NaN,NaN],[NaN,NaN]]:[[f,s],[h,g]]}}(),Sn.geo.centroid=function(n){Fn=Gn=Un=Dn=Wn=Zn=Tn=Jn=_n=Hn=Qn=0,Sn.geo.stream(n,tt);var t=_n,r=Hn,i=Qn,o=t*t+r*r+i*i;return bn>o&&(t=Zn,r=Tn,i=Jn,xn>Gn&&(t=Un,r=Dn,i=Wn),o=t*t+r*r+i*i,bn>o)?[NaN,NaN]:[Math.atan2(r,t)*jn,c(i/Math.sqrt(o))*jn]};var Fn,Gn,Un,Dn,Wn,Zn,Tn,Jn,_n,Hn,Qn,Vn,Kn,Yn,$n,Xn,nt,tt={sphere:s,point:on,lineStart:an,lineEnd:un,polygonStart:function(){tt.lineStart=cn},polygonEnd:function(){tt.lineStart=an}},rt={point:s,lineStart:s,lineEnd:s,polygonStart:function(){Kn=0,rt.lineStart=ln},polygonEnd:function(){rt.lineStart=rt.lineEnd=rt.point=s,Vn+=An(Kn/2)}},it={point:fn,lineStart:s,lineEnd:s,polygonStart:s,polygonEnd:s},ot={point:hn,lineStart:gn,lineEnd:vn,polygonStart:function(){ot.lineStart=dn},polygonEnd:function(){ot.point=hn,ot.lineStart=gn,ot.lineEnd=vn}};Sn.geo.path=function(){function n(n){return n&&("function"==typeof c&&a.pointRadius(+c.apply(this,arguments)),u&&u.valid||(u=o(a)),Sn.geo.stream(n,u)),a.result()}function t(){return u=null,n}var r,i,o,a,u,c=4.5;return n.area=function(n){return Vn=0,Sn.geo.stream(n,o(rt)),Vn},n.centroid=function(n){return Un=Dn=Wn=Zn=Tn=Jn=_n=Hn=Qn=0,Sn.geo.stream(n,o(ot)),Qn?[_n/Qn,Hn/Qn]:Jn?[Zn/Jn,Tn/Jn]:Wn?[Un/Wn,Dn/Wn]:[NaN,NaN]},n.bounds=function(n){return Xn=nt=-(Yn=$n=1/0),Sn.geo.stream(n,o(it)),[[Yn,$n],[Xn,nt]]},n.projection=function(n){return arguments.length?(o=(r=n)?n.stream||yn(n):e,t()):r},n.context=function(n){return arguments.length?(a=null==(i=n)?new sn:new Mn(n),"function"!=typeof c&&a.pointRadius(c),t()):i},n.pointRadius=function(t){return arguments.length?(c="function"==typeof t?t:(a.pointRadius(+t),+t),n):c},n.projection(Sn.geo.albersUsa()).context(null)},mn.invert=function(n,t){return[n,2*Math.atan(Math.exp(t))-qn]},(Sn.geo.mercator=function(){return En(mn)}).raw=mn,i=Sn,o="function"==typeof i?i.call(t,r,t,n):i,!(void 0!==o&&(n.exports=o)),this.d3f=Sn}()},function(n,t){function r(n){var t={};for(var r in n)t[r]=o.copy(n[r]);return t}function i(n,t){for(var r,i=n.length,o=i-t;o<--i;)r=n[o],n[o++]=n[i],n[i]=r}var o={};o.copy=function(n){return n instanceof Array?n.map(o.copy):"string"==typeof n||"number"==typeof n?n:r(n)},o.arctans=function(n,t){var r=n/t,i=Math.sqrt(1+r*r),o=t>0?i:-i,e=1/o,a=r*e;return{cos:e,sin:a}},o.object=function(n,t){function r(t,r){r.length&&r.pop();for(var o=n[0>t?~t:t],e=0,a=o.length;a>e;++e)r.push(o[e]);0>t&&i(r,a)}function o(n){for(var t=[],i=0,o=n.length;o>i;++i)r(n[i],t);return t}function e(n){return n.map(o)}function a(n){return n=Object.create(n),n.properties=n.properties,n.coordinates=u[n.type](n.arcs),n.type=n.type,n}var u={LineString:o,MultiLineString:e,Polygon:e,MultiPolygon:function(n){return n.map(e)}};return"GeometryCollection"===t.type?(t=Object.create(t),t.geometries=t.geometries.map(a),t):a(t)},o.properties=function(n){return n.properties||{}},o.transformer=function(n){function t(n){return[n[0]*r+o,n[1]*i+e]}var r=n.scale[0],i=n.scale[1],o=n.translate[0],e=n.translate[1];return t.invert=function(n){return[(n[0]-o)/r,(n[1]-e)/i]},t},n.exports=o},function(n,t){!function(t){function r(n){var t=y[n]={};return s(n.split(/\s+/),function(n){t[n]=!0}),t}var i={},o=Array.prototype,e=Object.prototype,a=e.hasOwnProperty,u=e.toString,c=o.forEach,l=o.indexOf,f=o.slice,s=function(n,t,r){var o,e,u;if(n)if(c&&n.forEach===c)n.forEach(t,r);else if(n.length===+n.length){for(e=0,u=n.length;u>e;e++)if(e in n&&t.call(r,n[e],e,n)===i)return}else for(o in n)if(a.call(n,o)&&t.call(r,n[o],o,n)===i)return},p=function(n){return!!(n&&n.constructor&&n.call&&n.apply)},h=function(n){return s(f.call(arguments,1),function(t){var r;for(r in t)void 0!==t[r]&&(n[r]=t[r])}),n},g=function(n,t,r){var i;if(t){if(l)return l.call(t,n,r);for(i=t.length,r=r?0>r?Math.max(0,i+r):r:0;i>r;r++)if(r in t&&t[r]===n)return r}return-1},v={};s("Boolean Number String Function Array Date RegExp Object".split(" "),function(n,t){v["[object "+n+"]"]=n.toLowerCase()});var d=function(n){return null==n?String(n):v[u.call(n)]||"object"},M={},y={};M.Callbacks=function(n){n="string"==typeof n?y[n]||r(n):h({},n);var t,i,o,e,a,u,c=[],l=!n.once&&[],f=function(r){for(t=n.memory&&r,i=!0,u=e||0,e=0,a=c.length,o=!0;c&&a>u;u++)if(c[u].apply(r[0],r[1])===!1&&n.stopOnFalse){t=!1;break}o=!1,c&&(l?l.length&&f(l.shift()):t?c=[]:p.disable())},p={add:function(){if(c){var r=c.length;!function i(t){s(t,function(t){var r=d(t);"function"===r?n.unique&&p.has(t)||c.push(t):t&&t.length&&"string"!==r&&i(t)})}(arguments),o?a=c.length:t&&(e=r,f(t))}return this},remove:function(){return c&&s(arguments,function(n){for(var t;(t=g(n,c,t))>-1;)c.splice(t,1),o&&(a>=t&&a--,u>=t&&u--)}),this},has:function(n){return g(n,c)>-1},empty:function(){return c=[],this},disable:function(){return c=l=t=void 0,this},disabled:function(){return!c},lock:function(){return l=void 0,t||p.disable(),this},locked:function(){return!l},fireWith:function(n,t){return t=t||[],t=[n,t.slice?t.slice():t],!c||i&&!l||(o?l.push(t):f(t)),this},fire:function(){return p.fireWith(this,arguments),this},fired:function(){return!!i}};return p},M.Deferred=function(n){var t=[["resolve","done",M.Callbacks("once memory"),"resolved"],["reject","fail",M.Callbacks("once memory"),"rejected"],["notify","progress",M.Callbacks("memory")]],r="pending",i={state:function(){return r},always:function(){return o.done(arguments).fail(arguments),this},then:function(){var n=arguments;return M.Deferred(function(r){s(t,function(t,i){var e=t[0],a=n[i];o[t[1]](p(a)?function(){var n;try{n=a.apply(this,arguments)}catch(t){return void r.reject(t)}n&&p(n.promise)?n.promise().done(r.resolve).fail(r.reject).progress(r.notify):r["notify"!==e?"resolveWith":e+"With"](this===o?r:this,[n])}:r[e])}),n=null}).promise()},promise:function(n){return null!=n?h(n,i):i}},o={};return i.pipe=i.then,s(t,function(n,e){var a=n[2],u=n[3];i[n[1]]=a.add,u&&a.add(function(){r=u},t[1^e][2].disable,t[2][2].lock),o[n[0]]=a.fire,o[n[0]+"With"]=a.fireWith}),i.promise(o),n&&n.call(o,o),o},M.when=function(n){var t,r,i,o=0,e="array"===d(n)&&1===arguments.length?n:f.call(arguments),a=e.length,u=1!==a||n&&p(n.promise)?a:0,c=1===u?n:M.Deferred(),l=function(n,r,i){return function(o){r[n]=this,i[n]=arguments.length>1?f.call(arguments):o,i===t?c.notifyWith(r,i):--u||c.resolveWith(r,i)}};if(a>1)for(t=new Array(a),r=new Array(a),i=new Array(a);a>o;o++)e[o]&&p(e[o].promise)?e[o].promise().done(l(o,i,e)).fail(c.reject).progress(l(o,r,t)):--u;return u||c.resolveWith(i,e),c.promise()},"undefined"!=typeof n&&n.exports?n.exports=M:"undefined"!=typeof t._?t._.mixin(M):t._=M}(this)}])}); -------------------------------------------------------------------------------- /example/Gruntfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var mountFolder = function(connect, dir) { 4 | return connect.static(require('path').resolve(dir)); 5 | }; 6 | 7 | var webpackDistConfig = require('./webpack.dist.config.js'), 8 | webpackDevConfig = require('./webpack.config.js'); 9 | 10 | module.exports = function(grunt) { 11 | // Let *load-grunt-tasks* require everything 12 | require('load-grunt-tasks')(grunt); 13 | 14 | // Read configuration from package.json 15 | var pkgConfig = grunt.file.readJSON('package.json'); 16 | 17 | grunt.initConfig({ 18 | pkg: pkgConfig, 19 | 20 | webpack: { 21 | options: webpackDistConfig, 22 | dist: { 23 | cache: false 24 | } 25 | }, 26 | 27 | 'webpack-dev-server': { 28 | options: { 29 | hot: true, 30 | port: 8000, 31 | webpack: webpackDevConfig, 32 | publicPath: '/assets/', 33 | contentBase: './<%= pkg.src %>/' 34 | }, 35 | 36 | start: { 37 | keepAlive: true 38 | } 39 | }, 40 | 41 | connect: { 42 | options: { 43 | port: 8000 44 | }, 45 | 46 | dist: { 47 | options: { 48 | keepalive: true, 49 | middleware: function(connect) { 50 | return [ 51 | mountFolder(connect, pkgConfig.dist) 52 | ]; 53 | } 54 | } 55 | } 56 | }, 57 | 58 | open: { 59 | options: { 60 | delay: 500 61 | }, 62 | dev: { 63 | path: 'http://localhost:<%= connect.options.port %>/webpack-dev-server/' 64 | }, 65 | dist: { 66 | path: 'http://localhost:<%= connect.options.port %>/' 67 | } 68 | }, 69 | 70 | copy: { 71 | dist: { 72 | files: [ 73 | // includes files within path 74 | { 75 | flatten: true, 76 | expand: true, 77 | src: ['<%= pkg.src %>/*'], 78 | dest: '<%= pkg.dist %>/', 79 | filter: 'isFile' 80 | }, { 81 | flatten: true, 82 | expand: true, 83 | src: ['<%= pkg.src %>/images/*'], 84 | dest: '<%= pkg.dist %>/images/' 85 | } 86 | ] 87 | } 88 | }, 89 | 90 | clean: { 91 | dist: { 92 | files: [{ 93 | dot: true, 94 | src: [ 95 | '<%= pkg.dist %>' 96 | ] 97 | }] 98 | } 99 | } 100 | }); 101 | 102 | grunt.registerTask('serve', function(target) { 103 | if (target === 'dist') { 104 | return grunt.task.run(['build', 'open:dist', 'connect:dist']); 105 | } 106 | 107 | grunt.task.run([ 108 | 'open:dev', 109 | 'webpack-dev-server' 110 | ]); 111 | }); 112 | 113 | grunt.registerTask('build', ['clean', 'copy', 'webpack']); 114 | 115 | grunt.registerTask('default', []); 116 | }; 117 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | Example app 2 | --------------------- 3 | This is an example of using async-cartogram to draw a cartogram of Paris' snail neighborhoods (called "arrondissements"). 4 | 5 | React is used for the UI, and webpack for bundling. 6 | 7 | To see the example, install grunt-cli, run `npm install`, `grunt serve` and check http://localhost:8000/. 8 | -------------------------------------------------------------------------------- /example/capture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laem/async-cartogram/17bbbbefa2b1bb7c0705dd3cd1460b88c88206d2/example/capture.png -------------------------------------------------------------------------------- /example/data/.~lock.metriques-paris.csv#: -------------------------------------------------------------------------------- 1 | Mama ,mama,cactus,22.06.2015 16:20,file:///home/mama/.config/libreoffice/4; -------------------------------------------------------------------------------- /example/data/arrondissements.json: -------------------------------------------------------------------------------- 1 | {"type":"Topology","objects":{"arrondissements":{"type":"GeometryCollection","geometries":[{"type":"Polygon","properties":{"n_sq_co":750001537,"perimetre":10775.579516,"objectid":17,"l_ar":"17ème Ardt","geom_x_y":[48.88732652202582,2.3067769905744058],"surface":5668834.50445393,"longueur":10775.34891467,"n_sq_ar":750000017,"l_aroff":"Batignolles-Monceau","c_arinsee":75117,"c_ar":17},"arcs":[[0,1,2,3,4]]},{"type":"Polygon","properties":{"n_sq_co":750001537,"perimetre":11253.1824786,"objectid":19,"l_ar":"19ème Ardt","geom_x_y":[48.88707599657253,2.3848209601525183],"surface":6792651.12902648,"longueur":11252.94322851,"n_sq_ar":750000019,"l_aroff":"Buttes-Chaumont","c_arinsee":75119,"c_ar":19},"arcs":[[5,6,7,8]]},{"type":"Polygon","properties":{"n_sq_co":750001537,"perimetre":6471.5882901,"objectid":9,"l_ar":"9ème Ardt","geom_x_y":[48.87716351732887,2.337457543482549],"surface":2178303.27487137,"longueur":6471.38914439,"n_sq_ar":750000009,"l_aroff":"Opéra","c_arinsee":75109,"c_ar":9},"arcs":[[9,10,11,12,-4,13]]},{"type":"Polygon","properties":{"n_sq_co":750001537,"perimetre":6739.37505466,"objectid":10,"l_ar":"10ème Ardt","geom_x_y":[48.87613003653914,2.360728487847452],"surface":2891739.44162064,"longueur":6739.16835945,"n_sq_ar":750000010,"l_aroff":"Entrepôt","c_arinsee":75110,"c_ar":10},"arcs":[[-7,14,15,16,-10,17]]},{"type":"Polygon","properties":{"n_sq_co":750001537,"perimetre":13678.7983149,"objectid":15,"l_ar":"15ème Ardt","geom_x_y":[48.84008537593825,2.2928258224249984],"surface":8494994.08101075,"longueur":13677.92366106,"n_sq_ar":750000015,"l_aroff":"Vaugirard","c_arinsee":75115,"c_ar":15},"arcs":[[18,19,20,21,22]]},{"type":"Polygon","properties":{"n_sq_co":750001537,"perimetre":24089.6662978,"objectid":12,"l_ar":"12ème Ardt","geom_x_y":[48.83497438148047,2.421324900784679],"surface":16314782.6372674,"longueur":24088.03892205,"n_sq_ar":750000012,"l_aroff":"Reuilly","c_arinsee":75112,"c_ar":12},"arcs":[[23,24,25,26,27,28]]},{"type":"Polygon","properties":{"n_sq_co":750001537,"perimetre":6054.93686218,"objectid":1,"l_ar":"1er Ardt","geom_x_y":[48.86256270183604,2.3364433620533864],"surface":1824612.86048666,"longueur":6054.68086176,"n_sq_ar":750000001,"l_aroff":"Louvre","c_arinsee":75101,"c_ar":1},"arcs":[[29,30,31,32,33,34,-12]]},{"type":"Polygon","properties":{"n_sq_co":750001537,"perimetre":11546.5465264,"objectid":13,"l_ar":"13ème Ardt","geom_x_y":[48.82838803174471,2.3622724404209063],"surface":7149311.09107136,"longueur":11545.68698553,"n_sq_ar":750000013,"l_aroff":"Gobelins","c_arinsee":75113,"c_ar":13},"arcs":[[35,36,37,-24]]},{"type":"Polygon","properties":{"n_sq_co":750001537,"perimetre":8099.42488348,"objectid":7,"l_ar":"7ème Ardt","geom_x_y":[48.85617442877932,2.312187691482009],"surface":4090057.18546976,"longueur":8099.03315106,"n_sq_ar":750000007,"l_aroff":"Palais-Bourbon","c_arinsee":75107,"c_ar":7},"arcs":[[-34,38,-23,39,40]]},{"type":"Polygon","properties":{"n_sq_co":750001537,"perimetre":6483.68678565,"objectid":6,"l_ar":"6ème Ardt","geom_x_y":[48.849130358585214,2.332897999053314],"surface":2153095.58639283,"longueur":6483.32455954,"n_sq_ar":750000006,"l_aroff":"Luxembourg","c_arinsee":75106,"c_ar":6},"arcs":[[41,42,-19,-39,-33]]},{"type":"Polygon","properties":{"n_sq_co":750001537,"perimetre":7880.53326819,"objectid":8,"l_ar":"8ème Ardt","geom_x_y":[48.872720837434485,2.312554022402066],"surface":3880036.39704363,"longueur":7880.26066073,"n_sq_ar":750000008,"l_aroff":"Élysée","c_arinsee":75108,"c_ar":8},"arcs":[[-35,-41,43,-5,-13]]},{"type":"Polygon","properties":{"n_sq_co":750001537,"perimetre":10704.9404863,"objectid":20,"l_ar":"20ème Ardt","geom_x_y":[48.863460578895626,2.4011881292846855],"surface":5983446.03718297,"longueur":10704.48377412,"n_sq_ar":750000020,"l_aroff":"Ménilmontant","c_arinsee":75120,"c_ar":20},"arcs":[[-28,44,-6,45]]},{"type":"Polygon","properties":{"n_sq_co":750001537,"perimetre":6239.19539614,"objectid":5,"l_ar":"5ème Ardt","geom_x_y":[48.84444315053269,2.3507146095752574],"surface":2539374.62284532,"longueur":6238.8234077,"n_sq_ar":750000005,"l_aroff":"Panthéon","c_arinsee":75105,"c_ar":5},"arcs":[[-25,-38,46,-42,47]]},{"type":"Polygon","properties":{"n_sq_co":750001537,"perimetre":17416.1096565,"objectid":16,"l_ar":"16ème Ardt","geom_x_y":[48.86039210541584,2.2619707883645424],"surface":16372542.1289739,"longueur":17415.32672605,"n_sq_ar":750000016,"l_aroff":"Passy","c_arinsee":75116,"c_ar":16},"arcs":[[-1,-44,-40,-22,48]]},{"type":"Polygon","properties":{"n_sq_co":750001537,"perimetre":4519.26364836,"objectid":3,"l_ar":"3ème Ardt","geom_x_y":[48.86287238001699,2.3600009858976954],"surface":1170882.82818778,"longueur":4519.07198175,"n_sq_ar":750000003,"l_aroff":"Temple","c_arinsee":75103,"c_ar":3},"arcs":[[49,50,-31,51,-16]]},{"type":"Polygon","properties":{"n_sq_co":750001537,"perimetre":10317.4833099,"objectid":14,"l_ar":"14ème Ardt","geom_x_y":[48.82924450048991,2.3265420441989475],"surface":5614877.30907921,"longueur":10316.72025836,"n_sq_ar":750000014,"l_aroff":"Observatoire","c_arinsee":75114,"c_ar":14},"arcs":[[-47,-37,52,-20,-43]]},{"type":"Polygon","properties":{"n_sq_co":750001537,"perimetre":8282.01188584,"objectid":11,"l_ar":"11ème Ardt","geom_x_y":[48.85905922134248,2.380058308197897],"surface":3665441.55248808,"longueur":8281.63528771,"n_sq_ar":750000011,"l_aroff":"Popincourt","c_arinsee":75111,"c_ar":11},"arcs":[[-27,53,-50,-15,-45]]},{"type":"Polygon","properties":{"n_sq_co":750001537,"perimetre":4554.10435957,"objectid":2,"l_ar":"2ème Ardt","geom_x_y":[48.8682792225225,2.3428025468913627],"surface":991153.74474596,"longueur":4553.93876373,"n_sq_ar":750000002,"l_aroff":"Bourse","c_arinsee":75102,"c_ar":2},"arcs":[[-30,-11,-17,-52]]},{"type":"Polygon","properties":{"n_sq_co":750001537,"perimetre":9916.46417634,"objectid":18,"l_ar":"18ème Ardt","geom_x_y":[48.892569268005836,2.348160519562041],"surface":5996051.30811905,"longueur":9916.30382695,"n_sq_ar":750000018,"l_aroff":"Buttes-Montmartre","c_arinsee":75118,"c_ar":18},"arcs":[[-18,-14,-3,54,-8]]},{"type":"Polygon","properties":{"n_sq_co":750001537,"perimetre":5420.90843368,"objectid":4,"l_ar":"4ème Ardt","geom_x_y":[48.85434142627283,2.357629620324992],"surface":1600585.63150251,"longueur":5420.63677909,"n_sq_ar":750000004,"l_aroff":"Hôtel-de-Ville","c_arinsee":75104,"c_ar":4},"arcs":[[-54,-26,-48,-32,-51]]}]}},"arcs":[[[2888,6721],[-4,4],[-16,14],[-21,19],[-42,38],[-102,92],[-21,19],[-32,28],[-48,45],[-33,30],[-119,106],[-8,7],[-5,4],[-1,2],[-3,2],[-2,2],[-1,1],[-16,14],[-2,2],[-83,77],[-61,57]],[[2268,7284],[6,6],[2,13],[0,2],[1,10],[4,51],[0,3],[1,15],[0,1],[2,19],[2,25],[1,9],[4,51],[1,11],[0,3],[1,10],[4,50],[1,12],[0,12],[3,38],[2,18],[1,19],[1,3],[6,84],[1,13],[1,10],[3,8],[3,5],[2,6],[25,54],[6,12],[4,9],[4,10],[8,17],[76,169],[9,18],[4,11],[1,0],[7,17],[28,60],[9,20],[4,11],[103,143],[18,25],[4,4],[0,1],[3,4],[10,14],[102,141],[13,5],[22,7],[10,4],[12,4],[23,7],[7,3],[11,3],[12,4],[3,2],[7,2],[3,1],[15,5],[4,1],[1,0],[17,26],[16,24],[3,4],[2,3],[15,23],[34,49],[9,15],[5,7],[20,29],[2,4],[4,6],[3,4],[1,2],[3,4],[2,2],[2,5],[3,3],[2,3],[2,3],[3,4],[1,1],[28,38],[11,15],[14,18],[13,18],[11,14],[25,34],[4,5],[38,51],[6,7],[22,30],[13,18],[15,20],[6,8],[39,53],[38,51],[6,8],[3,5],[23,30],[27,37],[7,9],[8,11],[2,2],[0,1],[43,45],[9,10],[29,31],[1,0],[5,6],[7,8],[4,4],[36,39],[26,27],[12,13],[3,3],[3,4],[14,15],[33,28],[13,11],[1,1],[16,14],[2,2],[11,10],[33,28],[1,0],[47,41],[7,6],[10,9],[21,18],[6,5],[7,6],[7,6],[12,10],[8,8],[20,16],[3,6],[5,8],[1,2],[1,2],[14,23],[29,49],[2,5],[1,2],[13,22],[5,9],[2,2],[27,3],[25,2],[9,1],[54,5],[1,0],[1,0],[1,1],[7,0],[11,1],[6,1],[19,2],[1,0],[61,6],[13,1],[8,1],[8,0],[1,0],[36,2],[87,5],[1,0],[20,1]],[[4317,9869],[-2,-20],[-5,-41],[-2,-12],[-1,-11],[-18,-152],[-15,-122],[-2,-12],[-1,-15],[-1,-9],[-1,-2],[-28,-236],[-34,-290],[-7,-54],[-16,-142],[-11,-89],[-13,-113],[-15,-124],[-7,-59],[-3,-28],[-3,-23],[-1,-9],[32,-193],[17,-106],[25,-151],[1,-10]],[[4206,7846],[-5,-2],[-5,-2]],[[4196,7842],[-5,-3],[-5,-3],[-9,-5],[0,-1],[-5,-2],[-2,-1],[-42,-25],[-2,-1],[-1,-1],[-4,-2],[-41,-24],[-4,-2],[-1,-1],[-1,0],[0,-1],[-4,-2],[-2,-1],[-2,-1],[-23,-13],[-1,-1],[-2,-1],[-2,-1],[-1,-1],[-111,-64],[-1,0],[-3,-2],[-5,-3],[-3,-2],[-25,-14],[-29,-17],[-3,-2],[-3,-1],[-1,-1],[-35,-21],[-43,-24],[-7,-4],[-7,-2],[-160,-56],[-38,-14],[-101,-34],[-12,-6],[-10,-5],[-126,-76],[-98,-61],[-87,-52],[-90,-56],[-1,0],[-5,-3],[-6,-3],[-12,-8],[-6,-23],[-1,-6],[-80,-313],[-29,-113],[-1,-4],[-5,-17],[-5,-21]],[[7601,7258],[-1,-1],[-41,-33],[-5,-4],[-10,-9],[-2,-2],[-2,-1],[-4,-3],[-2,-1],[-12,-10],[-16,-14],[-1,-1],[-1,0],[-2,-2],[-1,-1],[-12,-10],[-36,-36],[-10,-10],[-1,-1],[-10,-5],[-9,-5],[-20,-15],[-139,-115],[-1,-1],[-131,-50],[-14,-9],[-2,-1],[-40,-3],[-26,-2],[-80,-21],[-73,11],[-20,2],[-44,9],[-8,2],[-56,-2],[-25,-13],[-11,-14],[-22,-29],[-17,-20],[-14,-9],[-31,-5],[-18,-3],[-3,-1],[-4,-2],[-3,-2],[-19,-22],[-24,-35],[-20,-28],[-6,-7],[-4,-4],[-2,-2],[-19,-11],[-55,-20],[-22,-10],[-13,-6],[-14,-8],[-53,-31],[-16,-9],[-14,-10],[-13,-9],[-11,-8],[-12,-9],[-81,-64]],[[6223,6523],[-3,8],[-70,180],[-3,8],[-4,10],[-10,23],[-2,7],[-13,32],[-6,15],[-2,3],[-20,50],[-5,14],[-47,117],[-2,5],[-11,28],[-67,166],[-8,19],[0,42],[0,10],[0,8],[0,6],[-1,161],[0,7],[0,12],[0,1],[0,69],[0,83],[0,5],[0,4],[0,8],[-1,85],[0,9],[0,4],[0,4],[0,20],[0,1],[0,1],[0,1],[0,1],[-1,1],[0,1],[0,1],[0,1],[-1,2],[-1,1],[0,2],[-1,1],[-1,1],[-2,4],[-1,1],[-1,1],[-1,2],[-6,8],[-8,9],[-3,3],[-5,6],[-4,6],[-2,5],[-2,4],[-1,1],[-1,4],[-1,0],[0,1],[-1,4],[-1,1],[0,1],[-2,4],[-1,6],[-2,6],[-1,7],[-1,5],[-1,8],[-1,1],[-1,10],[-1,6],[-1,8],[0,1],[-1,2],[0,1],[-1,1],[0,1],[0,1],[0,1],[-1,1],[0,1],[0,1],[-1,1],[0,1],[-1,1],[-1,2],[-1,1],[0,1],[-1,1],[-1,2],[-1,0],[-1,1],[-1,2],[-2,2],[-4,3],[0,1],[-1,0],[-2,1],[-3,2],[-1,1],[-4,1],[-2,1],[-1,0],[-1,0],[-1,1],[-2,0],[-4,0],[-11,2],[-4,1],[-24,5],[-4,0],[-1,1],[-12,2],[-66,12],[-6,1]],[[5723,7944],[45,136],[16,47],[15,50],[9,30],[12,87],[20,144],[23,122],[7,43],[49,328],[21,129],[16,57],[4,13],[52,89],[-3,6],[-13,30],[-4,10],[-1,6],[-1,7],[0,1],[-2,4],[-20,47],[-11,25],[-2,4],[0,1],[-3,124],[-1,72],[-2,175],[-1,63],[-1,56],[-1,65],[0,10],[0,6],[0,5],[1,6],[1,5],[0,6],[1,5],[2,6]],[[5951,9964],[10,1],[9,0],[50,3],[4,1],[3,0],[18,1],[8,0],[4,1],[96,5],[49,3],[78,5],[38,2],[8,1],[3,0],[3,0],[2,0],[2,1],[2,0],[1,0],[18,1],[3,0],[7,0],[21,2],[13,0],[21,2],[67,4],[1,0],[36,2],[14,-7],[9,-5],[131,-70],[4,-2],[4,-3],[6,-3],[10,-5],[2,-2],[3,-1],[4,-2],[1,-1],[2,-1],[2,-1],[11,-6],[1,0],[28,-15],[24,-13],[10,-16],[68,-110],[36,-56],[3,-6],[1,-1],[78,-124],[0,-3],[8,-35],[8,-40],[2,-6],[39,-183],[34,-156],[11,-120],[24,-256],[7,-117],[6,-91],[0,-4],[6,-93],[0,-1],[2,-25],[1,-9],[1,-20],[0,-64],[0,-11],[0,-78],[0,-44],[1,-20],[0,-7],[0,-6],[0,-13],[0,-17],[0,-9],[0,-4],[0,-44],[0,-8],[1,-7],[9,-46],[1,-4],[0,-2],[3,-11],[9,-46],[2,-12],[4,-20],[0,-1],[7,-36],[25,-60],[12,-30],[1,-3],[9,-22],[7,-17],[2,-6],[1,0],[5,-7],[32,-41],[4,-4],[4,-5],[3,-4],[29,-37],[2,-2],[2,-2],[9,-12],[37,-47],[53,-30],[15,-9],[16,-9],[11,-6],[9,-6],[10,-6],[9,-5],[23,-13],[5,-3],[7,-4],[34,-20],[2,-8],[23,-78],[30,-104],[3,-13],[3,-9]],[[5105,7870],[3,-28],[5,-146],[5,-130],[1,-54],[-13,-109],[-1,-1],[-9,-72],[-4,-58],[-3,-124],[-1,-6],[-18,-91],[-13,-71],[-3,-26],[-14,-204],[-1,-233],[0,-151]],[[5039,6366],[-196,91],[-123,57],[-6,-3],[-7,-3],[-107,-53],[-4,-2],[-116,-56],[-84,-41],[-92,-44],[-74,-37]],[[4230,6275],[-1,2],[-87,-43],[-1,0]],[[4141,6234],[11,116],[7,81],[9,101],[11,116],[4,16],[4,30],[2,10],[2,12],[-1,13],[-1,5],[-3,34],[-14,162],[11,5],[4,256],[0,7],[0,8],[3,190],[0,3],[1,135],[3,151],[2,157]],[[4206,7846],[6,4],[7,4],[4,2],[12,12],[9,16],[7,12],[25,46],[2,6],[3,4],[7,14],[11,-8],[31,-20],[3,-3],[64,-42],[13,-9],[2,-1],[1,-1],[4,-4],[3,-3],[7,-5],[83,-71],[3,-3],[81,-68],[4,-3],[4,-4],[2,-1],[4,-4],[21,-10],[3,-1],[6,-2],[60,-24],[3,-2],[1,0],[3,-1],[4,-2],[1,-1],[72,40],[13,7],[4,2],[78,44],[21,12],[1,0],[3,2],[86,48],[1,0],[5,3],[7,4],[2,0],[5,2],[5,2],[64,21],[21,8],[7,2]],[[6223,6523],[-2,-2],[-109,-111],[-10,-12],[-17,-19],[-20,-22],[-9,-7],[-132,-115],[-12,-12],[-42,-45],[-18,-17],[-10,-11],[-9,-8],[-43,-34],[-33,-31],[-66,-76],[-3,-4]],[[5688,5997],[-3,4],[-1,1],[-2,4],[-4,7],[-7,12],[-3,5],[-3,6],[-3,1],[-4,2],[-6,2],[-5,2],[-9,4],[-11,4],[-109,45],[-26,11],[-71,29],[-37,16],[-10,6],[-11,7],[-65,43]],[[5298,6208],[-66,42],[-68,41],[-125,75]],[[5105,7870],[6,1],[7,2],[13,2],[6,2],[4,1],[11,2],[4,1],[8,2],[10,2],[5,1],[114,24],[49,9],[6,1],[7,2],[2,0],[9,2],[24,5],[32,6],[67,14],[7,1],[7,1],[2,0],[1,0],[6,0],[2,0],[8,-1],[67,-1],[5,0],[44,-1],[29,0],[27,-1],[15,-1],[8,-1],[6,-1]],[[3764,3609],[105,-153],[43,-45],[46,-44],[73,-68],[31,-29],[34,-32]],[[4096,3238],[-87,-199],[-40,-91],[-4,-11],[14,-97],[-19,-45],[-10,14],[-17,24],[-32,47],[-104,-242],[-7,-16],[-20,-47],[-1,0],[-154,-359],[-86,-201],[0,-1],[-38,-91],[-11,-25],[-44,-108],[-34,-78],[-22,-49],[-39,-87],[-27,-64],[-13,-29],[-27,-63],[-15,-36],[-22,-50],[-50,-116],[-27,-63],[0,-1],[-5,-11],[-6,-12],[-4,-11],[-3,-8],[-1,0],[-2,-6]],[[3139,1106],[-5,3],[-9,6],[-18,11],[-3,2],[-5,3],[-74,45],[-42,26],[-7,5],[-5,3],[-2,1],[-5,3],[-69,44],[-53,33],[-4,3],[-4,2],[-1,1],[-11,7],[-25,16],[-2,1],[-3,2],[-5,3],[-6,4],[-8,5],[-8,11],[-1,0],[-11,13],[-17,21],[-15,18],[-5,5],[-58,70],[-7,8],[-18,22],[-18,21],[-1,1],[-1,1],[-29,35],[-35,42],[-31,36],[-37,45],[-4,5],[-7,8],[-8,9],[-51,61],[-16,17],[-3,3],[-41,43],[-49,51],[-6,6],[-2,3],[-3,2],[-5,6],[-43,44],[-2,3],[-5,4],[-95,-221],[-32,-73],[-16,-37],[-1,-2],[-10,-16],[-17,-30],[-55,-94],[-8,-14],[-5,-8],[-2,-4],[-7,-11],[-7,-13],[-28,-1],[-172,-9],[-1,1],[-7,13],[-4,127],[-9,277],[0,3],[0,1],[1,3],[0,2],[2,3],[9,18],[40,65],[21,33],[3,5],[10,15],[6,9],[3,5],[13,13],[3,3],[-1,0],[-3,7],[-4,9],[-17,33],[-37,75],[-27,57],[-1,0],[-2,1],[-15,7],[-2,1],[-3,1],[-12,-6],[-37,-18],[-1,0],[-37,-19],[-1,0],[-14,-7],[-4,-3],[-6,-2],[-1,-1],[-7,-3],[-56,-28],[-1,0]],[[1582,2116],[31,88],[60,174],[98,271],[29,89],[5,14],[33,102],[15,45],[7,22],[48,139],[25,76],[27,78],[35,98],[11,32],[1,3],[2,6],[29,85],[43,125],[7,22],[14,39],[51,126],[41,91],[48,107],[16,34],[5,11],[6,12],[5,11],[6,12],[32,63],[61,123],[12,24],[3,5],[0,1],[2,2],[2,4],[6,13],[28,56],[27,54],[19,38],[38,78],[7,13],[3,7],[58,115],[1,3],[3,5],[0,1],[1,2],[1,2],[1,2],[2,5],[11,21],[3,6],[5,11],[9,48],[6,26],[4,20],[6,25],[5,22],[1,2],[2,7],[1,5],[3,9],[2,6],[2,8],[29,67]],[[2676,4927],[20,-36],[1,-1],[0,-1],[1,-1],[0,-1],[1,0],[0,-1],[0,-1],[1,0],[0,-1],[1,-1],[0,-1],[1,-1],[0,-1],[1,-1],[0,-1],[1,-1],[1,-1],[0,-1],[0,-1],[1,0],[0,-1],[1,-1],[0,-1],[1,-1],[0,-1],[4,-6],[3,-5],[87,-159],[6,-11],[109,-197],[5,-9],[2,-5],[60,-108],[21,-39],[57,-104],[64,-115],[3,6],[113,-206],[98,-178],[49,-88],[28,29],[7,5],[88,60],[6,4],[26,-47],[4,-8],[9,-17],[6,-10],[6,-10],[13,-24],[63,-115],[4,-6],[96,84],[19,19]],[[6763,1172],[-48,96],[-60,118],[-22,46],[-4,11],[-2,4],[-1,3],[-26,63],[-44,109],[-16,35],[-59,132],[-19,44],[-76,168],[-84,184],[-19,44],[-56,131],[-4,10],[-84,221],[-4,9],[-1,2],[-49,127],[-20,54],[-23,46],[-5,11],[-12,26],[-63,131],[-56,118],[-30,63],[-99,212]],[[5777,3390],[-48,105],[-17,35]],[[5712,3530],[38,64],[1,0],[4,8],[3,4],[0,1],[2,3],[2,3],[1,2],[0,1],[1,0],[10,17],[1,2],[6,10],[1,2],[15,25],[0,4],[32,204],[0,1],[28,172],[2,11],[14,90],[1,8],[7,38],[1,7],[0,3],[1,6],[2,8],[3,17],[2,16],[0,2],[3,15],[8,52],[1,6],[1,7],[1,2]],[[5904,4341],[8,1],[38,9],[1,0],[1,0],[1,0],[1,-1],[1,-1],[0,-1],[21,-37],[16,-26],[2,-4],[2,-2],[2,-2],[2,-4],[10,-12],[3,-3],[1,-1],[2,-2],[3,-3],[64,-54],[10,-7],[43,-30],[119,-83],[30,-21],[13,-9],[23,-9],[30,-12],[59,-7],[90,-20],[23,-10],[209,-89],[25,-11],[133,-58],[40,-17],[40,-16],[24,-10],[24,-6],[18,-5],[16,-4],[11,-3],[7,-2],[36,-9],[8,-2],[10,-2]],[[7124,3756],[6,-2],[6,-1],[2,-1],[162,-40],[26,-7],[89,-25],[2,0],[57,-13],[12,-3],[59,-13],[4,-2],[35,-9],[9,-2],[14,-3],[4,-2],[3,0],[5,-1],[12,-3],[5,-2],[67,-16],[11,-3],[1,0],[12,-3],[8,-2],[15,-4],[3,0],[29,-8],[8,-2],[21,-5]],[[7811,3584],[-3,-43],[-2,-25],[-1,-12],[-5,-67],[0,-11],[-2,-16],[-13,-123],[-5,-44],[-4,-40],[0,-3],[-2,-11],[-9,-83],[-2,-24],[-2,-12],[-3,-34],[-3,-29],[-5,-36],[0,-1],[-9,-88],[-29,-282],[-2,-24],[-8,-59],[-3,-19],[-6,-49],[-2,-15],[-5,-40],[-3,-21],[-12,-96],[-1,-8],[-2,-10],[-2,-16],[-1,-10],[-5,-34],[-1,-8],[-6,-11],[-2,-5],[-6,-10],[-1,-2],[-14,-27],[-8,-14],[-1,-1],[-1,-2],[-4,-8],[8,-4],[7,-4],[7,-3],[4,-2],[3,-1],[7,-3],[7,-2],[7,-3],[7,-2],[7,-2],[7,-2],[8,-1],[7,-1],[7,-2],[5,0],[5,-1],[6,0],[5,0],[5,0],[5,0],[6,1],[5,0],[5,1],[5,1],[5,1],[5,1],[10,3],[6,2],[6,2],[6,2],[6,3],[6,3],[5,2],[6,4],[6,3],[6,3],[6,4],[5,4],[6,4],[6,4],[5,4],[6,5],[60,53],[137,135],[-14,71],[0,2],[-6,29],[-2,9],[-1,5],[-1,4],[0,5],[-1,5],[-1,4],[0,5],[-1,5],[0,5],[-1,4],[0,4],[-1,10],[0,2],[-1,7],[-9,120],[-17,158],[-18,101],[-8,26],[-10,26],[-5,14],[-12,54],[-6,109],[4,22],[1,1],[0,1],[0,1],[0,1],[1,0],[0,1],[0,1],[0,1],[1,0],[0,1],[0,1],[1,0],[0,1],[0,1],[1,1],[0,1],[1,1],[0,1],[1,1],[0,1],[1,1],[0,1],[1,3],[1,1],[0,2],[1,1],[0,2],[1,1],[0,2],[1,1],[0,2],[0,2],[1,1],[0,2],[0,1],[1,2],[0,1],[0,1],[0,2],[0,1],[1,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[1,3],[0,1],[0,2],[0,1],[0,2],[0,2],[0,1],[-1,2],[0,1],[0,3],[0,1],[0,2],[0,2],[0,1],[0,2],[-1,1],[0,2],[0,1],[9,13],[68,93],[9,12],[5,6],[8,-12],[1,-2],[1,-2],[1,-1],[0,-1],[1,-2],[1,-1],[1,-2],[0,-1],[1,-2],[1,-2],[0,-1],[1,-2],[1,-2],[0,-1],[1,-2],[1,-2],[0,-2],[3,-8],[2,-4],[1,-5],[2,-5],[1,-5],[1,-5],[2,-5],[1,-5],[1,-5],[2,-5],[1,-4],[1,-8],[3,-9],[3,-16],[19,-76],[1,-4],[31,-76],[2,-6],[2,-3],[0,-1],[10,-5],[-2,-15],[12,-5],[102,-25],[1,0],[1,-1],[128,-31],[70,-17],[44,-11],[4,-1],[1,24],[71,-17],[72,-18],[1,8],[23,327],[5,67],[2,21],[0,2],[64,-16],[41,-10],[0,1],[0,2],[1,24],[3,32],[0,4],[8,-2],[2,38],[0,1],[-7,0],[0,7],[3,74],[0,1],[51,-5],[33,-2],[53,-5],[95,-7],[2,-1],[1,-2],[0,-5],[0,-1],[-1,-16],[-3,-57],[-1,-13],[5,-2],[24,-6],[29,-8],[1,-1],[62,-17],[55,-23],[4,-1],[31,-13],[1,-1],[24,-10],[36,-14],[16,-6],[136,-50],[50,-19],[103,-55],[1,-1],[8,-4],[7,-5],[7,-5],[7,-5],[7,-5],[8,-6],[7,-6],[7,-6],[7,-6],[6,-6],[1,-1],[3,-3],[3,-3],[7,-7],[7,-7],[9,-10],[5,-6],[5,-6],[5,-6],[5,-6],[5,-6],[5,-7],[5,-7],[5,-6],[4,-7],[5,-8],[5,-7],[0,-1],[2,-3],[7,-13],[7,-14],[7,-13],[7,-14],[7,-14],[7,-14],[6,-14],[6,-15],[7,-15],[6,-15],[6,-15],[5,-14],[0,-1],[2,-5],[1,-3],[1,-3],[1,-3],[1,-4],[1,-3],[1,-3],[0,-1],[25,-87],[1,-4],[1,-1],[4,-12],[40,-114],[5,-13],[35,-88],[1,-3],[0,-1],[1,-2],[1,-3],[1,-3],[0,-2],[1,-3],[1,-3],[1,-3],[0,-2],[1,-3],[1,-3],[0,-3],[1,0],[0,-3],[1,-3],[2,-10],[1,-5],[2,-6],[0,-1],[0,-1],[0,-1],[0,-1],[1,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[1,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-7],[-1,-15],[-1,-74],[-7,-70],[-1,-9],[-1,-6],[-1,-5],[-1,-6],[-1,-5],[-1,-6],[-1,-5],[-1,-5],[-1,-6],[-1,-5],[-1,-5],[-2,-5],[-1,-6],[-1,-5],[-2,-5],[-2,-10],[-2,-5],[-1,-5],[-2,-5],[-2,-5],[-2,-7],[-1,-3],[-2,-4],[-1,-3],[-1,-4],[-2,-3],[-1,-4],[-3,-5],[-2,-3],[-1,-3],[-2,-3],[-1,-3],[-2,-2],[-1,-3],[-3,-5],[-3,-4],[-2,-4],[-3,-3],[-2,-4],[-4,-4],[-2,-2],[-2,-3],[-2,-2],[-2,-2],[-2,-3],[-39,-57],[-8,-14],[-3,-6],[-2,-5],[-3,-4],[-2,-5],[-2,-5],[-2,-5],[-3,-7],[-3,-6],[-2,-6],[-2,-6],[-2,-7],[-3,-8],[-3,-7],[-2,-7],[-2,-7],[-2,-7],[-2,-8],[-2,-6],[-1,-6],[-2,-6],[0,-1],[-1,-5],[-1,-3],[-1,-4],[-2,-9],[-1,-8],[-1,-7],[-2,-8],[-1,-8],[-1,-8],[-1,-8],[-1,-7],[-1,-8],[-1,-8],[-2,-16],[-15,-136],[0,-7],[-1,-14],[-4,-65],[0,-7],[0,-9],[1,-50],[0,-18],[2,-36],[5,-3],[2,-1],[1,-1],[4,-2],[1,0],[1,-1],[3,-1],[3,-2],[6,16],[8,-12],[30,-28],[-11,-81],[0,-2],[-1,-3],[-2,-14],[-3,-21],[0,-2],[-2,-20],[-1,-11],[-13,-114],[-1,0],[-1,-1],[-2,0],[-2,0],[-3,-1],[0,-1],[4,-56],[-21,-141],[-2,0],[-13,-61],[-35,-177],[-24,-117],[-4,-40],[-3,-26],[-4,-33],[-5,-9],[1,0],[3,-6],[8,-16],[0,-5],[0,-1],[-28,-38],[-32,-45],[-10,-14],[-54,-74],[-23,-24],[2,-10],[-20,-21],[-5,-6],[-6,0],[-54,0],[-106,14],[-52,12],[-3,1],[-55,37],[-42,28],[-12,4],[-27,10],[-9,3],[-82,8],[-202,-8],[-21,2],[-21,8],[-63,34],[-1,1],[-1,0],[-1,0],[0,1],[-1,0],[-1,0],[-1,0],[-2,1],[-1,0],[-1,0],[-1,0],[-2,0],[-2,0],[-1,0],[-2,0],[-1,0],[-2,0],[0,-1],[-1,0],[-1,0],[-88,-16],[1,44],[2,26],[2,32],[-3,5],[-1,2],[0,1],[-1,1],[0,2],[-1,1],[-1,1],[0,1],[-1,1],[-1,1],[-1,1],[-1,1],[-1,2],[-1,2],[-1,0],[-2,3],[-2,2],[-1,1],[-1,2],[-1,1],[-1,1],[-1,1],[-1,1],[-1,0],[-3,3],[-3,3],[-4,3],[-3,2],[-2,2],[-2,1],[-1,1],[-2,1],[-1,1],[-2,1],[-1,1],[-2,1],[-2,1],[-1,0],[-1,1],[-1,0],[-2,1],[-1,0],[-1,0],[-1,1],[-2,0],[-30,5],[-1,0],[-1,0],[-1,0],[0,-1],[-1,0],[-2,-2],[-2,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[0,-1],[-1,0],[0,-1],[-1,0],[0,-1],[-13,-20],[-7,2],[-6,-12],[5,101],[-53,104],[-21,42],[16,31],[-10,24],[-18,-32],[-74,146],[7,21],[6,19],[-15,16],[-8,8],[-6,6],[-6,5],[-6,6],[-6,5],[-6,5],[-13,9],[-7,5],[-6,4],[-7,5],[-7,4],[-9,5],[-9,4],[-9,4],[-9,4],[-9,4],[-8,3],[-15,5],[-10,3],[-10,3],[-10,2],[-10,2],[-6,0],[-8,1],[-10,1],[-10,1],[-10,0],[-10,0],[-10,-1],[-10,0],[-5,-1],[-5,-1],[-10,-1],[-26,-4],[-3,0],[-8,-1],[-18,-2],[-29,-4],[-20,-1],[-4,-1],[-5,0],[-4,0],[-4,0],[-4,1],[-4,0],[-4,1],[-4,0],[-3,1],[-1,0],[-5,1],[-3,1],[-3,1],[-3,2],[-3,1],[-3,1],[-13,7],[-6,4],[-7,4],[-4,3],[-4,3],[-4,3],[-12,9],[-6,4],[-9,6],[-9,6],[-5,2],[-4,3],[-4,2],[-1,0],[-4,3],[-6,2],[-6,3],[-6,2],[-4,2],[-3,1],[-4,1],[-3,1],[-4,0],[-6,2],[-7,0],[-6,1],[-6,1],[-6,0],[-22,0],[-10,-1],[-4,0],[-4,-1],[-4,0],[-3,-1],[-4,-1],[-7,-1],[-4,-1],[-4,-1],[-4,0],[-13,-1],[-3,0],[-5,0],[-6,0],[-5,0],[-5,1],[-5,1],[-13,2],[-7,1],[-2,1],[-5,1],[-10,3],[-2,1],[-4,1],[-6,2],[-6,3],[-10,5],[-8,3],[-7,4],[-5,2],[-3,2],[-3,2],[-2,2],[-3,2],[-3,2],[-3,3],[-3,2],[-4,4],[-3,3],[-4,3],[-3,3],[-3,4],[-3,4],[-4,3],[-3,4],[-8,10],[-6,8],[-5,7],[-6,8],[-8,13],[-6,8],[-5,9],[-6,9],[-5,9],[-18,33],[-10,19],[-8,16],[-6,11],[-5,11],[-3,9],[-2,2],[-13,31],[-1,0],[-9,20],[-19,40],[-19,41],[-6,12],[-6,11],[-6,11],[-6,11],[-5,11],[-7,10],[-6,10],[-5,9],[-3,4],[-3,5],[-4,4],[0,1],[-3,4],[-3,4],[-12,14],[-11,14],[-10,11],[-7,8],[-1,1],[-9,10],[-8,8],[-2,-2],[-17,-13],[-15,-12],[-6,-6],[-8,-6],[-16,-12],[-75,-61],[-136,-109],[-44,-20],[-1,-1],[-11,-5],[-32,-39],[-4,-5],[-6,-7],[-33,-42],[-29,-35],[-4,-5],[-8,-9],[0,-1],[-5,-6],[-4,-5],[-6,-7],[-36,-45]],[[4230,6275],[79,-162],[14,-18],[15,-19],[43,-27],[79,-51],[90,-60],[61,-40],[151,-101],[4,-3],[1,-1],[3,-1],[156,-107],[64,-43],[173,-119]],[[5163,5523],[-29,-151],[-3,-12]],[[5131,5360],[-28,-145],[-4,-18],[-47,-238],[0,-3],[-26,-132],[-2,-4],[-9,-12],[-5,-5],[-5,-10],[-3,-8],[-7,-35],[-29,-130],[-5,-19],[-3,-12],[-1,-4],[-52,-142]],[[4905,4443],[-71,94],[-2,4],[-5,10],[-30,74],[-44,108],[-16,24],[-4,6],[0,1],[-115,180],[-38,22],[-151,89]],[[4429,5055],[-122,90],[-2,2],[-2,2],[-11,12],[-49,54],[-1,0],[-50,47],[-40,39],[-33,27],[-2,2],[-20,18],[-155,134],[-1,1]],[[3941,5483],[15,57],[2,6],[3,11],[0,1],[7,24],[36,132],[3,12],[32,117],[5,18],[5,19],[-1,2],[43,164],[3,14],[15,61],[11,50],[-6,50],[2,1],[2,1],[8,4],[2,1],[7,3],[6,3]],[[6763,1172],[-4,-5],[-3,-4],[-12,-15],[-14,-20],[-12,-19],[-5,-8],[-8,-12],[-37,-37],[-56,-57],[-23,-23],[-2,-2],[-4,-4],[-4,-4],[-1,-1],[-14,-14],[-6,-7],[-4,-3],[0,-1],[-4,-3],[-68,-69],[-11,-11],[-6,-6],[-57,-58],[-31,-82],[-17,-16],[-43,-40],[-6,-5],[-2,-3],[-1,-1],[-2,-1],[-10,-10],[-3,-3],[-30,-27],[-8,-8],[-50,-47],[-19,-17],[-10,-9],[-1,-1],[-1,-1],[-56,-52],[-12,-12],[-4,-3],[-11,-11],[-5,-4],[-1,-1],[-7,-7],[-10,-9],[-9,-9],[-6,-5],[-76,-71],[-4,-3],[-6,-6],[-9,-9],[-19,-17],[-40,-38],[-3,-2],[-11,-10],[-40,-36],[-15,-14],[-15,-13],[-66,-60],[-19,-17],[-25,-22],[-2,-2],[-1,-1],[-32,-28],[-94,-3],[-11,-1],[-9,0],[-5,0],[-9,-1],[-15,0],[-44,-1],[-28,-1],[-13,-1],[-40,-1],[-8,0],[-8,-1],[-8,0],[-1,0],[-9,0],[-2,0],[-88,180],[-7,16],[-1,1],[-3,8],[-3,5],[-21,44],[-2,4],[-1,1],[-17,37],[0,-1],[-1,0],[-27,-38],[-1,-1],[-7,-9],[-7,-10],[-30,-42],[-4,-5],[-1,-2],[-26,-35],[-30,-41],[-15,-22],[-1,-1],[-3,-4],[-3,-4],[-67,-94],[-10,3],[-2,0],[-52,15],[-19,5],[-8,2],[-11,3],[3,-61],[-1,1],[-2,2],[-3,3],[-2,3],[-13,13]],[[4880,22],[1,42],[0,2],[0,13],[1,51],[0,7],[0,12],[1,14],[0,7],[0,17],[0,12],[1,14],[0,7],[0,7],[1,11],[1,14],[0,4],[3,28],[16,156],[1,18],[-30,94],[-49,97],[-8,32],[-11,44],[-7,27],[-13,70],[-12,113],[1,35],[3,75],[7,163],[-1,28],[0,10],[-1,12],[0,7],[-1,54],[0,102],[0,27],[-2,48],[-3,32],[-6,69],[-4,101],[-2,128],[-2,46],[0,43],[-1,22],[0,16],[5,73],[13,196],[13,232],[0,2],[7,176]],[[4802,2632],[98,-59],[103,-62],[3,-1],[5,-2],[73,-24],[109,-35],[4,-1],[1,0],[1,1],[1,1],[1,0],[35,31],[107,97],[20,19],[45,40],[34,31],[55,50],[92,83],[8,7],[9,8],[12,45],[97,372],[3,10],[2,11],[3,10],[3,11],[1,3],[2,10],[0,4],[0,1],[1,4],[1,5],[0,3],[1,4],[1,4],[0,1],[4,8],[0,2],[2,4],[2,4],[36,58]],[[4429,5055],[-19,-97],[34,-28],[-1,-3],[0,-1],[-20,-70],[-44,-152],[-21,-79],[-13,-49],[-6,-28],[-9,-58],[-1,-5],[-43,-194],[-3,-14],[-36,-91],[-50,-23],[-14,-25],[-9,-17],[-55,-65],[-47,-54],[-34,-47],[-16,-22],[-31,-44],[-65,-96],[-15,-22],[-4,-6],[-29,-39],[-12,-12],[-42,-46],[-60,-59]],[[2676,4927],[58,128],[12,30],[9,20],[7,15],[30,61],[38,78],[12,25],[12,22],[5,8],[14,20],[17,23],[14,16],[5,8],[14,16],[5,7],[5,6],[3,3],[6,6],[1,1],[5,6],[2,2],[3,2],[5,4],[1,2],[2,2],[9,8],[8,7],[7,6],[1,0],[4,4],[15,12],[27,17],[4,3],[11,7],[12,6],[22,9],[12,4],[16,4],[24,3],[2,1],[20,2]],[[3155,5531],[5,1],[1,0],[7,0],[1,0],[4,0],[136,7],[9,0],[190,10],[1,0],[4,0],[199,10],[46,2],[84,5],[2,0],[3,-1],[3,-1],[3,-2],[28,-25],[3,-3],[11,-10],[3,-3],[19,-16],[24,-22]],[[4905,4443],[-13,-35],[-18,-113],[-27,-159],[-20,-119],[-1,-6],[-48,-224],[-22,-102],[-4,-16],[-14,-73],[-10,-50],[-52,-269],[-32,-168],[-17,-87],[-19,-95],[-1,-5],[-2,-4],[-1,-4],[-2,-3],[-1,-3],[-2,-2],[-3,-2],[-2,-2],[-1,0],[-1,-1],[-3,0],[-2,1],[0,-18],[-1,-23],[0,-25],[-1,-56]],[[4585,2780],[-119,111],[-111,104],[-26,24],[-25,24],[-111,103],[-29,28],[-68,64]],[[3155,5531],[-3,122],[-54,61],[-10,25],[-2,5],[0,1],[-23,210],[-4,36],[-13,127],[-25,122],[-3,10],[-29,142],[-1,4],[0,1],[-54,175],[-34,109],[-1,4],[-7,24],[-1,2],[0,1],[0,1],[-1,0],[0,1],[0,1],[0,1],[-1,0],[0,1],[0,1],[0,1],[-1,2]],[[7124,3756],[1,14],[1,16],[4,55],[-1,10],[-12,99],[-7,60],[-1,7],[0,1],[0,4],[-1,4],[0,2],[-1,5],[-9,84],[-2,10],[0,2],[0,1],[0,1],[-2,9],[-28,100],[-21,77],[-3,8],[-1,6],[-34,124],[-5,18],[-2,5],[-1,6],[-40,143],[-7,28],[-18,75],[-1,4],[-80,117],[-8,12],[-4,4],[-89,75],[-7,6],[-3,3],[-2,2],[-1,2],[-1,1],[-2,5],[-1,2],[-1,2],[0,1],[-1,2],[-18,108],[-5,28],[-4,25],[-6,35],[-14,82],[-30,180],[-4,23],[-2,8],[-1,4],[0,2],[0,2],[-1,1],[-1,7],[-3,9],[-1,5],[-2,4],[-48,119],[-34,86],[-9,20],[-3,7],[-2,6],[0,1],[-6,14],[-27,67],[-16,39],[-5,14],[-4,11],[-6,30],[-2,9],[-4,23],[-2,10],[-1,9],[-1,3],[-4,9],[-7,15],[-88,188],[-3,6],[-51,112],[-5,12],[-32,72],[-2,3],[-3,8],[-12,27],[-34,93],[-4,11],[-2,5],[-1,3]],[[7601,7258],[1,-3],[5,-17],[2,-10],[5,-15],[2,-7],[8,-30],[7,-23],[2,-8],[0,-1],[2,-5],[0,-1],[4,-14],[2,-6],[17,-60],[1,-4],[1,-1],[5,-19],[0,-1],[1,-1],[0,-3],[2,-6],[0,-1],[1,-3],[2,-22],[6,-55],[1,-15],[1,-10],[3,-28],[1,-5],[13,-123],[0,-6],[11,-100],[0,-1],[0,-4],[0,-1],[1,-5],[6,-61],[0,-5],[1,-10],[1,-32],[2,-47],[2,-44],[2,-55],[0,-12],[1,-17],[0,-17],[5,-119],[1,-49],[1,-18],[-2,-144],[0,-6],[0,-23],[0,-16],[0,-26],[0,-6],[-1,-21],[0,-15],[0,-55],[-1,-31],[-1,-87],[0,-4],[0,-62],[-1,-61],[2,-44],[0,-3],[2,-40],[0,-6],[2,-28],[3,-83],[2,-31],[20,-344],[1,-17],[1,-6],[3,-46],[0,-10],[1,-16],[19,-302],[2,-44],[1,-6],[0,-1],[0,-1],[16,-263],[4,-73],[1,-8],[0,-8],[0,-1],[1,-3],[6,-103],[1,-13],[1,-21],[11,-173],[1,-17],[0,-3],[0,-1],[4,-51],[-11,-145],[-2,-29],[0,-7],[-2,-30],[-1,-7],[-1,-17],[-1,-16]],[[4802,2632],[-95,56],[-2,1],[-120,91]],[[4905,4443],[4,-4],[4,-3],[82,-76],[3,-1],[3,-3],[62,-79],[32,-37],[5,-6],[10,-12],[10,-9],[1,-2],[3,-2],[3,-3],[7,-6],[56,-41],[37,-23],[28,-11],[21,-9],[67,-56],[6,-5],[4,-4],[2,-2],[95,-87],[3,-2],[3,-3],[5,-4],[3,-3],[9,-8],[13,-14],[12,-13],[7,-9],[9,-11],[5,-7],[5,-7],[40,-64],[30,-46],[15,-28],[24,-42],[79,-171]],[[1582,2116],[-7,2],[-54,18],[-2,1],[-2,0],[-8,3],[-170,56],[-10,3],[-19,7],[-45,15],[-39,129],[-23,76],[-2,8],[-24,76],[-28,95],[-27,89],[-1,35],[-15,381],[-1,45],[20,118],[19,109],[0,1],[11,60],[3,21],[0,1],[-75,5],[-1,0],[-5,4],[-5,3],[-2,2],[-92,67],[-13,10],[-6,4],[-47,34],[-10,8],[-76,55],[-57,41],[-12,9],[-9,6],[0,1],[-1,3],[-6,15],[-10,24],[-6,15],[-6,15],[0,1],[-10,23],[-1,3],[-1,3],[-3,6],[-42,104],[-2,4],[-1,4],[-2,4],[-1,2],[-1,1],[-1,3],[-1,1],[-1,3],[-1,2],[-1,2],[-1,1],[-1,2],[-1,2],[-1,2],[-2,2],[-2,4],[-2,2],[-2,3],[-2,2],[-1,2],[-2,2],[-1,2],[-1,1],[-1,1],[-1,1],[-1,1],[-1,1],[-1,0],[-1,1],[-1,1],[-1,1],[-1,1],[-1,0],[-1,1],[-10,6],[-3,2],[-36,23],[-2,1],[-5,3],[-5,3],[-1,1],[-5,3],[-6,4],[-2,1],[-397,248],[-45,28],[-1,0],[0,1],[-2,1],[-16,10],[-24,14],[-56,35],[-1,1],[-3,59],[3,127],[7,103],[13,106],[14,110],[17,123],[10,65],[4,31],[27,144],[9,41],[8,39],[19,105],[39,300],[16,69],[0,1],[38,125],[12,39],[2,8],[28,85],[53,157],[8,23],[17,14],[124,98],[9,8],[5,4],[60,45],[37,26],[4,3],[41,28],[3,2],[3,2],[3,2],[3,3],[3,2],[3,3],[3,2],[2,3],[3,3],[1,1],[2,2],[1,2],[2,2],[1,2],[2,2],[1,2],[4,5],[2,4],[2,3],[2,3],[2,4],[2,3],[7,13],[4,8],[4,7],[3,7],[4,8],[4,8],[32,73],[45,104],[2,3],[5,13],[93,259],[160,-113],[179,-125],[35,-25],[10,8],[0,1],[6,5],[8,7],[52,284],[1,5],[69,385],[2,2],[16,17],[1,1],[1,1],[108,-38],[266,-94],[94,-33],[1,-1],[1,0],[3,-1],[16,-6],[3,-1],[134,-47],[131,-46],[9,7],[8,7],[7,6],[11,10],[10,8],[24,20],[12,10],[3,3],[7,6],[2,2],[1,0]],[[5688,5997],[2,-5],[1,-1],[2,-3],[4,-7],[3,-5],[3,-5],[1,-2],[1,-1],[0,-3],[3,-10],[2,-12],[2,-11],[1,-4],[2,-6],[0,-2],[1,-4],[1,-5],[5,-25],[3,-12],[0,-3],[27,-125],[16,-76],[1,-7],[36,-167],[0,-3],[1,-5],[1,-5],[0,-4],[6,-65],[8,-96],[7,-78],[8,-98],[6,-75],[18,-216],[7,-76],[10,-126],[1,-12]],[[5878,4637],[-165,78],[-6,6],[-60,52],[-44,40],[-43,50],[-54,73],[-2,4],[-21,39],[-18,38],[-59,121],[-2,1],[-138,132],[-5,6],[-69,44],[-61,39]],[[5163,5523],[24,118],[18,94],[23,115],[18,92],[52,266]],[[4880,22],[-37,37],[-55,29],[-2,0],[-19,3],[-131,20],[-36,5],[-137,21],[-81,32],[1,4],[1,5],[0,1],[0,1],[4,22],[19,102],[0,1],[1,0],[-24,14],[-1,1],[-7,4],[-57,35],[-33,21],[-5,3],[-3,2],[-7,5],[-3,1],[-77,50],[-6,4],[-4,2],[-4,3],[-35,22],[-9,6],[-1,0],[-9,6],[-60,38],[-85,54],[-34,21],[-24,16],[-8,4],[-6,4],[-4,3],[-1,0],[-1,1],[-11,7],[-3,2],[-61,38],[-26,17],[-75,46],[-36,24],[-6,3],[-1,1],[-1,1],[-13,8],[-4,2],[-126,80],[-30,18],[-30,19],[-15,10],[-5,3],[-24,15],[-2,1],[-7,5],[-48,30],[-10,5],[-22,14],[-73,47],[-18,12],[-64,41],[-6,4],[-2,1],[-10,7],[-1,1],[-9,5],[-4,3],[-14,8],[-1,1],[-2,1],[-1,1]],[[5904,4341],[0,1],[-1,2],[0,8],[-1,4],[0,2],[-4,49],[-20,230]],[[4317,9869],[7,0],[9,1],[32,2],[7,0],[127,8],[3,1],[8,0],[4,1],[9,0],[51,3],[24,2],[11,0],[1,1],[2,0],[13,0],[4,1],[7,0],[20,1],[46,3],[6,0],[3,0],[68,4],[71,4],[2,0],[16,1],[17,1],[6,0],[22,2],[8,0],[61,4],[27,1],[35,2],[2,0],[3,1],[105,6],[32,2],[6,0],[13,1],[3,0],[1,0],[3,0],[3,0],[88,5],[53,2],[37,2],[48,3],[24,2],[2,0],[8,0],[6,1],[4,0],[11,1],[4,0],[7,0],[45,3],[37,2],[28,2],[8,0],[51,3],[3,0],[19,1],[11,1],[52,3],[4,0],[66,4],[2,0],[61,4],[16,1],[29,1],[12,1]]],"transform":{"scale":[0.000024570708180673968,0.000008659389799815013],"translate":[2.2240784472468698,48.81557666781795]}} -------------------------------------------------------------------------------- /example/data/metriques-paris.csv: -------------------------------------------------------------------------------- 1 | Code géographique,Salaire net horaire moyen en 2012 (€),Taux de pauvreté-Ensemble,Part Revenus du patrimoine 2 | 75101,24,"10,3","27,7" 3 | 75102,"21,9","15,8","18,2" 4 | 75103,"22,9","13,9",22 5 | 75104,23,"11,5",28 6 | 75105,"23,9","9,6","24,5" 7 | 75106,"30,1","8,6","38,8" 8 | 75107,34,"7,9","42,2" 9 | 75108,"30,9","9,4",35 10 | 75109,"23,6","12,1","20,2" 11 | 75110,"18,2","19,3","13,7" 12 | 75111,"18,6","16,6",15 13 | 75112,19,"14,2","15,9" 14 | 75113,"17,1","16,9","13,6" 15 | 75114,"19,6","12,5","19,3" 16 | 75115,"21,8","10,7","19,3" 17 | 75116,"31,5","10,2","38,5" 18 | 75117,"23,7","14,2","23,1" 19 | 75118,"16,9","23,9","12,8" 20 | 75119,"15,2","24,7","11,8" 21 | 75120,"15,9","22,3","11,2" 22 | -------------------------------------------------------------------------------- /example/data/source.md: -------------------------------------------------------------------------------- 1 | http://www.insee.fr/fr/bases-de-donnees/default.asp?page=statistiques-locales/bases_cc_2.htm 2 | -------------------------------------------------------------------------------- /example/dist/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laem/async-cartogram/17bbbbefa2b1bb7c0705dd3cd1460b88c88206d2/example/dist/favicon.ico -------------------------------------------------------------------------------- /example/dist/images/latoureiffel.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /example/dist/images/yeoman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laem/async-cartogram/17bbbbefa2b1bb7c0705dd3cd1460b88c88206d2/example/dist/images/yeoman.png -------------------------------------------------------------------------------- /example/dist/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 15 |
16 |

If you can see this, something is broken (or JS is not enabled)!!.

17 |
18 | 19 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "asynccartogram", 3 | "version": "0.0.0", 4 | "description": "", 5 | "repository": "", 6 | "private": true, 7 | "src": "src", 8 | "test": "test", 9 | "dist": "dist", 10 | "mainInput": "AsyncCartogramApp", 11 | "mainOutput": "main", 12 | "dependencies": { 13 | "d3": "^3.5.5", 14 | "normalize.css": "~3.0.3", 15 | "react": "~0.12.2" 16 | }, 17 | "devDependencies": { 18 | "grunt": "~0.4.5", 19 | "load-grunt-tasks": "~0.6.0", 20 | "grunt-contrib-connect": "~0.8.0", 21 | "webpack": "~1.9.11", 22 | "grunt-webpack": "~1.0.8", 23 | "style-loader": "~0.8.0", 24 | "url-loader": "~0.5.5", 25 | "css-loader": "~0.9.0", 26 | "json-loader": "0.5.2", 27 | "worker-loader": "0.6.0", 28 | "webpack-dev-server": "~1.9.0", 29 | "grunt-open": "~0.2.3", 30 | "jshint-loader": "~0.8.0", 31 | "jsxhint-loader": "~0.2.0", 32 | "grunt-contrib-copy": "~0.5.0", 33 | "babel": "^5.6.2", 34 | "babel-loader": "^5.1.4", 35 | "grunt-contrib-clean": "~0.6.0", 36 | "react-hot-loader": "^1.2.7", 37 | "dsv-loader": "^1.0.0", 38 | "topojson": "^1.6.19", 39 | "underscore.deferred": "^0.4.0" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /example/src/components/AsyncCartogramApp.js: -------------------------------------------------------------------------------- 1 | var React = require('react/addons'); 2 | var ReactTransitionGroup = React.addons.TransitionGroup; 3 | 4 | 5 | // CSS 6 | require('normalize.css'); 7 | require('../styles/main.css'); 8 | 9 | require('d3') 10 | var topojson = require('topojson') 11 | var AsyncCartogram = require('../../../dist/async-cartogram.js') 12 | var topojsonData = require('json!../../data/arrondissements.json') 13 | 14 | var parisData = require('dsv!../../data/metriques-paris.csv') 15 | // Transform this raw csv for the cartogram input 16 | var metrics = Object.keys(parisData[0]) 17 | metrics.shift() 18 | var data = {} 19 | metrics.map(function(metric){ 20 | data[metric] = {} 21 | parisData.map(function(line){ 22 | data[metric][line['Code géographique']] = +line[metric].replace(/,/g, '.') 23 | }) 24 | }) 25 | // end transform 26 | 27 | //Some vars for our d3 svg map 28 | var scaling = 200000, center = [2.2940220935081865, 48.874100811293694] 29 | 30 | var AsyncCartogramApp = React.createClass({ 31 | getInitialState: function(){ 32 | return {metric: null} 33 | }, 34 | render: function() { 35 | return ( 36 |
37 | 44 |
A map will replace me --------> :-o
45 |
This is just a demo of async-cartogram applied to Paris for three metrics
46 |
47 | ); 48 | }, 49 | 50 | mouseOverLi: function(m){ 51 | this.setState({metric: m}) 52 | }, 53 | 54 | componentDidUpdate: function(){ 55 | this.displayMap() 56 | }, 57 | 58 | componentDidMount: function(){ var _this = this; 59 | 60 | // compute the cartogram geometries for each metric, then cache them. 61 | 62 | var promiseOfGeos = AsyncCartogram({ 63 | topology: topojsonData, 64 | geometries: topojsonData.objects.arrondissements.geometries, 65 | projection: { 66 | name: 'mercator', //TODO 67 | scaling: scaling, 68 | center: center 69 | } 70 | }, 71 | data, 72 | 'c_arinsee' 73 | ); 74 | 75 | // Keep track of the progress (incremented for each task success) 76 | promiseOfGeos.progress(function(value){ 77 | document.querySelector('#playground').innerHTML = Math.round(value * 100) + "%" 78 | }) 79 | 80 | // All is done 81 | promiseOfGeos.then(function(a){ 82 | //cache the results 83 | _this.computedCartograms = a 84 | _this.displayMap() 85 | }).fail(function( err ){ 86 | console.log(err.message) // "Oops!" 87 | }) 88 | }, 89 | 90 | displayMap: function(){ 91 | var playground = document.querySelector('#playground') 92 | playground.innerHTML = '' 93 | var svg = d3.select(playground).append("svg").attr("id", "leSVG") 94 | var path, featureCollection; 95 | if (this.state.metric == null){// Show the real map 96 | 97 | var projection = d3.geo.mercator() 98 | .center(center) 99 | .scale(scaling) 100 | 101 | path = d3.geo.path().projection(projection) 102 | //convert to geojson 103 | featureCollection = topojson.feature(topojsonData, topojsonData.objects.arrondissements); 104 | } else { //Draw the population cartogram 105 | var a = this.computedCartograms 106 | featureCollection = a[this.state.metric] //get first task result 107 | 108 | // path with identity projection 109 | path = d3.geo.path() 110 | .projection(null); 111 | } 112 | 113 | // Now display the cartogram 114 | 115 | var nodes = [] 116 | 117 | //computing the area centroids from their geometry 118 | featureCollection.features.forEach((d, i) => { 119 | var centroid = path.centroid(d); 120 | if (centroid.some(isNaN)) { 121 | return; 122 | } 123 | centroid.x = centroid[0]; 124 | centroid.y = centroid[1]; 125 | centroid.feature = d; 126 | nodes.push(centroid); 127 | }); 128 | 129 | //adding the map areas 130 | var areas = svg.append('g').attr('class', 'nodes').selectAll("g") 131 | .data(nodes) 132 | .enter().append("g") 133 | areas 134 | .append("path") 135 | .attr("d", function(d) { return path(d.feature); }) 136 | 137 | 138 | //let's add names to our map areas 139 | areas 140 | .append("text") 141 | .attr("x", d => d.x) 142 | .attr("y", d => d.y) 143 | .text(d => d.feature.properties.c_ar) 144 | 145 | //let's add la Tour Eiffel 146 | areas.each(function(d){ 147 | if (d.feature.properties.c_ar == 7){ 148 | d3.select(this) 149 | .append("image") 150 | .attr({ 151 | "x": d => d.x - 35, 152 | "y": d => d.y - 35, 153 | "width": "20px", 154 | "height": "20px", 155 | "xlink:href": "../images/latoureiffel.svg" 156 | }) 157 | } 158 | }) 159 | } 160 | }); 161 | React.render(, document.getElementById('content')); // jshint ignore:line 162 | 163 | module.exports = AsyncCartogramApp; 164 | -------------------------------------------------------------------------------- /example/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laem/async-cartogram/17bbbbefa2b1bb7c0705dd3cd1460b88c88206d2/example/src/favicon.ico -------------------------------------------------------------------------------- /example/src/images/latoureiffel.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /example/src/images/yeoman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laem/async-cartogram/17bbbbefa2b1bb7c0705dd3cd1460b88c88206d2/example/src/images/yeoman.png -------------------------------------------------------------------------------- /example/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 15 |
16 |

If you can see this, something is broken (or JS is not enabled)!!.

17 |
18 | 19 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /example/src/styles/main.css: -------------------------------------------------------------------------------- 1 | /* Stiziles */ 2 | 3 | html, body { 4 | background: #fff; 5 | font-family: 'Fira Sans'; 6 | font-weight: 300; 7 | } 8 | 9 | .main { 10 | width: 100%; 11 | height: 100%; 12 | background: #fff; 13 | color: #333; 14 | } 15 | 16 | /* svg */ 17 | 18 | #playground { 19 | width: 80%; 20 | height: 600px; 21 | margin-left: 10% 22 | } 23 | 24 | #playground svg { 25 | width: 100%; 26 | height: 100%; 27 | } 28 | 29 | 30 | path { 31 | fill: #1ABC9C; 32 | stroke: white; 33 | stroke-width: 1.5px; 34 | } 35 | 36 | line { 37 | stroke: blue; 38 | opacity: 0.1 39 | } 40 | 41 | svg text { 42 | fill: white; 43 | font-weight: 500; 44 | stroke: #1ABC9C; 45 | stroke-width: 0.8px 46 | } 47 | 48 | /* Menu */ 49 | 50 | ul#menu { 51 | list-style-type: none; 52 | width: 80%; 53 | margin-left: 50%; 54 | transform: translateX(-50%); 55 | } 56 | 57 | #menu li { 58 | display: inline; 59 | margin-right: 5%; 60 | padding: 8px; 61 | font-size: 24px; 62 | line-height: 24px; 63 | cursor: pointer; 64 | border: 1px solid white; 65 | color: #1ABC9C; 66 | background: white; 67 | border: 1px solid #1ABC9C; 68 | } 69 | 70 | #menu li:hover { 71 | color: white; 72 | background: #1ABC9C; 73 | } 74 | 75 | #disclaimer { 76 | position: absolute; 77 | bottom: 1%; 78 | left: 50%; 79 | transform: translateX(-50%); 80 | } 81 | 82 | #disclaimer a { 83 | background: #34495e; 84 | color:white; 85 | text-decoration: none; 86 | padding: 0 2px 0 2px 87 | } 88 | -------------------------------------------------------------------------------- /example/webpack.config.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Webpack development server configuration 3 | * 4 | * This file is set up for serving the webpack-dev-server, which will watch for changes and recompile as required if 5 | * the subfolder /webpack-dev-server/ is visited. Visiting the root will not automatically reload. 6 | */ 7 | 'use strict'; 8 | var webpack = require('webpack'); 9 | 10 | module.exports = { 11 | 12 | output: { 13 | filename: 'main.js', 14 | publicPath: '/assets/' 15 | }, 16 | 17 | cache: true, 18 | debug: true, 19 | devtool: false, 20 | entry: [ 21 | 'webpack/hot/only-dev-server', 22 | './src/components/AsyncCartogramApp.js' 23 | ], 24 | 25 | stats: { 26 | colors: true, 27 | reasons: true 28 | }, 29 | 30 | resolve: { 31 | extensions: ['', '.js', '.jsx'], 32 | alias: { 33 | 'styles': __dirname + '/src/styles', 34 | 'mixins': __dirname + '/src/mixins', 35 | 'components': __dirname + '/src/components/' 36 | } 37 | }, 38 | module: { 39 | preLoaders: [], 40 | loaders: [{ 41 | test: /\.(js|jsx)$/, 42 | exclude: /node_modules/, 43 | loader: 'react-hot!babel-loader' 44 | }, { 45 | test: /\.css$/, 46 | loader: 'style-loader!css-loader' 47 | }, { 48 | test: /\.(png|jpg|woff|woff2)$/, 49 | loader: 'url-loader?limit=8192' 50 | }] 51 | }, 52 | 53 | plugins: [ 54 | new webpack.HotModuleReplacementPlugin(), 55 | new webpack.NoErrorsPlugin() 56 | ] 57 | 58 | }; 59 | -------------------------------------------------------------------------------- /example/webpack.dist.config.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Webpack distribution configuration 3 | * 4 | * This file is set up for serving the distribution version. It will be compiled to dist/ by default 5 | */ 6 | 7 | 'use strict'; 8 | 9 | var webpack = require('webpack'); 10 | 11 | module.exports = { 12 | 13 | output: { 14 | publicPath: '/assets/', 15 | path: 'dist/assets/', 16 | filename: 'main.js' 17 | }, 18 | 19 | debug: false, 20 | devtool: false, 21 | entry: './src/components/AsyncCartogramApp.js', 22 | 23 | stats: { 24 | colors: true, 25 | reasons: false 26 | }, 27 | 28 | plugins: [ 29 | new webpack.optimize.DedupePlugin(), 30 | new webpack.optimize.UglifyJsPlugin(), 31 | new webpack.optimize.OccurenceOrderPlugin(), 32 | new webpack.optimize.AggressiveMergingPlugin() 33 | ], 34 | 35 | resolve: { 36 | extensions: ['', '.js'], 37 | alias: { 38 | 'styles': __dirname + '/src/styles', 39 | 'mixins': __dirname + '/src/mixins', 40 | 'components': __dirname + '/src/components/' 41 | } 42 | }, 43 | 44 | module: { 45 | preLoaders: [{ 46 | test: /\.js$/, 47 | exclude: /node_modules/, 48 | loader: 'jsxhint' 49 | }], 50 | 51 | loaders: [{ 52 | test: /\.js$/, 53 | exclude: /node_modules/, 54 | loader: 'babel-loader' 55 | }, { 56 | test: /\.css$/, 57 | loader: 'style-loader!css-loader' 58 | }, { 59 | test: /\.(png|jpg|woff|woff2)$/, 60 | loader: 'url-loader?limit=8192' 61 | }] 62 | } 63 | }; 64 | -------------------------------------------------------------------------------- /interestingPapers/Dougeniketal1985.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laem/async-cartogram/17bbbbefa2b1bb7c0705dd3cd1460b88c88206d2/interestingPapers/Dougeniketal1985.pdf -------------------------------------------------------------------------------- /interestingPapers/IDEAS00.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laem/async-cartogram/17bbbbefa2b1bb7c0705dd3cd1460b88c88206d2/interestingPapers/IDEAS00.pdf -------------------------------------------------------------------------------- /interestingPapers/TD-54FMRT.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laem/async-cartogram/17bbbbefa2b1bb7c0705dd3cd1460b88c88206d2/interestingPapers/TD-54FMRT.pdf -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "async-cartogram", 3 | "version": "0.1.0", 4 | "description": "", 5 | "repository": "", 6 | "private": true, 7 | "src": "src", 8 | "dist": "dist", 9 | "mainInput": "cartogramaster", 10 | "mainOutput": "main", 11 | "dependencies": {}, 12 | "devDependencies": { 13 | "webpack": "~1.9.11", 14 | "style-loader": "~0.8.0", 15 | "url-loader": "~0.5.5", 16 | "css-loader": "~0.9.0", 17 | "json-loader": "0.5.2", 18 | "worker-loader": "0.6.0", 19 | "babel": "^5.6.2", 20 | "babel-loader": "^5.1.4", 21 | "react-hot-loader": "^1.2.7", 22 | "topojson": "^1.6.19", 23 | "underscore.deferred": "^0.4.0" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/cartogram-helpers.js: -------------------------------------------------------------------------------- 1 | var Helpers = {} 2 | 3 | Helpers.copy = function(o) { 4 | return (o instanceof Array) 5 | ? o.map(Helpers.copy) 6 | : (typeof o === "string" || typeof o === "number") 7 | ? o 8 | : copyObject(o); 9 | } 10 | 11 | function copyObject(o) { 12 | var obj = {}; 13 | for (var k in o) obj[k] = Helpers.copy(o[k]); 14 | return obj; 15 | } 16 | 17 | //Grouping cosArctan and sinArctan (see below) 18 | Helpers.arctans = function(dx, dy){ 19 | var div = dx/dy, 20 | sqrt = Math.sqrt(1+(div*div)), 21 | signedSqrt = (dy > 0) ? sqrt : -sqrt, 22 | cos = 1 / signedSqrt, 23 | sin = div * cos; 24 | 25 | return { 26 | cos: cos, 27 | sin: sin 28 | } 29 | } 30 | 31 | /* 32 | 33 | function cosArctan(dx,dy){ 34 | var div = dx/dy; 35 | return (dy>0)? 36 | (1/Math.sqrt(1+(div*div))): 37 | (-1/Math.sqrt(1+(div*div))); 38 | } 39 | 40 | function sinArctan(dx,dy){ 41 | var div = dx/dy; 42 | return (dy>0)? 43 | (div/Math.sqrt(1+(div*div))): 44 | (-div/Math.sqrt(1+(div*div))); 45 | } 46 | 47 | */ 48 | 49 | // TODO what's this method for ? To optimize 50 | Helpers.object = function(arcs, o) { 51 | function arc(i, points) { 52 | if (points.length) points.pop(); 53 | for (var a = arcs[i < 0 ? ~i : i], k = 0, n = a.length; k < n; ++k) { 54 | points.push(a[k]); 55 | } 56 | if (i < 0) reverse(points, n); 57 | } 58 | 59 | function line(arcs) { 60 | var points = []; 61 | for (var i = 0, n = arcs.length; i < n; ++i) arc(arcs[i], points); 62 | return points; 63 | } 64 | 65 | function polygon(arcs) { 66 | return arcs.map(line); 67 | } 68 | 69 | function geometry(o) { 70 | o = Object.create(o); 71 | o.properties = o.properties; // TODO possible duplicate 72 | o.coordinates = geometryType[o.type](o.arcs); 73 | //type is in o's prototype, which will be lost by worker.postMessage 74 | o.type = o.type 75 | return o; 76 | } 77 | var geometryType = { 78 | LineString: line, 79 | MultiLineString: polygon, 80 | Polygon: polygon, 81 | MultiPolygon: function(arcs) { return arcs.map(polygon); } 82 | }; 83 | 84 | return o.type === "GeometryCollection" 85 | ? (o = Object.create(o), o.geometries = o.geometries.map(geometry), o) 86 | : geometry(o); 87 | } 88 | 89 | 90 | function reverse(array, n) { 91 | var t, j = array.length, i = j - n; while (i < --j) t = array[i], array[i++] = array[j], array[j] = t; 92 | } 93 | 94 | Helpers.properties = function(obj) { 95 | return obj.properties || {}; 96 | } 97 | 98 | Helpers.transformer = function(tf) { 99 | var kx = tf.scale[0], 100 | ky = tf.scale[1], 101 | dx = tf.translate[0], 102 | dy = tf.translate[1]; 103 | 104 | function transform(c) { 105 | return [c[0] * kx + dx, c[1] * ky + dy]; 106 | } 107 | 108 | transform.invert = function(c) { 109 | return [(c[0] - dx) / kx, (c[1]- dy) / ky]; 110 | }; 111 | 112 | return transform; 113 | }; 114 | 115 | module.exports = Helpers 116 | -------------------------------------------------------------------------------- /src/cartogram-worker.js: -------------------------------------------------------------------------------- 1 | //Use a partial d3 build, that doesn't need the DOM. 2 | var d3f = require('../d3.geo/d3f.js') 3 | var Helpers = require('./cartogram-helpers') 4 | 5 | 6 | if (typeof onmessage !== 'undefined'){ 7 | onmessage = messaged 8 | } else { 9 | function Wo(){} 10 | Wo.prototype.postMessage = messaged 11 | Wo.prototype.terminate = function(){} 12 | module.exports = Wo 13 | } 14 | 15 | function messaged(event) { 16 | var data = event 17 | if (typeof event.data!== 'undefined') data = event.data 18 | 19 | if (data.do === 'carto'){ 20 | 21 | var geo = data.geo, 22 | topology = geo.topology, 23 | geometries = geo.geometries, 24 | path = geo.path, 25 | translation = geo.translation, 26 | projectionName = geo.projection.name, 27 | scaling = geo.projection.scaling, 28 | center = geo.projection.center, 29 | translation = geo.projection.translation; 30 | 31 | var values = data.values, 32 | featureProperty = data.featureProperty, 33 | task = data.task; 34 | 35 | 36 | 37 | // copy it first 38 | topology = Helpers.copy(topology); 39 | 40 | 41 | 42 | // objects are projected into screen coordinates 43 | // project the arcs into screen space 44 | 45 | 46 | var projection = 47 | d3f.geo[projectionName]() 48 | .scale(scaling); 49 | 50 | if (center != null) projection.center(center) 51 | if (translation != null) projection.translate(translation) 52 | 53 | var tf = Helpers.transformer(topology.transform),x,y,nArcVertices,vI,out1,nArcs=topology.arcs.length,aI=0, 54 | projectedArcs = new Array(nArcs); 55 | while(aI < nArcs){ 56 | x = 0; 57 | y = 0; 58 | nArcVertices = topology.arcs[aI].length; 59 | vI = 0; 60 | out1 = new Array(nArcVertices); 61 | while( vI < nArcVertices){ 62 | topology.arcs[aI][vI][0] = (x += topology.arcs[aI][vI][0]); 63 | topology.arcs[aI][vI][1] = (y += topology.arcs[aI][vI][1]); 64 | out1[vI] = projection(tf(topology.arcs[aI][vI])); 65 | vI++; 66 | } 67 | projectedArcs[aI++]=out1; 68 | 69 | } 70 | 71 | // path with identity projection 72 | var path = d3f.geo.path() 73 | .projection(null); 74 | 75 | 76 | var objects = Helpers.object(projectedArcs, {type: "GeometryCollection", geometries: geometries}) 77 | .geometries.map(function(geom) { 78 | return { 79 | type: "Feature", 80 | id: geom.id, 81 | properties: Helpers.properties.call(null, geom, topology), 82 | geometry: geom 83 | }; 84 | }); 85 | 86 | function value(d){ 87 | return values[d.properties[featureProperty]] 88 | } 89 | 90 | var objectValues = objects.map(value), 91 | totalValue = objectValues.reduce(function(a,b){return a + b;}); 92 | 93 | var iterations = 8; 94 | 95 | 96 | //console.time("processing:" + task) 97 | var i = 0; 98 | while (i++ < iterations) { 99 | 100 | //var areas = objects.map(path.area) 101 | //var totalArea = areas.reduce(function(a,b){return a + b}), 102 | var areas = [], totalArea = 0; 103 | for (var k = 0; k < objects.length; k++){ 104 | var area = path.area(objects[k]) 105 | areas.push(area) 106 | totalArea += area 107 | } 108 | 109 | var sizeErrorsTot = 0, 110 | sizeErrorsNum = 0; 111 | 112 | ///for i = 1 to n do 113 | var meta = [] 114 | for (var j = 0; j < objects.length; j++){ 115 | var o = objects[j], 116 | area = Math.abs(areas[j]), // XXX: why do we have negative areas? 117 | v = +objectValues[j], 118 | ///Compute AD i , the desired area of the ith cell 119 | desired = totalArea * v / totalValue, 120 | radius = Math.sqrt(area / Math.PI), 121 | mass = Math.sqrt(desired / Math.PI) - radius, 122 | sizeError = Math.max(area, desired) / Math.min(area, desired); 123 | 124 | sizeErrorsTot+=sizeError; 125 | sizeErrorsNum++; 126 | // console.log(o.id, "@", j, "area:", area, "value:", v, "->", desired, radius, mass, sizeError); 127 | meta.push({ 128 | id: o.id, 129 | area: area, 130 | centroid: path.centroid(o), 131 | value: v, 132 | desired: desired, 133 | range: 100 * (Math.abs(desired - area)) / (Math.sqrt(Math.PI * area)), 134 | radius: radius, 135 | mass: mass, 136 | sizeError: sizeError 137 | }) 138 | } 139 | 140 | var sizeError = sizeErrorsTot / sizeErrorsNum, 141 | forceReductionFactor = 1 / (1 + sizeError); 142 | 143 | // console.log("meta:", meta); 144 | // console.log(" total area:", totalArea); 145 | // console.log(" force reduction factor:", forceReductionFactor, "mean error:", sizeError); 146 | 147 | var nArcVertices,vI,delta,nArcs=projectedArcs.length,aI=0,delta,nPolygon,pI,centroid,mass,radius,rSquared,dx,dy,distSquared,dist,Fij; 148 | ///For each boundary line 149 | while(aI < nArcs){ 150 | nArcVertices=projectedArcs[aI].length; 151 | vI=0; 152 | ///For each coordinate pair 153 | while(vI < nArcVertices){ 154 | // create an array of vectors: [x, y] 155 | delta = [0,0]; 156 | nPolygon = meta.length; 157 | pI=0; 158 | ///For each polygon centroid 159 | while(pI < nPolygon) { 160 | centroid = meta[pI].centroid; 161 | mass = meta[pI].mass; 162 | radius = meta[pI].radius; 163 | rSquared = (radius*radius); 164 | dx = projectedArcs[aI][vI][0] - centroid[0]; 165 | dy = projectedArcs[aI][vI][1] - centroid[1]; 166 | distSquared = dx * dx + dy * dy; 167 | dist=Math.sqrt(distSquared); 168 | if (dist < meta[pI].range){ 169 | Fij = (dist > radius) 170 | ? mass * radius / dist 171 | : mass * 172 | (distSquared / rSquared) * 173 | (4 - 3 * dist / radius); 174 | var tans = Helpers.arctans(dy, dx) 175 | delta[0]+=(Fij * tans.cos); 176 | delta[1]+=(Fij * tans.sin); 177 | } 178 | pI++; 179 | } 180 | projectedArcs[aI][vI][0] += (delta[0]*forceReductionFactor); 181 | projectedArcs[aI][vI][1] += (delta[1]*forceReductionFactor); 182 | vI++; 183 | } 184 | aI++; 185 | } 186 | 187 | // break if we hit the target size error 188 | if (sizeError <= 1) break; 189 | } 190 | 191 | //console.timeEnd("processing:" + task) 192 | 193 | var response = { 194 | done: 'processing', 195 | //geohson featureCollection 196 | features: objects, 197 | //arcs can be useful to reconstruct topojson : 198 | //arcs: projectedArcs, 199 | task: task 200 | } 201 | 202 | if (typeof self !== 'undefined'){ 203 | self.postMessage(response) 204 | } else { 205 | this.onmessage(response) 206 | } 207 | } 208 | } 209 | -------------------------------------------------------------------------------- /src/cartogramaster.js: -------------------------------------------------------------------------------- 1 | function spawnWorker(){ 2 | var Wok 3 | if (typeof window !== 'undefined'){//browser 4 | Wok = require('worker?inline=true!./cartogram-worker.js'); 5 | } else {//node 6 | Wok = require('./cartogram-worker.js') 7 | } 8 | return new Wok() 9 | } 10 | 11 | 12 | var _ = require('underscore.deferred') 13 | 14 | function cartogramaster(geo, values, featureProperty) { 15 | var dfd = new _.Deferred() 16 | 17 | // How many cartograms to compute ? 18 | var tasks = Object.keys(values) 19 | 20 | var n = tasks.length 21 | 22 | var workers = [], 23 | results = {}; 24 | 25 | function post(worker, index){ 26 | worker.postMessage({ 27 | do: 'carto', 28 | geo: geo, 29 | values: values[index], 30 | featureProperty: featureProperty, 31 | task: index 32 | }) 33 | } 34 | 35 | // Spawn 8 workers max, feed them sequentially until all tasks are done. 36 | while (workers.length < 9 && tasks.length > 0){ 37 | 38 | var worker = spawnWorker() 39 | workers.push(worker) 40 | 41 | work(worker, tasks.pop()) 42 | 43 | } 44 | 45 | function work(worker, i){ 46 | 47 | worker.onmessage = function(event){ 48 | var data = event 49 | if (typeof event.data!== 'undefined') data = event.data 50 | 51 | if (data.done === 'processing'){ 52 | dfd.notify(Object.keys(results).length / n) 53 | results[data.task] = data 54 | 55 | //the end 56 | if (Object.keys(results).length === n){ 57 | dfd.resolve(results); 58 | workers.forEach(function(w){ 59 | w.terminate() 60 | }); 61 | return; 62 | } 63 | if (tasks.length > 0){ 64 | var j = tasks.pop() 65 | post(worker, j) 66 | } 67 | } 68 | } 69 | 70 | post(worker, i) 71 | } 72 | 73 | return dfd 74 | }; 75 | 76 | 77 | module.exports = cartogramaster 78 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | var webpack = require('webpack'); 2 | 3 | module.exports = { 4 | entry: "./src/cartogramaster.js", 5 | output: { 6 | path: "./dist", 7 | filename: "async-cartogram.js", 8 | libraryTarget: 'umd', 9 | library: "AsyncCartogram" 10 | }, 11 | 12 | plugins: [ 13 | new webpack.optimize.UglifyJsPlugin({ 14 | compress: { 15 | warnings: false 16 | } 17 | }) 18 | ] 19 | 20 | }; 21 | --------------------------------------------------------------------------------