Repository: OCHA-DAP/ocha-anticipy (language: python)

Issue:
Split up dependencies into extras
Some of the data sources have unique dependencies (e.g. GloFAS with CDS API and GRIB), these should be split up under `extras_require` in `setup.cfg` so that they don't need to be installed by default
Add easy HRP countries
Here are the "easy" HRP countries (without strange COD AB issues mentioned [here](https://github.com/OCHA-DAP/ocha-anticipy/issues/181). 

Opening as a draft because I still have to test everything myself!


Discussion / hints:

Fix patch:
diff --git a/.gitignore b/.gitignore
index bf3bbcaf..904f68d0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -69,9 +69,9 @@ instance/
 .scrapy
 
 # Sphinx documentation
-docs/build/
-docs/source/modules.rst
-docs/source/ochanticipy*.rst
+docs/_build/
+docs/modules.rst
+docs/ochanticipy*.rst
 
 # PyBuilder
 target/
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 00631deb..fb6913fb 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -46,24 +46,11 @@ repos:
     hooks:
       - id: markdownlint
   - repo: https://github.com/jazzband/pip-tools
-    rev: 6.12.2
+    rev: 6.13.0
     hooks:
       - id: pip-compile
         name: pip-compile requirements.txt
         files: setup.cfg
-        args: [setup.cfg, -q, -o, requirements/requirements.txt]
-      - id: pip-compile
-        name: pip-compile requirements-docs.txt
-        files: requirements/docs.in
-        args: [ requirements/docs.in, -q, -o,
-                requirements/requirements-docs.txt ]
-      - id: pip-compile
-        name: pip-compile requirements-tests.txt
-        files: requirements/tests.in
-        args: [requirements/tests.in, -q, -o,
-               requirements/requirements-tests.txt]
-      - id: pip-compile
-        name: pip-compile requirements-dev.txt
-        files: requirements/(dev.in|docs.in|tests.in|requirements.txt)
-        args: [requirements/dev.in, -q, -o,
-               requirements/requirements-dev.txt]
+        args: [setup.cfg, --resolver=backtracking,
+              --upgrade, --extra, dev, -q, -o,
+              requirements.txt]
diff --git a/.readthedocs.yaml b/.readthedocs.yaml
index 7131157e..44837ebc 100644
--- a/.readthedocs.yaml
+++ b/.readthedocs.yaml
@@ -11,10 +11,11 @@ build:
     python: "3.11"
 
 sphinx:
-   configuration: docs/source/conf.py
+   configuration: docs/conf.py
 
 python:
   install:
-    - requirements: requirements/requirements-docs.txt
     - method: pip
       path: .
+      extra_requirements:
+        - docs
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 8d6e993c..a845d4f3 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -8,6 +8,31 @@ Changelog <https://keepachangelog.com/en/1.0.0/>`__, and this project
 adheres to `Semantic
 Versioning <https://semver.org/spec/v2.0.0.html>`__.
 
+[1.1.0] - 2023-05-30
+--------------------
+
+Added
+~~~~~
+
+- Config files for 25/25 HRP countries and several non-HRP countries
+
+Changed
+~~~~~~~
+
+- Optional dependencies and documentation, testing, and development
+  reqirements moved to ``extras_require``
+- GloFAS module dependencies made optional
+- tox configuration moved to ``setup.cfg`` from ``tox.ini``
+
+Fixed
+~~~~~
+
+- Automatic raster dimension setting for `lat/lon` and `X/Y`
+  fixed to work for `rioxarray` methods
+- USGS NDVI documentation was updated to remove dead links
+  and improve users ability to use the historical data
+- GloFAS does not permit users to go above request limit of 500
+
 [1.0.2] - 2023-05-29
 --------------------
 
diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst
index a3eacb20..b1b794fe 100644
--- a/CONTRIBUTING.rst
+++ b/CONTRIBUTING.rst
@@ -12,14 +12,14 @@ environment such as ``venv``:
     python3.11 -m venv venv
     source venv/bin/activate
 
-In your virtual environment, please install all packages from
-``requirements/requirements-dev.txt``:
+In your virtual environment, please install all packages for
+development by running:
 
 .. code:: shell
 
-   pip install -r requirements/requirements-dev.txt
+   pip install -r requirements.txt
 
-OCHA AnticiPy makes use of
+``aa-toolbox`` makes use of
 `geopandas <https://geopandas.org/en/stable/>`__, which depends on
 `Fiona <https://github.com/Toblerity/Fiona>`__, so you will need to
 have `GDAL <https://github.com/Toblerity/Fiona#installation>`__
@@ -28,7 +28,7 @@ installed.
 Installation
 ------------
 
-To install in editable mode for development, execute:
+To install ``ocha-anticipy`` in editable mode for development, execute:
 
 .. code:: shell
 
@@ -103,9 +103,9 @@ To build the documentation and test your implementation, use the following comma
 
 .. code:: shell
 
-   sphinx-build -b html -d docs/build/doctrees docs/source docs/build/html
+   sphinx-build -b html -d docs/_build/doctrees docs docs/_build/html
 
-To view the docs, open up ``docs/build/html/index.html`` in your
+To view the docs, open up ``docs-build/html/index.html`` in your
 browser.
 
 pre-commit
@@ -133,21 +133,41 @@ package management.
 
 If you’ve introduced a new package to the source code (i.e. anywhere in
 ``src/``), please add it to the ``install_requires`` section of
-``setup.cfg`` with any known version constraints. For adding packages
-for development, documentation, or tests, add them to the relevant
-``.in`` file in the ``requirements`` directory. When you modify any of
-these lists, please try to keep them alphabetical! Any changes to the
-``requirements*.txt`` files will be generated with ``pre-commit``.
+``setup.cfg`` with any known version constraints. However, in the
+case where a library is only required for a single data source,
+these should be added in the ``options.extras_require`` section
+of ``setup.cfg``, with a relevant name for the requirements list.
+For example, the GLOFAS module relies on the ``cdsapi`` and
+``cfgrib`` packages, and would look like.
 
-To run this without commiting, execute:
+.. code::
+
+   [options.extras_require]
+   glofas =
+      cdsapi
+      cfgrib
+
+Also add reference to the subpackage under ``full`` using the
+``%(subpackage)s`` format.
+
+.. code::
+
+   full =
+      %(glofas)s
+
+For adding packages for testing, documentation, or development, add them to
+the relevant subpackage under ``[options.extras_require]``, ``test``,
+``doc``, and ``dev`` respectively. When you modify any of
+these lists, please try to keep them alphabetical!
+
+Any changes to the dependencies will be automatically reflected in
+``requirements.txt`` with ``pre-commit``, but you can re-generate
+the file without commiting by executing:
 
 .. code:: shell
 
    pre-commit run pip-compile --all-files
 
-For other functionality such as updating specific package versions,
-refer to the ``pip-tools`` documentation.
-
 Package Release
 ---------------
 
diff --git a/README.md b/README.md
index 072b5e41..6cd946f3 100644
--- a/README.md
+++ b/README.md
@@ -33,6 +33,14 @@ Install and update using [pip](https://pip.pypa.io/en/stable/getting-started/):
 pip install ocha-anticipy
 ```
 
+Some modules of the toolbox have specific dependencies not installed by default.
+These are documented within each module, but all dependencies can be installed
+using.
+
+```shell
+pip install -U ocha-anticipy[full]
+```
+
 ## A Simple Example
 
 OCHA AnticiPy downloads data to the directory referenced by the
diff --git a/docs/source/api.rst b/docs/api.rst
similarity index 93%
rename from docs/source/api.rst
rename to docs/api.rst
index 42714eba..61c49160 100644
--- a/docs/source/api.rst
+++ b/docs/api.rst
@@ -46,6 +46,9 @@ FEWS NET
 GloFAS
 ^^^^^^
 
+.. extras-require:: glofas
+    :setup.cfg:
+
 .. automodule:: ochanticipy.datasources.glofas.glofas
 
 Reanalysis
@@ -130,13 +133,13 @@ Raster module
 Data arrays
 """""""""""
 
-.. autoclass:: ochanticipy.utils.raster.AatRasterArray
+.. autoclass:: ochanticipy.utils.raster.OapRasterArray
    :members:
    :inherited-members: RasterArray
 
 Datasets
 """"""""
 
-.. autoclass:: ochanticipy.utils.raster.AatRasterDataset
+.. autoclass:: ochanticipy.utils.raster.OapRasterDataset
    :members:
    :inherited-members: RasterDataset
diff --git a/docs/changelog.rst b/docs/changelog.rst
new file mode 100644
index 00000000..565b0521
--- /dev/null
+++ b/docs/changelog.rst
@@ -0,0 +1,1 @@
+.. include:: ../CHANGELOG.rst
diff --git a/docs/source/conf.py b/docs/conf.py
similarity index 89%
rename from docs/source/conf.py
rename to docs/conf.py
index 9c8c769e..74b8457c 100644
--- a/docs/source/conf.py
+++ b/docs/conf.py
@@ -13,7 +13,7 @@
 import os
 import sys
 
-sys.path.insert(0, os.path.abspath("../.."))
+sys.path.insert(0, os.path.abspath(".."))
 
 # -- Project information -----------------------------------------------------
 
@@ -31,6 +31,7 @@
     "sphinx.ext.napoleon",
     "sphinx.ext.viewcode",
     "sphinxcontrib.apidoc",
+    "sphinxcontrib.extras_require",
 ]
 
 # Add any paths that contain templates here, relative to this directory.
@@ -42,9 +43,13 @@
 exclude_patterns = []  # type: list
 
 # API doc settings
-apidoc_module_dir = "../../src/ochanticipy"
+apidoc_module_dir = "../src/ochanticipy"
 apidoc_output_dir = "."
 
+# Needed to sphinxcontrib.extra_requires to work
+package_root = "ochanticipy"
+pypi_name = "ocha-anticipy"
+
 # -- Options for HTML output -------------------------------------------------
 
 # The theme to use for HTML and HTML Help pages.  See the documentation for
diff --git a/docs/source/config.rst b/docs/config.rst
similarity index 70%
rename from docs/source/config.rst
rename to docs/config.rst
index b552ee33..596acf2e 100644
--- a/docs/source/config.rst
+++ b/docs/config.rst
@@ -14,12 +14,38 @@ Several countries are already supported out-of-the box,
 and we are continuously adding more.
 
 .. _list of supported countries:
-A list of countries that we currently support:
 
+We currently support all 25 humanitarian response plan (HRP) countries:
+
+- Afghanistan (AFG)
 - Burkina Faso (BFA)
-- Bangladesh (BGD)
+- Burundi (BDI)
+- Cameroon (CMR)
+- Central African Republic (CAF)
+- Chad (TCD)
+- Colombia (COL)
 - Democratic Republic of the Congo (COD)
 - Ethiopia (ETH)
+- Haiti (HTI)
+- Iraq (IRQ)
+- Libya (LBY)
+- Mali (MLI)
+- Mozambique (MOZ)
+- Myanmar (MMR)
+- Niger (NER)
+- Nigeria (NGA)
+- Somalia (SOM)
+- South Sudan (SSD)
+- State of Palestine (PSE)
+- Sudan (SDN)
+- Syrian Arab Republic (SYR)
+- Ukraine (UKR)
+- Venezuela (VEN)
+- Yemen (YEM)
+
+In addition we support several non-HRP countries:
+
+- Bangladesh (BGD)
 - Malawi (MWI)
 - Nepal (NPL)
 
@@ -38,6 +64,16 @@ for Nepal, which has the ISO3 "NPL":
     from ochanticipy import create_country_config
     country_config = create_country_config(iso3="npl")
 
+
+Unavailable Data
+----------------
+
+For certain countries, data from a specific source may be unavailable. For the HRP countries,
+Afghanistan and Myanmar do not have COD AB configurations, as Afghanistan's COD AB shapefiles
+are private and Myanmar does not have COD AB files. In these cases, you will need to manually
+load and use other files.
+
+
 Custom Country Configuration
 ----------------------------
 
@@ -58,6 +94,7 @@ The YAML file will be validated and used to create the country configuration.
 At minimum, it must contain the country ISO3:
 
 .. code-block:: yaml
+
     iso3: "npl"
 
 For additional datasource-specific requirements, please see the
diff --git a/docs/contributing.rst b/docs/contributing.rst
new file mode 100644
index 00000000..e582053e
--- /dev/null
+++ b/docs/contributing.rst
@@ -0,0 +1,1 @@
+.. include:: ../CONTRIBUTING.rst
diff --git a/docs/source/datasources.rst b/docs/datasources.rst
similarity index 100%
rename from docs/source/datasources.rst
rename to docs/datasources.rst
diff --git a/docs/source/datasources/chirps.rst b/docs/datasources/chirps.rst
similarity index 100%
rename from docs/source/datasources/chirps.rst
rename to docs/datasources/chirps.rst
diff --git a/docs/source/datasources/codab.rst b/docs/datasources/codab.rst
similarity index 53%
rename from docs/source/datasources/codab.rst
rename to docs/datasources/codab.rst
index fa238517..c95a8435 100644
--- a/docs/source/datasources/codab.rst
+++ b/docs/datasources/codab.rst
@@ -25,7 +25,7 @@ for the country you would like to use:
 .. code-block:: python
 
     from ochanticipy import create_country_config
-    country_config = create_country_config(iso3="npl")
+    country_config = create_country_config(iso3="nga")
 
 Next you need to instantiate the CodAB class with the country config:
 
@@ -45,26 +45,26 @@ GeoPandas dataframe:
 
 .. code-block:: python
 
-    npl_admin1 = codab.load(admin_level=1)
+    nga_admin1 = codab.load(admin_level=1)
 
 Some COD AB files have additional layers that don't correspond to
-an admin level. For example, Nepal has a districts layer, which
+an admin level. For example, Nigeria has a districts layer, which
 is provided in the config file as the first custom layer:
 
 .. code-block:: python
 
-    npl_districts = codab.load_custom(custom_layer_number=0)
+    nga_districts = codab.load_custom(custom_layer_number=0)
 
 The full code snippet is below in case you would like to copy it:
 
 .. code-block:: python
 
     from ochanticipy import create_country_config, CodAB
-    country_config = create_country_config(iso3="npl")
+    country_config = create_country_config(iso3="nga")
     codab = CodAB(country_config=country_config)
     codab.download()
-    npl_admin1 = codab.load(admin_level=1)
-    npl_districts = codab.load_custom(custom_layer_number=0)
+    nga_admin1 = codab.load(admin_level=1)
+    nga_districts = codab.load_custom(custom_layer_number=0)
 
 Configuration
 -------------
@@ -75,35 +75,39 @@ should be setup as follows:
 .. code-block:: yaml
 
     codab:
-      hdx_dataset_name: npl_admbnda_nd_20201117_SHP.zip
-      layer_base_name: npl_admbnda_adm{admin_level}_nd_20201117.shp
-      admin_level_max: 2
-      admin2_name: npl_admbnda_adm2_nd.shp
-      custom_layer_names:
-        - npl_admbnda_districts_nd_20201117
+        hdx_resource_name:
+            - nga_adm_osgof_20190417.zip
+            - nga_additional.zip # contrived example for documentation
+        layer_base_name: nga_admbnda_adm{admin_level}_osgof_20190417.shp
+        admin_level_max: 3
+        adm1_name: nga_adm1.shp # contrived example for documentation
+        custom_layer_names:
+            - nga_admbnda_senDist_inec_osgof_20190417.shp
 
 Below is an explanation of the different parameters:
 
-``hdx_dataset_name``: The name of the shapefile dataset on HDX. It can be found by taking
+``hdx_resource_name``: The name of the shapefile resource on HDX. It can be found by taking
 the filename as it appears on the HDX page. For example, you can see on the
-`page for Nepal <https://data.humdata.org/dataset/cod-ab-npl>`_ that the shapefile
-(i.e. with the ``.shp``. or ``.SHP`` extension) has the name
-``npl_admbnda_nd_20201117_SHP.zip``.
+`page for Nigeria <https://data.humdata.org/dataset/cod-ab-nga>`_ that the shapefile
+(sometimes with the ``.shp``. or ``.SHP`` extension, this time not) has the name
+``nga_adm_osgof_20190417.zip``. If admin levels are stored in different resources, which is
+the case for some countries like Niger, then each individual resource should should be listed
+under ``hdx_resource_name``. The format is shown above as an example to follow.
 
 ``layer_base_name``: The baseline name of the different admin level layers, with the
 level number replaced by the variable ``{admin_level}``. To find this, you will need
 to open up the shpaefile in e.g. `QGIS <https://www.qgis.org/en/site/>`_.
-In the case of Nepal, the layers have the names ``npl_admbnda_adm0_nd_20201117.shp``,
-``npl_admbnda_adm1_nd_20201117.shp``, and ``npl_admbnda_adm2_nd_20201117.shp``
+In the case of Nigeria, the layers have the names ``nga_admbnda_adm0_osgof_20190417.shp``,
+``nga_admbnda_adm1_osgof_20190417.shp``, and ``nga_admbnda_adm2_osgof_20190417.shp``
 
-``admin_level_max``: The maximum admin level available in the layers. In the case of Nepal,
-the layer level numbers range from 0 to 2, so the maximum should be 2. In general the
-maximum admin level should not exceed 4.
+``admin_level_max``: The maximum admin level available in the layers. In the case of Nigeria,
+the layer level numbers range from 0 to 3, so the maximum should be 3. In general the
+ maximum admin level should not exceed 4.
 
 ``admin{level}_name``: An optional parameter for any admin level (``level`` can range from 0 to 4)
-whose layer names do not match the ``layer_base_name`` pattern. This example for Nepal
-is contrived, but this issue does exist for COD ABs from countries such as Ethiopia and DRC.
+whose layer names do not match the ``layer_base_name`` pattern. This example for Nigeria
+ is contrived, but this issue does exist for COD ABs from countries such as Ethiopia and DRC.
 
-``custom_layer_name``: An optional place to list any other layers that don't correspond to the
-admin level format specified above. In the case of Nepal, there is a layer for districts
-with the name ``npl_admbnda_districts_nd_20201117``.
+``custom_layer_name``: A place to list any other layers that don't correspond to the
+admin level format specified above. In the case of Nigeria, there is a layer for districts
+with the name ``nga_admbnda_senDist_inec_osgof_20190417.shp``.
diff --git a/docs/source/datasources/fewsnet.rst b/docs/datasources/fewsnet.rst
similarity index 100%
rename from docs/source/datasources/fewsnet.rst
rename to docs/datasources/fewsnet.rst
diff --git a/docs/source/datasources/glofas.rst b/docs/datasources/glofas.rst
similarity index 99%
rename from docs/source/datasources/glofas.rst
rename to docs/datasources/glofas.rst
index 8079ffdb..3b80fa58 100644
--- a/docs/source/datasources/glofas.rst
+++ b/docs/datasources/glofas.rst
@@ -1,6 +1,9 @@
 GloFAS
 ======
 
+.. extras-require:: glofas
+    :setup.cfg:
+
 Background
 ----------
 
@@ -185,6 +188,7 @@ ecCodes can be installed using `brew`:
 
     brew install eccodes
 
+
 Reporting points
 ~~~~~~~~~~~~~~~~
 
@@ -336,6 +340,7 @@ The full codde snippet is below:
     from ochanticipy import create_country_config, CodAB, \
         GeoBoundingBox, GlofasForecast
 
+    country_config = create_country_config(iso3="bgd")
     codab = CodAB(country_config=country_config)
     codab.download()
     admin0 = codab.load()
diff --git a/docs/source/datasources/iri_seasonal_forecast.rst b/docs/datasources/iri_seasonal_forecast.rst
similarity index 100%
rename from docs/source/datasources/iri_seasonal_forecast.rst
rename to docs/datasources/iri_seasonal_forecast.rst
diff --git a/docs/source/datasources/usgs_ndvi.rst b/docs/datasources/usgs_ndvi.rst
similarity index 76%
rename from docs/source/datasources/usgs_ndvi.rst
rename to docs/datasources/usgs_ndvi.rst
index f2e05479..4b84ad20 100644
--- a/docs/source/datasources/usgs_ndvi.rst
+++ b/docs/datasources/usgs_ndvi.rst
@@ -6,8 +6,12 @@ Warning
 
 The MODIS sensor has been reported by USGS to
 have degraded in quality since May 2022 (dekad 13), and
-updates to this data source have stopped. This module
-remains for users to have access to historic data but
+updates to this data source have stopped. Documentation of
+the product has been removed from the
+`USGS FEWS product page <https://earlywarning.usgs.gov/fews/product>`_,
+which has made methodological details and geographic links unavailable.
+
+This module remains for users to have access to historic data but
 recent data is unavailable and care should be used
 analyzing any data since dekad 13 of 2022.
 
@@ -29,18 +33,17 @@ difference vegetation index (`NDVI <https://en.wikipedia.org/wiki/Normalized_dif
 generated from eMODIS AQUA provided by the USGS.
 
 NDVI data is generated from eMODIS AQUA and published data includes temporally smoothed NDVI, median anomaly,
-difference from the previous year, and median anomaly presented as a percentile. Full methodological
-details are available on the `Documentation page <https://earlywarning.usgs.gov/fews/product/449#documentation>`_
-for the specific coverage area. The data is made available for the following areas of coverage:
-
-- `north-africa <https://earlywarning.usgs.gov/fews/product/449>`_
-- `east-africa <https://earlywarning.usgs.gov/fews/product/448>`_
-- `southern-africa <https://earlywarning.usgs.gov/fews/product/450>`_
-- `west-africa <https://earlywarning.usgs.gov/fews/product/451>`_
-- `central-asia <https://earlywarning.usgs.gov/fews/product/493>`_
-- `yemen <https://earlywarning.usgs.gov/fews/product/502>`_
-- `central-america <https://earlywarning.usgs.gov/fews/product/445>`_
-- `hispaniola <https://earlywarning.usgs.gov/fews/product/446>`_
+difference from the previous year, and median anomaly presented as a percentile.
+The data is made available for the following areas of coverage:
+
+- north-africa
+- east-africa
+- southern-africa
+- west-africa
+- central-asia
+- yemen
+- central-america
+- hispaniola
 
 Data is published at the dekadal level, and is released soon after the end of the dekad.
 After a period of 3 dekads, data is updated with temporal smoothing and error correction
@@ -48,9 +51,10 @@ for cloud cover. Files for a specific dekad and region can range from 30MB up to
 so downloading and processing can take a long time.
 
 
-USGS publishes its data through its `web portal <https://earlywarning.usgs.gov/fews/datadownloads/East%20Africa/eMODIS%20NDVI%20C6>`_,
-which allows downloading data for a single geographical area and dekad. This data
-is extracted from the `back end data explorer <https://edcintl.cr.usgs.gov/downloads/sciweb1/shared/fews/web/africa/east/dekadal/emodis/ndvi_c6/temporallysmoothedndvi/downloads/monthly/>`_.
+USGS published its data through its web portal,
+which allows downloading data for a single geographical area and dekad. However, the data
+is no longer made available there, and the data for this product are extracted
+from the `back end data explorer <https://edcintl.cr.usgs.gov/downloads/sciweb1/shared/fews/web/africa/east/dekadal/emodis/ndvi_c6/temporallysmoothedndvi/downloads/monthly/>`_.
 This module is designed to allow programmatic access to and analysis of NDVI data from the data explorer.
 
 Usage
@@ -58,9 +62,10 @@ Usage
 
 To use this class, you first need to create a country configuration
 for the country you would like to analyze. You also need to supply the area
-of coverage that the country data is contained in. You can Make sure FEWS NET covers the country
-of interest. You can find area of coverage for NDVI by searching for eMODIS NDVI C6 on
-the  `USGS search portal <https://earlywarning.usgs.gov/fews/search>`_.
+of coverage that the country data is contained in. Although the NDVI data
+are no longer available directly on the portal, you can derive the region
+name for the data using the primary
+`USGS FEWS data portal <https://earlywarning.usgs.gov/fews>`_.
 The valid values of the area names are listed above under Background.
 
 An example country config for Ethiopia is:
diff --git a/docs/source/index.rst b/docs/index.rst
similarity index 100%
rename from docs/source/index.rst
rename to docs/index.rst
diff --git a/docs/source/introduction.rst b/docs/introduction.rst
similarity index 81%
rename from docs/source/introduction.rst
rename to docs/introduction.rst
index e7933ab9..2c2cba4a 100644
--- a/docs/source/introduction.rst
+++ b/docs/introduction.rst
@@ -14,12 +14,12 @@ disaster risk reduction in humanitarian response.
 
 Datasets currently supported are:
 
-- CHIRPS rainfall
-- COD ABs (Common Operational Datasets administrative boundaries)
-- FEWS NET food insecurity
-- GloFAS river discharge
-- IRI seasonal rainfall forecast
-- USGS NDVI (normalized difference vegetation index)
+ - CHIRPS rainfall
+ - COD ABs (Common Operational Datasets administrative boundaries)
+ - FEWS NET food insecurity
+ - GloFAS river discharge
+ - IRI seasonal rainfall forecast
+ - USGS NDVI (normalized difference vegetation index)
 
 At the `Centre for Humanitarian Data <https://centre.humdata.org/>`_,
 we've now used OCHA Anticipy to develop
diff --git a/docs/source/license.rst b/docs/license.rst
similarity index 52%
rename from docs/source/license.rst
rename to docs/license.rst
index 58430fc0..cce9f6aa 100644
--- a/docs/source/license.rst
+++ b/docs/license.rst
@@ -1,5 +1,5 @@
 License
 =======
 
-.. include:: ../../LICENSE
+.. include:: ../LICENSE
    :literal:
diff --git a/docs/source/quickstart.rst b/docs/quickstart.rst
similarity index 82%
rename from docs/source/quickstart.rst
rename to docs/quickstart.rst
index b43e7c71..f4b902f8 100644
--- a/docs/source/quickstart.rst
+++ b/docs/quickstart.rst
@@ -15,7 +15,7 @@ sure that this environment variable is defined and points to where you would
 like the data to go.
 
 Install OCHA AnticiPy
-^^^^^^^^^^^^^^^^^^
+^^^^^^^^^^^^^^^^^^^^^
 
 OCHA AnticiPy supports Python 3.8 and newer. It can be installed in your Python
 environment using the following command:
@@ -26,6 +26,24 @@ environment using the following command:
 
 OCHA AnticiPy should now be installed.
 
+Optional dependencies
+*********************
+
+Some modules have optional dependencies that are not installed by default. Currently,
+this is just `glofas`. Optional dependencies will be included in the install if you
+specify the module as in the below command:
+
+.. code-block:: sh
+
+    $ pip install ocha-anticipy[glofas]
+
+Alternatively, you can install all requirements for all modules by specifying
+`full`:
+
+.. code-block:: sh
+
+    $ pip install ocha-anticipy[full]
+
 Usage Examples
 --------------
 
diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst
deleted file mode 100644
index 09929fe4..00000000
--- a/docs/source/changelog.rst
+++ /dev/null
@@ -1,1 +0,0 @@
-.. include:: ../../CHANGELOG.rst
diff --git a/docs/source/contributing.rst b/docs/source/contributing.rst
deleted file mode 100644
index ac7b6bcf..00000000
--- a/docs/source/contributing.rst
+++ /dev/null
@@ -1,1 +0,0 @@
-.. include:: ../../CONTRIBUTING.rst
diff --git a/docs/source/utilities.rst b/docs/utilities.rst
similarity index 100%
rename from docs/source/utilities.rst
rename to docs/utilities.rst
diff --git a/docs/source/utilities/raster.rst b/docs/utilities/raster.rst
similarity index 100%
rename from docs/source/utilities/raster.rst
rename to docs/utilities/raster.rst
diff --git a/requirements/requirements.txt b/requirements.txt
similarity index 50%
rename from requirements/requirements.txt
rename to requirements.txt
index 4d9d3c77..9a6e0332 100644
--- a/requirements/requirements.txt
+++ b/requirements.txt
@@ -2,13 +2,19 @@
 # This file is autogenerated by pip-compile with Python 3.11
 # by the following command:
 #
-#    pip-compile --output-file=requirements/requirements.txt setup.cfg
+#    pip-compile --extra=dev --output-file=requirements.txt --resolver=backtracking setup.cfg
 #
 affine==2.4.0
     # via rasterio
-appdirs==1.4.4
-    # via requests-cache
-attrs==22.2.0
+alabaster==0.7.13
+    # via sphinx
+apeye==1.3.0
+    # via
+    #   shippinglabel
+    #   sphinx-toolbox
+apeye-core==1.1.2
+    # via apeye
+attrs==23.1.0
     # via
     #   cattrs
     #   cfgrib
@@ -19,13 +25,23 @@ attrs==22.2.0
     #   jsonschema
     #   rasterio
     #   requests-cache
-bokeh==2.4.3
+autodocsumm==0.2.11
+    # via sphinx-toolbox
+babel==2.12.1
+    # via sphinx
+beautifulsoup4==4.12.2
+    # via sphinx-toolbox
+bokeh==3.1.1
     # via dask
+cachecontrol[filecache]==0.12.11
+    # via sphinx-toolbox
+cachetools==5.3.1
+    # via tox
 cattrs==22.2.0
     # via requests-cache
-cdsapi==0.5.1
+cdsapi==0.6.1
     # via ocha-anticipy (setup.cfg)
-certifi==2022.12.7
+certifi==2023.5.7
     # via
     #   fiona
     #   pyproj
@@ -35,15 +51,19 @@ cffi==1.15.1
     # via
     #   cryptography
     #   eccodes
-cfgrib==0.9.10.3
+cfgrib==0.9.10.4
     # via ocha-anticipy (setup.cfg)
+cfgv==3.3.1
+    # via pre-commit
 cftime==1.6.2
     # via
     #   netcdf4
     #   ocha-anticipy (setup.cfg)
 chardet==5.1.0
-    # via frictionless
-charset-normalizer==3.0.1
+    # via
+    #   frictionless
+    #   tox
+charset-normalizer==3.1.0
     # via requests
 ckanapi==4.7
     # via hdx-python-api
@@ -70,12 +90,18 @@ cloudpickle==2.2.1
     #   dask
     #   distributed
 colorama==0.4.6
-    # via typer
-commonmark==0.9.1
-    # via rich
-cryptography==39.0.0
+    # via
+    #   tox
+    #   typer
+contourpy==1.0.7
+    # via bokeh
+coverage[toml]==7.2.6
+    # via pytest-cov
+cryptography==40.0.2
     # via pyopenssl
-dask[complete]==2023.1.1
+cssutils==2.6.0
+    # via dict2css
+dask[array,complete,dataframe,diagnostics,distributed]==2023.5.1
     # via
     #   distributed
     #   xarray
@@ -85,7 +111,13 @@ decorator==5.1.1
     #   validators
 defopt==6.4.0
     # via hdx-python-api
-distributed==2023.1.1
+dict2css==0.3.0
+    # via sphinx-toolbox
+dist-meta==0.8.0
+    # via shippinglabel
+distlib==0.3.6
+    # via virtualenv
+distributed==2023.5.1
     # via dask
 dnspython==2.3.0
     # via email-validator
@@ -93,44 +125,79 @@ docopt==0.6.2
     # via
     #   ckanapi
     #   num2words
-docutils==0.19
-    # via defopt
-eccodes==1.5.1
+docutils==0.18.1
+    # via
+    #   defopt
+    #   extras-require
+    #   sphinx
+    #   sphinx-rtd-theme
+    #   sphinx-tabs
+    #   sphinx-toolbox
+dom-toml==0.6.1
+    # via shippinglabel
+domdf-python-tools==3.6.1
+    # via
+    #   apeye
+    #   apeye-core
+    #   dict2css
+    #   dist-meta
+    #   dom-toml
+    #   extras-require
+    #   shippinglabel
+    #   sphinx-toolbox
+eccodes==1.5.2
     # via cfgrib
-email-validator==1.3.1
+email-validator==2.0.0.post2
     # via hdx-python-api
 et-xmlfile==1.1.0
     # via openpyxl
-findlibs==0.0.2
+extras-require==0.5.0
+    # via ocha-anticipy (setup.cfg)
+filelock==3.12.0
+    # via
+    #   tox
+    #   virtualenv
+findlibs==0.0.5
     # via eccodes
-fiona==1.9.0
+fiona==1.9.4.post1
     # via geopandas
-frictionless==5.5.3
+frictionless==5.13.1
     # via hdx-python-utilities
-fsspec==2023.1.0
+fsspec==2023.5.0
     # via dask
-geopandas==0.12.2
+geopandas==0.13.0
     # via ocha-anticipy (setup.cfg)
-hdx-python-api==5.9.7
+handy-archives==0.1.4
+    # via dist-meta
+hdx-python-api==6.0.0
     # via ocha-anticipy (setup.cfg)
-hdx-python-country==3.4.6
+hdx-python-country==3.4.8
     # via
     #   hdx-python-api
     #   ocha-anticipy (setup.cfg)
-hdx-python-utilities==3.5.5
+hdx-python-utilities==3.5.8
     # via hdx-python-country
-heapdict==1.0.1
-    # via zict
-humanize==4.5.0
+html5lib==1.1
+    # via sphinx-toolbox
+humanize==4.6.0
     # via frictionless
+identify==2.5.24
+    # via pre-commit
 idna==3.4
     # via
+    #   apeye-core
     #   email-validator
     #   requests
 ijson==3.2.0.post0
     # via hdx-python-utilities
-inflect==6.0.2
+imagesize==1.4.1
+    # via sphinx
+importlib-metadata==6.6.0
+    # via dask
+inflect==6.0.4
     # via quantulum3
+iniconfig==2.0.0
+    # via pytest
 isodate==0.6.1
     # via frictionless
 jinja2==3.1.2
@@ -139,6 +206,8 @@ jinja2==3.1.2
     #   dask
     #   distributed
     #   frictionless
+    #   sphinx
+    #   sphinx-jinja2-compat
 jsonlines==3.1.0
     # via hdx-python-utilities
 jsonpath-ng==1.5.3
@@ -147,87 +216,135 @@ jsonschema==4.17.3
     # via
     #   frictionless
     #   tableschema-to-template
-libhxl==4.27.3
+libhxl==5.0
     # via hdx-python-country
 locket==1.0.0
     # via
     #   distributed
     #   partd
-loguru==0.6.0
+lockfile==0.12.2
+    # via
+    #   cachecontrol
+    #   sphinx-toolbox
+loguru==0.7.0
     # via hdx-python-utilities
-makefun==1.15.0
+lz4==4.3.2
+    # via dask
+makefun==1.15.1
     # via hdx-python-api
+markdown-it-py==2.2.0
+    # via rich
 marko==1.3.0
     # via frictionless
 markupsafe==2.1.2
-    # via jinja2
-msgpack==1.0.4
-    # via distributed
-munch==2.5.0
-    # via fiona
+    # via
+    #   jinja2
+    #   sphinx-jinja2-compat
+mdurl==0.1.2
+    # via markdown-it-py
+msgpack==1.0.5
+    # via
+    #   cachecontrol
+    #   distributed
+natsort==8.3.1
+    # via domdf-python-tools
 ndg-httpsclient==0.5.1
     # via hdx-python-api
-netcdf4==1.6.2
+netcdf4==1.6.3
     # via ocha-anticipy (setup.cfg)
+nodeenv==1.8.0
+    # via pre-commit
 num2words==0.5.12
     # via quantulum3
-numpy==1.24.1
+numpy==1.24.3
     # via
     #   bokeh
     #   cfgrib
     #   cftime
+    #   contourpy
     #   dask
     #   eccodes
     #   netcdf4
     #   ocha-anticipy (setup.cfg)
     #   pandas
+    #   pyarrow
     #   rasterio
+    #   rioxarray
     #   shapely
     #   snuggs
     #   xarray
-openpyxl==3.1.0
+openpyxl==3.1.2
     # via hdx-python-utilities
-packaging==23.0
+packaging==23.1
     # via
     #   bokeh
     #   dask
+    #   dist-meta
     #   distributed
+    #   extras-require
     #   geopandas
+    #   pyproject-api
+    #   pytest
     #   rioxarray
+    #   shippinglabel
+    #   sphinx
+    #   tox
     #   xarray
-pandas==1.5.3
+pandas==2.0.2
     # via
+    #   bokeh
     #   dask
     #   geopandas
     #   xarray
-partd==1.3.0
+partd==1.4.0
     # via dask
+pbr==5.11.1
+    # via sphinxcontrib-apidoc
 petl==1.7.12
     # via frictionless
-pillow==9.4.0
+pillow==9.5.0
     # via bokeh
+platformdirs==3.5.1
+    # via
+    #   apeye
+    #   requests-cache
+    #   shippinglabel
+    #   tox
+    #   virtualenv
+pluggy==1.0.0
+    # via
+    #   pytest
+    #   tox
 ply==3.11
     # via
     #   jsonpath-ng
     #   libhxl
 pockets==0.9.1
     # via sphinxcontrib-napoleon
-psutil==5.9.4
+pre-commit==3.3.2
+    # via ocha-anticipy (setup.cfg)
+psutil==5.9.5
     # via distributed
-pyasn1==0.4.8
+pyarrow==12.0.0
+    # via dask
+pyasn1==0.5.0
     # via
     #   hdx-python-api
     #   ndg-httpsclient
 pycparser==2.21
     # via cffi
-pydantic==1.10.4
+pydantic==1.10.8
     # via
     #   frictionless
     #   inflect
     #   ocha-anticipy (setup.cfg)
-pygments==2.14.0
-    # via rich
-pyopenssl==23.0.0
+pygments==2.15.1
+    # via
+    #   rich
+    #   sphinx
+    #   sphinx-prompt
+    #   sphinx-tabs
+pyopenssl==23.1.1
     # via
     #   hdx-python-api
     #   ndg-httpsclient
@@ -235,12 +352,24 @@ pyparsing==3.0.9
     # via snuggs
 pyphonetics==0.5.3
     # via hdx-python-country
-pyproj==3.4.1
+pyproj==3.5.0
     # via
     #   geopandas
     #   rioxarray
+pyproject-api==1.5.1
+    # via tox
 pyrsistent==0.19.3
     # via jsonschema
+pytest==7.3.1
+    # via
+    #   libhxl
+    #   ocha-anticipy (setup.cfg)
+    #   pytest-cov
+    #   pytest-mock
+pytest-cov==4.1.0
+    # via ocha-anticipy (setup.cfg)
+pytest-mock==3.10.0
+    # via ocha-anticipy (setup.cfg)
 python-dateutil==2.8.2
     # via
     #   frictionless
@@ -250,11 +379,11 @@ python-dateutil==2.8.2
     #   pandas
 python-io-wrapper==0.3.1
     # via libhxl
-python-slugify==8.0.0
+python-slugify==8.0.1
     # via
     #   ckanapi
     #   frictionless
-pytz==2022.7.1
+pytz==2023.3
     # via pandas
 pyyaml==6.0
     # via
@@ -263,17 +392,20 @@ pyyaml==6.0
     #   distributed
     #   frictionless
     #   ocha-anticipy (setup.cfg)
+    #   pre-commit
     #   tableschema-to-template
-quantulum3==0.8.1
+quantulum3==0.9.0
     # via hdx-python-api
-rasterio==1.3.5
+rasterio==1.3.7
     # via
     #   ocha-anticipy (setup.cfg)
     #   rioxarray
 ratelimit==2.2.1
     # via hdx-python-utilities
-requests==2.28.2
+requests==2.31.0
     # via
+    #   apeye
+    #   cachecontrol
     #   cdsapi
     #   ckanapi
     #   frictionless
@@ -281,78 +413,143 @@ requests==2.28.2
     #   ocha-anticipy (setup.cfg)
     #   requests-cache
     #   requests-file
-requests-cache==0.9.8
+    #   sphinx
+requests-cache==1.0.1
     # via libhxl
 requests-file==1.5.1
     # via hdx-python-utilities
 rfc3986==2.0.0
     # via frictionless
-rich==12.6.0
+rich==13.3.5
     # via typer
-rioxarray==0.13.3
+rioxarray==0.14.1
     # via ocha-anticipy (setup.cfg)
-ruamel-yaml==0.17.21
-    # via hdx-python-utilities
+ruamel-yaml==0.17.28
+    # via
+    #   hdx-python-utilities
+    #   sphinx-toolbox
+ruamel-yaml-clib==0.2.7
+    # via ruamel-yaml
 shapely==2.0.1
     # via geopandas
 shellingham==1.5.0.post1
     # via typer
-simpleeval==0.9.12
+shippinglabel==1.5.0
+    # via extras-require
+simpleeval==0.9.13
     # via frictionless
 six==1.16.0
     # via
     #   ckanapi
+    #   fiona
+    #   html5lib
     #   isodate
     #   jsonpath-ng
-    #   munch
     #   pockets
     #   python-dateutil
     #   requests-file
     #   sphinxcontrib-napoleon
     #   url-normalize
+snowballstemmer==2.2.0
+    # via sphinx
 snuggs==1.4.7
     # via rasterio
 sortedcontainers==2.4.0
     # via distributed
+soupsieve==2.4.1
+    # via beautifulsoup4
+sphinx==6.2.1
+    # via
+    #   autodocsumm
+    #   extras-require
+    #   ocha-anticipy (setup.cfg)
+    #   sphinx-autodoc-typehints
+    #   sphinx-prompt
+    #   sphinx-rtd-theme
+    #   sphinx-tabs
+    #   sphinx-toolbox
+    #   sphinxcontrib-apidoc
+    #   sphinxcontrib-jquery
+sphinx-autodoc-typehints==1.23.0
+    # via sphinx-toolbox
+sphinx-jinja2-compat==0.2.0
+    # via sphinx-toolbox
+sphinx-prompt==1.5.0
+    # via
+    #   extras-require
+    #   sphinx-toolbox
+sphinx-rtd-theme==1.2.1
+    # via ocha-anticipy (setup.cfg)
+sphinx-tabs==3.4.1
+    # via sphinx-toolbox
+sphinx-toolbox==3.4.0
+    # via extras-require
+sphinxcontrib-apidoc==0.3.0
+    # via ocha-anticipy (setup.cfg)
+sphinxcontrib-applehelp==1.0.4
+    # via sphinx
+sphinxcontrib-devhelp==1.0.2
+    # via sphinx
+sphinxcontrib-htmlhelp==2.0.1
+    # via sphinx
+sphinxcontrib-jquery==4.1
+    # via sphinx-rtd-theme
+sphinxcontrib-jsmath==1.0.1
+    # via sphinx
 sphinxcontrib-napoleon==0.7
     # via defopt
+sphinxcontrib-qthelp==1.0.3
+    # via sphinx
+sphinxcontrib-serializinghtml==1.1.5
+    # via sphinx
 stringcase==1.2.0
     # via frictionless
-structlog==22.1.0
+structlog==23.1.0
     # via libhxl
 tableschema-to-template==0.0.13
     # via hdx-python-utilities
 tabulate==0.9.0
-    # via frictionless
+    # via
+    #   frictionless
+    #   sphinx-toolbox
 tblib==1.7.0
     # via distributed
 text-unidecode==1.3
     # via python-slugify
+toml==0.10.2
+    # via dom-toml
 toolz==0.12.0
     # via
     #   dask
     #   distributed
     #   partd
-tornado==6.2
+tornado==6.3.2
     # via
     #   bokeh
     #   distributed
-tqdm==4.64.1
+tox==4.5.2
+    # via ocha-anticipy (setup.cfg)
+tqdm==4.65.0
     # via cdsapi
-typer[all]==0.7.0
+typer[all]==0.9.0
     # via frictionless
-typing-extensions==4.4.0
+typing-extensions==4.6.2
     # via
-    #   bokeh
+    #   domdf-python-tools
     #   frictionless
     #   pydantic
+    #   shippinglabel
+    #   sphinx-toolbox
+    #   typer
+tzdata==2023.3
+    # via pandas
 unidecode==1.3.6
     # via
     #   libhxl
     #   pyphonetics
 url-normalize==1.4.3
     # via requests-cache
-urllib3==1.26.14
+urllib3==1.26.16
     # via
     #   distributed
     #   libhxl
@@ -360,11 +557,17 @@ urllib3==1.26.14
     #   requests-cache
 validators==0.20.0
     # via frictionless
-wheel==0.38.4
+virtualenv==20.23.0
+    # via
+    #   pre-commit
+    #   tox
+webencodings==0.5.1
+    # via html5lib
+wheel==0.40.0
     # via libhxl
-wrapt==1.14.1
+wrapt==1.15.0
     # via ocha-anticipy (setup.cfg)
-xarray[parallel]==2023.1.0
+xarray[parallel]==2023.5.0
     # via
     #   ocha-anticipy (setup.cfg)
     #   rioxarray
@@ -372,12 +575,16 @@ xlrd==2.0.1
     # via hdx-python-utilities
 xlrd3==1.1.0
     # via libhxl
-xlsxwriter==3.0.8
+xlsxwriter==3.1.2
     # via tableschema-to-template
 xlwt==1.3.0
     # via hdx-python-utilities
-zict==2.2.0
+xyzservices==2023.5.0
+    # via bokeh
+zict==3.0.0
     # via distributed
+zipp==3.15.0
+    # via importlib-metadata
 
 # The following packages are considered to be unsafe in a requirements file:
 # setuptools
diff --git a/requirements/dev.in b/requirements/dev.in
deleted file mode 100644
index 5666eb11..00000000
--- a/requirements/dev.in
+++ /dev/null
@@ -1,5 +0,0 @@
--r requirements.txt
--r requirements-docs.txt
--r requirements-tests.txt
-pip-tools
-pre-commit
diff --git a/requirements/docs.in b/requirements/docs.in
deleted file mode 100644
index 58060d65..00000000
--- a/requirements/docs.in
+++ /dev/null
@@ -1,2 +0,0 @@
-sphinx
-sphinxcontrib-apidoc
diff --git a/requirements/requirements-dev.txt b/requirements/requirements-dev.txt
deleted file mode 100644
index 15339a20..00000000
--- a/requirements/requirements-dev.txt
+++ /dev/null
@@ -1,721 +0,0 @@
-#
-# This file is autogenerated by pip-compile with Python 3.11
-# by the following command:
-#
-#    pip-compile --output-file=requirements/requirements-dev.txt requirements/dev.in
-#
-affine==2.4.0
-    # via
-    #   -r requirements/requirements.txt
-    #   rasterio
-alabaster==0.7.13
-    # via
-    #   -r requirements/requirements-docs.txt
-    #   sphinx
-appdirs==1.4.4
-    # via
-    #   -r requirements/requirements.txt
-    #   requests-cache
-attrs==22.2.0
-    # via
-    #   -r requirements/requirements-tests.txt
-    #   -r requirements/requirements.txt
-    #   cattrs
-    #   cfgrib
-    #   eccodes
-    #   fiona
-    #   frictionless
-    #   jsonlines
-    #   jsonschema
-    #   pytest
-    #   rasterio
-    #   requests-cache
-babel==2.11.0
-    # via
-    #   -r requirements/requirements-docs.txt
-    #   sphinx
-bokeh==2.4.3
-    # via
-    #   -r requirements/requirements.txt
-    #   dask
-build==0.10.0
-    # via pip-tools
-cachetools==5.3.0
-    # via
-    #   -r requirements/requirements-tests.txt
-    #   tox
-cattrs==22.2.0
-    # via
-    #   -r requirements/requirements.txt
-    #   requests-cache
-cdsapi==0.5.1
-    # via -r requirements/requirements.txt
-certifi==2022.12.7
-    # via
-    #   -r requirements/requirements-docs.txt
-    #   -r requirements/requirements.txt
-    #   fiona
-    #   pyproj
-    #   rasterio
-    #   requests
-cffi==1.15.1
-    # via
-    #   -r requirements/requirements.txt
-    #   cryptography
-    #   eccodes
-cfgrib==0.9.10.3
-    # via -r requirements/requirements.txt
-cfgv==3.3.1
-    # via pre-commit
-cftime==1.6.2
-    # via
-    #   -r requirements/requirements-tests.txt
-    #   -r requirements/requirements.txt
-    #   netcdf4
-chardet==5.1.0
-    # via
-    #   -r requirements/requirements-tests.txt
-    #   -r requirements/requirements.txt
-    #   frictionless
-    #   tox
-charset-normalizer==3.0.1
-    # via
-    #   -r requirements/requirements-docs.txt
-    #   -r requirements/requirements.txt
-    #   requests
-ckanapi==4.7
-    # via
-    #   -r requirements/requirements.txt
-    #   hdx-python-api
-click==8.1.3
-    # via
-    #   -r requirements/requirements.txt
-    #   cfgrib
-    #   click-plugins
-    #   cligj
-    #   dask
-    #   distributed
-    #   fiona
-    #   pip-tools
-    #   rasterio
-    #   typer
-click-plugins==1.1.1
-    # via
-    #   -r requirements/requirements.txt
-    #   fiona
-    #   rasterio
-cligj==0.7.2
-    # via
-    #   -r requirements/requirements.txt
-    #   fiona
-    #   rasterio
-cloudpickle==2.2.1
-    # via
-    #   -r requirements/requirements.txt
-    #   dask
-    #   distributed
-colorama==0.4.6
-    # via
-    #   -r requirements/requirements-tests.txt
-    #   -r requirements/requirements.txt
-    #   tox
-    #   typer
-commonmark==0.9.1
-    # via
-    #   -r requirements/requirements.txt
-    #   rich
-coverage[toml]==7.1.0
-    # via
-    #   -r requirements/requirements-tests.txt
-    #   pytest-cov
-cryptography==39.0.0
-    # via
-    #   -r requirements/requirements.txt
-    #   pyopenssl
-dask[complete]==2023.1.1
-    # via
-    #   -r requirements/requirements.txt
-    #   distributed
-    #   xarray
-decorator==5.1.1
-    # via
-    #   -r requirements/requirements.txt
-    #   jsonpath-ng
-    #   validators
-defopt==6.4.0
-    # via
-    #   -r requirements/requirements.txt
-    #   hdx-python-api
-distlib==0.3.6
-    # via
-    #   -r requirements/requirements-tests.txt
-    #   virtualenv
-distributed==2023.1.1
-    # via
-    #   -r requirements/requirements.txt
-    #   dask
-dnspython==2.3.0
-    # via
-    #   -r requirements/requirements.txt
-    #   email-validator
-docopt==0.6.2
-    # via
-    #   -r requirements/requirements.txt
-    #   ckanapi
-    #   num2words
-docutils==0.19
-    # via
-    #   -r requirements/requirements-docs.txt
-    #   -r requirements/requirements.txt
-    #   defopt
-    #   sphinx
-eccodes==1.5.1
-    # via
-    #   -r requirements/requirements.txt
-    #   cfgrib
-email-validator==1.3.1
-    # via
-    #   -r requirements/requirements.txt
-    #   hdx-python-api
-et-xmlfile==1.1.0
-    # via
-    #   -r requirements/requirements.txt
-    #   openpyxl
-filelock==3.9.0
-    # via
-    #   -r requirements/requirements-tests.txt
-    #   tox
-    #   virtualenv
-findlibs==0.0.2
-    # via
-    #   -r requirements/requirements.txt
-    #   eccodes
-fiona==1.9.0
-    # via
-    #   -r requirements/requirements.txt
-    #   geopandas
-frictionless==5.5.3
-    # via
-    #   -r requirements/requirements.txt
-    #   hdx-python-utilities
-fsspec==2023.1.0
-    # via
-    #   -r requirements/requirements.txt
-    #   dask
-geopandas==0.12.2
-    # via -r requirements/requirements.txt
-hdx-python-api==5.9.7
-    # via -r requirements/requirements.txt
-hdx-python-country==3.4.6
-    # via
-    #   -r requirements/requirements.txt
-    #   hdx-python-api
-hdx-python-utilities==3.5.5
-    # via
-    #   -r requirements/requirements.txt
-    #   hdx-python-country
-heapdict==1.0.1
-    # via
-    #   -r requirements/requirements.txt
-    #   zict
-humanize==4.5.0
-    # via
-    #   -r requirements/requirements.txt
-    #   frictionless
-identify==2.5.17
-    # via pre-commit
-idna==3.4
-    # via
-    #   -r requirements/requirements-docs.txt
-    #   -r requirements/requirements.txt
-    #   email-validator
-    #   requests
-ijson==3.2.0.post0
-    # via
-    #   -r requirements/requirements.txt
-    #   hdx-python-utilities
-imagesize==1.4.1
-    # via
-    #   -r requirements/requirements-docs.txt
-    #   sphinx
-inflect==6.0.2
-    # via
-    #   -r requirements/requirements.txt
-    #   quantulum3
-iniconfig==2.0.0
-    # via
-    #   -r requirements/requirements-tests.txt
-    #   pytest
-isodate==0.6.1
-    # via
-    #   -r requirements/requirements.txt
-    #   frictionless
-jinja2==3.1.2
-    # via
-    #   -r requirements/requirements-docs.txt
-    #   -r requirements/requirements.txt
-    #   bokeh
-    #   dask
-    #   distributed
-    #   frictionless
-    #   sphinx
-jsonlines==3.1.0
-    # via
-    #   -r requirements/requirements.txt
-    #   hdx-python-utilities
-jsonpath-ng==1.5.3
-    # via
-    #   -r requirements/requirements.txt
-    #   libhxl
-jsonschema==4.17.3
-    # via
-    #   -r requirements/requirements.txt
-    #   frictionless
-    #   tableschema-to-template
-libhxl==4.27.3
-    # via
-    #   -r requirements/requirements.txt
-    #   hdx-python-country
-locket==1.0.0
-    # via
-    #   -r requirements/requirements.txt
-    #   distributed
-    #   partd
-loguru==0.6.0
-    # via
-    #   -r requirements/requirements.txt
-    #   hdx-python-utilities
-makefun==1.15.0
-    # via
-    #   -r requirements/requirements.txt
-    #   hdx-python-api
-marko==1.3.0
-    # via
-    #   -r requirements/requirements.txt
-    #   frictionless
-markupsafe==2.1.2
-    # via
-    #   -r requirements/requirements-docs.txt
-    #   -r requirements/requirements.txt
-    #   jinja2
-msgpack==1.0.4
-    # via
-    #   -r requirements/requirements.txt
-    #   distributed
-munch==2.5.0
-    # via
-    #   -r requirements/requirements.txt
-    #   fiona
-ndg-httpsclient==0.5.1
-    # via
-    #   -r requirements/requirements.txt
-    #   hdx-python-api
-netcdf4==1.6.2
-    # via
-    #   -r requirements/requirements-tests.txt
-    #   -r requirements/requirements.txt
-nodeenv==1.7.0
-    # via pre-commit
-num2words==0.5.12
-    # via
-    #   -r requirements/requirements.txt
-    #   quantulum3
-numpy==1.24.1
-    # via
-    #   -r requirements/requirements-tests.txt
-    #   -r requirements/requirements.txt
-    #   bokeh
-    #   cfgrib
-    #   cftime
-    #   dask
-    #   eccodes
-    #   netcdf4
-    #   pandas
-    #   rasterio
-    #   shapely
-    #   snuggs
-    #   xarray
-openpyxl==3.1.0
-    # via
-    #   -r requirements/requirements.txt
-    #   hdx-python-utilities
-packaging==23.0
-    # via
-    #   -r requirements/requirements-docs.txt
-    #   -r requirements/requirements-tests.txt
-    #   -r requirements/requirements.txt
-    #   bokeh
-    #   build
-    #   dask
-    #   distributed
-    #   geopandas
-    #   pyproject-api
-    #   pytest
-    #   rioxarray
-    #   sphinx
-    #   tox
-    #   xarray
-pandas==1.5.3
-    # via
-    #   -r requirements/requirements.txt
-    #   dask
-    #   geopandas
-    #   xarray
-partd==1.3.0
-    # via
-    #   -r requirements/requirements.txt
-    #   dask
-pbr==5.11.1
-    # via
-    #   -r requirements/requirements-docs.txt
-    #   sphinxcontrib-apidoc
-petl==1.7.12
-    # via
-    #   -r requirements/requirements.txt
-    #   frictionless
-pillow==9.4.0
-    # via
-    #   -r requirements/requirements.txt
-    #   bokeh
-pip-tools==6.12.2
-    # via -r requirements/dev.in
-platformdirs==2.6.2
-    # via
-    #   -r requirements/requirements-tests.txt
-    #   tox
-    #   virtualenv
-pluggy==1.0.0
-    # via
-    #   -r requirements/requirements-tests.txt
-    #   pytest
-    #   tox
-ply==3.11
-    # via
-    #   -r requirements/requirements.txt
-    #   jsonpath-ng
-    #   libhxl
-pockets==0.9.1
-    # via
-    #   -r requirements/requirements.txt
-    #   sphinxcontrib-napoleon
-pre-commit==3.0.3
-    # via -r requirements/dev.in
-psutil==5.9.4
-    # via
-    #   -r requirements/requirements.txt
-    #   distributed
-pyasn1==0.4.8
-    # via
-    #   -r requirements/requirements.txt
-    #   hdx-python-api
-    #   ndg-httpsclient
-pycparser==2.21
-    # via
-    #   -r requirements/requirements.txt
-    #   cffi
-pydantic==1.10.4
-    # via
-    #   -r requirements/requirements.txt
-    #   frictionless
-    #   inflect
-pygments==2.14.0
-    # via
-    #   -r requirements/requirements-docs.txt
-    #   -r requirements/requirements.txt
-    #   rich
-    #   sphinx
-pyopenssl==23.0.0
-    # via
-    #   -r requirements/requirements.txt
-    #   hdx-python-api
-    #   ndg-httpsclient
-pyparsing==3.0.9
-    # via
-    #   -r requirements/requirements.txt
-    #   snuggs
-pyphonetics==0.5.3
-    # via
-    #   -r requirements/requirements.txt
-    #   hdx-python-country
-pyproj==3.4.1
-    # via
-    #   -r requirements/requirements.txt
-    #   geopandas
-    #   rioxarray
-pyproject-api==1.5.0
-    # via
-    #   -r requirements/requirements-tests.txt
-    #   tox
-pyproject-hooks==1.0.0
-    # via build
-pyrsistent==0.19.3
-    # via
-    #   -r requirements/requirements.txt
-    #   jsonschema
-pytest==7.2.1
-    # via
-    #   -r requirements/requirements-tests.txt
-    #   pytest-cov
-    #   pytest-mock
-pytest-cov==4.0.0
-    # via -r requirements/requirements-tests.txt
-pytest-mock==3.10.0
-    # via -r requirements/requirements-tests.txt
-python-dateutil==2.8.2
-    # via
-    #   -r requirements/requirements.txt
-    #   frictionless
-    #   hdx-python-utilities
-    #   libhxl
-    #   pandas
-python-io-wrapper==0.3.1
-    # via
-    #   -r requirements/requirements.txt
-    #   libhxl
-python-slugify==8.0.0
-    # via
-    #   -r requirements/requirements.txt
-    #   ckanapi
-    #   frictionless
-pytz==2022.7.1
-    # via
-    #   -r requirements/requirements-docs.txt
-    #   -r requirements/requirements.txt
-    #   babel
-    #   pandas
-pyyaml==6.0
-    # via
-    #   -r requirements/requirements.txt
-    #   bokeh
-    #   dask
-    #   distributed
-    #   frictionless
-    #   pre-commit
-    #   tableschema-to-template
-quantulum3==0.8.1
-    # via
-    #   -r requirements/requirements.txt
-    #   hdx-python-api
-rasterio==1.3.5
-    # via
-    #   -r requirements/requirements.txt
-    #   rioxarray
-ratelimit==2.2.1
-    # via
-    #   -r requirements/requirements.txt
-    #   hdx-python-utilities
-requests==2.28.2
-    # via
-    #   -r requirements/requirements-docs.txt
-    #   -r requirements/requirements.txt
-    #   cdsapi
-    #   ckanapi
-    #   frictionless
-    #   libhxl
-    #   requests-cache
-    #   requests-file
-    #   sphinx
-requests-cache==0.9.8
-    # via
-    #   -r requirements/requirements.txt
-    #   libhxl
-requests-file==1.5.1
-    # via
-    #   -r requirements/requirements.txt
-    #   hdx-python-utilities
-rfc3986==2.0.0
-    # via
-    #   -r requirements/requirements.txt
-    #   frictionless
-rich==12.6.0
-    # via
-    #   -r requirements/requirements.txt
-    #   typer
-rioxarray==0.13.3
-    # via -r requirements/requirements.txt
-ruamel-yaml==0.17.21
-    # via
-    #   -r requirements/requirements.txt
-    #   hdx-python-utilities
-shapely==2.0.1
-    # via
-    #   -r requirements/requirements.txt
-    #   geopandas
-shellingham==1.5.0.post1
-    # via
-    #   -r requirements/requirements.txt
-    #   typer
-simpleeval==0.9.12
-    # via
-    #   -r requirements/requirements.txt
-    #   frictionless
-six==1.16.0
-    # via
-    #   -r requirements/requirements.txt
-    #   ckanapi
-    #   isodate
-    #   jsonpath-ng
-    #   munch
-    #   pockets
-    #   python-dateutil
-    #   requests-file
-    #   sphinxcontrib-napoleon
-    #   url-normalize
-snowballstemmer==2.2.0
-    # via
-    #   -r requirements/requirements-docs.txt
-    #   sphinx
-snuggs==1.4.7
-    # via
-    #   -r requirements/requirements.txt
-    #   rasterio
-sortedcontainers==2.4.0
-    # via
-    #   -r requirements/requirements.txt
-    #   distributed
-sphinx==6.1.3
-    # via
-    #   -r requirements/requirements-docs.txt
-    #   sphinxcontrib-apidoc
-sphinxcontrib-apidoc==0.3.0
-    # via -r requirements/requirements-docs.txt
-sphinxcontrib-applehelp==1.0.4
-    # via
-    #   -r requirements/requirements-docs.txt
-    #   sphinx
-sphinxcontrib-devhelp==1.0.2
-    # via
-    #   -r requirements/requirements-docs.txt
-    #   sphinx
-sphinxcontrib-htmlhelp==2.0.1
-    # via
-    #   -r requirements/requirements-docs.txt
-    #   sphinx
-sphinxcontrib-jsmath==1.0.1
-    # via
-    #   -r requirements/requirements-docs.txt
-    #   sphinx
-sphinxcontrib-napoleon==0.7
-    # via
-    #   -r requirements/requirements.txt
-    #   defopt
-sphinxcontrib-qthelp==1.0.3
-    # via
-    #   -r requirements/requirements-docs.txt
-    #   sphinx
-sphinxcontrib-serializinghtml==1.1.5
-    # via
-    #   -r requirements/requirements-docs.txt
-    #   sphinx
-stringcase==1.2.0
-    # via
-    #   -r requirements/requirements.txt
-    #   frictionless
-structlog==22.1.0
-    # via
-    #   -r requirements/requirements.txt
-    #   libhxl
-tableschema-to-template==0.0.13
-    # via
-    #   -r requirements/requirements.txt
-    #   hdx-python-utilities
-tabulate==0.9.0
-    # via
-    #   -r requirements/requirements.txt
-    #   frictionless
-tblib==1.7.0
-    # via
-    #   -r requirements/requirements.txt
-    #   distributed
-text-unidecode==1.3
-    # via
-    #   -r requirements/requirements.txt
-    #   python-slugify
-toolz==0.12.0
-    # via
-    #   -r requirements/requirements.txt
-    #   dask
-    #   distributed
-    #   partd
-tornado==6.2
-    # via
-    #   -r requirements/requirements.txt
-    #   bokeh
-    #   distributed
-tox==4.4.4
-    # via -r requirements/requirements-tests.txt
-tqdm==4.64.1
-    # via
-    #   -r requirements/requirements.txt
-    #   cdsapi
-typer[all]==0.7.0
-    # via
-    #   -r requirements/requirements.txt
-    #   frictionless
-typing-extensions==4.4.0
-    # via
-    #   -r requirements/requirements.txt
-    #   bokeh
-    #   frictionless
-    #   pydantic
-unidecode==1.3.6
-    # via
-    #   -r requirements/requirements.txt
-    #   libhxl
-    #   pyphonetics
-url-normalize==1.4.3
-    # via
-    #   -r requirements/requirements.txt
-    #   requests-cache
-urllib3==1.26.14
-    # via
-    #   -r requirements/requirements-docs.txt
-    #   -r requirements/requirements.txt
-    #   distributed
-    #   libhxl
-    #   requests
-    #   requests-cache
-validators==0.20.0
-    # via
-    #   -r requirements/requirements.txt
-    #   frictionless
-virtualenv==20.17.1
-    # via
-    #   -r requirements/requirements-tests.txt
-    #   pre-commit
-    #   tox
-wheel==0.38.4
-    # via
-    #   -r requirements/requirements.txt
-    #   libhxl
-    #   pip-tools
-wrapt==1.14.1
-    # via -r requirements/requirements.txt
-xarray[parallel]==2023.1.0
-    # via
-    #   -r requirements/requirements.txt
-    #   rioxarray
-xlrd==2.0.1
-    # via
-    #   -r requirements/requirements.txt
-    #   hdx-python-utilities
-xlrd3==1.1.0
-    # via
-    #   -r requirements/requirements.txt
-    #   libhxl
-xlsxwriter==3.0.8
-    # via
-    #   -r requirements/requirements.txt
-    #   tableschema-to-template
-xlwt==1.3.0
-    # via
-    #   -r requirements/requirements.txt
-    #   hdx-python-utilities
-zict==2.2.0
-    # via
-    #   -r requirements/requirements.txt
-    #   distributed
-
-# The following packages are considered to be unsafe in a requirements file:
-# pip
-# setuptools
diff --git a/requirements/requirements-docs.txt b/requirements/requirements-docs.txt
deleted file mode 100644
index 7eca7b14..00000000
--- a/requirements/requirements-docs.txt
+++ /dev/null
@@ -1,56 +0,0 @@
-#
-# This file is autogenerated by pip-compile with Python 3.11
-# by the following command:
-#
-#    pip-compile --output-file=requirements/requirements-docs.txt requirements/docs.in
-#
-alabaster==0.7.13
-    # via sphinx
-babel==2.11.0
-    # via sphinx
-certifi==2022.12.7
-    # via requests
-charset-normalizer==3.0.1
-    # via requests
-docutils==0.19
-    # via sphinx
-idna==3.4
-    # via requests
-imagesize==1.4.1
-    # via sphinx
-jinja2==3.1.2
-    # via sphinx
-markupsafe==2.1.2
-    # via jinja2
-packaging==23.0
-    # via sphinx
-pbr==5.11.1
-    # via sphinxcontrib-apidoc
-pygments==2.14.0
-    # via sphinx
-pytz==2022.7.1
-    # via babel
-requests==2.28.2
-    # via sphinx
-snowballstemmer==2.2.0
-    # via sphinx
-sphinx==6.1.3
-    # via
-    #   -r requirements/docs.in
-    #   sphinxcontrib-apidoc
-sphinxcontrib-apidoc==0.3.0
-    # via -r requirements/docs.in
-sphinxcontrib-applehelp==1.0.4
-    # via sphinx
-sphinxcontrib-devhelp==1.0.2
-    # via sphinx
-sphinxcontrib-htmlhelp==2.0.1
-    # via sphinx
-sphinxcontrib-jsmath==1.0.1
-    # via sphinx
-sphinxcontrib-qthelp==1.0.3
-    # via sphinx
-sphinxcontrib-serializinghtml==1.1.5
-    # via sphinx
-urllib3==1.26.14
-    # via requests
diff --git a/setup.cfg b/setup.cfg
index 679d08ce..509d9a99 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -34,8 +34,6 @@ python_requires = >= 3.8
 #  https://stackoverflow.com/questions/73929564/
 #  entrypoints-object-has-no-attribute-get-digital-ocean
 install_requires =
-    cdsapi
-    cfgrib
     cftime
     geopandas
     hdx-python-api>=5.6.4
@@ -63,3 +61,51 @@ build-dir = docs/build
 extend-ignore = SFS301 # Allow f-strings
 docstring-convention = numpy
 max-pos-args = 2
+
+[options.extras_require]
+glofas =
+    cdsapi
+    cfgrib
+full =
+    %(glofas)s
+test =
+    %(full)s
+    pytest
+    pytest-cov
+    pytest-mock
+    tox
+doc =
+    extras_require
+    sphinx
+    sphinx-rtd-theme
+    sphinxcontrib-apidoc
+dev =
+    %(test)s
+    %(doc)s
+    pre-commit
+[tox:tox]
+isolated_build = true
+envlist =
+    py38
+    py39
+    py310
+    py311
+
+[testenv]
+wheel = true
+recreate = true
+extras = test
+commands =
+    pytest \
+    --cov=ochanticipy \
+    --no-cov-on-fail \
+    --junitxml=.tox/test-results.xml \
+    --cov-report=xml \
+    --cov-report=term-missing
+
+[gh-actions]
+python =
+    3.8: py38
+    3.9: py39
+    3.10: py310
+    3.11: py311
diff --git a/src/ochanticipy/config/countries/afg.yaml b/src/ochanticipy/config/countries/afg.yaml
new file mode 100644
index 00000000..0f0cd5ee
--- /dev/null
+++ b/src/ochanticipy/config/countries/afg.yaml
@@ -0,0 +1,10 @@
+iso3: afg
+fewsnet:
+  region_name: central-asia
+glofas:
+  reporting_points:
+  - name: Na
+    lon: 70.75
+    lat: 34.35
+usgs_ndvi:
+  area_name: central-asia
diff --git a/src/ochanticipy/config/countries/bdi.yaml b/src/ochanticipy/config/countries/bdi.yaml
new file mode 100644
index 00000000..40028332
--- /dev/null
+++ b/src/ochanticipy/config/countries/bdi.yaml
@@ -0,0 +1,14 @@
+iso3: bdi
+codab:
+  hdx_resource_name: bdi_adm_igebu_ocha_20171103_SHP.zip
+  layer_base_name: bdi_admbnda_adm{admin_level}_igebu_ocha_20171103.shp
+  admin_level_max: 2
+fewsnet:
+  region_name: east-africa
+glofas:
+  reporting_points:
+  - name: Akanyaru-Upper
+    lon: 29.75
+    lat: -2.75
+usgs_ndvi:
+  area_name: east-africa
diff --git a/src/ochanticipy/config/countries/bfa.yaml b/src/ochanticipy/config/countries/bfa.yaml
index ad9b975a..0bd1e728 100644
--- a/src/ochanticipy/config/countries/bfa.yaml
+++ b/src/ochanticipy/config/countries/bfa.yaml
@@ -1,6 +1,6 @@
 iso3: bfa
 codab:
-  hdx_dataset_name: bfa_adm_igb_20200323_SHP.zip
+  hdx_resource_name: bfa_adm_igb_20200323_SHP.zip
   layer_base_name: bfa_admbnda_adm{admin_level}_igb_20200323.shp
   admin_level_max: 3
 fewsnet:
diff --git a/src/ochanticipy/config/countries/bgd.yaml b/src/ochanticipy/config/countries/bgd.yaml
index e3794941..1f8704ad 100644
--- a/src/ochanticipy/config/countries/bgd.yaml
+++ b/src/ochanticipy/config/countries/bgd.yaml
@@ -1,6 +1,6 @@
 iso3: bgd
 codab:
-  hdx_dataset_name: bgd_adm_bbs_20201113_SHP.zip
+  hdx_resource_name: bgd_adm_bbs_20201113_SHP.zip
   layer_base_name: bgd_adm_bbs_20201113_SHP/bgd_admbnda_adm{admin_level}_bbs_20201113.shp
   admin_level_max: 4
 glofas:
diff --git a/src/ochanticipy/config/countries/caf.yaml b/src/ochanticipy/config/countries/caf.yaml
new file mode 100644
index 00000000..79e0d8af
--- /dev/null
+++ b/src/ochanticipy/config/countries/caf.yaml
@@ -0,0 +1,161 @@
+iso3: caf
+codab:
+  hdx_resource_name:
+    - caf_admbnda_adm0_200k_sigcaf_reach_itos_v2.zip
+    - caf_admbnda_adm1_200k_sigcaf_reach_itos_v2.zip
+    - caf_admbnda_adm2_200k_sigcaf_reach_itos_v2.zip
+    - caf_admbnda_adm3_200k_sigcaf_reach_itos_v2.zip
+    - caf_admbnda_adm4_reach_ocha_v2.zip
+  layer_base_name: caf_admbnda_adm{admin_level}_200k_sigcaf_reach_itos_v2.shp
+  admin_level_max: 4
+  admin4_name: caf_admbnda_adm4_reach_ocha_v2.shp
+fewsnet:
+  region_name: west-africa
+glofas:
+  reporting_points:
+  - name: Bac Bea
+    lon: 17.05
+    lat: 6.45
+  - name: Bamingui A Bamingui
+    lon: 20.25
+    lat: 7.55
+  - name: Bangoran A Bangoran
+    lon: 20.35
+    lat: 8.05
+  - name: Bangui
+    lon: 18.65
+    lat: 4.35
+  - name: Bangui-Kette A Alindao
+    lon: 21.15
+    lat: 5.05
+  - name: Batangafo
+    lon: 18.25
+    lat: 7.35
+  - name: Bossangoa
+    lon: 17.45
+    lat: 6.45
+  - name: Bossele Bali
+    lon: 18.45
+    lat: 4.55
+  - name: Boungou Mahamat
+    lon: 21.95
+    lat: 6.75
+  - name: Chinko A Rafai
+    lon: 23.95
+    lat: 5.05
+  - name: Fafa Bouca
+    lon: 18.25
+    lat: 6.45
+  - name: Gribingui A Kaga Bandoro Ex. Crampel
+    lon: 19.15
+    lat: 6.95
+  - name: Kadei Sosso
+    lon: 15.55
+    lat: 3.95
+  - name: Kerre Kerre
+    lon: 25.75
+    lat: 5.35
+  - name: Kotto Bria
+    lon: 22.05
+    lat: 6.45
+  - name: Kotto Ira-Banda
+    lon: 22.15
+    lat: 5.95
+  - name: Kotto Kembe
+    lon: 21.95
+    lat: 4.55
+  - name: Koukourou A Koukourou
+    lon: 20.05
+    lat: 7.15
+  - name: Lesse Boyali
+    lon: 18.15
+    lat: 4.05
+  - name: Lim Sarki
+    lon: 15.35
+    lat: 6.95
+  - name: Lobaye A Kedingue-Yawa
+    lon: 16.95
+    lat: 4.45
+  - name: Lobaye A Zaoro-Yanga
+    lon: 16.55
+    lat: 5.25
+  - name: Lobaye Mongoumba
+    lon: 18.45
+    lat: 3.65
+  - name: Lobaye Ngotto
+    lon: 17.35
+    lat: 4.05
+  - name: Lobaye Safa
+    lon: 18.05
+    lat: 3.65
+  - name: Loungoumba
+    lon: 22.65
+    lat: 4.75
+  - name: M Bata
+    lon: 18.25
+    lat: 3.65
+  - name: Mambere A Carnot
+    lon: 15.85
+    lat: 4.95
+  - name: Mbaere Bambio
+    lon: 16.95
+    lat: 3.95
+  - name: Mbali A Boali - I.C.O.T
+    lon: 17.95
+    lat: 4.95
+  - name: Mbari Fode
+    lon: 23.35
+    lat: 5.55
+  - name: Mbokou A Obo
+    lon: 26.45
+    lat: 5.45
+  - name: Mpoko Bimbo
+    lon: 18.45
+    lat: 4.35
+  - name: Nana A Bewiti
+    lon: 15.15
+    lat: 5.85
+  - name: Ngou Bougouy
+    lon: 15.15
+    lat: 7.05
+  - name: Ombella Ombella
+    lon: 18.85
+    lat: 5.15
+  - name: Ouaka A Bambari
+    lon: 20.65
+    lat: 5.75
+  - name: Ouaka Bakala
+    lon: 20.35
+    lat: 6.15
+  - name: Ouaka Bambala
+    lon: 20.15
+    lat: 5.15
+  - name: Ouandja Ouandja
+    lon: 22.65
+    lat: 9.25
+  - name: Ouarra A Dembia
+    lon: 24.45
+    lat: 5.15
+  - name: Oubangui A Zinga
+    lon: 18.55
+    lat: 3.75
+  - name: Ouham A Bozoum
+    lon: 16.35
+    lat: 6.35
+  - name: Pende Pende
+    lon: 16.25
+    lat: 7.25
+  - name: Salo
+    lon: 16.15
+    lat: 3.25
+  - name: Tomi A Sibut
+    lon: 19.05
+    lat: 5.75
+  - name: Vokouma Ouanda-Djalle
+    lon: 22.65
+    lat: 8.85
+  - name: Yata Birao
+    lon: 22.85
+    lat: 10.25
+usgs_ndvi:
+  area_name: west-africa
diff --git a/src/ochanticipy/config/countries/cmr.yaml b/src/ochanticipy/config/countries/cmr.yaml
new file mode 100644
index 00000000..2e9ca4f5
--- /dev/null
+++ b/src/ochanticipy/config/countries/cmr.yaml
@@ -0,0 +1,71 @@
+iso3: cmr
+codab:
+  hdx_resource_name: cmr_admbnda_inc_20180104_SHP.zip
+  layer_base_name: cmr_admbnda_inc_20180104_SHP/cmr_admbnda_adm{admin_level}_inc_20180104.shp
+  admin_level_max: 3
+fewsnet:
+  region_name: west-africa
+glofas:
+  reporting_points:
+  - name: Benoue A Buffle Noir
+    lon: 13.85
+    lat: 8.15
+  - name: Boumba A Biwala
+    lon: 14.95
+    lat: 3.15
+  - name: Chagoua
+    lon: 15.05
+    lat: 12.05
+  - name: Dja A Bie
+    lon: 13.35
+    lat: 2.75
+  - name: Dja A Somalomo
+    lon: 12.75
+    lat: 3.35
+  - name: Djerem A Mbakaou Echelle Ii -Aval-
+    lon: 12.75
+    lat: 6.25
+  - name: Faro A Djelepo
+    lon: 12.85
+    lat: 8.65
+  - name: Kadei A Batouri
+    lon: 14.25
+    lat: 4.45
+  - name: Kadey A Pana
+    lon: 14.65
+    lat: 4.15
+  - name: Lokoundje A Lolodorf
+    lon: 10.75
+    lat: 3.25
+  - name: Lom A Betare Oya
+    lon: 14.15
+    lat: 5.85
+  - name: Mape A Au Pont De Magba  Amont
+    lon: 11.25
+    lat: 6.05
+  - name: Mayo Kebi A Cossi
+    lon: 13.85
+    lat: 9.55
+  - name: Mbalmayo
+    lon: 11.55
+    lat: 3.55
+  - name: Mbam A Bac De Goura
+    lon: 11.35
+    lat: 4.55
+  - name: Metchem A Gouri
+    lon: 10.05
+    lat: 6.25
+  - name: Mungo A Mundame
+    lon: 9.55
+    lat: 4.55
+  - name: Nyong A Olama
+    lon: 11.25
+    lat: 3.45
+  - name: Sanaga A Edea
+    lon: 10.05
+    lat: 3.75
+  - name: Sanaga A Nachtigal
+    lon: 11.65
+    lat: 4.35
+usgs_ndvi:
+  area_name: west-africa
diff --git a/src/ochanticipy/config/countries/cod.yaml b/src/ochanticipy/config/countries/cod.yaml
index 8dd9e9ce..4559388c 100644
--- a/src/ochanticipy/config/countries/cod.yaml
+++ b/src/ochanticipy/config/countries/cod.yaml
@@ -1,6 +1,6 @@
 iso3: cod
 codab:
-  hdx_dataset_name: cod_admbnda_rgc_itos_20190911_SHP.zip
+  hdx_resource_name: cod_admbnda_rgc_itos_20190911_SHP.zip
   layer_base_name: cod_admbnda_adm{admin_level}_rgc_itos_20190911.shp
   admin_level_max: 1
   admin2_name: cod_admbnda_adm2_rgc_20190911.shp
diff --git a/src/ochanticipy/config/countries/col.yaml b/src/ochanticipy/config/countries/col.yaml
new file mode 100644
index 00000000..cca9af3a
--- /dev/null
+++ b/src/ochanticipy/config/countries/col.yaml
@@ -0,0 +1,182 @@
+iso3: col
+codab:
+  hdx_resource_name: COL Administrative Divisions Shapefiles.zip
+  layer_base_name: col_admbnda_adm{admin_level}_mgn_20200416.shp
+  admin_level_max: 2
+  admin0_name: col_admbnda_adm0_mgn_itos_20200416.shp
+glofas:
+  reporting_points:
+  - name: Anacaro
+    lon: -76.05
+    lat: 4.65
+  - name: Angostura
+    lon: -75.15
+    lat: 3.35
+  - name: Apavi
+    lon: -75.35
+    lat: 7.45
+  - name: Arrancaplumas
+    lon: -74.75
+    lat: 5.15
+  - name: Barrancabermeja
+    lon: -73.85
+    lat: 7.05
+  - name: Bolombolo
+    lon: -75.85
+    lat: 5.95
+  - name: Calamar
+    lon: -74.95
+    lat: 10.25
+  - name: Canafisto
+    lon: -75.85
+    lat: 6.45
+  - name: Capitanejo
+    lon: -72.75
+    lat: 6.55
+  - name: Cascada Simon Bolivar
+    lon: -76.25
+    lat: 1.85
+  - name: Contento El
+    lon: -73.75
+    lat: 8.25
+  - name: El Banco
+    lon: -73.95
+    lat: 8.95
+  - name: Guayabal
+    lon: -76.15
+    lat: 4.35
+  - name: Hormiguero
+    lon: -76.45
+    lat: 3.25
+  - name: Irra
+    lon: -75.65
+    lat: 5.25
+  - name: Juanchito
+    lon: -76.45
+    lat: 3.35
+  - name: La Balsa
+    lon: -76.65
+    lat: 3.05
+  - name: La Bolsa
+    lon: -76.55
+    lat: 3.15
+  - name: La Coquera
+    lon: -75.25
+    lat: 7.95
+  - name: La Esperanza
+    lon: -75.35
+    lat: 2.75
+  - name: La Magdalena
+    lon: -76.45
+    lat: 1.95
+  - name: La Pintada
+    lon: -75.65
+    lat: 5.65
+  - name: La Victoria
+    lon: -76.05
+    lat: 4.45
+  - name: La Virginia
+    lon: -75.95
+    lat: 4.85
+  - name: Las Flores
+    lon: -74.75
+    lat: 8.15
+  - name: Las Varas
+    lon: -74.55
+    lat: 8.35
+  - name: Magangue Esperanza
+    lon: -74.75
+    lat: 9.25
+  - name: Margento
+    lon: -74.95
+    lat: 8.05
+  - name: Mediacanoa
+    lon: -76.35
+    lat: 3.85
+  - name: Na 1
+    lon: -76.95
+    lat: 7.15
+  - name: Na 2
+    lon: -74.85
+    lat: 7.85
+  - name: Na 3
+    lon: -72.25
+    lat: 2.85
+  - name: Na 4
+    lon: -70.25
+    lat: 5.55
+  - name: Narino
+    lon: -74.85
+    lat: 4.35
+  - name: Olaya
+    lon: -75.85
+    lat: 6.65
+  - name: Paso Del Colegio
+    lon: -75.55
+    lat: 2.45
+  - name: Paz De Rio
+    lon: -72.75
+    lat: 5.95
+  - name: Penas Blancas
+    lon: -74.05
+    lat: 6.85
+  - name: Penoncito
+    lon: -73.85
+    lat: 8.95
+  - name: Pericongo
+    lon: -75.85
+    lat: 2.05
+  - name: Pte Balseadero
+    lon: -75.65
+    lat: 2.25
+  - name: Puente Iglesias
+    lon: -75.75
+    lat: 5.85
+  - name: Puente Pescadero
+    lon: -75.75
+    lat: 7.05
+  - name: Puente Santander
+    lon: -75.35
+    lat: 2.95
+  - name: Puerto Berrio
+    lon: -74.45
+    lat: 6.45
+  - name: Puerto Inmarco
+    lon: -74.55
+    lat: 6.25
+  - name: Puerto Salgar
+    lon: -74.65
+    lat: 5.45
+  - name: Puerto Valdivia
+    lon: -75.45
+    lat: 7.25
+  - name: Purificacion
+    lon: -74.95
+    lat: 3.85
+  - name: Regidor
+    lon: -73.85
+    lat: 8.65
+  - name: Salado Blanco
+    lon: -76.05
+    lat: 1.95
+  - name: San Carlos De Rio Negro
+    lon: -67.15
+    lat: 1.95
+  - name: San Pablo
+    lon: -73.95
+    lat: 7.45
+  - name: Sitio Nuevo R 11
+    lon: -73.85
+    lat: 7.75
+  - name: Suarez
+    lon: -76.75
+    lat: 2.95
+  - name: Ta Blanca
+    lon: -76.55
+    lat: 3.15
+  - name: Tacamocho
+    lon: -74.75
+    lat: 9.45
+  - name: Tres Cruces
+    lon: -74.55
+    lat: 8.65
diff --git a/src/ochanticipy/config/countries/eth.yaml b/src/ochanticipy/config/countries/eth.yaml
index 4b530d25..a5c57c59 100644
--- a/src/ochanticipy/config/countries/eth.yaml
+++ b/src/ochanticipy/config/countries/eth.yaml
@@ -1,9 +1,9 @@
 iso3: eth
 codab:
-  hdx_dataset_name: ETH Administrative Divisions Shapefiles.zip
-  layer_base_name: eth_admbnda_adm{admin_level}_csa_bofed_20201008.shp
+  hdx_resource_name: eth_adm_csa_bofedb_2021_SHP.zip
+  layer_base_name: eth_admbnda_adm{admin_level}_csa_bofedb_2021.shp
   admin_level_max: 3
-  admin0_name: eth_admbnda_adm0_csa_bofedb_itos_2021
+  admin0_name: eth_admbnda_adm0_csa_bofedb_itos_2021.shp
 fewsnet:
   region_name: east-africa
 glofas:
diff --git a/src/ochanticipy/config/countries/hti.yaml b/src/ochanticipy/config/countries/hti.yaml
new file mode 100644
index 00000000..06cd80c4
--- /dev/null
+++ b/src/ochanticipy/config/countries/hti.yaml
@@ -0,0 +1,14 @@
+iso3: hti
+codab:
+  hdx_resource_name: hti_adm_cnigs_20181129.zip
+  layer_base_name: hti_adm_cnigs_20181129/hti_admbnda_adm{admin_level}_cnigs_20181129.shp
+  admin_level_max: 3
+fewsnet:
+  region_name: caribbean-central-america
+glofas:
+  reporting_points:
+  - name: Na
+    lon: -72.65
+    lat: 19.15
+usgs_ndvi:
+  area_name: hispaniola
diff --git a/src/ochanticipy/config/countries/irq.yaml b/src/ochanticipy/config/countries/irq.yaml
new file mode 100644
index 00000000..657ecfd2
--- /dev/null
+++ b/src/ochanticipy/config/countries/irq.yaml
@@ -0,0 +1,6 @@
+iso3: irq
+codab:
+  hdx_resource_name: IRQ Administrative Divisions Shapefiles.zip
+  layer_base_name: irq_admbnda_adm{admin_level}_cso_20190603.shp
+  admin_level_max: 3
+  admin0_name: irq_admbnda_adm0_cso_itos_20190603.shp
diff --git a/src/ochanticipy/config/countries/lby.yaml b/src/ochanticipy/config/countries/lby.yaml
new file mode 100644
index 00000000..8abfb899
--- /dev/null
+++ b/src/ochanticipy/config/countries/lby.yaml
@@ -0,0 +1,7 @@
+iso3: lby
+codab:
+  hdx_resource_name: lby_adm_unosat_lbsc_20180507_SHP.zip
+  layer_base_name: lby_adm_unosat_lbsc_20180507_SHP/lby_admbnda_adm{admin_level}_unosat_lbsc_20180507.shp
+  admin_level_max: 2
+usgs_ndvi:
+  area_name: north-africa
diff --git a/src/ochanticipy/config/countries/mli.yaml b/src/ochanticipy/config/countries/mli.yaml
new file mode 100644
index 00000000..26e12c23
--- /dev/null
+++ b/src/ochanticipy/config/countries/mli.yaml
@@ -0,0 +1,170 @@
+iso3: mli
+codab:
+  hdx_resource_name: mli_admb_1m_gov_20211220_SHP.zip
+  layer_base_name: mli_admbnda_adm{admin_level}_1m_gov_20211220.shp
+  admin_level_max: 3
+fewsnet:
+  region_name: west-africa
+glofas:
+  reporting_points:
+  - name: Akka
+    lon: -4.25
+    lat: 15.45
+  - name: Ansongo 1
+    lon: 0.45
+    lat: 15.65
+  - name: Ansongo 2
+    lon: 0.45
+    lat: 15.65
+  - name: Bafing Makana 1
+    lon: -10.25
+    lat: 12.55
+  - name: Bafing Makana 2
+    lon: -10.25
+    lat: 12.55
+  - name: Bamako 1
+    lon: -7.95
+    lat: 12.65
+  - name: Bamako 2
+    lon: -7.95
+    lat: 12.65
+  - name: Banankoro
+    lon: -8.65
+    lat: 11.65
+  - name: Banifing At Kolondieba
+    lon: -6.85
+    lat: 11.05
+  - name: Baoule At Madina Diassa
+    lon: -7.65
+    lat: 10.75
+  - name: Bara Issa A Awoye
+    lon: -4.15
+    lat: 15.45
+  - name: Beneny Kegny
+    lon: -4.95
+    lat: 13.35
+  - name: Bougouni
+    lon: -7.35
+    lat: 11.45
+  - name: Daka Saydou
+    lon: -10.35
+    lat: 12.15
+  - name: Diangola
+    lon: -9.45
+    lat: 12.75
+  - name: Dibia
+    lon: -10.75
+    lat: 13.25
+  - name: Dioila
+    lon: -6.75
+    lat: 12.55
+  - name: Dire
+    lon: -3.35
+    lat: 16.25
+  - name: Douna 1
+    lon: -5.85
+    lat: 13.25
+  - name: Douna 2
+    lon: -5.85
+    lat: 13.25
+  - name: Galougo
+    lon: -11.05
+    lat: 13.85
+  - name: Gao
+    lon: -0.05
+    lat: 16.25
+  - name: Gouala
+    lon: -8.25
+    lat: 11.95
+  - name: Goundam
+    lon: -3.65
+    lat: 16.45
+  - name: Gourbassy
+    lon: -11.65
+    lat: 13.35
+  - name: Guelelinkoro
+    lon: -8.55
+    lat: 11.15
+  - name: Kabate
+    lon: -11.15
+    lat: 14.55
+  - name: Kana
+    lon: -6.35
+    lat: 12.95
+  - name: Kara
+    lon: -5.05
+    lat: 14.15
+  - name: Kayes
+    lon: -11.35
+    lat: 14.35
+  - name: Ke Macina
+    lon: -5.35
+    lat: 13.95
+  - name: Kirango Aval
+    lon: -6.05
+    lat: 13.75
+  - name: Koli-Koli A Korientze
+    lon: -3.75
+    lat: 15.35
+  - name: Koryoume
+    lon: -3.05
+    lat: 16.65
+  - name: Koulikoro
+    lon: -7.55
+    lat: 12.85
+  - name: Kouoro 2
+    lon: -5.65
+    lat: 11.95
+  - name: Mahina
+    lon: -10.85
+    lat: 13.75
+  - name: Na
+    lon: -4.15
+    lat: 14.55
+  - name: Nantaka  Mopti
+    lon: -4.15
+    lat: 14.55
+  - name: Niger A Kenieroba
+    lon: -8.25
+    lat: 12.15
+  - name: Niger River At Tossaye
+    lon: -0.55
+    lat: 16.95
+  - name: Oualia
+    lon: -10.35
+    lat: 13.55
+  - name: Pankourou 1
+    lon: -6.55
+    lat: 11.45
+  - name: Pankourou 2
+    lon: -6.55
+    lat: 11.45
+  - name: Sarafere
+    lon: -3.65
+    lat: 15.85
+  - name: Selingue
+    lon: -8.25
+    lat: 11.65
+  - name: Siramakana
+    lon: -9.05
+    lat: 14.25
+  - name: Sofara
+    lon: -4.25
+    lat: 14.05
+  - name: Tamani
+    lon: -6.85
+    lat: 13.35
+  - name: Tilembeya
+    lon: -4.95
+    lat: 14.15
+  - name: Tonka
+    lon: -3.75
+    lat: 16.15
+  - name: Tossaye
+    lon: -3.25
+    lat: 16.35
+  - name: Toukoto
+    lon: -9.85
+    lat: 13.45
+usgs_ndvi:
+  area_name: west-africa
diff --git a/src/ochanticipy/config/countries/mmr.yaml b/src/ochanticipy/config/countries/mmr.yaml
new file mode 100644
index 00000000..44418599
--- /dev/null
+++ b/src/ochanticipy/config/countries/mmr.yaml
@@ -0,0 +1,45 @@
+iso3: mmr
+glofas:
+  reporting_points:
+  - name: Bago
+    lon: 96.45
+    lat: 17.35
+  - name: Dothaung
+    lon: 96.55
+    lat: 18.95
+  - name: Hkamti
+    lon: 95.65
+    lat: 25.95
+  - name: Katha
+    lon: 96.35
+    lat: 24.15
+  - name: Magway
+    lon: 94.85
+    lat: 20.15
+  - name: Mawlaik
+    lon: 94.45
+    lat: 23.65
+  - name: Monywa
+    lon: 95.15
+    lat: 22.05
+  - name: Na 1
+    lon: 94.95
+    lat: 24.75
+  - name: Na 2
+    lon: 94.95
+    lat: 17.35
+  - name: Na 3
+    lon: 96.05
+    lat: 23.65
+  - name: Na 4
+    lon: 94.75
+    lat: 24.25
+  - name: Pyay
+    lon: 95.15
+    lat: 18.75
+  - name: Sagaing
+    lon: 96.05
+    lat: 21.95
+  - name: Toungoo
+    lon: 96.45
+    lat: 19.05
diff --git a/src/ochanticipy/config/countries/moz.yaml b/src/ochanticipy/config/countries/moz.yaml
new file mode 100644
index 00000000..72e5e692
--- /dev/null
+++ b/src/ochanticipy/config/countries/moz.yaml
@@ -0,0 +1,209 @@
+iso3: moz
+codab:
+  hdx_resource_name: moz_adm_20190607b_SHP.zip
+  layer_base_name: moz_admbnda_adm{admin_level}_ine_20190607.shp
+  admin_level_max: 3
+fewsnet:
+  region_name: southern-africa
+glofas:
+  reporting_points:
+  - name: Aruangua em Aruangua Alfanedga
+    lon: 30.35
+    lat: -15.55
+  - name: Buzi At E.N.1 Goonda E456
+    lon: 33.85
+    lat: -19.85
+  - name: Buzi At Estaquinha E188
+    lon: 34.15
+    lat: -19.95
+  - name: Buzi em Chibabava
+    lon: 33.65
+    lat: -20.25
+  - name: Buzi em Espungabera
+    lon: 32.85
+    lat: -20.45
+  - name: Capoche-Capoche I
+    lon: 32.65
+    lat: -15.15
+  - name: Chefu
+    lon: 32.45
+    lat: -22.25
+  - name: Cherinze-Ponte Vuende
+    lon: 33.05
+    lat: -15.15
+  - name: Chire em Megaza Mutamba
+    lon: 35.25
+    lat: -17.15
+  - name: Combomune 58510033
+    lon: 32.35
+    lat: -23.45
+  - name: Est  Nairoto Montepuz 58262047
+    lon: 39.05
+    lat: -12.55
+  - name: Gurue
+    lon: 36.95
+    lat: -15.45
+  - name: Incomati en Magude
+    lon: 32.65
+    lat: -25.05
+  - name: Inhamhombe em Maxixe km12
+    lon: 35.35
+    lat: -24.05
+  - name: Jagamo
+    lon: 35.25
+    lat: -24.15
+  - name: Licungo At Mocuba Ponte E91
+    lon: 36.95
+    lat: -16.85
+  - name: Licungo At Nante Intabo E647
+    lon: 36.95
+    lat: -16.85
+  - name: Limpopo At Chokwe E35
+    lon: 32.95
+    lat: -24.45
+  - name: Limpopo At Combomune E33
+    lon: 32.35
+    lat: -23.35
+  - name: Limpopo em Xai-Xai
+    lon: 33.65
+    lat: -25.05
+  - name: Lucite At Dombe E246
+    lon: 33.35
+    lat: -19.95
+  - name: Luenha-Luenha 1
+    lon: 33.45
+    lat: -16.55
+  - name: Lugenda em Congerane E.N.8
+    lon: 35.95
+    lat: -14.15
+  - name: Lugenda em Est. Litunde Marrupa
+    lon: 36.25
+    lat: -13.55
+  - name: Lugenda em Est. Marrupa Mecula
+    lon: 37.65
+    lat: -12.45
+  - name: Lurio
+    lon: 36.85
+    lat: -14.85
+  - name: Macarretane
+    lon: 32.85
+    lat: -24.35
+  - name: Maputo em Madubula I
+    lon: 32.45
+    lat: -26.75
+  - name: Matundo Cais 58316087
+    lon: 33.55
+    lat: -16.15
+  - name: Megaruma em E.N.106 Chiure Pemba
+    lon: 39.85
+    lat: -13.25
+  - name: Messalo em Chai E.N 243
+    lon: 40.05
+    lat: -11.85
+  - name: Montepuez em Mecuia
+    lon: 38.95
+    lat: -13.05
+  - name: Montepuez em Moja E.N.243
+    lon: 40.05
+    lat: -12.65
+  - name: Mutamba em Jangamo E.N.1
+    lon: 30.55
+    lat: -15.85
+  - name: Na 1
+    lon: 32.15
+    lat: -26.85
+  - name: Na 2
+    lon: 30.55
+    lat: -15.85
+  - name: Na 3
+    lon: 32.35
+    lat: -26.05
+  - name: Na 4
+    lon: 32.95
+    lat: -25.05
+  - name: Na 5
+    lon: 34.25
+    lat: -24.45
+  - name: Na 6
+    lon: 32.15
+    lat: -23.75
+  - name: Na 7
+    lon: 32.65
+    lat: -25.05
+  - name: Na 8
+    lon: 36.35
+    lat: -16.35
+  - name: Na 9
+    lon: 38.55
+    lat: -15.65
+  - name: Na 10
+    lon: 39.05
+    lat: -15.45
+  - name: Na 11
+    lon: 38.95
+    lat: -15.15
+  - name: Na 12
+    lon: 38.95
+    lat: -14.05
+  - name: Na 13
+    lon: 38.35
+    lat: -13.05
+  - name: Nametil
+    lon: 39.25
+    lat: -15.65
+  - name: Nhandare em Vila Paiva de Andrad
+    lon: 34.05
+    lat: -18.65
+  - name: Nhazania em J.E. da Costa
+    lon: 33.35
+    lat: -18.45
+  - name: Pungoe At En1 Ponte Gorongosa E651
+    lon: 34.15
+    lat: -19.05
+  - name: Pungue em E.N.102
+    lon: 33.25
+    lat: -18.55
+  - name: Pungue em Fronteira
+    lon: 33.05
+    lat: -18.35
+  - name: Pungwe At Mafambisse En6 E67
+    lon: 34.55
+    lat: -19.45
+  - name: Revubue em Chingodzi
+    lon: 33.65
+    lat: -16.15
+  - name: Revue
+    lon: 33.85
+    lat: -19.75
+  - name: Save em Massangena
+    lon: 32.95
+    lat: -21.55
+  - name: Sicacate 58510036
+    lon: 33.55
+    lat: -24.75
+  - name: Sinoya South
+    lon: 35.25
+    lat: -16.25
+  - name: Umbeluzi em Goba Montante
+    lon: 32.15
+    lat: -26.25
+  - name: Vila Franca do Save
+    lon: 34.55
+    lat: -21.15
+  - name: Zambeze At Caia S.S. E291
+    lon: 35.35
+    lat: -17.75
+  - name: Zambeze At Marromeu Sena Sugar E285
+    lon: 35.95
+    lat: -18.25
+  - name: Zambeze At Tete E320
+    lon: 33.65
+    lat: -16.25
+  - name: Zambeze em Mutarara
+    lon: 35.05
+    lat: -17.45
+  - name: Zembeze em Zumbo
+    lon: 30.45
+    lat: -15.65
+usgs_ndvi:
+  area_name: southern-africa
diff --git a/src/ochanticipy/config/countries/mwi.yaml b/src/ochanticipy/config/countries/mwi.yaml
index e9549d59..ccdb382a 100644
--- a/src/ochanticipy/config/countries/mwi.yaml
+++ b/src/ochanticipy/config/countries/mwi.yaml
@@ -1,7 +1,7 @@
 iso3: mwi
 codab:
-  hdx_dataset_name: mwi_adm_nso_20181016_SHP.zip
-  layer_base_name: mwi_admbnda_adm{admin_level}_nso_20181016.shp
+  hdx_resource_name: mwi_adm_nso_hotosm_20230405_SHP.zipSHP
+  layer_base_name: mwi_admbnda_adm{admin_level}_nso_hotosm_20230405.shp
   admin_level_max: 3
 fewsnet:
   region_name: southern-africa
diff --git a/src/ochanticipy/config/countries/ner.yaml b/src/ochanticipy/config/countries/ner.yaml
new file mode 100644
index 00000000..1883b771
--- /dev/null
+++ b/src/ochanticipy/config/countries/ner.yaml
@@ -0,0 +1,51 @@
+iso3: ner
+codab:
+  hdx_resource_name:
+    - NER_adm00_feb2018.zip
+    - NER_adm01_feb2018.zip
+    - NER_adm02_feb2018.zip
+    - NER_adm03_feb2018.zip
+  layer_base_name: NER_adm0{admin_level}_feb2018.shp
+  admin_level_max: 3
+fewsnet:
+  region_name: west-africa
+glofas:
+  reporting_points:
+  - name: Alcongui
+    lon: 0.55
+    lat: 14.75
+  - name: Boss Bangou
+    lon: 1.25
+    lat: 13.35
+  - name: Dargol A Kakassi
+    lon: 1.45
+    lat: 13.85
+  - name: Dargol At Tera
+    lon: 0.75
+    lat: 13.95
+  - name: Diongore Amont
+    lon: 2.25
+    lat: 12.95
+  - name: Garbe-Kourou
+    lon: 1.55
+    lat: 13.65
+  - name: Gorouol At Dolbel
+    lon: 0.25
+    lat: 14.55
+  - name: Goulbi De Maradi A Nielloua
+    lon: 7.25
+    lat: 13.15
+  - name: Kandadji
+    lon: 0.95
+    lat: 14.65
+  - name: Majia At Tsernaoua
+    lon: 5.35
+    lat: 13.85
+  - name: Niamey
+    lon: 2.05
+    lat: 13.55
+  - name: W
+    lon: 2.65
+    lat: 12.55
+usgs_ndvi:
+  area_name: west-africa
diff --git a/src/ochanticipy/config/countries/nga.yaml b/src/ochanticipy/config/countries/nga.yaml
new file mode 100644
index 00000000..e84ca9e3
--- /dev/null
+++ b/src/ochanticipy/config/countries/nga.yaml
@@ -0,0 +1,89 @@
+iso3: nga
+codab:
+  hdx_resource_name: nga_adm_osgof_20190417.zip
+  layer_base_name: nga_admbnda_adm{admin_level}_osgof_20190417.shp
+  admin_level_max: 3
+  # Nigeria also has districts that don't fall into a particular admin level
+  custom_layer_names:
+    - nga_admbnda_senDist_inec_osgof_20190417.shp
+fewsnet:
+  region_name: west-africa
+glofas:
+  reporting_points:
+  - name: Benue A Makurdi
+    lon: 8.55
+    lat: 7.75
+  - name: Benue A Umaisha
+    lon: 7.15
+    lat: 7.95
+  - name: Benue A Wuro Boki
+    lon: 12.75
+    lat: 9.35
+  - name: Dapchi
+    lon: 11.55
+    lat: 12.45
+  - name: Dudurun Warwade At Chai-Chai
+    lon: 9.25
+    lat: 11.95
+  - name: Gashua
+    lon: 11.05
+    lat: 12.85
+  - name: Geidam
+    lon: 11.95
+    lat: 12.95
+  - name: Hadejia
+    lon: 10.05
+    lat: 12.45
+  - name: Ibi
+    lon: 9.75
+    lat: 8.15
+  - name: Jamaare River At Bunga
+    lon: 9.65
+    lat: 10.95
+  - name: Jamaare River At Foggo
+    lon: 9.95
+    lat: 11.45
+  - name: Kaduna A Wuya
+    lon: 5.85
+    lat: 9.15
+  - name: Kafin Hausa
+    lon: 9.95
+    lat: 12.25
+  - name: Kasaga
+    lon: 10.35
+    lat: 12.85
+  - name: Katagum
+    lon: 10.35
+    lat: 12.25
+  - name: Katsina Ala A Katsina Ala
+    lon: 9.35
+    lat: 7.25
+  - name: Likori
+    lon: 10.35
+    lat: 12.65
+  - name: Lokoja
+    lon: 6.75
+    lat: 7.75
+  - name: Na
+    lon: 6.85
+    lat: 6.55
+  - name: Niger A  Jebba Downstream Dam
+    lon: 4.95
+    lat: 9.15
+  - name: Niger A Baro
+    lon: 6.45
+    lat: 8.55
+  - name: Niger A Onitsha
+    lon: 6.75
+    lat: 6.25
+  - name: Sokoto A Kende
+    lon: 4.25
+    lat: 11.55
+  - name: Wudil
+    lon: 8.75
+    lat: 11.75
+  - name: Yidere Bode
+    lon: 4.15
+    lat: 11.35
+usgs_ndvi:
+  area_name: west-africa
diff --git a/src/ochanticipy/config/countries/npl.yaml b/src/ochanticipy/config/countries/npl.yaml
index 837d4e27..80478d51 100644
--- a/src/ochanticipy/config/countries/npl.yaml
+++ b/src/ochanticipy/config/countries/npl.yaml
@@ -1,11 +1,8 @@
 iso3: npl
 codab:
-  hdx_dataset_name: npl_admbnda_nd_20201117_SHP.zip
-  layer_base_name: npl_admbnda_adm{admin_level}_nd_20201117.shp
-  admin_level_max: 2
-  # Nepal also has districts that don't fall into a particular admin level
-  custom_layer_names:
-    - npl_admbnda_districts_nd_20201117
+  hdx_resource_name: npl_adm_nd_20190430_SHP.zip
+  layer_base_name: npl_admbnda_adm{admin_level}_nd_20190430.shp
+  admin_level_max: 3
 glofas:
   reporting_points:
   - name: After Lothar Confluence
diff --git a/src/ochanticipy/config/countries/pse.yaml b/src/ochanticipy/config/countries/pse.yaml
new file mode 100644
index 00000000..ba88f0cb
--- /dev/null
+++ b/src/ochanticipy/config/countries/pse.yaml
@@ -0,0 +1,5 @@
+iso3: pse
+codab:
+  hdx_resource_name: pse_adm_pamop_20210806_SHP.zip
+  layer_base_name: pse_admbnda_adm{admin_level}_pamop_20210806.shp
+  admin_level_max: 2
diff --git a/src/ochanticipy/config/countries/sdn.yaml b/src/ochanticipy/config/countries/sdn.yaml
new file mode 100644
index 00000000..84f13dbd
--- /dev/null
+++ b/src/ochanticipy/config/countries/sdn.yaml
@@ -0,0 +1,86 @@
+iso3: sdn
+codab:
+  hdx_resource_name: sdn_adm_cbs_nic_ssa_20200831_SHP.zip
+  layer_base_name: sdn_admbnda_adm{admin_level}_cbs_nic_ssa_20200831.shp
+  admin_level_max: 2
+fewsnet:
+  region_name: east-africa
+glofas:
+  reporting_points:
+  - name: Diem
+    lon: 34.95
+    lat: 11.25
+  - name: Dongola 1
+    lon: 30.45
+    lat: 19.15
+  - name: Dongola 2
+    lon: 30.45
+    lat: 19.15
+  - name: El Guisi
+    lon: 34.05
+    lat: 13.35
+  - name: El Hawata
+    lon: 34.65
+    lat: 13.35
+  - name: El Hsanab
+    lon: 33.95
+    lat: 17.65
+  - name: Girba Dam
+    lon: 35.95
+    lat: 14.95
+  - name: Jebal Aulia Dam
+    lon: 32.45
+    lat: 15.35
+  - name: Khartoum 1
+    lon: 32.55
+    lat: 15.55
+  - name: Khartoum 2
+    lon: 32.55
+    lat: 15.65
+  - name: Kubor
+    lon: 35.95
+    lat: 14.05
+  - name: Merowe Dam
+    lon: 31.85
+    lat: 18.55
+  - name: Na 1
+    lon: 32.75
+    lat: 12.35
+  - name: Na 2
+    lon: 32.45
+    lat: 13.75
+  - name: Na 3
+    lon: 32.25
+    lat: 14.75
+  - name: Na 4
+    lon: 33.95
+    lat: 13.15
+  - name: Na 5
+    lon: 33.55
+    lat: 13.75
+  - name: Na 6
+    lon: 32.45
+    lat: 15.55
+  - name: Na 7
+    lon: 32.85
+    lat: 16.45
+  - name: Ros Old
+    lon: 34.35
+    lat: 11.85
+  - name: Roseires Dam
+    lon: 34.35
+    lat: 11.85
+  - name: Sennar U S
+    lon: 33.65
+    lat: 13.55
+  - name: Sinnar Dam
+    lon: 33.65
+    lat: 13.55
+  - name: Tamaniat
+    lon: 32.55
+    lat: 15.95
+  - name: Wad El Hellewa
+    lon: 35.95
+    lat: 14.25
+usgs_ndvi:
+  area_name: east-africa
diff --git a/src/ochanticipy/config/countries/som.yaml b/src/ochanticipy/config/countries/som.yaml
new file mode 100644
index 00000000..ae21b647
--- /dev/null
+++ b/src/ochanticipy/config/countries/som.yaml
@@ -0,0 +1,61 @@
+iso3: som
+codab:
+  hdx_resource_name: som_adm_ocha_itos_20230308_SHP.zip
+  layer_base_name: som_admbnda_adm{admin_level}_ocha_20230308.shp
+  admin_level_max: 2
+  # Somalia also has states and 2 operational zones
+  custom_layer_names:
+    - som_admbnda_state_ocha_20230308.shp
+    - som_admbnda_opz1_ocha_20230308.shp
+    - som_admbnda_opz2_ocha_20230308.shp
+fewsnet:
+  region_name: east-africa
+glofas:
+  reporting_points:
+  - name: Afgooye
+    lon: 45.15
+    lat: 2.15
+  - name: Audegle
+    lon: 44.85
+    lat: 1.95
+  - name: Balcad
+    lon: 45.35
+    lat: 2.35
+  - name: Bardheere
+    lon: 42.25
+    lat: 2.35
+  - name: Beletweyne
+    lon: 45.15
+    lat: 4.75
+  - name: Bu Ale
+    lon: 42.55
+    lat: 1.25
+  - name: Bulo Burti
+    lon: 45.55
+    lat: 3.85
+  - name: Jamaame
+    lon: 42.65
+    lat: -0.05
+  - name: Jilib
+    lon: 42.75
+    lat: 0.55
+  - name: Jowhar
+    lon: 45.55
+    lat: 2.85
+  - name: Kamsuma
+    lon: 42.75
+    lat: 0.25
+  - name: Kaytooy
+    lon: 42.65
+    lat: 0.75
+  - name: Luuq
+    lon: 42.55
+    lat: 3.75
+  - name: Marerey
+    lon: 42.75
+    lat: 0.45
+  - name: Na
+    lon: 45.45
+    lat: 2.55
+usgs_ndvi:
+  area_name: east-africa
diff --git a/src/ochanticipy/config/countries/ssd.yaml b/src/ochanticipy/config/countries/ssd.yaml
new file mode 100644
index 00000000..359e559a
--- /dev/null
+++ b/src/ochanticipy/config/countries/ssd.yaml
@@ -0,0 +1,17 @@
+iso3: ssd
+codab:
+  hdx_resource_name: ssd_admbnda_imwg_nbs_20221219_SHP.zip
+  layer_base_name: ssd_admbnda_imwg_nbs_20221219_SHP/ssd_admbnda_adm{admin_level}_imwg_nbs_20221219.shp
+  admin_level_max: 3
+fewsnet:
+  region_name: east-africa
+glofas:
+  reporting_points:
+  - name: Malakal 1
+    lon: 31.65
+    lat: 9.55
+  - name: Malakal 2
+    lon: 31.65
+    lat: 9.55
+usgs_ndvi:
+  area_name: east-africa
diff --git a/src/ochanticipy/config/countries/syr.yaml b/src/ochanticipy/config/countries/syr.yaml
new file mode 100644
index 00000000..16752e3b
--- /dev/null
+++ b/src/ochanticipy/config/countries/syr.yaml
@@ -0,0 +1,13 @@
+iso3: syr
+codab:
+  hdx_resource_name: SYR Administrative Divisions Shapefiles.zip
+  layer_base_name: Shapefiles/syr_admbnda_adm{admin_level}_uncs_unocha_20201217.shp
+  admin_level_max: 3
+glofas:
+  reporting_points:
+  - name: Na 1
+    lon: 40.85
+    lat: 34.65
+  - name: Na 2
+    lon: 40.85
+    lat: 36.45
diff --git a/src/ochanticipy/config/countries/tcd.yaml b/src/ochanticipy/config/countries/tcd.yaml
new file mode 100644
index 00000000..84fbc7e8
--- /dev/null
+++ b/src/ochanticipy/config/countries/tcd.yaml
@@ -0,0 +1,98 @@
+iso3: tcd
+codab:
+  hdx_resource_name:
+    - tcd_admbnda_adm0_ocha.zip
+    - tcd_admbnda_adm1_ocha.zip
+    - tcd_admbnda_adm2_ocha_20170615.zip
+    - tcd_a_admbnd_adm3_ocha.zip
+  layer_base_name: tcd_admbnda_adm{admin_level}_ocha.shp
+  admin_level_max: 3
+  admin2_name: tcd_admbnda_adm2_ocha_20170615.shp
+  admin3_name: tcd_a_admbnd_adm3_ocha.shp
+fewsnet:
+  region_name: west-africa
+glofas:
+  reporting_points:
+  - name: Ba-Tha A Ati
+    lon: 18.35
+    lat: 13.15
+  - name: Ba-Tha A Oum-Hadjer
+    lon: 19.65
+    lat: 13.35
+  - name: Bahr Aouk Golongosso
+    lon: 19.15
+    lat: 9.05
+  - name: Bahr-Sara A Moissala
+    lon: 17.75
+    lat: 8.35
+  - name: Bongor
+    lon: 15.35
+    lat: 10.25
+  - name: Chari A Bousso
+    lon: 16.75
+    lat: 10.45
+  - name: Chari A Chagoua
+    lon: 15.15
+    lat: 12.05
+  - name: Chari A Hellibongo
+    lon: 18.35
+    lat: 9.25
+  - name: Chari A Mailao
+    lon: 15.25
+    lat: 11.55
+  - name: Chari A Ndjamena  -Ex Fort-Lamy-
+    lon: 15.05
+    lat: 12.05
+  - name: Kabia A Patalao
+    lon: 15.25
+    lat: 9.85
+  - name: Kabia At Pont Carol
+    lon: 15.45
+    lat: 9.25
+  - name: Lai
+    lon: 16.25
+    lat: 9.35
+  - name: Lim A Ouli Bangala
+    lon: 15.85
+    lat: 7.75
+  - name: Logone A Baibokoum
+    lon: 15.65
+    lat: 7.75
+  - name: Logone A Katoa
+    lon: 15.05
+    lat: 10.85
+  - name: Logone A Logone-Gana
+    lon: 15.15
+    lat: 11.55
+  - name: Logone A Moundou-Pont
+    lon: 16.05
+    lat: 8.55
+  - name: Mailao
+    lon: 15.25
+    lat: 11.55
+  - name: Manda
+    lon: 18.15
+    lat: 9.15
+  - name: Nana Barya A Markounda
+    lon: 16.95
+    lat: 7.65
+  - name: Ndjamena Fort Lamy
+    lon: 15.05
+    lat: 12.05
+  - name: Pende A Doba Cotonfran
+    lon: 16.85
+    lat: 8.65
+  - name: Pende A Gore
+    lon: 16.65
+    lat: 7.95
+  - name: Sarh Fort Archambault
+    lon: 18.45
+    lat: 9.05
+  - name: Tandjile A Bologo
+    lon: 15.75
+    lat: 9.05
+  - name: Tandjile A Tchoa
+    lon: 16.05
+    lat: 9.35
+usgs_ndvi:
+  area_name: west-africa
diff --git a/src/ochanticipy/config/countries/ukr.yaml b/src/ochanticipy/config/countries/ukr.yaml
new file mode 100644
index 00000000..5e8f5abe
--- /dev/null
+++ b/src/ochanticipy/config/countries/ukr.yaml
@@ -0,0 +1,13 @@
+iso3: ukr
+codab:
+  hdx_resource_name: ukr_admbnda_sspe_20230201_SHP.zip
+  layer_base_name: ukr_admbnda_sspe_20230201_SHP/ukr_admbnda_adm{admin_level}_sspe_20230201.shp
+  admin_level_max: 4
+glofas:
+  reporting_points:
+  - name: Dnepr Power Plant
+    lon: 35.05
+    lat: 47.85
+  - name: Kakhovskoye Vodokhranilishche Ges
+    lon: 33.15
+    lat: 46.75
diff --git a/src/ochanticipy/config/countries/ven.yaml b/src/ochanticipy/config/countries/ven.yaml
new file mode 100644
index 00000000..0813776c
--- /dev/null
+++ b/src/ochanticipy/config/countries/ven.yaml
@@ -0,0 +1,76 @@
+iso3: ven
+codab:
+  hdx_resource_name: ven_adm_ine_20210223_SHP.zip
+  layer_base_name: ven_admbnda_adm{admin_level}_ine_20210223.shp
+  admin_level_max: 3
+glofas:
+  reporting_points:
+  - name: Canagua
+    lon: -71.35
+    lat: 8.15
+  - name: Dos Aguas
+    lon: -64.45
+    lat: 5.95
+  - name: Ejido
+    lon: -71.15
+    lat: 8.55
+  - name: El Perro
+    lon: -64.55
+    lat: 5.85
+  - name: Entre Rios
+    lon: -64.45
+    lat: 5.85
+  - name: Na 1
+    lon: -72.25
+    lat: 9.05
+  - name: Na 2
+    lon: -66.65
+    lat: 7.35
+  - name: Na 3
+    lon: -67.65
+    lat: 7.95
+  - name: Na 4
+    lon: -62.15
+    lat: 8.55
+  - name: Na 5
+    lon: -69.45
+    lat: 7.05
+  - name: Na 6
+    lon: -67.15
+    lat: 6.65
+  - name: Na 7
+    lon: -69.35
+    lat: 8.05
+  - name: Na 8
+    lon: -68.75
+    lat: 7.85
+  - name: Na 9
+    lon: -68.45
+    lat: 7.45
+  - name: Na 10
+    lon: -68.65
+    lat: 7.65
+  - name: Na 11
+    lon: -67.75
+    lat: 7.35
+  - name: Na 12
+    lon: -67.55
+    lat: 7.05
+  - name: Na 13
+    lon: -63.45
+    lat: 8.15
+  - name: Na 14
+    lon: -67.45
+    lat: 7.85
+  - name: Pie De Salto
+    lon: -64.45
+    lat: 6.25
+  - name: Puente Angostura
+    lon: -63.55
+    lat: 8.15
+  - name: Solano
+    lon: -66.95
+    lat: 1.95
+  - name: Tama Tama
+    lon: -65.75
+    lat: 3.15
diff --git a/src/ochanticipy/config/countries/yem.yaml b/src/ochanticipy/config/countries/yem.yaml
new file mode 100644
index 00000000..1466ea6a
--- /dev/null
+++ b/src/ochanticipy/config/countries/yem.yaml
@@ -0,0 +1,9 @@
+iso3: yem
+codab:
+  hdx_resource_name: YEM Administrative Divisions Shapefiles.zip
+  layer_base_name: yem_admbnda_adm{admin_level}_govyem_cso_20191002.shp
+  admin_level_max: 3
+fewsnet:
+  region_name: east-africa
+usgs_ndvi:
+  area_name: yemen
diff --git a/src/ochanticipy/config/countryconfig.py b/src/ochanticipy/config/countryconfig.py
index 25018a87..0b94baf1 100644
--- a/src/ochanticipy/config/countryconfig.py
+++ b/src/ochanticipy/config/countryconfig.py
@@ -12,16 +12,20 @@ class CodABConfig(BaseModel):
 
     Parameters
     ----------
-    hdx_dataset_name: str
-        COD AB dataset name on HDX. Can be found by taking the filename as it
-        appears on the dataset page.
+    admin_level_max: int
+         The maximum admin level available in the shapefile, cannot be
+         greater than 4. If ``hdx_resource_name`` is a list, then
+         ``admin_level_max`` must match the number of items in
+         ``hdx_resource_name``.
+    hdx_resource_name: Union[str, List[str]]
+        COD AB resource name on HDX. Can be found by taking the filename as it
+        appears on the COD AB dataset page. If individual COD AB files are
+        contained in separate resources, provide each filename in a list, where
+        the index is equivalent to admin level.
     layer_base_name: str
         The base name of the different admin layers, that presumably only
         change by a single custom_layer_number depending on the level. Should
         contain {admin_level} in place of the custom_layer_number.
-    admin_level_max: int
-         The maximum admin level available in the shapefile, cannot be
-         greater than 4
     admin{level}_name: str, optional
         The names of any admin level layers that do not conform to the
         layer_base_name pattern, where {level} ranges from 0 to 4
@@ -29,15 +33,29 @@ class CodABConfig(BaseModel):
         Any additional layer names that don't fit into the admin level paradigm
     """
 
-    hdx_dataset_name: str
-    layer_base_name: str
     admin_level_max: int
+    hdx_resource_name: Union[str, List[str]]
+    layer_base_name: str
     admin0_name: Optional[str]
     admin1_name: Optional[str]
     admin2_name: Optional[str]
     admin3_name: Optional[str]
     admin4_name: Optional[str]
-    custom_layer_names: Optional[list]
+    custom_layer_names: Optional[List[str]]
+
+    @validator("hdx_resource_name")
+    def _validate_hdx_resource_name(cls, hdx_resource_name, values):
+        """Ensure hdx_resource_name is str or list for all admin areas."""
+        if isinstance(hdx_resource_name, list) and (
+            len(hdx_resource_name) != values["admin_level_max"] + 1
+        ):
+            raise ValueError(
+                "In the COD AB section of the country configuration file, "
+                "hdx_resource_name should be a string or list of length "
+                "admin_level_max, with a resource provided for each level "
+                "from 0 to admin_level_max."
+            )
+        return hdx_resource_name
 
     @validator("layer_base_name")
     def _validate_layer_base_name(cls, layer_base_name):
diff --git a/src/ochanticipy/datasources/chirps/chirps.py b/src/ochanticipy/datasources/chirps/chirps.py
index 8aa9b6cf..d69c9b1e 100644
--- a/src/ochanticipy/datasources/chirps/chirps.py
+++ b/src/ochanticipy/datasources/chirps/chirps.py
@@ -278,7 +278,6 @@ def _get_processed_path(self, raw_path: Path) -> Path:
         return self._processed_base_dir / raw_path.parts[-1]
 
     def _get_location_url(self):
-
         location_url = (
             f"X/%28{self._geobb.lon_min}%29%28{self._geobb.lon_max}"
             f"%29RANGEEDGES/"
@@ -330,7 +329,6 @@ def _get_to_be_loaded_path_list(self):
 
     @staticmethod
     def _read_csv_from_url(url):
-
         context = ssl.create_default_context()
         context.set_ciphers("DEFAULT")
         result = urlopen(url, context=context)
@@ -421,7 +419,6 @@ def _get_file_name(
         month: str,
         day: str,
     ) -> str:
-
         file_name_base = self._get_file_name_base(
             year=year,
             month=month,
@@ -455,7 +452,6 @@ def _get_last_available_date(self):
         return datetime_object.date()
 
     def _get_url(self, year: str, month: str, day: str) -> str:
-
         # Convert month from month number (in string format) to
         # three-letter name
         month_name = calendar.month_abbr[int(month)]
@@ -554,7 +550,6 @@ def _get_file_name(
         month: str,
         day: str,
     ) -> str:
-
         file_name_base = self._get_file_name_base(
             year=year,
             month=month,
@@ -589,7 +584,6 @@ def _get_last_available_date(self):
         return datetime_object.date()
 
     def _get_url(self, year: str, month: str, day: str) -> str:
-
         # Convert month from month number (in string format) to
         # three-letter name
         month_name = calendar.month_abbr[int(month)]
diff --git a/src/ochanticipy/datasources/codab/codab.py b/src/ochanticipy/datasources/codab/codab.py
index bdf35de7..98882ae2 100644
--- a/src/ochanticipy/datasources/codab/codab.py
+++ b/src/ochanticipy/datasources/codab/codab.py
@@ -1,6 +1,7 @@
 """Download and manipulate COD administrative boundaries."""
 import logging
 from pathlib import Path
+from typing import List, Union
 
 import geopandas as gpd
 from fiona.errors import DriverError
@@ -8,7 +9,7 @@
 from ochanticipy.config.countryconfig import CountryConfig
 from ochanticipy.datasources.datasource import DataSource
 from ochanticipy.utils.check_file_existence import check_file_existence
-from ochanticipy.utils.hdx_api import load_dataset_from_hdx
+from ochanticipy.utils.hdx_api import load_resource_from_hdx
 
 logger = logging.getLogger(__name__)
 
@@ -30,12 +31,27 @@ def __init__(self, country_config: CountryConfig):
             is_public=True,
             config_datasource_name="codab",
         )
-        self._raw_filepath = (
-            self._raw_base_dir / f"{self._country_config.iso3}_"
-            f"{self._datasource_base_dir}.shp.zip"
-        )
 
-    def download(self, clobber: bool = False) -> Path:
+        # account for files stored across multiple resources
+        res_name = self._datasource_config.hdx_resource_name
+        self._multiple_resources = isinstance(res_name, list)
+
+        if self._multiple_resources:
+            zip_filenames = [
+                f"{self._country_config.iso3}_adm{i}.shp.zip"
+                for i in range(len(res_name))
+            ]
+            self._hdx_resource_list = res_name
+        else:
+            zip_filenames = [f"{self._country_config.iso3}_adm.shp.zip"]
+            self._hdx_resource_list = [res_name]
+
+        # generate filepaths for all resource zipfiles
+        self._raw_filepaths = [
+            (self._raw_base_dir / fn) for fn in zip_filenames
+        ]
+
+    def download(self, clobber: bool = False) -> Union[Path, List[Path]]:
         """
         Download COD AB file from HDX.
 
@@ -46,7 +62,7 @@ def download(self, clobber: bool = False) -> Path:
 
         Returns
         -------
-        The downloaded filepath
+        The downloaded filepath(s)
 
         Examples
         --------
@@ -56,12 +72,16 @@ def download(self, clobber: bool = False) -> Path:
         >>> codab = CodAB(country_config=country_config)
         >>> npl_cod_shapefile = codab.download()
         """
-        return self._download(
-            filepath=self._raw_filepath,
-            hdx_address=f"cod-ab-{self._country_config.iso3}",
-            hdx_dataset_name=self._datasource_config.hdx_dataset_name,
-            clobber=clobber,
-        )
+        for filepath, hdx_resource_name in zip(
+            self._raw_filepaths, self._hdx_resource_list
+        ):
+            self._download(
+                filepath=filepath,
+                hdx_dataset=f"cod-ab-{self._country_config.iso3}",
+                hdx_resource_name=hdx_resource_name,
+                clobber=clobber,
+            )
+        return self._raw_base_dir
 
     def process(self, *args, **kwargs):
         """
@@ -71,13 +91,13 @@ def process(self, *args, **kwargs):
         """
         logger.info("`process()` method not implemented for CodAB.")
 
-    def load(self, admin_level: int) -> gpd.GeoDataFrame:  # type: ignore
+    def load(self, admin_level: int = 0) -> gpd.GeoDataFrame:  # type: ignore
         """
         Get the COD AB data by admin level.
 
         Parameters
         ----------
-        admin_level: int
+        admin_level: int, default = 0
             The administrative level
 
         Returns
@@ -110,7 +130,8 @@ def load(self, admin_level: int) -> gpd.GeoDataFrame:  # type: ignore
         return self._load_admin_layer(
             layer_name=getattr(
                 self._datasource_config, f"admin{admin_level}_name"
-            )
+            ),
+            admin_level=admin_level,
         )
 
     def load_custom(self, custom_layer_number: int = 0) -> gpd.GeoDataFrame:
@@ -150,37 +171,44 @@ def load_custom(self, custom_layer_number: int = 0) -> gpd.GeoDataFrame:
             layer_name = self._datasource_config.custom_layer_names[
                 custom_layer_number
             ]  # type: ignore
+
         except (IndexError, TypeError) as err:
             raise AttributeError(
                 f"{custom_layer_number}th custom layer requested but not "
                 f"available in {self._country_config.iso3.upper()} config file"
             ) from err
-        return self._load_admin_layer(layer_name=layer_name)
+        return self._load_admin_layer(
+            layer_name=layer_name,
+            admin_level=0,  # breaks if layer in multiple resources
+        )
+
+    def _load_admin_layer(
+        self, layer_name: str, admin_level: int
+    ) -> gpd.GeoDataFrame:
+        fp_index = int(admin_level) if self._multiple_resources else 0
 
-    def _load_admin_layer(self, layer_name: str) -> gpd.GeoDataFrame:
         try:
-            return gpd.read_file(
-                f"zip://{(self._raw_filepath / layer_name).as_posix()}"
-            )
+            zip_path = self._raw_filepaths[fp_index] / layer_name
+            return gpd.read_file(f"zip://{zip_path.as_posix()}")
         except DriverError as err:
             raise FileNotFoundError(
                 f"Could not read boundary shapefile. Make sure that "
                 f"you have already called the 'download' method and "
-                f"that the file {self._raw_filepath} exists. If it does "
-                f"exist, please check the validity of the layer name: "
-                f"'{layer_name}'."
+                f"that the file {self._raw_filepaths[fp_index]} exists."
+                f"If it does exist, please check the validity of the "
+                f"layer name: '{layer_name}'."
             ) from err
 
     @check_file_existence
     def _download(
         self,
         filepath: Path,
-        hdx_address: str,
-        hdx_dataset_name: str,
+        hdx_dataset: str,
+        hdx_resource_name: str,
         clobber: bool,
     ) -> Path:
-        return load_dataset_from_hdx(
-            hdx_address=hdx_address,
-            hdx_dataset_name=hdx_dataset_name,
+        return load_resource_from_hdx(
+            hdx_dataset=hdx_dataset,
+            hdx_resource_name=hdx_resource_name,
             output_filepath=filepath,
         )
diff --git a/src/ochanticipy/datasources/glofas/forecast.py b/src/ochanticipy/datasources/glofas/forecast.py
index 9b0f3b06..7f28c8e3 100644
--- a/src/ochanticipy/datasources/glofas/forecast.py
+++ b/src/ochanticipy/datasources/glofas/forecast.py
@@ -187,7 +187,7 @@ class GlofasReforecast(_GlofasForecastBase):
     Class for downloading and processing GloFAS reforecast data.
 
     The GloFAS reforecast dataset is a global raster presenting river
-    discharnge forecasted from 1999 until 2018, see
+    discharge forecasted from 1999 until 2018, see
     `this paper <https://hess.copernicus.org/preprints/hess-2020-532/>`_
     for more details.
 
@@ -197,8 +197,8 @@ class GlofasReforecast(_GlofasForecastBase):
     and processes it from a raster to a datasets of reporting points from the
     `GloFAS interface
     <https://www.globalfloods.eu/glofas-forecasting/>`_.
-     Due to the CDS request size limits, separate files are downloaded per
-     month (that contain all requested lead times).
+    Due to the CDS request size limits, separate files are downloaded per
+    month (that contain all requested lead times).
 
     Parameters
     ----------
diff --git a/src/ochanticipy/datasources/glofas/glofas.py b/src/ochanticipy/datasources/glofas/glofas.py
index cdb6a9ed..0cb7c3af 100644
--- a/src/ochanticipy/datasources/glofas/glofas.py
+++ b/src/ochanticipy/datasources/glofas/glofas.py
@@ -7,23 +7,30 @@
 from pathlib import Path
 from typing import List, Tuple, Union
 
-import cdsapi
 import numpy as np
 import xarray as xr
 from dateutil import rrule
 
 from ochanticipy.config.countryconfig import CountryConfig
 from ochanticipy.datasources.datasource import DataSource
+from ochanticipy.utils.check_extra_imports import check_extra_imports
 from ochanticipy.utils.dates import get_date_from_user_input
 from ochanticipy.utils.geoboundingbox import GeoBoundingBox
 
 _MODULE_BASENAME = "glofas"
 _HYDROLOGICAL_MODEL = "lisflood"
 _RIVER_DISCHARGE_VAR = "dis24"
+_CDS_MAX_REQUESTS = 500
 _REQUEST_SLEEP_TIME = 60  # seconds
 
 logger = logging.getLogger(__name__)
 
+# putting on top level to ensure easy mocking in tests
+try:
+    import cdsapi
+except ModuleNotFoundError:
+    pass
+
 
 @dataclass
 class _QueryParams:
@@ -104,6 +111,11 @@ def __init__(
             datasource_base_dir=_MODULE_BASENAME,
             is_public=True,
         )
+        # check that extra dependencies are installed
+        check_extra_imports(
+            libraries=["cdsapi", "cfgrib"], subpackage="glofas"
+        )
+
         # The GloFAS API on CDS requires coordinates have the format x.x5
         self._geo_bounding_box = geo_bounding_box.round_coords(
             offset_val=0.05, round_val=0.1
@@ -127,6 +139,15 @@ def __init__(
             dtstart=self._start_date,
             until=self._end_date,
         )
+        if self._date_range.count() > _CDS_MAX_REQUESTS:
+            msg = (
+                f"Your parameters would result in "
+                f"{self._date_range.count()} requests, however we "
+                f"currently only support the CDS maximum of "
+                f"{_CDS_MAX_REQUESTS} at this time. Please divide your "
+                f"query into multiple instances."
+            )
+            raise RuntimeError(msg)
 
     def download(  # type: ignore
         self,
@@ -370,6 +391,7 @@ def _download(
             # hammering on cds
             if query_params_list:
                 time.sleep(_REQUEST_SLEEP_TIME)
+                logger.info(f"Sleeping for {_REQUEST_SLEEP_TIME} s")
         return downloaded_filepaths
 
     def _get_query(
diff --git a/src/ochanticipy/datasources/iri/iri_seasonal_forecast.py b/src/ochanticipy/datasources/iri/iri_seasonal_forecast.py
index 1d7f3eda..d79be7b4 100644
--- a/src/ochanticipy/datasources/iri/iri_seasonal_forecast.py
+++ b/src/ochanticipy/datasources/iri/iri_seasonal_forecast.py
@@ -156,7 +156,6 @@ def _get_processed_path(self) -> Path:
         return self._processed_base_dir / self._get_file_name()
 
     def _get_url(self) -> str:
-
         base_url = (
             "https://iridl.ldeo.columbia.edu/SOURCES/.IRI/.FD/"
             f".NMME_Seasonal_Forecast/.Precipitation_ELR/"
@@ -190,7 +189,6 @@ def _load_raw(self) -> xr.Dataset:
     def _download(
         self, filepath: Path, url: str, iri_auth: str, clobber: bool
     ) -> Path:
-
         response = requests.get(
             url,
             # have to authenticate by using a cookie
diff --git a/src/ochanticipy/utils/check_extra_imports.py b/src/ochanticipy/utils/check_extra_imports.py
new file mode 100644
index 00000000..c14c7467
--- /dev/null
+++ b/src/ochanticipy/utils/check_extra_imports.py
@@ -0,0 +1,25 @@
+"""Check imports that are in extras_require."""
+import importlib.util
+
+
+def check_extra_imports(libraries: list, subpackage: str):
+    """Check that libraries are installed and available.
+
+    Parameters
+    ----------
+    libraries : str
+        List of libraries to check.
+    subpackage : str
+        String of subpackage defined for extra_requires
+        that import should warn to install from.
+    """
+    missing_pkgs = []
+    for library in libraries:
+        if importlib.util.find_spec(library) is None:
+            missing_pkgs += [library]
+
+    if missing_pkgs:
+        raise ModuleNotFoundError(
+            f"{', '.join(missing_pkgs)} were not found. Install the "
+            f"missing packages with `pip install ochanticipy[{subpackage}]`."
+        )
diff --git a/src/ochanticipy/utils/check_file_existence.py b/src/ochanticipy/utils/check_file_existence.py
index bef00355..21936341 100644
--- a/src/ochanticipy/utils/check_file_existence.py
+++ b/src/ochanticipy/utils/check_file_existence.py
@@ -65,7 +65,7 @@ def check_file_existence(
     # check filepath exists -> clobber
     usage_dict = {
         True: {True: "overwriting existing", False: "using existing"},
-        False: {True: "downloading new", False: "downloading new"},
+        False: {True: "creating new", False: "creating new"},
     }
     fp_exists = filepath.exists()
 
diff --git a/src/ochanticipy/utils/hdx_api.py b/src/ochanticipy/utils/hdx_api.py
index efc6e7a9..51e763de 100644
--- a/src/ochanticipy/utils/hdx_api.py
+++ b/src/ochanticipy/utils/hdx_api.py
@@ -15,19 +15,19 @@
 )
 
 
-def load_dataset_from_hdx(
-    hdx_address: str, hdx_dataset_name: str, output_filepath: Path
+def load_resource_from_hdx(
+    hdx_dataset: str, hdx_resource_name: str, output_filepath: Path
 ) -> Path:
     """
     Use the HDX API to download a dataset based on the address and dataset ID.
 
     Parameters
     ----------
-    hdx_address : str
-        The page where the dataset resides on on HDX. Can be found
+    hdx_dataset : str
+        The name of the HDX dataset where the resource is located. Can be found
         by taking the portion of the url after ``data.humdata.org/dataset/``
-    hdx_dataset_name : str
-        Dataset name on HDX. Can be found by taking the filename as it
+    hdx_resource_name : str
+        Resources name on HDX. Can be found by taking the filename as it
         appears on the dataset page.
     output_filepath : Path
         Target filepath for the dataset
@@ -37,12 +37,12 @@ def load_dataset_from_hdx(
     The full path of the downloaded dataset
 
     """
-    logger.info(f"Querying HDX API for dataset {hdx_address}")
-    resources = Dataset.read_from_hdx(hdx_address).get_resources()
+    logger.info(f"Querying HDX API for dataset {hdx_dataset}")
+    resources = Dataset.read_from_hdx(hdx_dataset).get_resources()
     logger.debug(f"Found the following resources: {resources}")
     for resource in resources:
-        if resource["name"] == hdx_dataset_name:
-            logger.info(f"Downloading dataset {hdx_dataset_name}")
+        if resource["name"] == hdx_resource_name:
+            logger.info(f"Downloading dataset {hdx_resource_name}")
             with tempfile.TemporaryDirectory() as tempdir:
                 _, downloaded_filepath = resource.download(folder=tempdir)
                 output_filepath.parent.mkdir(parents=True, exist_ok=True)
@@ -50,6 +50,6 @@ def load_dataset_from_hdx(
             logger.info(f"Saved to {output_filepath}")
             return Path(output_filepath)
     raise FileNotFoundError(
-        f'Dataset with name "{hdx_dataset_name}" not found'
-        f'at HDX address "{hdx_address}".'
+        f'Dataset with name "{hdx_resource_name}" not found'
+        f'at HDX address "{hdx_dataset}".'
     )
diff --git a/src/ochanticipy/utils/raster.py b/src/ochanticipy/utils/raster.py
index 3e9b17ef..04a91007 100644
--- a/src/ochanticipy/utils/raster.py
+++ b/src/ochanticipy/utils/raster.py
@@ -54,13 +54,13 @@ def __init__(self, xarray_obj):
 
         # Adding lat/lon to set of default spatial dims
         if "lat" in self._obj.dims and "lon" in self._obj.dims:
-            self._x_dim = "lon"
-            self._y_dim = "lat"
+            self._x_dim = self._obj.rio._x_dim = "lon"
+            self._y_dim = self._obj.rio._y_dim = "lat"
 
         # Adding Y/X to set of default spatial dims
         if "Y" in self._obj.dims and "X" in self._obj.dims:
-            self._x_dim = "X"
-            self._y_dim = "Y"
+            self._x_dim = self._obj.rio._x_dim = "X"
+            self._y_dim = self._obj.rio._y_dim = "Y"
 
         # Managing time coordinate default dims
         self._t_dim = None
@@ -533,7 +533,7 @@ def compute_raster_stats(
             if percentile_list is not None:
                 grid_quant = [
                     da_clip.quantile(quant / 100, dim=[self.x_dim, self.y_dim])
-                    .drop("quantile")
+                    .drop_vars("quantile")
                     .rename(f"{quant}quant")
                     for quant in percentile_list
                 ]
diff --git a/tox.ini b/tox.ini
deleted file mode 100644
index f95b39df..00000000
--- a/tox.ini
+++ /dev/null
@@ -1,26 +0,0 @@
-[tox]
-isolated_build = true
-envlist =
-    py38
-    py39
-    py310
-    py311
-
-[testenv]
-wheel = true
-recreate = true
-deps = -r requirements/tests.in
-commands =
-    pytest \
-    --cov=ochanticipy \
-    --no-cov-on-fail \
-    --junitxml=.tox/test-results.xml \
-    --cov-report=xml \
-    --cov-report=term-missing
-
-[gh-actions]
-python =
-    3.8: py38
-    3.9: py39
-    3.10: py310
-    3.11: py311


Test patch:
diff --git a/requirements/requirements-tests.txt b/requirements/requirements-tests.txt
deleted file mode 100644
index 71415fb0..00000000
--- a/requirements/requirements-tests.txt
+++ /dev/null
@@ -1,62 +0,0 @@
-#
-# This file is autogenerated by pip-compile with Python 3.11
-# by the following command:
-#
-#    pip-compile --output-file=requirements/requirements-tests.txt requirements/tests.in
-#
-attrs==22.2.0
-    # via pytest
-cachetools==5.3.0
-    # via tox
-cftime==1.6.2
-    # via
-    #   -r requirements/tests.in
-    #   netcdf4
-chardet==5.1.0
-    # via tox
-colorama==0.4.6
-    # via tox
-coverage[toml]==7.1.0
-    # via pytest-cov
-distlib==0.3.6
-    # via virtualenv
-filelock==3.9.0
-    # via
-    #   tox
-    #   virtualenv
-iniconfig==2.0.0
-    # via pytest
-netcdf4==1.6.2
-    # via -r requirements/tests.in
-numpy==1.24.1
-    # via
-    #   cftime
-    #   netcdf4
-packaging==23.0
-    # via
-    #   pyproject-api
-    #   pytest
-    #   tox
-platformdirs==2.6.2
-    # via
-    #   tox
-    #   virtualenv
-pluggy==1.0.0
-    # via
-    #   pytest
-    #   tox
-pyproject-api==1.5.0
-    # via tox
-pytest==7.2.1
-    # via
-    #   -r requirements/tests.in
-    #   pytest-cov
-    #   pytest-mock
-pytest-cov==4.0.0
-    # via -r requirements/tests.in
-pytest-mock==3.10.0
-    # via -r requirements/tests.in
-tox==4.4.4
-    # via -r requirements/tests.in
-virtualenv==20.17.1
-    # via tox
diff --git a/requirements/tests.in b/requirements/tests.in
deleted file mode 100644
index 5e38c873..00000000
--- a/requirements/tests.in
+++ /dev/null
@@ -1,6 +0,0 @@
-cftime
-netCDF4
-pytest
-pytest-cov
-pytest-mock
-tox
diff --git a/tests/datasources/fake_codab_multi.yaml b/tests/datasources/fake_codab_multi.yaml
new file mode 100644
index 00000000..88e264d2
--- /dev/null
+++ b/tests/datasources/fake_codab_multi.yaml
@@ -0,0 +1,5 @@
+iso3: abc
+codab:
+  hdx_resource_name: [fake_hdx_resource_0, fake_hdx_resource_1, fake_hdx_resource_2, fake_hdx_resource_3]
+  layer_base_name: fake_layer_base_name_level{admin_level}
+  admin_level_max: 3
diff --git a/tests/datasources/fake_config.yaml b/tests/datasources/fake_config.yaml
index d6ea950b..593533f8 100644
--- a/tests/datasources/fake_config.yaml
+++ b/tests/datasources/fake_config.yaml
@@ -1,6 +1,6 @@
 iso3: abc
 codab:
-  hdx_dataset_name: fake_hdx_dataset_name
+  hdx_resource_name: fake_hdx_resource_name
   layer_base_name: fake_layer_base_name_level{admin_level}
   admin_level_max: 2
   admin2_name: admin2_custom_name
diff --git a/tests/datasources/test_codab.py b/tests/datasources/test_codab.py
index f7343959..2fb51c8a 100644
--- a/tests/datasources/test_codab.py
+++ b/tests/datasources/test_codab.py
@@ -1,16 +1,25 @@
 """Test COD AB methods."""
+from unittest.mock import call
+
 import pytest
 
-from ochanticipy import CodAB
+from ochanticipy import CodAB, create_custom_country_config
 
 DATASOURCE_BASE_DIR = "cod_ab"
+MULTI_RESOURCE_CONFIG = "tests/datasources/fake_codab_multi.yaml"
+
+
+@pytest.fixture
+def mock_config_multi():
+    """Fixture for CODAB with multiple resources."""
+    return create_custom_country_config(filepath=MULTI_RESOURCE_CONFIG)
 
 
 @pytest.fixture
 def downloader(mocker):
     """Mock the HDX download function."""
     return mocker.patch(
-        "ochanticipy.datasources.codab.codab.load_dataset_from_hdx"
+        "ochanticipy.datasources.codab.codab.load_resource_from_hdx"
     )
 
 
@@ -25,12 +34,30 @@ def test_codab_download(mock_aa_data_dir, mock_country_config, downloader):
     codab = CodAB(country_config=mock_country_config)
     codab.download()
     downloader.assert_called_with(
-        hdx_address=f"cod-ab-{mock_country_config.iso3}",
-        hdx_dataset_name=mock_country_config.codab.hdx_dataset_name,
+        hdx_dataset=f"cod-ab-{mock_country_config.iso3}",
+        hdx_resource_name=mock_country_config.codab.hdx_resource_name,
         output_filepath=mock_aa_data_dir
         / f"public/raw/{mock_country_config.iso3}/"
-        f"{DATASOURCE_BASE_DIR}/{mock_country_config.iso3}_"
-        f"{DATASOURCE_BASE_DIR}.shp.zip",
+        f"{DATASOURCE_BASE_DIR}/{mock_country_config.iso3}_adm.shp.zip",
+    )
+
+
+def test_codab_download_multi(mock_aa_data_dir, mock_config_multi, downloader):
+    """Test that download calls the HDX API to download for multi resources."""
+    codab = CodAB(country_config=mock_config_multi)
+    codab.download()
+    downloader.assert_has_calls(
+        [
+            call(
+                hdx_dataset=f"cod-ab-{mock_config_multi.iso3}",
+                hdx_resource_name=mock_config_multi.codab.hdx_resource_name[i],
+                output_filepath=mock_aa_data_dir
+                / f"public/raw/{mock_config_multi.iso3}/"
+                f"{DATASOURCE_BASE_DIR}/{mock_config_multi.iso3}_"
+                f"adm{i}.shp.zip",
+            )
+            for i in range(4)
+        ]
     )
 
 
@@ -47,7 +74,7 @@ def test_codab_load_admin_level(
     gpd_read_file.assert_called_with(
         f"zip://{mock_aa_data_dir}/public/raw/{mock_country_config.iso3}/"
         f"{DATASOURCE_BASE_DIR}/{mock_country_config.iso3}_"
-        f"{DATASOURCE_BASE_DIR}.shp.zip/{expected_layer_name}"
+        f"adm.shp.zip/{expected_layer_name}"
     )
 
     # Then checking custom name
@@ -57,7 +84,24 @@ def test_codab_load_admin_level(
     gpd_read_file.assert_called_with(
         f"zip://{mock_aa_data_dir}/public/raw/{mock_country_config.iso3}/"
         f"{DATASOURCE_BASE_DIR}/{mock_country_config.iso3}_"
-        f"{DATASOURCE_BASE_DIR}.shp.zip/{expected_layer_name}"
+        f"adm.shp.zip/{expected_layer_name}"
+    )
+
+
+def test_codab_multi_load_admin_level(
+    mock_aa_data_dir, mock_config_multi, gpd_read_file
+):
+    """Test that load_codab retrieves expected file and layer name."""
+    codab = CodAB(country_config=mock_config_multi)
+
+    # First checking layer_base_name
+    expected_layer_name = "fake_layer_base_name_level1"
+    codab.load(admin_level=1)
+
+    gpd_read_file.assert_called_with(
+        f"zip://{mock_aa_data_dir}/public/raw/{mock_config_multi.iso3}/"
+        f"{DATASOURCE_BASE_DIR}/{mock_config_multi.iso3}_"
+        f"adm1.shp.zip/{expected_layer_name}"
     )
 
 
@@ -72,13 +116,14 @@ def test_codab_custom(mock_aa_data_dir, mock_country_config, gpd_read_file):
     """Test that load_codab_custom retrieves expected file and layer name."""
     custom_layer_number = 1
     custom_layer_name_list = ["custom_layer_A", "custom_layer_B"]
+
     mock_country_config.codab.custom_layer_names = custom_layer_name_list
     codab = CodAB(country_config=mock_country_config)
     codab.load_custom(custom_layer_number)
     gpd_read_file.assert_called_with(
         f"zip://{mock_aa_data_dir}/public/raw/{mock_country_config.iso3}/"
         f"{DATASOURCE_BASE_DIR}/{mock_country_config.iso3}_"
-        f"{DATASOURCE_BASE_DIR}.shp.zip/"
+        f"adm.shp.zip/"
         f"{custom_layer_name_list[custom_layer_number]}"
     )
 
@@ -93,8 +138,8 @@ def test_codab_custom_missing(mock_country_config, gpd_read_file):
 def test_codab_load_fail(mock_country_config):
     """Test raises file not found error when load fails."""
     codab = CodAB(country_config=mock_country_config)
-    # Remove file if it exists
-    codab._raw_filepath.unlink(missing_ok=True)
+    # Remove file for admin0 if it exists
+    codab._raw_filepaths[0].unlink(missing_ok=True)
     with pytest.raises(FileNotFoundError) as excinfo:
         codab.load(admin_level=0)
     assert (
diff --git a/tests/datasources/test_glofas.py b/tests/datasources/test_glofas.py
index c21e2f9c..c0052582 100644
--- a/tests/datasources/test_glofas.py
+++ b/tests/datasources/test_glofas.py
@@ -1,4 +1,5 @@
 """Test the GloFAS instantiation."""
+import sys
 from datetime import date
 
 import pytest
@@ -50,11 +51,13 @@ def glofas_forecast(end_date: date, start_date: date = None):
         )
 
     # Try using a string as a date
-    good_end_date = "2022-11-15"
+    good_end_date = "2022-05-15"
     # These should not throw an error
     glofas_forecast(end_date=good_end_date)
     # End date too far in future
-    glofas_future = glofas_forecast(end_date=date(year=3000, month=1, day=1))
+    glofas_future = glofas_forecast(
+        start_date=date.today(), end_date=date(year=3000, month=1, day=1)
+    )
     assert glofas_future._end_date == date.today()
     # Start date too early
     glofas_past = glofas_forecast(
@@ -97,3 +100,36 @@ def glofas_reforecast(start_date: date = None, end_date: date = None):
             start_date=date(year=2020, month=1, day=2),
             end_date=date(year=2020, month=1, day=1),
         )
+
+
+def test_optional_module_error(
+    mock_country_config, geo_bounding_box, monkeypatch
+):
+    """Test module error raised correctly if dependencies missing."""
+    monkeypatch.setitem(sys.modules, "cdsapi", None)  # noqa: FKA01
+    with pytest.raises(ModuleNotFoundError, match=r"ochanticipy"):
+        from ochanticipy import GlofasForecast
+
+        GlofasForecast(
+            country_config=mock_country_config,
+            geo_bounding_box=geo_bounding_box,
+            leadtime_max=1,
+        )
+
+
+def test_optional_module_no_error(monkeypatch):
+    """Test no errors generated on library import w/o dependencies."""
+    monkeypatch.setitem(sys.modules, "cdsapi", None)  # noqa: FKA01
+    import ochanticipy  # noqa: F401
+
+
+def test_max_requests(mock_country_config, geo_bounding_box):
+    """Test that max request number can't be exceeded."""
+    with pytest.raises(RuntimeError):
+        GlofasForecast(
+            country_config=mock_country_config,
+            geo_bounding_box=geo_bounding_box,
+            leadtime_max=15,
+            start_date=date(year=2021, month=5, day=26),
+            end_date=date(year=2023, month=5, day=29),
+        )
diff --git a/tests/datasources/test_glofas_download.py b/tests/datasources/test_glofas_download.py
index 7219144d..cff71ad0 100644
--- a/tests/datasources/test_glofas_download.py
+++ b/tests/datasources/test_glofas_download.py
@@ -28,9 +28,10 @@ def mock_retrieve(mocker):
 @pytest.fixture
 def mock_result(mocker):
     """Mock the entire Result class."""
-    # This is needed because the reply is changed dynamically
-    # so very difficult to use a Mock object
+
     class MockResult:
+        """Reply changes dynamically so need mock object."""
+
         def __init__(self, *args, **kwargs):
             self.reply = {"state": "completed"}
             self.state = None
@@ -61,6 +62,7 @@ def test_reanalysis_download(
     mock_retrieve,
     mock_result,
     geo_bounding_box,
+    mocker,
 ):
     """
     Test GloFAS reanalysis download.
diff --git a/tests/datasources/test_ndvi.py b/tests/datasources/test_ndvi.py
index ec7d1923..0cac41e0 100644
--- a/tests/datasources/test_ndvi.py
+++ b/tests/datasources/test_ndvi.py
@@ -34,7 +34,6 @@ def mock_ndvi(mock_country_config):
     }
 
     def _mock_ndvi(variable: str = "smoothed"):
-
         ndvi = instantiator[variable](
             country_config=mock_country_config,
             start_date=start_date,
diff --git a/tests/test_country_config.py b/tests/test_country_config.py
index dc5a890d..57ff4fa1 100644
--- a/tests/test_country_config.py
+++ b/tests/test_country_config.py
@@ -77,7 +77,7 @@ def test_codab_validate_layer_base_name(mock_parse_yaml):
     config_base = {
         "iso3": "abc",
         "codab": {
-            "hdx_dataset_name": "fake_dataset_name",
+            "hdx_resource_name": "fake_resource_name",
             "layer_base_name": "layer_base_name_",
             "admin_level_max": 1,
         },
@@ -99,7 +99,7 @@ def test_codab_validate_admin_level_max(mock_parse_yaml):
     config_base = {
         "iso3": "abc",
         "codab": {
-            "hdx_dataset_name": "fake_dataset_name",
+            "hdx_resource_name": "fake_resource_name",
             "layer_base_name": "layer_base_name_{admin_level}",
             "admin_level_max": 1,
         },
@@ -122,6 +122,36 @@ def test_codab_validate_admin_level_max(mock_parse_yaml):
         create_country_config(iso3="abc")
 
 
+def test_codab_validate_multiple_resources(mock_parse_yaml):
+    """Test that multiple resources must match admin levels."""
+    config_base = {
+        "iso3": "abc",
+        "codab": {
+            "hdx_resource_name": [f"fake_resource_name_{i}" for i in range(4)],
+            "layer_base_name": "layer_base_name_{admin_level}",
+            "admin_level_max": 3,
+        },
+    }
+    config_correct = copy.deepcopy(config_base)
+    config_incorrect_a = copy.deepcopy(config_base)
+    config_incorrect_a["codab"]["admin_level_max"] = 2
+    config_incorrect_b = copy.deepcopy(config_base)
+    config_incorrect_b["codab"]["admin_level_max"] = 4
+
+    mock_parse_yaml(
+        output_list=[config_correct, config_incorrect_a, config_incorrect_b]
+    )
+
+    # Check that correct config runs without issue
+    create_country_config(iso3="abc")
+    # Check that incorrect config raises error for config incorrect a
+    with pytest.raises(ValueError):
+        create_country_config(iso3="abc")
+    # Check that incorrect config raises error for config incorrect b
+    with pytest.raises(ValueError):
+        create_country_config(iso3="abc")
+
+
 def test_fewsnet_validate_region_name(mock_parse_yaml):
     """Test that fewsnet requires correct region name."""
     config_base = {
diff --git a/tests/utils/test_check_extra_imports.py b/tests/utils/test_check_extra_imports.py
new file mode 100644
index 00000000..ff11d9ef
--- /dev/null
+++ b/tests/utils/test_check_extra_imports.py
@@ -0,0 +1,16 @@
+"""Tests for check_file_existence decorator."""
+
+import pytest
+
+from ochanticipy.utils.check_extra_imports import check_extra_imports
+
+
+def test_import_missing():
+    """Test error raised for missing import."""
+    with pytest.raises(ModuleNotFoundError, match=r"ochanticipy\[a\]"):
+        check_extra_imports(libraries=["asdfasdf"], subpackage="a")
+
+
+def test_import_present():
+    """Test no error for available libraries."""
+    check_extra_imports(libraries=["math", "numpy"], subpackage="a")
diff --git a/tests/utils/test_check_file_existence.py b/tests/utils/test_check_file_existence.py
index aca57182..45cb1c3e 100644
--- a/tests/utils/test_check_file_existence.py
+++ b/tests/utils/test_check_file_existence.py
@@ -43,7 +43,7 @@ def test_fp_not_exists(tmp_path, caplog):
     assert output_filepath == "a"
     assert (
         f"File {path_new} does not exist and clobber set to "
-        "False, downloading new file." in caplog.text
+        "False, creating new file." in caplog.text
     )
 
 
diff --git a/tests/utils/test_hdx_api.py b/tests/utils/test_hdx_api.py
index d1c7cf5e..dd6644f5 100644
--- a/tests/utils/test_hdx_api.py
+++ b/tests/utils/test_hdx_api.py
@@ -3,14 +3,15 @@
 
 import pytest
 
-from ochanticipy.utils.hdx_api import load_dataset_from_hdx
+from ochanticipy.utils.hdx_api import load_resource_from_hdx
 
 
 @pytest.fixture(autouse=True)
 def mock_resource(mocker):
     """Mock the HDX download function."""
-    # Resource is a UserDict so need to make a class to mock that
+
     class MockResource(UserDict):
+        """Resource is a UserDict so need to make a class to mock."""
         def download(self, folder):
             return "", "resource_filepath"
 
@@ -29,9 +30,9 @@ def download(self, folder):
 def test_returns_filepath(tmp_path):
     """Test that querying HDX API returns expected filepath."""
     input_filepath = tmp_path / "hdx_test_path"
-    output_filepath = load_dataset_from_hdx(
-        hdx_address="hdx_address",
-        hdx_dataset_name="resource1",
+    output_filepath = load_resource_from_hdx(
+        hdx_dataset="hdx_address",
+        hdx_resource_name="resource1",
         output_filepath=input_filepath,
     )
     assert output_filepath == input_filepath
@@ -40,8 +41,8 @@ def test_returns_filepath(tmp_path):
 def test_error_when_not_found(tmp_path):
     """Test that missing resource raises error."""
     with pytest.raises(FileNotFoundError):
-        load_dataset_from_hdx(
-            hdx_address="hdx_address",
-            hdx_dataset_name="some_name_not_in_fake_resrouce",
+        load_resource_from_hdx(
+            hdx_dataset="hdx_address",
+            hdx_resource_name="some_name_not_in_fake_resrouce",
             output_filepath=tmp_path / "hdx_test_error",
         )
diff --git a/tests/utils/test_raster.py b/tests/utils/test_raster.py
index dd98a393..d7802afc 100644
--- a/tests/utils/test_raster.py
+++ b/tests/utils/test_raster.py
@@ -65,8 +65,8 @@ def da_2d():
     """Create 2d raster array."""
     da = xr.DataArray(
         [[1, 2, 3], [4, 5, 6]],
-        dims=("y", "x"),
-        coords={"y": [1.5, 0.5], "x": [0.5, 1.5, 2.5]},
+        dims=("Y", "X"),
+        coords={"Y": [1.5, 0.5], "X": [0.5, 1.5, 2.5]},
     ).rio.write_crs("EPSG:4326")
     return da
 
@@ -211,3 +211,16 @@ def test_get_raster_array_crs(ds_3d):
     """Ensure dataset to array works when CRS set."""
     da = ds_3d.oap.get_raster_array("val")
     assert da.rio.crs == ds_3d.rio.crs
+
+
+def test_auto_dim_detect():
+    """Ensure lat/lon dimensions automatically set."""
+    da = xr.DataArray(
+        [[1, 2, 3], [4, 5, 6]],
+        dims=("lat", "lon"),
+        coords={"lat": [1.5, 0.5], "lon": [0.5, 1.5, 2.5]},
+    ).rio.write_crs("EPSG:4326")
+    assert da.oap.x_dim == "lon"
+    assert da.oap.y_dim == "lat"
+    assert da.rio.x_dim == "lon"
+    assert da.rio.y_dim == "lat"