Skip to content

Commit

Permalink
Determine the default height when the projection domain is set (#2102)
Browse files Browse the repository at this point in the history
* Determine the default height when the projection domain is set

closes #2063

* Only scale if passed a width (height being always passed with width, there's no need to check both)
the case where width and height are undefined is when we are testing the projection's aspect ratio with a given domain

Note that the path in the SVG is broken (full of NaN) when projection.scale() === 0. This seems to be a secondary issue.

* adding the case where the projection is given by a {type: function, domain}
  • Loading branch information
Fil authored Jul 28, 2024
1 parent 53d757e commit 2f7a2c7
Show file tree
Hide file tree
Showing 16 changed files with 705 additions and 511 deletions.
3 changes: 2 additions & 1 deletion src/dimensions.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,8 @@ function autoHeight(
) {
const nfy = fy ? fy.scale.domain().length : 1;

// If a projection is specified, use its natural aspect ratio (if known).
// If a projection is specified, compute an aspect ratio based on the domain,
// defaulting to the projection’s natural aspect ratio (if known).
const ar = projectionAspectRatio(projection);
if (ar) {
const nfx = fx ? fx.scale.domain().length : 1;
Expand Down
33 changes: 22 additions & 11 deletions src/projection.js
Original file line number Diff line number Diff line change
Expand Up @@ -167,8 +167,10 @@ function scaleProjection(createProjection, kx, ky) {
if (precision != null) projection.precision?.(precision);
if (rotate != null) projection.rotate?.(rotate);
if (typeof clip === "number") projection.clipAngle?.(clip);
projection.scale(Math.min(width / kx, height / ky));
projection.translate([width / 2, height / 2]);
if (width != null) {
projection.scale(Math.min(width / kx, height / ky));
projection.translate([width / 2, height / 2]);
}
return projection;
},
aspectRatio: ky / kx
Expand All @@ -183,7 +185,7 @@ function conicProjection(createProjection, kx, ky) {
const projection = type(options);
if (parallels != null) {
projection.parallels(parallels);
if (domain === undefined) {
if (domain === undefined && width != null) {
projection.fitSize([width, height], {type: "Sphere"});
}
}
Expand Down Expand Up @@ -234,16 +236,25 @@ export function hasProjection({projection} = {}) {
return projection != null;
}

// When a named projection is specified, we can use its natural aspect ratio to
// determine a good value for the projection’s height based on the desired
// width. When we don’t have a way to know, the golden ratio is our best guess.
// Due to a circular dependency (we need to know the height before we can
// construct the projection), we have to test the raw projection option rather
// than the materialized projection; therefore we must be extremely careful that
// the logic of this function exactly matches createProjection above!
// When a projection is specified, we can use its aspect ratio to determine a
// good value for the projection’s height based on the desired width. When we
// don’t have a way to know, the golden ratio is our best guess. Due to a
// circular dependency (we need to know the height before we can construct the
// projection), we have to test the raw projection option rather than the
// materialized projection; therefore we must be extremely careful that the
// logic of this function exactly matches createProjection above!
export function projectionAspectRatio(projection) {
if (typeof projection?.stream === "function") return defaultAspectRatio;
if (isObject(projection)) projection = projection.type;
if (isObject(projection)) {
let domain, options;
({domain, type: projection, ...options} = projection);
if (domain != null && projection != null) {
const type = typeof projection === "string" ? namedProjection(projection).type : projection;
const [[x0, y0], [x1, y1]] = geoPath(type({...options, width: 100, height: 100})).bounds(domain);
const r = (y1 - y0) / (x1 - x0);
return r && isFinite(r) ? (r < 0.2 ? 0.2 : r > 5 ? 5 : r) : defaultAspectRatio;
}
}
if (projection == null) return;
if (typeof projection !== "function") {
const {aspectRatio} = namedProjection(projection);
Expand Down
134 changes: 67 additions & 67 deletions test/output/geoText.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
208 changes: 104 additions & 104 deletions test/output/geoTip.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
208 changes: 104 additions & 104 deletions test/output/geoTipCentroid.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
208 changes: 104 additions & 104 deletions test/output/geoTipGeoCentroid.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
208 changes: 104 additions & 104 deletions test/output/geoTipXY.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
22 changes: 22 additions & 0 deletions test/output/projectionDomainRatioME.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 2f7a2c7

Please sign in to comment.