Compare commits
7 Commits
302bdef429
...
87a244ef27
| Author | SHA1 | Date | |
|---|---|---|---|
| 87a244ef27 | |||
| 04fb321a40 | |||
|
|
b6d0acd62d | ||
|
|
533cb1e006 | ||
|
|
1727e606ee | ||
|
|
f35ffe52cc | ||
|
|
5b163f6f7a |
@ -72,7 +72,7 @@ tasks.register("generateManifest") {
|
|||||||
"latestVersionCode" to android.defaultConfig.versionCode,
|
"latestVersionCode" to android.defaultConfig.versionCode,
|
||||||
"developer" to "github.com/timklge",
|
"developer" to "github.com/timklge",
|
||||||
"description" to "Open-source extension that provides headwind direction, wind speed, forecast and other weather data fields.",
|
"description" to "Open-source extension that provides headwind direction, wind speed, forecast and other weather data fields.",
|
||||||
"releaseNotes" to "* Crop timespan in forecast widgets to 6 hours, use am / pm time format, enlarge font\n* Remove tailwind, icon forecast, weather data fields",
|
"releaseNotes" to "* Add route forecast support for OpenWeatherMap (thx @lockevod!)\n* Crop timespan in forecast widgets to 6 hours, use am / pm time format, enlarge font\n* Remove tailwind, icon forecast, weather data fields",
|
||||||
"screenshotUrls" to listOf(
|
"screenshotUrls" to listOf(
|
||||||
"$baseUrl/preview1.png",
|
"$baseUrl/preview1.png",
|
||||||
"$baseUrl/preview3.png",
|
"$baseUrl/preview3.png",
|
||||||
|
|||||||
@ -5,7 +5,8 @@ import android.util.Log
|
|||||||
import de.timklge.karooheadwind.datatypes.GpsCoordinates
|
import de.timklge.karooheadwind.datatypes.GpsCoordinates
|
||||||
import de.timklge.karooheadwind.util.signedAngleDifference
|
import de.timklge.karooheadwind.util.signedAngleDifference
|
||||||
import io.hammerhead.karooext.KarooSystemService
|
import io.hammerhead.karooext.KarooSystemService
|
||||||
import kotlinx.coroutines.awaitCancellation
|
import io.hammerhead.karooext.models.DataType
|
||||||
|
import io.hammerhead.karooext.models.StreamState
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
@ -13,6 +14,7 @@ import kotlinx.coroutines.flow.distinctUntilChanged
|
|||||||
import kotlinx.coroutines.flow.emitAll
|
import kotlinx.coroutines.flow.emitAll
|
||||||
import kotlinx.coroutines.flow.filter
|
import kotlinx.coroutines.flow.filter
|
||||||
import kotlinx.coroutines.flow.filterNotNull
|
import kotlinx.coroutines.flow.filterNotNull
|
||||||
|
import kotlinx.coroutines.flow.firstOrNull
|
||||||
import kotlinx.coroutines.flow.flow
|
import kotlinx.coroutines.flow.flow
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
|
|
||||||
@ -99,7 +101,33 @@ fun KarooSystemService.getGpsCoordinateFlow(context: Context): Flow<GpsCoordinat
|
|||||||
val initialFlow = flow {
|
val initialFlow = flow {
|
||||||
val lastKnownPosition = context.getLastKnownPosition()
|
val lastKnownPosition = context.getLastKnownPosition()
|
||||||
|
|
||||||
emit(lastKnownPosition)
|
if (lastKnownPosition == null) {
|
||||||
|
val initialState = streamDataFlow(DataType.Type.LOCATION).firstOrNull()?.let { it as? StreamState.Streaming }
|
||||||
|
|
||||||
|
initialState?.dataPoint?.let { dataPoint ->
|
||||||
|
val lat = dataPoint.values[DataType.Field.LOC_LATITUDE]
|
||||||
|
val lng = dataPoint.values[DataType.Field.LOC_LONGITUDE]
|
||||||
|
val orientation = dataPoint.values[DataType.Field.LOC_BEARING]
|
||||||
|
|
||||||
|
if (lat != null && lng != null) {
|
||||||
|
emit(GpsCoordinates(lat, lng, orientation))
|
||||||
|
|
||||||
|
Log.i(KarooHeadwindExtension.TAG, "No last known position found, fetched initial GPS position")
|
||||||
|
} else {
|
||||||
|
emit(null)
|
||||||
|
|
||||||
|
Log.w(KarooHeadwindExtension.TAG, "No last known position found, initial GPS position is unavailable")
|
||||||
|
}
|
||||||
|
} ?: run {
|
||||||
|
emit(null)
|
||||||
|
|
||||||
|
Log.w(KarooHeadwindExtension.TAG, "No last known position found, initial GPS position is unavailable")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
emit(lastKnownPosition)
|
||||||
|
|
||||||
|
Log.i(KarooHeadwindExtension.TAG, "Using last known position: $lastKnownPosition")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val gpsFlow = streamLocation()
|
val gpsFlow = streamLocation()
|
||||||
|
|||||||
@ -115,7 +115,7 @@ class KarooHeadwindExtension : KarooExtension("karoo-headwind", BuildConfig.VERS
|
|||||||
old == new
|
old == new
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.debounce(Duration.ofSeconds(5))
|
.throttle(5_000L)
|
||||||
|
|
||||||
var requestedGpsCoordinates: List<GpsCoordinates> = emptyList()
|
var requestedGpsCoordinates: List<GpsCoordinates> = emptyList()
|
||||||
|
|
||||||
@ -234,7 +234,7 @@ class KarooHeadwindExtension : KarooExtension("karoo-headwind", BuildConfig.VERS
|
|||||||
response
|
response
|
||||||
}.retry(Long.MAX_VALUE) { e ->
|
}.retry(Long.MAX_VALUE) { e ->
|
||||||
Log.w(TAG, "Failed to get weather data", e)
|
Log.w(TAG, "Failed to get weather data", e)
|
||||||
delay(1.minutes); true
|
delay(2.minutes); true
|
||||||
}.collect { response ->
|
}.collect { response ->
|
||||||
try {
|
try {
|
||||||
saveCurrentData(applicationContext, response)
|
saveCurrentData(applicationContext, response)
|
||||||
|
|||||||
@ -70,7 +70,8 @@ abstract class LineGraphForecastDataType(private val karooSystem: KarooSystemSer
|
|||||||
abstract fun getLineData(
|
abstract fun getLineData(
|
||||||
lineData: List<LineData>,
|
lineData: List<LineData>,
|
||||||
isImperial: Boolean,
|
isImperial: Boolean,
|
||||||
upcomingRoute: UpcomingRoute?
|
upcomingRoute: UpcomingRoute?,
|
||||||
|
isPreview: Boolean
|
||||||
): Set<LineGraphBuilder.Line>
|
): Set<LineGraphBuilder.Line>
|
||||||
|
|
||||||
private fun previewFlow(settingsAndProfileStream: Flow<SettingsAndProfile>): Flow<StreamData> =
|
private fun previewFlow(settingsAndProfileStream: Flow<SettingsAndProfile>): Flow<StreamData> =
|
||||||
@ -205,8 +206,7 @@ abstract class LineGraphForecastDataType(private val karooSystem: KarooSystemSer
|
|||||||
val viewJob = CoroutineScope(Dispatchers.IO).launch {
|
val viewJob = CoroutineScope(Dispatchers.IO).launch {
|
||||||
emitter.onNext(ShowCustomStreamState("", null))
|
emitter.onNext(ShowCustomStreamState("", null))
|
||||||
|
|
||||||
dataFlow.filter { it.isVisible }.collect { (allData, settingsAndProfile, widgetSettings, headingResponse, actualUpcomingRoute) ->
|
dataFlow.filter { it.isVisible }.collect { (allData, settingsAndProfile, _, headingResponse, upcomingRoute) ->
|
||||||
val upcomingRoute = if (allData?.provider == WeatherDataProvider.OPEN_METEO) actualUpcomingRoute else null
|
|
||||||
Log.d(KarooHeadwindExtension.TAG, "Updating weather forecast view")
|
Log.d(KarooHeadwindExtension.TAG, "Updating weather forecast view")
|
||||||
|
|
||||||
if (allData?.data.isNullOrEmpty()){
|
if (allData?.data.isNullOrEmpty()){
|
||||||
@ -261,7 +261,12 @@ abstract class LineGraphForecastDataType(private val karooSystem: KarooSystemSer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val pointData = getLineData(data, settingsAndProfile.isImperialTemperature, upcomingRoute)
|
val pointData = getLineData(
|
||||||
|
data,
|
||||||
|
settingsAndProfile.isImperialTemperature,
|
||||||
|
upcomingRoute,
|
||||||
|
config.preview
|
||||||
|
)
|
||||||
val bitmap = LineGraphBuilder(context).drawLineGraph(config.viewSize.first, config.viewSize.second, config.gridSize.first, config.gridSize.second, pointData) { x ->
|
val bitmap = LineGraphBuilder(context).drawLineGraph(config.viewSize.first, config.viewSize.second, config.gridSize.first, config.gridSize.second, pointData) { x ->
|
||||||
val startTime = data.firstOrNull()?.time
|
val startTime = data.firstOrNull()?.time
|
||||||
val time = startTime?.plus(floor(x).toLong(), ChronoUnit.HOURS)
|
val time = startTime?.plus(floor(x).toLong(), ChronoUnit.HOURS)
|
||||||
|
|||||||
@ -8,7 +8,8 @@ class PrecipitationForecastDataType(karooSystem: KarooSystemService) : LineGraph
|
|||||||
override fun getLineData(
|
override fun getLineData(
|
||||||
lineData: List<LineData>,
|
lineData: List<LineData>,
|
||||||
isImperial: Boolean,
|
isImperial: Boolean,
|
||||||
upcomingRoute: UpcomingRoute?
|
upcomingRoute: UpcomingRoute?,
|
||||||
|
isPreview: Boolean
|
||||||
): Set<LineGraphBuilder.Line> {
|
): Set<LineGraphBuilder.Line> {
|
||||||
val precipitationPoints = lineData.map { data ->
|
val precipitationPoints = lineData.map { data ->
|
||||||
if (isImperial) { // Convert mm to inches
|
if (isImperial) { // Convert mm to inches
|
||||||
|
|||||||
@ -8,7 +8,8 @@ class TemperatureForecastDataType(karooSystem: KarooSystemService) : LineGraphFo
|
|||||||
override fun getLineData(
|
override fun getLineData(
|
||||||
lineData: List<LineData>,
|
lineData: List<LineData>,
|
||||||
isImperial: Boolean,
|
isImperial: Boolean,
|
||||||
upcomingRoute: UpcomingRoute?
|
upcomingRoute: UpcomingRoute?,
|
||||||
|
isPreview: Boolean
|
||||||
): Set<LineGraphBuilder.Line> {
|
): Set<LineGraphBuilder.Line> {
|
||||||
val linePoints = lineData.map { data ->
|
val linePoints = lineData.map { data ->
|
||||||
if (isImperial) {
|
if (isImperial) {
|
||||||
|
|||||||
@ -22,7 +22,8 @@ class WindForecastDataType(karooSystem: KarooSystemService) : LineGraphForecastD
|
|||||||
override fun getLineData(
|
override fun getLineData(
|
||||||
lineData: List<LineData>,
|
lineData: List<LineData>,
|
||||||
isImperial: Boolean,
|
isImperial: Boolean,
|
||||||
upcomingRoute: UpcomingRoute?
|
upcomingRoute: UpcomingRoute?,
|
||||||
|
isPreview: Boolean
|
||||||
): Set<LineGraphBuilder.Line> {
|
): Set<LineGraphBuilder.Line> {
|
||||||
val windPoints = lineData.map { data ->
|
val windPoints = lineData.map { data ->
|
||||||
if (isImperial) { // Convert m/s to mph
|
if (isImperial) { // Convert m/s to mph
|
||||||
@ -41,9 +42,16 @@ class WindForecastDataType(karooSystem: KarooSystemService) : LineGraphForecastD
|
|||||||
}
|
}
|
||||||
|
|
||||||
val headwindPoints = try {
|
val headwindPoints = try {
|
||||||
if (upcomingRoute != null){
|
if (upcomingRoute != null || isPreview){
|
||||||
(0..<HEADWIND_SAMPLE_COUNT).mapNotNull { i ->
|
(0..<HEADWIND_SAMPLE_COUNT).mapNotNull { i ->
|
||||||
val t = i / HEADWIND_SAMPLE_COUNT.toDouble()
|
val t = i / HEADWIND_SAMPLE_COUNT.toDouble()
|
||||||
|
|
||||||
|
if (isPreview) {
|
||||||
|
return@mapNotNull LineGraphBuilder.DataPoint(i.toFloat() * (windPoints.size / HEADWIND_SAMPLE_COUNT.toFloat()), (-10f) + (20f * kotlin.random.Random.nextFloat()))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (upcomingRoute == null) return@mapNotNull null
|
||||||
|
|
||||||
val beforeLineData = lineData.getOrNull(floor(lineData.size * t).toInt()) ?: lineData.firstOrNull()
|
val beforeLineData = lineData.getOrNull(floor(lineData.size * t).toInt()) ?: lineData.firstOrNull()
|
||||||
val afterLineData = lineData.getOrNull(ceil(lineData.size * t).toInt()) ?: lineData.lastOrNull()
|
val afterLineData = lineData.getOrNull(ceil(lineData.size * t).toInt()) ?: lineData.lastOrNull()
|
||||||
|
|
||||||
@ -59,9 +67,24 @@ class WindForecastDataType(karooSystem: KarooSystemService) : LineGraphForecastD
|
|||||||
val beforeDistanceAlongRoute = beforeLineData.distance
|
val beforeDistanceAlongRoute = beforeLineData.distance
|
||||||
val afterDistanceAlongRoute = afterLineData.distance
|
val afterDistanceAlongRoute = afterLineData.distance
|
||||||
val distanceAlongRoute = (beforeDistanceAlongRoute + (afterDistanceAlongRoute - beforeDistanceAlongRoute) * dt).coerceIn(0.0, upcomingRoute.routeLength)
|
val distanceAlongRoute = (beforeDistanceAlongRoute + (afterDistanceAlongRoute - beforeDistanceAlongRoute) * dt).coerceIn(0.0, upcomingRoute.routeLength)
|
||||||
val coordsAlongRoute = TurfMeasurement.along(upcomingRoute.routePolyline, distanceAlongRoute, TurfConstants.UNIT_METERS)
|
val coordsAlongRoute = try {
|
||||||
val nextCoordsAlongRoute = TurfMeasurement.along(upcomingRoute.routePolyline, distanceAlongRoute + 5, TurfConstants.UNIT_METERS)
|
TurfMeasurement.along(upcomingRoute.routePolyline, distanceAlongRoute, TurfConstants.UNIT_METERS)
|
||||||
val bearingAlongRoute = TurfMeasurement.bearing(coordsAlongRoute, nextCoordsAlongRoute)
|
} catch(e: Exception) {
|
||||||
|
Log.e(KarooHeadwindExtension.TAG, "Error getting coordinates along route", e)
|
||||||
|
return@mapNotNull null
|
||||||
|
}
|
||||||
|
val nextCoordsAlongRoute = try {
|
||||||
|
TurfMeasurement.along(upcomingRoute.routePolyline, distanceAlongRoute + 5, TurfConstants.UNIT_METERS)
|
||||||
|
} catch(e: Exception) {
|
||||||
|
Log.e(KarooHeadwindExtension.TAG, "Error getting next coordinates along route", e)
|
||||||
|
return@mapNotNull null
|
||||||
|
}
|
||||||
|
val bearingAlongRoute = try {
|
||||||
|
TurfMeasurement.bearing(coordsAlongRoute, nextCoordsAlongRoute)
|
||||||
|
} catch(e: Exception) {
|
||||||
|
Log.e(KarooHeadwindExtension.TAG, "Error calculating bearing along route", e)
|
||||||
|
return@mapNotNull null
|
||||||
|
}
|
||||||
val windBearing = interpolatedWeather.windDirection + 180
|
val windBearing = interpolatedWeather.windDirection + 180
|
||||||
val diff = signedAngleDifference(bearingAlongRoute, windBearing)
|
val diff = signedAngleDifference(bearingAlongRoute, windBearing)
|
||||||
val headwindSpeed = cos( (diff + 180) * Math.PI / 180.0) * interpolatedWeather.windSpeed
|
val headwindSpeed = cos( (diff + 180) * Math.PI / 180.0) * interpolatedWeather.windSpeed
|
||||||
|
|||||||
@ -381,7 +381,7 @@ class LineGraphBuilder(val context: Context) {
|
|||||||
// Draw Left Y-axis ticks and labels
|
// Draw Left Y-axis ticks and labels
|
||||||
if (hasLeftYAxisData) {
|
if (hasLeftYAxisData) {
|
||||||
textPaint.textAlign = Align.RIGHT
|
textPaint.textAlign = Align.RIGHT
|
||||||
val numYTicks = if (gridWidth > 15) 2 else 1
|
val numYTicks = if (gridHeight > 15) (gridHeight / 10) else 1
|
||||||
if (abs(dataMaxYLeft - dataMinYLeft) > 0.0001f) {
|
if (abs(dataMaxYLeft - dataMinYLeft) > 0.0001f) {
|
||||||
for (i in 0..numYTicks) {
|
for (i in 0..numYTicks) {
|
||||||
val value = dataMinYLeft + ((dataMaxYLeft - dataMinYLeft) / numYTicks) * i
|
val value = dataMinYLeft + ((dataMaxYLeft - dataMinYLeft) / numYTicks) * i
|
||||||
|
|||||||
@ -6,7 +6,6 @@ import de.timklge.karooheadwind.KarooHeadwindExtension
|
|||||||
import de.timklge.karooheadwind.WeatherDataProvider
|
import de.timklge.karooheadwind.WeatherDataProvider
|
||||||
import de.timklge.karooheadwind.datatypes.GpsCoordinates
|
import de.timklge.karooheadwind.datatypes.GpsCoordinates
|
||||||
import de.timklge.karooheadwind.jsonWithUnknownKeys
|
import de.timklge.karooheadwind.jsonWithUnknownKeys
|
||||||
import de.timklge.karooheadwind.weatherprovider.WeatherDataForLocation
|
|
||||||
import de.timklge.karooheadwind.weatherprovider.WeatherDataResponse
|
import de.timklge.karooheadwind.weatherprovider.WeatherDataResponse
|
||||||
import de.timklge.karooheadwind.weatherprovider.WeatherProvider
|
import de.timklge.karooheadwind.weatherprovider.WeatherProvider
|
||||||
import de.timklge.karooheadwind.weatherprovider.WeatherProviderException
|
import de.timklge.karooheadwind.weatherprovider.WeatherProviderException
|
||||||
@ -17,12 +16,14 @@ import io.hammerhead.karooext.models.UserProfile
|
|||||||
import kotlinx.coroutines.FlowPreview
|
import kotlinx.coroutines.FlowPreview
|
||||||
import kotlinx.coroutines.TimeoutCancellationException
|
import kotlinx.coroutines.TimeoutCancellationException
|
||||||
import kotlinx.coroutines.channels.awaitClose
|
import kotlinx.coroutines.channels.awaitClose
|
||||||
|
import kotlinx.coroutines.coroutineScope
|
||||||
import kotlinx.coroutines.flow.callbackFlow
|
import kotlinx.coroutines.flow.callbackFlow
|
||||||
import kotlinx.coroutines.flow.catch
|
import kotlinx.coroutines.flow.catch
|
||||||
import kotlinx.coroutines.flow.single
|
import kotlinx.coroutines.flow.single
|
||||||
import kotlinx.coroutines.flow.timeout
|
import kotlinx.coroutines.flow.timeout
|
||||||
import kotlinx.serialization.SerialName
|
import kotlinx.serialization.SerialName
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlin.math.absoluteValue
|
||||||
import kotlin.time.Duration.Companion.seconds
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
|
||||||
|
|
||||||
@ -48,6 +49,8 @@ data class Snow(
|
|||||||
|
|
||||||
class OpenWeatherMapWeatherProvider(private val apiKey: String) : WeatherProvider {
|
class OpenWeatherMapWeatherProvider(private val apiKey: String) : WeatherProvider {
|
||||||
companion object {
|
companion object {
|
||||||
|
private const val MAX_API_CALLS = 3
|
||||||
|
|
||||||
fun convertWeatherCodeToOpenMeteo(owmCode: Int): Int {
|
fun convertWeatherCodeToOpenMeteo(owmCode: Int): Int {
|
||||||
// Mapping OpenWeatherMap to WMO OpenMeteo
|
// Mapping OpenWeatherMap to WMO OpenMeteo
|
||||||
return when (owmCode) {
|
return when (owmCode) {
|
||||||
@ -67,37 +70,68 @@ class OpenWeatherMapWeatherProvider(private val apiKey: String) : WeatherProvide
|
|||||||
coordinates: List<GpsCoordinates>,
|
coordinates: List<GpsCoordinates>,
|
||||||
settings: HeadwindSettings,
|
settings: HeadwindSettings,
|
||||||
profile: UserProfile?
|
profile: UserProfile?
|
||||||
): WeatherDataResponse {
|
): WeatherDataResponse = coroutineScope {
|
||||||
|
val selectedCoordinates = coordinates.take((MAX_API_CALLS - 1).coerceAtLeast(1)).toMutableList()
|
||||||
|
|
||||||
val response = makeOpenWeatherMapRequest(karooSystem, coordinates, apiKey)
|
if (coordinates.isNotEmpty() && !selectedCoordinates.contains(coordinates.last())){
|
||||||
val responseBody = response.body?.let { String(it) } ?: throw Exception("Null response from OpenWeatherMap")
|
selectedCoordinates.add(coordinates.last())
|
||||||
|
}
|
||||||
|
|
||||||
val responses = mutableListOf<WeatherDataForLocation>()
|
Log.d(KarooHeadwindExtension.TAG, "OpenWeatherMap: searching for ${selectedCoordinates.size} locations from ${coordinates.size} total")
|
||||||
|
selectedCoordinates.forEachIndexed { index, coord ->
|
||||||
|
Log.d(KarooHeadwindExtension.TAG, "Point #$index: ${coord.lat}, ${coord.lon}, distance: ${coord.distanceAlongRoute}")
|
||||||
|
}
|
||||||
|
|
||||||
val openWeatherMapWeatherDataForLocation = jsonWithUnknownKeys.decodeFromString<OpenWeatherMapWeatherDataForLocation>(responseBody)
|
val weatherDataForSelectedLocations = buildList {
|
||||||
responses.add(openWeatherMapWeatherDataForLocation.toWeatherDataForLocation(null))
|
for (coordinate in selectedCoordinates){
|
||||||
|
val response = makeOpenWeatherMapRequest(karooSystem, coordinate, apiKey)
|
||||||
|
val responseBody = response.body?.let { String(it) }
|
||||||
|
?: throw WeatherProviderException(response.statusCode, "Null Response from OpenWeatherMap")
|
||||||
|
|
||||||
// FIXME Route forecast
|
val weatherData = jsonWithUnknownKeys.decodeFromString<OpenWeatherMapWeatherDataForLocation>(responseBody)
|
||||||
|
|
||||||
return WeatherDataResponse(
|
add(coordinate to weatherData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val allLocationData = coordinates.map { originalCoord ->
|
||||||
|
val directMatch = weatherDataForSelectedLocations.find { it.first == originalCoord }
|
||||||
|
|
||||||
|
if (directMatch != null) {
|
||||||
|
directMatch.second.toWeatherDataForLocation(originalCoord.distanceAlongRoute)
|
||||||
|
} else {
|
||||||
|
|
||||||
|
val closestCoord = weatherDataForSelectedLocations.minByOrNull { (coord, _) ->
|
||||||
|
if (originalCoord.distanceAlongRoute != null && coord.distanceAlongRoute != null) {
|
||||||
|
(originalCoord.distanceAlongRoute - coord.distanceAlongRoute).absoluteValue
|
||||||
|
} else {
|
||||||
|
originalCoord.distanceTo(coord)
|
||||||
|
}
|
||||||
|
} ?: throw WeatherProviderException(500, "Error finding nearest coordinate")
|
||||||
|
|
||||||
|
|
||||||
|
closestCoord.second.toWeatherDataForLocation(originalCoord.distanceAlongRoute)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WeatherDataResponse(
|
||||||
provider = WeatherDataProvider.OPEN_WEATHER_MAP,
|
provider = WeatherDataProvider.OPEN_WEATHER_MAP,
|
||||||
data = responses
|
data = allLocationData
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@OptIn(FlowPreview::class)
|
@OptIn(FlowPreview::class)
|
||||||
private suspend fun makeOpenWeatherMapRequest(
|
private suspend fun makeOpenWeatherMapRequest(
|
||||||
service: KarooSystemService,
|
service: KarooSystemService,
|
||||||
coordinates: List<GpsCoordinates>,
|
coordinate: GpsCoordinates,
|
||||||
apiKey: String
|
apiKey: String
|
||||||
): HttpResponseState.Complete {
|
): HttpResponseState.Complete {
|
||||||
val response = callbackFlow {
|
val response = callbackFlow {
|
||||||
// OpenWeatherMap only supports setting imperial or metric units for all measurements, not individually for distance / temperature
|
|
||||||
val coordinate = coordinates.first()
|
|
||||||
|
|
||||||
// URL API 3.0 with onecall endpoint
|
// URL API 3.0 with onecall endpoint
|
||||||
val url = "https://api.openweathermap.org/data/3.0/onecall?lat=${coordinate.lat}&lon=${coordinate.lon}" +
|
val url = "https://api.openweathermap.org/data/3.0/onecall?lat=${coordinate.lat}&lon=${coordinate.lon}" +
|
||||||
"&appid=$apiKey&exclude=minutely,daily,alerts&units=metric"
|
"&appid=$apiKey&exclude=minutely,daily,alerts&units=metric"
|
||||||
|
|
||||||
Log.d(KarooHeadwindExtension.TAG, "Http request to OpenWeatherMap API 3.0: $url")
|
Log.d(KarooHeadwindExtension.TAG, "Http request to OpenWeatherMap API 3.0: $url")
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user