diff --git a/src/main/handlebars/feed.handlebars b/src/main/handlebars/feed.handlebars index fc4fbde..f233b1b 100755 --- a/src/main/handlebars/feed.handlebars +++ b/src/main/handlebars/feed.handlebars @@ -39,6 +39,7 @@ {{/each}} {{count views '' '(Eine Ansicht)' '(# Ansichten)'}} + {{> partials/weather in=.}} {{> partials/images in=. first=@first}} diff --git a/src/main/handlebars/journey.handlebars b/src/main/handlebars/journey.handlebars index 55d13ae..7ecb891 100755 --- a/src/main/handlebars/journey.handlebars +++ b/src/main/handlebars/journey.handlebars @@ -64,6 +64,7 @@ parent: feed {{/each}} {{count views '' '(Eine Ansicht)' '(# Ansichten)'}} + {{> partials/weather in=.}} {{> partials/images in=.}} diff --git a/src/main/handlebars/layout.handlebars b/src/main/handlebars/layout.handlebars index bf12387..a2a51eb 100755 --- a/src/main/handlebars/layout.handlebars +++ b/src/main/handlebars/layout.handlebars @@ -460,8 +460,22 @@ } } - .meta, .meta a { + .meta { + margin-block: .25rem -.5rem; color: var(--meta-color); + + a { + color: var(--meta-color); + } + + .weather { + display: flex; + + img { + width: 1lh; + height: 1lh; + } + } } ul.locations { diff --git a/src/main/php/de/thekid/dialog/Helpers.php b/src/main/php/de/thekid/dialog/Helpers.php index 965a812..25d26ed 100755 --- a/src/main/php/de/thekid/dialog/Helpers.php +++ b/src/main/php/de/thekid/dialog/Helpers.php @@ -33,6 +33,13 @@ public function helpers() { if ($time > $until) return 'passed'; return 'current'; }; + yield 'temperature' => function($node, $context, $options) { + $diff= abs($options[0] - $options[1]); + return $diff <= ($options['tolerance'] ?? 1) + ? sprintf('%.1f', ($options[0] + $options[1]) / 2) + : $options[0].' — '.$options[1] + ; + }; yield 'size-class' => function($node, $context, $options) { $s= (int)$options[0]; return match { diff --git a/src/main/php/de/thekid/dialog/OpenMeteo.php b/src/main/php/de/thekid/dialog/OpenMeteo.php new file mode 100755 index 0000000..422cce8 --- /dev/null +++ b/src/main/php/de/thekid/dialog/OpenMeteo.php @@ -0,0 +1,44 @@ +base= $base instanceof URI ? $base : new URI($base); + } + + /** Returns a given API endpoint */ + protected function endpoint(string $kind): Endpoint { + return $this->endpoints[$kind]??= new Endpoint($this->base->using() + ->host($kind.'.'.$this->base->host()) + ->create() + ); + } + + public function lookup(string|float $lat, string|float $lon, Date $start, ?Date $end= null, TimeZone $tz= null): array { + $params= $this->auth + [ + 'latitude' => $lat, + 'longitude' => $lon, + 'start_date' => $start->toString('Y-m-d'), + 'end_date' => ($end ?? $start)->toString('Y-m-d'), + 'timezone' => ($tz ?? $start->getTimeZone())->name(), + 'daily' => ['sunrise', 'sunset'], + 'hourly' => ['weather_code', 'apparent_temperature'], + ]; + return $this->endpoint('archive-api')->resource('archive')->get($params)->match([ + 200 => fn($r) => $r->value(), + 400 => fn($r) => throw new IllegalArgumentException($r->content()), + ]); + } +} \ No newline at end of file diff --git a/src/main/php/de/thekid/dialog/import/LookupWeather.php b/src/main/php/de/thekid/dialog/import/LookupWeather.php new file mode 100755 index 0000000..1f2d998 --- /dev/null +++ b/src/main/php/de/thekid/dialog/import/LookupWeather.php @@ -0,0 +1,58 @@ + $entry, private array $images) { } + + public function execute(Endpoint $api) { + $weather= []; + $min= $max= null; + foreach ($this->entry['locations'] as $location) { + $tz= new TimeZone($location['timezone']); + + // Infer date range from first and last images + $dates= []; + foreach ($this->images as $image) { + $dates[]= new Date(strtr($image['meta']['dateTime'], ['+00:00' => '']), $tz); + } + usort($dates, fn($a, $b) => $b->compareTo($a)); + $first= current($dates); + $last= end($dates); + + // Filter hourly weather for the duration of the images + $result= $this->weather->lookup($location['lat'], $location['lon'], $first, $last, $tz); + $start= array_search(Dates::truncate($first, TimeInterval::$HOURS)->toString('Y-m-d\TH:i'), $result['hourly']['time']); + $end= array_search(Dates::truncate($last, TimeInterval::$HOURS)->toString('Y-m-d\TH:i'), $result['hourly']['time']); + + // Determine most common weather codes and temperature range + $codes= array_count_values(array_slice($result['hourly']['weather_code'], $min, 1 + ($end - $start))); + $temp= array_slice($result['hourly']['apparent_temperature'], $start, 1 + ($end - $start)); + $min= null === $min ? min($temp) : min($min, min($temp)); + $max= null === $max ? max($temp) : max($max, max($temp)); + + arsort($codes); + foreach ($codes as $code => $count) { + $weather[$code]??= 0; + $weather[$code]+= $count; + } + + yield $location['name'] => sprintf('#%02d @ %.1f-%.1f °C', key($codes), min($temp), max($temp)); + } + + arsort($weather); + return [ + 'code' => sprintf('%02d', key($weather)), + 'min' => $min, + 'max' => $max, + ]; + } + + public function description(): string { return 'Looking up weather'; } +} \ No newline at end of file diff --git a/src/main/php/de/thekid/dialog/import/Source.php b/src/main/php/de/thekid/dialog/import/Source.php index c9c9993..a4d2213 100755 --- a/src/main/php/de/thekid/dialog/import/Source.php +++ b/src/main/php/de/thekid/dialog/import/Source.php @@ -76,6 +76,7 @@ public function synchronize(Files $files) { if (isset($updated)) { $changes['locations']= yield new LookupLocationInfos($changes); + $changes['weather']= yield new LookupWeather($changes, $this->entry['images'] ?? []); $changes['published']= time(); yield new PublishEntry($this->entry['slug'], $changes); }