diff --git a/.gitignore b/.gitignore index 86d823fc..ef0b9a18 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,5 @@ pt2matsim.iml test/gtfs-feed-rewrite/ test/stib-mivb-gtfs/ + +test/Gtfs2TransitScheduleIT/output/ \ No newline at end of file diff --git a/src/main/java/org/matsim/pt2matsim/examples/Workflow.java b/src/main/java/org/matsim/pt2matsim/examples/Workflow.java index 95bfdb3a..8354743a 100644 --- a/src/main/java/org/matsim/pt2matsim/examples/Workflow.java +++ b/src/main/java/org/matsim/pt2matsim/examples/Workflow.java @@ -28,7 +28,7 @@ public static void main(String[] args) { // convert schedule String unmappedSchedule = "intermediate/schedule_unmapped.xml.gz"; - Gtfs2TransitSchedule.run("gtfs", "dayWithMostTrips", osmConfig.getOutputCoordinateSystem(), unmappedSchedule, "output/vehicles.xml.gz"); + Gtfs2TransitSchedule.run("gtfs", "dayWithMostTrips", osmConfig.getOutputCoordinateSystem(), unmappedSchedule, "output/vehicles.xml.gz", "schedule"); // setup public transit mapper PublicTransitMappingConfigGroup mapperConfig = PublicTransitMappingConfigGroup.createDefaultConfig(); diff --git a/src/main/java/org/matsim/pt2matsim/gtfs/AdditionalTransitLineInfo.java b/src/main/java/org/matsim/pt2matsim/gtfs/AdditionalTransitLineInfo.java new file mode 100644 index 00000000..69ed3ace --- /dev/null +++ b/src/main/java/org/matsim/pt2matsim/gtfs/AdditionalTransitLineInfo.java @@ -0,0 +1,107 @@ +/* *********************************************************************** * + * project: org.matsim.* + * *********************************************************************** * + * * + * copyright : (C) 2024 by the members listed in the COPYING, * + * LICENSE and WARRANTY file. * + * email : info at matsim dot org * + * * + * *********************************************************************** * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * See also COPYING, LICENSE and WARRANTY file * + * * + * *********************************************************************** */ +package org.matsim.pt2matsim.gtfs; + +import org.matsim.pt2matsim.gtfs.lib.GtfsDefinitions; +import org.matsim.pt2matsim.gtfs.lib.Route; + +/** + * Simple container class for useful but not necessary info extracted from the gtfs schedule + * + * @author tkohl (Royal2Flush) + */ +public class AdditionalTransitLineInfo { + public static final String INFO_COLUMN_ID = "lineId"; + public static final String INFO_COLUMN_SHORTNAME = "shortName"; + public static final String INFO_COLUMN_LONGNAME = "longName"; + public static final String INFO_COLUMN_TYPE = "type"; + public static final String INFO_COLUMN_DESCRIPTION = "description"; + public static final String INFO_COLUMN_AGENCY_ID = "agencyId"; + public static final String INFO_COLUMN_AGENCY_NAME = "agencyName"; + public static final String INFO_COLUMN_AGENCY_URL = "agencyURL"; + public static final String INFO_COLUMN_NUM_TRANSIT_ROUTES = "numTransitRoutes"; + public static final String INFO_COLUMN_NUM_TOTAL_DEPARTURES = "totalDepartures"; + + private final String id; + private final String shortName; + private final String longName; + + private final GtfsDefinitions.RouteType routeType; + private final String routeDescription; + private final String agencyId; + private final String agencyName; + private final String agencyURL; + private int numberOfTransitRoutes = 0; + private int totalNumberOfDepartures = 0; + + AdditionalTransitLineInfo(Route gtfsRoute) { + this.id = gtfsRoute.getId(); + this.shortName = gtfsRoute.getShortName(); + this.longName = gtfsRoute.getLongName(); + this.routeType = gtfsRoute.getRouteType(); + this.routeDescription = gtfsRoute.getDescription(); + this.agencyId = gtfsRoute.getAgency().getId(); + this.agencyName = gtfsRoute.getAgency().getAgencyName(); + this.agencyURL = gtfsRoute.getAgency().getAgencyUrl(); + } + + void countRoute(int numRouteDepartures) { + this.numberOfTransitRoutes++; + this.totalNumberOfDepartures += numRouteDepartures; + } + + public String getId() { + return id; + } + + public String getShortName() { + return this.shortName; + } + + public String getLongName() { + return longName; + } + + public GtfsDefinitions.RouteType getRouteType() { + return routeType; + } + + public String getRouteDescription() { + return routeDescription; + } + + public String getAgencyId() { + return agencyId; + } + + public String getAgencyName() { + return agencyName; + } + + public String getAgencyURL() { + return agencyURL; + } + + public int getNumberOfTransitRoutes() { + return numberOfTransitRoutes; + } + + public int getTotalNumberOfDepartures() { + return totalNumberOfDepartures; + } +} diff --git a/src/main/java/org/matsim/pt2matsim/gtfs/GtfsConverter.java b/src/main/java/org/matsim/pt2matsim/gtfs/GtfsConverter.java index daee387e..815153da 100644 --- a/src/main/java/org/matsim/pt2matsim/gtfs/GtfsConverter.java +++ b/src/main/java/org/matsim/pt2matsim/gtfs/GtfsConverter.java @@ -35,6 +35,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.TreeMap; /** * Converts a GTFS feed to a MATSim transit schedule @@ -56,6 +57,7 @@ public class GtfsConverter { protected TransitSchedule transitSchedule; protected Vehicles vehiclesContainer; + protected Map, AdditionalTransitLineInfo> additionalLineInfo = new TreeMap<>(); protected int noStopTimeTrips; protected int stopPairsWithoutOffset; @@ -80,6 +82,10 @@ public TransitSchedule getSchedule() { public Vehicles getVehicles() { return this.vehiclesContainer; } + + public Map, AdditionalTransitLineInfo> getAdditionalLineInfo() { + return this.additionalLineInfo; + } /** * Converts the loaded gtfs data to the given matsim transit schedule @@ -184,6 +190,7 @@ protected void createTransitLines(TransitSchedule schedule, LocalDate extractDat for(Route gtfsRoute : this.feed.getRoutes().values()) { // create a MATSim TransitLine for each Route TransitLine newTransitLine = createTransitLine(gtfsRoute); + AdditionalTransitLineInfo info = additionalLineInfo.get(newTransitLine.getId()); if(newTransitLine != null) { schedule.addTransitLine(newTransitLine); @@ -194,6 +201,7 @@ protected void createTransitLines(TransitSchedule schedule, LocalDate extractDat TransitRoute transitRoute = createTransitRoute(trip, schedule.getFacilities()); if(transitRoute != null) { newTransitLine.addRoute(transitRoute); + info.countRoute(transitRoute.getDepartures().size()); } } } @@ -215,6 +223,8 @@ protected TransitLine createTransitLine(Route gtfsRoute) { Id id = createTransitLineId(gtfsRoute); TransitLine line = this.scheduleFactory.createTransitLine(id); line.setName(gtfsRoute.getShortName()); + AdditionalTransitLineInfo info = new AdditionalTransitLineInfo(gtfsRoute); + additionalLineInfo.put(id, info); return line; } diff --git a/src/main/java/org/matsim/pt2matsim/gtfs/GtfsFeed.java b/src/main/java/org/matsim/pt2matsim/gtfs/GtfsFeed.java index 4da28496..e0f2ca75 100644 --- a/src/main/java/org/matsim/pt2matsim/gtfs/GtfsFeed.java +++ b/src/main/java/org/matsim/pt2matsim/gtfs/GtfsFeed.java @@ -40,6 +40,8 @@ public interface GtfsFeed { Map getServices(); Map getTrips(); + + Map getAgencies(); Map, RouteShape> getShapes(); diff --git a/src/main/java/org/matsim/pt2matsim/gtfs/GtfsFeedImpl.java b/src/main/java/org/matsim/pt2matsim/gtfs/GtfsFeedImpl.java index a33bb95c..12597251 100755 --- a/src/main/java/org/matsim/pt2matsim/gtfs/GtfsFeedImpl.java +++ b/src/main/java/org/matsim/pt2matsim/gtfs/GtfsFeedImpl.java @@ -73,6 +73,7 @@ public class GtfsFeedImpl implements GtfsFeed { protected Set serviceIdsNotInCalendarTxt = new HashSet<>(); // containers for storing gtfs data + protected Map agencies = new HashMap<>(); protected Map stops = new HashMap<>(); protected Map routes = new TreeMap<>(); protected Map services = new HashMap<>(); @@ -138,6 +139,11 @@ protected void loadFiles(String inputPath) { this.root = inputPath; log.info("Loading GTFS files from " + root); + try { + loadAgencies(); + } catch (IOException e) { + throw new RuntimeException("File agency.txt not found!"); + } try { loadStops(); } catch (IOException e) { @@ -181,6 +187,44 @@ protected CSVReader createCSVReader(String path) throws FileNotFoundException { InputStream stream = new BOMInputStream(new FileInputStream(path)); return new CSVReader(new InputStreamReader(stream, StandardCharsets.UTF_8)); } + + /** + * Reads all agencies and puts them in {@link #agencies} + *

+ *

+ * agency.txt [https://developers.google.com/transit/gtfs/reference]
+ * Transit agencies with service represented in this dataset. + * @throws FileNotFoundException + * + * @throws IOException + */ + protected void loadAgencies() throws IOException { + log.info("Loading agency.txt"); + + int l = 1; + try { + CSVReader reader = createCSVReader(root + GtfsDefinitions.Files.AGENCY.fileName); + String[] header = reader.readNext(); // read header + Map col = getIndices(header, GtfsDefinitions.Files.AGENCY.columns, GtfsDefinitions.Files.AGENCY.optionalColumns); // get column numbers for required fields + + String[] line = reader.readNext(); + while(line != null) { + l++; + String agencyId = line[col.get(GtfsDefinitions.AGENCY_ID)]; + AgencyImpl agency = new AgencyImpl(agencyId, line[col.get(GtfsDefinitions.AGENCY_NAME)], line[col.get(GtfsDefinitions.AGENCY_URL)], line[col.get(GtfsDefinitions.AGENCY_TIMEZONE)]); + agencies.put(agencyId, agency); + + line = reader.readNext(); + } + + reader.close(); + } catch (ArrayIndexOutOfBoundsException e) { + throw new RuntimeException("Line " + l + " in agency.txt is empty or malformed."); + } catch (CsvValidationException e) { + throw new RuntimeException(e); + } + log.info("... agency.txt loaded"); + } /** * Reads all stops and puts them in {@link #stops} @@ -413,7 +457,12 @@ protected void loadRoutes() throws IOException { String routeId = line[col.get(GtfsDefinitions.ROUTE_ID)]; String shortName = line[col.get(GtfsDefinitions.ROUTE_SHORT_NAME)]; String longName = line[col.get(GtfsDefinitions.ROUTE_LONG_NAME)]; - Route newGtfsRoute = new RouteImpl(routeId, shortName, longName, extendedRouteType); + + Agency agency = this.agencies.get(line[col.get(GtfsDefinitions.AGENCY_ID)]); + if (agency == null) { + throw new RuntimeException("Line " + l + " in routes.txt references unknown agency id " + line[col.get(GtfsDefinitions.AGENCY_ID)]); + } + Route newGtfsRoute = new RouteImpl(routeId, shortName, longName, agency, extendedRouteType); routes.put(line[col.get(GtfsDefinitions.ROUTE_ID)], newGtfsRoute); line = reader.readNext(); @@ -752,5 +801,10 @@ public Map getServices() { public Map getTrips() { return trips; } + + @Override + public Map getAgencies() { + return agencies; + } } \ No newline at end of file diff --git a/src/main/java/org/matsim/pt2matsim/gtfs/lib/Agency.java b/src/main/java/org/matsim/pt2matsim/gtfs/lib/Agency.java index 008291f2..8dceb945 100644 --- a/src/main/java/org/matsim/pt2matsim/gtfs/lib/Agency.java +++ b/src/main/java/org/matsim/pt2matsim/gtfs/lib/Agency.java @@ -19,10 +19,11 @@ package org.matsim.pt2matsim.gtfs.lib; /** - * Not implemented * @author polettif */ public interface Agency { + + String getId(); String getAgencyName(); diff --git a/src/main/java/org/matsim/pt2matsim/gtfs/lib/AgencyImpl.java b/src/main/java/org/matsim/pt2matsim/gtfs/lib/AgencyImpl.java new file mode 100644 index 00000000..b658f121 --- /dev/null +++ b/src/main/java/org/matsim/pt2matsim/gtfs/lib/AgencyImpl.java @@ -0,0 +1,84 @@ +/* *********************************************************************** * + * project: org.matsim.* + * *********************************************************************** * + * * + * copyright : (C) 2024 by the members listed in the COPYING, * + * LICENSE and WARRANTY file. * + * email : info at matsim dot org * + * * + * *********************************************************************** * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * See also COPYING, LICENSE and WARRANTY file * + * * + * *********************************************************************** */ +package org.matsim.pt2matsim.gtfs.lib; + +/** + * @author tkohl (Royal2Flush) + */ +public class AgencyImpl implements Agency { + + private final String id; + private final String name; + private final String url; + private final String timezone; + + public AgencyImpl(String id, String name, String url, String timezone) { + this.id = id; + this.name = name; + this.url = url; + this.timezone = timezone; + } + + @Override + public String getId() { + return this.id; + } + + @Override + public String getAgencyName() { + return this.name; + } + + @Override + public String getAgencyUrl() { + return this.url; + } + + @Override + public String getAgencyTimeZone() { + return this.timezone; + } + + @Override + public boolean equals(Object o) { + if(this == o) return true; + if(o == null || getClass() != o.getClass()) return false; + + AgencyImpl other = (AgencyImpl) o; + + if(!this.id.equals(other.id)) return false; + if(!this.name.equals(other.name)) return false; + if(!this.url.equals(other.url)) return false; + return this.timezone.equals(other.timezone); + } + + @Override + public int hashCode() { + int result = id.hashCode(); + result = 31 * result + name.hashCode(); + result = 31 * result + url.hashCode(); + result = 31 * result + timezone.hashCode(); + return result; + } + + @Override + public String toString() { + return "[agency:" + id + ", \"" + name + "\"]"; + } + +} diff --git a/src/main/java/org/matsim/pt2matsim/gtfs/lib/GtfsDefinitions.java b/src/main/java/org/matsim/pt2matsim/gtfs/lib/GtfsDefinitions.java index 88400583..39205793 100644 --- a/src/main/java/org/matsim/pt2matsim/gtfs/lib/GtfsDefinitions.java +++ b/src/main/java/org/matsim/pt2matsim/gtfs/lib/GtfsDefinitions.java @@ -26,6 +26,15 @@ public final class GtfsDefinitions { // column names + public static final String AGENCY_ID = "agency_id"; + public static final String AGENCY_NAME = "agency_name"; + public static final String AGENCY_URL = "agency_url"; + public static final String AGENCY_TIMEZONE = "agency_timezone"; + public static final String AGENCY_LANG = "agency_lang"; + public static final String AGENCY_PHONE = "agency_phone"; + public static final String AGENCY_FARE_URL = "agency_fare_url"; + public static final String AGENCY_EMAIL = "agency_email"; + public static final String SHAPE_ID = "shape_id"; public static final String SHAPE_PT_LON = "shape_pt_lon"; public static final String SHAPE_PT_LAT = "shape_pt_lat"; @@ -98,6 +107,10 @@ public final class GtfsDefinitions { * Values */ public enum Files { + AGENCY("Agency", "agency.txt", + new String[]{AGENCY_ID, AGENCY_NAME, AGENCY_URL, AGENCY_TIMEZONE}, + new String[]{AGENCY_LANG, AGENCY_PHONE, AGENCY_FARE_URL, AGENCY_EMAIL}), + STOPS("Stop", "stops.txt", new String[]{STOP_ID, STOP_LON, STOP_LAT, STOP_NAME}, new String[]{STOP_CODE, STOP_DESC, ZONE_ID, STOP_URL, LOCATION_TYPE, PARENT_STATION, STOP_TIMEZONE, WHEELCHAIR_BOARDING}), @@ -119,7 +132,7 @@ public enum Files { ROUTES("Route", "routes.txt", - new String[]{ROUTE_ID, ROUTE_SHORT_NAME, ROUTE_LONG_NAME, ROUTE_TYPE}, + new String[]{ROUTE_ID, AGENCY_ID, ROUTE_SHORT_NAME, ROUTE_LONG_NAME, ROUTE_TYPE}, new String[]{ROUTE_DESC, ROUTE_URL, ROUTE_COLOR, ROUTE_TEXT_COLOR}), TRIPS("Trip", diff --git a/src/main/java/org/matsim/pt2matsim/gtfs/lib/Route.java b/src/main/java/org/matsim/pt2matsim/gtfs/lib/Route.java index fe9d6299..4a84d05e 100644 --- a/src/main/java/org/matsim/pt2matsim/gtfs/lib/Route.java +++ b/src/main/java/org/matsim/pt2matsim/gtfs/lib/Route.java @@ -33,6 +33,9 @@ public interface Route { /** required **/ String getLongName(); + + /** required **/ + Agency getAgency(); /** required **/ GtfsDefinitions.RouteType getRouteType(); @@ -43,4 +46,9 @@ public interface Route { * The base route types are part of the extended set, does not return null */ GtfsDefinitions.ExtendedRouteType getExtendedRouteType(); + + /** optional **/ + void setDescription(String description); + + String getDescription(); } diff --git a/src/main/java/org/matsim/pt2matsim/gtfs/lib/RouteImpl.java b/src/main/java/org/matsim/pt2matsim/gtfs/lib/RouteImpl.java index 89f4260e..89ba2355 100644 --- a/src/main/java/org/matsim/pt2matsim/gtfs/lib/RouteImpl.java +++ b/src/main/java/org/matsim/pt2matsim/gtfs/lib/RouteImpl.java @@ -31,23 +31,27 @@ public class RouteImpl implements Route { private final String routeId; private final String shortName; private final String longName; + private final Agency agency; private final RouteType routeType; private final GtfsDefinitions.ExtendedRouteType extendedRouteType; + private String description = ""; private final Map trips = new HashMap<>(); - public RouteImpl(String routeId, String shortName, String longName, RouteType routeType) { + public RouteImpl(String routeId, String shortName, String longName, Agency agency, RouteType routeType) { this.routeId = routeId; this.shortName = shortName; this.longName = longName; + this.agency = agency; this.routeType = routeType; this.extendedRouteType = GtfsDefinitions.ExtendedRouteType.getExtendedRouteType(routeType); } - public RouteImpl(String routeId, String shortName, String longName, GtfsDefinitions.ExtendedRouteType extendedRouteType) { + public RouteImpl(String routeId, String shortName, String longName, Agency agency, GtfsDefinitions.ExtendedRouteType extendedRouteType) { this.routeId = routeId; this.shortName = shortName; this.longName = longName; + this.agency = agency; this.routeType = extendedRouteType.routeType; this.extendedRouteType = extendedRouteType; } @@ -77,6 +81,11 @@ public String getShortName() { public String getLongName() { return longName; } + + @Override + public Agency getAgency() { + return agency; + } /** required */ @Override @@ -93,6 +102,16 @@ public Map getTrips() { public GtfsDefinitions.ExtendedRouteType getExtendedRouteType() { return extendedRouteType; } + + @Override + public void setDescription(String description) { + this.description = description; + } + + @Override + public String getDescription() { + return description; + } @Override public boolean equals(Object o) { diff --git a/src/main/java/org/matsim/pt2matsim/run/Gtfs2TransitSchedule.java b/src/main/java/org/matsim/pt2matsim/run/Gtfs2TransitSchedule.java index 3fef6d81..61e8b87a 100644 --- a/src/main/java/org/matsim/pt2matsim/run/Gtfs2TransitSchedule.java +++ b/src/main/java/org/matsim/pt2matsim/run/Gtfs2TransitSchedule.java @@ -22,13 +22,23 @@ import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.core.config.Configurator; import org.apache.logging.log4j.LogManager; +import org.matsim.api.core.v01.Id; import org.matsim.core.utils.geometry.geotools.MGC; +import org.matsim.core.utils.io.IOUtils; +import org.matsim.pt.transitSchedule.api.TransitLine; +import org.matsim.pt.transitSchedule.api.TransitSchedule; +import org.matsim.pt2matsim.gtfs.AdditionalTransitLineInfo; import org.matsim.pt2matsim.gtfs.GtfsConverter; import org.matsim.pt2matsim.gtfs.GtfsFeed; import org.matsim.pt2matsim.gtfs.GtfsFeedImpl; import org.matsim.pt2matsim.tools.ScheduleTools; +import com.opencsv.CSVWriter; + +import java.io.IOException; +import java.io.UncheckedIOException; import java.time.LocalDate; +import java.util.Map; import static org.matsim.pt2matsim.gtfs.GtfsConverter.*; @@ -40,6 +50,8 @@ public final class Gtfs2TransitSchedule { protected static Logger log = LogManager.getLogger(Gtfs2TransitSchedule.class); + + private static final String INFO_OUTPUT_OPTION_SCHEDULE = "schedule"; private Gtfs2TransitSchedule() { } @@ -63,13 +75,21 @@ private Gtfs2TransitSchedule() { * Use 'WGS84' for no transformation (though this may lead to errors with PT mapping).
* [3] output transit schedule file * [4] (optional) output default vehicles file + * [5] (optional) output for additional line info. One of the following: + *

    + *
  • empty -> will not be written
  • + *
  • "schedule" -> will be written as attributable in the schedule
  • + *
  • output file path (.csv) -> will be written as specified csv file
  • + *
* Calls {@link #run}. */ public static void main(final String[] args) { - if(args.length == 5) { - run(args[0], args[1], args[2], args[3], args[4]); + if(args.length == 6) { + run(args[0], args[1], args[2], args[3], args[4], args[5]); + } else if(args.length == 5) { + run(args[0], args[1], args[2], args[3], args[4], null); } else if(args.length == 4) { - run(args[0], args[1], args[2], args[3], null); + run(args[0], args[1], args[2], args[3], null, null); } else { throw new IllegalArgumentException("Wrong number of input arguments."); } @@ -92,8 +112,14 @@ public static void main(final String[] args) { * @param outputCoordinateSystem the output coordinate system. Use WGS84 for no transformation. * @param scheduleFile output transit schedule file * @param vehicleFile output default vehicles file (optional) + * @param additionalLineInfoFile output for additional line info (optional). One of the following: + *
    + *
  • empty -> will not be written
  • + *
  • "schedule" -> will be written as attributable in the schedule
  • + *
  • output file path (.csv) -> will be written as specified csv file
  • + *
*/ - public static void run(String gtfsFolder, String sampleDayParam, String outputCoordinateSystem, String scheduleFile, String vehicleFile) { + public static void run(String gtfsFolder, String sampleDayParam, String outputCoordinateSystem, String scheduleFile, String vehicleFile, String additionalLineInfoFile) { Configurator.setLevel(LogManager.getLogger(MGC.class).getName(), Level.ERROR); // check sample day parameter @@ -109,11 +135,17 @@ public static void run(String gtfsFolder, String sampleDayParam, String outputCo GtfsConverter converter = new GtfsConverter(gtfsFeed); converter.convert(param, outputCoordinateSystem); + if (additionalLineInfoFile != null && additionalLineInfoFile.equals(INFO_OUTPUT_OPTION_SCHEDULE)) { + writeInfoToSchedule(converter.getSchedule(), converter.getAdditionalLineInfo()); + } // write Files ScheduleTools.writeTransitSchedule(converter.getSchedule(), scheduleFile); if(vehicleFile != null) { ScheduleTools.writeVehicles(converter.getVehicles(), vehicleFile); } + if (additionalLineInfoFile != null && !additionalLineInfoFile.equals(INFO_OUTPUT_OPTION_SCHEDULE)) { + writeInfoToFile(additionalLineInfoFile, converter.getAdditionalLineInfo()); + } } /** @@ -129,5 +161,54 @@ private static boolean isValidSampleDayParam(String check) { } return true; } + + private static void writeInfoToSchedule(TransitSchedule schedule, Map, AdditionalTransitLineInfo> infos) { + for (TransitLine line : schedule.getTransitLines().values()) { + AdditionalTransitLineInfo info = infos.get(line.getId()); + if (info == null) { + log.warn("Could not find info for transit line " + line.getId().toString()); + return; + } + line.getAttributes().putAttribute(AdditionalTransitLineInfo.INFO_COLUMN_LONGNAME, info.getLongName()); + line.getAttributes().putAttribute(AdditionalTransitLineInfo.INFO_COLUMN_TYPE, info.getRouteType().name); + line.getAttributes().putAttribute(AdditionalTransitLineInfo.INFO_COLUMN_DESCRIPTION, info.getRouteDescription()); + line.getAttributes().putAttribute(AdditionalTransitLineInfo.INFO_COLUMN_AGENCY_ID, info.getAgencyId()); + line.getAttributes().putAttribute(AdditionalTransitLineInfo.INFO_COLUMN_AGENCY_NAME, info.getAgencyName()); + line.getAttributes().putAttribute(AdditionalTransitLineInfo.INFO_COLUMN_AGENCY_URL, info.getAgencyURL()); + } + } + + private static void writeInfoToFile(String filename, Map, AdditionalTransitLineInfo> infos) { + try(CSVWriter writer = new CSVWriter(IOUtils.getBufferedWriter(filename))) { + writer.writeNext(new String[] { + AdditionalTransitLineInfo.INFO_COLUMN_ID, + AdditionalTransitLineInfo.INFO_COLUMN_SHORTNAME, + AdditionalTransitLineInfo.INFO_COLUMN_LONGNAME, + AdditionalTransitLineInfo.INFO_COLUMN_TYPE, + AdditionalTransitLineInfo.INFO_COLUMN_DESCRIPTION, + AdditionalTransitLineInfo.INFO_COLUMN_AGENCY_ID, + AdditionalTransitLineInfo.INFO_COLUMN_AGENCY_NAME, + AdditionalTransitLineInfo.INFO_COLUMN_AGENCY_URL, + AdditionalTransitLineInfo.INFO_COLUMN_NUM_TRANSIT_ROUTES, + AdditionalTransitLineInfo.INFO_COLUMN_NUM_TOTAL_DEPARTURES + }); + for (AdditionalTransitLineInfo info : infos.values()) { + writer.writeNext(new String[] { + info.getId(), + info.getShortName(), + info.getLongName(), + info.getRouteType().name, + info.getRouteDescription(), + info.getAgencyId(), + info.getAgencyName(), + info.getAgencyURL(), + Integer.toString(info.getNumberOfTransitRoutes()), + Integer.toString(info.getTotalNumberOfDepartures()) + }); + } + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } } \ No newline at end of file diff --git a/src/test/java/org/matsim/pt2matsim/gtfs/GtfsConverterTest.java b/src/test/java/org/matsim/pt2matsim/gtfs/GtfsConverterTest.java index 6a0bd492..8ffceaf1 100644 --- a/src/test/java/org/matsim/pt2matsim/gtfs/GtfsConverterTest.java +++ b/src/test/java/org/matsim/pt2matsim/gtfs/GtfsConverterTest.java @@ -8,6 +8,7 @@ import org.matsim.core.utils.geometry.transformations.TransformationFactory; import org.matsim.pt.transitSchedule.api.*; import org.matsim.pt.utils.TransitScheduleValidator; +import org.matsim.pt2matsim.gtfs.lib.GtfsDefinitions.RouteType; import org.matsim.pt2matsim.tools.ScheduleTools; import org.matsim.pt2matsim.tools.ScheduleToolsTest; @@ -161,6 +162,41 @@ void testTransfers() { Assertions.assertEquals(expectedTransferTimes, actualTransferTime); } + + @Test + void testAdditionalLineInfo() { + Assertions.assertEquals(3, gtfsConverter.getAdditionalLineInfo().size()); + + AdditionalTransitLineInfo lineA = gtfsConverter.getAdditionalLineInfo().get(Id.create("lineA", TransitLine.class)); + Assertions.assertNotNull(lineA); + Assertions.assertEquals("lineA", lineA.getId()); + Assertions.assertEquals("Line A", lineA.getShortName()); + Assertions.assertEquals("Bus Line A", lineA.getLongName()); + Assertions.assertEquals(RouteType.BUS, lineA.getRouteType()); + Assertions.assertEquals("S42", lineA.getAgencyId()); + Assertions.assertEquals("Service 42", lineA.getAgencyName()); + Assertions.assertEquals("htpps://google.com", lineA.getAgencyURL()); + + AdditionalTransitLineInfo lineB = gtfsConverter.getAdditionalLineInfo().get(Id.create("lineB", TransitLine.class)); + Assertions.assertNotNull(lineB); + Assertions.assertEquals("lineB", lineB.getId()); + Assertions.assertEquals("Line B", lineB.getShortName()); + Assertions.assertEquals("Tram Line B", lineB.getLongName()); + Assertions.assertEquals(RouteType.TRAM, lineB.getRouteType()); + Assertions.assertEquals("P2M", lineB.getAgencyId()); + Assertions.assertEquals("pt2matsim", lineB.getAgencyName()); + Assertions.assertEquals("https://github.com/matsim-org/pt2matsim", lineB.getAgencyURL()); + + AdditionalTransitLineInfo lineC = gtfsConverter.getAdditionalLineInfo().get(Id.create("lineC", TransitLine.class)); + Assertions.assertNotNull(lineC); + Assertions.assertEquals("lineC", lineC.getId()); + Assertions.assertEquals("Line C", lineC.getShortName()); + Assertions.assertEquals("Something else", lineC.getLongName()); + Assertions.assertEquals(RouteType.OTHER, lineC.getRouteType()); + Assertions.assertEquals("P2M", lineC.getAgencyId()); + Assertions.assertEquals("pt2matsim", lineC.getAgencyName()); + Assertions.assertEquals("https://github.com/matsim-org/pt2matsim", lineC.getAgencyURL()); + } private String getTransferTimeTestString(MinimalTransferTimes.MinimalTransferTimesIterator iterator) { return iterator.getFromStopId().toString() + "-" + iterator.getToStopId().toString() + "-" + iterator.getSeconds(); diff --git a/src/test/java/org/matsim/pt2matsim/gtfs/GtfsFeedImplTest.java b/src/test/java/org/matsim/pt2matsim/gtfs/GtfsFeedImplTest.java index ddd1781c..0fb790d6 100644 --- a/src/test/java/org/matsim/pt2matsim/gtfs/GtfsFeedImplTest.java +++ b/src/test/java/org/matsim/pt2matsim/gtfs/GtfsFeedImplTest.java @@ -6,6 +6,7 @@ import org.matsim.api.core.v01.Coord; import org.matsim.api.core.v01.Id; import org.matsim.core.utils.geometry.transformations.TransformationFactory; +import org.matsim.pt2matsim.gtfs.lib.GtfsDefinitions; import org.matsim.pt2matsim.gtfs.lib.Service; import org.matsim.pt2matsim.gtfs.lib.Stop; import org.matsim.pt2matsim.gtfs.lib.StopImpl; @@ -52,6 +53,22 @@ void statistics() { Assertions.assertEquals(3, feed.getShapes().size()); Assertions.assertEquals(6, feed.getTrips().size()); Assertions.assertEquals(6, feed.getTransfers().size()); + Assertions.assertEquals(2, feed.getAgencies().size()); + } + + @Test + void agencyAssociation() { + feed.getRoutes().values().forEach(route -> Assertions.assertNotNull(route.getAgency(), "no agency in route " + route.getId())); + Assertions.assertEquals("Service 42", feed.getRoutes().get("lineA").getAgency().getAgencyName()); + Assertions.assertEquals("https://github.com/matsim-org/pt2matsim", feed.getRoutes().get("lineB").getAgency().getAgencyUrl()); + Assertions.assertEquals("Europe/Zurich", feed.getRoutes().get("lineC").getAgency().getAgencyTimeZone()); + } + + @Test + void routeData() { + Assertions.assertEquals("Line A", feed.getRoutes().get("lineA").getShortName()); + Assertions.assertEquals("Bus Line A", feed.getRoutes().get("lineA").getLongName()); + Assertions.assertEquals(GtfsDefinitions.RouteType.BUS, feed.getRoutes().get("lineA").getRouteType()); } @Test diff --git a/src/test/java/org/matsim/pt2matsim/run/Gtfs2TransitScheduleIT.java b/src/test/java/org/matsim/pt2matsim/run/Gtfs2TransitScheduleIT.java new file mode 100644 index 00000000..118d3d1a --- /dev/null +++ b/src/test/java/org/matsim/pt2matsim/run/Gtfs2TransitScheduleIT.java @@ -0,0 +1,59 @@ +package org.matsim.pt2matsim.run; + +import static org.junit.jupiter.api.Assertions.*; + +import java.io.File; +import java.nio.file.Paths; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.matsim.core.utils.geometry.transformations.TransformationFactory; +import org.matsim.core.utils.io.IOUtils; +import org.matsim.core.utils.misc.CRCChecksum; + +class Gtfs2TransitScheduleIT { + + private final String OUTPUTDIR = "test/Gtfs2TransitScheduleIT/output/"; + + @BeforeEach + void setUp() { + if (new File(OUTPUTDIR).exists()) { + IOUtils.deleteDirectoryRecursively(Paths.get(OUTPUTDIR)); + } + new File(OUTPUTDIR).mkdir(); + } + + @Test + void testNoAdditionalInfo() { + Gtfs2TransitSchedule.run("test/gtfs-feed/", "20181005", TransformationFactory.CH1903_LV03_Plus, OUTPUTDIR + "schedule.xml", OUTPUTDIR + "vehicles.xml", null); + Assertions.assertEquals(CRCChecksum.getCRCFromFile("test/Gtfs2TransitScheduleIT/testNoAdditionalInfo/schedule.xml"), CRCChecksum.getCRCFromFile(OUTPUTDIR + "schedule.xml")); + Assertions.assertEquals(CRCChecksum.getCRCFromFile("test/Gtfs2TransitScheduleIT/testNoAdditionalInfo/vehicles.xml"), CRCChecksum.getCRCFromFile(OUTPUTDIR + "vehicles.xml")); + Assertions.assertFalse(new File(OUTPUTDIR + "info.csv").exists()); + } + + @Test + void testInfoInSchedule() { + Gtfs2TransitSchedule.run("test/gtfs-feed/", "20181005", TransformationFactory.CH1903_LV03_Plus, OUTPUTDIR + "schedule.xml", OUTPUTDIR + "vehicles.xml", "schedule"); + Assertions.assertEquals(CRCChecksum.getCRCFromFile("test/Gtfs2TransitScheduleIT/testInfoInSchedule/schedule.xml"), CRCChecksum.getCRCFromFile(OUTPUTDIR + "schedule.xml")); + Assertions.assertEquals(CRCChecksum.getCRCFromFile("test/Gtfs2TransitScheduleIT/testNoAdditionalInfo/vehicles.xml"), CRCChecksum.getCRCFromFile(OUTPUTDIR + "vehicles.xml")); + Assertions.assertFalse(new File(OUTPUTDIR + "info.csv").exists()); + } + + @Test + void testInfoInSeparateFile() { + Gtfs2TransitSchedule.run("test/gtfs-feed/", "20181005", TransformationFactory.CH1903_LV03_Plus, OUTPUTDIR + "schedule.xml", OUTPUTDIR + "vehicles.xml", OUTPUTDIR + "info.csv"); + Assertions.assertEquals(CRCChecksum.getCRCFromFile("test/Gtfs2TransitScheduleIT/testNoAdditionalInfo/schedule.xml"), CRCChecksum.getCRCFromFile(OUTPUTDIR + "schedule.xml")); + Assertions.assertEquals(CRCChecksum.getCRCFromFile("test/Gtfs2TransitScheduleIT/testNoAdditionalInfo/vehicles.xml"), CRCChecksum.getCRCFromFile(OUTPUTDIR + "vehicles.xml")); + Assertions.assertEquals(CRCChecksum.getCRCFromFile("test/Gtfs2TransitScheduleIT/testInfoInSeparateFile/info.csv"), CRCChecksum.getCRCFromFile(OUTPUTDIR + "info.csv")); + } + + @Test + void testNoVehicles() { + Gtfs2TransitSchedule.run("test/gtfs-feed/", "20181005", TransformationFactory.CH1903_LV03_Plus, OUTPUTDIR + "schedule.xml", null, null); + Assertions.assertEquals(CRCChecksum.getCRCFromFile("test/Gtfs2TransitScheduleIT/testNoAdditionalInfo/schedule.xml"), CRCChecksum.getCRCFromFile(OUTPUTDIR + "schedule.xml")); + Assertions.assertFalse(new File(OUTPUTDIR + "vehicles.xml").exists()); + Assertions.assertFalse(new File(OUTPUTDIR + "info.csv").exists()); + } + +} diff --git a/test/Gtfs2TransitScheduleIT/testInfoInSchedule/schedule.xml b/test/Gtfs2TransitScheduleIT/testInfoInSchedule/schedule.xml new file mode 100644 index 00000000..55f65384 --- /dev/null +++ b/test/Gtfs2TransitScheduleIT/testInfoInSchedule/schedule.xml @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + S42 + Service 42 + htpps://google.com + + Bus Line A + bus + + + + bus + + + + + + + + + + + + + + bus + + + + + + + + + + + + + + + + P2M + pt2matsim + https://github.com/matsim-org/pt2matsim + + Tram Line B + tram + + + + tram + + + + + + + + + + + + + + + + + + P2M + pt2matsim + https://github.com/matsim-org/pt2matsim + + Something else + other + + + + \ No newline at end of file diff --git a/test/Gtfs2TransitScheduleIT/testInfoInSeparateFile/info.csv b/test/Gtfs2TransitScheduleIT/testInfoInSeparateFile/info.csv new file mode 100644 index 00000000..f67e59fa --- /dev/null +++ b/test/Gtfs2TransitScheduleIT/testInfoInSeparateFile/info.csv @@ -0,0 +1,4 @@ +"lineId","shortName","longName","type","description","agencyId","agencyName","agencyURL","numTransitRoutes","totalDepartures" +"lineA","Line A","Bus Line A","bus","","S42","Service 42","htpps://google.com","2","6" +"lineB","Line B","Tram Line B","tram","","P2M","pt2matsim","https://github.com/matsim-org/pt2matsim","1","4" +"lineC","Line C","Something else","other","","P2M","pt2matsim","https://github.com/matsim-org/pt2matsim","0","0" diff --git a/test/Gtfs2TransitScheduleIT/testNoAdditionalInfo/schedule.xml b/test/Gtfs2TransitScheduleIT/testNoAdditionalInfo/schedule.xml new file mode 100644 index 00000000..239d61f8 --- /dev/null +++ b/test/Gtfs2TransitScheduleIT/testNoAdditionalInfo/schedule.xml @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + bus + + + + + + + + + + + + + + bus + + + + + + + + + + + + + + + + tram + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/Gtfs2TransitScheduleIT/testNoAdditionalInfo/vehicles.xml b/test/Gtfs2TransitScheduleIT/testNoAdditionalInfo/vehicles.xml new file mode 100644 index 00000000..3bac9c7a --- /dev/null +++ b/test/Gtfs2TransitScheduleIT/testNoAdditionalInfo/vehicles.xml @@ -0,0 +1,74 @@ + + + + + + + 0.5 + serial + 0.5 + + + + + + + + + + + + + + + + + 0.25 + serial + 0.25 + + + + + + + + + + + + + + + + + 0.5 + serial + 0.5 + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/gtfs-feed-cal/agency.txt b/test/gtfs-feed-cal/agency.txt index 3aae7804..670f5dc7 100755 --- a/test/gtfs-feed-cal/agency.txt +++ b/test/gtfs-feed-cal/agency.txt @@ -1,2 +1,3 @@ agency_id,agency_name,agency_url,agency_timezone -P2M,pt2matsim,https://github.com/matsim-org/pt2matsim,Europe/Zurich \ No newline at end of file +P2M,pt2matsim,https://github.com/matsim-org/pt2matsim,Europe/Zurich +S42,Service 42,htpps://google.com,Europe/Berlin \ No newline at end of file diff --git a/test/gtfs-feed-cal/routes.txt b/test/gtfs-feed-cal/routes.txt index be5abf63..10a88a33 100755 --- a/test/gtfs-feed-cal/routes.txt +++ b/test/gtfs-feed-cal/routes.txt @@ -1,3 +1,4 @@ -route_id,route_short_name,route_long_name,route_type -lineA,Line A,Bus Line A,3 -lineB,Line B,Tram Line B,0 \ No newline at end of file +route_id,route_short_name,route_long_name,route_type,agency_id, +lineA,Line A,Bus Line A,3,S42 +lineB,Line B,Tram Line B,0,P2M +lineC,Line C,Something else,907,P2M \ No newline at end of file diff --git a/test/gtfs-feed/agency.txt b/test/gtfs-feed/agency.txt index 3aae7804..670f5dc7 100755 --- a/test/gtfs-feed/agency.txt +++ b/test/gtfs-feed/agency.txt @@ -1,2 +1,3 @@ agency_id,agency_name,agency_url,agency_timezone -P2M,pt2matsim,https://github.com/matsim-org/pt2matsim,Europe/Zurich \ No newline at end of file +P2M,pt2matsim,https://github.com/matsim-org/pt2matsim,Europe/Zurich +S42,Service 42,htpps://google.com,Europe/Berlin \ No newline at end of file diff --git a/test/gtfs-feed/routes.txt b/test/gtfs-feed/routes.txt index 63081991..10a88a33 100755 --- a/test/gtfs-feed/routes.txt +++ b/test/gtfs-feed/routes.txt @@ -1,4 +1,4 @@ -route_id,route_short_name,route_long_name,route_type -lineA,Line A,Bus Line A,3 -lineB,Line B,Tram Line B,0 -lineC,Line C,Something else,907 \ No newline at end of file +route_id,route_short_name,route_long_name,route_type,agency_id, +lineA,Line A,Bus Line A,3,S42 +lineB,Line B,Tram Line B,0,P2M +lineC,Line C,Something else,907,P2M \ No newline at end of file