forked from Albers USA with Puerto Rico by @Harry_Stevens
Code style changes to improve legibility.
Create a resizable map with an Albers USA projection that includes Puerto Rico. Adapted from this block but compatiable with D3 v4 & v5 and including a fitSize() function to help with resizing.
const feature = topojson.feature(states, states.objects.states_20m_2017)
const projection = d3.geoAlbersUsaPr()
const path = d3.geoPath().projection(projection)
const container = d3.select('body')
const aspect_ratio = 0.582
let width
let height
const svg = container.append('svg')
const paths_states = svg
.selectAll('.state')
.data(feature.features)
.enter()
.append('path')
.attr('class', 'state')
draw()
window.addEventListener('resize', draw)
function draw() {
width = container.node().getBoundingClientRect().width
height =
width * aspect_ratio > window.innerHeight
? window.innerHeight
: width * aspect_ratio
svg.attr('width', width).attr('height', height)
fitSize([width, height], feature)
paths_states.attr('d', path)
}
function fitSize(size, object) {
const width = size[0]
const height = size[1]
projection.scale(1).translate([0, 0])
const b = path.bounds(object)
const s =
1 / Math.max((b[1][0] - b[0][0]) / width, (b[1][1] - b[0][1]) / height)
const t = [
(width - s * (b[1][0] + b[0][0])) / 2,
(height - s * (b[1][1] + b[0][1])) / 2,
]
projection.scale(s).translate(t)
}
<!DOCTYPE html>
<html>
<head>
<style>
body {
margin: 0;
}
.state {
fill: #ccc;
stroke: #fff;
}
</style>
</head>
<body>
<script src="https://d3js.org/d3-selection.v1.min.js"></script>
<script src="https://d3js.org/d3-array.v1.min.js"></script>
<script src="https://d3js.org/d3-geo.v1.min.js"></script>
<script src="https://unpkg.com/topojson@3.0.2/dist/topojson.min.js"></script>
<script src="albers-usa-pr.js"></script>
<script src="states.js"></script>
<script src="index.js"></script>
</body>
</html>
d3.geoAlbersUsaPr = function () {
var epsilon = 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 >= 0.12 && y < 0.234 && x >= -0.425 && x < -0.214
? alaska
: y >= 0.166 && y < 0.234 && x >= -0.214 && x < -0.115
? hawaii
: y >= 0.204 && y < 0.234 && x >= 0.32 && x < 0.38
? 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(_ * 0.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 - 0.455 * k, y - 0.238 * k],
[x + 0.455 * k, y + 0.238 * k],
])
.stream(pointStream).point;
alaskaPoint = alaska
.translate([x - 0.307 * k, y + 0.201 * k])
.clipExtent([
[x - 0.425 * k + epsilon, y + 0.12 * k + epsilon],
[x - 0.214 * k - epsilon, y + 0.234 * k - epsilon],
])
.stream(pointStream).point;
hawaiiPoint = hawaii
.translate([x - 0.205 * k, y + 0.212 * k])
.clipExtent([
[x - 0.214 * k + epsilon, y + 0.166 * k + epsilon],
[x - 0.115 * k - epsilon, y + 0.234 * k - epsilon],
])
.stream(pointStream).point;
puertoRicoPoint = puertoRico
.translate([x + 0.35 * k, y + 0.224 * k])
.clipExtent([
[x + 0.32 * k, y + 0.204 * k],
[x + 0.38 * k, y + 0.234 * k],
])
.stream(pointStream).point;
return albersUsa;
};
return albersUsa.scale(1070);
};
module.exports = {
semi: false,
singleQuote: true,
};