"""Project-wide configuration: paths, units and the canonical NoData value.
Paths are resolved with :mod:`os.path` so that the same constants work on Windows and
POSIX. The original River Architect hard-coded ``"\\\\"`` separators throughout, which is
one of the reasons it could only run on Windows.
Data directories are resolved relative to a *project root*, which is by default the current
working directory. Point :envvar:`RIVERARCHITECT_HOME` at a directory to override it, or
call :func:`set_project_home` at runtime.
"""
import os
__all__ = ["NODATA", "FT2AC", "FT2M", "CFS2CMS", "UNITS",
"package_dir", "templates_dir", "symbology_dir",
"project_home", "set_project_home",
"dir_conditions", "dir_flows", "dir_maps", "dir_output",
"area_unit", "unit_labels"]
APP_ID = "org.riverarchitect.RiverArchitect"
#: Canonical NoData value for every raster River Architect writes.
#:
#: Never test cell values against this constant directly. Go through the declared NoData
#: mask instead (``rasterio.open(...).read(masked=True)``), because third-party inputs carry
#: ``-3.4e38``, ``3.4e38``, ``0`` or ``-9999`` interchangeably. Use
#: :mod:`riverarchitect.tools.reconcile_nodata` to normalise an input condition.
NODATA = -999.0
#: Square feet to acres.
FT2AC = 1.0 / 43560.0
#: Feet to metres.
FT2M = 0.3048
#: Cubic feet per second to cubic metres per second.
CFS2CMS = 0.0283168466
#: Supported unit systems.
UNITS = ("us", "si")
_PROJECT_HOME = None
def icon_path():
return os.path.join(package_dir(), "assets", "icon-v2.png")
[docs]
def package_dir():
"""Directory of the installed :mod:`riverarchitect` package."""
return os.path.dirname(os.path.abspath(__file__))
[docs]
def templates_dir():
"""Directory holding packaged templates (layer styles, workbooks)."""
return os.path.join(package_dir(), "templates")
[docs]
def symbology_dir():
"""Directory holding packaged QGIS layer styles (``.qml``)."""
return os.path.join(templates_dir(), "symbology")
[docs]
def project_home():
"""Root directory of the user's project data.
Resolution order: an explicit :func:`set_project_home`, then the
:envvar:`RIVERARCHITECT_HOME` environment variable, then the current working directory.
"""
if _PROJECT_HOME:
return _PROJECT_HOME
return os.path.abspath(os.environ.get("RIVERARCHITECT_HOME", os.getcwd()))
[docs]
def set_project_home(path):
"""Set the project root that the ``dir_*`` helpers resolve against."""
global _PROJECT_HOME
_PROJECT_HOME = os.path.abspath(path) if path else None
return project_home()
[docs]
def dir_conditions():
"""Directory holding input conditions (one sub-folder per condition)."""
return os.path.join(project_home(), "01_Conditions")
[docs]
def dir_flows():
"""Directory holding flow series and duration workbooks."""
return os.path.join(project_home(), "00_Flows")
[docs]
def dir_maps():
"""Directory holding map output (QGIS projects and PDFs)."""
return os.path.join(project_home(), "02_Maps")
[docs]
def dir_output(module=""):
"""Output directory, optionally for a named module."""
return os.path.join(project_home(), "Output", module) if module \
else os.path.join(project_home(), "Output")
[docs]
def area_unit(unit="us"):
"""Area unit label for a unit system: ``'sqft'`` or ``'sqm'``."""
return "sqft" if str(unit).lower() == "us" else "sqm"
[docs]
def unit_labels(unit="us"):
"""Discharge, depth, velocity, length and volume labels for a unit system.
Returns:
dict: keys ``q``, ``h``, ``u``, ``length``, ``volume``, ``area``.
"""
if str(unit).lower() == "us":
return {"q": "cfs", "h": "ft", "u": "ft/s", "length": "ft",
"volume": "cubic yard", "area": "sqft"}
return {"q": "cms", "h": "m", "u": "m/s", "length": "m",
"volume": "cubic meter", "area": "sqm"}