99:authors: Soeren Gebbert
1010"""
1111
12+ from __future__ import annotations
13+
14+ import re
1215import sys
1316from multiprocessing import Process
1417
1518import grass .script as gs
1619from grass .exceptions import CalledModuleError
1720
1821from .abstract_map_dataset import AbstractMapDataset
22+
1923from .core import (
2024 SQLDatabaseInterfaceConnection ,
2125 get_current_mapset ,
2226 get_tgis_message_interface ,
27+ get_tgis_db_version ,
2328)
2429from .datetime_math import (
2530 create_numeric_suffix ,
3136############################################################################
3237
3338
39+ def compile_new_map_name (
40+ sp ,
41+ base : str ,
42+ count : int ,
43+ map_id : str ,
44+ semantic_label : str | None ,
45+ time_suffix : str | None ,
46+ dbif : SQLDatabaseInterfaceConnection ,
47+ ):
48+ """Compile new map name with suffix and semantic label.
49+
50+ :param sp: An open SpaceTimeDataSet (STDS)
51+ :param count: Running number of the map to be used as numeric suffix (if not time suffix)
52+ :param map_id: Map ID to compile new map name for
53+ :param time_suffix: Type of time suffix to use (or None)
54+ :param dbif: initialized TGIS database interface
55+ """
56+ if semantic_label :
57+ base = f"{ base } _{ semantic_label } "
58+ if (
59+ sp .get_temporal_type () != "absolute"
60+ or not time_suffix
61+ or time_suffix .startswith ("num" )
62+ ):
63+ return create_numeric_suffix (base , count , time_suffix )
64+ old_map = sp .get_new_map_instance (map_id )
65+ old_map .select (dbif )
66+ if time_suffix == "gran" :
67+ suffix = create_suffix_from_datetime (
68+ old_map .temporal_extent .get_start_time (), sp .get_granularity ()
69+ )
70+ else :
71+ suffix = create_time_suffix (old_map )
72+ return f"{ base } _{ suffix } "
73+
74+
75+ def replace_stds_names (expression : str , simple_name : str , full_name : str ) -> str :
76+ """Safely replace simple with full STDS names.
77+
78+ When users provide inconsistent input for STDS in the expression
79+ (with and without mapset componenet) or if the STDS name is part
80+ of the name of other raster maps in the expression, the final
81+ mapcalc expression may become invalid when the STDS name later is
82+ replaced with the name of the individual maps in the time series.
83+ The replacement with the fully qualified STDS names avoids that
84+ confusion.
85+
86+ :param expression: The mapcalc expression to replace names in
87+ :param simple_name: STDS name *without* mapset component
88+ :param full_name: STDS name *with* mapset component
89+ """
90+ separators = r""" ,-+*/^:&|"'`()<>#^"""
91+ name_matches = re .finditer (simple_name , expression )
92+ new_expression = ""
93+ old_idx = 0
94+ for match in name_matches :
95+ # Fill-in expression component between matches
96+ new_expression += expression [old_idx : match .start ()]
97+ # Only replace STDS name if pre- and succeeded by a separator
98+ # Match is either at the start or preceeeded by a separator
99+ if match .start () == 0 or expression [match .start () - 1 ] in separators :
100+ # Match is either at the end or succeeded by a separator
101+ if (
102+ match .end () + 1 > len (expression )
103+ or expression [match .end ()] in separators
104+ ):
105+ new_expression += full_name
106+ else :
107+ new_expression += simple_name
108+ else :
109+ new_expression += simple_name
110+ old_idx = match .end ()
111+ new_expression += expression [old_idx :]
112+ return new_expression
113+
114+
34115def extract_dataset (
35116 input ,
36117 output ,
@@ -82,13 +163,24 @@ def extract_dataset(
82163 dbif = SQLDatabaseInterfaceConnection ()
83164 dbif .connect ()
84165
166+ tgis_version = get_tgis_db_version ()
167+
85168 sp = open_old_stds (input , type , dbif )
169+ has_semantic_labels = bool (
170+ tgis_version > 2 and type == "raster" and sp .metadata .semantic_labels
171+ )
172+
86173 # Check the new stds
87174 new_sp = check_new_stds (output , type , dbif , gs .overwrite ())
88175 if type == "vector" :
89176 rows = sp .get_registered_maps ("id,name,mapset,layer" , where , "start_time" , dbif )
90177 else :
91- rows = sp .get_registered_maps ("id" , where , "start_time" , dbif )
178+ rows = sp .get_registered_maps (
179+ f"id{ ',semantic_label' if has_semantic_labels else '' } " ,
180+ where ,
181+ "start_time" ,
182+ dbif ,
183+ )
92184
93185 new_maps = {}
94186 if rows :
@@ -102,33 +194,30 @@ def extract_dataset(
102194 proc_count = 0
103195 proc_list = []
104196
197+ # Make sure STRDS is in the expression referenced with fully qualified name
198+ expression = replace_stds_names (
199+ expression , sp .base .get_name (), sp .base .get_map_id ()
200+ )
105201 for row in rows :
106202 count += 1
107203
108204 if count % 10 == 0 :
109205 msgr .percent (count , num_rows , 1 )
110206
111- if sp .get_temporal_type () == "absolute" and time_suffix == "gran" :
112- old_map = sp .get_new_map_instance (row ["id" ])
113- old_map .select (dbif )
114- suffix = create_suffix_from_datetime (
115- old_map .temporal_extent .get_start_time (), sp .get_granularity ()
116- )
117- map_name = "{ba}_{su}" .format (ba = base , su = suffix )
118- elif sp .get_temporal_type () == "absolute" and time_suffix == "time" :
119- old_map = sp .get_new_map_instance (row ["id" ])
120- old_map .select (dbif )
121- suffix = create_time_suffix (old_map )
122- map_name = "{ba}_{su}" .format (ba = base , su = suffix )
123- else :
124- map_name = create_numeric_suffix (base , count , time_suffix )
207+ map_name = compile_new_map_name (
208+ sp ,
209+ base ,
210+ count ,
211+ row ["id" ],
212+ row ["semantic_label" ] if has_semantic_labels else None ,
213+ time_suffix ,
214+ dbif ,
215+ )
125216
126217 # We need to modify the r(3).mapcalc expression
127218 if type != "vector" :
128219 expr = expression
129220 expr = expr .replace (sp .base .get_map_id (), row ["id" ])
130- expr = expr .replace (sp .base .get_name (), row ["id" ])
131-
132221 expr = "%s = %s" % (map_name , expr )
133222
134223 # We need to build the id
@@ -273,9 +362,8 @@ def extract_dataset(
273362
274363 if type == "raster" :
275364 # Set the semantic label
276- semantic_label = old_map .metadata .get_semantic_label ()
277- if semantic_label is not None :
278- new_map .set_semantic_label (semantic_label )
365+ if has_semantic_labels :
366+ new_map .set_semantic_label (row ["semantic_label" ])
279367
280368 # Insert map in temporal database
281369 new_map .insert (dbif )
0 commit comments