How to use the CLI
Getting started
Running the script with the help option diplays CLI commands and options:
The help option can also be called for commands and sub-commands:Conversion between different encodings
XPlanung data can be converted between different encodings using the convert command, e.g. from GML to JSON-FG
convert command, together with the option --xtrasse:
If no output is provided, the data is written to stdout in the internal encoding.
Reading from a URL or a WFS
The input may be an http(s) URL — a plain GML or JSON-FG file, or a WFS GetFeature request.
This is off by default, since a service that passes a datasource it received from elsewhere would
otherwise be able to reach hosts on its own network. Turn it on per run:
XMAS_DS_ALLOW_REMOTE=1 xplan-tools convert "https://example.org/wfs?service=WFS&request=GetFeature" output.gml
XMAS_DS_REMOTE_TIMEOUT (default 60 seconds) and XMAS_DS_REMOTE_MAX_BYTES (default 256 MiB)
bound the request. Only http and https are ever retrieved; GDAL virtual filesystem paths
(/vsicurl/, /vsizip/, /vsis3/, …) are rejected whether or not remote access is enabled.
The output target is never fetched — it is always written locally. It is also identified by its name rather than by its content: whatever is at the path is about to be replaced, so it has no say in which format is written there. Where the name does not settle it, pass the format:
In library use, allow_remote is what opts in, so the decision is visible at the call site:
from xplan_tools.interface import Datasource
ds = Datasource("https://example.org/wfs?service=WFS&request=GetFeature", allow_remote=True)
collection = ds.repo().get_all()
Datasource is built — it only classifies the URL, and refuses it
outright when remote access is off. The request happens on first read of ds.buffer or
ds.format. To retrieve a URL yourself and hand the bytes on, fetch_remote does just that:
from xplan_tools.interface import fetch_remote
from xplan_tools.interface.gml import GMLRepository
buffer = fetch_remote("https://example.org/plan.gml", allow_remote=True)
collection = GMLRepository(buffer).get_all()
Documents that are refused
A GML document is checked at its root element before it is read, so an unusable file fails in milliseconds rather than after a full parse:
- A document declaring a DOCTYPE is rejected (
ForbiddenDoctypeError). XPlanung and INSPIRE never use one, and entity references are left unexpanded, so accepting a DTD would mean silently dropping element text. - A document whose root element is not
XPlanAuszugor a WFS/OGC API FeaturesFeatureCollectionis rejected (UnsupportedRootElementError). - A document using the same
gml:idfor more than one feature is rejected (DuplicateGmlIdError). Agml:idis anxsd:IDand so unique within a document; where it is not, every#GML_<id>andurn:uuid:<id>reference to it names two features at once. Two distinct ids carrying the same UUID (GML_<uuid>andGML_<uuid>_Bereich) are fine — the second feature is given a fresh id, and references keep resolving to the one that kept its own.
Both exit with code 2 and a one-line message. Every error raised by this package derives from
BaseError and carries a machine-readable code; those replacing an error from an earlier
release also remain a ValueError.
Feature references
Associations between features are intra-document by design. An xlink:href is read as a reference
to another feature in the same file when it is a local fragment or a UUID URN:
<xplan:gehoertZuPlan xlink:href="#GML_d83b1cb7-c702-478e-8471-adf973780873" />
<xplan:gehoertZuPlan xlink:href="urn:uuid:d83b1cb7-c702-478e-8471-adf973780873" />
#GML_<uuid> shorthand.
Anything else is rejected, in one of two ways:
- An absolute URI (
https://example.com/plan) is a well-formed but external reference. It is read, and the collection then reports it as unsupported, together with any reference whose UUID names no feature in the file. - Any other href - a relative path, a cross-document reference such as
other.gml#GML_<uuid>, or a bareGML_<uuid>- is malformed and fails while the feature is being read, naming the class and role.
To read a file with unresolvable or external references anyway, drop them:
Every dropped reference is logged, followed by a count of how many were dropped. A mandatory role is never left empty: a multi-valued one may lose entries as long as one resolvable entry remains, but a role that would end up with nothing still fails. Malformed hrefs are not covered by this option either, since they never get far enough to be dropped.Note that a reference nested inside a data type is not read as a feature reference at all. Data types are stored inside their parent feature rather than as features of their own, so they cannot participate in the reference graph.
Writing one file per feature type
JSON-FG output is a single heterogenous FeatureCollection by default. It can instead be fanned out
into one type-homogenous collection per feature type:
output_BP_Plan.json, output_BP_Bereich.json, and so on. The target itself is not written. Since
this names a family of files rather than one, it needs a file path — it cannot fan out into a buffer.
Writing one file per plan
A file holding several plans can be split into one output file per plan:
Each output file is named after its plan and holds the containment closure of that plan, derived from the application schema's ownership metadata. A plan is self-contained, so a reference from inside a plan to a feature belonging to another one is invalid data and reported as an unresolvable reference. INSPIRE PLU declares no ownership metadata, so--single-plans cannot be combined with
--to-version plu. The option applies to file output only; writing to a database always writes the
whole collection.
Utilizing style rules
XPlanung presentational objects can be augmented with style properties (stylesheetId, schriftinhalt) via the convert command with the option --add-style-properties:
--ppo-to-pto is added, XP_PPO are converted to XP_PTO objects in order to populate schriftinhalt.
The underlying rules can e.g. be exported like this:
Version migration
Migrating from Xplanung version 5.x to 6.0 is done with the CLI command
for a .gml data filexplan5x.gml.
INSPIRE transformation
Using the CLI tool to convert XPlanung data from version 6.0 to INSPIRE PLU 4.0 is straightforward. Given a dataset xplan60.gml we run
Chaining version migration and INSPIRE transformation
Applying the transformation to INSPIRE PLU 4.0 from XPlanung data version 5.* is done with same command as before. Given a dataset xplan5x.gml we run
Initializing a database
An XPlanung database with GeoPackage backend can be initialized like this:
libpq env vars are supported for Postgres connections. Assuming all required parameters (PGHOST etc.) are provided this way, an empty connection string can be used:
Use custom schema in postgres database
By default, the necessary tables in the database are created in the public namespace. For postgres, custom schemas are supported, via:
xplan-tools manage-db create-schema postgresql://postgres:postgres@localhost:5432/postgres --schema my_schema
Database configuration via settings
Database behaviour is configured through settings, read from environment variables prefixed with XMAS_ or from a .env file in the working directory. For example, db_srid is set via XMAS_DB_SRID:
The settings that affect schema creation are also exposed directly as options on create-schema, which take precedence when given:
Setting (XMAS_…) |
create-schema option |
Purpose |
|---|---|---|
XMAS_DB_SRID |
--srid |
EPSG SRID of the geometry columns. |
XMAS_DB_SCHEMA |
--schema |
PostgreSQL schema for the tables (see above); public if unset. |
XMAS_DB_VIEWS |
--views |
Whether per-geometry-type views are created. |
The remaining settings tune PostgreSQL data access rather than schema creation: XMAS_DB_INSERT_BATCH_SIZE (features per bulk-write statement, bounding how large a single statement's payload gets) and the connection-pool settings XMAS_DB_POOL_SIZE, XMAS_DB_POOL_MAX_OVERFLOW, XMAS_DB_POOL_TIMEOUT, XMAS_DB_POOL_RECYCLE and XMAS_DB_POOL_PRE_PING. See the settings reference for defaults and details.
Container image usage
Input data should be mounted in the /data directory in the container, e.g.