Compare commits

...

9 Commits

Author SHA1 Message Date
Paul van Tilburg 3fb899d1fd
Update the changelog 2022-05-10 15:05:57 +02:00
Paul van Tilburg f0cc54f074
Bump the version to 0.2.2 2022-05-10 14:58:21 +02:00
Paul van Tilburg ac653ef0c9
Cargo update 2022-05-10 14:57:03 +02:00
Paul van Tilburg e204e7905c
Update to Rocket 0.5-rc.2 2022-05-10 14:55:57 +02:00
Paul van Tilburg 08cdfe1e1c Merge pull request 'Fix timestamps for map samples not being correct' (#22) from 21-fix-ts-map-samples into main
Reviewed-on: #22
2022-05-10 14:26:38 +02:00
Paul van Tilburg 89395f21f6
Introduce RetrievedMaps; refactor around it
The `RetrievedMaps` struct captures the image and its metadata:
the last modification time and the base timestamp for the maps.

* No longer store the last modification time, called "stamp" before,
  separately in the `Maps` struct
* Update methods on `Maps` to use the `RetrievedMaps` structs and
  the timestamp base in particular for sampling and map marking
* Update the `MapsRefresh` implemention to use the last modification
  time
* Rename some variables from `map` to `image` in the helper functions
  for consistency
* Update tests and documentation
2022-05-10 14:19:09 +02:00
Paul van Tilburg ff9f1ac371
Parse timestamp base from filename 2022-05-10 13:21:21 +02:00
Paul van Tilburg 4a6eeab787
Fix sample/item being out of the combined series time range
For example, if there are 24 valid pollen samples and 20 valid air
quality items, the maximum pollen sample could be de 23th, but the
resulting combined series will only cover 20 items. So, it is should not
return that, but only look in the first 20 pollen samples for the
maximum sample.
2022-05-10 12:26:10 +02:00
Paul van Tilburg ab4b0bba72
Fix valid samples/items being discarded too early
A forecasted sample/item may be for example timestamped at 14:00.
For hourly forecasts, it will still be valid until 14:59:59, not
14:29:59.
2022-05-10 12:24:26 +02:00
7 changed files with 471 additions and 333 deletions

View File

@ -7,7 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
## [0.2.1] - 2002-05-08
## [0.2.2] - 2022-05-10
### Changed
* Switch to Rocket 0.5 RC2
### Fixed
* Fix timestamps for map samples not being correct (AQI, PAQI, UVI metrics) (#22)
* Valid samples/items will no longer be discarded too early
## [0.2.1] - 2022-05-08
### Added
@ -28,7 +39,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
Initial release.
[Unreleased]: https://git.luon.net/paul/sinoptik/compare/v0.2.1...HEAD
[Unreleased]: https://git.luon.net/paul/sinoptik/compare/v0.2.2...HEAD
[0.2.2]: https://git.luon.net/paul/sinoptik/compare/v0.2.1...v0.2.2
[0.2.1]: https://git.luon.net/paul/sinoptik/compare/v0.2.0...v0.2.1
[0.2.0]: https://git.luon.net/paul/sinoptik/compare/v0.1.0...v0.2.0
[0.1.0]: https://git.luon.net/paul/sinoptik/commits/tag/v0.1.0

492
Cargo.lock generated
View File

@ -23,6 +23,41 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234"
[[package]]
name = "aead"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b613b8e1e3cf911a086f53f03bf286f52fd7a7258e4fa606f0ef220d39d8877"
dependencies = [
"generic-array",
]
[[package]]
name = "aes"
version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8"
dependencies = [
"cfg-if 1.0.0",
"cipher",
"cpufeatures",
"opaque-debug",
]
[[package]]
name = "aes-gcm"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df5f85a83a7d8b0442b6aa7b504b8212c1733da07b98aae43d4bc21b2cb3cdf6"
dependencies = [
"aead",
"aes",
"cipher",
"ctr",
"ghash",
"subtle",
]
[[package]]
name = "ansi_term"
version = "0.12.1"
@ -132,12 +167,6 @@ dependencies = [
"rustc-demangle",
]
[[package]]
name = "base-x"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc19a4937b4fbd3fe3379793130e42060d10627a360f2127802b10b87e7baf74"
[[package]]
name = "base64"
version = "0.13.0"
@ -162,6 +191,15 @@ version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "block-buffer"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324"
dependencies = [
"generic-array",
]
[[package]]
name = "bstr"
version = "0.2.17"
@ -219,7 +257,7 @@ dependencies = [
"lazy_static",
"once_cell",
"thiserror",
"tokio 1.18.1",
"tokio 1.18.2",
]
[[package]]
@ -294,6 +332,15 @@ dependencies = [
"phf_codegen",
]
[[package]]
name = "cipher"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7"
dependencies = [
"generic-array",
]
[[package]]
name = "color-eyre"
version = "0.6.1"
@ -327,20 +374,21 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
[[package]]
name = "const_fn"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbdcdcb6d86f71c5e97409ad45898af11cbc995b4ee8112d59095a28d376c935"
[[package]]
name = "cookie"
version = "0.15.1"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d5f1c7727e460397e56abc4bddc1d49e07a1ad78fc98eb2e1c8f032a58a2f80d"
checksum = "94d4706de1b0fa5b132270cddffa8585166037822e260a944fe161acd137ca05"
dependencies = [
"aes-gcm",
"base64",
"hkdf",
"hmac",
"percent-encoding",
"time 0.2.27",
"rand",
"sha2",
"subtle",
"time 0.3.9",
"version_check",
]
@ -360,6 +408,15 @@ version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
[[package]]
name = "cpufeatures"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b"
dependencies = [
"libc",
]
[[package]]
name = "crc32fast"
version = "1.3.2"
@ -414,6 +471,16 @@ dependencies = [
"lazy_static",
]
[[package]]
name = "crypto-common"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57952ca27b5e3606ff4dd79b0020231aaf9d6aa76dc05fd30137538c50bd3ce8"
dependencies = [
"generic-array",
"typenum",
]
[[package]]
name = "csv"
version = "1.1.6"
@ -436,6 +503,15 @@ dependencies = [
"memchr",
]
[[package]]
name = "ctr"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "049bb91fb4aaf0e3c7efa6cd5ef877dbbbd15b39dad06d9948de4ec8a75761ea"
dependencies = [
"cipher",
]
[[package]]
name = "darling"
version = "0.13.4"
@ -514,10 +590,15 @@ dependencies = [
]
[[package]]
name = "discard"
version = "1.0.4"
name = "digest"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0"
checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506"
dependencies = [
"block-buffer",
"crypto-common",
"subtle",
]
[[package]]
name = "either"
@ -757,6 +838,16 @@ dependencies = [
"winapi 0.3.9",
]
[[package]]
name = "generic-array"
version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803"
dependencies = [
"typenum",
"version_check",
]
[[package]]
name = "geo-types"
version = "0.6.2"
@ -796,6 +887,16 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "ghash"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1583cc1656d7839fd3732b80cf4f38850336cdb9b8ded1cd399ca62958de3c99"
dependencies = [
"opaque-debug",
"polyval",
]
[[package]]
name = "gif"
version = "0.11.3"
@ -852,7 +953,7 @@ dependencies = [
"http",
"indexmap",
"slab",
"tokio 1.18.1",
"tokio 1.18.2",
"tokio-util 0.7.1",
"tracing",
]
@ -884,6 +985,24 @@ dependencies = [
"libc",
]
[[package]]
name = "hkdf"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437"
dependencies = [
"hmac",
]
[[package]]
name = "hmac"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
dependencies = [
"digest",
]
[[package]]
name = "http"
version = "0.2.7"
@ -976,7 +1095,7 @@ dependencies = [
"itoa 1.0.1",
"pin-project-lite 0.2.9",
"socket2 0.4.4",
"tokio 1.18.1",
"tokio 1.18.2",
"tower-service",
"tracing",
"want",
@ -1004,7 +1123,7 @@ dependencies = [
"bytes 1.1.0",
"hyper 0.14.18",
"native-tls",
"tokio 1.18.1",
"tokio 1.18.2",
"tokio-native-tls",
]
@ -1115,9 +1234,9 @@ checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35"
[[package]]
name = "jpeg-decoder"
version = "0.2.5"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be7ef4b99870f0c9f2fc2f20dbef72707e2bcca675bb9196734cf433e999b0c5"
checksum = "9478aa10f73e7528198d75109c8be5cd7d15fb530238040148d5f9a22d4c5b3b"
dependencies = [
"rayon",
]
@ -1261,7 +1380,7 @@ dependencies = [
"kernel32-sys",
"libc",
"log",
"miow 0.2.2",
"miow",
"net2",
"slab",
"winapi 0.2.8",
@ -1269,16 +1388,14 @@ dependencies = [
[[package]]
name = "mio"
version = "0.8.2"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52da4364ffb0e4fe33a9841a98a3f3014fb964045ce4f7a45a398243c8d6b0c9"
checksum = "713d550d9b44d89174e066b7a6217ae06234c10cb47819a88290d2b353c31799"
dependencies = [
"libc",
"log",
"miow 0.3.7",
"ntapi",
"wasi 0.11.0+wasi-snapshot-preview1",
"winapi 0.3.9",
"windows-sys",
]
[[package]]
@ -1293,15 +1410,6 @@ dependencies = [
"ws2_32-sys",
]
[[package]]
name = "miow"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21"
dependencies = [
"winapi 0.3.9",
]
[[package]]
name = "multer"
version = "2.0.2"
@ -1317,7 +1425,7 @@ dependencies = [
"memchr",
"mime",
"spin",
"tokio 1.18.1",
"tokio 1.18.2",
"tokio-util 0.6.9",
"version_check",
]
@ -1360,15 +1468,6 @@ dependencies = [
"winapi 0.3.9",
]
[[package]]
name = "ntapi"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c28774a7fd2fbb4f0babd8237ce554b73af68021b5f695a3cebd6c59bac0980f"
dependencies = [
"winapi 0.3.9",
]
[[package]]
name = "num-integer"
version = "0.1.45"
@ -1421,10 +1520,19 @@ dependencies = [
]
[[package]]
name = "object"
version = "0.28.3"
name = "num_threads"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40bec70ba014595f99f7aa110b84331ffe1ee9aece7fe6f387cc7e3ecda4d456"
checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44"
dependencies = [
"libc",
]
[[package]]
name = "object"
version = "0.28.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e42c982f2d955fac81dd7e1d0e1426a7d702acd9c98d19ab01083a6a0328c424"
dependencies = [
"memchr",
]
@ -1435,6 +1543,12 @@ version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9"
[[package]]
name = "opaque-debug"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
[[package]]
name = "openssl"
version = "0.10.40"
@ -1488,27 +1602,25 @@ checksum = "decf7381921fea4dcb2549c5667eda59b3ec297ab7e2b5fc33eac69d2e7da87b"
[[package]]
name = "parking_lot"
version = "0.11.2"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99"
checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58"
dependencies = [
"instant",
"lock_api",
"parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.8.5"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216"
checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929"
dependencies = [
"cfg-if 1.0.0",
"instant",
"libc",
"redox_syscall",
"smallvec",
"winapi 0.3.9",
"windows-sys",
]
[[package]]
@ -1644,18 +1756,24 @@ dependencies = [
"miniz_oxide",
]
[[package]]
name = "polyval"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8419d2b623c7c0896ff2d5d96e2cb4ede590fed28fcc34934f4c33c036e620a1"
dependencies = [
"cfg-if 1.0.0",
"cpufeatures",
"opaque-debug",
"universal-hash",
]
[[package]]
name = "ppv-lite86"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
[[package]]
name = "proc-macro-hack"
version = "0.5.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"
[[package]]
name = "proc-macro2"
version = "1.0.38"
@ -1866,7 +1984,7 @@ dependencies = [
"serde",
"serde_json",
"serde_urlencoded",
"tokio 1.18.1",
"tokio 1.18.2",
"tokio-native-tls",
"url",
"wasm-bindgen",
@ -1877,9 +1995,9 @@ dependencies = [
[[package]]
name = "rocket"
version = "0.5.0-rc.1"
version = "0.5.0-rc.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a71c18c42a0eb15bf3816831caf0dad11e7966f2a41aaf486a701979c4dd1f2"
checksum = "98ead083fce4a405feb349cf09abdf64471c6077f14e0ce59364aa90d4b99317"
dependencies = [
"async-stream",
"async-trait",
@ -1905,10 +2023,10 @@ dependencies = [
"serde_json",
"state",
"tempfile",
"time 0.2.27",
"tokio 1.18.1",
"time 0.3.9",
"tokio 1.18.2",
"tokio-stream",
"tokio-util 0.6.9",
"tokio-util 0.7.1",
"ubyte",
"version_check",
"yansi",
@ -1916,9 +2034,9 @@ dependencies = [
[[package]]
name = "rocket_codegen"
version = "0.5.0-rc.1"
version = "0.5.0-rc.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "66f5fa462f7eb958bba8710c17c5d774bbbd59809fa76fb1957af7e545aea8bb"
checksum = "d6aeb6bb9c61e9cd2c00d70ea267bf36f76a4cc615e5908b349c2f9d93999b47"
dependencies = [
"devise",
"glob",
@ -1932,19 +2050,18 @@ dependencies = [
[[package]]
name = "rocket_http"
version = "0.5.0-rc.1"
version = "0.5.0-rc.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23c8b7d512d2fcac2316ebe590cde67573844b99e6cc9ee0f53375fa16e25ebd"
checksum = "2ded65d127954de3c12471630bf4b81a2792f065984461e65b91d0fdaafc17a2"
dependencies = [
"cookie",
"either",
"futures",
"http",
"hyper 0.14.18",
"indexmap",
"log",
"memchr",
"mime",
"parking_lot",
"pear",
"percent-encoding",
"pin-project-lite 0.2.9",
@ -1953,8 +2070,8 @@ dependencies = [
"smallvec",
"stable-pattern",
"state",
"time 0.2.27",
"tokio 1.18.1",
"time 0.3.9",
"tokio 1.18.2",
"uncased",
]
@ -1964,15 +2081,6 @@ version = "0.1.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342"
[[package]]
name = "rustc_version"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
dependencies = [
"semver",
]
[[package]]
name = "rustversion"
version = "1.0.6"
@ -2036,21 +2144,6 @@ dependencies = [
"libc",
]
[[package]]
name = "semver"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
dependencies = [
"semver-parser",
]
[[package]]
name = "semver-parser"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
[[package]]
name = "serde"
version = "1.0.137"
@ -2095,20 +2188,16 @@ dependencies = [
]
[[package]]
name = "sha1"
version = "0.6.1"
name = "sha2"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1da05c97445caa12d05e848c4a4fcbbea29e748ac28f7e80e9b010392063770"
checksum = "55deaec60f81eefe3cce0dc50bda92d6d8e88f2a27df7c5033b42afeb1ed2676"
dependencies = [
"sha1_smol",
"cfg-if 1.0.0",
"cpufeatures",
"digest",
]
[[package]]
name = "sha1_smol"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012"
[[package]]
name = "sharded-slab"
version = "0.1.4"
@ -2129,7 +2218,7 @@ dependencies = [
[[package]]
name = "sinoptik"
version = "0.2.1"
version = "0.2.2"
dependencies = [
"assert_float_eq",
"assert_matches",
@ -2201,15 +2290,6 @@ dependencies = [
"memchr",
]
[[package]]
name = "standback"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e113fb6f3de07a243d434a56ec6f186dfd51cb08448239fe7bcae73f87ff28ff"
dependencies = [
"version_check",
]
[[package]]
name = "state"
version = "0.5.3"
@ -2219,55 +2299,6 @@ dependencies = [
"loom",
]
[[package]]
name = "stdweb"
version = "0.4.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5"
dependencies = [
"discard",
"rustc_version",
"stdweb-derive",
"stdweb-internal-macros",
"stdweb-internal-runtime",
"wasm-bindgen",
]
[[package]]
name = "stdweb-derive"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef"
dependencies = [
"proc-macro2",
"quote",
"serde",
"serde_derive",
"syn",
]
[[package]]
name = "stdweb-internal-macros"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11"
dependencies = [
"base-x",
"proc-macro2",
"quote",
"serde",
"serde_derive",
"serde_json",
"sha1",
"syn",
]
[[package]]
name = "stdweb-internal-runtime"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0"
[[package]]
name = "strsim"
version = "0.10.0"
@ -2275,10 +2306,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "syn"
version = "1.0.92"
name = "subtle"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ff7c592601f11445996a06f8ad0c27f094a58857c2f89e97974ab9235b92c52"
checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
[[package]]
name = "syn"
version = "1.0.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04066589568b72ec65f42d65a1a52436e954b168773148893c020269563decf2"
dependencies = [
"proc-macro2",
"quote",
@ -2361,41 +2398,21 @@ dependencies = [
[[package]]
name = "time"
version = "0.2.27"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4752a97f8eebd6854ff91f1c1824cd6160626ac4bd44287f7f4ea2035a02a242"
checksum = "c2702e08a7a860f005826c6815dcac101b19b5eb330c27fe4a5928fec1d20ddd"
dependencies = [
"const_fn",
"itoa 1.0.1",
"libc",
"standback",
"stdweb",
"num_threads",
"time-macros",
"version_check",
"winapi 0.3.9",
]
[[package]]
name = "time-macros"
version = "0.1.1"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "957e9c6e26f12cb6d0dd7fc776bb67a706312e7299aed74c8dd5b17ebb27e2f1"
dependencies = [
"proc-macro-hack",
"time-macros-impl",
]
[[package]]
name = "time-macros-impl"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd3c141a1b43194f3f56a1411225df8646c55781d5f26db825b3d98507eb482f"
dependencies = [
"proc-macro-hack",
"proc-macro2",
"quote",
"standback",
"syn",
]
checksum = "42657b1a6f4d817cda8e7a0ace261fe0cc946cf3a80314390b22cc61ae080792"
[[package]]
name = "tinyvec"
@ -2432,14 +2449,14 @@ dependencies = [
[[package]]
name = "tokio"
version = "1.18.1"
version = "1.18.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dce653fb475565de9f6fb0614b28bca8df2c430c0cf84bcd9c843f15de5414cc"
checksum = "4903bf0427cf68dddd5aa6a93220756f8be0c34fcfa9f5e6191e103e15a31395"
dependencies = [
"bytes 1.1.0",
"libc",
"memchr",
"mio 0.8.2",
"mio 0.8.3",
"num_cpus",
"once_cell",
"pin-project-lite 0.2.9",
@ -2467,7 +2484,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b"
dependencies = [
"native-tls",
"tokio 1.18.1",
"tokio 1.18.2",
]
[[package]]
@ -2478,7 +2495,7 @@ checksum = "50145484efff8818b5ccd256697f36863f587da82cf8b409c53adf1e840798e3"
dependencies = [
"futures-core",
"pin-project-lite 0.2.9",
"tokio 1.18.1",
"tokio 1.18.2",
]
[[package]]
@ -2516,7 +2533,7 @@ dependencies = [
"futures-sink",
"log",
"pin-project-lite 0.2.9",
"tokio 1.18.1",
"tokio 1.18.2",
]
[[package]]
@ -2529,7 +2546,7 @@ dependencies = [
"futures-core",
"futures-sink",
"pin-project-lite 0.2.9",
"tokio 1.18.1",
"tokio 1.18.2",
"tracing",
]
@ -2637,6 +2654,12 @@ version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642"
[[package]]
name = "typenum"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
[[package]]
name = "ubyte"
version = "0.10.1"
@ -2686,6 +2709,16 @@ version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04"
[[package]]
name = "universal-hash"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05"
dependencies = [
"generic-array",
"subtle",
]
[[package]]
name = "url"
version = "2.2.2"
@ -2856,6 +2889,49 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-sys"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2"
dependencies = [
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_msvc"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47"
[[package]]
name = "windows_i686_gnu"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6"
[[package]]
name = "windows_i686_msvc"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024"
[[package]]
name = "windows_x86_64_gnu"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1"
[[package]]
name = "windows_x86_64_msvc"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
[[package]]
name = "winreg"
version = "0.7.0"

View File

@ -1,6 +1,6 @@
[package]
name = "sinoptik"
version = "0.2.1"
version = "0.2.2"
authors = [
"Admar Schoonen <admar@luon.net",
"Paul van Tilburg <paul@luon.net>"
@ -20,7 +20,7 @@ csv = "1.1.6"
geocoding = "0.3.1"
image = "0.24.1"
reqwest = { version = "0.11.9", features = ["json"] }
rocket = { version = "0.5.0-rc.1", features = ["json"] }
rocket = { version = "0.5.0-rc.2", features = ["json"] }
[dev-dependencies]
assert_float_eq = "1.1.3"

View File

@ -10,8 +10,7 @@
use std::future::Future;
use std::sync::{Arc, Mutex};
use rocket::http::ContentType;
use rocket::response::content::Custom;
use rocket::response::Responder;
use rocket::serde::json::Json;
use rocket::{get, routes, Build, Rocket, State};
@ -25,6 +24,10 @@ pub(crate) mod maps;
pub(crate) mod position;
pub(crate) mod providers;
#[derive(Responder)]
#[response(status = 200, content_type = "image/png")]
struct PngImageData(Vec<u8>);
/// Handler for retrieving the forecast for an address.
#[get("/forecast?<address>&<metrics>")]
async fn forecast_address(
@ -61,11 +64,11 @@ async fn map_address(
address: String,
metric: Metric,
maps_handle: &State<MapsHandle>,
) -> Option<Custom<Vec<u8>>> {
) -> Option<PngImageData> {
let position = resolve_address(address).await?;
let image_data = mark_map(position, metric, maps_handle).await;
image_data.map(|id| Custom(ContentType::PNG, id))
image_data.map(PngImageData)
}
/// Handler for showing the current map with the geocoded position for a specific metric.
@ -77,11 +80,11 @@ async fn map_geo(
lon: f64,
metric: Metric,
maps_handle: &State<MapsHandle>,
) -> Option<Custom<Vec<u8>>> {
) -> Option<PngImageData> {
let position = Position::new(lat, lon);
let image_data = mark_map(position, metric, maps_handle).await;
image_data.map(|id| Custom(ContentType::PNG, id))
image_data.map(PngImageData)
}
/// Sets up Rocket.
@ -111,12 +114,15 @@ mod tests {
use rocket::local::blocking::Client;
use rocket::serde::json::Value as JsonValue;
use super::maps::RetrievedMaps;
use super::*;
fn maps_stub(map_count: u32) -> DynamicImage {
fn maps_stub(map_count: u32) -> RetrievedMaps {
let map_color = Rgba::from([73, 218, 33, 255]); // First color from map key.
let image =
DynamicImage::ImageRgba8(RgbaImage::from_pixel(820 * map_count, 988, map_color));
DynamicImage::ImageRgba8(RgbaImage::from_pixel(820 * map_count, 988, map_color))
RetrievedMaps::new(image)
}
fn maps_handle_stub() -> MapsHandle {

View File

@ -22,7 +22,7 @@ async fn main() -> Result<()> {
select! {
result = rocket.launch() => {
result?
result.map(|_| ())?
}
result = maps_refresher => {
shutdown.notify();

View File

@ -8,7 +8,7 @@ use std::f64::consts::PI;
use std::sync::{Arc, Mutex};
use chrono::serde::ts_seconds;
use chrono::{DateTime, Duration, Utc};
use chrono::{DateTime, Duration, NaiveDateTime, Utc};
use image::{DynamicImage, GenericImage, GenericImageView, ImageFormat, Pixel, Rgb, Rgba};
use reqwest::Url;
use rocket::serde::Serialize;
@ -113,26 +113,20 @@ trait MapsRefresh {
fn is_uvi_stale(&self) -> bool;
/// Updates the pollen maps.
fn set_pollen(&self, result: Option<(DynamicImage, DateTime<Utc>)>);
fn set_pollen(&self, result: Option<RetrievedMaps>);
/// Updates the UV index maps.
fn set_uvi(&self, result: Option<(DynamicImage, DateTime<Utc>)>);
fn set_uvi(&self, result: Option<RetrievedMaps>);
}
/// Container type for all in-memory cached maps.
#[derive(Debug)]
#[derive(Debug, Default)]
pub(crate) struct Maps {
/// The pollen maps (from Buienradar).
pub(crate) pollen: Option<DynamicImage>,
/// The timestamp the pollen maps were last refreshed.
pollen_stamp: DateTime<Utc>,
pub(crate) pollen: Option<RetrievedMaps>,
/// The UV index maps (from Buienradar).
pub(crate) uvi: Option<DynamicImage>,
/// The timestamp the UV index maps were last refreshed.
uvi_stamp: DateTime<Utc>,
pub(crate) uvi: Option<RetrievedMaps>,
}
impl Maps {
@ -141,12 +135,9 @@ impl Maps {
/// It contains an [`DynamicImage`] per maps type, if downloaded, and the timestamp of the last
/// update.
pub(crate) fn new() -> Self {
let now = Utc::now();
Self {
pollen: None,
pollen_stamp: now,
uvi: None,
uvi_stamp: now,
}
}
@ -156,16 +147,18 @@ impl Maps {
/// the current moment or if the provided position is not within the bounds of the map.
pub(crate) fn pollen_mark(&self, position: Position) -> Option<DynamicImage> {
self.pollen.as_ref().and_then(|maps| {
let map = map_at(
maps,
self.pollen_stamp,
let image = &maps.image;
let stamp = maps.timestamp_base;
let marked_image = map_at(
image,
stamp,
POLLEN_MAP_INTERVAL,
POLLEN_MAP_COUNT,
Utc::now(),
)?;
let coords = project(&map, POLLEN_MAP_REF_POINTS, position)?;
let coords = project(&marked_image, POLLEN_MAP_REF_POINTS, position)?;
Some(mark(map, coords))
Some(mark(marked_image, coords))
})
}
@ -176,16 +169,12 @@ impl Maps {
/// in the series of maps.
pub(crate) fn pollen_samples(&self, position: Position) -> Option<Vec<Sample>> {
self.pollen.as_ref().and_then(|maps| {
let map = maps.view(0, 0, maps.width() / UVI_MAP_COUNT, maps.height());
let image = &maps.image;
let map = image.view(0, 0, image.width() / UVI_MAP_COUNT, image.height());
let coords = project(&*map, POLLEN_MAP_REF_POINTS, position)?;
let stamp = maps.timestamp_base;
sample(
maps,
self.pollen_stamp,
POLLEN_MAP_INTERVAL,
POLLEN_MAP_COUNT,
coords,
)
sample(image, stamp, POLLEN_MAP_INTERVAL, POLLEN_MAP_COUNT, coords)
})
}
@ -195,16 +184,12 @@ impl Maps {
/// the current moment or if the provided position is not within the bounds of the map.
pub(crate) fn uvi_mark(&self, position: Position) -> Option<DynamicImage> {
self.uvi.as_ref().and_then(|maps| {
let map = map_at(
maps,
self.uvi_stamp,
UVI_MAP_INTERVAL,
UVI_MAP_COUNT,
Utc::now(),
)?;
let coords = project(&map, POLLEN_MAP_REF_POINTS, position)?;
let image = &maps.image;
let stamp = maps.timestamp_base;
let marked_image = map_at(image, stamp, UVI_MAP_INTERVAL, UVI_MAP_COUNT, Utc::now())?;
let coords = project(&marked_image, POLLEN_MAP_REF_POINTS, position)?;
Some(mark(map, coords))
Some(mark(marked_image, coords))
})
}
@ -215,16 +200,12 @@ impl Maps {
/// in the series of maps.
pub(crate) fn uvi_samples(&self, position: Position) -> Option<Vec<Sample>> {
self.uvi.as_ref().and_then(|maps| {
let map = maps.view(0, 0, maps.width() / UVI_MAP_COUNT, maps.height());
let image = &maps.image;
let map = image.view(0, 0, image.width() / UVI_MAP_COUNT, image.height());
let coords = project(&*map, UVI_MAP_REF_POINTS, position)?;
let stamp = maps.timestamp_base;
sample(
maps,
self.uvi_stamp,
UVI_MAP_INTERVAL,
UVI_MAP_COUNT,
coords,
)
sample(image, stamp, UVI_MAP_INTERVAL, UVI_MAP_COUNT, coords)
})
}
}
@ -233,60 +214,66 @@ impl MapsRefresh for MapsHandle {
fn is_pollen_stale(&self) -> bool {
let maps = self.lock().expect("Maps handle mutex was poisoned");
Utc::now().signed_duration_since(maps.pollen_stamp)
> Duration::seconds(POLLEN_MAP_COUNT as i64 * POLLEN_MAP_INTERVAL)
match &maps.pollen {
Some(pollen_maps) => {
Utc::now().signed_duration_since(pollen_maps.mtime)
> Duration::seconds(POLLEN_MAP_COUNT as i64 * POLLEN_MAP_INTERVAL)
}
None => false,
}
}
fn is_uvi_stale(&self) -> bool {
let maps = self.lock().expect("Maps handle mutex was poisoned");
Utc::now().signed_duration_since(maps.uvi_stamp)
> Duration::seconds(UVI_MAP_COUNT as i64 * UVI_MAP_INTERVAL)
match &maps.uvi {
Some(uvi_maps) => {
Utc::now().signed_duration_since(uvi_maps.mtime)
> Duration::seconds(UVI_MAP_COUNT as i64 * UVI_MAP_INTERVAL)
}
None => false,
}
}
fn needs_pollen_refresh(&self) -> bool {
let maps = self.lock().expect("Maps handle mutex was poisoned");
maps.pollen.is_none()
|| Utc::now()
.signed_duration_since(maps.pollen_stamp)
.num_seconds()
> POLLEN_INTERVAL
match &maps.pollen {
Some(pollen_maps) => {
Utc::now()
.signed_duration_since(pollen_maps.mtime)
.num_seconds()
> POLLEN_INTERVAL
}
None => true,
}
}
fn needs_uvi_refresh(&self) -> bool {
let maps = self.lock().expect("Maps handle mutex was poisoned");
maps.uvi.is_none()
|| Utc::now()
.signed_duration_since(maps.uvi_stamp)
.num_seconds()
> UVI_INTERVAL
}
fn set_pollen(&self, result: Option<(DynamicImage, DateTime<Utc>)>) {
if result.is_some() || self.is_pollen_stale() {
let mut maps = self.lock().expect("Maps handle mutex was poisoned");
if let Some((pollen, pollen_stamp)) = result {
maps.pollen = Some(pollen);
maps.pollen_stamp = pollen_stamp
} else {
maps.pollen = None
match &maps.uvi {
Some(uvi_maps) => {
Utc::now()
.signed_duration_since(uvi_maps.mtime)
.num_seconds()
> UVI_INTERVAL
}
None => true,
}
}
fn set_uvi(&self, result: Option<(DynamicImage, DateTime<Utc>)>) {
if result.is_some() || self.is_uvi_stale() {
fn set_pollen(&self, retrieved_maps: Option<RetrievedMaps>) {
if retrieved_maps.is_some() || self.is_pollen_stale() {
let mut maps = self.lock().expect("Maps handle mutex was poisoned");
maps.pollen = retrieved_maps;
}
}
if let Some((uvi, uvi_stamp)) = result {
maps.uvi = Some(uvi);
maps.uvi_stamp = uvi_stamp
} else {
maps.uvi = None
}
fn set_uvi(&self, retrieved_maps: Option<RetrievedMaps>) {
if retrieved_maps.is_some() || self.is_uvi_stale() {
let mut maps = self.lock().expect("Maps handle mutex was poisoned");
maps.uvi = retrieved_maps;
}
}
}
@ -331,23 +318,23 @@ fn map_key_histogram() -> MapKeyHistogram {
///
/// Returns [`None`] if it encounters no known colors in any of the samples.
fn sample<I: GenericImageView<Pixel = Rgba<u8>>>(
maps: &I,
image: &I,
stamp: DateTime<Utc>,
interval: i64,
count: u32,
coords: (u32, u32),
) -> Option<Vec<Sample>> {
let (x, y) = coords;
let width = maps.width() / count;
let height = maps.height();
let width = image.width() / count;
let height = image.height();
let max_sample_width = (width - x).min(MAP_SAMPLE_SIZE[0]);
let max_sample_height = (height - y).min(MAP_SAMPLE_SIZE[1]);
let mut samples = Vec::with_capacity(count as usize);
let mut time = stamp;
let mut offset = 0;
while offset < maps.width() {
let map = maps.view(
while offset < image.width() {
let map = image.view(
x.saturating_sub(MAP_SAMPLE_SIZE[0] / 2) + offset,
y.saturating_sub(MAP_SAMPLE_SIZE[1] / 2),
max_sample_width,
@ -381,12 +368,39 @@ fn sample<I: GenericImageView<Pixel = Rgba<u8>>>(
Some(samples)
}
/// A retrieved image with some metadata.
#[derive(Debug)]
pub(crate) struct RetrievedMaps {
/// The image data.
pub(crate) image: DynamicImage,
/// The date/time the image was last modified.
pub(crate) mtime: DateTime<Utc>,
/// The starting date/time the image corresponds with.
pub(crate) timestamp_base: DateTime<Utc>,
}
impl RetrievedMaps {
#[cfg(test)]
pub(crate) fn new(image: DynamicImage) -> Self {
let mtime = Utc::now();
let timestamp_base = Utc::now();
Self {
image,
mtime,
timestamp_base,
}
}
}
/// Retrieves an image from the provided URL.
///
/// This returns [`None`] if it fails in either performing the request, parsing the `Last-Modified`
/// reponse HTTP header, retrieving the bytes from the image or loading and the decoding the data
/// into [`DynamicImage`].
async fn retrieve_image(url: Url) -> Option<(DynamicImage, DateTime<Utc>)> {
async fn retrieve_image(url: Url) -> Option<RetrievedMaps> {
// TODO: Handle or log errors!
let response = reqwest::get(url).await.ok()?;
let mtime = response
@ -396,11 +410,23 @@ async fn retrieve_image(url: Url) -> Option<(DynamicImage, DateTime<Utc>)> {
.map(chrono::DateTime::parse_from_rfc2822)?
.map(DateTime::<Utc>::from)
.ok()?;
let timestamp_base = {
let path = response.url().path();
let (_, filename) = path.rsplit_once('/')?;
let (timestamp_str, _) = filename.split_once("__")?;
let timestamp = NaiveDateTime::parse_from_str(timestamp_str, "%Y%m%d%H%M").ok()?;
DateTime::<Utc>::from_utc(timestamp, Utc)
};
let bytes = response.bytes().await.ok()?;
tokio::task::spawn_blocking(move || {
if let Ok(image) = image::load_from_memory_with_format(&bytes, ImageFormat::Png) {
Some((image, mtime))
Some(RetrievedMaps {
image,
mtime,
timestamp_base,
})
} else {
None
}
@ -412,7 +438,7 @@ async fn retrieve_image(url: Url) -> Option<(DynamicImage, DateTime<Utc>)> {
/// Retrieves the pollen maps from Buienradar.
///
/// See [`POLLEN_BASE_URL`] for the base URL and [`retrieve_image`] for the retrieval function.
async fn retrieve_pollen_maps() -> Option<(DynamicImage, DateTime<Utc>)> {
async fn retrieve_pollen_maps() -> Option<RetrievedMaps> {
let timestamp = format!("{}", chrono::Local::now().format("%y%m%d%H%M"));
let mut url = Url::parse(POLLEN_BASE_URL).unwrap();
url.query_pairs_mut().append_pair("timestamp", &timestamp);
@ -424,7 +450,7 @@ async fn retrieve_pollen_maps() -> Option<(DynamicImage, DateTime<Utc>)> {
/// Retrieves the UV index maps from Buienradar.
///
/// See [`UVI_BASE_URL`] for the base URL and [`retrieve_image`] for the retrieval function.
async fn retrieve_uvi_maps() -> Option<(DynamicImage, DateTime<Utc>)> {
async fn retrieve_uvi_maps() -> Option<RetrievedMaps> {
let timestamp = format!("{}", chrono::Local::now().format("%y%m%d%H%M"));
let mut url = Url::parse(UVI_BASE_URL).unwrap();
url.query_pairs_mut().append_pair("timestamp", &timestamp);
@ -438,35 +464,35 @@ async fn retrieve_uvi_maps() -> Option<(DynamicImage, DateTime<Utc>)> {
/// This returns [`None`] if `instant` is too far in the future with respect to the number of
/// cached maps.
fn map_at(
maps: &DynamicImage,
maps_stamp: DateTime<Utc>,
image: &DynamicImage,
stamp: DateTime<Utc>,
interval: i64,
count: u32,
instant: DateTime<Utc>,
) -> Option<DynamicImage> {
let duration = instant.signed_duration_since(maps_stamp);
let duration = instant.signed_duration_since(stamp);
let offset = (duration.num_seconds() / interval) as u32;
// Check if out of bounds.
if offset >= count {
return None;
}
let width = maps.width() / count;
let width = image.width() / count;
Some(maps.crop_imm(offset * width, 0, width, maps.height()))
Some(image.crop_imm(offset * width, 0, width, image.height()))
}
/// Marks the provided coordinates on the map using a horizontal and vertical line.
fn mark(mut map: DynamicImage, coords: (u32, u32)) -> DynamicImage {
fn mark(mut image: DynamicImage, coords: (u32, u32)) -> DynamicImage {
let (x, y) = coords;
for py in 0..map.height() {
map.put_pixel(x, py, Rgba::from([0x00, 0x00, 0x00, 0x70]));
for py in 0..image.height() {
image.put_pixel(x, py, Rgba::from([0x00, 0x00, 0x00, 0x70]));
}
for px in 0..map.width() {
map.put_pixel(px, y, Rgba::from([0x00, 0x00, 0x00, 0x70]));
for px in 0..image.width() {
image.put_pixel(px, y, Rgba::from([0x00, 0x00, 0x00, 0x70]));
}
map
image
}
/// Projects the provided geocoded position to a coordinate on a map.
@ -476,7 +502,7 @@ fn mark(mut map: DynamicImage, coords: (u32, u32)) -> DynamicImage {
///
/// Returns [`None`] if the resulting coordinate is not within the bounds of the map.
fn project<I: GenericImageView>(
map: &I,
image: &I,
ref_points: [(Position, (u32, u32)); 2],
pos: Position,
) -> Option<(u32, u32)> {
@ -495,7 +521,7 @@ fn project<I: GenericImageView>(
let scale_y = ((ref1_y - ref2_y) as f64) / (ref2_merc_y - ref1_merc_y);
let y = ((ref2_merc_y - mercator_y(pos.lat_as_rad())) * scale_y + ref2_y as f64).round() as u32;
if map.in_bounds(x, y) {
if image.in_bounds(x, y) {
Some((x, y))
} else {
None
@ -548,13 +574,13 @@ pub(crate) async fn run(maps_handle: MapsHandle) {
println!("🕔 Refreshing the maps (if necessary)...");
if maps_handle.needs_pollen_refresh() {
let result = retrieve_pollen_maps().await;
maps_handle.set_pollen(result);
let retrieved_maps = retrieve_pollen_maps().await;
maps_handle.set_pollen(retrieved_maps);
}
if maps_handle.needs_uvi_refresh() {
let result = retrieve_uvi_maps().await;
maps_handle.set_uvi(result);
let retrieved_maps = retrieve_uvi_maps().await;
maps_handle.set_uvi(retrieved_maps);
}
sleep(REFRESH_INTERVAL).await;

View File

@ -50,10 +50,10 @@ fn merge(
let mut pollen_samples = pollen_samples;
let mut aqi_items = aqi_items;
// Only retain samples/items that have timestamps that are at least half an hour ago.
// Only retain samples/items that have timestamps that are at least an hour ago.
let now = Utc::now();
pollen_samples.retain(|smp| smp.time.signed_duration_since(now).num_seconds() > -1800);
aqi_items.retain(|item| item.time.signed_duration_since(now).num_seconds() > -1800);
pollen_samples.retain(|smp| smp.time.signed_duration_since(now).num_seconds() > -3600);
aqi_items.retain(|item| item.time.signed_duration_since(now).num_seconds() > -3600);
// Align the iterators based on the (hourly) timestamps!
let pollen_first_time = pollen_samples.first()?.time;
@ -81,14 +81,17 @@ fn merge(
}
// Find the maximum sample/item of each series.
// Note: Unwrapping is possible because each series has at least an item otherwise `.first`
// Note 1: Unwrapping is possible because each series has at least an item otherwise `.first`
// would have failed above.
let pollen_max = pollen_samples
// Note 2: Ensure that the maximum sample/item is in scope of the time range covered by the
// combined items.
let zip_len = std::cmp::min(pollen_samples.len(), aqi_items.len());
let pollen_max = pollen_samples[..zip_len]
.iter()
.max_by_key(|sample| sample.score)
.cloned()
.unwrap();
let aqi_max = aqi_items
let aqi_max = aqi_items[..zip_len]
.iter()
.max_by_key(|item| (item.value * 1_000.0) as u32)
.cloned()
@ -206,7 +209,7 @@ mod tests {
let merged = super::merge(shifted_pollen_samples, aqi_items.clone());
assert!(merged.is_some());
let (paqi, max_pollen, max_aqi) = merged.unwrap();
assert_eq!(paqi, Vec::from([Item::new(t_1, 2.9), Item::new(t_2, 3.0),]));
assert_eq!(paqi, Vec::from([Item::new(t_1, 2.9), Item::new(t_2, 3.0)]));
assert_eq!(max_pollen, BuienradarSample::new(t_2, 3));
assert_eq!(max_aqi, LuchtmeetnetItem::new(t_1, 2.9));
@ -222,10 +225,25 @@ mod tests {
let merged = super::merge(pollen_samples.clone(), shifted_aqi_items);
assert!(merged.is_some());
let (paqi, max_pollen, max_aqi) = merged.unwrap();
assert_eq!(paqi, Vec::from([Item::new(t_1, 3.0), Item::new(t_2, 2.9),]));
assert_eq!(paqi, Vec::from([Item::new(t_1, 3.0), Item::new(t_2, 2.9)]));
assert_eq!(max_pollen, BuienradarSample::new(t_1, 3));
assert_eq!(max_aqi, LuchtmeetnetItem::new(t_2, 2.9));
// The maximum sample/item should not be later then the interval the PAQI items cover.
let merged = super::merge(pollen_samples[..3].to_vec(), aqi_items.clone());
assert!(merged.is_some());
let (paqi, max_pollen, max_aqi) = merged.unwrap();
assert_eq!(paqi, Vec::from([Item::new(t_0, 1.1)]));
assert_eq!(max_pollen, BuienradarSample::new(t_0, 1));
assert_eq!(max_aqi, LuchtmeetnetItem::new(t_0, 1.1));
let merged = super::merge(pollen_samples.clone(), aqi_items[..3].to_vec());
assert!(merged.is_some());
let (paqi, max_pollen, max_aqi) = merged.unwrap();
assert_eq!(paqi, Vec::from([Item::new(t_0, 1.1)]));
assert_eq!(max_pollen, BuienradarSample::new(t_0, 1));
assert_eq!(max_aqi, LuchtmeetnetItem::new(t_0, 1.1));
// Merging fails because the samples/items are too far (6 hours) apart.
let shifted_aqi_items = aqi_items
.iter()