Compare commits
9 Commits
Author | SHA1 | Date |
---|---|---|
Paul van Tilburg | 3fb899d1fd | |
Paul van Tilburg | f0cc54f074 | |
Paul van Tilburg | ac653ef0c9 | |
Paul van Tilburg | e204e7905c | |
Paul van Tilburg | 08cdfe1e1c | |
Paul van Tilburg | 89395f21f6 | |
Paul van Tilburg | ff9f1ac371 | |
Paul van Tilburg | 4a6eeab787 | |
Paul van Tilburg | ab4b0bba72 |
16
CHANGELOG.md
16
CHANGELOG.md
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
22
src/lib.rs
22
src/lib.rs
|
@ -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 {
|
||||
|
|
|
@ -22,7 +22,7 @@ async fn main() -> Result<()> {
|
|||
|
||||
select! {
|
||||
result = rocket.launch() => {
|
||||
result?
|
||||
result.map(|_| ())?
|
||||
}
|
||||
result = maps_refresher => {
|
||||
shutdown.notify();
|
||||
|
|
234
src/maps.rs
234
src/maps.rs
|
@ -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", ×tamp);
|
||||
|
@ -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", ×tamp);
|
||||
|
@ -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;
|
||||
|
|
|
@ -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()
|
||||
|
|
Loading…
Reference in New Issue