This commit is contained in:
Mike Bostock 2011-09-27 15:00:27 -07:00
Родитель 151d09d479 bbcaa27732
Коммит 9e16bee0a5
40 изменённых файлов: 1652 добавлений и 621 удалений

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

@ -1,7 +1,8 @@
# See the README for installation instructions.
JS_COMPILER = ./node_modules/uglify-js/bin/uglifyjs
JS_TESTER = ./node_modules/vows/bin/vows
NODE_PATH ?= ./node_modules
JS_COMPILER = $(NODE_PATH)/uglify-js/bin/uglifyjs
JS_TESTER = $(NODE_PATH)/vows/bin/vows
all: \
d3.js \
@ -183,10 +184,14 @@ d3.geo.js: \
src/geo/geo.js \
src/geo/azimuthal.js \
src/geo/albers.js \
src/geo/bonne.js \
src/geo/equirectangular.js \
src/geo/mercator.js \
src/geo/type.js \
src/geo/path.js \
src/geo/bounds.js \
src/geo/circle.js \
src/geo/greatArc.js \
src/geo/greatCircle.js \
src/end.js
@ -245,6 +250,10 @@ d3.js d3%.js: Makefile
cat $(filter %.js,$^) > $@
@chmod a-w $@
install:
mkdir -p node_modules
npm install
package.json: d3.js
node src/package.js > $@

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

@ -45,7 +45,7 @@ developing on Mac OS X, an easy way to install Node and NPM is using
Next, from the root directory of this repository, install D3's dependencies:
npm install
make install
You can see the list of dependencies in package.json. The packages will be
installed in the node_modules directory.
You can see the list of dependencies in package.json. NPM will install the
packages in the node_modules directory.

485
d3.geo.js
Просмотреть файл

@ -1,7 +1,9 @@
(function(){d3.geo = {};
var d3_geo_radians = Math.PI / 180;
// TODO clip input coordinates on opposite hemisphere
d3.geo.azimuthal = function() {
var mode = "orthographic", // or stereographic
var mode = "orthographic", // or stereographic, gnomonic, equidistant or equalarea
origin,
scale = 200,
translate = [480, 250],
@ -11,13 +13,19 @@ d3.geo.azimuthal = function() {
sy0;
function azimuthal(coordinates) {
var x1 = coordinates[0] * d3_radians - x0,
y1 = coordinates[1] * d3_radians,
var x1 = coordinates[0] * d3_geo_radians - x0,
y1 = coordinates[1] * d3_geo_radians,
cx1 = Math.cos(x1),
sx1 = Math.sin(x1),
cy1 = Math.cos(y1),
sy1 = Math.sin(y1),
k = mode === "stereographic" ? 1 / (1 + sy0 * sy1 + cy0 * cy1 * cx1) : 1,
cc = mode !== "orthographic" ? sy0 * sy1 + cy0 * cy1 * cx1 : null,
c,
k = mode === "stereographic" ? 1 / (1 + cc)
: mode === "gnomonic" ? 1 / cc
: mode === "equidistant" ? (c = Math.acos(cc), c ? c / Math.sin(c) : 0)
: mode === "equalarea" ? Math.sqrt(2 / (1 + cc))
: 1,
x = k * cy1 * sx1,
y = k * (sy0 * cy1 * cx1 - cy0 * sy1);
return [
@ -30,12 +38,16 @@ d3.geo.azimuthal = function() {
var x = (coordinates[0] - translate[0]) / scale,
y = (coordinates[1] - translate[1]) / scale,
p = Math.sqrt(x * x + y * y),
c = mode === "stereographic" ? 2 * Math.atan(p) : Math.asin(p),
c = mode === "stereographic" ? 2 * Math.atan(p)
: mode === "gnomonic" ? Math.atan(p)
: mode === "equidistant" ? p
: mode === "equalarea" ? 2 * Math.asin(.5 * p)
: Math.asin(p),
sc = Math.sin(c),
cc = Math.cos(c);
return [
(x0 + Math.atan2(x * sc, p * cy0 * cc + y * sy0 * sc)) / d3_radians,
Math.asin(cc * sy0 - (y * sc * cy0) / p) / d3_radians
(x0 + Math.atan2(x * sc, p * cy0 * cc + y * sy0 * sc)) / d3_geo_radians,
Math.asin(cc * sy0 - (p ? (y * sc * cy0) / p : 0)) / d3_geo_radians
];
};
@ -48,8 +60,8 @@ d3.geo.azimuthal = function() {
azimuthal.origin = function(x) {
if (!arguments.length) return origin;
origin = x;
x0 = origin[0] * d3_radians;
y0 = origin[1] * d3_radians;
x0 = origin[0] * d3_geo_radians;
y0 = origin[1] * d3_geo_radians;
cy0 = Math.cos(y0);
sy0 = Math.sin(y0);
return azimuthal;
@ -78,14 +90,14 @@ d3.geo.albers = function() {
parallels = [29.5, 45.5],
scale = 1000,
translate = [480, 250],
lng0, // d3_radians * origin[0]
lng0, // d3_geo_radians * origin[0]
n,
C,
p0;
function albers(coordinates) {
var t = n * (d3_radians * coordinates[0] - lng0),
p = Math.sqrt(C - 2 * n * Math.sin(d3_radians * coordinates[1])) / n;
var t = n * (d3_geo_radians * coordinates[0] - lng0),
p = Math.sqrt(C - 2 * n * Math.sin(d3_geo_radians * coordinates[1])) / n;
return [
scale * p * Math.sin(t) + translate[0],
scale * (p * Math.cos(t) - p0) + translate[1]
@ -99,18 +111,18 @@ d3.geo.albers = function() {
t = Math.atan2(x, p0y),
p = Math.sqrt(x * x + p0y * p0y);
return [
(lng0 + t / n) / d3_radians,
Math.asin((C - p * p * n * n) / (2 * n)) / d3_radians
(lng0 + t / n) / d3_geo_radians,
Math.asin((C - p * p * n * n) / (2 * n)) / d3_geo_radians
];
};
function reload() {
var phi1 = d3_radians * parallels[0],
phi2 = d3_radians * parallels[1],
lat0 = d3_radians * origin[1],
var phi1 = d3_geo_radians * parallels[0],
phi2 = d3_geo_radians * parallels[1],
lat0 = d3_geo_radians * origin[1],
s = Math.sin(phi1),
c = Math.cos(phi1);
lng0 = d3_radians * origin[0];
lng0 = d3_geo_radians * origin[0];
n = .5 * (s + Math.sin(phi2));
C = c * c + 2 * n * s;
p0 = Math.sqrt(C - 2 * n * Math.sin(lat0)) / n;
@ -195,8 +207,76 @@ d3.geo.albersUsa = function() {
return albersUsa.scale(lower48.scale());
};
d3.geo.bonne = function() {
var scale = 200,
translate = [480, 250],
x0, // origin longitude in radians
y0, // origin latitude in radians
y1, // parallel latitude in radians
c1; // cot(y1)
var d3_radians = Math.PI / 180;
function bonne(coordinates) {
var x = coordinates[0] * d3_geo_radians - x0,
y = coordinates[1] * d3_geo_radians - y0;
if (y1) {
var p = c1 + y1 - y, E = x * Math.cos(y) / p;
x = p * Math.sin(E);
y = p * Math.cos(E) - c1;
} else {
x *= Math.cos(y);
y *= -1;
}
return [
scale * x + translate[0],
scale * y + translate[1]
];
}
bonne.invert = function(coordinates) {
var x = (coordinates[0] - translate[0]) / scale,
y = (coordinates[1] - translate[1]) / scale;
if (y1) {
var c = c1 + y, p = Math.sqrt(x * x + c * c);
y = c1 + y1 - p;
x = x0 + p * Math.atan2(x, c) / Math.cos(y);
} else {
y *= -1;
x /= Math.cos(y);
}
return [
x / d3_geo_radians,
y / d3_geo_radians
];
};
// 90° for Werner, 0° for Sinusoidal
bonne.parallel = function(x) {
if (!arguments.length) return y1 / d3_geo_radians;
c1 = 1 / Math.tan(y1 = x * d3_geo_radians);
return bonne;
};
bonne.origin = function(x) {
if (!arguments.length) return [x0 / d3_geo_radians, y0 / d3_geo_radians];
x0 = x[0] * d3_geo_radians;
y0 = x[1] * d3_geo_radians;
return bonne;
};
bonne.scale = function(x) {
if (!arguments.length) return scale;
scale = +x;
return bonne;
};
bonne.translate = function(x) {
if (!arguments.length) return translate;
translate = [+x[0], +x[1]];
return bonne;
};
return bonne.origin([0, 0]).parallel(45);
};
d3.geo.equirectangular = function() {
var scale = 500,
translate = [480, 250];
@ -239,7 +319,7 @@ d3.geo.mercator = function() {
function mercator(coordinates) {
var x = coordinates[0] / 360,
y = -(Math.log(Math.tan(Math.PI / 4 + coordinates[1] * d3_radians / 2)) / d3_radians) / 360;
y = -(Math.log(Math.tan(Math.PI / 4 + coordinates[1] * d3_geo_radians / 2)) / d3_geo_radians) / 360;
return [
scale * x + translate[0],
scale * Math.max(-.5, Math.min(.5, y)) + translate[1]
@ -251,7 +331,7 @@ d3.geo.mercator = function() {
y = (coordinates[1] - translate[1]) / scale;
return [
360 * x,
2 * Math.atan(Math.exp(-360 * y * d3_radians)) / d3_radians - 90
2 * Math.atan(Math.exp(-360 * y * d3_geo_radians)) / d3_geo_radians - 90
];
};
@ -269,6 +349,11 @@ d3.geo.mercator = function() {
return mercator;
};
function d3_geo_type(types, defaultValue) {
return function(object) {
return object && object.type in types ? types[object.type](object) : defaultValue;
};
}
/**
* Returns a function that, given a GeoJSON object (e.g., a feature), returns
* the corresponding SVG path. The function can be customized by overriding the
@ -285,26 +370,26 @@ d3.geo.path = function() {
if (typeof pointRadius === "function") {
pointCircle = d3_path_circle(pointRadius.apply(this, arguments));
}
return d3_geo_pathType(pathTypes, d);
return pathType(d) || null;
}
function project(coordinates) {
return projection(coordinates).join(",");
}
var pathTypes = {
var pathType = d3_geo_type({
FeatureCollection: function(f) {
FeatureCollection: function(o) {
var path = [],
features = f.features,
features = o.features,
i = -1, // features.index
n = features.length;
while (++i < n) path.push(d3_geo_pathType(pathTypes, features[i].geometry));
while (++i < n) path.push(pathType(features[i].geometry));
return path.join("");
},
Feature: function(f) {
return d3_geo_pathType(pathTypes, f.geometry);
Feature: function(o) {
return pathType(o.geometry);
},
Point: function(o) {
@ -360,10 +445,11 @@ d3.geo.path = function() {
while (++i < n) {
subcoordinates = coordinates[i];
j = -1;
m = subcoordinates.length;
path.push("M");
while (++j < m) path.push(project(subcoordinates[j]), "L");
path[path.length - 1] = "Z";
if ((m = subcoordinates.length - 1) > 0) {
path.push("M");
while (++j < m) path.push(project(subcoordinates[j]), "L");
path[path.length - 1] = "Z";
}
}
return path.join("");
},
@ -386,10 +472,11 @@ d3.geo.path = function() {
while (++j < m) {
subsubcoordinates = subcoordinates[j];
k = -1;
p = subsubcoordinates.length - 1;
path.push("M");
while (++k < p) path.push(project(subsubcoordinates[k]), "L");
path[path.length - 1] = "Z";
if ((p = subsubcoordinates.length - 1) > 0) {
path.push("M");
while (++k < p) path.push(project(subsubcoordinates[k]), "L");
path[path.length - 1] = "Z";
}
}
}
return path.join("");
@ -400,32 +487,27 @@ d3.geo.path = function() {
geometries = o.geometries,
i = -1, // geometries index
n = geometries.length;
while (++i < n) path.push(d3_geo_pathType(pathTypes, geometries[i]));
while (++i < n) path.push(pathType(geometries[i]));
return path.join("");
}
};
});
var areaTypes = {
var areaType = path.area = d3_geo_type({
FeatureCollection: function(f) {
FeatureCollection: function(o) {
var area = 0,
features = f.features,
features = o.features,
i = -1, // features.index
n = features.length;
while (++i < n) area += d3_geo_pathType(areaTypes, features[i]);
while (++i < n) area += areaType(features[i]);
return area;
},
Feature: function(f) {
return d3_geo_pathType(areaTypes, f.geometry);
Feature: function(o) {
return areaType(o.geometry);
},
Point: d3_geo_pathZero,
MultiPoint: d3_geo_pathZero,
LineString: d3_geo_pathZero,
MultiLineString: d3_geo_pathZero,
Polygon: function(o) {
return polygonArea(o.coordinates);
},
@ -444,11 +526,11 @@ d3.geo.path = function() {
geometries = o.geometries,
i = -1, // geometries index
n = geometries.length;
while (++i < n) sum += d3_geo_pathType(areaTypes, geometries[i]);
while (++i < n) sum += areaType(geometries[i]);
return sum;
}
};
}, 0);
function polygonArea(coordinates) {
var sum = area(coordinates[0]), // exterior ring
@ -476,7 +558,7 @@ d3.geo.path = function() {
return [x, y, 6 * z]; // weighted centroid
}
var centroidTypes = {
var centroidType = path.centroid = d3_geo_type({
// TODO FeatureCollection
// TODO Point
@ -485,8 +567,8 @@ d3.geo.path = function() {
// TODO MultiLineString
// TODO GeometryCollection
Feature: function(f) {
return d3_geo_pathType(centroidTypes, f.geometry);
Feature: function(o) {
return centroidType(o.geometry);
},
Polygon: function(o) {
@ -512,8 +594,7 @@ d3.geo.path = function() {
return [x / z, y / z];
}
};
});
function area(coordinates) {
return Math.abs(d3.geom.polygon(coordinates.map(projection)).area());
@ -524,14 +605,6 @@ d3.geo.path = function() {
return path;
};
path.area = function(d) {
return d3_geo_pathType(areaTypes, d);
};
path.centroid = function(d) {
return d3_geo_pathType(centroidTypes, d);
};
path.pointRadius = function(x) {
if (typeof x === "function") pointRadius = x;
else {
@ -550,14 +623,6 @@ function d3_path_circle(radius) {
+ "a" + radius + "," + radius + " 0 1,1 0," + (+2 * radius)
+ "z";
}
function d3_geo_pathZero() {
return 0;
}
function d3_geo_pathType(types, o) {
return o && o.type in types ? types[o.type](o) : "";
}
/**
* Given a GeoJSON object, returns the corresponding bounding box. The bounding
* box is represented by a two-dimensional array: [[left, bottom], [right,
@ -634,95 +699,231 @@ function d3_geo_boundsPolygon(o, f) {
f.apply(null, a[i]);
}
}
// From http://williams.best.vwh.net/avform.htm#Intermediate
d3.geo.greatCircle = function() {
var source = d3_geo_greatCircleSource,
target = d3_geo_greatCircleTarget,
n = 100,
radius = 6371; // Mean radius of Earth, in km.
// TODO: breakAtDateLine?
// TODO breakAtDateLine?
function greatCircle(d, i) {
var from = source.call(this, d, i),
to = target.call(this, d, i),
x0 = from[0] * d3_radians,
y0 = from[1] * d3_radians,
x1 = to[0] * d3_radians,
y1 = to[1] * d3_radians,
cx0 = Math.cos(x0), sx0 = Math.sin(x0),
cy0 = Math.cos(y0), sy0 = Math.sin(y0),
cx1 = Math.cos(x1), sx1 = Math.sin(x1),
cy1 = Math.cos(y1), sy1 = Math.sin(y1),
d = Math.acos(sy0 * sy1 + cy0 * cy1 * Math.cos(x1 - x0)),
sd = Math.sin(d),
f = d / (n - 1),
e = -f,
path = [],
i = -1;
d3.geo.circle = function() {
var origin = [0, 0],
degrees = 90 - 1e-2,
radians = degrees * d3_geo_radians,
arc = d3.geo.greatArc().target(Object);
while (++i < n) {
e += f;
var A = Math.sin(d - e) / sd,
B = Math.sin(e) / sd,
x = A * cy0 * cx0 + B * cy1 * cx1,
y = A * cy0 * sx0 + B * cy1 * sx1,
z = A * sy0 + B * sy1;
path[i] = [
Math.atan2(y, x) / d3_radians,
Math.atan2(z, Math.sqrt(x * x + y * y)) / d3_radians
];
}
return path;
function circle() {
// TODO render a circle as a Polygon
}
greatCircle.source = function(x) {
function visible(point) {
return arc.distance(point) < radians;
}
circle.clip = function(d) {
arc.source(typeof origin === "function" ? origin.apply(this, arguments) : origin);
return clipType(d);
};
var clipType = d3_geo_type({
FeatureCollection: function(o) {
var features = o.features.map(clipType).filter(Object);
return features && (o = Object.create(o), o.features = features, o);
},
Feature: function(o) {
var geometry = clipType(o.geometry);
return geometry && (o = Object.create(o), o.geometry = geometry, o);
},
Point: function(o) {
return visible(o.coordinates) && o;
},
MultiPoint: function(o) {
var coordinates = o.coordinates.filter(visible);
return coordinates.length && {
type: o.type,
coordinates: coordinates
};
},
LineString: function(o) {
var coordinates = clip(o.coordinates);
return coordinates.length && (o = Object.create(o), o.coordinates = coordinates, o);
},
MultiLineString: function(o) {
var coordinates = o.coordinates.map(clip).filter(function(d) { return d.length; });
return coordinates.length && (o = Object.create(o), o.coordinates = coordinates, o);
},
Polygon: function(o) {
var coordinates = o.coordinates.map(clip);
return coordinates[0].length && (o = Object.create(o), o.coordinates = coordinates, o);
},
MultiPolygon: function(o) {
var coordinates = o.coordinates.map(function(d) { return d.map(clip); }).filter(function(d) { return d[0].length; });
return coordinates.length && (o = Object.create(o), o.coordinates = coordinates, o);
},
GeometryCollection: function(o) {
var geometries = o.geometries.map(clipType).filter(Object);
return geometries.length && (o = Object.create(o), o.geometries = geometries, o);
}
});
function clip(coordinates) {
var i = -1,
n = coordinates.length,
clipped = [],
p0,
p1,
p2,
d0,
d1;
while (++i < n) {
d1 = arc.distance(p2 = coordinates[i]);
if (d1 < radians) {
if (p1) clipped.push(d3_geo_greatArcInterpolate(p1, p2)((d0 - radians) / (d0 - d1)));
clipped.push(p2);
p0 = p1 = null;
} else {
p1 = p2;
if (!p0 && clipped.length) {
clipped.push(d3_geo_greatArcInterpolate(clipped[clipped.length - 1], p1)((radians - d0) / (d1 - d0)));
p0 = p1;
}
}
d0 = d1;
}
if (p1 && clipped.length) {
d1 = arc.distance(p2 = clipped[0]);
clipped.push(d3_geo_greatArcInterpolate(p1, p2)((d0 - radians) / (d0 - d1)));
}
return resample(clipped);
}
// Resample coordinates, creating great arcs between each.
function resample(coordinates) {
var i = 0,
n = coordinates.length,
j,
m,
resampled = n ? [coordinates[0]] : coordinates,
resamples,
origin = arc.source();
while (++i < n) {
resamples = arc.source(coordinates[i - 1])(coordinates[i]).coordinates;
for (j = 0, m = resamples.length; ++j < m;) resampled.push(resamples[j]);
}
arc.source(origin);
return resampled;
}
circle.origin = function(x) {
if (!arguments.length) return origin;
origin = x;
return circle;
};
circle.angle = function(x) {
if (!arguments.length) return degrees;
radians = (degrees = +x) * d3_geo_radians;
return circle;
};
// Precision is specified in degrees.
circle.precision = function(x) {
if (!arguments.length) return arc.precision();
arc.precision(x);
return circle;
};
return circle;
}
d3.geo.greatArc = function() {
var source = d3_geo_greatArcSource,
target = d3_geo_greatArcTarget,
precision = 6 * d3_geo_radians;
function greatArc() {
var a = typeof source === "function" ? source.apply(this, arguments) : source,
b = typeof target === "function" ? target.apply(this, arguments) : target,
i = d3_geo_greatArcInterpolate(a, b),
dt = precision / i.d,
t = 0,
coordinates = [a];
while ((t += dt) < 1) coordinates.push(i(t));
coordinates.push(b);
return {
type: "LineString",
coordinates: coordinates
};
}
// Length returned in radians; multiply by radius for distance.
greatArc.distance = function() {
var a = typeof source === "function" ? source.apply(this, arguments) : source,
b = typeof target === "function" ? target.apply(this, arguments) : target;
return d3_geo_greatArcInterpolate(a, b).d;
};
greatArc.source = function(x) {
if (!arguments.length) return source;
source = x;
return greatCircle;
return greatArc;
};
greatCircle.target = function(x) {
greatArc.target = function(x) {
if (!arguments.length) return target;
target = x;
return greatCircle;
return greatArc;
};
greatCircle.n = function(x) {
if (!arguments.length) return n;
n = +x;
return greatCircle;
// Precision is specified in degrees.
greatArc.precision = function(x) {
if (!arguments.length) return precision / d3_geo_radians;
precision = x * d3_geo_radians;
return greatArc;
};
greatCircle.radius = function(x) {
if (!arguments.length) return radius;
radius = +x;
return greatCircle;
};
// Haversine formula for great-circle distance.
greatCircle.distance = function(d, i) {
var from = source.call(this, d, i),
to = target.call(this, d, i),
x0 = from[0] * d3_radians,
y0 = from[1] * d3_radians,
x1 = to[0] * d3_radians,
y1 = to[1] * d3_radians,
sy = Math.sin((y1 - y0) / 2),
sx = Math.sin((x1 - x0) / 2),
a = sy * sy + Math.cos(y0) * Math.cos(y1) * sx * sx;
return radius * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
};
return greatCircle;
return greatArc;
};
function d3_geo_greatCircleSource(d) {
function d3_geo_greatArcSource(d) {
return d.source;
}
function d3_geo_greatCircleTarget(d) {
function d3_geo_greatArcTarget(d) {
return d.target;
}
function d3_geo_greatArcInterpolate(a, b) {
var x0 = a[0] * d3_geo_radians, cx0 = Math.cos(x0), sx0 = Math.sin(x0),
y0 = a[1] * d3_geo_radians, cy0 = Math.cos(y0), sy0 = Math.sin(y0),
x1 = b[0] * d3_geo_radians, cx1 = Math.cos(x1), sx1 = Math.sin(x1),
y1 = b[1] * d3_geo_radians, cy1 = Math.cos(y1), sy1 = Math.sin(y1),
d = interpolate.d = Math.acos(Math.max(-1, Math.min(1, sy0 * sy1 + cy0 * cy1 * Math.cos(x1 - x0)))),
sd = Math.sin(d);
// From http://williams.best.vwh.net/avform.htm#Intermediate
function interpolate(t) {
var A = Math.sin(d - (t *= d)) / sd,
B = Math.sin(t) / sd,
x = A * cy0 * cx0 + B * cy1 * cx1,
y = A * cy0 * sx0 + B * cy1 * sx1,
z = A * sy0 + B * sy1;
return [
Math.atan2(y, x) / d3_geo_radians,
Math.atan2(z, Math.sqrt(x * x + y * y)) / d3_geo_radians
];
}
return interpolate;
}
d3.geo.greatCircle = d3.geo.circle;
})();

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

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

41
d3.js поставляемый
Просмотреть файл

@ -10,7 +10,7 @@ try {
d3_style_setProperty.call(this, name, value + "", priority);
};
}
d3 = {version: "2.2.1"}; // semver
d3 = {version: "2.3.0"}; // semver
var d3_array = d3_arraySlice; // conversion for NodeLists
function d3_arrayCopy(pseudoarray) {
@ -530,7 +530,7 @@ var d3_format_types = {
e: function(x, p) { return x.toExponential(p); },
f: function(x, p) { return x.toFixed(p); },
r: function(x, p) {
var n = 1 + Math.floor(1e-15 + Math.log(x) / Math.LN10);
var n = x ? 1 + Math.floor(1e-15 + Math.log(x) / Math.LN10) : 1;
return d3.round(x, p - n).toFixed(Math.max(0, Math.min(20, p - n)));
}
};
@ -2294,8 +2294,8 @@ function d3_scale_log(linear, log) {
if (extent.every(isFinite)) {
var i = Math.floor(extent[0]),
j = Math.ceil(extent[1]),
u = pow(extent[0]),
v = pow(extent[1]);
u = Math.round(pow(extent[0])),
v = Math.round(pow(extent[1]));
if (log === d3_scale_logn) {
ticks.push(pow(i));
for (; i++ < j;) for (var k = 9; k > 0; k--) ticks.push(pow(i) * k);
@ -2310,8 +2310,15 @@ function d3_scale_log(linear, log) {
return ticks;
};
scale.tickFormat = function() {
return d3_scale_logTickFormat;
scale.tickFormat = function(n, format) {
if (arguments.length < 2) format = d3_scale_logFormat;
if (arguments.length < 1) return format;
var k = n / scale.ticks().length,
f = log === d3_scale_logn ? (e = -1e-15, Math.floor) : (e = 1e-15, Math.ceil),
e;
return function(d) {
return d / pow(f(log(d) + e)) < k ? format(d) : "";
};
};
scale.copy = function() {
@ -2321,6 +2328,8 @@ function d3_scale_log(linear, log) {
return d3_scale_linearRebind(scale, linear);
};
var d3_scale_logFormat = d3.format("e");
function d3_scale_logp(x) {
return Math.log(x) / Math.LN10;
}
@ -2336,10 +2345,6 @@ d3_scale_logp.pow = function(x) {
d3_scale_logn.pow = function(x) {
return -Math.pow(10, -x);
};
function d3_scale_logTickFormat(d) {
return d.toPrecision(1);
}
d3.scale.pow = function() {
return d3_scale_pow(d3.scale.linear(), 1);
};
@ -3727,6 +3732,7 @@ d3.behavior.drag = function() {
// snapshot the local context for subsequent dispatch
function start() {
d3_behavior_dragEvent = event;
d3_behavior_dragEventTarget = d3.event.target;
d3_behavior_dragOffset = d3_behavior_dragPoint((d3_behavior_dragTarget = this).parentNode);
d3_behavior_dragMoved = 0;
d3_behavior_dragArguments = arguments;
@ -3746,6 +3752,7 @@ d3.behavior.drag = function() {
};
var d3_behavior_dragEvent,
d3_behavior_dragEventTarget,
d3_behavior_dragTarget,
d3_behavior_dragArguments,
d3_behavior_dragOffset,
@ -3797,16 +3804,17 @@ function d3_behavior_dragUp() {
// If the node was moved, prevent the mouseup from propagating.
// Also prevent the subsequent click from propagating (e.g., for anchors).
if (d3_behavior_dragMoved) {
if (d3_behavior_dragMoved && d3_behavior_dragEventTarget === d3.event.target) {
d3_behavior_dragStopClick = true;
d3_behavior_dragCancel();
}
}
function d3_behavior_dragClick() {
if (d3_behavior_dragStopClick) {
if (d3_behavior_dragStopClick && d3_behavior_dragEventTarget === d3.event.target) {
d3_behavior_dragCancel();
d3_behavior_dragStopClick = false;
d3_behavior_dragEventTarget = null;
}
}
@ -3840,6 +3848,7 @@ d3.behavior.zoom = function() {
function start() {
d3_behavior_zoomXyz = xyz;
d3_behavior_zoomDispatch = event.zoom.dispatch;
d3_behavior_zoomEventTarget = d3.event.target;
d3_behavior_zoomTarget = this;
d3_behavior_zoomArguments = arguments;
}
@ -3892,6 +3901,7 @@ var d3_behavior_zoomDiv,
d3_behavior_zoomLast = 0,
d3_behavior_zoomXyz,
d3_behavior_zoomDispatch,
d3_behavior_zoomEventTarget,
d3_behavior_zoomTarget,
d3_behavior_zoomArguments,
d3_behavior_zoomMoved,
@ -3982,17 +3992,20 @@ function d3_behavior_zoomMousemove() {
function d3_behavior_zoomMouseup() {
if (d3_behavior_zoomPanning) {
if (d3_behavior_zoomMoved) d3_behavior_zoomStopClick = true;
if (d3_behavior_zoomMoved && d3_behavior_zoomEventTarget === d3.event.target) {
d3_behavior_zoomStopClick = true;
}
d3_behavior_zoomMousemove();
d3_behavior_zoomPanning = null;
}
}
function d3_behavior_zoomClick() {
if (d3_behavior_zoomStopClick) {
if (d3_behavior_zoomStopClick && d3_behavior_zoomEventTarget === d3.event.target) {
d3.event.stopPropagation();
d3.event.preventDefault();
d3_behavior_zoomStopClick = false;
d3_behavior_zoomEventTarget = null;
}
}

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

@ -245,6 +245,7 @@ d3.layout.force = function() {
node.py -= dy * k;
}
}
return !quad.charge;
};
}

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

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

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

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

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

@ -2,7 +2,7 @@
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
<title>Polar Stereographic Projection</title>
<title>Azimuthal Projection</title>
<script type="text/javascript" src="../../d3.js"></script>
<script type="text/javascript" src="../../d3.geo.js"></script>
<script type="text/javascript" src="../../lib/jquery/jquery.min.js"></script>
@ -12,6 +12,13 @@
<body>
<h3>Azimuthal Projection</h3>
<script type="text/javascript" src="azimuthal.js"></script><p>
<select id="mode">
<option value="stereographic">stereographic</option>
<option value="orthographic">orthographic</option>
<option value="equidistant">equidistant</option>
<option value="gnomonic">gnomonic</option>
<option value="equalarea">equalarea</option>
</select>
<div id="lon">origin.longitude: <span>0</span></div>
<div id="lat">origin.latitude: <span>0</span></div><p>
<div id="scale">scale: <span>240</span></div><p>
@ -28,6 +35,7 @@ $("#lon").slider({
var origin = xy.origin();
origin[0] = ui.value;
xy.origin(origin);
circle.origin(origin);
refresh();
}
});
@ -41,6 +49,7 @@ $("#lat").slider({
var origin = xy.origin();
origin[1] = ui.value;
xy.origin(origin);
circle.origin(origin);
refresh();
}
});
@ -79,6 +88,12 @@ $("#translate-y").slider({
}
});
$("#mode").change(function() {
var mode = $(this).val();
xy.mode(mode);
refresh(500);
});
</script>
</body>
</html>

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

@ -1,4 +1,5 @@
var xy = d3.geo.azimuthal().scale(240).mode("stereographic"),
circle = d3.geo.greatCircle(),
path = d3.geo.path().projection(xy),
svg = d3.select("body").append("svg:svg");
@ -6,14 +7,15 @@ d3.json("../data/world-countries.json", function(collection) {
svg.selectAll("path")
.data(collection.features)
.enter().append("svg:path")
.attr("d", path)
.attr("d", function(d) { return path(circle.clip(d)); })
.append("svg:title")
.text(function(d) { return d.properties.name; });
});
function refresh() {
svg.selectAll("path")
.attr("d", path);
function refresh(duration) {
var p = svg.selectAll("path");
if (duration) p = p.transition().duration(duration);
p.attr("d", function(d) { return path(circle.clip(d)); });
d3.select("#lon span")
.text(xy.origin()[0]);
d3.select("#lat span")

159
examples/bonne/bonne.html Normal file
Просмотреть файл

@ -0,0 +1,159 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
<title>Bonne Projection</title>
<script type="text/javascript" src="../../d3.js"></script>
<script type="text/javascript" src="../../d3.geo.js"></script>
<script type="text/javascript" src="../../lib/jquery/jquery.min.js"></script>
<script type="text/javascript" src="../../lib/jquery-ui/jquery-ui.min.js"></script>
<style type="text/css">
@import url("../../lib/jquery-ui/jquery-ui.css");
body, .ui-widget {
font: 14px Helvetica Neue;
}
svg {
width: 960px;
height: 500px;
border: solid 1px #ccc;
background: #eee;
}
line {
stroke: brown;
stroke-dasharray: 4,2;
}
path {
fill: #ccc;
stroke: #fff;
}
div {
width: 960px;
}
</style>
</head>
<body>
<h3>Bonne Projection</h3>
<script type="text/javascript">
// Our projection.
var xy = d3.geo.bonne(),
path = d3.geo.path().projection(xy);
var countries = d3.select("body").append("svg:svg")
.attr("id", "countries");
d3.json("../data/world-countries.json", function(collection) {
countries.selectAll("path")
.data(collection.features)
.enter().append("svg:path")
.attr("d", path)
.append("svg:title")
.text(function(d) { return d.properties.name; });
});
function refresh() {
countries.selectAll("path")
.attr("d", path);
d3.select("#lon span")
.text(xy.origin()[0]);
d3.select("#lat span")
.text(xy.origin()[1]);
d3.select("#parallel span")
.text(xy.parallel());
d3.select("#scale span")
.text(xy.scale());
d3.select("#translate-x span")
.text(xy.translate()[0]);
d3.select("#translate-y span")
.text(xy.translate()[1]);
}
</script><p>
<div id="lon">origin.longitude: <span>0</span></div>
<div id="lat">origin.latitude: <span>0</span></div><p>
<div id="parallel">parallel: <span>45</span></div><p>
<div id="scale">scale: <span>200</span></div><p>
<div id="translate-x">translate.x: <span>480</span></div>
<div id="translate-y">translate.y: <span>250</span></div>
<script type="text/javascript">
$("#lon").slider({
min: -180,
max: 180,
step: 1e-1,
value: 0,
slide: function(event, ui) {
var origin = xy.origin();
origin[0] = ui.value;
xy.origin(origin);
refresh();
}
});
$("#lat").slider({
min: -90,
max: 90,
step: 1e-1,
value: 0,
slide: function(event, ui) {
var origin = xy.origin();
origin[1] = ui.value;
xy.origin(origin);
refresh();
}
});
$("#parallel").slider({
min: 0,
max: 90,
value: 45,
slide: function(event, ui) {
xy.parallel(ui.value);
refresh();
}
});
$("#scale").slider({
min: 0,
max: 800,
value: 200,
slide: function(event, ui) {
xy.scale(ui.value);
refresh();
}
});
$("#translate-x").slider({
min: -2000,
max: 2000,
value: 480,
slide: function(event, ui) {
var translate = xy.translate();
translate[0] = ui.value;
xy.translate(translate);
refresh();
}
});
$("#translate-y").slider({
min: -2000,
max: 2000,
value: 250,
slide: function(event, ui) {
var translate = xy.translate();
translate[1] = ui.value;
xy.translate(translate);
refresh();
}
});
</script>
</body>
</html>

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

@ -1,5 +0,0 @@
These are derived from the cartographic boundary files from the 2000 U.S. Census:
http://www.census.gov/geo/www/cob/bdy_files.html
Then, MapShaper was used to simplify the geometry, and ogr2ogr to convert the shapefiles to GeoJSON. Some additional work was done to preserve the FIPS codes, which are dropped from the shapefiles by MapShaper.

13
examples/data/README.md Normal file
Просмотреть файл

@ -0,0 +1,13 @@
## World Boundaries
These are derived from the public domain [Natural Earth](http://www.naturalearthdata.com/downloads/) cultural vector files, 110m resolution. Then, ogr2ogr was used to convert to GeoJSON. Lastly, the data was cleaned up slightly, removing extra properties and a degenerate edge from Antarctica.
collection.features.forEach(function(d, i) {
d.id = d.properties.ISO_A3;
d.properties = {name: d.properties.SOVEREIGNT};
});
## United States Boundaries
These are derived from the cartographic boundary files from the 2000 [U.S. Census](http://www.census.gov/geo/www/cob/bdy_files.html
). Then, MapShaper was used to simplify the geometry, and ogr2ogr to convert the shapefiles to GeoJSON. Some additional work was done to preserve the FIPS codes, which are dropped from the shapefiles by MapShaper.

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

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

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

@ -197,12 +197,12 @@ function init() {
hullg.selectAll("path.hull").remove();
hull = hullg.selectAll("path.hull")
.data(convexHulls(net.nodes, getGroup, off))
.data(convexHulls(net.nodes, getGroup, off))
.enter().append("svg:path")
.attr("class", "hull")
.attr("d", drawCluster)
.style("fill", function(d) { return fill(d.group); })
.on("dblclick", function(d) { expand[d.group] = false; init(); });
.attr("class", "hull")
.attr("d", drawCluster)
.style("fill", function(d) { return fill(d.group); })
.on("dblclick", function(d) { expand[d.group] = false; init(); });
link = linkg.selectAll("line.link").data(net.links, linkid);
link.exit().remove();
@ -213,7 +213,6 @@ function init() {
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; })
.style("stroke-width", function(d) { return d.size || 1; });
link = linkg.selectAll("line.link");
node = nodeg.selectAll("circle.node").data(net.nodes, nodeid);
node.exit().remove();
@ -227,8 +226,7 @@ function init() {
if (d.size) { expand[d.group] = true; init(); }
});
node = nodeg.selectAll("circle.node")
.call(force.drag);
node.call(force.drag);
force.on("tick", function() {
if (!hull.empty()) {

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

@ -0,0 +1,78 @@
<!DOCTYPE html>
<html>
<head>
<title>Great Arc</title>
<script type="text/javascript" src="../../d3.js"></script>
<script type="text/javascript" src="../../d3.geo.js"></script>
<style type="text/css">
#states path {
fill: #ddd;
stroke: #fff;
}
#arcs path {
fill: none;
stroke: #000;
stroke-width: .5px;
stroke-opacity: .2;
}
</style>
</head>
<body>
<script type="text/javascript">
var w = 960,
h = 500;
var projection = d3.geo.azimuthal()
.origin([-115, 50])
.scale(500);
var path = d3.geo.path()
.projection(projection);
var arc = d3.geo.greatArc();
var svg = d3.select("body").append("svg:svg")
.attr("width", w)
.attr("height", h);
var states = svg.append("svg:g")
.attr("id", "states");
var arcs = svg.append("svg:g")
.attr("id", "arcs");
d3.json("../data/us-states.json", function(collection) {
states.selectAll("path")
.data(collection.features)
.enter().append("svg:path")
.attr("d", path);
});
d3.json("../data/us-state-centroids.json", function(collection) {
var links = [];
// Create a link between each state centroid.
collection.features.forEach(function(a) {
collection.features.forEach(function(b) {
if (a !== b) {
links.push({
source: a.geometry.coordinates,
target: b.geometry.coordinates
});
}
});
});
arcs.selectAll("path")
.data(links)
.enter().append("svg:path")
.attr("d", function(d) { return path(arc(d)); });
});
</script>
</body>
</html>

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

@ -1,6 +1,6 @@
{
"name": "d3",
"version": "2.2.1",
"version": "2.3.0",
"description": "A small, free JavaScript library for manipulating documents based on data.",
"keywords": [
"dom",

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

@ -17,6 +17,7 @@ d3.behavior.drag = function() {
// snapshot the local context for subsequent dispatch
function start() {
d3_behavior_dragEvent = event;
d3_behavior_dragEventTarget = d3.event.target;
d3_behavior_dragOffset = d3_behavior_dragPoint((d3_behavior_dragTarget = this).parentNode);
d3_behavior_dragMoved = 0;
d3_behavior_dragArguments = arguments;
@ -36,6 +37,7 @@ d3.behavior.drag = function() {
};
var d3_behavior_dragEvent,
d3_behavior_dragEventTarget,
d3_behavior_dragTarget,
d3_behavior_dragArguments,
d3_behavior_dragOffset,
@ -87,16 +89,17 @@ function d3_behavior_dragUp() {
// If the node was moved, prevent the mouseup from propagating.
// Also prevent the subsequent click from propagating (e.g., for anchors).
if (d3_behavior_dragMoved) {
if (d3_behavior_dragMoved && d3_behavior_dragEventTarget === d3.event.target) {
d3_behavior_dragStopClick = true;
d3_behavior_dragCancel();
}
}
function d3_behavior_dragClick() {
if (d3_behavior_dragStopClick) {
if (d3_behavior_dragStopClick && d3_behavior_dragEventTarget === d3.event.target) {
d3_behavior_dragCancel();
d3_behavior_dragStopClick = false;
d3_behavior_dragEventTarget = null;
}
}

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

@ -24,6 +24,7 @@ d3.behavior.zoom = function() {
function start() {
d3_behavior_zoomXyz = xyz;
d3_behavior_zoomDispatch = event.zoom.dispatch;
d3_behavior_zoomEventTarget = d3.event.target;
d3_behavior_zoomTarget = this;
d3_behavior_zoomArguments = arguments;
}
@ -76,6 +77,7 @@ var d3_behavior_zoomDiv,
d3_behavior_zoomLast = 0,
d3_behavior_zoomXyz,
d3_behavior_zoomDispatch,
d3_behavior_zoomEventTarget,
d3_behavior_zoomTarget,
d3_behavior_zoomArguments,
d3_behavior_zoomMoved,
@ -166,17 +168,20 @@ function d3_behavior_zoomMousemove() {
function d3_behavior_zoomMouseup() {
if (d3_behavior_zoomPanning) {
if (d3_behavior_zoomMoved) d3_behavior_zoomStopClick = true;
if (d3_behavior_zoomMoved && d3_behavior_zoomEventTarget === d3.event.target) {
d3_behavior_zoomStopClick = true;
}
d3_behavior_zoomMousemove();
d3_behavior_zoomPanning = null;
}
}
function d3_behavior_zoomClick() {
if (d3_behavior_zoomStopClick) {
if (d3_behavior_zoomStopClick && d3_behavior_zoomEventTarget === d3.event.target) {
d3.event.stopPropagation();
d3.event.preventDefault();
d3_behavior_zoomStopClick = false;
d3_behavior_zoomEventTarget = null;
}
}

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

@ -1 +1 @@
d3 = {version: "2.2.1"}; // semver
d3 = {version: "2.3.0"}; // semver

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

@ -66,7 +66,7 @@ var d3_format_types = {
e: function(x, p) { return x.toExponential(p); },
f: function(x, p) { return x.toFixed(p); },
r: function(x, p) {
var n = 1 + Math.floor(1e-15 + Math.log(x) / Math.LN10);
var n = x ? 1 + Math.floor(1e-15 + Math.log(x) / Math.LN10) : 1;
return d3.round(x, p - n).toFixed(Math.max(0, Math.min(20, p - n)));
}
};

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

@ -7,14 +7,14 @@ d3.geo.albers = function() {
parallels = [29.5, 45.5],
scale = 1000,
translate = [480, 250],
lng0, // d3_radians * origin[0]
lng0, // d3_geo_radians * origin[0]
n,
C,
p0;
function albers(coordinates) {
var t = n * (d3_radians * coordinates[0] - lng0),
p = Math.sqrt(C - 2 * n * Math.sin(d3_radians * coordinates[1])) / n;
var t = n * (d3_geo_radians * coordinates[0] - lng0),
p = Math.sqrt(C - 2 * n * Math.sin(d3_geo_radians * coordinates[1])) / n;
return [
scale * p * Math.sin(t) + translate[0],
scale * (p * Math.cos(t) - p0) + translate[1]
@ -28,18 +28,18 @@ d3.geo.albers = function() {
t = Math.atan2(x, p0y),
p = Math.sqrt(x * x + p0y * p0y);
return [
(lng0 + t / n) / d3_radians,
Math.asin((C - p * p * n * n) / (2 * n)) / d3_radians
(lng0 + t / n) / d3_geo_radians,
Math.asin((C - p * p * n * n) / (2 * n)) / d3_geo_radians
];
};
function reload() {
var phi1 = d3_radians * parallels[0],
phi2 = d3_radians * parallels[1],
lat0 = d3_radians * origin[1],
var phi1 = d3_geo_radians * parallels[0],
phi2 = d3_geo_radians * parallels[1],
lat0 = d3_geo_radians * origin[1],
s = Math.sin(phi1),
c = Math.cos(phi1);
lng0 = d3_radians * origin[0];
lng0 = d3_geo_radians * origin[0];
n = .5 * (s + Math.sin(phi2));
C = c * c + 2 * n * s;
p0 = Math.sqrt(C - 2 * n * Math.sin(lat0)) / n;
@ -124,5 +124,3 @@ d3.geo.albersUsa = function() {
return albersUsa.scale(lower48.scale());
};
var d3_radians = Math.PI / 180;

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

@ -1,6 +1,6 @@
// TODO clip input coordinates on opposite hemisphere
d3.geo.azimuthal = function() {
var mode = "orthographic", // or stereographic
var mode = "orthographic", // or stereographic, gnomonic, equidistant or equalarea
origin,
scale = 200,
translate = [480, 250],
@ -10,13 +10,19 @@ d3.geo.azimuthal = function() {
sy0;
function azimuthal(coordinates) {
var x1 = coordinates[0] * d3_radians - x0,
y1 = coordinates[1] * d3_radians,
var x1 = coordinates[0] * d3_geo_radians - x0,
y1 = coordinates[1] * d3_geo_radians,
cx1 = Math.cos(x1),
sx1 = Math.sin(x1),
cy1 = Math.cos(y1),
sy1 = Math.sin(y1),
k = mode === "stereographic" ? 1 / (1 + sy0 * sy1 + cy0 * cy1 * cx1) : 1,
cc = mode !== "orthographic" ? sy0 * sy1 + cy0 * cy1 * cx1 : null,
c,
k = mode === "stereographic" ? 1 / (1 + cc)
: mode === "gnomonic" ? 1 / cc
: mode === "equidistant" ? (c = Math.acos(cc), c ? c / Math.sin(c) : 0)
: mode === "equalarea" ? Math.sqrt(2 / (1 + cc))
: 1,
x = k * cy1 * sx1,
y = k * (sy0 * cy1 * cx1 - cy0 * sy1);
return [
@ -29,12 +35,16 @@ d3.geo.azimuthal = function() {
var x = (coordinates[0] - translate[0]) / scale,
y = (coordinates[1] - translate[1]) / scale,
p = Math.sqrt(x * x + y * y),
c = mode === "stereographic" ? 2 * Math.atan(p) : Math.asin(p),
c = mode === "stereographic" ? 2 * Math.atan(p)
: mode === "gnomonic" ? Math.atan(p)
: mode === "equidistant" ? p
: mode === "equalarea" ? 2 * Math.asin(.5 * p)
: Math.asin(p),
sc = Math.sin(c),
cc = Math.cos(c);
return [
(x0 + Math.atan2(x * sc, p * cy0 * cc + y * sy0 * sc)) / d3_radians,
Math.asin(cc * sy0 - (y * sc * cy0) / p) / d3_radians
(x0 + Math.atan2(x * sc, p * cy0 * cc + y * sy0 * sc)) / d3_geo_radians,
Math.asin(cc * sy0 - (p ? (y * sc * cy0) / p : 0)) / d3_geo_radians
];
};
@ -47,8 +57,8 @@ d3.geo.azimuthal = function() {
azimuthal.origin = function(x) {
if (!arguments.length) return origin;
origin = x;
x0 = origin[0] * d3_radians;
y0 = origin[1] * d3_radians;
x0 = origin[0] * d3_geo_radians;
y0 = origin[1] * d3_geo_radians;
cy0 = Math.cos(y0);
sy0 = Math.sin(y0);
return azimuthal;

70
src/geo/bonne.js Normal file
Просмотреть файл

@ -0,0 +1,70 @@
d3.geo.bonne = function() {
var scale = 200,
translate = [480, 250],
x0, // origin longitude in radians
y0, // origin latitude in radians
y1, // parallel latitude in radians
c1; // cot(y1)
function bonne(coordinates) {
var x = coordinates[0] * d3_geo_radians - x0,
y = coordinates[1] * d3_geo_radians - y0;
if (y1) {
var p = c1 + y1 - y, E = x * Math.cos(y) / p;
x = p * Math.sin(E);
y = p * Math.cos(E) - c1;
} else {
x *= Math.cos(y);
y *= -1;
}
return [
scale * x + translate[0],
scale * y + translate[1]
];
}
bonne.invert = function(coordinates) {
var x = (coordinates[0] - translate[0]) / scale,
y = (coordinates[1] - translate[1]) / scale;
if (y1) {
var c = c1 + y, p = Math.sqrt(x * x + c * c);
y = c1 + y1 - p;
x = x0 + p * Math.atan2(x, c) / Math.cos(y);
} else {
y *= -1;
x /= Math.cos(y);
}
return [
x / d3_geo_radians,
y / d3_geo_radians
];
};
// 90° for Werner, 0° for Sinusoidal
bonne.parallel = function(x) {
if (!arguments.length) return y1 / d3_geo_radians;
c1 = 1 / Math.tan(y1 = x * d3_geo_radians);
return bonne;
};
bonne.origin = function(x) {
if (!arguments.length) return [x0 / d3_geo_radians, y0 / d3_geo_radians];
x0 = x[0] * d3_geo_radians;
y0 = x[1] * d3_geo_radians;
return bonne;
};
bonne.scale = function(x) {
if (!arguments.length) return scale;
scale = +x;
return bonne;
};
bonne.translate = function(x) {
if (!arguments.length) return translate;
translate = [+x[0], +x[1]];
return bonne;
};
return bonne.origin([0, 0]).parallel(45);
};

146
src/geo/circle.js Normal file
Просмотреть файл

@ -0,0 +1,146 @@
// TODO breakAtDateLine?
d3.geo.circle = function() {
var origin = [0, 0],
degrees = 90 - 1e-2,
radians = degrees * d3_geo_radians,
arc = d3.geo.greatArc().target(Object);
function circle() {
// TODO render a circle as a Polygon
}
function visible(point) {
return arc.distance(point) < radians;
}
circle.clip = function(d) {
arc.source(typeof origin === "function" ? origin.apply(this, arguments) : origin);
return clipType(d);
};
var clipType = d3_geo_type({
FeatureCollection: function(o) {
var features = o.features.map(clipType).filter(Object);
return features && (o = Object.create(o), o.features = features, o);
},
Feature: function(o) {
var geometry = clipType(o.geometry);
return geometry && (o = Object.create(o), o.geometry = geometry, o);
},
Point: function(o) {
return visible(o.coordinates) && o;
},
MultiPoint: function(o) {
var coordinates = o.coordinates.filter(visible);
return coordinates.length && {
type: o.type,
coordinates: coordinates
};
},
LineString: function(o) {
var coordinates = clip(o.coordinates);
return coordinates.length && (o = Object.create(o), o.coordinates = coordinates, o);
},
MultiLineString: function(o) {
var coordinates = o.coordinates.map(clip).filter(function(d) { return d.length; });
return coordinates.length && (o = Object.create(o), o.coordinates = coordinates, o);
},
Polygon: function(o) {
var coordinates = o.coordinates.map(clip);
return coordinates[0].length && (o = Object.create(o), o.coordinates = coordinates, o);
},
MultiPolygon: function(o) {
var coordinates = o.coordinates.map(function(d) { return d.map(clip); }).filter(function(d) { return d[0].length; });
return coordinates.length && (o = Object.create(o), o.coordinates = coordinates, o);
},
GeometryCollection: function(o) {
var geometries = o.geometries.map(clipType).filter(Object);
return geometries.length && (o = Object.create(o), o.geometries = geometries, o);
}
});
function clip(coordinates) {
var i = -1,
n = coordinates.length,
clipped = [],
p0,
p1,
p2,
d0,
d1;
while (++i < n) {
d1 = arc.distance(p2 = coordinates[i]);
if (d1 < radians) {
if (p1) clipped.push(d3_geo_greatArcInterpolate(p1, p2)((d0 - radians) / (d0 - d1)));
clipped.push(p2);
p0 = p1 = null;
} else {
p1 = p2;
if (!p0 && clipped.length) {
clipped.push(d3_geo_greatArcInterpolate(clipped[clipped.length - 1], p1)((radians - d0) / (d1 - d0)));
p0 = p1;
}
}
d0 = d1;
}
if (p1 && clipped.length) {
d1 = arc.distance(p2 = clipped[0]);
clipped.push(d3_geo_greatArcInterpolate(p1, p2)((d0 - radians) / (d0 - d1)));
}
return resample(clipped);
}
// Resample coordinates, creating great arcs between each.
function resample(coordinates) {
var i = 0,
n = coordinates.length,
j,
m,
resampled = n ? [coordinates[0]] : coordinates,
resamples,
origin = arc.source();
while (++i < n) {
resamples = arc.source(coordinates[i - 1])(coordinates[i]).coordinates;
for (j = 0, m = resamples.length; ++j < m;) resampled.push(resamples[j]);
}
arc.source(origin);
return resampled;
}
circle.origin = function(x) {
if (!arguments.length) return origin;
origin = x;
return circle;
};
circle.angle = function(x) {
if (!arguments.length) return degrees;
radians = (degrees = +x) * d3_geo_radians;
return circle;
};
// Precision is specified in degrees.
circle.precision = function(x) {
if (!arguments.length) return arc.precision();
arc.precision(x);
return circle;
};
return circle;
}

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

@ -1 +1,3 @@
d3.geo = {};
var d3_geo_radians = Math.PI / 180;

80
src/geo/greatArc.js Normal file
Просмотреть файл

@ -0,0 +1,80 @@
d3.geo.greatArc = function() {
var source = d3_geo_greatArcSource,
target = d3_geo_greatArcTarget,
precision = 6 * d3_geo_radians;
function greatArc() {
var a = typeof source === "function" ? source.apply(this, arguments) : source,
b = typeof target === "function" ? target.apply(this, arguments) : target,
i = d3_geo_greatArcInterpolate(a, b),
dt = precision / i.d,
t = 0,
coordinates = [a];
while ((t += dt) < 1) coordinates.push(i(t));
coordinates.push(b);
return {
type: "LineString",
coordinates: coordinates
};
}
// Length returned in radians; multiply by radius for distance.
greatArc.distance = function() {
var a = typeof source === "function" ? source.apply(this, arguments) : source,
b = typeof target === "function" ? target.apply(this, arguments) : target;
return d3_geo_greatArcInterpolate(a, b).d;
};
greatArc.source = function(x) {
if (!arguments.length) return source;
source = x;
return greatArc;
};
greatArc.target = function(x) {
if (!arguments.length) return target;
target = x;
return greatArc;
};
// Precision is specified in degrees.
greatArc.precision = function(x) {
if (!arguments.length) return precision / d3_geo_radians;
precision = x * d3_geo_radians;
return greatArc;
};
return greatArc;
};
function d3_geo_greatArcSource(d) {
return d.source;
}
function d3_geo_greatArcTarget(d) {
return d.target;
}
function d3_geo_greatArcInterpolate(a, b) {
var x0 = a[0] * d3_geo_radians, cx0 = Math.cos(x0), sx0 = Math.sin(x0),
y0 = a[1] * d3_geo_radians, cy0 = Math.cos(y0), sy0 = Math.sin(y0),
x1 = b[0] * d3_geo_radians, cx1 = Math.cos(x1), sx1 = Math.sin(x1),
y1 = b[1] * d3_geo_radians, cy1 = Math.cos(y1), sy1 = Math.sin(y1),
d = interpolate.d = Math.acos(Math.max(-1, Math.min(1, sy0 * sy1 + cy0 * cy1 * Math.cos(x1 - x0)))),
sd = Math.sin(d);
// From http://williams.best.vwh.net/avform.htm#Intermediate
function interpolate(t) {
var A = Math.sin(d - (t *= d)) / sd,
B = Math.sin(t) / sd,
x = A * cy0 * cx0 + B * cy1 * cx1,
y = A * cy0 * sx0 + B * cy1 * sx1,
z = A * sy0 + B * sy1;
return [
Math.atan2(y, x) / d3_geo_radians,
Math.atan2(z, Math.sqrt(x * x + y * y)) / d3_geo_radians
];
}
return interpolate;
}

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

@ -1,91 +1 @@
// From http://williams.best.vwh.net/avform.htm#Intermediate
d3.geo.greatCircle = function() {
var source = d3_geo_greatCircleSource,
target = d3_geo_greatCircleTarget,
n = 100,
radius = 6371; // Mean radius of Earth, in km.
// TODO: breakAtDateLine?
function greatCircle(d, i) {
var from = source.call(this, d, i),
to = target.call(this, d, i),
x0 = from[0] * d3_radians,
y0 = from[1] * d3_radians,
x1 = to[0] * d3_radians,
y1 = to[1] * d3_radians,
cx0 = Math.cos(x0), sx0 = Math.sin(x0),
cy0 = Math.cos(y0), sy0 = Math.sin(y0),
cx1 = Math.cos(x1), sx1 = Math.sin(x1),
cy1 = Math.cos(y1), sy1 = Math.sin(y1),
d = Math.acos(sy0 * sy1 + cy0 * cy1 * Math.cos(x1 - x0)),
sd = Math.sin(d),
f = d / (n - 1),
e = -f,
path = [],
i = -1;
while (++i < n) {
e += f;
var A = Math.sin(d - e) / sd,
B = Math.sin(e) / sd,
x = A * cy0 * cx0 + B * cy1 * cx1,
y = A * cy0 * sx0 + B * cy1 * sx1,
z = A * sy0 + B * sy1;
path[i] = [
Math.atan2(y, x) / d3_radians,
Math.atan2(z, Math.sqrt(x * x + y * y)) / d3_radians
];
}
return path;
}
greatCircle.source = function(x) {
if (!arguments.length) return source;
source = x;
return greatCircle;
};
greatCircle.target = function(x) {
if (!arguments.length) return target;
target = x;
return greatCircle;
};
greatCircle.n = function(x) {
if (!arguments.length) return n;
n = +x;
return greatCircle;
};
greatCircle.radius = function(x) {
if (!arguments.length) return radius;
radius = +x;
return greatCircle;
};
// Haversine formula for great-circle distance.
greatCircle.distance = function(d, i) {
var from = source.call(this, d, i),
to = target.call(this, d, i),
x0 = from[0] * d3_radians,
y0 = from[1] * d3_radians,
x1 = to[0] * d3_radians,
y1 = to[1] * d3_radians,
sy = Math.sin((y1 - y0) / 2),
sx = Math.sin((x1 - x0) / 2),
a = sy * sy + Math.cos(y0) * Math.cos(y1) * sx * sx;
return radius * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
};
return greatCircle;
};
function d3_geo_greatCircleSource(d) {
return d.source;
}
function d3_geo_greatCircleTarget(d) {
return d.target;
}
d3.geo.greatCircle = d3.geo.circle;

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

@ -4,7 +4,7 @@ d3.geo.mercator = function() {
function mercator(coordinates) {
var x = coordinates[0] / 360,
y = -(Math.log(Math.tan(Math.PI / 4 + coordinates[1] * d3_radians / 2)) / d3_radians) / 360;
y = -(Math.log(Math.tan(Math.PI / 4 + coordinates[1] * d3_geo_radians / 2)) / d3_geo_radians) / 360;
return [
scale * x + translate[0],
scale * Math.max(-.5, Math.min(.5, y)) + translate[1]
@ -16,7 +16,7 @@ d3.geo.mercator = function() {
y = (coordinates[1] - translate[1]) / scale;
return [
360 * x,
2 * Math.atan(Math.exp(-360 * y * d3_radians)) / d3_radians - 90
2 * Math.atan(Math.exp(-360 * y * d3_geo_radians)) / d3_geo_radians - 90
];
};

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

@ -14,26 +14,26 @@ d3.geo.path = function() {
if (typeof pointRadius === "function") {
pointCircle = d3_path_circle(pointRadius.apply(this, arguments));
}
return d3_geo_pathType(pathTypes, d);
return pathType(d) || null;
}
function project(coordinates) {
return projection(coordinates).join(",");
}
var pathTypes = {
var pathType = d3_geo_type({
FeatureCollection: function(f) {
FeatureCollection: function(o) {
var path = [],
features = f.features,
features = o.features,
i = -1, // features.index
n = features.length;
while (++i < n) path.push(d3_geo_pathType(pathTypes, features[i].geometry));
while (++i < n) path.push(pathType(features[i].geometry));
return path.join("");
},
Feature: function(f) {
return d3_geo_pathType(pathTypes, f.geometry);
Feature: function(o) {
return pathType(o.geometry);
},
Point: function(o) {
@ -89,10 +89,11 @@ d3.geo.path = function() {
while (++i < n) {
subcoordinates = coordinates[i];
j = -1;
m = subcoordinates.length;
path.push("M");
while (++j < m) path.push(project(subcoordinates[j]), "L");
path[path.length - 1] = "Z";
if ((m = subcoordinates.length - 1) > 0) {
path.push("M");
while (++j < m) path.push(project(subcoordinates[j]), "L");
path[path.length - 1] = "Z";
}
}
return path.join("");
},
@ -115,10 +116,11 @@ d3.geo.path = function() {
while (++j < m) {
subsubcoordinates = subcoordinates[j];
k = -1;
p = subsubcoordinates.length - 1;
path.push("M");
while (++k < p) path.push(project(subsubcoordinates[k]), "L");
path[path.length - 1] = "Z";
if ((p = subsubcoordinates.length - 1) > 0) {
path.push("M");
while (++k < p) path.push(project(subsubcoordinates[k]), "L");
path[path.length - 1] = "Z";
}
}
}
return path.join("");
@ -129,32 +131,27 @@ d3.geo.path = function() {
geometries = o.geometries,
i = -1, // geometries index
n = geometries.length;
while (++i < n) path.push(d3_geo_pathType(pathTypes, geometries[i]));
while (++i < n) path.push(pathType(geometries[i]));
return path.join("");
}
};
});
var areaTypes = {
var areaType = path.area = d3_geo_type({
FeatureCollection: function(f) {
FeatureCollection: function(o) {
var area = 0,
features = f.features,
features = o.features,
i = -1, // features.index
n = features.length;
while (++i < n) area += d3_geo_pathType(areaTypes, features[i]);
while (++i < n) area += areaType(features[i]);
return area;
},
Feature: function(f) {
return d3_geo_pathType(areaTypes, f.geometry);
Feature: function(o) {
return areaType(o.geometry);
},
Point: d3_geo_pathZero,
MultiPoint: d3_geo_pathZero,
LineString: d3_geo_pathZero,
MultiLineString: d3_geo_pathZero,
Polygon: function(o) {
return polygonArea(o.coordinates);
},
@ -173,11 +170,11 @@ d3.geo.path = function() {
geometries = o.geometries,
i = -1, // geometries index
n = geometries.length;
while (++i < n) sum += d3_geo_pathType(areaTypes, geometries[i]);
while (++i < n) sum += areaType(geometries[i]);
return sum;
}
};
}, 0);
function polygonArea(coordinates) {
var sum = area(coordinates[0]), // exterior ring
@ -205,7 +202,7 @@ d3.geo.path = function() {
return [x, y, 6 * z]; // weighted centroid
}
var centroidTypes = {
var centroidType = path.centroid = d3_geo_type({
// TODO FeatureCollection
// TODO Point
@ -214,8 +211,8 @@ d3.geo.path = function() {
// TODO MultiLineString
// TODO GeometryCollection
Feature: function(f) {
return d3_geo_pathType(centroidTypes, f.geometry);
Feature: function(o) {
return centroidType(o.geometry);
},
Polygon: function(o) {
@ -241,8 +238,7 @@ d3.geo.path = function() {
return [x / z, y / z];
}
};
});
function area(coordinates) {
return Math.abs(d3.geom.polygon(coordinates.map(projection)).area());
@ -253,14 +249,6 @@ d3.geo.path = function() {
return path;
};
path.area = function(d) {
return d3_geo_pathType(areaTypes, d);
};
path.centroid = function(d) {
return d3_geo_pathType(centroidTypes, d);
};
path.pointRadius = function(x) {
if (typeof x === "function") pointRadius = x;
else {
@ -279,11 +267,3 @@ function d3_path_circle(radius) {
+ "a" + radius + "," + radius + " 0 1,1 0," + (+2 * radius)
+ "z";
}
function d3_geo_pathZero() {
return 0;
}
function d3_geo_pathType(types, o) {
return o && o.type in types ? types[o.type](o) : "";
}

5
src/geo/type.js Normal file
Просмотреть файл

@ -0,0 +1,5 @@
function d3_geo_type(types, defaultValue) {
return function(object) {
return object && object.type in types ? types[object.type](object) : defaultValue;
};
}

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

@ -39,6 +39,7 @@ d3.layout.force = function() {
node.py -= dy * k;
}
}
return !quad.charge;
};
}

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

@ -32,8 +32,8 @@ function d3_scale_log(linear, log) {
if (extent.every(isFinite)) {
var i = Math.floor(extent[0]),
j = Math.ceil(extent[1]),
u = pow(extent[0]),
v = pow(extent[1]);
u = Math.round(pow(extent[0])),
v = Math.round(pow(extent[1]));
if (log === d3_scale_logn) {
ticks.push(pow(i));
for (; i++ < j;) for (var k = 9; k > 0; k--) ticks.push(pow(i) * k);
@ -48,8 +48,15 @@ function d3_scale_log(linear, log) {
return ticks;
};
scale.tickFormat = function() {
return d3_scale_logTickFormat;
scale.tickFormat = function(n, format) {
if (arguments.length < 2) format = d3_scale_logFormat;
if (arguments.length < 1) return format;
var k = n / scale.ticks().length,
f = log === d3_scale_logn ? (e = -1e-15, Math.floor) : (e = 1e-15, Math.ceil),
e;
return function(d) {
return d / pow(f(log(d) + e)) < k ? format(d) : "";
};
};
scale.copy = function() {
@ -59,6 +66,8 @@ function d3_scale_log(linear, log) {
return d3_scale_linearRebind(scale, linear);
};
var d3_scale_logFormat = d3.format("e");
function d3_scale_logp(x) {
return Math.log(x) / Math.LN10;
}
@ -74,7 +83,3 @@ d3_scale_logp.pow = function(x) {
d3_scale_logn.pow = function(x) {
return -Math.pow(10, -x);
};
function d3_scale_logTickFormat(d) {
return d.toPrecision(1);
}

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

@ -92,6 +92,7 @@ suite.addBatch({
assert.strictEqual(f(-1.23), "120%");
},
"can round to significant digits": function(format) {
assert.strictEqual(format(".2r")(0), "0.0");
assert.strictEqual(format(".1r")(0.049), "0.05");
assert.strictEqual(format(".1r")(0.49), "0.5");
assert.strictEqual(format(".2r")(0.449), "0.45");

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

@ -12,6 +12,14 @@ suite.addBatch({
topic: function() {
return d3.geo.azimuthal().mode("stereographic").translate([0, 0]).scale(100);
},
"origin": function(azimuthal) {
var coords = azimuthal([0, 0]);
assert.inDelta(coords[0], 0, 1e-6);
assert.inDelta(coords[1], 0, 1e-6);
var lonlat = azimuthal.invert(coords);
assert.inDelta(lonlat[0], 0, 1e-6);
assert.inDelta(lonlat[1], 0, 1e-6);
},
"Arctic": function(azimuthal) {
var coords = azimuthal([0, 85]);
assert.inDelta(coords[0], 0, 1e-6);
@ -50,6 +58,14 @@ suite.addBatch({
topic: function() {
return d3.geo.azimuthal().mode("orthographic").translate([0, 0]).scale(100);
},
"origin": function(azimuthal) {
var coords = azimuthal([0, 0]);
assert.inDelta(coords[0], 0, 1e-6);
assert.inDelta(coords[1], 0, 1e-6);
var lonlat = azimuthal.invert(coords);
assert.inDelta(lonlat[0], 0, 1e-6);
assert.inDelta(lonlat[1], 0, 1e-6);
},
"Arctic": function(azimuthal) {
var coords = azimuthal([0, 85]);
assert.inDelta(coords[0], 0, 1e-6);
@ -70,11 +86,17 @@ suite.addBatch({
var coords = azimuthal([-180, 0]);
assert.inDelta(coords[0], 0, 1e-6);
assert.inDelta(coords[1], 0, 1e-6);
var lonlat = azimuthal.invert(coords);
assert.inDelta(lonlat[0], 0, 1e-6);
assert.inDelta(lonlat[1], 0, 1e-6);
},
"Phillipines": function(azimuthal) {
var coords = azimuthal([180, 0]);
assert.inDelta(coords[0], 0, 1e-6);
assert.inDelta(coords[1], 0, 1e-6);
var lonlat = azimuthal.invert(coords);
assert.inDelta(lonlat[0], 0, 1e-6);
assert.inDelta(lonlat[1], 0, 1e-6);
},
"Inversion works for non-zero translation": function() {
var azimuthal = d3.geo.azimuthal().mode("orthographic").translate([123, 99]).scale(100),
@ -83,6 +105,156 @@ suite.addBatch({
assert.inDelta(lonlat[0], 0, 1e-6);
assert.inDelta(lonlat[1], 85, 1e-6);
}
},
"azimuthal.gnomonic": {
topic: function() {
return d3.geo.azimuthal().mode("gnomonic").translate([0, 0]).scale(100);
},
"origin": function(azimuthal) {
var coords = azimuthal([0, 0]);
assert.inDelta(coords[0], 0, 1e-6);
assert.inDelta(coords[1], 0, 1e-6);
var lonlat = azimuthal.invert(coords);
assert.inDelta(lonlat[0], 0, 1e-6);
assert.inDelta(lonlat[1], 0, 1e-6);
},
"Arctic": function(azimuthal) {
var coords = azimuthal([0, 85]);
assert.inDelta(coords[0], 0, 1e-6);
assert.inDelta(coords[1], -1143.005230, 1e-6);
var lonlat = azimuthal.invert(coords);
assert.inDelta(lonlat[0], 0, 1e-6);
assert.inDelta(lonlat[1], 85, 1e-6);
},
"Antarctic": function(azimuthal) {
var coords = azimuthal([0, -85]);
assert.inDelta(coords[0], 0, 1e-6);
assert.inDelta(coords[1], 1143.005230, 1e-6);
var lonlat = azimuthal.invert(coords);
assert.inDelta(lonlat[0], 0, 1e-6);
assert.inDelta(lonlat[1], -85, 1e-6);
},
"Hawaii": function(azimuthal) {
var coords = azimuthal([-180, 0]);
assert.inDelta(coords[0], 0, 1e-6);
assert.inDelta(coords[1], 0, 1e-6);
var lonlat = azimuthal.invert(coords);
assert.inDelta(lonlat[0], 0, 1e-6);
assert.inDelta(lonlat[1], 0, 1e-6);
},
"Phillipines": function(azimuthal) {
var coords = azimuthal([180, 0]);
assert.inDelta(coords[0], 0, 1e-6);
assert.inDelta(coords[1], 0, 1e-6);
var lonlat = azimuthal.invert(coords);
assert.inDelta(lonlat[0], 0, 1e-6);
assert.inDelta(lonlat[1], 0, 1e-6);
},
"Inversion works for non-zero translation": function() {
var azimuthal = d3.geo.azimuthal().mode("stereographic").translate([123, 99]).scale(100),
coords = azimuthal([0, 85]),
lonlat = azimuthal.invert(coords);
assert.inDelta(lonlat[0], 0, 1e-6);
assert.inDelta(lonlat[1], 85, 1e-6);
}
},
"azimuthal.equidistant": {
topic: function() {
return d3.geo.azimuthal().mode("equidistant").translate([0, 0]).scale(100);
},
"origin": function(azimuthal) {
var coords = azimuthal([0, 0]);
assert.inDelta(coords[0], 0, 1e-6);
assert.inDelta(coords[1], 0, 1e-6);
var lonlat = azimuthal.invert(coords);
assert.inDelta(lonlat[0], 0, 1e-6);
assert.inDelta(lonlat[1], 0, 1e-6);
},
"Arctic": function(azimuthal) {
var coords = azimuthal([0, 85]);
assert.inDelta(coords[0], 0, 1e-6);
assert.inDelta(coords[1], -148.352986, 1e-6);
var lonlat = azimuthal.invert(coords);
assert.inDelta(lonlat[0], 0, 1e-6);
assert.inDelta(lonlat[1], 85, 1e-6);
},
"Antarctic": function(azimuthal) {
var coords = azimuthal([0, -90]);
assert.inDelta(coords[0], 0, 1e-6);
assert.inDelta(coords[1], 157.079632, 1e-6);
var lonlat = azimuthal.invert(coords);
assert.inDelta(lonlat[0], 0, 1e-6);
assert.inDelta(lonlat[1], -90, 1e-6);
},
"Hawaii": function(azimuthal) {
var coords = azimuthal([-180, 0]);
assert.inDelta(coords[0], -314.159265, 1e-6);
assert.inDelta(coords[1], 0, 1e-6);
var lonlat = azimuthal.invert(coords);
assert.inDelta(lonlat[0], -180, 1e-6);
assert.inDelta(lonlat[1], 0, 1e-6);
},
"Phillipines": function(azimuthal) {
var coords = azimuthal([180, 0]);
assert.inDelta(coords[0], 314.159265, 1e-6);
assert.inDelta(coords[1], 0, 1e-6);
var lonlat = azimuthal.invert(coords);
assert.inDelta(lonlat[0], 180, 1e-6);
assert.inDelta(lonlat[1], 0, 1e-6);
},
"Inversion works for non-zero translation": function() {
var azimuthal = d3.geo.azimuthal().mode("stereographic").translate([123, 99]).scale(100),
coords = azimuthal([0, 85]),
lonlat = azimuthal.invert(coords);
assert.inDelta(lonlat[0], 0, 1e-6);
assert.inDelta(lonlat[1], 85, 1e-6);
}
},
"azimuthal.equalarea": {
topic: function() {
return d3.geo.azimuthal().mode("equalarea").translate([0, 0]).scale(100);
},
"origin": function(azimuthal) {
var coords = azimuthal([0, 0]);
assert.inDelta(coords[0], 0, 1e-6);
assert.inDelta(coords[1], 0, 1e-6);
var lonlat = azimuthal.invert(coords);
assert.inDelta(lonlat[0], 0, 1e-6);
assert.inDelta(lonlat[1], 0, 1e-6);
},
"Arctic": function(azimuthal) {
var coords = azimuthal([0, 85]);
assert.inDelta(coords[0], 0, 1e-6);
assert.inDelta(coords[1], -135.118041, 1e-6);
var lonlat = azimuthal.invert(coords);
assert.inDelta(lonlat[0], 0, 1e-6);
assert.inDelta(lonlat[1], 85, 1e-6);
},
"Antarctic": function(azimuthal) {
var coords = azimuthal([0, -90]);
assert.inDelta(coords[0], 0, 1e-6);
assert.inDelta(coords[1], 141.421356, 1e-6);
var lonlat = azimuthal.invert(coords);
assert.inDelta(lonlat[0], 180, 1e-6);
assert.inDelta(lonlat[1], -90, 1e-6);
},
"Hawaii": function(azimuthal) {
var coords = azimuthal([-180, 0]);
assert.equal(coords[0], -Infinity);
assert.isTrue(isNaN(coords[1]));
},
"Phillipines": function(azimuthal) {
var coords = azimuthal([180, 0]);
assert.equal(coords[0], Infinity);
assert.isTrue(isNaN(coords[1]));
},
"Inversion works for non-zero translation": function() {
var azimuthal = d3.geo.azimuthal().mode("stereographic").translate([123, 99]).scale(100),
coords = azimuthal([0, 85]),
lonlat = azimuthal.invert(coords);
assert.inDelta(lonlat[0], 0, 1e-6);
assert.inDelta(lonlat[1], 85, 1e-6);
}
}
});

116
test/geo/bonne-test.js Normal file
Просмотреть файл

@ -0,0 +1,116 @@
require("../env");
require("../../d3");
require("../../d3.geo");
var vows = require("vows"),
assert = require("assert");
var suite = vows.describe("d3.geo.bonne");
suite.addBatch({
"default instance": {
topic: function() {
return d3.geo.bonne();
},
"scale defaults to 200": function(bonne) {
assert.equal(bonne.scale(), 200);
},
"parallel defaults to 45°": function(bonne) {
assert.equal(bonne.parallel(), 45);
},
"origin defaults to [0,0]": function(bonne) {
assert.deepEqual(bonne.origin(), [0, 0]);
},
"translate defaults to [480, 250]": function(bonne) {
assert.deepEqual(bonne.translate(), [480, 250]);
}
},
"Bonne at 40° parallel": {
topic: function() {
return d3.geo.bonne().parallel(40);
},
"Arctic": function(bonne) {
var lonlat = [0, 85],
coords = bonne(lonlat);
assert.inDelta(coords, [480, 92.920367], 1e-6);
assert.inDelta(bonne.invert(coords), lonlat, 1e-6);
},
"Antarctic": function(bonne) {
var lonlat = [0, -85],
coords = bonne(lonlat);
assert.inDelta(coords, [480, 686.332312], 1e-6);
assert.inDelta(bonne.invert(coords), lonlat, 1e-6);
},
"Hawaii": function(bonne) {
var lonlat = [-180, 0],
coords = bonne(lonlat);
assert.inDelta(coords, [103.604887, -22.895998], 1e-6);
assert.inDelta(bonne.invert(coords), lonlat, 1e-6);
},
"Phillipines": function(bonne) {
var lonlat = [180, 0],
coords = bonne(lonlat);
assert.inDelta(coords, [856.395112, -22.895998], 1e-6);
assert.inDelta(bonne.invert(coords), lonlat, 1e-6);
},
"invert observes translation": function() {
var bonne = d3.geo.bonne().translate([123, 99]).scale(100),
coords = bonne([0, 85]),
lonlat = bonne.invert(coords);
assert.inDelta(lonlat[0], 0, 1e-6);
assert.inDelta(lonlat[1], 85, 1e-6);
},
"invert(project(location)) equals location": function(bonne) {
for (var i = -1; i < 20; ++i) {
var location = [Math.random() * 360 - 180, Math.random() * 180 - 90];
assert.inDelta(location, bonne.invert(bonne(location)), .5);
}
},
"project(invert(point)) equals point": function(bonne) {
for (var i = -1; i < 20; ++i) {
var point = [Math.random() * 700 + 100, Math.random() * 400 + 50];
assert.inDelta(point, bonne(bonne.invert(point)), .5);
}
}
},
"Werner": {
topic: function() {
return d3.geo.bonne().parallel(90);
},
"invert(project(location)) equals location": function(bonne) {
for (var i = -1; i < 20; ++i) {
var location = [Math.random() * 360 - 180, Math.random() * 180 - 90];
assert.inDelta(location, bonne.invert(bonne(location)), .5);
}
},
"project(invert(point)) equals point": function(bonne) {
for (var i = -1; i < 20; ++i) {
var point = [Math.random() * 700 + 100, Math.random() * 400 + 50];
assert.inDelta(point, bonne(bonne.invert(point)), .5);
}
}
},
"Sinsuoidal": {
topic: function() {
return d3.geo.bonne().parallel(0);
},
"invert(project(location)) equals location": function(bonne) {
for (var i = -1; i < 20; ++i) {
var location = [Math.random() * 360 - 180, Math.random() * 180 - 90];
assert.inDelta(location, bonne.invert(bonne(location)), .5);
}
},
"project(invert(point)) equals point": function(bonne) {
for (var i = -1; i < 20; ++i) {
var point = [Math.random() * 700 + 100, Math.random() * 400 + 50];
assert.inDelta(point, bonne(bonne.invert(point)), .5);
}
}
}
});
suite.export(module);

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

@ -5,23 +5,22 @@ require("../../d3.geo");
var vows = require("vows"),
assert = require("assert");
var suite = vows.describe("d3.geo.greatCircle");
var suite = vows.describe("d3.geo.greatArc");
suite.addBatch({
"greatCircle": {
"greatArc": {
topic: function() {
return d3.geo.greatCircle()
.n(12);
return d3.geo.greatArc();
},
"distance": function(circle) {
assert.equal(circle.distance({source: [0, 0], target: [0, 0]}), 0);
assert.inDelta(circle.distance({
"distance": function(arc) {
assert.equal(arc.distance({source: [0, 0], target: [0, 0]}), 0);
assert.inDelta(arc.distance({
source: [118 + 24 / 60, 33 + 57 / 60],
target: [ 73 + 47 / 60, 40 + 38 / 60]
}), 3973, .5);
}), 3973 / 6371, .5);
},
"geodesic": function(circle) {
assert.inDelta(circle({source: [5, 52], target: [-120, 37]}), [
"geodesic": function(arc) {
assert.inDelta(arc.precision(7.2)({source: [5, 52], target: [-120, 37]}).coordinates, [
[ 5, 52 ],
[ -3.805036, 57.05083],
[ -15.122869, 61.30118],

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

@ -0,0 +1,25 @@
require("../env");
require("../../d3");
require("../../d3.geo");
var vows = require("vows"),
assert = require("assert");
var suite = vows.describe("d3.geo.path");
suite.addBatch({
"path": {
topic: d3.geo.path,
"Polygon": function(path) {
assert.equal(path({
type: "Feature",
geometry: {
type: "Polygon",
coordinates: [[[-63.03, 18.02], [-63.14, 18.06], [-63.01, 18.07], [-63.03, 18.02]]]
},
}), "M984.5652086349427,468.99159422596244L981.8396467935554,467.9114977057422L985.0785139575695,467.688661596079Z");
}
}
});
suite.export(module);

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

@ -39,7 +39,11 @@ suite.addBatch({
},
"can specify negative domain values": function(log) {
var x = log().domain([-100, -1]);
assert.deepEqual(x.ticks().map(x.tickFormat()), [-100, -90, -80, -70, -60, -50, -40, -30, -20, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1]);
assert.deepEqual(x.ticks().map(x.tickFormat()), [
"1e+2",
"9e+1", "8e+1", "7e+1", "6e+1", "5e+1", "4e+1", "3e+1", "2e+1", "1e+1",
"9e+0", "8e+0", "7e+0", "6e+0", "5e+0", "4e+0", "3e+0", "2e+0", "1e+0"
]);
assert.inDelta(x(-50), 0.150515, 1e-6);
},
"can specify a polylog domain and range": function(log) {
@ -143,9 +147,38 @@ suite.addBatch({
"ticks": {
"can generate ticks": function(log) {
var x = log();
assert.deepEqual(x.ticks().map(x.tickFormat()), [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
assert.deepEqual(x.ticks().map(x.tickFormat()), [
"1e+0", "2e+0", "3e+0", "4e+0", "5e+0", "6e+0", "7e+0", "8e+0", "9e+0",
"1e+1"
]);
var x = log().domain([100, 1]);
assert.deepEqual(x.ticks().map(x.tickFormat()), [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100]);
assert.deepEqual(x.ticks().map(x.tickFormat()), [
"1e+0", "2e+0", "3e+0", "4e+0", "5e+0", "6e+0", "7e+0", "8e+0", "9e+0",
"1e+1", "2e+1", "3e+1", "4e+1", "5e+1", "6e+1", "7e+1", "8e+1", "9e+1",
"1e+2"
]);
},
"can generate fewer ticks, if desired": function(log) {
var x = log();
assert.deepEqual(x.ticks().map(x.tickFormat(5)), [
"1e+0", "2e+0", "3e+0", "4e+0", "", "", "", "", "",
"1e+1"
]);
var x = log().domain([100, 1]);
assert.deepEqual(x.ticks().map(x.tickFormat(10)), [
"1e+0", "2e+0", "3e+0", "4e+0", "5e+0", "", "", "", "",
"1e+1", "2e+1", "3e+1", "4e+1", "5e+1", "", "", "", "",
"1e+2"
]);
},
"can override the tick format": function(log) {
var x = log().domain([1000, 1]);
assert.deepEqual(x.ticks().map(x.tickFormat(10, d3.format("+,d"))), [
"+1", "+2", "+3", "", "", "", "", "", "",
"+10", "+20", "+30", "", "", "", "", "", "",
"+100", "+200", "+300", "", "", "", "", "", "",
"+1,000"
]);
}
},