Merge branch 'release'
This commit is contained in:
Коммит
9e16bee0a5
13
Makefile
13
Makefile
|
@ -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
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;
|
||||
})();
|
||||
|
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -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,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")
|
||||
|
|
|
@ -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.
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
};
|
|
@ -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;
|
||||
|
|
|
@ -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) : "";
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -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],
|
|
@ -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"
|
||||
]);
|
||||
}
|
||||
},
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче