Source code for ogstools.ogs6py.geo
# SPDX-FileCopyrightText: Copyright (c) OpenGeoSys Community (opengeosys.org)
# SPDX-License-Identifier: BSD-3-Clause
import shutil
from pathlib import Path
from lxml import etree as ET
from ogstools.core.storage import StorageBase
from ogstools.ogs6py import build_tree
[docs]
class Geo(build_tree.BuildTree, StorageBase):
"""
Class managing the geometry file (.gml) for an OGS project.
Tracks both the XML reference to the geometry and the actual file,
enabling proper save/copy operations.
"""
__hash__ = None
[docs]
def __init__(
self,
tree: ET.ElementTree,
id: str | None = None,
) -> None:
"""
Initialize a Geo object.
:param tree: The Project's XML ElementTree (shared reference)
:param source_path: Path to the source .gml file (if known)
:param id: Optional unique identifier
"""
build_tree.BuildTree.__init__(self, tree)
StorageBase.__init__(self, "Geo", "gml", id=id)
self.root = self.tree.getroot()
# Ensure <geometry> element exists in tree
self.populate_tree(self.root, "geometry", overwrite=True)
@property
def filename(self) -> str | None:
"""Get the geometry filename from the XML tree."""
geo_elem = self.root.find("geometry")
if geo_elem is not None and geo_elem.text:
return geo_elem.text.strip() or None
return None
@property
def has_geometry(self) -> bool:
"""Check if geometry is defined (either as file or inline in XML)."""
# Check if geometry element has content (inline definition)
return self.root.find("geometry") is not None
[docs]
def add_geometry(self, file_pathname: str | Path) -> None:
"""
Add/set a geometry file.
:param filename: The file path and name of the gml file
"""
file_pathname = Path(file_pathname)
self._bind_to_path(file_pathname)
self.populate_tree(
self.root,
"geometry",
text=str(file_pathname.name),
overwrite=True,
)
def _propagate_target(self) -> None:
"""No children to propagate to."""
def _save_impl(self, dry_run: bool = False) -> list[Path]:
"""
Save the geometry file to the target location.
:param dry_run: If True, don't actually copy the file
:returns: List of saved file paths
"""
if not self.filename:
return []
target = self.next_target
if dry_run:
return [target]
# Ensure parent directory exists
target.parent.mkdir(parents=True, exist_ok=True)
# Copy the file if source != target
if (
self.active_target
and self.active_target.exists()
and self.active_target.resolve() != target.resolve()
):
shutil.copy2(self.active_target, target)
return [target]
[docs]
def save(
self,
target: Path | str | None = None,
overwrite: bool | None = None,
dry_run: bool = False,
archive: bool = False,
id: str | None = None,
) -> list[Path]:
"""
Save the geometry file.
:param target: Optional target path
:param overwrite: If True, overwrite existing files
:param dry_run: If True, simulate without writing
:param archive: If True, materialize symlinks
:param id: Optional identifier. Mutually exclusive with target.
:returns: List of saved file paths
"""
if not self.has_geometry:
return []
user_defined = self._pre_save(target, overwrite, dry_run, id=id)
files = self._save_impl(dry_run)
if files: # Only post_save if we actually saved something
self._post_save(user_defined, archive, dry_run)
return files
def __eq__(self, other: object) -> bool:
if not isinstance(other, Geo):
return NotImplemented
return self.filename == other.filename
def __repr__(self) -> str:
return f"Geo(filename={self.filename!r}, is_saved={self.is_saved})"
def __str__(self) -> str:
if not self.has_geometry:
return "Geo: (no geometry defined)"
return f"Geo: {self.filename})"