diff --git a/pdm.lock b/pdm.lock
index c19b2b4..1a30326 100644
--- a/pdm.lock
+++ b/pdm.lock
@@ -5,7 +5,7 @@
 groups = ["default", "dev"]
 strategy = ["inherit_metadata"]
 lock_version = "4.5.0"
-content_hash = "sha256:2f7c4bee801973a3b7856ba0707891eb01fd05659948707f44be4aa302e5dabd"
+content_hash = "sha256:334553b78b7ede092e30982010c6a31e38fb1f7dbc468feac016e8b3c0bf3059"
 
 [[metadata.targets]]
 requires_python = ">=3.12,<3.13"
@@ -16,7 +16,6 @@ version = "2.1.0"
 requires_python = ">=3.7"
 summary = "Abseil Python Common Libraries, see https://github.com/abseil/abseil-py."
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 files = [
     {file = "absl-py-2.1.0.tar.gz", hash = "sha256:7820790efbb316739cde8b4e19357243fc3608a152024288513dd968d7d959ff"},
     {file = "absl_py-2.1.0-py3-none-any.whl", hash = "sha256:526a04eadab8b4ee719ce68f204172ead1027549089702d99b9059f129ff1308"},
@@ -28,18 +27,26 @@ version = "0.1.4"
 requires_python = ">=3.6"
 summary = "Disable App Nap on macOS >= 10.9"
 groups = ["dev"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\" and platform_system == \"Darwin\""
+marker = "platform_system == \"Darwin\""
 files = [
     {file = "appnope-0.1.4-py2.py3-none-any.whl", hash = "sha256:502575ee11cd7a28c0205f379b525beefebab9d161b7c964670864014ed7213c"},
     {file = "appnope-0.1.4.tar.gz", hash = "sha256:1de3860566df9caf38f01f86f65e0e13e379af54f9e4bee1e66b48f2efffd1ee"},
 ]
 
+[[package]]
+name = "asciitree"
+version = "0.3.3"
+summary = "Draws ASCII trees."
+groups = ["default"]
+files = [
+    {file = "asciitree-0.3.3.tar.gz", hash = "sha256:4aa4b9b649f85e3fcb343363d97564aa1fb62e249677f2e18a96765145cc0f6e"},
+]
+
 [[package]]
 name = "asttokens"
 version = "2.4.1"
 summary = "Annotate AST trees with source code positions"
 groups = ["dev"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 dependencies = [
     "six>=1.12.0",
     "typing; python_version < \"3.5\"",
@@ -49,13 +56,34 @@ files = [
     {file = "asttokens-2.4.1.tar.gz", hash = "sha256:b03869718ba9a6eb027e134bfdf69f38a236d681c83c160d510768af11254ba0"},
 ]
 
+[[package]]
+name = "cartopy"
+version = "0.24.1"
+requires_python = ">=3.10"
+summary = "A Python library for cartographic visualizations with Matplotlib"
+groups = ["default"]
+dependencies = [
+    "matplotlib>=3.6",
+    "numpy>=1.23",
+    "packaging>=21",
+    "pyproj>=3.3.1",
+    "pyshp>=2.3",
+    "shapely>=1.8",
+]
+files = [
+    {file = "Cartopy-0.24.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a984e33977daed8f760c09c331c8368a6af060db1190af89d74a027c272e39c3"},
+    {file = "Cartopy-0.24.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:71d8a6d061d0764aba3baf357a68f3d73796a8a46d34b8c9fb241171b273c69e"},
+    {file = "Cartopy-0.24.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2f354a1d902a8d6ee33b099acc86ac2e1af528bbc0ea718b834111c97e604981"},
+    {file = "Cartopy-0.24.1-cp312-cp312-win_amd64.whl", hash = "sha256:b1bb2d02b31884ee1d4f14e5b436bbf95745eac39c6fc0d6c67c83bb907b55b3"},
+    {file = "cartopy-0.24.1.tar.gz", hash = "sha256:01c910d5634c69a7efdec46e0a17d473d2328767f001d4dc0b5c4b48e585c8bd"},
+]
+
 [[package]]
 name = "certifi"
 version = "2024.8.30"
 requires_python = ">=3.6"
 summary = "Python package for providing Mozilla's CA Bundle."
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 files = [
     {file = "certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8"},
     {file = "certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"},
@@ -67,7 +95,7 @@ version = "1.17.1"
 requires_python = ">=3.8"
 summary = "Foreign Function Interface for Python calling C code."
 groups = ["dev"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\" and implementation_name == \"pypy\""
+marker = "implementation_name == \"pypy\""
 dependencies = [
     "pycparser",
 ]
@@ -92,7 +120,6 @@ version = "3.4.0"
 requires_python = ">=3.7.0"
 summary = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 files = [
     {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0713f3adb9d03d49d365b70b84775d0a0d18e4ab08d12bc46baa6132ba78aaf6"},
     {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:de7376c29d95d6719048c194a9cf1a1b0393fbe8488a22008610b0361d834ecf"},
@@ -119,7 +146,6 @@ version = "0.1.88"
 requires_python = ">=3.9"
 summary = "Chex: Testing made fun, in JAX!"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 dependencies = [
     "absl-py>=0.9.0",
     "jax>=0.4.27",
@@ -134,13 +160,27 @@ files = [
     {file = "chex-0.1.88.tar.gz", hash = "sha256:565de897b1373232cdfca5e699f50fa49403d2c7d23f6c5a75a97ef713d2fe36"},
 ]
 
+[[package]]
+name = "click"
+version = "8.1.8"
+requires_python = ">=3.7"
+summary = "Composable command line interface toolkit"
+groups = ["default"]
+dependencies = [
+    "colorama; platform_system == \"Windows\"",
+    "importlib-metadata; python_version < \"3.8\"",
+]
+files = [
+    {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"},
+    {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"},
+]
+
 [[package]]
 name = "cloudpickle"
 version = "3.1.0"
 requires_python = ">=3.8"
 summary = "Pickler class to extend the standard pickle.Pickler functionality"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 files = [
     {file = "cloudpickle-3.1.0-py3-none-any.whl", hash = "sha256:fe11acda67f61aaaec473e3afe030feb131d78a43461b718185363384f1ba12e"},
     {file = "cloudpickle-3.1.0.tar.gz", hash = "sha256:81a929b6e3c7335c863c771d673d105f02efdb89dfaba0c90495d1c64796601b"},
@@ -152,7 +192,7 @@ version = "0.4.6"
 requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
 summary = "Cross-platform colored terminal text."
 groups = ["default", "dev"]
-marker = "sys_platform == \"win32\" and python_version >= \"3.12\" and python_version < \"3.13\" or platform_system == \"Windows\" and python_version >= \"3.12\" and python_version < \"3.13\""
+marker = "sys_platform == \"win32\" or platform_system == \"Windows\""
 files = [
     {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
     {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
@@ -164,7 +204,6 @@ version = "0.2.2"
 requires_python = ">=3.8"
 summary = "Jupyter Python Comm implementation, for usage in ipykernel, xeus-python etc."
 groups = ["dev"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 dependencies = [
     "traitlets>=4",
 ]
@@ -173,13 +212,23 @@ files = [
     {file = "comm-0.2.2.tar.gz", hash = "sha256:3fd7a84065306e07bea1773df6eb8282de51ba82f77c72f9c85716ab11fe980e"},
 ]
 
+[[package]]
+name = "configobj"
+version = "5.0.9"
+requires_python = ">=3.7"
+summary = "Config file reading, writing and validation."
+groups = ["default"]
+files = [
+    {file = "configobj-5.0.9-py2.py3-none-any.whl", hash = "sha256:1ba10c5b6ee16229c79a05047aeda2b55eb4e80d7c7d8ecf17ec1ca600c79882"},
+    {file = "configobj-5.0.9.tar.gz", hash = "sha256:03c881bbf23aa07bccf1b837005975993c4ab4427ba57f959afdd9d1a2386848"},
+]
+
 [[package]]
 name = "contourpy"
 version = "1.3.1"
 requires_python = ">=3.10"
 summary = "Python library for calculating contours of 2D quadrilateral grids"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 dependencies = [
     "numpy>=1.23",
 ]
@@ -203,19 +252,54 @@ version = "0.12.1"
 requires_python = ">=3.8"
 summary = "Composable style cycles"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 files = [
     {file = "cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30"},
     {file = "cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c"},
 ]
 
+[[package]]
+name = "dask"
+version = "2024.12.1"
+requires_python = ">=3.10"
+summary = "Parallel PyData with Task Scheduling"
+groups = ["default"]
+dependencies = [
+    "click>=8.1",
+    "cloudpickle>=3.0.0",
+    "fsspec>=2021.09.0",
+    "importlib-metadata>=4.13.0; python_version < \"3.12\"",
+    "packaging>=20.0",
+    "partd>=1.4.0",
+    "pyyaml>=5.3.1",
+    "toolz>=0.10.0",
+]
+files = [
+    {file = "dask-2024.12.1-py3-none-any.whl", hash = "sha256:1f32acddf1a6994e3af6734756f0a92467c47050bc29f3555bb9b140420e8e19"},
+    {file = "dask-2024.12.1.tar.gz", hash = "sha256:bac809af21c2dd7eb06827bccbfc612504f3ee6435580e548af912828f823195"},
+]
+
+[[package]]
+name = "dask"
+version = "2024.12.1"
+extras = ["array"]
+requires_python = ">=3.10"
+summary = "Parallel PyData with Task Scheduling"
+groups = ["default"]
+dependencies = [
+    "dask==2024.12.1",
+    "numpy>=1.24",
+]
+files = [
+    {file = "dask-2024.12.1-py3-none-any.whl", hash = "sha256:1f32acddf1a6994e3af6734756f0a92467c47050bc29f3555bb9b140420e8e19"},
+    {file = "dask-2024.12.1.tar.gz", hash = "sha256:bac809af21c2dd7eb06827bccbfc612504f3ee6435580e548af912828f823195"},
+]
+
 [[package]]
 name = "debugpy"
 version = "1.8.9"
 requires_python = ">=3.8"
 summary = "An implementation of the Debug Adapter Protocol for Python"
 groups = ["dev"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 files = [
     {file = "debugpy-1.8.9-cp312-cp312-macosx_14_0_universal2.whl", hash = "sha256:957363d9a7a6612a37458d9a15e72d03a635047f946e5fceee74b50d52a9c8e2"},
     {file = "debugpy-1.8.9-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e565fc54b680292b418bb809f1386f17081d1346dca9a871bf69a8ac4071afe"},
@@ -231,19 +315,28 @@ version = "5.1.1"
 requires_python = ">=3.5"
 summary = "Decorators for Humans"
 groups = ["default", "dev"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 files = [
     {file = "decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186"},
     {file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"},
 ]
 
+[[package]]
+name = "defusedxml"
+version = "0.7.1"
+requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+summary = "XML bomb protection for Python stdlib modules"
+groups = ["default"]
+files = [
+    {file = "defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61"},
+    {file = "defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69"},
+]
+
 [[package]]
 name = "distrax"
 version = "0.1.5"
 requires_python = ">=3.9"
 summary = "Distrax: Probability distributions in JAX."
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 dependencies = [
     "absl-py>=0.9.0",
     "chex>=0.1.8",
@@ -263,7 +356,6 @@ name = "dm-tree"
 version = "0.1.8"
 summary = "Tree is a library for working with nested data structures."
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 files = [
     {file = "dm-tree-0.1.8.tar.gz", hash = "sha256:0fcaabbb14e7980377439e7140bd05552739ca5e515ecb3119f234acee4b9430"},
     {file = "dm_tree-0.1.8-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:ea9e59e0451e7d29aece402d9f908f2e2a80922bcde2ebfd5dcb07750fcbfee8"},
@@ -281,19 +373,31 @@ version = "0.16"
 requires_python = ">=3.6,<4.0"
 summary = "Parse Python docstrings in reST, Google and Numpydoc format"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 files = [
     {file = "docstring_parser-0.16-py3-none-any.whl", hash = "sha256:bf0a1387354d3691d102edef7ec124f219ef639982d096e26e3b60aeffa90637"},
     {file = "docstring_parser-0.16.tar.gz", hash = "sha256:538beabd0af1e2db0146b6bd3caa526c35a34d61af9fd2887f3a8a27a739aa6e"},
 ]
 
+[[package]]
+name = "donfig"
+version = "0.8.1.post1"
+requires_python = ">=3.8"
+summary = "Python package for configuring a python package"
+groups = ["default"]
+dependencies = [
+    "pyyaml",
+]
+files = [
+    {file = "donfig-0.8.1.post1-py3-none-any.whl", hash = "sha256:2a3175ce74a06109ff9307d90a230f81215cbac9a751f4d1c6194644b8204f9d"},
+    {file = "donfig-0.8.1.post1.tar.gz", hash = "sha256:3bef3413a4c1c601b585e8d297256d0c1470ea012afa6e8461dc28bfb7c23f52"},
+]
+
 [[package]]
 name = "etils"
 version = "1.11.0"
 requires_python = ">=3.10"
 summary = "Collection of common python utils"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 files = [
     {file = "etils-1.11.0-py3-none-any.whl", hash = "sha256:a394cf3476bcec51c221426a70c39cd1006e889456ba41e4d7f12fd6814be7a5"},
     {file = "etils-1.11.0.tar.gz", hash = "sha256:aff3278a3be7fddf302dfd80335e9f924244666c71239cd91e836f3d055f1c4a"},
@@ -306,7 +410,6 @@ extras = ["epath", "epy"]
 requires_python = ">=3.10"
 summary = "Collection of common python utils"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 dependencies = [
     "etils==1.11.0",
     "etils[epy]",
@@ -328,7 +431,6 @@ extras = ["epy"]
 requires_python = ">=3.10"
 summary = "Collection of common python utils"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 dependencies = [
     "etils==1.11.0",
     "typing-extensions",
@@ -344,7 +446,6 @@ version = "2.1.0"
 requires_python = ">=3.8"
 summary = "Get the currently executing AST node of a frame, and other information"
 groups = ["dev"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 files = [
     {file = "executing-2.1.0-py2.py3-none-any.whl", hash = "sha256:8d63781349375b5ebccc3142f4b30350c0cd9c79f921cde38be2be4637e98eaf"},
     {file = "executing-2.1.0.tar.gz", hash = "sha256:8ea27ddd260da8150fa5a708269c4a10e76161e2496ec3e587da9e3c0fe4b9ab"},
@@ -355,19 +456,29 @@ name = "farama-notifications"
 version = "0.0.4"
 summary = "Notifications for all Farama Foundation maintained libraries."
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 files = [
     {file = "Farama-Notifications-0.0.4.tar.gz", hash = "sha256:13fceff2d14314cf80703c8266462ebf3733c7d165336eee998fc58e545efd18"},
     {file = "Farama_Notifications-0.0.4-py3-none-any.whl", hash = "sha256:14de931035a41961f7c056361dc7f980762a143d05791ef5794a751a2caf05ae"},
 ]
 
+[[package]]
+name = "fasteners"
+version = "0.19"
+requires_python = ">=3.6"
+summary = "A python package that provides useful locks"
+groups = ["default"]
+marker = "sys_platform != \"emscripten\""
+files = [
+    {file = "fasteners-0.19-py3-none-any.whl", hash = "sha256:758819cb5d94cdedf4e836988b74de396ceacb8e2794d21f82d131fd9ee77237"},
+    {file = "fasteners-0.19.tar.gz", hash = "sha256:b4f37c3ac52d8a445af3a66bce57b33b5e90b97c696b7b984f530cf8f0ded09c"},
+]
+
 [[package]]
 name = "filelock"
 version = "3.16.1"
 requires_python = ">=3.8"
 summary = "A platform independent file lock."
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 files = [
     {file = "filelock-3.16.1-py3-none-any.whl", hash = "sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0"},
     {file = "filelock-3.16.1.tar.gz", hash = "sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435"},
@@ -379,7 +490,6 @@ version = "0.10.2"
 requires_python = ">=3.10"
 summary = "Flax: A neural network library for JAX designed for flexibility"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 dependencies = [
     "PyYAML>=5.4.1",
     "jax>=0.4.27",
@@ -403,7 +513,6 @@ version = "4.55.0"
 requires_python = ">=3.8"
 summary = "Tools to manipulate font files"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 files = [
     {file = "fonttools-4.55.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:838d2d8870f84fc785528a692e724f2379d5abd3fc9dad4d32f91cf99b41e4a7"},
     {file = "fonttools-4.55.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f46b863d74bab7bb0d395f3b68d3f52a03444964e67ce5c43ce43a75efce9246"},
@@ -423,7 +532,6 @@ version = "2024.10.0"
 requires_python = ">=3.8"
 summary = "File-system specification"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 files = [
     {file = "fsspec-2024.10.0-py3-none-any.whl", hash = "sha256:03b9a6785766a4de40368b88906366755e2819e758b83705c88cd7cb5fe81871"},
     {file = "fsspec-2024.10.0.tar.gz", hash = "sha256:eda2d8a4116d4f2429db8550f2457da57279247dd930bb12f821b58391359493"},
@@ -435,7 +543,6 @@ version = "0.6.0"
 requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7"
 summary = "Python AST that abstracts the underlying Python version"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 files = [
     {file = "gast-0.6.0-py3-none-any.whl", hash = "sha256:52b182313f7330389f72b069ba00f174cfe2a06411099547288839c6cbafbd54"},
     {file = "gast-0.6.0.tar.gz", hash = "sha256:88fc5300d32c7ac6ca7b515310862f71e6fdf2c029bbec7c66c0f5dd47b6b1fb"},
@@ -447,7 +554,6 @@ version = "1.68.1"
 requires_python = ">=3.8"
 summary = "HTTP/2-based RPC framework"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 files = [
     {file = "grpcio-1.68.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:8829924fffb25386995a31998ccbbeaa7367223e647e0122043dfc485a87c666"},
     {file = "grpcio-1.68.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:3aed6544e4d523cd6b3119b0916cef3d15ef2da51e088211e4d1eb91a6c7f4f1"},
@@ -467,7 +573,6 @@ version = "0.26.2"
 requires_python = ">=3.6"
 summary = "Gym: A universal API for reinforcement learning environments"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 dependencies = [
     "cloudpickle>=1.2.0",
     "dataclasses==0.8; python_version == \"3.6\"",
@@ -484,7 +589,6 @@ name = "gym-notices"
 version = "0.0.8"
 summary = "Notices for gym"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 files = [
     {file = "gym-notices-0.0.8.tar.gz", hash = "sha256:ad25e200487cafa369728625fe064e88ada1346618526102659b4640f2b4b911"},
     {file = "gym_notices-0.0.8-py3-none-any.whl", hash = "sha256:e5f82e00823a166747b4c2a07de63b6560b1acb880638547e0cabf825a01e463"},
@@ -496,7 +600,6 @@ version = "1.0.0"
 requires_python = ">=3.8"
 summary = "A standard API for reinforcement learning and a diverse set of reference environments (formerly Gym)."
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 dependencies = [
     "cloudpickle>=1.2.0",
     "farama-notifications>=0.0.1",
@@ -516,7 +619,6 @@ extras = ["jax"]
 requires_python = ">=3.8"
 summary = "A standard API for reinforcement learning and a diverse set of reference environments (formerly Gym)."
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 dependencies = [
     "flax>=0.5.0",
     "gymnasium==1.0.0",
@@ -534,7 +636,6 @@ version = "0.0.8"
 requires_python = ">=3.10"
 summary = "JAX-compatible version of Open AI's gym environments"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 dependencies = [
     "chex",
     "flax",
@@ -557,7 +658,6 @@ version = "4.11.0"
 requires_python = ">=3.9"
 summary = "Python humanize utilities"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 files = [
     {file = "humanize-4.11.0-py3-none-any.whl", hash = "sha256:b53caaec8532bcb2fff70c8826f904c35943f8cecaca29d272d9df38092736c0"},
     {file = "humanize-4.11.0.tar.gz", hash = "sha256:e66f36020a2d5a974c504bd2555cf770621dbdbb6d82f94a6857c0b1ea2608be"},
@@ -569,7 +669,6 @@ version = "3.10"
 requires_python = ">=3.6"
 summary = "Internationalized Domain Names in Applications (IDNA)"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 files = [
     {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"},
     {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"},
@@ -581,7 +680,6 @@ version = "6.4.5"
 requires_python = ">=3.8"
 summary = "Read resources from Python packages"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 dependencies = [
     "zipp>=3.1.0; python_version < \"3.10\"",
 ]
@@ -596,7 +694,6 @@ version = "2.0.0"
 requires_python = ">=3.7"
 summary = "brain-dead simple config-ini parsing"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 files = [
     {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"},
     {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"},
@@ -608,7 +705,6 @@ version = "6.29.5"
 requires_python = ">=3.8"
 summary = "IPython Kernel for Jupyter"
 groups = ["dev"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 dependencies = [
     "appnope; platform_system == \"Darwin\"",
     "comm>=0.1.1",
@@ -635,7 +731,6 @@ version = "8.29.0"
 requires_python = ">=3.10"
 summary = "IPython: Productive Interactive Computing"
 groups = ["dev"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 dependencies = [
     "colorama; sys_platform == \"win32\"",
     "decorator",
@@ -660,7 +755,6 @@ version = "0.4.37"
 requires_python = ">=3.10"
 summary = "Differentiate, compile, and transform Numpy code."
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 dependencies = [
     "jaxlib<=0.4.37,>=0.4.36",
     "ml-dtypes>=0.4.0",
@@ -680,7 +774,6 @@ name = "jax-cuda12-pjrt"
 version = "0.4.36"
 summary = "JAX XLA PJRT Plugin for NVIDIA GPUs"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 files = [
     {file = "jax_cuda12_pjrt-0.4.36-py3-none-manylinux2014_aarch64.whl", hash = "sha256:1dfc0bec0820ba801b61e9421064b6e58238c430b4ad8f54043323d93c0217c6"},
     {file = "jax_cuda12_pjrt-0.4.36-py3-none-manylinux2014_x86_64.whl", hash = "sha256:e3c3705d8db7d63da9abfaebf06f5cd0667f5acb0748a5c5eb00d80041e922ed"},
@@ -692,7 +785,6 @@ version = "0.4.36"
 requires_python = ">=3.10"
 summary = "JAX Plugin for NVIDIA GPUs"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 dependencies = [
     "jax-cuda12-pjrt==0.4.36",
 ]
@@ -708,7 +800,6 @@ extras = ["with_cuda"]
 requires_python = ">=3.10"
 summary = "JAX Plugin for NVIDIA GPUs"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 dependencies = [
     "jax-cuda12-plugin==0.4.36",
     "nvidia-cublas-cu12>=12.1.3.1",
@@ -734,7 +825,6 @@ extras = ["cuda12"]
 requires_python = ">=3.10"
 summary = "Differentiate, compile, and transform Numpy code."
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 dependencies = [
     "jax-cuda12-plugin[with_cuda]<=0.4.37,>=0.4.36",
     "jax==0.4.37",
@@ -751,7 +841,6 @@ version = "0.4.36"
 requires_python = ">=3.10"
 summary = "XLA library for JAX"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 dependencies = [
     "ml-dtypes>=0.2.0",
     "numpy>=1.24",
@@ -772,7 +861,6 @@ version = "0.19.2"
 requires_python = ">=3.6"
 summary = "An autocompletion tool for Python that can be used for text editors."
 groups = ["dev"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 dependencies = [
     "parso<0.9.0,>=0.8.4",
 ]
@@ -787,7 +875,6 @@ version = "3.1.4"
 requires_python = ">=3.7"
 summary = "A very fast and expressive template engine."
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 dependencies = [
     "MarkupSafe>=2.0",
 ]
@@ -802,7 +889,6 @@ version = "8.6.3"
 requires_python = ">=3.8"
 summary = "Jupyter protocol implementation and client libraries"
 groups = ["dev"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 dependencies = [
     "importlib-metadata>=4.8.3; python_version < \"3.10\"",
     "jupyter-core!=5.0.*,>=4.12",
@@ -822,7 +908,6 @@ version = "5.7.2"
 requires_python = ">=3.8"
 summary = "Jupyter core package. A base package on which Jupyter projects rely."
 groups = ["dev"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 dependencies = [
     "platformdirs>=2.5",
     "pywin32>=300; sys_platform == \"win32\" and platform_python_implementation != \"PyPy\"",
@@ -839,7 +924,6 @@ version = "1.4.7"
 requires_python = ">=3.8"
 summary = "A fast implementation of the Cassowary constraint solver"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 files = [
     {file = "kiwisolver-1.4.7-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:5360cc32706dab3931f738d3079652d20982511f7c0ac5711483e6eab08efff2"},
     {file = "kiwisolver-1.4.7-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:942216596dc64ddb25adb215c3c783215b23626f8d84e8eff8d6d45c3f29f75a"},
@@ -860,13 +944,23 @@ files = [
     {file = "kiwisolver-1.4.7.tar.gz", hash = "sha256:9893ff81bd7107f7b685d3017cc6583daadb4fc26e4a888350df530e41980a60"},
 ]
 
+[[package]]
+name = "locket"
+version = "1.0.0"
+requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+summary = "File-based locks for Python on Linux and Windows"
+groups = ["default"]
+files = [
+    {file = "locket-1.0.0-py2.py3-none-any.whl", hash = "sha256:b6c819a722f7b6bd955b80781788e4a66a55628b858d347536b7e81325a3a5e3"},
+    {file = "locket-1.0.0.tar.gz", hash = "sha256:5c0d4c052a8bbbf750e056a8e65ccd309086f4f0f18a2eac306a8dfa4112a632"},
+]
+
 [[package]]
 name = "markdown"
 version = "3.7"
 requires_python = ">=3.8"
 summary = "Python implementation of John Gruber's Markdown."
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 dependencies = [
     "importlib-metadata>=4.4; python_version < \"3.10\"",
 ]
@@ -881,7 +975,6 @@ version = "3.0.0"
 requires_python = ">=3.8"
 summary = "Python port of markdown-it. Markdown parsing, done right!"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 dependencies = [
     "mdurl~=0.1",
 ]
@@ -896,7 +989,6 @@ version = "3.0.2"
 requires_python = ">=3.9"
 summary = "Safely add untrusted strings to HTML/XML markup."
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 files = [
     {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf"},
     {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225"},
@@ -917,7 +1009,6 @@ version = "3.9.2"
 requires_python = ">=3.9"
 summary = "Python plotting package"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 dependencies = [
     "contourpy>=1.0.1",
     "cycler>=0.10",
@@ -946,7 +1037,6 @@ version = "0.1.7"
 requires_python = ">=3.8"
 summary = "Inline Matplotlib backend for Jupyter"
 groups = ["dev"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 dependencies = [
     "traitlets",
 ]
@@ -961,7 +1051,6 @@ version = "0.1.2"
 requires_python = ">=3.7"
 summary = "Markdown URL utilities"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 files = [
     {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"},
     {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"},
@@ -973,7 +1062,6 @@ version = "0.5.0"
 requires_python = ">=3.9"
 summary = ""
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 dependencies = [
     "numpy>=1.21",
     "numpy>=1.21.2; python_version >= \"3.10\"",
@@ -998,7 +1086,7 @@ name = "mpmath"
 version = "1.3.0"
 summary = "Python library for arbitrary-precision floating-point arithmetic"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
+marker = "python_version >= \"3.9\""
 files = [
     {file = "mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c"},
     {file = "mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f"},
@@ -1010,7 +1098,6 @@ version = "1.1.0"
 requires_python = ">=3.8"
 summary = "MessagePack serializer"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 files = [
     {file = "msgpack-1.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:d46cf9e3705ea9485687aa4001a76e44748b609d260af21c4ceea7f2212a501d"},
     {file = "msgpack-1.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5dbad74103df937e1325cc4bfeaf57713be0b4f15e1c2da43ccdd836393e2ea2"},
@@ -1032,7 +1119,6 @@ version = "1.6.0"
 requires_python = ">=3.5"
 summary = "Patch asyncio to allow nested event loops"
 groups = ["default", "dev"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 files = [
     {file = "nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c"},
     {file = "nest_asyncio-1.6.0.tar.gz", hash = "sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe"},
@@ -1044,19 +1130,34 @@ version = "3.4.2"
 requires_python = ">=3.10"
 summary = "Python package for creating and manipulating graphs and networks"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 files = [
     {file = "networkx-3.4.2-py3-none-any.whl", hash = "sha256:df5d4365b724cf81b8c6a7312509d0c22386097011ad1abe274afd5e9d3bbc5f"},
     {file = "networkx-3.4.2.tar.gz", hash = "sha256:307c3669428c5362aab27c8a1260aa8f47c4e91d3891f48be0141738d8d053e1"},
 ]
 
+[[package]]
+name = "numcodecs"
+version = "0.13.1"
+requires_python = ">=3.10"
+summary = "A Python package providing buffer compression and transformation codecs for use in data storage and communication applications."
+groups = ["default"]
+dependencies = [
+    "numpy>=1.7",
+]
+files = [
+    {file = "numcodecs-0.13.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5195bea384a6428f8afcece793860b1ab0ae28143c853f0b2b20d55a8947c917"},
+    {file = "numcodecs-0.13.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3501a848adaddce98a71a262fee15cd3618312692aa419da77acd18af4a6a3f6"},
+    {file = "numcodecs-0.13.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da2230484e6102e5fa3cc1a5dd37ca1f92dfbd183d91662074d6f7574e3e8f53"},
+    {file = "numcodecs-0.13.1-cp312-cp312-win_amd64.whl", hash = "sha256:e5db4824ebd5389ea30e54bc8aeccb82d514d28b6b68da6c536b8fa4596f4bca"},
+    {file = "numcodecs-0.13.1.tar.gz", hash = "sha256:a3cf37881df0898f3a9c0d4477df88133fe85185bffe57ba31bcc2fa207709bc"},
+]
+
 [[package]]
 name = "numpy"
 version = "1.26.4"
 requires_python = ">=3.9"
 summary = "Fundamental package for array computing in Python"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 files = [
     {file = "numpy-1.26.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b3ce300f3644fb06443ee2222c2201dd3a89ea6040541412b8fa189341847218"},
     {file = "numpy-1.26.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:03a8c78d01d9781b28a6989f6fa1bb2c4f2d51201cf99d3dd875df6fbd96b23b"},
@@ -1075,7 +1176,6 @@ version = "12.4.5.8"
 requires_python = ">=3"
 summary = "CUBLAS native runtime libraries"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 files = [
     {file = "nvidia_cublas_cu12-12.4.5.8-py3-none-manylinux2014_aarch64.whl", hash = "sha256:0f8aa1706812e00b9f19dfe0cdb3999b092ccb8ca168c0db5b8ea712456fd9b3"},
     {file = "nvidia_cublas_cu12-12.4.5.8-py3-none-manylinux2014_x86_64.whl", hash = "sha256:2fc8da60df463fdefa81e323eef2e36489e1c94335b5358bcb38360adf75ac9b"},
@@ -1088,7 +1188,6 @@ version = "12.4.127"
 requires_python = ">=3"
 summary = "CUDA profiling tools runtime libs."
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 files = [
     {file = "nvidia_cuda_cupti_cu12-12.4.127-py3-none-manylinux2014_aarch64.whl", hash = "sha256:79279b35cf6f91da114182a5ce1864997fd52294a87a16179ce275773799458a"},
     {file = "nvidia_cuda_cupti_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:9dec60f5ac126f7bb551c055072b69d85392b13311fcc1bcda2202d172df30fb"},
@@ -1101,7 +1200,6 @@ version = "12.6.85"
 requires_python = ">=3"
 summary = "CUDA nvcc"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 files = [
     {file = "nvidia_cuda_nvcc_cu12-12.6.85-py3-none-manylinux1_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d75d9d74599f4d7c0865df19ed21b739e6cb77a6497a3f73d6f61e8038a765e4"},
     {file = "nvidia_cuda_nvcc_cu12-12.6.85-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5d2edd5531b13e3daac8ffee9fc2b70a147e6088b2af2565924773d63d36d294"},
@@ -1114,7 +1212,7 @@ version = "12.4.127"
 requires_python = ">=3"
 summary = "NVRTC native runtime libraries"
 groups = ["default"]
-marker = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version >= \"3.12\" and python_version < \"3.13\""
+marker = "platform_system == \"Linux\" and platform_machine == \"x86_64\""
 files = [
     {file = "nvidia_cuda_nvrtc_cu12-12.4.127-py3-none-manylinux2014_aarch64.whl", hash = "sha256:0eedf14185e04b76aa05b1fea04133e59f465b6f960c0cbf4e37c3cb6b0ea198"},
     {file = "nvidia_cuda_nvrtc_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:a178759ebb095827bd30ef56598ec182b85547f1508941a3d560eb7ea1fbf338"},
@@ -1127,7 +1225,6 @@ version = "12.4.127"
 requires_python = ">=3"
 summary = "CUDA Runtime native Libraries"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 files = [
     {file = "nvidia_cuda_runtime_cu12-12.4.127-py3-none-manylinux2014_aarch64.whl", hash = "sha256:961fe0e2e716a2a1d967aab7caee97512f71767f852f67432d572e36cb3a11f3"},
     {file = "nvidia_cuda_runtime_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:64403288fa2136ee8e467cdc9c9427e0434110899d07c779f25b5c068934faa5"},
@@ -1140,7 +1237,6 @@ version = "9.1.0.70"
 requires_python = ">=3"
 summary = "cuDNN runtime libraries"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 dependencies = [
     "nvidia-cublas-cu12",
 ]
@@ -1155,7 +1251,6 @@ version = "11.2.1.3"
 requires_python = ">=3"
 summary = "CUFFT native runtime libraries"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 dependencies = [
     "nvidia-nvjitlink-cu12",
 ]
@@ -1171,7 +1266,7 @@ version = "10.3.5.147"
 requires_python = ">=3"
 summary = "CURAND native runtime libraries"
 groups = ["default"]
-marker = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version >= \"3.12\" and python_version < \"3.13\""
+marker = "platform_system == \"Linux\" and platform_machine == \"x86_64\""
 files = [
     {file = "nvidia_curand_cu12-10.3.5.147-py3-none-manylinux2014_aarch64.whl", hash = "sha256:1f173f09e3e3c76ab084aba0de819c49e56614feae5c12f69883f4ae9bb5fad9"},
     {file = "nvidia_curand_cu12-10.3.5.147-py3-none-manylinux2014_x86_64.whl", hash = "sha256:a88f583d4e0bb643c49743469964103aa59f7f708d862c3ddb0fc07f851e3b8b"},
@@ -1184,7 +1279,6 @@ version = "11.6.1.9"
 requires_python = ">=3"
 summary = "CUDA solver native runtime libraries"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 dependencies = [
     "nvidia-cublas-cu12",
     "nvidia-cusparse-cu12",
@@ -1202,7 +1296,6 @@ version = "12.3.1.170"
 requires_python = ">=3"
 summary = "CUSPARSE native runtime libraries"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 dependencies = [
     "nvidia-nvjitlink-cu12",
 ]
@@ -1218,7 +1311,6 @@ version = "2.21.5"
 requires_python = ">=3"
 summary = "NVIDIA Collective Communication Library (NCCL) Runtime"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 files = [
     {file = "nvidia_nccl_cu12-2.21.5-py3-none-manylinux2014_x86_64.whl", hash = "sha256:8579076d30a8c24988834445f8d633c697d42397e92ffc3f63fa26766d25e0a0"},
 ]
@@ -1229,7 +1321,6 @@ version = "12.4.127"
 requires_python = ">=3"
 summary = "Nvidia JIT LTO Library"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 files = [
     {file = "nvidia_nvjitlink_cu12-12.4.127-py3-none-manylinux2014_aarch64.whl", hash = "sha256:4abe7fef64914ccfa909bc2ba39739670ecc9e820c83ccc7a6ed414122599b83"},
     {file = "nvidia_nvjitlink_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:06b3b9b25bf3f8af351d664978ca26a16d2c5127dbd53c0497e28d1fb9611d57"},
@@ -1242,7 +1333,7 @@ version = "12.4.127"
 requires_python = ">=3"
 summary = "NVIDIA Tools Extension"
 groups = ["default"]
-marker = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version >= \"3.12\" and python_version < \"3.13\""
+marker = "platform_system == \"Linux\" and platform_machine == \"x86_64\""
 files = [
     {file = "nvidia_nvtx_cu12-12.4.127-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7959ad635db13edf4fc65c06a6e9f9e55fc2f92596db928d169c0bb031e88ef3"},
     {file = "nvidia_nvtx_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:781e950d9b9f60d8241ccea575b32f5105a5baf4c2351cab5256a24869f12a1a"},
@@ -1255,7 +1346,6 @@ version = "3.4.0"
 requires_python = ">=3.8"
 summary = "Path optimization of einsum functions."
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 files = [
     {file = "opt_einsum-3.4.0-py3-none-any.whl", hash = "sha256:69bb92469f86a1565195ece4ac0323943e83477171b91d24c35afe028a90d7cd"},
     {file = "opt_einsum-3.4.0.tar.gz", hash = "sha256:96ca72f1b886d148241348783498194c577fa30a8faac108586b14f1ba4473ac"},
@@ -1267,7 +1357,6 @@ version = "0.2.4"
 requires_python = ">=3.9"
 summary = "A gradient processing and optimization library in JAX."
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 dependencies = [
     "absl-py>=0.7.1",
     "chex>=0.1.87",
@@ -1287,7 +1376,6 @@ version = "0.10.2"
 requires_python = ">=3.10"
 summary = "Orbax Checkpoint"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 dependencies = [
     "absl-py",
     "etils[epath,epy]",
@@ -1313,7 +1401,6 @@ version = "24.2"
 requires_python = ">=3.8"
 summary = "Core utilities for Python packages"
 groups = ["default", "dev"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 files = [
     {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"},
     {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"},
@@ -1325,7 +1412,6 @@ version = "2.2.3"
 requires_python = ">=3.9"
 summary = "Powerful data structures for data analysis, time series, and statistics"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 dependencies = [
     "numpy>=1.22.4; python_version < \"3.11\"",
     "numpy>=1.23.2; python_version == \"3.11\"",
@@ -1351,18 +1437,32 @@ version = "0.8.4"
 requires_python = ">=3.6"
 summary = "A Python Parser"
 groups = ["dev"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 files = [
     {file = "parso-0.8.4-py2.py3-none-any.whl", hash = "sha256:a418670a20291dacd2dddc80c377c5c3791378ee1e8d12bffc35420643d43f18"},
     {file = "parso-0.8.4.tar.gz", hash = "sha256:eb3a7b58240fb99099a345571deecc0f9540ea5f4dd2fe14c2a99d6b281ab92d"},
 ]
 
+[[package]]
+name = "partd"
+version = "1.4.2"
+requires_python = ">=3.9"
+summary = "Appendable key-value storage"
+groups = ["default"]
+dependencies = [
+    "locket",
+    "toolz",
+]
+files = [
+    {file = "partd-1.4.2-py3-none-any.whl", hash = "sha256:978e4ac767ec4ba5b86c6eaa52e5a2a3bc748a2ca839e8cc798f1cc6ce6efb0f"},
+    {file = "partd-1.4.2.tar.gz", hash = "sha256:d022c33afbdc8405c226621b015e8067888173d85f7f5ecebb3cafed9a20f02c"},
+]
+
 [[package]]
 name = "pexpect"
 version = "4.9.0"
 summary = "Pexpect allows easy control of interactive console applications."
 groups = ["dev"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\" and (sys_platform != \"win32\" and sys_platform != \"emscripten\")"
+marker = "sys_platform != \"win32\" and sys_platform != \"emscripten\""
 dependencies = [
     "ptyprocess>=0.5",
 ]
@@ -1377,7 +1477,6 @@ version = "11.0.0"
 requires_python = ">=3.9"
 summary = "Python Imaging Library (Fork)"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 files = [
     {file = "pillow-11.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d2c0a187a92a1cb5ef2c8ed5412dd8d4334272617f532d4ad4de31e0495bd923"},
     {file = "pillow-11.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:084a07ef0821cfe4858fe86652fffac8e187b6ae677e9906e192aafcc1b69903"},
@@ -1399,7 +1498,6 @@ version = "4.3.6"
 requires_python = ">=3.8"
 summary = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`."
 groups = ["default", "dev"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 files = [
     {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"},
     {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"},
@@ -1411,7 +1509,6 @@ version = "1.5.0"
 requires_python = ">=3.8"
 summary = "plugin and hook calling mechanisms for python"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 files = [
     {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"},
     {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"},
@@ -1423,7 +1520,6 @@ version = "1.8.2"
 requires_python = ">=3.7"
 summary = "A friend to fetch your data files"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 dependencies = [
     "packaging>=20.0",
     "platformdirs>=2.5.0",
@@ -1440,7 +1536,6 @@ version = "3.0.48"
 requires_python = ">=3.7.0"
 summary = "Library for building powerful interactive command lines in Python"
 groups = ["dev"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 dependencies = [
     "wcwidth",
 ]
@@ -1455,7 +1550,6 @@ version = "5.29.1"
 requires_python = ">=3.8"
 summary = ""
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 files = [
     {file = "protobuf-5.29.1-cp310-abi3-win32.whl", hash = "sha256:22c1f539024241ee545cbcb00ee160ad1877975690b16656ff87dde107b5f110"},
     {file = "protobuf-5.29.1-cp310-abi3-win_amd64.whl", hash = "sha256:1fc55267f086dd4050d18ef839d7bd69300d0d08c2a53ca7df3920cc271a3c34"},
@@ -1472,7 +1566,6 @@ version = "6.1.0"
 requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7"
 summary = "Cross-platform lib for process and system monitoring in Python."
 groups = ["dev"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 files = [
     {file = "psutil-6.1.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:6e2dcd475ce8b80522e51d923d10c7871e45f20918e027ab682f94f1c6351688"},
     {file = "psutil-6.1.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:0895b8414afafc526712c498bd9de2b063deaac4021a3b3c34566283464aff8e"},
@@ -1489,7 +1582,7 @@ name = "ptyprocess"
 version = "0.7.0"
 summary = "Run a subprocess in a pseudo terminal"
 groups = ["dev"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\" and (sys_platform != \"win32\" and sys_platform != \"emscripten\")"
+marker = "sys_platform != \"win32\" and sys_platform != \"emscripten\""
 files = [
     {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"},
     {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"},
@@ -1500,7 +1593,6 @@ name = "pure-eval"
 version = "0.2.3"
 summary = "Safely evaluate AST nodes without side effects"
 groups = ["dev"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 files = [
     {file = "pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0"},
     {file = "pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42"},
@@ -1512,7 +1604,7 @@ version = "2.22"
 requires_python = ">=3.8"
 summary = "C parser in Python"
 groups = ["dev"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\" and implementation_name == \"pypy\""
+marker = "implementation_name == \"pypy\""
 files = [
     {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"},
     {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"},
@@ -1524,31 +1616,84 @@ version = "2.18.0"
 requires_python = ">=3.8"
 summary = "Pygments is a syntax highlighting package written in Python."
 groups = ["default", "dev"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 files = [
     {file = "pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"},
     {file = "pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199"},
 ]
 
+[[package]]
+name = "pykdtree"
+version = "1.3.13"
+requires_python = ">=3.9"
+summary = "Fast kd-tree implementation with OpenMP-enabled queries"
+groups = ["default"]
+dependencies = [
+    "numpy",
+]
+files = [
+    {file = "pykdtree-1.3.13-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:1ce7bb28aa469d032fe8f1a7068baba0b94bcd8da9e9b0b843c6d4c8fc6d362a"},
+    {file = "pykdtree-1.3.13-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:ded3c7a6a20b9e5eddf8a3e2ce1506d45e91f1c139f1d6de0c7028bad5bad47e"},
+    {file = "pykdtree-1.3.13-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc5dc024bfc47f5f32eaf7ac8b2535fc374486fd33eebfe914c24e4f9177f1ed"},
+    {file = "pykdtree-1.3.13-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e4d03bbd6655af89eef51b0445139e3ceea65bd6addd666966423d1a0bde3c8"},
+    {file = "pykdtree-1.3.13-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b89455f5c1e261522cd5560b2ec03dabaacaf3b17a44fbdc5f319618c167578b"},
+    {file = "pykdtree-1.3.13-cp312-cp312-win_amd64.whl", hash = "sha256:4699631cd52b7405cfc3846b65b98380fe3e47d8abc1ef13ae2f78966a0db0ad"},
+    {file = "pykdtree-1.3.13-cp312-cp312-win_arm64.whl", hash = "sha256:7f2eac6c372130afd2204443e719930cdd737d7d91b0b2be3c4f2d26f124d8b2"},
+    {file = "pykdtree-1.3.13.tar.gz", hash = "sha256:3accf852e946653e399c3d4dbbe119dbc6d3f72cfd2d5a95cabf0bf0c7f924fe"},
+]
+
+[[package]]
+name = "pyorbital"
+version = "1.9.2"
+requires_python = ">=3.10"
+summary = "Scheduling satellite passes in Python"
+groups = ["default"]
+dependencies = [
+    "defusedxml",
+    "numpy>=1.19.0",
+    "requests",
+    "scipy",
+]
+files = [
+    {file = "pyorbital-1.9.2-py3-none-any.whl", hash = "sha256:170fb5e811abf63a70936aea3dcbb8c7a87d7da3118fcd35e82113eb62e55409"},
+    {file = "pyorbital-1.9.2.tar.gz", hash = "sha256:bb905d5e115681ff77e5e2a4b208c60539fc58cf6be8cc06cec4ee20979164fb"},
+]
+
 [[package]]
 name = "pyparsing"
 version = "3.2.0"
 requires_python = ">=3.9"
 summary = "pyparsing module - Classes and methods to define and execute parsing grammars"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 files = [
     {file = "pyparsing-3.2.0-py3-none-any.whl", hash = "sha256:93d9577b88da0bbea8cc8334ee8b918ed014968fd2ec383e868fb8afb1ccef84"},
     {file = "pyparsing-3.2.0.tar.gz", hash = "sha256:cbf74e27246d595d9a74b186b810f6fbb86726dbf3b9532efb343f6d7294fe9c"},
 ]
 
+[[package]]
+name = "pyproj"
+version = "3.7.0"
+requires_python = ">=3.10"
+summary = "Python interface to PROJ (cartographic projections and coordinate transformations library)"
+groups = ["default"]
+dependencies = [
+    "certifi",
+]
+files = [
+    {file = "pyproj-3.7.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:8cbec92bdd6e9933ca08795c12717d1384e9b51cf4b1acf0d753db255a75c51e"},
+    {file = "pyproj-3.7.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:8c4a8e4d3ba76c3adac3c087544cf92f7f9a19ea34946904a13fca48cc1c0106"},
+    {file = "pyproj-3.7.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82624fb42aa31f6b1a860fbc0316babd07fd712642bc31022df4e9b4056bf463"},
+    {file = "pyproj-3.7.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:34e1bbb3f89c68d4a6835c40b2da8b27680eec60e8cc7cdb08c09bcc725b2b62"},
+    {file = "pyproj-3.7.0-cp312-cp312-win32.whl", hash = "sha256:952515d5592167ad4436b355485f82acebed2a49b46722159e4584b75a763dd3"},
+    {file = "pyproj-3.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:0692f806224e8ed82fe4acfa57268ff444fdaf9f330689f24c0d96e59480cce1"},
+    {file = "pyproj-3.7.0.tar.gz", hash = "sha256:bf658f4aaf815d9d03c8121650b6f0b8067265c36e31bc6660b98ef144d81813"},
+]
+
 [[package]]
 name = "pyqtgraph"
 version = "0.13.7"
 requires_python = ">=3.9"
 summary = "Scientific Graphics and GUI Library for Python"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 dependencies = [
     "numpy>=1.22.0",
 ]
@@ -1557,13 +1702,49 @@ files = [
     {file = "pyqtgraph-0.13.7.tar.gz", hash = "sha256:64f84f1935c6996d0e09b1ee66fe478a7771e3ca6f3aaa05f00f6e068321d9e3"},
 ]
 
+[[package]]
+name = "pyresample"
+version = "1.31.0"
+requires_python = ">=3.9"
+summary = "Geospatial image resampling in Python"
+groups = ["default"]
+dependencies = [
+    "configobj",
+    "donfig",
+    "numpy>=1.21.0",
+    "platformdirs",
+    "pykdtree>=1.3.1",
+    "pyproj>=3.0",
+    "pyyaml",
+    "shapely",
+]
+files = [
+    {file = "pyresample-1.31.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:713ba70d9cf2326945235abe21a1afec3eddfaced38b7a94acabc24c489ed5c6"},
+    {file = "pyresample-1.31.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:626688bba921a4164c2a481a5ace4b664ce75be3de90044639a1e6a325e9f967"},
+    {file = "pyresample-1.31.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b488e5d48b180d603bff339a4206ac4b85bbd77ffb0d3b83efa325bafc3f32c2"},
+    {file = "pyresample-1.31.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e6b6152d24b77e48b60d7f61ee7fc310b1c68648036202a84182f37e1f3018e0"},
+    {file = "pyresample-1.31.0-cp312-cp312-win_amd64.whl", hash = "sha256:781aedc03578ff445682cb0f56842bfdbd29b290b83a983e5f851d64cd81cac8"},
+    {file = "pyresample-1.31.0-cp312-cp312-win_arm64.whl", hash = "sha256:fadddb361f488a7a338bf206ed0d1bfa083856f6c47c80777f9533d71e6b9729"},
+    {file = "pyresample-1.31.0.tar.gz", hash = "sha256:b9cd365a3d5138c4b515b33ff37e7a851160d465e26468f2ba2b6342efef6a41"},
+]
+
+[[package]]
+name = "pyshp"
+version = "2.3.1"
+requires_python = ">=2.7"
+summary = "Pure Python read/write support for ESRI Shapefile format"
+groups = ["default"]
+files = [
+    {file = "pyshp-2.3.1-py2.py3-none-any.whl", hash = "sha256:67024c0ccdc352ba5db777c4e968483782dfa78f8e200672a90d2d30fd8b7b49"},
+    {file = "pyshp-2.3.1.tar.gz", hash = "sha256:4caec82fd8dd096feba8217858068bacb2a3b5950f43c048c6dc32a3489d5af1"},
+]
+
 [[package]]
 name = "pyside6"
 version = "6.8.0.2"
 requires_python = "<3.14,>=3.9"
 summary = "Python bindings for the Qt cross-platform application and UI framework"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 dependencies = [
     "PySide6-Addons==6.8.0.2",
     "PySide6-Essentials==6.8.0.2",
@@ -1582,7 +1763,6 @@ version = "6.8.0.2"
 requires_python = "<3.14,>=3.9"
 summary = "Python bindings for the Qt cross-platform application and UI framework (Addons)"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 dependencies = [
     "PySide6-Essentials==6.8.0.2",
     "shiboken6==6.8.0.2",
@@ -1600,7 +1780,6 @@ version = "6.8.0.2"
 requires_python = "<3.14,>=3.9"
 summary = "Python bindings for the Qt cross-platform application and UI framework (Essentials)"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 dependencies = [
     "shiboken6==6.8.0.2",
 ]
@@ -1617,7 +1796,6 @@ version = "8.3.3"
 requires_python = ">=3.8"
 summary = "pytest: simple powerful testing with Python"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 dependencies = [
     "colorama; sys_platform == \"win32\"",
     "exceptiongroup>=1.0.0rc8; python_version < \"3.11\"",
@@ -1637,7 +1815,6 @@ version = "2.9.0.post0"
 requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
 summary = "Extensions to the standard Python datetime module"
 groups = ["default", "dev"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 dependencies = [
     "six>=1.5",
 ]
@@ -1651,7 +1828,6 @@ name = "pytz"
 version = "2024.2"
 summary = "World timezone definitions, modern and historical"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 files = [
     {file = "pytz-2024.2-py2.py3-none-any.whl", hash = "sha256:31c7c1817eb7fae7ca4b8c7ee50c72f93aa2dd863de768e1ef4245d426aa0725"},
     {file = "pytz-2024.2.tar.gz", hash = "sha256:2aa355083c50a0f93fa581709deac0c9ad65cca8a9e9beac660adcbd493c798a"},
@@ -1663,7 +1839,6 @@ version = "0.44.2"
 requires_python = ">=3.8"
 summary = "Easier Pythonic interface to VTK"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 dependencies = [
     "matplotlib>=3.0.1",
     "numpy>=1.21.0",
@@ -1684,7 +1859,6 @@ version = "0.11.1"
 requires_python = ">=3.7"
 summary = "pyvista qt plotter"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 dependencies = [
     "QtPy>=1.9.0",
     "pyvista>=0.32.0",
@@ -1699,7 +1873,7 @@ name = "pywin32"
 version = "308"
 summary = "Python for Window Extensions"
 groups = ["dev"]
-marker = "sys_platform == \"win32\" and platform_python_implementation != \"PyPy\" and python_version >= \"3.12\" and python_version < \"3.13\""
+marker = "sys_platform == \"win32\" and platform_python_implementation != \"PyPy\""
 files = [
     {file = "pywin32-308-cp312-cp312-win32.whl", hash = "sha256:587f3e19696f4bf96fde9d8a57cec74a57021ad5f204c9e627e15c33ff568897"},
     {file = "pywin32-308-cp312-cp312-win_amd64.whl", hash = "sha256:00b3e11ef09ede56c6a43c71f2d31857cf7c54b0ab6e78ac659497abd2834f47"},
@@ -1712,7 +1886,6 @@ version = "6.0.2"
 requires_python = ">=3.8"
 summary = "YAML parser and emitter for Python"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 files = [
     {file = "PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab"},
     {file = "PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725"},
@@ -1732,7 +1905,6 @@ version = "26.2.0"
 requires_python = ">=3.7"
 summary = "Python bindings for 0MQ"
 groups = ["dev"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 dependencies = [
     "cffi; implementation_name == \"pypy\"",
 ]
@@ -1758,7 +1930,6 @@ version = "2.4.2"
 requires_python = ">=3.7"
 summary = "Provides an abstraction layer on top of the various Qt bindings (PyQt5/6 and PySide2/6)."
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 dependencies = [
     "packaging",
 ]
@@ -1773,7 +1944,6 @@ version = "2.32.3"
 requires_python = ">=3.8"
 summary = "Python HTTP for Humans."
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 dependencies = [
     "certifi>=2017.4.17",
     "charset-normalizer<4,>=2",
@@ -1791,7 +1961,6 @@ version = "13.9.4"
 requires_python = ">=3.8.0"
 summary = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 dependencies = [
     "markdown-it-py>=2.2.0",
     "pygments<3.0.0,>=2.13.0",
@@ -1802,13 +1971,41 @@ files = [
     {file = "rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098"},
 ]
 
+[[package]]
+name = "satpy"
+version = "0.53.0"
+requires_python = ">=3.10"
+summary = "Python package for earth-observing satellite data processing"
+groups = ["default"]
+dependencies = [
+    "dask[array]>=0.17.1",
+    "donfig",
+    "numpy>=1.21",
+    "packaging",
+    "pillow",
+    "platformdirs",
+    "pooch",
+    "pykdtree",
+    "pyorbital",
+    "pyproj>=2.2",
+    "pyresample>=1.24.0",
+    "pyyaml>=5.1",
+    "trollimage>=1.24",
+    "trollsift",
+    "xarray>=0.14.1",
+    "zarr",
+]
+files = [
+    {file = "satpy-0.53.0-py3-none-any.whl", hash = "sha256:abea68dcfb27793bca9073abb4927fb92c5c39735390aa6235664d1fbad848e2"},
+    {file = "satpy-0.53.0.tar.gz", hash = "sha256:cc0eb12462b2f1eda271c3456b158b7bd55287b686c57aa9454aff4aed64070b"},
+]
+
 [[package]]
 name = "sbx-rl"
 version = "0.18.0"
 requires_python = ">=3.8"
 summary = "Jax version of Stable Baselines, implementations of reinforcement learning algorithms."
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 dependencies = [
     "flax",
     "jax",
@@ -1831,7 +2028,6 @@ version = "1.14.1"
 requires_python = ">=3.10"
 summary = "Fundamental algorithms for scientific computing in Python"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 dependencies = [
     "numpy<2.3,>=1.23.5",
 ]
@@ -1861,7 +2057,6 @@ version = "0.10.0"
 requires_python = ">=3.8"
 summary = "A Great Dane turned Python environment detective"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 files = [
     {file = "scooby-0.10.0-py3-none-any.whl", hash = "sha256:0a3d7e304f8ebb16f69ff7f6360c345d7f50b45f2ddbf7c3d18a6a0dc2cb03a6"},
     {file = "scooby-0.10.0.tar.gz", hash = "sha256:7ea33c262c0cc6a33c6eeeb5648df787be4f22660e53c114e5fff1b811a8854f"},
@@ -1873,7 +2068,6 @@ version = "0.13.2"
 requires_python = ">=3.8"
 summary = "Statistical data visualization"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 dependencies = [
     "matplotlib!=3.6.1,>=3.4",
     "numpy!=1.24.0,>=1.20",
@@ -1890,19 +2084,36 @@ version = "75.6.0"
 requires_python = ">=3.9"
 summary = "Easily download, build, install, upgrade, and uninstall Python packages"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 files = [
     {file = "setuptools-75.6.0-py3-none-any.whl", hash = "sha256:ce74b49e8f7110f9bf04883b730f4765b774ef3ef28f722cce7c273d253aaf7d"},
     {file = "setuptools-75.6.0.tar.gz", hash = "sha256:8199222558df7c86216af4f84c30e9b34a61d8ba19366cc914424cdbd28252f6"},
 ]
 
+[[package]]
+name = "shapely"
+version = "2.0.6"
+requires_python = ">=3.7"
+summary = "Manipulation and analysis of geometric objects"
+groups = ["default"]
+dependencies = [
+    "numpy<3,>=1.14",
+]
+files = [
+    {file = "shapely-2.0.6-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cec9193519940e9d1b86a3b4f5af9eb6910197d24af02f247afbfb47bcb3fab0"},
+    {file = "shapely-2.0.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:83b94a44ab04a90e88be69e7ddcc6f332da7c0a0ebb1156e1c4f568bbec983c3"},
+    {file = "shapely-2.0.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:537c4b2716d22c92036d00b34aac9d3775e3691f80c7aa517c2c290351f42cd8"},
+    {file = "shapely-2.0.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98fea108334be345c283ce74bf064fa00cfdd718048a8af7343c59eb40f59726"},
+    {file = "shapely-2.0.6-cp312-cp312-win32.whl", hash = "sha256:42fd4cd4834747e4990227e4cbafb02242c0cffe9ce7ef9971f53ac52d80d55f"},
+    {file = "shapely-2.0.6-cp312-cp312-win_amd64.whl", hash = "sha256:665990c84aece05efb68a21b3523a6b2057e84a1afbef426ad287f0796ef8a48"},
+    {file = "shapely-2.0.6.tar.gz", hash = "sha256:997f6159b1484059ec239cacaa53467fd8b5564dabe186cd84ac2944663b0bf6"},
+]
+
 [[package]]
 name = "shiboken6"
 version = "6.8.0.2"
 requires_python = "<3.14,>=3.9"
 summary = "Python/C++ bindings helper module"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 files = [
     {file = "shiboken6-6.8.0.2-cp39-abi3-macosx_12_0_universal2.whl", hash = "sha256:9019e1fcfeed8bb350222e981748ef05a2fec11e31ddf616657be702f0b7a468"},
     {file = "shiboken6-6.8.0.2-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:fa7d411c3c67b4296847b3f5f572268e219d947d029ff9d8bce72fe6982d92bc"},
@@ -1916,7 +2127,6 @@ version = "1.7.1"
 requires_python = ">=3.7"
 summary = "Automagic shell tab completion for Python CLI applications"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 files = [
     {file = "shtab-1.7.1-py3-none-any.whl", hash = "sha256:32d3d2ff9022d4c77a62492b6ec875527883891e33c6b479ba4d41a51e259983"},
     {file = "shtab-1.7.1.tar.gz", hash = "sha256:4e4bcb02eeb82ec45920a5d0add92eac9c9b63b2804c9196c1f1fdc2d039243c"},
@@ -1928,7 +2138,6 @@ version = "3.19.3"
 requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.5"
 summary = "Simple, fast, extensible JSON encoder/decoder for Python"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 files = [
     {file = "simplejson-3.19.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:66a0399e21c2112acacfebf3d832ebe2884f823b1c7e6d1363f2944f1db31a99"},
     {file = "simplejson-3.19.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6ef9383c5e05f445be60f1735c1816163c874c0b1ede8bb4390aff2ced34f333"},
@@ -1953,7 +2162,6 @@ version = "1.16.0"
 requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
 summary = "Python 2 and 3 compatibility utilities"
 groups = ["default", "dev"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 files = [
     {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
     {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
@@ -1965,7 +2173,6 @@ version = "2.4.0"
 requires_python = ">=3.8"
 summary = "Pytorch version of Stable Baselines, implementations of reinforcement learning algorithms."
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 dependencies = [
     "cloudpickle",
     "gymnasium<1.1.0,>=0.29.1",
@@ -1984,7 +2191,6 @@ name = "stack-data"
 version = "0.6.3"
 summary = "Extract data from python stack frames and tracebacks for informative displays"
 groups = ["dev"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 dependencies = [
     "asttokens>=2.1.0",
     "executing>=1.2.0",
@@ -2001,7 +2207,7 @@ version = "1.13.1"
 requires_python = ">=3.8"
 summary = "Computer algebra system (CAS) in Python"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
+marker = "python_version >= \"3.9\""
 dependencies = [
     "mpmath<1.4,>=1.1.0",
 ]
@@ -2016,7 +2222,6 @@ version = "2.18.0"
 requires_python = ">=3.9"
 summary = "TensorBoard lets you watch Tensors Flow"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 dependencies = [
     "absl-py>=0.4",
     "grpcio>=1.48.2",
@@ -2039,7 +2244,6 @@ version = "0.7.2"
 requires_python = ">=3.7"
 summary = "Fast data loading for TensorBoard"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 files = [
     {file = "tensorboard_data_server-0.7.2-py3-none-any.whl", hash = "sha256:7e0610d205889588983836ec05dc098e80f97b7e7bbff7e994ebb78f578d0ddb"},
     {file = "tensorboard_data_server-0.7.2-py3-none-macosx_10_9_x86_64.whl", hash = "sha256:9fe5d24221b29625dbc7328b0436ca7fc1c23de4acf4d272f1180856e32f9f60"},
@@ -2052,7 +2256,6 @@ version = "0.25.0"
 requires_python = ">=3.9"
 summary = "Probabilistic modeling and statistical inference in TensorFlow"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 dependencies = [
     "absl-py",
     "cloudpickle>=1.3",
@@ -2072,7 +2275,6 @@ version = "0.1.71"
 requires_python = ">=3.10"
 summary = "Read and write large, multi-dimensional arrays"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 dependencies = [
     "ml-dtypes>=0.3.1",
     "numpy>=1.22.0",
@@ -2092,7 +2294,6 @@ version = "1.0.0"
 requires_python = ">=3.8"
 summary = "List processing tools and functional utilities"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 files = [
     {file = "toolz-1.0.0-py3-none-any.whl", hash = "sha256:292c8f1c4e7516bf9086f8850935c799a874039c8bcf959d47b600e4c44a6236"},
     {file = "toolz-1.0.0.tar.gz", hash = "sha256:2c86e3d9a04798ac556793bced838816296a2f085017664e4995cb40a1047a02"},
@@ -2104,7 +2305,6 @@ version = "2.5.1"
 requires_python = ">=3.8.0"
 summary = "Tensors and Dynamic neural networks in Python with strong GPU acceleration"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 dependencies = [
     "filelock",
     "fsspec",
@@ -2141,7 +2341,6 @@ version = "6.4.2"
 requires_python = ">=3.8"
 summary = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed."
 groups = ["dev"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 files = [
     {file = "tornado-6.4.2-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:e828cce1123e9e44ae2a50a9de3055497ab1d0aeb440c5ac23064d9e44880da1"},
     {file = "tornado-6.4.2-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:072ce12ada169c5b00b7d92a99ba089447ccc993ea2143c9ede887e0937aa803"},
@@ -2162,7 +2361,6 @@ version = "4.67.1"
 requires_python = ">=3.7"
 summary = "Fast, Extensible Progress Meter"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 dependencies = [
     "colorama; platform_system == \"Windows\"",
 ]
@@ -2177,7 +2375,6 @@ version = "5.14.3"
 requires_python = ">=3.8"
 summary = "Traitlets Python configuration system"
 groups = ["dev"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 files = [
     {file = "traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f"},
     {file = "traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7"},
@@ -2188,7 +2385,7 @@ name = "triton"
 version = "3.1.0"
 summary = "A language and compiler for custom Deep Learning operations"
 groups = ["default"]
-marker = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version < \"3.13\" and python_version >= \"3.12\""
+marker = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version < \"3.13\""
 dependencies = [
     "filelock",
 ]
@@ -2196,13 +2393,44 @@ files = [
     {file = "triton-3.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8182f42fd8080a7d39d666814fa36c5e30cc00ea7eeeb1a2983dbb4c99a0fdc"},
 ]
 
+[[package]]
+name = "trollimage"
+version = "1.26.0"
+requires_python = ">=3.9"
+summary = "Pytroll imaging library"
+groups = ["default"]
+dependencies = [
+    "numpy>=1.20",
+    "pillow",
+]
+files = [
+    {file = "trollimage-1.26.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f3e35da686c2c34a958537a130355b429f6b54f10bc813f3c69d92b8257a859"},
+    {file = "trollimage-1.26.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:306b8b7686dc7bc2e5796f3f956d9238c88f02ab723ea130bcc07ef88ffb1318"},
+    {file = "trollimage-1.26.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c8220bd5609a98b0fd9c2692de2578fb86da1dba1e599b165de859dba3eff226"},
+    {file = "trollimage-1.26.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b61714307cc04d99d721108b468d86e712df44646e2c965f9d36d1bc1a773d0"},
+    {file = "trollimage-1.26.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b18dbe3ce43554decd60d7c07cee7a8342a7fcf2e1e37ca8b8b35cccdd4987f9"},
+    {file = "trollimage-1.26.0-cp312-cp312-win_amd64.whl", hash = "sha256:a10412374aacd093ab8f7e8f2aff3059f31f8c82b7342bfd9b360df912fd3b5a"},
+    {file = "trollimage-1.26.0-cp312-cp312-win_arm64.whl", hash = "sha256:dfd9e2ffbf25629df522d52a2e547b01f5820efdecc3fe0bdf56b592a6c6cb9d"},
+    {file = "trollimage-1.26.0.tar.gz", hash = "sha256:4ea98c7eb6ef48d7c8c03dbc874fec4071972d4036e5f2a751ca6bc669be50d1"},
+]
+
+[[package]]
+name = "trollsift"
+version = "0.5.3"
+requires_python = ">=3.9"
+summary = "String parser/formatter"
+groups = ["default"]
+files = [
+    {file = "trollsift-0.5.3-py3-none-any.whl", hash = "sha256:312fd8d14b5c4bfc6a551625a6827ded953abe2ee950f44b86194fe0e8dc65b6"},
+    {file = "trollsift-0.5.3.tar.gz", hash = "sha256:2f09f37f79027668be36610d4c39c36679cb65d2629368f8ae32c7902161263a"},
+]
+
 [[package]]
 name = "typeguard"
 version = "4.4.1"
 requires_python = ">=3.9"
 summary = "Run-time type checker for Python"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 dependencies = [
     "importlib-metadata>=3.6; python_version < \"3.10\"",
     "typing-extensions>=4.10.0",
@@ -2218,7 +2446,6 @@ version = "4.12.2"
 requires_python = ">=3.8"
 summary = "Backported and Experimental Type Hints for Python 3.8+"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 files = [
     {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"},
     {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"},
@@ -2230,7 +2457,6 @@ version = "0.9.2"
 requires_python = ">=3.7"
 summary = "CLI interfaces & config objects, from types"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 dependencies = [
     "backports-cached-property>=1.0.2; python_version < \"3.8\"",
     "colorama>=0.4.0; platform_system == \"Windows\"",
@@ -2253,7 +2479,6 @@ version = "2024.2"
 requires_python = ">=2"
 summary = "Provider of IANA time zone data"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 files = [
     {file = "tzdata-2024.2-py2.py3-none-any.whl", hash = "sha256:a48093786cdcde33cad18c2555e8532f34422074448fbc874186f0abd79565cd"},
     {file = "tzdata-2024.2.tar.gz", hash = "sha256:7d85cc416e9382e69095b7bdf4afd9e3880418a2413feec7069d533d6b4e31cc"},
@@ -2265,7 +2490,6 @@ version = "2.2.3"
 requires_python = ">=3.8"
 summary = "HTTP library with thread-safe connection pooling, file post, and more."
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 files = [
     {file = "urllib3-2.2.3-py3-none-any.whl", hash = "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac"},
     {file = "urllib3-2.2.3.tar.gz", hash = "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9"},
@@ -2276,7 +2500,6 @@ name = "vtk"
 version = "9.3.1"
 summary = "VTK is an open-source toolkit for 3D computer graphics, image processing, and visualization"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 dependencies = [
     "matplotlib>=2.0.0",
 ]
@@ -2292,7 +2515,6 @@ name = "wcwidth"
 version = "0.2.13"
 summary = "Measures the displayed width of unicode strings in a terminal"
 groups = ["dev"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 dependencies = [
     "backports-functools-lru-cache>=1.2.1; python_version < \"3.2\"",
 ]
@@ -2307,7 +2529,6 @@ version = "3.1.3"
 requires_python = ">=3.9"
 summary = "The comprehensive WSGI web application library."
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 dependencies = [
     "MarkupSafe>=2.1.1",
 ]
@@ -2316,13 +2537,45 @@ files = [
     {file = "werkzeug-3.1.3.tar.gz", hash = "sha256:60723ce945c19328679790e3282cc758aa4a6040e4bb330f53d30fa546d44746"},
 ]
 
+[[package]]
+name = "xarray"
+version = "2024.11.0"
+requires_python = ">=3.10"
+summary = "N-D labeled arrays and datasets in Python"
+groups = ["default"]
+dependencies = [
+    "numpy>=1.24",
+    "packaging>=23.2",
+    "pandas>=2.1",
+]
+files = [
+    {file = "xarray-2024.11.0-py3-none-any.whl", hash = "sha256:6ee94f63ddcbdd0cf3909d1177f78cdac756640279c0e32ae36819a89cdaba37"},
+    {file = "xarray-2024.11.0.tar.gz", hash = "sha256:1ccace44573ddb862e210ad3ec204210654d2c750bec11bbe7d842dfc298591f"},
+]
+
+[[package]]
+name = "zarr"
+version = "2.18.4"
+requires_python = ">=3.11"
+summary = "An implementation of chunked, compressed, N-dimensional arrays for Python"
+groups = ["default"]
+dependencies = [
+    "asciitree",
+    "fasteners; sys_platform != \"emscripten\"",
+    "numcodecs!=0.14.0,!=0.14.1,>=0.10.0",
+    "numpy>=1.24",
+]
+files = [
+    {file = "zarr-2.18.4-py3-none-any.whl", hash = "sha256:2795e20aff91093ce7e4da36ab1a138aededbd8ab66bf01fd01512e61d31e5d1"},
+    {file = "zarr-2.18.4.tar.gz", hash = "sha256:37790ededd0683ae1abe6ff90aa16c22543b3436810060f53d72c15e910c24bb"},
+]
+
 [[package]]
 name = "zipp"
 version = "3.21.0"
 requires_python = ">=3.9"
 summary = "Backport of pathlib-compatible object wrapper for zip files"
 groups = ["default"]
-marker = "python_version >= \"3.12\" and python_version < \"3.13\""
 files = [
     {file = "zipp-3.21.0-py3-none-any.whl", hash = "sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931"},
     {file = "zipp-3.21.0.tar.gz", hash = "sha256:2c9958f6430a2040341a52eb608ed6dd93ef4392e02ffe219417c1b28b5dd1f4"},
diff --git a/pyproject.toml b/pyproject.toml
index 4c0e2a8..9b1cc89 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -5,8 +5,8 @@ description = "A solar car racing simulation library and GUI tool"
 authors = [
     {name = "saji", email = "saji@saji.dev"},
 ]
-dependencies = ["pyqtgraph>=0.13.7", "jax[cuda12]>=0.4.37", "pytest>=8.3.3", "pyside6>=6.8.0.2", "matplotlib>=3.9.2", "gymnasium[jax]>=1.0.0", "pyvista>=0.44.2", "pyvistaqt>=0.11.1", "stable-baselines3>=2.4.0", "gymnax>=0.0.8", "sbx-rl>=0.18.0", "tyro>=0.9.2", "tensorboard>=2.18.0", "distrax>=0.1.5"]
-requires-python = ">=3.10,<3.13"
+dependencies = ["pyqtgraph>=0.13.7", "jax[cuda12]>=0.4.37", "pytest>=8.3.3", "pyside6>=6.8.0.2", "matplotlib>=3.9.2", "gymnasium[jax]>=1.0.0", "pyvista>=0.44.2", "pyvistaqt>=0.11.1", "stable-baselines3>=2.4.0", "gymnax>=0.0.8", "sbx-rl>=0.18.0", "tyro>=0.9.2", "tensorboard>=2.18.0", "distrax>=0.1.5", "satpy>=0.53.0", "cartopy>=0.24.1", "xarray>=2024.11.0"]
+requires-python = ">=3.12,<3.13"
 readme = "README.md"
 license = {text = "MIT"}
 
diff --git a/report/PPO_results.pdf b/report/PPO_results.pdf
new file mode 100644
index 0000000..039886f
Binary files /dev/null and b/report/PPO_results.pdf differ
diff --git a/report/environment.pdf b/report/environment.pdf
new file mode 100644
index 0000000..3a533e6
Binary files /dev/null and b/report/environment.pdf differ
diff --git a/report/references.bib b/report/references.bib
new file mode 100644
index 0000000..0f243fa
--- /dev/null
+++ b/report/references.bib
@@ -0,0 +1,93 @@
+@article{lu2022discovered,
+    title={Discovered policy optimisation},
+    author={Lu, Chris and Kuba, Jakub and Letcher, Alistair and Metz, Luke and Schroeder de Witt, Christian and Foerster, Jakob},
+    journal={Advances in Neural Information Processing Systems},
+    volume={35},
+    pages={16455--16468},
+    year={2022}
+}
+@software{jax2018github,
+  author = {James Bradbury and Roy Frostig and Peter Hawkins and Matthew James Johnson and Chris Leary and Dougal Maclaurin and George Necula and Adam Paszke and Jake Vander{P}las and Skye Wanderman-{M}ilne and Qiao Zhang},
+  title = {{JAX}: composable transformations of {P}ython+{N}um{P}y programs},
+  url = {http://github.com/jax-ml/jax},
+  version = {0.3.13},
+  year = {2018},
+}
+
+@article{doi:10.1518/106480407X312374,
+author = {Antony Hilliard and Greg A. Jamieson},
+title ={Winning Solar Races with Interface Design},
+
+journal = {Ergonomics in Design},
+volume = {16},
+number = {2},
+pages = {6-11},
+year = {2008},
+doi = {10.1518/106480407X312374},
+URL = {https://doi.org/10.1518/106480407X312374},
+eprint = {https://doi.org/10.1518/106480407X312374},
+abstract = { Solar car racing is both a highly competitive sport and a test arena for tomorrow's renewable-energy applications. This article describes the design of a graphical interface for solar car race strategy planning. The coupling, unpredictability, and size of the solar car racing environment present tough challenges to racing strategy teams. Representation-aiding techniques provide a useful approach for managing this complexity, translating difficult problems into visual analogues that are better suited to human information processing. }
+}
+
+@Article{heuristicsolar,
+AUTHOR = {Betancur, Esteban and Osorio-Gómez, Gilberto and Rivera, Juan Carlos},
+TITLE = {Heuristic Optimization for the Energy Management and Race Strategy of a Solar Car},
+JOURNAL = {Sustainability},
+VOLUME = {9},
+YEAR = {2017},
+NUMBER = {10},
+ARTICLE-NUMBER = {1576},
+URL = {https://www.mdpi.com/2071-1050/9/10/1576},
+ISSN = {2071-1050},
+ABSTRACT = {Solar cars are known for their energy efficiency, and different races are designed to measure their performance under certain conditions. For these races, in addition to an efficient vehicle, a competition strategy is required to define the optimal speed, with the objective of finishing the race in the shortest possible time using the energy available. Two heuristic optimization methods are implemented to solve this problem, a convergence and performance comparison of both methods is presented. A computational model of the race is developed, including energy input, consumption and storage systems. Based on this model, the different optimization methods are tested on the optimization of the World Solar Challenge 2015 race strategy under two different environmental conditions. A suitable method for solar car racing strategy is developed with the vehicle specifications taken as an independent input to permit the simulation of different solar or electric vehicles.},
+DOI = {10.3390/su9101576}
+}
+
+
+@misc{gymnasium,
+      title={Gymnasium: A Standard Interface for Reinforcement Learning Environments}, 
+      author={Mark Towers and Ariel Kwiatkowski and Jordan Terry and John U. Balis and Gianluca De Cola and Tristan Deleu and Manuel Goulão and Andreas Kallinteris and Markus Krimmel and Arjun KG and Rodrigo Perez-Vicente and Andrea Pierré and Sander Schulhoff and Jun Jet Tai and Hannah Tan and Omar G. Younis},
+      year={2024},
+      eprint={2407.17032},
+      archivePrefix={arXiv},
+      primaryClass={cs.LG},
+      url={https://arxiv.org/abs/2407.17032}, 
+}
+@inproceedings{Ansel_PyTorch_2_Faster_2024,
+author = {Ansel, Jason and Yang, Edward and He, Horace and Gimelshein, Natalia and Jain, Animesh and Voznesensky, Michael and Bao, Bin and Bell, Peter and Berard, David and Burovski, Evgeni and Chauhan, Geeta and Chourdia, Anjali and Constable, Will and Desmaison, Alban and DeVito, Zachary and Ellison, Elias and Feng, Will and Gong, Jiong and Gschwind, Michael and Hirsh, Brian and Huang, Sherlock and Kalambarkar, Kshiteej and Kirsch, Laurent and Lazos, Michael and Lezcano, Mario and Liang, Yanbo and Liang, Jason and Lu, Yinghai and Luk, CK and Maher, Bert and Pan, Yunjie and Puhrsch, Christian and Reso, Matthias and Saroufim, Mark and Siraichi, Marcos Yukio and Suk, Helen and Suo, Michael and Tillet, Phil and Wang, Eikan and Wang, Xiaodong and Wen, William and Zhang, Shunting and Zhao, Xu and Zhou, Keren and Zou, Richard and Mathews, Ajit and Chanan, Gregory and Wu, Peng and Chintala, Soumith},
+booktitle = {29th ACM International Conference on Architectural Support for Programming Languages and Operating Systems, Volume 2 (ASPLOS '24)},
+doi = {10.1145/3620665.3640366},
+month = apr,
+publisher = {ACM},
+title = {{PyTorch 2: Faster Machine Learning Through Dynamic Python Bytecode Transformation and Graph Compilation}},
+url = {https://pytorch.org/assets/pytorch2-2.pdf},
+year = {2024}
+}
+
+@article{stable-baselines3,
+  author  = {Antonin Raffin and Ashley Hill and Adam Gleave and Anssi Kanervisto and Maximilian Ernestus and Noah Dormann},
+  title   = {Stable-Baselines3: Reliable Reinforcement Learning Implementations},
+  journal = {Journal of Machine Learning Research},
+  year    = {2021},
+  volume  = {22},
+  number  = {268},
+  pages   = {1-8},
+  url     = {http://jmlr.org/papers/v22/20-1364.html}
+}
+
+@software{gymnax2022github,
+  author = {Robert Tjarko Lange},
+  title = {{gymnax}: A {JAX}-based Reinforcement Learning Environment Library},
+  url = {http://github.com/RobertTLange/gymnax},
+  version = {0.0.4},
+  year = {2022},
+}
+	@misc{proximalpolicyoptimization,
+      title={Proximal Policy Optimization Algorithms}, 
+      author={John Schulman and Filip Wolski and Prafulla Dhariwal and Alec Radford and Oleg Klimov},
+      year={2017},
+      eprint={1707.06347},
+      archivePrefix={arXiv},
+      primaryClass={cs.LG},
+      url={https://arxiv.org/abs/1707.06347}, 
+}
diff --git a/src/solarcarsim/cleanrl_td3_jax.py b/src/solarcarsim/cleanrl_td3_jax.py
deleted file mode 100644
index 82ed58d..0000000
--- a/src/solarcarsim/cleanrl_td3_jax.py
+++ /dev/null
@@ -1,368 +0,0 @@
-# docs and experiment results can be found at https://docs.cleanrl.dev/rl-algorithms/td3/#td3_continuous_action_jaxpy
-import os
-import random
-import time
-from dataclasses import dataclass
-
-import flax
-import flax.linen as nn
-import gymnasium as gym
-import jax
-import jax.numpy as jnp
-import numpy as np
-import optax
-import tyro
-from flax.training.train_state import TrainState
-from stable_baselines3.common.buffers import ReplayBuffer
-from torch.utils.tensorboard.writer import SummaryWriter
-
-
-@dataclass
-class Args:
-    exp_name: str = os.path.basename(__file__)[: -len(".py")]
-    """the name of this experiment"""
-    seed: int = 1
-    """seed of the experiment"""
-    track: bool = False
-    """if toggled, this experiment will be tracked with Weights and Biases"""
-    wandb_project_name: str = "cleanRL"
-    """the wandb's project name"""
-    wandb_entity: str = None
-    """the entity (team) of wandb's project"""
-    capture_video: bool = False
-    """whether to capture videos of the agent performances (check out `videos` folder)"""
-    save_model: bool = False
-    """whether to save model into the `runs/{run_name}` folder"""
-    upload_model: bool = False
-    """whether to upload the saved model to huggingface"""
-    hf_entity: str = ""
-    """the user or org name of the model repository from the Hugging Face Hub"""
-
-    # Algorithm specific arguments
-    env_id: str = "MountainCarContinuous-v0"
-    """the id of the environment"""
-    total_timesteps: int = 1000000
-    """total timesteps of the experiments"""
-    learning_rate: float = 3e-4
-    """the learning rate of the optimizer"""
-    buffer_size: int = int(1e6)
-    """the replay memory buffer size"""
-    gamma: float = 0.99
-    """the discount factor gamma"""
-    tau: float = 0.005
-    """target smoothing coefficient (default: 0.005)"""
-    batch_size: int = 256
-    """the batch size of sample from the reply memory"""
-    policy_noise: float = 0.2
-    """the scale of policy noise"""
-    exploration_noise: float = 0.1
-    """the scale of exploration noise"""
-    learning_starts: int = 25e3
-    """timestep to start learning"""
-    policy_frequency: int = 2
-    """the frequency of training policy (delayed)"""
-    noise_clip: float = 0.5
-    """noise clip parameter of the Target Policy Smoothing Regularization"""
-
-
-def make_env(env_id, seed, idx, capture_video, run_name):
-    def thunk():
-        if capture_video and idx == 0:
-            env = gym.make(env_id, render_mode="rgb_array")
-            env = gym.wrappers.RecordVideo(env, f"videos/{run_name}")
-        else:
-            env = gym.make(env_id)
-        env = gym.wrappers.RecordEpisodeStatistics(env)
-        env.action_space.seed(seed)
-        return env
-
-    return thunk
-
-
-# ALGO LOGIC: initialize agent here:
-class QNetwork(nn.Module):
-    @nn.compact
-    def __call__(self, x: jnp.ndarray, a: jnp.ndarray):
-        x = jnp.concatenate([x, a], -1)
-        x = nn.Dense(256)(x)
-        x = nn.relu(x)
-        x = nn.Dense(256)(x)
-        x = nn.relu(x)
-        x = nn.Dense(1)(x)
-        return x
-
-
-class Actor(nn.Module):
-    action_dim: int
-    action_scale: jnp.ndarray
-    action_bias: jnp.ndarray
-
-    @nn.compact
-    def __call__(self, x):
-        x = nn.Dense(256)(x)
-        x = nn.relu(x)
-        x = nn.Dense(256)(x)
-        x = nn.relu(x)
-        x = nn.Dense(self.action_dim)(x)
-        x = nn.tanh(x)
-        x = x * self.action_scale + self.action_bias
-        return x
-
-
-class TrainState(TrainState):
-    target_params: flax.core.FrozenDict
-
-
-if __name__ == "__main__":
-    import stable_baselines3 as sb3
-
-    if sb3.__version__ < "2.0":
-        raise ValueError(
-            """Ongoing migration: run the following command to install the new dependencies:
-poetry run pip install "stable_baselines3==2.0.0a1"
-"""
-        )
-    args = tyro.cli(Args)
-    run_name = f"{args.env_id}__{args.exp_name}__{args.seed}__{int(time.time())}"
-    if args.track:
-        import wandb
-
-        wandb.init(
-            project=args.wandb_project_name,
-            entity=args.wandb_entity,
-            sync_tensorboard=True,
-            config=vars(args),
-            name=run_name,
-            monitor_gym=True,
-            save_code=True,
-        )
-    writer = SummaryWriter(f"runs/{run_name}")
-    writer.add_text(
-        "hyperparameters",
-        "|param|value|\n|-|-|\n%s" % ("\n".join([f"|{key}|{value}|" for key, value in vars(args).items()])),
-    )
-
-    # TRY NOT TO MODIFY: seeding
-    random.seed(args.seed)
-    np.random.seed(args.seed)
-    key = jax.random.PRNGKey(args.seed)
-    key, actor_key, qf1_key, qf2_key = jax.random.split(key, 4)
-
-    # env setup
-    envs = gym.vector.SyncVectorEnv([make_env(args.env_id, args.seed, 0, args.capture_video, run_name)])
-    assert isinstance(envs.single_action_space, gym.spaces.Box), "only continuous action space is supported"
-
-    max_action = float(envs.single_action_space.high[0])
-    envs.single_observation_space.dtype = np.float32
-    rb = ReplayBuffer(
-        args.buffer_size,
-        envs.single_observation_space,
-        envs.single_action_space,
-        device="cpu",
-        handle_timeout_termination=False,
-    )
-
-    # TRY NOT TO MODIFY: start the game
-    obs, _ = envs.reset(seed=args.seed)
-
-    actor = Actor(
-        action_dim=np.prod(envs.single_action_space.shape),
-        action_scale=jnp.array((envs.action_space.high - envs.action_space.low) / 2.0),
-        action_bias=jnp.array((envs.action_space.high + envs.action_space.low) / 2.0),
-    )
-    actor_state = TrainState.create(
-        apply_fn=actor.apply,
-        params=actor.init(actor_key, obs),
-        target_params=actor.init(actor_key, obs),
-        tx=optax.adam(learning_rate=args.learning_rate),
-    )
-    qf = QNetwork()
-    qf1_state = TrainState.create(
-        apply_fn=qf.apply,
-        params=qf.init(qf1_key, obs, envs.action_space.sample()),
-        target_params=qf.init(qf1_key, obs, envs.action_space.sample()),
-        tx=optax.adam(learning_rate=args.learning_rate),
-    )
-    qf2_state = TrainState.create(
-        apply_fn=qf.apply,
-        params=qf.init(qf2_key, obs, envs.action_space.sample()),
-        target_params=qf.init(qf2_key, obs, envs.action_space.sample()),
-        tx=optax.adam(learning_rate=args.learning_rate),
-    )
-    actor.apply = jax.jit(actor.apply)
-    qf.apply = jax.jit(qf.apply)
-
-    @jax.jit
-    def update_critic(
-        actor_state: TrainState,
-        qf1_state: TrainState,
-        qf2_state: TrainState,
-        observations: np.ndarray,
-        actions: np.ndarray,
-        next_observations: np.ndarray,
-        rewards: np.ndarray,
-        terminations: np.ndarray,
-        key: jnp.ndarray,
-    ):
-        # TODO Maybe pre-generate a lot of random keys
-        # also check https://jax.readthedocs.io/en/latest/jax.random.html
-        key, noise_key = jax.random.split(key, 2)
-        clipped_noise = (
-            jnp.clip(
-                (jax.random.normal(noise_key, actions.shape) * args.policy_noise),
-                -args.noise_clip,
-                args.noise_clip,
-            )
-            * actor.action_scale
-        )
-        next_state_actions = jnp.clip(
-            actor.apply(actor_state.target_params, next_observations) + clipped_noise,
-            envs.single_action_space.low,
-            envs.single_action_space.high,
-        )
-        qf1_next_target = qf.apply(qf1_state.target_params, next_observations, next_state_actions).reshape(-1)
-        qf2_next_target = qf.apply(qf2_state.target_params, next_observations, next_state_actions).reshape(-1)
-        min_qf_next_target = jnp.minimum(qf1_next_target, qf2_next_target)
-        next_q_value = (rewards + (1 - terminations) * args.gamma * (min_qf_next_target)).reshape(-1)
-
-        def mse_loss(params):
-            qf_a_values = qf.apply(params, observations, actions).squeeze()
-            return ((qf_a_values - next_q_value) ** 2).mean(), qf_a_values.mean()
-
-        (qf1_loss_value, qf1_a_values), grads1 = jax.value_and_grad(mse_loss, has_aux=True)(qf1_state.params)
-        (qf2_loss_value, qf2_a_values), grads2 = jax.value_and_grad(mse_loss, has_aux=True)(qf2_state.params)
-        qf1_state = qf1_state.apply_gradients(grads=grads1)
-        qf2_state = qf2_state.apply_gradients(grads=grads2)
-
-        return (qf1_state, qf2_state), (qf1_loss_value, qf2_loss_value), (qf1_a_values, qf2_a_values), key
-
-    @jax.jit
-    def update_actor(
-        actor_state: TrainState,
-        qf1_state: TrainState,
-        qf2_state: TrainState,
-        observations: np.ndarray,
-    ):
-        def actor_loss(params):
-            return -qf.apply(qf1_state.params, observations, actor.apply(params, observations)).mean()
-
-        actor_loss_value, grads = jax.value_and_grad(actor_loss)(actor_state.params)
-        actor_state = actor_state.apply_gradients(grads=grads)
-        actor_state = actor_state.replace(
-            target_params=optax.incremental_update(actor_state.params, actor_state.target_params, args.tau)
-        )
-
-        qf1_state = qf1_state.replace(
-            target_params=optax.incremental_update(qf1_state.params, qf1_state.target_params, args.tau)
-        )
-        qf2_state = qf2_state.replace(
-            target_params=optax.incremental_update(qf2_state.params, qf2_state.target_params, args.tau)
-        )
-        return actor_state, (qf1_state, qf2_state), actor_loss_value
-
-    start_time = time.time()
-    for global_step in range(args.total_timesteps):
-        # ALGO LOGIC: put action logic here
-        if global_step < args.learning_starts:
-            actions = np.array([envs.single_action_space.sample() for _ in range(envs.num_envs)])
-        else:
-            actions = actor.apply(actor_state.params, obs)
-            actions = np.array(
-                [
-                    (
-                        jax.device_get(actions)[0]
-                        + np.random.normal(0, max_action * args.exploration_noise, size=envs.single_action_space.shape)
-                    ).clip(envs.single_action_space.low, envs.single_action_space.high)
-                ]
-            )
-
-        # TRY NOT TO MODIFY: execute the game and log data.
-        next_obs, rewards, terminations, truncations, infos = envs.step(actions)
-
-        # TRY NOT TO MODIFY: record rewards for plotting purposes
-        if "final_info" in infos:
-            for info in infos["final_info"]:
-                print(f"global_step={global_step}, episodic_return={info['episode']['r']}")
-                writer.add_scalar("charts/episodic_return", info["episode"]["r"], global_step)
-                writer.add_scalar("charts/episodic_length", info["episode"]["l"], global_step)
-                break
-
-        # TRY NOT TO MODIFY: save data to replay buffer; handle `final_observation`
-        real_next_obs = next_obs.copy()
-        for idx, trunc in enumerate(truncations):
-            if trunc:
-                real_next_obs[idx] = infos["final_observation"][idx]
-        rb.add(obs, real_next_obs, actions, rewards, terminations, infos)
-
-        # TRY NOT TO MODIFY: CRUCIAL step easy to overlook
-        obs = next_obs
-
-        # ALGO LOGIC: training.
-        if global_step > args.learning_starts:
-            data = rb.sample(args.batch_size)
-
-            (qf1_state, qf2_state), (qf1_loss_value, qf2_loss_value), (qf1_a_values, qf2_a_values), key = update_critic(
-                actor_state,
-                qf1_state,
-                qf2_state,
-                data.observations.numpy(),
-                data.actions.numpy(),
-                data.next_observations.numpy(),
-                data.rewards.flatten().numpy(),
-                data.dones.flatten().numpy(),
-                key,
-            )
-
-            if global_step % args.policy_frequency == 0:
-                actor_state, (qf1_state, qf2_state), actor_loss_value = update_actor(
-                    actor_state,
-                    qf1_state,
-                    qf2_state,
-                    data.observations.numpy(),
-                )
-
-            if global_step % 100 == 0:
-                writer.add_scalar("losses/qf1_loss", qf1_loss_value.item(), global_step)
-                writer.add_scalar("losses/qf2_loss", qf2_loss_value.item(), global_step)
-                writer.add_scalar("losses/qf1_values", qf1_a_values.item(), global_step)
-                writer.add_scalar("losses/qf2_values", qf2_a_values.item(), global_step)
-                writer.add_scalar("losses/actor_loss", actor_loss_value.item(), global_step)
-                print("SPS:", int(global_step / (time.time() - start_time)))
-                writer.add_scalar("charts/SPS", int(global_step / (time.time() - start_time)), global_step)
-
-    if args.save_model:
-        model_path = f"runs/{run_name}/{args.exp_name}.cleanrl_model"
-        with open(model_path, "wb") as f:
-            f.write(
-                flax.serialization.to_bytes(
-                    [
-                        actor_state.params,
-                        qf1_state.params,
-                        qf2_state.params,
-                    ]
-                )
-            )
-        print(f"model saved to {model_path}")
-        from cleanrl_utils.evals.td3_jax_eval import evaluate
-
-        episodic_returns = evaluate(
-            model_path,
-            make_env,
-            args.env_id,
-            eval_episodes=10,
-            run_name=f"{run_name}-eval",
-            Model=(Actor, QNetwork),
-            exploration_noise=args.exploration_noise,
-        )
-        for idx, episodic_return in enumerate(episodic_returns):
-            writer.add_scalar("eval/episodic_return", episodic_return, idx)
-
-        if args.upload_model:
-            from cleanrl_utils.huggingface import push_to_hub
-
-            repo_name = f"{args.env_id}-{args.exp_name}-seed{args.seed}"
-            repo_id = f"{args.hf_entity}/{repo_name}" if args.hf_entity else repo_name
-            push_to_hub(args, episodic_returns, repo_id, "TD3", f"runs/{run_name}", f"videos/{run_name}-eval")
-
-    envs.close()
-    writer.close()
\ No newline at end of file
diff --git a/src/solarcarsim/environments.py b/src/solarcarsim/environments.py
deleted file mode 100644
index ab7fffe..0000000
--- a/src/solarcarsim/environments.py
+++ /dev/null
@@ -1,121 +0,0 @@
-# models to generate different environments that the car can drive in.
-# This includes terrain, clouds, wind, solar conditions, and the route along the terrain.
-
-
-import jax
-import jax.numpy as jnp
-from jax import random
-import pyqtgraph as pg
-from functools import partial
-from pyqtgraph.Qt import QtCore, QtGui
-from typing import NamedTuple
-import matplotlib.pyplot as plt
-import sys
-
-
-class TerrainParams(NamedTuple):
-    size: int = 256
-    octaves: int = 6
-    persistence: float = 0.5
-    lacunarity: float = 2.0
-    seed: int = 42
-
-
-def lerp(a, b, t):
-    # assume a and b are pairs of numbers
-    x = jnp.array([0,1])
-    f = jnp.array([a,b])
-    return jnp.interp(t, x, f)
-
-# @partial(jax.jit, static_argnums=(2,))
-# def _make_noise_layer(key: random.PRNGKey, frequency: float, shape) -> jnp.ndarray:
-#
-#     noise = random.normal(key, shape)
-#     # create the grid.
-#     x = jnp.linspace(0, shape[0] - 1, )
-
-import jax
-import jax.numpy as jnp
-
-def generate_permutation():
-    """Generate a permutation table."""
-    p = jnp.arange(256, dtype=jnp.int32)
-    return jnp.concatenate([p, p])
-
-@jax.jit
-def fade(t):
-    """Fade function for smooth interpolation."""
-    return t * t * t * (t * (t * 6 - 15) + 10)
-
-@jax.jit
-def lerp(t, a, b):
-    """Linear interpolation."""
-    return a + t * (b - a)
-
-@jax.jit
-def grad(hash, x, y):
-    """Calculate gradient."""
-    h = hash & 15
-    grad_x = jnp.where(h < 8, x, y)
-    grad_y = jnp.where(h < 4, y, jnp.where((h == 12) | (h == 14), x, y))
-    return jnp.where(h & 1, -grad_x, grad_x) + jnp.where(h & 2, -grad_y, grad_y)
-
-def perlin(pos):
-    """ Perlin noise. Shape (N) where N = n_dims (2,3) """
-    
-    cellpos = pos % 1.0 # get the position inside the cell
-
-    upos = fade(pos)
-
-@jax.jit
-def perlin_noise_2d(x, y, p):
-    """Generate 2D Perlin noise value."""
-    # Floor coordinates
-    xi = jnp.floor(x).astype(jnp.int32) & 255
-    yi = jnp.floor(y).astype(jnp.int32) & 255
-    
-    # Fractional coordinates
-    xf = x - jnp.floor(x)
-    yf = y - jnp.floor(y)
-    
-    # Fade curves
-    u = fade(xf)
-    v = fade(yf)
-    
-    # Hash coordinates of cube corners
-    aa = p[p[xi] + yi]
-    ab = p[p[xi] + yi + 1]
-    ba = p[p[xi + 1] + yi]
-    bb = p[p[xi + 1] + yi + 1]
-    
-    # Gradients
-    g1 = grad(aa, xf, yf)
-    g2 = grad(ba, xf - 1, yf)
-    g3 = grad(ab, xf, yf - 1)
-    g4 = grad(bb, xf - 1, yf - 1)
-    
-    # Interpolate
-    x1 = lerp(u, g1, g2)
-    x2 = lerp(u, g3, g4)
-    return lerp(v, x1, x2)
-
-def generate_noise_grid(width, height, scale=50.0):
-    """Generate a grid of Perlin noise values."""
-    p = generate_permutation()
-    # compute the gradient grid.
-    gradgrid = 
-    x = jnp.linspace(0, width/scale, width)
-    y = jnp.linspace(0, height/scale, height)
-    X, Y = jnp.meshgrid(x, y)
-    return perlin_noise_2d(X, Y, p)
-
-# Example usage:
-key = jax.random.PRNGKey(23)
-noise = generate_noise_grid(256, 256)
-plt.imshow(noise)
-plt.savefig("output.png")
-
-
-def GymV1():
-    """ Makes a version 1 gym - simply an elevation profile. """
-
diff --git a/src/solarcarsim/main.py b/src/solarcarsim/main.py
index e69de29..b181518 100644
--- a/src/solarcarsim/main.py
+++ b/src/solarcarsim/main.py
@@ -0,0 +1,74 @@
+# start up the main Qt application and load plugins
+
+from PySide6.QtCore import QCoreApplication, QObject, Signal, QTimer
+from PySide6.QtWidgets import QApplication, QToolBar, QWidget, QMainWindow, QPlainTextEdit
+import logging
+from logging import LogRecord
+import sys
+from pyqtgraph.dockarea import Dock, DockArea
+
+from solarcarsim.satellaview.ui import SatellaUI
+
+
+class LogHandler(logging.Handler):
+    class Carrier(QObject):
+        # We need this because both QObject and logging.Handler need an `emit` method
+        # and they collide.
+        appendplaintext = Signal(str)
+
+    def __init__(self, parent) -> None:
+        super().__init__()
+        self.widget = QPlainTextEdit(parent=parent)
+        self.carrier = self.Carrier(parent=parent)
+        self.widget.setReadOnly(True)
+        self.carrier.appendplaintext.connect(self.widget.appendPlainText) # type: ignore
+        self.dock = Dock("Logger", widget=self.widget)
+
+    def emit(self, record: LogRecord) -> None:
+        msg = self.format(record)
+        self.carrier.appendplaintext.emit(msg) # type: ignore
+
+
+class LogViewer(QPlainTextEdit):
+    def __init__(self, parent=None):
+        super().__init__(parent)
+
+
+class App():
+    """ Core application. Sets up logger, main window (toolbar, etc),
+    and loads plugins."""
+    @staticmethod
+    def init_core_app():
+        # sets the name of the application etc
+        QCoreApplication.setApplicationName("SolarCarSim")
+
+    def __init__(self) -> None:
+        self.main_window = main = QMainWindow()
+        main.setWindowTitle("SolarCarSim")
+        self.dockarea = DockArea(main)
+
+        self.loghandler = LogHandler(main)
+        self.plugins = {}
+
+        self.plugins['satellaview'] = SatellaUI(main)
+        logging.getLogger().addHandler(self.loghandler)
+        
+        main.setCentralWidget(self.dockarea)
+        self.dockarea.addDock(self.loghandler.dock)
+        self.dockarea.addDock(self.plugins['satellaview'].dock)
+        self.file_menu = main.menuBar().addMenu("File")
+        self.settings_menu = main.menuBar().addMenu("Settings")
+
+
+
+    def run(self):
+        return self.main_window.show()
+    
+
+if __name__ == "__main__":
+    app = QApplication()
+    myapp = App()
+    myapp.run()
+    QTimer.singleShot(1000, lambda: logging.warning("logging test"))
+    QTimer.singleShot(2000, lambda: logging.warning("logging tes2332t"))
+    sys.exit(app.exec())
\ No newline at end of file
diff --git a/src/solarcarsim/plugin.py b/src/solarcarsim/plugin.py
new file mode 100644
index 0000000..f473a3f
--- /dev/null
+++ b/src/solarcarsim/plugin.py
@@ -0,0 +1,19 @@
+# Plugin base class
+
+
+
+class BasePlugin():
+    """ Base class for plugins. A plugin is a tool or feature that can extend the application.
+    All application features are implemented as a plugin. The list of plugins can be seen in the toolbar.
+    """
+    def name(self) -> str:
+        raise NotImplementedError()
+    
+    def version(self) -> str:
+        raise NotImplementedError()
+    
+    def startup(self, window, toolbar, application):
+        raise NotImplementedError()
+    
+    def teardown(self, window, toolbar, application):
+        raise NotImplementedError()
\ No newline at end of file
diff --git a/src/solarcarsim/satellaview/__init__.py b/src/solarcarsim/satellaview/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/src/solarcarsim/satellaview/goes.py b/src/solarcarsim/satellaview/goes.py
new file mode 100644
index 0000000..e69de29
diff --git a/src/solarcarsim/satellaview/himawari.py b/src/solarcarsim/satellaview/himawari.py
new file mode 100644
index 0000000..e69de29
diff --git a/src/solarcarsim/satellaview/rendering.py b/src/solarcarsim/satellaview/rendering.py
new file mode 100644
index 0000000..e69de29
diff --git a/src/solarcarsim/satellaview/source.py b/src/solarcarsim/satellaview/source.py
new file mode 100644
index 0000000..e69de29
diff --git a/src/solarcarsim/satellaview/ui.py b/src/solarcarsim/satellaview/ui.py
new file mode 100644
index 0000000..2367bb4
--- /dev/null
+++ b/src/solarcarsim/satellaview/ui.py
@@ -0,0 +1,36 @@
+# Qt widget to select/render satellite data.
+# this ties together the renderer and source plugins
+
+
+from PySide6.QtCore import QObject
+from PySide6.QtWidgets import QPushButton, QSplitter, QVBoxLayout, QWidget, QGridLayout
+from pyqtgraph import ImageView
+from pyqtgraph.dockarea import Dock
+from pyqtgraph.parametertree import ParameterTree
+
+class SatellaUI(QObject):
+
+    def __init__(self, parent = None) -> None:
+        super().__init__(parent)
+
+        self.splitter = split = QSplitter()
+
+
+        self.param_tree = ParameterTree(split)
+
+        self.run_button = QPushButton("Execute", split)
+
+        split.addWidget(self.param_tree)
+        split.addWidget(self.run_button)
+
+        self.viewer = ImageView(split, "Data")
+
+        split.addWidget(self.viewer)
+
+        self._dock = None
+    @property
+    def dock(self):
+        if self._dock is not None:
+            return self._dock
+        self._dock = Dock("Satellaview", widget=self.splitter)
+        return self._dock
\ No newline at end of file
diff --git a/src/solarcarsim/simulator/__init__.py b/src/solarcarsim/simulator/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/src/solarcarsim/simulator/main.py b/src/solarcarsim/simulator/main.py
new file mode 100644
index 0000000..e69de29
diff --git a/src/solarcarsim/noise.py b/src/solarcarsim/simulator/noise.py
similarity index 100%
rename from src/solarcarsim/noise.py
rename to src/solarcarsim/simulator/noise.py
diff --git a/src/solarcarsim/perlin.py b/src/solarcarsim/simulator/perlin.py
similarity index 100%
rename from src/solarcarsim/perlin.py
rename to src/solarcarsim/simulator/perlin.py
diff --git a/src/solarcarsim/physsim.py b/src/solarcarsim/simulator/physsim.py
similarity index 99%
rename from src/solarcarsim/physsim.py
rename to src/solarcarsim/simulator/physsim.py
index c9a2da8..39fdd1c 100644
--- a/src/solarcarsim/physsim.py
+++ b/src/solarcarsim/simulator/physsim.py
@@ -7,7 +7,7 @@ from functools import partial
 
 from typing import NamedTuple, Tuple
 
-from solarcarsim.noise import (
+from .noise import (
     fractal_noise_1d,
     generate_wind_field,
 )
diff --git a/src/solarcarsim/simv1.py b/src/solarcarsim/simulator/simv1.py
similarity index 99%
rename from src/solarcarsim/simv1.py
rename to src/solarcarsim/simulator/simv1.py
index 5444b94..65a9bbd 100644
--- a/src/solarcarsim/simv1.py
+++ b/src/solarcarsim/simulator/simv1.py
@@ -1,5 +1,5 @@
 import gymnasium as gym
-import solarcarsim.physsim as sim
+import solarcarsim.simulator.physsim as sim
 import jax
 import jax.numpy as jnp
 from jax import jit
diff --git a/src/solarcarsim/simv2.py b/src/solarcarsim/simulator/simv2.py
similarity index 98%
rename from src/solarcarsim/simv2.py
rename to src/solarcarsim/simulator/simv2.py
index a79320b..ff03fe3 100644
--- a/src/solarcarsim/simv2.py
+++ b/src/solarcarsim/simulator/simv2.py
@@ -9,8 +9,8 @@ from jax import lax, vmap
 from gymnax.environments import environment
 from gymnax.environments import spaces
 
-from solarcarsim.physsim import CarParams, fractal_noise_1d
-import solarcarsim.physsim as sim
+from solarcarsim.simulator.physsim import CarParams, fractal_noise_1d
+import solarcarsim.simulator.physsim as sim
 
 
 @struct.dataclass
diff --git a/src/solarcarsim/vis.py b/src/solarcarsim/vis.py
deleted file mode 100644
index bb1776c..0000000
--- a/src/solarcarsim/vis.py
+++ /dev/null
@@ -1 +0,0 @@
-import pyray as ray
\ No newline at end of file