Implement d3.geo.path centroid via d3.geo.stream.
This commit is contained in:
Родитель
8f02a72c95
Коммит
1a779c6f4d
2
Makefile
2
Makefile
|
@ -208,6 +208,8 @@ d3.geo.js: \
|
|||
src/geo/mercator.js \
|
||||
src/geo/orthographic.js \
|
||||
src/geo/path.js \
|
||||
src/geo/path-area.js \
|
||||
src/geo/path-centroid.js \
|
||||
src/geo/area.js \
|
||||
src/geo/centroid.js \
|
||||
src/geo/projection.js \
|
||||
|
|
|
@ -6336,76 +6336,14 @@
|
|||
d3.geo.stream(object, projection.stream(d3_geo_pathArea));
|
||||
return d3_geo_areaSum;
|
||||
};
|
||||
path.centroid = function(object) {
|
||||
d3_geo_centroidX = d3_geo_centroidY = d3_geo_centroidZ = 0;
|
||||
d3.geo.stream(object, projection.stream(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(projection)(object);
|
||||
};
|
||||
path.centroid = function(object) {
|
||||
return (object = projection.object(object)) ? centroidType(object) : null;
|
||||
};
|
||||
var centroidType = d3_geo_type({
|
||||
FeatureCollection: d3_noop,
|
||||
GeometryCollection: d3_noop,
|
||||
MultiLineString: weightedCentroid(function(line) {
|
||||
var n = line.length, i = 0, p = line[0], x0 = p[0], y0 = p[1], x, y, z = 0, cx = 0, cy = 0, dx, dy, δ;
|
||||
while (++i < n) {
|
||||
p = line[i];
|
||||
x = p[0];
|
||||
y = p[1];
|
||||
dx = x - x0;
|
||||
dy = y - y0;
|
||||
z += δ = Math.sqrt(dx * dx + dy * dy);
|
||||
cx += δ * (x0 + x) / 2;
|
||||
cy += δ * (y0 + y) / 2;
|
||||
x0 = x;
|
||||
y0 = y;
|
||||
}
|
||||
return [ cx, cy, z ];
|
||||
}),
|
||||
Point: function(point) {
|
||||
return point.coordinates;
|
||||
},
|
||||
MultiPoint: weightedCentroid(function(point) {
|
||||
return [ point[0], point[1], 1 ];
|
||||
}),
|
||||
Polygon: weightedCentroid(ringCentroid),
|
||||
MultiPolygon: weightedCentroid(function(polygon) {
|
||||
var x = 0, y = 0, z = 0, centroid;
|
||||
for (var i = 0, n = polygon.length; i < n; ++i) {
|
||||
centroid = ringCentroid(polygon[i]);
|
||||
x += centroid[0];
|
||||
y += centroid[1];
|
||||
z += centroid[2];
|
||||
}
|
||||
return [ x, y, z ];
|
||||
})
|
||||
});
|
||||
function ringCentroid(ring) {
|
||||
var n = ring.length, i = 0, p = ring[0], x0 = p[0], y0 = p[1], x, y, z = 0, cx = 0, cy = 0, δ;
|
||||
while (++i < n) {
|
||||
p = ring[i];
|
||||
x = p[0];
|
||||
y = p[1];
|
||||
δ = y0 * x - x0 * y;
|
||||
z += δ * 3;
|
||||
cx += δ * (x0 + x);
|
||||
cy += δ * (y0 + y);
|
||||
x0 = x;
|
||||
y0 = y;
|
||||
}
|
||||
return [ cx, cy, z ];
|
||||
}
|
||||
function weightedCentroid(f) {
|
||||
return function(o) {
|
||||
var coordinates = o.coordinates, centroid, x = 0, y = 0, z = 0, i = -1, n = coordinates.length;
|
||||
while (++i < n) {
|
||||
centroid = f(coordinates[i]);
|
||||
x += centroid[0];
|
||||
y += centroid[1];
|
||||
z += centroid[2];
|
||||
}
|
||||
return z ? [ x / z, y / z ] : null;
|
||||
};
|
||||
}
|
||||
path.projection = function(_) {
|
||||
if (!arguments.length) return projection;
|
||||
projection = _;
|
||||
|
@ -6426,13 +6364,12 @@
|
|||
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_pathExterior;
|
||||
var d3_geo_pathArea = {
|
||||
var d3_geo_pathAreaScale, d3_geo_pathArea = {
|
||||
point: d3_noop,
|
||||
lineStart: d3_noop,
|
||||
lineEnd: d3_noop,
|
||||
polygonStart: function() {
|
||||
d3_geo_pathExterior = true;
|
||||
d3_geo_pathAreaScale = .5;
|
||||
d3_geo_pathArea.lineStart = d3_geo_pathAreaRingStart;
|
||||
d3_geo_pathArea.lineEnd = d3_geo_pathAreaRingEnd;
|
||||
},
|
||||
|
@ -6453,8 +6390,53 @@
|
|||
}
|
||||
}
|
||||
function d3_geo_pathAreaRingEnd() {
|
||||
d3_geo_areaSum += Math.abs(d3_geo_areaRing) / (d3_geo_pathExterior ? 2 : -2);
|
||||
d3_geo_pathExterior = false;
|
||||
d3_geo_areaSum += Math.abs(d3_geo_areaRing) * d3_geo_pathAreaScale;
|
||||
d3_geo_pathAreaScale = -.5;
|
||||
}
|
||||
var d3_geo_pathCentroid = {
|
||||
point: function(x, y) {
|
||||
d3_geo_centroidX += x;
|
||||
d3_geo_centroidY += y;
|
||||
++d3_geo_centroidZ;
|
||||
},
|
||||
lineStart: d3_geo_pathCentroidLineStart,
|
||||
lineEnd: function() {
|
||||
d3_geo_pathCentroid.point = d3_noop;
|
||||
},
|
||||
polygonStart: function() {
|
||||
d3_geo_pathCentroid.lineStart = d3_geo_pathCentroidRingStart;
|
||||
},
|
||||
polygonEnd: function() {
|
||||
d3_geo_pathCentroid.lineStart = d3_geo_pathCentroidLineStart;
|
||||
}
|
||||
};
|
||||
function d3_geo_pathCentroidLineStart() {
|
||||
var x0, y0;
|
||||
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_pathCentroidRingStart() {
|
||||
var x0, y0;
|
||||
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 = 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.area = function(object) {
|
||||
d3_geo_areaSum = 0;
|
||||
|
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -0,0 +1,40 @@
|
|||
// TODO Unify this code with d3.geom.polygon area?
|
||||
|
||||
var d3_geo_pathAreaScale, d3_geo_pathArea = {
|
||||
point: d3_noop,
|
||||
lineStart: d3_noop,
|
||||
lineEnd: d3_noop,
|
||||
|
||||
// Only count area for polygon rings.
|
||||
polygonStart: function() {
|
||||
d3_geo_pathAreaScale = .5;
|
||||
d3_geo_pathArea.lineStart = d3_geo_pathAreaRingStart;
|
||||
d3_geo_pathArea.lineEnd = d3_geo_pathAreaRingEnd;
|
||||
},
|
||||
polygonEnd: function() {
|
||||
d3_geo_pathArea.lineStart = d3_geo_pathArea.lineEnd = d3_geo_pathArea.point = d3_noop;
|
||||
}
|
||||
};
|
||||
|
||||
function d3_geo_pathAreaRingStart() {
|
||||
var x0, y0;
|
||||
|
||||
d3_geo_areaRing = 0;
|
||||
|
||||
// For the first point, …
|
||||
d3_geo_pathArea.point = function(x, y) {
|
||||
d3_geo_pathArea.point = nextPoint;
|
||||
x0 = x, y0 = y;
|
||||
};
|
||||
|
||||
// For subsequent points, …
|
||||
function nextPoint(x, y) {
|
||||
d3_geo_areaRing += y0 * x - x0 * y;
|
||||
x0 = x, y0 = y;
|
||||
}
|
||||
}
|
||||
|
||||
function d3_geo_pathAreaRingEnd() {
|
||||
d3_geo_areaSum += Math.abs(d3_geo_areaRing) * d3_geo_pathAreaScale;
|
||||
d3_geo_pathAreaScale = -.5;
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
// TODO Unify this code with d3.geom.polygon centroid?
|
||||
// TODO Enforce positive area for exterior, negative area for interior?
|
||||
|
||||
var d3_geo_pathCentroid = {
|
||||
point: function(x, y) {
|
||||
d3_geo_centroidX += x;
|
||||
d3_geo_centroidY += y;
|
||||
++d3_geo_centroidZ;
|
||||
},
|
||||
|
||||
// For lines, weight by length.
|
||||
lineStart: d3_geo_pathCentroidLineStart,
|
||||
lineEnd: function() { d3_geo_pathCentroid.point = d3_noop; },
|
||||
|
||||
// For polygons, weight by area.
|
||||
polygonStart: function() { d3_geo_pathCentroid.lineStart = d3_geo_pathCentroidRingStart; },
|
||||
polygonEnd: function() { d3_geo_pathCentroid.lineStart = d3_geo_pathCentroidLineStart; }
|
||||
};
|
||||
|
||||
function d3_geo_pathCentroidLineStart() {
|
||||
var x0, y0;
|
||||
|
||||
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_pathCentroidRingStart() {
|
||||
var x0, y0;
|
||||
|
||||
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 = 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;
|
||||
}
|
||||
}
|
152
src/geo/path.js
152
src/geo/path.js
|
@ -23,115 +23,16 @@ d3.geo.path = function() {
|
|||
return d3_geo_areaSum;
|
||||
};
|
||||
|
||||
path.centroid = function(object) {
|
||||
d3_geo_centroidX = d3_geo_centroidY = d3_geo_centroidZ = 0;
|
||||
d3.geo.stream(object, projection.stream(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(projection)(object);
|
||||
};
|
||||
|
||||
path.centroid = function(object) {
|
||||
return (object = projection.object(object)) ? centroidType(object) : null;
|
||||
};
|
||||
|
||||
var centroidType = d3_geo_type({
|
||||
FeatureCollection: d3_noop,
|
||||
GeometryCollection: d3_noop,
|
||||
|
||||
MultiLineString: weightedCentroid(function(line) {
|
||||
var n = line.length,
|
||||
i = 0,
|
||||
p = line[0],
|
||||
x0 = p[0],
|
||||
y0 = p[1],
|
||||
x,
|
||||
y,
|
||||
z = 0,
|
||||
cx = 0,
|
||||
cy = 0,
|
||||
dx,
|
||||
dy,
|
||||
δ;
|
||||
while (++i < n) {
|
||||
p = line[i];
|
||||
x = p[0];
|
||||
y = p[1];
|
||||
dx = x - x0;
|
||||
dy = y - y0;
|
||||
z += δ = Math.sqrt(dx * dx + dy * dy);
|
||||
cx += δ * (x0 + x) / 2;
|
||||
cy += δ * (y0 + y) / 2;
|
||||
x0 = x;
|
||||
y0 = y;
|
||||
}
|
||||
return [cx, cy, z];
|
||||
}),
|
||||
|
||||
Point: function(point) { return point.coordinates; },
|
||||
|
||||
MultiPoint: weightedCentroid(function(point) {
|
||||
return [point[0], point[1], 1];
|
||||
}),
|
||||
|
||||
Polygon: weightedCentroid(ringCentroid),
|
||||
|
||||
MultiPolygon: weightedCentroid(function(polygon) {
|
||||
var x = 0,
|
||||
y = 0,
|
||||
z = 0,
|
||||
centroid;
|
||||
for (var i = 0, n = polygon.length; i < n; ++i) {
|
||||
centroid = ringCentroid(polygon[i]);
|
||||
x += centroid[0];
|
||||
y += centroid[1];
|
||||
z += centroid[2];
|
||||
}
|
||||
return [x, y, z];
|
||||
})
|
||||
});
|
||||
|
||||
function ringCentroid(ring) {
|
||||
var n = ring.length,
|
||||
i = 0,
|
||||
p = ring[0],
|
||||
x0 = p[0],
|
||||
y0 = p[1],
|
||||
x,
|
||||
y,
|
||||
z = 0,
|
||||
cx = 0,
|
||||
cy = 0,
|
||||
δ;
|
||||
while (++i < n) {
|
||||
p = ring[i];
|
||||
x = p[0];
|
||||
y = p[1];
|
||||
δ = y0 * x - x0 * y;
|
||||
z += δ * 3;
|
||||
cx += δ * (x0 + x);
|
||||
cy += δ * (y0 + y);
|
||||
x0 = x;
|
||||
y0 = y;
|
||||
}
|
||||
return [cx, cy, z];
|
||||
}
|
||||
|
||||
function weightedCentroid(f) {
|
||||
return function(o) {
|
||||
var coordinates = o.coordinates,
|
||||
centroid,
|
||||
x = 0,
|
||||
y = 0,
|
||||
z = 0,
|
||||
i = -1,
|
||||
n = coordinates.length;
|
||||
while (++i < n) {
|
||||
centroid = f(coordinates[i]);
|
||||
x += centroid[0];
|
||||
y += centroid[1];
|
||||
z += centroid[2];
|
||||
}
|
||||
return z ? [x / z, y / z] : null;
|
||||
};
|
||||
}
|
||||
|
||||
path.projection = function(_) {
|
||||
if (!arguments.length) return projection;
|
||||
projection = _;
|
||||
|
@ -160,44 +61,3 @@ function d3_geo_pathCircle(radius) {
|
|||
+ "a" + radius + "," + radius + " 0 1,1 0," + (+2 * radius)
|
||||
+ "z";
|
||||
}
|
||||
|
||||
var d3_geo_pathExterior;
|
||||
|
||||
var d3_geo_pathArea = {
|
||||
point: d3_noop,
|
||||
lineStart: d3_noop,
|
||||
lineEnd: d3_noop,
|
||||
|
||||
// Only count area for polygon rings.
|
||||
polygonStart: function() {
|
||||
d3_geo_pathExterior = true;
|
||||
d3_geo_pathArea.lineStart = d3_geo_pathAreaRingStart;
|
||||
d3_geo_pathArea.lineEnd = d3_geo_pathAreaRingEnd;
|
||||
},
|
||||
polygonEnd: function() {
|
||||
d3_geo_pathArea.lineStart = d3_geo_pathArea.lineEnd = d3_geo_pathArea.point = d3_noop;
|
||||
}
|
||||
};
|
||||
|
||||
function d3_geo_pathAreaRingStart() {
|
||||
var x0, y0;
|
||||
|
||||
d3_geo_areaRing = 0;
|
||||
|
||||
// For the first point, …
|
||||
d3_geo_pathArea.point = function(x, y) {
|
||||
d3_geo_pathArea.point = nextPoint;
|
||||
x0 = x, y0 = y;
|
||||
};
|
||||
|
||||
// For subsequent points, …
|
||||
function nextPoint(x, y) {
|
||||
d3_geo_areaRing += y0 * x - x0 * y;
|
||||
x0 = x, y0 = y;
|
||||
}
|
||||
}
|
||||
|
||||
function d3_geo_pathAreaRingEnd() {
|
||||
d3_geo_areaSum += Math.abs(d3_geo_areaRing) / (d3_geo_pathExterior ? 2 : -2);
|
||||
d3_geo_pathExterior = false;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче