From 70d8c1b95ea6cdee970cb5dd5c4882b56c04cca8 Mon Sep 17 00:00:00 2001 From: Merlin Unterfinger Date: Thu, 11 Apr 2024 13:40:56 +0200 Subject: [PATCH] Parse all required fields and fields - Refactor entities into model package. - Add stop times to trip and trips to routes. --- .../ch/naviqore/gtfs/schedule/Calendar.java | 8 ---- .../gtfs/schedule/GtfsScheduleReader.java | 25 ++++++----- .../java/ch/naviqore/gtfs/schedule/Route.java | 4 -- .../java/ch/naviqore/gtfs/schedule/Trip.java | 4 -- .../gtfs/schedule/{ => model}/Agency.java | 2 +- .../gtfs/schedule/model/Calendar.java | 45 +++++++++++++++++++ .../schedule/{ => model}/CalendarDate.java | 4 +- .../schedule/{ => model}/GtfsSchedule.java | 2 +- .../{ => model}/GtfsScheduleBuilder.java | 32 ++++++++++--- .../naviqore/gtfs/schedule/model/Route.java | 44 ++++++++++++++++++ .../gtfs/schedule/{ => model}/Stop.java | 2 +- .../gtfs/schedule/model/StopTime.java | 6 +++ .../ch/naviqore/gtfs/schedule/model/Trip.java | 45 +++++++++++++++++++ .../schedule/{ => type}/ExceptionType.java | 2 +- .../gtfs/schedule/{ => type}/RouteType.java | 2 +- .../gtfs/schedule/type/ServiceDayTime.java | 44 ++++++++++++++++++ .../gtfs/schedule/GtfsScheduleReaderIT.java | 1 + 17 files changed, 233 insertions(+), 39 deletions(-) delete mode 100644 src/main/java/ch/naviqore/gtfs/schedule/Calendar.java delete mode 100644 src/main/java/ch/naviqore/gtfs/schedule/Route.java delete mode 100644 src/main/java/ch/naviqore/gtfs/schedule/Trip.java rename src/main/java/ch/naviqore/gtfs/schedule/{ => model}/Agency.java (66%) create mode 100644 src/main/java/ch/naviqore/gtfs/schedule/model/Calendar.java rename src/main/java/ch/naviqore/gtfs/schedule/{ => model}/CalendarDate.java (54%) rename src/main/java/ch/naviqore/gtfs/schedule/{ => model}/GtfsSchedule.java (88%) rename src/main/java/ch/naviqore/gtfs/schedule/{ => model}/GtfsScheduleBuilder.java (74%) create mode 100644 src/main/java/ch/naviqore/gtfs/schedule/model/Route.java rename src/main/java/ch/naviqore/gtfs/schedule/{ => model}/Stop.java (63%) create mode 100644 src/main/java/ch/naviqore/gtfs/schedule/model/StopTime.java create mode 100644 src/main/java/ch/naviqore/gtfs/schedule/model/Trip.java rename src/main/java/ch/naviqore/gtfs/schedule/{ => type}/ExceptionType.java (95%) rename src/main/java/ch/naviqore/gtfs/schedule/{ => type}/RouteType.java (97%) create mode 100644 src/main/java/ch/naviqore/gtfs/schedule/type/ServiceDayTime.java diff --git a/src/main/java/ch/naviqore/gtfs/schedule/Calendar.java b/src/main/java/ch/naviqore/gtfs/schedule/Calendar.java deleted file mode 100644 index aaf5cf07..00000000 --- a/src/main/java/ch/naviqore/gtfs/schedule/Calendar.java +++ /dev/null @@ -1,8 +0,0 @@ -package ch.naviqore.gtfs.schedule; - -import java.time.DayOfWeek; -import java.time.LocalDate; -import java.util.EnumSet; - -public record Calendar(String id, EnumSet serviceDays, LocalDate startDate, LocalDate endDate) { -} diff --git a/src/main/java/ch/naviqore/gtfs/schedule/GtfsScheduleReader.java b/src/main/java/ch/naviqore/gtfs/schedule/GtfsScheduleReader.java index 5b6d81fb..cb6c095c 100644 --- a/src/main/java/ch/naviqore/gtfs/schedule/GtfsScheduleReader.java +++ b/src/main/java/ch/naviqore/gtfs/schedule/GtfsScheduleReader.java @@ -1,5 +1,10 @@ package ch.naviqore.gtfs.schedule; +import ch.naviqore.gtfs.schedule.model.GtfsSchedule; +import ch.naviqore.gtfs.schedule.model.GtfsScheduleBuilder; +import ch.naviqore.gtfs.schedule.type.ExceptionType; +import ch.naviqore.gtfs.schedule.type.RouteType; +import ch.naviqore.gtfs.schedule.type.ServiceDayTime; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.RequiredArgsConstructor; @@ -53,17 +58,10 @@ public class GtfsScheduleReader { @RequiredArgsConstructor @Getter public enum GtfsFile { - AGENCY("agency.txt"), - CALENDAR_DATES("calendar_dates.txt"), - CALENDAR("calendar.txt"), - FARE_ATTRIBUTES("fare_attributes.txt"), - FARE_RULES("fare_rules.txt"), - FREQUENCIES("frequencies.txt"), - ROUTES("routes.txt"), - SHAPES("shapes.txt"), - STOP_TIMES("stop_times.txt"), - STOPS("stops.txt"), - TRIPS("trips.txt"); + AGENCY("agency.txt"), CALENDAR_DATES("calendar_dates.txt"), CALENDAR("calendar.txt"), FARE_ATTRIBUTES( + "fare_attributes.txt"), FARE_RULES("fare_rules.txt"), FREQUENCIES("frequencies.txt"), ROUTES( + "routes.txt"), SHAPES("shapes.txt"), STOP_TIMES("stop_times.txt"), STOPS("stops.txt"), TRIPS( + "trips.txt"); private final String fileName; } @@ -117,6 +115,11 @@ private GtfsSchedule buildSchedule(Map> records) { for (CSVRecord record : records.get(GtfsFile.TRIPS)) { builder.addTrip(record.get("trip_id"), record.get("route_id"), record.get("service_id")); } + for (CSVRecord record : records.get(GtfsFile.STOP_TIMES)) { + builder.addStopTime(record.get("trip_id"), record.get("stop_id"), + ServiceDayTime.parse(record.get("arrival_time")), + ServiceDayTime.parse(record.get("departure_time"))); + } return builder.build(); } diff --git a/src/main/java/ch/naviqore/gtfs/schedule/Route.java b/src/main/java/ch/naviqore/gtfs/schedule/Route.java deleted file mode 100644 index 91c97ee2..00000000 --- a/src/main/java/ch/naviqore/gtfs/schedule/Route.java +++ /dev/null @@ -1,4 +0,0 @@ -package ch.naviqore.gtfs.schedule; - -public record Route(String id, Agency agency, String shortName, String longName, RouteType type) { -} diff --git a/src/main/java/ch/naviqore/gtfs/schedule/Trip.java b/src/main/java/ch/naviqore/gtfs/schedule/Trip.java deleted file mode 100644 index 9c4f12e8..00000000 --- a/src/main/java/ch/naviqore/gtfs/schedule/Trip.java +++ /dev/null @@ -1,4 +0,0 @@ -package ch.naviqore.gtfs.schedule; - -public record Trip(String id, Route route, Calendar calendar) { -} diff --git a/src/main/java/ch/naviqore/gtfs/schedule/Agency.java b/src/main/java/ch/naviqore/gtfs/schedule/model/Agency.java similarity index 66% rename from src/main/java/ch/naviqore/gtfs/schedule/Agency.java rename to src/main/java/ch/naviqore/gtfs/schedule/model/Agency.java index f2b376e0..a15dfee9 100644 --- a/src/main/java/ch/naviqore/gtfs/schedule/Agency.java +++ b/src/main/java/ch/naviqore/gtfs/schedule/model/Agency.java @@ -1,4 +1,4 @@ -package ch.naviqore.gtfs.schedule; +package ch.naviqore.gtfs.schedule.model; public record Agency(String agency, String name, String url, String timezone) { } diff --git a/src/main/java/ch/naviqore/gtfs/schedule/model/Calendar.java b/src/main/java/ch/naviqore/gtfs/schedule/model/Calendar.java new file mode 100644 index 00000000..c718e784 --- /dev/null +++ b/src/main/java/ch/naviqore/gtfs/schedule/model/Calendar.java @@ -0,0 +1,45 @@ +package ch.naviqore.gtfs.schedule.model; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.time.DayOfWeek; +import java.time.LocalDate; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +@RequiredArgsConstructor(access = AccessLevel.PACKAGE) +@Getter +public final class Calendar { + private final String id; + private final EnumSet serviceDays; + private final LocalDate startDate; + private final LocalDate endDate; + private final Map calendarDates = new HashMap<>(); + + void addCalendarDate(CalendarDate calendarDate) { + calendarDates.put(calendarDate.date(), calendarDate); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) return true; + if (obj == null || obj.getClass() != this.getClass()) return false; + var that = (Calendar) obj; + return Objects.equals(this.id, that.id); + } + + @Override + public int hashCode() { + return Objects.hash(id); + } + + @Override + public String toString() { + return "Calendar[" + "id=" + id + ", " + "serviceDays=" + serviceDays + ", " + "startDate=" + startDate + ", " + "endDate=" + endDate + ']'; + } + +} diff --git a/src/main/java/ch/naviqore/gtfs/schedule/CalendarDate.java b/src/main/java/ch/naviqore/gtfs/schedule/model/CalendarDate.java similarity index 54% rename from src/main/java/ch/naviqore/gtfs/schedule/CalendarDate.java rename to src/main/java/ch/naviqore/gtfs/schedule/model/CalendarDate.java index 78c9f9f9..4e943425 100644 --- a/src/main/java/ch/naviqore/gtfs/schedule/CalendarDate.java +++ b/src/main/java/ch/naviqore/gtfs/schedule/model/CalendarDate.java @@ -1,4 +1,6 @@ -package ch.naviqore.gtfs.schedule; +package ch.naviqore.gtfs.schedule.model; + +import ch.naviqore.gtfs.schedule.type.ExceptionType; import java.time.LocalDate; diff --git a/src/main/java/ch/naviqore/gtfs/schedule/GtfsSchedule.java b/src/main/java/ch/naviqore/gtfs/schedule/model/GtfsSchedule.java similarity index 88% rename from src/main/java/ch/naviqore/gtfs/schedule/GtfsSchedule.java rename to src/main/java/ch/naviqore/gtfs/schedule/model/GtfsSchedule.java index 4473613b..003e808b 100644 --- a/src/main/java/ch/naviqore/gtfs/schedule/GtfsSchedule.java +++ b/src/main/java/ch/naviqore/gtfs/schedule/model/GtfsSchedule.java @@ -1,4 +1,4 @@ -package ch.naviqore.gtfs.schedule; +package ch.naviqore.gtfs.schedule.model; import lombok.AccessLevel; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/ch/naviqore/gtfs/schedule/GtfsScheduleBuilder.java b/src/main/java/ch/naviqore/gtfs/schedule/model/GtfsScheduleBuilder.java similarity index 74% rename from src/main/java/ch/naviqore/gtfs/schedule/GtfsScheduleBuilder.java rename to src/main/java/ch/naviqore/gtfs/schedule/model/GtfsScheduleBuilder.java index 8c151bf3..1de36e6c 100644 --- a/src/main/java/ch/naviqore/gtfs/schedule/GtfsScheduleBuilder.java +++ b/src/main/java/ch/naviqore/gtfs/schedule/model/GtfsScheduleBuilder.java @@ -1,5 +1,8 @@ -package ch.naviqore.gtfs.schedule; +package ch.naviqore.gtfs.schedule.model; +import ch.naviqore.gtfs.schedule.type.ExceptionType; +import ch.naviqore.gtfs.schedule.type.RouteType; +import ch.naviqore.gtfs.schedule.type.ServiceDayTime; import lombok.AccessLevel; import lombok.NoArgsConstructor; import lombok.extern.log4j.Log4j2; @@ -55,8 +58,7 @@ public GtfsScheduleBuilder addRoute(String id, String agencyId, String shortName return this; } - public GtfsScheduleBuilder addCalendar(String id, EnumSet serviceDays, LocalDate startDate, - LocalDate endDate) { + public GtfsScheduleBuilder addCalendar(String id, EnumSet serviceDays, LocalDate startDate, LocalDate endDate) { if (calendars.containsKey(id)) { throw new IllegalArgumentException("Calendar " + id + " already exists"); } @@ -71,8 +73,8 @@ public GtfsScheduleBuilder addCalendarDate(String calendarId, LocalDate date, Ex throw new IllegalArgumentException("Calendar " + calendarId + " does not exist"); } log.debug("Adding calendar {}-{}", calendarId, date); - // TODO: Handle calendar dates - var calendarDate = new CalendarDate(calendar, date, type); + CalendarDate calendarDate = new CalendarDate(calendar, date, type); + calendar.addCalendarDate(calendarDate); return this; } @@ -89,7 +91,25 @@ public GtfsScheduleBuilder addTrip(String id, String routeId, String serviceId) throw new IllegalArgumentException("Calendar " + serviceId + " does not exist"); } log.debug("Adding trip {}", id); - trips.put(id, new Trip(id, route, calendar)); + Trip trip = new Trip(id, route, calendar); + route.addTrip(trip); + trips.put(id, trip); + return this; + } + + public GtfsScheduleBuilder addStopTime(String tripId, String stopId, ServiceDayTime arrival, ServiceDayTime departure) { + Trip trip = trips.get(tripId); + if (trip == null) { + throw new IllegalArgumentException("Trip " + tripId + " does not exist"); + } + Stop stop = stops.get(stopId); + if (stop == null) { + throw new IllegalArgumentException("Stop " + stopId + " does not exist"); + } + log.debug("Adding stop {} to trip {} ({}-{})", stopId, tripId, arrival, departure); + StopTime stopTime = new StopTime(stop, trip, arrival, departure); + trip.addStopTime(stopTime); + // TODO: Add stop time to stop return this; } diff --git a/src/main/java/ch/naviqore/gtfs/schedule/model/Route.java b/src/main/java/ch/naviqore/gtfs/schedule/model/Route.java new file mode 100644 index 00000000..47139f65 --- /dev/null +++ b/src/main/java/ch/naviqore/gtfs/schedule/model/Route.java @@ -0,0 +1,44 @@ +package ch.naviqore.gtfs.schedule.model; + +import ch.naviqore.gtfs.schedule.type.RouteType; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +@RequiredArgsConstructor(access = AccessLevel.PACKAGE) +@Getter +public final class Route { + private final String id; + private final Agency agency; + private final String shortName; + private final String longName; + private final RouteType type; + private final List trips = new ArrayList<>(); + + void addTrip(Trip trip) { + trips.add(trip); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) return true; + if (obj == null || obj.getClass() != this.getClass()) return false; + var that = (Route) obj; + return Objects.equals(this.id, that.id); + } + + @Override + public int hashCode() { + return Objects.hash(id); + } + + @Override + public String toString() { + return "Route[" + "id=" + id + ", " + "agency=" + agency + ", " + "shortName=" + shortName + ", " + "longName=" + longName + ", " + "type=" + type + ']'; + } + +} diff --git a/src/main/java/ch/naviqore/gtfs/schedule/Stop.java b/src/main/java/ch/naviqore/gtfs/schedule/model/Stop.java similarity index 63% rename from src/main/java/ch/naviqore/gtfs/schedule/Stop.java rename to src/main/java/ch/naviqore/gtfs/schedule/model/Stop.java index e10b3ef9..d570ddff 100644 --- a/src/main/java/ch/naviqore/gtfs/schedule/Stop.java +++ b/src/main/java/ch/naviqore/gtfs/schedule/model/Stop.java @@ -1,4 +1,4 @@ -package ch.naviqore.gtfs.schedule; +package ch.naviqore.gtfs.schedule.model; public record Stop(String id, String name, double lat, double lon) { } diff --git a/src/main/java/ch/naviqore/gtfs/schedule/model/StopTime.java b/src/main/java/ch/naviqore/gtfs/schedule/model/StopTime.java new file mode 100644 index 00000000..122eeecf --- /dev/null +++ b/src/main/java/ch/naviqore/gtfs/schedule/model/StopTime.java @@ -0,0 +1,6 @@ +package ch.naviqore.gtfs.schedule.model; + +import ch.naviqore.gtfs.schedule.type.ServiceDayTime; + +public record StopTime(Stop stop, Trip trip, ServiceDayTime arrival, ServiceDayTime departure) { +} diff --git a/src/main/java/ch/naviqore/gtfs/schedule/model/Trip.java b/src/main/java/ch/naviqore/gtfs/schedule/model/Trip.java new file mode 100644 index 00000000..973a8e29 --- /dev/null +++ b/src/main/java/ch/naviqore/gtfs/schedule/model/Trip.java @@ -0,0 +1,45 @@ +package ch.naviqore.gtfs.schedule.model; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +@RequiredArgsConstructor +@Getter +public final class Trip implements Comparable { + private final String id; + private final Route route; + private final Calendar calendar; + private final List stopTimes = new ArrayList<>(); + + void addStopTime(StopTime stopTime) { + stopTimes.add(stopTime); + } + + @Override + public int compareTo(Trip o) { + // TODO: Sort stopTimes, then return first stop. + return 0; + } + + @Override + public boolean equals(Object obj) { + if (obj == this) return true; + if (obj == null || obj.getClass() != this.getClass()) return false; + var that = (Trip) obj; + return Objects.equals(this.id, that.id); + } + + @Override + public int hashCode() { + return Objects.hash(id); + } + + @Override + public String toString() { + return "Trip[" + "id=" + id + ", " + "route=" + route + ", " + "calendar=" + calendar + ']'; + } +} diff --git a/src/main/java/ch/naviqore/gtfs/schedule/ExceptionType.java b/src/main/java/ch/naviqore/gtfs/schedule/type/ExceptionType.java similarity index 95% rename from src/main/java/ch/naviqore/gtfs/schedule/ExceptionType.java rename to src/main/java/ch/naviqore/gtfs/schedule/type/ExceptionType.java index 1f6c8ec6..6bd88e71 100644 --- a/src/main/java/ch/naviqore/gtfs/schedule/ExceptionType.java +++ b/src/main/java/ch/naviqore/gtfs/schedule/type/ExceptionType.java @@ -1,4 +1,4 @@ -package ch.naviqore.gtfs.schedule; +package ch.naviqore.gtfs.schedule.type; import lombok.AccessLevel; import lombok.Getter; diff --git a/src/main/java/ch/naviqore/gtfs/schedule/RouteType.java b/src/main/java/ch/naviqore/gtfs/schedule/type/RouteType.java similarity index 97% rename from src/main/java/ch/naviqore/gtfs/schedule/RouteType.java rename to src/main/java/ch/naviqore/gtfs/schedule/type/RouteType.java index e476fda3..9c17d404 100644 --- a/src/main/java/ch/naviqore/gtfs/schedule/RouteType.java +++ b/src/main/java/ch/naviqore/gtfs/schedule/type/RouteType.java @@ -1,4 +1,4 @@ -package ch.naviqore.gtfs.schedule; +package ch.naviqore.gtfs.schedule.type; import lombok.AccessLevel; import lombok.Getter; diff --git a/src/main/java/ch/naviqore/gtfs/schedule/type/ServiceDayTime.java b/src/main/java/ch/naviqore/gtfs/schedule/type/ServiceDayTime.java new file mode 100644 index 00000000..ea37c8b5 --- /dev/null +++ b/src/main/java/ch/naviqore/gtfs/schedule/type/ServiceDayTime.java @@ -0,0 +1,44 @@ +package ch.naviqore.gtfs.schedule.type; + +import lombok.EqualsAndHashCode; +import lombok.Getter; + + +/** + * Service day time + *

+ * A service day may end after 24:00:00 and times like 25:30:00 are possible values. java.time.LocalTime does not + * support this. Therefore, this time class is used to be able to cover service days bigger than 24 hours. + * + * @author munterfi + */ +@EqualsAndHashCode +@Getter +public class ServiceDayTime implements Comparable { + private final int totalSeconds; + + public ServiceDayTime(int hours, int minutes, int seconds) { + this.totalSeconds = seconds + 60 * minutes + 3600 * hours; + } + + public static ServiceDayTime parse(String timeString) { + String[] parts = timeString.split(":"); + int hours = Integer.parseInt(parts[0]); + int minutes = Integer.parseInt(parts[1]); + int seconds = Integer.parseInt(parts[2]); + return new ServiceDayTime(hours, minutes, seconds); + } + + @Override + public int compareTo(ServiceDayTime o) { + return Integer.compare(totalSeconds, o.totalSeconds); + } + + @Override + public String toString() { + int hours = totalSeconds / 3600; + int minutes = (totalSeconds % 3600) / 60; + int seconds = totalSeconds % 60; + return String.format("%02d:%02d:%02d", hours, minutes, seconds); + } +} diff --git a/src/test/java/ch/naviqore/gtfs/schedule/GtfsScheduleReaderIT.java b/src/test/java/ch/naviqore/gtfs/schedule/GtfsScheduleReaderIT.java index 0da46364..42937656 100644 --- a/src/test/java/ch/naviqore/gtfs/schedule/GtfsScheduleReaderIT.java +++ b/src/test/java/ch/naviqore/gtfs/schedule/GtfsScheduleReaderIT.java @@ -1,5 +1,6 @@ package ch.naviqore.gtfs.schedule; +import ch.naviqore.gtfs.schedule.model.GtfsSchedule; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir;