11import type { Feature , FeatureCollection , GeoJSON , Point } from "geojson" ;
2+ import Papa from "papaparse" ;
23
34import type {
45 CustomStation ,
@@ -7,42 +8,49 @@ import type {
78} from "./types" ;
89
910function parseCSV ( text : string ) : CustomStation [ ] {
10- // Expect headers including lat/lng or latitude/longitude; optional name,id
11- const lines = text
12- . split ( / \r ? \n / )
13- . map ( ( l ) => l . trim ( ) )
14- . filter ( Boolean ) ;
15- if ( lines . length === 0 ) return [ ] ;
16- const header = lines [ 0 ]
17- . split ( / , | \t | ; | \| / )
18- . map ( ( h ) => h . trim ( ) . toLowerCase ( ) ) ;
19- const latIdx = header . findIndex ( ( h ) => [ "lat" , "latitude" ] . includes ( h ) ) ;
20- const lngIdx = header . findIndex ( ( h ) =>
11+ const { data, errors } = Papa . parse < Record < string , string > > ( text , {
12+ header : true ,
13+ skipEmptyLines : true ,
14+ transformHeader : ( h ) => h . toLowerCase ( ) . trim ( ) ,
15+ } ) ;
16+
17+ if ( errors . length > 0 ) {
18+ throw new Error ( `CSV parse error: ${ errors [ 0 ] . message } ` ) ;
19+ }
20+
21+ const firstRow = data [ 0 ] ?? { } ;
22+ const headers = Object . keys ( firstRow ) ;
23+
24+ const latKey = headers . find ( ( h ) => [ "lat" , "latitude" ] . includes ( h ) ) ;
25+ const lngKey = headers . find ( ( h ) =>
2126 [ "lng" , "lon" , "long" , "longitude" ] . includes ( h ) ,
2227 ) ;
23- const nameIdx = header . findIndex ( ( h ) =>
28+ const nameKey = headers . find ( ( h ) =>
2429 [ "name" , "title" , "station" , "label" ] . includes ( h ) ,
2530 ) ;
26- const idIdx = header . findIndex ( ( h ) =>
31+ const idKey = headers . find ( ( h ) =>
2732 [ "id" , "station_id" , "osm_id" ] . includes ( h ) ,
2833 ) ;
29- const delimiter = lines [ 0 ] . includes ( "\t" )
30- ? "\t"
31- : lines [ 0 ] . includes ( ";" )
32- ? ";"
33- : lines [ 0 ] . includes ( "|" )
34- ? "|"
35- : "," ;
34+
35+ if ( ! latKey )
36+ throw new Error ( "CSV missing required 'lat' or 'latitude' column" ) ;
37+ if ( ! lngKey )
38+ throw new Error (
39+ "CSV missing required 'lng', 'lon', 'long', or 'longitude' column" ,
40+ ) ;
41+ if ( ! nameKey )
42+ throw new Error (
43+ "CSV missing required 'name', 'title', 'station', or 'label' column" ,
44+ ) ;
3645
3746 const stations : CustomStation [ ] = [ ] ;
38- for ( let i = 1 ; i < lines . length ; i ++ ) {
39- const cols = lines [ i ] . split ( delimiter ) . map ( ( c ) => c . trim ( ) ) ;
40- if ( latIdx < 0 || lngIdx < 0 ) continue ;
41- const lat = parseFloat ( cols [ latIdx ] ) ;
42- const lng = parseFloat ( cols [ lngIdx ] ) ;
47+ for ( const row of data ) {
48+ const lat = parseFloat ( row [ latKey ] ) ;
49+ const lng = parseFloat ( row [ lngKey ] ) ;
4350 if ( ! isFinite ( lat ) || ! isFinite ( lng ) ) continue ;
44- const name = nameIdx >= 0 ? cols [ nameIdx ] : undefined ;
45- const id = idIdx >= 0 && cols [ idIdx ] ? cols [ idIdx ] : `${ lat } ,${ lng } ` ;
51+ const name = row [ nameKey ] ;
52+ if ( ! name ) continue ;
53+ const id = idKey && row [ idKey ] ? row [ idKey ] : `${ lat } ,${ lng } ` ;
4654 stations . push ( { id, name, lat, lng } ) ;
4755 }
4856 return stations ;
0 commit comments