364 строки
9.2 KiB
JavaScript
364 строки
9.2 KiB
JavaScript
d3.geo = {};
|
|
// Derived from Tom Carden's Albers implementation for Protovis.
|
|
// http://gist.github.com/476238
|
|
// http://mathworld.wolfram.com/AlbersEqual-AreaConicProjection.html
|
|
|
|
d3.geo.albers = function() {
|
|
var origin = [-98, 38],
|
|
parallels = [29.5, 45.5],
|
|
scale = 1000,
|
|
translate = [480, 250],
|
|
lng0, // d3_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;
|
|
return [
|
|
scale * p * Math.sin(t) + translate[0],
|
|
scale * (p * Math.cos(t) - p0) + translate[1]
|
|
];
|
|
}
|
|
|
|
function reload() {
|
|
var phi1 = d3_radians * parallels[0],
|
|
phi2 = d3_radians * parallels[1],
|
|
lat0 = d3_radians * origin[1],
|
|
s = Math.sin(phi1),
|
|
c = Math.cos(phi1);
|
|
lng0 = d3_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;
|
|
return albers;
|
|
}
|
|
|
|
albers.origin = function(x) {
|
|
if (!arguments.length) return origin;
|
|
origin = [+x[0], +x[1]];
|
|
return reload();
|
|
};
|
|
|
|
albers.parallels = function(x) {
|
|
if (!arguments.length) return parallels;
|
|
parallels = [+x[0], +x[1]];
|
|
return reload();
|
|
};
|
|
|
|
albers.scale = function(x) {
|
|
if (!arguments.length) return scale;
|
|
scale = +x;
|
|
return albers;
|
|
};
|
|
|
|
albers.translate = function(x) {
|
|
if (!arguments.length) return translate;
|
|
translate = [+x[0], +x[1]];
|
|
return albers;
|
|
};
|
|
|
|
return reload();
|
|
};
|
|
|
|
// A composite projection for the United States, 960x500. The set of standard
|
|
// parallels for each region comes from USGS, which is published here:
|
|
// http://egsc.usgs.gov/isb/pubs/MapProjections/projections.html#albers
|
|
// TODO allow the composite projection to be rescaled?
|
|
d3.geo.albersUsa = function() {
|
|
var lower48 = d3.geo.albers();
|
|
|
|
var alaska = d3.geo.albers()
|
|
.origin([-160, 60])
|
|
.parallels([55, 65])
|
|
.scale([600])
|
|
.translate([80, 420]);
|
|
|
|
var hawaii = d3.geo.albers()
|
|
.origin([-160, 20])
|
|
.parallels([8, 18])
|
|
.translate([290, 450]);
|
|
|
|
var puertoRico = d3.geo.albers()
|
|
.origin([-60, 10])
|
|
.parallels([8, 18])
|
|
.scale([1500])
|
|
.translate([1060, 680]);
|
|
|
|
return function(coordinates) {
|
|
var lon = coordinates[0],
|
|
lat = coordinates[1];
|
|
return (lat < 25
|
|
? (lon < -100 ? hawaii : puertoRico)
|
|
: (lat > 50 ? alaska : lower48))(coordinates);
|
|
};
|
|
};
|
|
|
|
var d3_radians = Math.PI / 180;
|
|
d3.geo.mercator = function() {
|
|
var scale = 500,
|
|
translate = [480, 250];
|
|
|
|
function mercator(coordinates) {
|
|
var x = (coordinates[0]) / 360,
|
|
y = (-180 / Math.PI * Math.log(Math.tan(Math.PI / 4 + coordinates[1] * Math.PI / 360))) / 360;
|
|
return [
|
|
scale * x + translate[0],
|
|
scale * Math.max(-.5, Math.min(.5, y)) + translate[1]
|
|
];
|
|
}
|
|
|
|
mercator.scale = function(x) {
|
|
if (!arguments.length) return scale;
|
|
scale = +x;
|
|
return mercator;
|
|
};
|
|
|
|
mercator.translate = function(x) {
|
|
if (!arguments.length) return translate;
|
|
translate = [+x[0], +x[1]];
|
|
return mercator;
|
|
};
|
|
|
|
return mercator;
|
|
};
|
|
/**
|
|
* Returns a function that, given a GeoJSON feature, returns the corresponding
|
|
* SVG path. The function can be customized by overriding the projection. Point
|
|
* features are mapped to circles with a default radius of 4.5px; the radius
|
|
* can be specified either as a constant or a function that is evaluated per
|
|
* feature.
|
|
*/
|
|
d3.geo.path = function() {
|
|
var pointRadius = 4.5,
|
|
pointCircle = d3_path_circle(pointRadius),
|
|
projection = d3.geo.albersUsa();
|
|
|
|
function path(d, i) {
|
|
if (typeof pointRadius == "function") {
|
|
pointCircle = d3_path_circle(pointRadius.apply(this, arguments));
|
|
}
|
|
return type(featurePaths, d);
|
|
}
|
|
|
|
function project(coordinates) {
|
|
return projection(coordinates).join(",");
|
|
}
|
|
|
|
function type(types, o) {
|
|
return o && o.type in types
|
|
? types[o.type](o)
|
|
: "";
|
|
}
|
|
|
|
var featurePaths = {
|
|
|
|
FeatureCollection: function(f) {
|
|
var path = [],
|
|
features = f.features,
|
|
i = -1, // features.index
|
|
n = features.length;
|
|
while (++i < n) path.push(type(featurePaths, features[i]));
|
|
return path.join("");
|
|
},
|
|
|
|
Feature: function(f) {
|
|
return type(geometryPaths, f.geometry);
|
|
}
|
|
|
|
};
|
|
|
|
var geometryPaths = {
|
|
|
|
Point: function(o) {
|
|
return "M" + project(o.coordinates) + pointCircle;
|
|
},
|
|
|
|
MultiPoint: function(o) {
|
|
var path = [],
|
|
coordinates = o.coordinates,
|
|
i = -1, // coordinates.index
|
|
n = coordinates.length;
|
|
while (++i < n) path.push("M", project(coordinates[i]), pointCircle);
|
|
return path.join("");
|
|
},
|
|
|
|
LineString: function(o) {
|
|
var path = ["M"],
|
|
coordinates = o.coordinates,
|
|
i = -1, // coordinates.index
|
|
n = coordinates.length;
|
|
while (++i < n) path.push(project(coordinates[i]), "L");
|
|
path.pop();
|
|
return path.join("");
|
|
},
|
|
|
|
MultiLineString: function(o) {
|
|
var path = [],
|
|
coordinates = o.coordinates,
|
|
i = -1, // coordinates.index
|
|
n = coordinates.length,
|
|
subcoordinates, // coordinates[i]
|
|
j, // subcoordinates.index
|
|
m; // subcoordinates.length
|
|
while (++i < n) {
|
|
subcoordinates = coordinates[i];
|
|
j = -1;
|
|
m = subcoordinates.length;
|
|
path.push("M");
|
|
while (++j < m) path.push(project(subcoordinates[j]), "L");
|
|
path.pop();
|
|
}
|
|
return path.join("");
|
|
},
|
|
|
|
Polygon: function(o) {
|
|
var path = [],
|
|
coordinates = o.coordinates,
|
|
i = -1, // coordinates.index
|
|
n = coordinates.length,
|
|
subcoordinates, // coordinates[i]
|
|
j, // subcoordinates.index
|
|
m; // subcoordinates.length
|
|
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";
|
|
}
|
|
return path.join("");
|
|
},
|
|
|
|
MultiPolygon: function(o) {
|
|
var path = [],
|
|
coordinates = o.coordinates,
|
|
i = -1, // coordinates index
|
|
n = coordinates.length,
|
|
subcoordinates, // coordinates[i]
|
|
j, // subcoordinates index
|
|
m, // subcoordinates.length
|
|
subsubcoordinates, // subcoordinates[j]
|
|
k, // subsubcoordinates index
|
|
p; // subsubcoordinates.length
|
|
while (++i < n) {
|
|
subcoordinates = coordinates[i];
|
|
j = -1;
|
|
m = subcoordinates.length;
|
|
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";
|
|
}
|
|
}
|
|
return path.join("");
|
|
},
|
|
|
|
GeometryCollection: function(o) {
|
|
var path = [],
|
|
geometries = o.geometries,
|
|
i = -1, // geometries index
|
|
n = geometries.length;
|
|
while (++i < n) path.push(type(geometryPaths, geometries[i]));
|
|
return path.join("");
|
|
}
|
|
|
|
};
|
|
|
|
var featureAreas = {
|
|
|
|
FeatureCollection: function(f) {
|
|
var area = 0,
|
|
features = f.features,
|
|
i = -1, // features.index
|
|
n = features.length;
|
|
while (++i < n) area += type(featureAreas, features[i]);
|
|
return area;
|
|
},
|
|
|
|
Feature: function(f) {
|
|
return type(geometryAreas, f.geometry);
|
|
}
|
|
|
|
};
|
|
|
|
var geometryAreas = {
|
|
|
|
Point: d3_geo_pathZero,
|
|
MultiPoint: d3_geo_pathZero,
|
|
LineString: d3_geo_pathZero,
|
|
MultiLineString: d3_geo_pathZero,
|
|
|
|
Polygon: function(o) {
|
|
return polygonArea(o.coordinates);
|
|
},
|
|
|
|
MultiPolygon: function(o) {
|
|
var sum = 0,
|
|
coordinates = o.coordinates,
|
|
i = -1, // coordinates index
|
|
n = coordinates.length;
|
|
while (++i < n) sum += polygonArea(coordinates[i]);
|
|
return sum;
|
|
},
|
|
|
|
GeometryCollection: function(o) {
|
|
var sum = 0,
|
|
geometries = o.geometries,
|
|
i = -1, // geometries index
|
|
n = geometries.length;
|
|
while (++i < n) sum += type(geometryAreas, geometries[i]);
|
|
return sum;
|
|
}
|
|
|
|
};
|
|
|
|
function polygonArea(coordinates) {
|
|
var sum = area(coordinates[0]),
|
|
i = 0, // coordinates.index
|
|
n = coordinates.length;
|
|
while (++i < n) sum -= area(coordinates[i]);
|
|
return sum;
|
|
}
|
|
|
|
function area(coordinates) {
|
|
return Math.abs(d3.geom.polygon(coordinates.map(projection)).area());
|
|
}
|
|
|
|
path.projection = function(x) {
|
|
projection = x;
|
|
return path;
|
|
};
|
|
|
|
path.area = function(d) {
|
|
return type(featureAreas, d);
|
|
};
|
|
|
|
path.pointRadius = function(x) {
|
|
if (typeof x == "function") pointRadius = x;
|
|
else {
|
|
pointRadius = +x;
|
|
pointCircle = d3_path_circle(pointRadius);
|
|
}
|
|
return path;
|
|
};
|
|
|
|
return path;
|
|
};
|
|
|
|
function d3_path_circle(radius) {
|
|
return "m0," + radius
|
|
+ "a" + radius + "," + radius + " 0 1,1 0," + (-2 * radius)
|
|
+ "a" + radius + "," + radius + " 0 1,1 0," + (+2 * radius)
|
|
+ "z";
|
|
}
|
|
|
|
function d3_geo_pathZero() {
|
|
return 0;
|
|
}
|