Skip to content

Adhere to geo_span setting from tenant if one is defined#1492

Merged
jeslefcourt merged 5 commits intodevelopfrom
ERA-12791
Mar 26, 2026
Merged

Adhere to geo_span setting from tenant if one is defined#1492
jeslefcourt merged 5 commits intodevelopfrom
ERA-12791

Conversation

@jeslefcourt
Copy link
Contributor

@jeslefcourt jeslefcourt commented Mar 23, 2026

What does this PR do?

  • Reduces client and server overload by allowing the definition of a bounding box for an EarthRanger instance. Outside of these bounds, the client won't try to load data from the server. Currently this just works on features, but it can be easily used for events and subjects once those move to vector tiles.

Note for developers of the other vector tiles: See the way geoSpanFilter is defined and used around line 100 in src/SpatialFeaturesLayer/index.js

The bounds are defined as part of a tenant's envSettings in the TMS in the form:

    "geoSpan": {
        "lat": [-40.5, 40.5],
        "lon": [-180.0, 180.0]
    }

One known oddity of the behavior is that it won't load features that intersect, but aren't entirely contained in, the bounding box. For example, if you define a country as a feature and the bounding box being within that country but around the protected area, once you zoom in enough that you no longer see the edges of the country (+/-), the rest of the country or any formatting of the country will disappear. This can't be fixed with a reasonable amount of work, so we'll accept this and give guidance to make the bounding box big enough to encompass all features you care about.

Relevant link(s)

Where / how to start reviewing (optional)

  • Play around with intersecting views with polygons, lines and points: Overlapping, intersecting, non-intersecting

This comment was marked as outdated.

jeslefcourt and others added 2 commits March 26, 2026 13:23
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.

Comment on lines +216 to +237
export const buildGeoSpanFilter = (geoSpan) => {
if (!geoSpan) return null;

const { lon, lat } = geoSpan;

if (!Array.isArray(lon) || !Array.isArray(lat) || lon.length < 2 || lat.length < 2) {
return null;
}

const [minLon, maxLon] = lon;
const [minLat, maxLat] = lat;

if (
!Number.isFinite(minLon) ||
!Number.isFinite(maxLon) ||
!Number.isFinite(minLat) ||
!Number.isFinite(maxLat)
) {
return null;
}
return [minLon, minLat, maxLon, maxLat];
};
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

buildGeoSpanFilter is new behavior in src/utils/map.js, but there are no unit tests covering valid/invalid geoSpan inputs (missing keys, wrong types, inverted ranges, etc.). Since this file already has a dedicated src/utils/map.test.js, adding focused tests there would help prevent regressions and clarify expected input handling.

Copilot generated this review using guidance from repository custom instructions.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can add a new describe block in src/utils/map.test.js and ask an agent to add some coverage to this new method:

describe('buildGeoSpanFilter', () => {
  // TODO: Claude, add unit tests for buildGeoSpanFilter here.
});

Comment on lines 98 to 108
useEffect(() => {
if (!map) return;

if (!map.getSource(SPATIAL_FEATURES_SOURCE)) {
const geoSpanFilter = buildGeoSpanFilter(geoSpan);
map.addSource(SPATIAL_FEATURES_SOURCE, {
type: 'vector',
tiles: [VECTOR_TILE_URL],
minzoom: 0,
maxzoom: 22,
...(geoSpanFilter && { bounds: geoSpanFilter }),
transformRequest: (url, resourceType) => {
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new geo-span behavior (adding bounds to the vector source when tenant config is present) isn’t covered by the existing SpatialFeaturesLayer tests. It would be good to add a test case where useSelector returns a non-null geoSpan and buildGeoSpanFilter returns a bbox, then assert map.addSource is called with a bounds field so this feature doesn’t silently regress.

Copilot generated this review using guidance from repository custom instructions.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copy link
Contributor

@luixlive luixlive left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great idea 💯 A simple way to avoid unnecessary network calls.

I added one non-blocking suggestion, but the code seems good.



export const buildGeoSpanFilter = (geoSpan) => {
if (!geoSpan) return null;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The validation steps in this method take more lines than its actual logic. We have a couple handy utilities isValidLatitude and isValidLongitude in src/utils/location/index.js that you can export / import and simplify these validations a bit:

import { isValidLatitude, isValidLongitude } from 'utils/location;

const areLatitudeValuesValid = Array.isArray(geoSpan?.lat)
  && geoSpan.lat.length === 2
  && geoSpan.lat.every((latitude) => isValidLatitude(latitude));
const areLongitudeValuesValid = Array.isArray(geoSpan?.lng)
  && geoSpan.lng.length === 2
  && geoSpan.lng.every((longitude) => isValidLongitude(longitude));

if (!areLatitudeValuesValid || !areLongitudeValuesValid) {
  return null;
}
...

Comment on lines +216 to +237
export const buildGeoSpanFilter = (geoSpan) => {
if (!geoSpan) return null;

const { lon, lat } = geoSpan;

if (!Array.isArray(lon) || !Array.isArray(lat) || lon.length < 2 || lat.length < 2) {
return null;
}

const [minLon, maxLon] = lon;
const [minLat, maxLat] = lat;

if (
!Number.isFinite(minLon) ||
!Number.isFinite(maxLon) ||
!Number.isFinite(minLat) ||
!Number.isFinite(maxLat)
) {
return null;
}
return [minLon, minLat, maxLon, maxLat];
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can add a new describe block in src/utils/map.test.js and ask an agent to add some coverage to this new method:

describe('buildGeoSpanFilter', () => {
  // TODO: Claude, add unit tests for buildGeoSpanFilter here.
});

@jeslefcourt jeslefcourt merged commit bdce5aa into develop Mar 26, 2026
4 checks passed
@jeslefcourt jeslefcourt deleted the ERA-12791 branch March 26, 2026 21:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants