-
Notifications
You must be signed in to change notification settings - Fork 1
GeoDMS through Python
GeoDMS and Python can be combined in three different ways. Which one fits best depends on who is "in charge" (Python or the GeoDMS) and on whether the two need to share data in-process or through data files.
| # | Architecture | Who drives | Data exchange |
|---|---|---|---|
| 1 | Run the GeoDMS as a separate process from Python | Python | data files (the GeoDMS reads/writes its configured storages) |
| 2 | Run Python as a separate process from within the GeoDMS | GeoDMS | data files (parquet / csv) |
| 3 | Use the GeoDMS Python bindings (in-process) | Python | in-process, through the geodms module |
Python orchestrates the workflow and calls the command line tool GeoDMSRun (GeoDmsRun.exe) to let the GeoDMS update one or more items of a configuration. The GeoDMS reads its inputs and writes its results to the files/databases configured as storages (see Data Source); Python reads those results back.
This requires no GeoDMS-specific Python code and keeps the two processes fully isolated.
import subprocess
exit_code = subprocess.call([
r"C:\Program Files\ObjectVision\GeoDms\GeoDmsRun.exe",
r"/L", r"C:\tmp\run.log", # write a log file
r"C:\prj\my_project\cfg\model.dms", # the configuration
r"/export/result" # the item (path relative to the desktop root) to update
])
if exit_code != 0:
raise RuntimeError(f"GeoDmsRun failed with exit code {exit_code}")
# now read the file that /export/result was configured to write (e.g. a .parquet or .csv)See GeoDMSRun and User Guide GeoDMS RUN for the available command line options.
The GeoDMS drives the workflow: it writes an input file, starts a Python script on it, and reads the script's output back — all triggered automatically the moment the output item is requested. This is useful when a calculation is easier (or only possible) in Python, but it should fit transparently inside a GeoDMS configuration.
The mechanism combines three features:
- write the input as a typed, fast parquet file (see parquet);
- start the script with exec_ec, capturing its ExitCode;
- read the output, with the ExitCode woven into the output StorageName so the read waits until the script has finished.
A worked end-to-end example (write input → run script → read output) is documented on the exec_ec page. The conditional, lazy triggering of such a script — only running it when its result is actually needed — is described on the Indirect-expression page.
Relevant pages:
- exec_ec — start the script and gate the read on its ExitCode
- parquet — exchange typed tabular data with Python (pandas / pyarrow), and relate the imported domain back to an existing domain
- Indirect-expression — only calculate (and run) what is needed
The GeoDMS ships a geodms Python module (PyDms.pyd) that embeds the GeoDMS engine inside the Python process. Python can then load a configuration, navigate the item tree, change expressions, update items and read values back without starting a separate process. See Python Bindings for installation and version notes.
The bindings are a work in progress. Since GeoDMS 20.5.0 the exposed set has grown beyond orchestrating and inspecting a configuration: a configuration can also be built in memory (containers, units, attributes, parameters — no model script), parameter and attribute values can be set, and results can be read back through the Primary Data Access functions. See Python Bindings for the full, version-annotated API reference and worked examples.
The exposed API (from python/dll/src/Bindings.cpp), in brief:
module geodms — version(), and the ValueComposition and ValueTypeId enums (20.5.0+).
class Engine — Engine(), load_config(file) → Config, and (20.5.0+)
create_config_root(name) → Config, default_unit(value_type), void_unit().
class Config — root() → MutableTreeItem, const_root() → ConstTreeItem (20.5.0+).
class ConstTreeItem / MutableTreeItem — is_null(), find(path), name(), expr(),
update(), isDataItem()/asDataItem(), isUnitItem()/asUnitItem(); and (20.5.0+)
full_name(), descr(), sub_items(), parent(), fail_reason().
MutableTreeItem adds set_expr(str), asConst() and the configuration-building methods
add_container, create_unit, add_data_item, add_attribute, add_param,
set_storage_manager, disable_storage (all 20.5.0+).
class UnitItem / MutableUnitItem (20.5.0+) — value_type_id() (a ValueTypeId enum),
count(), get_range(), and set_count(n) / set_range(b, e) on the mutable variant.
class DataItem / MutableDataItem — read values with LockAndGetStringValue(i) and (20.5.0+)
get_value_as_float/int/str(i), get_values_as_float_list(), size(), domain_unit(),
values_unit(); write values with set_value_as_float/int, set_values_from_float_list,
and the parameter helpers set_param_float/int/str, get_param_float.
Adapted from python/tst/UnitTests.py:
import os
import sys
# make the geodms module and its dependencies (e.g. gdal.dll) findable
geodms_path = os.path.abspath('../../bin/Release/x64')
sys.path.append(geodms_path)
os.environ['PATH'] += os.pathsep + geodms_path
from geodms import *
# initialise the engine and load a configuration
engine = Engine()
config = engine.load_config('basic_data_test.dms')
# navigate the item tree from the root
root = config.root()
param_item = root.find("/parameters/test_param")
print(param_item.is_null()) # False if found
# find returns a null item for non-existent paths
missing = root.find("/does/not/exist")
print(missing.is_null()) # True
# change an expression and (re)calculate a dependent result
param_item.set_expr("3b")
result_item = root.find("/export/IntegerAtt")
result_item.update() # updates the item and writes its storage, if configuredGeoDMS ©Object Vision BV. Source code distributed under GNU GPL-3. Documentation distributed under CC BY-SA 4.0.