diff --git a/src/main/java/org/mtransit/parser/DefaultAgencyTools.java b/src/main/java/org/mtransit/parser/DefaultAgencyTools.java index 36438fb..90dd151 100644 --- a/src/main/java/org/mtransit/parser/DefaultAgencyTools.java +++ b/src/main/java/org/mtransit/parser/DefaultAgencyTools.java @@ -1,5 +1,7 @@ package org.mtransit.parser; +import static org.mtransit.parser.Constants.EMPTY; + import androidx.annotation.Discouraged; import androidx.annotation.VisibleForTesting; @@ -67,7 +69,7 @@ public class DefaultAgencyTools implements GAgencyTools { static { - CommonsApp.setup(false); + CommonsApp.setup(false); // isAndroidPlatform } @SuppressWarnings("WeakerAccess") @@ -871,9 +873,10 @@ public void setDirectionHeadsign(@NotNull MRoute mRoute, @NotNull MDirection mDi } final boolean fromStopName = mDirection.getHeadsignType() == MDirection.HEADSIGN_TYPE_STOP_ID; final boolean fromTripHeadSign = mDirection.getHeadsignType() == MDirection.HEADSIGN_TYPE_STRING; + final boolean fromDirection = false; // using trip head-sign here if (directionFinderEnabled(mRoute.getId(), gRoute)) { mDirection.setHeadsignString( - cleanDirectionHeadsign(gRoute, gTrip.getDirectionIdOrDefault(), fromStopName, fromTripHeadSign, gTrip.getTripHeadsignOrDefault()), + cleanDirectionHeadsign(gRoute, gTrip.getDirectionIdOrDefault(), fromStopName, fromTripHeadSign, fromDirection, gTrip.getTripHeadsignOrDefault()), gTrip.getDirectionIdOrDefault() ); return; @@ -883,7 +886,7 @@ public void setDirectionHeadsign(@NotNull MRoute mRoute, @NotNull MDirection mDi } try { mDirection.setHeadsignString( - cleanDirectionHeadsign(gRoute, gTrip.getDirectionIdOrDefault(), fromStopName, fromTripHeadSign, gTrip.getTripHeadsignOrDefault()), + cleanDirectionHeadsign(gRoute, gTrip.getDirectionIdOrDefault(), fromStopName, fromTripHeadSign, fromDirection, gTrip.getTripHeadsignOrDefault()), gTrip.getDirectionIdOrDefault() ); } catch (NumberFormatException nfe) { @@ -997,10 +1000,25 @@ public boolean removeRouteDescFromDirectionHeadsign() { * @param directionId {@link org.mtransit.parser.gtfs.data.GDirectionId} (0 or 1 or missing/generated) */ @Override + public @NotNull String cleanDirectionHeadsign(@Nullable GRoute gRoute, int directionId, boolean fromStopName, boolean fromTripHeadSign, boolean fromDirection, @NotNull String directionHeadSign) { + if (fromDirection) { + //noinspection DiscouragedApi + final String originalRouteId = gRoute == null ? null : gRoute.getOriginalRouteId(); + if (Configs.getRouteConfig().isDirectionHeadsignIgnoreProvidedDirection(getTodayDateInt(), originalRouteId)) { + return EMPTY; + } + } + //noinspection deprecation + return cleanDirectionHeadsign(gRoute, directionId, fromStopName, fromTripHeadSign, directionHeadSign); + } + + @SuppressWarnings("DeprecatedIsStillUsed") + @Deprecated + @Override public @NotNull String cleanDirectionHeadsign(@Nullable GRoute gRoute, int directionId, boolean fromStopName, boolean fromTripHeadSign, @NotNull String directionHeadSign) { if (fromTripHeadSign) { if (Configs.getRouteConfig().getDirectionHeadsignIgnoreTripHeadsign()) { - directionHeadSign = ""; + return EMPTY; } } //noinspection deprecation @@ -1013,16 +1031,15 @@ public boolean removeRouteDescFromDirectionHeadsign() { public @NotNull String cleanDirectionHeadsign(@Nullable GRoute gRoute, int directionId, boolean fromStopName, @NotNull String directionHeadSign) { if (gRoute != null) { if (removeRouteLongNameFromDirectionHeadsign() && directionHeadSign.equals(gRoute.getRouteLongNameOrDefault())) { - directionHeadSign = ""; + return EMPTY; } else if (removeRouteShortNameFromDirectionHeadsign() && directionHeadSign.equals(gRoute.getRouteShortName())) { - directionHeadSign = ""; + return EMPTY; } else if (removeRouteDescFromDirectionHeadsign() && directionHeadSign.equals(gRoute.getRouteDescOrDefault())) { - directionHeadSign = ""; + return EMPTY; } } //noinspection deprecation - directionHeadSign = cleanDirectionHeadsign(directionId, fromStopName, directionHeadSign); - return directionHeadSign; + return cleanDirectionHeadsign(directionId, fromStopName, directionHeadSign); } @SuppressWarnings("DeprecatedIsStillUsed") @@ -1030,9 +1047,10 @@ public boolean removeRouteDescFromDirectionHeadsign() { @NotNull @Override public String cleanDirectionHeadsign(int directionId, boolean fromStopName, @NotNull String directionHeadSign) { - directionHeadSign = Configs.getRouteConfig().cleanDirectionHeadsign(getFirstLanguageNN(), directionHeadSign); //noinspection deprecation - return cleanDirectionHeadsign(fromStopName, directionHeadSign); + return cleanDirectionHeadsign(fromStopName, + Configs.getRouteConfig().cleanDirectionHeadsign(getFirstLanguageNN(), directionHeadSign) + ); } @SuppressWarnings("DeprecatedIsStillUsed") diff --git a/src/main/java/org/mtransit/parser/FileUtils.java b/src/main/java/org/mtransit/parser/FileUtils.java index 1fd8726..dc4efdc 100644 --- a/src/main/java/org/mtransit/parser/FileUtils.java +++ b/src/main/java/org/mtransit/parser/FileUtils.java @@ -6,6 +6,7 @@ import java.io.File; import java.io.IOException; import java.nio.file.Files; +import java.util.List; @SuppressWarnings("unused") public final class FileUtils { @@ -55,7 +56,7 @@ public static String sizeToDiplayString(@Nullable Long size) { } @Nullable - public static File findFileCaseInsensitive(@NotNull String directoryPath, @NotNull String fileNameToFind) { + public static File findFileCaseInsensitive(@NotNull String directoryPath, @NotNull List fileNamesToFind) { final File directory = new File(directoryPath); // Check if the provided path is a directory and exists if (!directory.isDirectory()) { @@ -64,9 +65,9 @@ public static File findFileCaseInsensitive(@NotNull String directoryPath, @NotNu } final File[] files = directory.listFiles(); // Get all files and subdirectories if (files != null) { - for (File file : files) { - if (file.isFile()) { // Check if it's a file (not a directory) - if (file.getName().equalsIgnoreCase(fileNameToFind)) { + for (String fileNameToFind : fileNamesToFind) { + for (File file : files) { + if (file.isFile() && file.getName().equalsIgnoreCase(fileNameToFind)) { return file; // Found the file (case-insensitive) } } diff --git a/src/main/java/org/mtransit/parser/config/gtfs/data/RouteConfig.kt b/src/main/java/org/mtransit/parser/config/gtfs/data/RouteConfig.kt index 3b5b2eb..edc2800 100644 --- a/src/main/java/org/mtransit/parser/config/gtfs/data/RouteConfig.kt +++ b/src/main/java/org/mtransit/parser/config/gtfs/data/RouteConfig.kt @@ -92,6 +92,10 @@ data class RouteConfig( val directionHeadsignRemoveRouteShortName: Boolean = false, // OPT-IN feature @SerialName("direction_headsign_remove_route_desc") val directionHeadsignRemoveRouteDesc: Boolean = false, // OPT-IN feature + @SerialName("direction_headsign_ignore_provided_direction") + val directionHeadsignIgnoreProvidedDirection: Boolean = false, // OPT-IN feature + @SerialName("direction_headsign_ignore_provided_direction_until") + val directionHeadsignIgnoreProvidedDirectionUntil: Map = emptyMap(), // OPT-IN feature @SerialName("direction_headsign_ignore_trip_headsign") val directionHeadsignIgnoreTripHeadsign: Boolean = false, // OPT-IN feature @SerialName("direction_splitter_enabled") @@ -453,6 +457,10 @@ data class RouteConfig( return string } + @JvmOverloads + fun isDirectionHeadsignIgnoreProvidedDirection(todayDate: Int, gOriginalRouteId: String? = null) = + directionHeadsignIgnoreProvidedDirection || isAllowedUntil(directionHeadsignIgnoreProvidedDirectionUntil[gOriginalRouteId], todayDate) + @JvmOverloads fun isDirectionSplitterEnabled(todayDate: Int, routeId: Long? = null) = directionSplitterEnabled && !isAllowedUntil(directionSplitterDisabledUntil[routeId], todayDate) @@ -470,6 +478,6 @@ data class RouteConfig( fun allowNonDescriptiveHeadSigns(todayDate: Int, mRouteId: Long) = allowNonDescriptiveHeadSigns[mRouteId] == true || isAllowedUntil(allowNonDescriptiveHeadSignsUntil[mRouteId], todayDate) - private fun isAllowedUntil(dateStr: String?, todayDate: Int) = + fun isAllowedUntil(dateStr: String?, todayDate: Int) = dateStr?.toIntOrNull()?.let { todayDate <= it } ?: false } diff --git a/src/main/java/org/mtransit/parser/gtfs/GAgencyTools.java b/src/main/java/org/mtransit/parser/gtfs/GAgencyTools.java index a45fb2d..d24a5c9 100644 --- a/src/main/java/org/mtransit/parser/gtfs/GAgencyTools.java +++ b/src/main/java/org/mtransit/parser/gtfs/GAgencyTools.java @@ -214,6 +214,10 @@ public interface GAgencyTools { boolean removeRouteDescFromDirectionHeadsign(); + @NotNull + String cleanDirectionHeadsign(@Nullable GRoute gRoute, int directionId, boolean fromStopName, boolean fromTripHeadSign, boolean fromDirection, @NotNull String directionHeadSign); + + @Deprecated @NotNull String cleanDirectionHeadsign(@Nullable GRoute gRoute, int directionId, boolean fromStopName, boolean fromTripHeadSign, @NotNull String directionHeadSign); diff --git a/src/main/java/org/mtransit/parser/gtfs/GReader.java b/src/main/java/org/mtransit/parser/gtfs/GReader.java index 6907058..a085c0e 100644 --- a/src/main/java/org/mtransit/parser/gtfs/GReader.java +++ b/src/main/java/org/mtransit/parser/gtfs/GReader.java @@ -36,6 +36,7 @@ import java.sql.PreparedStatement; import java.text.DateFormat; import java.util.Arrays; +import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.HashSet; @@ -125,7 +126,7 @@ public static GSpec readGtfsZipFile( } // DIRECTIONS (ext) (after route) if (!calendarsOnly && !routeTripCalendarsOnly) { - readFile(gtfsDir, GDirection.FILENAME, false, line -> + readFiles(gtfsDir, GDirection.getFILENAMES(), false, line -> processDirection(agencyTools, gSpec, line, skipDataCleanup) ); } @@ -183,9 +184,24 @@ private static boolean readFile( boolean fileRequired, @NotNull LineProcessor lineProcessor ) { - return readFile(gtfsDir, fileName, fileRequired, lineProcessor, null); + return readFiles( + gtfsDir, + Collections.singletonList(fileName), + fileRequired, + lineProcessor + ); } + private static boolean readFiles( + @NotNull String gtfsDir, + @NotNull List fileNames, + boolean fileRequired, + @NotNull LineProcessor lineProcessor + ) { + return readFiles(gtfsDir, fileNames, fileRequired, lineProcessor, null); + } + + @SuppressWarnings({"UnusedReturnValue", "SameParameterValue"}) private static boolean readFile( @NotNull String gtfsDir, @NotNull String fileName, @@ -193,12 +209,28 @@ private static boolean readFile( @NotNull LineProcessor lineProcessor, @Nullable OnColumnNamesFound onColumnNamesFoundCallback ) { - final File gtfsFile = FileUtils.findFileCaseInsensitive(gtfsDir, fileName); + return readFiles( + gtfsDir, + Collections.singletonList(fileName), + fileRequired, + lineProcessor, + onColumnNamesFoundCallback + ); + } + + private static boolean readFiles( + @NotNull String gtfsDir, + @NotNull List fileNames, + boolean fileRequired, + @NotNull LineProcessor lineProcessor, + @Nullable OnColumnNamesFound onColumnNamesFoundCallback + ) { + final File gtfsFile = FileUtils.findFileCaseInsensitive(gtfsDir, fileNames); if (gtfsFile == null || !gtfsFile.exists()) { if (fileRequired) { - throw new MTLog.Fatal("'%s' file does not exist!", gtfsFile); + throw new MTLog.Fatal("'%s' file does not exist!", fileNames); } else { - MTLog.log("Reading file '%s'... SKIP (non-existing).", fileName); + MTLog.log("Reading file(s) '%s'... SKIP (non-existing).", fileNames); return false; } } @@ -236,58 +268,55 @@ private static void readCsv( String line; String[] columnNames; line = reader.readLine(); - if (line == null || line.isEmpty()) { - return; - } + if (line == null || line.isEmpty()) return; if (line.charAt(0) == '\uFEFF') { // remove 1st empty char MTLog.log("Reading file '%s'... > remove 1st empty car", filename); line = String.copyValueOf(line.toCharArray(), 1, line.length() - 1); } - CSVRecord recordColumns = CSVParser.parse(line, CSV_FORMAT).getRecords().get(0); - columnNames = new String[recordColumns.size()]; - for (int i = 0; i < recordColumns.size(); i++) { - columnNames[i] = recordColumns.get(i); + CSVRecord lineRecordColumns = CSVParser.parse(line, CSV_FORMAT).getRecords().get(0); + columnNames = new String[lineRecordColumns.size()]; + for (int i = 0; i < lineRecordColumns.size(); i++) { + columnNames[i] = lineRecordColumns.get(i); } if (onColumnNamesFoundCallback != null) { onColumnNamesFoundCallback.processColumnNames(Arrays.asList(columnNames)); } - if (columnNames.length == 0) { - return; - } - List records; - HashMap map = new HashMap<>(); - String[] lineColumns = new String[columnNames.length]; - int recordColumnsSize; + if (columnNames.length == 0) return; + List lineRecords; + final HashMap map = new HashMap<>(); int l = 0; boolean withQuotes; + int warningCount = 0; while ((line = reader.readLine()) != null) { try { try { - records = CSVParser.parse(line, CSV_FORMAT).getRecords(); + lineRecords = CSVParser.parse(line, CSV_FORMAT).getRecords(); withQuotes = true; } catch (Exception e) { - records = CSVParser.parse(line, CSV_FORMAT_NO_QUOTE).getRecords(); + lineRecords = CSVParser.parse(line, CSV_FORMAT_NO_QUOTE).getRecords(); withQuotes = false; } - if (records.isEmpty()) { - continue; // empty line - } - recordColumns = records.get(0); - recordColumnsSize = recordColumns.size(); - if (columnNames.length != recordColumnsSize - && columnNames.length != (recordColumnsSize + 1)) { - MTLog.log("File '%s' line invalid: %s columns instead of %s: %s", filename, recordColumnsSize, columnNames.length, line); - continue; + if (lineRecords.isEmpty()) continue; // empty line + lineRecordColumns = lineRecords.get(0); + // recordColumnsSize = lineRecordColumns.size(); + if (lineRecordColumns.size() > columnNames.length) { + if (warningCount < 10) { + MTLog.log("File '%s' line contains MORE columns (%s:%s) than expected (%s:%s)!", filename, lineRecordColumns.size(), line, columnNames.length, Arrays.asList(columnNames)); + warningCount++; + } + } else if (lineRecordColumns.size() < columnNames.length) { + if (warningCount < 10) { + MTLog.log("File '%s' line contains LESS columns (%s:%s) than expected (%s:%s)!", filename, lineRecordColumns.size(), line, columnNames.length, Arrays.asList(columnNames)); + warningCount++; + } } - for (int i = 0; i < lineColumns.length; i++) { - final String lineColumn = i >= recordColumns.size() ? EMPTY : recordColumns.get(i); - lineColumns[i] = withQuotes ? + map.clear(); + for (int i = 0; i < columnNames.length; i++) { + String lineColumn = i < lineRecordColumns.size() ? lineRecordColumns.get(i) : EMPTY; + lineColumn = withQuotes ? lineColumn : QUOTE_.matcher(lineColumn).replaceAll(EMPTY); - } - map.clear(); - for (int ci = 0; ci < recordColumnsSize; ++ci) { - map.put(columnNames[ci], lineColumns[ci]); + map.put(columnNames[i], lineColumn); } if (lineProcessor != null) { lineProcessor.processLine(map); @@ -443,7 +472,7 @@ private static void processDirection(GAgencyTools agencyTools, GSpec gSpec, Hash logExclude("Exclude direction (!route): %s | %s.", gRoute == null ? null : gRoute.getRouteId(), gDirection.getDirectionId()); return; } - final GDirection existingDirection = gSpec.getDirection(gDirection.getRouteIdInt(), gDirection.getDirectionId()); + final GDirection existingDirection = gSpec.getRouteDirection(gDirection.getRouteIdInt(), gDirection.getDirectionId().getId()); if (existingDirection != null) { //noinspection DiscouragedApi MTLog.logDebug("Duplicate direction ID for route ID! (new:%s|old:%s)", gDirection.getDirectionId(), existingDirection.getDirectionId()); diff --git a/src/main/java/org/mtransit/parser/gtfs/data/GDirection.kt b/src/main/java/org/mtransit/parser/gtfs/data/GDirection.kt index c34dce8..934f9ff 100644 --- a/src/main/java/org/mtransit/parser/gtfs/data/GDirection.kt +++ b/src/main/java/org/mtransit/parser/gtfs/data/GDirection.kt @@ -1,7 +1,6 @@ package org.mtransit.parser.gtfs.data import androidx.annotation.Discouraged -import org.mtransit.commons.GTFSCommons import org.mtransit.commons.gtfs.data.Direction import org.mtransit.commons.gtfs.data.DirectionType import org.mtransit.parser.MTLog @@ -32,6 +31,10 @@ data class GDirection( private val _routeId: String get() = GIDs.getString(routeIdInt) + val destinationOrDirection: String? = + destination + ?: directionType.takeIf { it != GDirectionType.UNKNOWN }?.value + @Suppress("unused") fun toStringPlus(): String { return toString() + @@ -46,14 +49,23 @@ data class GDirection( ) companion object { - const val FILENAME = "directions.txt" + + @JvmStatic + val FILENAMES = listOf( + "directions.txt", + "route_directions.txt" + ) private const val ROUTE_ID = GRoute.ROUTE_ID private const val DIRECTION_ID = "direction_id" - private const val DIRECTION = "direction" + private const val DIRECTION = "direction" // can be parsed to enum or regular string (TODO enum useless?) + + private const val DIRECTION_DESTINATION = "direction_destination" private const val DESTINATION = "destination" - private const val DESTINATION_DIRECTION_NAME = "direction_name" + private const val DIRECTION_NAME = "direction_name" + private const val ROUTE_DIRECTION_NAME = "route_direction_name" + private const val DIRECTION_LEGACY = "direction_legacy" // #STM // TODO other alternatives @JvmOverloads @@ -64,7 +76,12 @@ data class GDirection( ?: throw MTLog.Fatal("Invalid GDirection from $line!"), directionIdInt = line[DIRECTION_ID]?.toIntOrNull() ?: throw MTLog.Fatal("Invalid GDirection from $line!"), directionTypeValue = line[DIRECTION]?.trim(), - destination = line[DESTINATION]?.trim() ?: line[DESTINATION_DIRECTION_NAME]?.trim(), + destination = line[DESTINATION]?.trim()?.takeIf { it.isNotBlank() } + ?: line[DIRECTION_NAME]?.trim()?.takeIf { it.isNotBlank() } + ?: line[ROUTE_DIRECTION_NAME]?.trim()?.takeIf { it.isNotBlank() } + ?: line[DIRECTION_DESTINATION]?.trim()?.takeIf { it.isNotBlank() } + ?: line[DIRECTION_LEGACY]?.trim()?.takeIf { it.isNotBlank() } + ?: line[DIRECTION]?.trim()?.takeIf { it.isNotBlank() }, ) @JvmStatic diff --git a/src/main/java/org/mtransit/parser/gtfs/data/GSpec.java b/src/main/java/org/mtransit/parser/gtfs/data/GSpec.java index 067d1c4..2f617b9 100644 --- a/src/main/java/org/mtransit/parser/gtfs/data/GSpec.java +++ b/src/main/java/org/mtransit/parser/gtfs/data/GSpec.java @@ -100,7 +100,7 @@ public GAgency getSingleAgency() { } @Nullable - public GAgency getAgency(@NotNull Integer agencyIdInt) { + public GAgency getAgency(int agencyIdInt) { if (USE_DB_ONLY) { return GAgency.from(GTFSDataBase.selectAgency(GIDs.getString(agencyIdInt))); } @@ -259,7 +259,7 @@ public List getRoutes(@Nullable Long optMRouteId) { } @Nullable - public GRoute getRoute(@NotNull Integer routeIdInt) { + public GRoute getRoute(int routeIdInt) { if (USE_DB_ONLY) { return GRoute.from(GTFSDataBase.selectRoute(GIDs.getString(routeIdInt))); } @@ -311,7 +311,7 @@ public GStop getStop(@NotNull String gStopId) { } @Nullable - public GStop getStop(@NotNull Integer gStopIdInt) { + public GStop getStop(int gStopIdInt) { if (USE_DB_ONLY) { return GStop.from(GTFSDataBase.selectStop(GIDs.getString(gStopIdInt))); } @@ -389,7 +389,7 @@ public void updateTripDirectionId(int directionId, @Nullable Collection } @Nullable - public GTrip getTrip(@NotNull Integer tripIdInt) { + public GTrip getTrip(int tripIdInt) { if (USE_DB_ONLY) { return GTrip.from(GTFSDataBase.selectTrip(GIDs.getString(tripIdInt))); } @@ -410,7 +410,7 @@ private Integer getTripRouteId(Integer tripIdInt) { return this.tripIdIntRouteIdInt.get(tripIdInt); } - public boolean hasTripsOriginalRouteId(@NotNull Integer routeIdInt) { + public boolean hasTripsOriginalRouteId(int routeIdInt) { return this.tripOriginalRouteIdInt.contains(routeIdInt); } @@ -424,7 +424,7 @@ public List getTrips(@Nullable Integer optRouteId) { } @NotNull - public List getRouteTrips(@NotNull Integer routeIdInt) { + public List getRouteTrips(int routeIdInt) { if (USE_DB_ONLY) { return GTrip.from(GTFSDataBase.selectTrips(null, Collections.singleton(GIDs.getString(routeIdInt)))); } @@ -437,7 +437,7 @@ public void addDirection(@NotNull GDirection gDirection) { } @Nullable - public List getDirection(@NotNull Integer routeIdInt) { + public List getRouteDirections(int routeIdInt) { if (USE_DB_ONLY) { return GDirection.from(GTFSDataBase.selectDirections(GIDs.getString(routeIdInt))); } @@ -445,11 +445,11 @@ public List getDirection(@NotNull Integer routeIdInt) { } @Nullable - public GDirection getDirection(@NotNull Integer routeIdInt, @NotNull GDirectionId directionId) { - List directions = getDirection(routeIdInt); + public GDirection getRouteDirection(int routeIdInt, int directionId) { + final List directions = getRouteDirections(routeIdInt); if (directions != null) { for (GDirection direction : directions) { - if (direction.getDirectionId() == directionId) { + if (direction.getDirectionId().getId() == directionId) { return direction; } } @@ -466,14 +466,13 @@ public void remove(long mRouteId) { } @NotNull - public List getStopTimes(@NotNull Long mRouteId, - @NotNull Integer gTripIdInt) { + public List getStopTimes(long mRouteId, int gTripIdInt) { return getStopTimes(mRouteId, gTripIdInt, null, null); } @NotNull - public List getStopTimes(@NotNull Long mRouteId, - @NotNull Integer gTripIdInt, + public List getStopTimes(long mRouteId, + int gTripIdInt, @SuppressWarnings("unused") @Nullable String optGStopId, @SuppressWarnings("unused") @Nullable Integer optGStopSequence) { GenerateMObjectsTask routeGenerator = this.routeGenerators.get(mRouteId); @@ -491,7 +490,7 @@ public void addStopTime(@NotNull GStopTime gStopTime, @NotNull PreparedStatement GTFSDataBase.insertStopTime(gStopTime.to(), insertStopTimePrepared); } - private int removeTripStopTimes(@NotNull Integer gTripId) { + private int removeTripStopTimes(int gTripId) { int r = 0; r += GTFSDataBase.deleteStopTimes(GIDs.getString(gTripId)); return r; @@ -739,9 +738,6 @@ public void generateStopTimesFromFrequencies(@SuppressWarnings("unused") @NotNul for (GStopTime gStopTime : tripStopTimes) { stopTimeCal.add(Calendar.SECOND, gStopTimeIncInSec.get(gStopTime.getUID())); final int newDepartureTime = getNewDepartureTime(stopTimeCal); - final GPickupType pickupType = gStopTime.getPickupType(); - final GDropOffType dropOffType = gStopTime.getDropOffType(); - final GTimePoint timePoint = gStopTime.getTimePoint(); final GStopTime newGStopTime = new GStopTime( newGeneratedTripIdInt, newDepartureTime, @@ -749,9 +745,9 @@ public void generateStopTimesFromFrequencies(@SuppressWarnings("unused") @NotNul gStopTime.getStopIdInt(), gStopTime.getStopSequence(), gStopTime.getStopHeadsign(), - pickupType, - dropOffType, - timePoint + gStopTime.getPickupType(), + gStopTime.getDropOffType(), + gStopTime.getTimePoint() ); newGStopTimes.add(newGStopTime); } @@ -832,7 +828,7 @@ public Set getRouteIds() { } @NotNull - public GSpec getRouteGTFS(@SuppressWarnings("unused") @NotNull Long mRouteId) { + public GSpec getRouteGTFS(@SuppressWarnings("unused") long mRouteId) { return this; } @@ -972,7 +968,7 @@ public void clearRawData() { } @SuppressWarnings("unused") - public void cleanupRouteGTFS(@NotNull Long mRouteId) { + public void cleanupRouteGTFS(long mRouteId) { MTLog.log("%d: Removing route data...", mRouteId); int r = 0; try { @@ -989,7 +985,7 @@ public void cleanupRouteGTFS(@NotNull Long mRouteId) { MTLog.log("%d: Removing route data...DONE (%d removed objects)", mRouteId, r); } - public boolean hasRouteTrips(@NotNull Long mRouteId) { + public boolean hasRouteTrips(long mRouteId) { return this.mRouteWithTripIds.contains(mRouteId); } diff --git a/src/main/java/org/mtransit/parser/mt/GenerateMObjectsTask.java b/src/main/java/org/mtransit/parser/mt/GenerateMObjectsTask.java index 8047b8b..112dc1f 100644 --- a/src/main/java/org/mtransit/parser/mt/GenerateMObjectsTask.java +++ b/src/main/java/org/mtransit/parser/mt/GenerateMObjectsTask.java @@ -420,9 +420,13 @@ private void fixRouteLongName(HashMap mRoutes, HashMap 0) sb.append(" <> "); - sb.append(mDirection.getHeadsignValue()); + if (mDirection.getHeadsignType() == MDirection.HEADSIGN_TYPE_STRING + && !mDirection.getHeadsignValue().isBlank()) { + final String cleanDirectionHeadsign = agencyTools.cleanRouteLongName(mDirection.getHeadsignValue()); + if (!cleanDirectionHeadsign.isBlank()) { + if (sb.length() > 0) sb.append(" <> "); + sb.append(cleanDirectionHeadsign); + } } } } diff --git a/src/main/java/org/mtransit/parser/mt/MDirectionHeadSignFinder.kt b/src/main/java/org/mtransit/parser/mt/MDirectionHeadSignFinder.kt index d4bfdc0..db1a144 100644 --- a/src/main/java/org/mtransit/parser/mt/MDirectionHeadSignFinder.kt +++ b/src/main/java/org/mtransit/parser/mt/MDirectionHeadSignFinder.kt @@ -1,5 +1,6 @@ package org.mtransit.parser.mt +import org.jetbrains.annotations.VisibleForTesting import org.mtransit.commons.StringUtils.EMPTY import org.mtransit.commons.containsExactList import org.mtransit.commons.indexOf @@ -40,6 +41,29 @@ object MDirectionHeadSignFinder { val directionRouteIdInts = mutableMapOf>() GDirectionId.entries.forEach { gDirectionId -> val directionId = gDirectionId.id + val directionTrips = gRouteTrips.filter { gTrip -> gTrip.directionIdOrDefault == directionId } + if (directionTrips.isEmpty()) { + MTLog.logDebug("$routeId: $directionId: no trips -> no head-sign.") + return@forEach + } + val directionRouteIdInt = directionTrips.firstOrNull()?.routeIdInt + ?.takeIf { firstTripRouteIdInt -> + directionTrips.all { it.routeIdInt == firstTripRouteIdInt } + } + MTLog.logDebug("$routeId: $directionId: direction original route ID(s): ${GIDs.toStringPlus(directionRouteIdInt)} (trips: ${directionTrips.size})") + directionRouteIdInt?.let { routeIdInt -> + val routeDirection = routeGTFS.getRouteDirection(routeIdInt, directionId) + ?: return@let + routeDirection.destinationOrDirection + ?.let { agencyTools.cleanDirectionHeadsign(routeGTFS.getRoute(routeIdInt), directionId, false, false, true, it) } + ?.takeIf { it.isNotBlank() } + ?.let { + MTLog.logDebug("$routeId: $directionId: direction > use '$it'.") + directionHeadSigns[directionId] = it + return@forEach + } + MTLog.logDebug("$routeId: $directionId: ignore direction ${routeDirection.toStringPlus()}'.") + } findDirectionHeadSign( routeId, gRouteTrips, @@ -64,9 +88,9 @@ object MDirectionHeadSignFinder { val firstAndLastTime = directionAmPm[directionId] ?: continue val gRoute = directionRouteIdInts[directionId].takeIf { it?.size == 1 }?.getOrNull(0)?.let { routeGTFS.getRoute(it) } if (GTime.areAM(firstAndLastTime)) { - amPmDirectionHeadSigns[directionId] = agencyTools.cleanDirectionHeadsign(gRoute, directionId, false, false, "AM") + amPmDirectionHeadSigns[directionId] = agencyTools.cleanDirectionHeadsign(gRoute, directionId, false, false, false, "AM") } else if (GTime.arePM(firstAndLastTime)) { - amPmDirectionHeadSigns[directionId] = agencyTools.cleanDirectionHeadsign(gRoute, directionId, false, false, "PM") + amPmDirectionHeadSigns[directionId] = agencyTools.cleanDirectionHeadsign(gRoute, directionId, false, false, false, "PM") } } if (amPmDirectionHeadSigns.size == 2 // all AM/PM or nothing @@ -85,7 +109,8 @@ object MDirectionHeadSignFinder { ?: continue val rln = gRoute.routeLongNameOrDefault.takeIf { it.isNotBlank() } ?: continue - routeDirectionHeadSigns[directionId] = agencyTools.cleanDirectionHeadsign(gRoute, directionId, false, false, agencyTools.cleanRouteLongName(rln)) + routeDirectionHeadSigns[directionId] = + agencyTools.cleanDirectionHeadsign(gRoute, directionId, false, false, false, agencyTools.cleanRouteLongName(rln)) } if (routeDirectionHeadSigns.size == directionHeadSigns.size // all route long name or nothing && agencyTools.directionHeadSignsDescriptive(routeDirectionHeadSigns) @@ -103,7 +128,7 @@ object MDirectionHeadSignFinder { val stop = routeGTFS.getStop(stopIdInt) ?: continue val gRoute = directionRouteIdInts[directionId].takeIf { it?.size == 1 }?.getOrNull(0)?.let { routeGTFS.getRoute(it) } lastStopDirectionHeadSigns[directionId] = - agencyTools.cleanDirectionHeadsign(gRoute, directionId, true, false, agencyTools.cleanStopName(stop.stopName)) + agencyTools.cleanDirectionHeadsign(gRoute, directionId, true, false, false, agencyTools.cleanStopName(stop.stopName)) MTLog.logDebug("$routeId: $directionId Stop '${stop.toStringPlus(false)}' > '${lastStopDirectionHeadSigns[directionId]}'.") } val allDirectionHeadSignsEmpty: Boolean = directionHeadSigns @@ -137,7 +162,7 @@ object MDirectionHeadSignFinder { val gRoute = directionRouteIdInts[directionId].takeIf { it?.size == 1 }?.getOrNull(0)?.let { routeGTFS.getRoute(it) } var cleanDirectionHeadsign: String? = null for (stopTimeHeadSign in stopTimeHeadSigns) { - cleanDirectionHeadsign = agencyTools.cleanDirectionHeadsign(gRoute, directionId, false, false, stopTimeHeadSign) // already cleaned + cleanDirectionHeadsign = agencyTools.cleanDirectionHeadsign(gRoute, directionId, false, false, false, stopTimeHeadSign) // already cleaned if (cleanDirectionHeadsign.isNotBlank()) { break } @@ -174,7 +199,8 @@ object MDirectionHeadSignFinder { return directionHeadSigns } - fun findDirectionHeadSign( + @VisibleForTesting + internal fun findDirectionHeadSign( routeId: Long, gRouteTrips: List, routeGTFS: GSpec, @@ -186,9 +212,8 @@ object MDirectionHeadSignFinder { gTrip.directionIdOrDefault == directionId }.map { gTrip -> val routeIdInt = gTrip.routeIdInt - val gRoute = routeGTFS.getRoute(routeIdInt) val headSign = gTrip.tripHeadsign - ?.let { agencyTools.cleanDirectionHeadsign(gRoute, directionId, false, true, it) } ?: EMPTY + ?.let { agencyTools.cleanDirectionHeadsign(routeGTFS.getRoute(routeIdInt), directionId, false, true, false, it) } ?: EMPTY val stopTimes = routeGTFS.getStopTimes(routeId, gTrip.tripIdInt, null, null) Triple(routeIdInt, headSign, stopTimes) }.filterNot { (_, _, stopTimes) -> diff --git a/src/test/java/org/mtransit/parser/mt/MDirectionHeadSignFinderTest.kt b/src/test/java/org/mtransit/parser/mt/MDirectionHeadSignFinderTest.kt index ac7ae4b..32555a4 100644 --- a/src/test/java/org/mtransit/parser/mt/MDirectionHeadSignFinderTest.kt +++ b/src/test/java/org/mtransit/parser/mt/MDirectionHeadSignFinderTest.kt @@ -42,9 +42,9 @@ class MDirectionHeadSignFinderTest { @Suppress("DEPRECATION") @Before fun setUp() { - whenever(agencyTools.cleanDirectionHeadsign(anyOrNull(), anyInt(), anyBoolean(), anyBoolean(), anyString())) + whenever(agencyTools.cleanDirectionHeadsign(anyOrNull(), anyInt(), anyBoolean(), anyBoolean(), anyBoolean(), anyString())) .then { - it.arguments[4] + it.arguments[5] } whenever(agencyTools.cleanStopHeadSign(any(), any(), any(), anyString())) .then { @@ -821,6 +821,7 @@ class MDirectionHeadSignFinderTest { assertEquals(false, result?.firstAndLast?.let { GTime.arePM(it) } == true) } + @Suppress("SameParameterValue") private fun makeGTrip( routeId: String, serviceId: String, @@ -843,6 +844,7 @@ class MDirectionHeadSignFinderTest { bikesAllowed = null, ) + @Suppress("SameParameterValue") private fun makeGTrip( routeId: Int, serviceId: Int,