├── compass-tools-android
├── api
│ └── compass-tools-android.api
├── src
│ └── androidMain
│ │ ├── kotlin
│ │ └── dev
│ │ │ └── jordond
│ │ │ └── compass
│ │ │ └── tools
│ │ │ ├── ContextProviderInitializer.kt
│ │ │ └── ContextProvider.kt
│ │ └── AndroidManifest.xml
└── build.gradle.kts
├── art
└── logo-full.png
├── demo
├── iosApp
│ ├── Configuration
│ │ └── Config.xcconfig
│ └── iosApp
│ │ ├── Assets.xcassets
│ │ ├── Contents.json
│ │ ├── AppIcon.appiconset
│ │ │ ├── app-icon-1024.png
│ │ │ └── Contents.json
│ │ └── AccentColor.colorset
│ │ │ └── Contents.json
│ │ ├── Preview Content
│ │ └── Preview Assets.xcassets
│ │ │ └── Contents.json
│ │ ├── iOSApp.swift
│ │ ├── ContentView.swift
│ │ └── Info.plist
├── composeApp
│ └── src
│ │ ├── androidMain
│ │ ├── res
│ │ │ ├── values
│ │ │ │ └── strings.xml
│ │ │ ├── mipmap-hdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-mdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-anydpi-v26
│ │ │ │ ├── ic_launcher.xml
│ │ │ │ └── ic_launcher_round.xml
│ │ │ └── drawable-v24
│ │ │ │ └── ic_launcher_foreground.xml
│ │ ├── kotlin
│ │ │ └── dev
│ │ │ │ └── jordond
│ │ │ │ └── compass
│ │ │ │ └── demo
│ │ │ │ └── MainActivity.kt
│ │ └── AndroidManifest.xml
│ │ ├── iosMain
│ │ └── kotlin
│ │ │ └── MainViewController.kt
│ │ ├── commonMain
│ │ └── kotlin
│ │ │ ├── geolocation
│ │ │ └── BrowserGeolocationScreen.kt
│ │ │ ├── geocoder
│ │ │ ├── PlatformGeocoderScreen.kt
│ │ │ ├── PlatformGeocoderFallbackScreen.kt
│ │ │ ├── MapboxGeocoderScreen.kt
│ │ │ ├── OpenCageGeocoderScreen.kt
│ │ │ └── GoogleMapsGeocoderScreen.kt
│ │ │ ├── Platform.kt
│ │ │ └── App.kt
│ │ ├── desktopMain
│ │ └── kotlin
│ │ │ └── main.kt
│ │ ├── wasmJsMain
│ │ ├── kotlin
│ │ │ ├── main.kt
│ │ │ └── geolocation
│ │ │ │ └── BrowserGeolocationScreen.browser.kt
│ │ └── resources
│ │ │ └── index.html
│ │ ├── browserMain
│ │ └── resources
│ │ │ └── index.html
│ │ ├── nonBrowser
│ │ └── kotlin
│ │ │ └── geolocation
│ │ │ └── BrowserGeolocationScreen.nonBrowser.kt
│ │ ├── mobileMain
│ │ └── kotlin
│ │ │ └── Platform.mobile.kt
│ │ └── nonMobileMain
│ │ └── kotlin
│ │ └── Platform.nonMobile.kt
└── README.md
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── compass-geolocation-browser
├── src
│ ├── jsMain
│ │ └── kotlin
│ │ │ └── dev
│ │ │ └── jordond
│ │ │ └── compass
│ │ │ └── geolocation
│ │ │ └── browser
│ │ │ ├── internal
│ │ │ └── Platform.js.kt
│ │ │ └── api
│ │ │ ├── Navigator.js.kt
│ │ │ ├── model
│ │ │ └── CreateGeolocationOptions.kt
│ │ │ └── Geolocation.js.kt
│ ├── wasmJsMain
│ │ └── kotlin
│ │ │ └── dev
│ │ │ └── jordond
│ │ │ └── compass
│ │ │ └── geolocation
│ │ │ └── browser
│ │ │ ├── internal
│ │ │ └── Platform.wasmJs.kt
│ │ │ └── api
│ │ │ ├── Navigator.wasmJs.kt
│ │ │ ├── model
│ │ │ └── CreateGeolocationOptions.kt
│ │ │ └── Geolocation.wasmJs.kt
│ └── commonMain
│ │ └── kotlin
│ │ └── dev
│ │ └── jordond
│ │ └── compass
│ │ └── geolocation
│ │ ├── browser
│ │ ├── internal
│ │ │ ├── Platform.kt
│ │ │ └── Mapper.kt
│ │ ├── api
│ │ │ ├── model
│ │ │ │ ├── GeolocationPosition.kt
│ │ │ │ ├── GeolocationPositionError.kt
│ │ │ │ ├── CreateGeolocationOptions.kt
│ │ │ │ └── GeolocationCoordinates.kt
│ │ │ └── Navigator.kt
│ │ └── BrowserLocator.kt
│ │ └── BrowserGeolocator.kt
└── build.gradle.kts
├── .github
├── ci-gradle.properties
├── dependabot.yml
└── version.sh
├── docs
├── roadmap.md
├── autocomplete
│ ├── via-geocoding.md
│ ├── geocoding
│ │ ├── web-api-service.md
│ │ └── android-ios.md
│ └── overview.md
├── geocoding
│ ├── request-a-geocoder-api.md
│ └── web-api-service
│ │ ├── mapbox.md
│ │ ├── opencage.md
│ │ └── google-maps.md
├── misc
│ ├── demo.md
│ └── contributing.md
├── geocoder
│ └── overview.md
├── geolocation
│ ├── location.md
│ ├── browser.md
│ ├── overview.md
│ └── android-ios.md
└── SUMMARY.md
├── compass-geocoder-mobile
├── src
│ ├── androidMain
│ │ ├── AndroidManifest.xml
│ │ └── kotlin
│ │ │ └── dev
│ │ │ └── jordond
│ │ │ └── compass
│ │ │ └── geocoder
│ │ │ └── mobile
│ │ │ └── internal
│ │ │ ├── AddressMapper.kt
│ │ │ └── Operation.android.kt
│ ├── commonMain
│ │ └── kotlin
│ │ │ └── dev
│ │ │ └── jordond
│ │ │ └── compass
│ │ │ └── geocoder
│ │ │ ├── mobile
│ │ │ ├── Extensions.kt
│ │ │ └── MobilePlatformGeocoder.kt
│ │ │ └── MobileGeocoder.kt
│ └── iosMain
│ │ └── kotlin
│ │ └── dev
│ │ └── jordond
│ │ └── compass
│ │ └── geocoder
│ │ └── mobile
│ │ ├── internal
│ │ ├── Operation.ios.kt
│ │ └── PlacemarkMapper.kt
│ │ └── MobilePlatformGeocoder.ios.kt
└── build.gradle.kts
├── compass-core
├── src
│ └── commonMain
│ │ └── kotlin
│ │ └── dev
│ │ └── jordond
│ │ └── compass
│ │ ├── exception
│ │ ├── NotFoundException.kt
│ │ └── NotSupportedException.kt
│ │ ├── InternalCompassApi.kt
│ │ ├── Coordinates.kt
│ │ └── Priority.kt
└── build.gradle.kts
├── compass-geolocation-mobile
├── src
│ ├── androidMain
│ │ ├── AndroidManifest.xml
│ │ └── kotlin
│ │ │ └── dev
│ │ │ └── jordond
│ │ │ └── compass
│ │ │ └── geolocation
│ │ │ └── mobile
│ │ │ └── internal
│ │ │ └── Mapper.kt
│ └── commonMain
│ │ └── kotlin
│ │ └── dev
│ │ └── jordond
│ │ └── compass
│ │ └── geolocation
│ │ └── mobile
│ │ └── MobileLocator.kt
└── build.gradle.kts
├── .gitignore
├── compass-geocoder
├── src
│ └── commonMain
│ │ └── kotlin
│ │ └── dev
│ │ └── jordond
│ │ └── compass
│ │ └── geocoder
│ │ ├── exception
│ │ └── GeocodeException.kt
│ │ ├── Geocoder.kt
│ │ ├── PlatformGeocoder.kt
│ │ └── ForwardGeocoder.kt
└── build.gradle.kts
├── compass-geolocation
├── src
│ └── commonMain
│ │ └── kotlin
│ │ └── dev
│ │ └── jordond
│ │ └── compass
│ │ └── geolocation
│ │ ├── exception
│ │ └── GeolocationException.kt
│ │ ├── LocationRequest.kt
│ │ ├── TrackingUpdate.kt
│ │ └── Extensions.kt
└── build.gradle.kts
├── compass-tools-web
├── src
│ └── commonMain
│ │ └── kotlin
│ │ └── dev
│ │ └── jordond
│ │ └── compass
│ │ └── tools
│ │ └── web
│ │ ├── parameter
│ │ ├── QueryParamValue.kt
│ │ ├── QueryParametersBuilder.kt
│ │ ├── QueryParamListValue.kt
│ │ └── QueryParameters.kt
│ │ ├── exception
│ │ └── WebException.kt
│ │ └── Request.kt
└── build.gradle.kts
├── compass-geocoder-web-mapbox
├── src
│ └── commonMain
│ │ └── kotlin
│ │ └── dev
│ │ └── jordond
│ │ └── compass
│ │ └── geocoder
│ │ └── web
│ │ ├── mapbox
│ │ └── internal
│ │ │ ├── FeatureResponse.kt
│ │ │ ├── CoordinatesResponse.kt
│ │ │ ├── PropertiesResponse.kt
│ │ │ ├── GeocodeResponse.kt
│ │ │ └── ContextResponse.kt
│ │ ├── parameter
│ │ ├── MapboxWorldView.kt
│ │ ├── MapboxTypes.kt
│ │ └── MapboxBoundingBox.kt
│ │ ├── MapboxForwardEndpoint.kt
│ │ └── MapboxReverseEndpoint.kt
└── build.gradle.kts
├── .run
├── demo.ios.run.xml
├── demo.browser.run.xml
└── demo.desktop.run.xml
├── compass-permissions-mobile
├── src
│ ├── iosMain
│ │ └── kotlin
│ │ │ └── dev
│ │ │ └── jordond
│ │ │ └── compass
│ │ │ └── permissions
│ │ │ ├── mobile
│ │ │ ├── Extensions.ios.kt
│ │ │ └── internal
│ │ │ │ ├── PermissionStateMapper.kt
│ │ │ │ └── LocationPermissionManagerDelegate.kt
│ │ │ └── PermissionController.ios.kt
│ ├── androidMain
│ │ ├── kotlin
│ │ │ └── dev
│ │ │ │ └── jordond
│ │ │ │ └── compass
│ │ │ │ └── permissions
│ │ │ │ └── mobile
│ │ │ │ ├── internal
│ │ │ │ └── activity
│ │ │ │ │ ├── ActivityProviderInitializer.kt
│ │ │ │ │ ├── ActivityLifecycleObserver.kt
│ │ │ │ │ └── ActivityProvider.kt
│ │ │ │ └── Extensions.android.kt
│ │ └── AndroidManifest.xml
│ └── commonMain
│ │ └── kotlin
│ │ └── dev
│ │ └── jordond
│ │ └── compass
│ │ └── permissions
│ │ ├── mobile
│ │ └── Extensions.kt
│ │ └── MobilePermissionController.kt
├── api
│ └── compass-permissions-mobile.api
└── build.gradle.kts
├── compass-permissions
├── build.gradle.kts
└── src
│ └── commonMain
│ └── kotlin
│ └── dev
│ └── jordond
│ └── compass
│ └── permissions
│ ├── exception
│ └── PermissionException.kt
│ ├── PermissionState.kt
│ └── LocationPermissionController.kt
├── compass-autocomplete
├── build.gradle.kts
└── src
│ └── commonMain
│ └── kotlin
│ └── dev
│ └── jordond
│ └── compass
│ └── autocomplete
│ ├── AutocompleteOptions.kt
│ ├── AutocompleteService.kt
│ ├── internal
│ └── DefaultAutocomplete.kt
│ └── Autocomplete.kt
├── compass-geocoder-web-googlemaps
├── src
│ └── commonMain
│ │ └── kotlin
│ │ └── dev
│ │ └── jordond
│ │ └── compass
│ │ └── geocoder
│ │ └── web
│ │ ├── google
│ │ └── internal
│ │ │ ├── GeometryResponse.kt
│ │ │ ├── StatusResponse.kt
│ │ │ └── ResultResponse.kt
│ │ ├── GoogleMapsReverseEndpoint.kt
│ │ ├── GoogleMapsForwardEndpoint.kt
│ │ └── parameter
│ │ └── GoogleMapsLocationType.kt
└── build.gradle.kts
├── compass-autocomplete-mobile
├── src
│ └── commonMain
│ │ └── kotlin
│ │ └── dev
│ │ └── jordond
│ │ └── compass
│ │ └── autocomplete
│ │ ├── mobile
│ │ ├── internal
│ │ │ └── DefaultMobileAutocompleteService.kt
│ │ └── MobileAutocompleteService.kt
│ │ └── MobileAutocomplete.kt
├── build.gradle.kts
└── api
│ └── compass-autocomplete-mobile.api
├── compass-autocomplete-web
├── build.gradle.kts
├── src
│ └── commonMain
│ │ └── kotlin
│ │ └── dev
│ │ └── jordond
│ │ └── compass
│ │ └── autocomplete
│ │ ├── web
│ │ └── HttpAutocompleteService.kt
│ │ └── HttpAutocomplete.kt
└── api
│ ├── android
│ └── compass-autocomplete-web.api
│ └── jvm
│ └── compass-autocomplete-web.api
├── renovate.json
├── compass-geocoder-web-opencage
├── build.gradle.kts
└── src
│ └── commonMain
│ └── kotlin
│ └── dev
│ └── jordond
│ └── compass
│ └── geocoder
│ └── web
│ ├── opencage
│ └── internal
│ │ ├── GeocodeResponse.kt
│ │ └── ResultResponse.kt
│ ├── OpenCageForwardEndpoint.kt
│ └── OpenCageReverseEndpoint.kt
├── compass-geocoder-web-template
├── build.gradle.kts
└── src
│ └── commonMain
│ └── kotlin
│ └── dev
│ └── jordond
│ └── compass
│ └── geocoder
│ └── web
│ ├── template
│ └── internal
│ │ └── GeocodeResponse.kt
│ ├── parameter
│ └── TemplateParameters.kt
│ ├── TemplateForwardEndpoint.kt
│ └── TemplateReverseEndpoint.kt
├── compass-autocomplete-geocoder-mapbox
└── build.gradle.kts
├── compass-autocomplete-geocoder-googlemaps
└── build.gradle.kts
├── LICENSE
├── compass-geocoder-web
├── build.gradle.kts
└── src
│ └── commonMain
│ └── kotlin
│ └── dev
│ └── jordond
│ └── compass
│ └── geocoder
│ └── web
│ ├── ReverseHttpApiPlatformGeocoder.kt
│ └── ForwardHttpApiPlatformGeocoder.kt
├── gradle.properties
└── settings.gradle.kts
/compass-tools-android/api/compass-tools-android.api:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/art/logo-full.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jordond/compass/HEAD/art/logo-full.png
--------------------------------------------------------------------------------
/demo/iosApp/Configuration/Config.xcconfig:
--------------------------------------------------------------------------------
1 | TEAM_ID=
2 | BUNDLE_ID=dev.jordond.compass.demo
3 | APP_NAME=demo
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jordond/compass/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/demo/composeApp/src/androidMain/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | demo
3 |
--------------------------------------------------------------------------------
/demo/iosApp/iosApp/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
--------------------------------------------------------------------------------
/demo/iosApp/iosApp/Preview Content/Preview Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
--------------------------------------------------------------------------------
/demo/composeApp/src/androidMain/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jordond/compass/HEAD/demo/composeApp/src/androidMain/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/demo/composeApp/src/androidMain/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jordond/compass/HEAD/demo/composeApp/src/androidMain/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/demo/composeApp/src/androidMain/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jordond/compass/HEAD/demo/composeApp/src/androidMain/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/demo/composeApp/src/androidMain/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jordond/compass/HEAD/demo/composeApp/src/androidMain/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/demo/composeApp/src/androidMain/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jordond/compass/HEAD/demo/composeApp/src/androidMain/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/demo/iosApp/iosApp/iOSApp.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 |
3 | @main
4 | struct iOSApp: App {
5 | var body: some Scene {
6 | WindowGroup {
7 | ContentView()
8 | }
9 | }
10 | }
--------------------------------------------------------------------------------
/demo/composeApp/src/androidMain/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jordond/compass/HEAD/demo/composeApp/src/androidMain/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/demo/composeApp/src/androidMain/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jordond/compass/HEAD/demo/composeApp/src/androidMain/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/demo/composeApp/src/androidMain/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jordond/compass/HEAD/demo/composeApp/src/androidMain/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/demo/composeApp/src/androidMain/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jordond/compass/HEAD/demo/composeApp/src/androidMain/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/demo/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/app-icon-1024.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jordond/compass/HEAD/demo/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/app-icon-1024.png
--------------------------------------------------------------------------------
/demo/composeApp/src/androidMain/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jordond/compass/HEAD/demo/composeApp/src/androidMain/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/demo/composeApp/src/iosMain/kotlin/MainViewController.kt:
--------------------------------------------------------------------------------
1 | import androidx.compose.ui.window.ComposeUIViewController
2 |
3 | @Suppress("unused")
4 | fun MainViewController() = ComposeUIViewController { App() }
--------------------------------------------------------------------------------
/compass-geolocation-browser/src/jsMain/kotlin/dev/jordond/compass/geolocation/browser/internal/Platform.js.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.geolocation.browser.internal
2 |
3 | public actual external class Object
4 |
--------------------------------------------------------------------------------
/.github/ci-gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.daemon=false
2 | org.gradle.parallel=true
3 | org.gradle.jvmargs=-Xmx4608m -XX:MaxMetaspaceSize=1536m -XX:+HeapDumpOnOutOfMemoryError
4 | kotlin.compiler.execution.strategy=in-process
--------------------------------------------------------------------------------
/demo/composeApp/src/commonMain/kotlin/geolocation/BrowserGeolocationScreen.kt:
--------------------------------------------------------------------------------
1 | package geolocation
2 |
3 | import cafe.adriel.voyager.core.screen.Screen
4 |
5 | expect class BrowserGeolocationScreen constructor() : Screen
--------------------------------------------------------------------------------
/compass-geolocation-browser/src/wasmJsMain/kotlin/dev/jordond/compass/geolocation/browser/internal/Platform.wasmJs.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.geolocation.browser.internal
2 |
3 | public actual external class Object : JsAny
4 |
--------------------------------------------------------------------------------
/demo/iosApp/iosApp/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "idiom" : "universal"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
--------------------------------------------------------------------------------
/docs/roadmap.md:
--------------------------------------------------------------------------------
1 | # 🚗 Roadmap
2 |
3 | These items are in no particular order.
4 |
5 | * Add support for different languages
6 | * Currently only English is supported
7 | * Autocomplete support from third party APIs like Google's Places API
8 |
--------------------------------------------------------------------------------
/docs/autocomplete/via-geocoding.md:
--------------------------------------------------------------------------------
1 | # 🌎 Via Geocoding
2 |
3 | Currently Autocomplete is powered by the existing Geocoder functionality of Compass. In the future,
4 | we plan to integrate with a dedicated autocomplete API service such as Google Places API.
--------------------------------------------------------------------------------
/compass-geocoder-mobile/src/androidMain/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/demo/composeApp/src/desktopMain/kotlin/main.kt:
--------------------------------------------------------------------------------
1 | import androidx.compose.ui.window.Window
2 | import androidx.compose.ui.window.application
3 |
4 | fun main() = application {
5 | Window(onCloseRequest = ::exitApplication, title = "Compass Demo") {
6 | App()
7 | }
8 | }
--------------------------------------------------------------------------------
/compass-core/src/commonMain/kotlin/dev/jordond/compass/exception/NotFoundException.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.exception
2 |
3 | /**
4 | * Thrown when a geocoder or geolocation operation returns null.
5 | */
6 | public class NotFoundException : Throwable("Unable to find requested result.")
--------------------------------------------------------------------------------
/compass-geolocation-browser/src/commonMain/kotlin/dev/jordond/compass/geolocation/browser/internal/Platform.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.geolocation.browser.internal
2 |
3 | /**
4 | * Define a platform interface for creating objects in JS or WASM.
5 | */
6 | public expect class Object
7 |
--------------------------------------------------------------------------------
/compass-core/src/commonMain/kotlin/dev/jordond/compass/exception/NotSupportedException.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.exception
2 |
3 | /**
4 | * Indicates that operation is not supported on this platform.
5 | */
6 | public class NotSupportedException : Throwable("Operation is not supported on this device")
--------------------------------------------------------------------------------
/demo/composeApp/src/wasmJsMain/kotlin/main.kt:
--------------------------------------------------------------------------------
1 | import androidx.compose.ui.ExperimentalComposeUiApi
2 | import androidx.compose.ui.window.CanvasBasedWindow
3 |
4 | @OptIn(ExperimentalComposeUiApi::class)
5 | fun main() {
6 | CanvasBasedWindow(canvasElementId = "ComposeTarget") { App(isBrowser = true) }
7 | }
--------------------------------------------------------------------------------
/compass-geolocation-browser/src/jsMain/kotlin/dev/jordond/compass/geolocation/browser/api/Navigator.js.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.geolocation.browser.api
2 |
3 | public actual external val navigator: Navigator?
4 |
5 | public actual external class Navigator {
6 | public actual val geolocation: Geolocation?
7 | }
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip
4 | networkTimeout=10000
5 | validateDistributionUrl=true
6 | zipStoreBase=GRADLE_USER_HOME
7 | zipStorePath=wrapper/dists
8 |
--------------------------------------------------------------------------------
/compass-geolocation-browser/src/wasmJsMain/kotlin/dev/jordond/compass/geolocation/browser/api/Navigator.wasmJs.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.geolocation.browser.api
2 |
3 | public actual external val navigator: Navigator?
4 |
5 | public actual external class Navigator {
6 | public actual val geolocation: Geolocation?
7 | }
--------------------------------------------------------------------------------
/demo/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "app-icon-1024.png",
5 | "idiom" : "universal",
6 | "platform" : "ios",
7 | "size" : "1024x1024"
8 | }
9 | ],
10 | "info" : {
11 | "author" : "xcode",
12 | "version" : 1
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/demo/composeApp/src/androidMain/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/demo/composeApp/src/androidMain/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/compass-geolocation-mobile/src/androidMain/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/docs/geocoding/request-a-geocoder-api.md:
--------------------------------------------------------------------------------
1 | # ❓ Request a Geocoder API
2 |
3 | If there is a Web based Geocoder API that you would like to be added to Compass.\
4 | \
5 | You can either [create an issue](https://github.com/jordond/compass/issues/new), or implement it yourself and open up a PR. If you plan on going that way, make sure you read [contributing.md](../misc/contributing.md "mention")first.
6 |
--------------------------------------------------------------------------------
/demo/composeApp/src/wasmJsMain/resources/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Compass Demo
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | *.iml
3 | .gradle
4 | .idea
5 | .DS_Store
6 | build
7 | */build
8 | captures
9 | .externalNativeBuild
10 | .cxx
11 | local.properties
12 | xcuserdata/
13 | Pods/
14 | *.jks
15 | *yarn.lock
16 | demo/iosApp/Podfile.lock
17 | demo/iosApp/Pods/*
18 | demo/iosApp/iosApp.xcworkspace/*
19 | demo/iosApp/iosApp.xcodeproj/*
20 | !demo/iosApp/iosApp.xcodeproj/project.pbxproj
21 | publish.sh
22 | .kotlin
--------------------------------------------------------------------------------
/demo/composeApp/src/browserMain/resources/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Compass Demo
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/compass-geocoder/src/commonMain/kotlin/dev/jordond/compass/geocoder/exception/GeocodeException.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.geocoder.exception
2 |
3 | /**
4 | * Indicate something went wrong while using the geocoding service.
5 | *
6 | * @param message The error message.
7 | */
8 | public class GeocodeException(message: String?) : Throwable(
9 | "Geocoding failed: ${message ?: "Unknown error"}",
10 | )
--------------------------------------------------------------------------------
/compass-core/src/commonMain/kotlin/dev/jordond/compass/InternalCompassApi.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass
2 |
3 | @Target(
4 | allowedTargets = [
5 | AnnotationTarget.CLASS,
6 | AnnotationTarget.PROPERTY,
7 | AnnotationTarget.FUNCTION,
8 | AnnotationTarget.TYPEALIAS,
9 | ],
10 | )
11 | @RequiresOptIn(level = RequiresOptIn.Level.ERROR)
12 | public annotation class InternalCompassApi
13 |
--------------------------------------------------------------------------------
/compass-geolocation/src/commonMain/kotlin/dev/jordond/compass/geolocation/exception/GeolocationException.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.geolocation.exception
2 |
3 | /**
4 | * Indicate something went wrong while using the geolocation service.
5 | *
6 | * @param message The error message.
7 | */
8 | public class GeolocationException(message: String?) : Throwable(
9 | "Geolocation failed: ${message ?: "Unknown error"}",
10 | )
--------------------------------------------------------------------------------
/compass-tools-web/src/commonMain/kotlin/dev/jordond/compass/tools/web/parameter/QueryParamValue.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.tools.web.parameter
2 |
3 | /**
4 | * Represents a query parameter value.
5 | *
6 | * Encoded by [QueryParameters] to make a URL safe string.
7 | */
8 | public interface QueryParamValue {
9 |
10 | /**
11 | * The value of the query parameter.
12 | */
13 | public val value: String
14 | }
--------------------------------------------------------------------------------
/compass-geocoder-web-mapbox/src/commonMain/kotlin/dev/jordond/compass/geocoder/web/mapbox/internal/FeatureResponse.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.geocoder.web.mapbox.internal
2 |
3 | import dev.jordond.compass.InternalCompassApi
4 | import kotlinx.serialization.SerialName
5 | import kotlinx.serialization.Serializable
6 |
7 | @InternalCompassApi
8 | @Serializable
9 | public data class FeatureResponse(
10 | @SerialName("properties")
11 | val properties: PropertiesResponse? = null,
12 | )
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | # To get started with Dependabot version updates, you'll need to specify which
2 | # package ecosystems to update and where the package manifests are located.
3 | # Please see the documentation for all configuration options:
4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
5 |
6 | version: 2
7 | updates:
8 | - package-ecosystem: "gradle"
9 | directory: "gradle/"
10 | schedule:
11 | interval: "weekly"
12 |
--------------------------------------------------------------------------------
/compass-tools-web/src/commonMain/kotlin/dev/jordond/compass/tools/web/parameter/QueryParametersBuilder.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.tools.web.parameter
2 |
3 | /**
4 | * Denotes a class that can build a [QueryParameters] instance of type [T].
5 | *
6 | * @param T the type of [QueryParameters] to build.
7 | */
8 | public interface QueryParametersBuilder {
9 |
10 | /**
11 | * @return a [QueryParameters] instance of type [T].
12 | */
13 | public fun build(): T
14 | }
--------------------------------------------------------------------------------
/compass-geolocation-browser/src/wasmJsMain/kotlin/dev/jordond/compass/geolocation/browser/api/model/CreateGeolocationOptions.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.geolocation.browser.api.model
2 |
3 | import dev.jordond.compass.geolocation.browser.internal.Object
4 |
5 | public actual fun createGeolocationOptions(
6 | enableHighAccuracy: Boolean,
7 | timeout: Double,
8 | maximumAge: Double,
9 | ): Object =
10 | js("({enableHighAccuracy: enableHighAccuracy, timeout: timeout, maximumAge: maximumAge})")
--------------------------------------------------------------------------------
/compass-geolocation-browser/src/jsMain/kotlin/dev/jordond/compass/geolocation/browser/api/model/CreateGeolocationOptions.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.geolocation.browser.api.model
2 |
3 | import dev.jordond.compass.geolocation.browser.internal.Object
4 |
5 | public actual fun createGeolocationOptions(
6 | enableHighAccuracy: Boolean,
7 | timeout: Double,
8 | maximumAge: Double,
9 | ): Object = js(
10 | "({enableHighAccuracy: enableHighAccuracy, timeout: timeout, maximumAge: maximumAge})",
11 | ) as Object
--------------------------------------------------------------------------------
/.run/demo.ios.run.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/compass-tools-web/src/commonMain/kotlin/dev/jordond/compass/tools/web/exception/WebException.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.tools.web.exception
2 |
3 | import io.ktor.http.HttpStatusCode
4 |
5 | /**
6 | * Represents an exception that occurred during a web request.
7 | *
8 | * @property statusCode The status code of the response.
9 | * @property message The message of the exception.
10 | */
11 | public class WebException(
12 | public val statusCode: HttpStatusCode,
13 | public override val message: String,
14 | ) : Throwable(message)
--------------------------------------------------------------------------------
/compass-geocoder-web-mapbox/src/commonMain/kotlin/dev/jordond/compass/geocoder/web/mapbox/internal/CoordinatesResponse.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.geocoder.web.mapbox.internal
2 |
3 | import dev.jordond.compass.InternalCompassApi
4 | import kotlinx.serialization.SerialName
5 | import kotlinx.serialization.Serializable
6 |
7 | @InternalCompassApi
8 | @Serializable
9 | public data class CoordinatesResponse(
10 | @SerialName("latitude")
11 | val latitude: Double,
12 |
13 | @SerialName("longitude")
14 | val longitude: Double,
15 | )
--------------------------------------------------------------------------------
/compass-permissions-mobile/src/iosMain/kotlin/dev/jordond/compass/permissions/mobile/Extensions.ios.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.permissions.mobile
2 |
3 | import platform.Foundation.NSURL
4 | import platform.UIKit.UIApplication
5 | import platform.UIKit.UIApplicationOpenSettingsURLString
6 |
7 | internal actual fun openPermissionSettings() {
8 | UIApplication.sharedApplication().openURL(
9 | url = NSURL(string = UIApplicationOpenSettingsURLString),
10 | options = emptyMap(),
11 | completionHandler = null
12 | )
13 | }
--------------------------------------------------------------------------------
/compass-permissions-mobile/src/androidMain/kotlin/dev/jordond/compass/permissions/mobile/internal/activity/ActivityProviderInitializer.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.permissions.mobile.internal.activity
2 |
3 | import android.content.Context
4 | import androidx.startup.Initializer
5 |
6 | internal class ActivityProviderInitializer : Initializer {
7 |
8 | override fun create(context: Context): ActivityProvider {
9 | return ActivityProvider.create(context)
10 | }
11 |
12 | override fun dependencies(): List>> = emptyList()
13 | }
--------------------------------------------------------------------------------
/compass-permissions-mobile/src/commonMain/kotlin/dev/jordond/compass/permissions/mobile/Extensions.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.permissions.mobile
2 |
3 | import dev.jordond.compass.permissions.LocationPermissionController
4 |
5 | /**
6 | * Opens the settings for the app.
7 | *
8 | * This is useful for directing the user to the app settings to enable permissions after they have
9 | * been denied permanently.
10 | */
11 | public fun LocationPermissionController.Companion.openSettings() {
12 | openPermissionSettings()
13 | }
14 |
15 | internal expect fun openPermissionSettings()
--------------------------------------------------------------------------------
/compass-tools-android/src/androidMain/kotlin/dev/jordond/compass/tools/ContextProviderInitializer.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.tools
2 |
3 | import android.content.Context
4 | import androidx.startup.Initializer
5 | import dev.jordond.compass.InternalCompassApi
6 |
7 | @InternalCompassApi
8 | internal class ContextProviderInitializer : Initializer {
9 |
10 | override fun create(context: Context): ContextProvider {
11 | return ContextProvider.create(context)
12 | }
13 |
14 | override fun dependencies(): List>> = emptyList()
15 | }
16 |
--------------------------------------------------------------------------------
/compass-permissions/build.gradle.kts:
--------------------------------------------------------------------------------
1 | import dev.jordond.compass.convention.configureMultiplatform
2 |
3 | plugins {
4 | alias(libs.plugins.android.library)
5 | alias(libs.plugins.multiplatform)
6 | alias(libs.plugins.poko)
7 | alias(libs.plugins.dokka)
8 | alias(libs.plugins.publish)
9 | alias(libs.plugins.convention.multiplatform)
10 | }
11 |
12 | configureMultiplatform()
13 |
14 | kotlin {
15 | sourceSets {
16 | commonMain.dependencies {
17 | api(projects.compassCore)
18 | implementation(libs.kotlinx.coroutines.core)
19 | }
20 | }
21 | }
--------------------------------------------------------------------------------
/demo/iosApp/iosApp/ContentView.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import SwiftUI
3 | import ComposeApp
4 |
5 | struct ComposeView: UIViewControllerRepresentable {
6 | func makeUIViewController(context: Context) -> UIViewController {
7 | MainViewControllerKt.MainViewController()
8 | }
9 |
10 | func updateUIViewController(_ uiViewController: UIViewController, context: Context) {}
11 | }
12 |
13 | struct ContentView: View {
14 | var body: some View {
15 | ComposeView()
16 | .ignoresSafeArea(.keyboard) // Compose has own keyboard handler
17 | }
18 | }
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/compass-geocoder/build.gradle.kts:
--------------------------------------------------------------------------------
1 | import dev.jordond.compass.convention.configureMultiplatform
2 |
3 | plugins {
4 | alias(libs.plugins.android.library)
5 | alias(libs.plugins.multiplatform)
6 | alias(libs.plugins.poko)
7 | alias(libs.plugins.dokka)
8 | alias(libs.plugins.publish)
9 | alias(libs.plugins.convention.multiplatform)
10 | }
11 |
12 | configureMultiplatform()
13 |
14 | kotlin {
15 | sourceSets {
16 | commonMain.dependencies {
17 | api(projects.compassCore)
18 | implementation(libs.kotlinx.coroutines.core)
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/compass-autocomplete/build.gradle.kts:
--------------------------------------------------------------------------------
1 | import dev.jordond.compass.convention.configureMultiplatform
2 |
3 | plugins {
4 | alias(libs.plugins.android.library)
5 | alias(libs.plugins.multiplatform)
6 | alias(libs.plugins.poko)
7 | alias(libs.plugins.dokka)
8 | alias(libs.plugins.publish)
9 | alias(libs.plugins.convention.multiplatform)
10 | }
11 |
12 | configureMultiplatform()
13 |
14 | kotlin {
15 | sourceSets {
16 | commonMain.dependencies {
17 | implementation(projects.compassCore)
18 | implementation(libs.kotlinx.coroutines.core)
19 | }
20 | }
21 | }
--------------------------------------------------------------------------------
/compass-core/src/commonMain/kotlin/dev/jordond/compass/Coordinates.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass
2 |
3 | import dev.drewhamilton.poko.Poko
4 |
5 | /**
6 | * A geographic location represented by latitude and longitude.
7 | *
8 | * Valid latitude values are between -90.0 and 90.0, both inclusive.
9 | * Valid longitude values are between -180.0 and 180.0, both inclusive.
10 | *
11 | * @property latitude The latitude of the location.
12 | * @property longitude The longitude of the location.
13 | */
14 | @Poko
15 | public class Coordinates(
16 | public val latitude: Double,
17 | public val longitude: Double,
18 | )
--------------------------------------------------------------------------------
/compass-autocomplete/src/commonMain/kotlin/dev/jordond/compass/autocomplete/AutocompleteOptions.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.autocomplete
2 |
3 | import dev.drewhamilton.poko.Poko
4 |
5 | /**
6 | * Options for configuring an [Autocomplete].
7 | *
8 | * @property minimumQuery The minimum number of characters required in a query before an
9 | * autocomplete request is made.
10 | */
11 | @Poko
12 | public open class AutocompleteOptions(
13 | public val minimumQuery: Int = DEFAULT_MINIMUM_QUERY,
14 | ) {
15 |
16 | internal companion object {
17 |
18 | private const val DEFAULT_MINIMUM_QUERY = 3
19 | }
20 | }
--------------------------------------------------------------------------------
/compass-geocoder-web-mapbox/src/commonMain/kotlin/dev/jordond/compass/geocoder/web/mapbox/internal/PropertiesResponse.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.geocoder.web.mapbox.internal
2 |
3 | import dev.jordond.compass.InternalCompassApi
4 | import kotlinx.serialization.SerialName
5 | import kotlinx.serialization.Serializable
6 |
7 | @InternalCompassApi
8 | @Serializable
9 | public data class PropertiesResponse(
10 | @SerialName("name")
11 | val name: String? = null,
12 |
13 | @SerialName("coordinates")
14 | val coordinates: CoordinatesResponse? = null,
15 |
16 | @SerialName("context")
17 | val context: ContextResponse? = null,
18 | )
--------------------------------------------------------------------------------
/compass-core/build.gradle.kts:
--------------------------------------------------------------------------------
1 | import dev.jordond.compass.convention.configureMultiplatform
2 |
3 | plugins {
4 | alias(libs.plugins.android.library)
5 | alias(libs.plugins.multiplatform)
6 | alias(libs.plugins.poko)
7 | alias(libs.plugins.dokka)
8 | alias(libs.plugins.publish)
9 | alias(libs.plugins.convention.multiplatform)
10 | }
11 |
12 | configureMultiplatform()
13 |
14 | kotlin {
15 | sourceSets {
16 | commonMain.dependencies {
17 | api(libs.kotlinx.coroutines.core)
18 | }
19 |
20 | androidMain.dependencies {
21 | api(libs.kotlinx.coroutines.android)
22 | }
23 | }
24 | }
--------------------------------------------------------------------------------
/compass-geolocation/build.gradle.kts:
--------------------------------------------------------------------------------
1 | import dev.jordond.compass.convention.configureMultiplatform
2 |
3 | plugins {
4 | alias(libs.plugins.android.library)
5 | alias(libs.plugins.multiplatform)
6 | alias(libs.plugins.poko)
7 | alias(libs.plugins.dokka)
8 | alias(libs.plugins.publish)
9 | alias(libs.plugins.convention.multiplatform)
10 | }
11 |
12 | configureMultiplatform()
13 |
14 | kotlin {
15 | sourceSets {
16 | commonMain.dependencies {
17 | api(projects.compassCore)
18 | api(projects.compassPermissions)
19 |
20 | implementation(libs.kotlinx.coroutines.core)
21 | }
22 | }
23 | }
--------------------------------------------------------------------------------
/demo/composeApp/src/androidMain/kotlin/dev/jordond/compass/demo/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.demo
2 |
3 | import App
4 | import android.os.Bundle
5 | import androidx.activity.ComponentActivity
6 | import androidx.activity.compose.setContent
7 | import androidx.compose.runtime.Composable
8 | import androidx.compose.ui.tooling.preview.Preview
9 |
10 | class MainActivity : ComponentActivity() {
11 | override fun onCreate(savedInstanceState: Bundle?) {
12 | super.onCreate(savedInstanceState)
13 |
14 | setContent {
15 | App()
16 | }
17 | }
18 | }
19 |
20 | @Preview
21 | @Composable
22 | fun AppAndroidPreview() {
23 | App()
24 | }
--------------------------------------------------------------------------------
/compass-geocoder-web-googlemaps/src/commonMain/kotlin/dev/jordond/compass/geocoder/web/google/internal/GeometryResponse.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.geocoder.web.google.internal
2 |
3 | import dev.jordond.compass.InternalCompassApi
4 | import kotlinx.serialization.SerialName
5 | import kotlinx.serialization.Serializable
6 |
7 | @InternalCompassApi
8 | @Serializable
9 | public data class GeometryResponse(
10 | @Serializable
11 | val location: LocationResponse? = null,
12 | )
13 |
14 | @InternalCompassApi
15 | @Serializable
16 | public data class LocationResponse(
17 | @SerialName("lat")
18 | val lat: Double,
19 |
20 | @SerialName("lng")
21 | val lng: Double,
22 | )
--------------------------------------------------------------------------------
/compass-geocoder-web-mapbox/src/commonMain/kotlin/dev/jordond/compass/geocoder/web/parameter/MapboxWorldView.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.geocoder.web.parameter
2 |
3 | import dev.jordond.compass.tools.web.parameter.QueryParamValue
4 |
5 | /**
6 | * The world view parameter is used to filter the results to a specific country or region.
7 | *
8 | * See [Mapbox Geocoding API](https://docs.mapbox.com/api/search/geocoding-v6/#forward-geocoding-with-search-text-input)
9 | */
10 | public enum class MapboxWorldView : QueryParamValue {
11 | AR,
12 | CN,
13 | IN,
14 | JP,
15 | MA,
16 | RU,
17 | TR,
18 | US;
19 |
20 | override val value: String = name.lowercase()
21 | }
--------------------------------------------------------------------------------
/compass-core/src/commonMain/kotlin/dev/jordond/compass/Priority.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass
2 |
3 | /**
4 | * Represents the priority of a location request.
5 | */
6 | public enum class Priority {
7 |
8 | /**
9 | * Balanced power and accuracy. The default priority.
10 | */
11 | Balanced,
12 |
13 | /**
14 | * High accuracy and high power consumption.
15 | *
16 | * **Note:** On Android this requires the LOCATION_FINE permission.
17 | */
18 | HighAccuracy,
19 |
20 | /**
21 | * Low power and low accuracy.
22 | */
23 | LowPower,
24 |
25 | /**
26 | * Lowest accuracy and lowest power consumption.
27 | */
28 | Passive,
29 | }
--------------------------------------------------------------------------------
/compass-tools-android/build.gradle.kts:
--------------------------------------------------------------------------------
1 | import dev.jordond.compass.convention.Platform
2 | import dev.jordond.compass.convention.configureMultiplatform
3 |
4 | plugins {
5 | alias(libs.plugins.android.library)
6 | alias(libs.plugins.multiplatform)
7 | alias(libs.plugins.dokka)
8 | alias(libs.plugins.publish)
9 | alias(libs.plugins.convention.multiplatform)
10 | }
11 |
12 | configureMultiplatform(Platform.Android)
13 |
14 | kotlin {
15 | sourceSets {
16 | androidMain.dependencies {
17 | implementation(projects.compassCore)
18 | implementation(libs.androidx.startup)
19 | implementation(libs.androidx.activity)
20 | }
21 | }
22 | }
--------------------------------------------------------------------------------
/compass-tools-web/src/commonMain/kotlin/dev/jordond/compass/tools/web/parameter/QueryParamListValue.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.tools.web.parameter
2 |
3 | /**
4 | * Denotes a query parameter value that is a list of values.
5 | *
6 | * @param T The type of the list elements.
7 | * @property separator The separator to use when joining the list elements into a single string.
8 | * @property values The list of values.
9 | */
10 | public interface QueryParamListValue : QueryParamValue {
11 |
12 | public val separator: String
13 | get() = ","
14 |
15 | public val values: List
16 |
17 | override val value: String
18 | get() = values.joinToString(separator) { it.value }
19 | }
20 |
--------------------------------------------------------------------------------
/compass-autocomplete-mobile/src/commonMain/kotlin/dev/jordond/compass/autocomplete/mobile/internal/DefaultMobileAutocompleteService.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.autocomplete.mobile.internal
2 |
3 | import dev.jordond.compass.Place
4 | import dev.jordond.compass.autocomplete.AutocompleteService
5 | import dev.jordond.compass.geocoder.mobile.MobilePlatformGeocoder
6 |
7 | internal class DefaultMobileAutocompleteService(
8 | private val platformGeocoder: MobilePlatformGeocoder,
9 | ) : AutocompleteService {
10 |
11 | override fun isAvailable(): Boolean = platformGeocoder.isAvailable()
12 |
13 | override suspend fun search(query: String): List {
14 | return platformGeocoder.placeFromAddress(query)
15 | }
16 | }
--------------------------------------------------------------------------------
/docs/autocomplete/geocoding/web-api-service.md:
--------------------------------------------------------------------------------
1 | # 📈 Web API Service
2 |
3 | Compass supports Autocomplete on all platforms using the Geocoding services provided by Mapbox and
4 | Google Maps.
5 |
6 | ## Getting started
7 |
8 | To get started you first need to create an autocomplete instance:
9 |
10 | ```kotlin
11 | val autocomplete = Autocomplete.googleMapsGeocoder(apiKey = "your-api-key")
12 | ```
13 |
14 | Then you can use the `autocomplete` instance:
15 |
16 | ```kotlin
17 | val results = mutableListOf()
18 |
19 | suspend fun search(query: String) {
20 | val result = autocomplete.getOrNull() ?: return
21 | if (result.isNotEmpty()) {
22 | results.clear()
23 | results.addAll(result)
24 | }
25 | }
26 | ```
--------------------------------------------------------------------------------
/compass-geolocation-browser/build.gradle.kts:
--------------------------------------------------------------------------------
1 | import dev.jordond.compass.convention.Platforms
2 | import dev.jordond.compass.convention.configureMultiplatform
3 |
4 | plugins {
5 | alias(libs.plugins.multiplatform)
6 | alias(libs.plugins.dokka)
7 | alias(libs.plugins.poko)
8 | alias(libs.plugins.publish)
9 | alias(libs.plugins.convention.multiplatform)
10 | }
11 |
12 | configureMultiplatform(Platforms.Browser)
13 |
14 | kotlin {
15 | sourceSets {
16 | commonMain.dependencies {
17 | api(projects.compassCore)
18 | api(projects.compassPermissions)
19 | implementation(projects.compassGeolocation)
20 | implementation(libs.kotlinx.coroutines.core)
21 | }
22 | }
23 | }
--------------------------------------------------------------------------------
/compass-autocomplete-web/build.gradle.kts:
--------------------------------------------------------------------------------
1 | import dev.jordond.compass.convention.configureMultiplatform
2 |
3 | plugins {
4 | alias(libs.plugins.android.library)
5 | alias(libs.plugins.multiplatform)
6 | alias(libs.plugins.poko)
7 | alias(libs.plugins.kotlinx.serialization)
8 | alias(libs.plugins.dokka)
9 | alias(libs.plugins.publish)
10 | alias(libs.plugins.convention.multiplatform)
11 | }
12 |
13 | configureMultiplatform()
14 |
15 | kotlin {
16 | sourceSets {
17 | commonMain.dependencies {
18 | api(projects.compassCore)
19 | api(projects.compassToolsWeb)
20 | implementation(projects.compassAutocomplete)
21 |
22 | implementation(libs.kotlinx.coroutines.core)
23 | }
24 | }
25 | }
--------------------------------------------------------------------------------
/compass-permissions-mobile/api/compass-permissions-mobile.api:
--------------------------------------------------------------------------------
1 | public final class dev/jordond/compass/permissions/MobilePermissionControllerKt {
2 | public static final fun LocationPermissionController ()Ldev/jordond/compass/permissions/LocationPermissionController;
3 | public static final fun MobileLocationPermissionController ()Ldev/jordond/compass/permissions/LocationPermissionController;
4 | public static final fun mobile (Ldev/jordond/compass/permissions/LocationPermissionController$Companion;)Ldev/jordond/compass/permissions/LocationPermissionController;
5 | }
6 |
7 | public final class dev/jordond/compass/permissions/mobile/ExtensionsKt {
8 | public static final fun openSettings (Ldev/jordond/compass/permissions/LocationPermissionController$Companion;)V
9 | }
10 |
11 |
--------------------------------------------------------------------------------
/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3 | "extends": [
4 | "config:base"
5 | ],
6 | "labels": [
7 | "dependencies"
8 | ],
9 | "packageRules": [
10 | {
11 | "groupName": "Kotlin",
12 | "matchPackagePrefixes": [
13 | "androidx.compose.compiler",
14 | "com.google.devtools.ksp",
15 | "org.jetbrains.kotlin",
16 | "org.jetbrains.kotlinx:binary-compatibility-validator"
17 | ]
18 | }
19 | ],
20 | "ignoreDeps": [
21 | "org.jetbrains.kotlinx:kotlinx-coroutines-test",
22 | "org.jetbrains.kotlinx:kotlinx-coroutines-android",
23 | "org.jetbrains.kotlinx:kotlinx-coroutines-core",
24 | "androidx.core:core-ktx"
25 | ]
26 | }
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/demo/composeApp/src/commonMain/kotlin/geocoder/PlatformGeocoderScreen.kt:
--------------------------------------------------------------------------------
1 | package geocoder
2 |
3 | import androidx.compose.runtime.Composable
4 | import androidx.compose.runtime.remember
5 | import cafe.adriel.voyager.core.screen.Screen
6 | import createGeocoder
7 |
8 | /**
9 | * This screen demonstrates if you support Mobile + other platforms, but don't want to fallback
10 | * to a HTTP based PlatformGeocoder
11 | */
12 | class PlatformGeocoderScreen : Screen {
13 |
14 | @Composable
15 | override fun Content() {
16 | val geocoder = remember { createGeocoder() }
17 |
18 | GeocoderContent(
19 | title = "Google Maps",
20 | geocoder = geocoder,
21 | apiKey = null,
22 | updateApiKey = null,
23 | )
24 | }
25 | }
--------------------------------------------------------------------------------
/compass-permissions-mobile/src/androidMain/kotlin/dev/jordond/compass/permissions/mobile/Extensions.android.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.permissions.mobile
2 |
3 | import android.content.Intent
4 | import android.net.Uri
5 | import android.provider.Settings
6 | import dev.jordond.compass.tools.ContextProvider
7 |
8 | internal actual fun openPermissionSettings() {
9 | val context = ContextProvider.getInstance().context.applicationContext
10 | val packageName = context.applicationInfo.packageName
11 | val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
12 | .apply {
13 | flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
14 | data = Uri.fromParts("package", packageName, null)
15 | }
16 |
17 | context.startActivity(intent)
18 | }
--------------------------------------------------------------------------------
/compass-autocomplete/src/commonMain/kotlin/dev/jordond/compass/autocomplete/AutocompleteService.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.autocomplete
2 |
3 | /**
4 | * Represents a service that provides autocomplete suggestions.
5 | *
6 | * @param T The type of the autocomplete suggestions.
7 | */
8 | public interface AutocompleteService {
9 |
10 | /**
11 | * Checks if the service is available.
12 | *
13 | * @return `true` if the service is available, `false` otherwise.
14 | */
15 | public fun isAvailable(): Boolean
16 |
17 | /**
18 | * Searches for autocomplete suggestions.
19 | *
20 | * @param query The search query.
21 | * @return A list of autocomplete suggestions.
22 | */
23 | public suspend fun search(query: String): List
24 |
25 | public companion object
26 | }
--------------------------------------------------------------------------------
/compass-autocomplete-mobile/src/commonMain/kotlin/dev/jordond/compass/autocomplete/mobile/MobileAutocompleteService.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.autocomplete.mobile
2 |
3 | import dev.jordond.compass.Place
4 | import dev.jordond.compass.autocomplete.AutocompleteService
5 | import dev.jordond.compass.autocomplete.mobile.internal.DefaultMobileAutocompleteService
6 | import dev.jordond.compass.geocoder.mobile.MobilePlatformGeocoder
7 |
8 | @Suppress("FunctionName")
9 | public fun MobileAutocompleteService(
10 | locale: String? = null,
11 | platformGeocoder: MobilePlatformGeocoder = MobilePlatformGeocoder(locale),
12 | ): AutocompleteService = DefaultMobileAutocompleteService(
13 | platformGeocoder = platformGeocoder,
14 | )
15 |
16 | public fun AutocompleteService.Companion.mobile(): AutocompleteService =
17 | MobileAutocompleteService()
--------------------------------------------------------------------------------
/compass-permissions-mobile/src/iosMain/kotlin/dev/jordond/compass/permissions/mobile/internal/PermissionStateMapper.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.permissions.mobile.internal
2 |
3 | import dev.jordond.compass.permissions.PermissionState
4 | import platform.CoreLocation.CLAuthorizationStatus
5 |
6 | internal val CLAuthorizationStatus.toPermissionState: PermissionState
7 | get() = when (this) {
8 | platform.CoreLocation.kCLAuthorizationStatusAuthorizedAlways,
9 | platform.CoreLocation.kCLAuthorizationStatusAuthorizedWhenInUse,
10 | -> PermissionState.Granted
11 | platform.CoreLocation.kCLAuthorizationStatusNotDetermined -> PermissionState.NotDetermined
12 | platform.CoreLocation.kCLAuthorizationStatusDenied -> PermissionState.DeniedForever
13 | else -> error("Unknown location authorization status $this")
14 | }
15 |
--------------------------------------------------------------------------------
/compass-tools-android/src/androidMain/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
10 |
16 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/compass-autocomplete-mobile/build.gradle.kts:
--------------------------------------------------------------------------------
1 | import dev.jordond.compass.convention.Platforms
2 | import dev.jordond.compass.convention.configureMultiplatform
3 |
4 | plugins {
5 | alias(libs.plugins.android.library)
6 | alias(libs.plugins.multiplatform)
7 | alias(libs.plugins.poko)
8 | alias(libs.plugins.dokka)
9 | alias(libs.plugins.publish)
10 | alias(libs.plugins.convention.multiplatform)
11 | }
12 |
13 | configureMultiplatform(Platforms.Mobile)
14 |
15 | kotlin {
16 | sourceSets {
17 | commonMain.dependencies {
18 | api(projects.compassCore)
19 | implementation(projects.compassAutocomplete)
20 | implementation(projects.compassGeocoder)
21 | implementation(projects.compassGeocoderMobile)
22 |
23 | implementation(libs.kotlinx.coroutines.core)
24 | }
25 | }
26 | }
--------------------------------------------------------------------------------
/demo/composeApp/src/nonBrowser/kotlin/geolocation/BrowserGeolocationScreen.nonBrowser.kt:
--------------------------------------------------------------------------------
1 | package geolocation
2 |
3 | import androidx.compose.foundation.layout.Arrangement
4 | import androidx.compose.foundation.layout.Column
5 | import androidx.compose.material3.Text
6 | import androidx.compose.runtime.Composable
7 | import androidx.compose.ui.Alignment
8 | import androidx.compose.ui.Modifier
9 | import cafe.adriel.voyager.core.screen.Screen
10 |
11 | actual class BrowserGeolocationScreen actual constructor() : Screen {
12 |
13 | @Composable
14 | override fun Content() {
15 | Column(
16 | horizontalAlignment = Alignment.CenterHorizontally,
17 | verticalArrangement = Arrangement.Center,
18 | modifier = Modifier,
19 | ) {
20 | Text("Not supported on non-browser platforms")
21 | }
22 | }
23 | }
--------------------------------------------------------------------------------
/compass-geocoder-web-mapbox/build.gradle.kts:
--------------------------------------------------------------------------------
1 | import dev.jordond.compass.convention.configureMultiplatform
2 |
3 | plugins {
4 | alias(libs.plugins.android.library)
5 | alias(libs.plugins.multiplatform)
6 | alias(libs.plugins.poko)
7 | alias(libs.plugins.kotlinx.serialization)
8 | alias(libs.plugins.dokka)
9 | alias(libs.plugins.publish)
10 | alias(libs.plugins.convention.multiplatform)
11 | }
12 |
13 | configureMultiplatform()
14 |
15 | kotlin {
16 | sourceSets {
17 | commonMain.dependencies {
18 | api(projects.compassCore)
19 | implementation(projects.compassGeocoder)
20 | api(projects.compassGeocoderWeb)
21 | api(projects.compassToolsWeb)
22 |
23 | implementation(libs.kotlinx.coroutines.core)
24 | implementation(libs.kotlinx.serialization.json)
25 | }
26 | }
27 | }
--------------------------------------------------------------------------------
/compass-geocoder-mobile/src/commonMain/kotlin/dev/jordond/compass/geocoder/mobile/Extensions.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.geocoder.mobile
2 |
3 | import dev.jordond.compass.Place
4 | import dev.jordond.compass.geocoder.Geocoder
5 |
6 | /**
7 | * Get a list of [Place]s for a given address or `null` if unsuccessful.
8 | *
9 | * In most cases, the list will contain a single [Place]. However, in some cases, it may return
10 | * multiple [Place]s.
11 | *
12 | * @receiver The [Geocoder] to use for geocoding.
13 | * @param address The address to geocode.
14 | * @return A list of [Place]s or `null` if the geocoding was unsuccessful, either due to a geocoder
15 | * error or not being able to find the location.
16 | */
17 | public suspend fun Geocoder.placesOrNull(address: String): List? {
18 | return (platformGeocoder as? MobilePlatformGeocoder)?.placeFromAddress(address)
19 | }
--------------------------------------------------------------------------------
/demo/composeApp/src/mobileMain/kotlin/Platform.mobile.kt:
--------------------------------------------------------------------------------
1 | import dev.jordond.compass.geocoder.PlatformGeocoder
2 | import dev.jordond.compass.geocoder.mobile.MobilePlatformGeocoder
3 | import dev.jordond.compass.geolocation.Locator
4 | import dev.jordond.compass.geolocation.mobile.mobile
5 | import dev.jordond.compass.permissions.LocationPermissionController
6 | import dev.jordond.compass.permissions.mobile.openSettings
7 |
8 | actual fun getPlatformGeocoder(): PlatformGeocoder = getPlatformGeocoderOrFallback("")
9 |
10 | actual fun getPlatformGeocoderOrFallback(apiKey: String): PlatformGeocoder {
11 | return MobilePlatformGeocoder()
12 | }
13 |
14 | actual fun getPlatformLocator(): Locator {
15 | return Locator.mobile()
16 | }
17 |
18 | actual val canShowAppSettings: Boolean = true
19 |
20 | actual fun showAppSettings() {
21 | LocationPermissionController.openSettings()
22 | }
--------------------------------------------------------------------------------
/compass-geocoder-web-googlemaps/build.gradle.kts:
--------------------------------------------------------------------------------
1 | import dev.jordond.compass.convention.configureMultiplatform
2 |
3 | plugins {
4 | alias(libs.plugins.android.library)
5 | alias(libs.plugins.multiplatform)
6 | alias(libs.plugins.poko)
7 | alias(libs.plugins.kotlinx.serialization)
8 | alias(libs.plugins.dokka)
9 | alias(libs.plugins.publish)
10 | alias(libs.plugins.convention.multiplatform)
11 | }
12 |
13 | configureMultiplatform()
14 |
15 | kotlin {
16 | sourceSets {
17 | commonMain.dependencies {
18 | api(projects.compassCore)
19 | implementation(projects.compassGeocoder)
20 | api(projects.compassGeocoderWeb)
21 | api(projects.compassToolsWeb)
22 |
23 | implementation(libs.kotlinx.coroutines.core)
24 | implementation(libs.kotlinx.serialization.json)
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/compass-geocoder-web-opencage/build.gradle.kts:
--------------------------------------------------------------------------------
1 | import dev.jordond.compass.convention.configureMultiplatform
2 |
3 | plugins {
4 | alias(libs.plugins.android.library)
5 | alias(libs.plugins.multiplatform)
6 | alias(libs.plugins.poko)
7 | alias(libs.plugins.kotlinx.serialization)
8 | alias(libs.plugins.dokka)
9 | alias(libs.plugins.publish)
10 | alias(libs.plugins.convention.multiplatform)
11 | }
12 |
13 | configureMultiplatform()
14 |
15 | kotlin {
16 | sourceSets {
17 | commonMain.dependencies {
18 | api(projects.compassCore)
19 | implementation(projects.compassGeocoder)
20 | api(projects.compassGeocoderWeb)
21 | api(projects.compassToolsWeb)
22 |
23 | implementation(libs.kotlinx.coroutines.core)
24 | implementation(libs.kotlinx.serialization.json)
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/compass-geocoder-web-template/build.gradle.kts:
--------------------------------------------------------------------------------
1 | import dev.jordond.compass.convention.configureMultiplatform
2 |
3 | plugins {
4 | alias(libs.plugins.android.library)
5 | alias(libs.plugins.multiplatform)
6 | alias(libs.plugins.poko)
7 | alias(libs.plugins.kotlinx.serialization)
8 | // alias(libs.plugins.dokka)
9 | // alias(libs.plugins.publish)
10 | alias(libs.plugins.convention.multiplatform)
11 | }
12 |
13 | configureMultiplatform()
14 |
15 | kotlin {
16 | sourceSets {
17 | commonMain.dependencies {
18 | api(projects.compassCore)
19 | implementation(projects.compassGeocoder)
20 | api(projects.compassGeocoderWeb)
21 | api(projects.compassToolsWeb)
22 |
23 | implementation(libs.kotlinx.coroutines.core)
24 | implementation(libs.kotlinx.serialization.json)
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/compass-geolocation-browser/src/commonMain/kotlin/dev/jordond/compass/geolocation/browser/internal/Mapper.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.geolocation.browser.internal
2 |
3 | import dev.jordond.compass.Altitude
4 | import dev.jordond.compass.Azimuth
5 | import dev.jordond.compass.Coordinates
6 | import dev.jordond.compass.Location
7 | import dev.jordond.compass.Speed
8 | import dev.jordond.compass.geolocation.browser.api.model.GeolocationPosition
9 |
10 | internal fun GeolocationPosition.toModel(): Location = Location(
11 | coordinates = Coordinates(coords.latitude, coords.longitude),
12 | accuracy = coords.accuracy,
13 | altitude = coords.altitude?.let { Altitude(it, coords.altitudeAccuracy?.toFloat()) },
14 | azimuth = coords.heading?.let { Azimuth(it.toFloat(), null) },
15 | speed = coords.speed?.let { Speed(it.toFloat(), null) },
16 | timestampMillis = timestamp.toLong(),
17 | )
--------------------------------------------------------------------------------
/compass-autocomplete-geocoder-mapbox/build.gradle.kts:
--------------------------------------------------------------------------------
1 | import dev.jordond.compass.convention.configureMultiplatform
2 |
3 | plugins {
4 | alias(libs.plugins.android.library)
5 | alias(libs.plugins.multiplatform)
6 | alias(libs.plugins.poko)
7 | alias(libs.plugins.kotlinx.serialization)
8 | alias(libs.plugins.dokka)
9 | alias(libs.plugins.publish)
10 | alias(libs.plugins.convention.multiplatform)
11 | }
12 |
13 | configureMultiplatform()
14 |
15 | kotlin {
16 | sourceSets {
17 | commonMain.dependencies {
18 | api(projects.compassCore)
19 | api(projects.compassToolsWeb)
20 | implementation(projects.compassAutocomplete)
21 | api(projects.compassGeocoderWebMapbox)
22 |
23 | implementation(libs.kotlinx.coroutines.core)
24 | implementation(libs.kotlinx.serialization.json)
25 | }
26 | }
27 | }
--------------------------------------------------------------------------------
/compass-geolocation-browser/src/jsMain/kotlin/dev/jordond/compass/geolocation/browser/api/Geolocation.js.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.geolocation.browser.api
2 |
3 | import dev.jordond.compass.geolocation.browser.api.model.GeolocationPosition
4 | import dev.jordond.compass.geolocation.browser.api.model.GeolocationPositionError
5 | import dev.jordond.compass.geolocation.browser.internal.Object
6 |
7 | public actual external class Geolocation {
8 |
9 | public actual fun getCurrentPosition(
10 | success: (GeolocationPosition?) -> Unit,
11 | error: (GeolocationPositionError) -> Unit,
12 | options: Object,
13 | )
14 |
15 | public actual fun watchPosition(
16 | success: (GeolocationPosition?) -> Unit,
17 | error: (GeolocationPositionError) -> Unit,
18 | options: Object,
19 | ): Int
20 |
21 | public actual fun clearWatch(watchId: Int)
22 | }
--------------------------------------------------------------------------------
/compass-geolocation-browser/src/wasmJsMain/kotlin/dev/jordond/compass/geolocation/browser/api/Geolocation.wasmJs.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.geolocation.browser.api
2 |
3 | import dev.jordond.compass.geolocation.browser.api.model.GeolocationPosition
4 | import dev.jordond.compass.geolocation.browser.api.model.GeolocationPositionError
5 | import dev.jordond.compass.geolocation.browser.internal.Object
6 |
7 | public actual external class Geolocation {
8 |
9 | public actual fun getCurrentPosition(
10 | success: (GeolocationPosition?) -> Unit,
11 | error: (GeolocationPositionError) -> Unit,
12 | options: Object,
13 | )
14 |
15 | public actual fun watchPosition(
16 | success: (GeolocationPosition?) -> Unit,
17 | error: (GeolocationPositionError) -> Unit,
18 | options: Object,
19 | ): Int
20 |
21 | public actual fun clearWatch(watchId: Int)
22 | }
--------------------------------------------------------------------------------
/compass-autocomplete-geocoder-googlemaps/build.gradle.kts:
--------------------------------------------------------------------------------
1 | import dev.jordond.compass.convention.configureMultiplatform
2 |
3 | plugins {
4 | alias(libs.plugins.android.library)
5 | alias(libs.plugins.multiplatform)
6 | alias(libs.plugins.poko)
7 | alias(libs.plugins.kotlinx.serialization)
8 | alias(libs.plugins.dokka)
9 | alias(libs.plugins.publish)
10 | alias(libs.plugins.convention.multiplatform)
11 | }
12 |
13 | configureMultiplatform()
14 |
15 | kotlin {
16 | sourceSets {
17 | commonMain.dependencies {
18 | api(projects.compassCore)
19 | api(projects.compassToolsWeb)
20 | implementation(projects.compassAutocomplete)
21 | api(projects.compassGeocoderWebGooglemaps)
22 |
23 | implementation(libs.kotlinx.coroutines.core)
24 | implementation(libs.kotlinx.serialization.json)
25 | }
26 | }
27 | }
--------------------------------------------------------------------------------
/compass-geolocation-browser/src/commonMain/kotlin/dev/jordond/compass/geolocation/browser/api/model/GeolocationPosition.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.geolocation.browser.api.model
2 |
3 | /**
4 | * The GeolocationPosition interface represents the position of the concerned device at a given
5 | * time. The position, represented by a GeolocationCoordinates object, comprehends the 2D position
6 | * of the device, on a spheroid representing the Earth, but also its altitude and its speed.
7 | *
8 | * [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/API/GeolocationPosition)
9 | *
10 | * @property coords The [GeolocationCoordinates] object containing the device's current location.
11 | * @property timestamp The time at which the location was retrieved.
12 | */
13 | public external class GeolocationPosition {
14 |
15 | public val coords: GeolocationCoordinates
16 |
17 | public val timestamp: Double
18 | }
--------------------------------------------------------------------------------
/compass-permissions/src/commonMain/kotlin/dev/jordond/compass/permissions/exception/PermissionException.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.permissions.exception
2 |
3 | /**
4 | * Defines an exception that is related to Geolocation permissions.
5 | */
6 | public sealed class PermissionException(message: String) : Throwable(message)
7 |
8 | /**
9 | * The user denied the permission request.
10 | *
11 | * @param permission The permission that was denied.
12 | */
13 | public class PermissionDeniedException(
14 | permission: String = "Location",
15 | ) : PermissionException("Permission $permission was denied")
16 |
17 | /**
18 | * The user denied the permission request and selected "Don't ask again".
19 | *
20 | * @param permission The permission that was denied.
21 | */
22 | public class PermissionDeniedForeverException(
23 | permission: String = "Location",
24 | ) : PermissionException("Permission $permission was denied permanently")
25 |
--------------------------------------------------------------------------------
/compass-geolocation-browser/src/commonMain/kotlin/dev/jordond/compass/geolocation/browser/api/Navigator.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.geolocation.browser.api
2 |
3 | import dev.jordond.compass.InternalCompassApi
4 |
5 | /**
6 | * Browser provided Navigator object for accessing Geolocation API.
7 | *
8 | * See [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/API/Navigator)
9 | */
10 | @InternalCompassApi
11 | public expect val navigator: Navigator?
12 |
13 | /**
14 | * Browser provided Navigator object for accessing Geolocation API.
15 | *
16 | * See [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/API/Navigator)
17 | */
18 | @InternalCompassApi
19 | public expect class Navigator {
20 |
21 | /**
22 | * GeoLocation API for accessing the browser's location data.
23 | *
24 | * See [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/API/Geolocation)
25 | */
26 | public val geolocation: Geolocation?
27 | }
--------------------------------------------------------------------------------
/docs/geocoding/web-api-service/mapbox.md:
--------------------------------------------------------------------------------
1 | # Mapbox
2 |
3 | To use [Mapbox API ](https://docs.mapbox.com/api/search/geocoding/)as your geocoding backend you will need to do the following:
4 |
5 | ### Obtain an API key
6 |
7 | Create an account for [Mapbox](https://account.mapbox.com/), then copy your access token.
8 |
9 | ### Create the Geocoder
10 |
11 | Simple pass your API key to the `Geocoder` function:
12 |
13 | ```kotlin
14 | val geocoder = MapboxGeocoder(apiKey = "your-api-key")
15 |
16 | // Or this helper function
17 | val geocoder = Geocoder(apiKey = "your-api-key")
18 | ```
19 |
20 | #### Customizing Google Maps request
21 |
22 | The Mapbox geocoding API has some [optional parameters.](https://docs.mapbox.com/api/search/geocoding/#forward-geocoding-with-search-text-input) You can modify these by passing them into the `Geocoder` function:
23 |
24 | ```kotlin
25 | val geocoder = MapboxGeocoder(apiKey = "your-api-key") {
26 | limit = 3
27 | }
28 | ```
29 |
--------------------------------------------------------------------------------
/docs/geocoding/web-api-service/opencage.md:
--------------------------------------------------------------------------------
1 | # OpenCage
2 |
3 | To use [OpenCage API](https://opencagedata.com/api) as your geocoding backend you will need to do
4 | the following:
5 |
6 | ### Obtain an API key
7 |
8 | Create an account for [OpenCage](https://opencagedata.com/dashboard), then copy your access token.
9 |
10 | ### Create the Geocoder
11 |
12 | Simple pass your API key to the `Geocoder` function:
13 |
14 | ```kotlin
15 | val geocoder = OpenCageGeocoder(apiKey = "your-api-key")
16 |
17 | // Or this helper function
18 | val geocoder = Geocoder(apiKey = "your-api-key")
19 | ```
20 |
21 | #### Customizing Google Maps request
22 |
23 | The OpenCage geocoding API has
24 | some [optional parameters.](https://opencagedata.com/api#optional-params) You can modify these by
25 | passing them into the `Geocoder` function:
26 |
27 | ```kotlin
28 | val geocoder = OpenCageGeocoder(apiKey = "your-api-key") {
29 | limit = 3
30 | noRecord = true
31 | }
32 | ```
33 |
--------------------------------------------------------------------------------
/compass-geocoder-web-googlemaps/src/commonMain/kotlin/dev/jordond/compass/geocoder/web/google/internal/StatusResponse.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.geocoder.web.google.internal
2 |
3 | import dev.jordond.compass.InternalCompassApi
4 | import kotlinx.serialization.SerialName
5 |
6 | /**
7 | * The possible status responses from the Google Maps Geocoding API.
8 | *
9 | * @see [Status and Error Codes](https://developers.google.com/maps/documentation/geocoding/requests-reverse-geocoding#reverse-response)
10 | */
11 | @Suppress("unused")
12 | @InternalCompassApi
13 | public enum class StatusResponse {
14 | @SerialName("OK")
15 | Ok,
16 |
17 | @SerialName("ZERO_RESULTS")
18 | ZeroResults,
19 |
20 | @SerialName("OVER_QUERY_LIMIT")
21 | OverQueryLimit,
22 |
23 | @SerialName("REQUEST_DENIED")
24 | RequestDenied,
25 |
26 | @SerialName("INVALID_REQUEST")
27 | InvalidRequest,
28 |
29 | @SerialName("UNKNOWN_ERROR")
30 | UnknownError,
31 | }
--------------------------------------------------------------------------------
/compass-geocoder-mobile/build.gradle.kts:
--------------------------------------------------------------------------------
1 | import dev.jordond.compass.convention.Platforms
2 | import dev.jordond.compass.convention.configureMultiplatform
3 |
4 | plugins {
5 | alias(libs.plugins.android.library)
6 | alias(libs.plugins.multiplatform)
7 | alias(libs.plugins.poko)
8 | alias(libs.plugins.dokka)
9 | alias(libs.plugins.publish)
10 | alias(libs.plugins.convention.multiplatform)
11 | }
12 |
13 | configureMultiplatform(Platforms.Mobile)
14 |
15 | kotlin {
16 | sourceSets {
17 | commonMain.dependencies {
18 | api(projects.compassCore)
19 | implementation(projects.compassGeocoder)
20 | implementation(libs.kotlinx.coroutines.core)
21 | }
22 |
23 | androidMain.dependencies {
24 | implementation(projects.compassToolsAndroid)
25 | implementation(libs.kotlinx.coroutines.android)
26 | implementation(libs.androidx.activity)
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/compass-geocoder-web-template/src/commonMain/kotlin/dev/jordond/compass/geocoder/web/template/internal/GeocodeResponse.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.geocoder.web.template.internal
2 |
3 | import dev.jordond.compass.Coordinates
4 | import dev.jordond.compass.Place
5 | import kotlinx.serialization.SerialName
6 | import kotlinx.serialization.Serializable
7 |
8 | @Serializable
9 | internal data class GeocodeResponse(
10 | @SerialName("results")
11 | val results: List = emptyList(),
12 | )
13 |
14 | @Serializable
15 | internal data class ResultResponse(
16 | @SerialName("foo")
17 | val foo: String? = null,
18 | )
19 |
20 | internal fun GeocodeResponse.toCoordinates(): List {
21 | return results.mapNotNull {
22 | // TODO: Implement this
23 | null
24 | }
25 | }
26 |
27 | internal fun GeocodeResponse.toPlaces(): List {
28 | return results.mapNotNull {
29 | // TODO: Implement this
30 | null
31 | }
32 | }
--------------------------------------------------------------------------------
/compass-permissions-mobile/src/androidMain/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
10 |
11 |
17 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/compass-tools-android/src/androidMain/kotlin/dev/jordond/compass/tools/ContextProvider.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.tools
2 |
3 | import android.annotation.SuppressLint
4 | import android.content.Context
5 | import dev.jordond.compass.InternalCompassApi
6 |
7 | /**
8 | * Class for providing the application context.
9 | */
10 | @InternalCompassApi
11 | public class ContextProvider(public val context: Context) {
12 |
13 | @InternalCompassApi
14 | public companion object {
15 |
16 | @SuppressLint("StaticFieldLeak")
17 | private var instance: ContextProvider? = null
18 |
19 | public fun create(context: Context): ContextProvider {
20 | if (instance == null) {
21 | instance = ContextProvider(context)
22 | }
23 |
24 | return instance!!
25 | }
26 |
27 | public fun getInstance(): ContextProvider = instance
28 | ?: throw IllegalStateException("ContextProvider has not been initialized")
29 | }
30 | }
--------------------------------------------------------------------------------
/.run/demo.browser.run.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
11 |
16 |
17 |
18 | true
19 | true
20 | false
21 | false
22 |
23 |
24 |
--------------------------------------------------------------------------------
/compass-geolocation/src/commonMain/kotlin/dev/jordond/compass/geolocation/LocationRequest.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.geolocation
2 |
3 | import dev.drewhamilton.poko.Poko
4 | import dev.jordond.compass.Priority
5 |
6 | /**
7 | * Represents a request for location updates.
8 | *
9 | * @param priority The priority of the request.
10 | * @param interval The interval at which to receive location updates, in milliseconds.
11 | * @param maximumAge The maximum age of a cached location that can be used before a new location is
12 | * requested, in milliseconds.
13 | * @param ignoreAvailableCheck Whether to ignore the availability of location services when
14 | * requesting a location. If `true`, the request will be made regardless of whether location
15 | * services are available, potentially resulting in an error if they are not.
16 | */
17 | @Poko
18 | public class LocationRequest(
19 | public val priority: Priority = Priority.Balanced,
20 | public val interval: Long = 5000L,
21 | public val maximumAge: Long = 0L,
22 | public val ignoreAvailableCheck: Boolean = false,
23 | )
--------------------------------------------------------------------------------
/compass-geocoder-mobile/src/androidMain/kotlin/dev/jordond/compass/geocoder/mobile/internal/AddressMapper.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.geocoder.mobile.internal
2 |
3 | import android.location.Address
4 | import dev.jordond.compass.Coordinates
5 | import dev.jordond.compass.Place
6 |
7 | internal fun Address.toPlace(): Place = Place(
8 | coordinates = Coordinates(latitude, longitude),
9 | name = featureName,
10 | street = getAddressLine(0),
11 | isoCountryCode = countryCode,
12 | country = countryName,
13 | postalCode = postalCode,
14 | administrativeArea = adminArea,
15 | subAdministrativeArea = subAdminArea,
16 | locality = locality,
17 | subLocality = subLocality,
18 | thoroughfare = thoroughfare,
19 | subThoroughfare = subThoroughfare,
20 | )
21 |
22 | internal fun List.toPlaces(): List = map { it.toPlace() }
23 |
24 | internal fun Address.toCoordinates(): Coordinates =
25 | Coordinates(latitude = latitude, longitude = longitude)
26 |
27 | internal fun List.toCoordinates(): List = map { it.toCoordinates() }
--------------------------------------------------------------------------------
/demo/composeApp/src/commonMain/kotlin/Platform.kt:
--------------------------------------------------------------------------------
1 | import dev.jordond.compass.geocoder.Geocoder
2 | import dev.jordond.compass.geocoder.PlatformGeocoder
3 | import dev.jordond.compass.geolocation.Geolocator
4 | import dev.jordond.compass.geolocation.Locator
5 |
6 | /**
7 | * If an API key is provided, create a platform geocoder which falls back to an HTTP Geocoder
8 | * with the API key..
9 | *
10 | * If it is not provided it will create a platform geocoder.
11 | */
12 | fun createGeocoder(apiKey: String? = null): Geocoder {
13 | val platformGeocoder =
14 | if (apiKey == null) getPlatformGeocoder()
15 | else getPlatformGeocoderOrFallback(apiKey)
16 | return Geocoder(platformGeocoder)
17 | }
18 |
19 | expect fun getPlatformGeocoder(): PlatformGeocoder
20 |
21 | expect fun getPlatformGeocoderOrFallback(apiKey: String): PlatformGeocoder
22 |
23 | fun createGeolocator(): Geolocator {
24 | return Geolocator(getPlatformLocator())
25 | }
26 |
27 | expect fun getPlatformLocator(): Locator
28 |
29 | expect val canShowAppSettings: Boolean
30 |
31 | expect fun showAppSettings()
--------------------------------------------------------------------------------
/compass-geocoder-web-mapbox/src/commonMain/kotlin/dev/jordond/compass/geocoder/web/parameter/MapboxTypes.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.geocoder.web.parameter
2 |
3 | import dev.jordond.compass.tools.web.parameter.QueryParamListValue
4 | import dev.jordond.compass.tools.web.parameter.QueryParamValue
5 |
6 | /**
7 | * The types of features that can be returned by the Mapbox Geocoding API.
8 | *
9 | * See [Mapbox Geocoding API](https://docs.mapbox.com/api/search/geocoding-v6/#forward-geocoding-with-search-text-input)
10 | */
11 | public enum class MapboxTypes : QueryParamValue {
12 | Country,
13 | Region,
14 | Postcode,
15 | District,
16 | Place,
17 | Locality,
18 | Neighborhood,
19 | Street,
20 | Address;
21 |
22 | override val value: String = name.lowercase()
23 | }
24 |
25 | /**
26 | * A list of [MapboxTypes] to be used as a query parameter.
27 | */
28 | public class MapboxTypesList(
29 | override val values: List,
30 | ) : QueryParamListValue {
31 |
32 | public constructor(vararg values: MapboxTypes) : this(values.toList())
33 | }
--------------------------------------------------------------------------------
/demo/composeApp/src/wasmJsMain/kotlin/geolocation/BrowserGeolocationScreen.browser.kt:
--------------------------------------------------------------------------------
1 | package geolocation
2 |
3 | import androidx.compose.runtime.Composable
4 | import androidx.compose.runtime.getValue
5 | import cafe.adriel.voyager.core.model.rememberScreenModel
6 | import cafe.adriel.voyager.core.screen.Screen
7 | import dev.jordond.compass.geolocation.Geolocator
8 | import dev.jordond.compass.geolocation.Locator
9 | import dev.jordond.compass.geolocation.browser.browser
10 | import dev.stateholder.extensions.collectAsState
11 |
12 | actual class BrowserGeolocationScreen actual constructor() : Screen {
13 |
14 | @Composable
15 | override fun Content() {
16 | val model = rememberScreenModel {
17 | GeolocationModel(Geolocator(Locator.browser()))
18 | }
19 | val state by model.collectAsState()
20 |
21 | GeolocationContent(
22 | state = state,
23 | currentLocation = model::currentLocation,
24 | startTracking = model::startTracking,
25 | stopTracking = model::stopTracking,
26 | )
27 | }
28 | }
--------------------------------------------------------------------------------
/compass-geolocation-browser/src/commonMain/kotlin/dev/jordond/compass/geolocation/browser/BrowserLocator.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.geolocation.browser
2 |
3 | import dev.jordond.compass.geolocation.Locator
4 | import dev.jordond.compass.geolocation.browser.internal.DefaultBrowserLocator
5 |
6 | /**
7 | * A locator that uses a web browser to determine the current location.
8 | */
9 | public interface BrowserLocator : Locator
10 |
11 | /**
12 | * Creates a new [BrowserLocator] that uses a web browser to determine the current location.
13 | *
14 | * [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/API/Geolocation)
15 | *
16 | * @return A new [BrowserLocator].
17 | */
18 | public fun createBrowserLocator(): BrowserLocator = DefaultBrowserLocator()
19 |
20 | /**
21 | * Creates a new [BrowserLocator] that uses a web browser to determine the current location.
22 | *
23 | * [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/API/Geolocation)
24 | *
25 | * @return A new [BrowserLocator].
26 | */
27 | public fun Locator.Companion.browser(): BrowserLocator = createBrowserLocator()
28 |
29 |
--------------------------------------------------------------------------------
/compass-permissions-mobile/src/commonMain/kotlin/dev/jordond/compass/permissions/MobilePermissionController.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.permissions
2 |
3 | /**
4 | * Creates a new [LocationPermissionController] for Android and iOS.
5 | *
6 | * @return A new [LocationPermissionController].
7 | */
8 | public fun LocationPermissionController(): LocationPermissionController {
9 | return createPermissionController()
10 | }
11 |
12 | /**
13 | * Creates a new [LocationPermissionController] for Android and iOS.
14 | *
15 | * @return A new [LocationPermissionController].
16 | */
17 | @Suppress("FunctionName")
18 | public fun MobileLocationPermissionController(): LocationPermissionController {
19 | return LocationPermissionController()
20 | }
21 |
22 | /**
23 | * Creates a new [LocationPermissionController] for Android and iOS.
24 | *
25 | * @return A new [LocationPermissionController].
26 | */
27 | public fun LocationPermissionController.Companion.mobile(): LocationPermissionController {
28 | return LocationPermissionController()
29 | }
30 |
31 | internal expect fun createPermissionController(): LocationPermissionController
--------------------------------------------------------------------------------
/demo/composeApp/src/commonMain/kotlin/geocoder/PlatformGeocoderFallbackScreen.kt:
--------------------------------------------------------------------------------
1 | package geocoder
2 |
3 | import androidx.compose.runtime.Composable
4 | import androidx.compose.runtime.derivedStateOf
5 | import androidx.compose.runtime.getValue
6 | import androidx.compose.runtime.mutableStateOf
7 | import androidx.compose.runtime.remember
8 | import androidx.compose.runtime.setValue
9 | import cafe.adriel.voyager.core.screen.Screen
10 | import createGeocoder
11 |
12 | /**
13 | * This screen demonstrates how to support Mobile with its native geocoder, as well as falling back
14 | * to an http based geocoder on other platforms.
15 | */
16 | class PlatformGeocoderFallbackScreen : Screen {
17 |
18 | @Composable
19 | override fun Content() {
20 | var apiKey by remember { mutableStateOf("") }
21 | val geocoder by remember(apiKey) {
22 | derivedStateOf { createGeocoder(apiKey) }
23 | }
24 |
25 | GeocoderContent(
26 | title = "Google Maps",
27 | geocoder = geocoder,
28 | apiKey = apiKey,
29 | updateApiKey = { apiKey = it },
30 | )
31 | }
32 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Jordon de Hoog
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/compass-autocomplete-mobile/src/commonMain/kotlin/dev/jordond/compass/autocomplete/MobileAutocomplete.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.autocomplete
2 |
3 | import dev.jordond.compass.Place
4 | import dev.jordond.compass.autocomplete.mobile.mobile
5 | import kotlinx.coroutines.CoroutineDispatcher
6 | import kotlinx.coroutines.Dispatchers
7 |
8 | public fun Autocomplete(
9 | options: AutocompleteOptions = AutocompleteOptions(),
10 | dispatcher: CoroutineDispatcher = Dispatchers.Default,
11 | ): Autocomplete = MobileAutocomplete(options, dispatcher)
12 |
13 | @Suppress("FunctionName")
14 | public fun MobileAutocomplete(
15 | options: AutocompleteOptions = AutocompleteOptions(),
16 | dispatcher: CoroutineDispatcher = Dispatchers.Default,
17 | ): Autocomplete = Autocomplete.mobile(options, dispatcher)
18 |
19 | public fun Autocomplete.Companion.mobile(
20 | options: AutocompleteOptions = AutocompleteOptions(),
21 | dispatcher: CoroutineDispatcher = Dispatchers.Default,
22 | ): Autocomplete = Autocomplete(
23 | service = AutocompleteService.mobile(),
24 | options = options,
25 | dispatcher = dispatcher
26 | )
--------------------------------------------------------------------------------
/demo/composeApp/src/androidMain/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
13 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/compass-permissions-mobile/build.gradle.kts:
--------------------------------------------------------------------------------
1 | import dev.jordond.compass.convention.Platforms
2 | import dev.jordond.compass.convention.configureMultiplatform
3 |
4 | plugins {
5 | alias(libs.plugins.android.library)
6 | alias(libs.plugins.multiplatform)
7 | alias(libs.plugins.poko)
8 | alias(libs.plugins.dokka)
9 | alias(libs.plugins.publish)
10 | alias(libs.plugins.convention.multiplatform)
11 | }
12 |
13 | configureMultiplatform(Platforms.Mobile)
14 |
15 | kotlin {
16 | sourceSets {
17 | commonMain.dependencies {
18 | api(projects.compassCore)
19 | api(projects.compassPermissions)
20 | implementation(libs.kotlinx.coroutines.core)
21 | }
22 |
23 | androidMain.dependencies {
24 | implementation(projects.compassToolsAndroid)
25 | api(libs.play.services.location)
26 | implementation(libs.androidx.activity)
27 | implementation(libs.androidx.fragment)
28 | implementation(libs.androidx.startup)
29 |
30 | }
31 |
32 | iosMain.dependencies {
33 | implementation(libs.kotlinx.atomicfu)
34 | }
35 | }
36 | }
--------------------------------------------------------------------------------
/.run/demo.desktop.run.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | true
20 | true
21 | false
22 | false
23 |
24 |
25 |
--------------------------------------------------------------------------------
/docs/misc/demo.md:
--------------------------------------------------------------------------------
1 | # 📲 Demo
2 |
3 | A demo is available in the `/demo` folder.
4 |
5 | ### Before running
6 |
7 | * Check your system with [KDoctor](https://github.com/Kotlin/kdoctor)
8 | * Install the [Kotlin Multiplatform Mobile plugin](https://plugins.jetbrains.com/plugin/14936-kotlin-multiplatform-mobile)
9 |
10 | ### Running
11 |
12 | Clone the repository and open it in Android Studio. In the "Run configuration" drop-down you should see the following:
13 |
14 | * demo.composeApp
15 | * demo.browser
16 | * demo.desktop
17 | * demo.ios
18 |
19 | Select the target you want and click Run.
20 |
21 | ### Manual
22 |
23 | To manually run the demo you can do the following:
24 |
25 | #### Android
26 |
27 | To run the application on android device/emulator:
28 |
29 | * open project in Android Studio and run imported android run configuration
30 |
31 | To build the application bundle:
32 |
33 | * run `./gradlew :demo:composeApp:assembleDebug`
34 | * find `.apk` file in `demo/composeApp/build/outputs/apk/debug/composeApp-debug.apk`
35 |
36 | #### iOS
37 |
38 | To run the application on iPhone device/simulator:
39 |
40 | * Open `iosApp/iosApp.xcworkspace` in Xcode and run standard configuration
41 |
--------------------------------------------------------------------------------
/docs/geocoding/web-api-service/google-maps.md:
--------------------------------------------------------------------------------
1 | # Google Maps
2 |
3 | To use[ Google Maps API](https://developers.google.com/maps/documentation/geocoding/overview) as your geocoding backend you will need to do the following:
4 |
5 | ### Obtain an API key
6 |
7 | 1. First you will need to [create a cloud project and enable the API](https://developers.google.com/maps/documentation/geocoding/cloud-setup)
8 | 2. Create the [API key](https://developers.google.com/maps/documentation/geocoding/get-api-key)
9 | 3. Copy your key
10 |
11 | ### Create the Geocoder
12 |
13 | Simple pass your API key to the `Geocoder` function:
14 |
15 | ```kotlin
16 | val geocoder = GoogleMapsGeocoder(apiKey = "your-api-key")
17 |
18 | // Or this helper function
19 | val geocoder = Geocoder(apiKey = "your-api-key")
20 | ```
21 |
22 | #### Customizing Google Maps request
23 |
24 | The Google Maps geocoding API has some [optional parameters](https://developers.google.com/maps/documentation/geocoding/requests-geocoding). You can modify these by passing them into the `Geocoder` function:
25 |
26 | ```kotlin
27 | val geocoder = GoogleMapsGeocoder(apiKey = "your-api-key") {
28 | locationTypes(GoogleMapsLocationType.GeometricCenter)
29 | }
30 | ```
31 |
--------------------------------------------------------------------------------
/docs/autocomplete/geocoding/android-ios.md:
--------------------------------------------------------------------------------
1 | # 📱 Android / iOS
2 |
3 | Compass supports Autocomplete natively on Android and iOS by using the built in Geocoding services.
4 |
5 | {% hint style="info" %}
6 | If your project supports both Mobile and other targets, you need to configure your setup to provide
7 | a `Autocomplete` for each platform. Check
8 | out [mixed-platforms.md](../usage/mixed-platforms.md "mention")for more information.
9 | {% endhint %}
10 |
11 | ## Getting started
12 |
13 | To get started with Autocomplete on Android and iOS, you need to create an instance
14 | of `Autocomplete`:
15 |
16 | ```kotlin
17 | val autocomplete = Autocomplete.mobile()
18 | ```
19 |
20 | Then you can use the `autocomplete` instance:
21 |
22 | ```kotlin
23 | val results = mutableListOf()
24 |
25 | suspend fun search(query: String) {
26 | val result = autocomplete.getOrNull() ?: return
27 | if (result.isNotEmpty()) {
28 | results.clear()
29 | results.addAll(result)
30 | }
31 | }
32 | ```
33 |
34 | {% hint style="info" %}
35 | The Mobile Autocomplete uses the device's built-in Geocoding services to provide the results. These
36 | results can vary in quality and quantity.
37 | {% endhint %}
--------------------------------------------------------------------------------
/demo/README.md:
--------------------------------------------------------------------------------
1 | # Material Kolor Demo
2 |
3 |
4 |
5 | ## Before running!
6 |
7 | - check your system with [KDoctor](https://github.com/Kotlin/kdoctor)
8 | - install JDK 8 on your machine
9 | - install
10 | the [Kotlin Multiplatform Mobile plugin](https://plugins.jetbrains.com/plugin/14936-kotlin-multiplatform-mobile)
11 |
12 | ## Running
13 |
14 | Open the project in Android Studio and let it sync the project. In the configuration dropdown should
15 | be the following:
16 |
17 | - androidApp
18 | - iosApp
19 |
20 | You can select a configuration and run it, or follow the steps below.
21 |
22 | ### Android
23 |
24 | To run the application on android device/emulator:
25 |
26 | - open project in Android Studio and run imported android run configuration
27 |
28 | To build the application bundle:
29 |
30 | - run `./gradlew :composeApp:assembleDebug`
31 | - find `.apk` file in `composeApp/build/outputs/apk/debug/composeApp-debug.apk`
32 |
33 | ### iOS
34 |
35 | To run the application on iPhone device/simulator:
36 |
37 | - Open `iosApp/iosApp.xcworkspace` in Xcode and run standard configuration
38 | - Or
39 | use [Kotlin Multiplatform Mobile plugin](https://plugins.jetbrains.com/plugin/14936-kotlin-multiplatform-mobile)
40 | for Android Studio
41 |
--------------------------------------------------------------------------------
/docs/geocoder/overview.md:
--------------------------------------------------------------------------------
1 | # 🌎 Overview
2 |
3 | Geocoding is the process of turning coordinates into a Place (reverse), or turning an address name into coordinates.
4 |
5 | Compass offers the following features:
6 |
7 | * Reverse: Turn coordinates (latitude, longitude) into a Place object.
8 | * Forward: Turn an address search query into coordinates.
9 | * Android and iOS support, using built-in services.
10 | * Support for web based API services.
11 | * Currently support for [Google Maps ](https://developers.google.com/maps/documentation/geocoding)and [Mapbox](https://docs.mapbox.com/#search) is included.
12 | * Easily create a wrapper for your own API.
13 |
14 | {% hint style="info" %}
15 | Built-in services are used for Android and iOS. That means that there is restrictions to its usage. You can be throttled or blocked from using the geocoding services if you make too many requests in a short period of time.
16 | {% endhint %}
17 |
18 | ### Quick start
19 |
20 | ```kotlin
21 | fun getPlaceFromCoordinates(lat: Long, lng: Long): Place? {
22 | val geocoder = Geocoder()
23 | return geocoder.placeOrNull()
24 | }
25 | ```
26 |
27 | In this quick example the `Geocoder()` function is a convenience function for creating a `Geocoder`
28 | object. Each of the `geocoder-*` artifacts provide one.
29 |
--------------------------------------------------------------------------------
/compass-permissions-mobile/src/androidMain/kotlin/dev/jordond/compass/permissions/mobile/internal/activity/ActivityLifecycleObserver.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.permissions.mobile.internal.activity
2 |
3 | import android.app.Activity
4 | import android.app.Application
5 | import android.os.Bundle
6 | import android.util.Log
7 | import androidx.activity.ComponentActivity
8 |
9 | internal fun createActivityLifecycleObserver(
10 | block: (ComponentActivity) -> Unit,
11 | ): Application.ActivityLifecycleCallbacks = object : Application.ActivityLifecycleCallbacks {
12 |
13 | override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
14 | if (activity is ComponentActivity) {
15 | block(activity)
16 | } else {
17 | Log.e("CompassTools", "Activity is not a ComponentActivity. Cannot attach lifecycle observer.")
18 | }
19 | }
20 |
21 | override fun onActivityStarted(activity: Activity) {}
22 | override fun onActivityResumed(activity: Activity) {}
23 | override fun onActivityPaused(activity: Activity) {}
24 | override fun onActivityStopped(activity: Activity) {}
25 | override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {}
26 | override fun onActivityDestroyed(activity: Activity) {}
27 | }
--------------------------------------------------------------------------------
/compass-tools-web/build.gradle.kts:
--------------------------------------------------------------------------------
1 | import dev.jordond.compass.convention.configureMultiplatform
2 |
3 | plugins {
4 | alias(libs.plugins.android.library)
5 | alias(libs.plugins.multiplatform)
6 | alias(libs.plugins.poko)
7 | alias(libs.plugins.dokka)
8 | alias(libs.plugins.publish)
9 | alias(libs.plugins.convention.multiplatform)
10 | }
11 |
12 | configureMultiplatform()
13 | kotlin {
14 | sourceSets {
15 | commonMain.dependencies {
16 | implementation(projects.compassCore)
17 |
18 | implementation(libs.kermit)
19 | implementation(libs.kotlinx.coroutines.core)
20 | api(libs.kotlinx.serialization.json)
21 | api(libs.ktor.client.core)
22 | api(libs.ktor.client.contentNegotiation)
23 | api(libs.ktor.client.logging)
24 | api(libs.ktor.serialization.json)
25 | }
26 |
27 | androidMain.dependencies {
28 | api(libs.ktor.client.android)
29 | }
30 |
31 | appleMain.dependencies {
32 | api(libs.ktor.client.darwin)
33 | }
34 |
35 | jsMain.dependencies {
36 | api(libs.ktor.client.js)
37 | }
38 |
39 | jvmMain.dependencies {
40 | api(libs.ktor.client.okhttp)
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/docs/geolocation/location.md:
--------------------------------------------------------------------------------
1 | # 🌐 Location
2 |
3 | The result from Geolocation operations will return a `Location` object, which looks like this:
4 |
5 | ```kotlin
6 | class Location(
7 | public val coordinates: Coordinates,
8 | public val accuracy: Double,
9 | public val azimuth: Azimuth?,
10 | public val speed: Speed?,
11 | public val altitude: Altitude?,
12 | public val timestampMillis: Long,
13 | )
14 | ```
15 |
16 | You can read the [KDocs here](https://docs.compass.jordond.dev/compass-core/dev.jordond.compass/-location/index.html).
17 |
18 | ### Detailed location data
19 |
20 | Depending on the `Priority` you use to request the location, determines what kind of data is present.
21 |
22 | All location results will return the coordinates and the accuracy of those coordinates in meters. In order to get the other values you will need to use a more accurate `Priority`, like so:
23 |
24 | ```kotlin
25 | val location: Location = geolocator.current(Priority.HighAccuracy)
26 | ```
27 |
28 | There is still a chance those values will be `null`, since it is up to the device to determine what data is returned.
29 |
30 | {% hint style="info" %}
31 | On Android the `Priority.HighAccuracy` requires the `ACCESS_FINE_LOCATION` permission. This will be requested automatically by Compass.
32 | {% endhint %}
33 |
--------------------------------------------------------------------------------
/demo/composeApp/src/commonMain/kotlin/geocoder/MapboxGeocoderScreen.kt:
--------------------------------------------------------------------------------
1 | package geocoder
2 |
3 | import androidx.compose.runtime.Composable
4 | import androidx.compose.runtime.derivedStateOf
5 | import androidx.compose.runtime.getValue
6 | import androidx.compose.runtime.mutableStateOf
7 | import androidx.compose.runtime.remember
8 | import androidx.compose.runtime.saveable.rememberSaveable
9 | import androidx.compose.runtime.setValue
10 | import cafe.adriel.voyager.core.screen.Screen
11 | import dev.jordond.compass.geocoder.MapboxGeocoder
12 | import dev.jordond.compass.tools.web.HttpApiEndpoint
13 |
14 | /**
15 | * This screen demonstrates how to use the Mapbox Geocoder for all platforms.
16 | */
17 | class MapboxGeocoderScreen : Screen {
18 |
19 | @Composable
20 | override fun Content() {
21 | var apiKey by rememberSaveable { mutableStateOf("") }
22 | val geocoder by remember(apiKey) {
23 | derivedStateOf {
24 | MapboxGeocoder(
25 | apiKey = apiKey,
26 | client = HttpApiEndpoint.httpClient(enableLogging = true)
27 | )
28 | }
29 | }
30 |
31 | GeocoderContent(
32 | title = "Mapbox",
33 | geocoder = geocoder,
34 | apiKey = apiKey,
35 | updateApiKey = { apiKey = it },
36 | )
37 | }
38 | }
--------------------------------------------------------------------------------
/docs/SUMMARY.md:
--------------------------------------------------------------------------------
1 | # Table of contents
2 |
3 | * [🧭 Compass](README.md)
4 | * [🖥️ Artifacts](supported-platforms.md)
5 | * [🚗 Roadmap](roadmap.md)
6 |
7 | ## Setup
8 |
9 | * [📇 All Dependencies](setup/add-dependencies.md)
10 | * [🤖 Android / iOS](setup/android-ios.md)
11 | * [📱 Mixed platforms](usage/mixed-platforms.md)
12 |
13 | ## Geocoding
14 |
15 | * [🌎 Geocoding Overview](geocoder/overview.md)
16 | * [📈 Web API Service](geocoding/web-api-service/README.md)
17 | * [Google Maps](geocoding/web-api-service/google-maps.md)
18 | * [Mapbox](geocoding/web-api-service/mapbox.md)
19 | * [❓ Request a Geocoder API](geocoding/request-a-geocoder-api.md)
20 |
21 | ## Geolocation
22 |
23 | * [📍 Geolocation Overview](geolocation/overview.md)
24 | * [🔍 Geolocator](geolocation/geolocator.md)
25 | * [🌐 Location](geolocation/location.md)
26 | * [📱 Android / iOS](geolocation/android-ios.md)
27 | * [🖥️ Browser](geolocation/browser.md)
28 |
29 | ## Autocomplete
30 |
31 | * [🔍 Autocomplete Overview](autocomplete/overview.md)
32 | * [🌎 Via Geocoding](autocomplete/via-geocoding.md)
33 | * [📱 Android / iOS](autocomplete/geocoding/android-ios.md)
34 | * [📈 Web API Service](autocomplete/geocoding/web-api-service.md)
35 |
36 | ## Misc
37 |
38 | * [📲 Demo](misc/demo.md)
39 | * [💾 Contributing](misc/contributing.md)
40 | * [🔧 Compass Reference](https://docs.compass.jordond.dev)
41 |
--------------------------------------------------------------------------------
/compass-geocoder-mobile/src/iosMain/kotlin/dev/jordond/compass/geocoder/mobile/internal/Operation.ios.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.geocoder.mobile.internal
2 |
3 | import dev.jordond.compass.geocoder.exception.GeocodeException
4 | import kotlinx.coroutines.suspendCancellableCoroutine
5 | import platform.CoreLocation.CLGeocodeCompletionHandler
6 | import platform.CoreLocation.CLGeocoder
7 | import platform.CoreLocation.CLPlacemark
8 | import kotlin.coroutines.resume
9 | import kotlin.coroutines.resumeWithException
10 |
11 | internal suspend fun geocodeOperation(
12 | block: CLGeocoder.(listener: CLGeocodeCompletionHandler) -> Unit,
13 | ): List {
14 | val geocoder = CLGeocoder()
15 |
16 | @Suppress("UNCHECKED_CAST")
17 | return suspendCancellableCoroutine { continuation ->
18 | val completionHandler: CLGeocodeCompletionHandler = { result, error ->
19 | when {
20 | error != null ->
21 | continuation.resumeWithException(GeocodeException(error.localizedDescription))
22 | else -> {
23 | continuation.resume((result as? List) ?: emptyList())
24 | }
25 | }
26 | }
27 |
28 | geocoder.block(completionHandler)
29 |
30 | continuation.invokeOnCancellation {
31 | geocoder.cancelGeocode()
32 | }
33 | }
34 | }
--------------------------------------------------------------------------------
/compass-geolocation-mobile/build.gradle.kts:
--------------------------------------------------------------------------------
1 | import dev.jordond.compass.convention.Platforms
2 | import dev.jordond.compass.convention.configureMultiplatform
3 |
4 | plugins {
5 | alias(libs.plugins.android.library)
6 | alias(libs.plugins.multiplatform)
7 | alias(libs.plugins.poko)
8 | alias(libs.plugins.dokka)
9 | alias(libs.plugins.publish)
10 | alias(libs.plugins.convention.multiplatform)
11 | }
12 |
13 | configureMultiplatform(Platforms.Mobile)
14 |
15 | kotlin {
16 | sourceSets {
17 | commonMain.dependencies {
18 | api(projects.compassCore)
19 | implementation(projects.compassPermissions)
20 | implementation(projects.compassPermissionsMobile)
21 | implementation(projects.compassGeolocation)
22 |
23 | implementation(libs.kotlinx.coroutines.core)
24 | implementation(libs.kermit)
25 | }
26 |
27 | androidMain.dependencies {
28 | implementation(projects.compassToolsAndroid)
29 | api(libs.play.services.location)
30 | implementation(libs.play.services.coroutines)
31 | implementation(libs.androidx.activity)
32 | implementation(libs.androidx.fragment)
33 | implementation(libs.androidx.startup)
34 | }
35 |
36 | iosMain.dependencies {
37 | implementation(libs.kotlinx.atomicfu)
38 | }
39 | }
40 | }
--------------------------------------------------------------------------------
/demo/composeApp/src/nonMobileMain/kotlin/Platform.nonMobile.kt:
--------------------------------------------------------------------------------
1 | import dev.jordond.compass.geocoder.NotSupportedPlatformGeocoder
2 | import dev.jordond.compass.geocoder.PlatformGeocoder
3 | import dev.jordond.compass.geocoder.web.GoogleMapsPlatformGeocoder
4 | import dev.jordond.compass.geocoder.web.HttpApiPlatformGeocoder
5 | import dev.jordond.compass.geocoder.web.parameter.GoogleMapsLocationType
6 | import dev.jordond.compass.geolocation.Locator
7 | import dev.jordond.compass.geolocation.NotSupportedLocator
8 | import dev.jordond.compass.tools.web.HttpApiEndpoint
9 |
10 | /**
11 | * Normally you would use a custom [PlatformGeocoder] here or a [HttpApiPlatformGeocoder]. But in
12 | * this demo that is handled by the different screens that demonstrate different geocoder APIs.
13 | */
14 | actual fun getPlatformGeocoder(): PlatformGeocoder {
15 | return NotSupportedPlatformGeocoder
16 | }
17 |
18 | actual fun getPlatformGeocoderOrFallback(apiKey: String): PlatformGeocoder {
19 | return GoogleMapsPlatformGeocoder(
20 | apiKey = apiKey,
21 | client = HttpApiEndpoint.httpClient(enableLogging = true)
22 | ) {
23 | locationType(GoogleMapsLocationType.Approximate)
24 | }
25 | }
26 |
27 | actual fun getPlatformLocator(): Locator {
28 | return NotSupportedLocator
29 | }
30 |
31 | actual val canShowAppSettings: Boolean = false
32 |
33 | actual fun showAppSettings() {
34 | // No-op
35 | }
--------------------------------------------------------------------------------
/demo/composeApp/src/commonMain/kotlin/geocoder/OpenCageGeocoderScreen.kt:
--------------------------------------------------------------------------------
1 | package geocoder
2 |
3 | import androidx.compose.runtime.Composable
4 | import androidx.compose.runtime.derivedStateOf
5 | import androidx.compose.runtime.getValue
6 | import androidx.compose.runtime.mutableStateOf
7 | import androidx.compose.runtime.remember
8 | import androidx.compose.runtime.saveable.rememberSaveable
9 | import androidx.compose.runtime.setValue
10 | import cafe.adriel.voyager.core.screen.Screen
11 | import dev.jordond.compass.geocoder.MapboxGeocoder
12 | import dev.jordond.compass.geocoder.OpenCageGeocoder
13 | import dev.jordond.compass.tools.web.HttpApiEndpoint
14 |
15 | /**
16 | * This screen demonstrates how to use the Mapbox Geocoder for all platforms.
17 | */
18 | class OpenCageGeocoderScreen : Screen {
19 |
20 | @Composable
21 | override fun Content() {
22 | var apiKey by rememberSaveable { mutableStateOf("") }
23 | val geocoder by remember(apiKey) {
24 | derivedStateOf {
25 | OpenCageGeocoder(
26 | apiKey = apiKey,
27 | client = HttpApiEndpoint.httpClient(enableLogging = true)
28 | )
29 | }
30 | }
31 |
32 | GeocoderContent(
33 | title = "OpenCage",
34 | geocoder = geocoder,
35 | apiKey = apiKey,
36 | updateApiKey = { apiKey = it },
37 | )
38 | }
39 | }
--------------------------------------------------------------------------------
/compass-geocoder-web/build.gradle.kts:
--------------------------------------------------------------------------------
1 | import dev.jordond.compass.convention.configureMultiplatform
2 |
3 | plugins {
4 | alias(libs.plugins.android.library)
5 | alias(libs.plugins.multiplatform)
6 | alias(libs.plugins.poko)
7 | alias(libs.plugins.kotlinx.serialization)
8 | alias(libs.plugins.dokka)
9 | alias(libs.plugins.publish)
10 | alias(libs.plugins.convention.multiplatform)
11 | }
12 |
13 | configureMultiplatform()
14 |
15 | kotlin {
16 | sourceSets {
17 | commonMain.dependencies {
18 | api(projects.compassCore)
19 | api(projects.compassToolsWeb)
20 | implementation(projects.compassGeocoder)
21 |
22 | implementation(libs.kermit)
23 | implementation(libs.kotlinx.coroutines.core)
24 | api(libs.kotlinx.serialization.json)
25 | api(libs.ktor.client.core)
26 | api(libs.ktor.client.contentNegotiation)
27 | api(libs.ktor.client.logging)
28 | api(libs.ktor.serialization.json)
29 | }
30 |
31 | androidMain.dependencies {
32 | api(libs.ktor.client.android)
33 | }
34 |
35 | appleMain.dependencies {
36 | api(libs.ktor.client.darwin)
37 | }
38 |
39 | jsMain.dependencies {
40 | api(libs.ktor.client.js)
41 | }
42 |
43 | jvmMain.dependencies {
44 | api(libs.ktor.client.okhttp)
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/compass-geocoder/src/commonMain/kotlin/dev/jordond/compass/geocoder/Geocoder.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.geocoder
2 |
3 | import dev.jordond.compass.geocoder.internal.DefaultGeocoder
4 | import kotlinx.coroutines.CoroutineDispatcher
5 | import kotlinx.coroutines.Dispatchers
6 |
7 | /**
8 | * Provides geocoding operations:
9 | *
10 | * - Convert an address to a latitude and longitude (forward geocoding)
11 | * - Convert a latitude and longitude to an address (reverse geocoding)
12 | */
13 | public interface Geocoder : ForwardGeocoder, ReverseGeocoder {
14 |
15 | public val platformGeocoder: PlatformGeocoder
16 |
17 | /**
18 | * Check if the geocoder is available.
19 | *
20 | * @return `true` if the geocoder is available, `false` otherwise.
21 | */
22 | public fun isAvailable(): Boolean
23 |
24 | public companion object {
25 |
26 | @Suppress("ConstPropertyName")
27 | public const val DefaultMaxResults: Int = 5
28 | }
29 | }
30 |
31 | /**
32 | * Create a new [Geocoder] instance for geocoding operations.
33 | *
34 | * @param platformGeocoder The platform-specific geocoder to use.
35 | * @param dispatcher The [CoroutineDispatcher] to use for geocoding operations.
36 | * @return A new [Geocoder] instance.
37 | */
38 | public fun Geocoder(
39 | platformGeocoder: PlatformGeocoder,
40 | dispatcher: CoroutineDispatcher = Dispatchers.Default,
41 | ): Geocoder = DefaultGeocoder(platformGeocoder, dispatcher)
--------------------------------------------------------------------------------
/compass-geolocation/src/commonMain/kotlin/dev/jordond/compass/geolocation/TrackingUpdate.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.geolocation
2 |
3 | import dev.drewhamilton.poko.Poko
4 | import dev.jordond.compass.Location
5 |
6 | /**
7 | * Represents the current status of a [Geolocator] tracking operation.
8 | */
9 | public sealed interface TrackingStatus {
10 |
11 | /**
12 | * Check whether the tracking status is active.
13 | *
14 | * @returns `true` if the tracking status is [Tracking] or [Update], `false` otherwise.
15 | */
16 | public val isActive: Boolean
17 | get() = this is Tracking || this is Update
18 |
19 | /**
20 | * Tracker is idle and waiting to start.
21 | */
22 | public data object Idle : TrackingStatus
23 |
24 | /**
25 | * Tracker is actively tracking the user's location, and waiting for an update.
26 | */
27 | public data object Tracking : TrackingStatus
28 |
29 | /**
30 | * Tracker has received an update to the user's location.
31 | *
32 | * @property location The updated location.
33 | */
34 | @Poko
35 | public class Update(public val location: Location) : TrackingStatus
36 |
37 | /**
38 | * Tracker has encountered an error and is no longer active.
39 | *
40 | * @property cause The error that caused the tracking to stop.
41 | */
42 | @Poko
43 | public class Error(public val cause: GeolocatorResult.Error) : TrackingStatus
44 | }
--------------------------------------------------------------------------------
/compass-permissions/src/commonMain/kotlin/dev/jordond/compass/permissions/PermissionState.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.permissions
2 |
3 | import dev.jordond.compass.permissions.exception.PermissionDeniedException
4 | import dev.jordond.compass.permissions.exception.PermissionDeniedForeverException
5 |
6 | /**
7 | * Describes the state of the location permission.
8 | */
9 | public enum class PermissionState {
10 |
11 | /**
12 | * The permission has not been determined.
13 | */
14 | NotDetermined,
15 |
16 | /**
17 | * The permission has been granted.
18 | */
19 | Granted,
20 |
21 | /**
22 | * The permission has been denied.
23 | */
24 | Denied,
25 |
26 | /**
27 | * The permission has been denied forever.
28 | *
29 | * This means the user has denied the permission and selected "Don't ask again". The user must
30 | * manually enable the permission in the settings.
31 | */
32 | DeniedForever,
33 | }
34 |
35 | /**
36 | * Throws an exception if the permission state is not [PermissionState.Granted] or
37 | * [PermissionState.NotDetermined].
38 | */
39 | public fun PermissionState.throwOnError() {
40 | when (this) {
41 | PermissionState.NotDetermined,
42 | PermissionState.Granted,
43 | -> Unit
44 | PermissionState.Denied -> throw PermissionDeniedException()
45 | PermissionState.DeniedForever -> throw PermissionDeniedForeverException()
46 | }
47 | }
--------------------------------------------------------------------------------
/compass-geolocation-browser/src/commonMain/kotlin/dev/jordond/compass/geolocation/browser/api/model/GeolocationPositionError.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.geolocation.browser.api.model
2 |
3 | /**
4 | * Represents an error that occurred while retrieving the geolocation.
5 | *
6 | * [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/API/GeolocationPositionError)
7 | *
8 | * @property code The error code.
9 | * @property message A human-readable message describing the error.
10 | */
11 | public external class GeolocationPositionError {
12 |
13 | /**
14 | * The error code.
15 | *
16 | * [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/API/GeolocationPositionError/code)
17 | *
18 | * The following values are supported:
19 | * - 1: Permission denied
20 | * - 2: Position unavailable
21 | * - 3: Timeout
22 | */
23 | public var code: Int
24 |
25 | /**
26 | * A human-readable message describing the error.
27 | *
28 | * [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/API/GeolocationPositionError/message)
29 | */
30 | public var message: String
31 | }
32 |
33 | internal enum class GeolocationPositionErrorCode(val code: Int) {
34 | PermissionDenied(1),
35 | PositionUnavailable(2),
36 | Timeout(3)
37 | }
38 |
39 | internal fun GeolocationPositionError.value(): GeolocationPositionErrorCode =
40 | GeolocationPositionErrorCode.entries.first { it.code == code }
41 |
--------------------------------------------------------------------------------
/docs/misc/contributing.md:
--------------------------------------------------------------------------------
1 | # 💾 Contributing
2 |
3 | Compass is open to contributions, if there is a bug or feature. Feel free to open up a PR or an issue.
4 |
5 | Please make every effort to follow existing conventions and style in order to keep the code as readable as possible.
6 |
7 | Contribute code changes through GitHub by forking the repository and sending a pull request. We squash all pull requests on merge.
8 |
9 | If you add, remove, or change `public` objects, make sure you update the binary-compatibility definitions by running the following command:
10 |
11 | ```bash
12 | ./gradlew apiDump
13 | ```
14 |
15 | Then commit and push the changed files.
16 |
17 | ### Custom Geocoder API
18 |
19 | Compass includes a template module: `compass-geocoder-web-template`
20 |
21 | If there is a Geocoding Web API that you would wish to add to Compass, follow these steps:
22 |
23 | 1. Clone the repo: `git clone git@github.com:jordond/compass`
24 | 2. Make a copy of the `compass-geocoder-web-template` module
25 | 3. Rename the copy to match the web service you're adding:
26 | 1. ex: `compass-geocoder-web-myapi`
27 | 4. Search for the word "Template" in the module and replace it with your service name
28 | 5. Implement the `HttpApiPlatformGeocoder` interface
29 | 6. Customize the URL, query parameters, and the response models
30 | 7. Implement the `ForwardEndpoint` and `ReverseEndpoint`
31 | 8. Finish the implementation
32 | 9. Run `./gradlew apiDump` and commit the changes
33 | 10. Push & open a Pull Request
34 |
35 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Gradle
2 | org.gradle.jvmargs=-Xmx4096M -Dfile.encoding=UTF-8 -Dkotlin.daemon.jvm.options\="-Xmx4096M"
3 | org.gradle.caching=true
4 | org.gradle.configuration-cache=true
5 |
6 | # Kotlin
7 | kotlin.code.style=official
8 |
9 | # Android
10 | android.useAndroidX=true
11 | android.nonTransitiveRClass=true
12 |
13 | # MPP
14 | kotlin.mpp.stability.nowarn=true
15 | kotlin.mpp.enableCInteropCommonization=true
16 | kotlin.mpp.androidSourceSetLayoutVersion=2
17 |
18 | # Compose
19 | org.jetbrains.compose.experimental.uikit.enabled=true
20 | org.jetbrains.compose.experimental.wasm.enabled=true
21 | org.jetbrains.compose.experimental.jscanvas.enabled=true
22 | org.jetbrains.compose.experimental.macos.enabled=true
23 |
24 | # Maven
25 | SONATYPE_HOST=CENTRAL_PORTAL
26 | SONATYPE_AUTOMATIC_RELEASE=true
27 | RELEASE_SIGNING_ENABLED=true
28 |
29 | GROUP=dev.jordond.compass
30 |
31 | POM_NAME=compass
32 | POM_DESCRIPTION=A multiplatform location library.
33 | POM_INCEPTION_YEAR=2024
34 | POM_URL=https://github.com/jordond/compass
35 |
36 | POM_LICENCE_NAME=The MIT License
37 | POM_LICENCE_URL=https://github.com/jordond/compass/blob/master/LICENSE
38 | POM_LICENCE_DIST=repo
39 |
40 | POM_SCM_URL=https://github.com/jordond/compass
41 | POM_SCM_CONNECTION=scm:git:https://github.com/jordond/compass.git
42 | POM_SCM_DEV_CONNECTION=scm:git:git@github.com:jordond/compass.git
43 |
44 | POM_DEVELOPER_ID=jordond
45 | POM_DEVELOPER_NAME=Jordon de Hoog
46 | POM_DEVELOPER_URL=https://github.com/jordond
47 | POM_DEVELOPER_EMAIL=jordon.dehoog@gmail.com
--------------------------------------------------------------------------------
/compass-permissions/src/commonMain/kotlin/dev/jordond/compass/permissions/LocationPermissionController.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.permissions
2 |
3 | import dev.jordond.compass.Priority
4 | import dev.jordond.compass.permissions.exception.PermissionDeniedException
5 | import dev.jordond.compass.permissions.exception.PermissionDeniedForeverException
6 |
7 | /**
8 | * Defines a controller for managing location permissions.
9 | */
10 | public interface LocationPermissionController {
11 |
12 | /**
13 | * Checks if the app has the necessary permissions to access the device's location.
14 | *
15 | * @return `true` if the app has the necessary permissions, `false` otherwise.
16 | */
17 | public fun hasPermission(): Boolean
18 |
19 | /**
20 | * Requests the necessary permissions to access the device's location.
21 | *
22 | * @return The state of the permission after the request.
23 | */
24 | public suspend fun requirePermissionFor(priority: Priority): PermissionState
25 |
26 | /**
27 | * Requests the necessary permissions to access the device's location, and throws an exception
28 | * if the permission is denied.
29 | *
30 | * @throws PermissionDeniedException If the permission is denied.
31 | * @throws PermissionDeniedForeverException If the permission is missing.
32 | */
33 | public suspend fun requirePermissionForOrThrow(priority: Priority) {
34 | requirePermissionFor(priority).also { it.throwOnError() }
35 | }
36 |
37 | public companion object
38 | }
39 |
--------------------------------------------------------------------------------
/demo/composeApp/src/commonMain/kotlin/geocoder/GoogleMapsGeocoderScreen.kt:
--------------------------------------------------------------------------------
1 | package geocoder
2 |
3 | import androidx.compose.runtime.Composable
4 | import androidx.compose.runtime.derivedStateOf
5 | import androidx.compose.runtime.getValue
6 | import androidx.compose.runtime.mutableStateOf
7 | import androidx.compose.runtime.remember
8 | import androidx.compose.runtime.saveable.rememberSaveable
9 | import androidx.compose.runtime.setValue
10 | import cafe.adriel.voyager.core.screen.Screen
11 | import dev.jordond.compass.geocoder.GoogleMapsGeocoder
12 | import dev.jordond.compass.geocoder.web.parameter.GoogleMapsLocationType
13 | import dev.jordond.compass.tools.web.HttpApiEndpoint
14 |
15 | /**
16 | * This screen demonstrates how to use the Google Maps Geocoder for all platforms.
17 | */
18 | class GoogleMapsGeocoderScreen : Screen {
19 |
20 | @Composable
21 | override fun Content() {
22 | var apiKey by rememberSaveable { mutableStateOf("") }
23 | val geocoder by remember(apiKey) {
24 | derivedStateOf {
25 | GoogleMapsGeocoder(
26 | apiKey = apiKey,
27 | client = HttpApiEndpoint.httpClient(enableLogging = true)
28 | ) {
29 | locationType(GoogleMapsLocationType.Approximate)
30 | }
31 | }
32 | }
33 |
34 | GeocoderContent(
35 | title = "Google Maps",
36 | geocoder = geocoder,
37 | apiKey = apiKey,
38 | updateApiKey = { apiKey = it },
39 | )
40 | }
41 | }
--------------------------------------------------------------------------------
/compass-autocomplete/src/commonMain/kotlin/dev/jordond/compass/autocomplete/internal/DefaultAutocomplete.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.autocomplete.internal
2 |
3 | import dev.jordond.compass.autocomplete.Autocomplete
4 | import dev.jordond.compass.autocomplete.AutocompleteOptions
5 | import dev.jordond.compass.autocomplete.AutocompleteResult
6 | import dev.jordond.compass.autocomplete.AutocompleteService
7 | import kotlinx.coroutines.CancellationException
8 | import kotlinx.coroutines.CoroutineDispatcher
9 | import kotlinx.coroutines.withContext
10 |
11 | internal class DefaultAutocomplete(
12 | private val service: AutocompleteService,
13 | override val options: AutocompleteOptions,
14 | private val dispatcher: CoroutineDispatcher,
15 | ) : Autocomplete {
16 |
17 | override suspend fun search(query: String): AutocompleteResult {
18 | if (!service.isAvailable()) {
19 | return AutocompleteResult.NotSupported
20 | }
21 |
22 | try {
23 | if (query.length <= options.minimumQuery) {
24 | return AutocompleteResult.Success(emptyList())
25 | }
26 |
27 | val result = withContext(dispatcher) {
28 | service.search(query)
29 | }
30 |
31 | return AutocompleteResult.Success(result)
32 | } catch (cancellation: CancellationException) {
33 | throw cancellation
34 | } catch (cause: Throwable) {
35 | return AutocompleteResult.Failed(cause.message ?: "An unknown error occurred")
36 | }
37 | }
38 | }
--------------------------------------------------------------------------------
/compass-geocoder-mobile/src/iosMain/kotlin/dev/jordond/compass/geocoder/mobile/MobilePlatformGeocoder.ios.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.geocoder.mobile
2 |
3 | import dev.jordond.compass.Coordinates
4 | import dev.jordond.compass.Place
5 | import dev.jordond.compass.geocoder.mobile.internal.geocodeOperation
6 | import dev.jordond.compass.geocoder.mobile.internal.toCoordinates
7 | import dev.jordond.compass.geocoder.mobile.internal.toPlaces
8 | import platform.CoreLocation.CLLocation
9 | import platform.Foundation.NSLocale
10 |
11 | internal class IosPlatformGeocoder(private val locale: NSLocale?) : MobilePlatformGeocoder {
12 |
13 | override fun isAvailable(): Boolean = true
14 |
15 | override suspend fun reverse(latitude: Double, longitude: Double): List {
16 | val platformLocation = CLLocation(latitude, longitude)
17 | return geocodeOperation { listener ->
18 | reverseGeocodeLocation(platformLocation, locale, listener)
19 | }.toPlaces()
20 | }
21 |
22 | override suspend fun placeFromAddress(address: String): List {
23 | return geocodeOperation { listener -> geocodeAddressString(address, null, locale, listener) }.toPlaces()
24 | }
25 |
26 | override suspend fun forward(address: String): List {
27 | return geocodeOperation { listener ->
28 | geocodeAddressString(address, null, locale, listener)
29 | }.toCoordinates()
30 | }
31 | }
32 |
33 | internal actual fun createPlatformGeocoder(locale: String?): MobilePlatformGeocoder {
34 | return IosPlatformGeocoder(locale?.let(::NSLocale))
35 | }
--------------------------------------------------------------------------------
/compass-permissions-mobile/src/iosMain/kotlin/dev/jordond/compass/permissions/mobile/internal/LocationPermissionManagerDelegate.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.permissions.mobile.internal
2 |
3 | import platform.CoreLocation.CLAuthorizationStatus
4 | import platform.CoreLocation.CLLocationManager
5 | import platform.CoreLocation.CLLocationManagerDelegateProtocol
6 | import platform.darwin.NSObject
7 |
8 | internal class LocationPermissionManagerDelegate : NSObject(), CLLocationManagerDelegateProtocol {
9 |
10 | internal val manager = CLLocationManager()
11 |
12 | private var permissionCallback: ((CLAuthorizationStatus) -> Unit)? = null
13 | private var requestPermissionCallback: ((CLAuthorizationStatus) -> Unit)? = null
14 |
15 | init {
16 | manager.delegate = this
17 | }
18 |
19 | fun currentPermissionStatus(): CLAuthorizationStatus {
20 | return manager.authorizationStatus
21 | }
22 |
23 | fun monitorPermission(callback: (CLAuthorizationStatus) -> Unit) {
24 | permissionCallback = callback
25 | callback(manager.authorizationStatus)
26 | }
27 |
28 | override fun locationManagerDidChangeAuthorization(manager: CLLocationManager) {
29 | val status = manager.authorizationStatus
30 | permissionCallback?.invoke(status)
31 | requestPermissionCallback?.invoke(status)
32 | }
33 |
34 | fun requestPermission(callback: (CLAuthorizationStatus) -> Unit) {
35 | requestPermissionCallback = { status ->
36 | callback(status)
37 | requestPermissionCallback = null
38 | }
39 |
40 | manager.requestWhenInUseAuthorization()
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/compass-geocoder-web-googlemaps/src/commonMain/kotlin/dev/jordond/compass/geocoder/web/google/internal/ResultResponse.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.geocoder.web.google.internal
2 |
3 | import dev.jordond.compass.InternalCompassApi
4 | import kotlinx.serialization.SerialName
5 | import kotlinx.serialization.Serializable
6 |
7 | @InternalCompassApi
8 | @Serializable
9 | public data class ResultResponse(
10 | @SerialName("address_components")
11 | public val addressComponents: List = emptyList(),
12 |
13 | @SerialName("formatted_address")
14 | public val formattedAddress: String? = null,
15 |
16 | @SerialName("geometry")
17 | public val geometry: GeometryResponse? = null,
18 | )
19 |
20 | @InternalCompassApi
21 | @Serializable
22 | public data class AddressComponentResponse(
23 | @SerialName("long_name")
24 | val long: String,
25 |
26 | @SerialName("short_name")
27 | val short: String,
28 |
29 | @SerialName("types")
30 | val types: List = emptyList(),
31 | )
32 |
33 | internal enum class AddressComponentType(val value: String) {
34 | Name("route"),
35 | Thoroughfare("route"),
36 | Neighborhood("neighborhood"),
37 | Locality("locality"),
38 | Country("country"),
39 | AdministrativeAreaLevel1("administrative_area_level_1"),
40 | AdministrativeAreaLevel2("administrative_area_level_2"),
41 | AdministrativeAreaLevel3("administrative_area_level_3"),
42 | PostalCode("postal_code"),
43 | }
44 |
45 | internal fun List.find(
46 | component: AddressComponentType,
47 | ): AddressComponentResponse? {
48 | return find { it.types.contains(component.value) }
49 | }
--------------------------------------------------------------------------------
/compass-geocoder-web-template/src/commonMain/kotlin/dev/jordond/compass/geocoder/web/parameter/TemplateParameters.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.geocoder.web.parameter
2 |
3 | import dev.drewhamilton.poko.Poko
4 | import dev.jordond.compass.tools.web.parameter.QueryParameters
5 | import dev.jordond.compass.tools.web.parameter.QueryParametersBuilder
6 | import dev.jordond.compass.tools.web.parameter.parametersOf
7 |
8 | /**
9 | * Options for the TEMPLATE geocoder.
10 | *
11 | * See the []()
12 | * for more information.
13 | */
14 | @Poko
15 | public class TemplateParameters(
16 | public val foo: String? = null,
17 | ) : QueryParameters {
18 |
19 | override val parameters: Map = parametersOf(
20 | "foo" to foo,
21 | )
22 | }
23 |
24 | /**
25 | * A builder class to build [TemplateParameters].
26 | */
27 | @Suppress("MemberVisibilityCanBePrivate")
28 | public class TemplateParametersBuilder : QueryParametersBuilder {
29 |
30 | /**
31 | * @see TemplateParameters.locationType
32 | */
33 | private var foo: String? = null
34 |
35 | /**
36 | * Build the instance of [TemplateParameters].
37 | */
38 | override fun build(): TemplateParameters = TemplateParameters(
39 | foo = foo,
40 | )
41 | }
42 |
43 | /**
44 | * Create a [TemplateParameters] instance with the provided [block].
45 | *
46 | * @param block The builder block to build the instance of [TemplateParameters].
47 | * @return The instance of [TemplateParameters].
48 | */
49 | public fun templateParameters(
50 | block: TemplateParametersBuilder.() -> Unit,
51 | ): TemplateParameters = TemplateParametersBuilder().apply(block).build()
--------------------------------------------------------------------------------
/compass-geocoder/src/commonMain/kotlin/dev/jordond/compass/geocoder/PlatformGeocoder.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.geocoder
2 |
3 | import dev.jordond.compass.Coordinates
4 | import dev.jordond.compass.Place
5 |
6 | /**
7 | * Wrapper for platform-specific geocoding services.
8 | */
9 | public interface PlatformGeocoder {
10 |
11 | /**
12 | * Returns true if the geocoder is available on the current platform.
13 | *
14 | * @return `true` if the geocoder is available, false otherwise.
15 | */
16 | public fun isAvailable(): Boolean
17 |
18 | /**
19 | * Get a list of [Coordinates]s from an address.
20 | */
21 | public suspend fun forward(address: String): List
22 |
23 | /**
24 | * Get a list of [Place]s from latitude and longitude coordinates.
25 | *
26 | * @param latitude The latitude of the coordinates.
27 | * @param longitude The longitude of the coordinates.
28 | * @return The address of the coordinates or empty list if the address could not be found.
29 | */
30 | public suspend fun reverse(latitude: Double, longitude: Double): List
31 |
32 | public companion object
33 | }
34 |
35 | /**
36 | * A no-op [PlatformGeocoder] that is used when the platform does not support geocoding.
37 | *
38 | * This can be used as a fallback when the platform does not support geocoding.
39 | */
40 | public object NotSupportedPlatformGeocoder : PlatformGeocoder {
41 |
42 | override fun isAvailable(): Boolean = false
43 | override suspend fun forward(address: String): List = emptyList()
44 | override suspend fun reverse(latitude: Double, longitude: Double): List = emptyList()
45 | }
--------------------------------------------------------------------------------
/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
2 |
3 | pluginManagement {
4 | repositories {
5 | gradlePluginPortal()
6 | google()
7 | mavenCentral()
8 | }
9 |
10 | includeBuild("buildLogic")
11 | }
12 |
13 | dependencyResolutionManagement {
14 | @Suppress("UnstableApiUsage")
15 | repositories {
16 | google()
17 | mavenCentral()
18 | }
19 | }
20 |
21 | plugins {
22 | id("com.gradle.develocity") version "4.2.2"
23 | }
24 |
25 | develocity {
26 | buildScan {
27 | termsOfUseUrl.set("https://gradle.com/help/legal-terms-of-use")
28 | termsOfUseAgree.set("yes")
29 |
30 | publishing.onlyIf { context ->
31 | context.buildResult.failures.isNotEmpty() && !System.getenv("CI").isNullOrEmpty()
32 | }
33 | }
34 | }
35 |
36 | rootProject.name = "compass"
37 |
38 | include(
39 | ":compass-core",
40 | ":compass-autocomplete",
41 | ":compass-autocomplete-geocoder-googlemaps",
42 | ":compass-autocomplete-geocoder-mapbox",
43 | ":compass-autocomplete-mobile",
44 | ":compass-autocomplete-web",
45 | ":compass-geocoder",
46 | ":compass-geocoder-mobile",
47 | ":compass-geocoder-web",
48 | ":compass-geocoder-web-googlemaps",
49 | ":compass-geocoder-web-mapbox",
50 | ":compass-geocoder-web-opencage",
51 | ":compass-geocoder-web-template",
52 | ":compass-geolocation",
53 | ":compass-geolocation-mobile",
54 | ":compass-geolocation-browser",
55 | ":compass-permissions",
56 | ":compass-permissions-mobile",
57 | ":compass-tools-android",
58 | ":compass-tools-web",
59 | )
60 |
61 | include(":demo:composeApp")
62 |
--------------------------------------------------------------------------------
/compass-geocoder-mobile/src/androidMain/kotlin/dev/jordond/compass/geocoder/mobile/internal/Operation.android.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.geocoder.mobile.internal
2 |
3 | import android.annotation.TargetApi
4 | import android.location.Address
5 | import android.location.Geocoder
6 | import android.os.Build
7 | import dev.jordond.compass.geocoder.exception.GeocodeException
8 | import kotlinx.coroutines.Dispatchers
9 | import kotlinx.coroutines.withContext
10 | import kotlin.coroutines.resume
11 | import kotlin.coroutines.resumeWithException
12 | import kotlin.coroutines.suspendCoroutine
13 |
14 | internal suspend fun syncOperation(
15 | block: () -> MutableList?,
16 | ): List {
17 | val result: Result?> = withContext(Dispatchers.IO) {
18 | runCatching {
19 | block()
20 | }
21 | }
22 |
23 | val exception = result.exceptionOrNull()
24 | if (exception != null) throw GeocodeException(exception.message)
25 |
26 | return result.getOrNull() ?: emptyList()
27 | }
28 |
29 | @TargetApi(Build.VERSION_CODES.TIRAMISU)
30 | internal suspend fun asyncOperation(
31 | block: (listener: Geocoder.GeocodeListener) -> Unit,
32 | ): List {
33 | return suspendCoroutine { continuation ->
34 | val listener = object : Geocoder.GeocodeListener {
35 | override fun onGeocode(addresses: MutableList) {
36 | continuation.resume(addresses.toList())
37 | }
38 |
39 | override fun onError(errorMessage: String?) {
40 | continuation.resumeWithException(GeocodeException(errorMessage))
41 | }
42 | }
43 |
44 | block(listener)
45 | }
46 | }
--------------------------------------------------------------------------------
/compass-geocoder-mobile/src/commonMain/kotlin/dev/jordond/compass/geocoder/mobile/MobilePlatformGeocoder.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.geocoder.mobile
2 |
3 | import dev.jordond.compass.Place
4 | import dev.jordond.compass.geocoder.GeocoderResult
5 | import dev.jordond.compass.geocoder.PlatformGeocoder
6 |
7 | public interface MobilePlatformGeocoder : PlatformGeocoder {
8 |
9 | /**
10 | * Geocode an address to a list of [Place].
11 | *
12 | * In most cases, the list will contain a single [Place]. However, in some cases, it may
13 | * return multiple [Place]s.
14 | *
15 | * @param address The address to geocode.
16 | * @return A [GeocoderResult] containing the list of [Place]s or an error.
17 | */
18 | public suspend fun placeFromAddress(address: String): List
19 | }
20 |
21 | /**
22 | * Create an Android/iOS [PlatformGeocoder] instance for geocoding operations.
23 | *
24 | * @param locale The locale string used for reverse geocoding, null will use device default
25 | * @return A new Android/iOS [PlatformGeocoder] instance.
26 | */
27 | public fun MobilePlatformGeocoder(locale: String? = null): MobilePlatformGeocoder =
28 | createPlatformGeocoder(locale)
29 |
30 | /**
31 | * Create an Android/iOS [PlatformGeocoder] instance for geocoding operations.
32 | *
33 | * @param locale The locale string used for reverse geocoding, null will use device default
34 | * @return A new Android/iOS [PlatformGeocoder] instance.
35 | */
36 | public fun PlatformGeocoder.Companion.mobile(locale: String? = null): MobilePlatformGeocoder =
37 | MobilePlatformGeocoder(locale)
38 |
39 | internal expect fun createPlatformGeocoder(locale: String? = null): MobilePlatformGeocoder
--------------------------------------------------------------------------------
/docs/geolocation/browser.md:
--------------------------------------------------------------------------------
1 | # 🖥️ Browser
2 |
3 | Compass supports [HTML5 Geolocation API](https://developer.mozilla.org/en-US/docs/Web/API/Geolocation\_API) with the `compass-geolocation-browser` artifact. The artifact supports JS and WASM targets.
4 |
5 | Geolocation in the browser requires the user to provide permission. This is handled automatically when you start tracking or attempt to get the current location.
6 |
7 | {% hint style="info" %}
8 | If your project supports both Browser and other targets, you need to configure your setup to provide a `Geolocator` for each platform. Check out [mixed-platforms.md](../usage/mixed-platforms.md "mention")for more information.
9 | {% endhint %}
10 |
11 | ## Usage
12 |
13 | Follow these steps to create a `Geolocator`
14 |
15 | ### Create Locator
16 |
17 | Geolocator is powered by the `Locator` object:
18 |
19 | ```kotlin
20 | val locator: Locator = createBrowserLocator()
21 | val locator: Locator = Locator.browser()
22 | ```
23 |
24 | ### Create Geolocator
25 |
26 | Then you can use that to create the `Geolocator` object:
27 |
28 | ```kotlin
29 | val geolocator: Geolocator = Geolocator(locator)
30 | ```
31 |
32 | Or you can use an extension function to skip the `Locator` step:
33 |
34 | ```kotlin
35 | val geolocator: Geolocator = Geolocator()
36 | val geolocator: Geolocator = BrowserGeolocator()
37 | val geolocator: Geolocator = Geolocator.browser()
38 | ```
39 |
40 | ## Permissions
41 |
42 | Nothing extra is required to setup permissions for the browser.
43 |
44 | When you attempt to get the location, the browser will show a prompt to the user. They can allow, deny, or do one of those permanently.
45 |
46 | ## Get location
47 |
48 | Now you can follow the steps in [geolocator.md](geolocator.md "mention")
49 |
--------------------------------------------------------------------------------
/compass-tools-web/src/commonMain/kotlin/dev/jordond/compass/tools/web/Request.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.tools.web
2 |
3 | import co.touchlab.kermit.Logger
4 | import dev.jordond.compass.InternalCompassApi
5 | import dev.jordond.compass.tools.web.exception.WebException
6 | import io.ktor.client.HttpClient
7 | import io.ktor.client.request.get
8 | import io.ktor.client.request.url
9 | import io.ktor.client.statement.HttpResponse
10 | import kotlinx.coroutines.CancellationException
11 |
12 | @InternalCompassApi
13 | /**
14 | * Makes a web request to the specified URL and returns the result of the request.
15 | *
16 | * Internal API.
17 | *
18 | * @param Result The type of the result of the request.
19 | * @param url The URL to make the request to.
20 | * @param resultMapper A function that maps the HTTP response to the result type.
21 | * @return The result of the request.
22 | * @throws WebException If the request fails.
23 | */
24 | public suspend fun HttpClient.makeRequest(
25 | url: String,
26 | resultMapper: suspend (HttpResponse) -> Result,
27 | ): Result {
28 | try {
29 | val response = get { url(url) }
30 | if (response.status.value in 200..299) {
31 | return resultMapper(response)
32 | } else {
33 | throw WebException(
34 | statusCode = response.status,
35 | message = "HTTP request failed with status code ${response.status.value}",
36 | )
37 | }
38 | } catch (cancellation: CancellationException) {
39 | throw cancellation
40 | } catch (webException: WebException) {
41 | throw webException
42 | } catch (cause: Throwable) {
43 | Logger.e(cause) { "Unable to make web request" }
44 | throw cause
45 | }
46 | }
--------------------------------------------------------------------------------
/docs/autocomplete/overview.md:
--------------------------------------------------------------------------------
1 | # 🔍 Overview
2 |
3 | Autocomplete is used to provide real-time suggestions for locations as the user types in a query.
4 |
5 | Compass currently provides an interface for interacting with a geocoding service, such as Mapbox.
6 | The
7 | autocomplete feature provides real-time suggestions to the user as they type an address or location
8 | into a search field.
9 |
10 | The library is structured around several key components:
11 |
12 | 1. `AutocompleteService`: This is an interface that represents a service providing autocomplete
13 | suggestions. It has a `search(query: String)` function, which searches for autocomplete
14 | suggestions based on the provided
15 | query.
16 |
17 | 2. `AutocompleteResult`: This is a sealed class that represents the result of an autocomplete
18 | operation. It can either be a `Success` with a list of data or an `Error` with a specific error
19 | message.
20 |
21 | 3. `Autocomplete`: This is a function that creates a new instance of `AutocompleteService` that uses
22 | a HTTP service to provide autocomplete suggestions.
23 |
24 | {% hint style="info" %}
25 | The current implementations of the `AutocompleteService` use the Geocoding artifacts from Compass
26 | and perform a forward geocode operation. That means that the results may not be the highest of
27 | quality.
28 | {% endhint %}
29 |
30 | ### Quick start
31 |
32 | ```kotlin
33 | suspend fun autocomplete(query: String): List {
34 | val autocomplete = AutoComplete.mobile()
35 | return autocomplete.search("London").getOrNull()
36 | }
37 | ```
38 |
39 | In this quick example the `AutoComplete.mobile()()` function is a convenience function for creating
40 | a `AutoComplete` object. Each of the `autocomplete-*` artifacts provide one.
41 |
--------------------------------------------------------------------------------
/compass-geocoder-web/src/commonMain/kotlin/dev/jordond/compass/geocoder/web/ReverseHttpApiPlatformGeocoder.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.geocoder.web
2 |
3 | import dev.jordond.compass.Coordinates
4 | import dev.jordond.compass.Place
5 | import dev.jordond.compass.exception.NotSupportedException
6 | import dev.jordond.compass.tools.web.HttpApiEndpoint
7 | import dev.jordond.compass.tools.web.makeRequest
8 | import io.ktor.client.HttpClient
9 | import io.ktor.client.statement.HttpResponse
10 |
11 | /**
12 | * A [HttpApiPlatformGeocoder] for reverse geocoding that uses the provided [ReverseEndpoint].
13 | *
14 | * @see HttpApiPlatformGeocoder
15 | * @property endpoint The endpoint to use for reverse geocoding.
16 | * @property client The [HttpClient] to use for making requests.
17 | */
18 | public class ReverseHttpApiPlatformGeocoder(
19 | private val endpoint: ReverseEndpoint,
20 | private val client: HttpClient,
21 | ) : HttpApiPlatformGeocoder {
22 |
23 | override fun isAvailable(): Boolean = true
24 |
25 | override suspend fun forward(address: String): List {
26 | throw NotSupportedException()
27 | }
28 |
29 | override suspend fun reverse(latitude: Double, longitude: Double): List {
30 | val url = endpoint.url(Coordinates(latitude, longitude))
31 | return client.makeRequest(url, endpoint::mapResponse)
32 | }
33 | }
34 |
35 | /**
36 | * An endpoint for reverse geocoding.
37 | *
38 | * @param url The URL to use for reverse geocoding.
39 | * @param mapResponse A function to map the response to a list of [Coordinates].
40 | */
41 | public fun ReverseEndpoint(
42 | url: (Coordinates) -> String,
43 | mapResponse: suspend (HttpResponse) -> List,
44 | ): ReverseEndpoint = HttpApiEndpoint(url, mapResponse)
--------------------------------------------------------------------------------
/compass-geocoder-web-opencage/src/commonMain/kotlin/dev/jordond/compass/geocoder/web/opencage/internal/GeocodeResponse.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.geocoder.web.opencage.internal
2 |
3 | import dev.jordond.compass.Coordinates
4 | import dev.jordond.compass.Place
5 | import kotlinx.serialization.SerialName
6 | import kotlinx.serialization.Serializable
7 |
8 | @Serializable
9 | internal data class GeocodeResponse(
10 | @SerialName("results")
11 | val results: List,
12 | )
13 |
14 | internal fun GeocodeResponse.toCoordinates(): List {
15 | return results.map { result -> result.toCoordinates() }
16 | }
17 |
18 | internal fun GeocodeResponse.toPlaces(): List = results.mapNotNull { result ->
19 | val components = result.components
20 | Place(
21 | coordinates = result.toCoordinates(),
22 | name = components.restaurant?.takeIf { it.isNotBlank() },
23 | street = components.road?.takeIf { it.isNotBlank() },
24 | isoCountryCode = components.countryCode?.takeIf { it.isNotBlank() },
25 | country = components.country?.takeIf { it.isNotBlank() },
26 | postalCode = components.postcode?.takeIf { it.isNotBlank() },
27 | administrativeArea = components.state?.takeIf { it.isNotBlank() },
28 | subAdministrativeArea = null,
29 | locality = components.normalizedCity?.takeIf { it.isNotBlank() },
30 | subLocality = components.suburb?.takeIf { it.isNotBlank() },
31 | thoroughfare = components.road?.takeIf { it.isNotBlank() },
32 | subThoroughfare = null,
33 | ).takeIf { it.isEmpty.not() }
34 | }
35 |
36 | private fun ResultResponse.toCoordinates(): Coordinates {
37 | return Coordinates(
38 | latitude = geometry.lat,
39 | longitude = geometry.lng,
40 | )
41 | }
--------------------------------------------------------------------------------
/compass-geocoder-mobile/src/commonMain/kotlin/dev/jordond/compass/geocoder/MobileGeocoder.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.geocoder
2 |
3 | import dev.jordond.compass.geocoder.mobile.MobilePlatformGeocoder
4 | import kotlinx.coroutines.CoroutineDispatcher
5 | import kotlinx.coroutines.Dispatchers
6 |
7 | /**
8 | * Create a new [Geocoder] instance for geocoding operations.
9 | *
10 | * @param dispatcher The [CoroutineDispatcher] to use for geocoding operations.
11 | * @param locale The locale string used for reverse geocoding, null will use device default
12 | * @return A new [Geocoder] instance.
13 | */
14 | public fun Geocoder(
15 | dispatcher: CoroutineDispatcher = Dispatchers.Default,
16 | locale: String? = null,
17 | ): Geocoder = MobileGeocoder(dispatcher, locale)
18 |
19 | /**
20 | * Create a new [Geocoder] instance for geocoding operations.
21 | *
22 | * @param dispatcher The [CoroutineDispatcher] to use for geocoding operations.
23 | * @param locale The locale string used for reverse geocoding, null will use device default
24 | * @return A new [Geocoder] instance.
25 | */
26 | @Suppress("FunctionName")
27 | public fun MobileGeocoder(
28 | dispatcher: CoroutineDispatcher = Dispatchers.Default,
29 | locale: String? = null,
30 | ): Geocoder = Geocoder(MobilePlatformGeocoder(locale), dispatcher)
31 |
32 | /**
33 | * Create a new [Geocoder] instance for geocoding operations.
34 | *
35 | * @param dispatcher The [CoroutineDispatcher] to use for geocoding operations.
36 | * @param locale The locale string used for reverse geocoding, null will use device default
37 | * @return A new [Geocoder] instance.
38 | */
39 | public fun Geocoder.Companion.mobile(
40 | dispatcher: CoroutineDispatcher = Dispatchers.Default,
41 | locale: String? = null,
42 | ): Geocoder = MobileGeocoder(dispatcher, locale)
--------------------------------------------------------------------------------
/compass-geocoder-web/src/commonMain/kotlin/dev/jordond/compass/geocoder/web/ForwardHttpApiPlatformGeocoder.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.geocoder.web
2 |
3 | import dev.jordond.compass.Coordinates
4 | import dev.jordond.compass.Place
5 | import dev.jordond.compass.exception.NotSupportedException
6 | import dev.jordond.compass.tools.web.HttpApiEndpoint
7 | import dev.jordond.compass.tools.web.makeRequest
8 | import io.ktor.client.HttpClient
9 | import io.ktor.client.statement.HttpResponse
10 | import io.ktor.http.encodeURLParameter
11 |
12 | /**
13 | * A [HttpApiPlatformGeocoder] for forward geocoding that uses the provided [ForwardEndpoint].
14 | *
15 | * @see HttpApiPlatformGeocoder
16 | * @property endpoint The endpoint to use for forward geocoding.
17 | * @property client The [HttpClient] to use for making requests.
18 | */
19 | public class ForwardHttpApiPlatformGeocoder(
20 | private val endpoint: ForwardEndpoint,
21 | private val client: HttpClient,
22 | ) : HttpApiPlatformGeocoder {
23 |
24 | override fun isAvailable(): Boolean = true
25 |
26 | override suspend fun forward(address: String): List {
27 | val url = endpoint.url(address.encodeURLParameter())
28 | return client.makeRequest(url, endpoint::mapResponse)
29 | }
30 |
31 | override suspend fun reverse(latitude: Double, longitude: Double): List {
32 | throw NotSupportedException()
33 | }
34 | }
35 |
36 | /**
37 | * An endpoint for forward geocoding.
38 | *
39 | * @param url The URL to use for forward geocoding.
40 | * @param mapResponse A function to map the response to a list of [Coordinates].
41 | */
42 | public fun ForwardEndpoint(
43 | url: (String) -> String,
44 | mapResponse: suspend (HttpResponse) -> List,
45 | ): ForwardEndpoint = HttpApiEndpoint(url, mapResponse)
--------------------------------------------------------------------------------
/compass-geocoder-mobile/src/iosMain/kotlin/dev/jordond/compass/geocoder/mobile/internal/PlacemarkMapper.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.geocoder.mobile.internal
2 |
3 | import dev.jordond.compass.Coordinates
4 | import dev.jordond.compass.Place
5 | import kotlinx.cinterop.ExperimentalForeignApi
6 | import kotlinx.cinterop.useContents
7 | import platform.Contacts.CNPostalAddress
8 | import platform.CoreLocation.CLPlacemark
9 | import platform.CoreLocation.postalAddress
10 |
11 | /**
12 | * Map a [CLPlacemark] to a [Place].
13 | */
14 | @Suppress("CAST_NEVER_SUCCEEDS")
15 | @OptIn(ExperimentalForeignApi::class)
16 | internal fun CLPlacemark.toPlace(): Place? {
17 | val street = runCatching { (postalAddress as? CNPostalAddress)?.street }.getOrNull()
18 | return Place(
19 | coordinates = toCoordinates() ?: return null,
20 | name = name,
21 | street = street,
22 | isoCountryCode = ISOcountryCode,
23 | country = country,
24 | postalCode = postalCode,
25 | administrativeArea = administrativeArea,
26 | subAdministrativeArea = subAdministrativeArea,
27 | locality = locality,
28 | subLocality = subLocality,
29 | thoroughfare = thoroughfare,
30 | subThoroughfare = subThoroughfare
31 | )
32 | }
33 |
34 | internal fun List.toPlaces(): List = mapNotNull { it.toPlace() }
35 |
36 | @OptIn(ExperimentalForeignApi::class)
37 | internal fun CLPlacemark.toCoordinates(): Coordinates? {
38 | val (latitude, longitude) = location?.coordinate?.useContents { latitude to longitude }
39 | ?: return null
40 |
41 | return Coordinates(
42 | latitude = latitude,
43 | longitude = longitude,
44 | )
45 | }
46 |
47 | internal fun List.toCoordinates(): List =
48 | mapNotNull { it.toCoordinates() }
--------------------------------------------------------------------------------
/compass-geocoder-web-opencage/src/commonMain/kotlin/dev/jordond/compass/geocoder/web/opencage/internal/ResultResponse.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.geocoder.web.opencage.internal
2 |
3 | import kotlinx.serialization.SerialName
4 | import kotlinx.serialization.Serializable
5 |
6 | @Serializable
7 | internal class ResultResponse(
8 | @SerialName("components")
9 | val components: Components,
10 |
11 | @SerialName("formatted")
12 | val formatted: String,
13 |
14 | @SerialName("geometry")
15 | val geometry: Geometry,
16 | )
17 |
18 | @Serializable
19 | internal class Coordinate(
20 | @SerialName("lat")
21 | val lat: Double,
22 |
23 | @SerialName("lng")
24 | val lng: Double,
25 | )
26 |
27 | @Serializable
28 | internal class Components(
29 | @SerialName("_category")
30 | val category: String? = null,
31 |
32 | @SerialName("_normalized_city")
33 | val normalizedCity: String? = null,
34 |
35 | @SerialName("_type")
36 | val type: String? = null,
37 |
38 | @SerialName("city")
39 | val city: String? = null,
40 |
41 | @SerialName("continent")
42 | val continent: String? = null,
43 |
44 | @SerialName("country")
45 | val country: String? = null,
46 |
47 | @SerialName("country_code")
48 | val countryCode: String? = null,
49 |
50 | @SerialName("postcode")
51 | val postcode: String? = null,
52 |
53 | @SerialName("restaurant")
54 | val restaurant: String? = null,
55 |
56 | @SerialName("road")
57 | val road: String? = null,
58 |
59 | @SerialName("state")
60 | val state: String? = null,
61 |
62 | @SerialName("suburb")
63 | val suburb: String? = null,
64 | )
65 |
66 | @Serializable
67 | internal class Geometry(
68 | @SerialName("lat")
69 | val lat: Double,
70 |
71 | @SerialName("lng")
72 | val lng: Double,
73 | )
--------------------------------------------------------------------------------
/compass-geolocation-browser/src/commonMain/kotlin/dev/jordond/compass/geolocation/browser/api/model/CreateGeolocationOptions.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.geolocation.browser.api.model
2 |
3 | import dev.jordond.compass.geolocation.browser.api.Geolocation
4 | import dev.jordond.compass.geolocation.browser.internal.Object
5 |
6 | /**
7 | * Represents the options that can be passed to the [Geolocation] methods.
8 | *
9 | * [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/API/Geolocation/getCurrentPosition#options)
10 | *
11 | * @param enableHighAccuracy A boolean value that indicates the application would like to receive
12 | * the best possible results. If true and if the device is able to provide a more accurate position,
13 | * it will do so. Note that this can result in slower response times or increased power consumption
14 | * (with a GPS chip on a mobile device for example). On the other hand, if false, the device can
15 | * take the liberty to save resources by responding more quickly and/or using less power.
16 | * @param timeout A positive long value representing the maximum length of time (in milliseconds)
17 | * the device is allowed to take in order to return a position. The default value is Infinity,
18 | * meaning that getCurrentPosition() won't return until the position is available.
19 | * @param maximumAge A positive long value indicating the maximum age in milliseconds of a possible cached
20 | * position that is acceptable to return. If set to 0, it means that the device cannot use a cached
21 | * position and must attempt to retrieve the real current position. If set to Infinity the device
22 | * must return a cached position regardless of its age. Default: 0.
23 | */
24 | public expect fun createGeolocationOptions(
25 | enableHighAccuracy: Boolean,
26 | timeout: Double,
27 | maximumAge: Double,
28 | ): Object
--------------------------------------------------------------------------------
/compass-geocoder/src/commonMain/kotlin/dev/jordond/compass/geocoder/ForwardGeocoder.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.geocoder
2 |
3 | import dev.jordond.compass.Coordinates
4 | import dev.jordond.compass.geocoder.internal.DefaultGeocoder
5 | import kotlinx.coroutines.CoroutineDispatcher
6 | import kotlinx.coroutines.Dispatchers
7 |
8 | public interface ForwardGeocoder {
9 |
10 | /**
11 | * Get a list of [Coordinates]s for a given address.
12 | *
13 | * In most cases, the list will contain a single [Coordinates]. However, in some cases, it may
14 | * return multiple [Coordinates]s.
15 | *
16 | * @param address The address to geocode.
17 | * @return A [GeocoderResult] containing the list of [Coordinates]s or an error.
18 | */
19 | public suspend fun forward(address: String): GeocoderResult
20 |
21 | /**
22 | * Get a list of [Coordinates]s for a given address.
23 | *
24 | * In most cases, the list will contain a single [Coordinates]. However, in some cases, it may
25 | * return multiple [Coordinates]s.
26 | *
27 | * @param address The address to geocode.
28 | * @return A [GeocoderResult] containing the list of [Coordinates]s or an error.
29 | */
30 | public suspend fun locations(address: String): GeocoderResult = forward(address)
31 | }
32 |
33 | /**
34 | * Create a new [ForwardGeocoder] using the provided [PlatformGeocoder].
35 | *
36 | * @param platformGeocoder The [PlatformGeocoder] to use for geocoding.
37 | * @param dispatcher The [CoroutineDispatcher] to use for running the geocoding operations.
38 | * @return A new [ForwardGeocoder].
39 | */
40 | public fun ForwardGeocoder(
41 | platformGeocoder: PlatformGeocoder,
42 | dispatcher: CoroutineDispatcher = Dispatchers.Default,
43 | ): ForwardGeocoder = DefaultGeocoder(platformGeocoder, dispatcher)
44 |
--------------------------------------------------------------------------------
/compass-geolocation-browser/src/commonMain/kotlin/dev/jordond/compass/geolocation/BrowserGeolocator.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.geolocation
2 |
3 | import dev.jordond.compass.geolocation.browser.BrowserLocator
4 | import dev.jordond.compass.geolocation.browser.createBrowserLocator
5 | import kotlinx.coroutines.CoroutineDispatcher
6 | import kotlinx.coroutines.Dispatchers
7 |
8 | /**
9 | * Create a new [Geolocator] that uses the [BrowserLocator] for geolocation operations.
10 | *
11 | * [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/API/Geolocation)
12 | *
13 | * @param dispatcher The [CoroutineDispatcher] to use for geolocation operations.
14 | * @return A new [Geolocator] instance.
15 | */
16 | public fun Geolocator(
17 | dispatcher: CoroutineDispatcher = Dispatchers.Default,
18 | ): Geolocator = BrowserGeolocator(dispatcher)
19 |
20 | /**
21 | * Create a new [Geolocator] that uses the [BrowserLocator] for geolocation operations.
22 | *
23 | * [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/API/Geolocation)
24 | *
25 | * @param dispatcher The [CoroutineDispatcher] to use for geolocation operations.
26 | * @return A new [Geolocator] instance.
27 | */
28 | @Suppress("FunctionName")
29 | public fun BrowserGeolocator(
30 | dispatcher: CoroutineDispatcher = Dispatchers.Default,
31 | ): Geolocator = Geolocator(createBrowserLocator(), dispatcher)
32 |
33 | /**
34 | * Create a new [Geolocator] that uses the [BrowserLocator] for geolocation operations.
35 | *
36 | * [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/API/Geolocation)
37 | *
38 | * @param dispatcher The [CoroutineDispatcher] to use for geolocation operations.
39 | * @return A new [Geolocator] instance.
40 | */
41 | public fun Geolocator.Companion.browser(
42 | dispatcher: CoroutineDispatcher = Dispatchers.Default,
43 | ): Geolocator = BrowserGeolocator(dispatcher)
--------------------------------------------------------------------------------
/demo/iosApp/iosApp/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CADisableMinimumFrameDurationOnPhone
6 |
7 | CFBundleDevelopmentRegion
8 | $(DEVELOPMENT_LANGUAGE)
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleVersion
22 | 1
23 | LSRequiresIPhoneOS
24 |
25 | UIApplicationSceneManifest
26 |
27 | UIApplicationSupportsMultipleScenes
28 |
29 |
30 | UILaunchScreen
31 |
32 | UIRequiredDeviceCapabilities
33 |
34 | armv7
35 |
36 | UISupportedInterfaceOrientations
37 |
38 | UIInterfaceOrientationPortrait
39 | UIInterfaceOrientationLandscapeLeft
40 | UIInterfaceOrientationLandscapeRight
41 |
42 | UISupportedInterfaceOrientations~ipad
43 |
44 | UIInterfaceOrientationPortrait
45 | UIInterfaceOrientationPortraitUpsideDown
46 | UIInterfaceOrientationLandscapeLeft
47 | UIInterfaceOrientationLandscapeRight
48 |
49 | NSLocationWhenInUseUsageDescription
50 | Its a demo about location
51 |
52 |
53 |
--------------------------------------------------------------------------------
/demo/composeApp/src/commonMain/kotlin/App.kt:
--------------------------------------------------------------------------------
1 | @file:OptIn(ExperimentalMaterial3Api::class)
2 |
3 | import androidx.compose.foundation.layout.Box
4 | import androidx.compose.foundation.layout.padding
5 | import androidx.compose.material.icons.Icons
6 | import androidx.compose.material.icons.automirrored.filled.ArrowBack
7 | import androidx.compose.material3.ExperimentalMaterial3Api
8 | import androidx.compose.material3.Icon
9 | import androidx.compose.material3.IconButton
10 | import androidx.compose.material3.MaterialTheme
11 | import androidx.compose.material3.Scaffold
12 | import androidx.compose.material3.Text
13 | import androidx.compose.material3.TopAppBar
14 | import androidx.compose.runtime.Composable
15 | import androidx.compose.ui.Modifier
16 | import cafe.adriel.voyager.navigator.CurrentScreen
17 | import cafe.adriel.voyager.navigator.Navigator
18 |
19 | @Composable
20 | fun App(isBrowser: Boolean = false) {
21 | MaterialTheme {
22 | Navigator(HomeScreen(isBrowser)) { navigator ->
23 | Scaffold(
24 | topBar = {
25 | TopAppBar(
26 | title = { Text("Compass Demo") },
27 | navigationIcon = {
28 | if (navigator.canPop) {
29 | IconButton(onClick = { navigator.pop() }) {
30 | Icon(
31 | imageVector = Icons.AutoMirrored.Filled.ArrowBack,
32 | contentDescription = "Back",
33 | )
34 | }
35 | }
36 | },
37 | )
38 | }
39 | ) { innerPadding ->
40 | Box(Modifier.padding(innerPadding)) {
41 | CurrentScreen()
42 | }
43 | }
44 | }
45 | }
46 | }
--------------------------------------------------------------------------------
/compass-geocoder-web-mapbox/src/commonMain/kotlin/dev/jordond/compass/geocoder/web/parameter/MapboxBoundingBox.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.geocoder.web.parameter
2 |
3 | import dev.drewhamilton.poko.Poko
4 | import dev.jordond.compass.tools.web.parameter.QueryParamValue
5 |
6 | /**
7 | * A bounding box to limit search results to a specific area.
8 | *
9 | * See the [Mapbox API documentation](https://docs.mapbox.com/api/search/geocoding-v6/#forward-geocoding-with-structured-input) for more information.
10 | *
11 | * @property minLongitude The minimum longitude of the bounding box.
12 | * @property minLatitude The minimum latitude of the bounding box.
13 | * @property maxLongitude The maximum longitude of the bounding box.
14 | * @property maxLatitude The maximum latitude of the bounding box.
15 | */
16 | @Poko
17 | @Suppress("MemberVisibilityCanBePrivate")
18 | public class MapboxBoundingBox(
19 | public val minLongitude: Double,
20 | public val minLatitude: Double,
21 | public val maxLongitude: Double,
22 | public val maxLatitude: Double,
23 | ) : QueryParamValue {
24 |
25 | public override val value: String = "$minLongitude,$minLatitude,$maxLongitude,$maxLatitude"
26 |
27 | /**
28 | * Create a copy of [MapboxBoundingBox] with the given or current values.
29 | *
30 | * @param minLongitude The minimum longitude of the bounding box.
31 | * @param minLatitude The minimum latitude of the bounding box.
32 | * @param maxLongitude The maximum longitude of the bounding box.
33 | * @param maxLatitude The maximum latitude of the bounding box.
34 | */
35 | public fun copy(
36 | minLongitude: Double = this.minLongitude,
37 | minLatitude: Double = this.minLatitude,
38 | maxLongitude: Double = this.maxLongitude,
39 | maxLatitude: Double = this.maxLatitude,
40 | ): MapboxBoundingBox = MapboxBoundingBox(minLongitude, minLatitude, maxLongitude, maxLatitude)
41 | }
--------------------------------------------------------------------------------
/compass-permissions-mobile/src/androidMain/kotlin/dev/jordond/compass/permissions/mobile/internal/activity/ActivityProvider.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.permissions.mobile.internal.activity
2 |
3 | import android.annotation.SuppressLint
4 | import android.app.Application
5 | import android.content.Context
6 | import androidx.activity.ComponentActivity
7 | import dev.jordond.compass.permissions.mobile.internal.PermissionRequester
8 | import java.lang.ref.WeakReference
9 |
10 | internal class ActivityProvider(
11 | private val context: Context,
12 | ) {
13 |
14 | private var requester: WeakReference? = null
15 |
16 | val permissionRequester: PermissionRequester
17 | get() = requester?.get() ?: throw IllegalStateException("PermissionRequester is not available")
18 |
19 | private val lifecycleObserver = createActivityLifecycleObserver { activity ->
20 | this.requester = WeakReference(PermissionRequester(activity))
21 | }
22 |
23 | init {
24 | val application = context.applicationContext as? Application
25 | application?.registerActivityLifecycleCallbacks(lifecycleObserver) ?: run {
26 | val activity = context.applicationContext as? ComponentActivity
27 | if (activity != null) {
28 | this.requester = WeakReference(PermissionRequester(activity))
29 | }
30 | }
31 | }
32 |
33 | companion object {
34 |
35 | @SuppressLint("StaticFieldLeak")
36 | private var instance: ActivityProvider? = null
37 |
38 | fun create(context: Context): ActivityProvider {
39 | if (instance == null) {
40 | instance = ActivityProvider(context)
41 | }
42 |
43 | return instance!!
44 | }
45 |
46 | fun getInstance(): ActivityProvider = instance
47 | ?: throw IllegalStateException("ActivityProvider has not been initialized")
48 | }
49 | }
--------------------------------------------------------------------------------
/docs/geolocation/overview.md:
--------------------------------------------------------------------------------
1 | # 📍 Overview
2 |
3 | Geolocation includes the following features:
4 |
5 | * Track a user's location
6 | * Get the current location
7 | * Customize the accuracy of the tracking
8 | * Android and iOS support, using built-in services.
9 | * Built-in Location permission handling
10 |
11 | {% hint style="info" %}
12 | Built-in services are used for Android and iOS. That means that there is restrictions to its usage.
13 | {% endhint %}
14 |
15 | ## Quickstart
16 |
17 | Include the required dependencies for your targets, see [add-dependencies.md](../setup/add-dependencies.md "mention").
18 |
19 | #### Create the Geolocator
20 |
21 | Creating a geolocator depends on your platform, but in the simplest use-case of an Android & iOS only targets. You can do something like this:
22 |
23 | ```kotlin
24 | val geolocator: Geolocator = Geolocator.mobile()
25 | ```
26 |
27 | #### Get current location
28 |
29 | Getting the current location is simple, just call:
30 |
31 | ```kotlin
32 | val result: GeolocatorResult = geolocator.current()
33 | // Handle the result:
34 |
35 | when (result) {
36 | is GeolocatorResult.Success -> {
37 | // Do something with result.location
38 | }
39 | is GeolocatorResult.Error -> when(result) {
40 | is GeolocatorResult.NotSupported -> TODO()
41 | is GeolocatorResult.NotFound -> TODO()
42 | is GeolocatorResult.PermissionError -> TODO()
43 | is GeolocatorResult.GeolocationFailed -> TODO()
44 | }
45 | }
46 | ```
47 |
48 | Or if you only care about the result:
49 |
50 | ```kotlin
51 | val location: Location? = geolocator.currentOrNull()
52 | ```
53 |
54 | {% hint style="info" %}
55 | Compass will handle asking the user for while-in-use location permissions. If they deny the permission then the result will be `PermissionError.` If the user has denied the permission permanently, you will need to prompt the user to open the settings.
56 | {% endhint %}
57 |
58 |
--------------------------------------------------------------------------------
/.github/version.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | CWD="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"
4 | PARENT="$(cd "$CWD"/.. >/dev/null 2>&1 && pwd)"
5 |
6 | set -e
7 |
8 | SEMVER_REG="([[:digit:]]+(\.[[:digit:]]+)+)"
9 |
10 | README_FILE="$PARENT/README.md"
11 | VERSION_FILE="$PARENT/gradle/libs.versions.toml"
12 |
13 | NEW_VERSION="$ORG_GRADLE_PROJECT_VERSION_NAME"
14 | if [ -z "$NEW_VERSION" ]; then
15 | NEW_VERSION="$1"
16 | if [ -n "$NEW_VERSION" ]; then
17 | echo "Update README with version: '$NEW_VERSION'"
18 |
19 | if [[ "$OSTYPE" == "darwin"* ]]; then
20 | # Update artifact versions in README.md
21 | sed -i '' -E "s/\:$SEMVER_REG\"\)/\:$NEW_VERSION\"\)/" "$README_FILE"
22 |
23 | # Update version catalog in README.md
24 | sed -i '' -E "s/compass = \"$SEMVER_REG\"/compass = \"$NEW_VERSION\"/" "$README_FILE"
25 | else
26 | sed -i -E "s/\:$SEMVER_REG\"/\:$NEW_VERSION\"/g" "$README_FILE"
27 | sed -i -E "s/compass = \"$SEMVER_REG\"/compass = \"$NEW_VERSION\"/g" "$README_FILE"
28 | fi
29 | fi
30 | fi
31 |
32 | # Update Kotlin badge in README.md
33 | LIBS_KOTLIN_VERSION=$(grep -m 1 "^kotlin = " "$VERSION_FILE" | cut -d= -f2 | tr -d ' "' | tr -d '\n')
34 | if [ -z "$LIBS_KOTLIN_VERSION" ]; then
35 | echo "Unable to find Kotlin version in '$VERSION_FILE'"
36 | else
37 | echo "Updating Kotlin version: '$LIBS_KOTLIN_VERSION'"
38 | sed -i '' -E "s/kotlin-v$SEMVER_REG/kotlin-v$LIBS_KOTLIN_VERSION/" "$README_FILE"
39 | fi
40 |
41 | # Update Compose Multiplatform badge in README.md
42 | LIBS_COMPOSE_VERSION=$(grep "compose-multiplatform = " "$VERSION_FILE" | cut -d= -f2 | tr -d ' "')
43 | if [ -z "$LIBS_COMPOSE_VERSION" ]; then
44 | echo "Unable to find Compose Multiplatform version in '$VERSION_FILE'"
45 | else
46 | echo "Updating Compose version: '$LIBS_COMPOSE_VERSION'"
47 | sed -i '' -E "s/Compose%20Multiplatform-v$SEMVER_REG/Compose%20Multiplatform-v$LIBS_COMPOSE_VERSION/" "$README_FILE"
48 | fi
49 |
--------------------------------------------------------------------------------
/demo/composeApp/src/androidMain/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
18 |
22 |
26 |
27 |
28 |
29 |
36 |
--------------------------------------------------------------------------------
/compass-geocoder-web-mapbox/src/commonMain/kotlin/dev/jordond/compass/geocoder/web/MapboxForwardEndpoint.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.geocoder.web
2 |
3 | import dev.jordond.compass.Coordinates
4 | import dev.jordond.compass.geocoder.web.mapbox.internal.GeocodeResponse
5 | import dev.jordond.compass.geocoder.web.mapbox.internal.toCoordinates
6 | import dev.jordond.compass.geocoder.web.parameter.MapboxParameters
7 | import dev.jordond.compass.geocoder.web.parameter.MapboxParametersBuilder
8 | import dev.jordond.compass.geocoder.web.parameter.mapboxParameters
9 | import io.ktor.client.call.body
10 | import io.ktor.client.statement.HttpResponse
11 | import io.ktor.http.encodeURLQueryComponent
12 |
13 | /**
14 | * A [ForwardEndpoint] that uses the Mapbox Geocoding API.
15 | *
16 | * @property apiKey The API key to use for the Mapbox Geocoding API.
17 | * @property parameters The parameters to use for the Mapbox Geocoding API.
18 | */
19 | public class MapboxForwardEndpoint(
20 | private val apiKey: String,
21 | private val parameters: MapboxParameters = MapboxParameters(),
22 | ) : ForwardEndpoint {
23 |
24 | /**
25 | * Creates a new [MapboxForwardEndpoint] with the given API key.
26 | *
27 | * @param apiKey The API key to use for the Mapbox Geocoding API.
28 | * @param block A block to configure the parameters for the Mapbox Geocoding API.
29 | */
30 | public constructor(
31 | apiKey: String,
32 | block: MapboxParametersBuilder.() -> Unit,
33 | ) : this(apiKey, mapboxParameters(block))
34 |
35 | override fun url(param: String): String {
36 | val encodedQuery = param.encodeURLQueryComponent()
37 | return MapboxPlatformGeocoder.forwardUrl(encodedQuery, apiKey, parameters)
38 | }
39 |
40 | override suspend fun mapResponse(response: HttpResponse): List {
41 | val forwardResponse = response.body()
42 | return forwardResponse.toCoordinates()
43 | }
44 | }
--------------------------------------------------------------------------------
/compass-geocoder-web-mapbox/src/commonMain/kotlin/dev/jordond/compass/geocoder/web/MapboxReverseEndpoint.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.geocoder.web
2 |
3 | import dev.jordond.compass.Coordinates
4 | import dev.jordond.compass.Place
5 | import dev.jordond.compass.geocoder.web.mapbox.internal.GeocodeResponse
6 | import dev.jordond.compass.geocoder.web.mapbox.internal.toPlaces
7 | import dev.jordond.compass.geocoder.web.parameter.MapboxParameters
8 | import dev.jordond.compass.geocoder.web.parameter.MapboxParametersBuilder
9 | import dev.jordond.compass.geocoder.web.parameter.mapboxParameters
10 | import io.ktor.client.call.body
11 | import io.ktor.client.statement.HttpResponse
12 |
13 | /**
14 | * A [ReverseEndpoint] that uses the Mapbox Geocoding API.
15 | *
16 | * @property apiKey The API key to use for the Mapbox Geocoding API.
17 | * @property parameters The parameters to use for the Mapbox Geocoding API.
18 | */
19 | public class MapboxReverseEndpoint(
20 | private val apiKey: String,
21 | private val parameters: MapboxParameters = MapboxParameters(),
22 | ) : ReverseEndpoint {
23 |
24 | /**
25 | * Creates a new [MapboxReverseEndpoint] with the given API key.
26 | *
27 | * @param apiKey The API key to use for the Mapbox Geocoding API.
28 | * @param block A block to configure the parameters for the Mapbox Geocoding API.
29 | */
30 | public constructor(
31 | apiKey: String,
32 | block: MapboxParametersBuilder.() -> Unit,
33 | ) : this(apiKey, mapboxParameters(block))
34 |
35 | override fun url(param: Coordinates): String {
36 | val (latitude, longitude) = param.run { latitude to longitude }
37 | return MapboxPlatformGeocoder.reverseUrl(latitude, longitude, apiKey, parameters)
38 | }
39 |
40 | override suspend fun mapResponse(response: HttpResponse): List {
41 | val forwardResponse = response.body()
42 | return forwardResponse.toPlaces()
43 | }
44 | }
--------------------------------------------------------------------------------
/compass-geolocation/src/commonMain/kotlin/dev/jordond/compass/geolocation/Extensions.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.geolocation
2 |
3 | import dev.jordond.compass.Location
4 | import dev.jordond.compass.geolocation.internal.DefaultGeolocator
5 |
6 | /**
7 | * Gets the current location or null if the location is not available.
8 | *
9 | * @receiver The [Geolocator] to get the location from.
10 | * @return The current location or null if the location is not available.
11 | */
12 | public suspend fun Geolocator.currentLocationOrNull(): Location? = current().getOrNull()
13 |
14 | /**
15 | * Check if the current [Geolocator] has the permissions required for geolocation.
16 | *
17 | * This will always return `true` if the [Locator] used by the [Geolocator] is not a [PermissionLocator].
18 | *
19 | * @receiver The [Geolocator] to check the permissions of.
20 | * @return True if the [Geolocator] has the permissions required for geolocation, false otherwise.
21 | */
22 | public fun Geolocator.hasPermission(): Boolean {
23 | val locator = (this as? DefaultGeolocator)?.locator
24 | return (locator as? PermissionLocator)?.hasPermission() ?: true
25 | }
26 |
27 | /**
28 | * Check if the current [GeolocatorResult] is a [GeolocatorResult.PermissionDenied].
29 | *
30 | * @receiver The [GeolocatorResult] to check.
31 | * @return True if the [GeolocatorResult] is a [GeolocatorResult.PermissionDenied], false otherwise.
32 | */
33 | public fun GeolocatorResult.Error.isPermissionDenied(): Boolean {
34 | return this is GeolocatorResult.PermissionDenied
35 | }
36 |
37 | /**
38 | * Check if the current [GeolocatorResult] is a [GeolocatorResult.PermissionDenied].
39 | *
40 | * @receiver The [GeolocatorResult] to check.
41 | * @return True if the [GeolocatorResult] is a [GeolocatorResult.PermissionDenied], false otherwise.
42 | */
43 | public fun GeolocatorResult.Error.isPermissionDeniedForever(): Boolean {
44 | return (this as? GeolocatorResult.PermissionDenied)?.forever ?: false
45 | }
--------------------------------------------------------------------------------
/compass-autocomplete-web/src/commonMain/kotlin/dev/jordond/compass/autocomplete/web/HttpAutocompleteService.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.autocomplete.web
2 |
3 | import dev.jordond.compass.autocomplete.AutocompleteService
4 | import dev.jordond.compass.tools.web.HttpApiEndpoint
5 | import dev.jordond.compass.tools.web.makeRequest
6 | import io.ktor.client.HttpClient
7 | import kotlinx.serialization.json.Json
8 |
9 | /**
10 | * Represents an HTTP endpoint that can be used to search for autocomplete suggestions.
11 | *
12 | * @param T The type of the autocomplete suggestions.
13 | */
14 | public typealias SearchEndpoint = HttpApiEndpoint>
15 |
16 | /**
17 | * Represents an autocomplete service that uses an HTTP endpoint to search for suggestions.
18 | *
19 | * @param T The type of the autocomplete suggestions.
20 | */
21 | public interface HttpAutocompleteService : AutocompleteService {
22 |
23 | public companion object
24 | }
25 |
26 | /**
27 | * Creates an [HttpAutocompleteService] that uses the specified [searchEndpoint] to search for
28 | * autocomplete suggestions.
29 | *
30 | * @param T The type of the autocomplete suggestions.
31 | * @param searchEndpoint The HTTP endpoint to use for searching.
32 | * @param json The JSON serializer to use.
33 | * @param client The HTTP client to use.
34 | * @return An [HttpAutocompleteService] that uses the specified [searchEndpoint] to search for
35 | * autocomplete suggestions.
36 | */
37 | public fun HttpAutocompleteService(
38 | searchEndpoint: SearchEndpoint,
39 | json: Json = HttpApiEndpoint.json(),
40 | client: HttpClient = HttpApiEndpoint.httpClient(json),
41 | ): HttpAutocompleteService = object : HttpAutocompleteService {
42 |
43 | override fun isAvailable(): Boolean = true
44 |
45 | override suspend fun search(query: String): List {
46 | val url = searchEndpoint.url(query)
47 | return client.makeRequest(url, searchEndpoint::mapResponse)
48 | }
49 | }
--------------------------------------------------------------------------------
/compass-geocoder-web-mapbox/src/commonMain/kotlin/dev/jordond/compass/geocoder/web/mapbox/internal/GeocodeResponse.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.geocoder.web.mapbox.internal
2 |
3 | import dev.jordond.compass.Coordinates
4 | import dev.jordond.compass.InternalCompassApi
5 | import dev.jordond.compass.Place
6 | import kotlinx.serialization.SerialName
7 | import kotlinx.serialization.Serializable
8 |
9 | @InternalCompassApi
10 | @Serializable
11 | public data class GeocodeResponse(
12 | @SerialName("features")
13 | val features: List,
14 | )
15 |
16 | internal fun GeocodeResponse.toCoordinates(): List {
17 | return features.mapNotNull { response ->
18 | val coordinates = response.properties?.coordinates ?: return@mapNotNull null
19 | Coordinates(
20 | latitude = coordinates.latitude,
21 | longitude = coordinates.longitude,
22 | )
23 | }
24 | }
25 |
26 | @InternalCompassApi
27 | public fun GeocodeResponse.toPlaces(): List {
28 | return features.mapNotNull { response ->
29 | val data = response.properties?.context ?: return@mapNotNull null
30 | val coordinates = response.properties.coordinates?.run {
31 | Coordinates(latitude = latitude, longitude = longitude)
32 | } ?: return@mapNotNull null
33 |
34 | Place(
35 | coordinates = coordinates,
36 | name = response.properties.name,
37 | street = data.street?.name,
38 | isoCountryCode = data.country?.countryCode,
39 | country = data.country?.name,
40 | postalCode = data.postcode?.name,
41 | administrativeArea = data.region?.name,
42 | subAdministrativeArea = data.district?.name,
43 | locality = data.locality?.name ?: data.place?.name,
44 | subLocality = data.neighborhood?.name,
45 | thoroughfare = data.street?.name,
46 | subThoroughfare = null,
47 | )
48 | }
49 | }
--------------------------------------------------------------------------------
/compass-geocoder-web-opencage/src/commonMain/kotlin/dev/jordond/compass/geocoder/web/OpenCageForwardEndpoint.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.geocoder.web
2 |
3 | import dev.jordond.compass.Coordinates
4 | import dev.jordond.compass.geocoder.web.opencage.internal.GeocodeResponse
5 | import dev.jordond.compass.geocoder.web.opencage.internal.toCoordinates
6 | import dev.jordond.compass.geocoder.web.parameter.OpenCageParameters
7 | import dev.jordond.compass.geocoder.web.parameter.OpenCageParametersBuilder
8 | import dev.jordond.compass.geocoder.web.parameter.openCageParameters
9 | import io.ktor.client.call.body
10 | import io.ktor.client.statement.HttpResponse
11 | import io.ktor.http.encodeURLQueryComponent
12 |
13 | /**
14 | * A [ForwardEndpoint] that uses the OpenCage Geocoding API.
15 | *
16 | * @property apiKey The API key to use for the OpenCage Geocoding API.
17 | * @property parameters The parameters to use for the OpenCage Geocoding API.
18 | */
19 | public class OpenCageForwardEndpoint(
20 | private val apiKey: String,
21 | private val parameters: OpenCageParameters = OpenCageParameters(),
22 | ) : ForwardEndpoint {
23 |
24 | /**
25 | * Creates a new [OpenCageForwardEndpoint] with the given API key.
26 | *
27 | * @param apiKey The API key to use for the OpenCage Geocoding API.
28 | * @param block A block to configure the parameters for the OpenCage Geocoding API.
29 | */
30 | public constructor(
31 | apiKey: String,
32 | block: OpenCageParametersBuilder.() -> Unit,
33 | ) : this(apiKey, openCageParameters(block))
34 |
35 | override fun url(param: String): String {
36 | val encodedQuery = param.encodeURLQueryComponent()
37 | return OpenCagePlatformGeocoder.forwardUrl(encodedQuery, apiKey, parameters)
38 | }
39 |
40 | override suspend fun mapResponse(response: HttpResponse): List {
41 | val result = response.body()
42 | return result.toCoordinates()
43 | }
44 | }
--------------------------------------------------------------------------------
/compass-geocoder-web-opencage/src/commonMain/kotlin/dev/jordond/compass/geocoder/web/OpenCageReverseEndpoint.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.geocoder.web
2 |
3 | import dev.jordond.compass.Coordinates
4 | import dev.jordond.compass.Place
5 | import dev.jordond.compass.geocoder.web.opencage.internal.GeocodeResponse
6 | import dev.jordond.compass.geocoder.web.opencage.internal.toPlaces
7 | import dev.jordond.compass.geocoder.web.parameter.OpenCageParameters
8 | import dev.jordond.compass.geocoder.web.parameter.OpenCageParametersBuilder
9 | import dev.jordond.compass.geocoder.web.parameter.openCageParameters
10 | import io.ktor.client.call.body
11 | import io.ktor.client.statement.HttpResponse
12 |
13 | /**
14 | * A [ReverseEndpoint] that uses the OpenCage Geocoding API.
15 | *
16 | * @property apiKey The API key to use for the OpenCage Geocoding API.
17 | * @property parameters The parameters to use for the OpenCage Geocoding API.
18 | */
19 | public class OpenCageReverseEndpoint(
20 | private val apiKey: String,
21 | private val parameters: OpenCageParameters = OpenCageParameters(),
22 | ) : ReverseEndpoint {
23 |
24 | /**
25 | * Creates a new [OpenCageReverseEndpoint] with the given API key.
26 | *
27 | * @param apiKey The API key to use for the OpenCage Geocoding API.
28 | * @param block A block to configure the parameters for the OpenCage Geocoding API.
29 | */
30 | public constructor(
31 | apiKey: String,
32 | block: OpenCageParametersBuilder.() -> Unit,
33 | ) : this(apiKey, openCageParameters(block))
34 |
35 | override fun url(param: Coordinates): String {
36 | val (latitude, longitude) = param.run { latitude to longitude }
37 | return OpenCagePlatformGeocoder.reverseUrl(latitude, longitude, apiKey, parameters)
38 | }
39 |
40 | override suspend fun mapResponse(response: HttpResponse): List {
41 | val result = response.body()
42 | return result.toPlaces()
43 | }
44 | }
--------------------------------------------------------------------------------
/compass-geocoder-web-template/src/commonMain/kotlin/dev/jordond/compass/geocoder/web/TemplateForwardEndpoint.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.geocoder.web
2 |
3 | import dev.jordond.compass.Coordinates
4 | import dev.jordond.compass.geocoder.web.parameter.TemplateParameters
5 | import dev.jordond.compass.geocoder.web.parameter.TemplateParametersBuilder
6 | import dev.jordond.compass.geocoder.web.parameter.templateParameters
7 | import dev.jordond.compass.geocoder.web.template.internal.GeocodeResponse
8 | import dev.jordond.compass.geocoder.web.template.internal.toCoordinates
9 | import io.ktor.client.call.body
10 | import io.ktor.client.statement.HttpResponse
11 | import io.ktor.http.encodeURLQueryComponent
12 |
13 | /**
14 | * A [ForwardEndpoint] that uses the TEMPLATE Geocoding API.
15 | *
16 | * @property apiKey The API key to use for the TEMPLATE Geocoding API.
17 | * @property parameters The parameters to use for the TEMPLATE Geocoding API.
18 | */
19 | public class TemplateForwardEndpoint(
20 | private val apiKey: String,
21 | private val parameters: TemplateParameters = TemplateParameters(),
22 | ) : ForwardEndpoint {
23 |
24 | /**
25 | * Creates a new [TemplateForwardEndpoint] with the given API key.
26 | *
27 | * @param apiKey The API key to use for the TEMPLATE Geocoding API.
28 | * @param block A block to configure the parameters for the TEMPLATE Geocoding API.
29 | */
30 | public constructor(
31 | apiKey: String,
32 | block: TemplateParametersBuilder.() -> Unit,
33 | ) : this(apiKey, templateParameters(block))
34 |
35 | override fun url(param: String): String {
36 | val encodedQuery = param.encodeURLQueryComponent()
37 | return TemplatePlatformGeocoder.forwardUrl(encodedQuery, apiKey, parameters)
38 | }
39 |
40 | override suspend fun mapResponse(response: HttpResponse): List {
41 | val result = response.body()
42 | return result.toCoordinates()
43 | }
44 | }
--------------------------------------------------------------------------------
/compass-geocoder-web-template/src/commonMain/kotlin/dev/jordond/compass/geocoder/web/TemplateReverseEndpoint.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.geocoder.web
2 |
3 | import dev.jordond.compass.Coordinates
4 | import dev.jordond.compass.Place
5 | import dev.jordond.compass.geocoder.web.parameter.TemplateParameters
6 | import dev.jordond.compass.geocoder.web.parameter.TemplateParametersBuilder
7 | import dev.jordond.compass.geocoder.web.parameter.templateParameters
8 | import dev.jordond.compass.geocoder.web.template.internal.GeocodeResponse
9 | import dev.jordond.compass.geocoder.web.template.internal.toPlaces
10 | import io.ktor.client.call.body
11 | import io.ktor.client.statement.HttpResponse
12 |
13 | /**
14 | * A [ReverseEndpoint] that uses the TEMPLATE Geocoding API.
15 | *
16 | * @property apiKey The API key to use for the TEMPLATE Geocoding API.
17 | * @property parameters The parameters to use for the TEMPLATE Geocoding API.
18 | */
19 | public class TemplateReverseEndpoint(
20 | private val apiKey: String,
21 | private val parameters: TemplateParameters = TemplateParameters(),
22 | ) : ReverseEndpoint {
23 |
24 | /**
25 | * Creates a new [TemplateReverseEndpoint] with the given API key.
26 | *
27 | * @param apiKey The API key to use for the TEMPLATE Geocoding API.
28 | * @param block A block to configure the parameters for the TEMPLATE Geocoding API.
29 | */
30 | public constructor(
31 | apiKey: String,
32 | block: TemplateParametersBuilder.() -> Unit,
33 | ) : this(apiKey, templateParameters(block))
34 |
35 | override fun url(param: Coordinates): String {
36 | val (latitude, longitude) = param.run { latitude to longitude }
37 | return TemplatePlatformGeocoder.reverseUrl(latitude, longitude, apiKey, parameters)
38 | }
39 |
40 | override suspend fun mapResponse(response: HttpResponse): List {
41 | val result = response.body()
42 | return result.toPlaces()
43 | }
44 | }
--------------------------------------------------------------------------------
/compass-permissions-mobile/src/iosMain/kotlin/dev/jordond/compass/permissions/PermissionController.ios.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.permissions
2 |
3 | import dev.jordond.compass.Priority
4 | import dev.jordond.compass.permissions.mobile.internal.LocationPermissionManagerDelegate
5 | import dev.jordond.compass.permissions.mobile.internal.toPermissionState
6 | import kotlinx.coroutines.flow.MutableStateFlow
7 | import kotlinx.coroutines.flow.update
8 | import kotlin.coroutines.resume
9 | import kotlin.coroutines.suspendCoroutine
10 |
11 | internal actual fun createPermissionController(): LocationPermissionController {
12 | return IosLocationPermissionController(LocationPermissionManagerDelegate())
13 | }
14 |
15 | internal class IosLocationPermissionController(
16 | private val locationDelegate: LocationPermissionManagerDelegate,
17 | ) : LocationPermissionController {
18 |
19 | private val _permissionsStatus = MutableStateFlow(
20 | value = locationDelegate.currentPermissionStatus().toPermissionState,
21 | )
22 |
23 | init {
24 | locationDelegate.monitorPermission { permissionStatus ->
25 | _permissionsStatus.update { permissionStatus.toPermissionState }
26 | }
27 | }
28 |
29 | override fun hasPermission(): Boolean {
30 | return _permissionsStatus.value == PermissionState.Granted
31 | }
32 |
33 | override suspend fun requirePermissionFor(priority: Priority): PermissionState {
34 | val currentState = locationDelegate.currentPermissionStatus().toPermissionState
35 | return when {
36 | currentState == PermissionState.Granted ||
37 | currentState == PermissionState.DeniedForever -> currentState
38 | else -> {
39 | val result = suspendCoroutine { continuation ->
40 | locationDelegate.requestPermission { continuation.resume(it) }
41 | }
42 |
43 | result.toPermissionState
44 | }
45 | }
46 | }
47 | }
--------------------------------------------------------------------------------
/docs/geolocation/android-ios.md:
--------------------------------------------------------------------------------
1 | # 📱 Android / iOS
2 |
3 | Compass supports Geolocation on Android and iOS by using the built in location services.
4 |
5 | To use the Geolocator, it requires the user to provide permission. This is handled automatically when you start tracking or attempt to get the current location.
6 |
7 | Make sure you read the [Android](https://developer.android.com/develop/sensors-and-location/location) and [iOS](https://developer.apple.com/documentation/corelocation) documentation to fully understand how the location services work.
8 |
9 | {% hint style="info" %}
10 | If your project supports both Mobile and other targets, you need to configure your setup to provide a `Geolocator` for each platform. Check out [mixed-platforms.md](../usage/mixed-platforms.md "mention")for more information.
11 | {% endhint %}
12 |
13 | ## Usage
14 |
15 | Follow these steps to create a `Geolocator`
16 |
17 | ### Create Locator
18 |
19 | Geolocator is powered by the `Locator` object:
20 |
21 | ```kotlin
22 | val locator: Locator = MobileLocator()
23 | val locator: Locator = Locator.mobile()
24 | ```
25 |
26 | ### Create Geolocator
27 |
28 | Then you can use that to create the `Geolocator` object:
29 |
30 | ```kotlin
31 | val geolocator: Geolocator = Geolocator(locator)
32 | ```
33 |
34 | Or you can use an extension function to skip the `Locator` step:
35 |
36 | ```kotlin
37 | val geolocator: Geolocator = Geolocator()
38 | val geolocator: Geolocator = MobileGeolocator()
39 | val geolocator: Geolocator = Geolocator.mobile()
40 | ```
41 |
42 | ## Permissions
43 |
44 | The user needs to grant permission in order to get location data.
45 |
46 | On Android there are no further steps required, but on iOS you need to edit your `info.plist`, see [android-ios.md](../setup/android-ios.md "mention") to learn more.
47 |
48 | When you attempt to access the location, Compass will automatically ask the user for permission.
49 |
50 | ## Get location
51 |
52 | Now you can follow the steps in [geolocator.md](geolocator.md "mention")
53 |
--------------------------------------------------------------------------------
/compass-autocomplete-mobile/api/compass-autocomplete-mobile.api:
--------------------------------------------------------------------------------
1 | public final class dev/jordond/compass/autocomplete/MobileAutocompleteKt {
2 | public static final fun Autocomplete (Ldev/jordond/compass/autocomplete/AutocompleteOptions;Lkotlinx/coroutines/CoroutineDispatcher;)Ldev/jordond/compass/autocomplete/Autocomplete;
3 | public static synthetic fun Autocomplete$default (Ldev/jordond/compass/autocomplete/AutocompleteOptions;Lkotlinx/coroutines/CoroutineDispatcher;ILjava/lang/Object;)Ldev/jordond/compass/autocomplete/Autocomplete;
4 | public static final fun MobileAutocomplete (Ldev/jordond/compass/autocomplete/AutocompleteOptions;Lkotlinx/coroutines/CoroutineDispatcher;)Ldev/jordond/compass/autocomplete/Autocomplete;
5 | public static synthetic fun MobileAutocomplete$default (Ldev/jordond/compass/autocomplete/AutocompleteOptions;Lkotlinx/coroutines/CoroutineDispatcher;ILjava/lang/Object;)Ldev/jordond/compass/autocomplete/Autocomplete;
6 | public static final fun mobile (Ldev/jordond/compass/autocomplete/Autocomplete$Companion;Ldev/jordond/compass/autocomplete/AutocompleteOptions;Lkotlinx/coroutines/CoroutineDispatcher;)Ldev/jordond/compass/autocomplete/Autocomplete;
7 | public static synthetic fun mobile$default (Ldev/jordond/compass/autocomplete/Autocomplete$Companion;Ldev/jordond/compass/autocomplete/AutocompleteOptions;Lkotlinx/coroutines/CoroutineDispatcher;ILjava/lang/Object;)Ldev/jordond/compass/autocomplete/Autocomplete;
8 | }
9 |
10 | public final class dev/jordond/compass/autocomplete/mobile/MobileAutocompleteServiceKt {
11 | public static final fun MobileAutocompleteService (Ljava/lang/String;Ldev/jordond/compass/geocoder/mobile/MobilePlatformGeocoder;)Ldev/jordond/compass/autocomplete/AutocompleteService;
12 | public static synthetic fun MobileAutocompleteService$default (Ljava/lang/String;Ldev/jordond/compass/geocoder/mobile/MobilePlatformGeocoder;ILjava/lang/Object;)Ldev/jordond/compass/autocomplete/AutocompleteService;
13 | public static final fun mobile (Ldev/jordond/compass/autocomplete/AutocompleteService$Companion;)Ldev/jordond/compass/autocomplete/AutocompleteService;
14 | }
15 |
16 |
--------------------------------------------------------------------------------
/compass-geocoder-web-googlemaps/src/commonMain/kotlin/dev/jordond/compass/geocoder/web/GoogleMapsReverseEndpoint.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.geocoder.web
2 |
3 | import dev.jordond.compass.Coordinates
4 | import dev.jordond.compass.Place
5 | import dev.jordond.compass.geocoder.web.google.internal.GeocodeResponse
6 | import dev.jordond.compass.geocoder.web.google.internal.resultsOrThrow
7 | import dev.jordond.compass.geocoder.web.google.internal.toPlaces
8 | import dev.jordond.compass.geocoder.web.parameter.GoogleMapsParameters
9 | import dev.jordond.compass.geocoder.web.parameter.GoogleMapsParametersBuilder
10 | import dev.jordond.compass.geocoder.web.parameter.googleMapsParameters
11 | import io.ktor.client.call.body
12 | import io.ktor.client.statement.HttpResponse
13 |
14 | /**
15 | * A [ReverseEndpoint] that uses the Google Maps Geocoding API.
16 | *
17 | * @property apiKey The API key to use for the Google Maps Geocoding API.
18 | * @property parameters The parameters to use for the Google Maps Geocoding API.
19 | */
20 | public class GoogleMapsReverseEndpoint(
21 | private val apiKey: String,
22 | private val parameters: GoogleMapsParameters = GoogleMapsParameters(),
23 | ) : ReverseEndpoint {
24 |
25 | /**
26 | * Creates a new [GoogleMapsReverseEndpoint] with the given API key.
27 | *
28 | * @param apiKey The API key to use for the Google Maps Geocoding API.
29 | * @param block A block to configure the parameters for the Google Maps Geocoding API.
30 | */
31 | public constructor(
32 | apiKey: String,
33 | block: GoogleMapsParametersBuilder.() -> Unit,
34 | ) : this(apiKey, googleMapsParameters(block))
35 |
36 | override fun url(param: Coordinates): String {
37 | val (latitude, longitude) = param.run { latitude to longitude }
38 | return GoogleMapsPlatformGeocoder.reverseUrl(latitude, longitude, apiKey, parameters)
39 | }
40 |
41 | override suspend fun mapResponse(response: HttpResponse): List {
42 | val result = response.body().resultsOrThrow()
43 | return result.toPlaces()
44 | }
45 | }
--------------------------------------------------------------------------------
/compass-geolocation-mobile/src/commonMain/kotlin/dev/jordond/compass/geolocation/mobile/MobileLocator.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.geolocation.mobile
2 |
3 | import dev.jordond.compass.geolocation.Locator
4 | import dev.jordond.compass.geolocation.PermissionLocator
5 | import dev.jordond.compass.permissions.LocationPermissionController
6 | import dev.jordond.compass.permissions.mobile
7 |
8 | /**
9 | * A locator that provides geolocation services on Android and iOS.
10 | */
11 | public interface MobileLocator : PermissionLocator
12 |
13 | /**
14 | * Create an Android/iOS [MobileLocator] instance for geolocation operations.
15 | *
16 | * Make sure you read the [Android documentation](https://developer.android.com/develop/sensors-and-location/location)
17 | * as well as the [iOS documentation](https://developer.apple.com/documentation/corelocation)
18 | * to understand the permissions and accuracy.
19 | *
20 | * @param permissionController The permission controller to use for handling location permissions.
21 | * @return A new Android/iOS [MobileLocator] instance.
22 | */
23 | public fun MobileLocator(
24 | permissionController: LocationPermissionController = LocationPermissionController.mobile(),
25 | ): MobileLocator = createLocator(permissionController)
26 |
27 | /**
28 | * Create an Android/iOS [MobileLocator] instance for geolocation operations.
29 | *
30 | * Make sure you read the [Android documentation](https://developer.android.com/develop/sensors-and-location/location)
31 | * as well as the [iOS documentation](https://developer.apple.com/documentation/corelocation)
32 | * to understand the permissions and accuracy.
33 | *
34 | * @param permissionController The permission controller to use for handling location permissions.
35 | * @return A new Android/iOS [MobileLocator] instance.
36 | */
37 | public fun Locator.Companion.mobile(
38 | permissionController: LocationPermissionController = LocationPermissionController.mobile(),
39 | ): MobileLocator = MobileLocator(permissionController)
40 |
41 | internal expect fun createLocator(permissionController: LocationPermissionController): MobileLocator
--------------------------------------------------------------------------------
/compass-geocoder-web-googlemaps/src/commonMain/kotlin/dev/jordond/compass/geocoder/web/GoogleMapsForwardEndpoint.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.geocoder.web
2 |
3 | import dev.jordond.compass.Coordinates
4 | import dev.jordond.compass.geocoder.web.google.internal.GeocodeResponse
5 | import dev.jordond.compass.geocoder.web.google.internal.resultsOrThrow
6 | import dev.jordond.compass.geocoder.web.google.internal.toCoordinates
7 | import dev.jordond.compass.geocoder.web.parameter.GoogleMapsParameters
8 | import dev.jordond.compass.geocoder.web.parameter.GoogleMapsParametersBuilder
9 | import dev.jordond.compass.geocoder.web.parameter.googleMapsParameters
10 | import io.ktor.client.call.body
11 | import io.ktor.client.statement.HttpResponse
12 | import io.ktor.http.encodeURLQueryComponent
13 |
14 | /**
15 | * A [ForwardEndpoint] that uses the Google Maps Geocoding API.
16 | *
17 | * @property apiKey The API key to use for the Google Maps Geocoding API.
18 | * @property parameters The parameters to use for the Google Maps Geocoding API.
19 | */
20 | public class GoogleMapsForwardEndpoint(
21 | private val apiKey: String,
22 | private val parameters: GoogleMapsParameters = GoogleMapsParameters(),
23 | ) : ForwardEndpoint {
24 |
25 | /**
26 | * Creates a new [GoogleMapsForwardEndpoint] with the given API key.
27 | *
28 | * @param apiKey The API key to use for the Google Maps Geocoding API.
29 | * @param block A block to configure the parameters for the Google Maps Geocoding API.
30 | */
31 | public constructor(
32 | apiKey: String,
33 | block: GoogleMapsParametersBuilder.() -> Unit,
34 | ) : this(apiKey, googleMapsParameters(block))
35 |
36 | override fun url(param: String): String {
37 | val encodedQuery = param.encodeURLQueryComponent()
38 | return GoogleMapsPlatformGeocoder.forwardUrl(encodedQuery, apiKey, parameters)
39 | }
40 |
41 | override suspend fun mapResponse(response: HttpResponse): List {
42 | val result = response.body().resultsOrThrow()
43 | return result.toCoordinates()
44 | }
45 | }
--------------------------------------------------------------------------------
/compass-tools-web/src/commonMain/kotlin/dev/jordond/compass/tools/web/parameter/QueryParameters.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.tools.web.parameter
2 |
3 | import io.ktor.http.encodeURLQueryComponent
4 |
5 | /**
6 | * Represents a set of query parameters for a URL request.
7 | */
8 | public interface QueryParameters {
9 |
10 | /**
11 | * Key-value pairs of query parameters.
12 | */
13 | public val parameters: Map
14 |
15 | /**
16 | * Encodes the query parameters into a URL-encoded string.
17 | *
18 | * @return The URL-encoded query parameters.
19 | */
20 | public fun encode(): String = parameters
21 | .map { (key, value) -> "$key=$value" }
22 | .joinToString("&")
23 | .encodeURLQueryComponent(encodeFull = true)
24 |
25 | /**
26 | * Convert a boolean value to an integer.
27 | */
28 | public fun Boolean.toInt(): Int = if (this) 1 else 0
29 | }
30 |
31 | /**
32 | * Create a query parameter map from the given pairs.
33 | *
34 | * @param parameters The key-value pairs of query parameters.
35 | * @return The query parameter map.
36 | */
37 | @Suppress("UnusedReceiverParameter")
38 | public fun QueryParameters.parametersOf(
39 | vararg parameters: Pair,
40 | ): Map = internalParametersOf(*parameters)
41 |
42 | internal fun internalParametersOf(
43 | vararg parameters: Pair,
44 | ): Map = parameters
45 | .mapNotNull { (key, value) ->
46 | value?.let { nonNullValue ->
47 | when (nonNullValue) {
48 | is QueryParamListValue<*> -> key to nonNullValue.value
49 | is QueryParamValue -> key to nonNullValue.value
50 | is List<*> -> {
51 | key to nonNullValue
52 | .map { if (it is QueryParamValue) it.value else it.toString() }
53 | .toQueryParameterValue()
54 | }
55 | else -> key to nonNullValue.toString()
56 | }
57 | }
58 | }
59 | .toMap()
60 |
61 | private fun List.toQueryParameterValue(): String = joinToString(",")
--------------------------------------------------------------------------------
/compass-autocomplete/src/commonMain/kotlin/dev/jordond/compass/autocomplete/Autocomplete.kt:
--------------------------------------------------------------------------------
1 | @file:Suppress("FunctionName")
2 |
3 | package dev.jordond.compass.autocomplete
4 |
5 | import dev.jordond.compass.Place
6 | import dev.jordond.compass.autocomplete.internal.DefaultAutocomplete
7 | import kotlinx.coroutines.CoroutineDispatcher
8 | import kotlinx.coroutines.Dispatchers
9 |
10 | /**
11 | * Represents an autocomplete service.
12 | *
13 | * @param T The type of the autocomplete results.
14 | */
15 | public interface Autocomplete {
16 |
17 | /**
18 | * The options for the autocomplete.
19 | */
20 | public val options: AutocompleteOptions
21 |
22 | /**
23 | * Performs an autocomplete search using the provided [query].
24 | *
25 | * @param query The query to search for.
26 | * @return The autocomplete result.
27 | */
28 | public suspend fun search(query: String): AutocompleteResult
29 |
30 | public companion object
31 | }
32 |
33 | /**
34 | * Creates a new [Autocomplete] instance.
35 | *
36 | * @param T The type of the autocomplete results.
37 | * @param service The autocomplete service to use.
38 | * @param options The options for the autocomplete.
39 | * @param dispatcher The coroutine dispatcher to use.
40 | * @return A new [Autocomplete] instance.
41 | */
42 | public fun Autocomplete(
43 | service: AutocompleteService,
44 | options: AutocompleteOptions = AutocompleteOptions(),
45 | dispatcher: CoroutineDispatcher = Dispatchers.Default,
46 | ): Autocomplete = DefaultAutocomplete(service, options, dispatcher)
47 |
48 | /**
49 | * Creates a new [Autocomplete] instance for [Place]s.
50 | *
51 | * @param service The autocomplete service to use.
52 | * @param options The options for the autocomplete.
53 | * @param dispatcher The coroutine dispatcher to use.
54 | * @return A new [Autocomplete] instance.
55 | */
56 | public fun PlaceAutocomplete(
57 | service: AutocompleteService,
58 | options: AutocompleteOptions = AutocompleteOptions(),
59 | dispatcher: CoroutineDispatcher = Dispatchers.Default,
60 | ): Autocomplete = DefaultAutocomplete(service, options, dispatcher)
--------------------------------------------------------------------------------
/compass-geocoder-web-mapbox/src/commonMain/kotlin/dev/jordond/compass/geocoder/web/mapbox/internal/ContextResponse.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.geocoder.web.mapbox.internal
2 |
3 | import dev.jordond.compass.InternalCompassApi
4 | import kotlinx.serialization.SerialName
5 | import kotlinx.serialization.Serializable
6 |
7 | @InternalCompassApi
8 | @Serializable
9 | public data class ContextResponse(
10 | @SerialName("address")
11 | public val address: AddressDataPoint? = null,
12 |
13 | @SerialName("street")
14 | public val street: DataPoint? = null,
15 |
16 | @SerialName("neighbourhood")
17 | public val neighborhood: DataPoint? = null,
18 |
19 | @SerialName("postcode")
20 | public val postcode: DataPoint? = null,
21 |
22 | @SerialName("locality")
23 | public val locality: DataPoint? = null,
24 |
25 | @SerialName("place")
26 | public val place: DataPoint? = null,
27 |
28 | @SerialName("district")
29 | public val district: DataPoint? = null,
30 |
31 | @SerialName("region")
32 | public val region: RegionDataPoint? = null,
33 |
34 | @SerialName("country")
35 | public val country: CountryDataPoint? = null,
36 | )
37 |
38 | @InternalCompassApi
39 | @Serializable
40 | public data class DataPoint(
41 | @SerialName("name")
42 | public val name: String? = null,
43 | )
44 |
45 | @InternalCompassApi
46 | @Serializable
47 | public data class AddressDataPoint(
48 | @SerialName("name")
49 | public val name: String? = null,
50 |
51 | @SerialName("street_name")
52 | public val streetName: String? = null,
53 |
54 | @SerialName("address_number")
55 | public val addressNumber: String? = null,
56 | )
57 |
58 | @InternalCompassApi
59 | @Serializable
60 | public data class RegionDataPoint(
61 | @SerialName("name")
62 | public val name: String? = null,
63 |
64 | @SerialName("region_code")
65 | public val regionCode: String? = null,
66 | )
67 |
68 | @InternalCompassApi
69 | @Serializable
70 | public data class CountryDataPoint(
71 | @SerialName("name")
72 | public val name: String? = null,
73 |
74 | @SerialName("country_code")
75 | public val countryCode: String? = null,
76 | )
--------------------------------------------------------------------------------
/compass-autocomplete-web/api/android/compass-autocomplete-web.api:
--------------------------------------------------------------------------------
1 | public final class dev/jordond/compass/autocomplete/HttpAutocompleteKt {
2 | public static final fun Autocomplete (Ldev/jordond/compass/autocomplete/web/HttpAutocompleteService;Ldev/jordond/compass/autocomplete/AutocompleteOptions;Lkotlinx/coroutines/CoroutineDispatcher;)Ldev/jordond/compass/autocomplete/Autocomplete;
3 | public static final fun Autocomplete (Ldev/jordond/compass/tools/web/HttpApiEndpoint;Ldev/jordond/compass/autocomplete/AutocompleteOptions;Lkotlinx/serialization/json/Json;Lio/ktor/client/HttpClient;Lkotlinx/coroutines/CoroutineDispatcher;)Ldev/jordond/compass/autocomplete/Autocomplete;
4 | public static synthetic fun Autocomplete$default (Ldev/jordond/compass/autocomplete/web/HttpAutocompleteService;Ldev/jordond/compass/autocomplete/AutocompleteOptions;Lkotlinx/coroutines/CoroutineDispatcher;ILjava/lang/Object;)Ldev/jordond/compass/autocomplete/Autocomplete;
5 | public static synthetic fun Autocomplete$default (Ldev/jordond/compass/tools/web/HttpApiEndpoint;Ldev/jordond/compass/autocomplete/AutocompleteOptions;Lkotlinx/serialization/json/Json;Lio/ktor/client/HttpClient;Lkotlinx/coroutines/CoroutineDispatcher;ILjava/lang/Object;)Ldev/jordond/compass/autocomplete/Autocomplete;
6 | }
7 |
8 | public abstract interface class dev/jordond/compass/autocomplete/web/HttpAutocompleteService : dev/jordond/compass/autocomplete/AutocompleteService {
9 | public static final field Companion Ldev/jordond/compass/autocomplete/web/HttpAutocompleteService$Companion;
10 | }
11 |
12 | public final class dev/jordond/compass/autocomplete/web/HttpAutocompleteService$Companion {
13 | }
14 |
15 | public final class dev/jordond/compass/autocomplete/web/HttpAutocompleteServiceKt {
16 | public static final fun HttpAutocompleteService (Ldev/jordond/compass/tools/web/HttpApiEndpoint;Lkotlinx/serialization/json/Json;Lio/ktor/client/HttpClient;)Ldev/jordond/compass/autocomplete/web/HttpAutocompleteService;
17 | public static synthetic fun HttpAutocompleteService$default (Ldev/jordond/compass/tools/web/HttpApiEndpoint;Lkotlinx/serialization/json/Json;Lio/ktor/client/HttpClient;ILjava/lang/Object;)Ldev/jordond/compass/autocomplete/web/HttpAutocompleteService;
18 | }
19 |
20 |
--------------------------------------------------------------------------------
/compass-autocomplete-web/api/jvm/compass-autocomplete-web.api:
--------------------------------------------------------------------------------
1 | public final class dev/jordond/compass/autocomplete/HttpAutocompleteKt {
2 | public static final fun Autocomplete (Ldev/jordond/compass/autocomplete/web/HttpAutocompleteService;Ldev/jordond/compass/autocomplete/AutocompleteOptions;Lkotlinx/coroutines/CoroutineDispatcher;)Ldev/jordond/compass/autocomplete/Autocomplete;
3 | public static final fun Autocomplete (Ldev/jordond/compass/tools/web/HttpApiEndpoint;Ldev/jordond/compass/autocomplete/AutocompleteOptions;Lkotlinx/serialization/json/Json;Lio/ktor/client/HttpClient;Lkotlinx/coroutines/CoroutineDispatcher;)Ldev/jordond/compass/autocomplete/Autocomplete;
4 | public static synthetic fun Autocomplete$default (Ldev/jordond/compass/autocomplete/web/HttpAutocompleteService;Ldev/jordond/compass/autocomplete/AutocompleteOptions;Lkotlinx/coroutines/CoroutineDispatcher;ILjava/lang/Object;)Ldev/jordond/compass/autocomplete/Autocomplete;
5 | public static synthetic fun Autocomplete$default (Ldev/jordond/compass/tools/web/HttpApiEndpoint;Ldev/jordond/compass/autocomplete/AutocompleteOptions;Lkotlinx/serialization/json/Json;Lio/ktor/client/HttpClient;Lkotlinx/coroutines/CoroutineDispatcher;ILjava/lang/Object;)Ldev/jordond/compass/autocomplete/Autocomplete;
6 | }
7 |
8 | public abstract interface class dev/jordond/compass/autocomplete/web/HttpAutocompleteService : dev/jordond/compass/autocomplete/AutocompleteService {
9 | public static final field Companion Ldev/jordond/compass/autocomplete/web/HttpAutocompleteService$Companion;
10 | }
11 |
12 | public final class dev/jordond/compass/autocomplete/web/HttpAutocompleteService$Companion {
13 | }
14 |
15 | public final class dev/jordond/compass/autocomplete/web/HttpAutocompleteServiceKt {
16 | public static final fun HttpAutocompleteService (Ldev/jordond/compass/tools/web/HttpApiEndpoint;Lkotlinx/serialization/json/Json;Lio/ktor/client/HttpClient;)Ldev/jordond/compass/autocomplete/web/HttpAutocompleteService;
17 | public static synthetic fun HttpAutocompleteService$default (Ldev/jordond/compass/tools/web/HttpApiEndpoint;Lkotlinx/serialization/json/Json;Lio/ktor/client/HttpClient;ILjava/lang/Object;)Ldev/jordond/compass/autocomplete/web/HttpAutocompleteService;
18 | }
19 |
20 |
--------------------------------------------------------------------------------
/compass-geolocation-browser/src/commonMain/kotlin/dev/jordond/compass/geolocation/browser/api/model/GeolocationCoordinates.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.geolocation.browser.api.model
2 |
3 | /**
4 | * The GeolocationCoordinates interface represents the position and altitude of the device on Earth,
5 | * as well as the accuracy with which these properties are calculated. The geographic position
6 | * information is provided in terms of World Geodetic System coordinates (WGS84).
7 | *
8 | * [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/API/GeolocationCoordinates).
9 | *
10 | * @property latitude Returns a double representing the position's latitude in decimal degrees.
11 | * @property longitude Returns a double representing the position's longitude in decimal degrees.
12 | * @property accuracy Returns a double representing the accuracy of the latitude and longitude
13 | * properties, expressed in meters.
14 | * @property altitude Returns a double representing the position's altitude in meters, relative to
15 | * nominal sea level. This value can be null if the implementation cannot provide the data.
16 | * @property altitudeAccuracy Returns a double representing the accuracy of the altitude expressed
17 | * in meters. This value can be null if the implementation cannot provide the data.
18 | * @property heading Returns a double representing the direction towards which the device is facing.
19 | * This value, specified in degrees, indicates how far off from heading true north the device is.
20 | * 0 degrees represents true north, and the direction is determined clockwise (which means that east
21 | * is 90 degrees and west is 270 degrees). If speed is 0, heading is NaN. If the device is unable to
22 | * provide heading information, this value is null.
23 | * @property speed Returns a double representing the velocity of the device in meters per second.
24 | * This value can be null.
25 | */
26 | public external class GeolocationCoordinates {
27 | public val latitude: Double
28 | public val longitude: Double
29 | public val accuracy: Double
30 | public val altitude: Double?
31 | public val altitudeAccuracy: Double?
32 | public val heading: Double?
33 | public val speed: Double?
34 | }
35 |
--------------------------------------------------------------------------------
/compass-geolocation-mobile/src/androidMain/kotlin/dev/jordond/compass/geolocation/mobile/internal/Mapper.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.geolocation.mobile.internal
2 |
3 | import android.location.Location
4 | import android.os.Build.VERSION
5 | import android.os.Build.VERSION_CODES
6 | import com.google.android.gms.location.LocationRequest
7 | import dev.jordond.compass.Altitude
8 | import dev.jordond.compass.Azimuth
9 | import dev.jordond.compass.Coordinates
10 | import dev.jordond.compass.Priority
11 | import dev.jordond.compass.Speed
12 | import dev.jordond.compass.geolocation.LocationRequest as CompassLocationRequest
13 |
14 | /**
15 | * Converts a [Location] to a [dev.jordond.compass.Location].
16 | */
17 | internal fun Location.toModel(): dev.jordond.compass.Location = dev.jordond.compass.Location(
18 | coordinates = Coordinates(latitude = latitude, longitude = longitude),
19 | accuracy = accuracy.toDouble(),
20 | azimuth = Azimuth(
21 | degrees = bearing,
22 | accuracy = if (VERSION.SDK_INT < VERSION_CODES.O) null else bearingAccuracyDegrees,
23 | ),
24 | speed = Speed(
25 | mps = speed,
26 | accuracy = if (VERSION.SDK_INT < VERSION_CODES.O) null else speedAccuracyMetersPerSecond,
27 | ),
28 | altitude = Altitude(
29 | meters = altitude,
30 | accuracy = if (VERSION.SDK_INT < VERSION_CODES.O) null else verticalAccuracyMeters,
31 | ),
32 | timestampMillis = time,
33 | )
34 |
35 | internal val Priority.toAndroidPriority: Int
36 | get() = when (this) {
37 | Priority.Balanced -> com.google.android.gms.location.Priority.PRIORITY_BALANCED_POWER_ACCURACY
38 | Priority.HighAccuracy -> com.google.android.gms.location.Priority.PRIORITY_HIGH_ACCURACY
39 | Priority.LowPower -> com.google.android.gms.location.Priority.PRIORITY_LOW_POWER
40 | Priority.Passive -> com.google.android.gms.location.Priority.PRIORITY_PASSIVE
41 | }
42 |
43 | internal fun CompassLocationRequest.toAndroidLocationRequest(): LocationRequest {
44 | return LocationRequest
45 | .Builder(priority.toAndroidPriority, interval)
46 | .setGranularity(com.google.android.gms.location.Granularity.GRANULARITY_PERMISSION_LEVEL)
47 | .build()
48 | }
49 |
--------------------------------------------------------------------------------
/compass-autocomplete-web/src/commonMain/kotlin/dev/jordond/compass/autocomplete/HttpAutocomplete.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.autocomplete
2 |
3 | import dev.jordond.compass.autocomplete.web.HttpAutocompleteService
4 | import dev.jordond.compass.autocomplete.web.SearchEndpoint
5 | import dev.jordond.compass.tools.web.HttpApiEndpoint
6 | import io.ktor.client.HttpClient
7 | import kotlinx.coroutines.CoroutineDispatcher
8 | import kotlinx.coroutines.Dispatchers
9 | import kotlinx.serialization.json.Json
10 |
11 | /**
12 | * Creates a new [Autocomplete] instance that uses a HTTP service to provide autocomplete
13 | * suggestions.
14 | *
15 | * @param T The type of the autocomplete suggestions results.
16 | * @param httpService The HTTP service to use for autocomplete suggestions.
17 | * @param options The options to use for the autocomplete.
18 | * @param dispatcher The coroutine dispatcher to use for the autocomplete.
19 | * @return A new [Autocomplete] instance.
20 | */
21 | public fun Autocomplete(
22 | httpService: HttpAutocompleteService,
23 | options: AutocompleteOptions = AutocompleteOptions(),
24 | dispatcher: CoroutineDispatcher = Dispatchers.Default,
25 | ): Autocomplete = Autocomplete(httpService, options, dispatcher)
26 |
27 | /**
28 | * Creates a new [Autocomplete] instance that uses a HTTP service to provide autocomplete
29 | * suggestions.
30 | *
31 | * @param T The type of the autocomplete suggestions results.
32 | * @param endpoint The search endpoint to use for autocomplete suggestions.
33 | * @param options The options to use for the autocomplete.
34 | * @param json The JSON serializer to use for the autocomplete.
35 | * @param httpClient The HTTP client to use for the autocomplete.
36 | * @param dispatcher The coroutine dispatcher to use for the autocomplete.
37 | * @return A new [Autocomplete] instance.
38 | */
39 | public fun Autocomplete(
40 | endpoint: SearchEndpoint,
41 | options: AutocompleteOptions = AutocompleteOptions(),
42 | json: Json = HttpApiEndpoint.json(),
43 | httpClient: HttpClient = HttpApiEndpoint.httpClient(json),
44 | dispatcher: CoroutineDispatcher = Dispatchers.Default,
45 | ): Autocomplete {
46 | val service = HttpAutocompleteService(endpoint, json, httpClient)
47 | return Autocomplete(service, options, dispatcher)
48 | }
--------------------------------------------------------------------------------
/compass-geocoder-web-googlemaps/src/commonMain/kotlin/dev/jordond/compass/geocoder/web/parameter/GoogleMapsLocationType.kt:
--------------------------------------------------------------------------------
1 | package dev.jordond.compass.geocoder.web.parameter
2 |
3 | import dev.jordond.compass.tools.web.parameter.QueryParamListValue
4 | import dev.jordond.compass.tools.web.parameter.QueryParamValue
5 |
6 | /**
7 | * A filter of one or more location types, separated by a pipe (|). If the parameter contains
8 | * multiple location types, the API returns all addresses that match any of the types.
9 | * A note about processing: The location_type parameter does not restrict the search to the
10 | * specified location types. Rather, the location_type acts as a post-search filter: the API fetches
11 | * all results for the specified latlng, then discards those results that do not match the specified
12 | * location types.
13 | *
14 | * See [Google Maps Geocoding API](https://developers.google.com/maps/documentation/geocoding/requests-reverse-geocoding#optional_parameters)
15 | */
16 | public enum class GoogleMapsLocationType : QueryParamValue {
17 |
18 | /**
19 | * returns only the addresses for which Google has location information accurate down to street
20 | * address precision.
21 | */
22 | Rooftop,
23 |
24 | /**
25 | * returns only the addresses that reflect an approximation (usually on a road) interpolated
26 | * between two precise points (such as intersections). An interpolated range generally indicates
27 | * that rooftop geocodes are unavailable for a street address.
28 | */
29 | RangeInterpolated,
30 |
31 | /**
32 | * returns only geometric centers of a location such as a polyline (for example, a street) or
33 | * polygon (region).
34 | */
35 | GeometricCenter,
36 |
37 | /**
38 | * returns only the addresses that are characterized as approximate.
39 | */
40 | Approximate;
41 |
42 | override val value: String = name.uppercase()
43 | }
44 |
45 | /**
46 | * A list of [GoogleMapsLocationType] to be used as a query parameter.
47 | */
48 | public class GoogleMapsLocationTypeList(
49 | override val values: List,
50 | ) : QueryParamListValue {
51 |
52 | override val separator: String = "|"
53 |
54 | public constructor(vararg values: GoogleMapsLocationType) : this(values.toList())
55 | }
--------------------------------------------------------------------------------