rendezvous-kit 0.0.0-development
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +184 -0
- package/dist/engines/graphhopper.d.ts +14 -0
- package/dist/engines/graphhopper.d.ts.map +1 -0
- package/dist/engines/graphhopper.js +72 -0
- package/dist/engines/graphhopper.js.map +1 -0
- package/dist/engines/openrouteservice.d.ts +14 -0
- package/dist/engines/openrouteservice.d.ts.map +1 -0
- package/dist/engines/openrouteservice.js +79 -0
- package/dist/engines/openrouteservice.js.map +1 -0
- package/dist/engines/osrm.d.ts +11 -0
- package/dist/engines/osrm.d.ts.map +1 -0
- package/dist/engines/osrm.js +48 -0
- package/dist/engines/osrm.js.map +1 -0
- package/dist/engines/valhalla.d.ts +11 -0
- package/dist/engines/valhalla.d.ts.map +1 -0
- package/dist/engines/valhalla.js +72 -0
- package/dist/engines/valhalla.js.map +1 -0
- package/dist/geo.d.ts +16 -0
- package/dist/geo.d.ts.map +1 -0
- package/dist/geo.js +142 -0
- package/dist/geo.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/rendezvous.d.ts +14 -0
- package/dist/rendezvous.d.ts.map +1 -0
- package/dist/rendezvous.js +72 -0
- package/dist/rendezvous.js.map +1 -0
- package/dist/types.d.ts +73 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/venues.d.ts +7 -0
- package/dist/venues.d.ts.map +1 -0
- package/dist/venues.js +74 -0
- package/dist/venues.js.map +1 -0
- package/llms-full.txt +481 -0
- package/llms.txt +86 -0
- package/package.json +76 -0
package/llms-full.txt
ADDED
|
@@ -0,0 +1,481 @@
|
|
|
1
|
+
# rendezvous-kit — Full API Reference
|
|
2
|
+
|
|
3
|
+
> TypeScript library for finding fair meeting points for N people — isochrone intersection, venue search, and fairness scoring.
|
|
4
|
+
|
|
5
|
+
One runtime dependency: geohash-kit. Zero third-party dependencies. ESM-only.
|
|
6
|
+
|
|
7
|
+
Repository: https://github.com/TheCryptoDonkey/rendezvous-kit
|
|
8
|
+
Licence: MIT
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
npm install rendezvous-kit
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Imports
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
// Barrel (everything)
|
|
20
|
+
import { findRendezvous, ValhallaEngine, intersectPolygons } from 'rendezvous-kit'
|
|
21
|
+
|
|
22
|
+
// Subpath (tree-shakeable)
|
|
23
|
+
import { findRendezvous } from 'rendezvous-kit/rendezvous'
|
|
24
|
+
import { intersectPolygons, boundingBox, centroid, polygonArea } from 'rendezvous-kit/geo'
|
|
25
|
+
import { ValhallaEngine } from 'rendezvous-kit/engines/valhalla'
|
|
26
|
+
import { OpenRouteServiceEngine } from 'rendezvous-kit/engines/openrouteservice'
|
|
27
|
+
import { GraphHopperEngine } from 'rendezvous-kit/engines/graphhopper'
|
|
28
|
+
import { OsrmEngine } from 'rendezvous-kit/engines/osrm'
|
|
29
|
+
import { searchVenues } from 'rendezvous-kit/venues'
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## Types
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
/** A point with coordinates and optional label. */
|
|
38
|
+
interface LatLon {
|
|
39
|
+
lat: number
|
|
40
|
+
lon: number
|
|
41
|
+
label?: string
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Standard GeoJSON polygon geometry. Coordinates are [longitude, latitude] pairs. */
|
|
45
|
+
interface GeoJSONPolygon {
|
|
46
|
+
type: 'Polygon'
|
|
47
|
+
coordinates: number[][][]
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Transport mode for routing calculations. */
|
|
51
|
+
type TransportMode = 'drive' | 'cycle' | 'walk' | 'public_transit'
|
|
52
|
+
|
|
53
|
+
/** Fairness strategy for rendezvous scoring. */
|
|
54
|
+
type FairnessStrategy = 'min_max' | 'min_total' | 'min_variance'
|
|
55
|
+
|
|
56
|
+
/** Venue type for filtering. Custom strings are passed through to Overpass as amenity=<value>. */
|
|
57
|
+
type VenueType =
|
|
58
|
+
| 'park'
|
|
59
|
+
| 'cafe'
|
|
60
|
+
| 'restaurant'
|
|
61
|
+
| 'service_station'
|
|
62
|
+
| 'library'
|
|
63
|
+
| 'pub'
|
|
64
|
+
| 'playground'
|
|
65
|
+
| 'community_centre'
|
|
66
|
+
| string
|
|
67
|
+
|
|
68
|
+
/** Result of an isochrone computation. */
|
|
69
|
+
interface Isochrone {
|
|
70
|
+
origin: LatLon
|
|
71
|
+
mode: TransportMode
|
|
72
|
+
timeMinutes: number
|
|
73
|
+
polygon: GeoJSONPolygon
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** A single cell in a route matrix. */
|
|
77
|
+
interface MatrixEntry {
|
|
78
|
+
originIndex: number
|
|
79
|
+
destinationIndex: number
|
|
80
|
+
durationMinutes: number
|
|
81
|
+
distanceKm: number
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** Result of a route matrix computation. */
|
|
85
|
+
interface RouteMatrix {
|
|
86
|
+
origins: LatLon[]
|
|
87
|
+
destinations: LatLon[]
|
|
88
|
+
entries: MatrixEntry[]
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** A venue found within the rendezvous zone. */
|
|
92
|
+
interface Venue {
|
|
93
|
+
name: string
|
|
94
|
+
lat: number
|
|
95
|
+
lon: number
|
|
96
|
+
venueType: VenueType
|
|
97
|
+
osmId?: string // e.g. 'node/123456'
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** Options for rendezvous calculation. */
|
|
101
|
+
interface RendezvousOptions {
|
|
102
|
+
participants: LatLon[] // at least 2 required
|
|
103
|
+
mode: TransportMode
|
|
104
|
+
maxTimeMinutes: number // isochrone time limit
|
|
105
|
+
venueTypes: VenueType[]
|
|
106
|
+
fairness?: FairnessStrategy // default: 'min_max'
|
|
107
|
+
limit?: number // max suggestions to return, default: 5
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** A ranked rendezvous suggestion. */
|
|
111
|
+
interface RendezvousSuggestion {
|
|
112
|
+
venue: Venue
|
|
113
|
+
travelTimes: Record<string, number> // keyed by participant label or 'participant_N'
|
|
114
|
+
fairnessScore: number // lower is better
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** Engine-agnostic routing interface. Implement this to add a custom engine. */
|
|
118
|
+
interface RoutingEngine {
|
|
119
|
+
readonly name: string
|
|
120
|
+
computeIsochrone(origin: LatLon, mode: TransportMode, timeMinutes: number): Promise<Isochrone>
|
|
121
|
+
computeRouteMatrix(origins: LatLon[], destinations: LatLon[], mode: TransportMode): Promise<RouteMatrix>
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** Bounding box (from rendezvous-kit/geo). */
|
|
125
|
+
interface BBox {
|
|
126
|
+
minLon: number
|
|
127
|
+
minLat: number
|
|
128
|
+
maxLon: number
|
|
129
|
+
maxLat: number
|
|
130
|
+
}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## rendezvous-kit/rendezvous
|
|
136
|
+
|
|
137
|
+
### findRendezvous(engine, options)
|
|
138
|
+
|
|
139
|
+
Run the full rendezvous pipeline and return ranked venue suggestions.
|
|
140
|
+
|
|
141
|
+
**Algorithm:**
|
|
142
|
+
1. Compute an isochrone polygon for each participant
|
|
143
|
+
2. Intersect all isochrone polygons (Sutherland–Hodgman)
|
|
144
|
+
3. Search for venues within the intersection bounding box (Overpass API)
|
|
145
|
+
4. Compute a route matrix from all participants to all candidate venues
|
|
146
|
+
5. Score each venue by the selected fairness strategy
|
|
147
|
+
6. Sort ascending by score and return the top `limit` results
|
|
148
|
+
|
|
149
|
+
Returns an empty array if the isochrones do not overlap. Falls back to the geometric centroid if no venues are found in the intersection.
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
findRendezvous(engine: RoutingEngine, options: RendezvousOptions): Promise<RendezvousSuggestion[]>
|
|
153
|
+
|
|
154
|
+
// Throws RangeError if participants.length < 2
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
**Example — three participants, Valhalla, minimise worst case:**
|
|
158
|
+
|
|
159
|
+
```typescript
|
|
160
|
+
import { findRendezvous } from 'rendezvous-kit'
|
|
161
|
+
import { ValhallaEngine } from 'rendezvous-kit/engines/valhalla'
|
|
162
|
+
|
|
163
|
+
const engine = new ValhallaEngine({ baseUrl: 'http://localhost:8002' })
|
|
164
|
+
|
|
165
|
+
const suggestions = await findRendezvous(engine, {
|
|
166
|
+
participants: [
|
|
167
|
+
{ lat: 51.5074, lon: -0.1278, label: 'Alice' }, // London
|
|
168
|
+
{ lat: 51.4545, lon: -2.5879, label: 'Bob' }, // Bristol
|
|
169
|
+
{ lat: 52.4862, lon: -1.8904, label: 'Carol' }, // Birmingham
|
|
170
|
+
],
|
|
171
|
+
mode: 'drive',
|
|
172
|
+
maxTimeMinutes: 90,
|
|
173
|
+
venueTypes: ['cafe', 'restaurant'],
|
|
174
|
+
fairness: 'min_max',
|
|
175
|
+
limit: 5,
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
// suggestions[0] is the venue with the lowest worst-case travel time
|
|
179
|
+
console.log(suggestions[0].venue.name)
|
|
180
|
+
console.log(suggestions[0].travelTimes) // { Alice: 72.3, Bob: 81.1, Carol: 68.4 }
|
|
181
|
+
console.log(suggestions[0].fairnessScore) // 81.1 (max of travel times, for min_max)
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
**Example — two participants, OpenRouteService, equalise travel:**
|
|
185
|
+
|
|
186
|
+
```typescript
|
|
187
|
+
import { findRendezvous } from 'rendezvous-kit'
|
|
188
|
+
import { OpenRouteServiceEngine } from 'rendezvous-kit/engines/openrouteservice'
|
|
189
|
+
|
|
190
|
+
const engine = new OpenRouteServiceEngine({ apiKey: 'your-api-key' })
|
|
191
|
+
|
|
192
|
+
const suggestions = await findRendezvous(engine, {
|
|
193
|
+
participants: [
|
|
194
|
+
{ lat: 48.8566, lon: 2.3522, label: 'Paris' },
|
|
195
|
+
{ lat: 50.8503, lon: 4.3517, label: 'Brussels' },
|
|
196
|
+
],
|
|
197
|
+
mode: 'drive',
|
|
198
|
+
maxTimeMinutes: 120,
|
|
199
|
+
venueTypes: ['restaurant'],
|
|
200
|
+
fairness: 'min_variance',
|
|
201
|
+
})
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
## Fairness Strategies
|
|
207
|
+
|
|
208
|
+
### min_max (default)
|
|
209
|
+
Minimises the maximum travel time among all participants.
|
|
210
|
+
|
|
211
|
+
Score = `Math.max(...travelTimes)`
|
|
212
|
+
|
|
213
|
+
Best when you want to guarantee nobody has an excessively long journey. The classic minimax fairness criterion.
|
|
214
|
+
|
|
215
|
+
### min_total
|
|
216
|
+
Minimises the total travel time across all participants.
|
|
217
|
+
|
|
218
|
+
Score = `sum(travelTimes)`
|
|
219
|
+
|
|
220
|
+
Best when you want the group's combined travel effort to be as low as possible. Can allow one person to travel much further than others.
|
|
221
|
+
|
|
222
|
+
### min_variance
|
|
223
|
+
Minimises variance in travel times (standard deviation).
|
|
224
|
+
|
|
225
|
+
Score = `sqrt(variance(travelTimes))`
|
|
226
|
+
|
|
227
|
+
Best when you want everyone to travel roughly the same distance. Venues near the geometric centre of the group tend to score well.
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
## rendezvous-kit/geo
|
|
232
|
+
|
|
233
|
+
### intersectPolygons(polygons)
|
|
234
|
+
|
|
235
|
+
Compute the intersection of N GeoJSON polygons using the Sutherland–Hodgman algorithm. Returns `null` if the intersection is empty or if any input polygon is degenerate.
|
|
236
|
+
|
|
237
|
+
```typescript
|
|
238
|
+
intersectPolygons(polygons: GeoJSONPolygon[]): GeoJSONPolygon | null
|
|
239
|
+
|
|
240
|
+
// Single polygon — returned unchanged
|
|
241
|
+
intersectPolygons([poly]) // poly
|
|
242
|
+
|
|
243
|
+
// Two overlapping polygons
|
|
244
|
+
intersectPolygons([polyA, polyB]) // intersection polygon
|
|
245
|
+
|
|
246
|
+
// No overlap
|
|
247
|
+
intersectPolygons([polyA, polyFarAway]) // null
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
### boundingBox(polygon)
|
|
251
|
+
|
|
252
|
+
Compute the axis-aligned bounding box of a GeoJSON polygon.
|
|
253
|
+
|
|
254
|
+
```typescript
|
|
255
|
+
boundingBox(polygon: GeoJSONPolygon): BBox
|
|
256
|
+
|
|
257
|
+
boundingBox(poly)
|
|
258
|
+
// { minLon: -0.15, minLat: 51.50, maxLon: -0.10, maxLat: 51.52 }
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### centroid(polygon)
|
|
262
|
+
|
|
263
|
+
Compute the arithmetic centroid of the outer ring of a GeoJSON polygon.
|
|
264
|
+
|
|
265
|
+
```typescript
|
|
266
|
+
centroid(polygon: GeoJSONPolygon): { lat: number; lon: number }
|
|
267
|
+
|
|
268
|
+
centroid(poly)
|
|
269
|
+
// { lat: 51.51, lon: -0.125 }
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
### polygonArea(polygon)
|
|
273
|
+
|
|
274
|
+
Compute the area of a GeoJSON polygon in square metres using the shoelace formula with local metric projection.
|
|
275
|
+
|
|
276
|
+
```typescript
|
|
277
|
+
polygonArea(polygon: GeoJSONPolygon): number
|
|
278
|
+
|
|
279
|
+
polygonArea(poly) // area in m²
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
---
|
|
283
|
+
|
|
284
|
+
## Engines
|
|
285
|
+
|
|
286
|
+
### ValhallaEngine
|
|
287
|
+
|
|
288
|
+
Self-hosted [Valhalla](https://github.com/valhalla/valhalla) routing engine. Supports isochrone and route matrix. No authentication required.
|
|
289
|
+
|
|
290
|
+
```typescript
|
|
291
|
+
class ValhallaEngine implements RoutingEngine {
|
|
292
|
+
readonly name = 'Valhalla'
|
|
293
|
+
constructor(config: { baseUrl: string })
|
|
294
|
+
}
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
```typescript
|
|
298
|
+
import { ValhallaEngine } from 'rendezvous-kit/engines/valhalla'
|
|
299
|
+
|
|
300
|
+
const engine = new ValhallaEngine({ baseUrl: 'http://localhost:8002' })
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
Transport mode mapping: `drive → auto`, `cycle → bicycle`, `walk → pedestrian`, `public_transit → multimodal`
|
|
304
|
+
|
|
305
|
+
### OpenRouteServiceEngine
|
|
306
|
+
|
|
307
|
+
[OpenRouteService](https://openrouteservice.org/) cloud or self-hosted. Supports isochrone and route matrix. Requires API key for the cloud service.
|
|
308
|
+
|
|
309
|
+
```typescript
|
|
310
|
+
class OpenRouteServiceEngine implements RoutingEngine {
|
|
311
|
+
readonly name = 'OpenRouteService'
|
|
312
|
+
constructor(options: { apiKey: string; baseUrl?: string })
|
|
313
|
+
// baseUrl defaults to 'https://api.openrouteservice.org'
|
|
314
|
+
}
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
```typescript
|
|
318
|
+
import { OpenRouteServiceEngine } from 'rendezvous-kit/engines/openrouteservice'
|
|
319
|
+
|
|
320
|
+
// Cloud (requires free API key from openrouteservice.org)
|
|
321
|
+
const engine = new OpenRouteServiceEngine({ apiKey: 'your-api-key' })
|
|
322
|
+
|
|
323
|
+
// Self-hosted
|
|
324
|
+
const engine = new OpenRouteServiceEngine({
|
|
325
|
+
apiKey: 'local',
|
|
326
|
+
baseUrl: 'http://localhost:8080/ors',
|
|
327
|
+
})
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
Note: ORS does not support `public_transit`; the engine falls back to `driving-car`.
|
|
331
|
+
|
|
332
|
+
### GraphHopperEngine
|
|
333
|
+
|
|
334
|
+
[GraphHopper](https://www.graphhopper.com/) cloud or self-hosted. Supports isochrone and route matrix. API key is optional for self-hosted instances.
|
|
335
|
+
|
|
336
|
+
```typescript
|
|
337
|
+
class GraphHopperEngine implements RoutingEngine {
|
|
338
|
+
readonly name = 'GraphHopper'
|
|
339
|
+
constructor(config: { baseUrl: string; apiKey?: string })
|
|
340
|
+
}
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
```typescript
|
|
344
|
+
import { GraphHopperEngine } from 'rendezvous-kit/engines/graphhopper'
|
|
345
|
+
|
|
346
|
+
// Self-hosted (no key needed)
|
|
347
|
+
const engine = new GraphHopperEngine({ baseUrl: 'http://localhost:8989' })
|
|
348
|
+
|
|
349
|
+
// Cloud
|
|
350
|
+
const engine = new GraphHopperEngine({
|
|
351
|
+
baseUrl: 'https://graphhopper.com/api/1',
|
|
352
|
+
apiKey: 'your-api-key',
|
|
353
|
+
})
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
### OsrmEngine
|
|
357
|
+
|
|
358
|
+
Self-hosted [OSRM](http://project-osrm.org/). Route matrix only — `computeIsochrone` throws an error. No authentication required.
|
|
359
|
+
|
|
360
|
+
```typescript
|
|
361
|
+
class OsrmEngine implements RoutingEngine {
|
|
362
|
+
readonly name = 'OSRM'
|
|
363
|
+
constructor(config: { baseUrl: string })
|
|
364
|
+
// computeIsochrone() throws — use Valhalla, ORS, or GraphHopper for isochrones
|
|
365
|
+
}
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
```typescript
|
|
369
|
+
import { OsrmEngine } from 'rendezvous-kit/engines/osrm'
|
|
370
|
+
|
|
371
|
+
const engine = new OsrmEngine({ baseUrl: 'http://localhost:5000' })
|
|
372
|
+
|
|
373
|
+
// Use directly for matrix-only workflows
|
|
374
|
+
const matrix = await engine.computeRouteMatrix(origins, destinations, 'drive')
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
---
|
|
378
|
+
|
|
379
|
+
## rendezvous-kit/venues
|
|
380
|
+
|
|
381
|
+
### searchVenues(polygon, venueTypes, overpassUrl?)
|
|
382
|
+
|
|
383
|
+
Search for venues within a polygon using the [Overpass API](https://overpass-api.de/). Queries the bounding box of the polygon. Named nodes only.
|
|
384
|
+
|
|
385
|
+
```typescript
|
|
386
|
+
searchVenues(
|
|
387
|
+
polygon: GeoJSONPolygon,
|
|
388
|
+
venueTypes: VenueType[],
|
|
389
|
+
overpassUrl?: string, // defaults to 'https://overpass-api.de/api/interpreter'
|
|
390
|
+
): Promise<Venue[]>
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
```typescript
|
|
394
|
+
import { searchVenues } from 'rendezvous-kit/venues'
|
|
395
|
+
import { intersectPolygons } from 'rendezvous-kit/geo'
|
|
396
|
+
|
|
397
|
+
const intersection = intersectPolygons([isoA.polygon, isoB.polygon])
|
|
398
|
+
if (intersection) {
|
|
399
|
+
const venues = await searchVenues(intersection, ['cafe', 'restaurant'])
|
|
400
|
+
// [{ name: 'The Red Lion', lat: 51.9, lon: -1.4, venueType: 'pub', osmId: 'node/123' }, ...]
|
|
401
|
+
}
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
Built-in `VenueType` to Overpass tag mapping:
|
|
405
|
+
|
|
406
|
+
| VenueType | Overpass tag |
|
|
407
|
+
|-----------|-------------|
|
|
408
|
+
| `park` | `leisure=park` |
|
|
409
|
+
| `cafe` | `amenity=cafe` |
|
|
410
|
+
| `restaurant` | `amenity=restaurant` |
|
|
411
|
+
| `service_station` | `amenity=fuel` |
|
|
412
|
+
| `library` | `amenity=library` |
|
|
413
|
+
| `pub` | `amenity=pub` |
|
|
414
|
+
| `playground` | `leisure=playground` |
|
|
415
|
+
| `community_centre` | `amenity=community_centre` |
|
|
416
|
+
|
|
417
|
+
Unknown strings are passed through as `amenity=<value>`.
|
|
418
|
+
|
|
419
|
+
---
|
|
420
|
+
|
|
421
|
+
## Implementing a Custom Engine
|
|
422
|
+
|
|
423
|
+
```typescript
|
|
424
|
+
import type { RoutingEngine, LatLon, TransportMode, Isochrone, RouteMatrix } from 'rendezvous-kit'
|
|
425
|
+
|
|
426
|
+
class MyRoutingEngine implements RoutingEngine {
|
|
427
|
+
readonly name = 'MyEngine'
|
|
428
|
+
|
|
429
|
+
async computeIsochrone(
|
|
430
|
+
origin: LatLon,
|
|
431
|
+
mode: TransportMode,
|
|
432
|
+
timeMinutes: number,
|
|
433
|
+
): Promise<Isochrone> {
|
|
434
|
+
const polygon = await myApi.getIsochrone(origin.lat, origin.lon, timeMinutes)
|
|
435
|
+
return { origin, mode, timeMinutes, polygon }
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
async computeRouteMatrix(
|
|
439
|
+
origins: LatLon[],
|
|
440
|
+
destinations: LatLon[],
|
|
441
|
+
mode: TransportMode,
|
|
442
|
+
): Promise<RouteMatrix> {
|
|
443
|
+
const raw = await myApi.getMatrix(origins, destinations)
|
|
444
|
+
const entries = []
|
|
445
|
+
for (let oi = 0; oi < origins.length; oi++) {
|
|
446
|
+
for (let di = 0; di < destinations.length; di++) {
|
|
447
|
+
entries.push({
|
|
448
|
+
originIndex: oi,
|
|
449
|
+
destinationIndex: di,
|
|
450
|
+
durationMinutes: raw[oi][di].seconds / 60,
|
|
451
|
+
distanceKm: raw[oi][di].metres / 1000,
|
|
452
|
+
})
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
return { origins, destinations, entries }
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
// Use it with findRendezvous
|
|
460
|
+
const suggestions = await findRendezvous(new MyRoutingEngine(), options)
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
---
|
|
464
|
+
|
|
465
|
+
## Engine Comparison
|
|
466
|
+
|
|
467
|
+
| Engine | Isochrone | Matrix | Auth | Notes |
|
|
468
|
+
|--------|:---------:|:------:|------|-------|
|
|
469
|
+
| Valhalla | Yes | Yes | None | Best self-hosted option; supports public transit |
|
|
470
|
+
| OpenRouteService | Yes | Yes | API key | Free tier available; no public transit |
|
|
471
|
+
| GraphHopper | Yes | Yes | Optional | API key only for cloud; optional self-hosted |
|
|
472
|
+
| OSRM | No | Yes | None | Fastest matrix; no isochrone support |
|
|
473
|
+
|
|
474
|
+
---
|
|
475
|
+
|
|
476
|
+
## Companion Library
|
|
477
|
+
|
|
478
|
+
**geohash-kit** — spatial primitives used internally by rendezvous-kit.
|
|
479
|
+
|
|
480
|
+
- Repository: https://github.com/TheCryptoDonkey/geohash-kit
|
|
481
|
+
- npm: geohash-kit
|
package/llms.txt
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# rendezvous-kit
|
|
2
|
+
|
|
3
|
+
> TypeScript library for finding fair meeting points for N people — isochrone intersection, venue search, and fairness scoring.
|
|
4
|
+
|
|
5
|
+
One runtime dependency: geohash-kit. Zero third-party dependencies. ESM-only. Five subpath exports.
|
|
6
|
+
|
|
7
|
+
Repository: https://github.com/TheCryptoDonkey/rendezvous-kit
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
npm install rendezvous-kit
|
|
12
|
+
|
|
13
|
+
## Subpath Exports
|
|
14
|
+
|
|
15
|
+
### rendezvous-kit (barrel)
|
|
16
|
+
Re-exports everything below.
|
|
17
|
+
|
|
18
|
+
### rendezvous-kit/rendezvous
|
|
19
|
+
The main pipeline function.
|
|
20
|
+
|
|
21
|
+
- findRendezvous(engine, options) → Promise<RendezvousSuggestion[]>
|
|
22
|
+
|
|
23
|
+
### rendezvous-kit/geo
|
|
24
|
+
Pure-TypeScript polygon geometry.
|
|
25
|
+
|
|
26
|
+
- intersectPolygons(polygons: GeoJSONPolygon[]) → GeoJSONPolygon | null
|
|
27
|
+
- boundingBox(polygon: GeoJSONPolygon) → BBox
|
|
28
|
+
- centroid(polygon: GeoJSONPolygon) → { lat, lon }
|
|
29
|
+
- polygonArea(polygon: GeoJSONPolygon) → number (square metres)
|
|
30
|
+
|
|
31
|
+
### rendezvous-kit/engines/valhalla
|
|
32
|
+
- new ValhallaEngine({ baseUrl: string })
|
|
33
|
+
|
|
34
|
+
### rendezvous-kit/engines/openrouteservice
|
|
35
|
+
- new OpenRouteServiceEngine({ apiKey: string, baseUrl?: string })
|
|
36
|
+
|
|
37
|
+
### rendezvous-kit/engines/graphhopper
|
|
38
|
+
- new GraphHopperEngine({ baseUrl: string, apiKey?: string })
|
|
39
|
+
|
|
40
|
+
### rendezvous-kit/engines/osrm
|
|
41
|
+
- new OsrmEngine({ baseUrl: string }) — matrix only, no isochrone
|
|
42
|
+
|
|
43
|
+
### rendezvous-kit/venues
|
|
44
|
+
- searchVenues(polygon: GeoJSONPolygon, venueTypes: VenueType[], overpassUrl?: string) → Promise<Venue[]>
|
|
45
|
+
|
|
46
|
+
## Key Types
|
|
47
|
+
|
|
48
|
+
- LatLon: { lat, lon, label? }
|
|
49
|
+
- GeoJSONPolygon: { type: 'Polygon', coordinates: number[][][] }
|
|
50
|
+
- TransportMode: 'drive' | 'cycle' | 'walk' | 'public_transit'
|
|
51
|
+
- FairnessStrategy: 'min_max' | 'min_total' | 'min_variance'
|
|
52
|
+
- VenueType: 'park' | 'cafe' | 'restaurant' | 'service_station' | 'library' | 'pub' | 'playground' | 'community_centre' | string
|
|
53
|
+
- RendezvousOptions: { participants, mode, maxTimeMinutes, venueTypes, fairness?, limit? }
|
|
54
|
+
- RendezvousSuggestion: { venue, travelTimes: Record<string, number>, fairnessScore }
|
|
55
|
+
- RoutingEngine: interface { name, computeIsochrone, computeRouteMatrix }
|
|
56
|
+
|
|
57
|
+
## Fairness Strategies
|
|
58
|
+
|
|
59
|
+
- min_max — minimise worst-case travel time (default)
|
|
60
|
+
- min_total — minimise total travel time across all participants
|
|
61
|
+
- min_variance — equalise travel times (minimise standard deviation)
|
|
62
|
+
|
|
63
|
+
## Quick Example
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
import { findRendezvous } from 'rendezvous-kit'
|
|
67
|
+
import { ValhallaEngine } from 'rendezvous-kit/engines/valhalla'
|
|
68
|
+
|
|
69
|
+
const engine = new ValhallaEngine({ baseUrl: 'http://localhost:8002' })
|
|
70
|
+
|
|
71
|
+
const suggestions = await findRendezvous(engine, {
|
|
72
|
+
participants: [
|
|
73
|
+
{ lat: 51.5074, lon: -0.1278, label: 'Alice' },
|
|
74
|
+
{ lat: 51.4545, lon: -2.5879, label: 'Bob' },
|
|
75
|
+
],
|
|
76
|
+
mode: 'drive',
|
|
77
|
+
maxTimeMinutes: 60,
|
|
78
|
+
venueTypes: ['cafe'],
|
|
79
|
+
fairness: 'min_max',
|
|
80
|
+
limit: 3,
|
|
81
|
+
})
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Optional
|
|
85
|
+
|
|
86
|
+
- llms-full.txt: Full API reference with all type signatures and examples
|
package/package.json
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "rendezvous-kit",
|
|
3
|
+
"version": "0.0.0-development",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Find fair meeting points for N people — isochrone intersection, venue search, and fairness scoring.",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"default": "./dist/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./geo": {
|
|
14
|
+
"types": "./dist/geo.d.ts",
|
|
15
|
+
"default": "./dist/geo.js"
|
|
16
|
+
},
|
|
17
|
+
"./engines/*": {
|
|
18
|
+
"types": "./dist/engines/*.d.ts",
|
|
19
|
+
"default": "./dist/engines/*.js"
|
|
20
|
+
},
|
|
21
|
+
"./venues": {
|
|
22
|
+
"types": "./dist/venues.d.ts",
|
|
23
|
+
"default": "./dist/venues.js"
|
|
24
|
+
},
|
|
25
|
+
"./rendezvous": {
|
|
26
|
+
"types": "./dist/rendezvous.d.ts",
|
|
27
|
+
"default": "./dist/rendezvous.js"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"scripts": {
|
|
31
|
+
"build": "tsc",
|
|
32
|
+
"test": "vitest run",
|
|
33
|
+
"test:watch": "vitest",
|
|
34
|
+
"typecheck": "tsc --noEmit",
|
|
35
|
+
"bench": "vitest bench"
|
|
36
|
+
},
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"geohash-kit": "^1.1.0"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@semantic-release/changelog": "^6.0.3",
|
|
42
|
+
"@semantic-release/git": "^10.0.1",
|
|
43
|
+
"@semantic-release/github": "^12.0.6",
|
|
44
|
+
"@semantic-release/npm": "^13.1.4",
|
|
45
|
+
"semantic-release": "^25.0.3",
|
|
46
|
+
"typescript": "^5.7.0",
|
|
47
|
+
"vitest": "^3.0.0"
|
|
48
|
+
},
|
|
49
|
+
"files": [
|
|
50
|
+
"dist",
|
|
51
|
+
"LICENSE",
|
|
52
|
+
"llms.txt",
|
|
53
|
+
"llms-full.txt"
|
|
54
|
+
],
|
|
55
|
+
"keywords": [
|
|
56
|
+
"rendezvous",
|
|
57
|
+
"meeting-point",
|
|
58
|
+
"isochrone",
|
|
59
|
+
"fairness",
|
|
60
|
+
"routing",
|
|
61
|
+
"valhalla",
|
|
62
|
+
"openrouteservice",
|
|
63
|
+
"graphhopper",
|
|
64
|
+
"osrm",
|
|
65
|
+
"geospatial",
|
|
66
|
+
"typescript",
|
|
67
|
+
"esm"
|
|
68
|
+
],
|
|
69
|
+
"author": "TheCryptoDonkey",
|
|
70
|
+
"license": "MIT",
|
|
71
|
+
"repository": {
|
|
72
|
+
"type": "git",
|
|
73
|
+
"url": "https://github.com/TheCryptoDonkey/rendezvous-kit.git"
|
|
74
|
+
},
|
|
75
|
+
"homepage": "https://github.com/TheCryptoDonkey/rendezvous-kit"
|
|
76
|
+
}
|