Skip to content

Python bindings

MaartenHilferink edited this page Nov 10, 2026 · 27 revisions

The geodms bindings are a work in progress. The Windows setups provide interfaces for 64-bit CPython 3.12, 3.13 and 3.14; 3.9 is provided only by the GLOBIO (.g) setup.

This page describes the geodms module, which embeds the GeoDMS engine inside Python (load a configuration, find items, update them). This is a different mechanism from exchanging data files between a GeoDMS configuration and a separate Python script: for that, write/read tabular data as parquet (or csv) and, if needed, let the GeoDMS run the script on demand via exec_ec. See GeoDMS through Python for an overview of all three integration architectures.

The setup contains one ABI-tagged geodms extension per supported CPython minor. Python automatically selects its matching file. Release builders supply the development installations through PYTHON312_ROOT, PYTHON313_ROOT, and PYTHON314_ROOT (and PYTHON39_ROOT for the GLOBIO flavour), or the corresponding ABI-specific *_INCLUDE_DIR and *_LIBRARIES pairs; the setup does not bundle Python itself.

The code examples below show how to use the functions that the geodms module exposes: loading a configuration and querying it, building a configuration in memory, setting parameter values and reading results back.

In order to import the geodms module in python make sure the installation folder is added to your path so that the module can be found:

import sys
sys.path.append('C:/"Program Files"/ObjectVision/GeoDms14.15.2')
from geodms import *

Retrieving the geodms version:

dms_version = version()

Initializing the geodms module:

dms_engine = Engine()

Loading and querying a configuration

Loading a dms configuration:

dms_config = dms_engine.load_config('./prj/config.dms')

Get the root item of a loaded dms configuration:

root = dms_config.root()

Finding an item in a loaded dms configuration from the root item:

parameters = root.find("/parameters")

Testing if the item is found by asserting that it is not null:

if (parameters.is_null()):
    raise Exception(f"Cannot find output item: '/parameters'")

Get the name (or full path) of a found item:

item_name      = parameters.name()
item_full_name = parameters.full_name()

Get the dms expression of a found item:

item_expr = parameters.expr()

Iterating over the direct sub-items of a container (the kind of an item follows from isUnitItem() / isDataItem()):

for child in root.sub_items():
    kind = 'unit' if child.isUnitItem() else ('attr' if child.isDataItem() else 'container')
    print(child.name(), kind)

Inspecting an item that failed to calculate:

reason = some_item.fail_reason()   # empty string when the item is valid

Listing the red items of a configuration

The GUI colours an item red when determining its meta info has failed. update_metainfo() runs exactly that step and reports its outcome:

ok = some_item.update_metainfo()   # False when the item failed
if not ok:
    print(some_item.full_name(), some_item.fail_reason())

update_metainfo() resolves the calculation rule, the domain and values units, the storage and the sub-items the rule implies — everything a configuration error shows up in — and reads no primary data. update() does read primary data, of the item and of all of its suppliers, which for a raster attribute is a full calculation; that is why a configuration-wide check must use update_metainfo() and not update().

Template bodies are inert and are not meant to be updated at all, so a walker skips them with in_template() (true for a template and for everything inside one; is_template() is true for the template item itself):

def red_items(item):
    if item.in_template():
        return
    if not item.update_metainfo():
        yield item.full_name(), item.fail_reason()
    for child in item.sub_items():
        yield from red_items(child)

for name, reason in red_items(config.const_root()):
    print(f"{name}: {reason}")

An item that failed an earlier calculation reports False here too, exactly as the tree-view colours it red: its meta info is fine, but the item is not valid.

Change the expression of a found item:

out_dir = root.find("/parameters/input/OutDir")
out_dir.set_expr("path/to/new/out/dir")

Update an item:

target_item_fullname = "dms/target/item"
target_item = root.find(target_item_fullname)
if target_item.is_null():
  raise Exception(f"Cannot find output item: {target_item}")

target_item.update()

Building a configuration in memory

Instead of loading a .dms model script, a configuration can be built from scratch in Python. Start from an empty configuration root:

engine = Engine()
config = engine.create_config_root('demo')   # an empty in-memory configuration
root   = config.root()

Add sub-containers, units, attributes and parameters:

# a sub-container
data = root.add_container('data')

# a domain unit of a basic value type, sized by an expression. A domain's element
# count comes from its calculation rule; range(uint32, 0, n) defines an n-element domain.
entities = root.create_unit('Entity', 'uint32')
entities.set_expr('range(uint32, 0, 5)')

# an attribute over that domain, with a values unit of a basic value type
value = data.add_attribute('value', entities.asConst(), 'float64')

# an attribute with an explicit domain unit, values unit and value composition
geometry = data.add_data_item('geometry', point_domain, point_values, ValueComposition.Polygon)

# a parameter (Void domain) of a basic value type
nr_entries = root.add_param('nrEntries', 'uint32')

Attach a storage manager (e.g. to read a source file), or force in-memory / calculator-only operation:

src = root.add_container('src')
src.set_storage_manager('path/to/source.gpkg', 'gdal.vect', read_only=True)

derived.disable_storage()

Define derived items purely by an expression, exactly as in a model script:

doubled = data.add_attribute('doubled', entities.asConst(), 'float64')
doubled.set_expr('value * 2.0')

total = root.add_param('total', 'float64')
total.set_expr('sum(value)')

The Engine also exposes the shared default unit of a basic value type (handy as a values unit) and the Void unit (the domain of every parameter):

f64  = engine.default_unit('float64')
void = engine.void_unit()

Setting parameter values

Parameters (items with a Void domain) can be assigned a value directly:

nr_entries.set_param_int(5)
pi    = root.add_param('pi', 'float64');  pi.set_param_float(3.14159)
label = root.add_param('label', 'string'); label.set_param_str('hello from python')

print(pi.get_param_float())   # 3.14159

Querying results (Primary Data Access)

After updating an item, its values can be read back through the Primary Data Access functions. Cast a tree item to a DataItem (or use a MutableDataItem returned by the builders) and read scalar or bulk values:

result = root.find('/data/doubled')
result.update()                       # (re)calculate first

di = result.asDataItem()
print(di.size())                      # number of values (= domain unit count)
print(di.get_value_as_float(0))       # a single value, by index
print(di.get_values_as_float_list())  # all values as a python list

# the same for integers and strings
print(di.get_values_as_int_list())
print(di.get_value_as_str(0))

Writing primary data into a fresh in-memory attribute is done in bulk; the list length should equal the domain element count:

value.set_values_from_float_list([1.0, 2.0, 3.0, 4.0, 5.0])
value.set_values_from_int_list([1, 2, 3, 4, 5])

The list must hold exactly one value per domain element: a list of another length is an error. The values are written tile by tile, so a domain of more than one tile is filled as well.

A data item also exposes its domain and values unit, and the unit its entity count / numeric range:

dom = di.domain_unit()
print(dom.value_type_id(), dom.count())   # e.g. ValueTypeId.UInt32 5
print(di.values_unit().value_type_id())   # e.g. ValueTypeId.Float64
print(di.values_unit().value_type_id().name)  # 'Float64'

API reference

module geodms

  • version() → str: the GeoDMS version string
  • ValueComposition, an enum: Single, Polygon, Sequence, MultiPoint
  • ValueTypeId, an enum of value types: Float64, Float32, UInt32, Int32, UInt16, Int16, UInt8, Int8, UInt64, Int64, Bool, UInt4, SPoint, WPoint, IPoint, UPoint, FPoint, DPoint, String, Void, Unknown

class Engine — the single engine instance for the process. Constructing it also registers a message callback that prints GeoDMS diagnostics (warnings, calculation errors) to stderr, so failures are visible rather than silently yielding undefined values.

  • Engine(): construct (only once per process)
  • load_config(config_file_name) → Config: load a .dms configuration
  • create_config_root(name) → Config: create an empty in-memory configuration
  • default_unit(value_type) → UnitItem: the shared default unit of a basic value type
  • void_unit() → UnitItem: the Void unit (domain of parameters)

class Config

  • root() → MutableTreeItem: the (mutable) root item
  • const_root() → ConstTreeItem: a read-only root item

class ConstTreeItem / MutableTreeItem — an item in the tree

  • is_null() → bool
  • find(item_path) → tree item: look up an item by path
  • name() → str, full_name() → str, expr() → str, descr() → str
  • sub_items() → list, parent() → tree item, fail_reason() → str
  • update(): calculate/update the item (and its storage, if configured)
  • update_metainfo() → bool: determine the meta info without calculating; False for a failed item, whose reason fail_reason() then gives
  • in_template() → bool, is_template() → bool: in_template() is true for a template and for every item inside one
  • isDataItem() / asDataItem(): test / cast to a DataItem
  • isUnitItem() / asUnitItem(): test / cast to a unit item

asDataItem() and asUnitItem() raise an exception, on both the read-only and the mutable tree item, for an item of another kind and for the null item that find() returns for a name that does not exist. Test with isDataItem(), isUnitItem() or is_null() first where either can happen.

ConstTreeItem additionally exposes first_subitem() and next() for tree traversal. MutableTreeItem additionally exposes:

  • set_expr(str), set_descr(str), asConst()
  • add_container(name) → MutableTreeItem
  • create_unit(name, value_type) → MutableUnitItem
  • add_data_item(name, domain, values, vc=ValueComposition.Single) → MutableDataItem
  • add_attribute(name, domain, values_value_type) → MutableDataItem
  • add_param(name, values_value_type) → MutableDataItem
  • set_storage_manager(storage_name, storage_type, read_only=True), disable_storage()

class UnitItem / MutableUnitItem

  • is_null(), name(), full_name(), value_type_id() → ValueTypeId
  • count() → int, get_range() → (begin, end)
  • MutableUnitItem: set_expr(str) (e.g. 'range(uint32, 0, n)' to size a domain), set_count(n), set_range(begin, end), asConst()

class DataItem

  • is_null() → bool
  • name(), full_name()
  • domain_unit() / values_unit() → UnitItem, value_composition()
  • size() → int, update()
  • get_value_as_float(i), get_value_as_int(i), get_value_as_str(i)
  • get_values_as_float_list(), get_values_as_int_list()
  • LockAndGetStringValue(i) → str: read the value at index i as a string

class MutableDataItem

  • asDataItem(): read-only view; domain_unit(), values_unit(), value_composition(), size(), name(), full_name(), update(), set_expr(str)
  • set_values_from_float_list(list), set_values_from_int_list(list): write primary data (list length = domain element count)
  • set_param_float(v), set_param_int(v), set_param_str(v), get_param_float()

example usage

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

# 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 configured

A complete in-memory example (build a configuration without a model script and read results back) is available in python/tst/InMemoryConfig.py.

since version

  • 15.0.1: the geodms module, with the bindings that load a configuration and query it.
  • 20.5.0: building a configuration in memory, setting parameter values and Primary Data Access, with ValueTypeId, create_config_root(), default_unit(), void_unit(), const_root(), full_name(), descr(), sub_items(), parent(), fail_reason(), the building methods of MutableTreeItem, the classes UnitItem, MutableUnitItem and MutableDataItem, and the members of DataItem other than is_null(), name() and LockAndGetStringValue().
  • 20.23.1: update_metainfo(), in_template() and is_template(); fail_reason() on an item that has not failed; the exception of asDataItem() and asUnitItem() for an item of another kind or a null item; and set_values_from_float_list() and set_values_from_int_list() on a domain of more than one tile.

Clone this wiki locally