Skip to content

Conversation

@chrisgervang
Copy link
Collaborator

@chrisgervang chrisgervang commented Dec 3, 2025

Summary

This PR adds a comprehensive basemap-browser example application for testing deck.gl with multiple basemap providers across different frameworks and rendering modes.

Screenshot 2025-12-02 at 6 13 37 PM

Features

  • Multiple basemap providers: Google Maps, Mapbox, MapLibre, and MapLibre Globe
  • Framework coverage: Both Pure JS and React implementations for each provider
  • Interleaved mode testing: Toggle between shared GL context (interleaved) and separate contexts (overlaid)
  • Real-time metrics panel:
    • Device Pixel Ratio monitoring
    • Canvas dimensions (width x height)
    • Client size
    • WebGL drawing buffer size
  • Isolation: Pure JS implementations mount directly to DOM. Multiple React roots pattern for separation.

Use Case

This application makes it easy to test the resize/DPR bug fix across all 16+ test scenarios:

  • 3 map providers × 2 frameworks × 2 interleaved modes
  • Plus globe variants for Maplibre with proper projection ordering

Architecture Highlights

  • Two separate React roots: Control panel and map area are independent
  • Pure JS isolation: Pure JS code mounts directly via .mount() functions, no React in call stack
  • React components: Separate components in examples-react/ directory
  • Clean state management: Proper cleanup when switching between examples

Technical Details

  • Label rendering: Uses beforeId: 'watername_ocean' for MapLibre and slot: 'middle' for Mapbox
  • Continuous polling: Updates canvas metrics every 100ms for real-time feedback

Note

Introduces a self-contained example app to quickly exercise deck.gl against multiple basemaps, frameworks, and rendering modes.

  • New examples/basemap-browser app (Vite) with React control panel and separate map root
  • Selectable examples covering Google Maps, Mapbox, MapLibre, and MapLibre Globe; both React and Pure JS
  • Interleaved mode toggle; live DPR, canvas client/size, and WebGL drawing buffer metrics
  • Shared constants, layers (airport + arcs) with interleaved positioning (slot for Mapbox, beforeId for MapLibre)
  • MapLibre globe handled by setting projection before overlay; clean mount/unmount when switching examples
  • Provider-specific setups: env var checks for Google Maps and Mapbox tokens

Written by Cursor Bugbot for commit c45a863. This will update automatically on new commits. Configure here.

- Comprehensive test application for deck.gl with multiple basemap providers
- Supports Google Maps, Mapbox, MapLibre, and MapLibre Globe
- Both Pure JS and React implementations for complete coverage
- Interleaved mode toggle for testing shared vs separate GL contexts
- Real-time metrics panel showing DPR, canvas dimensions, and drawing buffer
- Complete isolation between Pure JS and React implementations
- Fixed globe projection in interleaved mode (set projection before overlay)
- Labels render above deck.gl layers in interleaved mode using beforeId/slot
- TypeScript throughout with full type safety
- Covers all 16 test scenarios from resize/DPR bug fix
- Pure JS code now mounts directly to DOM with no React wrapper
- Control panel is separate React root that calls callback function
- Map area is either Pure JS mount OR separate React root
- Complete isolation between Pure JS and React implementations
- Removed app.tsx, map-container.tsx, pure-js-container.tsx
- Created control-panel.tsx standalone component
- Created examples-react/ directory with separate components:
  - google-maps-component.tsx
  - mapbox-component.tsx
  - maplibre-component.tsx
- Updated index.html with side-by-side layout (#controls and #map divs)
- index.tsx orchestrates two separate React roots
- No more React wrapping Pure JS code - true isolation achieved
- Defer cleanup and remounting to next tick using setTimeout
- Prevents synchronous unmount during React render phase
- Fixes 'Attempted to synchronously unmount a root' warning
- Fixes 'Failed to execute removeChild' error
- Extracted mountExample function for cleaner separation
@coveralls
Copy link

coveralls commented Dec 3, 2025

Coverage Status

coverage: 91.115%. remained the same
when pulling c45a863 on basemap-browser
into 13ac1a8 on master.

Copy link
Collaborator

@felixpalmer felixpalmer left a comment

Choose a reason for hiding this comment

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

Nice! Given that we have a screenshot widget now, would it be possible to somehow run through all the variants and display a set of thumbnails as a sort of smoke test? Alternatively, could we somehow include these in our automated tests?

@chrisgervang
Copy link
Collaborator Author

display a set of thumbnails as a sort of smoke test?

I think this is sort of possible!

We could add the line of code from the screenshot widget that captures the canvas, and collect screenshots. Similar to a render test it'd go one by one through the examples to collect the results. This would really only be applicable to interleaved renders since those can capture both basemap and deck.

Additionally, we could add a maplibre-based render test for the globe and Mercator variants (the others would require API keys to run the tests, which doesn't sound great)

@chrisgervang
Copy link
Collaborator Author

chrisgervang commented Jan 12, 2026

I gave the thumbnails idea a try and ran into issues where the images were empty unless I was actively interacting with the maps. I think something is clearing the buffers, but I'm not sure what since I've tried all of the usual suspects.

I'm going to merge this in for now - happy to share what I tried if you're curious and want to give it a shot

@chrisgervang
Copy link
Collaborator Author

@copilot I left a series of comments on this PR I'd like you to address

Copy link
Contributor

Copilot AI commented Jan 12, 2026

@chrisgervang I've opened a new pull request, #9934, to work on those changes. Once the pull request is ready, I'll request review from you.

cursor[bot]

This comment was marked as outdated.

chrisgervang and others added 3 commits January 20, 2026 09:20
Fixed logic that incorrectly applied MapLibre-specific `beforeId` prop
to Google Maps layers. Google Maps doesn't support layer positioning
props like `beforeId` or `slot` in interleaved mode.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Changed cleanup function to use overlay.finalize() instead of
overlay.setMap(null) for proper cleanup of the GoogleMapsOverlay.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Added cancellation mechanisms to prevent async callbacks from running
after cleanup:

- maplibre.ts: Added cancelled flag to prevent map.on('load') callback
  from executing after cleanup, matching the pattern used in google-maps.ts
- maplibre-component.tsx: Added isMountedRef to prevent state updates
  on unmounted components in the onLoad callback

This prevents potential errors when components unmount before maps finish
loading.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 3 potential issues.

// Mount new example
mountExample(example, interleaved);
}, 0);
}
Copy link

Choose a reason for hiding this comment

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

Missing cancellation flag for rapid example switching

Medium Severity

The loadExample function uses setTimeout to defer cleanup but lacks a cancellation mechanism. When called rapidly (e.g., user quickly switching between examples), each call schedules a separate timeout, causing all intermediate examples to be mounted and immediately cleaned up. This results in unnecessary work and potential visual flickering. The pure JS mount functions already implement cancellation patterns with cancelled flags, but the orchestrating loadExample function does not.

Fix in Cursor Fix in Web

return () => {
cancelled = true;
map.remove();
};
Copy link

Choose a reason for hiding this comment

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

Overlay created before cancellation check may leak resources

Low Severity

The deckOverlay is created at line 27 before the cancellation check, but only added to the map inside the load event handler after checking cancelled. If cleanup runs before the map finishes loading, the overlay is never added to the map and never cleaned up since the cleanup function only calls map.remove(). This is inconsistent with google-maps.ts which creates the overlay only after verifying the operation wasn't cancelled, preventing orphaned resources.

Fix in Cursor Fix in Web

@chrisgervang chrisgervang merged commit 83ac412 into master Jan 20, 2026
6 checks passed
@chrisgervang chrisgervang deleted the basemap-browser branch January 20, 2026 17:51
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.

4 participants