# Packaging algorithm The plugin registers a single Processing algorithm, **`stratified_packager:package`** (*Package project*). It partitions the open project's layers against a **stratification layer** and writes **one zipped GeoPackage per stratum** into an output directory. Features are matched to strata either by **attribute** (following chains of the project's `QgsRelation`s) or **spatially** (a predicate, including a raw DE-9IM pattern), chosen per layer. Outputs are zip-only: a `.gpkg` exists on disk only transiently while it is built and then inside its zip. ## Running it ### From the QGIS GUI Open *Processing ▸ Toolbox ▸ Stratified Packager ▸ Package project*. The dialog's defaults are pre-filled from the active project (see [Defaults & precedence](#defaults-precedence)); the **Layers** field is pre-checked with the eligible layers. ### Headless, with `qgis_process` `qgis_process` only loads provider plugins that have been enabled **for it** (separately from the desktop). Enable it once per user profile: ```bash qgis_process plugins enable stratified_packager ``` Then run the algorithm: ```bash qgis_process run "stratified_packager:package" \ --project_path=/path/to/project.qgz \ --OUTPUT_DIRECTORY=/path/to/out \ --STRATIFICATION_LAYER=districts ``` `--project_path` is **required** — the algorithm declares `Qgis.ProcessingAlgorithmFlag.RequiresProject`. The Processing framework re-instantiates the algorithm *after* the project loads, so project-variable and plugin-setting defaults resolve correctly without a GUI. `QgsSettings` is per-profile, so `qgis_process` uses the **default profile** unless you override it. (parameters)= ## Parameters Advanced parameters are collapsed in the GUI by default. | Parameter | Meaning | | ---------------------------------------------- | -------------------------------------------------------------------------------------------------------- | | `LAYERS` | Layers to package; empty = every eligible layer not marked `exclude=true`. | | `STRATIFICATION_LAYER` | The partition source — one stratum per feature. | | `STRATUM_NAME_EXPRESSION` | How each stratum is named (empty = feature id). | | `GPKG_PATH_EXPRESSION` / `ZIP_PATH_EXPRESSION` | GeoPackage / zip paths (advanced). | | `OUTPUT_DIRECTORY` | Where the zips are published (atomic `.part` rename). | | `COMPRESSION_LEVEL` | Zip Deflate level 0–9 (0 stores uncompressed). | | `OVERWRITE_MODE` | `overwrite` \| `error` \| `skip-existing`. | | `PROJECT_INCLUSION` | `none` \| `gpkg` \| `qgz` — embed a project per stratum. | | `STRATA_FROM_SELECTION` | Only the stratification layer's *selected* features become strata (fails when nothing is selected). | | `USE_TEMP_FOLDER` | Build in a temporary folder and publish finished zips (off: build inside the output directory). | | `INCLUDE_STYLES` / `INCLUDE_METADATA` | Embed layer styles / metadata. | | `STYLE_CATEGORIES` | Which style categories the QML copies (none checked = all). | | `KEEP_EMPTY_LAYERS` | Keep zero-feature layers as empty tables. | | `DEDUPLICATE_SHARED_SOURCES` | Write layers sharing a source as one table. | | `STAGE_PROVIDERS` | Stage every layer of these data providers into a fast local copy first (see the `stage` layer variable). | | `EXPORT_FULL_PACKAGE` / `FULL_PACKAGE_PATH` | Also emit the unpartitioned dataset. | | `REPORT` | Run report output: an in-memory table layer when no path is given, else a file. | | `GENERATE_REPORT` | Also write a `report.csv` into each published zip. | | `EXTRA_DIR` | Extra files copied into every zip root. | | `WARM_START_DIR` / `WARM_START_MODE` | The warm cache: `off` \| `use` \| `update` (below). | | `WRITE_CHECKSUMS` | Write a `.sha256` sidecar next to each zip. | | `DRY_RUN` | Validate and report without writing packages. | The expression contexts for the naming and path expressions expose `@stratum_name`, `@stratum_name_sanitized`, `@gpkg_path` and `@gpkg_name`. (per-layer-variables)= ## Per-layer variables Each layer carries QGIS **layer variables** that tune its participation. Edit them under *Layer Properties ▸ Variables*, on the plugin's per-layer page, or in the all-layers **Configure layers for packaging** dialog (plugin menu). | Variable | Type | Default | Meaning | | ----------------------------------------------- | ---------- | ------- | ------------------------------------------------------------------------------------------------------------------------------ | | `stratified_packager_exclude` | bool | `false` | Skip this layer when `LAYERS` is empty. | | `stratified_packager_matching_method` | enum | `auto` | `auto` \| `attribute` \| `spatial` \| `whole_export`. | | `stratified_packager_spatial_predicate` | list | `auto` | Comma-separated named predicates and/or 9-char DE-9IM patterns, combined with OR (invalid patterns are rejected at run-start). | | `stratified_packager_relation_path` | JSON list | unset | Pin an ambiguous attribute chain. | | `stratified_packager_excluded_fields` | JSON list | `[]` | Fields dropped from the export. | | `stratified_packager_stage` | bool | `auto` | Force (`true`) or forbid (`false`) staging this layer into a fast local copy; `auto` follows `STAGE_PROVIDERS`. | | `stratified_packager_warm_marked` | bool | `false` | Layer belongs to the warm cache. | | `stratified_packager_layer_name` | expression | unset | Custom display name inside each embedded per-stratum project (may use `@stratum_name`). | | `stratified_packager_materialize_virtual_layer` | bool | `false` | Write a virtual layer's features into the GeoPackage instead of keeping the layer live in the embedded project. | With `matching_method = auto`: if a relation path to the stratification layer exists the layer matches by **attribute**; otherwise, if both the layer and the stratification layer have geometry, it matches **spatially**; otherwise the run aborts naming the layer and the remedies. `auto` never resolves to `whole_export` — whole export is always an explicit choice. For spatial matching, `spatial_predicate` accepts several predicates at once — a comma-separated list of named predicates (`intersects`, `contains`, `within`, `overlaps`, `crosses`, `touches`) and/or raw DE-9IM patterns — that combine additively (a feature matches if **any** of them holds). With `spatial_predicate = auto`, a polygon stratum paired with a line layer (or the reverse) defaults to "interiors intersect, or the line runs along the polygon boundary", a point on either side to `intersects`, and otherwise to "interiors intersect". (defaults-precedence)= ## Defaults & precedence Every omitted parameter resolves through a four-tier chain: > **explicit input → project variable (`stratified_packager_`) → plugin setting → > builtin default** Project- and layer-scope values are editable from three places, each showing the inherited effective value as a placeholder: - the plugin **Options** page (plugin-wide settings, with a note when a project shadows one); - the **Project Properties** page (project-scoped defaults); - the per-layer page and the **Configure layers for packaging** dialog (layer variables). In a GUI session the dialog defaults refresh automatically when you load a project or edit the relevant variables. The same chain is re-applied at run start, so headless runs and stale instances resolve identically. ## Warm cache When a warm-cache directory is set, `WARM_START_MODE=use` begins each stratum GeoPackage from a cached copy and appends only the layers that are *not* warm-marked; `WARM_START_MODE=update` first writes every stratum's cache file and only then builds the deliverables, seeded from that fresh cache — an interrupted run still leaves a complete, reusable cache. If a cached file no longer matches its warm-marked tables, that stratum falls back to a cold build (reported as `cold-fallback`). ## Output layout Each published zip mirrors this structure (optional members depend on the parameters): ```text / ├── .zip │ ├── .gpkg # the stratum GeoPackage: one table per layer, │ │ # plus layer_styles / gpkg_metadata when embedded │ ├── .qgz # embedded project (PROJECT_INCLUSION = qgz) │ ├── data/ # whole-export & non-vector sources, with sidecars │ │ └── /… │ ├── resources/ # style assets (SVGs, images) referenced by the QML │ │ └── … │ └── report.csv # per-zip report (GENERATE_REPORT) ├── .zip.sha256 # checksum sidecar (WRITE_CHECKSUMS) └── report.csv # run report, only when REPORT is given a path here ``` With `PROJECT_INCLUSION = gpkg` the per-stratum project is stored *inside* the GeoPackage (via `QgsProjectStorage`) instead of as a sibling `.qgz`. ## Reports The algorithm **always** produces a **run-level report** as the `REPORT` output: with no destination it is loaded as an in-memory table layer; given a path it is written there (CSV, GeoPackage, …). Independently, `GENERATE_REPORT` (on by default) adds a **per-zip `report.csv`** to each published zip. Both are UTF-8 without BOM and are data, not translated. Rows record each layer's per-stratum status (written, empty, skipped, `cold-fallback`, …), unmatched features (``), and — under `DRY_RUN` — what *would* be written.