diff --git a/src/main/java/org/mtransit/parser/DefaultAgencyTools.java b/src/main/java/org/mtransit/parser/DefaultAgencyTools.java index 97370ed..c643e70 100644 --- a/src/main/java/org/mtransit/parser/DefaultAgencyTools.java +++ b/src/main/java/org/mtransit/parser/DefaultAgencyTools.java @@ -334,6 +334,9 @@ public Integer getAgencyRouteType() { @Nullable public Integer getAgencyExtendedRouteType() { + if (Configs.getAgencyConfig() != null) { + return Configs.getAgencyConfig().getExtendedTargetRouteTypeId(); + } return null; // optional } @@ -1105,6 +1108,9 @@ public boolean forceStopTimeFirstNoDropOffType() { @Override public boolean excludeStopTime(@NotNull GStopTime gStopTime) { + if (Configs.getRouteConfig().excludeStopTime(gStopTime)) { + return EXCLUDE; + } // https://gtfs.org/schedule/best-practices/#stop_timestxt return GPickupType.NO_PICKUP == gStopTime.getPickupType() // && GDropOffType.NO_DROP_OFF == gStopTime.getDropOffType(); 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 9b8b3cf..5ff10ec 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,6 +17,8 @@ data class AgencyConfig( */ @SerialName("target_route_type_id") val targetRouteTypeId: Int, // REQUIRED + @SerialName("extended_target_route_type_id") + val extendedTargetRouteTypeId: Int? = null, // OPTIONAL // STRINGS /** * (Optional) Default string cleaner enabled/disabled (based on language/country/field) 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 cb077c8..d1fbce3 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.GStopTime import org.mtransit.parser.gtfs.data.GTrip @Serializable @@ -104,6 +105,8 @@ data class RouteConfig( @SerialName("stop_headsign_cleanup_regex") val stopHeadsignCleanupRegex: String? = null, // optional // STOP TIMES + @SerialName("stop_time_excludes") + val stopTimeExcludes: List = emptyList(), // optional @SerialName("allow_invalid_stop_times") val allowInvalidStopTimes: Boolean = false, // OPT-IN feature @SerialName("allow_invalid_stop_times_until") @@ -182,6 +185,14 @@ data class RouteConfig( val ignoreCase: Boolean = false, ) + @Serializable + data class StopTimeFilter( + @SerialName("stop_time_headsign_regex") + val stopTimeHeadsignRegex: String? = null, + @SerialName("ignore_case") + val ignoreCase: Boolean = false, + ) + fun keepRoutes(gRoute: GRoute) = this.keepRoutes.any { //noinspection DiscouragedApi @@ -252,8 +263,8 @@ data class RouteConfig( } fun excludeTrip(gTrip: GTrip): Boolean { + val gTripHeadsign = gTrip.tripHeadsign ?: return false // EXCLUDE this._tripExcludes.forEach { - val gTripHeadsign = gTrip.tripHeadsign ?: return@forEach if (it.matches(gTripHeadsign)) { return true // EXCLUDE } @@ -261,6 +272,27 @@ data class RouteConfig( return false // KEEP } + private val _stopTimeExcludes: List by lazy { + this.stopTimeExcludes.mapNotNull { + if (it.stopTimeHeadsignRegex.isNullOrEmpty()) return@mapNotNull null + val regexOptions = mutableSetOf() + if (it.ignoreCase) { + regexOptions.add(RegexOption.IGNORE_CASE) + } + it.stopTimeHeadsignRegex.toRegex(regexOptions) + } + } + + fun excludeStopTime(gStopTime: GStopTime): Boolean { + val gStopTimeHeadsign = gStopTime.stopHeadsign ?: return false // KEEP + this._stopTimeExcludes.forEach { + if (it.matches(gStopTimeHeadsign)) { + return true // EXCLUDE + } + } + return false // KEEP + } + fun cleanDirectionHeadsign(directionHeadsign: String) = cleanString(directionHeadsign, this.directionHeadsignCleaners)