Skip to content

temporal: t.rast.aggregate: add -e flag to extend existing STRDS#7223

Open
Sourish-spc wants to merge 23 commits intoOSGeo:mainfrom
Sourish-spc:temporal/t.rast.aggregate-extend-mode
Open

temporal: t.rast.aggregate: add -e flag to extend existing STRDS#7223
Sourish-spc wants to merge 23 commits intoOSGeo:mainfrom
Sourish-spc:temporal/t.rast.aggregate-extend-mode

Conversation

@Sourish-spc
Copy link
Copy Markdown
Contributor

Description

This PR adds support for extending an existing Space Time Raster Dataset (STRDS) in t.rast.aggregate by introducing a new -e flag, following the same pattern implemented in #3798 for t.rast.neighbors.

Changes

  • Added -e flag (Extend existing space time raster dataset) to module interface
  • Modified STRDS creation logic to open existing STRDS when -e flag is set instead of always creating a new one
  • Added history update via update_command_string() when extending existing STRDS
  • Added test test_extend_existing_strds to existing test suite

Usage

First run — creates new STRDS:
t.rast.aggregate input=daily_rain output=monthly_rain granularity="1 month" method=average basename=result

Second run — extends existing STRDS:
t.rast.aggregate input=daily_rain output=monthly_rain granularity="1 month" method=average basename=result_new -e

Related

Closes part of #3427
See also #3798

In stds_import.py, the finally block used the deprecated 'gisdbase'
option when calling g.mapset to switch back to the original location.
This caused a deprecation warning on every t.rast.import run with
the project parameter.

Replaced 'gisdbase' with 'dbase' to match the current g.mapset API.

Fixes OSGeo#4231
@github-actions github-actions bot added temporal Related to temporal data processing Python Related code is in Python libraries module tests Related to Test Suite labels Mar 27, 2026
@Sourish-spc Sourish-spc force-pushed the temporal/t.rast.aggregate-extend-mode branch from 7ee4adc to 9b2110e Compare March 27, 2026 07:45
Copy link
Copy Markdown
Member

@ninsbl ninsbl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please also install pre-commit in your local working directory to avoid CI runs for formalities...

Comment on lines +162 to +168
gs.fatal(
_(
"Space time raster dataset <%s> does not exist. "
"Cannot extend a non-existing dataset."
)
% output
)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Personally, I do not think a fatal error should be thrown here. I would say, a warning at best. In any case, this part should be consistent across all temporal tools that receive the e-flag.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated: changed fatal error to warning and fall back to creating new STRDS when dataset doesn't exist.

# Verify STRDS was extended
lister = SimpleModule("t.rast.list", input="B", columns="name", flags="u")
self.runModule(lister)
self.assertIn("b_ext", lister.outputs.stdout)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This only checks that some map with "b_ext" in the name exists. It doesn't verify the original maps are still present, that the STRDS wasn't just overwritten, and/or the total map count.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the review. The test has been strengthened to also verify that original maps are still present and the total map count increased after extending.

Comment on lines +258 to +259
if output_exists:
output_strds.update_command_string(dbif=dbif)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this make sense when overwriting?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, iI did not really think about it. It may make sense in any case. I did not check the behavior. Updates of the STDS should be written to the history I`ld say, which is what update_command_string does. The command should not be duplicated in the history though. That should be checked...

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated the condition to if not output_exists or extend to avoid duplicating the command string in history when overwriting, while still updating it correctly when extending.


if __name__ == "__main__":
options, flags = gs.parser()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please, review your code, including the PR changes.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the review. The extra blank line has been removed in the latest commit.

Copy link
Copy Markdown
Member

@ninsbl ninsbl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please keep in mind that the code in open_stds pre-dates Python 3 and has not been re-factored after since.
We should not do re-factoring as part of this PR, but don`t repeat outdated pattern in stuff you add....

Comment on lines +243 to +262
dbif, connection_state_changed = init_dbif(dbif)
msgr = get_tgis_message_interface()

mapset = get_current_mapset()
id = name + "@" + mapset if name.find("@") < 0 else name

if type in {"strds", "rast", "raster"}:
sp = dataset_factory("strds", id)
elif type in {"str3ds", "raster3d", "rast3d", "raster_3d"}:
sp = dataset_factory("str3ds", id)
elif type in {"stvds", "vect", "vector"}:
sp = dataset_factory("stvds", id)
else:
msgr.fatal(_("Unknown type: %s") % (type))
if extend and sp.is_in_db(dbif):
# extend-if-exists: load and return existing dataset
sp.select(dbif)
if connection_state_changed:
dbif.close()
return sp
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You duplicate quite a bit of code... You may consider adding a helper function similar to e.g. (untested, so not suitable for copy and paste!):

def get_stds(stds_id: str, stds_type: str) -> SpaceTimeDataset:
    """Return an initialized SpaceTimeDataset (STDS)."""
    msgr = get_tgis_message_interface()
    supported_stds_types = {
        "strds": "strds",
        "rast": "strds",
        "raster": "strds",
        "str3ds": "str3ds",
        "raster3d": "str3ds",
        "rast3d": "str3ds",
        "raster_3d": "str3ds",
        "stvds": "stvds",
        "vect": "stvds",
        "vector": "stvds",
    }
    if stds_type not in supported_stds_types:
        msgr.fatal(_("Unknown type: %s") % (type))
    return dataset_factory(supported_stds_types[stds_type], stds_id)]

Copy link
Copy Markdown
Contributor Author

@Sourish-spc Sourish-spc Apr 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added _get_stds helper function to avoid repeating the type mapping block, and ensure_id helper to avoid repeating the id construction pattern. Kept them private ( prefix) since they're only used internally in open_stds.py happy to make them public if needed.

Copy link
Copy Markdown
Member

@ninsbl ninsbl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now we are getting somewhere. Almost done. I just had some hopefully final, minor comments.
Once those are addressed, this can be approved as far as I am concerned. I would appreciate a second opinion, especially on having the new helper function public or private...


dbif, connection_state_changed = init_dbif(dbif)

if sp.is_in_db(dbif) and overwrite is False:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The old syntax is preferable. See: https://docs.astral.sh/ruff/rules/collapsible-if/

dbif, connection_state_changed = init_dbif(dbif)
output_exists = sp.is_in_db(dbif)

if output_exists:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See the comment above about collapsible-if...

"""
msgr = get_tgis_message_interface()

if "@" in name:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can skip this check if you _ensure-id before.
If ensure_id is a public function and used in t.rast.aggregate, the new functions you add here could expect an id as input and those checks are not needed at all. Yet, they are insignificant and may be safer to double-check. No strong opinion from my side on this...

)

# Check original maps are still present (STRDS was not overwritten)
for original_map in original_maps.strip().split(os.linesep):
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe use all() or a set() based check and only one assertion...

self.assertIn(original_map, extended_maps)

# Check total map count increased (original 3 + extended 3 = 6)
info = SimpleModule("t.info", flags="g", input="B")
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You get the number of maps from t.rast.list. No need to run t.info in addition for this check. If you want to check that the STRDS history is updated properly (command added to comments) you need to.info...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

libraries module Python Related code is in Python temporal Related to temporal data processing tests Related to Test Suite

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants