Skip to content

feat(spp_api_v2_gis): add OGC API Features GIS endpoints#71

Open
jeremi wants to merge 6 commits into19.0from
feat/api-v2-gis
Open

feat(spp_api_v2_gis): add OGC API Features GIS endpoints#71
jeremi wants to merge 6 commits into19.0from
feat/api-v2-gis

Conversation

@jeremi
Copy link
Member

@jeremi jeremi commented Mar 5, 2026

Summary

New module providing OGC API Features-compliant GIS endpoints:

  • OGC API Features Core endpoints (landing, conformance, collections, items)
  • Spatial statistics queries with CEL variable computation
  • Geofence CRUD for saved areas of interest
  • QML style files for QGIS auto-styling
  • Report collections split by admin level
  • Proximity query endpoint
  • Per-level QML thresholds and statistics ACL
  • GeoPackage export for offline use

Dependencies: spp_api_v2, spp_gis, spp_gis_report, spp_area, spp_hazard, spp_vocabulary, spp_statistic, spp_aggregation

Test plan

  • Run ./scripts/test_single_module.sh spp_api_v2_gis
  • Verify OGC endpoints respond with correct GeoJSON
  • Verify QGIS can connect via OGC API Features

@gemini-code-assist
Copy link

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a new module, spp_api_v2_gis, designed to significantly enhance the geospatial capabilities of the OpenSPP API. By implementing OGC API Features-compliant endpoints, it ensures broad interoperability with external GIS clients and adherence to GovStack GIS Building Block standards. The module provides advanced functionalities for spatial data analysis, including flexible spatial and proximity queries with privacy-preserving aggregation, alongside tools for managing user-defined geofences and exporting data for offline use. This foundational work enables a powerful and secure GIS integration layer for the platform.

Highlights

  • OGC API Features Endpoints: Introduced a comprehensive set of OGC API Features-compliant GIS endpoints, including landing page, conformance, collections, and individual feature items, enabling interoperability with various GIS clients like QGIS and ArcGIS.
  • Spatial Statistics Queries: Added robust spatial statistics query capabilities, allowing users to compute aggregate statistics within arbitrary GeoJSON polygons, with support for CEL variable computation and k-anonymity suppression for data privacy.
  • Geofence Management: Implemented full CRUD (Create, Read, Update, Delete) functionality for geofences, enabling users to define, save, and manage areas of interest, which can be used for spatial queries and reporting.
  • QGIS Styling Integration: Provided dynamic QML style file generation for QGIS, allowing automatic and consistent visualization of GIS layers based on report configurations, color schemes, and per-level thresholds.
  • GeoPackage Export: Enabled export of GIS layers and geofences as GeoPackage (.gpkg) or ZIP of GeoJSON files for offline use, enhancing data portability and accessibility for field operations.
  • Proximity Query Endpoint: Introduced a proximity query endpoint to find registrants within or beyond a specified radius from a set of reference points, utilizing efficient PostGIS spatial indexing.
  • API Client Scope Extension: Extended the API client scope model to include 'gis' and 'statistics' resources, allowing fine-grained access control for GIS-related functionalities.
  • FastAPI Router Integration: Integrated new FastAPI routers for export, geofence, OGC features, proximity, spatial query, and statistics into the existing API V2 infrastructure.
Changelog
  • spp_api_v2_gis/README.rst
    • Added a new README file detailing the OpenSPP GIS API, its key features, architecture, API endpoints, required OAuth scopes, data privacy controls, and dependencies.
  • spp_api_v2_gis/init.py
    • Added module imports for models, routers, schemas, and services.
  • spp_api_v2_gis/manifest.py
    • Added the module manifest, defining its name, category, version, author, license, dependencies, and a detailed description of OGC API Features and proprietary endpoints.
  • spp_api_v2_gis/data/qml_templates/graduated_polygon.qml
    • Added a QML template for graduated polygon styling in QGIS, using placeholders for field name, ranges, symbols, and opacity.
  • spp_api_v2_gis/data/qml_templates/point_basic.qml
    • Added a QML template for basic point styling in QGIS, with placeholders for color and opacity.
  • spp_api_v2_gis/data/qml_templates/point_cluster.qml
    • Added a QML template for clustered point styling in QGIS, including placeholders for color, opacity, and cluster size.
  • spp_api_v2_gis/models/init.py
    • Added module imports for api_client_scope, fastapi_endpoint, and geofence models.
  • spp_api_v2_gis/models/api_client_scope.py
    • Extended the spp.api.client.scope model to include 'gis' and 'statistics' as new resource selections.
  • spp_api_v2_gis/models/fastapi_endpoint.py
    • Extended the fastapi.endpoint model to integrate new GIS-related FastAPI routers (export, geofence, OGC features, proximity, spatial query, statistics) into API V2.
  • spp_api_v2_gis/models/geofence.py
    • Added a new Odoo model spp.gis.geofence for managing saved geographic areas of interest, including fields for name, description, geometry, type, tags, incident association, and methods for GeoJSON conversion and creation.
  • spp_api_v2_gis/pyproject.toml
    • Added whool as a build system requirement.
  • spp_api_v2_gis/readme/DESCRIPTION.md
    • Added a detailed markdown description of the GIS API, its features, architecture, endpoints, OAuth scopes, and data privacy considerations.
  • spp_api_v2_gis/routers/init.py
    • Added module imports for all new GIS-related FastAPI routers.
  • spp_api_v2_gis/routers/export.py
    • Added FastAPI router for GIS data export, providing an endpoint to export layers and geofences as GeoPackage or ZIP of GeoJSON files.
  • spp_api_v2_gis/routers/geofence.py
    • Added FastAPI router for geofence management, including endpoints for creating, listing, retrieving, and archiving geofences.
  • spp_api_v2_gis/routers/ogc_features.py
    • Added FastAPI router implementing OGC API Features Core endpoints for GIS data, including landing page, conformance, collections, items, and QML style file retrieval.
  • spp_api_v2_gis/routers/proximity.py
    • Added FastAPI router for proximity queries, allowing users to find registrants within or beyond a specified radius from reference points.
  • spp_api_v2_gis/routers/spatial_query.py
    • Added FastAPI router for spatial statistics queries, enabling calculation of aggregate statistics within arbitrary GeoJSON polygons, including batch query capabilities.
  • spp_api_v2_gis/routers/statistics.py
    • Added FastAPI router for discovering published GIS statistics, grouped by category.
  • spp_api_v2_gis/schemas/init.py
    • Added module imports for geofence, geojson, ogc, and query schemas.
  • spp_api_v2_gis/schemas/geofence.py
    • Added Pydantic schemas for geofence creation requests, responses, and list items.
  • spp_api_v2_gis/schemas/geojson.py
    • Added Pydantic schemas for GeoJSON geometry, features, and feature collections.
  • spp_api_v2_gis/schemas/ogc.py
    • Added Pydantic schemas for OGC API Features responses, including links, landing page, conformance, extents, and collection information.
  • spp_api_v2_gis/schemas/query.py
    • Added Pydantic schemas for spatial query requests and responses, including batch and proximity queries.
  • spp_api_v2_gis/schemas/statistics.py
    • Added Pydantic schemas for statistic information, category information, and statistics list responses.
  • spp_api_v2_gis/security/ir.model.access.csv
    • Added security access rules for the spp.gis.geofence model, defining read, write, create, and unlink permissions for different user groups.
  • spp_api_v2_gis/services/init.py
    • Added module imports and __all__ declaration for all new GIS-related services.
  • spp_api_v2_gis/services/catalog_service.py
    • Added a service for building a GIS catalog from reports and data layers, including methods for retrieving report and layer metadata, and normalizing geometry types.
  • spp_api_v2_gis/services/export_service.py
    • Added a service for exporting GIS data as GeoPackage or ZIP files, including methods for collecting layers and geofences, and handling file creation.
  • spp_api_v2_gis/services/layers_service.py
    • Added a service for retrieving GIS layer data as GeoJSON, supporting reports and data layers, with caching, bbox filtering, and styling hints.
  • spp_api_v2_gis/services/ogc_service.py
    • Added an adapter service for OGC API Features, translating OGC parameters to existing GIS service calls and producing OGC-compliant responses.
  • spp_api_v2_gis/services/qml_template_service.py
    • Added a service for generating QML style files for QGIS, dynamically creating styling based on report configurations, color schemes, and thresholds.
  • spp_api_v2_gis/services/spatial_query_service.py
    • Added a service for executing PostGIS spatial queries, supporting coordinate-based and area-based queries, batch queries, proximity queries, and integrating with the aggregation service for statistics.
  • spp_api_v2_gis/static/description/index.html
    • Added an HTML version of the README for display in Odoo.
  • spp_api_v2_gis/tests/init.py
    • Added module imports for all new GIS-related test files.
  • spp_api_v2_gis/tests/test_batch_query.py
    • Added tests for the batch spatial query service and its Pydantic schemas.
  • spp_api_v2_gis/tests/test_catalog_service.py
    • Added tests for the GIS catalog service, verifying report and data layer retrieval, ordering, and freshness indicators.
  • spp_api_v2_gis/tests/test_export_service.py
    • Added tests for the GIS export service, covering GeoPackage/ZIP export, layer collection, geofence collection, and filename sanitization.
  • spp_api_v2_gis/tests/test_geofence_model.py
    • Added tests for the spp.gis.geofence model, covering creation, uniqueness constraints, GeoJSON conversion, and area computation.
  • spp_api_v2_gis/tests/test_layers_service.py
    • Added tests for the GIS layers service, including GeoJSON retrieval for reports and data layers, filtering, styling, and bbox filtering.
  • spp_api_v2_gis/tests/test_ogc_features.py
    • Added tests for the OGC API Features service, verifying landing page, conformance, collections, items, and QML links.
  • spp_api_v2_gis/tests/test_ogc_http.py
    • Added HTTP integration tests for OGC API Features endpoints, covering authentication, status codes, content types, and parameter parsing.
  • spp_api_v2_gis/tests/test_proximity_query.py
    • Added tests for the proximity query service, covering input validation, area fallback, and result metadata.
  • spp_api_v2_gis/tests/test_qml_template_service.py
    • Added tests for the QML template service, verifying generation of graduated polygon, point, and clustered point QML, hex to RGB conversion, and XML escaping.
  • spp_api_v2_gis/tests/test_spatial_query_service.py
    • Added tests for the spatial query service, covering statistics computation, CEL integration, and privacy metadata.
  • spp_api_v2_gis/tests/test_statistics_endpoint.py
    • Added tests for the statistics discovery endpoint, verifying GIS-published stats, category grouping, and scope access.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@github-advanced-security github-advanced-security bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Semgrep OSS found more than 20 potential problems in the proposed changes. Check the Files changed tab for more details.

@codecov
Copy link

codecov bot commented Mar 5, 2026

Codecov Report

❌ Patch coverage is 72.24436% with 418 lines in your changes missing coverage. Please review.
✅ Project coverage is 58.09%. Comparing base (69e0e5a) to head (41bd6c6).

Files with missing lines Patch % Lines
spp_api_v2_gis/services/layers_service.py 48.00% 143 Missing ⚠️
spp_api_v2_gis/routers/geofence.py 24.35% 59 Missing ⚠️
spp_api_v2_gis/services/spatial_query_service.py 75.23% 53 Missing ⚠️
spp_api_v2_gis/services/export_service.py 74.19% 32 Missing ⚠️
spp_api_v2_gis/routers/spatial_query.py 35.00% 26 Missing ⚠️
spp_api_v2_gis/services/ogc_service.py 83.06% 21 Missing ⚠️
spp_api_v2_gis/routers/statistics.py 36.66% 19 Missing ⚠️
spp_api_v2_gis/routers/export.py 39.28% 17 Missing ⚠️
spp_api_v2_gis/routers/ogc_features.py 86.44% 16 Missing ⚠️
spp_api_v2_gis/routers/proximity.py 46.15% 14 Missing ⚠️
... and 3 more
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             19.0      #71      +/-   ##
==========================================
+ Coverage   55.79%   58.09%   +2.29%     
==========================================
  Files         162      188      +26     
  Lines        9291    10797    +1506     
==========================================
+ Hits         5184     6272    +1088     
- Misses       4107     4525     +418     
Flag Coverage Δ
spp_api_v2_gis 72.24% <72.24%> (?)
spp_base_common 90.26% <ø> (ø)
spp_programs 45.43% <ø> (ø)
spp_security 66.66% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
spp_api_v2_gis/__init__.py 100.00% <100.00%> (ø)
spp_api_v2_gis/models/__init__.py 100.00% <100.00%> (ø)
spp_api_v2_gis/models/api_client_scope.py 100.00% <100.00%> (ø)
spp_api_v2_gis/models/fastapi_endpoint.py 100.00% <100.00%> (ø)
spp_api_v2_gis/routers/__init__.py 100.00% <100.00%> (ø)
spp_api_v2_gis/schemas/__init__.py 100.00% <100.00%> (ø)
spp_api_v2_gis/schemas/geofence.py 100.00% <100.00%> (ø)
spp_api_v2_gis/schemas/geojson.py 100.00% <100.00%> (ø)
spp_api_v2_gis/schemas/ogc.py 100.00% <100.00%> (ø)
spp_api_v2_gis/schemas/query.py 100.00% <100.00%> (ø)
... and 16 more
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a comprehensive GIS API module with OGC-compliant endpoints, which is a significant and well-structured feature addition. The code is generally of high quality, with good separation of concerns into services, routers, and schemas. The inclusion of extensive tests, performance considerations like caching, and robust error handling is commendable.

My review has identified a couple of areas for improvement:

  • A metadata inconsistency in the module's development status.
  • A potential performance issue (N+1 query) in a computed field method.

Addressing these points will further enhance the quality and robustness of this new module.

Note: Security Review did not run due to the size of the PR.

"author": "OpenSPP.org",
"website": "https://github.com/OpenSPP/OpenSPP2",
"license": "LGPL-3",
"development_status": "Production/Stable",

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The development_status is set to Production/Stable, but this contradicts the Alpha status mentioned in the README.rst file and the PR description. To avoid confusion, these should be consistent. Given the context, changing this to Alpha seems appropriate.

Suggested change
"development_status": "Production/Stable",
"development_status": "Alpha",

Comment on lines +116 to +140
def _compute_area_sqkm(self):
"""Compute area in square kilometers from geometry using PostGIS.

Uses ST_Area with geography type for accurate area calculation
in square meters, then converts to square kilometers.
"""
for rec in self:
if not rec.geometry or not rec.id:
rec.area_sqkm = 0.0
continue

try:
# Use PostGIS ST_Area with geography cast for accurate measurement
# Geography type automatically uses spheroid calculations
query = """
SELECT ST_Area(ST_Transform(geometry::geometry, 4326)::geography) / 1000000.0 as area_sqkm
FROM spp_gis_geofence
WHERE id = %s
"""
self.env.cr.execute(query, (rec.id,))
result = self.env.cr.fetchone()
rec.area_sqkm = result[0] if result else 0.0
except Exception as e:
_logger.warning("Failed to compute area for geofence %s: %s", rec.name, str(e))
rec.area_sqkm = 0.0

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The current implementation of _compute_area_sqkm executes a SQL query for each record within the loop, which can lead to performance issues (N+1 problem) when creating or updating multiple geofences at once. This can be optimized by refactoring to use a single batch query to compute the areas for all records in the recordset.

    def _compute_area_sqkm(self):
        """Compute area in square kilometers from geometry using PostGIS.

        Uses ST_Area with geography type for accurate area calculation
        in square meters, then converts to square kilometers.
        """
        recs_to_compute = self.filtered(lambda r: r.geometry and r.id)

        # Set area to 0 for records without geometry or not yet in DB
        for rec in self - recs_to_compute:
            rec.area_sqkm = 0.0

        if not recs_to_compute:
            return

        try:
            query = """
                SELECT id, ST_Area(ST_Transform(geometry::geometry, 4326)::geography) / 1000000.0 as area
                FROM spp_gis_geofence
                WHERE id = ANY(%s)
            """
            self.env.cr.execute(query, (list(recs_to_compute.ids),))
            area_map = {res["id"]: res["area"] for res in self.env.cr.dictfetchall()}

            for rec in recs_to_compute:
                rec.area_sqkm = area_map.get(rec.id, 0.0)
        except Exception as e:
            _logger.warning("Failed to compute area for geofences: %s", str(e))
            for rec in recs_to_compute:
                rec.area_sqkm = 0.0

@jeremi jeremi force-pushed the feat/api-v2-gis branch from d78e3ae to 317b544 Compare March 5, 2026 09:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant