| import "../core/identity"; | |
| import "../core/rebind"; | |
| import "../math/trigonometry"; | |
| import "clip-antimeridian"; | |
| import "clip-circle"; | |
| import "clip-extent"; | |
| import "compose"; | |
| import "geo"; | |
| import "path"; | |
| import "resample"; | |
| import "rotation"; | |
| import "transform"; | |
| d3.geo.projection = d3_geo_projection; | |
| d3.geo.projectionMutator = d3_geo_projectionMutator; | |
| function d3_geo_projection(project) { | |
| return d3_geo_projectionMutator(function() { return project; })(); | |
| } | |
| function d3_geo_projectionMutator(projectAt) { | |
| 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, // scale | |
| x = 480, y = 250, // translate | |
| λ = 0, φ = 0, // center | |
| δλ = 0, δφ = 0, δγ = 0, // rotate | |
| δx, δy, // center | |
| preclip = d3_geo_clipAntimeridian, | |
| postclip = d3_identity, | |
| clipAngle = null, | |
| clipExtent = null, | |
| stream; | |
| function projection(point) { | |
| point = projectRotate(point[0] * d3_radians, point[1] * d3_radians); | |
| return [point[0] * k + δx, δy - point[1] * k]; | |
| } | |
| 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.stream = function(output) { | |
| if (stream) stream.valid = false; | |
| stream = d3_geo_projectionRadians(preclip(rotate, projectResample(postclip(output)))); | |
| stream.valid = true; // allow caching by d3.geo.path | |
| return stream; | |
| }; | |
| projection.clipAngle = function(_) { | |
| if (!arguments.length) return clipAngle; | |
| preclip = _ == null ? (clipAngle = _, d3_geo_clipAntimeridian) : d3_geo_clipCircle((clipAngle = +_) * d3_radians); | |
| return invalidate(); | |
| }; | |
| projection.clipExtent = function(_) { | |
| if (!arguments.length) return clipExtent; | |
| clipExtent = _; | |
| postclip = _ ? d3_geo_clipExtent(_[0][0], _[0][1], _[1][0], _[1][1]) : d3_identity; | |
| return invalidate(); | |
| }; | |
| projection.scale = function(_) { | |
| if (!arguments.length) return k; | |
| k = +_; | |
| return reset(); | |
| }; | |
| projection.translate = function(_) { | |
| if (!arguments.length) return [x, y]; | |
| x = +_[0]; | |
| y = +_[1]; | |
| return reset(); | |
| }; | |
| projection.center = function(_) { | |
| if (!arguments.length) return [λ * d3_degrees, φ * d3_degrees]; | |
| λ = _[0] % 360 * d3_radians; | |
| φ = _[1] % 360 * d3_radians; | |
| return reset(); | |
| }; | |
| projection.rotate = function(_) { | |
| if (!arguments.length) return [δλ * d3_degrees, δφ * d3_degrees, δγ * d3_degrees]; | |
| δλ = _[0] % 360 * d3_radians; | |
| δφ = _[1] % 360 * d3_radians; | |
| δγ = _.length > 2 ? _[2] % 360 * d3_radians : 0; | |
| return reset(); | |
| }; | |
| d3.rebind(projection, projectResample, "precision"); | |
| function reset() { | |
| projectRotate = d3_geo_compose(rotate = d3_geo_rotation(δλ, δφ, δγ), project); | |
| var center = project(λ, φ); | |
| δx = x - center[0] * k; | |
| δy = y + center[1] * k; | |
| return invalidate(); | |
| } | |
| function invalidate() { | |
| if (stream) stream.valid = false, stream = null; | |
| return projection; | |
| } | |
| return function() { | |
| project = projectAt.apply(this, arguments); | |
| projection.invert = project.invert && invert; | |
| return reset(); | |
| }; | |
| } | |
| function d3_geo_projectionRadians(stream) { | |
| return d3_geo_transformPoint(stream, function(x, y) { | |
| stream.point(x * d3_radians, y * d3_radians); | |
| }); | |
| } |