fix(gbfs): read underscores in station names as word breaks

JCDecaux's Spanish contracts prefix every station name with "_", and
Valenbisi joins its words with it ("_PLAZA_MUSICO_LOPEZ_CHAVARRI"), so
the card and HUD would show the raw identifier. The shared parser now
reads a run of underscores, with the spaces around it, as one word break
and trims the ends. It applies to `name`, the `short_name` fallback and
GBFS v3 localized names, so the layer and get_bike_share both read it.

No registered system changes: run over all 52 live station_information
feeds on 2026-10-17, the old and new parsers give identical names for
all 13,397 stations. Other spacing stays as the operator published it.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
ulihhhhandClaude Opus 5.5 authored and Said Turksever committed 2026-10-17 22:33:57 +01:00
1 parent dd079db9b3
commit ee686a3207
2 files changed
+48 -2

No files matched your search

+12 -2
View File
@@ -51,15 +51,25 @@ export function extractStationsArray(payload) {
return [];
}
/**
* Station name text as a reader expects it. JCDecaux's Spanish contracts
* (Valenbisi, Sevici) prefix every name with "_" and Valenbisi joins words
* with it ("_PLAZA_MUSICO_LOPEZ_CHAVARRI"), so an underscore reads as a word
* break. Other spacing is the operator's own and stays as published.
*/
function tidyStationName(text) {
return text.replace(/\s*_+\s*/g, ' ').trim();
}
/** Read legacy text or a GBFS v3 Localized String, preferring usable English text. */
function stationName(value) {
if (typeof value === 'string') return value.trim();
if (typeof value === 'string') return tidyStationName(value);
if (!Array.isArray(value)) return '';
let fallback = '';
for (const translation of value) {
if (typeof translation?.text !== 'string') continue;
const text = translation.text.trim();
const text = tidyStationName(translation.text);
if (!text) continue;
if (!fallback) fallback = text;
if (
+36
View File
@@ -22,6 +22,42 @@ test('GBFS v2 station names remain trimmed strings', () => {
);
});
test('underscores in station names read as word breaks', () => {
// As published by JCDecaux's Spanish contracts (checked 2026-10-17).
for (const [published, shown] of [
['_PLAZA_MUSICO_LOPEZ_CHAVARRI', 'PLAZA MUSICO LOPEZ CHAVARRI'],
['_GLORIETA OLIMPICA', 'GLORIETA OLIMPICA'],
['_CAMPAMENTO_81', 'CAMPAMENTO 81'],
['Plaza__Mayor_ ', 'Plaza Mayor'],
]) {
assert.equal(parseName({ name: published }), shown);
}
});
test('spacing without underscores stays as the operator published it', () => {
assert.equal(parseName({ name: 'C/ NÀPOLS, 82' }), 'C/ NÀPOLS, 82');
});
test('a name of only underscores falls back to short_name, then to empty', () => {
assert.equal(parseName({ name: '_', short_name: '_S_1_' }), 'S 1');
assert.equal(parseName({ name: '__', short_name: '_' }), '');
});
test('underscores are read the same in GBFS v3 localized names', () => {
assert.equal(
parseName(
{
name: [
{ language: 'es', text: '_' },
{ language: 'en', text: '_COLON_I' },
],
},
'3.0',
),
'COLON I',
);
});
test('GBFS v3 station names prefer English over the first translation', () => {
assert.equal(
parseName(