Maps
Wavemap treats a map as a coordinated feature subsystem. Domain publication policy decides which points may be public, a focused projection supplies semantic point data, page adapters decide what those points mean, and an app-local runtime owns provider mechanics. A map provider supplies the basemap, projection, and camera engine; it does not become the authority for Venue or Event identity, location truth, status, routing, or localized content.
Use Maps Provider Operations for key restrictions, map IDs, quotas, attribution, CSP, privacy, smoke checks, and incident response. Use Venue Authoring for the content-editor meaning of exact and hidden Venue locations.
End-To-End Ownership
Section titled “End-To-End Ownership”Domain publication policy -> complete public map projection -> page/domain adapter -> provider-neutral map runtime -> Google basemap, camera, markers, and clustering adapterThe current Venue collection contract is deliberately different from ordinary page browsing:
POST /api/v1/venues/map/queryaccepts durable Venue filter groups but no page, page size, or presentation sorting.- A successful response is complete for its filter identity and contains only active Venues whose location is exactly published and has a complete coordinate pair.
- The response ceiling is 2,000 points. A larger match returns the stable complete-result
422error instead of silently truncating a global map. - Grid and list keep their paginated
VenueSummaryDTOquery. Map view uses its own query key and public projection. - Camera movement and viewport size do not refetch or geofence Venue data. That change requires measured scale evidence and a new accepted query contract.
The map query may include prepared profile media for the selected-Venue summary, but it does not fetch provider place data or infer coordinates during an ordinary read. Persisted Wavemap publication and address truth remains authoritative.
Event Series Map Projections
Section titled “Event Series Map Projections”Event Series mapping reuses the runtime while preserving domain-specific contracts:
GET /api/v1/event-series/:eventSeriesPublicId/mapaccepts one active direct-Event Series identity and returns its complete active member program. An editioned Series has no aggregate map across Editions.GET /api/v1/event-series/:eventSeriesPublicId/editions/:eventSeriesEditionPublicId/mapreturns both complete official-location and program projections for one active Edition beneath its active Series.- An official-location point is one active official Venue with exact published coordinates. Marker identity, cluster counts, selection copy, and canonical navigation remain Venue-owned.
- A program point is one Event-plus-Venue spatial instance. A multi-Venue Event contributes one point per mapped Venue; co-located instances remain distinct; online-only Events contribute no physical point; hybrid and TBA-schedule Events may contribute points when their Venue publication truth permits it.
- Each projection reports aggregate mapped and omitted coverage without exposing hidden coordinates or omission detail.
The 2,000-spatial-instance ceiling returns a stable
422instead of truncating a successful map. - Identity, not camera or Edition mode, owns the TanStack Query key. Camera movement, selection, and switching between official and program modes remain runtime-local and do not refetch.
These are map-specific complete reads, not substitutes for paginated relationship previews or ordinary Event queries.
Runtime And Adapter Split
Section titled “Runtime And Adapter Split”apps/wavemap-front-end/src/components/MapRuntime is an app-local reusable family. It follows the same general split as
Carousel plus MediaCarousel: reusable mechanics live below, while domain meaning and product presentation stay in a
page adapter.
| Surface | Owns |
|---|---|
MapRuntime | Configuration validation, SDK lifecycle, appearance readiness, loading/failure slots, retry, and framing. |
MapCanvas | Provider canvas, gesture posture, world bounds, visible insets, camera snapshot reporting, and controls. |
MapCameraCoordinator | One ordered authority for center/zoom and bounds-fit requests. |
MapLayer | Semantic layer identity and lifecycle. |
MapPointMarker | One provider-neutral labeled point adapted to an Advanced Marker. |
MapPointCollection | Point-marker registration and one MarkerClusterer lifecycle for a semantic collection. |
MapControlSlot | Logical map-control placement without exposing provider control positions to callers. |
MapNavigationControls | Generic zoom and reset mechanics with caller-owned labels and reset meaning. |
MapAppearanceControls | follow-app, light, and dark preference selection. |
VenueDetailsMap | One Venue point, constrained interaction, reset-to-Venue semantics, and Venue labels. |
VenueIndexMap | Venue projection mapping, collection selection, summary UI, canonical routing, and fit-results semantics. |
EventSeriesMap | Series/Edition projection mapping, semantic modes, Event-plus-Venue identity, summaries, and coverage. |
Generic runtime components must not import Venue DTOs, fetch domain data, choose application routes, or resolve
translation keys. Domain adapters map prepared DTOs into TMapCoordinate, semantic item IDs, labels, camera requests,
and presentation slots.
MapPointCollection is declarative from the caller’s perspective, but it coordinates imperative provider objects
internally. React renders one AdvancedMarker per item. Marker refs populate an item-ID map. The collection derives the
currently available provider markers, creates one clusterer when the provider map becomes ready, and replaces its marker
set when item/ref identity changes. Cleanup detaches the clusterer and clears its markers. The cluster algorithm and
camera projection remain provider-library concerns; Wavemap owns the inputs, renderer, labels, selection, and lifecycle.
State Boundaries
Section titled “State Boundaries”| State | Owner |
|---|---|
| View mode, search, and durable filters | URL and saved-view contracts. |
| Complete Venue map response | TanStack Query cache keyed only by normalized durable filters. |
| Complete Event Series map response | TanStack Query cache keyed only by Series identity or nested Series/Edition identity. |
| Initial/refit camera requests | Domain adapter, expressed as stable provider-neutral request IDs. |
| User pan and zoom | Mounted map runtime. Not URL or saved-view state. |
| Selected Venue and open summary | VenueIndexMap runtime-local state. |
| Edition map mode and selected map item | EventSeriesMap runtime-local state. |
| Summary card/sheet geometry | Page adapter measurement used to derive visible map insets. |
| Map appearance preference | Local browser preference: follow-app, light, or dark. |
| Effective application theme | Application theme owner. It drives maps only while the map preference is follow-app. |
| Camera continuity across provider restart | A provider-neutral snapshot retained by MapRuntime and restored into the next MapCanvas. |
Camera observation should not create browser-history entries. A map-only light or dark override intentionally diverges
from the application theme until the user returns it to follow-app. Storage events synchronize that preference across
same-origin tabs.
Camera And Responsive Geometry
Section titled “Camera And Responsive Geometry”Camera requests describe outcomes, not direct provider mutation:
- A center request supplies a coordinate and zoom.
- A bounds request supplies points, visible insets, and an optional maximum zoom.
- A stable request ID prevents ordinary React rerenders from overwriting user movement.
- A deliberate reset, filter change, or visible-inset change receives a new request identity.
The map rectangle and the usable map rectangle are not the same. Controls, attribution, a desktop summary card, and a
mobile summary sheet consume visible space. VenueIndexMap measures its runtime and active summary surface, derives
bounded insets, and lets the camera fit results into the remaining rectangle. Resize observation changes camera geometry
without changing the Venue query.
Consumers must provide a stable region while the provider loads or fails. The Index fills its available page region; the
Details map preserves the page-owned aspect ratio on wider layouts and a bounded narrow height. Runtime and canvas must
remain min-width: 0, fill the consumer width, and clip provider content to the accepted radius. Latitude bounds prevent
panning beyond Web Mercator’s useful pole extent while horizontal world wrapping remains available.
Interaction And Accessibility
Section titled “Interaction And Accessibility”- Venue Index exposes keyboard-operable zoom and fit-results controls, primary-themed overlay controls, localized tooltips, labeled markers/clusters, and one selected summary at a time.
- Selecting the active marker while its summary is open is a no-op. Escape, the close control, and outside pointer interaction dismiss the card or sheet and restore focus to the marker or reset control.
- Desktop summaries use the shared fade transition. Narrow summaries remain mounted through the drawer exit so the sheet can animate down before selection clears.
- Venue Details uses cooperative gestures, keyboard navigation, zoom, and a reset-to-Venue action inside its constrained region. The surrounding page remains scrollable and usable.
- Edition maps expose labeled mutually exclusive official-Venue and program-Event modes, preferring mapped official locations initially. Direct-Series maps omit that Edition-only control and present one program collection.
- Event Series selections announce Venue or Event-plus-Venue truth, open an accessible desktop card or narrow sheet, target the owning canonical Details route, and restore focus after dismissal.
- Reduced-motion preferences remove decorative marker and summary movement without removing state changes.
- Provider loading or failure stays inside the map region. Ordinary Venue and Event Series content plus grid/list discovery remain usable.
Do not cover, restyle, relocate, or remove provider attribution. App-owned controls and summaries must reserve enough visible inset to keep legal UI legible and operable.
Appearance
Section titled “Appearance”The runtime uses provider-authored LIGHT and DARK color schemes. Do not simulate dark mode with a CSS filter: it can
distort labels, imagery, attribution, and custom marker contrast.
Changing the effective scheme recreates the provider boundary. MapRuntime captures the last provider-neutral camera,
changes the provider execution key, and initializes the replacement canvas from that snapshot. Domain adapters should
not special-case that lifecycle or retain a google.maps.Map object.
Scale Triggers
Section titled “Scale Triggers”The first implementation uses a complete client-side point collection because current result volume and local query measurements support it. Reconsider the projection or clustering architecture when representative production evidence shows one or more of these conditions:
- A typical compressed map response exceeds 500 KiB or preview media remains more than half of transferred bytes.
- Server p95 exceeds 250 ms or browser end-to-end p95 exceeds 500 ms for ordinary filtered collections.
- Supported devices repeatedly produce long tasks above 50 ms or visible interaction delay during pan, zoom, cluster, or selection work.
- Result volume approaches the 2,000-point completeness ceiling.
- A real product requirement needs viewport-bounded discovery, density aggregation, areas, routing, or time/spatial joins.
These are review triggers, not automatic implementation instructions. Profile before adopting narrower projections, spatial indexes, server clustering, vector tiles, H3, PostGIS, or a GPU layer.
Testing
Section titled “Testing”Use the narrowest layer that owns the risk:
- Pure helper tests: camera request normalization, bounds, insets, appearance preference parsing, and responsive math.
- Component tests: provider lifecycle doubles, retries, layers, controls, markers, clusterer wiring, focus, and adapter behavior.
- Deterministic Playwright tests: loading/failure containment, routing, history, responsive dimensions, and fallback.
- Opt-in provider smoke: real SDK readiness, theme recreation and camera continuity, attribution, and mounted-canvas resizing.
Run the provider-connected smoke only with reviewed local browser configuration:
PLAYWRIGHT_ENABLE_GOOGLE_MAPS_PROVIDER_SMOKE=true \ pnpm -C apps/wavemap-front-end exec playwright test \ test/e2e/maps/google-maps-provider.e2e.test.ts --project=chromium --reporter=listKeep ordinary CI deterministic and provider-independent. A provider smoke is evidence about integration and current configuration, not a substitute for component, route, API-contract, mocked-request, or DB-backed proof.
Event Adoption Boundary
Section titled “Event Adoption Boundary”The Event Series adapter proves one accepted Event-location model for complete Series and Edition programs: a point is an Event-plus-Venue spatial instance, selection leads to Event Details with Venue context, and a cluster counts spatial instances. It does not decide the product job for Event Details or Event Index.
Future Event Details and Event Index work must still define their own zero/one/multiple, online, hybrid, TBA, cancelled, postponed, rescheduled, time-window, filter, and completeness semantics before adopting a map. Reuse runtime mechanics and the relevant Event Series adapter evidence, not Venue-only assumptions or an implicit universal domain contract.
Do not create a universal cross-entity map DTO or add Event fields to MapPointCollection. The reusable seam remains the
provider-neutral point/camera/layer vocabulary plus a domain adapter that supplies meaning.
What Makes This Page Stale
Section titled “What Makes This Page Stale”Review this page when the public-location vocabulary, Venue or Event Series map-query completeness contract, provider, runtime component family, appearance persistence, camera ownership, clustering boundary, or another Event map adapter changes.