A navigational tool to rule them all, right in the browser.
No API keys, no registration, no app stores and no invasions of privacy.
A loveletter to the OpenStreetMap ecosystem ❤️ Special thanks to OpenFreeMap for tile hosting.
Note
The app is currently in SPA front-end only mode — the account, maps and collections features (which require a backend) are disabled in the UI.
Both heroes are live, intentionally non-deterministic captures — a random-country cold start with the Info panel open — refreshed in place by every full local test run, and rebuilt and committed back automatically whenever a release is cut. The committed screenshot matrix is under screenshots/; see Screenshot matrix.
ogis.app is a Vue 3 + Vite single-page application — the map is MapLibre GL JS with a Bootstrap SCSS theme, and a hand-rolled service worker (public/sw.js, no Workbox) provides the app shell and offline map support. The backend features (account, maps, collections) are disabled in the current SPA front-end only mode.
- Entry point —
src/main.jsreads the URL parameters, creates the Vue app, installs the core features and mounts to#app. - Features — self-contained bundles registered from
src/main.js; each is a plain object with aninstall(ctx)method that registers a side-panel tab and/or a corner control chip. See Features. - Composables — per-instance state lives in
src/composables/, cached in a module-levelMapkeyed by the instance id (the?id=URL parameter). See Instances. - Offline — the service worker caches the app shell and map resources; the Offline feature pre-warms its cache by fetching every tile and glyph URL for a region. See Offline.
Full developer documentation lives in docs/README.md.
- Detailed, free global map — no install, no account
- Globe view on first load
- Corner controls for menu and locate, plus a merged Info panel (share links, app info, privacy)
- GPS locate with compass heading
- Record GPS tracks and export as GPX
- Import GPX routes and navigate offline
- Download map regions for offline use (via a service worker)
- Multilingual — follows your device language
- Shareable camera views
- Map view persisted between sessions
- Light and dark mode, following your device setting
- Works on any device
Note
Offline maps — drag a region on the map to download its tiles and glyphs for offline use. A hand-rolled service worker (public/sw.js) caches the app shell and map resources; see docs/12.offline.md.
- Location permissions are required for GPS/Compass features. Some users have a deny-all approach to browser permissions and changing them varies between browsers and devices.
- The app does not work in the background or when the device is locked — a common limitation of web apps.
- The app requires an initial connection to load assets and map tiles, even though it works offline thereafter.
- Much more language support
- Better handling of denied location permissions
| Component | Source |
|---|---|
| Map Data | © OpenStreetMap contributors |
| Tile Hosting | OpenFreeMap |
| Rendering | MapLibre GL JS |
| Tile Schema | OpenMapTiles / OSM Bright |
| User Interface | Vue JS / Bootstrap |
| Icons | OpenGIS icons / Bootstrap Icons |
Requires Node 24 — the unit suite depends on Intl.Locale.prototype.getTimeZones.
git clone https://github.com/OpenGIS/app.git
npm install
npm run dev # http://localhost:5174
npm run build # production build to dist/E2E runs on a fresh machine also need the Chromium build: npx playwright install chromium.
The canonical loop, from first checkout to merge. Deep dives are linked inline:
- Set up — see Quick start.
- Inner loop —
npm run watch:e2ereruns the functional E2E suite on change; run a single spec withnpm run test:e2e -- tests/e2e/{spec}.spec.js; unit tests arenpm test. Deep dive: Testing — Local iteration loops. - UI changes — capture the screenshot matrix with
npm run test:matrix(writes to a temp directory), then inspect what moved withnpm run screenshots:diff(relative/visual report). See Screenshot matrix. - Format —
npm run format. CI gates onnpm run format:check. - Pre-push gate —
npm run check(Prettier check + unit tests) before pushing. - Commit and push — Conventional Commits only (see Contributing).
- CI is the gate — green on your pushed commits means done:
ci.ymlruns the unit suite, the commit-message check and the functional E2E suite, while theScreenshotsworkflow verifies the committed matrix within a strict pixel tolerance (its aggregateScreenshots — verify committed matrixjob is the required branch-protection check) and re-baselines it automatically onmasterwhen a real visual change lands. The full local E2E run (npm run test:e2e) is an optional fallback — offline, or when the local GPU functional path is specifically wanted. See CI.
The committed PNGs under screenshots/ are visual evidence that ships with the repository: core chrome states across phone, tablet and desktop in both orientations, plus feature states at desktop landscape. Captures run in a dedicated, always-software-rendered Playwright project with a dark theme and a pinned locale, so they are reproducible run-to-run on a given machine.
- Local —
npm run test:matrixcaptures into a gitignored temp directory; local runs never touch the committed PNGs.npm run screenshots:diffreports what moved, whether comparing a local capture against the committed matrix or two local capture trees before and after a change. - CI verification — the
ScreenshotsGitHub workflow captures the matrix on macOS shards and verifies the captures against the committed PNGs within a strict pixel tolerance: sub-level anti-aliasing drift is tolerated and reported, while real changes fail. An aggregate job checks the full matrix in both directions. - Re-baselining — when a
masterpush genuinely moves the matrix (verification fails beyond tolerance), the workflow'srefreshjob rebuilds the committed baseline automatically from that run's own captured artefacts and re-verifies, so the developer justgit pull. Pull requests stay strict (a red verification is a review signal) and sub-tolerance drift never triggers it.npm run screenshots:fetch -- --run <run-id> [--write]is the manual exception path for a PR or a failed/incomplete capture set — never from local captures.
See docs/9.testing.md for the testing strategy and docs/13.ci.md for the workflow details.
Set the backend API origin for cross-subdomain auth/API calls:
Note
Only needed when the auth/backend features are re-enabled — the app currently runs in SPA front-end only mode.
echo "VITE_API_BASE_URL=https://api.example.com" > .env.localIcon sources are vendored in src/icons/ alongside the committed generated bundle.
npm run icons:build # regenerate the bundle after adding or updating source iconsSee docs/14.icons.md for the pipeline.
The end-to-end journey, checkout to release:
- Set up — see Quick start.
- Make the change — follow the Development workflow.
- Run the pre-push gate —
npm run check(Prettier check + unit tests). - Commit — a Conventional Commit:
<type>(optional scope): <description>, e.g.fix(offline): purge stale tile URLs. The type table maps types to release impact; breaking changes use!or aBREAKING CHANGE:footer. - Push and open a PR —
ci.ymlruns the unit suite, the commit check and the functional E2E suite; theScreenshotsworkflow verifies the committed matrix. A red matrix on a PR is a review signal for a real visual change — the aggregateScreenshots — verify committed matrixcheck is the required one. - After merge — the
releasejob cuts versions, changelogs and GitHub releases from the commit messages; a real visual change re-baselines the screenshot matrix onmaster; the README heroes refresh automatically on each release.
AGENTS.md is the project's conventions file — the same journey, commands and gates in agent-facing form.
See docs/README.md for the full developer documentation.

