lt-public-transport-sdk 1.0.0 → 1.1.0
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 -21
- package/README.md +340 -283
- package/dist/config.d.ts +41 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +34 -0
- package/dist/config.js.map +1 -1
- package/dist/errors.d.ts +12 -13
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +1 -1
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +80 -18
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +124 -37
- package/dist/index.js.map +1 -1
- package/dist/parsers/gps-lite.d.ts +34 -36
- package/dist/parsers/gps-lite.d.ts.map +1 -1
- package/dist/parsers/gps-lite.js +77 -69
- package/dist/parsers/gps-lite.js.map +1 -1
- package/dist/parsers/index.d.ts +2 -1
- package/dist/parsers/index.d.ts.map +1 -1
- package/dist/parsers/index.js +4 -1
- package/dist/parsers/index.js.map +1 -1
- package/dist/schemas.d.ts +158 -1
- package/dist/schemas.d.ts.map +1 -1
- package/dist/schemas.js +87 -1
- package/dist/schemas.js.map +1 -1
- package/package.json +1 -1
- package/dist/scripts/test-city-specific.d.ts +0 -2
- package/dist/scripts/test-city-specific.d.ts.map +0 -1
- package/dist/scripts/test-city-specific.js +0 -264
- package/dist/scripts/test-city-specific.js.map +0 -1
- package/dist/scripts/test-config-options.d.ts +0 -2
- package/dist/scripts/test-config-options.d.ts.map +0 -1
- package/dist/scripts/test-config-options.js +0 -166
- package/dist/scripts/test-config-options.js.map +0 -1
- package/dist/scripts/test-data-quality.d.ts +0 -2
- package/dist/scripts/test-data-quality.d.ts.map +0 -1
- package/dist/scripts/test-data-quality.js +0 -204
- package/dist/scripts/test-data-quality.js.map +0 -1
- package/dist/scripts/test-error-handling.d.ts +0 -2
- package/dist/scripts/test-error-handling.d.ts.map +0 -1
- package/dist/scripts/test-error-handling.js +0 -146
- package/dist/scripts/test-error-handling.js.map +0 -1
- package/dist/scripts/test-live.d.ts +0 -2
- package/dist/scripts/test-live.d.ts.map +0 -1
- package/dist/scripts/test-live.js +0 -121
- package/dist/scripts/test-live.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -24,10 +24,10 @@
|
|
|
24
24
|
*/
|
|
25
25
|
import { tmpdir } from 'node:os';
|
|
26
26
|
import { join } from 'node:path';
|
|
27
|
-
import { CITY_CONFIGS,
|
|
27
|
+
import { CITY_CONFIGS, ALL_CITY_IDS } from './config.js';
|
|
28
28
|
import { TransportNetworkError, GpsNotAvailableError, SyncRequiredError, InvalidCityError, } from './errors.js';
|
|
29
29
|
import { parseGpsFullStream } from './parsers/gps-full.js';
|
|
30
|
-
import { parseGpsLiteStream,
|
|
30
|
+
import { parseGpsLiteStream, getLiteFormatDescriptor } from './parsers/gps-lite.js';
|
|
31
31
|
import { syncGtfs, loadGtfsCache, loadCachedRoutes, loadCachedStops } from './gtfs/sync.js';
|
|
32
32
|
import { enrichVehicles, buildRouteCache } from './enrichment/route-matcher.js';
|
|
33
33
|
import { clientConfigSchema } from './schemas.js';
|
|
@@ -72,6 +72,13 @@ export class LtTransport {
|
|
|
72
72
|
autoEnrich;
|
|
73
73
|
filterInvalidCoords;
|
|
74
74
|
filterStale;
|
|
75
|
+
/**
|
|
76
|
+
* Effective city configurations (built-in + custom + overrides merged).
|
|
77
|
+
* This is the source of truth for city configs in this instance.
|
|
78
|
+
*/
|
|
79
|
+
effectiveCityConfigs;
|
|
80
|
+
/** Sorted list of all effective city IDs */
|
|
81
|
+
effectiveCityIds;
|
|
75
82
|
/** In-memory route cache for fast enrichment */
|
|
76
83
|
routeCaches = new Map();
|
|
77
84
|
/** Last sync timestamps for throttling */
|
|
@@ -80,9 +87,11 @@ export class LtTransport {
|
|
|
80
87
|
* Create a new LtTransport client.
|
|
81
88
|
*
|
|
82
89
|
* @param config - Client configuration options
|
|
90
|
+
* @throws {ZodError} If config validation fails (includes helpful error messages)
|
|
83
91
|
*/
|
|
84
92
|
constructor(config = {}) {
|
|
85
|
-
// Validate config with Zod schema for runtime safety
|
|
93
|
+
// Validate entire config with Zod schema for runtime safety
|
|
94
|
+
// This validates customCities and cityOverrides structure and values
|
|
86
95
|
const validated = clientConfigSchema.parse({
|
|
87
96
|
cacheDir: config.cacheDir,
|
|
88
97
|
requestTimeout: config.requestTimeout ?? DEFAULT_TIMEOUT,
|
|
@@ -91,6 +100,8 @@ export class LtTransport {
|
|
|
91
100
|
autoEnrich: config.autoEnrich ?? true,
|
|
92
101
|
filterInvalidCoords: config.filterInvalidCoords ?? true,
|
|
93
102
|
filterStale: config.filterStale ?? false,
|
|
103
|
+
customCities: config.customCities,
|
|
104
|
+
cityOverrides: config.cityOverrides,
|
|
94
105
|
});
|
|
95
106
|
this.cacheDir = validated.cacheDir ?? getDefaultCacheDir();
|
|
96
107
|
this.requestTimeout = validated.requestTimeout;
|
|
@@ -99,6 +110,58 @@ export class LtTransport {
|
|
|
99
110
|
this.autoEnrich = validated.autoEnrich;
|
|
100
111
|
this.filterInvalidCoords = validated.filterInvalidCoords;
|
|
101
112
|
this.filterStale = validated.filterStale;
|
|
113
|
+
// Build effective city configurations by merging:
|
|
114
|
+
// 1. Built-in CITY_CONFIGS
|
|
115
|
+
// 2. Custom cities from config.customCities
|
|
116
|
+
// 3. Overrides from config.cityOverrides
|
|
117
|
+
this.effectiveCityConfigs = this.buildEffectiveCityConfigs(validated.customCities, validated.cityOverrides);
|
|
118
|
+
// Build sorted list of city IDs
|
|
119
|
+
this.effectiveCityIds = Array.from(this.effectiveCityConfigs.keys()).sort();
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Build effective city configurations by merging built-in, custom, and overrides.
|
|
123
|
+
*/
|
|
124
|
+
buildEffectiveCityConfigs(customCities, cityOverrides) {
|
|
125
|
+
const configs = new Map();
|
|
126
|
+
// Step 1: Add all built-in cities
|
|
127
|
+
for (const cityId of ALL_CITY_IDS) {
|
|
128
|
+
configs.set(cityId, CITY_CONFIGS[cityId]);
|
|
129
|
+
}
|
|
130
|
+
// Step 2: Apply overrides to built-in cities
|
|
131
|
+
if (cityOverrides) {
|
|
132
|
+
for (const [cityId, override] of Object.entries(cityOverrides)) {
|
|
133
|
+
const existing = configs.get(cityId);
|
|
134
|
+
if (existing !== undefined) {
|
|
135
|
+
configs.set(cityId, this.mergeCityConfig(existing, override));
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
// Step 3: Add custom cities (can override built-in if same ID)
|
|
140
|
+
if (customCities) {
|
|
141
|
+
for (const [cityId, cityConfig] of Object.entries(customCities)) {
|
|
142
|
+
configs.set(cityId, cityConfig);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return configs;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Deep merge a city config with partial overrides.
|
|
149
|
+
*/
|
|
150
|
+
mergeCityConfig(base, override) {
|
|
151
|
+
return {
|
|
152
|
+
id: override.id ?? base.id,
|
|
153
|
+
tier: override.tier ?? base.tier,
|
|
154
|
+
gps: override.gps ? { ...base.gps, ...override.gps } : base.gps,
|
|
155
|
+
gtfs: override.gtfs ? { ...base.gtfs, ...override.gtfs } : base.gtfs,
|
|
156
|
+
liteFormat: override.liteFormat ?? base.liteFormat,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Get effective city config for a city ID.
|
|
161
|
+
* Checks the merged config map which includes built-in, custom, and overridden configs.
|
|
162
|
+
*/
|
|
163
|
+
getEffectiveCityConfig(cityId) {
|
|
164
|
+
return this.effectiveCityConfigs.get(cityId);
|
|
102
165
|
}
|
|
103
166
|
// ===========================================================================
|
|
104
167
|
// Public API
|
|
@@ -106,13 +169,14 @@ export class LtTransport {
|
|
|
106
169
|
/**
|
|
107
170
|
* Get real-time vehicle positions for a city.
|
|
108
171
|
*
|
|
109
|
-
* For silver-tier cities
|
|
110
|
-
*
|
|
172
|
+
* For silver-tier cities, vehicles will be enriched with GTFS data
|
|
173
|
+
* if `sync()` has been called and `autoEnrich` is enabled.
|
|
111
174
|
*
|
|
112
|
-
* @param city - City identifier
|
|
175
|
+
* @param city - City identifier (built-in or custom)
|
|
113
176
|
* @returns Array of vehicle positions
|
|
114
|
-
* @throws {GpsNotAvailableError} If city
|
|
177
|
+
* @throws {GpsNotAvailableError} If city has no GPS data (bronze tier)
|
|
115
178
|
* @throws {TransportNetworkError} If network request fails
|
|
179
|
+
* @throws {InvalidCityError} If city is not recognized
|
|
116
180
|
*
|
|
117
181
|
* @example
|
|
118
182
|
* ```typescript
|
|
@@ -121,8 +185,10 @@ export class LtTransport {
|
|
|
121
185
|
* ```
|
|
122
186
|
*/
|
|
123
187
|
async getVehicles(city) {
|
|
124
|
-
this.
|
|
125
|
-
|
|
188
|
+
const config = this.getEffectiveCityConfig(city);
|
|
189
|
+
if (!config) {
|
|
190
|
+
throw new InvalidCityError(city);
|
|
191
|
+
}
|
|
126
192
|
if (!config.gps.enabled || config.gps.url === null) {
|
|
127
193
|
throw new GpsNotAvailableError(city);
|
|
128
194
|
}
|
|
@@ -131,26 +197,36 @@ export class LtTransport {
|
|
|
131
197
|
// Parse based on format
|
|
132
198
|
let vehicles;
|
|
133
199
|
if (config.gps.format === 'full') {
|
|
200
|
+
// Gold tier: header-based CSV with rich metadata
|
|
134
201
|
vehicles = parseGpsFullStream(text, city, {
|
|
135
202
|
staleThresholdMs: this.staleThresholdMs,
|
|
136
203
|
filterStale: this.filterStale,
|
|
137
204
|
filterInvalidCoords: this.filterInvalidCoords,
|
|
138
205
|
});
|
|
139
206
|
}
|
|
140
|
-
else if (
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
207
|
+
else if (config.gps.format === 'lite') {
|
|
208
|
+
// Silver tier: headerless CSV using format descriptor
|
|
209
|
+
const liteFormat = getLiteFormatDescriptor(city, config);
|
|
210
|
+
if (!liteFormat) {
|
|
211
|
+
// No format descriptor available - can't parse
|
|
212
|
+
console.warn(`No lite format descriptor for city: ${city}. Add liteFormat to city config.`);
|
|
213
|
+
vehicles = [];
|
|
214
|
+
}
|
|
215
|
+
else {
|
|
216
|
+
vehicles = parseGpsLiteStream(text, city, liteFormat, {
|
|
217
|
+
filterInvalidCoords: this.filterInvalidCoords,
|
|
218
|
+
});
|
|
219
|
+
// Enrich silver-tier cities with GTFS data
|
|
220
|
+
if (this.autoEnrich) {
|
|
221
|
+
const routeCache = await this.getRouteCache(city);
|
|
222
|
+
if (routeCache) {
|
|
223
|
+
vehicles = enrichVehicles(vehicles, routeCache);
|
|
224
|
+
}
|
|
149
225
|
}
|
|
150
226
|
}
|
|
151
227
|
}
|
|
152
228
|
else {
|
|
153
|
-
// Unknown format
|
|
229
|
+
// Unknown format (bronze tier or misconfigured)
|
|
154
230
|
vehicles = [];
|
|
155
231
|
}
|
|
156
232
|
return vehicles;
|
|
@@ -163,10 +239,11 @@ export class LtTransport {
|
|
|
163
239
|
*
|
|
164
240
|
* Throttled to minimum 60 seconds between calls for same city.
|
|
165
241
|
*
|
|
166
|
-
* @param city - City to sync
|
|
242
|
+
* @param city - City to sync (built-in or custom)
|
|
167
243
|
* @param force - Force re-download even if cache is current
|
|
168
244
|
* @returns Sync result with counts and status
|
|
169
245
|
* @throws {GtfsSyncError} If sync fails
|
|
246
|
+
* @throws {InvalidCityError} If city is not recognized
|
|
170
247
|
*
|
|
171
248
|
* @example
|
|
172
249
|
* ```typescript
|
|
@@ -175,7 +252,10 @@ export class LtTransport {
|
|
|
175
252
|
* ```
|
|
176
253
|
*/
|
|
177
254
|
async sync(city, force = false) {
|
|
178
|
-
this.
|
|
255
|
+
const config = this.getEffectiveCityConfig(city);
|
|
256
|
+
if (!config) {
|
|
257
|
+
throw new InvalidCityError(city);
|
|
258
|
+
}
|
|
179
259
|
// Throttle sync calls (60s minimum between calls)
|
|
180
260
|
const lastSync = this.lastSyncTimes.get(city);
|
|
181
261
|
const now = Date.now();
|
|
@@ -184,7 +264,7 @@ export class LtTransport {
|
|
|
184
264
|
const cache = await loadGtfsCache(city, this.cacheDir);
|
|
185
265
|
if (cache) {
|
|
186
266
|
return {
|
|
187
|
-
city,
|
|
267
|
+
city: city,
|
|
188
268
|
status: 'up-to-date',
|
|
189
269
|
routeCount: cache.meta.routeCount,
|
|
190
270
|
stopCount: cache.meta.stopCount,
|
|
@@ -212,6 +292,7 @@ export class LtTransport {
|
|
|
212
292
|
* @param city - City to get stops for
|
|
213
293
|
* @returns Array of stops
|
|
214
294
|
* @throws {SyncRequiredError} If GTFS data not synced
|
|
295
|
+
* @throws {InvalidCityError} If city is not recognized
|
|
215
296
|
*
|
|
216
297
|
* @example
|
|
217
298
|
* ```typescript
|
|
@@ -221,7 +302,10 @@ export class LtTransport {
|
|
|
221
302
|
* ```
|
|
222
303
|
*/
|
|
223
304
|
async getStops(city) {
|
|
224
|
-
this.
|
|
305
|
+
const config = this.getEffectiveCityConfig(city);
|
|
306
|
+
if (!config) {
|
|
307
|
+
throw new InvalidCityError(city);
|
|
308
|
+
}
|
|
225
309
|
const stops = await loadCachedStops(this.cacheDir, city);
|
|
226
310
|
if (!stops) {
|
|
227
311
|
throw new SyncRequiredError(city);
|
|
@@ -236,6 +320,7 @@ export class LtTransport {
|
|
|
236
320
|
* @param city - City to get routes for
|
|
237
321
|
* @returns Array of routes
|
|
238
322
|
* @throws {SyncRequiredError} If GTFS data not synced
|
|
323
|
+
* @throws {InvalidCityError} If city is not recognized
|
|
239
324
|
*
|
|
240
325
|
* @example
|
|
241
326
|
* ```typescript
|
|
@@ -245,7 +330,10 @@ export class LtTransport {
|
|
|
245
330
|
* ```
|
|
246
331
|
*/
|
|
247
332
|
async getRoutes(city) {
|
|
248
|
-
this.
|
|
333
|
+
const config = this.getEffectiveCityConfig(city);
|
|
334
|
+
if (!config) {
|
|
335
|
+
throw new InvalidCityError(city);
|
|
336
|
+
}
|
|
249
337
|
const routeCache = await this.getRouteCache(city);
|
|
250
338
|
if (!routeCache) {
|
|
251
339
|
throw new SyncRequiredError(city);
|
|
@@ -262,29 +350,28 @@ export class LtTransport {
|
|
|
262
350
|
return routes;
|
|
263
351
|
}
|
|
264
352
|
/**
|
|
265
|
-
* Get list of all
|
|
353
|
+
* Get list of all available city IDs.
|
|
354
|
+
* Includes built-in cities and any custom cities added via config.
|
|
266
355
|
*/
|
|
267
356
|
getCities() {
|
|
268
|
-
return
|
|
357
|
+
return this.effectiveCityIds;
|
|
269
358
|
}
|
|
270
359
|
/**
|
|
271
360
|
* Get configuration for a specific city.
|
|
361
|
+
* Returns effective config (with any overrides applied).
|
|
362
|
+
*
|
|
363
|
+
* @throws {InvalidCityError} If city is not found
|
|
272
364
|
*/
|
|
273
365
|
getCityConfig(city) {
|
|
274
|
-
this.
|
|
275
|
-
|
|
366
|
+
const config = this.getEffectiveCityConfig(city);
|
|
367
|
+
if (!config) {
|
|
368
|
+
throw new InvalidCityError(city);
|
|
369
|
+
}
|
|
370
|
+
return config;
|
|
276
371
|
}
|
|
277
372
|
// ===========================================================================
|
|
278
373
|
// Private Helpers
|
|
279
374
|
// ===========================================================================
|
|
280
|
-
/**
|
|
281
|
-
* Validate that city ID is valid.
|
|
282
|
-
*/
|
|
283
|
-
validateCity(city) {
|
|
284
|
-
if (!(city in CITY_CONFIGS)) {
|
|
285
|
-
throw new InvalidCityError(city);
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
375
|
/**
|
|
289
376
|
* Fetch text content from URL.
|
|
290
377
|
*/
|
|
@@ -334,7 +421,7 @@ export class LtTransport {
|
|
|
334
421
|
}
|
|
335
422
|
export { GTFS_ROUTE_TYPE_MAP, LT_TRANSPORT_TYPE_MAP } from './types.js';
|
|
336
423
|
// Config
|
|
337
|
-
export { CITY_CONFIGS, ALL_CITY_IDS, getCityConfig, getCitiesByTier, hasGpsData, hasGtfsData } from './config.js';
|
|
424
|
+
export { CITY_CONFIGS, ALL_CITY_IDS, getCityConfig, getCitiesByTier, hasGpsData, hasGtfsData, LITE_FORMAT_DESCRIPTORS } from './config.js';
|
|
338
425
|
// Errors
|
|
339
426
|
export { TransportError, TransportNetworkError, GpsNotAvailableError, SyncRequiredError, GtfsSyncError, ParseError, InvalidCityError, isTransportError, isNetworkError, } from './errors.js';
|
|
340
427
|
// Utilities
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAGjC,OAAO,EAAE,YAAY,EAAE,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,37 +2,16 @@
|
|
|
2
2
|
* GPS Lite Format Parser for silver-tier cities
|
|
3
3
|
* @module parsers/gps-lite
|
|
4
4
|
*
|
|
5
|
-
* Handles the "lite" GPS format used by silver-tier cities
|
|
6
|
-
* These streams have no header row and
|
|
5
|
+
* Handles the "lite" GPS format used by silver-tier cities.
|
|
6
|
+
* These streams have no header row and use a data-driven format descriptor
|
|
7
|
+
* to parse columns at specified indices.
|
|
7
8
|
*
|
|
8
|
-
*
|
|
9
|
-
* -
|
|
10
|
-
* -
|
|
11
|
-
*/
|
|
12
|
-
import type { CityId, Vehicle } from '../types.js';
|
|
13
|
-
/**
|
|
14
|
-
* Panevėžys format (9 columns, no header):
|
|
15
|
-
* [0] type - Always "2" (bus?)
|
|
16
|
-
* [1] route - Route name (often empty)
|
|
17
|
-
* [2] longitude - Integer format (÷1,000,000)
|
|
18
|
-
* [3] latitude - Integer format (÷1,000,000)
|
|
19
|
-
* [4] speed - Speed or delay?
|
|
20
|
-
* [5] azimuth - Bearing in degrees
|
|
21
|
-
* [6] (empty) - Unknown
|
|
22
|
-
* [7] vehicleId - Vehicle identifier
|
|
23
|
-
* [8] (empty) - Unknown
|
|
24
|
-
*/
|
|
25
|
-
/**
|
|
26
|
-
* Tauragė format (8 columns, no header):
|
|
27
|
-
* [0] type - Always "2" (bus?)
|
|
28
|
-
* [1] route - Route name (S11, S19) - ALPHANUMERIC!
|
|
29
|
-
* [2] longitude - Integer format (÷1,000,000)
|
|
30
|
-
* [3] latitude - Integer format (÷1,000,000)
|
|
31
|
-
* [4] speed - Speed in km/h
|
|
32
|
-
* [5] azimuth - Bearing in degrees
|
|
33
|
-
* [6] vehicleId - Vehicle identifier
|
|
34
|
-
* [7] (empty) - Unknown
|
|
9
|
+
* This design allows users to:
|
|
10
|
+
* - Add new cities without SDK updates
|
|
11
|
+
* - Override formats when cities change their data structure
|
|
35
12
|
*/
|
|
13
|
+
import type { Vehicle } from '../types.js';
|
|
14
|
+
import type { LiteFormatDescriptor, CityConfig } from '../config.js';
|
|
36
15
|
/**
|
|
37
16
|
* Options for GPS lite format parsing.
|
|
38
17
|
*/
|
|
@@ -41,20 +20,39 @@ export interface GpsLiteParseOptions {
|
|
|
41
20
|
filterInvalidCoords?: boolean;
|
|
42
21
|
}
|
|
43
22
|
/**
|
|
44
|
-
*
|
|
23
|
+
* Get the lite format descriptor for a city.
|
|
24
|
+
* Checks city config first, then falls back to built-in descriptors.
|
|
25
|
+
*
|
|
26
|
+
* @param cityId - The city identifier
|
|
27
|
+
* @param cityConfig - Optional city config with custom liteFormat
|
|
28
|
+
* @returns The format descriptor, or undefined if not found
|
|
45
29
|
*/
|
|
46
|
-
export
|
|
30
|
+
export declare function getLiteFormatDescriptor(cityId: string, cityConfig?: CityConfig): LiteFormatDescriptor | undefined;
|
|
47
31
|
/**
|
|
48
|
-
* Check if a city uses lite GPS format.
|
|
32
|
+
* Check if a city uses lite GPS format based on its config.
|
|
33
|
+
*
|
|
34
|
+
* @param cityConfig - The city configuration
|
|
35
|
+
* @returns True if the city uses lite format
|
|
49
36
|
*/
|
|
50
|
-
export declare function
|
|
37
|
+
export declare function isLiteFormat(cityConfig: CityConfig): boolean;
|
|
51
38
|
/**
|
|
52
|
-
* Parse GPS lite format stream
|
|
39
|
+
* Parse GPS lite format stream using a format descriptor.
|
|
53
40
|
*
|
|
54
41
|
* @param text - Raw text content from gps.txt
|
|
55
|
-
* @param
|
|
42
|
+
* @param cityId - City identifier for vehicle ID prefixing
|
|
43
|
+
* @param format - Format descriptor defining column indices
|
|
56
44
|
* @param options - Parse options
|
|
57
45
|
* @returns Array of normalized Vehicle objects
|
|
58
46
|
*/
|
|
59
|
-
export declare function parseGpsLiteStream(text: string,
|
|
47
|
+
export declare function parseGpsLiteStream(text: string, cityId: string, format: LiteFormatDescriptor, options?: GpsLiteParseOptions): Vehicle[];
|
|
48
|
+
/**
|
|
49
|
+
* @deprecated Use isLiteFormat(cityConfig) instead.
|
|
50
|
+
* Legacy type for cities that use lite GPS format.
|
|
51
|
+
*/
|
|
52
|
+
export type LiteCityId = 'panevezys' | 'taurage';
|
|
53
|
+
/**
|
|
54
|
+
* @deprecated Use isLiteFormat(cityConfig) instead.
|
|
55
|
+
* Check if a city uses lite GPS format.
|
|
56
|
+
*/
|
|
57
|
+
export declare function isLiteCity(cityId: string): cityId is LiteCityId;
|
|
60
58
|
//# sourceMappingURL=gps-lite.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gps-lite.d.ts","sourceRoot":"","sources":["../../src/parsers/gps-lite.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"gps-lite.d.ts","sourceRoot":"","sources":["../../src/parsers/gps-lite.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAe,MAAM,aAAa,CAAC;AACxD,OAAO,KAAK,EAAE,oBAAoB,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAarE;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,6EAA6E;IAC7E,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAMD;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,MAAM,EACd,UAAU,CAAC,EAAE,UAAU,GACtB,oBAAoB,GAAG,SAAS,CAQlC;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,UAAU,EAAE,UAAU,GAAG,OAAO,CAE5D;AAMD;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,oBAAoB,EAC5B,OAAO,GAAE,mBAAwB,GAChC,OAAO,EAAE,CA8BX;AAkFD;;;GAGG;AACH,MAAM,MAAM,UAAU,GAAG,WAAW,GAAG,SAAS,CAAC;AAEjD;;;GAGG;AAEH,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,IAAI,UAAU,CAE/D"}
|
package/dist/parsers/gps-lite.js
CHANGED
|
@@ -2,33 +2,57 @@
|
|
|
2
2
|
* GPS Lite Format Parser for silver-tier cities
|
|
3
3
|
* @module parsers/gps-lite
|
|
4
4
|
*
|
|
5
|
-
* Handles the "lite" GPS format used by silver-tier cities
|
|
6
|
-
* These streams have no header row and
|
|
5
|
+
* Handles the "lite" GPS format used by silver-tier cities.
|
|
6
|
+
* These streams have no header row and use a data-driven format descriptor
|
|
7
|
+
* to parse columns at specified indices.
|
|
7
8
|
*
|
|
8
|
-
*
|
|
9
|
-
* -
|
|
10
|
-
* -
|
|
9
|
+
* This design allows users to:
|
|
10
|
+
* - Add new cities without SDK updates
|
|
11
|
+
* - Override formats when cities change their data structure
|
|
11
12
|
*/
|
|
13
|
+
import { LITE_FORMAT_DESCRIPTORS } from '../config.js';
|
|
12
14
|
import { normalizeCoordinate, isValidLithuaniaCoord, normalizeBearing, normalizeSpeed, } from '../utils/index.js';
|
|
13
|
-
|
|
15
|
+
// =============================================================================
|
|
16
|
+
// Format Detection
|
|
17
|
+
// =============================================================================
|
|
14
18
|
/**
|
|
15
|
-
*
|
|
19
|
+
* Get the lite format descriptor for a city.
|
|
20
|
+
* Checks city config first, then falls back to built-in descriptors.
|
|
21
|
+
*
|
|
22
|
+
* @param cityId - The city identifier
|
|
23
|
+
* @param cityConfig - Optional city config with custom liteFormat
|
|
24
|
+
* @returns The format descriptor, or undefined if not found
|
|
25
|
+
*/
|
|
26
|
+
export function getLiteFormatDescriptor(cityId, cityConfig) {
|
|
27
|
+
// Priority 1: Explicit liteFormat in city config
|
|
28
|
+
if (cityConfig?.liteFormat) {
|
|
29
|
+
return cityConfig.liteFormat;
|
|
30
|
+
}
|
|
31
|
+
// Priority 2: Built-in descriptor by city ID
|
|
32
|
+
return LITE_FORMAT_DESCRIPTORS[cityId];
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Check if a city uses lite GPS format based on its config.
|
|
36
|
+
*
|
|
37
|
+
* @param cityConfig - The city configuration
|
|
38
|
+
* @returns True if the city uses lite format
|
|
16
39
|
*/
|
|
17
|
-
export function
|
|
18
|
-
return
|
|
40
|
+
export function isLiteFormat(cityConfig) {
|
|
41
|
+
return cityConfig.gps.format === 'lite';
|
|
19
42
|
}
|
|
20
43
|
// =============================================================================
|
|
21
44
|
// Main Parser
|
|
22
45
|
// =============================================================================
|
|
23
46
|
/**
|
|
24
|
-
* Parse GPS lite format stream
|
|
47
|
+
* Parse GPS lite format stream using a format descriptor.
|
|
25
48
|
*
|
|
26
49
|
* @param text - Raw text content from gps.txt
|
|
27
|
-
* @param
|
|
50
|
+
* @param cityId - City identifier for vehicle ID prefixing
|
|
51
|
+
* @param format - Format descriptor defining column indices
|
|
28
52
|
* @param options - Parse options
|
|
29
53
|
* @returns Array of normalized Vehicle objects
|
|
30
54
|
*/
|
|
31
|
-
export function parseGpsLiteStream(text,
|
|
55
|
+
export function parseGpsLiteStream(text, cityId, format, options = {}) {
|
|
32
56
|
const { filterInvalidCoords = true } = options;
|
|
33
57
|
const lines = text.split('\n').filter(line => line.trim());
|
|
34
58
|
if (lines.length === 0) {
|
|
@@ -38,9 +62,7 @@ export function parseGpsLiteStream(text, city, options = {}) {
|
|
|
38
62
|
for (const line of lines) {
|
|
39
63
|
const cols = line.split(',');
|
|
40
64
|
try {
|
|
41
|
-
const vehicle =
|
|
42
|
-
? parsePanevezysLine(cols, city)
|
|
43
|
-
: parseTaurageLine(cols, city);
|
|
65
|
+
const vehicle = parseLiteLine(cols, cityId, format);
|
|
44
66
|
if (vehicle) {
|
|
45
67
|
if (filterInvalidCoords && !isValidLithuaniaCoord(vehicle.latitude, vehicle.longitude)) {
|
|
46
68
|
continue;
|
|
@@ -56,34 +78,52 @@ export function parseGpsLiteStream(text, city, options = {}) {
|
|
|
56
78
|
return vehicles;
|
|
57
79
|
}
|
|
58
80
|
// =============================================================================
|
|
59
|
-
//
|
|
81
|
+
// Generic Line Parser
|
|
60
82
|
// =============================================================================
|
|
61
83
|
/**
|
|
62
|
-
* Parse a line
|
|
84
|
+
* Parse a single line using the format descriptor.
|
|
85
|
+
* This is the core data-driven parser that uses column indices
|
|
86
|
+
* from the descriptor instead of hardcoded positions.
|
|
87
|
+
*
|
|
88
|
+
* @param cols - Array of column values from the CSV line
|
|
89
|
+
* @param cityId - City identifier for vehicle ID prefixing
|
|
90
|
+
* @param format - Format descriptor with column indices
|
|
91
|
+
* @returns Parsed Vehicle or null if line is invalid
|
|
63
92
|
*/
|
|
64
|
-
function
|
|
65
|
-
//
|
|
66
|
-
|
|
67
|
-
|
|
93
|
+
function parseLiteLine(cols, cityId, format) {
|
|
94
|
+
// Check minimum column count
|
|
95
|
+
if (cols.length < format.minColumns) {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
// Extract vehicle ID
|
|
99
|
+
const vehicleNumber = cols[format.vehicleIdIndex]?.trim();
|
|
100
|
+
if (vehicleNumber === undefined || vehicleNumber === '') {
|
|
68
101
|
return null;
|
|
69
102
|
}
|
|
70
|
-
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
if (
|
|
103
|
+
// Extract and validate coordinates
|
|
104
|
+
const latRaw = Number(cols[format.coordIndices[0]]);
|
|
105
|
+
const lonRaw = Number(cols[format.coordIndices[1]]);
|
|
106
|
+
if (!Number.isFinite(latRaw) || !Number.isFinite(lonRaw)) {
|
|
74
107
|
return null;
|
|
75
108
|
}
|
|
76
|
-
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
const
|
|
80
|
-
const
|
|
81
|
-
|
|
109
|
+
// Extract route (may be empty)
|
|
110
|
+
const route = cols[format.routeIndex]?.trim() ?? '';
|
|
111
|
+
// Extract speed and bearing
|
|
112
|
+
const speedRaw = Number(cols[format.speedIndex]);
|
|
113
|
+
const bearingRaw = Number(cols[format.bearingIndex]);
|
|
114
|
+
// Normalize values
|
|
115
|
+
const latitude = normalizeCoordinate(latRaw);
|
|
116
|
+
const longitude = normalizeCoordinate(lonRaw);
|
|
117
|
+
const speed = normalizeSpeed(Number.isFinite(speedRaw) ? speedRaw : 0);
|
|
118
|
+
const bearing = normalizeBearing(Number.isFinite(bearingRaw) ? bearingRaw : 0);
|
|
119
|
+
// Determine vehicle type (lite format typically doesn't specify, default to bus)
|
|
120
|
+
const type = 'bus';
|
|
121
|
+
const id = `${cityId}-${vehicleNumber}`;
|
|
82
122
|
return {
|
|
83
123
|
id,
|
|
84
124
|
vehicleNumber,
|
|
85
125
|
route,
|
|
86
|
-
type
|
|
126
|
+
type,
|
|
87
127
|
latitude,
|
|
88
128
|
longitude,
|
|
89
129
|
bearing,
|
|
@@ -99,43 +139,11 @@ function parsePanevezysLine(cols, city) {
|
|
|
99
139
|
};
|
|
100
140
|
}
|
|
101
141
|
/**
|
|
102
|
-
*
|
|
142
|
+
* @deprecated Use isLiteFormat(cityConfig) instead.
|
|
143
|
+
* Check if a city uses lite GPS format.
|
|
103
144
|
*/
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
if (!parseResult.success) {
|
|
108
|
-
return null;
|
|
109
|
-
}
|
|
110
|
-
const row = parseResult.data;
|
|
111
|
-
const vehicleNumber = row[6];
|
|
112
|
-
// Validate essential fields
|
|
113
|
-
if (vehicleNumber === '' || !Number.isFinite(row[2]) || !Number.isFinite(row[3])) {
|
|
114
|
-
return null;
|
|
115
|
-
}
|
|
116
|
-
const longitude = normalizeCoordinate(row[2]);
|
|
117
|
-
const latitude = normalizeCoordinate(row[3]);
|
|
118
|
-
const speed = normalizeSpeed(row[4]);
|
|
119
|
-
const bearing = normalizeBearing(row[5]);
|
|
120
|
-
const route = row[1];
|
|
121
|
-
const id = `${city}-${vehicleNumber}`;
|
|
122
|
-
return {
|
|
123
|
-
id,
|
|
124
|
-
vehicleNumber,
|
|
125
|
-
route,
|
|
126
|
-
type: 'bus', // Lite format doesn't specify type
|
|
127
|
-
latitude,
|
|
128
|
-
longitude,
|
|
129
|
-
bearing,
|
|
130
|
-
speed,
|
|
131
|
-
destination: null, // Needs GTFS enrichment
|
|
132
|
-
delaySeconds: null,
|
|
133
|
-
tripId: null,
|
|
134
|
-
gtfsTripId: null,
|
|
135
|
-
nextStopId: null,
|
|
136
|
-
arrivalTimeSeconds: null,
|
|
137
|
-
isStale: false, // No timestamp in lite format
|
|
138
|
-
measuredAt: new Date(), // Use server receive time
|
|
139
|
-
};
|
|
145
|
+
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
146
|
+
export function isLiteCity(cityId) {
|
|
147
|
+
return cityId in LITE_FORMAT_DESCRIPTORS;
|
|
140
148
|
}
|
|
141
149
|
//# sourceMappingURL=gps-lite.js.map
|