chargers

package
v0.40.21 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 1, 2026 License: MIT Imports: 18 Imported by: 0

Documentation

Overview

Package chargers serves the chargers.pmtiles archive from the rivolt API: the entire ~10 MB file is fetched once at startup (or first request), parsed in-memory, and queries against it run fully server-side. This replaces the SPA's per-tile fan-out over the same archive — for long corridors that was ~hundreds of HTTPS range requests per planner re-render, saturating the browser's per-origin socket pool and feeling sluggish even after the upstream tile server moved to tmpfs.

Wire path: tiles nginx pod serves /chargers.pmtiles → rivolt fetches it once via the in-cluster Service → keeps a *[]byte in memory → answers /api/maps/chargers-along with a flat JSON list.

Index

Constants

View Source
const (
	// MaxPathPoints is the most route points a scan walks; a longer polyline (up to MaxRoutePoints) is thinned to it.
	MaxPathPoints = 5000
	// MaxCorridorKm is the widest search band accepted (half-width).
	MaxCorridorKm = 50.0
)

Limits on a corridor query, so one request cannot ask for a continent of tiles.

View Source
const MaxRoutePoints = 200_000

MaxRoutePoints is the longest route polyline an API caller may send. A long drive's polyline has tens of thousands of points (a 1,300 km route is about 25,000), far over MaxPathPoints; the handler thins it first.

Variables

View Source
var (
	// ErrBadPath is a path with a coordinate that is not a number.
	ErrBadPath = errors.New("chargers: path has a coordinate that is not a number")
	// ErrQueryTooLarge is a path with too many points or spanning too many tiles.
	ErrQueryTooLarge = errors.New("chargers: query is too large")
)

Functions

func PointQueryPath added in v0.40.0

func PointQueryPath(lat, lon, radiusKm float64) [][2]float64

PointQueryPath is the path to give QueryCorridor to ask for everything within radiusKm of a point (use CorridorKm = radiusKm, and filter the answer by distance if the exact circle matters). QueryCorridor is a band along a path and drops a station whose nearest point on the path is an end of it, so a tiny path around a point, or the point twice, finds nothing at all: only a station in a thin strip beside it would pass. A north-south segment through the point, longer than the radius on each side, keeps every station within the radius inside the band.

func ThinPath added in v0.39.2

func ThinPath(path [][2]float64, max int) [][2]float64

ThinPath returns path with points dropped so that at most max remain, keeping the first and the last and spacing the rest evenly along the route, so the shape follows the road to within about the spacing (a few hundred metres on a continental route, against a corridor of kilometres). A path that is already short enough, or has a coordinate that is not a number (QueryCorridor refuses that), is returned as it is.

Types

type Archive

type Archive struct {
	// contains filtered or unexported fields
}

Archive holds the entire chargers PMTiles file in memory plus a decoded root directory. Leaf directories are decoded lazily and cached in leafDirs. Safe for concurrent readers; the only writer is Reload.

func New

func New(url string) *Archive

New returns an empty archive; the caller drives Reload to populate it (so startup wiring can defer the fetch until the URL is known).

func (*Archive) LoadedAt

func (a *Archive) LoadedAt() time.Time

LoadedAt returns the timestamp of the last successful Reload, or the zero Time if the archive has not yet been loaded.

func (*Archive) Nearest added in v0.32.0

func (a *Archive) Nearest(lat, lon, maxM float64, filter Filter) (POI, bool, error)

Nearest returns the closest charger passing filter within maxM meters of (lat, lon). Used to attribute a recorded charging session to the station (and network) it happened at. Searches the point's tile and its eight neighbours, so maxM must stay well under a z14 tile (~2.4 km at the equator). Concurrent-safe.

func (*Archive) QueryCorridor

func (a *Archive) QueryCorridor(path [][2]float64, filter Filter, opts QueryCorridorOptions) ([]POI, error)

QueryCorridor returns all chargers passing `filter` within `corridorKm` of any non-endpoint point on `path`. Concurrent-safe. The query is validated first (see planCorridor); ErrBadPath and ErrQueryTooLarge say why it was refused, and a done opts.Context stops it early with the context's error.

func (*Archive) Refresh added in v0.38.8

func (a *Archive) Refresh(ctx context.Context) (changed bool, err error)

Refresh reloads the archive only when the file behind the URL changed (a conditional GET on the ETag of the loaded copy), so a rebuilt archive is picked up without a restart. changed is false when the server says 304; a server that sends no ETag gets the full file each time (and reports changed). A failed fetch leaves the loaded archive in place.

func (*Archive) Reload

func (a *Archive) Reload(ctx context.Context) error

Reload fetches the .pmtiles file from a.url, parses the header and root directory, and atomically swaps the live state. Existing in-flight queries continue to read the previous bytes via their captured RLock; the next query sees the new state.

func (*Archive) Within added in v0.33.0

func (a *Archive) Within(lat, lon, maxM float64, filter Filter) ([]NearPOI, error)

Within returns every charger passing filter within maxM meters of (lat, lon), nearest first: the whole site, so a caller can tell a co-located one (a Supercharger beside RAN) from a single network. Same tile search and maxM limit as Nearest. Concurrent-safe.

type Filter

type Filter string

Filter matches the SPA's ChargerFilter strings.

const (
	FilterDCFC   Filter = "dcfc"
	FilterL2     Filter = "l2"
	FilterHotels Filter = "hotels"
	FilterCamp   Filter = "camp"
	FilterAll    Filter = "all"
)

type NearPOI added in v0.33.0

type NearPOI struct {
	POI
	DistM float64
}

NearPOI is a charger with its distance from the query point.

type POI

type POI struct {
	Lat           float64 `json:"lat"`
	Lon           float64 `json:"lon"`
	Name          string  `json:"name,omitempty"`
	IsDCFC        bool    `json:"isDCFC"`
	IsL2          bool    `json:"isL2"`
	MaxPowerKW    float64 `json:"maxPowerKW,omitempty"`
	FacilityType  string  `json:"facilityType,omitempty"`
	EVNetwork     string  `json:"evNetwork,omitempty"`
	EVPricing     string  `json:"evPricing,omitempty"`
	EntityID      string  `json:"entityID,omitempty"`
	DCFCCount     int     `json:"dcfcCount,omitempty"`
	L2Count       int     `json:"l2Count,omitempty"`
	SocketTesla   bool    `json:"socketTeslaSupercharger,omitempty"`
	SocketCCS1    bool    `json:"socketType1Combo,omitempty"`
	SocketChademo bool    `json:"socketChademo,omitempty"`
	// SocketJ1772 and SocketNEMA1450 are the AC connectors the listing flags
	// (the archive's socket:type1 and socket:nema_1450).
	SocketJ1772    bool `json:"socketJ1772,omitempty"`
	SocketNEMA1450 bool `json:"socketNema1450,omitempty"`
	// The campground fields come from the Recreation.gov (RIDB) step of the
	// archive build; zero on every NREL-only station. HookupAmps is the
	// highest electric hookup of any campsite, ElectricSites of Sites the
	// number with power. AsOf is the export's date ("2026-10-20").
	HookupAmps    int    `json:"hookupAmps,omitempty"`
	ElectricSites int    `json:"electricSites,omitempty"`
	Sites         int    `json:"sites,omitempty"`
	Reservable    bool   `json:"reservable,omitempty"`
	Source        string `json:"source,omitempty"`
	// RIDBID is the Recreation.gov FacilityID of the campground, set on a
	// station that came from, or was merged with, a RIDB record.
	RIDBID string `json:"ridbID,omitempty"`
	AsOf   string `json:"asof,omitempty"`
}

POI is one charging-station feature as the SPA expects it. Fields mirror the SPA's POI type in web/src/lib/poi.ts so the renderer code is unchanged. Names are camelCase on the JSON wire to match the existing TS type.

func (POI) IsCampground added in v0.38.9

func (poi POI) IsCampground() bool

IsCampground reports whether poi is a campground or RV park with a way to plug in: a listed hookup (HookupAmps), a Level 2 stall or a NEMA 14-50.

type QueryCorridorOptions

type QueryCorridorOptions struct {
	CorridorKm        float64
	EndpointTrimMeter float64
	MinPowerKW        float64
	// If true, includes destinations that project onto the route's
	// endpoint segment. Off by default (matches SPA behaviour).
	IncludeEndpoints bool
	// Context, when set, ends the query early once it is done (a client that
	// went away, a request timeout).
	Context context.Context
}

QueryCorridorOptions tunes the corridor scan; zero values pick the SPA defaults.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL