Skip to content

HECer/supercharge-compass

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

21 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

🧭⚑ SuperchargeCompass

Find the nearest Tesla Superchargers by real road distance β€” address in, routed results out.

Node License: MIT Tests Dependencies

Unofficial Β· self-hostable Β· OpenStreetMap-based Β· data refreshed daily

SuperchargeCompass: map with five colour-coded routes from an address in Bretten to the nearest Superchargers, results list on the left

Table of contents

What it does

You type an address (anywhere in Europe). SuperchargeCompass geocodes it, finds the nearest Tesla Superchargers by actual driving distance (not air line), draws the route on an OpenStreetMap map, and lists several alternatives with distance, drive time, stall count, and whether the site is open to non-Tesla EVs.

Everything the web UI does is also available as a JSON API, so you can integrate it into your own tools.

Features

Feature
πŸ“ Address β†’ nearest Superchargers, ranked by real road distance or drive time (OpenRouteService matrix)
πŸ”’ Multiple results (3 / 5 / 10) β€” not just the single closest
πŸ—ΊοΈ Routes on the map β€” click any result to route to it, or enable show all routes to compare them in distinct colours
πŸ”œ "Coming soon" toggle β€” include planned / under-construction sites
πŸ”Œ "Open to all EVs" filter β€” only sites usable by non-Tesla vehicles
πŸ”„ Daily data refresh from two sources, with a stale-data banner if it ever fails
🧩 JSON API for machines β€” same capabilities as the UI
πŸ” Swappable routing backend β€” OpenRouteService today, self-hosted OSRM tomorrow

Why the data stays accurate

Tesla's own location feed keeps stale "coming soon" ghosts β€” sites still flagged β€œTarget opening Q1 2023” years after they actually opened (~19 % of its coming-soon entries when we measured). SuperchargeCompass therefore merges two sources on every daily grab:

Source Role
Tesla all-locations feed Base: official coverage, names, coordinates, coming-soon flags
supercharge.info community DB Corrector/enricher: fixes stale coming soon β†’ open, fills stall counts and open-to-all-EVs flags (coordinate match < 600 m)

Failure behaviour is deliberately conservative:

  • One source unreachable β†’ the other is used alone.
  • Both fail β†’ the grabber refuses to overwrite the last good data file, and the UI shows a stale-data warning banner instead of silently serving nothing.

Quick start

Requirements: Node.js β‰₯ 20.6 and a free OpenRouteService API key (free tier β‰ˆ 2,000 requests/day).

git clone https://github.com/HECer/supercharge-compass.git
cd supercharge-compass
npm install

cp .env.example .env
# open .env and set:  ORS_API_KEY=<your key>

npm run grab     # fetch supercharger data  β†’  data/superchargers.json
npm start        # serve the app            β†’  http://localhost:3000

That's it. The running server re-grabs the data automatically every day at 03:15 (node-cron). If you don't keep the server running 24/7, schedule npm run grab with your OS scheduler (cron / Task Scheduler) instead.

Configuration

All configuration lives in .env (loaded via Node's --env-file):

Variable Default Purpose
PORT 3000 HTTP port
ORS_API_KEY β€” (required) OpenRouteService key (geocoding + routing)
ORS_BASE_URL https://api.openrouteservice.org Point at a self-hosted ORS instance
ROUTING_PROVIDER ors Routing backend selector
DATA_FILE data/superchargers.json Location of the grabbed dataset

API reference

GET /api/nearest β€” find the nearest Superchargers

curl "http://localhost:3000/api/nearest?address=Alexanderplatz%201,%20Berlin&limit=5"
curl "http://localhost:3000/api/nearest?lat=52.52&lon=13.41&comingSoon=true&sort=duration"
Param Type Default Meaning
address or lat+lon string / number β€” Origin. address is geocoded; coordinates skip geocoding
limit number 5 Number of results
sort distance | duration distance Ranking criterion
comingSoon boolean false Include planned sites
openToAllEvs boolean false Only sites open to all EVs
Example response (click to expand)
{
  "origin": { "lat": 49.0355, "lon": 8.7186, "label": "Merianstraße 10, Bretten, BW, Germany" },
  "results": [
    {
      "id": "tesla-bruchsalsupercharger",
      "name": "Bruchsal Supercharger",
      "lat": 49.1292,
      "lon": 8.5855,
      "address": "17 Kammerforststraße, 76646 Bruchsal, Germany",
      "status": "open",
      "openToAllEvs": true,
      "stallCount": 14,
      "distanceMeters": 22648,
      "durationSeconds": 1804
    }
  ],
  "route": {
    "toId": "tesla-bruchsalsupercharger",
    "geometry": { "type": "LineString", "coordinates": [[8.7185, 49.0356], ["…"]] },
    "distanceMeters": 22648,
    "durationSeconds": 1804
  },
  "meta": {
    "dataTimestamp": "2026-07-14T14:41:18.366Z",
    "source": "tesla+supercharge.info",
    "count": 4555,
    "stale": false
  }
}

Errors: 400 missing origin Β· 422 address could not be geocoded Β· 503 routing backend unavailable.

GET /api/route β€” road route between two points

curl "http://localhost:3000/api/route?fromLat=49.03&fromLon=8.71&toLat=49.12&toLon=8.58"

Returns { geometry (GeoJSON LineString), distanceMeters, durationSeconds }. Used by the UI to draw the route to any clicked result. 400 on missing coordinates.

GET /api/superchargers β€” the full site list

curl "http://localhost:3000/api/superchargers?comingSoon=false&openToAllEvs=true"

Returns { superchargers: [...], meta } β€” every normalized site, filterable. Each site:

{
  "id": "tesla-pforzheimsupercharger1",
  "name": "Pforzheim",
  "lat": 48.9051, "lon": 8.6542,
  "address": "Pforzheim, Germany",
  "status": "open",
  "openToAllEvs": true,
  "stallCount": 20,
  "source": "tesla"
}

status is "open" or "coming_soon". stallCount may be null when unknown.

GET /api/meta β€” data freshness

Returns { dataTimestamp, source, count, stale }. stale is true when the dataset is older than 48 hours β€” treat results with caution then.

How it works

                 grabber (daily cron  +  npm run grab)
      Tesla all-locations ──┐
                            β”œβ”€β”€ merge (coordinate match < 600 m)
      supercharge.info β”€β”€β”€β”€β”€β”˜            β”‚
                                         β–Ό
                              data/superchargers.json
                                         β”‚ (loaded in-memory)
   Browser (Leaflet + OSM tiles)         β–Ό
        β”‚                          Express API ──► datastore
        └── /api/nearest ─────────────────
                                         β”‚ 1. geocode address            (ORS, 1 call)
                                         β”‚ 2. haversine pre-filter       (in-memory, 0 calls)
                                         β”‚    β†’ 25 nearest candidates
                                         β”‚ 3. distance matrix 1 β†’ 25     (ORS, 1 call)
                                         β”‚ 4. sort by road distance/time
                                         β”” 5. route geometry for #1      (ORS, 1 call)

The in-memory haversine pre-filter means a search costs ~3 external requests total, no matter how many thousands of sites are in the dataset. Routes to other results are fetched lazily on click and cached client-side.

Swapping the routing provider

src/routing/index.js is a small factory keyed on ROUTING_PROVIDER. A provider is any object with three async methods:

geocode(address)              // β†’ { lat, lon, label } | null
matrix(origin, destinations)  // β†’ [{ distanceMeters, durationSeconds }, …]
route(origin, destination)    // β†’ { geometry, distanceMeters, durationSeconds }

All providers take an injectable fetchImpl so tests never touch the network. To go self-hosted, add src/routing/providers/osrm.js, register it in the factory, set ROUTING_PROVIDER=osrm β€” nothing else changes.

Development & testing

npm test   # 52 tests, Node's built-in test runner β€” zero network access
  • External calls (fetch) are dependency-injected and faked in every test.
  • Fixtures for both data sources live in test/fixtures/.
  • Modules under test in isolation: geo, datastore, routing provider, nearest-search orchestration, both parsers, source merging, grabber safety, HTTP API, server wiring, frontend serving.

Troubleshooting

Symptom Cause & fix
EADDRINUSE on start Port taken (often a previous instance). Change PORT in .env or stop the old process. Restart the server after every .env change.
UI says "Dienst momentan nicht verfΓΌgbar" / API returns 503 ORS_API_KEY missing/invalid, or ORS rate limit hit. Verify the key, wait, retry.
npm run grab reports tesla: … 403 Tesla's endpoint rejected the request; the grabber automatically falls back to supercharge.info. Nothing to do.
Stale-data banner in the UI Last successful grab is > 48 h old. Run npm run grab and check its output.
A site's status looks wrong Data comes from Tesla + supercharge.info; some sites can't be cross-matched. See Why the data stays accurate.

Legal & fair use

  • Not affiliated with, endorsed by, or connected to Tesla, Inc. β€œTesla” and β€œSupercharger” are trademarks of Tesla, Inc., used here solely to describe compatibility (nominative use).
  • Location data is fetched once per day from Tesla's public website and supercharge.info. Accuracy is not guaranteed β€” always verify before you drive.
  • Map data Β© OpenStreetMap contributors. Geocoding & routing by OpenRouteService β€” respect their rate limits and terms of service.

License

MIT Β© HECer

About

Find the nearest Tesla Superchargers by real road distance - unofficial, OSM-based, daily-refreshed data. Web UI + JSON API.

Topics

Resources

License

Stars

1 star

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors