Skip to content

Instantly share code, notes, and snippets.

@Fil
Last active July 29, 2019 00:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Fil/7723167596af40d9159be34ffbf8d36b to your computer and use it in GitHub Desktop.
Save Fil/7723167596af40d9159be34ffbf8d36b to your computer and use it in GitHub Desktop.
AlbersUSA inverse proj parameters
license: gpl-3.0

This block to understand how the inverse AlbersUsa works, in answer to d3 issue #1984. In green, values that would work for Puerto Rico.

Forked from mbostock's block: AlbersUSA + PR

// A modified d3.geoAlbersUsa to include Puerto Rico.
// See also https://bl.ocks.org/rveciana/5040be82aea528b6f785464f8816690f
function albersUsaPr() {
var ε = 1e-6;
var lower48 = d3.geoAlbers();
// EPSG:3338
var alaska = d3.geoConicEqualArea()
.rotate([154, 0])
.center([-2, 58.5])
.parallels([55, 65]);
// ESRI:102007
var hawaii = d3.geoConicEqualArea()
.rotate([157, 0])
.center([-3, 19.9])
.parallels([8, 18]);
// XXX? You should check that this is a standard PR projection!
var puertoRico = d3.geoConicEqualArea()
.rotate([66, 0])
.center([0, 18])
.parallels([8, 18]);
var point,
pointStream = {point: function(x, y) { point = [x, y]; }},
lower48Point,
alaskaPoint,
hawaiiPoint,
puertoRicoPoint;
function albersUsa(coordinates) {
var x = coordinates[0], y = coordinates[1];
point = null;
(lower48Point(x, y), point)
|| (alaskaPoint(x, y), point)
|| (hawaiiPoint(x, y), point)
|| (puertoRicoPoint(x, y), point);
return point;
}
albersUsa.invert = function(coordinates) {
var k = lower48.scale(),
t = lower48.translate(),
x = (coordinates[0] - t[0]) / k,
y = (coordinates[1] - t[1]) / k;
return (y >= .120 && y < .234 && x >= -.425 && x < -.214 ? alaska
: y >= .166 && y < .234 && x >= -.214 && x < -.115 ? hawaii
: y >= .204 && y < .234 && x >= .320 && x < .380 ? puertoRico
: lower48).invert(coordinates);
};
// A naïve multi-projection stream.
// The projections must have mutually exclusive clip regions on the sphere,
// as this will avoid emitting interleaving lines and polygons.
albersUsa.stream = function(stream) {
var lower48Stream = lower48.stream(stream),
alaskaStream = alaska.stream(stream),
hawaiiStream = hawaii.stream(stream),
puertoRicoStream = puertoRico.stream(stream);
return {
point: function(x, y) {
lower48Stream.point(x, y);
alaskaStream.point(x, y);
hawaiiStream.point(x, y);
puertoRicoStream.point(x, y);
},
sphere: function() {
lower48Stream.sphere();
alaskaStream.sphere();
hawaiiStream.sphere();
puertoRicoStream.sphere();
},
lineStart: function() {
lower48Stream.lineStart();
alaskaStream.lineStart();
hawaiiStream.lineStart();
puertoRicoStream.lineStart();
},
lineEnd: function() {
lower48Stream.lineEnd();
alaskaStream.lineEnd();
hawaiiStream.lineEnd();
puertoRicoStream.lineEnd();
},
polygonStart: function() {
lower48Stream.polygonStart();
alaskaStream.polygonStart();
hawaiiStream.polygonStart();
puertoRicoStream.polygonStart();
},
polygonEnd: function() {
lower48Stream.polygonEnd();
alaskaStream.polygonEnd();
hawaiiStream.polygonEnd();
puertoRicoStream.polygonEnd();
}
};
};
albersUsa.precision = function(_) {
if (!arguments.length) return lower48.precision();
lower48.precision(_);
alaska.precision(_);
hawaii.precision(_);
puertoRico.precision(_);
return albersUsa;
};
albersUsa.scale = function(_) {
if (!arguments.length) return lower48.scale();
lower48.scale(_);
alaska.scale(_ * .35);
hawaii.scale(_);
puertoRico.scale(_);
return albersUsa.translate(lower48.translate());
};
albersUsa.translate = function(_) {
if (!arguments.length) return lower48.translate();
var k = lower48.scale(), x = +_[0], y = +_[1];
lower48Point = lower48
.translate(_)
.clipExtent([[x - .455 * k, y - .238 * k], [x + .455 * k, y + .238 * k]])
.stream(pointStream).point;
alaskaPoint = alaska
.translate([x - .307 * k, y + .201 * k])
.clipExtent([[x - .425 * k + ε, y + .120 * k + ε], [x - .214 * k - ε, y + .234 * k - ε]])
.stream(pointStream).point;
hawaiiPoint = hawaii
.translate([x - .205 * k, y + .212 * k])
.clipExtent([[x - .214 * k + ε, y + .166 * k + ε], [x - .115 * k - ε, y + .234 * k - ε]])
.stream(pointStream).point;
puertoRicoPoint = puertoRico
.translate([x + .350 * k, y + .224 * k])
.clipExtent([[x + .320 * k, y + .204 * k], [x + .380 * k, y + .234 * k]])
.stream(pointStream).point;
return albersUsa;
};
return albersUsa.scale(1070);
}
<!DOCTYPE html>
<meta charset="utf-8">
<style>
rect {
fill: none;
pointer-events: all;
}
.sphere {
fill: none;
stroke: #ccc;
shape-rendering: crispEdges;
}
.graticule {
fill: none;
stroke: #777;
stroke-opacity: .2;
}
.mesh {
fill: none;
stroke: #000;
}
</style>
<body>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/topojson.v2.min.js"></script>
<script src="albers-usa-pr.js"></script>
<script>
var width = 960,
height = 500;
var projection = albersUsaPr()
.scale(1070)
.translate([width / 2, height / 2]);
var path = d3.geoPath()
.projection(projection);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var graticule = d3.geoGraticule()
.step([2, 2]);
svg.append("rect")
.attr("width", width)
.attr("height", height);
svg.append("path")
.datum({type: "Sphere"})
.attr("class", "sphere")
.attr("d", path);
svg.append("path")
.datum(graticule)
.attr("class", "graticule")
.attr("d", path);
var k = projection.scale(), t = projection.translate();
svg.append('circle')
.attr('transform', 'translate(' + t + ')')
.attr('r', 5)
.attr('fill', 'red');
var vertical = svg.selectAll('line.vertical')
.data([ -.425, -.214, -.115, .32, .38])
.enter();
vertical
.append('line')
.classed('vertical', true)
.attr('y2', 500)
.attr('stroke', function(d){ return d>0 ? 'green' : 'red'})
.attr('transform', function(d) { return 'translate('+[ t[0] + d * k, 0 ]+')'; });
vertical.append('text')
.attr('transform', function(d) { return 'translate('+[ t[0] + d * k + 2, 14 ]+')'; })
.text(function(d) { return d; });
var horizontal = svg.selectAll('line.horizontal')
.data([ 0., .120, .166, .234, .204])
.enter();
horizontal
.append('line')
.classed('horizontal', true)
.attr('x2', 960)
.attr('stroke', function(d){ return d==.204 ? 'green' : 'red'})
.attr('transform', function(d) { return 'translate('+[ 0, t[1] + d * k ]+')'; });
horizontal.append('text')
.attr('transform', function(d) { return 'translate('+[ width-50, t[1] + d * k -2 ]+')'; })
.text(function(d) { return d; });
svg.selectAll('rect.bg')
.data( [ [[-.425,.120], [-.214,.234]], [[-.214,.166], [-.115,.234]], [[.32,.204], [.38,.234]], ] )
.enter()
.append('rect')
.attr('class', 'bg')
.attr('x', function(d) { return t[0] + d[0][0] * k; })
.attr('y', function(d) { return t[1] + d[0][1] * k; })
.attr('width', function(d) { return (d[1][0] - d[0][0]) * k; })
.attr('height', function(d) { return (d[1][1] - d[0][1]) * k; })
.style('fill', 'rgba(255,200,200,.3)')
d3.json("https://gist.githubusercontent.com/mbostock/4090846/raw/d534aba169207548a8a3d670c9c2cc719ff05c47/us.json", function(error, us) {
if (error) throw error;
svg.append("path")
.datum(topojson.mesh(us))
.attr("class", "mesh")
.attr("d", path);
});
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment