# This file contains metadata for your plugin.

# This file should be included when you package your plugin.# Mandatory items:

[general]
name=geodb.io
qgisMinimumVersion=3.0
qgisMaximumVersion=4.99
description=Connects to spatial data from geodb.io via api
version=2.35.2
author=geodb.io
email=admin@geodb.io

about=This plugin allows you connect to spatial data hosted at geodb.io including land holding, drill collars, point samples, field notes, and more.

tracker=https://github.com/joswhite1/geodb-qgis-plugin/issues
repository=https://github.com/joswhite1/geodb-qgis-plugin
# End of mandatory metadata

# Recommended items:

# License identifier (SPDX format)
license=GPL-2.0-or-later

hasProcessingProvider=no
changelog=
    2.35.2 - Pass the plugin repository's security scan
        - No change in behaviour. The plugin repository now blocks any
          version with a security-scanner finding, and 2.35.1 was blocked
          on 73 of them. None was a vulnerability: 69 were cleanup code that
          deliberately ignores a failure (now written the idiomatic way),
          two flagged an empty login-token default, one flagged a settings
          key whose NAME contains "password" (it stores a yes/no choice,
          never a password), and one flagged the GPX exporter's XML import
          (it only writes GPX, it never reads XML).
    2.35.1 - Show what the server actually said when it refuses
        - A refusal carrying an HTTP status is now read and shown, instead of
          being reported as "Network error: ... Unprocessable Entity" with the
          server's explanation thrown away. Claim document generation was the
          case that exposed it: the server named the claim and the fields to
          shorten, and none of it reached the screen.
        - A refused request is no longer retried three times. Only a genuine
          transport failure retries now.
    2.35.0 - Work a claim BLOCK end to end, against the server's geometry
        - A project CRS with no EPSG authority id now REFUSES to generate a
          grid, instead of silently sending WGS84 degrees into a field the
          server reads as metres.
        - "Load Staff Orders" now keeps the block you pulled from. Pulling a
          block's proposed claims used to drop the order link, so every
          document generated afterwards came back attached to nothing. The
          claims and the order were always the same job; the wizard now knows
          it, including on single-block projects where the block picker stays
          hidden.
        - The wizard reads the block's STAGE and the processing the server has
          already stored for it. Monuments you dragged, reference points you
          tied to and LM corners you picked in an earlier session (or on the
          web map) come back with the block instead of being silently
          discarded.
        - Processing a pulled block now runs on the server AND is saved there.
          The result leads with what the processor found - corners on private
          land, claims with no PLSS section, claims straddling a state or
          county line, an LM corner it could not honour, MTRSC claims it does
          not handle - so you see the problems before you commit anything.
        - Step 7 writes a processed block directly from the numbers you
          reviewed, instead of re-uploading the claims. Same LandHoldings,
          same stakes, same package - but nothing can drift between what you
          approved and what is written, and re-running is safe: claims already
          written are skipped and reported.
        - Claim grids are always generated by the server now. The offline
          fallback has been removed: it drew claims from its own copy of the
          600 x 1500 ft dimensions, and it also ran when the server returned an
          error - so a momentary server problem could quietly give you
          client-drawn claims. Offline now says so instead.
        - Internal: the Idaho / New Mexico "monument at a corner" rule lived in
          five places and each one drifted. It now lives in one.
    2.34.1 - Fix: claim layers split across two GeoPackages
        - The claims workflow now keeps ALL generated layers (Lode Claims,
          Corner Points, LM Corners, Monuments, Center Lines) AND the Reference
          Points layer in the SAME GeoPackage as your Initial Layout, the way
          it did before 2.28.0. Since 2.28.0, reopening the wizard (or reloading
          the plugin) could leave the workflow without the GeoPackage path in
          memory, which caused it to create a SECOND file in the GeodbData
          folder and write the generated layers there — splitting one claim
          package across two GeoPackages, with the Reference Points layer
          falling back to a non-persistent memory layer. The workflow no longer
          auto-creates a GeoPackage; it uses the one you created in Step 1
          (recovering its path from your claim layout if needed). If you did
          not create a GeoPackage in Step 1, layers are held in memory as they
          were before 2.28.0 — create one in Step 1 to persist your work.
    2.34.0 - Pushed claims keep their claim type
        - The claims wizard has always known whether each claim is a lode
          claim, a placer claim, a mill site or a tunnel site, and the push to
          the server never sent it. Every pushed claim therefore landed with
          the server's default land status of "Lode Claim", whatever it
          actually was.
        - The push now sends the matching land holding type for each claim.
          The type is matched against your company's own list, so a company
          that has renamed its types keeps its own wording.
        - If a claim's type cannot be matched to one of your existing land
          holding types, the push simply leaves the land status to the server
          as before, rather than guessing. Nothing about the push can fail
          because of this.
        - The claim type now also survives processing in Step 6, where it was
          previously dropped on the way back from the server.
    2.33.1 - Fix rasters loading as blank layers
        - Tiled rasters (GeoTIFFs, DEMs) could load as a layer that showed the
          correct name, CRS and extent but drew nothing at all. The tile
          requests were being redirected, and QGIS does not follow redirects
          for tile layers, so no image data ever arrived.
        - The plugin now checks that tiles actually load before using the
          tile-streaming path, and downloads the full raster file instead when
          they do not. Previously this fallback existed but never ran, because
          a layer with no working tiles still reports itself as valid.
        - Needs the matching geodb.io server update to take full effect.
    2.33.0 - Type-to-search on the company, project and claim-block dropdowns
        - Account tab: the Company and Project dropdowns now filter as you
          type instead of forcing you to scroll a long list.
        - Proposed Claims tab: the Project dropdown filters the same way, and
          because each entry reads "Project (Company) - N claims" you can type
          a company name to find its projects.
        - Matching is anywhere in the entry and ignores case, so "sunshine"
          finds "Sunshine Mine" and "nevada king" finds that company's work.
        - Typing something that matches nothing leaves your current selection
          untouched; the box snaps back rather than showing a stale entry.
    2.32.0 - Claim-block picker on the Proposed Claims pull
        - Multi-block projects: a "Claim block" dropdown appears when the
          project has more than one block, so you can pull one batch (e.g.
          a second phase) without wading through every earlier generation.
          Filtering happens server-side; "All blocks" keeps prior behavior.
        - Works against older servers too: no dropdown appears until the
          server reports blocks, and unfiltered pulls are byte-compatible.
    2.31.0 - Expired sessions now prompt you to log in instead of failing the push
        - Sessions expire after 24 hours of inactivity. Previously, starting a
          push with an expired session reported the upload as "complete" with
          every row listed as an error, which looked like a data problem rather
          than an expired login.
        - The plugin now detects the expired session, renews it automatically
          when your password is saved, or asks you to log in again. Nothing is
          uploaded until the session is valid, so no partial pushes.
        - Affects planned sample pushes and all model data pushes.
    2.30.0 - Roads layer: pull, edit and push access roads
        - Roads now sync both ways like any other layer. Pull the project's
          access roads into QGIS, edit their attributes (gate status, surface
          type, condition, max width, seasonal window), and push the changes
          back. Roads match on name within the project, so an edited road
          updates in place rather than duplicating.
        - Roads are styled automatically by access status - open green,
          closed red, gated orange, seasonal amber, unrecorded grey, with
          gated and closed drawn dashed and seasonal dotted. Paved surfaces
          draw one weight heavier.
        - The styling comes from the server, so the QGIS layer, the web map
          and the mobile field app always agree. Previously each client
          carried its own copy of the palette and they could drift apart.
        - A road with no access attributes recorded renders grey. That is
          expected, not an error: fill in gate status or route status and the
          colour follows on the next pull.
    2.29.2 - Stamp the plugin version on every API request
        - Every request the plugin makes now carries an X-Plugin-Version
          header identifying the exact build. This is purely diagnostic: it
          lets support pinpoint which plugin version a customer is running
          straight from the server logs (the QGIS user-agent does not include
          the plugin version), so a "which build is this?" question is answered
          in seconds instead of guesswork. No change to any feature behavior.
    2.29.1 - Fix: claims silently dropped from the Lode Claims layer
        - Editing a claim block with snapping (copy / move / snap / delete a
          grid down to the claims you want) could leave some claims with a
          duplicated corner vertex - an invalid ring the server rejected, so
          those claims silently vanished when the Lode Claims layer was built.
        - Claim boundaries are now de-duplicated of coincident vertices when
          read: a claim whose four corners are still present is repaired and
          kept; a claim actually collapsed below four distinct corners is
          skipped and named in the log so you know which one to redraw.
        - Also hardened ring-closing throughout so an already-closed corner
          list is never doubly-closed, and geometry is cleaned once more right
          before it is sent to the server.
        - If the server still has to skip a claim for invalid geometry, the
          skipped claims are now listed by name after layer generation instead
          of quietly disappearing, so you know exactly which ones to redraw.
        - Root cause is a known, still-open QGIS bug (QGIS #62427): snapping
          can duplicate a corner vertex during grid editing.
    2.29.0 - Location Notices: no more timeouts on large claim blocks
        - Generating Location Notices for large blocks (hundreds of claims) no
          longer times out. Document generation now runs as a background job
          with a live progress dialog and polls until finished, instead of
          holding one long request open (which could be cut off at ~100s on
          big blocks even though the server kept working). Same reliable
          mechanism already used by Process Claims and Update LM Corners.
        - The "Generate Location Notices" button is now the primary blue style,
          matching the "Process Claims" button, with its own progress bar.
    2.28.0 - Claim layers persist to GeoPackage; base layers survive reopen
        - Generated claim layers (Lode Claims, Corner Points, LM Corners,
          Monuments, Center Lines) are now always written to a GeoPackage.
          Previously, if no GeoPackage was created in Step 1 they were held in
          memory only and were lost if QGIS crashed or closed while adjusting
          LM corners. A GeoPackage is now auto-created in your GeodbData folder
          when one wasn't selected, so this work always survives a crash/reopen.
        - Streaming base layers (PLSS Grid, Federal Lands, AK State Lands) now
          remember their on/off state in the project. When you reopen a project
          (or after a crash) they re-add and resume streaming automatically
          instead of having to be removed and re-toggled.
    2.27.1 - Vector layer dash-pattern fidelity
        - Dash arrays in server styles are recognized in both separator
          conventions ("8 6" and "8,6"), so dashed/dotted line styles map to
          the matching QGIS line style instead of a generic dash. Found by
          the headless QGIS 3.34 integration drive (16/16 scenarios passed).
    2.27.0 - Vector Layers: styled pull + push-as-draft
        - New "Vector Layers" entry in the Data Sync tab. Pick any vector
          layer uploaded to geodb.io (geology units, faults, targets, ...)
          and pull it into QGIS with its styling translated to a native
          QGIS renderer - single, categorized and graduated styles map to
          the matching QGIS renderer on the original attributes; fill
          patterns translate to the nearest QGIS fill style. Layers styled
          by the geoDB lithology/alteration/formation catalogs render with
          their catalog colors and legend labels.
        - Pull is always full resolution - geometry is never simplified,
          so an edit round trip cannot degrade untouched features.
        - Push uploads your edited copy back as a NEW DRAFT layer on the
          server (the published layer is never modified from QGIS); review
          and promote the draft on the web. A snapshot diff detects whether
          anything actually changed - unchanged layers report "nothing to
          push" without touching the server. Your current QGIS symbology
          travels with the draft.
    2.26.0 - Stay signed in + Data Sync tab refresh
        - Login credentials now actually persist. Previously a failed session
          restore for ANY reason (offline at QGIS startup, a transient network
          error) deleted the stored token, username and password; now only a
          server-rejected password clears the saved password, and transient
          errors keep everything. If the saved token has expired the plugin
          silently re-authenticates with the saved password instead of
          prompting again.
        - "Remember my email" and "Save password" now default to ON, and your
          choice is remembered between sessions. Two-factor accounts can save
          their password too. Re-logging in with the save box unticked no
          longer silently wipes a previously saved password mid-session.
        - Data Sync tab restyled: friendly data type names with descriptions
          (Land Holdings, Drill Collars, ...), a live inline status line while
          a pull or push runs plus a color-coded result summary right in the
          tab (no more digging through the Log tab), and a per-project "last
          pulled / last pushed" line for the selected data type. Fully
          theme-aware in light and dark mode, consistent with the login and
          claims styling.
    2.25.0 - Number & Rename fix: server ordering restored, simple offline fallback
        - "Number & Rename Claims" silently numbered nothing on the server
          path: the request sent sort_direction where the endpoint reads
          direction, and the reply was parsed for ordered_claims where the
          endpoint returns claims. The empty result skipped Manual_FID
          assignment without an error, so renaming fell back to feature
          insertion order - the scrambled / right-to-left numbering seen in
          the field. The request/response now match the endpoint, and any
          empty or mismatched reply raises so the offline fallback runs.
        - The offline fallback is now deliberately simple (strict north-south
          then west-east sort). The row-banding port added in 2.22.1 was
          removed: the rotation-tolerant banding rule is maintained
          server-side only, so the two implementations cannot drift apart.
        - When the simple offline ordering runs (offline or server error),
          the plugin warns on the message bar that rotated blocks may be
          misnumbered and to re-run Number & Rename when online.
    2.24.0 - Lead-file plotted claims + tri-state quarter-sections
        - New streaming layers for the lead-file claim plotting service:
          "Public Plotted Claims" (claims whose private window has elapsed) and
          "My Plotted Claims" (your own plots, including in-window private ones
          with a days-remaining note). Only you ever see your private plots.
        - New "Quarter-sections (tri-state)" layer colors each quarter-section by
          plot state: unplotted (class-color fill), plotted (tier-styled outline),
          or mixed/partially-plotted (black diagonal hatch that survives
          black-and-white filing-map prints and color-blindness). The state is
          computed on the server for the signed-in account, so a private plot
          never leaks its existence to anyone else.
        - The plotted-claims layer also carries the derivation chain (reference
          point to tie line to corners) with the parsed course labels, matching
          the web map overlay.
    2.23.1 - Fix Windows crash in PLSS/Federal Lands/BLM claims streaming
        - QGIS could crash with a fatal access violation while panning or
          zooming with PLSS Grid, Federal Lands, or BLM Claims streaming
          enabled. Each background fetch built its own SSL context on its
          own thread; on Windows, building more than one at the same time
          races inside Python's SSL layer and corrupts memory, which then
          crashed somewhere unrelated (often the map renderer) shortly
          after.
        - Fix: all three streaming layers now share one SSL context per
          plugin session, built once and reused, so concurrent background
          fetches can no longer race during setup. No behavior change to
          streaming itself.
    2.23.0 - Arizona filing maps ready for e-recording
        - AZ filing maps now keep all content inside a 1 inch page margin,
          as required by the Arizona e-file platform. The layout is scaled
          uniformly, so proportions, map scale, and the scale bar all stay
          correct.
        - Claim corners on AZ filing maps now show as plain black dots
          without C1-C4 text. Corner identification is already covered by
          the bearing and distance edge labels and the bearings narrative,
          so the text only added clutter.
    2.22.10 - Planned samples no longer overwrite existing ones
        - When you push a new batch of planned samples, the sequence numbers
          now start after the highest one already in the project, so a fresh
          batch can't overwrite samples that are already planned or assigned.
          The confirmation and log show the start that was used (e.g. "started
          at SS-025 to avoid existing samples"). Requires the matching server
          update; on older servers the existing overwrite warning still applies.
    2.22.9 - Fix: sample-name field dropdown now populates on open
        - The "Name field" dropdown in Plan Field Samples was empty because
          it was only bound when the layer changed, not for the layer already
          shown when the dialog opens. It now populates immediately and
          re-syncs when you tick the option, so the layer's fields are always
          listed.
    2.22.8 - Plan samples using your own names from a layer field
        - When pushing a point layer as planned samples, you can now tick
          "Use a layer field for the sample name" and pick an attribute
          (e.g. your own "AK26-1001S" IDs). Those become the sample names
          shown on the web dashboard and the mobile app map / collect page.
        - Off by default - sequence numbers are still generated for
          navigation, and names stay blank for the field crew to scan as
          before. The chosen field is checked up front: blanks, duplicates
          and over-50-character values are flagged before anything is pushed.
    2.22.7 - Claim map title block + grid polish
        - Field and filing map title blocks: the company name and the map
          type/name line below it (e.g. "BMX Lode Claims - Field Map") now
          auto-fit to a single line. Long names shrink just enough to stay
          legible and never wrap to a second line.
        - The author line populates from the signed-in geoDB user's name
          (e.g. "Elizabeth White") instead of a fixed placeholder.
        - Coordinate grid: the eastern (right-side) labels now sit outside
          the map in the page margin instead of inside the map frame.
    2.22.6 - SQL identifier hardening
        - Queries that must name a table chosen at runtime (the
          format-specific metadata table, table drops) no longer assemble SQL
          from the table name. The metadata read uses a ready-made literal
          query per format; the remaining identifier sites validate the name
          as a bare SQL identifier (shared quote_identifier helper) before
          use, so a malformed name raises instead of reaching the database.
          Clears the string-built-query pattern flagged by security scanners.
          No behavior change for normal use.
    2.22.4 - Claim numbering fix on rotated blocks
        - "Number & Rename Claims" could number claims right-to-left / out of
          order when the claim block was rotated even slightly off north. Two
          causes: the numbering UI never passed the API client, so it always
          used the local fallback; and that fallback (plus the server path it
          should have used) sorted strictly by northing-then-easting, which
          interleaves rows when a row's claims don't share an exact northing.
          Numbering now bands claims into rows first, then orders each row
          west-to-east — top-to-bottom, left-to-right book order — matching
          how grid generation already numbers. Serpentine ordering uses the
          same row banding so the two never disagree.
        - Security: HTTP requests now go through a scheme-restricted opener (http/https only) for defense in depth.
    2.22.0 - Dark-mode compliance for QGIS 4
        - The plugin's UI hardcoded a light-theme color palette, so on
          QGIS 4's dark theme some text was low-contrast or invisible (most
          visibly the login form, where input text rendered white-on-white).
          All widget styling now flows through one palette-aware token layer
          (utils/theme.py) that detects light vs dark and resolves each color
          accordingly. Light mode is unchanged; dark mode is readable.
        - Semantic data colors (commodity/mineral swatches, map-feature and
          claim-type fills, range-category colors) are intentionally
          preserved across both themes.
    2.21.2 - QGIS 4 (Qt6) Storage dialog crash fix
        - Configuring storage / pulling data crashed under QGIS 4 because
          QStandardPaths.DocumentsLocation and .TempLocation were still
          referenced in their old unscoped form, which PyQt6 removed. These
          now resolve through the existing Qt5/Qt6 compatibility shim.
    2.21.1 - QGIS 4 (Qt6) load-crash fix
        - The main dialog crashed on open under QGIS 4 because a few Qt
          enums were still referenced in their old unscoped form
          (QFrame.HLine, QFont.Bold, QSizePolicy.*), which PyQt6 removed.
          These now resolve through the existing Qt5/Qt6 compatibility
          shim, so the plugin loads on QGIS 4 as well as 3.
    2.21.0 - Map Capture: resolution control + fix CRS-dependent distortion
        - New "Resolution" selector in the Project Files > Map Capture tab
          (Screen / High 2048 / Very high 4096 / Maximum 8192 px) with a
          live estimated output size (W x H px and approx MB) that updates
          with the current view and selection. Higher resolutions give
          crisper web tiles; the longest edge is the target and the other
          edge is derived from the Web-Mercator extent so aspect is exact.
        - Fix: captured rasters were distorted when the project CRS was
          WGS84 (or a Web-Mercator alias) and undistorted only in UTM. The
          re-render into Web Mercator reused the native canvas pixel
          dimensions, whose aspect ratio differs from the Web-Mercator
          extent (Mercator stretches latitude by sec(phi)), so QGIS
          re-fit the extent and the reported bounds no longer matched the
          image. The server's affine georeferencing then stretched it.
          Every capture now renders through one path: reproject to
          EPSG:3857, size the image to the 3857 extent's aspect ratio, and
          report the actual rendered extent as bounds. Correct at any
          project CRS and latitude.
    2.20.0 - Stream Alaska state-land polygons from geodb.io
        - New "Show AK State Lands (Streaming)" toggle under Basemaps,
          alongside the existing Federal Lands toggle. Reuses the same
          /services/api/federal-lands/ endpoint with
          ownership=state&state=AK, sourced from AK DNR's
          Ownership_StateLandAll FeatureServer (~22k polygons
          statewide). Same QClaims access gate as federal lands.
        - Important for MTRSC siting: AK DNR's Sept-2022 fact sheet
          requires witness posts on adjacent state land when a section
          corner falls on private overlap. Surfacing the overlay live
          in QGIS lets the operator see those alternative monument
          sites at planning time.
        - Style: pale gold (#F5E6A1) single-category fill — matches
          the staff PLSS preview map's state-land overlay and the
          MTRSC filing-map renderer's `state` category.
    2.19.7 - Drop ID/NM LM-typed stakes from Step-7 push (CP-2026-0059 final)
        - Idaho Code 47-602 and NMSA 69-3-1 require the Location Monument
          to be at a CORNER. Those states have no separate Location
          Monument stake — the LM is one of the four WP-typed corner
          stakes, identified via LandHolding.lm_corner. Pushing a
          stake_type='LM' for an ID/NM claim creates a stake at a
          regulatorily-invalid (non-corner) position; the resulting
          location notice cannot be filed.
        - Server stopped emitting these for ID/NM in production
          2026-05-07 (offset_lm waypoint removed, monument-overrides at
          non-corner positions rejected). API serializer now rejects
          inbound LM stakes linked to ID/NM LandHoldings as a backstop.
        - This release adds the matching client-side filter: claims
          manager's push_to_server drops 'discovery'-typed waypoints
          whose claim is in ID/NM before they're formatted as stakes.
          Stops the per-stake 400 chatter when an old QGIS project still
          has a stale Monuments layer from a pre-2026-05-07 run; user
          sees a clean push instead. Filter only triggers when EVERY
          linked claim is ID/NM — shared waypoints with at least one
          non-ID/NM claim still go through (server validates per-link).
        - No behavior change for non-ID/NM states: NV/CA/AZ/WY/MT/CO/OR/
          WA/UT/AK/SD continue to push their discovery monuments
          normally. Auto-LM placement for those states is unchanged.
    2.19.6 - Fix v2.19.5 reading State attribute from the wrong layer
        - v2.19.5's per-claim state lookup used `state.claims_layer` to read
          the State attribute. That property resolves to the Initial Layout
          polygon layer (from Step 2), NOT the Lode Claims layer — Initial
          Layout has no State attribute, so the lookup always returned None,
          and `None in {ID, NM}` was False. Filter never fired. Every claim
          in the Monuments layer was sent through as an override regardless
          of state, and the server obediently re-stamped manual_override=True
          on the same 155 RC claims (visible in pod logs as "Manual LM
          override applied — using ...")
        - Fix: build a per-claim {name: state} lookup from the best
          available source. Prefer state.processed_claims (in-memory dicts
          from the most recent server response — has 'state' per claim);
          fall back to scanning the project for a layer whose name starts
          with "Lode Claims" and reading its State attribute. State has no
          tracked layer id for the Lode Claims layer, so this name-based
          search is the only reliable path
    2.19.5 - Skip ID/NM claims in monument_overrides capture
        - The plugin's read_monument_overrides_from_state reads ALL features
          from the QGIS Monuments layer and sends them to the server as
          user-moved overrides ("preserve these positions across regen").
          For ID and NM that flow has no legal use — those states require
          the LM at a corner. Sending an override for an ID/NM claim made
          the server stamp manual_override=True and re-emit the off-corner
          LM the v2.19.4 layer-clear was supposed to make impossible
        - Created a feedback loop on Joshua's Rock Creek RC re-walk:
          stale Monuments features → "overrides" pushed → server replays →
          fresh-but-identical Monuments rendered → still 155 LMs
        - Fix: skip claims whose state is ID/NM in the override capture.
          State is read from the Lode Claims layer's `State` attribute
          (already populated by claims_layer_generator). Non-ID/NM states
          (NV/CA/AZ/WY/etc.) keep the override-preservation behavior
          unchanged
    2.19.4 - Always clear stale Monuments / Sideline / Endline layers on regen
        - The plugin's claims_layer_generator only created the Monuments,
          Sideline Monuments, and Endline Monuments layers when the server
          response had non-empty data for them. After 2026-05-06's removal
          of the regulatorily-invalid Phase 3 slide-from-corner code, the
          server correctly stopped emitting discovery monuments for ID/NM
          claims. But the plugin's "remove existing layers before regen"
          pass only matched layer names that were ALSO in the new response,
          so the old Monuments layer's 155 stale features (from yesterday's
          Phase-3 run) persisted in QGIS memory across re-walks
        - Step 6's _read_monument_positions_from_layers then read the stale
          features and stamped them into state.processed_claims; the Step 7
          push wrote `discovery_monument` back into qclaims_data on 155
          LandHolding rows (Rock Creek CP-2026-0059) even though the new
          server algorithm had decided not to place any. The pod-side
          [CLAIM_STAKE_LINKING] log lines reading "discovery=yes" for ID
          claims were the visible signal
        - Fix: _remove_existing_layers now always includes the optional
          state-dependent layers (Monuments / Sideline / Endline) in its
          removal set, regardless of whether the new response contains
          entries for them. Missing entry in the new response now correctly
          translates to "layer absent in project," not "layer left as it
          was last time." Side-effect cleanup of the Step 6 read path is
          implicit: no layer means no features means no monument data
          merged into the push.
    2.19.3 - Fix v2.19.2 rehydrate building invalid LandHolding geometry
        - Rebuilt corners only carried easting/northing (UTM), no lat/lon.
          claims_manager._format_landholding falls back to building GeoJSON
          from `[c.get('lon'), c.get('lat')]` when no `geometry` field is
          set on the claim — but those keys were missing, so it shipped
          [[None, None], ...] to the server. OGR rejected all 711 LandHolding
          rows with "Invalid geometry pointer returned from
          OGR_G_CreateGeometryFromJson".
        - Fix: rehydrate now transforms each polygon ring vertex through a
          QgsCoordinateTransform from the layer's source CRS to EPSG:4326,
          stores both UTM (easting/northing) and WGS84 (lat/lon) on every
          corner, and constructs `rotated_geometry` (WGS84 GeoJSON) and
          `rotated_geometry_utm` (UTM GeoJSON) directly so the format
          function doesn't take the fallback path at all.
    2.19.2 - Fix v2.19.1 rehydrate matching the wrong polygon layer
        - rehydrate_from_layers used substring "Lode Claims" matching, which
          also matched the Initial Layout layer because its display name
          ("Initial Layout [<prefix> Lode Claims]") repeats the same suffix.
          On RC-style multi-block projects the matcher picked Initial Layout
          first, then skipped every feature for missing fields, leaving
          processed_claims at zero and the buttons greyed.
        - Switched to startswith("Lode Claims") so only the actual lode
          claims layer matches.
    2.19.1 - Auto-rehydrate processed claims/waypoints after plugin reload
        - Plugin reloads (from upgrades) and QGIS restarts wiped the
          in-memory processed_claims and processed_waypoints lists, leaving
          the wizard auto-resumed at Step 7 but with greyed-out Push and
          Generate Maps buttons. The data was always available in the
          project's QGIS layers (Lode Claims, Waypoints, Monuments,
          Endline/Sideline) — there was just no path to read it back into
          state without forcing a server re-process
        - New ClaimsWizardState.rehydrate_from_layers() reconstructs both
          lists by walking the existing layers. Called automatically:
          (a) at the end of load_from_qgis_project when completed_steps
              indicates Step 6 has been finished, and
          (b) defensively on Step 6 and Step 7 on_enter when the in-memory
              list is empty
        - Server-side data is authoritative for any field the rehydrate
          path can't reconstruct (PLSS, deadlines, calculated_acreage,
          state_requirements_snapshot). The bulk-upsert merges qclaims_data,
          so re-pushing rehydrated claims preserves those server-side values
        - Same idempotent push semantics: re-pushing rehydrated claims is
          a no-op against natural-key matched rows
    2.19.0 - Async push, "Cluster LMs" option, ID/NM placement fixes
        - Step 7 "Push & Upload to Server" now uses the async/poll pattern
          shipped in v2.16-v2.17 for the rest of the long claim-flow calls.
          For 700+ claim blocks the previous synchronous bulk-upsert pair
          (ClaimStake bulk + LandHolding bulk) tripped Cloudflare's ~100s
          origin-read timeout and the request died at the proxy even though
          the server was happily processing it
        - New consolidated server endpoint `/api/v2/claims/push-to-server/`
          combines ClaimStake bulk + LandHolding bulk + document linking
          into a single async job; progress dialog reports stage
          (pushing_stakes → pushing_landholdings → linking_documents →
          finalizing) with live percentage fed by per-record progress from
          the server's bulk-upsert mixin
        - Document linking (previously a follow-up call after the push)
          is now folded into the same job — saves a round-trip and means
          the success path is one HTTP request from the plugin's POV
        - Step 4 "LM Corner" dropdown gains a "Cluster LMs (smart, ID/NM)"
          option. When chosen, the server runs the shared-corner clustering
          algorithm: 4-claim interior intersections cluster all 4 LMs at one
          shared corner so the crew surveys one point instead of four
          scattered ones. Prefers shared corners on public land; if no
          corner is on public land, slides along an edge to the nearest
          public-land position
        - Behavior change for ID/NM: explicit corner picks (1-4) are now
          honored literally — the server no longer silently overrides the
          user's choice with the algorithm. Cluster behavior is opt-in via
          the new dropdown entry. (This was the 2026-05-05 phase-2 work
          that wasn't actually firing the way users could see; it now does)
        - Backwards compatible: against an older server without the
          consolidated push endpoint, the plugin transparently falls back
          to the legacy two-call path
    2.18.0 - Sticky monument overrides preserve user-moved LM positions
        - When the user drags a discovery monument in Step 5 (Adjust) and then
          triggers a server-side regen — full preview rebuild OR LM-corner
          rotation — the moved positions are now read from the live monument
          layer and sent up as monument_overrides in the request body. Server
          honors the override (lm-placement-improvements server-side change,
          2026-05-05) and the user's move survives the rerun
        - Without this hand-off the algorithm recomputed a fresh LM and the
          user's positioning was silently clobbered when the new layers were
          written back; this was the standing complaint that motivated the
          server-side override-passthrough work
        - New shared helper processors/monument_overrides.py reads the
          monuments layer, transforms UTM → WGS84 via the claims-layer CRS,
          and emits the {claim_name: {lat, lon, easting, northing}} dict
          shape the server's processing_options['monument_overrides']
          contract expects
        - First-time generation (no monuments layer yet) sends an empty dict
          → no-op on the server, identical to pre-2.18 behavior
    2.17.0 - Async + progress for the rest of the long claim-flow endpoints
        - Async polling helper (X-Async-Capable: 1 + 202 + poll + progress dialog) is
          now shared across the plugin: APIClient.post_async_capable handles the full
          handshake so any caller — manager method or processor — can opt into async
          with one line
        - Applied to: Process Claims (Step 6), Align Corners (Step 6 finalize), Validate
          Claim Grid (Step 6), and Generate Witness Points (Step 7). All now show the
          progress dialog (stage / current claim / %% bar / elapsed / Cancel) and survive
          1000-claim blocks comfortably
        - Pairs with server-side ~3x speed-up of the underlying processor so the typical
          1000-claim run finishes in ~110s server-side
        - Deferred (next release): Generate Documents, Generate Package Maps, and bulk
          ClaimStake/LandHolding push — these need a different shape (chunking with
          cleanup_orphans semantics)
    2.16.1 - Live progress dialog for async claim operations
        - Progress dialog (stage, current claim name, %% complete, elapsed time, Cancel button)
          now appears for the duration of Generate Preview Layers (Step 5) and Update LM
          Corner — fed by per-claim progress fields the server reports in each poll response,
          so the user sees real progress instead of a frozen wizard
        - Cancel button cleanly abandons polling on the client; the server worker finishes
          on its own and the result simply expires from cache (nothing to roll back)
        - Pairs with a server-side ~3x speed-up of the underlying processor (federal-lands
          union pre-fetch always runs now), so a 714-claim block typically completes in
          ~70-80s instead of ~190s — comfortably within the proxy timeout window
    2.16.0 - Async preview-layer generation for large claim blocks (700+ claims)
        - Plugin now opts into the server's async path on POST .../preview-layers/ and
          .../update-lm-corner-layers/ via X-Async-Capable header — the server kicks the
          heavy compute onto a background worker, returns 202 + a session id, and the
          plugin polls a status endpoint until the result is ready
        - Side-steps the ~100s Cloudflare origin-read timeout that was returning 502
          Bad Gateway to the plugin even though the server was happily computing the
          full 714-claim result over 3+ minutes server-side
        - Backwards compatible: legacy synchronous responses from older servers (or
          small block fast-paths) pass through the polling helper unchanged
        - 14-minute client-side wall-clock cap on polling so a stuck job surfaces a
          clean error instead of hanging forever
    2.15.1 - Long-running request reliability for large claim blocks
        - Set explicit 5-minute network transfer timeout on all API calls so QGIS does not
          abort the read mid-response on long-running endpoints (claims/preview-layers/
          for 700+ claims, batch sync, etc.) — prevents the "Server returned empty
          response" failure mode where the body was truncated by a client-side timeout
        - Surface a real diagnostic error (status code, body length, body snippet) when
          the server response is not parseable JSON instead of silently returning an
          empty dict — turns "Server returned empty response" into something actionable
        - Configurable via api.transfer_timeout_ms (default 300000)
    2.14.1 - Persistent filing-map annotations
        - Red reference tie line + bearing/distance label now persist as a GeoPackage-backed
          layer so they remain visible when "Lock Styles For Layers" is unchecked in the
          print layout composer (previously ephemeral memory layer — vanished on unlock)
        - Corner labels (C1/C2/C3/C4, with LM flag) persist the same way
        - Reference Point marker: when a map is regenerated and no persistent layer exists
          yet, the state reference points are written into the GeoPackage reference_points
          table and loaded as a real layer — covers legacy QClaims GeoPackages and any
          walkthrough that reaches Step 7 without Step 3 having persisted the points
        - Works on both entry paths: walkthrough (Step 7 Generate Maps) and legacy loader
          (one-click "Load Claims & Generate Maps" on older GeoPackages)
    2.14.0 - Persistent reference points, map-orientation control, NV legal landscape
        - Reference Points layer is now loaded into the Claims Workflow group on project revisit,
          so it remains visible when "Lock Styles For Layers" is toggled in the layout composer
        - Reference Points persist as a real GeoPackage-backed layer (not an ephemeral memory layer),
          so their symbology survives across map regenerations
        - Add Page Orientation dropdown on Step 7: Auto (default), Portrait, Landscape, or Both
        - Field Map and generic Filing Map honor the orientation choice; "Both" generates two layouts
          with "(Portrait)"/"(Landscape)" suffixes so users can pick whichever fits the claim-block
          plus reference-point extent
        - NV state filing map now ships the 8.5"x14" legal sheet in BOTH portrait and landscape
          orientations alongside the 36"x24" ARCH D sheet (NRS 517.040 does not mandate legal orientation)
        - New template: nv_state_filing_map_legal_landscape.qpt (14"x8.5" with vertical title block)
        - AZ state filing map continues to use its fixed template (unchanged)
    2.13.0 - Unified claims loader and AZ filing map polish
        - Claims loader now handles both old QClaims and new geodb-plugin GeoPackage formats
        - Single "Load Claims & Generate Maps" button loads either format without re-running the wizard
        - Extract format differences into ClaimsFormat spec so one loader class handles both
        - Build corners from claim polygon vertices (authoritative geometry) instead of corner_points table
        - New format reads reference points from claims_metadata JSON; legacy reads from References table
        - AZ generic Filing Map now reuses the polished AZ state filing map pipeline (same template and output)
        - AZ filing maps use claims-only extent; tie to survey monument conveyed via reference text
        - Add monument-only legend (Location + Endline Monuments) to AZ filing maps
        - Auto-fit variable-length text labels (bearings, monument, reference) so they wrap within the page
        - Scale-bar segment size now adapts to map scale so small claim groups get appropriate 50-250 ft segments
        - Move main scale bar and scale text into the title block so they don't overlap map content
    2.12.0 - Map capture Web Mercator re-render and claims UI streamlining
        - Re-render map captures in EPSG:3857 for better alignment on mobile basemaps
        - Send pixel_width and pixel_height with map capture uploads for server-side georeferencing
        - Merge Auto-Number and Rename Claims into single "Number & Rename Claims" button (Step 2 and Order widget)
    2.11.0 - Legacy claims loader, field map tweaks, and staff order improvements
        - Add one-click legacy claims loader: load old QClaims GeoPackages and generate all maps
        - New LegacyClaimsLoader processor reads qclaims_metadata, maps old table names, applies styling
        - Builds processed_claims from legacy corner points, monuments, and endline monuments
        - Parses QSecs/Meridian fields into PLSS dict for filing map labels
        - Creates Claims Waypoints layer with waypoint_type field from old Symbol values
        - Auto-generates field map, filing map, and AZ state filing map from legacy data
        - Remove corner point symbols and C1-C4 labels from field maps (waypoint labels suffice)
        - Resize landscape map template for better label clearance
        - Auto-populate claimant info and monument type from staff order data
        - Pass company address and default monument type through staff orders dialog
    2.10.0 - NV filing maps, document upload, and compatibility fixes
        - Generate dual NV state filing maps: 36"x24" ARCH D and 8.5"x14" legal size
        - Add claim inset diagram on NV filing maps showing corner numbering and dimensions
        - Filter waypoint labels on field maps: show corner/witness names only (LM symbols unlabeled)
        - Hide LM corner layer for states without separate discovery monuments (ID, NM)
        - Upload documents to claim packages on geodb.io (maps, filing receipts, etc.)
        - List and manage uploaded package documents from Step 7
        - Fix reference points layer creation in storage manager (auto-create table)
        - Add string typeName fallback for QgsField on Mac QGIS builds
        - Fix RuntimeError on deleted Qt C++ objects in step5 layer cleanup and reference map tool
        - Remove plain geodb.zip copy from packaging script (versioned zip only)
        - Multi-line NV filing map title for better readability
    2.9.0 - Claims GeoPackage server integration
        - Merge Push to Server into single Push & Upload button on Step 7
        - Auto-upload claims GeoPackage to server after pushing claims/stakes
        - Link uploaded GeoPackage (ProjectFile) to ClaimPackage on server
        - Save layer styles into GeoPackage before upload
        - Add server GeoPackage dropdown on Step 1 (pull claims GeoPackages from server)
        - Download and load server GeoPackages with metadata and claim_package_id restoration
        - Keep Browse Local and Create New options as fallback
        - Extract shared GeoPackage utilities (style save, download) to utils/gpkg_utils.py
    2.8.3 - Professional filing maps with surveyor's bearings and annotations
        - Add surveyor's bearing notation (N 45°00' W) replacing cardinal directions on all filing maps
        - Add dimension annotations on claim polygon edges (1,500' and 600' labels)
        - Add reference tie line from survey monument to Corner No. 1 with bearing and distance
        - Add corner labels (C1-C4 with LM designation) on filing maps
        - Add reference point marker (red triangle) at survey monument location
        - Differentiate Filing Map from Field Map with metes & bounds text, monument info, and tie text
        - Fix AZ state filing map: add all annotation layers, proper surveyor's notation
        - Fix NV state filing map: add UTM coordinate grid, fix rotated reference label
        - Convert all scale bars from meters to feet (US mining claim standard)
        - Keep user on Step 7 after Finish so Generate Maps button remains accessible
        - Fix map extent to include reference point on filing maps (tie line visibility)
    2.8.2 - UI improvements, map generation, and security cleanup
        - Reorganize Basemaps widget into unified sections (Basemaps, PLSS Grid, Public Lands, MRDS)
        - Consolidate PLSS/Federal Lands streaming controls into Public Lands section
        - Add Federal Lands streaming with color legend (BLM yellow, Forest Service green)
        - Add verbose logging to Federal Lands streaming worker for diagnostics
        - Fix Federal Lands layer geometry type (Polygon to MultiPolygon) for mixed responses
        - Add Generate Maps button to Step 7 (claims print layouts for field and filing use)
        - Add ClaimsMapGenerator processor for print-ready QGIS layouts
        - Centralize all urlopen calls into safe_urlopen() wrapper with scheme validation
        - Eliminates Bandit S310 findings from all call sites (5 files cleaned up)
    2.7.1 - Fix CRS transform crashes and claims wizard Next button
        - Fix QgsCsException crash when BLM/PLSS streaming layers transform extent with wide-extent basemaps
        - Add extent_to_wgs84() utility that clamps to CRS valid bounds before transforming
        - Apply safe transform to all 4 bounding box transform sites (BLM, PLSS, basemaps PLSS/MRDS)
        - Fix Step 6 (Finalize) Next button permanently disabled after processing and doc generation
        - Remove redundant tos_accepted/access_info validation from Step 6 (already validated in Step 1)
        - Persist tos_accepted in GeoPackage metadata for proper resume across sessions
        - Add conflict check before pushing planned samples (warns before overwriting existing records)
    2.7.0 - Claims workflow layer group consistency and auto-resume
        - Unify all claims layer group management under shared utility (get_or_create_claims_group)
        - All wizard steps now use the same "Claims Workflow [XX Lode Claims]" group
        - Add Claim Name Prefix field to Step 1 (used for GeoPackage filename and group naming)
        - Auto-suggest GeoPackage filename from prefix (e.g. GE_claims.gpkg)
        - Auto-resume wizard to next incomplete step when reopening a previous project
        - Resume dialog shows completed steps and offers to continue or start from Step 1
        - Step indicators still allow jumping to any completed step
        - Remove duplicate _add_layer_to_claims_group implementations across files
    2.6.2 - Streamline claims wizard finish flow
        - Remove redundant Download Documents button from Step 7
        - Finish button now opens Claim Packages page in browser and returns to Sync Data tab
    2.6.1 - Fix claims wizard step indicator and waypoint layer tracking
        - Fix off-by-one in step indicator (completed steps showed green on wrong step)
        - Fix step indicators not updating to gray when going back invalidates downstream steps
        - Fix step circle showing checkmark instead of number when navigating back to current step
        - Fix connector line coloring between steps
        - Add confirmation dialog when Back button or step click would invalidate completed steps
        - Fix Step 7 waypoints layer fallback picking wrong claim block in multi-block projects
        - Fix Step 6 monument adjustment updating wrong waypoints layer in multi-block projects
    2.6.0 - Code audit, pay-per-claim wizard, and refactoring
        - Add pay-per-claim 3-step wizard (Setup, Layout, Order) with Stripe checkout
        - Dynamic wizard step rebuild based on user access level (enterprise vs pay-per-claim)
        - Fix broken submit_order call in claims_order_widget (migrated to create_checkout_session)
        - Fix Project.company AttributeError in UserContext.to_dict() serialization
        - Fix null_geom_count double-counting geometry parse failures in layer_processor
        - Rename PermissionError to APIPermissionError to avoid shadowing Python built-in
        - Extract shared utilities: geometry.py, crs_utils.py, format_helpers.py
        - Centralize claims endpoint URL building in config.get_claims_url()
        - Fix SQLite connection resource leaks in claims_storage_manager (contextlib.closing)
        - Fix QNetworkReply memory leak in API client timeout path
        - Remove ~15 unused methods and 2 dead modules (api_response.py, photo_cache.py)
        - Replace print() calls with proper logger throughout codebase
        - Add claims_pushed state tracking with double-push warning in Step 7
        - Add layer cleanup on wizard reset and step re-entry
    2.5.0 - Claims Wizard & UI Improvements
        - Fix claims layer ownership tracking to prevent cross-group layer collisions
        - Use session-based layer IDs instead of name matching in Step 5/Step 6
        - Fix combo box signal blocking during refresh to prevent state corruption
        - Add project name suffix to Claims Workflow layer group name
        - Use server-provided sequence_number for location monument naming
        - Style QComboBox dropdown menus across all claims and assay dialogs
        - Add URL scheme validation for BLM and PLSS streaming requests
    2.4.3 - Qt6 Method Compatibility
        - Replace deprecated .exec_() with .exec() across all dialogs and event loops
        - Ensures forward compatibility with Qt6 / QGIS 4.0+
    2.4.2 - Cross-Version Compatibility (QGIS 3.36 / 3.38+ / 4.0)
        - Fix QgsField TypeError across all QGIS versions (3.36, 3.38+, 4.0) with runtime probe
        - Fix Qt enum compatibility for QGIS 4.0 (Qt.DashLine, Qt.Checked, QFrame.HLine, etc.)
        - Fix streaming layer crashes on project switch (wrapped C++ object deleted)
        - Fix PLSS grid streaming checkbox staying disabled after session restore
        - Add URL scheme validation for BLM and PLSS streaming requests (security hardening)
        - Centralized Qt5/Qt6 enum compatibility layer in utils/compat.py
    2.4.1 - Compatibility & Streaming Fixes
        - Add Qt5/Qt6 compatibility layer for QgsField types
        - Improve login error messaging
        - Add BLM claims manager and PLSS streaming manager
        - Basemaps widget and config updates
    2.3.1 - Toolbar Icon Fix
        - Fix missing toolbar icon (Qt resources module was not imported)
    2.3.0 - Claims & Stakes Sync Fix
        - Fix ClaimStakes not linking to ClaimPackage during push
        - Add claim_package field to ClaimStake schema for proper package scoping
        - Stakes now correctly appear under their ClaimPackage on the web dashboard
    2.2.2 - Code Quality & Bug Fixes
        - Fix division by zero when pulling model with no records
        - Remove unused imports and variables across codebase
        - Fix PEP 8 style issues (whitespace, line breaks, f-strings)
        - Pass Bandit security scan and Flake8 quality checks cleanly
    2.2.1 - Project Files & GeoPackage Sync
        - New "Project Files" tab with sub-tabs for Map Capture, Upload Files, and GeoPackage Sync
        - Upload georeferenced raster layers from QGIS to geodb.io server
        - GeoPackage sync: push/pull GeoPackage files with embedded styles
        - Improved world file generation with native CRS bounds support
        - Fix HiDPI/Retina display handling for map captures
        - Pre-select active layer in field work dialog
        - Security fixes: SQL injection prevention, URL scheme validation, safe XML parsing
        - Removed deprecated supportsQt6 metadata flag
        - Bug fixes and stability improvements
    2.1.1 - QGIS 4.0 Compatibility & Security Hardening
        - QGIS 4.0 / Qt6 compatibility (supports QGIS 3.0 through 4.99)
        - Security hardening of API client (request retry logic, removed debug exposure)
        - Bi-directional deletion sync with server
        - Deletion conflict detection (local changes vs server deletes)
        - Orphan cleanup for child records
        - Map canvas capture and georeferenced upload
        - GeoPackage storage option for persistent local data
        - Bug fixes and stability improvements
    2.1.0 - Major feature release
        - BC mineral claims workflow with 7-step wizard
        - Two-factor authentication (2FA) support
        - Photo viewer for drill photos and field notes
        - Staff order management interface
        - Basemaps widget for quick layer access
        - Structure model with geological symbology
        - GPX export for GPS devices
        - Fixed heap corruption crashes (QgsBlockingNetworkRequest)
        - Improved cross-platform compatibility
    2.0.0 - Major architectural rebuild
        - Modular design with separation of concerns
        - Improved error handling and logging
        - Progress feedback for long operations
        - Incremental data synchronization
        - Type-safe code with full type hints
        - Comprehensive documentation
    0.1 - Initial release

# Tags are comma separated with spaces allowed
tags=python,database,api,geospatial,data sync

homepage=https://geodb.io
category=Database
icon=icon.png
# experimental flag
experimental=False

# deprecated flag (applies to the whole plugin, not just a single version)
deprecated=False

# Since QGIS 3.8, a comma separated list of plugins to be installed
# (or upgraded) can be specified.
# Check the documentation for more information.
# plugin_dependencies=

# If the plugin can run on QGIS Server.
server=False
