hermes-explorigin f43530bed0
Some checks failed
CI / test-and-build (push) Failing after 1m18s
Add Gitea Actions CI: run tests on every push
- .gitea/workflows/ci.yml: on push/PR, runs npm ci, svelte-check, offline
  regression tests, and a static (no-Overpass) build, then verifies output
- scripts/test.mjs: offline test suite — validates manifest + GeoJSON
  structure and asserts fuzzy-search regressions against committed data
- package.json: add 'test' and 'build:static' scripts
- osm-data.mjs: tag OSM layers layerType='mixed' in the manifest
2026-08-08 18:32:48 +00:00

Navigator

A custom web application that serves OpenStreetMap (OSM) data. It has a build process that:

  1. Pulls OSM data from the Overpass API for configurable geographic areas and feature types.
  2. Merges hand-authored custom layers — zones (polygons) and points of interest — from scripts/custom-layers.mjs.
  3. Converts everything to static GeoJSON files.
  4. Builds a SvelteKit frontend (with the static adapter) into a plain static site that renders each data source as a toggleable layer on an interactive Leaflet map.

The final output in build/ is a fully self-contained static site — no server runtime required. You can serve it with any static HTTP server (nginx, Apache, python -m http.server, S3/Cloudflare Pages, etc.).

Built With

Quick Start

npm install

# Fetch & convert OSM data into static/data/*.geojson
npm run build:data

# Full production build: (fetch data) + (static site) -> build/
npm run build

# Preview the production build locally
npm run preview

Development

npm run dev         # Vite dev server (your data in static/data is served too)
npm run check       # Type + Svelte checks

Build Process

Command What it does
npm run build:data Runs scripts/osm-data.mjs to fetch OSM data → static/data/
npm run build build:data followed by the SvelteKit static build → build/
npm run preview Serves the build/ output locally

Data layout

static/data/
├── _index.json              # Manifest of all built layers (drives the UI)
├── <area>.geojson           # One FeatureCollection per Overpass area
└── custom/
    └── <layer>.geojson      # One FeatureCollection per custom zone/POI layer

The frontend loads /data/_index.json, then fetches each layer's GeoJSON and renders it as a toggleable overlay with a legend (top-right). Every layer can be shown/hidden independently.

Configuring Areas & Queries

Edit scripts/osm-data.mjs — the AREAS object describes each area:

  • bbox[south, west, north, east] in decimal degrees.
  • queries — a map of name → Overpass QL snippet. Use {{bbox}} as the placeholder for the area's bounding box.
  • The script falls back across multiple public Overpass mirrors if one is overloaded, and is polite to the API (short delay between queries).

Example:

parks: `
  way["leisure"="park"]({{bbox}});
  out center tags geom;
`

List configured queries with npm run data -- --queries, or build a single area with npm run data -- --area=tulsa.

Custom Layers (Zones, Points of Interest & Paths)

Besides Overpass-fetched data, you can define your own layers by hand in scripts/custom-layers.mjs. It ships with several example layers (my-places POIs, districts zones, trails path, and rogers-county local POIs) that you can extend or replace.

Each layer has:

  • id — unique slug (becomes the output filename)
  • name — label shown in the UI legend
  • layerType'poi' (point), 'zone' (polygon), or 'path' (LineString)
  • color — hex color used for markers/fill/lines
  • features — array of feature objects

POI (point) — optional address, bearing, and fov fields:

{
  name: 'Gathering Place',
  desc: 'Riverside park',
  address: '2650 S John Williams Way E, Tulsa, OK 74114',
  bearing: 'SE',   // compass point (N, NNE, NE, ...) OR numeric degrees (0-360)
  fov: 140,        // field of view in degrees (optional)
  lon: -95.9791,
  lat: 36.1098
}
  • address — street address shown in the popup
  • bearing — compass direction the POI faces; accepts a compass point ('N', 'NNE', 'NE', ...) or a numeric bearing in degrees
  • fov — field of view in degrees

The popup renders these as dedicated rows (Facing / Field of view).

Zone (polygon — the ring is closed for you):

{
  name: 'Downtown Tulsa',
  desc: 'Rough downtown core',
  polygon: [
    [-96.0000, 36.1300],
    [-95.9900, 36.1300],
    [-95.9900, 36.1600],
    [-96.0000, 36.1600]
  ]
}

Path / route (LineString — an ordered list of points like a GPX track):

{
  name: 'Claremore Lake Loop',
  desc: 'Example walking route around the lake',
  line: [
    [-95.5649, 36.3415],
    [-95.5700, 36.3430],
    [-95.5750, 36.3420],
    [-95.5720, 36.3360]
  ]
}

Paths render as dashed polylines and are searchable by name.

Coordinates are [longitude, latitude] (lon first, like GeoJSON). The file also includes a copy-paste TEMPLATE block for adding new layers. After editing, run npm run build:data (or npm run build) to regenerate the static data.

The webapp includes a fuzzy search in the top bar. It matches your POIs, zones, and paths by name or address — and, as a fallback, queries the Nominatim OSM geocoder so you can also search for any location by name (cities, landmarks, addresses anywhere). A "Places (online)" section appears beneath the local matches; selecting a result pans/zooms the map to it (fit-to-bounds for places with a bounding box).

The matcher:

  • Normalizes text (case, punctuation, and common address words — ststreet, rdroad, aveavenue, nnorth, etc.), so abbreviated or partial addresses resolve correctly.
  • Uses substring + Levenshtein edit-distance tolerance, so typos and near- matches still hit (e.g. philbrick finds Philbrook).
  • Ranks name matches above address matches above description matches.

Deployment

The build/ directory is entirely static. Serve it directly:

cd build
python3 -m http.server 8080
# or: nginx -s  listen 8080; root /path/to/build;  (with a fallback to index.html)

Because the site uses a static adapter with prerendering, everything can be hosted on any static file host or CDN.

Continuous Integration

Gitea Actions runs tests on every commit/push (and on pull requests) via .gitea/workflows/ci.yml. It runs offline (no Overpass/Nominatim calls) so it won't trip API rate limits:

  • npm run check — Svelte/TypeScript checks
  • npm test — data-integrity + fuzzy-search regression suite against committed data
  • npm run build:static — static site build (uses committed static/data)

Requires a registered Gitea Actions runner (label host).

Repository

Hosted on Gitea: https://gitea.thecookiejar.me/hermes-explorigin/navigator

Description
Software project repository
Readme 14 MiB
Languages
Svelte 41.3%
JavaScript 30%
TypeScript 28.5%
HTML 0.2%