Skip to content

Commit

Permalink
Merge pull request #6003 from ibi-group/trimet-vector-layer
Browse files Browse the repository at this point in the history
Filter vector tiles stops by current service week
  • Loading branch information
leonardehrenfried committed Sep 20, 2024
2 parents 12273e7 + 253bb17 commit dc56145
Show file tree
Hide file tree
Showing 19 changed files with 446 additions and 89 deletions.
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": "sunday-to-sunday-service-week"
},
{
"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` | `sunday-to-sunday-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;

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.transit.model._data.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.buildCurrentServiceWeekPredicate(
s -> List.of(PATTERN),
trip -> List.of(DATE),
() -> DATE
);

assertTrue(predicate.test(STOP));
}

@Test
void excludeOutsideServiceWeek() {
var inThreeWeeks = DATE.plusDays(21);
var predicate = LayerFilters.buildCurrentServiceWeekPredicate(
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,73 @@
package org.opentripplanner.ext.vectortiles.layers;

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 {

/**
* 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
* "service week", which lasts from Sunday to Sunday.
*/
public static Predicate<RegularStop> buildCurrentServiceWeekPredicate(
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 nextSundayPlusOne = serviceDate.with(TemporalAdjusters.next(DayOfWeek.SUNDAY)).plusDays(1);

var filter = new PatternByServiceDatesFilter(
// reminder, the end of the date range is exclusive so it's the next Sunday plus one day
new LocalDateRange(lastSunday, nextSundayPlusOne),
// 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 SUNDAY_TO_SUNDAY_SERVICE_WEEK -> buildCurrentServiceWeekPredicate(
transitService::getPatternsForStop,
trip ->
transitService.getCalendarService().getServiceDatesForServiceId(trip.getServiceId()),
() -> LocalDate.now(transitService.getTimeZone())
);
};
}

public enum FilterType {
NONE,
SUNDAY_TO_SUNDAY_SERVICE_WEEK,
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,32 +5,28 @@
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.ext.vectortiles.layers.LayerFilters;
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 +39,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,8 +615,11 @@ public DataFetcher<Iterable<Route>> routes() {
}

if (LocalDateRangeUtil.hasServiceDateFilter(args.getGraphQLServiceDates())) {
var filter = new PatternByServiceDatesFilter(args.getGraphQLServiceDates(), transitService);
routeStream = filter.filterRoutes(routeStream).stream();
var filter = PatternByDateFilterUtil.ofGraphQL(
args.getGraphQLServiceDates(),
transitService
);
routeStream = filter.filterRoutes(routeStream.toList()).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
Expand Up @@ -18,6 +18,8 @@
import org.opentripplanner.apis.gtfs.GraphQLUtils;
import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers;
import org.opentripplanner.apis.gtfs.generated.GraphQLTypes;
import org.opentripplanner.apis.gtfs.support.filter.PatternByDateFilterUtil;
import org.opentripplanner.apis.gtfs.support.time.LocalDateRangeUtil;
import org.opentripplanner.framework.time.ServiceDateUtils;
import org.opentripplanner.model.StopTimesInPattern;
import org.opentripplanner.model.TripTimeOnDate;
Expand Down Expand Up @@ -243,7 +245,19 @@ public DataFetcher<String> platformCode() {

@Override
public DataFetcher<Iterable<Route>> routes() {
return this::getRoutes;
return env -> {
var args = new GraphQLTypes.GraphQLStopRoutesArgs(env.getArguments());
var routes = getRoutes(env);
if (LocalDateRangeUtil.hasServiceDateFilter(args.getGraphQLServiceDates())) {
var filter = PatternByDateFilterUtil.ofGraphQL(
args.getGraphQLServiceDates(),
getTransitService(env)
);
return filter.filterRoutes(routes);
} else {
return routes;
}
};
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4231,6 +4231,26 @@ public void setGraphQLLanguage(String language) {
}
}

public static class GraphQLStopRoutesArgs {

private GraphQLLocalDateRangeInput serviceDates;

public GraphQLStopRoutesArgs(Map<String, Object> args) {
if (args != null) {
this.serviceDates =
new GraphQLLocalDateRangeInput((Map<String, Object>) args.get("serviceDates"));
}
}

public GraphQLLocalDateRangeInput getGraphQLServiceDates() {
return this.serviceDates;
}

public void setGraphQLServiceDates(GraphQLLocalDateRangeInput serviceDates) {
this.serviceDates = serviceDates;
}
}

public static class GraphQLStopStopTimesForPatternArgs {

private String id;
Expand Down
Loading

0 comments on commit dc56145

Please sign in to comment.