55 "context"
66 "crypto/sha1"
77 "encoding/csv"
8+ "encoding/json"
89 "errors"
910 "fmt"
1011 "io"
@@ -17,6 +18,7 @@ import (
1718 "github.com/interline-io/log"
1819 "github.com/interline-io/transitland-lib/causes"
1920 "github.com/interline-io/transitland-lib/request"
21+ "github.com/twpayne/go-geom/encoding/geojson"
2022)
2123
2224// Adapter provides an interface for working with various kinds of GTFS sources: zip, directory, url.
@@ -35,6 +37,7 @@ type Adapter interface {
3537// WriterAdapter provides a writing interface.
3638type WriterAdapter interface {
3739 WriteRows (string , [][]string ) error
40+ WriteFeatures (string , []* geojson.Feature ) error
3841 Adapter
3942}
4043
@@ -381,15 +384,17 @@ func (adapter *ZipAdapter) findInternalPrefix() (string, error) {
381384
382385// DirAdapter supports plain directories of CSV files.
383386type DirAdapter struct {
384- path string
385- files map [string ]* os.File
387+ path string
388+ files map [string ]* os.File
389+ geojsonFeatures map [string ][]* geojson.Feature
386390}
387391
388392// NewDirAdapter returns an initialized DirAdapter.
389393func NewDirAdapter (path string ) * DirAdapter {
390394 return & DirAdapter {
391- path : strings .TrimPrefix (path , "file://" ),
392- files : map [string ]* os.File {},
395+ path : strings .TrimPrefix (path , "file://" ),
396+ files : map [string ]* os.File {},
397+ geojsonFeatures : map [string ][]* geojson.Feature {},
393398 }
394399}
395400
@@ -457,8 +462,19 @@ func (adapter *DirAdapter) Open() error {
457462 return nil
458463}
459464
460- // Close the adapter.
465+ // Close the adapter. Flushes any buffered GeoJSON files before closing.
461466func (adapter * DirAdapter ) Close () error {
467+ // Flush all buffered GeoJSON files
468+ for filename , features := range adapter .geojsonFeatures {
469+ if len (features ) > 0 {
470+ if err := adapter .flushGeoJSON (filename , features ); err != nil {
471+ return err
472+ }
473+ }
474+ }
475+ adapter .geojsonFeatures = map [string ][]* geojson.Feature {}
476+
477+ // Close all file handles
462478 for _ , f := range adapter .files {
463479 if err := f .Close (); err != nil {
464480 return err
@@ -542,6 +558,40 @@ func (adapter *DirAdapter) WriteRows(filename string, rows [][]string) error {
542558 return nil
543559}
544560
561+ // WriteFeatures buffers GeoJSON features to be written when the adapter is closed.
562+ // This mirrors how CSV rows are buffered and written.
563+ func (adapter * DirAdapter ) WriteFeatures (filename string , features []* geojson.Feature ) error {
564+ if len (features ) == 0 {
565+ return nil
566+ }
567+ adapter .geojsonFeatures [filename ] = append (adapter .geojsonFeatures [filename ], features ... )
568+ return nil
569+ }
570+
571+ // flushGeoJSON writes all buffered features for a file as a FeatureCollection.
572+ func (adapter * DirAdapter ) flushGeoJSON (filename string , features []* geojson.Feature ) error {
573+ // Close existing file if open (we need to overwrite, not append)
574+ if in , ok := adapter .files [filename ]; ok {
575+ in .Close ()
576+ delete (adapter .files , filename )
577+ }
578+
579+ // Create new file
580+ in , err := os .Create (filepath .Join (adapter .path , filename ))
581+ if err != nil {
582+ return err
583+ }
584+ adapter .files [filename ] = in
585+
586+ // Write FeatureCollection
587+ fc := geojson.FeatureCollection {
588+ Features : features ,
589+ }
590+ encoder := json .NewEncoder (in )
591+ encoder .SetIndent ("" , " " )
592+ return encoder .Encode (& fc )
593+ }
594+
545595/////////////////////
546596
547597// ZipWriterAdapter functions the same as DirAdapter, but writes to a temporary directory, and creates a zip archive when closed.
@@ -564,6 +614,16 @@ func NewZipWriterAdapter(path string) *ZipWriterAdapter {
564614
565615// Close creates a zip archive of all the written files at the specified destination.
566616func (adapter * ZipWriterAdapter ) Close () error {
617+ // Flush any buffered GeoJSON files first
618+ for filename , features := range adapter .DirAdapter .geojsonFeatures {
619+ if len (features ) > 0 {
620+ if err := adapter .DirAdapter .flushGeoJSON (filename , features ); err != nil {
621+ return err
622+ }
623+ }
624+ }
625+ adapter .DirAdapter .geojsonFeatures = map [string ][]* geojson.Feature {}
626+
567627 out , err := os .Create (adapter .outpath )
568628 if err != nil {
569629 return nil
0 commit comments