Merge pull request #823 from jasondavies/projection
Interpolate polygon cuts correctly, and resample.
This commit is contained in:
Коммит
9ac8ad3c7d
|
@ -2030,6 +2030,36 @@
|
|||
coordinates = projectRotate.invert((coordinates[0] - δx) / k, (δy - coordinates[1]) / k);
|
||||
return [ coordinates[0] * d3_degrees, coordinates[1] * d3_degrees ];
|
||||
}
|
||||
function resample(context) {
|
||||
function lineTo(x0, y0, x, y, λ0, φ0, λ, φ, depth) {
|
||||
var dx, dy;
|
||||
if (depth && (dx = x - x0) * dx + (dy = y - y0) * dy > δ2) {
|
||||
var sinφ0 = Math.sin(φ0), cosφ0 = Math.cos(φ0), sinφ = Math.sin(φ), cosφ = Math.cos(φ), cosΩ = sinφ0 * sinφ + cosφ0 * cosφ * Math.cos(λ - λ0), k = 1 / (Math.SQRT2 * Math.sqrt(1 + cosΩ)), x1 = k * cosφ0 * Math.cos(λ0) + k * cosφ * Math.cos(λ), y1 = k * cosφ0 * Math.sin(λ0) + k * cosφ * Math.sin(λ), z1 = k * sinφ0 + k * sinφ, λ1 = Math.abs(x1) < ε || Math.abs(y1) < ε ? (λ0 + λ) / 2 : Math.atan2(y1, x1), φ1 = Math.asin(Math.max(-1, Math.min(1, z1))), point = projectPoint(λ1, φ1);
|
||||
lineTo(x0, y0, x0 = point[0], y0 = point[1], λ0, φ0, λ1, φ1, --depth);
|
||||
lineTo(x0, y0, x, y, λ1, φ1, λ, φ, depth);
|
||||
} else context.lineTo(x, y);
|
||||
}
|
||||
var λ0, φ0, x0, y0, δ2 = 100;
|
||||
return {
|
||||
moveTo: function(λ, φ) {
|
||||
var point = projectPoint(λ0 = λ, φ0 = φ);
|
||||
context.moveTo(x0 = point[0], y0 = point[1]);
|
||||
},
|
||||
lineTo: function(λ, φ) {
|
||||
var point = projectPoint(λ, φ);
|
||||
lineTo(x0, y0, x0 = point[0], y0 = point[1], λ0, φ0, λ0 = λ, φ0 = φ, 16);
|
||||
},
|
||||
closePath: function() {
|
||||
context.closePath();
|
||||
}
|
||||
};
|
||||
}
|
||||
function interpolateTo(s, context) {
|
||||
var point, φ = s / 2;
|
||||
context.lineTo(-s, φ);
|
||||
context.lineTo(0, φ);
|
||||
context.lineTo(s, φ);
|
||||
}
|
||||
function rotatePoint(coordinates) {
|
||||
return rotate(coordinates[0] * d3_radians, coordinates[1] * d3_radians);
|
||||
}
|
||||
|
@ -2051,44 +2081,51 @@
|
|||
};
|
||||
p.line = function(coordinates, context) {
|
||||
if (!(n = coordinates.length)) return;
|
||||
var point = rotatePoint(coordinates[0]), λ0 = point[0], φ0 = point[1], λ1, φ1, δλ, sλ0, i = 0, n;
|
||||
context.moveTo((point = projectPoint(λ0, φ0))[0], point[1]);
|
||||
context = resample(context);
|
||||
var point = rotatePoint(coordinates[0]), λ0 = point[0], φ0 = point[1], λ1, φ1, sλ0 = λ0 > 0 ? π : -π, sλ1, δλ, i = 0, n;
|
||||
context.moveTo(λ0, φ0);
|
||||
while (++i < n) {
|
||||
point = rotatePoint(coordinates[i]);
|
||||
λ1 = point[0];
|
||||
φ1 = point[1];
|
||||
sλ0 = λ0 > 0;
|
||||
if (sλ0 ^ λ1 > 0 && ((δλ = Math.abs(λ1 - λ0)) >= π || δλ < ε && Math.abs(Math.abs(λ0) - π) < ε)) {
|
||||
sλ1 = λ1 > 0 ? π : -π;
|
||||
if (sλ0 !== sλ1 && (δλ = Math.abs(λ1 - λ0)) >= π) {
|
||||
φ0 = d3_geo_projectionIntersectAntemeridian(λ0, φ0, λ1, φ1);
|
||||
context.lineTo((point = projectPoint(sλ0 ? π : -π, φ0))[0], point[1]);
|
||||
context.moveTo((point = projectPoint(sλ0 ? -π : π, φ0))[0], point[1]);
|
||||
context.lineTo(sλ0, φ0);
|
||||
context.moveTo(sλ1, φ0);
|
||||
}
|
||||
context.lineTo((point = projectPoint(λ0 = λ1, φ0 = φ1))[0], point[1]);
|
||||
context.lineTo(λ0 = λ1, φ0 = φ1);
|
||||
sλ0 = sλ1;
|
||||
}
|
||||
};
|
||||
p.ring = function(coordinates, context) {
|
||||
if (!(n = coordinates.length)) return;
|
||||
var point = rotatePoint(coordinates[0]), λ0 = point[0], φ0 = point[1], segment = [ point = projectPoint(λ0, φ0) ], λ1, φ1, δλ, sλ0, i = 0, first = true, start, n;
|
||||
context = resample(context);
|
||||
var point = rotatePoint(coordinates[0]), λ0 = point[0], φ0 = point[1], segment = [ point ], λ1, φ1, sλ0 = λ0 > 0 ? π : -π, sλ1, segmentSide, δλ, i = 0, first = true, side, n;
|
||||
while (++i < n) {
|
||||
point = rotatePoint(coordinates[i]);
|
||||
λ1 = point[0];
|
||||
φ1 = point[1];
|
||||
sλ0 = λ0 > 0;
|
||||
if (sλ0 ^ λ1 > 0 && ((δλ = Math.abs(λ1 - λ0)) >= π || δλ < ε && Math.abs(Math.abs(λ0) - π) < ε)) {
|
||||
sλ1 = λ1 > 0 ? π : -π;
|
||||
if (sλ0 !== sλ1 && (δλ = Math.abs(λ1 - λ0)) >= π) {
|
||||
φ0 = d3_geo_projectionIntersectAntemeridian(λ0, φ0, λ1, φ1);
|
||||
point = projectPoint(sλ0 ? π : -π, φ0);
|
||||
if (first) segment.push(point); else {
|
||||
context.lineTo(point[0], point[1]);
|
||||
if (first) segment.push([ sλ0, φ0 ]), segmentSide = sλ0; else {
|
||||
context.lineTo(sλ0, φ0);
|
||||
if (sλ0 !== side) interpolateTo(side, context);
|
||||
context.closePath();
|
||||
}
|
||||
context.moveTo((start = projectPoint(sλ0 ? -π : π, φ0))[0], start[1]);
|
||||
context.moveTo(sλ1, φ0);
|
||||
side = sλ1;
|
||||
first = false;
|
||||
}
|
||||
point = projectPoint(λ0 = λ1, φ0 = φ1);
|
||||
if (first) segment.push(point); else context.lineTo(point[0], point[1]);
|
||||
if (first) segment.push(point); else context.lineTo(λ1, φ1);
|
||||
λ0 = λ1;
|
||||
φ0 = φ1;
|
||||
sλ0 = sλ1;
|
||||
}
|
||||
if (first) context.moveTo((point = segment[0])[0], point[1]);
|
||||
for (i = 1, n = segment.length; i < n; i++) context.lineTo((point = segment[i])[0], point[1]);
|
||||
if (!first && side !== segmentSide) interpolateTo(side, context);
|
||||
context.closePath();
|
||||
};
|
||||
p.scale = function(_) {
|
||||
|
@ -6260,13 +6297,13 @@
|
|||
geometries: graticule.lines()
|
||||
};
|
||||
}
|
||||
var x1 = 180, x0 = -x1, y1 = 90, y0 = -y1, dx = 22.5, dy = dx;
|
||||
var x1 = 180, x0 = -x1, y1 = 90, y0 = -y1, dx = 22.5, dy = dx, δx = 2, δy = 2;
|
||||
graticule.lines = function() {
|
||||
var xSteps = d3.range(Math.ceil(x0 / dx) * dx, x1, dx).concat(x1), ySteps = d3.range(Math.ceil(y0 / dy) * dy, y1, dy).concat(y1), xLines = xSteps.map(function(x) {
|
||||
var xSteps = d3.range(x0, x1 - δx / 2, δx).concat(x1), ySteps = d3.range(y0, y1 - δy / 2, δy).concat(y1), xLines = d3.range(Math.ceil(x0 / dx) * dx, x1, dx).map(function(x) {
|
||||
return ySteps.map(function(y) {
|
||||
return [ x, y ];
|
||||
});
|
||||
}), yLines = ySteps.map(function(y) {
|
||||
}), yLines = d3.range(Math.ceil(y0 / dy) * dy, y1, dy).map(function(y) {
|
||||
return xSteps.map(function(x) {
|
||||
return [ x, y ];
|
||||
});
|
||||
|
@ -6350,22 +6387,10 @@
|
|||
d3.geo.path = function() {
|
||||
function path(object) {
|
||||
if (typeof pointRadius === "function") pointCircle = d3_geo_path_circle(pointRadius.apply(this, arguments));
|
||||
var buffer = [];
|
||||
pathObject(object, context || {
|
||||
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");
|
||||
}
|
||||
});
|
||||
return buffer.length ? buffer.join("") : null;
|
||||
pathObject(object, context);
|
||||
var path = buffer.length ? buffer.join("") : null;
|
||||
buffer = [];
|
||||
return path;
|
||||
}
|
||||
function pathObject(object, context) {
|
||||
var pathType = pathObjectByType.get(object.type);
|
||||
|
@ -6411,7 +6436,20 @@
|
|||
var coordinates = polygon.coordinates, i = -1, n = coordinates.length;
|
||||
while (++i < n) projection.ring(coordinates[i], context);
|
||||
}
|
||||
var pointRadius = 4.5, pointCircle = d3_geo_path_circle(pointRadius), projection = d3.geo.albersUsa(), context;
|
||||
var pointRadius = 4.5, pointCircle = d3_geo_path_circle(pointRadius), projection = d3.geo.albersUsa(), buffer = [], 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");
|
||||
}
|
||||
}, context = bufferContext;
|
||||
var pathObjectByType = d3.map({
|
||||
Feature: pathFeature,
|
||||
FeatureCollection: pathFeatureCollection,
|
||||
|
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -1,8 +1,8 @@
|
|||
// TODO projections need to resample for this to be usable
|
||||
d3.geo.graticule = function() {
|
||||
var x1 = 180, x0 = -x1,
|
||||
y1 = 90, y0 = -y1,
|
||||
dx = 22.5, dy = dx;
|
||||
dx = 22.5, dy = dx,
|
||||
δx = 2, δy = 2;
|
||||
|
||||
function graticule() {
|
||||
return {
|
||||
|
@ -12,10 +12,10 @@ d3.geo.graticule = function() {
|
|||
}
|
||||
|
||||
graticule.lines = function() {
|
||||
var xSteps = d3.range(Math.ceil(x0 / dx) * dx, x1, dx).concat(x1),
|
||||
ySteps = d3.range(Math.ceil(y0 / dy) * dy, y1, dy).concat(y1),
|
||||
xLines = xSteps.map(function(x) { return ySteps.map(function(y) { return [x, y]; }); }),
|
||||
yLines = ySteps.map(function(y) { return xSteps.map(function(x) { return [x, y]; }); });
|
||||
var xSteps = d3.range(x0, x1 - δx / 2, δx).concat(x1),
|
||||
ySteps = d3.range(y0, y1 - δy / 2, δy).concat(y1),
|
||||
xLines = d3.range(Math.ceil(x0 / dx) * dx, x1, dx).map(function(x) { return ySteps.map(function(y) { return [x, y]; }); }),
|
||||
yLines = d3.range(Math.ceil(y0 / dy) * dy, y1, dy).map(function(y) { return xSteps.map(function(x) { return [x, y]; }); });
|
||||
return xLines.concat(yLines).map(function(coordinates) {
|
||||
return {
|
||||
type: "LineString",
|
||||
|
|
|
@ -5,18 +5,21 @@ d3.geo.path = function() {
|
|||
var pointRadius = 4.5,
|
||||
pointCircle = d3_geo_path_circle(pointRadius),
|
||||
projection = d3.geo.albersUsa(),
|
||||
context;
|
||||
buffer = [],
|
||||
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"); }
|
||||
},
|
||||
context = bufferContext;
|
||||
|
||||
function path(object) {
|
||||
if (typeof pointRadius === "function") pointCircle = d3_geo_path_circle(pointRadius.apply(this, arguments));
|
||||
var buffer = [];
|
||||
pathObject(object, context || {
|
||||
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"); }
|
||||
});
|
||||
return buffer.length ? buffer.join("") : null;
|
||||
pathObject(object, context);
|
||||
var path = buffer.length ? buffer.join("") : null;
|
||||
buffer = [];
|
||||
return path;
|
||||
}
|
||||
|
||||
function pathObject(object, context) {
|
||||
|
|
|
@ -37,72 +37,131 @@ function d3_geo_projectionMutator(projectAt) {
|
|||
|
||||
p.line = function(coordinates, context) {
|
||||
if (!(n = coordinates.length)) return;
|
||||
context = resample(context);
|
||||
var point = rotatePoint(coordinates[0]),
|
||||
λ0 = point[0],
|
||||
φ0 = point[1],
|
||||
λ1,
|
||||
φ1,
|
||||
sλ0 = λ0 > 0 ? π : -π,
|
||||
sλ1,
|
||||
δλ,
|
||||
sλ0,
|
||||
i = 0,
|
||||
n;
|
||||
context.moveTo((point = projectPoint(λ0, φ0))[0], point[1]);
|
||||
context.moveTo(λ0, φ0);
|
||||
while (++i < n) {
|
||||
point = rotatePoint(coordinates[i]);
|
||||
λ1 = point[0];
|
||||
φ1 = point[1];
|
||||
sλ0 = λ0 > 0;
|
||||
if (sλ0 ^ (λ1 > 0) && ((δλ = Math.abs(λ1 - λ0)) >= π || δλ < ε && Math.abs(Math.abs(λ0) - π) < ε)) {
|
||||
sλ1 = λ1 > 0 ? π : -π;
|
||||
if (sλ0 !== sλ1 && (δλ = Math.abs(λ1 - λ0)) >= π) {
|
||||
φ0 = d3_geo_projectionIntersectAntemeridian(λ0, φ0, λ1, φ1);
|
||||
context.lineTo((point = projectPoint(sλ0 ? π : -π, φ0))[0], point[1]);
|
||||
context.moveTo((point = projectPoint(sλ0 ? -π : π, φ0))[0], point[1]);
|
||||
context.lineTo(sλ0, φ0);
|
||||
context.moveTo(sλ1, φ0);
|
||||
}
|
||||
context.lineTo((point = projectPoint(λ0 = λ1, φ0 = φ1))[0], point[1]);
|
||||
context.lineTo(λ0 = λ1, φ0 = φ1);
|
||||
sλ0 = sλ1;
|
||||
}
|
||||
};
|
||||
|
||||
p.ring = function(coordinates, context) {
|
||||
if (!(n = coordinates.length)) return;
|
||||
context = resample(context);
|
||||
var point = rotatePoint(coordinates[0]),
|
||||
λ0 = point[0],
|
||||
φ0 = point[1],
|
||||
segment = [point = projectPoint(λ0, φ0)],
|
||||
segment = [point],
|
||||
λ1,
|
||||
φ1,
|
||||
sλ0 = λ0 > 0 ? π : -π,
|
||||
sλ1,
|
||||
segmentSide, // the side of the last point in the buffered segment.
|
||||
δλ,
|
||||
sλ0,
|
||||
i = 0,
|
||||
first = true, // true when no intersections have been found yet.
|
||||
start, // the last start point (moveTo call).
|
||||
side, // the side of the last start point (moveTo call).
|
||||
n;
|
||||
while (++i < n) {
|
||||
point = rotatePoint(coordinates[i]);
|
||||
λ1 = point[0];
|
||||
φ1 = point[1];
|
||||
sλ0 = λ0 > 0;
|
||||
if (sλ0 ^ (λ1 > 0) && ((δλ = Math.abs(λ1 - λ0)) >= π || δλ < ε && Math.abs(Math.abs(λ0) - π) < ε)) {
|
||||
sλ1 = λ1 > 0 ? π : -π;
|
||||
if (sλ0 !== sλ1 && (δλ = Math.abs(λ1 - λ0)) >= π) {
|
||||
φ0 = d3_geo_projectionIntersectAntemeridian(λ0, φ0, λ1, φ1);
|
||||
point = projectPoint(sλ0 ? π : -π, φ0);
|
||||
if (first) segment.push(point);
|
||||
if (first) segment.push([sλ0, φ0]), segmentSide = sλ0;
|
||||
else {
|
||||
// TODO interpolate along cut edge.
|
||||
context.lineTo(point[0], point[1]);
|
||||
context.lineTo(sλ0, φ0);
|
||||
if (sλ0 !== side) interpolateTo(side, context);
|
||||
context.closePath();
|
||||
}
|
||||
context.moveTo((start = projectPoint(sλ0 ? -π : π, φ0))[0], start[1]);
|
||||
context.moveTo(sλ1, φ0);
|
||||
side = sλ1;
|
||||
first = false;
|
||||
}
|
||||
point = projectPoint(λ0 = λ1, φ0 = φ1);
|
||||
if (first) segment.push(point);
|
||||
else context.lineTo(point[0], point[1]);
|
||||
else context.lineTo(λ1, φ1);
|
||||
λ0 = λ1;
|
||||
φ0 = φ1;
|
||||
sλ0 = sλ1;
|
||||
}
|
||||
if (first) context.moveTo((point = segment[0])[0], point[1]);
|
||||
for (i = 1, n = segment.length; i < n; i++) context.lineTo((point = segment[i])[0], point[1]);
|
||||
// TODO interpolate along cut edge.
|
||||
// if (start) ...
|
||||
if (!first && side !== segmentSide) interpolateTo(side, context);
|
||||
context.closePath();
|
||||
};
|
||||
|
||||
function resample(context) {
|
||||
var λ0,
|
||||
φ0,
|
||||
x0,
|
||||
y0,
|
||||
δ2 = 100; // (precision in px)².
|
||||
|
||||
return {
|
||||
moveTo: function(λ, φ) {
|
||||
var point = projectPoint(λ0 = λ, φ0 = φ);
|
||||
context.moveTo(x0 = point[0], y0 = point[1]);
|
||||
},
|
||||
lineTo: function(λ, φ) {
|
||||
var point = projectPoint(λ, φ);
|
||||
lineTo(x0, y0, x0 = point[0], y0 = point[1], λ0, φ0, λ0 = λ, φ0 = φ, 16);
|
||||
},
|
||||
closePath: function() {
|
||||
context.closePath();
|
||||
}
|
||||
};
|
||||
|
||||
function lineTo(x0, y0, x, y, λ0, φ0, λ, φ, depth) {
|
||||
var dx,
|
||||
dy;
|
||||
if (depth && (dx = x - x0) * dx + (dy = y - y0) * dy > δ2) {
|
||||
var sinφ0 = Math.sin(φ0),
|
||||
cosφ0 = Math.cos(φ0),
|
||||
sinφ = Math.sin(φ),
|
||||
cosφ = Math.cos(φ),
|
||||
cosΩ = sinφ0 * sinφ + cosφ0 * cosφ * Math.cos(λ - λ0),
|
||||
k = 1 / (Math.SQRT2 * Math.sqrt(1 + cosΩ)),
|
||||
x1 = k * cosφ0 * Math.cos(λ0) + k * cosφ * Math.cos(λ),
|
||||
y1 = k * cosφ0 * Math.sin(λ0) + k * cosφ * Math.sin(λ),
|
||||
z1 = k * sinφ0 + k * sinφ,
|
||||
λ1 = Math.abs(x1) < ε || Math.abs(y1) < ε ? (λ0 + λ) / 2 : Math.atan2(y1, x1),
|
||||
φ1 = Math.asin(Math.max(-1, Math.min(1, z1))),
|
||||
point = projectPoint(λ1, φ1);
|
||||
lineTo(x0, y0, x0 = point[0], y0 = point[1], λ0, φ0, λ1, φ1, --depth);
|
||||
lineTo(x0, y0, x, y, λ1, φ1, λ, φ, depth);
|
||||
} else context.lineTo(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
function interpolateTo(s, context) {
|
||||
// TODO cache
|
||||
var point,
|
||||
φ = s / 2;
|
||||
context.lineTo(-s, φ);
|
||||
context.lineTo( 0, φ);
|
||||
context.lineTo( s, φ);
|
||||
}
|
||||
|
||||
// TODO remove redundant code with p(coordinates)
|
||||
function rotatePoint(coordinates) {
|
||||
return rotate(coordinates[0] * d3_radians, coordinates[1] * d3_radians);
|
||||
|
|
Загрузка…
Ссылка в новой задаче