From 1fdfe1d58dd0af11cde1b2f527d8116273bd1c8b Mon Sep 17 00:00:00 2001 From: Jayesh Bhade <52350067+Jaybhade@users.noreply.github.com> Date: Fri, 7 Aug 2026 13:58:33 +0530 Subject: [PATCH] fix(square): return a bounding box that surrounds the input square() decided which side was longer by comparing great circle distances in kilometres, then grew the shorter side by a span measured in degrees. Away from the equator a degree of longitude is shorter on the ground than a degree of latitude, so the two disagree: a box wider than it is tall in degrees can still have the shorter horizontal edge in kilometres. In that case the else branch narrowed the box to the height in degrees, returning a square that cuts off the east and west of the input rather than one that contains it. Germany's bounding box [5.87, 47.27, 15.04, 55.06] came back as [6.56, 47.27, 14.35, 55.06], losing 1.38 degrees of longitude off the sides. 14.8% of 200,000 random boxes were clipped this way. Compare the spans in the same units they are grown in. The result is now always a square that surrounds the input, with a side equal to the longer input side. Behaviour at the equator, where the two measures agree, is unchanged. This is only about the containment guarantee the docs already state; it does not address #1727, which asks for a box that is square on the ground rather than in degrees. --- packages/turf-square/index.ts | 17 +++++++++-------- packages/turf-square/package.json | 4 +++- packages/turf-square/test.ts | 11 +++++++++++ pnpm-lock.yaml | 3 --- 4 files changed, 23 insertions(+), 12 deletions(-) diff --git a/packages/turf-square/index.ts b/packages/turf-square/index.ts index 7818261b92..e3d3765fc5 100644 --- a/packages/turf-square/index.ts +++ b/packages/turf-square/index.ts @@ -1,4 +1,3 @@ -import { distance } from "@turf/distance"; import { BBox } from "geojson"; /** @@ -21,22 +20,24 @@ function square(bbox: BBox): BBox { var east = bbox[2]; var north = bbox[3]; - var horizontalDistance = distance(bbox.slice(0, 2), [east, south]); - var verticalDistance = distance(bbox.slice(0, 2), [west, north]); - if (horizontalDistance >= verticalDistance) { + // Spans are compared in degrees, the same units they are grown in below, so + // that the result always surrounds the input. + var horizontalSpan = east - west; + var verticalSpan = north - south; + if (horizontalSpan >= verticalSpan) { var verticalMidpoint = (south + north) / 2; return [ west, - verticalMidpoint - (east - west) / 2, + verticalMidpoint - horizontalSpan / 2, east, - verticalMidpoint + (east - west) / 2, + verticalMidpoint + horizontalSpan / 2, ]; } else { var horizontalMidpoint = (west + east) / 2; return [ - horizontalMidpoint - (north - south) / 2, + horizontalMidpoint - verticalSpan / 2, south, - horizontalMidpoint + (north - south) / 2, + horizontalMidpoint + verticalSpan / 2, north, ]; } diff --git a/packages/turf-square/package.json b/packages/turf-square/package.json index 0f09088120..d2f1334bb9 100644 --- a/packages/turf-square/package.json +++ b/packages/turf-square/package.json @@ -3,6 +3,9 @@ "version": "7.4.0", "description": "Takes a bounding box and calculates the minimum square bounding box that would contain the input.", "author": "Turf Authors", + "contributors": [ + "Jayesh Bhade <@Jaybhade>" + ], "license": "MIT", "bugs": { "url": "https://github.com/Turfjs/turf/issues" @@ -49,7 +52,6 @@ "typescript": "catalog:" }, "dependencies": { - "@turf/distance": "workspace:*", "@turf/helpers": "workspace:*", "@types/geojson": "catalog:" }, diff --git a/packages/turf-square/test.ts b/packages/turf-square/test.ts index a83e6f927b..588c295947 100644 --- a/packages/turf-square/test.ts +++ b/packages/turf-square/test.ts @@ -13,3 +13,14 @@ test("square", function (t) { t.deepEqual(sq2, [0, -2.5, 10, 7.5]); t.end(); }); + +test("square -- surrounds the input away from the equator", function (t) { + // Wider than they are tall in degrees, but the great circle distance along + // their southern edge is the shorter of the two. + const bbox1: BBox = [0, 60, 10, 66]; + const bbox2: BBox = [100, -70, 112, -63]; + + t.deepEqual(square(bbox1), [0, 58, 10, 68]); + t.deepEqual(square(bbox2), [100, -72.5, 112, -60.5]); + t.end(); +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f69d9af216..3819f9c563 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5132,9 +5132,6 @@ importers: packages/turf-square: dependencies: - '@turf/distance': - specifier: workspace:* - version: link:../turf-distance '@turf/helpers': specifier: workspace:* version: link:../turf-helpers