Loading cardinal-android/app/src/main/java/earth/maps/cardinal/geocoding/GeocodingService.kt +31 −26 Original line number Diff line number Diff line Loading @@ -21,10 +21,8 @@ package earth.maps.cardinal.geocoding import earth.maps.cardinal.data.GeocodeResult import earth.maps.cardinal.data.GeocodeResult.Companion.generatePlaceId import earth.maps.cardinal.data.LatLng import earth.maps.cardinal.data.Place import earth.maps.cardinal.data.LocationRepository import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map import earth.maps.cardinal.data.Place abstract class GeocodingService(private val locationRepository: LocationRepository) { Loading @@ -32,9 +30,9 @@ abstract class GeocodingService(private val locationRepository: LocationReposito * Geocode a query string to find matching locations, returning Place objects. * @param query The search query (e.g., address, place name) * @param focusPoint Optional focus point for viewport biasing * @return Flow of Place objects * @return Place objects */ suspend fun geocode(query: String, focusPoint: LatLng? = null): Flow<List<Place>> { suspend fun geocode(query: String, focusPoint: LatLng? = null): List<Place> { return convertResultsToPlaces(geocodeRaw(query, focusPoint)) } Loading @@ -42,9 +40,9 @@ abstract class GeocodingService(private val locationRepository: LocationReposito * Reverse geocode coordinates to find address information, returning Place objects. * @param latitude The latitude coordinate * @param longitude The longitude coordinate * @return Flow of Place objects * @return Place objects */ suspend fun reverseGeocode(latitude: Double, longitude: Double): Flow<List<Place>> { suspend fun reverseGeocode(latitude: Double, longitude: Double): List<Place> { return convertResultsToPlaces(reverseGeocodeRaw(latitude, longitude)) } Loading @@ -52,50 +50,57 @@ abstract class GeocodingService(private val locationRepository: LocationReposito * Find nearby places around a given point, returning Place objects. * @param latitude The latitude coordinate * @param longitude The longitude coordinate * @return Flow of Place objects * @return Place objects */ suspend fun nearby(latitude: Double, longitude: Double): Flow<List<Place>> { return convertResultsToPlaces(nearbyRaw(latitude, longitude)) suspend fun nearby(latitude: Double, longitude: Double, selectedCategories: List<String>): List<Place> { return convertResultsToPlaces(nearbyRaw(latitude, longitude, selectedCategories)) } /** * Converts a Flow of GeocodeResult to a Flow of Place, including deduplication. * @param resultsFlow The Flow of GeocodeResult to convert. * @return Flow of Place objects. * Converts a list of GeocodeResult to a list of Place, including deduplication. * @param results The GeocodeResults to convert. * @return Place objects. */ protected open suspend fun convertResultsToPlaces(resultsFlow: Flow<List<GeocodeResult>>): Flow<List<Place>> { return resultsFlow.map { results -> // Deduplicate based on GeocodeResult before converting to Place protected open suspend fun convertResultsToPlaces(results: List<GeocodeResult>): List<Place> { val deduplicatedResults = deduplicateSearchResults(results) deduplicatedResults.map { geocodeResult -> return deduplicatedResults.map { geocodeResult -> locationRepository.createSearchResultPlace(geocodeResult) } } } /** * Geocode a query string to find matching locations, returning raw GeocodeResult objects. * @param query The search query (e.g., address, place name) * @param focusPoint Optional focus point for viewport biasing * @return Flow of raw geocoding results * @return Raw geocoding results */ abstract suspend fun geocodeRaw(query: String, focusPoint: LatLng? = null): Flow<List<GeocodeResult>> abstract suspend fun geocodeRaw( query: String, focusPoint: LatLng? = null ): List<GeocodeResult> /** * Reverse geocode coordinates to find address information, returning raw GeocodeResult objects. * @param latitude The latitude coordinate * @param longitude The longitude coordinate * @return Flow of raw geocoding results * @return Raw geocoding results */ abstract suspend fun reverseGeocodeRaw(latitude: Double, longitude: Double): Flow<List<GeocodeResult>> abstract suspend fun reverseGeocodeRaw( latitude: Double, longitude: Double ): List<GeocodeResult> /** * Find nearby places around a given point, returning raw GeocodeResult objects. * @param latitude The latitude coordinate * @param longitude The longitude coordinate * @return Flow of raw nearby places * @return Raw nearby places */ abstract suspend fun nearbyRaw(latitude: Double, longitude: Double): Flow<List<GeocodeResult>> abstract suspend fun nearbyRaw( latitude: Double, longitude: Double, selectedCategories: List<String> ): List<GeocodeResult> } fun deduplicateSearchResults(results: List<GeocodeResult>): List<GeocodeResult> { Loading cardinal-android/app/src/main/java/earth/maps/cardinal/geocoding/MultiplexedGeocodingService.kt +9 −6 Original line number Diff line number Diff line Loading @@ -22,7 +22,6 @@ import earth.maps.cardinal.data.AppPreferenceRepository import earth.maps.cardinal.data.GeocodeResult import earth.maps.cardinal.data.LatLng import earth.maps.cardinal.data.LocationRepository import kotlinx.coroutines.flow.Flow class MultiplexedGeocodingService( private val appPreferenceRepository: AppPreferenceRepository, Loading @@ -31,7 +30,7 @@ class MultiplexedGeocodingService( locationRepository: LocationRepository, ) : GeocodingService(locationRepository) { override suspend fun geocodeRaw(query: String, focusPoint: LatLng?): Flow<List<GeocodeResult>> { override suspend fun geocodeRaw(query: String, focusPoint: LatLng?): List<GeocodeResult> { return if (appPreferenceRepository.offlineMode.value) { offlineGeocodingService.geocodeRaw(query, focusPoint) } else { Loading @@ -42,7 +41,7 @@ class MultiplexedGeocodingService( override suspend fun reverseGeocodeRaw( latitude: Double, longitude: Double ): Flow<List<GeocodeResult>> { ): List<GeocodeResult> { return if (appPreferenceRepository.offlineMode.value) { offlineGeocodingService.reverseGeocodeRaw(latitude, longitude) } else { Loading @@ -50,11 +49,15 @@ class MultiplexedGeocodingService( } } override suspend fun nearbyRaw(latitude: Double, longitude: Double): Flow<List<GeocodeResult>> { override suspend fun nearbyRaw( latitude: Double, longitude: Double, selectedCategories: List<String> ): List<GeocodeResult> { return if (appPreferenceRepository.offlineMode.value) { offlineGeocodingService.nearbyRaw(latitude, longitude) offlineGeocodingService.nearbyRaw(latitude, longitude, selectedCategories) } else { onlineGeocodingService.nearbyRaw(latitude, longitude) onlineGeocodingService.nearbyRaw(latitude, longitude, selectedCategories) } } } cardinal-android/app/src/main/java/earth/maps/cardinal/geocoding/OfflineGeocodingService.kt +23 −23 Original line number Diff line number Diff line Loading @@ -25,8 +25,6 @@ import earth.maps.cardinal.data.Address import earth.maps.cardinal.data.GeocodeResult import earth.maps.cardinal.data.LatLng import earth.maps.cardinal.data.LocationRepository import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow import uniffi.cardinal_geocoder.newAirmailIndex import java.io.File Loading @@ -37,8 +35,7 @@ class OfflineGeocodingService( private val geocoderDir = File(context.filesDir, "geocoder").apply { mkdirs() } private val airmailIndex = newAirmailIndex("en", geocoderDir.absolutePath) override suspend fun geocodeRaw(query: String, focusPoint: LatLng?): Flow<List<GeocodeResult>> = flow { override suspend fun geocodeRaw(query: String, focusPoint: LatLng?): List<GeocodeResult> { try { val results = airmailIndex.searchPhrase(query) val geocodeResults = results.map { poi -> Loading @@ -48,24 +45,27 @@ class OfflineGeocodingService( } buildResult(tagMap, poi.lat, poi.lng) } emit(geocodeResults) return geocodeResults } catch (e: Exception) { Log.e(TAG, "Geocode failed with exception", e) // If there's an error, return empty list emit(emptyList()) return emptyList() } } override suspend fun reverseGeocodeRaw( latitude: Double, longitude: Double ): Flow<List<GeocodeResult>> = flow { emit(emptyList()) ): List<GeocodeResult> { return emptyList() } override suspend fun nearbyRaw(latitude: Double, longitude: Double): Flow<List<GeocodeResult>> = flow { emit(emptyList()) override suspend fun nearbyRaw( latitude: Double, longitude: Double, selectedCategories: List<String> ): List<GeocodeResult> { return emptyList() } override suspend fun beginTileProcessing() { Loading cardinal-android/app/src/main/java/earth/maps/cardinal/geocoding/PeliasGeocodingService.kt +58 −56 Original line number Diff line number Diff line Loading @@ -32,8 +32,6 @@ import io.ktor.client.plugins.logging.Logging import io.ktor.client.request.get import io.ktor.client.request.parameter import io.ktor.serialization.kotlinx.json.json import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonArray import kotlinx.serialization.json.JsonElement Loading @@ -60,8 +58,7 @@ class PeliasGeocodingService( install(Logging) } override suspend fun geocodeRaw(query: String, focusPoint: LatLng?): Flow<List<GeocodeResult>> = flow { override suspend fun geocodeRaw(query: String, focusPoint: LatLng?): List<GeocodeResult> { try { Log.d(TAG, "Geocoding query: $query, focusPoint: $focusPoint") val config = appPreferenceRepository.peliasApiConfig.value Loading @@ -85,17 +82,17 @@ class PeliasGeocodingService( } Log.d(TAG, "Parsed results: ${geocodeResults.size}") emit(geocodeResults) return geocodeResults } catch (e: Exception) { Log.e(TAG, "Error during geocoding", e) emit(emptyList()) return emptyList() } } override suspend fun reverseGeocodeRaw( latitude: Double, longitude: Double ): Flow<List<GeocodeResult>> = flow { ): List<GeocodeResult> { try { Log.d(TAG, "Reverse geocoding: $latitude, $longitude") val config = appPreferenceRepository.peliasApiConfig.value Loading @@ -116,15 +113,17 @@ class PeliasGeocodingService( } Log.d(TAG, "Parsed reverse results: ${geocodeResults.size}") emit(geocodeResults) return geocodeResults } catch (e: Exception) { Log.e(TAG, "Error during reverse geocoding", e) emit(emptyList()) return emptyList() } } override suspend fun nearbyRaw(latitude: Double, longitude: Double): Flow<List<GeocodeResult>> = flow { override suspend fun nearbyRaw( latitude: Double, longitude: Double, selectedCategories: List<String> ): List<GeocodeResult> { try { Log.d(TAG, "Nearby: $latitude, $longitude") val config = appPreferenceRepository.peliasApiConfig.value Loading @@ -133,6 +132,9 @@ class PeliasGeocodingService( parameter("point.lon", longitude.toString()) parameter("size", "50") parameter("layers", "venue") if (selectedCategories.isNotEmpty()) { parameter("categories", selectedCategories.joinToString(",")) } config.apiKey?.let { parameter("api_key", it) } } Loading @@ -146,10 +148,10 @@ class PeliasGeocodingService( } Log.d(TAG, "Parsed nearby results: ${geocodeResults.size}") emit(geocodeResults) return geocodeResults } catch (e: Exception) { Log.e(TAG, "Error during nearby", e) emit(emptyList()) return emptyList() } } Loading cardinal-android/app/src/main/java/earth/maps/cardinal/ui/directions/DirectionsViewModel.kt +3 −4 Original line number Diff line number Diff line Loading @@ -459,10 +459,9 @@ class DirectionsViewModel @Inject constructor( // Use fromPlace as focus point for viewport biasing if available, // otherwise fall back to current viewport center val focusPoint = fromPlace?.latLng ?: viewportRepository.viewportCenter.value geocodingService.geocode(query, focusPoint).collect { results -> geocodeResults.value = results geocodeResults.value = geocodingService.geocode(query, focusPoint) isSearching = false } } catch (e: Exception) { // Handle error searchError = e.message ?: "An error occurred during search" Loading Loading
cardinal-android/app/src/main/java/earth/maps/cardinal/geocoding/GeocodingService.kt +31 −26 Original line number Diff line number Diff line Loading @@ -21,10 +21,8 @@ package earth.maps.cardinal.geocoding import earth.maps.cardinal.data.GeocodeResult import earth.maps.cardinal.data.GeocodeResult.Companion.generatePlaceId import earth.maps.cardinal.data.LatLng import earth.maps.cardinal.data.Place import earth.maps.cardinal.data.LocationRepository import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map import earth.maps.cardinal.data.Place abstract class GeocodingService(private val locationRepository: LocationRepository) { Loading @@ -32,9 +30,9 @@ abstract class GeocodingService(private val locationRepository: LocationReposito * Geocode a query string to find matching locations, returning Place objects. * @param query The search query (e.g., address, place name) * @param focusPoint Optional focus point for viewport biasing * @return Flow of Place objects * @return Place objects */ suspend fun geocode(query: String, focusPoint: LatLng? = null): Flow<List<Place>> { suspend fun geocode(query: String, focusPoint: LatLng? = null): List<Place> { return convertResultsToPlaces(geocodeRaw(query, focusPoint)) } Loading @@ -42,9 +40,9 @@ abstract class GeocodingService(private val locationRepository: LocationReposito * Reverse geocode coordinates to find address information, returning Place objects. * @param latitude The latitude coordinate * @param longitude The longitude coordinate * @return Flow of Place objects * @return Place objects */ suspend fun reverseGeocode(latitude: Double, longitude: Double): Flow<List<Place>> { suspend fun reverseGeocode(latitude: Double, longitude: Double): List<Place> { return convertResultsToPlaces(reverseGeocodeRaw(latitude, longitude)) } Loading @@ -52,50 +50,57 @@ abstract class GeocodingService(private val locationRepository: LocationReposito * Find nearby places around a given point, returning Place objects. * @param latitude The latitude coordinate * @param longitude The longitude coordinate * @return Flow of Place objects * @return Place objects */ suspend fun nearby(latitude: Double, longitude: Double): Flow<List<Place>> { return convertResultsToPlaces(nearbyRaw(latitude, longitude)) suspend fun nearby(latitude: Double, longitude: Double, selectedCategories: List<String>): List<Place> { return convertResultsToPlaces(nearbyRaw(latitude, longitude, selectedCategories)) } /** * Converts a Flow of GeocodeResult to a Flow of Place, including deduplication. * @param resultsFlow The Flow of GeocodeResult to convert. * @return Flow of Place objects. * Converts a list of GeocodeResult to a list of Place, including deduplication. * @param results The GeocodeResults to convert. * @return Place objects. */ protected open suspend fun convertResultsToPlaces(resultsFlow: Flow<List<GeocodeResult>>): Flow<List<Place>> { return resultsFlow.map { results -> // Deduplicate based on GeocodeResult before converting to Place protected open suspend fun convertResultsToPlaces(results: List<GeocodeResult>): List<Place> { val deduplicatedResults = deduplicateSearchResults(results) deduplicatedResults.map { geocodeResult -> return deduplicatedResults.map { geocodeResult -> locationRepository.createSearchResultPlace(geocodeResult) } } } /** * Geocode a query string to find matching locations, returning raw GeocodeResult objects. * @param query The search query (e.g., address, place name) * @param focusPoint Optional focus point for viewport biasing * @return Flow of raw geocoding results * @return Raw geocoding results */ abstract suspend fun geocodeRaw(query: String, focusPoint: LatLng? = null): Flow<List<GeocodeResult>> abstract suspend fun geocodeRaw( query: String, focusPoint: LatLng? = null ): List<GeocodeResult> /** * Reverse geocode coordinates to find address information, returning raw GeocodeResult objects. * @param latitude The latitude coordinate * @param longitude The longitude coordinate * @return Flow of raw geocoding results * @return Raw geocoding results */ abstract suspend fun reverseGeocodeRaw(latitude: Double, longitude: Double): Flow<List<GeocodeResult>> abstract suspend fun reverseGeocodeRaw( latitude: Double, longitude: Double ): List<GeocodeResult> /** * Find nearby places around a given point, returning raw GeocodeResult objects. * @param latitude The latitude coordinate * @param longitude The longitude coordinate * @return Flow of raw nearby places * @return Raw nearby places */ abstract suspend fun nearbyRaw(latitude: Double, longitude: Double): Flow<List<GeocodeResult>> abstract suspend fun nearbyRaw( latitude: Double, longitude: Double, selectedCategories: List<String> ): List<GeocodeResult> } fun deduplicateSearchResults(results: List<GeocodeResult>): List<GeocodeResult> { Loading
cardinal-android/app/src/main/java/earth/maps/cardinal/geocoding/MultiplexedGeocodingService.kt +9 −6 Original line number Diff line number Diff line Loading @@ -22,7 +22,6 @@ import earth.maps.cardinal.data.AppPreferenceRepository import earth.maps.cardinal.data.GeocodeResult import earth.maps.cardinal.data.LatLng import earth.maps.cardinal.data.LocationRepository import kotlinx.coroutines.flow.Flow class MultiplexedGeocodingService( private val appPreferenceRepository: AppPreferenceRepository, Loading @@ -31,7 +30,7 @@ class MultiplexedGeocodingService( locationRepository: LocationRepository, ) : GeocodingService(locationRepository) { override suspend fun geocodeRaw(query: String, focusPoint: LatLng?): Flow<List<GeocodeResult>> { override suspend fun geocodeRaw(query: String, focusPoint: LatLng?): List<GeocodeResult> { return if (appPreferenceRepository.offlineMode.value) { offlineGeocodingService.geocodeRaw(query, focusPoint) } else { Loading @@ -42,7 +41,7 @@ class MultiplexedGeocodingService( override suspend fun reverseGeocodeRaw( latitude: Double, longitude: Double ): Flow<List<GeocodeResult>> { ): List<GeocodeResult> { return if (appPreferenceRepository.offlineMode.value) { offlineGeocodingService.reverseGeocodeRaw(latitude, longitude) } else { Loading @@ -50,11 +49,15 @@ class MultiplexedGeocodingService( } } override suspend fun nearbyRaw(latitude: Double, longitude: Double): Flow<List<GeocodeResult>> { override suspend fun nearbyRaw( latitude: Double, longitude: Double, selectedCategories: List<String> ): List<GeocodeResult> { return if (appPreferenceRepository.offlineMode.value) { offlineGeocodingService.nearbyRaw(latitude, longitude) offlineGeocodingService.nearbyRaw(latitude, longitude, selectedCategories) } else { onlineGeocodingService.nearbyRaw(latitude, longitude) onlineGeocodingService.nearbyRaw(latitude, longitude, selectedCategories) } } }
cardinal-android/app/src/main/java/earth/maps/cardinal/geocoding/OfflineGeocodingService.kt +23 −23 Original line number Diff line number Diff line Loading @@ -25,8 +25,6 @@ import earth.maps.cardinal.data.Address import earth.maps.cardinal.data.GeocodeResult import earth.maps.cardinal.data.LatLng import earth.maps.cardinal.data.LocationRepository import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow import uniffi.cardinal_geocoder.newAirmailIndex import java.io.File Loading @@ -37,8 +35,7 @@ class OfflineGeocodingService( private val geocoderDir = File(context.filesDir, "geocoder").apply { mkdirs() } private val airmailIndex = newAirmailIndex("en", geocoderDir.absolutePath) override suspend fun geocodeRaw(query: String, focusPoint: LatLng?): Flow<List<GeocodeResult>> = flow { override suspend fun geocodeRaw(query: String, focusPoint: LatLng?): List<GeocodeResult> { try { val results = airmailIndex.searchPhrase(query) val geocodeResults = results.map { poi -> Loading @@ -48,24 +45,27 @@ class OfflineGeocodingService( } buildResult(tagMap, poi.lat, poi.lng) } emit(geocodeResults) return geocodeResults } catch (e: Exception) { Log.e(TAG, "Geocode failed with exception", e) // If there's an error, return empty list emit(emptyList()) return emptyList() } } override suspend fun reverseGeocodeRaw( latitude: Double, longitude: Double ): Flow<List<GeocodeResult>> = flow { emit(emptyList()) ): List<GeocodeResult> { return emptyList() } override suspend fun nearbyRaw(latitude: Double, longitude: Double): Flow<List<GeocodeResult>> = flow { emit(emptyList()) override suspend fun nearbyRaw( latitude: Double, longitude: Double, selectedCategories: List<String> ): List<GeocodeResult> { return emptyList() } override suspend fun beginTileProcessing() { Loading
cardinal-android/app/src/main/java/earth/maps/cardinal/geocoding/PeliasGeocodingService.kt +58 −56 Original line number Diff line number Diff line Loading @@ -32,8 +32,6 @@ import io.ktor.client.plugins.logging.Logging import io.ktor.client.request.get import io.ktor.client.request.parameter import io.ktor.serialization.kotlinx.json.json import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonArray import kotlinx.serialization.json.JsonElement Loading @@ -60,8 +58,7 @@ class PeliasGeocodingService( install(Logging) } override suspend fun geocodeRaw(query: String, focusPoint: LatLng?): Flow<List<GeocodeResult>> = flow { override suspend fun geocodeRaw(query: String, focusPoint: LatLng?): List<GeocodeResult> { try { Log.d(TAG, "Geocoding query: $query, focusPoint: $focusPoint") val config = appPreferenceRepository.peliasApiConfig.value Loading @@ -85,17 +82,17 @@ class PeliasGeocodingService( } Log.d(TAG, "Parsed results: ${geocodeResults.size}") emit(geocodeResults) return geocodeResults } catch (e: Exception) { Log.e(TAG, "Error during geocoding", e) emit(emptyList()) return emptyList() } } override suspend fun reverseGeocodeRaw( latitude: Double, longitude: Double ): Flow<List<GeocodeResult>> = flow { ): List<GeocodeResult> { try { Log.d(TAG, "Reverse geocoding: $latitude, $longitude") val config = appPreferenceRepository.peliasApiConfig.value Loading @@ -116,15 +113,17 @@ class PeliasGeocodingService( } Log.d(TAG, "Parsed reverse results: ${geocodeResults.size}") emit(geocodeResults) return geocodeResults } catch (e: Exception) { Log.e(TAG, "Error during reverse geocoding", e) emit(emptyList()) return emptyList() } } override suspend fun nearbyRaw(latitude: Double, longitude: Double): Flow<List<GeocodeResult>> = flow { override suspend fun nearbyRaw( latitude: Double, longitude: Double, selectedCategories: List<String> ): List<GeocodeResult> { try { Log.d(TAG, "Nearby: $latitude, $longitude") val config = appPreferenceRepository.peliasApiConfig.value Loading @@ -133,6 +132,9 @@ class PeliasGeocodingService( parameter("point.lon", longitude.toString()) parameter("size", "50") parameter("layers", "venue") if (selectedCategories.isNotEmpty()) { parameter("categories", selectedCategories.joinToString(",")) } config.apiKey?.let { parameter("api_key", it) } } Loading @@ -146,10 +148,10 @@ class PeliasGeocodingService( } Log.d(TAG, "Parsed nearby results: ${geocodeResults.size}") emit(geocodeResults) return geocodeResults } catch (e: Exception) { Log.e(TAG, "Error during nearby", e) emit(emptyList()) return emptyList() } } Loading
cardinal-android/app/src/main/java/earth/maps/cardinal/ui/directions/DirectionsViewModel.kt +3 −4 Original line number Diff line number Diff line Loading @@ -459,10 +459,9 @@ class DirectionsViewModel @Inject constructor( // Use fromPlace as focus point for viewport biasing if available, // otherwise fall back to current viewport center val focusPoint = fromPlace?.latLng ?: viewportRepository.viewportCenter.value geocodingService.geocode(query, focusPoint).collect { results -> geocodeResults.value = results geocodeResults.value = geocodingService.geocode(query, focusPoint) isSearching = false } } catch (e: Exception) { // Handle error searchError = e.message ?: "An error occurred during search" Loading