CLAUDE.md 4.8 KB

GPX Visualizer — Project Notes for Claude

Repository layout

gpx-vis/
├── gpx-vis-backend/     Node.js/Express REST API (default port 3000)
├── gpx-vis-frontend/    Vanilla HTML/CSS/JS SPA (served by any web server)
└── sample/              Standalone drag-and-drop GPX viewer (no backend)

Backend (gpx-vis-backend/)

Stack: Express 5, Sequelize ORM, SQLite (default) or MySQL/MariaDB, bcryptjs, JWT, multer, xml2js

Entry point: index.js Config: Copy config.example.jsconfig.js

Routes:

  • src/routes/auth.js — login, register, /api/auth/me
  • src/routes/directories.js — folder CRUD
  • src/routes/tracks.js — track upload, list, update, delete, share
  • src/routes/stats.js — per-user and per-directory stats
  • src/routes/share.js — public share link access
  • src/routes/admin.js — user management (admin only)

Models: User, Directory (tree via parentId), Track, TrackPoint, ShareLink

Key behaviours:

  • First registered user becomes admin + active; subsequent users need admin activation
  • GPX track points are stored in the DB (TrackPoint table), not as files
  • GPX compression: skip a point if distance < 2 m OR (time < 30 s AND bearing change < 30°)
  • Track date = first GPX timestamp if present, else upload date
  • Share links use 10-char consonant-vowel alternating codes (e.g. bodaveximu)

Tests: npm test — mocha, uses in-memory SQLite via test/setup.js (overrides Module._load for config at module level)

Frontend (gpx-vis-frontend/)

Stack: Vanilla JS, Leaflet.js (CDN), no build step Config: Copy config.example.jsconfig.js

JS modules (loaded in order):

  • js/api.js — all fetch wrappers (API.*)
  • js/auth.js — login/register forms, JWT storage
  • js/map.js — Leaflet map, track layers, hover marker, URL hash state (#map=lat,lng,zoom&tracks=id1,id2&open=id)
  • js/elevation.js — canvas altitude-profile chart, chart↔map hover cross-linking
  • js/browser.js — file/folder tree browser, drag-and-drop, track actions
  • js/stats.js — stats tab rendering
  • js/app.js — global utilities, admin panel, share-page init, main entry point

browser.js design:

  • Tree view with expand/collapse triangles; expanded state survives reloads
  • Clicking a folder selects it (upload context) and loads all its tracks onto the map, then zooms to fit
  • Tracks are draggable onto folders; drop asks for confirmation before moving
  • selectedDirId is the current upload context (new folder / file upload go here)
  • dirContents cache: key 'root' or numeric dir id → {dirs, tracks}
  • dirMeta cache: dir id → {id, name, parentId} (used for breadcrumb path)

elevation.js design:

  • Elevation.setTrack(pts) — accepts flat [{lat,lon,ele,time,dist}] array from MapView.getTrackPoints()
  • Elevation.onMapHover(point) / onMapLeave() — called by map.js when hovering the current track
  • Elevation.formatTooltip(point) — shared HTML formatter used by the Leaflet map tooltip
  • Chart hover calls MapView.showHoverMarker() / hideHoverMarker() to move the map dot
  • Both modules reference each other at call time (not at definition time) — load order doesn't matter

map.js hover design:

  • flattenPoints(trackData) converts segments [[lat,lon,ele,time],…][{lat,lon,ele,time,dist}] with cumulative haversine distance
  • On every mousemove, iterates all loaded track points to find the nearest within HOVER_TOLERANCE_PX = 20 px
  • If nearest point belongs to currentTrackId, calls Elevation.onMapHover(); otherwise calls Elevation.onMapLeave()
  • Click on the map pins/unpins the hover tooltip (sticky mode)

Deployment — when to restart the backend

Backend restart required (changes to Node.js source, config, or dependencies):

  • Any edit under gpx-vis-backend/src/
  • Changes to gpx-vis-backend/index.js or config.js
  • Running npm install (new/updated packages)
  • After git pull when any backend file changed

No restart needed (frontend-only changes):

  • Any edit under gpx-vis-frontend/ (HTML, CSS, JS, config.js)
  • Changes to CLAUDE.md, README.md, or other docs

After git pull, check git diff HEAD~1 --name-only — if all changed files are under gpx-vis-frontend/ or are docs, skip the restart.

Development notes

  • Do not mock the database in tests — test/setup.js wires real in-memory SQLite
  • All track geometry is stored as TrackPoint rows; no file storage after upload
  • The Module._load override in test/setup.js is required because config.js is loaded at module level in several files
  • API.getTracks('') returns root-level tracks (directoryId = null)
  • Track point array format from /api/tracks/:id/points: segments: [[[lat, lon, elevation, time], …], …]