Skip to content

Commit

Permalink
Display route lines on map when stop selected
Browse files Browse the repository at this point in the history
  • Loading branch information
dellisd committed Jan 29, 2024
1 parent 663f29e commit 9dc093d
Show file tree
Hide file tree
Showing 11 changed files with 127 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import kotlin.time.Duration.Companion.minutes
class OcTranspoClientTest {
private val config = LoadedServerConfig(
dataPath = Path(""),
ocTranspo = OcTranspoCredentials("", "")
ocTranspo = OcTranspoCredentials("", ""),
)

private fun makeClient(engine: MockEngine): OcTranspoClient = OcTranspoClient(config, HttpClient(engine))
Expand All @@ -35,7 +35,7 @@ class OcTranspoClientTest {
respond(
DEFAULT,
status = HttpStatusCode.OK,
headers = headersOf(HttpHeaders.ContentType, "text/html")
headers = headersOf(HttpHeaders.ContentType, "text/html"),
)
}

Expand Down Expand Up @@ -119,4 +119,4 @@ class OcTranspoClientTest {
</soap:Envelope>
""".trimIndent()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,6 @@ CREATE TABLE RouteVariant (

getAll:
SELECT * FROM RouteVariant;

getByIds:
SELECT * FROM RouteVariant WHERE id IN ?;
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,10 @@ CREATE TABLE RouteVariantAtStop (
FOREIGN KEY (stopId) REFERENCES Stop(id),
FOREIGN KEY (routeVariantId) REFERENCES RouteVariant(id)
);

getVariantsByStopCode:
SELECT RouteVariantAtStop.* FROM Stop
JOIN RouteVariantAtStop
ON Stop.id = RouteVariantAtStop.stopId
WHERE Stop.code = ?
ORDER BY RouteVariantAtStop.routeVariantId;
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package ca.derekellis.reroute.data

import ca.derekellis.reroute.db.GetRoutesByStopCode
import ca.derekellis.reroute.db.RouteVariantAtStop
import ca.derekellis.reroute.models.Stop
import kotlinx.coroutines.flow.Flow

Expand All @@ -14,4 +15,6 @@ interface DataSource {
fun getStops(): Flow<List<Stop>>

fun getRoutesAtStop(code: String): Flow<List<GetRoutesByStopCode>>

fun getRouteVariantsAtStop(code: String): Flow<List<RouteVariantAtStop>>
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import app.cash.sqldelight.coroutines.mapToList
import ca.derekellis.reroute.db.DatabaseHelper
import ca.derekellis.reroute.db.GetRoutesByStopCode
import ca.derekellis.reroute.db.RerouteDatabase
import ca.derekellis.reroute.db.RouteVariantAtStop
import ca.derekellis.reroute.di.AppScope
import ca.derekellis.reroute.models.Route
import ca.derekellis.reroute.models.Stop
Expand Down Expand Up @@ -51,6 +52,13 @@ class SqlJsDataSource(private val withDatabase: DatabaseHelper) : DataSource {
.mapToList(Dispatchers.Main)
}

override fun getRouteVariantsAtStop(code: String): Flow<List<RouteVariantAtStop>> = withDatabaseFlowFlatten { database ->
database.routeVariantAtStopQueries
.getVariantsByStopCode(code)
.asFlow()
.mapToList(Dispatchers.Main)
}

private fun <T> withDatabaseFlow(block: suspend (database: RerouteDatabase) -> T): Flow<T> =
flow { withDatabase { emit(block(it)) } }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ class DatabaseHelper(private val worker: Worker, private val client: RerouteClie
MetadataAdapter = Metadata.Adapter(DateTimeAdapter),
RouteAdapter = Route.Adapter(StringListAdapter(Json)),
RouteVariantAdapter = RouteVariant.Adapter(IntColumnAdapter, IntColumnAdapter, LineStringAdapter),
RouteVariantAtStopAdapter = RouteVariantAtStop.Adapter(IntColumnAdapter),
)

private var initialized by atomic(false)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ package ca.derekellis.reroute.map
import ca.derekellis.reroute.di.AppScope
import ca.derekellis.reroute.models.Stop
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow
import me.tatarka.inject.annotations.Inject

@AppScope
Expand All @@ -12,7 +14,18 @@ class MapInteractionsManager {
private val _targetStop = MutableSharedFlow<Stop?>()
val targetStop = _targetStop.asSharedFlow()

private val _routeVariants = MutableStateFlow<Set<String>>(emptySet())
val routeVariants = _routeVariants.asStateFlow()

suspend fun goTo(stop: Stop?) {
_targetStop.emit(stop)
}

fun showRouteVariant(vararg variantIds: String) {
_routeVariants.value += variantIds
}

fun clearRoutes() {
_routeVariants.value = emptySet()
}
}
55 changes: 52 additions & 3 deletions web/src/jsMain/kotlin/ca/derekellis/reroute/map/MapPresenter.kt
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
package ca.derekellis.reroute.map

import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import app.cash.sqldelight.coroutines.asFlow
import app.cash.sqldelight.coroutines.mapToList
import ca.derekellis.reroute.data.DataSource
import ca.derekellis.reroute.db.DatabaseHelper
import ca.derekellis.reroute.ui.CollectEffect
import ca.derekellis.reroute.ui.Navigator
import ca.derekellis.reroute.ui.Presenter
import io.github.dellisd.spatialk.geojson.Feature
import io.github.dellisd.spatialk.geojson.dsl.feature
import kotlinx.coroutines.flow.Flow
import me.tatarka.inject.annotations.Inject
import ca.derekellis.reroute.stops.Stop as StopScreen
Expand All @@ -16,9 +25,9 @@ class MapPresenter(
private val dataSource: DataSource,
private val interactionsManager: MapInteractionsManager,
private val navigator: Navigator,
private val withDatabase: DatabaseHelper,
private val args: Map,
) :
Presenter<MapViewModel, MapViewEvent> {
) : Presenter<MapViewModel, MapViewEvent> {
@Composable
override fun produceModel(events: Flow<MapViewEvent>): MapViewModel {
CollectEffect(events) { event ->
Expand All @@ -27,8 +36,48 @@ class MapPresenter(
}
}

var routeFeatures by remember { mutableStateOf<Set<Feature>>(emptySet()) }
val displayedRoutes by interactionsManager.routeVariants.collectAsState()
LaunchedEffect(displayedRoutes) {
withDatabase { database ->
if (displayedRoutes.isEmpty()) {
routeFeatures = emptySet()
} else {
database.routeVariantQueries.getByIds(displayedRoutes).asFlow().mapToList(coroutineContext)
.collect { routes ->
routeFeatures = routes.mapIndexedTo(mutableSetOf()) { index, route ->
feature(route.shape) {
put("id", route.id)
put("gtfsId", route.gtfsId)
put("color", routeColor(route.gtfsId.split("-").first()))
}
}
}
}
}
}

val targetStop by interactionsManager.targetStop.collectAsState(null)

return MapViewModel(targetStop)
return MapViewModel(targetStop, routeFeatures)
}

// TODO: Extract route colours into dataset
private fun routeColor(identifier: String) = when (identifier) {
"E1", "1" -> COLOR_CONFEDERATION
"2" -> COLOR_TRILLIUM
"6", "7", "10", "11", "12", "14", "25", "40", "44", "51", "53", "80", "85", "87", "88", "90", "111" -> COLOR_FREQUENT
"39", "45", "57", "61", "62", "63", "74", "75", "97", "98", "99" -> COLOR_RAPID
"221", "222", "228", "231", "232", "234", "236", "237", "252", "256", "257", "258", "261", "262", "263", "264", "265", "267", "268", "270", "271", "272", "273", "277", "278", "282", "283", "284", "290", "291", "294", "299" -> COLOR_CONNEXION
else -> COLOR_LOCAL
}

companion object {
private const val COLOR_LOCAL = "#404040"
private const val COLOR_FREQUENT = "#f26532"
private const val COLOR_RAPID = "#1a559b"
private const val COLOR_CONNEXION = "#9b5ba4"
private const val COLOR_CONFEDERATION = "#d62e3b"
private const val COLOR_TRILLIUM = "#76bf43"
}
}
22 changes: 20 additions & 2 deletions web/src/jsMain/kotlin/ca/derekellis/reroute/map/MapView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,20 @@ package ca.derekellis.reroute.map

import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
import ca.derekellis.mapbox.LngLat
import ca.derekellis.mapbox.MapboxState
import ca.derekellis.mapbox.rememberMapboxState
import ca.derekellis.mapbox.style.InterpolationType.Companion.exponential
import ca.derekellis.mapbox.style.expression
import ca.derekellis.mapbox.style.get
import ca.derekellis.mapbox.style.interpolate
import ca.derekellis.reroute.RerouteConfig
import ca.derekellis.reroute.ui.View
import ca.derekellis.reroute.utils.jsObject
import geojson.Feature
import geojson.GeoJsonObject
import io.github.dellisd.spatialk.geojson.FeatureCollection
import me.tatarka.inject.annotations.Inject
import org.jetbrains.compose.web.css.height
import org.jetbrains.compose.web.css.hsl
Expand Down Expand Up @@ -43,12 +47,16 @@ class MapView : View<MapViewModel, MapViewEvent> {
}
}

MapContent(mapState, onEvent = emit)
MapContent(mapState, onEvent = emit, model.routeFeatures)
}
}

@Composable
private fun MapContent(mapState: MapboxState, onEvent: (MapViewEvent) -> Unit) {
private fun MapContent(
mapState: MapboxState,
onEvent: (MapViewEvent) -> Unit,
routeFeatures: Set<io.github.dellisd.spatialk.geojson.Feature>,
) {
Div {
ca.derekellis.mapbox.MapboxMap(
accessToken = RerouteConfig.MAPBOX_ACCESS_KEY,
Expand Down Expand Up @@ -90,6 +98,16 @@ private fun MapContent(mapState: MapboxState, onEvent: (MapViewEvent) -> Unit) {
)
}
}

val routeGeojson = remember(routeFeatures) {
JSON.parse<GeoJsonObject>(FeatureCollection(routeFeatures.toList()).json())
}
GeoJsonSource("routes", routeGeojson) {
LineLayer("route-lines") {
lineWidth(5.0)
lineColor(get("color"))
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package ca.derekellis.reroute.map

import ca.derekellis.reroute.models.Stop
import io.github.dellisd.spatialk.geojson.Feature

data class MapViewModel(
val targetStop: Stop?,
val routeFeatures: Set<Feature>,
)
15 changes: 15 additions & 0 deletions web/src/jsMain/kotlin/ca/derekellis/reroute/stops/StopPresenter.kt
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package ca.derekellis.reroute.stops

import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import ca.derekellis.reroute.data.DataSource
import ca.derekellis.reroute.data.RerouteClient
import ca.derekellis.reroute.home.Home
Expand All @@ -14,6 +16,7 @@ import ca.derekellis.reroute.ui.CollectEffect
import ca.derekellis.reroute.ui.Navigator
import ca.derekellis.reroute.ui.Presenter
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import me.tatarka.inject.annotations.Inject

Expand All @@ -27,6 +30,7 @@ class StopPresenter(
) : Presenter<StopViewModel, StopViewEvent> {
@Composable
override fun produceModel(events: Flow<StopViewEvent>): StopViewModel {
val scope = rememberCoroutineScope()
CollectEffect(events) { event ->
when (event) {
Close -> {
Expand Down Expand Up @@ -63,6 +67,17 @@ class StopPresenter(
mapInteractionsManager.goTo(stop)
}

DisposableEffect(stop) {
scope.launch {
val ids = dataSource.getRouteVariantsAtStop(stop.code).first().map { it.routeVariantId }
mapInteractionsManager.showRouteVariant(*ids.toTypedArray())
}

onDispose {
mapInteractionsManager.clearRoutes()
}
}

return StopViewModel.Loaded(stop, realtimeData?.let { zipRealtimeData(it, routeSections) } ?: routeSections)
}

Expand Down

0 comments on commit 9dc093d

Please sign in to comment.