Skip to content

Add deck.gl and MapLibre GL for enhanced map visualization#28

Merged
koala73 merged 102 commits into
mainfrom
claude/add-deckgl-visualization-D1VHX
Jan 25, 2026
Merged

Add deck.gl and MapLibre GL for enhanced map visualization#28
koala73 merged 102 commits into
mainfrom
claude/add-deckgl-visualization-D1VHX

Conversation

@koala73

@koala73 koala73 commented Jan 23, 2026

Copy link
Copy Markdown
Owner

Summary

This PR introduces deck.gl and MapLibre GL dependencies to enhance map visualization capabilities, replacing the previous MapComponent with a new MapContainer that supports WebGL-based rendering on desktop while maintaining D3/SVG fallback for mobile devices.

Key Changes

  • Dependencies Added:

    • deck.gl (^9.2.6) and related packages (@deck.gl/core, @deck.gl/geo-layers, @deck.gl/layers)
    • maplibre-gl (^5.16.0) for modern map rendering
    • TypeScript types for MapLibre GL
  • Component Refactoring:

    • Renamed MapComponent to MapContainer throughout the codebase
    • Updated App.ts to use the new MapContainer class
    • Updated type annotations and imports accordingly
  • Implementation Details:

    • MapContainer now uses deck.gl (WebGL) rendering on desktop for better performance and visual effects
    • Mobile devices fall back to D3/SVG rendering for compatibility
    • Default view adjusted: MENA focus on mobile, global view on desktop
    • Zoom levels optimized for each platform (2.5 for mobile, 1.0 for desktop)

Notes

  • This change enables more advanced geospatial visualizations while maintaining backward compatibility with mobile devices
  • The deck.gl integration allows for better performance with large datasets and more sophisticated layer effects

Implement Palantir-like interactive map experience using deck.gl with
MapLibre GL as the base map. Key changes:

- Add deck.gl (@deck.gl/core, @deck.gl/layers, @deck.gl/geo-layers) and
  maplibre-gl dependencies for GPU-accelerated rendering
- Create DeckGLMap component with WebGL layers for:
  - Undersea cables and pipelines (PathLayer)
  - Military bases, nuclear facilities, datacenters (ScatterplotLayer)
  - Conflict zones (GeoJsonLayer with polygons)
  - Hotspots, earthquakes, weather, outages, protests
  - AIS density, military vessels and flights
  - Strategic waterways, economic centers
  - Tech variant: startup hubs, tech HQs, accelerators, cloud regions
- Create MapContainer wrapper that conditionally renders:
  - DeckGLMap (WebGL) on desktop with WebGL support
  - Existing D3/SVG MapComponent on mobile for graceful degradation
- Add dark theme CSS styles for deck.gl controls, legend, layer toggles
- Import maplibre-gl CSS in main.ts

Desktop users now get smooth 60fps interactions with large datasets
while mobile users retain the optimized SVG experience.

https://claude.ai/code/session_01GTanC7R6aSQNsnijqJRUFz
- Fix coordinate swap bugs in createCablesLayer, createPipelinesLayer,
  and createConflictZonesLayer - data is already [lon, lat] format
- Fix coordinate order in triggerConflictClick, triggerPipelineClick,
  and triggerCableClick to pass (lat, lon) to setCenter correctly
- Fix handleClick to not show popup for hotspots (they have their own handler)
- Add missing tooltips for all layer types (cables, pipelines, conflicts,
  natural events, weather, outages, AIS density, military flights,
  waterways, economic centers, tech HQs, accelerators, cloud regions,
  tech events)

https://claude.ai/code/session_01GTanC7R6aSQNsnijqJRUFz
@vercel

vercel Bot commented Jan 23, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Review Updated (UTC)
worldmonitor Ready Ready Preview, Comment Jan 25, 2026 6:28pm
worldmonitor-startup Ready Ready Preview, Comment Jan 25, 2026 6:28pm
worldmonitor-test Ready Ready Preview, Comment Jan 25, 2026 6:28pm

Request Review

The MapPopup.show() method expects a PopupData object with:
- type: PopupType string identifying the popup renderer
- data: the actual data object to display
- x, y: click coordinates for popup positioning

Previously was passing raw objects which resulted in broken/missing popups.

Changes:
- Import PopupType from MapPopup
- Update handleClick to map layer IDs to PopupType and create proper PopupData
- Update all trigger methods (triggerConflictClick, triggerBaseClick, etc.)
  to create proper PopupData with centered x/y coordinates
- Add getContainerCenter() helper for programmatic popup positioning

https://claude.ai/code/session_01GTanC7R6aSQNsnijqJRUFz
Root cause: CONFLICT_ZONES config had inconsistent coordinate formats.
Red Sea Crisis and South Lebanon entries were in [lat, lon] format
while other entries were in [lon, lat] format (GeoJSON standard).
This caused polygons to render in completely wrong locations.

Fixes:
- Fix Red Sea Crisis coords: [[12,42]...] → [[42,12]...] (swap to [lon,lat])
- Fix Red Sea Crisis center: [14,43] → [43,14]
- Fix South Lebanon coords: [[33.0,35.1]...] → [[35.1,33.0]...]
- Fix South Lebanon center: [33.2,35.4] → [35.4,33.2]
- Add explicit transparent background to #deckgl-overlay CSS

https://claude.ai/code/session_01GTanC7R6aSQNsnijqJRUFz
Two-pronged approach to fix the dark semi-transparent overlay:

1. Add MapLibre background layer:
   - Added 'background' layer with dark color (#0a0f0c) to MapLibre style
   - This ensures no transparent gaps while tiles are loading

2. Set WebGL clear color to transparent:
   - Added onWebGLInitialized callback to Deck initialization
   - Explicitly set gl.clearColor(0, 0, 0, 0) for transparent background
   - Enable proper alpha blending with gl.blendFunc

https://claude.ai/code/session_01GTanC7R6aSQNsnijqJRUFz
Instead of relying on deck.gl's default canvas/context creation,
manually create canvas and WebGL context with explicit alpha support:

- Create canvas element with transparent background style
- Get WebGL2/WebGL context with alpha: true, premultipliedAlpha: true
- Set gl.clearColor(0, 0, 0, 0) for transparent clear
- Pass pre-created canvas and gl context to Deck constructor

This ensures the WebGL context is properly configured for alpha
transparency from the start, allowing the MapLibre base map to
show through the deck.gl overlay.

https://claude.ai/code/session_01GTanC7R6aSQNsnijqJRUFz
Changes:
- Add explicit z-index to MapLibre (1) and deck.gl overlay (2)
- Add overflow: hidden to wrapper
- Reverted to letting deck.gl create its own canvas
- Added effects: [] to disable any default deck.gl effects
- Set canvas background to transparent after deck initializes
- Added comprehensive CSS rules to ensure all canvas elements
  and containers have transparent backgrounds

https://claude.ai/code/session_01GTanC7R6aSQNsnijqJRUFz
Root cause: The timestamp element had both classes 'map-timestamp' and
'deckgl-timestamp'. These classes set conflicting positioning:
- .map-timestamp: bottom: 8px; right: 10px;
- .deckgl-timestamp: top: 10px; left: 50%;

When all four positioning values (top/bottom/left/right) are set on an
absolutely positioned element without explicit width/height, it stretches
to fill the space between those edges - creating a 724x315px dark overlay.

Fixes:
- Remove 'map-timestamp' class from timestamp element in DeckGLMap.ts
- Add explicit bottom: auto; right: auto; width: auto; height: auto;
  to .deckgl-timestamp CSS to prevent any accidental stretching

https://claude.ai/code/session_01GTanC7R6aSQNsnijqJRUFz
Override MapLibre GL's default grab cursor to use the standard
pointer cursor, only switching to grabbing when actively dragging.

https://claude.ai/code/session_01GTanC7R6aSQNsnijqJRUFz
The deck.gl canvas was blocking mouse events from reaching MapLibre
because it had pointer-events: auto for picking but controller: false.
Enable deck.gl's controller and sync view state changes back to MapLibre.

https://claude.ai/code/session_01GTanC7R6aSQNsnijqJRUFz
Add CSS to override deck.gl's default grab cursor on its canvas,
using the default pointer cursor and only grabbing when dragging.

https://claude.ai/code/session_01GTanC7R6aSQNsnijqJRUFz
Problem: Once cached scores loaded, usedCachedScores=true forever,
blocking recalculation even when focal points became ready.

Fix: Add forceLocal parameter to refresh() - when focal-points-ready
fires, it now calls refresh(true) to force local recalculation with
focal point urgency boosts applied.
Problem: CII showed cached backend scores immediately, then updated
when focal points ready - showing misleading preliminary data.

Fix:
- Show "Analyzing intelligence..." state until focal points ready
- Only calculate and render after focal-points-ready event
- Remove unused cached scores logic (simpler, more accurate)
- Add news storage for related news lookup on hotspot click
- Add getRelatedNews() method matching old Map.ts behavior
- Fix click handler to show popup with related news + GDELT intel
- Add HTML overlays for high-activity hotspots with CSS pulsating animation
- Filter high-activity hotspots from deck.gl layer to avoid double-render
- Add escapeHtml for safety in HTML templates
- Add debug logging for panel persistence investigation
Feature parity improvements:
- Add layer help popup with ? button (explains all layer types)
- Add LAYER_ZOOM_THRESHOLDS constant for zoom-based label visibility
- Add toggleLayer() public method to toggle layers on/off
- Make zoomIn()/zoomOut() public methods for external access
- Add military cluster data storage (militaryFlightClusters, militaryVesselClusters)
- Add getMilitaryFlightClusters() and getMilitaryVesselClusters() getters
- Store clusters in setMilitaryFlights/setMilitaryVessels methods
- Create gdacs.ts service fetching from GDACS API (earthquakes, floods,
  tropical cyclones, volcanoes, wildfires, droughts)
- Filter to Orange/Red alerts only for relevance
- Merge GDACS events with EONET natural events with deduplication
- Color-code markers: red for Red alerts, orange for Orange alerts
- Prioritize GDACS data (more authoritative) over EONET for overlaps
- Add missing military vessel/flight cluster layers
- Add cluster tooltips and click type mappings
- Fix activity type mismatches (deployment/transport vs patrol/surveillance)
- Add congestion coloring to AIS density (orange for deltaPct >= 15)
- Add port type-based colors and tooltip icons
- Fix getHotspotLevels/setHotspotLevels to use h.name
- Fix setLayerReady to use 'active' class with layer state check
Header:
- Add Global/Americas/MENA/EU/Asia/LatAm/Africa/Oceania dropdown
- Sync with map view state bidirectionally
- Hide duplicate selector in map controls

DeckGLMap render throttling:
- Convert all updateLayers() calls to render() for debouncing
- render() uses requestAnimationFrame to batch updates
- Reduces CPU usage when multiple data updates arrive quickly

APT markers: Already present as subtle orange dots (geopolitical variant)
Wait for MapLibre 'load' event before initializing deck.gl overlay.
Previously, deck.gl layers were created before MapLibre finished
initializing its view state, causing incorrect coordinate projections
on first render. After any view change, the map would correct itself.
- Add loadIntelligenceSignals() that ALWAYS fetches protests, military,
  outages regardless of layer visibility
- Add intelligenceCache to prevent duplicate API calls when layers enabled
- Remove redundant layer-gated initial load tasks (handled by intelligence)
- Remove redundant refresh schedules for intelligence layers
- Add surge/foreign presence detection to loadIntelligenceSignals()
- Fix missing ACLED warning status in cached loadProtests() path

This ensures CII scores are accurate even when map layers are disabled.
Previously, Iran showed S:0 (no signals) because military layer was off.
- Make popup header sticky so it stays visible when scrolling
- Add solid backdrop via ::before to prevent content showing through
Increased z-index from 100 to 500 for map controls to ensure they
always appear above other map elements like overlays and canvases.
- Add getFocalPointForCountry() and getNewsCorrelationContext() to focal-point-detector
- Enhance foreignPresenceToSignal to include news correlation from focal points
- Map operator countries and affected regions to ISO codes for focal point lookup
- Update SignalModal to display correlated focal points and news headlines
- Add military_surge type label and styling for the signal modal
- Add CSS for focal point and news correlation sections in intelligence findings
- Add AI Insights Panel section: summarization fallback chain, headline scoring, sentiment analysis
- Add Focal Point Detector section: intelligence synthesis, scoring algorithm, urgency classification
- Add Natural Disaster Tracking section: GDACS integration, EONET merge, deduplication
- Add Military Surge Detection section: surge criteria, severity levels, news correlation
- Add Service Status Monitoring section: external service health tracking
- Add Signal Aggregator section: central signal collection, country grouping, convergence
- Add Browser-Based ML section: ONNX Runtime, fallback strategy, lazy loading
- Update Tech Stack: add deck.gl + MapLibre GL for WebGL map rendering
- Update project structure: add new services (focal-point-detector, signal-aggregator, etc.)
- Update components: add DeckGLMap, MapContainer, InsightsPanel, ServiceStatusPanel
- Update Roadmap: add recently completed features
- Add Entity Registry Architecture section: lookup indexes, entity types
…tion

The original documentation incorrectly described surge detection as
operator-count based. The actual algorithm uses:
- Baseline-based detection (2x historical activity = surge)
- Separate foreign presence detection for operators outside home regions
- Theater-based grouping with 48-hour baseline window
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: map Map, globe, DeckGL visualization enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants