var vows = require(“vows”),
_ = require("../../"), load = require("../load"), assert = require("../assert");
var suite = vows.describe(“d3.geo.path”);
suite.addBatch({
"path": { topic: load("geo/path").expression("d3.geo.path"), "with an equirectangular projection": { topic: function(path) { return path() .context(testContext) .projection(_.geo.equirectangular() .scale(900 / Math.PI) .precision(0)); }, "renders a point": function(p) { p({ type: "Point", coordinates: [-63, 18] }); assert.deepEqual(testContext.buffer(), [ {type: "moveTo", x: 165, y: 160}, {type: "arc", x: 165, y: 160, r: 4.5} ]); }, "renders a multipoint": function(p) { p({ type: "MultiPoint", coordinates: [[-63, 18], [-62, 18], [-62, 17]] }); assert.deepEqual(testContext.buffer(), [ {type: "moveTo", x: 165, y: 160}, {type: "arc", x: 165, y: 160, r: 4.5}, {type: "moveTo", x: 170, y: 160}, {type: "arc", x: 170, y: 160, r: 4.5}, {type: "moveTo", x: 170, y: 165}, {type: "arc", x: 170, y: 165, r: 4.5} ]); }, "renders a line string": function(p) { p({ type: "Feature", geometry: { type: "LineString", coordinates: [[-63, 18], [-62, 18], [-62, 17]] } }); assert.deepEqual(testContext.buffer(), [ {type: "moveTo", x: 165, y: 160}, {type: "lineTo", x: 170, y: 160}, {type: "lineTo", x: 170, y: 165} ]); }, "renders a polygon": function(p) { p({ type: "Feature", geometry: { type: "Polygon", coordinates: [[[-63, 18], [-62, 18], [-62, 17], [-63, 18]]] } }); assert.deepEqual(testContext.buffer(), [ {type: "moveTo", x: 165, y: 160}, {type: "lineTo", x: 170, y: 160}, {type: "lineTo", x: 170, y: 165}, {type: "closePath"} ]); }, "renders a geometry collection": function(p) { p({ type: "GeometryCollection", geometries: [{type: "Point", coordinates: [0, 0]}] }); assert.deepEqual(testContext.buffer(), [ {type: "moveTo", x: 480, y: 250}, {type: "arc", x: 480, y: 250, r: 4.5} ]); }, "renders a feature collection": function(p) { p({ type: "FeatureCollection", features: [{type: "Feature", geometry: {type: "Point", coordinates: [0, 0]}}] }); assert.deepEqual(testContext.buffer(), [ {type: "moveTo", x: 480, y: 250}, {type: "arc", x: 480, y: 250, r: 4.5} ]); }, "longitudes wrap at ±180°": function(p) { p({type: "Point", coordinates: [180 + 1e-6, 0]}); assert.deepEqual(testContext.buffer(), [{type: "moveTo", x: -420, y: 250}, {type: "arc", x: -420, y: 250, r: 4.5}]); }, "observes the correct winding order of a tiny polygon": function(p) { p({type: "Polygon", coordinates: [[ [-0.06904102953339501, 0.346043661846373], [-6.725674252975136e-15, 0.3981303360336475], [-6.742247658534323e-15, -0.08812465346531581], [-0.17301258217724075, -0.12278150669440671], [-0.06904102953339501, 0.346043661846373]]]}); assert.equal(testContext.buffer().filter(function(d) { return d.type === "moveTo"; }).length, 1); }, "area": { topic: function(p) { return p.area; }, "of a polygon with no holes": function(area) { assert.strictEqual(area({type: "Polygon", coordinates: [[[100, 0], [100, 1], [101, 1], [101, 0], [100, 0]]]}), 25); }, "of a polygon with holes": function(area) { assert.strictEqual(area({type: "Polygon", coordinates: [[[100, 0], [100, 1], [101, 1], [101, 0], [100, 0]], [[100.2, .2], [100.8, .2], [100.8, .8], [100.2, .8], [100.2, .2]]]}), 16); }, "area of a Sphere": function(area) { assert.strictEqual(area({type: "Sphere"}), 1620000); } }, "centroid": { topic: function(p) { return p.centroid; }, "of a point": function(centroid) { assert.deepEqual(centroid({type: "Point", coordinates: [0, 0]}), [480, 250]); }, "of an empty multipoint": function(centroid) { assert.ok(centroid({type: "MultiPoint", coordinates: []}).every(isNaN)); }, "of a singleton multipoint": function(centroid) { assert.deepEqual(centroid({type: "MultiPoint", coordinates: [[0, 0]]}), [480, 250]); }, "of a multipoint with two points": function(centroid) { assert.deepEqual(centroid({type: "MultiPoint", coordinates: [[-122, 37], [-74, 40]]}), [-10, 57.5]); }, "of an empty linestring": function(centroid) { assert.ok(centroid({type: "LineString", coordinates: []}).every(isNaN)); }, "of a linestring with two points": function(centroid) { assert.deepEqual(centroid({type: "LineString", coordinates: [[100, 0], [0, 0]]}), [730, 250]); assert.deepEqual(centroid({type: "LineString", coordinates: [[0, 0], [100, 0], [101, 0]]}), [732.5, 250]); }, "of a linestring with two points, one unique": function(centroid) { assert.deepEqual(centroid({type: "LineString", coordinates: [[-122, 37], [-122, 37]]}), [-130, 65]); assert.deepEqual(centroid({type: "LineString", coordinates: [[-74, 40], [-74, 40]]}), [110, 50]); }, "of a linestring with three points; two unique": function(centroid) { assert.deepEqual(centroid({type: "LineString", coordinates: [[-122, 37], [-74, 40], [-74, 40]]}), [-10, 57.5]); }, "of a linestring with three points": function(centroid) { assert.inDelta(centroid({type: "LineString", coordinates: [[-122, 37], [-74, 40], [-100, 0]]}), [17.389135, 103.563545], 1e-6); }, "of a multilinestring": function(centroid) { assert.deepEqual(centroid({type: "MultiLineString", coordinates: [[[100, 0], [0, 0]], [[-10, 0], [0, 0]]]}), [705, 250]); }, "of a single-ring polygon": function(centroid) { assert.deepEqual(centroid({type: "Polygon", coordinates: [[[100, 0], [100, 1], [101, 1], [101, 0], [100, 0]]]}), [982.5, 247.5]); }, "of a zero-area polygon": function(centroid) { assert.deepEqual(centroid({type: "Polygon", coordinates: [[[1, 0], [2, 0], [3, 0], [1, 0]]]}), [490, 250]); }, "of a polygon with two rings, one with zero area": function(centroid) { assert.deepEqual(centroid({type: "Polygon", coordinates: [ [[100, 0], [100, 1], [101, 1], [101, 0], [100, 0]], [[100.1, 0], [100.2, 0], [100.3, 0], [100.1, 0] ]]}), [982.5, 247.5]); }, "of a polygon with clockwise exterior and anticlockwise interior": function(centroid) { assert.inDelta(centroid({ type: "Polygon", coordinates: [ [[-2, -2], [2, -2], [2, 2], [-2, 2], [-2, -2]].reverse(), [[ 0, -1], [1, -1], [1, 1], [ 0, 1], [ 0, -1]] ] }), [479.642857, 250], 1e-6); }, "of an empty multipolygon": function(centroid) { assert.ok(centroid({type: "MultiPolygon", coordinates: []}).every(isNaN)); }, "of a singleton multipolygon": function(centroid) { assert.deepEqual(centroid({type: "MultiPolygon", coordinates: [[[[100, 0], [100, 1], [101, 1], [101, 0], [100, 0]]]]}), [982.5, 247.5]); }, "of a multipolygon with two polygons": function(centroid) { assert.deepEqual(centroid({type: "MultiPolygon", coordinates: [ [[[100, 0], [100, 1], [101, 1], [101, 0], [100, 0]]], [[[0, 0], [1, 0], [1, -1], [0, -1], [0, 0]]] ]}), [732.5, 250]); }, "of a multipolygon with two polygons, one zero area": function(centroid) { assert.deepEqual(centroid({type: "MultiPolygon", coordinates: [ [[[100, 0], [100, 1], [101, 1], [101, 0], [100, 0]]], [[[0, 0], [1, 0], [2, 0], [0, 0]]] ]}), [982.5, 247.5]); }, "of a geometry collection with a single point": function(centroid) { assert.deepEqual(centroid({type: "GeometryCollection", geometries: [{type: "Point", coordinates: [0, 0]}]}), [480, 250]); }, "of a geometry collection with a point and a linestring": function(centroid) { assert.deepEqual(centroid({type: "GeometryCollection", geometries: [ {type: "LineString", coordinates: [[179, 0], [180, 0]]}, {type: "Point", coordinates: [0, 0]} ]}), [1377.5, 250]); }, "of a geometry collection with a point, linestring and polygon": function(centroid) { assert.deepEqual(centroid({type: "GeometryCollection", geometries: [ {type: "Polygon", coordinates: [[[-180, 0], [-180, 1], [-179, 1], [-179, 0], [-180, 0]]]}, {type: "LineString", coordinates: [[179, 0], [180, 0]]}, {type: "Point", coordinates: [0, 0]} ]}), [-417.5, 247.5]); }, "of a feature collection with a point": function(centroid) { assert.deepEqual(centroid({type: "FeatureCollection", features: [{type: "Feature", geometry: {type: "Point", coordinates: [0, 0]}}]}), [480, 250]); }, "of a feature collection with a point and a line string": function(centroid) { assert.deepEqual(centroid({type: "FeatureCollection", features: [ {type: "Feature", geometry: {type: "LineString", coordinates: [[179, 0], [180, 0]]}}, {type: "Feature", geometry: {type: "Point", coordinates: [0, 0]}} ]}), [1377.5, 250]); }, "of a feature collection with a point, line string and polygon": function(centroid) { assert.deepEqual(centroid({type: "FeatureCollection", features: [ {type: "Feature", geometry: {type: "Polygon", coordinates: [[[-180, 0], [-180, 1], [-179, 1], [-179, 0], [-180, 0]]]}}, {type: "Feature", geometry: {type: "LineString", coordinates: [[179, 0], [180, 0]]}}, {type: "Feature", geometry: {type: "Point", coordinates: [0, 0]}} ]}), [-417.5, 247.5]); }, "of a sphere": function(centroid) { assert.deepEqual(centroid({type: "Sphere"}), [480, 250]); } } }, "with a null (identity) projection": { topic: function(path) { return path() .context(testContext) .projection(null); }, "renders a Polygon": function(p) { p({ type: "Feature", geometry: { type: "Polygon", coordinates: [[[-63, 18], [-62, 18], [-62, 17], [-63, 18]]] } }); assert.deepEqual(testContext.buffer(), [ {type: "moveTo", x: -63, y: 18}, {type: "lineTo", x: -62, y: 18}, {type: "lineTo", x: -62, y: 17}, {type: "closePath"} ]); } }, "with the default context (null) and an identity projection": { topic: function(path) { return path() .projection(_.geo.equirectangular() .scale(900 / Math.PI) .precision(0)); }, "returns null when passed null or undefined": function(p) { assert.equal(p(null), null); assert.equal(p(undefined), null); assert.equal(p(), null); }, "returns null with bogus type name": function(p) { assert.equal(p({ type: "Feature", geometry: { type: "__proto__", coordinates: [[[-63.03, 18.02], [-63.14, 18.06], [-63.01, 18.07], [-63.03, 18.02]]] } }), null); } }, "with the default context (null) and default projection (albers-usa)": { topic: function(path) { return path(); }, "area of a polygon": function(p) { var area = p.area({type: "Polygon", coordinates: [[[-122, 37], [-71, 42], [-80, 25], [-122, 37]]]}); assert.inDelta(area, 124884.274, 1e-3); }, "bounds of a line string": function(p) { assert.inDelta(p.bounds({type: "LineString", coordinates: [[-122, 37], [-74, 40], [-100, 0]]}), [[109.378, 189.584], [797.758, 504.660]], 1e-3); }, "centroid of a line string": function(p) { assert.inDelta(p.centroid({type: "LineString", coordinates: [[-122, 37], [-74, 40], [-100, 0]]}), [545.131, 253.860], 1e-3); } }, "with an equirectangular projection rotated by [180, -248]": { topic: function(path) { return path() .context(testContext) .projection(_.geo.equirectangular() .rotate([-180, -248]) .scale(900 / Math.PI) .precision(0)); }, "renders a polygon": function(p) { p({type: "Polygon", coordinates: [[[-175.03150315031502, 66.57410661866186], [-174.34743474347434, 66.33097912391239], [-174.5994599459946, 67.0603616081608], [-171.86318631863185, 66.90406536153614], [-169.9189918991899, 65.96628788178816], [-170.89108910891088, 65.53213164116411], [-172.54725472547256, 65.42793414341432], [-172.5832583258326, 64.45542416441643], [-172.97929792979298, 64.2470291689169], [-173.91539153915392, 64.28176166816681], [-174.67146714671466, 64.62908666066605], [-176.003600360036, 64.90694665466546], [-176.21962196219621, 65.34110289528951], [-177.22772277227722, 65.51476539153916], [-178.37983798379838, 65.37583539453945], [-178.91989198919893, 65.72316038703869], [-178.7038703870387, 66.10521787878787], [-179.8919891989199, 65.8620903840384], [-179.45994599459945, 65.3932016441644], [-180, 64.97641165316531], [-180, 68.95328281728172], [-177.55175517551754, 68.18916783378336], [-174.95949594959495, 67.19929160516051], [-175.03150315031502, 66.57410661866186]]]}); assert.deepEqual(testContext.buffer().filter(function(d) { return d.type === "moveTo"; }), [{type: "moveTo", x: 1370, y: 243}]); } }, "projection": { "returns the current projection when called with no arguments": function(path) { var p = path(), projection = _.geo.equirectangular(); p.projection(projection); assert.strictEqual(p.projection(), projection); } }, "pointRadius": { "returns the current point radius when called with no arguments": function(path) { var p = path(), radius = function() { return 5; }; assert.strictEqual(p.pointRadius(), 4.5); assert.strictEqual(p.pointRadius(radius).pointRadius(), radius); }, "coerces point radius to a number": { "when the radius is specified as a constant": function(path) { var p = path().projection(null).context(testContext).pointRadius("6"); assert.strictEqual(p.pointRadius(), 6); p({type: "Point", coordinates: [0, 0]}); assert.strictEqual(testContext.buffer().filter(function(d) { return d.type === "arc"; })[0].r, 6); }, "when the radius is specified as a function": function(path) { var p = path().projection(null).context(testContext).pointRadius(function() { return "6"; }); p({type: "Point", coordinates: [0, 0]}); assert.strictEqual(testContext.buffer().filter(function(d) { return d.type === "arc"; })[0].r, 6); } } }, "with an equirectangular projection clipped to 90°": { topic: function(path) { return path() .context(testContext) .projection(_.geo.equirectangular() .scale(900 / Math.PI) .precision(0) .clipAngle(90)); }, "renders a point": function(p) { p({ type: "Point", coordinates: [-63, 18] }); assert.deepEqual(testContext.buffer(), [ {type: "moveTo", x: 165, y: 160}, {type: "arc", x: 165, y: 160, r: 4.5} ]); }, "renders a multipoint": function(p) { p({ type: "MultiPoint", coordinates: [[-63, 18], [-62, 18], [-62, 17]] }); assert.deepEqual(testContext.buffer(), [ {type: "moveTo", x: 165, y: 160}, {type: "arc", x: 165, y: 160, r: 4.5}, {type: "moveTo", x: 170, y: 160}, {type: "arc", x: 170, y: 160, r: 4.5}, {type: "moveTo", x: 170, y: 165}, {type: "arc", x: 170, y: 165, r: 4.5} ]); }, "inserts exterior along clip edge if polygon interior surrounds it": function(p) { p({type: "Polygon", coordinates: [[[80, -80], [80, 80], [-80, 80], [-80, -80], [80, -80]]]}); assert.equal(testContext.buffer().filter(function(d) { return d.type === "moveTo"; }).length, 2); }, "inserts exterior along clip edge if polygon exterior surrounds it": function(p) { p({type: "Polygon", coordinates: [[[100, -80], [-100, -80], [-100, 80], [100, 80], [100, -80]]]}); assert.equal(testContext.buffer().filter(function(d) { return d.type === "moveTo"; }).length, 1); }, "renders a small circle of 60°": function(p) { p(_.geo.circle().angle(60)()); assert.deepEqual(testContext.buffer().filter(function(d) { return d.type === "moveTo"; }), [{type: "moveTo", x: 276, y: 493}]); }, "renders a small circle of 120°": function(p) { p(_.geo.circle().angle(120)()); assert.deepEqual(testContext.buffer().filter(function(d) { return d.type === "moveTo"; }), [{type: "moveTo", x: 87, y: 700}]); } }, "with an equirectangular projection clipped to 90° and rotated by [-17°, -451°]": { "renders a polygon": function(path) { var pole = _.range(-180, 180, 10).map(function(x) { return [x, 70]; }); pole.push(pole[0]); path() .context(testContext) .projection(_.geo.equirectangular() .rotate([-17, -451]) .scale(900 / Math.PI) .precision(0) .clipAngle(90))({type: "Polygon", coordinates: [pole]}); assert.deepEqual(testContext.buffer().filter(function(d) { return d.type === "moveTo"; }), [ {type: "moveTo", x: 510, y: 160}, {type: "moveTo", x: 87, y: 700} ]); } }, "with an equirectangular projection clipped to 90° and rotated by [71.03°, 42.37°]": { topic: function(path) { return path() .context(testContext) .projection(_.geo.equirectangular() .rotate([71.03, 42.37]) .scale(900 / Math.PI) .precision(0) .clipAngle(90)); }, /* "grid component": function(path) { var yStepsBig = _.range(-90, 90, 10); path({type: "LineString", coordinates: yStepsBig.map(function(y) { return [110, y]; })}); assert.inDelta(testContext.buffer(), [[ [109.538009, -90], [110, -80], [110, -70], [110, -60], [110, -50], [110, -47.625390] ]], 1e-6); }, */ "can completely clip a LineString": function(p) { p({type: "LineString", coordinates: [[90.0, -42.37], [95.0, -42.37], [90.0, -42.37]]}); assert.deepEqual(testContext.buffer(), []); }, "doesn't insert a duplicate point": function(p) { p({type: "LineString", coordinates: [[0, 0]]}); assert.deepEqual(testContext.buffer(), [{type: "moveTo", x: 859, y: 187}]); }, "renders a visible point": function(p) { p({type: "Point", coordinates: [0, 0]}); assert.deepEqual(testContext.buffer(), [{type: "moveTo", x: 859, y: 187}, {type: "arc", x: 859, y: 187, r: 4.5}]); }, "does not render an invisible point": function(p) { p({type: "Point", coordinates: [-180, 0]}); assert.deepEqual(testContext.buffer(), []); }, "renders a multipoint": function(p) { p({type: "MultiPoint", coordinates: [[0, 0], [-180, 0]]}); assert.deepEqual(testContext.buffer(), [{type: "moveTo", x: 859, y: 187}, {type: "arc", x: 859, y: 187, r: 4.5}]); } }, "with an equirectangular projection clipped to 90° and rotated by [-24°, -175.5°]": { topic: function(path) { return path() .context(testContext) .projection(_.geo.equirectangular() .rotate([-24, -175.5]) .scale(900 / Math.PI) .precision(0) .clipAngle(90)); }, "renders Antarctica with no gaps": function(p) { p(antarctica); assert.equal(testContext.buffer().filter(function(d) { return d.type === "moveTo"; }).length, 2); } }, "with an equirectangular projection clipped to 90° and rotated by [90°, 0°]": { topic: function(path) { return path() .context(testContext) .projection(_.geo.equirectangular() .rotate([90, 0]) .scale(900 / Math.PI) .precision(0) .clipAngle(90)); }, "renders a small circle of 60°": function(p) { p(_.geo.circle().angle(60)()); assert.deepEqual(testContext.buffer().filter(function(d) { return d.type === "moveTo"; }), [{type: "moveTo", x: 930, y: 550}]); }, "renders a small circle of 120°": function(p) { p(_.geo.circle().angle(120)()); assert.deepEqual(testContext.buffer().filter(function(d) { return d.type === "moveTo"; }), [{type: "moveTo", x: 30, y: 550}]); } }, "with an equirectangular projection clipped to 90° and rotated by [180°, 0°]": { topic: function(path) { return path() .context(testContext) .projection(_.geo.equirectangular() .rotate([180, 0]) .scale(900 / Math.PI) .precision(0) .clipAngle(90)); }, "does not render a small circle of 60°": function(p) { p(_.geo.circle().angle(60)()); assert.deepEqual(testContext.buffer().filter(function(d) { return d.type === "moveTo"; }), []); }, "renders a small circle of 120° in two parts": function(p) { p(_.geo.circle().angle(120)()); assert.deepEqual(testContext.buffer().filter(function(d) { return d.type === "moveTo"; }), [ {type: "moveTo", x: 276, y: 493}, {type: "moveTo", x: 87, y: 700} ]); } }, "with an equirectangular projection clipped to 90° and rotated by [270°, 0°]": { topic: function(path) { return path() .context(testContext) .projection(_.geo.equirectangular() .rotate([270, 0]) .scale(900 / Math.PI) .precision(0) .clipAngle(90)); }, "renders a small circle of 60°": function(p) { p(_.geo.circle().angle(60)()); assert.deepEqual(testContext.buffer().filter(function(d) { return d.type === "moveTo"; }), [{type: "moveTo", x: 30, y: -50}]); }, "renders a small circle of 120°": function(p) { p(_.geo.circle().angle(120)()); assert.deepEqual(testContext.buffer().filter(function(d) { return d.type === "moveTo"; }), [{type: "moveTo", x: 930, y: -50}]); } }, "with an equirectangular projection clipped to 90° and rotated by [210°, 1°]": { topic: function(path) { return path() .context(testContext) .projection(_.geo.equirectangular() .rotate([210, 1]) .scale(900 / Math.PI) .precision(0) .clipAngle(90)); }, "renders a small circle of 120°": function(p) { p(_.geo.circle().angle(120)()); assert.deepEqual(testContext.buffer().filter(function(d) { return d.type === "moveTo"; }), [{type: "moveTo", x: 930, y: 250}]); } }, "with an equirectangular projection clipped to 90° and rotated by [-150°, 60°]": { topic: function(path) { return path() .context(testContext) .projection(_.geo.equirectangular() .rotate([-150, 60]) .scale(900 / Math.PI) .precision(0) .clipAngle(90)); }, "renders a small circle of 120°": function(p) { p(_.geo.circle().angle(120)()); assert.deepEqual(testContext.buffer().filter(function(d) { return d.type === "moveTo"; }), [{type: "moveTo", x: 30, y: -87}]); }, "renders a sphere": function(p) { p({type: "Sphere"}); assert.deepEqual(testContext.buffer().filter(function(d) { return d.type === "moveTo"; }), [{type: "moveTo", x: 87, y: 700}]); } }, "with an equirectangular projection clipped to 170°": { topic: function(path) { return path() .context(testContext) .projection(_.geo.equirectangular() .scale(900 / Math.PI) .precision(0) .clipAngle(170)); }, "renders stripes": function(p) { p(stripes(80, -80)); assert.deepEqual(testContext.buffer().filter(function(d) { return d.type === "moveTo"; }), [ {type: "moveTo", x: -420, y: -150}, {type: "moveTo", x: -420, y: 650}, {type: "moveTo", x: 1331, y: 259} ]); } }, "with an equirectangular projection clipped to 170° and rotated by [0°, -90°]": { topic: function(path) { return path() .context(testContext) .projection(_.geo.equirectangular() .scale(900 / Math.PI) .rotate([0, -90]) .precision(0) .clipAngle(170)); }, "renders stripes": function(p) { p(stripes(80, -80)); assert.deepEqual(testContext.buffer().filter(function(d) { return d.type === "moveTo"; }), [ {type: "moveTo", x: 480, y: 200}, {type: "moveTo", x: 1350, y: 210} ]); } }, "with an equirectangular projection clipped to 30°": { topic: function(path) { return path() .context(testContext) .projection(_.geo.equirectangular() .scale(900 / Math.PI) .precision(0) .clipAngle(30)); }, "clips lines with two invisible endpoints and visible middle": function(p) { p({type: "LineString", coordinates: [[-45, 0], [45, 0]]}); assert.deepEqual(testContext.buffer(), [ {type: "moveTo", x: 330, y: 250}, {type: "lineTo", x: 630, y: 250} ]); } }, "with an equirectangular projection clipped to 150°": { topic: function(path) { return path() .context(testContext) .projection(_.geo.equirectangular() .scale(900 / Math.PI) .precision(0) .clipAngle(150)); }, "clips lines with two visible endpoints and invisible middle": function(p) { p({type: "LineString", coordinates: [[135, 0], [-135, 0]]}); assert.deepEqual(testContext.buffer(), [ {type: "moveTo", x: 1155, y: 250}, {type: "lineTo", x: 1230, y: 250}, {type: "moveTo", x: -270, y: 250}, {type: "lineTo", x: -195, y: 250} ]); } }, "with an equirectangular projection rotated by [98°, 0°]": { topic: function(path) { return path() .context(testContext) .projection(_.geo.equirectangular() .scale(900 / Math.PI) .rotate([98, 0]) .precision(0)); }, "renders Keweenaw, a small U.S. county": function(p) { p({ type: "Polygon", coordinates: [[[-88.23013, 47.198326], [-88.514931, 47.285957], [-88.383484, 47.285957], [-88.23013, 47.198326]]] }); assert.equal(testContext.buffer().filter(function(d) { return d.type === "moveTo"; }).length, 1); }, "renders Accomack, a small U.S. county": function(p) { p({ type: "MultiPolygon", coordinates: [ [[[-75.397659, 38.013497], [-75.244304, 38.029928], [-75.666029, 37.465803], [-75.939876, 37.547957], [-75.671506, 37.95325], [-75.622213, 37.991589], [-75.397659, 38.013497]]], [[[-76.016553, 37.95325], [-76.043938, 37.95325], [-75.994645, 37.95325], [-76.016553, 37.95325]]] ] }); assert.equal(testContext.buffer().filter(function(d) { return d.type === "moveTo"; }).length, 2); }, "renders Hopewell, a small U.S. county": function(p) { p({ type: "Polygon", coordinates: [[[-77.298157, 37.312448], [-77.298157, 37.312448], [-77.336496, 37.312448], [-77.281726, 37.312448], [-77.298157, 37.312448]]] }); assert.equal(testContext.buffer().filter(function(d) { return d.type === "moveTo"; }).length, 1); } }, "with an equirectangular projection rotated by [330°, 232°]": { topic: function(path) { return path() .context(testContext) .projection(_.geo.equirectangular() .scale(900 / Math.PI) .rotate([330, 232]) .precision(0)); }, "renders degenerate points for a small circle of 30°": function(p) { p(_.geo.circle().angle(30)()); assert.equal(testContext.buffer().filter(function(d) { return d.type === "moveTo"; }).length, 2); } }, "with an equirectangular projection rotated by [34.5°, 90°]": { topic: function(path) { return path() .context(testContext) .projection(_.geo.equirectangular() .scale(900 / Math.PI) .rotate([34.5, 90]) .precision(0)); }, "observes proper clip point ordering for lines": function(p) { var line = _.range(-90, 180, 10).map(function(x) { return [x, 20]; }) .concat(_.range(170, -100, -10).map(function(x) { return [x, 0]; })) .concat([[-90, 20]]); p({type: "Polygon", coordinates: [line]}); assert.equal(testContext.buffer().filter(function(d) { return d.type === "moveTo"; }).length, 3); } }, "with an equirectangular projection with the viewport clipped to 960×500": { topic: function(path) { return path() .context(testContext) .projection(_.geo.equirectangular() .scale(900 / Math.PI) .clipExtent([[0, 0], [960, 500]]) .precision(0)); }, "doesn't generate a redundant closing point": function(p) { p({type: "Polygon", coordinates: [[[0, 0], [0, 1], [1, 1], [1, 0], [0, 0]]]}); assert.deepEqual(testContext.buffer(), [ {type: "moveTo", x: 480, y: 250}, {type: "lineTo", x: 480, y: 245}, {type: "lineTo", x: 485, y: 245}, {type: "lineTo", x: 485, y: 250}, {type: "closePath"} ]); } }, "with a stereographic projection and adaptive resampling": { topic: function(path) { return path() .context(testContext) .projection(_.geo.stereographic() .precision(1)); }, "correctly resamples points on antimeridian": function(p) { p({type: "LineString", coordinates: [[0, 90], [90, 0]]}); assert.deepEqual(testContext.buffer(), [ {type: "moveTo", x: 480, y: 100}, {type: "lineTo", x: 509, y: 103}, {type: "lineTo", x: 537, y: 111}, {type: "lineTo", x: 563, y: 125}, {type: "lineTo", x: 586, y: 144}, {type: "lineTo", x: 605, y: 167}, {type: "lineTo", x: 619, y: 193}, {type: "lineTo", x: 627, y: 221}, {type: "lineTo", x: 630, y: 250} ]); } }, "with an Albers projection and adaptive resampling": { "correctly resamples near the poles": function(path) { var p = path() .context(testContext) .projection(_.geo.albers() .scale(140) .rotate([0, 0]) .precision(1)); p({type: "LineString", coordinates: [[0, 88], [180, 89]]}); assert.isTrue(testContext.buffer().filter(function(d) { return d.type === "lineTo"; }).length > 1); p({type: "LineString", coordinates: [[180, 90], [1, 89.5]]}); assert.isTrue(testContext.buffer().filter(function(d) { return d.type === "lineTo"; }).length > 1); }, "rotate([11.5, 285])": function(path) { var p = path() .context(testContext) .projection(_.geo.albers() .scale(140) .rotate([11.5, 285]) .precision(1)); p({type: "LineString", coordinates: [[170, 20], [170, 0]]}); assert.isTrue(testContext.buffer().filter(function(d) { return d.type === "lineTo"; }).length > 1); }, "wavy projection": function(path) { var p = path() .context(testContext) .projection(_.geo.projection(function(λ, φ) { return [λ, Math.sin(λ * 4)]; }) .scale(140) .precision(1)); p({type: "LineString", coordinates: [[-45, 0], [45, 0]]}); assert.isTrue(testContext.buffer().filter(function(d) { return d.type === "lineTo"; }).length > 1); } }, "with an Albers projection rotated by [11.5°, 285°] and adaptive resampling": { topic: function(path) { return path() .context(testContext) .projection(_.geo.albers() .scale(140) .rotate([11.5, 285]) .precision(1)); }, "correctly resamples near the poles": function(p) { p({type: "LineString", coordinates: [[170, 20], [170, 0]]}); assert.isTrue(testContext.buffer().filter(function(d) { return d.type === "lineTo"; }).length > 1); } } }
});
var testBuffer = [];
var testContext = {
arc: function(x, y, r, a0, a1) { testBuffer.push({type: "arc", x: Math.round(x), y: Math.round(y), r: r}); }, moveTo: function(x, y) { testBuffer.push({type: "moveTo", x: Math.round(x), y: Math.round(y)}); }, lineTo: function(x, y) { testBuffer.push({type: "lineTo", x: Math.round(x), y: Math.round(y)}); }, closePath: function() { testBuffer.push({type: "closePath"}); }, buffer: function() { var result = testBuffer; testBuffer = []; return result; }
};
function stripes(a, b) {
return {type: "Polygon", coordinates: [a, b].map(function(d, i) { var stripe = _.range(-180, 180, 1).map(function(x) { return [x, d]; }); stripe.push(stripe[0]); return i ? stripe.reverse() : stripe; })};
}
var antarctica = {type: “Polygon”, coordinates: [[[-58.61414,-64.15246],,[-59.78934,-64.21122],,[-61.29741,-64.54432],,[-62.51176,-65.09302],,[-62.59012,-65.85721],,[-62.80556,-66.42550],,[-64.29410,-66.83700],,[-65.50842,-67.58161],,[-65.31254,-68.36533],,[-63.96110,-68.91398],,[-62.78595,-69.61941],,[-62.27673,-70.38366],,[-61.51290,-71.08904],,[-61.08197,-72.38235],,[-60.69026,-73.16617],,[-61.37580,-74.10674],,[-63.29520,-74.57699],,[-64.35283,-75.26284],,[-67.19281,-75.79191],,[-69.79772,-76.22299],,[-72.20677,-76.67366],,[-75.55597,-76.71288],,[-76.92697,-77.10480],,[-74.28287,-77.55542],,[-74.77253,-78.22163],,[-77.92585,-78.37841],,[-78.02378,-79.18183],,[-76.63322,-79.88721],,[-73.24485,-80.41633],,[-70.01316,-81.00415],,[-65.70427,-81.47445],,[-61.55202,-82.04269],,[-58.71212,-82.84610],,[-57.00811,-82.86569],,[-53.61977,-82.25823],,[-49.76134,-81.72917],,[-44.82570,-81.84673],,[-42.16202,-81.65082],,[-38.24481,-81.33730],,[-34.38639,-80.90617],,[-30.09709,-80.59265],,[-29.25490,-79.98519],,[-29.68580,-79.26022],,[-33.68132,-79.45613],,[-35.91410,-79.08385],,[-35.32654,-78.12365],,[-32.21236,-77.65345],,[-29.78373,-77.06557],,[-27.51175,-76.49734],,[-25.47482,-76.28180],,[-22.45859,-76.10543],,[-20.01037,-75.67434],,[-17.52298,-75.12569],,[-15.70149,-74.49860],,[-16.46532,-73.87161],,[-15.44685,-73.14654],,[-13.31197,-72.71545],,[-11.51006,-72.01007],,[-10.29577,-71.26541],,[-8.61138,-71.65733],,[-7.37745,-71.32422],,[-5.79098,-71.03028],,[-4.34166,-71.46137],,[-1.79549,-71.16743],,[-0.22863,-71.63774],,[1.88668,-71.12826],,[4.13905,-70.85391],,[6.27391,-70.46205],,[7.74286,-69.89376],,[9.52513,-70.01133],,[10.81782,-70.83433],,[12.40428,-70.24651],,[14.73499,-70.03091],,[15.94934,-70.03091],,[18.20171,-69.87418],,[20.37573,-70.01133],,[21.92303,-70.40324],,[23.66618,-70.52081],,[25.97730,-70.48163],,[28.09258,-70.32485],,[30.03158,-69.93293],,[31.99017,-69.65864],,[33.30244,-68.83564],,[34.90849,-68.65927],,[36.16201,-69.24714],,[37.90510,-69.52144],,[39.66789,-69.54107],,[40.92135,-68.93362],,[42.93870,-68.46331],,[44.89729,-68.05186],,[46.50334,-67.60119],,[48.34441,-67.36606],,[49.93088,-67.11130],,[50.94932,-66.52348],,[52.61413,-66.05317],,[54.53355,-65.81804],,[56.35504,-65.97478],,[57.25596,-66.68021],,[58.74450,-67.28767],,[60.60522,-67.67958],,[62.38748,-68.01269],,[64.05234,-67.40523],,[65.97171,-67.73834],,[67.89113,-67.93430],,[69.71262,-68.97279],,[69.55594,-69.67822],,[67.81273,-70.30526],,[69.06630,-70.67754],,[68.41998,-71.44178],,[68.71376,-72.16680],,[71.02489,-72.08841],,[71.90628,-71.32422],,[73.08141,-70.71676],,[73.86487,-69.87418],,[75.62755,-69.73703],,[77.64490,-69.46268],,[78.42837,-68.69844],,[80.09312,-68.07150],,[81.48379,-67.54238],,[82.77642,-67.20928],,[84.67620,-67.20928],,[86.75235,-67.15047],,[87.98628,-66.20991],,[88.82840,-66.95456],,[90.63036,-67.22886],,[92.60853,-67.18969],,[94.17541,-67.11130],,[95.78147,-67.38565],,[97.75964,-67.24850],,[99.71818,-67.24850],,[100.89335,-66.58223],,[102.83241,-65.56328],,[104.24255,-65.97478],,[106.18156,-66.93493],,[108.08139,-66.95456],,[110.23583,-66.69980],,[111.74395,-66.13156],,[113.60467,-65.87680],,[114.89730,-66.38628],,[116.69916,-66.66063],,[118.57946,-67.17011],,[120.87099,-67.18969],,[122.32036,-66.56265],,[124.12227,-66.62146],,[126.10039,-66.56265],,[127.88276,-66.66063],,[129.70425,-66.58223],,[131.79994,-66.38628],,[133.85646,-66.28830],,[135.03158,-65.72007],,[135.69748,-65.58286],,[136.20670,-66.44509],,[137.46027,-66.95456],,[139.90844,-66.87617],,[142.12169,-66.81736],,[144.37406,-66.83700],,[146.19555,-67.22886],,[146.64606,-67.89513],,[148.83962,-68.38502],,[151.48370,-68.71812],,[153.63819,-68.89450],,[155.16585,-68.83564],,[156.81113,-69.38429],,[159.18101,-69.59983],,[160.80665,-70.22687],,[162.68689,-70.73635],,[164.91968,-70.77552],,[167.30909,-70.83433],,[169.46358,-71.20666],,[171.20679,-71.69650],,[170.56042,-72.44115],,[169.75736,-73.24452],,[167.97510,-73.81280],,[166.09480,-74.38104],,[164.95885,-75.14528],,[163.82279,-75.87030],,[163.47026,-76.69330],,[164.05787,-77.45744],,[164.74346,-78.18251],,[166.99578,-78.75074],,[163.66621,-79.12302],,[160.92416,-79.73048],,[160.31696,-80.57306],,[161.12001,-81.27850],,[162.49099,-82.06227],,[165.09594,-82.70895],,[168.89566,-83.33599],,[172.28393,-84.04143],,[173.22408,-84.41371],,[178.27721,-84.47251],,[180.00000,-90.0],,[-180.0,-84.71338],,[-179.05867,-84.13941],,[-177.14080,-84.41794],,[-176.52395,-84.23181],,[-176.08467,-84.09925],,[-175.82988,-84.11791],,[-173.11655,-84.11791],,[-169.95122,-83.88464],,[-168.53019,-84.23739],,[-164.18214,-84.82520],,[-158.07137,-85.37391],,[-150.94209,-85.29551],,[-145.88891,-85.31510],,[-142.89227,-84.57049],,[-150.06073,-84.29614],,[-153.58620,-83.68868],,[-153.03775,-82.82652],,[-152.86151,-82.04269],,[-155.29017,-81.41565],,[-154.40878,-81.16093],,[-150.64829,-81.33730],,[-147.22074,-80.67104],,[-146.77028,-79.92643],,[-149.53190,-79.35820],,[-153.39032,-79.16224],,[-155.97566,-78.69193],,[-158.05176,-78.02567],,[-157.87547,-76.98723],,[-155.32937,-77.20272],,[-152.92024,-77.49666],,[-150.00194,-77.18314],,[-147.61248,-76.57573],,[-146.14352,-76.10543],,[-146.20230,-75.38041],,[-144.32203,-75.53719],,[-141.63876,-75.08647],,[-138.85759,-74.96891],,[-136.42890,-74.51824],,[-134.43119,-74.36145],,[-132.25716,-74.30269],,[-129.55428,-74.45943],,[-126.89062,-74.42026],,[-124.01149,-74.47901],,[-121.07361,-74.51824],,[-118.68414,-74.18508],,[-116.21631,-74.24389],,[-113.94433,-73.71482],,[-112.94545,-74.38104],,[-111.26105,-74.42026],,[-108.71490,-74.91010],,[-106.14914,-75.12569],,[-103.36794,-74.98849],,[-100.64553,-75.30201],,[-100.76304,-74.53782],,[-102.54533,-74.10674],,[-103.32875,-73.36208],,[-102.91748,-72.75467],,[-100.31252,-72.75467],,[-98.11888,-73.20534],,[-96.33659,-73.61684],,[-93.67290,-73.28374],,[-91.42056,-73.40130],,[-89.22695,-72.55872],,[-87.26833,-73.18576],,[-85.19223,-73.47969],,[-82.66564,-73.63643],,[-80.68744,-73.47969],,[-79.29688,-73.51887],,[-76.90736,-73.63643],,[-74.89004,-73.87161],,[-72.83353,-73.40130],,[-70.20904,-73.14654],,[-67.95662,-72.79385],,[-67.13403,-72.04924],,[-67.56494,-71.24583],,[-68.23084,-70.46205],,[-68.54420,-69.71739],,[-67.97623,-68.95320],,[-67.42784,-68.14984],,[-67.74118,-67.32684],,[-66.70318,-66.58223],,[-65.37132,-65.89639],,[-64.17654,-65.17142],,[-63.00139,-64.64230],,[-61.41492,-64.27003],,[-59.88726,-63.95651],,[-58.59455,-63.38822],,[-57.22358,-63.52542],,[-58.61414,-64.15246]]]};
suite.export(module);