How Atlas Tracks Visited Countries and Sub-Regions in TREK: A Technical Deep Dive
Atlas maintains a live model of your travel history by combining three synchronized data sources—country statistics, regional visit records, and ISO code mappings—loaded and cached inside the useAtlas hook.
The TREK application's Atlas feature provides users with an interactive map visualization of their travels, supporting both country-level and sub-regional tracking. According to the source code in the mauriceboe/TREK repository, this functionality relies on a sophisticated client-side state management system that fetches, caches, and renders visited territories in real-time. Understanding this architecture is essential for developers looking to extend the Atlas functionality or integrate similar geographic tracking into their own React applications.
The Three Data Sources
Atlas tracks visited territories by aggregating data from three distinct sources, each serving a specific purpose in the rendering pipeline.
Visited Countries
Country-level data is stored in the data.countries property of the AtlasData object. This structure contains trip counts, place counts, and first/last visit timestamps for each visited nation.
On component mount, the useAtlas hook initiates a fetch to /addons/atlas/stats via apiClient.get (lines 24-34 in client/src/pages/atlas/useAtlas.ts). The response populates the state with an array of AtlasCountry objects. When the map renders, the GeoJSON country layer builds a visitedA3 set from these entries to apply coloured fills and tooltips. Clicking a visited country triggers the un-mark flow if no trips or places are associated.
Visited Sub-Regions
Sub-regional tracking—covering states, provinces, and territories—uses a separate state variable called visitedRegions. This is typed as Record<string, {code: string; name: string; placeCount: number; manuallyMarked?:boolean}[]>, keyed by the country's ISO-A2 code.
A second effect in useAtlas (lines 61-66) fetches /addons/atlas/regions, returning a map of ISO-3166-2 codes and names. During region layer rendering (lines 403-427), the hook constructs visitedRegionCodes and visitedRegionNamesByCountry. The isVisitedFeature helper then checks whether a region feature's code or name matches a visited entry, determining its visual state and click actions.
Country-to-Region Lookup
To ensure consistent code conversion between ISO-A2 and ISO-A3 standards, Atlas utilizes a mutable module-level map called A2_TO_A3 defined in client/src/pages/atlas/atlasModel.ts.
After loading the country borders GeoJSON from /addons/atlas/countries/geo (lines 36-56), the hook augments this mapping with any missing alpha-2 to alpha-3 conversions found in the GeoJSON properties. This mapping facilitates fast lookups when rendering countries and provides fallback support for unvisited territories.
Step-by-Step Data Flow
The initialization sequence follows a specific order to ensure data consistency before rendering.
1. Initialization and Stats Loading
When AtlasPage mounts, useAtlas executes two parallel fetches to populate the core data structures:
// Load atlas stats + bucket list
Promise.all([
apiClient.get('/addons/atlas/stats'), // → visited countries & overall stats
apiClient.get('/addons/atlas/bucket-list')
]).then(([statsRes, bucketRes]) => {
setData(statsRes.data); // ← country visit data stored here
setBucketList(bucketRes.data.items);
});
Source: client/src/pages/atlas/useAtlas.ts (lines 24-34)
2. Geographic Data Loading
The country borders GeoJSON is fetched and used to enrich the A2_TO_A3 map, ensuring every country can be referenced consistently across the application.
Source: client/src/pages/atlas/useAtlas.ts (lines 36-56)
3. Region Data Loading
A lightweight request retrieves visited regions:
// Fetched in useEffect lines 61-66
apiClient.get('/addons/atlas/regions').then((res) => {
setVisitedRegions(res.data);
});
Source: client/src/pages/atlas/useAtlas.ts (lines 61-66)
4. Rendering the Country Layer
The country layer iterates over GeoJSON features, building a visitedA3 set from data.countries and applying a deterministic colour palette to visited nations.
Source: client/src/pages/atlas/useAtlas.ts (lines 84-90)
5. Rendering Sub-Regions
When the map zoom level reaches 5 or higher, the region layer activates. The isVisitedFeature helper checks against visitedRegions using either ISO-3166-2 codes or region names scoped to the parent country. Visited regions receive solid fills, while unvisited regions display only outlines.
Source: client/src/pages/atlas/useAtlas.ts (lines 403-429)
6. Mark and Un-Mark Actions
Clicking an unvisited territory opens a confirmation popup. Confirmed actions call the API endpoints (/addons/atlas/country/:code/mark or /unmark) and immediately update local state via setData or setVisitedRegions, ensuring UI responsiveness without full page reloads.
Source: client/src/pages/atlas/useAtlas.ts (lines 84-106 for countries, lines 81-106 for regions)
Practical Implementation Examples
Accessing Visited Country Data
To display visited countries elsewhere in your application:
import { useAtlas } from '@/pages/atlas/useAtlas';
function VisitedCountryList() {
const { data } = useAtlas(); // data?.countries holds visited entries
return (
<ul>
{data?.countries.map(c => (
<li key={c.code}>
{c.code} – {c.tripCount} trips, {c.placeCount} places
</li>
))}
</ul>
);
}
Checking Sub-Region Visit Status
To programmatically verify if a specific region has been visited:
import { useAtlas } from '@/pages/atlas/useAtlas';
function isRegionVisited(countryA2: string, regionCode: string) {
const { visitedRegions } = useAtlas();
const regions = visitedRegions[countryA2] ?? [];
return regions.some(r => r.code === regionCode);
}
Manually Marking a Country
The same logic used by the UI for manual marking:
import apiClient from '@/api/client';
import { useAtlas } from '@/pages/atlas/useAtlas';
async function markCountry(code: string) {
await apiClient.post(`/addons/atlas/country/${code}/mark`);
// The hook's state updates automatically via the confirm action flow
}
Key Files and Architecture
| File | Purpose |
|---|---|
client/src/pages/atlas/atlasModel.ts |
Type definitions (AtlasCountry, AtlasData, BucketItem) and the mutable A2_TO_A3 map |
client/src/pages/atlas/useAtlas.ts |
Core hook implementing data fetching, state management, and mark/un-mark logic |
client/src/pages/AtlasPage.tsx |
UI wrapper integrating the hook with map components and sidebars |
shared/continent.ts |
Helper continentForCountry used when updating stats after mark/un-mark operations |
Summary
- Three data sources power the Atlas:
data.countriesfor nations,visitedRegionsfor sub-regions, andA2_TO_A3for code consistency. - Parallel initialization fetches stats and bucket lists via
Promise.all, followed by separate GeoJSON and region data loads. - Real-time rendering uses
visitedA3sets for countries andisVisitedFeaturehelper functions for regions, with zoom-level gated rendering. - Optimistic updates occur through immediate state mutations after API calls to
/addons/atlas/country/:code/markor/unmark, eliminating UI latency. - Type-safe architecture relies on
AtlasCountryandAtlasDatainterfaces defined inatlasModel.ts.
Frequently Asked Questions
How does Atlas handle country code conversions between ISO-A2 and ISO-A3?
The system maintains a mutable module-level map called A2_TO_A3 in client/src/pages/atlas/atlasModel.ts. After loading the GeoJSON country borders, the useAtlas hook populates this map with any missing mappings found in the GeoJSON properties (lines 66-70). This ensures consistent referencing when the API returns alpha-2 codes but the map visualization requires alpha-3 codes for feature matching.
What determines whether a sub-region appears as visited on the map?
The isVisitedFeature helper checks two criteria: the region's ISO-3166-2 code and its display name, scoped to the parent country. If either matches an entry in the visitedRegions record (keyed by ISO-A2), the region receives a solid fill colour. This dual-check approach handles cases where GeoJSON features might use full names while the API stores codes, or vice versa.
Can users mark countries as visited without having trip data?
Yes. The system supports manually marking territories through the /addons/atlas/country/:code/mark endpoint. When a user clicks an unvisited country or region, the UI presents a confirmation dialog. Upon confirmation, the hook updates setData or setVisitedRegions immediately while synchronizing with the backend, allowing users to track places they visited outside of logged trips.
How does the map optimize performance when rendering thousands of regions?
Atlas implements zoom-level gating (threshold of 5) for the region layer, ensuring sub-regions only render when the user has zoomed in sufficiently. Additionally, the hook builds memoized lookup sets (visitedA3, visitedRegionCodes) rather than scanning arrays during each render cycle, providing O(1) complexity when checking visit status for individual GeoJSON features.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →