Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,13 @@ package earth.maps.cardinal.data

data class BoundingBox(
val north: Double, val south: Double, val east: Double, val west: Double
)
) {
fun toGeoJsonBoundingBox(): io.github.dellisd.spatialk.geojson.BoundingBox {
return io.github.dellisd.spatialk.geojson.BoundingBox(
north = north,
south = south,
west = west,
east = east
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

package earth.maps.cardinal.data

import io.github.dellisd.spatialk.geojson.Position

data class Place(
val id: String? = null,
val name: String,
Expand All @@ -26,5 +28,9 @@ data class Place(
val isMyLocation: Boolean = false,
val isTransitStop: Boolean = false,
val transitStopId: String? = null,
)
) {
fun toPosition(): Position {
return Position(latitude = latLng.latitude, longitude = latLng.longitude)
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ data class FareTransfer(
)

@Serializable
data class Place(
data class TransitPlace(
val name: String,
val stopId: String? = null,
val lat: Double,
Expand All @@ -266,8 +266,8 @@ data class Place(
@Serializable
data class Leg(
val mode: Mode,
@SerialName("from") val fromPlace: Place,
@SerialName("to") val toPlace: Place,
@SerialName("from") val fromTransitPlace: TransitPlace,
@SerialName("to") val toTransitPlace: TransitPlace,
val duration: Int,
val startTime: String,
val endTime: String,
Expand All @@ -288,7 +288,7 @@ data class Leg(
val routeShortName: String? = null,
val cancelled: Boolean? = null,
val source: String? = null,
val intermediateStops: List<Place>? = null,
val intermediateStops: List<TransitPlace>? = null,
val legGeometry: EncodedPolyline? = null,
val steps: List<StepInstruction>? = null,
val rental: Rental? = null,
Expand All @@ -309,8 +309,8 @@ data class Itinerary(

@Serializable
data class PlanResponse(
val from: Place,
val to: Place,
val from: TransitPlace,
val to: TransitPlace,
val direct: List<Itinerary>,
val itineraries: List<Itinerary>,
val previousPageCursor: String,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -211,8 +211,8 @@ class TransitousService @Inject constructor(private val appPreferenceRepository:
Log.e(TAG, "Error fetching plan", e)
emit(
PlanResponse(
Place("", null, 0.0, 0.0, 0.0),
Place("", null, 0.0, 0.0, 0.0),
TransitPlace("", null, 0.0, 0.0, 0.0),
TransitPlace("", null, 0.0, 0.0, 0.0),
emptyList(),
emptyList(),
"",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ import earth.maps.cardinal.bottomsheet.rememberBottomSheetScaffoldState
import earth.maps.cardinal.bottomsheet.rememberBottomSheetState
import earth.maps.cardinal.data.AppPreferenceRepository
import earth.maps.cardinal.data.Place
import earth.maps.cardinal.data.PolylineUtils
import earth.maps.cardinal.data.RoutingMode
import earth.maps.cardinal.data.room.OfflineArea
import earth.maps.cardinal.routing.RouteRepository
Expand Down Expand Up @@ -130,6 +131,7 @@ import io.github.dellisd.spatialk.geojson.BoundingBox
import io.github.dellisd.spatialk.geojson.Position
import kotlinx.coroutines.launch
import org.maplibre.compose.camera.CameraPosition
import org.maplibre.compose.camera.CameraState
import org.maplibre.compose.camera.rememberCameraState
import uniffi.ferrostar.Route

Expand All @@ -149,7 +151,7 @@ fun AppContent(
routeRepository: RouteRepository,
appPreferenceRepository: AppPreferenceRepository,
) {
val mapPins = remember { mutableStateListOf<Position>() }
val mapPins = remember { mutableStateListOf<Place>() }
val cameraState = rememberCameraState()
var fabHeight by remember { mutableStateOf(0.dp) }
val coroutineScope = rememberCoroutineScope()
Expand Down Expand Up @@ -261,6 +263,7 @@ fun AppContent(
showToolbar = true
HomeScreenComposable(
viewModel = homeViewModel,
cameraState = cameraState,
mapPins = mapPins,
peekHeight = peekHeight,
navController = navController,
Expand All @@ -272,6 +275,9 @@ fun AppContent(
},
topOfBackStack = topOfBackStack,
backStackEntry = backStackEntry,
screenWidthDp = screenWidthDp,
screenHeightDp = screenHeightDp,
appPreferenceRepository = appPreferenceRepository
)
}

Expand Down Expand Up @@ -355,11 +361,9 @@ fun AppContent(
place?.let { place ->
viewModel.setPlace(place)
LaunchedEffect(place) {
mapPins.clear()
val position = Position(place.latLng.longitude, place.latLng.latitude)
// Clear any existing pins and add the new one to ensure only one pin is shown at a time
mapPins.clear()
mapPins.add(position)
mapPins.add(place)

val previousBackStackEntry = navController.previousBackStackEntry
val shouldFlyToPoi =
Expand All @@ -371,7 +375,10 @@ fun AppContent(
coroutineScope.launch {
cameraState.animateTo(
CameraPosition(
target = position, zoom = 15.0, padding = PaddingValues(
target = Position(
latitude = place.latLng.latitude,
longitude = place.latLng.longitude
), zoom = 15.0, padding = PaddingValues(
start = screenWidthDp / 8,
top = screenHeightDp / 8,
end = screenWidthDp / 8,
Expand Down Expand Up @@ -919,13 +926,17 @@ private fun CardinalToolbar(
@Composable
private fun HomeScreenComposable(
viewModel: HomeViewModel,
mapPins: SnapshotStateList<Position>,
cameraState: CameraState,
mapPins: SnapshotStateList<Place>,
peekHeight: Dp,
navController: NavHostController,
onPeekHeightChange: (Dp) -> Unit,
onFabHeightChange: (Dp) -> Unit,
topOfBackStack: NavBackStackEntry?,
backStackEntry: NavBackStackEntry,
screenWidthDp: Dp,
screenHeightDp: Dp,
appPreferenceRepository: AppPreferenceRepository,
) {
val coroutineScope = rememberCoroutineScope()
val searchExpanded: Boolean? by viewModel.searchExpanded.collectAsState(null)
Expand Down Expand Up @@ -994,6 +1005,31 @@ private fun HomeScreenComposable(
viewModel.expandSearch()
}
},
onResultPinsChange = {
mapPins.clear()
mapPins.addAll(it)
},
onSearchEvent = {
viewModel.collapseSearch()
coroutineScope.launch {
val boundingBox =
PolylineUtils.calculateBoundingBox(mapPins.map { it.toPosition() })
?: return@launch
cameraState.animateTo(
boundingBox = boundingBox.toGeoJsonBoundingBox(),
padding = PaddingValues(
start = screenWidthDp / 8,
top = screenHeightDp / 8,
end = screenWidthDp / 8,
bottom = min(
3f * screenHeightDp / 4,
peekHeight + screenHeightDp / 8
)
),
duration = appPreferenceRepository.animationSpeedDurationValue
)
}
}
)
})
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@ import earth.maps.cardinal.ui.map.LocationPuck
import io.github.dellisd.spatialk.geojson.Feature
import io.github.dellisd.spatialk.geojson.FeatureCollection
import io.github.dellisd.spatialk.geojson.LineString
import io.github.dellisd.spatialk.geojson.Point
import io.github.dellisd.spatialk.geojson.Polygon
import io.github.dellisd.spatialk.geojson.Position
import kotlinx.coroutines.launch
Expand Down Expand Up @@ -98,7 +97,7 @@ fun MapView(
onDropPin: (LatLng) -> Unit,
onRequestLocationPermission: () -> Unit,
hasLocationPermission: Boolean,
mapPins: List<Position>,
mapPins: List<Place>,
fabInsets: PaddingValues,
cameraState: CameraState,
appPreferences: AppPreferenceRepository,
Expand All @@ -108,7 +107,7 @@ fun MapView(
) {
val context = LocalContext.current
val styleState = rememberStyleState()
val pinFeatures = mapPins.map { Feature(geometry = Point(it)) }
val pinFeatures = mapPins.map { mapViewModel.createFeatureFromPlace(it) }
rememberCoroutineScope()

val styleVariant = if (isSystemInDarkTheme()) "dark" else "light"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,17 +96,46 @@ class MapViewModel @Inject constructor(
var screenWidth: Dp = 0.dp

val savedPlacesFlow: Flow<FeatureCollection> = placeDao.getAllPlacesAsFlow().map { placeList ->
FeatureCollection(placeList.map { createFeatureFromPlace(it) })
FeatureCollection(placeList.map { createFeatureFromSavedPlace(it) })
}

init {
locationRepository.startContinuousLocationUpdates(context)
}

/**
* Creates a Feature from a Place with proper JSON escaping.
*/
fun createFeatureFromPlace(place: Place): Feature {
val properties = mutableMapOf(
"name" to escapeJsonString(place.name),
"description" to escapeJsonString(place.description)
)

place.address?.houseNumber?.let { properties["addr:housenumber"] = escapeJsonString(it) }
place.address?.road?.let { properties["addr:street"] = escapeJsonString(it) }
place.address?.city?.let { properties["addr:city"] = escapeJsonString(it) }
place.address?.postcode?.let { properties["addr:postcode"] = escapeJsonString(it) }
place.address?.state?.let { properties["addr:state"] = escapeJsonString(it) }
place.address?.country?.let { properties["addr:country"] = escapeJsonString(it) }
place.address?.countryCode?.let { properties["country_code"] = escapeJsonString(it) }
place.transitStopId?.let { properties["transit_stop_id"] = escapeJsonString(it) }

return Feature(
geometry = Point(
Position(
latitude = place.latLng.latitude,
longitude = place.latLng.longitude
)
),
properties = properties
)
}

/**
* Creates a Feature from a SavedPlace with proper JSON escaping.
*/
private fun createFeatureFromPlace(place: SavedPlace): Feature {
private fun createFeatureFromSavedPlace(place: SavedPlace): Feature {
val name = place.customName ?: place.name
val description = place.customDescription ?: place.type

Expand Down Expand Up @@ -179,7 +208,14 @@ class MapViewModel @Inject constructor(
) {
val features = cameraState.projection?.queryRenderedFeatures(
dpOffset,
layerIds = setOf("user_favorites", "poi_z14", "poi_z15", "poi_z16", "poi_transit")
layerIds = setOf(
"map_pins",
"user_favorites",
"poi_z14",
"poi_z15",
"poi_z16",
"poi_transit"
)
)
Log.d(TAG, "${features?.count()} features available at tap location")
val filteredFeatures = features?.filter {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -285,11 +285,11 @@ private fun DetailedLegCard(
color = MaterialTheme.colorScheme.onSurfaceVariant
)
Text(
text = leg.fromPlace.name,
text = leg.fromTransitPlace.name,
style = MaterialTheme.typography.bodyMedium,
fontWeight = FontWeight.Medium
)
leg.fromPlace.departure?.let { departure ->
leg.fromTransitPlace.departure?.let { departure ->
Text(
text = "Depart: ${departure.formatTime(use24HourFormat)}",
style = MaterialTheme.typography.bodySmall,
Expand Down Expand Up @@ -317,11 +317,11 @@ private fun DetailedLegCard(
color = MaterialTheme.colorScheme.onSurfaceVariant
)
Text(
text = leg.toPlace.name,
text = leg.toTransitPlace.name,
style = MaterialTheme.typography.bodyMedium,
fontWeight = FontWeight.Medium
)
leg.toPlace.arrival?.let { arrival ->
leg.toTransitPlace.arrival?.let { arrival ->
Text(
text = "Arrive: ${arrival.formatTime(use24HourFormat)}",
style = MaterialTheme.typography.bodySmall,
Expand Down
Loading
Loading