Skip to content

Commit

Permalink
feat(HomeMapView): Show alerting route segments as dashed lines
Browse files Browse the repository at this point in the history
  • Loading branch information
KaylaBrady committed Apr 9, 2024
1 parent 45d5c47 commit 61ac3f0
Show file tree
Hide file tree
Showing 7 changed files with 158 additions and 20 deletions.
27 changes: 21 additions & 6 deletions iosApp/iosApp/Pages/Map/HomeMapView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -63,15 +63,24 @@ struct HomeMapView: View {
}
.gestureOptions(.init(rotateEnabled: false, pitchEnabled: false))
.mapStyle(.light)
.onCameraChanged { change in handleCameraChange(change) }
.onCameraChanged { change in handleCameraChange(change)
}
.ornamentOptions(.init(scaleBar: .init(visibility: .hidden)))
.onLayerTapGesture(StopLayerGenerator.getStopLayerId(.stop), perform: handleStopLayerTap)
.onLayerTapGesture(StopLayerGenerator.getStopLayerId(.station), perform: handleStopLayerTap)
.additionalSafeAreaInsets(.bottom, sheetHeight)
.accessibilityIdentifier("transitMap")
.onAppear { handleAppear(location: proxy.location, map: proxy.map) }
.onChange(of: globalFetcher.response) { _ in handleTryLayerInit(map: proxy.map) }
.onChange(of: railRouteShapeFetcher.response) { _ in handleTryLayerInit(map: proxy.map) }
.onChange(of: globalFetcher.response) { _ in
handleTryLayerInit(map: proxy.map)
currentStopAlerts = globalFetcher.getRealtimeAlertsByStop(
alerts: alertsFetcher.alerts,
filterAtTime: now.toKotlinInstant()
)
}
.onChange(of: railRouteShapeFetcher.response) { _ in
handleTryLayerInit(map: proxy.map)
}
.onChange(of: locationDataManager.authorizationStatus) { status in
if status == .authorizedAlways || status == .authorizedWhenInUse {
Task { viewportProvider.follow(animation: .easeInOut(duration: 0)) }
Expand Down Expand Up @@ -152,7 +161,8 @@ struct HomeMapView: View {
) {
let layerManager = MapLayerManager(map: map)

let routeSourceGenerator = RouteSourceGenerator(routeData: routeResponse, stopsById: stops, alertsByStop: currentStopAlerts)
let routeSourceGenerator = RouteSourceGenerator(routeData: routeResponse, stopsById: stops,
alertsByStop: currentStopAlerts)
layerManager.addSources(
routeSourceGenerator: routeSourceGenerator,
stopSourceGenerator: StopSourceGenerator(
Expand All @@ -171,12 +181,17 @@ struct HomeMapView: View {
}

func handleStopAlertChange(alertsByStop: [String: AlertAssociatedStop]) {
let updatedSources = StopSourceGenerator(
let updatedStopSources = StopSourceGenerator(
stops: globalFetcher.stops,
routeSourceDetails: layerManager?.routeSourceGenerator?.routeSourceDetails,
alertsByStop: alertsByStop
)
layerManager?.updateSourceData(stopSourceGenerator: updatedSources)
layerManager?.updateSourceData(stopSourceGenerator: updatedStopSources)
if let railResponse = railRouteShapeFetcher.response {
let updatedRouteSources = RouteSourceGenerator(routeData: railResponse, stopsById: globalFetcher.stops,
alertsByStop: alertsByStop)
layerManager?.updateSourceData(routeSourceGenerator: updatedRouteSources)
}
}

func handleStopLayerTap(feature _: QueriedFeature, _: MapContentGestureContext) -> Bool {
Expand Down
2 changes: 1 addition & 1 deletion iosApp/iosApp/Pages/Map/RouteLayerGenerator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ class RouteLayerGenerator {
source: RouteSourceGenerator.getRouteSourceId(route.id)
)
alertingLayer.filter = Exp(.get) { RouteSourceGenerator.propIsAlertingKey }
alertingLayer.lineDasharray = .constant([2.0, 2.0])
alertingLayer.lineDasharray = .constant([2.0, 3.0])

alertingLayer.lineWidth = .constant(4.0)
alertingLayer.lineColor = .constant(StyleColor(UIColor.white))
Expand Down
21 changes: 14 additions & 7 deletions iosApp/iosApp/Pages/Map/RouteSourceGenerator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,14 @@ class RouteSourceGenerator {

init(routeData: MapFriendlyRouteResponse, stopsById: [String: Stop], alertsByStop: [String: AlertAssociatedStop]) {
self.routeData = routeData
routeSourceDetails = Self.generateRouteSources(routeData: routeData, stopsById: stopsById, alertsByStop: alertsByStop)
routeSourceDetails = Self.generateRouteSources(routeData: routeData, stopsById: stopsById,
alertsByStop: alertsByStop)
routeSources = routeSourceDetails.map(\.source)
}

static func generateRouteSources(routeData: MapFriendlyRouteResponse,
stopsById: [String: Stop], alertsByStop: [String: AlertAssociatedStop]) -> [RouteSourceData] {
stopsById: [String: Stop],
alertsByStop: [String: AlertAssociatedStop]) -> [RouteSourceData] {
routeData.routesWithSegmentedShapes
.map { generateRouteSource(routeId: $0.routeId,
routeShapes: $0.segmentedShapes,
Expand All @@ -64,8 +66,10 @@ class RouteSourceGenerator {
}

static func generateRouteSource(routeId: String, routeShapes: [SegmentedRouteShape],
stopsById: [String: Stop], alertsByStop: [String: AlertAssociatedStop]) -> RouteSourceData {
let routeLines = generateRouteLines(routeId: routeId, routeShapes: routeShapes, stopsById: stopsById, alertsByStop: alertsByStop)
stopsById: [String: Stop],
alertsByStop: [String: AlertAssociatedStop]) -> RouteSourceData {
let routeLines = generateRouteLines(routeId: routeId, routeShapes: routeShapes, stopsById: stopsById,
alertsByStop: alertsByStop)
let routeFeatures: [Feature] = routeLines.map { lineData in
var feature = Feature(geometry: lineData.line)
var featureProps = JSONObject()
Expand All @@ -79,15 +83,18 @@ class RouteSourceGenerator {
}

static func generateRouteLines(routeId _: String, routeShapes: [SegmentedRouteShape],
stopsById: [String: Stop], alertsByStop: [String: AlertAssociatedStop]) -> [RouteLineData] {
stopsById: [String: Stop],
alertsByStop: [String: AlertAssociatedStop]) -> [RouteLineData] {
routeShapes
.flatMap { routePatternShape in
routeShapeToLineData(routePatternShape: routePatternShape, stopsById: stopsById, alertsByStop: alertsByStop)
routeShapeToLineData(routePatternShape: routePatternShape, stopsById: stopsById,
alertsByStop: alertsByStop)
}
}

private static func routeShapeToLineData(routePatternShape: SegmentedRouteShape,
stopsById: [String: Stop], alertsByStop: [String: AlertAssociatedStop]) -> [RouteLineData] {
stopsById: [String: Stop],
alertsByStop: [String: AlertAssociatedStop]) -> [RouteLineData] {
guard let polyline = routePatternShape.shape.polyline,
let coordinates = Polyline(encodedPolyline: polyline).coordinates
else {
Expand Down
23 changes: 22 additions & 1 deletion iosApp/iosAppTests/Pages/Map/MapTestDataHelper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,27 @@ enum MapTestDataHelper {
stop.locationType = LocationType.station
}

static let stopPorter = objects.stop { stop in
stop.id = "place-porter"
stop.latitude = 42.3884
stop.longitude = -71.119149
stop.locationType = LocationType.station
}

static let stopHarvard = objects.stop { stop in
stop.id = "place-harsq"
stop.latitude = 42.373362
stop.longitude = -71.118956
stop.locationType = LocationType.station
}

static let stopCentral = objects.stop { stop in
stop.id = "place-cntsq"
stop.latitude = 42.365486
stop.longitude = -71.103802
stop.locationType = LocationType.station
}

static let stopAssembly = objects.stop { stop in
stop.id = "place-astao"
stop.latitude = 42.392811
Expand Down Expand Up @@ -126,7 +147,7 @@ enum MapTestDataHelper {
routeSegments: [RouteSegment(id: "segment2",
sourceRoutePatternId: patternRed30.id,
sourceRouteId: patternRed30.routeId,
stopIds: [stopAlewife.id, stopDavis.id],
stopIds: [stopPorter.id, stopHarvard.id, stopCentral.id],
otherPatternsByStopId: [:])],
shape: shapeRedC1),
]),
Expand Down
10 changes: 9 additions & 1 deletion iosApp/iosAppTests/Pages/Map/RouteLayerGeneratorTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,18 @@ final class RouteLayerGeneratorTests: XCTestCase {
MapTestDataHelper.routeOrange.id: MapTestDataHelper.routeOrange])
let routeLayers = routeLayerGenerator.routeLayers

XCTAssertEqual(routeLayers.count, 2)
// 2 layers per route - alerting & non-alerting
XCTAssertEqual(routeLayers.count, 4)
let redRouteLayer = routeLayers.first { $0.id == RouteLayerGenerator.getRouteLayerId(MapTestDataHelper.routeRed.id) }
XCTAssertNotNil(redRouteLayer)
guard let redRouteLayer else { return }
XCTAssertEqual(redRouteLayer.lineColor, .constant(StyleColor(.init(hex: MapTestDataHelper.routeRed.color))))

let alertingRedLayer = routeLayers.first { $0.id == RouteLayerGenerator
.getRouteLayerId("\(MapTestDataHelper.routeRed.id)-alerting")
}

XCTAssertNotNil(alertingRedLayer)
XCTAssertNotNil(alertingRedLayer!.lineDasharray)
}
}
86 changes: 85 additions & 1 deletion iosApp/iosAppTests/Pages/Map/RouteSourceGeneratorTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,12 @@ final class RouteSourceGeneratorTests: XCTestCase {
let routeSourceGenerator = RouteSourceGenerator(routeData: MapTestDataHelper.routeResponse,
stopsById: [MapTestDataHelper.stopAlewife.id: MapTestDataHelper.stopAlewife,
MapTestDataHelper.stopDavis.id: MapTestDataHelper.stopDavis,
MapTestDataHelper.stopPorter.id: MapTestDataHelper.stopPorter,
MapTestDataHelper.stopHarvard.id: MapTestDataHelper.stopHarvard,
MapTestDataHelper.stopCentral.id: MapTestDataHelper.stopCentral,
MapTestDataHelper.stopAssembly.id: MapTestDataHelper.stopAssembly,
MapTestDataHelper.stopSullivan.id: MapTestDataHelper.stopSullivan])
MapTestDataHelper.stopSullivan.id: MapTestDataHelper.stopSullivan],
alertsByStop: [:])

XCTAssertEqual(routeSourceGenerator.routeSources.count, 2)

Expand Down Expand Up @@ -52,4 +56,84 @@ final class RouteSourceGeneratorTests: XCTestCase {
XCTFail("Orange route source had no features")
}
}

func testAlertingSourcesCreated() {
let now = Date.now

let objects = ObjectCollectionBuilder()

let redAlert = objects.alert { alert in
alert.id = "a1"
alert.effect = .shuttle
alert.activePeriod(start: now.addingTimeInterval(-1).toKotlinInstant(), end: nil)
alert.informedEntity(activities: [.board], directionId: nil, facility: nil,
route: MapTestDataHelper.routeRed.id, routeType: .heavyRail,
stop: MapTestDataHelper.stopPorter.id, trip: nil)
alert.informedEntity(activities: [.board], directionId: nil, facility: nil,
route: MapTestDataHelper.routeRed.id, routeType: .heavyRail,
stop: MapTestDataHelper.stopHarvard.id, trip: nil)
}
let alertsByStop = [
MapTestDataHelper.stopPorter.id: AlertAssociatedStop(
stop: MapTestDataHelper.stopPorter,
relevantAlerts: [redAlert],
routePatterns: [MapTestDataHelper.patternRed30],
childStops: [:],
childAlerts: [:]
),
MapTestDataHelper.stopHarvard.id: AlertAssociatedStop(
stop: MapTestDataHelper.stopHarvard,
relevantAlerts: [redAlert],
routePatterns: [MapTestDataHelper.patternRed30],
childStops: [:],
childAlerts: [:]
),
]

let routeSourceGenerator = RouteSourceGenerator(routeData: MapTestDataHelper.routeResponse,
stopsById: [MapTestDataHelper.stopAlewife.id: MapTestDataHelper.stopAlewife,
MapTestDataHelper.stopDavis.id: MapTestDataHelper.stopDavis,
MapTestDataHelper.stopPorter.id: MapTestDataHelper.stopPorter,
MapTestDataHelper.stopHarvard.id: MapTestDataHelper.stopHarvard,
MapTestDataHelper.stopCentral.id: MapTestDataHelper.stopCentral],
alertsByStop: alertsByStop)

// RL & OL
XCTAssertEqual(routeSourceGenerator.routeSources.count, 2)

let redSource = routeSourceGenerator.routeSources.first
XCTAssertNotNil(redSource)
if case let .featureCollection(collection) = redSource!.data.unsafelyUnwrapped {
// Alewife - Davis (normal), Harvard - Porter (alerting), Porter - Central (normal)
XCTAssertEqual(collection.features.count, 3)
XCTAssertEqual(
collection.features[0].properties![RouteSourceGenerator.propIsAlertingKey]!, JSONValue(Bool(false))
)
XCTAssertEqual(
collection.features[0].geometry,
.lineString(LineString(Polyline(encodedPolyline: MapTestDataHelper.shapeRedC1.polyline!).coordinates!)
.sliced(from: MapTestDataHelper.stopAlewife.coordinate, to: MapTestDataHelper.stopDavis.coordinate)!)
)

XCTAssertEqual(
collection.features[1].properties![RouteSourceGenerator.propIsAlertingKey]!, JSONValue(Bool(true))
)
XCTAssertEqual(
collection.features[1].geometry,
.lineString(LineString(Polyline(encodedPolyline: MapTestDataHelper.shapeRedC1.polyline!).coordinates!)
.sliced(from: MapTestDataHelper.stopPorter.coordinate, to: MapTestDataHelper.stopHarvard.coordinate)!)
)
XCTAssertEqual(
collection.features[2].properties![RouteSourceGenerator.propIsAlertingKey]!, JSONValue(Bool(false))
)
XCTAssertEqual(
collection.features[2].geometry,
.lineString(LineString(Polyline(encodedPolyline: MapTestDataHelper.shapeRedC1.polyline!).coordinates!)
.sliced(from: MapTestDataHelper.stopHarvard.coordinate, to: MapTestDataHelper.stopCentral.coordinate)!)
)

} else {
XCTFail("Red route source had no features")
}
}
}
9 changes: 6 additions & 3 deletions iosApp/iosAppTests/Pages/Map/StopSourceGeneratorTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,8 @@ final class StopSourceGeneratorTests: XCTestCase {
XCTAssertNotNil(stopSource)
if case let .featureCollection(collection) = stopSource!.data.unsafelyUnwrapped {
XCTAssertEqual(collection.features.count, 2)
if case let .string(serviceStatus) = collection.features[0].properties![StopSourceGenerator.propServiceStatusKey] {
if case let .string(serviceStatus) = collection.features[0]
.properties![StopSourceGenerator.propServiceStatusKey] {
XCTAssertEqual(serviceStatus, String(describing: StopServiceStatus.normal))
} else {
XCTFail("Source status property was not set correctly")
Expand All @@ -105,8 +106,10 @@ final class StopSourceGeneratorTests: XCTestCase {
MapTestDataHelper.stopDavis.id: MapTestDataHelper.stopDavis,
]

let routeSourceGenerator = RouteSourceGenerator(routeData: MapTestDataHelper.routeResponse, stopsById: stops)
let stopSourceGenerator = StopSourceGenerator(stops: stops, routeSourceDetails: routeSourceGenerator.routeSourceDetails)
let routeSourceGenerator = RouteSourceGenerator(routeData: MapTestDataHelper.routeResponse, stopsById: stops,
alertsByStop: [:])
let stopSourceGenerator = StopSourceGenerator(stops: stops,
routeSourceDetails: routeSourceGenerator.routeSourceDetails)
let sources = stopSourceGenerator.stopSources
let snappedStopCoordinates = CLLocationCoordinate2D(latitude: 42.3961623851223, longitude: -71.14129664101432)

Expand Down

0 comments on commit 61ac3f0

Please sign in to comment.