Skip to content

fix(data): add a service's layers past the first 2000 by name, and say when a listing stops (#1519) - #1526

Draft
elpidioassinaturas wants to merge 4 commits into
opengeos:mainfrom
elpidioassinaturas:fix/1519-layers-past-2000
Draft

elpidioassinaturas wants to merge 4 commits into
opengeos:mainfrom
elpidioassinaturas:fix/1519-layers-past-2000

Conversation

@elpidioassinaturas

@elpidioassinaturas elpidioassinaturas commented Nov 6, 2026 •

Copy link
Copy Markdown
Contributor

Summary

service.add answered no layer for a layer the service offers when it came past position 2000 of the capabilities, and nothing said the list had been cut. IBGE's WFS lists 10,186 types and changes their order between answers, so the same command failed for some minutes and then worked. The issue has the samples: #1519.

The parsers now read past what service.layers shows, so service.add, service.coverage and identify find any layer of the document by name.

  • Reading: WFS, WMS, WMTS, WCS and OGC API read up to 100,000 layers or collections from one document (MAX_READ_LAYERS), and say how many they leave out past that.
  • Listing: service.layers still stops at 2000 entries, through a new Service::listed, and now adds a note: only the first 2000 of 10186 layers are listed; service.add takes any of them by name. The note appears in Add From Service and in the connections tree too. It replaces OGC API's own note.
  • Refusal: a name the document doesn't give no longer points to a list said to hold them all: no layer 'x' (service.layers lists the first 2000 of 10186 layers). Past the read limit, that limit's note is added, since a mistyped name can't be told from a layer past it.

Quadratic time in the WMS parser, also fixed. wms_native looked through the children of every ancestor for each layer, which means all its siblings when the layers share one parent. That was already visible on main: a document of 100,000 layers took 7.9 s though only 2000 were kept. Reading them all would have taken over 5 minutes, so each layer's boxes are now read once, by system (LayerBoxes). The per-layer work no longer depends on how many siblings or boxes a parent has.

Choices and limits

  • OGC API counts collections, not layers. A collection can make several layers, and ogc_discovery_preserves_all_offers_within_the_collection_limit keeps them all listed: listed is set at the 2001st collection.
  • Service groups are unchanged. incomplete is still set past 2000 collections, so a service group still refuses so long a list.
  • WCS identifiers. A coverage with an identifier over 500 bytes was skipped silently, and is now counted in a note.
  • ArcGIS differs from what [Bug]: service.add can't find a layer past the first 2000 in a service's capabilities, and the cut is silent (IBGE's WFS fails or works depending on the answer's order) #1519 proposed. A service or directory is still read up to 2000 layers, folders and services, because a MapServer's layer is offered twice and the two loops mix, so there's no single place to stop its list. It now says how many it leaves out, and a layer past that is refused with the reason. I don't know of an ArcGIS service that long.
  • The 100,000 is a bound against hostile documents, not a measured need. The largest real service I measured has 10,186 types.
  • The listing of a WMS past 2000 layers changes. Its parser reads from the end of the document, so it used to keep the last 2000. It now lists the first 2000, in document order.
  • A WMS's time values. MAX_DOCUMENT_TIME_VALUES (50,000 for the whole document) is spent in reading order, from the end of the document. A WMS with more than 2000 layers and more time values than that can now spend it on layers past the listing, leaving listed layers with fewer times. Untested, and no such service is known to me.
  • The desktop dialog still shows only the first 2000. The note says so. Listing them all with the dialog's word filter is a separate change.
  • Service::listed with several documents: only OGC API follows documents, and only from a landing page, which has no collections of its own (collections_url is set only there). So a document with collections always ends the chain, and it is the only one that sets listed: the listing is its 2000 collections' layers plus the few of the landing page before it.
  • The WMS count of what's left out includes named layers past the limit that would have been skipped anyway for having no usable coordinate system. Telling them apart would mean reading them.

Testing

Measured with service.add of a missing layer, given the document as text, so it reads everything and fetches nothing. v0.5.0 is a release build and this branch a dev build, so the times aren't strictly comparable; take them as upper bounds for the branch.

Document v0.5.0 This branch
IBGE's WFS (12 MB, 10,186 types) 159 ms, 110 MB peak 236 ms, 120 MB peak
WFS, 100,000 types (6.5 MB) 93 ms, 86 MB 543 ms, 177 MB
WMS, 100,000 layers under one parent (9.3 MB) 7,853 ms, 143 MB 1,233 ms, 230 MB
WMS, 50,000 boxes and 50,000 layers on one parent 28.8 s 0.6 s
OGC API, 100,000 collections (7.9 MB) 221 ms, 212 MB 974 ms, 279 MB

WMS by number of layers on this branch: 16,000 in 195 ms, 100,000 in 1,233 ms. Before the fix, 16,000 took 2,776 ms.

With a real answer from IBGE's service, moving only BDIA:pedo_area: positions 1, 2000, 2001 and 10181 all give 9 features (service.add, over the network). On v0.5.0, 2001 and 10181 gave no layer.

New tests:

  • a_document_past_the_read_limit_says_how_much_it_left_out: each of the nine kinds of document at its limit (no note) and past it (a note with the count).
  • collections_past_the_listing_are_read_and_marked_incomplete.
  • a_layer_past_the_listing_is_added_by_name: 2000 types with no note, 2001 with the note, the last one added from a local server, and a service group refusing 2001 collections.
  • a_layer_past_the_read_limit_is_refused_saying_so: WFS, WCS, ArcGIS and two WMS documents, one of 100,001 layers under one parent and one of 50,000 boxes and 50,000 layers. The WMS parts take minutes if the quadratic time returns.
  • a_coverage_with_a_long_identifier_is_said_to_be_left_out.

cargo fmt, cargo clippy and cargo xtask layers pass. cargo test --workspace --no-fail-fast with RUSTGIS_LANGUAGE=en on Windows: 4,237 passed, 1 failed, 20 ignored. The failure is mbtiles_archives_frame_the_map_on_their_bounds, which passes alone (3 of 3) and is flaky under load on this PC on main too; pmtiles_archives_frame_the_map_on_their_bounds, an_oversized_ogc_items_binding_is_refused_before_native_fetch and a_reopened_http_raster_recovers_its_pixels_after_login_without_reloading_unchanged_paths failed the same way in some runs of this branch; all are known to fail on main on this PC. That full run was before the review rounds; since then, the ogc:: tests, the engine's service tests and clippy on the two crates. service_logins fails intermittently in parallel here and passes 3 of 3 with --test-threads=1. The branch is on main at 3225fd6, not the latest; none of the four changed files differ there.

UI evidence

One visible change, new text only: when a list is cut, a note appears in the existing notes area of Add From Service and the connections tree. The rest of the dialog is as before. Screenshot of IBGE's WFS, after:

Add From Service on IBGE's WFS, with the note under the service's title

Related issue

Closes #1519.

🤖 Generated with Claude Code

…y when a listing stops (opengeos#1519)

service.add answered "no layer" for a layer past position 2000 of the
capabilities, and service.layers said nothing about the cut. IBGE's WFS
lists 10,186 types and changes their order between answers, so the same
command failed for some minutes and then worked.

The WFS, WMS, WMTS, WCS and OGC API parsers now read up to 100,000 layers
or collections from one document, and say how many they leave out past
that. service.layers still stops at 2000 entries (Service::listed), now
with a note. ArcGIS stays read up to 2000, with a note.

wms_native looked through every ancestor's children for each layer, which
is quadratic when layers share a parent; each layer's boxes are now read
once.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Nov 6, 2026

Copy link
Copy Markdown
Contributor

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

Comment thread crates/engine/src/cmd/service.rs Resolved
Comment thread crates/engine/src/cmd/service.rs Resolved
Comment thread crates/engine/src/tests.rs Resolved
@github-actions

github-actions Bot commented Nov 6, 2026

Copy link
Copy Markdown
Contributor

Code review

Bugs

  • The merged listed count in service.rs:175 is overwritten by each chained OGC API document. Two documents with 1,500 and 2,001 collections give a listing of 3,500 entries and a note "only the first 3500 of 3501". The listing cap is 2,000, so this breaks the stated contract. Confidence: high (~85%). Posted inline.
  • service.add in service.rs:469 (and coverage() just above it) attributes a read-limit note to any missing name. A typo on a long service reports that the layer may be among the left-out ones, and the (service.layers lists them) hint is dropped. Confidence: ~70%, low severity. Posted inline.
  • The WMS left-out count (ogc.rs, the named(...) sum) includes named layers that would be skipped anyway for having no usable CRS, so the note can overstate the count. Confidence: ~50%, low severity. Not posted.

Security

  • No issues found. No new unwrap, expect, unsafe or indexing in non-test code. Limits are bounded (MAX_READ_LAYERS), and the WMS quadratic walk is replaced by a per-ancestor cache.

Performance

  • No regressions found. The LayerBoxes cache makes each layer O(depth) instead of O(siblings). The named(...) count runs once, at the read limit.

Quality

AGENTS.md

  • Silent-loss rule: every read-limit path (WFS, WMS, WMTS, WCS, ArcGIS, OGC API) pushes a note with counts, and tests cover both sides. Looks compliant.
  • Layering, wasm and i18n: the new data-crate notes use format!, matching the existing notes in that file. No new platform or dependency concerns in the diff.
  • I could not compile or run the tests here. Compile-level claims (the LayerBoxes lifetimes, roxmltree::NodeId) are from reading only.

Already raised in earlier threads: none (prior-reviews.json was empty), so no concerns were left out as duplicates.

Change size

Compared: the whole PR against main (merge base 3225fd6 to PR head b3d349f).

Measure Value
Files changed 4 (0 added, 4 modified, 0 removed, 0 renamed)
Size before 2.13 MB
Size after 2.14 MB
Total size change +14.3 KB
Path Before After Change
crates/data/src/ogc.rs 356.2 KB 364.9 KB +8.9 KB
crates/engine/src/tests.rs 1.67 MB 1.68 MB +4.6 KB
crates/engine/src/cmd/service.rs 43.9 KB 44.6 KB +774 B
docs/user-guide/data-formats.md 66.4 KB 66.8 KB +416 B

@github-actions

github-actions Bot commented Nov 6, 2026 •

Copy link
Copy Markdown
Contributor

📖 Docs preview

Item Value
Preview https://preview.rustgis.app/pr-1526/
Web app https://preview.rustgis.app/pr-1526/app/
Commit 141242a

GitHub Pages takes a minute or two to publish a new commit.

…geos#1519)

A name the document doesn't give keeps "(service.layers lists them)", with
the read limit's note beside it: a mistyped name can't be told from one past
the limit. The note now reads right for any count, and the new tests no
longer split the opengeos#1053 test from its doc comment.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Comment thread crates/engine/src/cmd/service.rs Resolved
Comment thread crates/data/src/ogc.rs Resolved
Comment thread docs/user-guide/data-formats.md Resolved
@github-actions

github-actions Bot commented Nov 6, 2026

Copy link
Copy Markdown
Contributor

Code review

Bugs

  • The "service.layers lists them" hint in not_listed is false for layers past the 2000 listing. With no read-limit note, a typo on a long service sends the user to a listing that doesn't contain the layer. With the note, it contradicts itself. Confidence ~70%. Inline comment posted at crates/engine/src/cmd/service.rs:449.
  • The WMS read-limit note can read "0 left out" when the remaining stack holds only unnamed layers. Confidence ~45%. Inline comment at crates/data/src/ogc.rs:937. The same comment notes that the WMS listing's "first 2000" is the first 2000 of the last 100,000 layers when the document is past the read limit.

Security

  • No new issues found. The new code has no unwrap/expect/indexing, and the XML path keeps its nodes_limit.

Performance

  • Peak memory rises with the 100,000 read limit: about 177 MB for a 6.5 MB WFS and 279 MB for a 7.9 MB OGC API document (the PR's own measurements). The amplification matters for the wasm build. Confidence ~35%. Not a defect, and I have no change to suggest.

Quality

  • Docs (docs/user-guide/data-formats.md:265): "up to 100,000 in one document" doesn't hold for ArcGIS, which is capped at 2,000. Confidence ~80%. Inline comment posted.
  • MAX_READ_LAYERS is 3,000 under cfg!(test) in the data crate, so the data crate's tests never exercise the production limit. The engine tests do cover 100,000. Nit.
  • The listing can show 2,001 entries when a landing page contributes its one map layer. The PR body documents this, so it's a wording nit at most.

AGENTS.md

  • The PR body says "No visible UI change, except a note…", but the note appears in Add From Service and the connections tree. AGENTS.md asks for screenshots for any visible change (before and after for changed UI). None are attached. Confidence ~85%. This applies to the PR description, not a line.
  • Note strings are built with format! and aren't translated. That matches the existing notes in ogc.rs, so I'm not raising it.

Prior review threads

  • I left 2 earlier concerns alone. (1) The merged-listed overflow: the author's reply holds. Only the landing branch sets collections_url, and that branch contributes at most one layer. (2) The misplaced doc comment: fixed in the diff.
  • I re-raised the not_listed hint (the third thread) because the fix introduced a new problem with the wording.

Checked and found fine

  • The WMS sibling-quadratic fix (LayerBoxes) is correct.
  • The layers() truncation keeps sources aligned.
  • capabilities() offsets listed correctly.
  • The service-group path is unchanged (OGC API only, refuses past 2000 collections).
  • The UI goes through the service.layers command, so it sees the same cut and note.

Change size

Compared with main (merge base 3225fd6 to PR head 2b542aa), the whole PR.

Measure Value
Files changed 4 (0 added, 4 modified, 0 removed, 0 renamed)
Size before 2.13 MB
Size after 2.14 MB
Total size change +14.9 KB
Path Before After Change
crates/data/src/ogc.rs 356.2 KB 365.1 KB +8.9 KB
crates/engine/src/tests.rs 1.67 MB 1.68 MB +4.5 KB
crates/engine/src/cmd/service.rs 43.9 KB 44.8 KB +935 B
docs/user-guide/data-formats.md 66.4 KB 66.8 KB +406 B

opengeos#1519)

On a service whose list is cut, the error for a name the document doesn't
give now says how much service.layers shows. A WMS past the read limit with
only unnamed layers left no longer notes "0 left out". The guide names
ArcGIS as the exception to the 100,000.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Comment thread crates/data/src/ogc.rs Resolved
Comment thread crates/engine/src/cmd/service.rs Resolved
Comment thread crates/data/src/ogc.rs Resolved
@github-actions

github-actions Bot commented Nov 6, 2026

Copy link
Copy Markdown
Contributor

Code review

Bugs

  • Miscounted total in the listing note, crates/engine/src/cmd/service.rs:430 (also not_listed, line 450). Past the read limit, "only the first 2000 of 100000 layers" is short by the number left out (a 100,001-type WFS reads as 100000). The test pins this wording. Confidence ~70%. Inline comment posted.
  • WMS boxes still quadratic in a hostile case, crates/data/src/ogc.rs:1139. Each layer walks every cached BoundingBox of its ancestors, so one parent with many boxes is still slow. MAX_READ_LAYERS bounds layers, not boxes. Confidence ~40%. Inline comment posted.

Security

  • None found. New input paths are bounded with saturating_sub, get() and take(). No unwrap or expect in non-test code.

Performance

  • The WMS sibling-scan fix is sound: LayerBoxes caches per ancestor, and left counts only unprocessed entries. The remaining hostile-input cost is the point above.

Quality

  • The listing is described as stopping at "2000 entries", but OGC API cuts at 2000 collections, so a tiled collection can list up to 4000 layers. The note text is accurate; the PR body and data-formats.md wording should say "collections" in the listing bullet. Minor nit, left for the release quality pass.

AGENTS.md

  • Silent drop in touched code, crates/data/src/ogc.rs:1820 (and :1834). WCS coverages with ids over 500 characters are skipped with no note, although this function now reports read-limit losses. This breaks "Never silent". Confidence ~65%. Inline comment posted. The WFS, WMTS and ArcGIS missing-name skips predate this PR.
  • Missing screenshot. The PR body says the change is visible and the UI note appears in Add From Service and the connections tree, but the screenshot is "to be attached". AGENTS.md requires an after screenshot for new visible UI. Confidence high.

Already raised, left alone: the merged listed count across documents (answered; I agree the chain ends at one listed document), the WMS note saying 0 left out (fixed in f0df8df), the WMS listing reading from the end (acknowledged), the misplaced doc comment (fixed), the not_listed hints (fixed), and the ArcGIS docs exception (fixed). That is 5 earlier concerns left alone, none reopened.

Not verified: I could not compile or run tests here (no shell). I checked by reading that roxmltree 0.21 NodeId is usable as a HashMap key and that vet() keeps listed.

Change size

Compared: the whole PR against main (merge base 3225fd6 to PR head f0df8df).

Measure Value
Files changed 4 (0 added, 4 modified, 0 removed, 0 renamed)
Size before 2.13 MB
Size after 2.15 MB
Total size change +16.9 KB
Path Before After Change
crates/data/src/ogc.rs 356.2 KB 365.8 KB +9.6 KB
crates/engine/src/tests.rs 1.67 MB 1.68 MB +5.5 KB
crates/engine/src/cmd/service.rs 43.9 KB 45.1 KB +1.3 KB
docs/user-guide/data-formats.md 66.4 KB 66.9 KB +447 B

@elpidioassinaturas

Copy link
Copy Markdown
Contributor Author
image

…CS identifiers (opengeos#1519)

Each WMS layer still went through every box of its ancestors, so a parent with
many boxes took quadratic time, and reading past 2000 layers made that worse:
50,000 boxes and 50,000 layers took 28.8 s on v0.5.0, over 2 minutes at 20,000
on this branch, and take 0.6 s now.

A coverage with an identifier over 500 bytes was skipped silently and is now
counted in a note. Past the read limit, the listing counts "the 100000 layers
read", not the document's layers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Comment thread crates/data/src/ogc.rs Resolved
Comment thread crates/engine/src/cmd/service.rs Resolved
Comment thread crates/data/src/ogc.rs Resolved
@github-actions

github-actions Bot commented Nov 7, 2026

Copy link
Copy Markdown
Contributor

Code review

I found no correctness bugs, crash paths, silent data loss, or security issues in the changed code. I checked the merge in capabilities(), the WMS layer-box cache, the read-limit notes, and the layers() truncation. I also confirmed the prior answer that collections_url is set only on the landing-page branch, so the merge sees at most one listed value.

Bugs: None found.

Security: None found.

Performance

  • Raising the read bound from 2,000 to 100,000 multiplies per-document work and memory by about 50×. The PR's own numbers show about 280 MB peak for an 8 MB OGC API document on desktop. The web build is not measured. Confidence ~35%, low severity. Not commented inline.

Quality

  • Listing past 2,000 layers: a collection can make up to three layers, so an OGC API listing can exceed 2,000 layers, and the docs don't say so. Confidence ~90%, low. Inline at crates/data/src/ogc.rs:2515.
  • Read-limit state is inferred by matching note text (contains(PAST_READ_LIMIT)), and layer_count returns a phrase rather than a count. Confidence ~80%, low. Inline at crates/engine/src/cmd/service.rs:445.
  • The WFS and WMTS "left out" counts include unnamed entries that would be skipped anyway, so the number can overstate. Confidence ~50%, low. Inline at crates/data/src/ogc.rs:1629.
  • Grouped nits: [("folders","folders"),("services","services")] at ogc.rs:2458 is a redundant tuple. The data-crate tests run with MAX_READ_LAYERS at 3,000 while the engine tests run at 100,000, so the two suites exercise different limits.

AGENTS.md: No violations found. The new engine strings follow the existing untranslated engine notes. The WCS long-identifier and WMS unnamed-layer cases are tested both ways.

Already raised: 9 earlier threads on this PR. All are resolved and I left them alone, because the code they point at matches the replies. The one about the merged listed count still holds on the evidence above.

Correction: My inline comment at service.rs:445 contains a garbled phrase ("The 'a'-style fix"). I can't edit a posted comment, so please read it as "a field on Service" and ignore that phrase.

Change size

Compared with main (merge base 3225fd6 to PR head 141242a).

Measure Value
Files changed 4 (0 added, 4 modified, 0 removed, 0 renamed)
Size before 2.13 MB
Size after 2.15 MB
Total size change +19.4 KB
Path Before After Change
crates/data/src/ogc.rs 356.2 KB 367.3 KB +11.1 KB
crates/engine/src/tests.rs 1715.3 KB 1721.6 KB +6.2 KB
crates/engine/src/cmd/service.rs 43.9 KB 45.5 KB +1.6 KB
docs/user-guide/data-formats.md 66.4 KB 66.9 KB +0.4 KB

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants