Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Filter vector tiles stops by current service week #6003

Merged
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions doc/user/examples/ibi/portland/router-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,62 @@
"url": "https://gbfs.spin.pm/api/gbfs/v2/portland"
}
],
"vectorTiles": {
"basePath": "/rtp/routers/default/vectorTiles",
"attribution": "<a href='https://trimet.org/mod'>Regional Partners</a>",
"layers": [
{
"name": "stops",
"type": "Stop",
"mapper": "Digitransit",
"maxZoom": 20,
"minZoom": 14,
"cacheMaxSeconds": 600,
"filter": "current-trimet-service-week"
leonardehrenfried marked this conversation as resolved.
Show resolved Hide resolved
},
{
"name": "areaStops",
"type": "AreaStop",
"mapper": "OTPRR",
"maxZoom": 30,
"minZoom": 8,
"cacheMaxSeconds": 600
},
{
"name": "stations",
"type": "Station",
"mapper": "Digitransit",
"maxZoom": 20,
"minZoom": 2,
"cacheMaxSeconds": 600
},
{
"name": "rentalVehicles",
"type": "VehicleRentalVehicle",
"mapper": "Digitransit",
"maxZoom": 20,
"minZoom": 2,
"cacheMaxSeconds": 60
},
{
"name": "rentalStations",
"type": "VehicleRentalStation",
"mapper": "Digitransit",
"maxZoom": 20,
"minZoom": 2,
"cacheMaxSeconds": 600
},
{
"name": "vehicleParking",
"type": "VehicleParking",
"mapper": "Digitransit",
"maxZoom": 20,
"minZoom": 10,
"cacheMaxSeconds": 60,
"expansionFactor": 0.25
}
]
},
"rideHailingServices": [
{
"type": "uber-car-hailing",
Expand Down
13 changes: 13 additions & 0 deletions doc/user/sandbox/MapboxVectorTilesApi.md
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ For each layer, the configuration includes:
|       type = "stop" | `enum` | Type of the layer. | *Required* | | 2.0 |
|       [cacheMaxSeconds](#vectorTiles_layers_0_cacheMaxSeconds) | `integer` | Sets the cache header in the response. | *Optional* | `-1` | 2.0 |
|       [expansionFactor](#vectorTiles_layers_0_expansionFactor) | `double` | How far outside its boundaries should the tile contain information. | *Optional* | `0.25` | 2.0 |
|       [filter](#vectorTiles_layers_0_filter) | `enum` | Reduce the result set of a layer further by a specific filter. | *Optional* | `"none"` | 2.6 |
|       [mapper](#vectorTiles_layers_0_mapper) | `string` | Describes the mapper converting from the OTP model entities to the vector tile properties. | *Required* | | 2.0 |
|       maxZoom | `integer` | Maximum zoom levels the layer is active for. | *Optional* | `20` | 2.0 |
|       minZoom | `integer` | Minimum zoom levels the layer is active for. | *Optional* | `9` | 2.0 |
Expand Down Expand Up @@ -245,6 +246,18 @@ How far outside its boundaries should the tile contain information.

The value is a fraction of the tile size. If you are having problem with icons and shapes being clipped at tile edges, then increase this number.

<h4 id="vectorTiles_layers_0_filter">filter</h4>

**Since version:** `2.6` ∙ **Type:** `enum` ∙ **Cardinality:** `Optional` ∙ **Default value:** `"none"`
**Path:** /vectorTiles/layers/[0]
**Enum values:** `none` | `current-trimet-service-week`

Reduce the result set of a layer further by a specific filter.

This is useful for when the schema of a layer, say stops, should remain unchanged but some
elements should not be included in the result.


<h4 id="vectorTiles_layers_0_mapper">mapper</h4>

**Since version:** `2.0` ∙ **Type:** `string` ∙ **Cardinality:** `Required`
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package org.opentripplanner.ext.vectortiles.layers.stops;

import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.time.LocalDate;
import java.util.List;
import org.junit.jupiter.api.Test;
import org.opentripplanner.apis.gtfs.PatternTestModel;
import org.opentripplanner.transit.model._data.TransitModelForTest;
import org.opentripplanner.transit.model.network.TripPattern;
import org.opentripplanner.transit.model.site.RegularStop;

class LayerFiltersTest {

private static final RegularStop STOP = TransitModelForTest.of().stop("1").build();
private static final LocalDate DATE = LocalDate.of(2024, 9, 5);
private static final TripPattern PATTERN = PatternTestModel.pattern();

@Test
void includeStopWithinServiceWeek() {
var predicate = LayerFilters.currentServiceWeek(
s -> List.of(PATTERN),
trip -> List.of(DATE),
() -> DATE
);

assertTrue(predicate.test(STOP));
}

@Test
void excludeOutsideServiceWeek() {
var inThreeWeeks = DATE.plusDays(21);
var predicate = LayerFilters.currentServiceWeek(
s -> List.of(PATTERN),
trip -> List.of(inThreeWeeks),
() -> DATE
);

assertFalse(predicate.test(STOP));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package org.opentripplanner.ext.vectortiles.layers.stops;

import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.temporal.TemporalAdjusters;
import java.util.Collection;
import java.util.List;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import org.opentripplanner.apis.gtfs.model.LocalDateRange;
import org.opentripplanner.transit.model.network.TripPattern;
import org.opentripplanner.transit.model.site.RegularStop;
import org.opentripplanner.transit.model.timetable.Trip;
import org.opentripplanner.transit.service.PatternByServiceDatesFilter;
import org.opentripplanner.transit.service.TransitService;

/**
* Predicates for filtering elements of vector tile layers. Currently only contains predicates
* for {@link RegularStop}. Once more types need to be filtered, this may need some refactoring.
*/
public class LayerFilters {
leonardehrenfried marked this conversation as resolved.
Show resolved Hide resolved

/**
* No filter is applied: all stops are included in the result.
*/
public static final Predicate<RegularStop> NO_FILTER = x -> true;

/**
* Returns a predicate which only includes stop which are visited by a pattern that is in the current
* TriMet service week, namely from Sunday to Sunday.
*/
public static Predicate<RegularStop> currentServiceWeek(
leonardehrenfried marked this conversation as resolved.
Show resolved Hide resolved
Function<RegularStop, Collection<TripPattern>> getPatternsForStop,
Function<Trip, Collection<LocalDate>> getServiceDatesForTrip,
Supplier<LocalDate> nowSupplier
) {
var serviceDate = nowSupplier.get();
var lastSunday = serviceDate.with(TemporalAdjusters.previousOrSame(DayOfWeek.SUNDAY));
var nextSunday = serviceDate.with(TemporalAdjusters.next(DayOfWeek.SUNDAY)).plusDays(1);
leonardehrenfried marked this conversation as resolved.
Show resolved Hide resolved

var filter = new PatternByServiceDatesFilter(
new LocalDateRange(lastSunday, nextSunday),
// not used
route -> List.of(),
getServiceDatesForTrip
);

return regularStop -> {
var patterns = getPatternsForStop.apply(regularStop);
var patternsInCurrentWeek = filter.filterPatterns(patterns);
return !patternsInCurrentWeek.isEmpty();
};
}

public static Predicate<RegularStop> forType(FilterType type, TransitService transitService) {
return switch (type) {
case NONE -> NO_FILTER;
case CURRENT_TRIMET_SERVICE_WEEK -> currentServiceWeek(
transitService::getPatternsForStop,
trip ->
transitService.getCalendarService().getServiceDatesForServiceId(trip.getServiceId()),
() -> LocalDate.now(transitService.getTimeZone())
);
};
}

public enum FilterType {
NONE,
CURRENT_TRIMET_SERVICE_WEEK,
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,32 +5,27 @@
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.function.BiFunction;
import java.util.stream.Collectors;
import java.util.function.Predicate;
import org.locationtech.jts.geom.Envelope;
import org.locationtech.jts.geom.Geometry;
import org.opentripplanner.apis.support.mapping.PropertyMapper;
import org.opentripplanner.ext.vectortiles.VectorTilesResource;
import org.opentripplanner.inspector.vector.LayerBuilder;
import org.opentripplanner.inspector.vector.LayerParameters;
import org.opentripplanner.transit.model.site.RegularStop;
import org.opentripplanner.transit.service.TransitService;

public class StopsLayerBuilder<T> extends LayerBuilder<T> {
public class StopsLayerBuilder extends LayerBuilder<RegularStop> {

static Map<MapperType, BiFunction<TransitService, Locale, PropertyMapper<RegularStop>>> mappers = Map.of(
MapperType.Digitransit,
DigitransitStopPropertyMapper::create
);
private final TransitService transitService;
private final Predicate<RegularStop> filter;

public StopsLayerBuilder(
TransitService transitService,
LayerParameters<VectorTilesResource.LayerType> layerParameters,
Locale locale
) {
super(
(PropertyMapper<T>) Map
Map
.ofEntries(
entry(MapperType.Digitransit, new DigitransitStopPropertyMapper(transitService, locale)),
entry(
Expand All @@ -43,20 +38,22 @@ public StopsLayerBuilder(
layerParameters.expansionFactor()
);
this.transitService = transitService;
this.filter = LayerFilters.forType(layerParameters.filterType(), transitService);
}

protected List<Geometry> getGeometries(Envelope query) {
return transitService
.findRegularStops(query)
.stream()
.filter(filter)
.map(stop -> {
Geometry point = stop.getGeometry();

point.setUserData(stop);

return point;
})
.collect(Collectors.toList());
.toList();
}

enum MapperType {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,12 @@
import org.locationtech.jts.geom.Envelope;
import org.opentripplanner.apis.gtfs.GraphQLRequestContext;
import org.opentripplanner.apis.gtfs.GraphQLUtils;
import org.opentripplanner.apis.gtfs.PatternByServiceDatesFilter;
import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers;
import org.opentripplanner.apis.gtfs.generated.GraphQLTypes;
import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLQueryTypeStopsByRadiusArgs;
import org.opentripplanner.apis.gtfs.mapping.routerequest.LegacyRouteRequestMapper;
import org.opentripplanner.apis.gtfs.mapping.routerequest.RouteRequestMapper;
import org.opentripplanner.apis.gtfs.support.filter.PatternByDateFilterUtil;
import org.opentripplanner.apis.gtfs.support.time.LocalDateRangeUtil;
import org.opentripplanner.ext.fares.impl.DefaultFareService;
import org.opentripplanner.ext.fares.impl.GtfsFaresService;
Expand Down Expand Up @@ -615,7 +615,10 @@ public DataFetcher<Iterable<Route>> routes() {
}

if (LocalDateRangeUtil.hasServiceDateFilter(args.getGraphQLServiceDates())) {
var filter = new PatternByServiceDatesFilter(args.getGraphQLServiceDates(), transitService);
var filter = PatternByDateFilterUtil.ofGraphQL(
args.getGraphQLServiceDates(),
transitService
);
routeStream = filter.filterRoutes(routeStream).stream();
}
return routeStream.toList();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@
import java.util.stream.Collectors;
import org.opentripplanner.apis.gtfs.GraphQLRequestContext;
import org.opentripplanner.apis.gtfs.GraphQLUtils;
import org.opentripplanner.apis.gtfs.PatternByServiceDatesFilter;
import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers;
import org.opentripplanner.apis.gtfs.generated.GraphQLTypes;
import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLBikesAllowed;
import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLTransitMode;
import org.opentripplanner.apis.gtfs.mapping.BikesAllowedMapper;
import org.opentripplanner.apis.gtfs.support.filter.PatternByDateFilterUtil;
import org.opentripplanner.apis.gtfs.support.time.LocalDateRangeUtil;
import org.opentripplanner.routing.alertpatch.EntitySelector;
import org.opentripplanner.routing.alertpatch.TransitAlert;
Expand Down Expand Up @@ -183,7 +183,10 @@ public DataFetcher<Iterable<TripPattern>> patterns() {
var args = new GraphQLTypes.GraphQLRoutePatternsArgs(environment.getArguments());

if (LocalDateRangeUtil.hasServiceDateFilter(args.getGraphQLServiceDates())) {
var filter = new PatternByServiceDatesFilter(args.getGraphQLServiceDates(), transitService);
var filter = PatternByDateFilterUtil.ofGraphQL(
args.getGraphQLServiceDates(),
transitService
);
return filter.filterPatterns(patterns);
} else {
return patterns;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package org.opentripplanner.apis.gtfs.support.filter;

import org.opentripplanner.apis.gtfs.generated.GraphQLTypes;
import org.opentripplanner.apis.gtfs.model.LocalDateRange;
import org.opentripplanner.transit.service.PatternByServiceDatesFilter;
import org.opentripplanner.transit.service.TransitService;

/**
* Utility methods for instantiating a {@link PatternByServiceDatesFilter}/
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* Utility methods for instantiating a {@link PatternByServiceDatesFilter}/
* Utility methods for instantiating a {@link PatternByServiceDatesFilter}.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the / was a mistake. but if not, feel free to ignore this suggestion.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was. Applied.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why am I still seeing the / here? What caused this 🤔 ?

*/
public class PatternByDateFilterUtil {

public static PatternByServiceDatesFilter ofGraphQL(
GraphQLTypes.GraphQLLocalDateRangeInput range,
TransitService transitService
) {
return new PatternByServiceDatesFilter(
new LocalDateRange(range.getGraphQLStart(), range.getGraphQLEnd()),
transitService::getPatternsForRoute,
trip -> transitService.getCalendarService().getServiceDatesForServiceId(trip.getServiceId())
);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.opentripplanner.inspector.vector;

import org.opentripplanner.apis.support.mapping.PropertyMapper;
import org.opentripplanner.ext.vectortiles.layers.stops.LayerFilters;

/**
* Configuration options for a single vector tile layer.
Expand Down Expand Up @@ -53,4 +54,8 @@ default int cacheMaxSeconds() {
default double expansionFactor() {
return EXPANSION_FACTOR;
}

default LayerFilters.FilterType filterType() {
return LayerFilters.FilterType.NONE;
}
}
Loading
Loading