diff --git a/src/main/java/org/mtransit/parser/DefaultAgencyTools.java b/src/main/java/org/mtransit/parser/DefaultAgencyTools.java index c3eb3304..4ef9ef1a 100644 --- a/src/main/java/org/mtransit/parser/DefaultAgencyTools.java +++ b/src/main/java/org/mtransit/parser/DefaultAgencyTools.java @@ -591,11 +591,36 @@ public String cleanRouteLongName(@NotNull String routeLongName) { } routeLongName = Configs.getRouteConfig().cleanRouteLongName(routeLongName); if (defaultStringsCleanerEnabled()) { - return StringsCleaner.cleanRouteLongName(routeLongName, getSupportedLanguages()); + return StringsCleaner.cleanRouteLongName(routeLongName, getSupportedLanguages(), lowerUCStrings(), lowerUCWords(), getIgnoreUCWords()); } return org.mtransit.commons.CleanUtils.cleanLabel(getFirstLanguageNN(), routeLongName); } + @Override + public boolean lowerUCWords() { + if (Configs.getAgencyConfig() != null) { + return Configs.getAgencyConfig().getToLowerUpperCaseWords(); + } + return false; // OPT-IN feature + } + + @Override + public boolean lowerUCStrings() { + if (Configs.getAgencyConfig() != null) { + return Configs.getAgencyConfig().getToLowerUpperCaseStrings(); + } + return false; // OPT-IN feature + } + + + @Override + public @NotNull String[] getIgnoreUCWords() { + if (Configs.getAgencyConfig() != null) { + return Configs.getAgencyConfig().getIgnoreUpperCaseWords().toArray(new String[0]); + } + return new String[0]; + } + @Deprecated @Override public boolean allowGTFSIdOverride() { @@ -709,7 +734,7 @@ public void setDirectionHeadsign(@NotNull MRoute mRoute, @NotNull MDirection mDi public String cleanTripHeadsign(@NotNull String tripHeadsign) { tripHeadsign = Configs.getRouteConfig().cleanTripHeadsign(tripHeadsign); if (defaultStringsCleanerEnabled()) { - return StringsCleaner.cleanTripHeadsign(tripHeadsign, getSupportedLanguages(), Configs.getRouteConfig().getTripHeadsignRemoveVia()); + return StringsCleaner.cleanTripHeadsign(tripHeadsign, getSupportedLanguages(), lowerUCStrings(), lowerUCWords(), getIgnoreUCWords(), Configs.getRouteConfig().getTripHeadsignRemoveVia()); } return tripHeadsign; } @@ -1058,6 +1083,9 @@ public boolean excludeTripNullable(@Nullable GTrip gTrip) { @Override public boolean excludeTrip(@NotNull GTrip gTrip) { + if (Configs.getRouteConfig().excludeTrip(gTrip)) { + return EXCLUDE; + } if (this.serviceIdInts != null) { return excludeUselessTripInt(gTrip, this.serviceIdInts); } @@ -1084,7 +1112,7 @@ public boolean excludeCalendar(@NotNull GCalendar gCalendar) { @Override public String cleanStopName(@NotNull String gStopName) { if (defaultStringsCleanerEnabled()) { - return StringsCleaner.cleanStopName(gStopName, getSupportedLanguages()); + return StringsCleaner.cleanStopName(gStopName, getSupportedLanguages(), lowerUCStrings(), lowerUCWords(), getIgnoreUCWords()); } return org.mtransit.commons.CleanUtils.cleanLabel(getFirstLanguageNN(), gStopName); } @@ -1300,7 +1328,7 @@ private static Pair extractTimes(GStopTime gStopTime, @NotNull public static Pair extractTimeInMs(@NotNull GStopTime gStopTime, - @NotNull List tripStopTimes) { + @NotNull List tripGStopTimes) { int previousArrivalTime = -1; int previousArrivalTimeStopSequence = -1; int previousDepartureTime = -1; @@ -1309,51 +1337,51 @@ public static Pair extractTimeInMs(@NotNull GStopTime gStopTime, int nextArrivalTimeStopSequence = -1; int nextDepartureTime = -1; int nextDepartureTimeStopSequence = -1; - for (GStopTime aStopTime : tripStopTimes) { - if (gStopTime.getTripIdInt() != aStopTime.getTripIdInt()) { + for (GStopTime tripGStopTime : tripGStopTimes) { + if (gStopTime.getTripIdInt() != tripGStopTime.getTripIdInt()) { continue; } - if (aStopTime.getStopSequence() == gStopTime.getStopSequence() - && aStopTime.getStopIdInt() == gStopTime.getStopIdInt() - && aStopTime.hasArrivalTime() - && aStopTime.hasDepartureTime()) { + if (tripGStopTime.getStopSequence() == gStopTime.getStopSequence() + && tripGStopTime.getStopIdInt() == gStopTime.getStopIdInt() + && tripGStopTime.hasArrivalTime() + && tripGStopTime.hasDepartureTime()) { return new Pair<>( // re-use provided stop times from another trip #frequencies - aStopTime.getArrivalTimeMs(), - aStopTime.getDepartureTimeMs() + tripGStopTime.getArrivalTimeMs(), + tripGStopTime.getDepartureTimeMs() ); } - if (aStopTime.getStopSequence() < gStopTime.getStopSequence()) { - if (aStopTime.hasDepartureTime()) { + if (tripGStopTime.getStopSequence() < gStopTime.getStopSequence()) { + if (tripGStopTime.hasDepartureTime()) { if (previousDepartureTime < 0 || previousDepartureTimeStopSequence < 0 - || previousDepartureTimeStopSequence < aStopTime.getStopSequence()) { - previousDepartureTime = aStopTime.getDepartureTime(); - previousDepartureTimeStopSequence = aStopTime.getStopSequence(); + || previousDepartureTimeStopSequence < tripGStopTime.getStopSequence()) { + previousDepartureTime = tripGStopTime.getDepartureTime(); + previousDepartureTimeStopSequence = tripGStopTime.getStopSequence(); } } - if (aStopTime.hasArrivalTime()) { + if (tripGStopTime.hasArrivalTime()) { if (previousArrivalTime < 0 || previousArrivalTimeStopSequence < 0 - || previousArrivalTimeStopSequence < aStopTime.getStopSequence()) { - previousArrivalTime = aStopTime.getArrivalTime(); - previousArrivalTimeStopSequence = aStopTime.getStopSequence(); + || previousArrivalTimeStopSequence < tripGStopTime.getStopSequence()) { + previousArrivalTime = tripGStopTime.getArrivalTime(); + previousArrivalTimeStopSequence = tripGStopTime.getStopSequence(); } } - } else if (aStopTime.getStopSequence() > gStopTime.getStopSequence()) { - if (aStopTime.hasDepartureTime()) { + } else if (tripGStopTime.getStopSequence() > gStopTime.getStopSequence()) { + if (tripGStopTime.hasDepartureTime()) { if (nextDepartureTime < 0 || nextDepartureTimeStopSequence < 0 - || nextDepartureTimeStopSequence > aStopTime.getStopSequence()) { - nextDepartureTime = aStopTime.getDepartureTime(); - nextDepartureTimeStopSequence = aStopTime.getStopSequence(); + || nextDepartureTimeStopSequence > tripGStopTime.getStopSequence()) { + nextDepartureTime = tripGStopTime.getDepartureTime(); + nextDepartureTimeStopSequence = tripGStopTime.getStopSequence(); } } - if (aStopTime.hasArrivalTime()) { + if (tripGStopTime.hasArrivalTime()) { if (nextArrivalTime < 0 || nextArrivalTimeStopSequence < 0 - || nextArrivalTimeStopSequence > aStopTime.getStopSequence()) { - nextArrivalTime = aStopTime.getArrivalTime(); - nextArrivalTimeStopSequence = aStopTime.getStopSequence(); + || nextArrivalTimeStopSequence > tripGStopTime.getStopSequence()) { + nextArrivalTime = tripGStopTime.getArrivalTime(); + nextArrivalTimeStopSequence = tripGStopTime.getStopSequence(); } } } @@ -1361,7 +1389,7 @@ public static Pair extractTimeInMs(@NotNull GStopTime gStopTime, if (previousArrivalTime == -1 || previousDepartureTime == -1) { //noinspection DiscouragedApi MTLog.log("Trip ID '%s' stops: ", gStopTime.getTripId()); - for (GStopTime aStopTime : tripStopTimes) { + for (GStopTime aStopTime : tripGStopTimes) { MTLog.log("- %s", aStopTime.toStringPlus(true)); } //noinspection DiscouragedApi @@ -1372,7 +1400,7 @@ public static Pair extractTimeInMs(@NotNull GStopTime gStopTime, if (nextArrivalTime == -1 || nextDepartureTime == -1) { //noinspection DiscouragedApi MTLog.log("Trip ID '%s' stops: ", gStopTime.getTripId()); - for (GStopTime aStopTime : tripStopTimes) { + for (GStopTime aStopTime : tripGStopTimes) { MTLog.log("- %s", aStopTime.toStringPlus(true)); } //noinspection DiscouragedApi diff --git a/src/main/java/org/mtransit/parser/config/gtfs/data/AgencyConfig.kt b/src/main/java/org/mtransit/parser/config/gtfs/data/AgencyConfig.kt index 0f561bf4..9b8b3cf3 100644 --- a/src/main/java/org/mtransit/parser/config/gtfs/data/AgencyConfig.kt +++ b/src/main/java/org/mtransit/parser/config/gtfs/data/AgencyConfig.kt @@ -17,11 +17,19 @@ data class AgencyConfig( */ @SerialName("target_route_type_id") val targetRouteTypeId: Int, // REQUIRED + // STRINGS /** * (Optional) Default string cleaner enabled/disabled (based on language/country/field) */ @SerialName("default_strings_cleaner_enabled") val defaultStringsCleanerEnabled: Boolean = false, // OPT-IN feature + @SerialName("to_lower_upper_case_strings") + val toLowerUpperCaseStrings: Boolean = false, // OPT-IN feature + @SerialName("to_lower_upper_case_words") + val toLowerUpperCaseWords: Boolean = false, // OPT-IN feature + @SerialName("ignore_upper_case_words") + val ignoreUpperCaseWords: List = emptyList(), // OPT-IN feature + // COLOR /** * (Optional) Default color enabled/disabled for agency (based on routes colors) */ @@ -32,6 +40,7 @@ data class AgencyConfig( */ @SerialName("default_color") val defaultColor: String, // REQUIRED + // SERVICE @SerialName("service_id_cleanup_regex") val serviceIdCleanupRegex: String? = null, // optional @SerialName("service_id_clean_merged") 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 e6ec5b02..41613a3b 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 @@ -4,6 +4,7 @@ import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import org.mtransit.commons.CleanUtils import org.mtransit.parser.gtfs.data.GRoute +import org.mtransit.parser.gtfs.data.GTrip @Serializable data class RouteConfig( @@ -40,6 +41,8 @@ data class RouteConfig( @SerialName("route_colors_ignored") val routeColorsIgnored: List = emptyList(), // optional // TRIP + @SerialName("trip_excludes") + val tripExcludes: List = emptyList(), // optional @SerialName("trip_headsign_cleaners") val tripHeadsignCleaners: List = emptyList(), @SerialName("trip_headsign_remove_via") @@ -127,6 +130,14 @@ data class RouteConfig( val replacement: String = "", ) + @Serializable + data class TripFilter( + @SerialName("trip_headsign_regex") + val tripHeadsignRegex: String? = null, + @SerialName("ignore_case") + val ignoreCase: Boolean = false, + ) + fun convertRouteIdFromShortNameNotSupported(routeShortName: String) = this.routeShortNameToRouteIdConfigs .singleOrNull { it.routeShortName == routeShortName }?.routeId @@ -160,6 +171,27 @@ data class RouteConfig( fun cleanTripHeadsign(tripHeadsign: String) = cleanString(tripHeadsign, this.tripHeadsignCleaners) + private val _tripExcludes: List by lazy { + this.tripExcludes.mapNotNull { + if (it.tripHeadsignRegex.isNullOrEmpty()) return@mapNotNull null + val regexOptions = mutableSetOf() + if (it.ignoreCase) { + regexOptions.add(RegexOption.IGNORE_CASE) + } + it.tripHeadsignRegex.toRegex(regexOptions) + } + } + + fun excludeTrip(gTrip: GTrip): Boolean { + this._tripExcludes.forEach { + val gTripHeadsign = gTrip.tripHeadsign ?: return@forEach + if (it.matches(gTripHeadsign)) { + return true // EXCLUDE + } + } + return false // KEEP + } + fun cleanDirectionHeadsign(directionHeadsign: String) = cleanString(directionHeadsign, this.directionHeadsignCleaners) diff --git a/src/main/java/org/mtransit/parser/gtfs/GAgencyTools.java b/src/main/java/org/mtransit/parser/gtfs/GAgencyTools.java index e4db6bb8..cfb89390 100644 --- a/src/main/java/org/mtransit/parser/gtfs/GAgencyTools.java +++ b/src/main/java/org/mtransit/parser/gtfs/GAgencyTools.java @@ -147,6 +147,12 @@ public interface GAgencyTools { @NotNull String cleanRouteLongName(@NotNull String routeLongName); + boolean lowerUCWords(); + + boolean lowerUCStrings(); + + @NotNull String[] getIgnoreUCWords(); + @SuppressWarnings("DeprecatedIsStillUsed") @Deprecated boolean allowGTFSIdOverride(); diff --git a/src/main/java/org/mtransit/parser/mt/GenerateMObjectsTask.java b/src/main/java/org/mtransit/parser/mt/GenerateMObjectsTask.java index 7f2b1ebf..bf9378ed 100644 --- a/src/main/java/org/mtransit/parser/mt/GenerateMObjectsTask.java +++ b/src/main/java/org/mtransit/parser/mt/GenerateMObjectsTask.java @@ -558,7 +558,7 @@ private void parseGTrips(HashMap mSchedules, if (cDirectionStopsList != null) { if (!CollectionUtils.equalsList(mDirectionStopsList, cDirectionStopsList)) { if (MDirectionStop.containsStopIds(cDirectionStopsList, mDirectionStopsList)) { - MTLog.logDebug("%s: Skip merge because current direction stops list contains other list.", this.routeId, MDirectionStop.printDirectionStops(cDirectionStopsList)); + MTLog.logDebug("%s: Skip merge because current direction stops list contains new stops list.", this.routeId); } else { MTLog.log("%s: Need to merge direction ID '%s' stops lists (sizes: %d in %d).", this.routeId, mDirection.getId(), mDirectionStopsList.size(), cDirectionStopsList.size()); if (Constants.DEBUG) { diff --git a/src/main/java/org/mtransit/parser/mt/MGenerator.java b/src/main/java/org/mtransit/parser/mt/MGenerator.java index f9353d92..d051b5ca 100644 --- a/src/main/java/org/mtransit/parser/mt/MGenerator.java +++ b/src/main/java/org/mtransit/parser/mt/MGenerator.java @@ -1240,6 +1240,7 @@ private static String getCommentedDateTime(int timestampInSec, @NotNull MSpec mS private static final String RESOURCE_INTEGER_AND_NAME_VALUE = RESOURCE_TAB + "%s"; private static final String RESOURCE_BOOL_AND_NAME_VALUE = RESOURCE_TAB + "%s"; private static final String RESOURCE_STRING_AND_NAME_VALUE = RESOURCE_TAB + "%s"; + private static final String RESOURCE_STRING_WITHOUT_VALUE = RESOURCE_TAB + ""; private static final String RESOURCES_END = ""; private static String getRESOURCES_INTEGER(String resName, Integer resValue) { @@ -1251,6 +1252,7 @@ private static String getRESOURCES_BOOL(String resName, boolean resValue) { } private static String getRESOURCES_STRING(@NotNull String resName, @Nullable Object resValue) { + if (resValue == null || resValue.toString().isEmpty()) return String.format(RESOURCE_STRING_WITHOUT_VALUE, resName); return String.format(RESOURCE_STRING_AND_NAME_VALUE, resName, resValue); }