Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 108 additions & 20 deletions python/grass/temporal/extract.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,22 @@
:authors: Soeren Gebbert
"""

from __future__ import annotations

import re
import sys
from multiprocessing import Process

import grass.script as gs
from grass.exceptions import CalledModuleError

from .abstract_map_dataset import AbstractMapDataset

from .core import (
SQLDatabaseInterfaceConnection,
get_current_mapset,
get_tgis_message_interface,
get_tgis_db_version,
)
from .datetime_math import (
create_numeric_suffix,
Expand All @@ -31,6 +36,82 @@
############################################################################


def compile_new_map_name(
sp,
base: str,
count: int,
map_id: str,
semantic_label: str | None,
time_suffix: str | None,
dbif: SQLDatabaseInterfaceConnection,
):
"""Compile new map name with suffix and semantic label.

:param sp: An open SpaceTimeDataSet (STDS)
:param count: Running number of the map to be used as numeric suffix (if not time suffix)
:param map_id: Map ID to compile new map name for
:param time_suffix: Type of time suffix to use (or None)
:param dbif: initialized TGIS database interface
"""
if semantic_label:
base = f"{base}_{semantic_label}"
if (
sp.get_temporal_type() != "absolute"
or not time_suffix
or time_suffix.startswith("num")
):
return create_numeric_suffix(base, count, time_suffix)
old_map = sp.get_new_map_instance(map_id)
old_map.select(dbif)
if time_suffix == "gran":
suffix = create_suffix_from_datetime(
old_map.temporal_extent.get_start_time(), sp.get_granularity()
)
else:
suffix = create_time_suffix(old_map)
return f"{base}_{suffix}"


def replace_stds_names(expression: str, simple_name: str, full_name: str) -> str:
"""Safely replace simple with full STDS names.

When users provide inconsistent input for STDS in the expression
(with and without mapset componenet) or if the STDS name is part
of the name of other raster maps in the expression, the final
mapcalc expression may become invalid when the STDS name later is
replaced with the name of the individual maps in the time series.
The replacement with the fully qualified STDS names avoids that
confusion.

:param expression: The mapcalc expression to replace names in
:param simple_name: STDS name *without* mapset component
:param full_name: STDS name *with* mapset component
"""
separators = r""" ,-+*/^:&|"'`()<>#^"""
name_matches = re.finditer(simple_name, expression)
new_expression = ""
old_idx = 0
for match in name_matches:
# Fill-in expression component between matches
new_expression += expression[old_idx : match.start()]
# Only replace STDS name if pre- and succeeded by a separator
# Match is either at the start or preceeeded by a separator
if match.start() == 0 or expression[match.start() - 1] in separators:
# Match is either at the end or succeeded by a separator
if (
match.end() + 1 > len(expression)
or expression[match.end()] in separators
):
new_expression += full_name
else:
new_expression += simple_name
else:
new_expression += simple_name
old_idx = match.end()
new_expression += expression[old_idx:]
return new_expression


def extract_dataset(
input,
output,
Expand Down Expand Up @@ -82,13 +163,24 @@ def extract_dataset(
dbif = SQLDatabaseInterfaceConnection()
dbif.connect()

tgis_version = get_tgis_db_version()

sp = open_old_stds(input, type, dbif)
has_semantic_labels = bool(
tgis_version > 2 and type == "raster" and sp.metadata.semantic_labels
)

# Check the new stds
new_sp = check_new_stds(output, type, dbif, gs.overwrite())
if type == "vector":
rows = sp.get_registered_maps("id,name,mapset,layer", where, "start_time", dbif)
else:
rows = sp.get_registered_maps("id", where, "start_time", dbif)
rows = sp.get_registered_maps(
f"id{',semantic_label' if has_semantic_labels else ''}",
where,
"start_time",
dbif,
)

new_maps = {}
if rows:
Expand All @@ -102,33 +194,30 @@ def extract_dataset(
proc_count = 0
proc_list = []

# Make sure STRDS is in the expression referenced with fully qualified name
expression = replace_stds_names(
expression, sp.base.get_name(), sp.base.get_map_id()
)
for row in rows:
count += 1

if count % 10 == 0:
msgr.percent(count, num_rows, 1)

if sp.get_temporal_type() == "absolute" and time_suffix == "gran":
old_map = sp.get_new_map_instance(row["id"])
old_map.select(dbif)
suffix = create_suffix_from_datetime(
old_map.temporal_extent.get_start_time(), sp.get_granularity()
)
map_name = "{ba}_{su}".format(ba=base, su=suffix)
elif sp.get_temporal_type() == "absolute" and time_suffix == "time":
old_map = sp.get_new_map_instance(row["id"])
old_map.select(dbif)
suffix = create_time_suffix(old_map)
map_name = "{ba}_{su}".format(ba=base, su=suffix)
else:
map_name = create_numeric_suffix(base, count, time_suffix)
map_name = compile_new_map_name(
sp,
base,
count,
row["id"],
row["semantic_label"] if has_semantic_labels else None,
time_suffix,
dbif,
)

# We need to modify the r(3).mapcalc expression
if type != "vector":
expr = expression
expr = expr.replace(sp.base.get_map_id(), row["id"])
expr = expr.replace(sp.base.get_name(), row["id"])

expr = "%s = %s" % (map_name, expr)

# We need to build the id
Expand Down Expand Up @@ -273,9 +362,8 @@ def extract_dataset(

if type == "raster":
# Set the semantic label
semantic_label = old_map.metadata.get_semantic_label()
if semantic_label is not None:
new_map.set_semantic_label(semantic_label)
if has_semantic_labels:
new_map.set_semantic_label(row["semantic_label"])

# Insert map in temporal database
new_map.insert(dbif)
Expand Down
Loading
Loading