Writing to a GTFS file
Similarly reading GTFS file, one can write GTFS files easily using this library. You can either modify an existing file, or create a new one from scratch.
The writing pipes, live in the write
namespace within the GtfsFile
class. This namespace provides a handful of Pipe
s which give access to standard GTFS files.
import com.mobimeo.gtfs.file._
import com.mobimeo.gtfs.model._
import cats.effect._
import cats.effect.unsafe.implicits.global
import fs2.io.file.{CopyFlag, CopyFlags, Path}
val gtfs = GtfsFile[IO](Path("site/gtfs.zip"))
// gtfs: Resource[IO, GtfsFile[IO]] = Bind(
// source = Allocate(
// resource = cats.effect.kernel.Resource$$$Lambda$16478/0x0000000804362840@1554ed9
// ),
// fs = cats.effect.kernel.Resource$$Lambda$16480/0x0000000804364040@35cd7257
// )
Modifying an existing file
Oftentimes you already have an existing file in your hands and you want to modify it. To this end, you can pipe the read stream into the corresponding pipe.
gtfs.use { gtfs =>
gtfs.read
.rawStops
.map(s => s.modify("stop_name")(_.toUpperCase))
.through(gtfs.write.rawStops)
.compile
.drain
}
This code modifies the file in place, making all stop names uppercase. However this is usually not recommended as data are overwritten and original data are replaced.
One should prefer to work on a copy of the original file. The GtfsFile
provides a way to do it conveniently.
val modified = Path("site/modified-gtfs.zip")
// modified: Path = site/modified-gtfs.zip
gtfs.use { src =>
src.copyTo(modified, CopyFlags(CopyFlag.ReplaceExisting)).use { tgt =>
src.read
.rawStops
.map(s => s.modify("stop_name")(_.toUpperCase))
.through(tgt.write.rawStops)
.compile
.drain
}
}.unsafeRunSync()
def printStops(gtfs: GtfsFile[IO]) =
gtfs.read
.rawStops
.map(s => s("stop_name"))
.unNone
.take(5)
.intersperse("\n")
.evalMap(s => IO(print(s)))
.compile
.drain
// original file
gtfs.use(printStops(_)).unsafeRunSync()
// S+U Berlin Hauptbahnhof
// S+U Berlin Hauptbahnhof
// Berlin, Friedrich-Olbricht-Damm/Saatwinkler Damm
// Berlin, Stieffring
// Berlin, Lehrter Str./Invalidenstr.
// modified file
GtfsFile[IO](modified).use(printStops(_)).unsafeRunSync()
// S+U BERLIN HAUPTBAHNHOF
// S+U BERLIN HAUPTBAHNHOF
// BERLIN, FRIEDRICH-OLBRICHT-DAMM/SAATWINKLER DAMM
// BERLIN, STIEFFRING
// BERLIN, LEHRTER STR./INVALIDENSTR.
When using copyTo
the entirety of the original GTFS file content is copied and only files that are written to are modified. The rest is identical to the original file (including potential non standard files).
Creating a new file
If one wants to create a new file from scratch, one need to tell the file needs to be created when creating the GTFS resource. An empty GTFS file will be created, and files can be added to it by using the associated write
pipes.
def makeStop(id: String, name: String) =
Stop(id, None, Some(name), None, None, None, None, None, None, None, None, None, None, None)
val file = Path("site/gtfs2.zip")
// file: Path = site/gtfs2.zip
GtfsFile[IO](file, create = true).use { gtfs =>
fs2.Stream.emits(List(makeStop("stop1", "Some Stop"), makeStop("stop2", "Some Other Stop")))
.covary[IO]
.through(gtfs.write.stops[Stop])
.compile
.drain
}.unsafeRunSync()
GtfsFile[IO](file).use(printStops(_)).unsafeRunSync()
// Some Stop
// Some Other Stop