trips/router/internal/plan/corridor.go
Greg Pomerantz efde2cc71b maps project: design, survey, mock app, and route-aware planning backend
- DESIGN.md: full design incl. driving-trip requirements (R1-R4),
  stays model, focus mode, mobile, provenance rules
- SURVEY.md: open-source landscape
- mock/: interaction mock (Florence itinerary, focus mode, stays,
  region stops, mobile layout)
- router/: Go module (stdlib-only) with Router interface
  (Valhalla + OSRM backends), stop_cost, optimize_stops, corridor,
  routectl CLI, bench (5 real NE-corridor tasks, 26 checks passing),
  integration tests, and setup-osrm.sh for the self-hosted router
- osm/: NH+MA+CT+NY PBFs (gitignored) + setup artifacts
2026-09-06 00:05:17 -04:00

70 lines
2.0 KiB
Go

package plan
import (
"maps/router/internal/geo"
"maps/router/internal/route"
)
// Corridor is the spatial constraint for "along the route" search
// (DESIGN.md R2). It is a buffer band around the routed polyline.
//
// Quality depends on the router: local OSRM returns full geometry
// (exact corridor); hosted Valhalla does not, so we fall back to a
// band around the great-circle chord (coarse, flagged).
type Corridor struct {
Polyline []geo.Point // may be nil → chord fallback
Origin geo.Point
Dest geo.Point
BufferM float64 // band half-width
FromRoute *route.Route
}
// NewCorridor wraps a computed route. BufferM is the corridor half-width.
func NewCorridor(r *route.Route, bufferM float64) *Corridor {
c := &Corridor{
Polyline: r.Geometry,
BufferM: bufferM,
FromRoute: r,
}
if len(r.Legs) > 0 {
// endpoints aren't in the Route struct; caller sets them
}
return c
}
// SetEndpoints records A and C (needed for the chord fallback).
func (c *Corridor) SetEndpoints(a, dst geo.Point) {
c.Origin = a
c.Dest = dst
}
// Exact reports whether the corridor is built on the true route
// geometry (vs the chord fallback).
func (c *Corridor) Exact() bool { return len(c.Polyline) >= 2 }
// Contains reports whether p lies within the corridor band.
func (c *Corridor) Contains(p geo.Point) bool {
if c.Exact() {
return geo.DistToPolylineMeters(p, c.Polyline) <= c.BufferM
}
// Chord fallback: distance to the A→C great-circle chord.
return geo.DistToPolylineMeters(p, []geo.Point{c.Origin, c.Dest}) <= c.BufferM
}
// ChordSample returns a sampled chord polyline (for map drawing when
// the router gave no geometry).
func (c *Corridor) ChordSample(n int) []geo.Point {
if c.Exact() {
return c.Polyline
}
poly := make([]geo.Point, 0, n+1)
for i := 0; i <= n; i++ {
t := float64(i) / float64(n)
poly = append(poly, geo.Point{
Lat: c.Origin.Lat + t*(c.Dest.Lat-c.Origin.Lat),
Lon: c.Origin.Lon + t*(c.Dest.Lon-c.Origin.Lon),
})
}
return poly
}