Show headwind line in wind forecast preview (#153)

This commit is contained in:
timklge 2025-06-11 20:43:08 +02:00 committed by GitHub
parent 1727e606ee
commit 533cb1e006
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 41 additions and 29 deletions

View File

@ -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> =
@ -261,7 +262,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)

View File

@ -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

View File

@ -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) {

View File

@ -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

View File

@ -73,32 +73,13 @@ class OpenWeatherMapWeatherProvider(private val apiKey: String) : WeatherProvide
settings: HeadwindSettings, settings: HeadwindSettings,
profile: UserProfile? profile: UserProfile?
): WeatherDataResponse = coroutineScope { ): WeatherDataResponse = coroutineScope {
val selectedCoordinates = coordinates.take(4)
val selectedCoordinates = when {
coordinates.size <= MAX_API_CALLS -> coordinates
else -> {
val mandatoryCoordinates = coordinates.take(3).toMutableList()
val fourthIndex = if (coordinates.size > 6) {
coordinates.size - 3
} else {
(coordinates.size / 2) + 1
}
mandatoryCoordinates.add(coordinates[fourthIndex.coerceIn(3, coordinates.lastIndex)])
mandatoryCoordinates
}
}
Log.d(KarooHeadwindExtension.TAG, "OpenWeatherMap: searching for ${selectedCoordinates.size} locations from ${coordinates.size} total") Log.d(KarooHeadwindExtension.TAG, "OpenWeatherMap: searching for ${selectedCoordinates.size} locations from ${coordinates.size} total")
selectedCoordinates.forEachIndexed { index, coord -> selectedCoordinates.forEachIndexed { index, coord ->
Log.d(KarooHeadwindExtension.TAG, "Point #$index: ${coord.lat}, ${coord.lon}, distance: ${coord.distanceAlongRoute}") Log.d(KarooHeadwindExtension.TAG, "Point #$index: ${coord.lat}, ${coord.lon}, distance: ${coord.distanceAlongRoute}")
} }
val weatherDataForSelectedLocations = selectedCoordinates.map { coordinate -> val weatherDataForSelectedLocations = selectedCoordinates.map { coordinate ->
async { async {
val response = makeOpenWeatherMapRequest(karooSystem, coordinate, apiKey) val response = makeOpenWeatherMapRequest(karooSystem, coordinate, apiKey)