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 }