From 4fbbd1cf822a08310cdbdb819fde6f9cb5fa5162 Mon Sep 17 00:00:00 2001 From: Tom MacWright Date: Fri, 1 Feb 2013 14:11:32 -0500 Subject: [PATCH] Update to pre-release 3.0.5 d3 --- js/lib/d3.v3.js | 1604 ++++++++++++++++++++++++++--------------------- 1 file changed, 900 insertions(+), 704 deletions(-) diff --git a/js/lib/d3.v3.js b/js/lib/d3.v3.js index 93b676f46..818bab9dc 100644 --- a/js/lib/d3.v3.js +++ b/js/lib/d3.v3.js @@ -12,12 +12,9 @@ }; } d3 = { - version: "3.0.0pre" + version: "3.0.5" }; - var π = Math.PI, ε = 1e-6, εε = .001, d3_radians = π / 180, d3_degrees = 180 / π; - function d3_zero() { - return 0; - } + var π = Math.PI, ε = 1e-6, d3_radians = π / 180, d3_degrees = 180 / π; function d3_target(d) { return d.target; } @@ -234,7 +231,7 @@ return s; }; d3.quantile = function(values, p) { - var H = (values.length - 1) * p + 1, h = Math.floor(H), v = values[h - 1], e = H - h; + var H = (values.length - 1) * p + 1, h = Math.floor(H), v = +values[h - 1], e = H - h; return e ? v + e * (values[h] - v) : v; }; d3.shuffle = function(array) { @@ -454,8 +451,13 @@ d3.rebind(xhr, dispatch, "on"); if (arguments.length === 2 && typeof mimeType === "function") callback = mimeType, mimeType = null; - return callback == null ? xhr : xhr.get(callback); + return callback == null ? xhr : xhr.get(d3_xhr_fixCallback(callback)); }; + function d3_xhr_fixCallback(callback) { + return callback.length === 1 ? function(error, request) { + callback(error == null ? request : null); + } : callback; + } d3.text = function() { return d3.xhr.apply(d3, arguments).response(d3_text); }; @@ -1687,13 +1689,13 @@ return value; } function bind(group, groupData) { - var i, n = group.length, m = groupData.length, n0 = Math.min(n, m), n1 = Math.max(n, m), updateNodes = [], enterNodes = [], exitNodes = [], node, nodeData; + var i, n = group.length, m = groupData.length, n0 = Math.min(n, m), updateNodes = new Array(m), enterNodes = new Array(m), exitNodes = new Array(n), node, nodeData; if (key) { - var nodeByKeyValue = new d3_Map(), keyValues = [], keyValue, j = groupData.length; + var nodeByKeyValue = new d3_Map(), dataByKeyValue = new d3_Map(), keyValues = [], keyValue; for (i = -1; ++i < n; ) { keyValue = key.call(node = group[i], node.__data__, i); if (nodeByKeyValue.has(keyValue)) { - exitNodes[j++] = node; + exitNodes[i] = node; } else { nodeByKeyValue.set(keyValue, node); } @@ -1701,14 +1703,13 @@ } for (i = -1; ++i < m; ) { keyValue = key.call(groupData, nodeData = groupData[i], i); - if (nodeByKeyValue.has(keyValue)) { - updateNodes[i] = node = nodeByKeyValue.get(keyValue); + if (node = nodeByKeyValue.get(keyValue)) { + updateNodes[i] = node; node.__data__ = nodeData; - enterNodes[i] = exitNodes[i] = null; - } else { + } else if (!dataByKeyValue.has(keyValue)) { enterNodes[i] = d3_selection_dataNode(nodeData); - updateNodes[i] = exitNodes[i] = null; } + dataByKeyValue.set(keyValue, nodeData); nodeByKeyValue.remove(keyValue); } for (i = -1; ++i < n; ) { @@ -1723,19 +1724,15 @@ if (node) { node.__data__ = nodeData; updateNodes[i] = node; - enterNodes[i] = exitNodes[i] = null; } else { enterNodes[i] = d3_selection_dataNode(nodeData); - updateNodes[i] = exitNodes[i] = null; } } for (;i < m; ++i) { enterNodes[i] = d3_selection_dataNode(groupData[i]); - updateNodes[i] = exitNodes[i] = null; } - for (;i < n1; ++i) { + for (;i < n; ++i) { exitNodes[i] = group[i]; - enterNodes[i] = updateNodes[i] = null; } } enterNodes.update = updateNodes; @@ -3769,7 +3766,7 @@ d3.behavior.zoom = function() { var translate = [ 0, 0 ], translate0, scale = 1, scale0, scaleExtent = d3_behavior_zoomInfinity, event = d3_eventDispatch(zoom, "zoom"), x0, x1, y0, y1, touchtime; function zoom() { - this.on("mousedown.zoom", mousedown).on("mousewheel.zoom", mousewheel).on("mousemove.zoom", mousemove).on("DOMMouseScroll.zoom", mousewheel).on("dblclick.zoom", dblclick).on("touchstart.zoom", touchstart).on("touchmove.zoom", touchmove).on("touchend.zoom", touchstart); + this.on("mousedown.zoom", mousedown).on("mousemove.zoom", mousemove).on(d3_behavior_zoomWheel + ".zoom", mousewheel).on("dblclick.zoom", dblclick).on("touchstart.zoom", touchstart).on("touchmove.zoom", touchmove).on("touchend.zoom", touchstart); } zoom.translate = function(x) { if (!arguments.length) return translate; @@ -3901,21 +3898,14 @@ } return d3.rebind(zoom, event, "on"); }; - var d3_behavior_zoomDiv, d3_behavior_zoomInfinity = [ 0, Infinity ]; - function d3_behavior_zoomDelta() { - if (!d3_behavior_zoomDiv) { - d3_behavior_zoomDiv = d3.select("body").append("div").style("visibility", "hidden").style("top", 0).style("height", 0).style("width", 0).style("overflow-y", "scroll").append("div").style("height", "2000px").node().parentNode; - } - var e = d3.event, delta; - try { - d3_behavior_zoomDiv.scrollTop = 1e3; - d3_behavior_zoomDiv.dispatchEvent(e); - delta = 1e3 - d3_behavior_zoomDiv.scrollTop; - } catch (error) { - delta = e.wheelDelta || -e.detail * 5; - } - return delta; - } + var d3_behavior_zoomInfinity = [ 0, Infinity ]; + var d3_behavior_zoomDelta, d3_behavior_zoomWheel = "onwheel" in document ? (d3_behavior_zoomDelta = function() { + return -d3.event.deltaY * (d3.event.deltaMode ? 120 : 1); + }, "wheel") : "onmousewheel" in document ? (d3_behavior_zoomDelta = function() { + return d3.event.wheelDelta; + }, "mousewheel") : (d3_behavior_zoomDelta = function() { + return -d3.event.detail; + }, "MozMousePixelScroll"); d3.layout = {}; d3.layout.bundle = function() { return function(links) { @@ -4625,10 +4615,8 @@ } d3.layout.hierarchy = function() { var sort = d3_layout_hierarchySort, children = d3_layout_hierarchyChildren, value = d3_layout_hierarchyValue; - function recurse(data, depth, nodes) { - var childs = children.call(hierarchy, data, depth), node = d3_layout_hierarchyInline ? data : { - data: data - }; + function recurse(node, depth, nodes) { + var childs = children.call(hierarchy, node, depth); node.depth = depth; nodes.push(node); if (childs && (n = childs.length)) { @@ -4642,7 +4630,7 @@ if (sort) c.sort(sort); if (value) node.value = v; } else if (value) { - node.value = +value.call(hierarchy, data, depth) || 0; + node.value = +value.call(hierarchy, node, depth) || 0; } return node; } @@ -4652,7 +4640,7 @@ var i = -1, n, j = depth + 1; while (++i < n) v += revalue(children[i], j); } else if (value) { - v = +value.call(hierarchy, d3_layout_hierarchyInline ? node : node.data, depth) || 0; + v = +value.call(hierarchy, node, depth) || 0; } if (value) node.value = v; return v; @@ -4685,11 +4673,8 @@ }; function d3_layout_hierarchyRebind(object, hierarchy) { d3.rebind(object, hierarchy, "sort", "children", "value"); + object.nodes = object; object.links = d3_layout_hierarchyLinks; - object.nodes = function(d) { - d3_layout_hierarchyInline = true; - return (object.nodes = object)(d); - }; return object; } function d3_layout_hierarchyChildren(d) { @@ -4711,7 +4696,6 @@ }); })); } - var d3_layout_hierarchyInline = false; d3.layout.pack = function() { var hierarchy = d3.layout.hierarchy().sort(d3_layout_packSort), padding = 0, size = [ 1, 1 ]; function pack(d, i) { @@ -5096,7 +5080,7 @@ function squarify(node) { var children = node.children; if (children && children.length) { - var rect = pad(node), row = [], remaining = children.slice(), child, best = Infinity, score, u = mode === "slice" ? rect.dx : mode === "dice" || mode === "slice-dice" && node.depth & 1 ? rect.dy : Math.min(rect.dx, rect.dy), n; + var rect = pad(node), row = [], remaining = children.slice(), child, best = Infinity, score, u = mode === "slice" ? rect.dx : mode === "dice" ? rect.dy : mode === "slice-dice" ? node.depth & 1 ? rect.dy : rect.dx : Math.min(rect.dx, rect.dy), n; scale(remaining, rect.dx * rect.dy / node.value); row.area = 0; while ((n = remaining.length) > 0) { @@ -5333,78 +5317,168 @@ d3.csv = d3_dsv(",", "text/csv"); d3.tsv = d3_dsv(" ", "text/tab-separated-values"); d3.geo = {}; - function d3_geo_type(types) { - for (var type in d3_geo_typeDefaults) { - if (!(type in types)) { - types[type] = d3_geo_typeDefaults[type]; + d3.geo.stream = function(object, listener) { + if (d3_geo_streamObjectType.hasOwnProperty(object.type)) { + d3_geo_streamObjectType[object.type](object, listener); + } else { + d3_geo_streamGeometry(object, listener); + } + }; + function d3_geo_streamGeometry(geometry, listener) { + if (d3_geo_streamGeometryType.hasOwnProperty(geometry.type)) { + d3_geo_streamGeometryType[geometry.type](geometry, listener); + } + } + var d3_geo_streamObjectType = { + Feature: function(feature, listener) { + d3_geo_streamGeometry(feature.geometry, listener); + }, + FeatureCollection: function(object, listener) { + var features = object.features, i = -1, n = features.length; + while (++i < n) d3_geo_streamGeometry(features[i].geometry, listener); + } + }; + var d3_geo_streamGeometryType = { + Sphere: function(object, listener) { + listener.sphere(); + }, + Point: function(object, listener) { + var coordinate = object.coordinates; + listener.point(coordinate[0], coordinate[1]); + }, + MultiPoint: function(object, listener) { + var coordinates = object.coordinates, i = -1, n = coordinates.length, coordinate; + while (++i < n) coordinate = coordinates[i], listener.point(coordinate[0], coordinate[1]); + }, + LineString: function(object, listener) { + d3_geo_streamLine(object.coordinates, listener, 0); + }, + MultiLineString: function(object, listener) { + var coordinates = object.coordinates, i = -1, n = coordinates.length; + while (++i < n) d3_geo_streamLine(coordinates[i], listener, 0); + }, + Polygon: function(object, listener) { + d3_geo_streamPolygon(object.coordinates, listener); + }, + MultiPolygon: function(object, listener) { + var coordinates = object.coordinates, i = -1, n = coordinates.length; + while (++i < n) d3_geo_streamPolygon(coordinates[i], listener); + }, + GeometryCollection: function(object, listener) { + var geometries = object.geometries, i = -1, n = geometries.length; + while (++i < n) d3_geo_streamGeometry(geometries[i], listener); + } + }; + function d3_geo_streamLine(coordinates, listener, closed) { + var i = -1, n = coordinates.length - closed, coordinate; + listener.lineStart(); + while (++i < n) coordinate = coordinates[i], listener.point(coordinate[0], coordinate[1]); + listener.lineEnd(); + } + function d3_geo_streamPolygon(coordinates, listener) { + var i = -1, n = coordinates.length; + listener.polygonStart(); + while (++i < n) d3_geo_streamLine(coordinates[i], listener, 1); + listener.polygonEnd(); + } + function d3_geo_spherical(cartesian) { + return [ Math.atan2(cartesian[1], cartesian[0]), Math.asin(Math.max(-1, Math.min(1, cartesian[2]))) ]; + } + function d3_geo_sphericalEqual(a, b) { + return Math.abs(a[0] - b[0]) < ε && Math.abs(a[1] - b[1]) < ε; + } + function d3_geo_cartesian(spherical) { + var λ = spherical[0], φ = spherical[1], cosφ = Math.cos(φ); + return [ cosφ * Math.cos(λ), cosφ * Math.sin(λ), Math.sin(φ) ]; + } + function d3_geo_cartesianDot(a, b) { + return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; + } + function d3_geo_cartesianCross(a, b) { + return [ a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0] ]; + } + function d3_geo_cartesianAdd(a, b) { + a[0] += b[0]; + a[1] += b[1]; + a[2] += b[2]; + } + function d3_geo_cartesianScale(vector, k) { + return [ vector[0] * k, vector[1] * k, vector[2] * k ]; + } + function d3_geo_cartesianNormalize(d) { + var l = Math.sqrt(d[0] * d[0] + d[1] * d[1] + d[2] * d[2]); + d[0] /= l; + d[1] /= l; + d[2] /= l; + } + function d3_geo_resample(project) { + var δ2 = .5, maxDepth = 16; + function resample(stream) { + var λ0, x0, y0, a0, b0, c0; + var resample = { + point: point, + lineStart: lineStart, + lineEnd: lineEnd, + polygonStart: function() { + stream.polygonStart(); + resample.lineStart = polygonLineStart; + }, + polygonEnd: function() { + stream.polygonEnd(); + resample.lineStart = lineStart; + } + }; + function point(x, y) { + x = project(x, y); + stream.point(x[0], x[1]); + } + function lineStart() { + x0 = NaN; + resample.point = linePoint; + stream.lineStart(); + } + function linePoint(λ, φ) { + var c = d3_geo_cartesian([ λ, φ ]), p = project(λ, φ); + 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); + stream.point(x0, y0); + } + function lineEnd() { + resample.point = point; + stream.lineEnd(); + } + function polygonLineStart() { + var λ00, φ00, x00, y00, a00, b00, c00; + lineStart(); + resample.point = function(λ, φ) { + linePoint(λ00 = λ, φ00 = φ), x00 = x0, y00 = y0, a00 = a0, b00 = b0, c00 = c0; + resample.point = linePoint; + }; + resample.lineEnd = function() { + resampleLineTo(x0, y0, λ0, a0, b0, c0, x00, y00, λ00, a00, b00, c00, maxDepth, stream); + resample.lineEnd = lineEnd; + lineEnd(); + }; + } + return resample; + } + function resampleLineTo(x0, y0, λ0, a0, b0, c0, x1, y1, λ1, a1, b1, c1, depth, stream) { + var dx = x1 - x0, dy = y1 - y0, d2 = dx * dx + dy * dy; + if (d2 > 4 * δ2 && depth--) { + var a = a0 + a1, b = b0 + b1, c = c0 + c1, m = Math.sqrt(a * a + b * b + c * c), φ2 = Math.asin(c /= m), λ2 = Math.abs(Math.abs(c) - 1) < ε ? (λ0 + λ1) / 2 : Math.atan2(b, a), p = project(λ2, φ2), x2 = p[0], y2 = p[1], dx2 = x2 - x0, dy2 = y2 - y0, dz = dy * dx2 - dx * dy2; + if (dz * dz / d2 > δ2 || Math.abs((dx * dx2 + dy * dy2) / d2 - .5) > .3) { + resampleLineTo(x0, y0, λ0, a0, b0, c0, x2, y2, λ2, a /= m, b /= m, c, depth, stream); + stream.point(x2, y2); + resampleLineTo(x2, y2, λ2, a, b, c, x1, y1, λ1, a1, b1, c1, depth, stream); + } } } - return types; + resample.precision = function(_) { + if (!arguments.length) return Math.sqrt(δ2); + maxDepth = (δ2 = _ * _) > 0 && 16; + return resample; + }; + return resample; } - var d3_geo_typeDefaults = { - Feature: function(feature) { - this.geometry(feature.geometry); - }, - FeatureCollection: function(collection) { - var features = collection.features, i = -1, n = features.length; - while (++i < n) this.Feature(features[i]); - }, - GeometryCollection: function(collection) { - var geometries = collection.geometries, i = -1, n = geometries.length; - while (++i < n) this.geometry(geometries[i]); - }, - LineString: function(lineString) { - this.line(lineString.coordinates); - }, - MultiLineString: function(multiLineString) { - var coordinates = multiLineString.coordinates, i = -1, n = coordinates.length; - while (++i < n) this.line(coordinates[i]); - }, - MultiPoint: function(multiPoint) { - var coordinates = multiPoint.coordinates, i = -1, n = coordinates.length; - while (++i < n) this.point(coordinates[i]); - }, - MultiPolygon: function(multiPolygon) { - var coordinates = multiPolygon.coordinates, i = -1, n = coordinates.length; - while (++i < n) this.polygon(coordinates[i]); - }, - Point: function(point) { - this.point(point.coordinates); - }, - Polygon: function(polygon) { - this.polygon(polygon.coordinates); - }, - Sphere: d3_noop, - object: function(object) { - return d3_geo_typeObjects.hasOwnProperty(object.type) ? this[object.type](object) : this.geometry(object); - }, - geometry: function(geometry) { - return d3_geo_typeGeometries.hasOwnProperty(geometry.type) ? this[geometry.type](geometry) : null; - }, - point: d3_noop, - line: function(coordinates) { - var i = -1, n = coordinates.length; - while (++i < n) this.point(coordinates[i]); - }, - polygon: function(coordinates) { - var i = -1, n = coordinates.length; - while (++i < n) this.line(coordinates[i]); - } - }; - var d3_geo_typeGeometries = { - LineString: 1, - MultiLineString: 1, - MultiPoint: 1, - MultiPolygon: 1, - Point: 1, - Polygon: 1, - Sphere: 1 - }; - var d3_geo_typeObjects = { - Feature: 1, - FeatureCollection: 1, - GeometryCollection: 1 - }; d3.geo.albersUsa = function() { var lower48 = d3.geo.albers(); var alaska = d3.geo.albers().rotate([ 160, 0 ]).center([ 0, 60 ]).parallels([ 55, 65 ]); @@ -5417,15 +5491,6 @@ var lon = point[0], lat = point[1]; return lat > 50 ? alaska : lon < -140 ? hawaii : lat < 21 ? puertoRico : lower48; } - albersUsa.point = function(coordinates, context) { - return projection(coordinates).point(coordinates, context); - }; - albersUsa.line = function(coordinates, context) { - return projection(coordinates[0]).line(coordinates, context); - }; - albersUsa.polygon = function(coordinates, context) { - return projection(coordinates[0][0]).polygon(coordinates, context); - }; albersUsa.scale = function(x) { if (!arguments.length) return lower48.scale(); lower48.scale(x); @@ -5481,38 +5546,123 @@ return d3_geo_projection(d3_geo_azimuthalEquidistant); }).raw = d3_geo_azimuthalEquidistant; d3.geo.bounds = d3_geo_bounds(d3_identity); - function d3_geo_bounds(projection) { - var x0, y0, x1, y1, bounds = d3_geo_type({ - point: function(point) { - point = projection(point); - var x = point[0], y = point[1]; - if (x < x0) x0 = x; - if (x > x1) x1 = x; - if (y < y0) y0 = y; - if (y > y1) y1 = y; + function d3_geo_bounds(projectStream) { + var x0, y0, x1, y1; + var bound = { + point: boundPoint, + lineStart: d3_noop, + lineEnd: d3_noop, + polygonStart: function() { + bound.lineEnd = boundPolygonLineEnd; }, - polygon: function(coordinates) { - this.line(coordinates[0]); + polygonEnd: function() { + bound.point = boundPoint; } - }); + }; + function boundPoint(x, y) { + if (x < x0) x0 = x; + if (x > x1) x1 = x; + if (y < y0) y0 = y; + if (y > y1) y1 = y; + } + function boundPolygonLineEnd() { + bound.point = bound.lineEnd = d3_noop; + } return function(feature) { y1 = x1 = -(x0 = y0 = Infinity); - bounds.object(feature); + d3.geo.stream(feature, projectStream(bound)); return [ [ x0, y0 ], [ x1, y1 ] ]; }; } + d3.geo.centroid = function(object) { + d3_geo_centroidDimension = d3_geo_centroidW = d3_geo_centroidX = d3_geo_centroidY = d3_geo_centroidZ = 0; + d3.geo.stream(object, d3_geo_centroid); + var m; + if (d3_geo_centroidW && Math.abs(m = Math.sqrt(d3_geo_centroidX * d3_geo_centroidX + d3_geo_centroidY * d3_geo_centroidY + d3_geo_centroidZ * d3_geo_centroidZ)) > ε) { + return [ Math.atan2(d3_geo_centroidY, d3_geo_centroidX) * d3_degrees, Math.asin(Math.max(-1, Math.min(1, d3_geo_centroidZ / m))) * d3_degrees ]; + } + }; + var d3_geo_centroidDimension, d3_geo_centroidW, d3_geo_centroidX, d3_geo_centroidY, d3_geo_centroidZ; + var d3_geo_centroid = { + sphere: function() { + if (d3_geo_centroidDimension < 2) { + d3_geo_centroidDimension = 2; + d3_geo_centroidW = d3_geo_centroidX = d3_geo_centroidY = d3_geo_centroidZ = 0; + } + }, + point: d3_geo_centroidPoint, + lineStart: d3_geo_centroidLineStart, + lineEnd: d3_geo_centroidLineEnd, + polygonStart: function() { + if (d3_geo_centroidDimension < 2) { + d3_geo_centroidDimension = 2; + d3_geo_centroidW = d3_geo_centroidX = d3_geo_centroidY = d3_geo_centroidZ = 0; + } + d3_geo_centroid.lineStart = d3_geo_centroidRingStart; + }, + polygonEnd: function() { + d3_geo_centroid.lineStart = d3_geo_centroidLineStart; + } + }; + function d3_geo_centroidPoint(λ, φ) { + if (d3_geo_centroidDimension) return; + ++d3_geo_centroidW; + λ *= d3_radians; + var cosφ = Math.cos(φ *= d3_radians); + d3_geo_centroidX += (cosφ * Math.cos(λ) - d3_geo_centroidX) / d3_geo_centroidW; + d3_geo_centroidY += (cosφ * Math.sin(λ) - d3_geo_centroidY) / d3_geo_centroidW; + d3_geo_centroidZ += (Math.sin(φ) - d3_geo_centroidZ) / d3_geo_centroidW; + } + function d3_geo_centroidRingStart() { + var λ00, φ00; + d3_geo_centroidDimension = 1; + d3_geo_centroidLineStart(); + d3_geo_centroidDimension = 2; + var linePoint = d3_geo_centroid.point; + d3_geo_centroid.point = function(λ, φ) { + linePoint(λ00 = λ, φ00 = φ); + }; + d3_geo_centroid.lineEnd = function() { + d3_geo_centroid.point(λ00, φ00); + d3_geo_centroidLineEnd(); + d3_geo_centroid.lineEnd = d3_geo_centroidLineEnd; + }; + } + function d3_geo_centroidLineStart() { + var x0, y0, z0; + if (d3_geo_centroidDimension > 1) return; + if (d3_geo_centroidDimension < 1) { + d3_geo_centroidDimension = 1; + d3_geo_centroidW = d3_geo_centroidX = d3_geo_centroidY = d3_geo_centroidZ = 0; + } + d3_geo_centroid.point = function(λ, φ) { + λ *= d3_radians; + var cosφ = Math.cos(φ *= d3_radians); + x0 = cosφ * Math.cos(λ); + y0 = cosφ * Math.sin(λ); + z0 = Math.sin(φ); + d3_geo_centroid.point = nextPoint; + }; + function nextPoint(λ, φ) { + λ *= d3_radians; + var cosφ = Math.cos(φ *= d3_radians), x = cosφ * Math.cos(λ), y = cosφ * Math.sin(λ), z = Math.sin(φ), w = Math.atan2(Math.sqrt((w = y0 * z - z0 * y) * w + (w = z0 * x - x0 * z) * w + (w = x0 * y - y0 * x) * w), x0 * x + y0 * y + z0 * z); + d3_geo_centroidW += w; + d3_geo_centroidX += w * (x0 + (x0 = x)); + d3_geo_centroidY += w * (y0 + (y0 = y)); + d3_geo_centroidZ += w * (z0 + (z0 = z)); + } + } + function d3_geo_centroidLineEnd() { + d3_geo_centroid.point = d3_geo_centroidPoint; + } d3.geo.circle = function() { - var origin = [ 0, 0 ], angle, precision = 6, rotate, interpolate; + var origin = [ 0, 0 ], angle, precision = 6, interpolate; function circle() { - var o = typeof origin === "function" ? origin.apply(this, arguments) : origin; - rotate = d3_geo_rotation(-o[0] * d3_radians, -o[1] * d3_radians, 0); - var ring = []; + var center = typeof origin === "function" ? origin.apply(this, arguments) : origin, rotate = d3_geo_rotation(-center[0] * d3_radians, -center[1] * d3_radians, 0).invert, ring = []; interpolate(null, null, 1, { - lineTo: function(λ, φ) { - var point = rotate.invert(λ, φ); - point[0] *= d3_degrees; - point[1] *= d3_degrees; - ring.push(point); + point: function(x, y) { + ring.push(x = rotate(x, y)); + x[0] *= d3_degrees, x[1] *= d3_degrees; } }); return { @@ -5537,84 +5687,9 @@ }; return circle.angle(90); }; - function d3_geo_circleClip(degrees, rotate) { - var radians = degrees * d3_radians, cr = Math.cos(radians), interpolate = d3_geo_circleInterpolate(radians, 6 * d3_radians); - return { - point: function(coordinates, context) { - if (visible(coordinates = rotate(coordinates))) { - context.point(coordinates[0], coordinates[1]); - } - }, - line: function(coordinates, context) { - clipLine(coordinates, context); - }, - polygon: function(polygon, context) { - d3_geo_circleClipPolygon(polygon, context, clipLine, interpolate); - }, - sphere: function(context) { - d3_geo_projectionSphere(context, interpolate); - } - }; - function visible(point) { - return Math.cos(point[1]) * Math.cos(point[0]) > cr; - } - function clipLine(coordinates, context, ring) { - if (!(n = coordinates.length)) return [ ring && 0, false ]; - var point0 = rotate(coordinates[0]), point1, point2, v0 = visible(point0), v00 = ring && v0, v, n, clean = ring, area = 0, p, x0, x, y0, y; - if (clean) { - x0 = (p = d3_geo_stereographic(point0[0] + (v0 ? 0 : π), point0[1]))[0]; - y0 = p[1]; - } - if (v0) context.moveTo(point0[0], point0[1]); - for (var i = 1; i < n; i++) { - point1 = rotate(coordinates[i]); - v = visible(point1); - if (v !== v0) { - point2 = intersect(point0, point1); - if (d3_geo_circlePointsEqual(point0, point2) || d3_geo_circlePointsEqual(point1, point2)) { - point1[0] += ε; - point1[1] += ε; - v = visible(point1); - } - } - if (v !== v0) { - clean = false; - if (v0 = v) { - point2 = intersect(point1, point0); - context.moveTo(point2[0], point2[1]); - } else { - point2 = intersect(point0, point1); - context.lineTo(point2[0], point2[1]); - } - point0 = point2; - } - if (clean) { - p = d3_geo_stereographic(point1[0] + (v ? 0 : π), point1[1]); - x = p[0]; - y = p[1]; - area += y0 * x - x0 * y; - x0 = x; - y0 = y; - } - if (v && !d3_geo_circlePointsEqual(point0, point1)) context.lineTo(point1[0], point1[1]); - point0 = point1; - } - return [ clean && area * .5, v00 && v ]; - } - function intersect(a, b) { - var pa = d3_geo_circleCartesian(a, [ 0, 0, 0 ]), pb = d3_geo_circleCartesian(b, [ 0, 0, 0 ]); - var n1 = [ 1, 0, 0 ], n2 = d3_geo_circleCross(pa, pb), n2n2 = d3_geo_circleDot(n2, n2), n1n2 = n2[0], determinant = n2n2 - n1n2 * n1n2; - if (!determinant) return a; - var c1 = cr * n2n2 / determinant, c2 = -cr * n1n2 / determinant, n1xn2 = d3_geo_circleCross(n1, n2), A = d3_geo_circleScale(n1, c1), B = d3_geo_circleScale(n2, c2); - d3_geo_circleAdd(A, B); - var u = n1xn2, w = d3_geo_circleDot(A, u), uu = d3_geo_circleDot(u, u), t = Math.sqrt(w * w - uu * (d3_geo_circleDot(A, A) - 1)), q = d3_geo_circleScale(u, (-w - t) / uu); - d3_geo_circleAdd(q, A); - return d3_geo_circleSpherical(q); - } - } function d3_geo_circleInterpolate(radians, precision) { var cr = Math.cos(radians), sr = Math.sin(radians); - return function(from, to, direction, context) { + return function(from, to, direction, listener) { if (from != null) { from = d3_geo_circleAngle(cr, from); to = d3_geo_circleAngle(cr, to); @@ -5623,38 +5698,110 @@ from = radians + direction * 2 * π; to = radians; } + var point; for (var step = direction * precision, t = from; direction > 0 ? t > to : t < to; t -= step) { - var c = Math.cos(t), s = Math.sin(t), point = d3_geo_circleSpherical([ cr, -sr * c, -sr * s ]); - context.lineTo(point[0], point[1]); + listener.point((point = d3_geo_spherical([ cr, -sr * Math.cos(t), -sr * Math.sin(t) ]))[0], point[1]); } }; } - function d3_geo_circleClipPolygon(coordinates, context, clipLine, interpolate) { - var subject = [], clip = [], segments = [], buffer = d3_geo_circleBufferSegments(clipLine), draw = [], visibleArea = 0, invisibleArea = 0, invisible = false; - coordinates.forEach(function(ring) { - var x = buffer(ring, context), ringSegments = x[1], segment, n = ringSegments.length; - if (!n) { - invisible = true; - invisibleArea += x[0][0]; - return; + function d3_geo_circleAngle(cr, point) { + var a = d3_geo_cartesian(point); + a[0] -= cr; + d3_geo_cartesianNormalize(a); + var angle = Math.acos(Math.max(-1, Math.min(1, -a[1]))); + return ((-a[2] < 0 ? -angle : angle) + 2 * Math.PI - ε) % (2 * Math.PI); + } + function d3_geo_clip(pointVisible, clipLine, interpolate) { + return function(listener) { + var line = clipLine(listener); + var clip = { + point: point, + lineStart: lineStart, + lineEnd: lineEnd, + polygonStart: function() { + clip.point = pointRing; + clip.lineStart = ringStart; + clip.lineEnd = ringEnd; + invisible = false; + invisibleArea = visibleArea = 0; + segments = []; + listener.polygonStart(); + }, + polygonEnd: function() { + clip.point = point; + clip.lineStart = lineStart; + clip.lineEnd = lineEnd; + segments = d3.merge(segments); + if (segments.length) { + d3_geo_clipPolygon(segments, interpolate, listener); + } else if (visibleArea < -ε || invisible && invisibleArea < -ε) { + listener.lineStart(); + interpolate(null, null, 1, listener); + listener.lineEnd(); + } + listener.polygonEnd(); + segments = null; + }, + sphere: function() { + listener.polygonStart(); + listener.lineStart(); + interpolate(null, null, 1, listener); + listener.lineEnd(); + listener.polygonEnd(); + } + }; + function point(λ, φ) { + if (pointVisible(λ, φ)) listener.point(λ, φ); } - if (x[0][0] !== false) { - visibleArea += x[0][0]; - draw.push(segment = ringSegments[0]); - var point = segment[0], n = segment.length - 1, i = 0; - context.moveTo(point[0], point[1]); - while (++i < n) context.lineTo((point = segment[i])[0], point[1]); - context.closePath(); - return; + function pointLine(λ, φ) { + line.point(λ, φ); } - if (n > 1 && x[0][1]) ringSegments.push(ringSegments.pop().concat(ringSegments.shift())); - segments = segments.concat(ringSegments.filter(d3_geo_circleSegmentLength1)); - }); - if (!segments.length) { - if (visibleArea < 0 || invisible && invisibleArea < 0) { - d3_geo_projectionSphere(context, interpolate); + function lineStart() { + clip.point = pointLine; + line.lineStart(); } - } + function lineEnd() { + clip.point = point; + line.lineEnd(); + } + var segments, visibleArea, invisibleArea, invisible; + var buffer = d3_geo_clipBufferListener(), ringListener = clipLine(buffer), ring; + function pointRing(λ, φ) { + ringListener.point(λ, φ); + ring.push([ λ, φ ]); + } + function ringStart() { + ringListener.lineStart(); + ring = []; + } + function ringEnd() { + pointRing(ring[0][0], ring[0][1]); + ringListener.lineEnd(); + var clean = ringListener.clean(), ringSegments = buffer.buffer(), segment, n = ringSegments.length; + if (!n) { + invisible = true; + invisibleArea += d3_geo_clipAreaRing(ring, -1); + ring = null; + return; + } + ring = null; + if (clean & 1) { + segment = ringSegments[0]; + visibleArea += d3_geo_clipAreaRing(segment, 1); + var n = segment.length - 1, i = -1, point; + listener.lineStart(); + while (++i < n) listener.point((point = segment[i])[0], point[1]); + listener.lineEnd(); + return; + } + if (n > 1 && clean & 2) ringSegments.push(ringSegments.pop().concat(ringSegments.shift())); + segments.push(ringSegments.filter(d3_geo_clipSegmentLength1)); + } + return clip; + }; + } + function d3_geo_clipPolygon(segments, interpolate, listener) { + var subject = [], clip = []; segments.forEach(function(segment) { var n = segment.length; if (n <= 1) return; @@ -5696,112 +5843,224 @@ subject.push(a); clip.push(b); }); - clip.sort(d3_geo_circleClipSort); - d3_geo_circleLinkCircular(subject); - d3_geo_circleLinkCircular(clip); + clip.sort(d3_geo_clipSort); + d3_geo_clipLinkCircular(subject); + d3_geo_clipLinkCircular(clip); if (!subject.length) return; var start = subject[0], current, points, point; while (1) { current = start; while (current.visited) if ((current = current.next) === start) return; points = current.points; - context.moveTo((point = points.shift())[0], point[1]); + listener.lineStart(); do { current.visited = current.other.visited = true; if (current.entry) { if (current.subject) { - for (var i = 0; i < points.length; i++) context.lineTo((point = points[i])[0], point[1]); + for (var i = 0; i < points.length; i++) listener.point((point = points[i])[0], point[1]); } else { - interpolate(current.point, current.next.point, 1, context); + interpolate(current.point, current.next.point, 1, listener); } current = current.next; } else { if (current.subject) { points = current.prev.points; - for (var i = points.length; --i >= 0; ) context.lineTo((point = points[i])[0], point[1]); + for (var i = points.length; --i >= 0; ) listener.point((point = points[i])[0], point[1]); } else { - interpolate(current.point, current.prev.point, -1, context); + interpolate(current.point, current.prev.point, -1, listener); } current = current.prev; } current = current.other; points = current.points; } while (!current.visited); - context.closePath(); + listener.lineEnd(); } } - function d3_geo_circleLinkCircular(array) { - for (var i = 0, a = array[0], b, n = array.length; i < n; ) { - a.next = b = array[++i % n]; + function d3_geo_clipLinkCircular(array) { + if (!(n = array.length)) return; + var n, i = 0, a = array[0], b; + while (++i < n) { + a.next = b = array[i]; b.prev = a; a = b; } + a.next = b = array[0]; + b.prev = a; } - function d3_geo_circleClipSort(a, b) { + function d3_geo_clipSort(a, b) { return ((a = a.point)[0] < 0 ? a[1] - π / 2 - ε : π / 2 - a[1]) - ((b = b.point)[0] < 0 ? b[1] - π / 2 - ε : π / 2 - b[1]); } - function d3_geo_circleAngle(cr, point) { - var a = d3_geo_circleCartesian(point, [ cr, 0, 0 ]); - d3_geo_circleNormalize(a); - var angle = Math.acos(Math.max(-1, Math.min(1, -a[1]))); - return ((-a[2] < 0 ? -angle : angle) + 2 * Math.PI - ε) % (2 * Math.PI); - } - function d3_geo_circleCartesian(point, origin) { - var p0 = point[0], p1 = point[1], c1 = Math.cos(p1); - return [ c1 * Math.cos(p0) - origin[0], c1 * Math.sin(p0) - origin[1], Math.sin(p1) - origin[2] ]; - } - function d3_geo_circleSpherical(point) { - return [ Math.atan2(point[1], point[0]), Math.asin(Math.max(-1, Math.min(1, point[2]))) ]; - } - function d3_geo_circleDot(a, b) { - return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; - } - function d3_geo_circleCross(a, b) { - return [ a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0] ]; - } - function d3_geo_circleAdd(a, b) { - a[0] += b[0]; - a[1] += b[1]; - a[2] += b[2]; - } - function d3_geo_circleScale(vector, s) { - return [ vector[0] * s, vector[1] * s, vector[2] * s ]; - } - function d3_geo_circleNormalize(d) { - var l = Math.sqrt(d[0] * d[0] + d[1] * d[1] + d[2] * d[2]); - d[0] /= l; - d[1] /= l; - d[2] /= l; - } - function d3_geo_circleBufferSegments(f) { - return function(coordinates) { - var segments = [], segment; - return [ f(coordinates, { - moveTo: function(x, y) { - segments.push(segment = [ [ x, y ] ]); - }, - lineTo: function(x, y) { - segment.push([ x, y ]); - } - }, true), segments ]; - }; - } - function d3_geo_circlePointsEqual(a, b) { - return Math.abs(a[0] - b[0]) < ε && Math.abs(a[1] - b[1]) < ε; - } - function d3_geo_circleSegmentLength1(segment) { + function d3_geo_clipSegmentLength1(segment) { return segment.length > 1; } + function d3_geo_clipBufferListener() { + var lines = [], line; + return { + lineStart: function() { + lines.push(line = []); + }, + point: function(λ, φ) { + line.push([ λ, φ ]); + }, + lineEnd: d3_noop, + buffer: function() { + var buffer = lines; + lines = []; + line = null; + return buffer; + } + }; + } + function d3_geo_clipAreaRing(ring, invisible) { + if (!(n = ring.length)) return 0; + var n, i = 0, area = 0, p = ring[0], λ = p[0], φ = p[1], cosφ = Math.cos(φ), x0 = Math.atan2(invisible * Math.sin(λ) * cosφ, Math.sin(φ)), y0 = 1 - invisible * Math.cos(λ) * cosφ, x1 = x0, x, y; + while (++i < n) { + p = ring[i]; + cosφ = Math.cos(φ = p[1]); + x = Math.atan2(invisible * Math.sin(λ = p[0]) * cosφ, Math.sin(φ)); + y = 1 - invisible * Math.cos(λ) * cosφ; + if (Math.abs(y0 - 2) < ε && Math.abs(y - 2) < ε) continue; + if (Math.abs(y) < ε || Math.abs(y0) < ε) {} else if (Math.abs(Math.abs(x - x0) - π) < ε) { + if (y + y0 > 2) area += 4 * (x - x0); + } else if (Math.abs(y0 - 2) < ε) area += 4 * (x - x1); else area += ((3 * π + x - x0) % (2 * π) - π) * (y0 + y); + x1 = x0, x0 = x, y0 = y; + } + return area; + } + var d3_geo_clipAntimeridian = d3_geo_clip(d3_true, d3_geo_clipAntimeridianLine, d3_geo_clipAntimeridianInterpolate); + function d3_geo_clipAntimeridianLine(listener) { + var λ0 = NaN, φ0 = NaN, sλ0 = NaN, clean; + return { + lineStart: function() { + listener.lineStart(); + clean = 1; + }, + point: function(λ1, φ1) { + var sλ1 = λ1 > 0 ? π : -π, dλ = Math.abs(λ1 - λ0); + if (Math.abs(dλ - π) < ε) { + listener.point(λ0, φ0 = (φ0 + φ1) / 2 > 0 ? π / 2 : -π / 2); + listener.point(sλ0, φ0); + listener.lineEnd(); + listener.lineStart(); + listener.point(sλ1, φ0); + listener.point(λ1, φ0); + clean = 0; + } else if (sλ0 !== sλ1 && dλ >= π) { + if (Math.abs(λ0 - sλ0) < ε) λ0 -= sλ0 * ε; + if (Math.abs(λ1 - sλ1) < ε) λ1 -= sλ1 * ε; + φ0 = d3_geo_clipAntimeridianIntersect(λ0, φ0, λ1, φ1); + listener.point(sλ0, φ0); + listener.lineEnd(); + listener.lineStart(); + listener.point(sλ1, φ0); + clean = 0; + } + listener.point(λ0 = λ1, φ0 = φ1); + sλ0 = sλ1; + }, + lineEnd: function() { + listener.lineEnd(); + λ0 = φ0 = NaN; + }, + clean: function() { + return 2 - clean; + } + }; + } + function d3_geo_clipAntimeridianIntersect(λ0, φ0, λ1, φ1) { + var cosφ0, cosφ1, sinλ0_λ1 = Math.sin(λ0 - λ1); + return Math.abs(sinλ0_λ1) > ε ? Math.atan((Math.sin(φ0) * (cosφ1 = Math.cos(φ1)) * Math.sin(λ1) - Math.sin(φ1) * (cosφ0 = Math.cos(φ0)) * Math.sin(λ0)) / (cosφ0 * cosφ1 * sinλ0_λ1)) : (φ0 + φ1) / 2; + } + function d3_geo_clipAntimeridianInterpolate(from, to, direction, listener) { + var φ; + if (from == null) { + φ = direction * π / 2; + listener.point(-π, φ); + listener.point(0, φ); + listener.point(π, φ); + listener.point(π, 0); + listener.point(π, -φ); + listener.point(0, -φ); + listener.point(-π, -φ); + listener.point(-π, 0); + listener.point(-π, φ); + } else if (Math.abs(from[0] - to[0]) > ε) { + var s = (from[0] < to[0] ? 1 : -1) * π; + φ = direction * s / 2; + listener.point(-s, φ); + listener.point(0, φ); + listener.point(s, φ); + } else { + listener.point(to[0], to[1]); + } + } + function d3_geo_clipCircle(degrees) { + var radians = degrees * d3_radians, cr = Math.cos(radians), interpolate = d3_geo_circleInterpolate(radians, 6 * d3_radians); + return d3_geo_clip(visible, clipLine, interpolate); + function visible(λ, φ) { + return Math.cos(λ) * Math.cos(φ) > cr; + } + function clipLine(listener) { + var point0, v0, v00, clean; + return { + lineStart: function() { + v00 = v0 = false; + clean = 1; + }, + point: function(λ, φ) { + var point1 = [ λ, φ ], point2, v = visible(λ, φ); + if (!point0 && (v00 = v0 = v)) listener.lineStart(); + if (v !== v0) { + point2 = intersect(point0, point1); + if (d3_geo_sphericalEqual(point0, point2) || d3_geo_sphericalEqual(point1, point2)) { + point1[0] += ε; + point1[1] += ε; + v = visible(point1[0], point1[1]); + } + } + if (v !== v0) { + clean = 0; + if (v0 = v) { + listener.lineStart(); + point2 = intersect(point1, point0); + listener.point(point2[0], point2[1]); + } else { + point2 = intersect(point0, point1); + listener.point(point2[0], point2[1]); + listener.lineEnd(); + } + point0 = point2; + } + if (v && (!point0 || !d3_geo_sphericalEqual(point0, point1))) listener.point(point1[0], point1[1]); + point0 = point1; + }, + lineEnd: function() { + if (v0) listener.lineEnd(); + point0 = null; + }, + clean: function() { + return clean | (v00 && v0) << 1; + } + }; + } + function intersect(a, b) { + var pa = d3_geo_cartesian(a, 0), pb = d3_geo_cartesian(b, 0); + var n1 = [ 1, 0, 0 ], n2 = d3_geo_cartesianCross(pa, pb), n2n2 = d3_geo_cartesianDot(n2, n2), n1n2 = n2[0], determinant = n2n2 - n1n2 * n1n2; + if (!determinant) return a; + var c1 = cr * n2n2 / determinant, c2 = -cr * n1n2 / determinant, n1xn2 = d3_geo_cartesianCross(n1, n2), A = d3_geo_cartesianScale(n1, c1), B = d3_geo_cartesianScale(n2, c2); + d3_geo_cartesianAdd(A, B); + var u = n1xn2, w = d3_geo_cartesianDot(A, u), uu = d3_geo_cartesianDot(u, u), t = Math.sqrt(w * w - uu * (d3_geo_cartesianDot(A, A) - 1)), q = d3_geo_cartesianScale(u, (-w - t) / uu); + d3_geo_cartesianAdd(q, A); + return d3_geo_spherical(q); + } + } function d3_geo_compose(a, b) { - if (a === d3_geo_equirectangular) return b; - if (b === d3_geo_equirectangular) return a; - function compose(λ, φ) { - var coordinates = a(λ, φ); - return b(coordinates[0], coordinates[1]); + function compose(x, y) { + return x = a(x, y), b(x[0], x[1]); } if (a.invert && b.invert) compose.invert = function(x, y) { - var coordinates = b.invert(x, y); - return a.invert(coordinates[0], coordinates[1]); + return x = b.invert(x, y), x && a.invert(x[0], x[1]); }; return compose; } @@ -5821,12 +6080,15 @@ var x1, x0, y1, y0, dx = 22.5, dy = dx, x, y, precision = 2.5; function graticule() { return { - type: "GeometryCollection", - geometries: graticule.lines() + type: "MultiLineString", + coordinates: lines() }; } + function lines() { + return d3.range(Math.ceil(x0 / dx) * dx, x1, dx).map(x).concat(d3.range(Math.ceil(y0 / dy) * dy, y1, dy).map(y)); + } graticule.lines = function() { - return d3.range(Math.ceil(x0 / dx) * dx, x1, dx).map(x).concat(d3.range(Math.ceil(y0 / dy) * dy, y1, dy).map(y)).map(function(coordinates) { + return lines().map(function(coordinates) { return { type: "LineString", coordinates: coordinates @@ -5877,11 +6139,23 @@ }); }; } + d3.geo.interpolate = function(source, target) { + return d3_geo_interpolate(source[0] * d3_radians, source[1] * d3_radians, target[0] * d3_radians, target[1] * d3_radians); + }; + function d3_geo_interpolate(x0, y0, x1, y1) { + var cy0 = Math.cos(y0), sy0 = Math.sin(y0), cy1 = Math.cos(y1), sy1 = Math.sin(y1), kx0 = cy0 * Math.cos(x0), ky0 = cy0 * Math.sin(x0), kx1 = cy1 * Math.cos(x1), ky1 = cy1 * Math.sin(x1), d = Math.acos(Math.max(-1, Math.min(1, sy0 * sy1 + cy0 * cy1 * Math.cos(x1 - x0)))), k = 1 / Math.sin(d); + function interpolate(t) { + var B = Math.sin(t *= d) * k, A = Math.sin(d - t) * k, x = A * kx0 + B * kx1, y = A * ky0 + B * ky1, z = A * sy0 + B * sy1; + return [ Math.atan2(y, x) / d3_radians, Math.atan2(z, Math.sqrt(x * x + y * y)) / d3_radians ]; + } + interpolate.distance = d; + return interpolate; + } d3.geo.greatArc = function() { - var source = d3_source, p0, target = d3_target, p1, precision = 6 * d3_radians, interpolate = d3_geo_greatArcInterpolator(); + var source = d3_source, source_, target = d3_target, target_, precision = 6 * d3_radians, interpolate; function greatArc() { - var d = greatArc.distance.apply(this, arguments), t = 0, dt = precision / d, coordinates = [ p0 ]; - while ((t += dt) < 1) coordinates.push(interpolate(t)); + var p0 = source_ || source.apply(this, arguments), p1 = target_ || target.apply(this, arguments), i = interpolate || d3.geo.interpolate(p0, p1), t = 0, dt = precision / i.distance, coordinates = [ p0 ]; + while ((t += dt) < 1) coordinates.push(i(t)); coordinates.push(p1); return { type: "LineString", @@ -5889,20 +6163,18 @@ }; } greatArc.distance = function() { - if (typeof source === "function") interpolate.source(p0 = source.apply(this, arguments)); - if (typeof target === "function") interpolate.target(p1 = target.apply(this, arguments)); - return interpolate.distance(); + return (interpolate || d3.geo.interpolate(source_ || source.apply(this, arguments), target_ || target.apply(this, arguments))).distance; }; greatArc.source = function(_) { if (!arguments.length) return source; - source = _; - if (typeof source !== "function") interpolate.source(p0 = source); + source = _, source_ = typeof _ === "function" ? null : _; + interpolate = source_ && target_ ? d3.geo.interpolate(source_, target_) : null; return greatArc; }; greatArc.target = function(_) { if (!arguments.length) return target; - target = _; - if (typeof target !== "function") interpolate.target(p1 = target); + target = _, target_ = typeof _ === "function" ? null : _; + interpolate = source_ && target_ ? d3.geo.interpolate(source_, target_) : null; return greatArc; }; greatArc.precision = function(_) { @@ -5912,36 +6184,6 @@ }; return greatArc; }; - function d3_geo_greatArcInterpolator() { - var x0, y0, cy0, sy0, kx0, ky0, x1, y1, cy1, sy1, kx1, ky1, d, k; - function interpolate(t) { - var B = Math.sin(t *= d) * k, A = Math.sin(d - t) * k, x = A * kx0 + B * kx1, y = A * ky0 + B * ky1, z = A * sy0 + B * sy1; - return [ Math.atan2(y, x) / d3_radians, Math.atan2(z, Math.sqrt(x * x + y * y)) / d3_radians ]; - } - interpolate.distance = function() { - if (d == null) k = 1 / Math.sin(d = Math.acos(Math.max(-1, Math.min(1, sy0 * sy1 + cy0 * cy1 * Math.cos(x1 - x0))))); - return d; - }; - interpolate.source = function(_) { - var cx0 = Math.cos(x0 = _[0] * d3_radians), sx0 = Math.sin(x0); - cy0 = Math.cos(y0 = _[1] * d3_radians); - sy0 = Math.sin(y0); - kx0 = cy0 * cx0; - ky0 = cy0 * sx0; - d = null; - return interpolate; - }; - interpolate.target = function(_) { - var cx1 = Math.cos(x1 = _[0] * d3_radians), sx1 = Math.sin(x1); - cy1 = Math.cos(y1 = _[1] * d3_radians); - sy1 = Math.sin(y1); - kx1 = cy1 * cx1; - ky1 = cy1 * sx1; - d = null; - return interpolate; - }; - return interpolate; - } function d3_geo_mercator(λ, φ) { return [ λ / (2 * π), Math.max(-.5, Math.min(+.5, Math.log(Math.tan(π / 4 + φ / 2)) / (2 * π))) ]; } @@ -5958,203 +6200,290 @@ return d3_geo_projection(d3_geo_orthographic); }).raw = d3_geo_orthographic; d3.geo.path = function() { - var pointRadius = 4.5, pointCircle = d3_geo_pathCircle(pointRadius), projection = d3.geo.albersUsa(), bounds, buffer = []; - var bufferContext = { - point: function(x, y) { - buffer.push("M", x, ",", y, pointCircle); - }, - moveTo: function(x, y) { - buffer.push("M", x, ",", y); - }, - lineTo: function(x, y) { - buffer.push("L", x, ",", y); - }, - closePath: function() { - buffer.push("Z"); - } - }; - var area, centroidWeight, x00, y00, x0, y0, cx, cy; - var areaContext = { - point: d3_noop, - moveTo: moveTo, - lineTo: function(x, y) { - area += y0 * x - x0 * y; - x0 = x; - y0 = y; - }, - closePath: closePath - }; - var lineCentroidContext = { - point: function(x, y) { - cx += x; - cy += y; - ++centroidWeight; - }, - moveTo: moveTo, - lineTo: function(x, y) { - var dx = x - x0, dy = y - y0, δ = Math.sqrt(dx * dx + dy * dy); - centroidWeight += δ; - cx += δ * (x0 + x) / 2; - cy += δ * (y0 + y) / 2; - x0 = x; - y0 = y; - }, - closePath: closePath - }; - var polygonCentroidContext = { - point: d3_noop, - moveTo: moveTo, - lineTo: function(x, y) { - var δ = y0 * x - x0 * y; - centroidWeight += δ * 3; - cx += δ * (x0 + x); - cy += δ * (y0 + y); - x0 = x; - y0 = y; - }, - closePath: closePath - }; - function moveTo(x, y) { - x00 = x0 = x; - y00 = y0 = y; - } - function closePath() { - this.lineTo(x00, y00); - } - var context = bufferContext; + var pointRadius = 4.5, projection, context, projectStream, contextStream; function path(object) { - var result = null; - if (object != result) { - if (typeof pointRadius === "function") pointCircle = d3_geo_pathCircle(pointRadius.apply(this, arguments)); - pathType.object(object); - if (buffer.length) result = buffer.join(""), buffer = []; - } - return result; - } - var pathType = d3_geo_type({ - line: function(coordinates) { - projection.line(coordinates, context); - }, - polygon: function(coordinates) { - projection.polygon(coordinates, context); - }, - point: function(coordinates) { - projection.point(coordinates, context); - }, - Sphere: function() { - projection.sphere(context); - } - }); - var areaType = d3_geo_type({ - Feature: function(feature) { - return areaType.geometry(feature.geometry); - }, - FeatureCollection: function(collection) { - return d3.sum(collection.features, areaType.Feature); - }, - GeometryCollection: function(collection) { - return d3.sum(collection.geometries, areaType.geometry); - }, - LineString: d3_zero, - MultiLineString: d3_zero, - MultiPoint: d3_zero, - MultiPolygon: function(multiPolygon) { - return d3.sum(multiPolygon.coordinates, polygonArea); - }, - Point: d3_zero, - Polygon: function(polygon) { - return polygonArea(polygon.coordinates); - }, - Sphere: sphereArea - }); - function polygonArea(coordinates) { - area = 0; - projection.polygon(coordinates, areaContext); - return Math.abs(area) / 2; - } - function sphereArea() { - area = 0; - projection.sphere(areaContext); - return Math.abs(area) / 2; + if (object) d3.geo.stream(object, projectStream(contextStream.pointRadius(typeof pointRadius === "function" ? +pointRadius.apply(this, arguments) : pointRadius))); + return contextStream.result(); } path.area = function(object) { - return areaType.object(object); - }; - var centroidType = d3_geo_type({ - Feature: function(feature) { - return centroidType.geometry(feature.geometry); - }, - LineString: weightedCentroid(function(lineString) { - projection.line(lineString.coordinates, lineCentroidContext); - }), - MultiLineString: weightedCentroid(function(multiLineString) { - var coordinates = multiLineString.coordinates, i = -1, n = coordinates.length; - while (++i < n) projection.line(coordinates[i], lineCentroidContext); - }), - MultiPoint: weightedCentroid(function(multiPoint) { - var coordinates = multiPoint.coordinates, i = -1, n = coordinates.length; - while (++i < n) projection.point(coordinates[i], lineCentroidContext); - }), - MultiPolygon: weightedCentroid(function(multiPolygon) { - var coordinates = multiPolygon.coordinates, i = -1, n = coordinates.length; - while (++i < n) projection.polygon(coordinates[i], polygonCentroidContext); - }), - Point: weightedCentroid(function(point) { - projection.point(point.coordinates, lineCentroidContext); - }), - Polygon: weightedCentroid(function(polygon) { - projection.polygon(polygon.coordinates, polygonCentroidContext); - }), - Sphere: weightedCentroid(function() { - projection.sphere(polygonCentroidContext); - }) - }); - function weightedCentroid(f) { - return function() { - centroidWeight = cx = cy = 0; - f.apply(this, arguments); - return centroidWeight ? [ cx / centroidWeight, cy / centroidWeight ] : null; - }; - } - path.bounds = function(object) { - return (bounds || (bounds = d3_geo_bounds(projection)))(object); + d3_geo_pathAreaSum = 0; + d3.geo.stream(object, projectStream(d3_geo_pathArea)); + return d3_geo_pathAreaSum; }; path.centroid = function(object) { - return centroidType.object(object); + d3_geo_centroidDimension = d3_geo_centroidX = d3_geo_centroidY = d3_geo_centroidZ = 0; + d3.geo.stream(object, projectStream(d3_geo_pathCentroid)); + return d3_geo_centroidZ ? [ d3_geo_centroidX / d3_geo_centroidZ, d3_geo_centroidY / d3_geo_centroidZ ] : undefined; + }; + path.bounds = function(object) { + return d3_geo_bounds(projectStream)(object); }; path.projection = function(_) { if (!arguments.length) return projection; - projection = _; - bounds = null; + projectStream = (projection = _) ? _.stream || d3_geo_pathProjectStream(_) : d3_identity; return path; }; path.context = function(_) { - if (!arguments.length) return context === bufferContext ? null : context; - context = _; - if (context == null) context = bufferContext; + if (!arguments.length) return context; + contextStream = (context = _) == null ? new d3_geo_pathBuffer() : new d3_geo_pathContext(_); return path; }; - path.pointRadius = function(x) { + path.pointRadius = function(_) { if (!arguments.length) return pointRadius; - if (typeof x === "function") pointRadius = x; else pointCircle = d3_geo_pathCircle(pointRadius = +x); + pointRadius = typeof _ === "function" ? _ : +_; return path; }; - return path; + return path.projection(d3.geo.albersUsa()).context(null); }; function d3_geo_pathCircle(radius) { return "m0," + radius + "a" + radius + "," + radius + " 0 1,1 0," + -2 * radius + "a" + radius + "," + radius + " 0 1,1 0," + +2 * radius + "z"; } - var d3_geo_pathIdentity = d3.geo.path().projection({ - polygon: function(polygon, context) { - polygon.forEach(function(ring) { - var n = ring.length, i = 0, point; - context.moveTo((point = ring[0])[0], point[1]); - while (++i < n) context.lineTo((point = ring[i])[0], point[1]); - context.closePath(); - }); + function d3_geo_pathProjectStream(project) { + var resample = d3_geo_resample(function(λ, φ) { + return project([ λ * d3_degrees, φ * d3_degrees ]); + }); + return function(stream) { + stream = resample(stream); + return { + point: function(λ, φ) { + stream.point(λ * d3_radians, φ * d3_radians); + }, + sphere: function() { + stream.sphere(); + }, + lineStart: function() { + stream.lineStart(); + }, + lineEnd: function() { + stream.lineEnd(); + }, + polygonStart: function() { + stream.polygonStart(); + }, + polygonEnd: function() { + stream.polygonEnd(); + } + }; + }; + } + function d3_geo_pathBuffer() { + var pointCircle = d3_geo_pathCircle(4.5), buffer = []; + var stream = { + point: point, + lineStart: function() { + stream.point = pointLineStart; + }, + lineEnd: lineEnd, + polygonStart: function() { + stream.lineEnd = lineEndPolygon; + }, + polygonEnd: function() { + stream.lineEnd = lineEnd; + stream.point = point; + }, + pointRadius: function(_) { + pointCircle = d3_geo_pathCircle(_); + return stream; + }, + result: function() { + if (buffer.length) { + var result = buffer.join(""); + buffer = []; + return result; + } + } + }; + function point(x, y) { + buffer.push("M", x, ",", y, pointCircle); } - }); - d3.geo.centroid = d3_geo_pathIdentity.centroid; + function pointLineStart(x, y) { + buffer.push("M", x, ",", y); + stream.point = pointLine; + } + function pointLine(x, y) { + buffer.push("L", x, ",", y); + } + function lineEnd() { + stream.point = point; + } + function lineEndPolygon() { + buffer.push("Z"); + } + return stream; + } + function d3_geo_pathContext(context) { + var pointRadius = 4.5; + var stream = { + point: point, + lineStart: function() { + stream.point = pointLineStart; + }, + lineEnd: lineEnd, + polygonStart: function() { + stream.lineEnd = lineEndPolygon; + }, + polygonEnd: function() { + stream.lineEnd = lineEnd; + stream.point = point; + }, + pointRadius: function(_) { + pointRadius = _; + return stream; + }, + result: d3_noop + }; + function point(x, y) { + context.moveTo(x, y); + context.arc(x, y, pointRadius, 0, 2 * π); + } + function pointLineStart(x, y) { + context.moveTo(x, y); + stream.point = pointLine; + } + function pointLine(x, y) { + context.lineTo(x, y); + } + function lineEnd() { + stream.point = point; + } + function lineEndPolygon() { + context.closePath(); + } + return stream; + } + var d3_geo_pathAreaSum, d3_geo_pathAreaPolygon, d3_geo_pathArea = { + point: d3_noop, + lineStart: d3_noop, + lineEnd: d3_noop, + polygonStart: function() { + d3_geo_pathAreaPolygon = 0; + d3_geo_pathArea.lineStart = d3_geo_pathAreaRingStart; + }, + polygonEnd: function() { + d3_geo_pathArea.lineStart = d3_geo_pathArea.lineEnd = d3_geo_pathArea.point = d3_noop; + d3_geo_pathAreaSum += Math.abs(d3_geo_pathAreaPolygon / 2); + } + }; + function d3_geo_pathAreaRingStart() { + var x00, y00, x0, y0; + d3_geo_pathArea.point = function(x, y) { + d3_geo_pathArea.point = nextPoint; + x00 = x0 = x, y00 = y0 = y; + }; + function nextPoint(x, y) { + d3_geo_pathAreaPolygon += y0 * x - x0 * y; + x0 = x, y0 = y; + } + d3_geo_pathArea.lineEnd = function() { + nextPoint(x00, y00); + }; + } + var d3_geo_pathCentroid = { + point: d3_geo_pathCentroidPoint, + lineStart: d3_geo_pathCentroidLineStart, + lineEnd: d3_geo_pathCentroidLineEnd, + polygonStart: function() { + d3_geo_pathCentroid.lineStart = d3_geo_pathCentroidRingStart; + }, + polygonEnd: function() { + d3_geo_pathCentroid.point = d3_geo_pathCentroidPoint; + d3_geo_pathCentroid.lineStart = d3_geo_pathCentroidLineStart; + d3_geo_pathCentroid.lineEnd = d3_geo_pathCentroidLineEnd; + } + }; + function d3_geo_pathCentroidPoint(x, y) { + if (d3_geo_centroidDimension) return; + d3_geo_centroidX += x; + d3_geo_centroidY += y; + ++d3_geo_centroidZ; + } + function d3_geo_pathCentroidLineStart() { + var x0, y0; + if (d3_geo_centroidDimension !== 1) { + if (d3_geo_centroidDimension < 1) { + d3_geo_centroidDimension = 1; + d3_geo_centroidX = d3_geo_centroidY = d3_geo_centroidZ = 0; + } else return; + } + d3_geo_pathCentroid.point = function(x, y) { + d3_geo_pathCentroid.point = nextPoint; + x0 = x, y0 = y; + }; + function nextPoint(x, y) { + var dx = x - x0, dy = y - y0, z = Math.sqrt(dx * dx + dy * dy); + d3_geo_centroidX += z * (x0 + x) / 2; + d3_geo_centroidY += z * (y0 + y) / 2; + d3_geo_centroidZ += z; + x0 = x, y0 = y; + } + } + function d3_geo_pathCentroidLineEnd() { + d3_geo_pathCentroid.point = d3_geo_pathCentroidPoint; + } + function d3_geo_pathCentroidRingStart() { + var x00, y00, x0, y0; + if (d3_geo_centroidDimension < 2) { + d3_geo_centroidDimension = 2; + d3_geo_centroidX = d3_geo_centroidY = d3_geo_centroidZ = 0; + } + d3_geo_pathCentroid.point = function(x, y) { + d3_geo_pathCentroid.point = nextPoint; + x00 = x0 = x, y00 = y0 = y; + }; + function nextPoint(x, y) { + var z = y0 * x - x0 * y; + d3_geo_centroidX += z * (x0 + x); + d3_geo_centroidY += z * (y0 + y); + d3_geo_centroidZ += z * 3; + x0 = x, y0 = y; + } + d3_geo_pathCentroid.lineEnd = function() { + nextPoint(x00, y00); + }; + } + d3.geo.area = function(object) { + d3_geo_areaSum = 0; + d3.geo.stream(object, d3_geo_area); + return d3_geo_areaSum; + }; + var d3_geo_areaSum, d3_geo_areaRing; + var d3_geo_area = { + sphere: function() { + d3_geo_areaSum += 4 * π; + }, + point: d3_noop, + lineStart: d3_noop, + lineEnd: d3_noop, + polygonStart: function() { + d3_geo_areaRing = 0; + d3_geo_area.lineStart = d3_geo_areaRingStart; + }, + polygonEnd: function() { + d3_geo_areaSum += d3_geo_areaRing < 0 ? 4 * π + d3_geo_areaRing : d3_geo_areaRing; + d3_geo_area.lineStart = d3_geo_area.lineEnd = d3_geo_area.point = d3_noop; + } + }; + function d3_geo_areaRingStart() { + var λ00, φ00, λ1, λ0, φ0, cosφ0, sinφ0; + d3_geo_area.point = function(λ, φ) { + d3_geo_area.point = nextPoint; + λ1 = λ0 = (λ00 = λ) * d3_radians, φ0 = (φ00 = φ) * d3_radians, cosφ0 = Math.cos(φ0), + sinφ0 = Math.sin(φ0); + }; + function nextPoint(λ, φ) { + λ *= d3_radians, φ *= d3_radians; + if (Math.abs(Math.abs(φ0) - π / 2) < ε && Math.abs(Math.abs(φ) - π / 2) < ε) return; + var cosφ = Math.cos(φ), sinφ = Math.sin(φ); + if (Math.abs(φ0 - π / 2) < ε) d3_geo_areaRing += (λ - λ1) * 2; else { + var dλ = λ - λ0, cosdλ = Math.cos(dλ), d = Math.atan2(Math.sqrt((d = cosφ * Math.sin(dλ)) * d + (d = cosφ0 * sinφ - sinφ0 * cosφ * cosdλ) * d), sinφ0 * sinφ + cosφ0 * cosφ * cosdλ), s = (d + π + φ0 + φ) / 4; + d3_geo_areaRing += (dλ < 0 && dλ > -π || dλ > π ? -4 : 4) * Math.atan(Math.sqrt(Math.abs(Math.tan(s) * Math.tan(s - d / 2) * Math.tan(s - π / 4 - φ0 / 2) * Math.tan(s - π / 4 - φ / 2)))); + } + λ1 = λ0, λ0 = λ, φ0 = φ, cosφ0 = cosφ, sinφ0 = sinφ; + } + d3_geo_area.lineEnd = function() { + nextPoint(λ00, φ00); + }; + } d3.geo.projection = d3_geo_projection; d3.geo.projectionMutator = d3_geo_projectionMutator; function d3_geo_projection(project) { @@ -6163,86 +6492,26 @@ })(); } function d3_geo_projectionMutator(projectAt) { - var project, rotate, projectRotate, k = 150, x = 480, y = 250, λ = 0, φ = 0, δλ = 0, δφ = 0, δγ = 0, δx = x, δy = y, δ2 = .5, clip = d3_geo_projectionCutAntemeridian(rotatePoint), clipAngle = null, context; - function projection(coordinates) { - coordinates = projectRotate(coordinates[0] * d3_radians, coordinates[1] * d3_radians); - return [ coordinates[0] * k + δx, δy - coordinates[1] * k ]; + var project, rotate, projectRotate, projectResample = d3_geo_resample(function(x, y) { + x = project(x, y); + return [ x[0] * k + δx, δy - x[1] * k ]; + }), k = 150, x = 480, y = 250, λ = 0, φ = 0, δλ = 0, δφ = 0, δγ = 0, δx, δy, clip = d3_geo_clipAntimeridian, clipAngle = null; + function projection(point) { + point = projectRotate(point[0] * d3_radians, point[1] * d3_radians); + return [ point[0] * k + δx, δy - point[1] * k ]; } - function invert(coordinates) { - coordinates = projectRotate.invert((coordinates[0] - δx) / k, (δy - coordinates[1]) / k); - return [ coordinates[0] * d3_degrees, coordinates[1] * d3_degrees ]; + function invert(point) { + point = projectRotate.invert((point[0] - δx) / k, (δy - point[1]) / k); + return point && [ point[0] * d3_degrees, point[1] * d3_degrees ]; } - projection.point = function(coordinates, c) { - context = c; - clip.point(coordinates, resample); - context = null; - }; - projection.line = function(coordinates, c) { - context = c; - clip.line(coordinates, resample); - context = null; - }; - projection.polygon = function(coordinates, c) { - context = c; - clip.polygon(coordinates, resample); - context = null; - }; - projection.sphere = function(c) { - context = c; - clip.sphere(resample); - context = null; + projection.stream = function(stream) { + return d3_geo_projectionRadiansRotate(rotate, clip(projectResample(stream))); }; projection.clipAngle = function(_) { if (!arguments.length) return clipAngle; - clip = _ == null ? (clipAngle = _, d3_geo_projectionCutAntemeridian(rotatePoint)) : d3_geo_circleClip(clipAngle = +_, rotatePoint); + clip = _ == null ? (clipAngle = _, d3_geo_clipAntimeridian) : d3_geo_clipCircle(clipAngle = +_); return projection; }; - var λ00, φ00, λ0, sinφ0, cosφ0, x0, y0, maxDepth = 16; - function point(λ, φ) { - var p = projectPoint(λ, φ); - context.point(p[0], p[1]); - } - function moveTo(λ, φ) { - var p = projectPoint(λ00 = λ0 = λ, φ00 = φ); - sinφ0 = Math.sin(φ); - cosφ0 = Math.cos(φ); - context.moveTo(x0 = p[0], y0 = p[1]); - } - function lineTo(λ, φ) { - var p = projectPoint(λ, φ); - resampleLineTo(x0, y0, λ0, sinφ0, cosφ0, x0 = p[0], y0 = p[1], λ0 = λ, sinφ0 = Math.sin(φ), cosφ0 = Math.cos(φ), maxDepth); - context.lineTo(x0, y0); - } - function resampleLineTo(x0, y0, λ0, sinφ0, cosφ0, x1, y1, λ1, sinφ1, cosφ1, depth) { - var dx = x1 - x0, dy = y1 - y0, distance2 = dx * dx + dy * dy; - if (distance2 > 4 * δ2 && depth--) { - var cosΩ = sinφ0 * sinφ1 + cosφ0 * cosφ1 * Math.cos(λ1 - λ0), k = 1 / (Math.SQRT2 * Math.sqrt(1 + cosΩ)), x = k * (cosφ0 * Math.cos(λ0) + cosφ1 * Math.cos(λ1)), y = k * (cosφ0 * Math.sin(λ0) + cosφ1 * Math.sin(λ1)), z = Math.max(-1, Math.min(1, k * (sinφ0 + sinφ1))), φ2 = Math.asin(z), zε = Math.abs(Math.abs(z) - 1), λ2 = zε < ε || zε < εε && (Math.abs(cosφ0) < εε || Math.abs(cosφ1) < εε) ? (λ0 + λ1) / 2 : Math.atan2(y, x), p = projectPoint(λ2, φ2), x2 = p[0], y2 = p[1], dx2 = x0 - x2, dy2 = y0 - y2, dz = dx * dy2 - dy * dx2; - if (dz * dz / distance2 > δ2) { - var cosφ2 = Math.cos(φ2); - resampleLineTo(x0, y0, λ0, sinφ0, cosφ0, x2, y2, λ2, z, cosφ2, depth); - context.lineTo(x2, y2); - resampleLineTo(x2, y2, λ2, z, cosφ2, x1, y1, λ1, sinφ1, cosφ1, depth); - } - } - } - function closePath() { - var p = projectPoint(λ00, φ00); - resampleLineTo(x0, y0, λ0, sinφ0, cosφ0, p[0], p[1], λ00, Math.sin(φ00), Math.cos(φ00), maxDepth); - context.closePath(); - } - var resample = { - point: point, - moveTo: moveTo, - lineTo: lineTo, - closePath: closePath - }; - function rotatePoint(coordinates) { - return rotate(coordinates[0] * d3_radians, coordinates[1] * d3_radians); - } - function projectPoint(λ, φ) { - var point = project(λ, φ); - return [ point[0] * k + δx, δy - point[1] * k ]; - } projection.scale = function(_) { if (!arguments.length) return k; k = +_; @@ -6267,11 +6536,7 @@ δγ = _.length > 2 ? _[2] % 360 * d3_radians : 0; return reset(); }; - projection.precision = function(_) { - if (!arguments.length) return Math.sqrt(δ2); - maxDepth = (δ2 = _ * _) > 0 && 16; - return projection; - }; + d3.rebind(projection, projectResample, "precision"); function reset() { projectRotate = d3_geo_compose(rotate = d3_geo_rotation(δλ, δφ, δγ), project); var center = project(λ, φ); @@ -6285,104 +6550,35 @@ return reset(); }; } - function d3_geo_projectionIntersectAntemeridian(λ0, φ0, λ1, φ1) { - var cosφ0, cosφ1, sinλ0_λ1 = Math.sin(λ0 - λ1); - return Math.abs(sinλ0_λ1) > ε ? Math.atan((Math.sin(φ0) * (cosφ1 = Math.cos(φ1)) * Math.sin(λ1) - Math.sin(φ1) * (cosφ0 = Math.cos(φ0)) * Math.sin(λ0)) / (cosφ0 * cosφ1 * sinλ0_λ1)) : (φ0 + φ1) / 2; - } - function d3_geo_projectionCutAntemeridian(rotatePoint) { - var clip = { - point: function(coordinates, context) { - var point = rotatePoint(coordinates); - context.point(point[0], point[1]); + function d3_geo_projectionRadiansRotate(rotate, stream) { + return { + point: function(x, y) { + y = rotate(x * d3_radians, y * d3_radians), x = y[0]; + stream.point(x > π ? x - 2 * π : x < -π ? x + 2 * π : x, y[1]); }, - line: function(coordinates, context, ring) { - if (!(n = coordinates.length)) return [ ring && 0, false ]; - var point = rotatePoint(coordinates[0]), λ0 = point[0], φ0 = point[1], λ1, φ1, sλ0 = λ0 > 0 ? π : -π, sλ1, dλ, i = 0, n, clean = ring, area = 0, x0 = (point = d3_geo_stereographic(λ0, φ0))[0], x, y0 = point[1], y; - context.moveTo(λ0, φ0); - while (++i < n) { - point = rotatePoint(coordinates[i]); - λ1 = point[0]; - φ1 = point[1]; - sλ1 = λ1 > 0 ? π : -π; - dλ = Math.abs(λ1 - λ0); - if (Math.abs(dλ - π) < ε) { - context.lineTo(λ0, φ0 = (φ0 + φ1) / 2 > 0 ? π / 2 : -π / 2); - context.lineTo(sλ0, φ0); - context.moveTo(sλ1, φ0); - context.lineTo(λ1, φ0); - clean = false; - } else if (sλ0 !== sλ1 && dλ >= π) { - if (Math.abs(λ0 - sλ0) < ε) λ0 -= sλ0 * ε; - if (Math.abs(λ1 - sλ1) < ε) λ1 -= sλ1 * ε; - φ0 = d3_geo_projectionIntersectAntemeridian(λ0, φ0, λ1, φ1); - context.lineTo(sλ0, φ0); - context.moveTo(sλ1, φ0); - clean = false; - } - if (clean) { - x = (point = d3_geo_stereographic(λ1, φ1))[0]; - y = point[1]; - area += y0 * x - x0 * y; - x0 = x; - y0 = y; - } - context.lineTo(λ0 = λ1, φ0 = φ1); - sλ0 = sλ1; - } - return [ clean && area, true ]; + sphere: function() { + stream.sphere(); }, - polygon: function(polygon, context) { - d3_geo_circleClipPolygon(polygon, context, clip.line, d3_geo_antemeridianInterpolate); + lineStart: function() { + stream.lineStart(); }, - sphere: function(context) { - d3_geo_projectionSphere(context, d3_geo_antemeridianInterpolate); + lineEnd: function() { + stream.lineEnd(); + }, + polygonStart: function() { + stream.polygonStart(); + }, + polygonEnd: function() { + stream.polygonEnd(); } }; - return clip; - } - function d3_geo_antemeridianInterpolate(from, to, direction, context) { - var φ; - if (from == null) { - φ = direction * π / 2; - context.lineTo(-π, φ); - context.lineTo(0, φ); - context.lineTo(π, φ); - context.lineTo(π, 0); - context.lineTo(π, -φ); - context.lineTo(0, -φ); - context.lineTo(-π, -φ); - context.lineTo(-π, 0); - } else if (Math.abs(from[0] - to[0]) > ε) { - var s = (from[0] < to[0] ? 1 : -1) * π; - φ = direction * s / 2; - context.lineTo(-s, φ); - context.lineTo(0, φ); - context.lineTo(s, φ); - } else { - context.lineTo(to[0], to[1]); - } - } - function d3_geo_projectionSphere(context, interpolate) { - var moved = false; - interpolate(null, null, 1, { - lineTo: function(x, y) { - (moved ? context.lineTo : (moved = true, context.moveTo))(x, y); - } - }); - context.closePath(); } function d3_geo_rotation(δλ, δφ, δγ) { - return δλ ? δφ || δγ ? d3_geo_compose(d3_geo_rotationλ(δλ), d3_geo_rotationφγ(δφ, δγ)) : d3_geo_rotationλ(δλ) : δφ || δγ ? d3_geo_rotationφγ(δφ, δγ) : d3_geo_identityRotation; + return δλ ? δφ || δγ ? d3_geo_compose(d3_geo_rotationλ(δλ), d3_geo_rotationφγ(δφ, δγ)) : d3_geo_rotationλ(δλ) : δφ || δγ ? d3_geo_rotationφγ(δφ, δγ) : d3_geo_equirectangular; } - function d3_geo_identityRotation(λ, φ) { - return [ λ > π ? λ - 2 * π : λ < -π ? λ + 2 * π : λ, φ ]; - } - d3_geo_identityRotation.invert = function(x, y) { - return [ x, y ]; - }; function d3_geo_forwardRotationλ(δλ) { return function(λ, φ) { - return [ (λ += δλ) > π ? λ - 2 * π : λ < -π ? λ + 2 * π : λ, φ ]; + return λ += δλ, [ λ > π ? λ - 2 * π : λ < -π ? λ + 2 * π : λ, φ ]; }; } function d3_geo_rotationλ(δλ) {