Skip to content

Commit be07602

Browse files
committed
feat: "smart" client method
Can filter all options and merges them in a generic response.
1 parent 1cf1573 commit be07602

1 file changed

Lines changed: 182 additions & 0 deletions

File tree

lib/src/main/kotlin/com/openmeteo/api/OpenMeteo.kt

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,4 +214,186 @@ class OpenMeteo(
214214
)
215215
)
216216

217+
private inline fun <reified T> separate(iterable: Iterable<Any>?) =
218+
iterable?.filterIsInstance<T>()?.ifEmpty { null }
219+
220+
class AnyResponse(
221+
override val latitude: Float,
222+
override val longitude: Float,
223+
val elevation: Float? = null,
224+
@SerialName("utc_offset_seconds")
225+
override val utcOffsetSeconds: Int,
226+
@SerialName("timezone")
227+
override val timeZone: TimeZone,
228+
@SerialName("timezone_abbreviation")
229+
override val timeZoneAbbreviation: String,
230+
@SerialName("hourly_units")
231+
override val hourlyUnits: Map<out QueryHourly.Options, Unit> = emptyMap(),
232+
@SerialName("hourly")
233+
override val hourlyValues: Map<out QueryHourly.Options, Array<Double?>> = emptyMap(),
234+
@SerialName("daily_units")
235+
override val dailyUnits: Map<out QueryDaily.Options, Unit> = emptyMap(),
236+
@SerialName("daily")
237+
override val dailyValues: Map<out QueryDaily.Options, Array<Double?>> = emptyMap(),
238+
) : ResponseCoordinates,
239+
ResponseHourly,
240+
ResponseDaily
241+
242+
operator fun invoke(
243+
hourly: Iterable<QueryHourly.Options>? = null,
244+
daily: Iterable<QueryDaily.Options>? = null,
245+
timeZone: TimeZone? = null,
246+
startDate: Date? = null,
247+
endDate: Date? = null,
248+
pastDays: Int? = null,
249+
forecastDays: Int? = null,
250+
domains: AirQualityDomains? = null,
251+
currentWeather: Boolean? = null,
252+
temperatureUnit: TemperatureUnit? = null,
253+
windSpeedUnit: WindSpeedUnit? = null,
254+
precipitationUnit: PrecipitationUnit? = null,
255+
latitude: Float = this.latitude,
256+
longitude: Float = this.longitude,
257+
) = runCatching {
258+
259+
val airQualityHourly = separate<AirQualityHourly>(hourly)
260+
val airQualityResponse = airQualityHourly?.let {
261+
airQuality(
262+
airQualityHourly, domains, timeZone, startDate, endDate,
263+
pastDays, latitude, longitude
264+
).getOrThrow()
265+
}
266+
267+
val ecmwfHourly = separate<EcmwfHourly>(hourly)
268+
val ecmwfResponse = ecmwfHourly?.let {
269+
ecmwf(
270+
ecmwfHourly, temperatureUnit, windSpeedUnit, precipitationUnit,
271+
timeZone, startDate, endDate, pastDays, latitude, longitude
272+
).getOrThrow()
273+
}
274+
275+
val forecastHourly = separate<ForecastHourly>(hourly)
276+
val forecastDaily = separate<ForecastDaily>(daily)
277+
// if both are null return null, else return hourly *or* daily
278+
val forecastResponse = (forecastHourly ?: forecastDaily)?.let {
279+
forecast(
280+
forecastHourly, forecastDaily, currentWeather,
281+
temperatureUnit, windSpeedUnit, precipitationUnit, timeZone,
282+
startDate, endDate, pastDays, latitude, longitude
283+
).getOrThrow()
284+
}
285+
286+
val gfsHourly = separate<GfsHourly>(hourly)
287+
val gfsDaily = separate<GfsDaily>(daily)
288+
val gfsResponse = (gfsHourly ?: gfsDaily)?.let {
289+
gfs(
290+
gfsHourly, gfsDaily, currentWeather, temperatureUnit,
291+
windSpeedUnit, precipitationUnit, timeZone, startDate, endDate,
292+
pastDays, forecastDays, latitude, longitude
293+
).getOrThrow()
294+
}
295+
296+
val historicalHourly = separate<HistoricalHourly>(hourly)
297+
val historicalDaily = separate<HistoricalDaily>(daily)
298+
val historicalResponse = (historicalHourly ?: historicalDaily)?.let {
299+
historical(
300+
historicalHourly, historicalDaily, temperatureUnit,
301+
windSpeedUnit, precipitationUnit, timeZone, startDate, endDate,
302+
latitude, longitude
303+
).getOrThrow()
304+
}
305+
306+
val marineHourly = separate<MarineHourly>(hourly)
307+
val marineDaily = separate<MarineDaily>(daily)
308+
val marineResponse = (marineHourly ?: marineDaily)?.let {
309+
marine(
310+
marineHourly, marineDaily, timeZone, startDate, endDate,
311+
latitude, longitude
312+
).getOrThrow()
313+
}
314+
315+
val hourlyResponses: List<ResponseHourly> = listOfNotNull(
316+
airQualityResponse,
317+
ecmwfResponse,
318+
forecastResponse,
319+
gfsResponse,
320+
historicalResponse,
321+
marineResponse,
322+
)
323+
324+
val dailyResponses: List<ResponseDaily> = listOfNotNull(
325+
forecastResponse,
326+
gfsResponse,
327+
historicalResponse,
328+
marineResponse,
329+
)
330+
331+
val responses = (hourlyResponses + dailyResponses)
332+
// we don't want duplicates
333+
.toSet().toList()
334+
335+
if (responses.isEmpty())
336+
throw Error("No request was performed! No valid option")
337+
338+
val elevation = forecastResponse?.elevation
339+
?: gfsResponse?.elevation
340+
341+
val utcOffsetSeconds = hourlyResponses[0].utcOffsetSeconds
342+
val timeZone0 = hourlyResponses[0].timeZone
343+
val timeZoneAbbreviation = hourlyResponses[0].timeZoneAbbreviation
344+
345+
val hourlyUnits = hourlyResponses
346+
.map { it.hourlyUnits }
347+
.reduce { acc, map -> acc + map }
348+
349+
val hourlyValues = hourlyResponses
350+
.map { it.hourlyValues }
351+
.reduce { acc, map -> acc + map }
352+
353+
val dailyUnits = dailyResponses
354+
.map { it.dailyUnits }
355+
.reduce { acc, map -> acc + map }
356+
357+
val dailyValues = dailyResponses
358+
.map { it.dailyValues }
359+
.reduce { acc, map -> acc + map }
360+
361+
362+
AnyResponse(
363+
latitude,
364+
longitude,
365+
elevation,
366+
utcOffsetSeconds,
367+
timeZone0,
368+
timeZoneAbbreviation,
369+
hourlyUnits,
370+
hourlyValues,
371+
dailyUnits,
372+
dailyValues,
373+
)
374+
375+
}
376+
377+
operator fun invoke(
378+
vararg options: Query.Options,
379+
timeZone: TimeZone? = null,
380+
startDate: Date? = null,
381+
endDate: Date? = null,
382+
pastDays: Int? = null,
383+
forecastDays: Int? = null,
384+
domains: AirQualityDomains? = null,
385+
currentWeather: Boolean? = null,
386+
temperatureUnit: TemperatureUnit? = null,
387+
windSpeedUnit: WindSpeedUnit? = null,
388+
precipitationUnit: PrecipitationUnit? = null,
389+
latitude: Float = this.latitude,
390+
longitude: Float = this.longitude,
391+
) = invoke(
392+
options.filterIsInstance<QueryHourly.Options>(),
393+
options.filterIsInstance<QueryDaily.Options>(),
394+
timeZone, startDate, endDate, pastDays, forecastDays, domains,
395+
currentWeather, temperatureUnit, windSpeedUnit, precipitationUnit,
396+
latitude, longitude,
397+
)
398+
217399
}

0 commit comments

Comments
 (0)