113 lines
4.2 KiB
Markdown
113 lines
4.2 KiB
Markdown
|
|
# CLAUDE.md
|
|||
|
|
|
|||
|
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|||
|
|
|
|||
|
|
## What This Project Is
|
|||
|
|
|
|||
|
|
**Line of Sight** is a geospatial web app that draws a great-circle line from a user-selected point on Earth in a chosen direction, then finds all cities along that path. It includes a flight-over animation that speeds up 20x over water and slows to normal speed over land.
|
|||
|
|
|
|||
|
|
## Commands
|
|||
|
|
|
|||
|
|
### Docker (preferred workflow)
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
docker-compose up --build # Start all services (frontend :3050, backend :3001, postgres)
|
|||
|
|
docker-compose down # Stop all services
|
|||
|
|
docker-compose logs -f # View logs
|
|||
|
|
|
|||
|
|
# Run tests inside containers
|
|||
|
|
docker-compose exec backend npm test
|
|||
|
|
docker-compose exec frontend npm test -- --run
|
|||
|
|
docker-compose exec frontend npm run test:e2e
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Local development (without Docker)
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# Backend
|
|||
|
|
cd backend && npm run dev # Nodemon dev server on :3001
|
|||
|
|
|
|||
|
|
# Frontend
|
|||
|
|
cd frontend && npm run start # Vite dev server on :3050
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Tests
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# Backend unit tests (Jest + Supertest)
|
|||
|
|
cd backend && npm test
|
|||
|
|
|
|||
|
|
# Frontend unit tests (Vitest)
|
|||
|
|
cd frontend && npm test # Watch mode
|
|||
|
|
cd frontend && npm test -- --run # Single run (CI mode)
|
|||
|
|
|
|||
|
|
# E2E tests (Playwright)
|
|||
|
|
cd frontend && npm run test:e2e
|
|||
|
|
|
|||
|
|
# Seed the database with GeoNames city data
|
|||
|
|
cd backend && npm run seed-data
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Build
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
cd frontend && npm run build # Vite production build → frontend/build/
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## Architecture
|
|||
|
|
|
|||
|
|
This is a monorepo with three services orchestrated by Docker Compose:
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
frontend/ → React 19 + Vite 6 SPA (MapLibre GL, Turf.js, Axios)
|
|||
|
|
backend/ → Node 24 + Express 5 REST API
|
|||
|
|
docker/ → PostgreSQL + PostGIS initialization
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Data flow
|
|||
|
|
|
|||
|
|
1. User clicks map → sets start point (lat/lon)
|
|||
|
|
2. User selects direction (0–360°) and tolerance (km radius)
|
|||
|
|
3. Frontend calls `GET /api/line-of-sight?lat=&lon=&direction=&tolerance=`
|
|||
|
|
4. Backend generates 80 path points along a great-circle arc (up to 20,000 km)
|
|||
|
|
5. For each point, a PostGIS `ST_DWithin()` query finds cities within tolerance
|
|||
|
|
6. Backend also checks whether each point is over land (500 km radius land check)
|
|||
|
|
7. Response includes line coordinates with per-point water flags and matching cities
|
|||
|
|
8. Frontend renders the path on a 3D globe map and shows city markers
|
|||
|
|
|
|||
|
|
### Backend: `backend/app/server.js`
|
|||
|
|
|
|||
|
|
Single-file Express app. Key internals:
|
|||
|
|
- `calculateDestination()` — Haversine formula for great-circle destination points
|
|||
|
|
- `GET /api/line-of-sight` — main endpoint: path generation + PostGIS city lookup
|
|||
|
|
- `GET /api/health` — health check
|
|||
|
|
- Returns up to 200 cities ordered by position along the line
|
|||
|
|
|
|||
|
|
### Frontend: `frontend/src/App.jsx`
|
|||
|
|
|
|||
|
|
Single large React component (~687 lines) managing all state and map logic:
|
|||
|
|
- MapLibre GL map with 3D globe projection and terrain/sky effects
|
|||
|
|
- Direction slider drives a live preview line before the user commits
|
|||
|
|
- After "Show Line of Sight", map locks (prevents moving start point)
|
|||
|
|
- Flight animation uses Turf.js to interpolate positions along the path; speed is 1× over land, 20× over water with smooth acceleration (0.005 step), predictive look-ahead of 2000 km
|
|||
|
|
- New cities are fetched every 2000 km during flight via the same API
|
|||
|
|
- Three map styles: light, dark, satellite (Esri tiles)
|
|||
|
|
|
|||
|
|
### Database
|
|||
|
|
|
|||
|
|
PostGIS `cities` table with a GIST index on `geom GEOGRAPHY(POINT, 4326)`. All coordinates use WGS84 / SRID 4326. The init SQL seeds 10 major cities; the `seed-data` script imports from GeoNames for a fuller dataset.
|
|||
|
|
|
|||
|
|
## Conventions
|
|||
|
|
|
|||
|
|
- **Geospatial**: WGS84 / SRID 4326 everywhere; PostGIS `GEOGRAPHY` type for distance queries
|
|||
|
|
- **API**: RESTful; query params for `GET /api/line-of-sight`; env var `VITE_API_URL` (frontend) or `DATABASE_URL` (backend)
|
|||
|
|
- **Styling**: Plain CSS files in `frontend/src/styles/` — no CSS framework
|
|||
|
|
- **Tests**: Backend mocks the `pg` Pool; frontend mocks MapLibre GL and the API service. E2E tests use Playwright with a mock API response
|
|||
|
|
|
|||
|
|
## CI
|
|||
|
|
|
|||
|
|
Gitea workflow (`.gitea/workflows/test.yml`) runs on push/PR to `main`:
|
|||
|
|
1. **backend-test** — `npm ci && npm test`
|
|||
|
|
2. **frontend-test** — `npm ci && npm test -- --run`
|
|||
|
|
3. **e2e-test** — uses `mcr.microsoft.com/playwright:v1.50.1` container
|