The hard part of Island Watch wasn't the map
Designing a trust layer for Vancouver Island public updates.
Island Watch looks like a map dashboard. That is the easy description, and probably the least interesting one.
The harder problem was this: Vancouver Island status information is scattered across ferry notices, road events, weather alerts, wildfire layers, flood advisories, evacuation boundaries, transit alerts, marine warnings, earthquake reports, air quality readings, and other public sources. Most of those sources are useful. None of them are designed to answer the everyday regional question:
What on the Island needs attention right now?
Island Watch is my attempt to answer that question without pretending to become an official source. It is a scan layer: a way to notice what may affect travel, access, safety, or planning, then follow the trail back to the original provider.
That boundary matters. A public-data product earns trust by being clear about what it is, what it knows, what it does not know, and where the user should go next.
The regional problem
Vancouver Island is a specific product problem.
Ferries are not optional context. They define movement on and off the Island, and often between nearby communities. A highway closure can change a trip. A ferry cancellation can change the same trip differently. Weather, marine conditions, wildfire restrictions, flood advisories, evacuation boundaries, transit alerts, park advisories, and earthquake reports can all matter, but they do not matter in the same way.
A province-wide status surface can be too broad. A single-source page can be too narrow. An Island-specific view can make smarter decisions about relevance because it knows the regional frame: ferry corridors, watched highways, coastal exposure, Island communities, and the difference between broad awareness and route-critical disruption.
The goal was never "show everything." The useful goal was "show what someone on Vancouver Island might need to notice."
That distinction shaped the whole system.
The product is a trust layer
The map is visible. The trust layer is the product.
Island Watch has to answer questions that a normal dashboard can avoid:
- Is this update current?
- Is the source healthy, stale, degraded, paused, or down?
- Is the event directly Island-relevant or only nearby context?
- Is this a public-awareness item, a travel impact, or an official emergency instruction?
- Can the app summarize this source, or should it preserve a shorter attribution and hand off?
- Should this item appear on the main dashboard at all?
Those are product decisions, but they are also data-model decisions and interface decisions.
The source registry keeps track of provider identity, lifecycle status, source URLs, terms URLs, attribution text, fetch intervals, source-of-record warnings, official confirmation warnings, and display permissions. Some sources are active. Some are planned. Some can be summarized. Some should not show full text. BC Hydro is intentionally disabled from public display while permission, terms, and product fit are confirmed.
That is not a missing feature. That is restraint.
A public utility should not grab every feed it can find and hope the interface makes it feel legitimate. It should be conservative about source use, clear about attribution, and honest when a source is not ready for the main experience.
Many sources, one usable model
Under the interface, Island Watch uses Supabase as the normalized alert foundation.
Public providers
-> provider-specific Edge Functions
-> normalized alert records
-> dashboard-safe public view
-> adapter layer
-> briefing, map, filters, source health, and detail panels
The providers do not agree with each other. DriveBC road events are not BC Ferries service notices. Wildfire points and perimeters are not evacuation boundaries. AQHI readings are not weather alerts. Earthquake reports are not tsunami bulletins. Marine warnings are not normal marine forecasts. Transit alerts are not flood advisories.
So Island Watch normalizes without flattening away the important differences.
The shared dashboard model keeps source type, original status and severity, source details, normalized severity, source URL, coordinates, multiple map markers, affected routes, regions, communities, route views, relevance scope, why-shown reasons, public impact, action text, confidence, freshness, stale state, and sort score.
That sounds like plumbing until the interface has to make a decision. Then it becomes the reason the product can say something useful without overstating itself.
A ferry cancellation is not one marker
BC Ferries became the best example of why the work is more complicated than "put a pin on a map."
A ferry notice can affect a route, two terminals, a sailing time, a direction, a region, and a person's Check view. Treating that as one marker can be wrong. Treating it as generic text can also be wrong.
The ferry ingest scrapes official service notice pages, filters for Island-relevant routes, parses detail pages, classifies severity, matches terminals, extracts route names and terminal pairs, keeps delay and cause details, detects water taxi and extra sailing notices, preserves reissue metadata, chooses a map mode, records terminal confidence, dedupes notices by source notice code, and keeps the strongest severity when duplicates appear.
It also protects existing ferry notices if a scrape returns zero parseable notice links. That failure mode matters. If an upstream page changes or returns unexpected markup, the app should not confidently resolve every active ferry notice just because the parser came back empty.
That is the kind of engineering decision users may never see directly, but they feel it when the app behaves calmly under messy conditions.
Spatial trust is part of the product. If a selected notice, marker, route, and detail panel disagree, the app feels less reliable even when the raw data is technically present. The interface has to preserve source identity, location identity, and route context together.
Freshness is not one timestamp
"Fresh" is not universal.
A tsunami alert feed and a park advisory feed should not have the same freshness rule. Island Watch treats source health as source-specific. BC Ferries, evacuation boundaries, current emergencies, weather alerts, flood advisories, AQHI, BC Parks advisories, earthquakes, tsunami alerts, marine warnings, wildfire layers, and fire restrictions all have different refresh expectations and stale/degraded thresholds.
That means source health is not just a backend log. It is part of the user experience.
The Source Health page shows when each source was last checked, expected refresh timing, stale timing, visible update counts, errors, paused reasons, attributions, source links, and terms links. A healthy source can show zero visible updates because a quiet feed, no-alert bulletin, low AQHI reading, normal marine forecast, or low-impact item does not need to occupy the main dashboard.
This is one of the most important trust behaviors in the app:
A quiet dashboard should mean "nothing Island-relevant is currently visible," not "we forgot to check."
That requires the product to separate source health from alert visibility.
Suppressing noise is product work
Dashboards often get worse by getting louder.
Island Watch has visibility rules because not every public data point deserves the same level of attention. AQHI 3 stays in source-health context; AQHI 4 can become a visible advisory. Routine small earthquakes stay quiet unless they are felt or significant enough to matter regionally. Tsunami no-alert bulletins stay hidden, while watches, advisories, and warnings become visible. Normal marine forecasts stay suppressed, while marine warnings, watches, and advisories for Island-adjacent areas can appear.
The same idea applies across sources. Broad regional notices need different handling from exact-coordinate incidents. Off-Island ferry notices should usually stay quiet unless they connect to an Island corridor. Park advisories, fire restrictions, flood statements, road events, and transit disruptions need thresholds that fit how people actually use the app.
The dashboard gets better not only when it shows the right things, but when it stays quiet about the wrong things.
Briefing first, map second
The interface is map-first visually, but the product is briefing-first in behavior.
The map provides spatial context. The briefing provides hierarchy.
Island Watch has area mode, Check mode, category filters, region selection, route relevance, search, selected updates, selected corridors, grouped markers, mobile map/list surfaces, local context notices, snoozes, share payloads, refresh state, offline state, and URL-restored view state. That is not just React state. It is the operational shape of the product.
The interface has two jobs:
- Summarize whether anything needs attention.
- Let people inspect the source, location, and next step.
The map helps answer "where." The briefing helps answer "so what."
That is why official links, freshness labels, source names, confidence language, action text, and public-impact summaries stay close to the alert. The app should reduce the user's effort, then hand them back to the source before the decision becomes safety-critical or provider-specific.
What makes this more than a map demo
These are the decisions I want a reviewer to notice:
- Production does not silently fall back to fake alerts when Supabase is missing.
- Public users read from a dashboard-safe view, not the raw
alertstable. - Sources have lifecycle status, terms, attribution, display permissions, and source-of-record warnings.
- Freshness is calculated per source.
- Source health is visible in the product, not hidden in logs.
- Quiet sources can still be healthy.
- Multi-location alerts can become multiple map markers.
- Ferry notices preserve route, terminal, sailing, delay, cause, and official notice context.
- Official source links stay close to user-facing alerts.
- Some sources are disabled, paused, or planned until they are appropriate to show.
None of that is flashy. That is the point.
Island Watch is supposed to feel calm, useful, and grounded. The system can have complicated internals, but the user should feel like the app is doing the boring work of sorting, checking, labeling, and handing off properly.
What I learned
The biggest lesson from Island Watch is that public-data products are not mainly about collection.
Collection is table stakes.
The harder work is interpretation: deciding what should be visible, how confident the app should sound, when a source is too stale to trust, how a regional notice should appear on a map, and how close the official handoff needs to be.
That is the work I am most proud of. Not because Island Watch has every possible source or every possible feature, but because the project forced the right kind of restraint. It made data quality, source limits, freshness, relevance, and interface behavior part of the same product.
The next useful version is not a bigger pile of feeds. It is more reliability, sharper source transparency, better share links, local feedback, and careful expansion only when the trust layer can support it.
Island Watch may look like a map dashboard. Underneath, it is really a question about how to make messy public information usable without making it seem simpler than it is.