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

Add stop layer to new Debug UI #5602

Merged
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
2218c1a
Add new debug vector tiles
leonardehrenfried Dec 19, 2023
176187c
Add stop layer to tile.json
leonardehrenfried Dec 19, 2023
7aaf79f
Move classes into their own packages
leonardehrenfried Dec 19, 2023
033000a
Move styles into separate class
leonardehrenfried Dec 20, 2023
57f07dd
Use style.json from server
leonardehrenfried Dec 21, 2023
71f7393
Refactor dependencies of map style classes
leonardehrenfried Dec 21, 2023
ffd8ae6
Add popup data
leonardehrenfried Dec 22, 2023
da28cf2
Remove edge layer builder
leonardehrenfried Dec 30, 2023
18eb243
Make styles a but prettier
leonardehrenfried Dec 30, 2023
7cfbae8
Add test for debug style spec
leonardehrenfried Dec 30, 2023
c7395d8
Save a JSON round trip
leonardehrenfried Dec 30, 2023
934b809
Add attribution, remove client side style
leonardehrenfried Jan 2, 2024
826a842
Add documentation, fix frontend code
leonardehrenfried Jan 2, 2024
c864200
Make TS compiler happy
leonardehrenfried Jan 2, 2024
a18d77d
Convert from class to type
leonardehrenfried Jan 2, 2024
1cbce1f
Use optional chaining
leonardehrenfried Jan 2, 2024
bed4eff
Rename the feature flag DebugClient to DebugUi, remove separate one f…
leonardehrenfried Jan 9, 2024
c4523af
Merge remote-tracking branch 'upstream/dev-2.x' into vector-debug
leonardehrenfried Jan 9, 2024
4b98891
Update comment
leonardehrenfried Jan 9, 2024
2b65d57
Make mapStyle configurable
leonardehrenfried Jan 9, 2024
756a289
Use proper copyright symbol
leonardehrenfried Jan 9, 2024
0503e9f
Merge remote-tracking branch 'upstream/dev-2.x' into vector-debug
leonardehrenfried Jan 11, 2024
5972e5d
Resolve merge artifacts
leonardehrenfried Jan 11, 2024
4fd4325
Update docs
leonardehrenfried Jan 11, 2024
c5dc289
Apply review feedback
leonardehrenfried Jan 11, 2024
24d5929
Remove extra function definition
leonardehrenfried Jan 11, 2024
cf5d875
Remove extra method call indirection
leonardehrenfried Jan 11, 2024
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
50 changes: 42 additions & 8 deletions client-next/src/components/MapView/MapView.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { LngLat, Map, NavigationControl } from 'react-map-gl';
import { LngLat, Map, MapboxGeoJSONFeature, NavigationControl, Popup } from 'react-map-gl';
import 'maplibre-gl/dist/maplibre-gl.css';
import { TripPattern, TripQuery, TripQueryVariables } from '../../gql/graphql.ts';
import { NavigationMarkers } from './NavigationMarkers.tsx';
import { LegLines } from './LegLines.tsx';
import { useMapDoubleClick } from './useMapDoubleClick.ts';
import { mapStyle } from './mapStyle.ts';
import { useState } from 'react';
import { ContextMenuPopup } from './ContextMenuPopup.tsx';
import { Table } from 'react-bootstrap';

// TODO: this should be configurable
const initialViewState = {
Expand All @@ -15,6 +15,8 @@ const initialViewState = {
zoom: 4,
};

type PopupData = { coordinates: LngLat; feature: MapboxGeoJSONFeature };

export function MapView({
tripQueryVariables,
setTripQueryVariables,
Expand All @@ -29,20 +31,33 @@ export function MapView({
loading: boolean;
}) {
const onMapDoubleClick = useMapDoubleClick({ tripQueryVariables, setTripQueryVariables });
const [showPopup, setShowPopup] = useState<LngLat | null>(null);
const [showContextPopup, setShowContextPopup] = useState<LngLat | null>(null);
const [showPropsPopup, setShowPropsPopup] = useState<PopupData | null>(null);

return (
<div className="map-container below-content">
<Map
// @ts-ignore
mapLib={import('maplibre-gl')}
// @ts-ignore
mapStyle={mapStyle}
mapStyle="http://localhost:8080/otp/routers/default/inspector/vectortile/style.json"
initialViewState={initialViewState}
leonardehrenfried marked this conversation as resolved.
Show resolved Hide resolved
onDblClick={onMapDoubleClick}
onContextMenu={(e) => {
setShowPopup(e.lngLat);
setShowContextPopup(e.lngLat);
}}
interactiveLayerIds={['regular-stop']}
onClick={(e) => {
if (e.features) {
leonardehrenfried marked this conversation as resolved.
Show resolved Hide resolved
const props = e.features[0];
setShowPropsPopup({ coordinates: e.lngLat, feature: props });
leonardehrenfried marked this conversation as resolved.
Show resolved Hide resolved
}
}}
// put lat/long in URL and pan to it on page reload
hash={true}
// disable pitching and rotating the map
touchPitch={false}
dragRotate={false}
>
<NavigationControl position="top-left" />
<NavigationMarkers
Expand All @@ -53,14 +68,33 @@ export function MapView({
{tripQueryResult?.trip.tripPatterns.length && (
<LegLines tripPattern={tripQueryResult.trip.tripPatterns[selectedTripPatternIndex] as TripPattern} />
)}
{showPopup && (
{showContextPopup && (
<ContextMenuPopup
tripQueryVariables={tripQueryVariables}
setTripQueryVariables={setTripQueryVariables}
coordinates={showPopup}
onClose={() => setShowPopup(null)}
coordinates={showContextPopup}
onClose={() => setShowContextPopup(null)}
/>
)}
{showPropsPopup && showPropsPopup.feature && showPropsPopup.feature.properties && (
<Popup
latitude={showPropsPopup.coordinates.lat}
testower marked this conversation as resolved.
Show resolved Hide resolved
longitude={showPropsPopup.coordinates.lng}
closeButton={true}
onClose={() => setShowPropsPopup(null)}
>
<Table bordered>
<tbody>
{Object.entries(showPropsPopup.feature.properties).map(([key, value]) => (
<tr key={key}>
leonardehrenfried marked this conversation as resolved.
Show resolved Hide resolved
<th scope="row">{key}</th>
<td>{value}</td>
</tr>
))}
</tbody>
</Table>
</Popup>
)}
</Map>
</div>
);
Expand Down
19 changes: 0 additions & 19 deletions client-next/src/components/MapView/mapStyle.ts

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@
import java.util.List;
import org.opentripplanner.api.resource.BikeRental;
import org.opentripplanner.api.resource.GraphInspectorTileResource;
import org.opentripplanner.api.resource.GraphInspectorVectorTileResource;
import org.opentripplanner.api.resource.PlannerResource;
import org.opentripplanner.api.resource.Routers;
import org.opentripplanner.api.resource.ServerInfo;
import org.opentripplanner.api.resource.UpdaterStatusResource;
import org.opentripplanner.apis.gtfs.GtfsGraphQLAPI;
import org.opentripplanner.apis.transmodel.TransmodelAPI;
import org.opentripplanner.apis.vectortiles.GraphInspectorVectorTileResource;
import org.opentripplanner.ext.actuator.ActuatorAPI;
import org.opentripplanner.ext.geocoder.GeocoderResource;
import org.opentripplanner.ext.parkAndRideApi.ParkAndRideResource;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package org.opentripplanner.apis.vectortiles;

import java.util.List;
import org.opentripplanner.apis.vectortiles.model.LayerStyleBuilder;
import org.opentripplanner.apis.vectortiles.model.StyleSpec;
import org.opentripplanner.apis.vectortiles.model.TileSource;
import org.opentripplanner.apis.vectortiles.model.TileSource.RasterSource;
import org.opentripplanner.apis.vectortiles.model.TileSource.VectorSource;

/**
* A Mapbox/Mapblibre style specification for rendering debug information about transit and
* street data.
*/
public class DebugStyleSpec {

private static final RasterSource BACKGROUND_SOURCE = new RasterSource(
"background",
List.of("https://a.tile.openstreetmap.org/{z}/{x}/{y}.png"),
256,
"&copy; OpenStreetMap Contributors"
);

public record VectorSourceLayer(VectorSource vectorSource, String vectorLayer) {}

static StyleSpec build(VectorSource debugSource, VectorSourceLayer regularStops) {
List<TileSource> sources = List.of(BACKGROUND_SOURCE, debugSource);
return new StyleSpec(
"OTP Debug Tiles",
sources,
List.of(
LayerStyleBuilder
.ofId("background")
.typeRaster()
.source(BACKGROUND_SOURCE)
.minZoom(0)
.maxZoom(22),
LayerStyleBuilder
.ofId("regular-stop")
.typeCircle()
.vectorSourceLayer(regularStops)
.circleStroke("#140d0e", 2)
.circleColor("#fcf9fa")
.minZoom(13)
.maxZoom(22)
)
);
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.opentripplanner.api.resource;
package org.opentripplanner.apis.vectortiles;

import static org.opentripplanner.framework.io.HttpUtils.APPLICATION_X_PROTOBUF;

Expand All @@ -16,13 +16,20 @@
import java.util.Locale;
import java.util.Objects;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import javax.annotation.Nonnull;
import org.glassfish.grizzly.http.server.Request;
import org.opentripplanner.api.model.TileJson;
import org.opentripplanner.inspector.vector.AreaStopsLayerBuilder;
import org.opentripplanner.apis.vectortiles.model.LayerParams;
import org.opentripplanner.apis.vectortiles.model.LayerType;
import org.opentripplanner.apis.vectortiles.model.StyleSpec;
import org.opentripplanner.apis.vectortiles.model.TileSource.VectorSource;
import org.opentripplanner.framework.io.HttpUtils;
import org.opentripplanner.inspector.vector.LayerBuilder;
import org.opentripplanner.inspector.vector.LayerParameters;
import org.opentripplanner.inspector.vector.VectorTileResponseFactory;
import org.opentripplanner.inspector.vector.geofencing.GeofencingZonesLayerBuilder;
import org.opentripplanner.inspector.vector.stop.StopLayerBuilder;
import org.opentripplanner.model.FeedInfo;
import org.opentripplanner.standalone.api.OtpServerRequestContext;

Expand All @@ -33,9 +40,19 @@
@Path("/routers/{ignoreRouterId}/inspector/vectortile")
public class GraphInspectorVectorTileResource {

private static final LayerParams REGULAR_STOPS = new LayerParams(
"regularStops",
LayerType.RegularStop
);
private static final LayerParams AREA_STOPS = new LayerParams("areaStops", LayerType.AreaStop);
private static final LayerParams GEOFENCING_ZONES = new LayerParams(
"geofencingZones",
LayerType.GeofencingZones
);
private static final List<LayerParameters<LayerType>> DEBUG_LAYERS = List.of(
new LayerParams("areaStops", LayerType.AreaStop),
new LayerParams("geofencingZones", LayerType.GeofencingZones)
REGULAR_STOPS,
AREA_STOPS,
GEOFENCING_ZONES
);

private final OtpServerRequestContext serverContext;
Expand Down Expand Up @@ -84,13 +101,7 @@ public TileJson getTileJson(
@PathParam("layers") String requestedLayers
) {
var envelope = serverContext.worldEnvelopeService().envelope().orElseThrow();
List<FeedInfo> feedInfos = serverContext
.transitService()
.getFeedIds()
.stream()
.map(serverContext.transitService()::getFeedInfo)
.filter(Predicate.not(Objects::isNull))
.toList();
List<FeedInfo> feedInfos = feedInfos();

return new TileJson(
uri,
Expand All @@ -103,26 +114,54 @@ public TileJson getTileJson(
);
}

@GET
@Path("/style.json")
@Produces(MediaType.APPLICATION_JSON)
public StyleSpec getTileJson(@Context UriInfo uri, @Context HttpHeaders headers) {
var base = HttpUtils.getBaseAddress(uri, headers);
final String allLayers = DEBUG_LAYERS
.stream()
.map(LayerParameters::name)
.collect(Collectors.joining(","));
var url =
"%s/otp/routers/%s/inspector/vectortile/%s/tilejson.json".formatted(
base,
ignoreRouterId,
allLayers
);

var vectorSource = new VectorSource("debug", url);
return DebugStyleSpec.build(vectorSource, REGULAR_STOPS.toVectorSourceLayer(vectorSource));
}

@Nonnull
private List<FeedInfo> feedInfos() {
return serverContext
.transitService()
.getFeedIds()
.stream()
.map(serverContext.transitService()::getFeedInfo)
.filter(Predicate.not(Objects::isNull))
.toList();
}

private static LayerBuilder<?> createLayerBuilder(
LayerParameters<LayerType> layerParameters,
Locale locale,
OtpServerRequestContext context
) {
return switch (layerParameters.type()) {
case AreaStop -> new AreaStopsLayerBuilder(context.transitService(), layerParameters, locale);
case RegularStop -> new StopLayerBuilder<>(
layerParameters,
locale,
e -> context.transitService().findRegularStop(e)
);
case AreaStop -> new StopLayerBuilder<>(
layerParameters,
locale,
e -> context.transitService().findAreaStops(e)
);
case GeofencingZones -> new GeofencingZonesLayerBuilder(context.graph(), layerParameters);
};
}

private enum LayerType {
AreaStop,
GeofencingZones,
}

private record LayerParams(String name, LayerType type) implements LayerParameters<LayerType> {
@Override
public String mapper() {
return "DebugClient";
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package org.opentripplanner.apis.vectortiles.model;

import org.opentripplanner.apis.vectortiles.DebugStyleSpec.VectorSourceLayer;
import org.opentripplanner.apis.vectortiles.model.TileSource.VectorSource;
import org.opentripplanner.inspector.vector.LayerParameters;

public record LayerParams(String name, LayerType type) implements LayerParameters<LayerType> {
@Override
public String mapper() {
return "DebugClient";
}

/**
* Convert these params to a vector source layer so that it can be used in the style for rendering
* in the frontend.
*/
public VectorSourceLayer toVectorSourceLayer(VectorSource source) {
return new VectorSourceLayer(source, name);
}
}
Loading
Loading