Implement d3.geo.path centroid via d3.geo.stream.

This commit is contained in:
Mike Bostock 2012-12-12 16:17:02 -08:00
Родитель 8f02a72c95
Коммит 1a779c6f4d
6 изменённых файлов: 158 добавлений и 222 удалений

Просмотреть файл

@ -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 \

126
d3.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;

8
d3.min.js поставляемый

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

40
src/geo/path-area.js Normal file
Просмотреть файл

@ -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;
}

52
src/geo/path-centroid.js Normal file
Просмотреть файл

@ -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;
}
}

Просмотреть файл

@ -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;
}