# 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.22.6
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.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
