# QGIS Integration

The QGIS integration lets Pro and Max users take a saved planting plan into a desktop GIS as a GeoPackage and, with the official AgroForestDesigner Sync plugin, synchronize supported spatial changes back to the plan.

Use the integration when you need GIS inspection, spatial editing, overlays, or a controlled round trip between AgroForest Designer and QGIS. The browser plan remains the authoritative application record. A downloaded file is a versioned snapshot, not a live connection.

> QGIS and the GeoPackage improve spatial interoperability, but they do not turn map imagery or design coordinates into survey-grade data. Verify critical boundaries, positions, levels, dimensions, and coordinate reference systems against an appropriate field survey.

## Choose the right workflow

The integration supports two related workflows:

| Workflow | What it does | Connection token required |
| --- | --- | --- |
| **GeoPackage export** | Downloads the current saved plan as a multi-layer `.gpkg` file for inspection or offline work in QGIS | No token for a download started while signed in to AgroForest Designer |
| **Connected synchronization** | Lets the official QGIS plugin discover your projects, download plan data, preview supported changes, and push an approved batch back | Yes; write access is required for a push |

Opening and styling an exported GeoPackage does not change the server plan. Changes return to AgroForest Designer only through the validated synchronization workflow.

## Before you start

Prepare the following:

- an active subscription that includes **QGIS interoperability**;
- a project and planting plan owned by your account;
- a current saved plan version;
- a maintained QGIS installation with GeoPackage support; and
- the official AgroForestDesigner Sync plugin when you need connected synchronization.

Plugin downloads require the same Pro or Max access as connected synchronization. AgroForest Designer discovers validated releases from the deployment's download directory, so the [QGIS plugin download page](https://agroforestdesigner.com/account/integrations/qgis/plugin/) always marks the newest available version and also lists older releases.

Save the plan immediately before export. The GeoPackage and API read the latest server-side plan; unsaved changes that exist only in the browser are not included.

## Install the QGIS plugin

1. Sign in to AgroForest Designer and open the [QGIS plugin download page](https://agroforestdesigner.com/account/integrations/qgis/plugin/).
2. Download the release marked **Latest**. Keep the `.zip` archive intact; do not extract it.
3. In QGIS, open **Plugins** > **Manage and Install Plugins**.
4. Select **Install from ZIP**, choose the downloaded archive, and select **Install Plugin**.
5. Acknowledge the experimental-plugin warning when QGIS shows it. The release page reads this status directly from the plugin metadata.
6. Open **Web** > **AgroForestDesigner** > **AgroForestDesigner Sync**, or select its toolbar button.

The release page shows the supported QGIS range, archive size, and SHA-256 checksum for every validated ZIP. If QGIS rejects an archive, download it again from the page and compare its checksum before retrying. Never install an archive received through an untrusted message or unrelated website.

## Download a GeoPackage

1. Open the required saved plan in the **Digital Planting Plan**.
2. Select **Save version** and wait for the success confirmation.
3. Open **Execution** > **Output**.
4. Select **Download GeoPackage**.
5. Store the downloaded `.gpkg` file in a project folder with a clear date or revision reference.
6. In QGIS, add the GeoPackage through the data-source manager or drag the file into the project.

The filename is derived from the project and plan name. The package uses GeoPackage 1.3 and WGS 84 (`EPSG:4326`) with coordinates stored as longitude, latitude. QGIS can display the layers in another project coordinate reference system, but do not redefine the source layer CRS merely to make it match the canvas.

Every export contains identifiers for the project, plan, package, base revision, and base content. It also contains the server URL and synchronization metadata used to detect later conflicts. Keep one untouched copy of an important export before doing extensive editing, and do not move internal `afd_` tables between unrelated packages or plans.

> Treat the GeoPackage as confidential project data because its visible features can describe a real site and design. Synchronization tables contain feature baselines, identifiers, revision fingerprints, and the server address, but new exports do not embed the raw plan, raw annotations, backend-only metadata, credentials, or an API token. Removing one visible layer is still not a reliable anonymization method.

## Understand the exported layers

Every GeoPackage contains the complete layer structure, including layers that are empty in the selected plan.

### Layers that support synchronization

| Layer | Geometry | Supported use |
| --- | --- | --- |
| `plantings` | Point | Move, add, or remove individual plants and update supported planting or implementation fields |
| `plant_zones` | Polygon | Edit planting-zone geometry, colour, and notes |
| `paths` | Line | Edit path centre lines and supported name, width, colour, opacity, or smoothing values |
| `design_polygons` | Polygon | Edit polygon or rectangle drawings and supported display properties |
| `design_circles` | Point plus `radius_m` | Move or resize circular drawings and edit supported display properties |
| `ground_control` | Point | Move, add, or remove ground-control markers and edit their labels |

### Work with individual plants

`plantings` is one QGIS layer for performance and practical editing, but every row is one individual placed plant. Plants are never grouped into one aggregate feature. Open the attribute table or the plugin's **Plant inspector** to inspect, search, select, and zoom to an individual. The inspector also reports the feature count so a pulled plan can be checked against the number expected in the browser.

Each individual has two complementary identifiers:

- `afd_id` is the stable technical identity used for synchronization. Never edit, copy between plans, or use it as a display number.
- `plant_number` is the positive planner inventory number and `plant_code` is its readable form, such as `P12`. The number is persisted with the plant. New plants receive a number after the plan's high-water mark, so gaps left by removed plants are not silently recycled. `afd_src_idx` is only an internal source position and is never a plant number.

GeoPackage schema 1.2 exposes the following plant-source and display context in addition to editable planting fields. This context is read-only on an existing feature; `source_type` and `source_id` may be supplied only to identify the source while creating a new plant or planting zone:

| Fields | Meaning |
| --- | --- |
| `display_name`, `scientific_name` | A useful display fallback and the botanical name |
| `common_name_en`, `common_name_nl`, `common_name_fr` | English, Dutch, and French common names when present in the catalogue or saved marker |
| `cultivar_id`, `cultivar_name` | Cultivar identity and label where applicable |
| `plant_layer` | Food-forest or vegetation layer |
| `generation_layer_id`, `generation_layer_name`, `generation_sequence`, `generated_instance_id` | Context for algorithm-generated placements |
| `source_type`, `source_id`, `catalog_id` | Typed origin and stable source identifier. `source_type` plus `source_id` is canonical; `catalog_id` remains available for compatible older clients |

The same localized/display and cultivar fields are available on `plant_zones` where meaningful, but zones do not consume P-numbers.

In the plugin, choose the preferred common-name language under **Settings**. **Auto (QGIS language)** starts with the QGIS interface language; **English**, **Dutch**, and **French** prefer the matching field and fall back to another available common name and then the scientific name. **Scientific name** forces the botanical name. Label modes are **None**, **Plant code**, **Preferred name**, and **Code + preferred name**. Labels are scale-limited to keep dense plans readable. The selected language affects presentation only and is never pushed as a catalogue change.

The server accepts geometry plus only the following properties during synchronization:

| Layer | Accepted properties |
| --- | --- |
| `plantings` | `source_type`, `source_id`, `catalog_id`, `radius_m`, `color`, `stem_type`, `planted`, `planted_date`, `planted_notes`, `planted_nursery_id`, `planted_nursery`, `existing_plant` |
| `plant_zones` | `source_type`, `source_id`, `catalog_id`, `color`, `notes` |
| `paths` | `name`, `description`, `width_m`, `color`, `opacity`, `smoothing` |
| `design_polygons` | `name`, `description`, `height_m`, `fill_color`, `fill_opacity`, `stroke_color`, `stroke_opacity`, `stroke_weight`, `visible` |
| `design_circles` | `name`, `description`, `height_m`, `radius_m`, `fill_color`, `fill_opacity`, `stroke_color`, `stroke_opacity`, `stroke_weight`, `visible` |
| `ground_control` | `label` |

An editable layer is not an unrestricted database table. In particular:

- a new plant or planting zone needs either an existing AgroForest Designer catalogue plant or an active personal plant owned by the plan owner;
- the plant-source identity of an existing plant or zone cannot be changed in place;
- `afd_id` is the stable feature identity and must never be edited;
- fields such as `afd_locked`, source indexes, plant codes, localized or derived plant names, cultivar/generation context, `diameter_m`, plant `height_m`, and template references are transport or reference data; and
- invalid, unknown, or out-of-range values cause validation to reject the batch.

The current plugin does not include a plant-library browser. For a catalogue plant, use `source_type` = `catalog` and its numeric `source_id`, or the compatible `catalog:<id>` reference. For an entry in **My Plants**, use `source_type` = `user` and its UUID `source_id`, or the canonical `user:<uuid>` reference. Older packages may still contain `preset:<pk>`; this form is accepted only for backward compatibility.

Personal references are always resolved for the plan owner. A missing, foreign, archived, or guessed entry cannot create a new placement, and a personal plant that is read-only under the current plan allowance also cannot be newly placed from QGIS. Existing placements keep their embedded display snapshot, so they remain visible and can receive ordinary geometry or implementation updates even if the library entry is later archived or unavailable. The snapshot, not a later library lookup, is authoritative for an unchanged placement.

A GeoPackage includes only the placement fields needed to render and synchronize a personal plant. It does not export private library notes, owner identity, moderation or review state, submission data, or administrator metadata.

### Reference-only layers

| Layer | Geometry | Meaning |
| --- | --- | --- |
| `project_boundary` | Polygon | The project boundary controlled by AgroForest Designer |
| `measurements_lines` | Line | Saved distance measurements and derived distance values |
| `measurements_areas` | Polygon | Saved area measurements and derived area or perimeter values |
| `structures` | Point | Placed structure templates and their anchor information |
| `annotations` | Point | Plan comments and replies |
| `orthomap_extents` | Polygon | Reserved for orthomap coverage metadata; the current export leaves this layer empty and never embeds raster pixels |

These layers are reference-only. The GeoPackage enforces that restriction with database triggers, so QGIS should report an error if an insert, update, or delete is attempted. Make boundary, measurement, structure, annotation, or orthomap changes in AgroForest Designer and then create a fresh export.

## Create a connection token

Open **Execution** > **Output** > **QGIS connection**, or [open the QGIS integration page](https://agroforestdesigner.com/account/integrations/qgis/) directly.

1. Enter a **Device name** that identifies the computer or QGIS profile.
2. Choose whether the token expires after 7, 30, or 90 days.
3. Leave **Allow validated planting-plan changes to be pushed from QGIS** clear for read-only access, or select it when this device must synchronize changes.
4. Select **Create token**.
5. Select **Copy token** and store the complete token immediately. It is shown only once.
6. Store it in QGIS Authentication Manager and configure the exact **Server URL** shown on the integration page.

A read-only token receives `gis:read`. Selecting write access adds `gis:write`, which also retains read access. Read access is sufficient for connection checks, project and plan discovery, snapshots, and downloads. A push requires write access.

The token is not your web password. AgroForest Designer stores only a cryptographic digest plus the visible prefix, scopes, dates, and status. Do not:

- email or message the token;
- put it in a QGIS project file, GeoPackage, script, screenshot, or issue report;
- place it in a URL or query string; or
- reuse one token across several people or devices.

Create one named token per device. This makes it possible to revoke one lost or retired connection without disrupting the others.

## Connect the QGIS plugin

After installing the plugin, connect it as follows:

1. In QGIS, open **Settings** > **Options** > **Authentication** and add a new **API Header** configuration. Its **Resource** field must exactly match the Server URL shown on the AgroForest Designer integration page. Add a Header Name of `Authorization` and a Header Value of `Bearer ` followed by the complete token.
2. Open **AgroForestDesigner Sync** and select **Settings…**.
3. Select the authentication entry and choose a local workspace folder for downloaded packages.
4. Save the settings and select **Refresh projects**. A successful connection shows **Connected as** followed by your name or email address.
5. Choose a project owned by the same account and one of its saved planting plans.
6. Select **Pull GeoPackage** before beginning a new edit session.

The API accepts the token only as an `Authorization: Bearer` credential. A client must not append it to a URL. Tokens cannot access projects owned by another account.

If a project exceeds the account's current project capacity, the client can still read it while GIS access remains active, but the project is reported as read-only and cannot accept a synchronized batch.

## Edit and synchronize safely

Avoid editing the same plan in QGIS and the browser at the same time. A safe round trip is:

1. Save the browser plan and download a fresh package.
2. Make changes only in supported layers and fields.
3. Save all layer edits in QGIS.
4. Select **Preview changes** in the plugin to validate the batch.
5. Review the create, update, and delete summary and the listed layer, ID, action, and result for each preview item. Resolve any error shown by QGIS before retrying.
6. Push the exact batch that was previewed.
7. Reopen or refresh the browser plan and inspect the new saved revision.
8. Create a fresh GeoPackage before the next separate editing session.

Preview is non-mutating: it validates the entire batch without changing the plan. A successful preview is signed and remains valid for ten minutes. Push accepts only the exact content covered by that preview. If any item changes afterward, create a new preview.

A push is atomic. AgroForest Designer applies every accepted change in one transaction or applies none of them. A successful content-changing push creates a normal planting-plan revision with a QGIS source marker, so the change remains visible in plan history. A valid batch that produces no content change does not create an extra revision.

### Handle concurrent changes

Every package is tied to the synchronized plan content that existed when it was exported. If the plan content changes in the browser or another QGIS session, the server detects the different plan-content ETag and returns a revision conflict instead of silently overwriting it. A save with identical plan content, or a change limited to annotations or the project boundary, does not use that plan-content conflict check.

Do not try to remove the package metadata or force the old version through. Keep your edited file, download a fresh plan, compare the changes, and deliberately reapply the required edits to the current version before previewing again. The server does not automatically merge competing spatial edits.

### Respect geometry and batch limits

The synchronization contract currently enforces these limits:

- no more than 5,000 feature changes in one batch;
- keep each line or polygon at no more than 10,000 working coordinate points;
- point, single-line, and single-ring polygon geometries only;
- at least three distinct points and a real enclosed area for a polygon;
- WGS 84 coordinates in longitude, latitude order; and
- each feature identity may occur only once in a batch.

Polygons with holes, multi-geometries, non-finite coordinates, invalid plant-source references, and unsupported layers or properties are rejected. A closed polygon can contain a repeated start/end coordinate in addition to its working vertices. The server checks geometry type, coordinate ranges, point counts, distinct polygon points, and non-zero signed area, but it does not guarantee full topological validity or detect every self-intersection. Run QGIS geometry validation before previewing important polygons. Split oversized work into deliberate batches and preview each one.

## Manage or revoke tokens

The integration page lists every token with its name, public prefix, creation date, expiry, scopes, and revoked status.

Select **Revoke** when:

- a computer, QGIS profile, or token may be lost or exposed;
- a contractor or collaborator no longer needs access;
- a token was stored outside QGIS Authentication Manager;
- write access is no longer appropriate; or
- the connection is being replaced.

Revocation is immediate and cannot be undone. Create a new token when access is needed again. Changing to a plan without QGIS interoperability blocks GIS data access for existing tokens; those tokens can still be revoked from the integration page.

## Troubleshooting

| Message or symptom | What to do |
| --- | --- |
| **GIS access is not available** or `feature_unavailable` | Confirm that the account has an active plan which includes QGIS interoperability. Open **Subscription** to review current access. |
| `missing_token` or `invalid_token` | Check that the client uses the complete token in QGIS Authentication Manager and sends it as a Bearer credential, not in the URL. Create a new token if the original value was not saved. |
| `revoked_token` or `expired_token` | Create a replacement token and update the QGIS authentication entry. |
| `insufficient_scope` | The token is read-only. Create a separate token with **Allow validated planting-plan changes to be pushed from QGIS** selected. |
| `project_read_only` | The project is frozen under the current capacity. Review **Subscription** and project usage before attempting another push. |
| `revision_conflict` | The server plan changed after the package was created. Download the current plan and reapply the required edits deliberately. |
| `preview_expired` | Preview the unchanged batch again; previews expire after ten minutes. |
| `preview_payload_mismatch` or `batch_digest_mismatch` | The batch changed after validation. Save the QGIS edits and create a new preview before pushing. |
| `invalid_catalog_id` or `unknown_catalog_id` | Use a valid catalogue plant for a new feature. Do not guess a catalogue ID. |
| `user_plant_unavailable` | The personal plant is missing, archived, or not available to this plan owner. Select an active entry from the owner's **My Plants** library. The same safe message is used for foreign and missing UUIDs. |
| `user_plant_read_only` | The personal plant is frozen under the current plan allowance and cannot create a new QGIS placement. Existing embedded placements remain editable. |
| `invalid_source_reference`, `missing_source_reference`, or `source_reference_mismatch` | Correct the typed source. Use a matching `source_type` and `source_id`; if `catalog_id` is also present, both forms must identify the same plant. |
| `immutable_plant_source` | Do not change the plant source of an existing plant or zone. Create a new feature when a different plant is required. |
| `unsupported_layer`, `unsupported_property`, or a read-only database error | Limit edits to the supported layers and fields listed in this guide. |
| `invalid_geometry` or `number_out_of_range` | Repair the feature geometry or attribute value and preview again. Check the source CRS and longitude/latitude order. |
| `unsupported_schema_version` | The plugin and server use different synchronization wire versions. Install the latest release from the plugin download page before retrying. GeoPackage 1.0 and 1.1 files remain readable after the 1.2 rollout, but push still uses the documented 1.0 wire contract. |
| The latest browser edits are missing from QGIS | Save a plan version in the browser, then download or fetch a fresh package. |
| An orthomap is not visible in QGIS | The current `orthomap_extents` layer is empty and the GeoPackage does not embed raster pixels. Add the original georeferenced raster separately when required. |

If a repeatable problem remains, revoke any token that may have been exposed and contact support with the project and plan names, QGIS version, plugin version, package export time, action being attempted, and exact error code. Never include the raw token or confidential project data in a support message.

## Connection reference for supported clients

The canonical API base is the server URL followed by `/api/v1/gis/`. Supported clients use these endpoints:

| Endpoint | Purpose | Required scope |
| --- | --- | --- |
| `capabilities/` | Report API/schema versions, layers, formats, limits, and current feature availability | `gis:read` |
| `me/` | Report the authenticated account and token metadata | `gis:read` |
| `projects/` | List owned projects | `gis:read` |
| `projects/{project_id}/plans/` | List saved plans for one owned project | `gis:read` |
| `projects/{project_id}/plans/{plan_id}/snapshot/` | Return current layers as GeoJSON feature collections | `gis:read` |
| `projects/{project_id}/plans/{plan_id}/geopackage/` | Download the current GeoPackage | `gis:read` |
| `projects/{project_id}/plans/{plan_id}/sync/preview/` | Validate and sign a proposed batch without saving it | `gis:write` |
| `projects/{project_id}/plans/{plan_id}/sync/push/` | Atomically apply the exact previewed batch | `gis:write` |

The current public contract reports API `v1`, GeoPackage schema `1.2`, and synchronization wire schema `1.0`. Capabilities separately report the current package version, supported package versions (`1.0`, `1.1`, and `1.2`), and the sync version. This separation lets an older package complete its batch workflow while new pulls receive typed plant-source fields. A package version is not permission to send arbitrary properties: push remains a closed 1.0 allowlist. The typed `source_type` and `source_id` fields may establish the source of a newly created plant or zone, while all other identity, language, cultivar, and generation fields remain read-only.

Push clients must preserve package metadata, the base revision metadata, and the plan-content ETag, use a unique batch UUID, and send the matching conditional request information. Plan-content conflict detection uses the ETag; `base_revision` is validated as metadata but is not the conflict comparator. Retrying the identical batch is safe; reusing its identifier for different content is rejected.

## Continue learning

Return to [Digital Planting Plan](https://agroforestdesigner.com/docs/basics/digital-planting-plan.md) for the complete browser planning workflow. Review [Plans and Feature Access](https://agroforestdesigner.com/docs/basics/plans-and-feature-access.md) when an output or connection is locked, or use [Projects](https://agroforestdesigner.com/docs/basics/projects.md) to manage the plan and site information that remain authoritative outside the GeoPackage.
