Routista is a client-side heavy Next.js application that leverages external APIs for mapping and routing. Image and shape processing happens entirely in the browser (images never leave the device). Route generation requires server-side API calls to Radar, with routes cached temporarily for performance.
-
Image Upload (
ImageUpload.tsx)- User uploads an image.
- Image is drawn to an off-screen Canvas.
imageProcessing.tsextracts the shape as a series of normalized coordinates (0-1).
-
Shape Editor (
ShapeEditor.tsx)- Manual Drawing: Allows users to draw a polygon on a blank canvas.
- Overlay Editing: Allows users to edit the automatically extracted shape (move points, drag corners) overlaid on the original image.
- Simplification: Shapes extracted from images are simplified using the Douglas-Peucker algorithm to reduce noise (points on straight lines) while preserving key vertices.
-
Area Selection (
AreaSelector.tsx)- User selects a center point (lat/lng) and a radius (meters).
geoUtils.tsscales the normalized shape points to real-world coordinates based on this center and radius.
-
Route Generation (
routeGenerator.ts)- The scaled points are simplified (Ramer-Douglas-Peucker) to reduce API load.
- Points are sent to the internal API route (
/api/radar/directions). - The API route proxies the request to the Radar API in chunks to respect limits and avoid CORS.
- Radar returns navigable paths between the points.
- Segments are stitched together to form a continuous
LineString.
-
Visualization (
ResultMap.tsx)- The original shape (scaled) and the generated route are overlaid on a Leaflet map.
- Accuracy is calculated by comparing the two shapes.
- Map Components: Wrappers around
react-leafletto handle map state and interactions. - UI Components: Reusable UI elements (Buttons, Inputs) styled with Tailwind CSS.
ShapeEditor.tsx: A vector-based editor for creating and modifying polygon shapes using SVG. Handles point manipulation (add, drag, delete) and aspect ratio locking.
geoUtils.ts: Pure functions for geographic calculations (distance, scaling, accuracy).routeGenerator.ts: Handles interaction with the ORS API and response processing.imageProcessing.ts: Canvas manipulation, edge detection, and uniform point sampling for consistent shape extraction.
The system doesn't "search" for a shape in the road network in the traditional sense. Instead, it:
- Projects the desired shape onto the map.
- Snaps the shape's points to the nearest road using the routing engine.
- Routes between these snapped points.
To objectively measure how well the route matches the shape, we use a bidirectional error metric:
- Forward Error: Average distance from each point on the original shape to the nearest segment of the route.
- Backward Error: Average distance from sampled points on the route to the nearest point on the original shape.
This prevents "cheating" where a route could just be a single point (low forward error for that point, but high backward error for the rest of the shape) or a massive scribble (low forward error, high backward error).
State is primarily managed in the parent page (src/app/create/page.tsx or similar) and passed down via props. There is no global state management library (Redux/Zustand) as the application flow is linear and simple.
- Routes are cached by hashing coordinates + mode into a cache key
- Cache TTL: 24 hours
- Reduces Radar API calls for identical requests
- Graceful fallback: App works without Redis configured
Files: src/lib/radarService.ts
- IP-based rate limiting: 10 requests per minute per IP
- Implemented in middleware using sliding window algorithm
- Returns 429 with
Retry-Afterheader when exceeded - Blocked requests logged to Sentry
Files: middleware.ts, src/lib/rateLimit.ts
- Client, server, and edge runtime error capture
- Global error boundary for React errors
- API route errors captured with context
- Rate limit blocks logged as warnings
Files: sentry.client.config.ts, sentry.server.config.ts, sentry.edge.config.ts, src/app/global-error.tsx
Anonymous product analytics to understand user behavior and improve the app.
Features:
- Automatic pageview tracking for SPA navigation
- Custom event tracking with type-safe properties
- Localhost protection (events skipped on localhost)
- localStorage+cookie persistence for reliable tracking
Event Flow:
User Action → track() → isLocalhost? → PostHog API
↓ (if localhost)
Console log only
Key Events:
| Event | Purpose |
|---|---|
wizard_started |
Track create page visits |
shape_selected |
Measure shape input methods (upload/example/draw) |
area_selected |
Track area configuration |
generation_request |
Track route generation attempts |
generation_result |
Measure success rate, latency, accuracy |
gpx_exported |
Primary conversion metric |
social_share |
Track sharing engagement |
support_click |
Track donation interest |
Files: src/lib/analytics.ts, src/components/PostHogProvider.tsx
Dashboard: PostHog Cloud (EU instance)
| Variable | Purpose |
|---|---|
NEXT_PUBLIC_RADAR_LIVE_PK |
Radar API key (production) |
NEXT_PUBLIC_RADAR_TEST_PK |
Radar API key (fallback/testing) |
KV_REST_API_URL / UPSTASH_REDIS_REST_URL |
Redis endpoint for caching |
KV_REST_API_TOKEN / UPSTASH_REDIS_REST_TOKEN |
Redis auth token |
NEXT_PUBLIC_SENTRY_DSN |
Sentry error tracking |
SENTRY_AUTH_TOKEN |
Source map uploads |
SENTRY_ORG / SENTRY_PROJECT |
Sentry project identifiers |
NEXT_PUBLIC_POSTHOG_KEY |
PostHog project API key (analytics) |
NEXT_PUBLIC_POSTHOG_HOST |
PostHog API host (eu.i.posthog.com) |
- Hosting: Vercel (Hobby plan)
- CI/CD: Vercel Git Integration + GitHub Actions (security checks, SonarCloud)
- Repository: Connected via Vercel Dashboard, auto-deploys on push
- Static code analysis on every push/PR to main
- Coverage reports uploaded from Vitest (lcov format)
- Quality gate checks for code smells, bugs, vulnerabilities
- Dashboard: https://sonarcloud.io/project/overview?id=JakubAnderwald_routista
Files: sonar-project.properties, .github/workflows/security.yml
- AI-powered code review on every PR
- Generates PR summaries, walkthroughs, and sequence diagrams
- Context-aware feedback on code changes
- Free for open source projects
Files: .coderabbit.yaml
| Environment | Branch | URL | Purpose |
|---|---|---|---|
| Production | main |
routista.eu | Live user traffic |
| Preview | Any other branch | routista-git-<branch>-*.vercel.app |
Testing before merge |
- Automatically created for every push to non-main branches
- Each preview gets a unique URL accessible from any device (including mobile)
- Environment variables can have different values per environment (configured in Vercel Dashboard)
- PR comments from Vercel bot include preview URL
Configure different values for Production vs Preview in Vercel Dashboard:
- Vercel Dashboard → Project → Settings → Environment Variables
- Each variable can have separate values for: Production, Preview, Development
When developing features that need mobile testing, use these prompts:
Create a new branch called feature/[name] and push it to trigger a preview deployment
Commit my current changes and push to the feature branch for mobile testing
What's the Vercel preview URL for my current branch?
→ Pattern: https://routista-git-[branch]-jakubanderwalds-projects.vercel.app
Merge my feature branch to main and push to deploy to production
Push my changes to the feature branch so I can test on mobile
Notes:
- Preview deployments take ~1-2 minutes to build
- Same URL updates with each push
- Deleting Git branch auto-deletes deployment via GitHub Action (
.github/workflows/cleanup-vercel-deployments.yml)
- Feature docs:
docs/features/ - Testing:
docs/technical/AUTOMATED_TESTING.md - Debugging:
docs/technical/DEBUGGING.md - File locations:
docs/technical/CONTEXT_MAP.md