gispulse 2.2.0__tar.gz → 2.2.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {gispulse-2.2.0/src/gispulse.egg-info → gispulse-2.2.1}/PKG-INFO +1 -1
- {gispulse-2.2.0 → gispulse-2.2.1}/pyproject.toml +1 -1
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/persistence/datamart.py +61 -14
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/persistence/loader.py +84 -4
- {gispulse-2.2.0 → gispulse-2.2.1/src/gispulse.egg-info}/PKG-INFO +1 -1
- {gispulse-2.2.0 → gispulse-2.2.1}/LICENSE +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/LICENSE-COMMERCIAL.md +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/README.md +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/setup.cfg +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/__init__.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/_compat.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/_pyogrio_warnings.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/__init__.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/apicarto.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/esb/__init__.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/esb/action_dispatcher.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/esb/bus_message.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/esb/circuit_breaker.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/esb/dlq.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/esb/enums.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/esb/event_router.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/esb/pg_notify.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/esb/pool.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/esb/predicate_evaluator.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/esb/state_store.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/esb/trigger_manager.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/esb/workers/__init__.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/esb/workers/base_worker.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/esb/workers/dispatch_worker.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/esb/workers/identify_worker.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/http/__init__.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/http/app.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/http/auth.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/http/dataset_ops.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/http/dependencies.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/http/error_handlers.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/http/event_hub.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/http/layer_utils.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/http/middleware/__init__.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/http/middleware/audit_middleware.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/http/middleware/metrics_middleware.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/http/middleware/read_only.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/http/portal_app.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/http/rate_limit.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/http/routers/__init__.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/http/routers/_upload_utils.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/http/routers/auth_router.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/http/routers/capabilities_router.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/http/routers/catalog_router.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/http/routers/datasets_router.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/http/routers/esb_router.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/http/routers/examples_router.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/http/routers/filter_router.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/http/routers/jobs_router.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/http/routers/marketplace_router.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/http/routers/ogc_features_router.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/http/routers/pipelines_router.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/http/routers/portal_datasets_router.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/http/routers/portal_features_router.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/http/routers/portal_router.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/http/routers/portal_sql_router.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/http/routers/portal_upload_router.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/http/routers/projects_router.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/http/routers/relations_router.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/http/routers/rules_router.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/http/routers/scenarios_router.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/http/routers/schedules_router.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/http/routers/sessions_router.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/http/routers/system_router.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/http/routers/templates_router.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/http/routers/tiles_router.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/http/routers/triggers_router.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/http/routers/viewer_router.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/http/routers/watchers_router.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/http/routers/ws_router.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/http/schemas.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/http/serve_app.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/mcp/__init__.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/mcp/dryrun.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/mcp/server.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/mcp/workdir.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/metrics.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/ogc/__init__.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/ogc/auth.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/ogc/loader.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/ogc/wfs_client.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/ogc/wfs_fetcher.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/rest/__init__.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/rest/rest_fetcher.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/rest/rest_table_fetcher.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/stac/__init__.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/stac/stac_fetcher.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/webhooks/__init__.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/webhooks/http_client.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/app.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/capabilities/__init__.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/capabilities/_attribute_sql.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/capabilities/_geometry_sql.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/capabilities/base.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/capabilities/classification.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/capabilities/clustering.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/capabilities/density.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/capabilities/network.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/capabilities/network_components.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/capabilities/network_graph.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/capabilities/network_topology.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/capabilities/overlay.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/capabilities/palettes.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/capabilities/pointcloud.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/capabilities/polygon_topology.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/capabilities/postgis_sql.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/capabilities/raster.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/capabilities/registry.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/capabilities/relation_detector.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/capabilities/schema.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/capabilities/selection.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/capabilities/spatial_stats.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/capabilities/sql_pushdown.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/capabilities/strategy.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/capabilities/temporal.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/capabilities/transforms.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/capabilities/validation.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/capabilities/vector/__init__.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/capabilities/vector/aggregate.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/capabilities/vector/assign_projection.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/capabilities/vector/boundary.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/capabilities/vector/buffer.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/capabilities/vector/calculate.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/capabilities/vector/centroid_area.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/capabilities/vector/chaikin.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/capabilities/vector/classify.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/capabilities/vector/clip.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/capabilities/vector/concave_hull.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/capabilities/vector/diff.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/capabilities/vector/dissolve.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/capabilities/vector/extract_holes.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/capabilities/vector/extract_ops.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/capabilities/vector/filter.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/capabilities/vector/force_geometry_type.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/capabilities/vector/intersects.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/capabilities/vector/line_merge.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/capabilities/vector/line_ops.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/capabilities/vector/merge.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/capabilities/vector/nearest.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/capabilities/vector/offset_curve.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/capabilities/vector/parts.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/capabilities/vector/polygonize.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/capabilities/vector/reproject.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/capabilities/vector/shape_ops_advanced.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/capabilities/vector/shape_ops_basic.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/capabilities/vector/simplify.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/capabilities/vector/snap_grid.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/capabilities/vector/snap_points.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/capabilities/vector/spatial_join.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/capabilities/vector/split_lines.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/capabilities/vector/union.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/capabilities/vector/voronoi.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/catalog/__init__.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/catalog/data/basemaps.json +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/catalog/data/epsg_common.json +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/catalog/models.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/catalog/providers/__init__.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/catalog/providers/base.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/catalog/providers/basemaps.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/catalog/providers/flux_ign.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/catalog/providers/flux_osm.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/catalog/providers/opendata_datagouv.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/catalog/providers/opendata_hub.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/catalog/providers/opendata_ign.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/catalog/providers/projections.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/catalog/providers/stac_client.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/catalog/registry.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/catalog/source_bridge.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/cli.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/cli_mcp.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/cli_portal.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/cli_track.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/cli_triggers.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/cli_triggers_watch.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/cli_watch.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/config.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/core/__init__.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/core/assertions.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/core/bulk_ingest.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/core/bulk_runner.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/core/cache.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/core/capability_params.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/core/conditions.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/core/config.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/core/crs.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/core/dag.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/core/data/worldwide_catalog.yml +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/core/data_pack_signature.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/core/dispatcher.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/core/enums.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/core/explain.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/core/fetchers/__init__.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/core/fetchers/base.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/core/fetchers/geoparquet_s3.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/core/fetchers/http_file.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/core/fetchers/ogc_client.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/core/fetchers/ogc_features.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/core/fetchers/stac.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/core/fetchers/table_file.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/core/filter/__init__.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/core/filter/cache.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/core/filter/chain.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/core/filter/expression.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/core/filter/expression_converter.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/core/filter/result.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/core/filter/service.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/core/filter/types.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/core/graph.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/core/io/__init__.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/core/io/geoparquet.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/core/licence_format.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/core/logging.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/core/manifest_v3.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/core/models.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/core/network_graph_handle.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/core/observability.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/core/pipeline.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/core/pipeline_schema.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/core/plugin_contracts.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/core/plugin_hub.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/core/plugin_model.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/core/predicates.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/core/pricing_catalog.yml +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/core/registry.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/core/regulatory_zoning_entry.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/core/relations.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/core/session.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/core/sources.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/core/spatial_index.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/core/sql_safety.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/core/ssrf.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/core/zoning_normalizer.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/diagnostics/__init__.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/diagnostics/system.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/dsl/__init__.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/dsl/expression_parser.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/dsl/geom_fcts.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/orchestration/__init__.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/orchestration/capability_executor.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/orchestration/graph_executor.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/orchestration/job_queue.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/orchestration/job_queue_factory.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/orchestration/metering.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/orchestration/pipeline_executor.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/orchestration/runner.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/orchestration/scenario_runner.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/orchestration/scheduler.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/orchestration/session_manager.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/orchestration/trigger_bridge.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/orchestration/worker.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/persistence/__init__.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/persistence/audit.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/persistence/auth_models.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/persistence/auth_repository.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/persistence/bridge.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/persistence/change_log_watcher.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/persistence/changelog_doctor.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/persistence/changelog_reader.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/persistence/duckdb_change_detector.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/persistence/duckdb_diff_engine.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/persistence/duckdb_engine.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/persistence/duckdb_engine_adapter.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/persistence/duckdb_relations.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/persistence/engine.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/persistence/engine_factory.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/persistence/file_blob_cdc.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/persistence/geonode.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/persistence/gpkg.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/persistence/gpkg_connection.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/persistence/gpkg_engine.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/persistence/gpkg_repository.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/persistence/gpkg_schema.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/persistence/gpkg_spatial.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/persistence/io.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/persistence/licence.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/persistence/postgis.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/persistence/project_io.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/persistence/raster_io.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/persistence/repository.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/persistence/schedule_repository.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/persistence/schema.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/persistence/session_provisioner.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/persistence/sld_converter.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/persistence/source_watcher.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/persistence/spatial_queries.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/persistence/spatialite_engine.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/persistence/spatialite_session.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/persistence/sql_dialect.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/persistence/sql_guardrails.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/persistence/sqlite_repository.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/persistence/storage.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/persistence/style_converter.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/persistence/style_sidecar.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/persistence/tier.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/persistence/virtual_dataset.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/persistence/watcher_registry.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/plugins/__init__.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/plugins/api.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/plugins/datagouv_refresh.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/plugins/mcp_pilot.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/plugins/pipeline.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/plugins/sources.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/plugins/spatial.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/plugins/worldwide_source.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/rules/__init__.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/rules/engine.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/rules/loader.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/rules/operation_executor.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/rules/predicates.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/rules/trigger_evaluator.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/rules/validation.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/runtime/__init__.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/runtime/config_loader.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/runtime/dialect_scanner.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/runtime/duckdb_engine.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/runtime/engine_inference.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/runtime/headless_runtime.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/runtime/layer_registry.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/runtime/manifest_runner.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/runtime/predicate_dsl.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/runtime/source_watch.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/runtime/sqlite_retry.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/runtime/validation_runner.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/telemetry.py +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse.egg-info/SOURCES.txt +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse.egg-info/dependency_links.txt +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse.egg-info/entry_points.txt +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse.egg-info/requires.txt +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse.egg-info/top_level.txt +0 -0
- {gispulse-2.2.0 → gispulse-2.2.1}/tests/test_read_only_middleware.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gispulse
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.1
|
|
4
4
|
Summary: Modular geospatial engine with business rules, triggers, and dual operating modes (DuckDB/PostGIS)
|
|
5
5
|
Author-email: ImagoData <contact@imagodata.com>
|
|
6
6
|
License-Expression: AGPL-3.0-or-later
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "gispulse"
|
|
7
|
-
version = "2.2.
|
|
7
|
+
version = "2.2.1"
|
|
8
8
|
description = "Modular geospatial engine with business rules, triggers, and dual operating modes (DuckDB/PostGIS)"
|
|
9
9
|
license = "AGPL-3.0-or-later"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -19,8 +19,14 @@ from the ``GISPULSE_DATAMARTS`` environment variable (a JSON object), e.g.::
|
|
|
19
19
|
|
|
20
20
|
GISPULSE_DATAMARTS='{
|
|
21
21
|
"referentiel": {"location": "s3://bucket/marts/referentiel"},
|
|
22
|
-
"local": {"location": "/data/marts/local", "table_pattern": "{table}.parquet"}
|
|
22
|
+
"local": {"location": "/data/marts/local", "table_pattern": "{table}.parquet"},
|
|
23
|
+
"warehouse": {"location": "/data/marts/warehouse.duckdb", "kind": "duckdb"}
|
|
23
24
|
}'
|
|
25
|
+
|
|
26
|
+
A ``kind: "duckdb"`` mart points :attr:`Datamart.location` at a single
|
|
27
|
+
DuckDB database file; ``datamart://warehouse/<table>`` then reads the table
|
|
28
|
+
of that name from inside the file (attached read-only, bbox push-down still
|
|
29
|
+
applies via an ``ST_Intersects`` predicate on a ``geom`` column).
|
|
24
30
|
"""
|
|
25
31
|
|
|
26
32
|
from __future__ import annotations
|
|
@@ -42,17 +48,33 @@ DATAMARTS_ENV = "GISPULSE_DATAMARTS"
|
|
|
42
48
|
DATAMART_SCHEME = "datamart"
|
|
43
49
|
|
|
44
50
|
|
|
51
|
+
#: Supported datamart backends.
|
|
52
|
+
DATAMART_KINDS = ("parquet", "duckdb")
|
|
53
|
+
|
|
54
|
+
|
|
45
55
|
@dataclass(frozen=True)
|
|
46
56
|
class Datamart:
|
|
47
57
|
"""Declaration of one curated table store.
|
|
48
58
|
|
|
59
|
+
Two backends are supported, selected by :attr:`kind`:
|
|
60
|
+
|
|
61
|
+
* ``"parquet"`` (default) — *location* is a directory / ``s3://`` /
|
|
62
|
+
``https://`` prefix holding **one file per table**; the file is
|
|
63
|
+
addressed by joining *location* with *table_pattern* and read as an
|
|
64
|
+
ordinary source (DuckDB-scannable, bbox push-down applies).
|
|
65
|
+
* ``"duckdb"`` — *location* is the path to a **single DuckDB database
|
|
66
|
+
file** (``.duckdb``); a table is a relation *inside* that file, read by
|
|
67
|
+
attaching the database read-only and selecting the table.
|
|
68
|
+
|
|
49
69
|
Args:
|
|
50
70
|
name: Logical mart name used in ``datamart://<name>/…``.
|
|
51
|
-
location: Base location
|
|
52
|
-
|
|
53
|
-
``
|
|
54
|
-
table_pattern: Filename pattern for a table, ``{table}``
|
|
55
|
-
table name. Defaults to ``"{table}.parquet"``.
|
|
71
|
+
location: Base location: a directory / ``s3://`` / ``https://``
|
|
72
|
+
prefix (``parquet``), or the path to a ``.duckdb``
|
|
73
|
+
file (``duckdb``).
|
|
74
|
+
table_pattern: Filename pattern for a ``parquet`` table, ``{table}``
|
|
75
|
+
being the table name. Defaults to ``"{table}.parquet"``.
|
|
76
|
+
Ignored for ``duckdb`` marts.
|
|
77
|
+
kind: Backend, ``"parquet"`` (default) or ``"duckdb"``.
|
|
56
78
|
crs: Optional CRS to force on tables that declare none.
|
|
57
79
|
metadata: Free-form descriptive metadata.
|
|
58
80
|
"""
|
|
@@ -60,13 +82,31 @@ class Datamart:
|
|
|
60
82
|
name: str
|
|
61
83
|
location: str
|
|
62
84
|
table_pattern: str = "{table}.parquet"
|
|
85
|
+
kind: str = "parquet"
|
|
63
86
|
crs: str | None = None
|
|
64
87
|
metadata: dict[str, Any] = field(default_factory=dict)
|
|
65
88
|
|
|
89
|
+
def __post_init__(self) -> None:
|
|
90
|
+
if self.kind not in DATAMART_KINDS:
|
|
91
|
+
raise ValueError(
|
|
92
|
+
f"datamart '{self.name}': unknown kind {self.kind!r} "
|
|
93
|
+
f"(expected one of {DATAMART_KINDS})"
|
|
94
|
+
)
|
|
95
|
+
|
|
66
96
|
def uri_for(self, table: str) -> str:
|
|
67
|
-
"""Return the concrete source URI for *table*
|
|
97
|
+
"""Return the concrete source URI for *table* (``parquet`` marts).
|
|
98
|
+
|
|
99
|
+
Raises:
|
|
100
|
+
ValueError: for a ``duckdb`` mart — a table there is not a file
|
|
101
|
+
URI; resolve it via the loader's DuckDB-table path instead.
|
|
102
|
+
"""
|
|
68
103
|
if not table:
|
|
69
104
|
raise ValueError(f"datamart '{self.name}': empty table name")
|
|
105
|
+
if self.kind != "parquet":
|
|
106
|
+
raise ValueError(
|
|
107
|
+
f"datamart '{self.name}': uri_for is only valid for parquet "
|
|
108
|
+
f"marts, not kind={self.kind!r}"
|
|
109
|
+
)
|
|
70
110
|
filename = self.table_pattern.format(table=table)
|
|
71
111
|
base = self.location.rstrip("/")
|
|
72
112
|
return f"{base}/{filename}"
|
|
@@ -141,13 +181,19 @@ class DatamartRegistry:
|
|
|
141
181
|
if not isinstance(spec, dict) or "location" not in spec:
|
|
142
182
|
log.warning("datamart_env_bad_decl", name=name)
|
|
143
183
|
continue
|
|
144
|
-
|
|
145
|
-
name=
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
184
|
+
try:
|
|
185
|
+
self._marts[name] = Datamart(
|
|
186
|
+
name=name,
|
|
187
|
+
location=str(spec["location"]),
|
|
188
|
+
table_pattern=str(
|
|
189
|
+
spec.get("table_pattern", "{table}.parquet")
|
|
190
|
+
),
|
|
191
|
+
kind=str(spec.get("kind", "parquet")),
|
|
192
|
+
crs=spec.get("crs"),
|
|
193
|
+
metadata=dict(spec.get("metadata") or {}),
|
|
194
|
+
)
|
|
195
|
+
except ValueError as exc:
|
|
196
|
+
log.warning("datamart_env_bad_decl", name=name, error=str(exc))
|
|
151
197
|
|
|
152
198
|
|
|
153
199
|
def parse_datamart_uri(uri: str) -> tuple[str, str]:
|
|
@@ -177,6 +223,7 @@ DATAMARTS = DatamartRegistry()
|
|
|
177
223
|
__all__ = [
|
|
178
224
|
"DATAMARTS",
|
|
179
225
|
"DATAMARTS_ENV",
|
|
226
|
+
"DATAMART_KINDS",
|
|
180
227
|
"DATAMART_SCHEME",
|
|
181
228
|
"Datamart",
|
|
182
229
|
"DatamartRegistry",
|
|
@@ -119,12 +119,30 @@ class LazyDataset:
|
|
|
119
119
|
return _scan_to_gdf(self.scan_sql, crs=self.crs, bbox=bbox)
|
|
120
120
|
|
|
121
121
|
|
|
122
|
+
@dataclass(frozen=True)
|
|
123
|
+
class _DuckDBTableRef:
|
|
124
|
+
"""Reference to a table living inside a single DuckDB database file.
|
|
125
|
+
|
|
126
|
+
Produced by :func:`resolve_source` for a ``duckdb``-kind datamart
|
|
127
|
+
(``datamart://<mart>/<table>`` where the mart's location is a
|
|
128
|
+
``.duckdb`` file). Unlike a Parquet table it is not a file URI the
|
|
129
|
+
loader can hand to ``read_vector`` / a remote-table scan, so the loader
|
|
130
|
+
reads it via :func:`_duckdb_table_to_gdf` (attach read-only, select).
|
|
131
|
+
"""
|
|
132
|
+
|
|
133
|
+
db_path: str
|
|
134
|
+
table: str
|
|
135
|
+
crs: str | None = None
|
|
136
|
+
|
|
137
|
+
|
|
122
138
|
# ---------------------------------------------------------------------------
|
|
123
139
|
# Source resolution
|
|
124
140
|
# ---------------------------------------------------------------------------
|
|
125
141
|
|
|
126
142
|
|
|
127
|
-
def resolve_source(
|
|
143
|
+
def resolve_source(
|
|
144
|
+
source: "str | Path | AccessSpec | dict[str, Any]",
|
|
145
|
+
) -> "Path | AccessSpec | _DuckDBTableRef":
|
|
128
146
|
"""Resolve a user-supplied source into a local path or an access spec.
|
|
129
147
|
|
|
130
148
|
Accepted forms:
|
|
@@ -162,12 +180,19 @@ def resolve_source(source: "str | Path | AccessSpec | dict[str, Any]") -> "Path
|
|
|
162
180
|
scheme = head.lower()
|
|
163
181
|
|
|
164
182
|
# Datamart indirection: resolve datamart://<mart>/<table> to its
|
|
165
|
-
# concrete source
|
|
183
|
+
# concrete source. A "parquet" mart yields a file URI resolved
|
|
184
|
+
# recursively; a "duckdb" mart yields a table reference inside a
|
|
185
|
+
# database file, read via _duckdb_table_to_gdf.
|
|
166
186
|
if scheme == DATAMART_SCHEME:
|
|
167
187
|
from gispulse.persistence.datamart import DATAMARTS, parse_datamart_uri
|
|
168
188
|
|
|
169
|
-
|
|
170
|
-
|
|
189
|
+
mart_name, table = parse_datamart_uri(raw)
|
|
190
|
+
mart = DATAMARTS.get(mart_name)
|
|
191
|
+
if mart.kind == "duckdb":
|
|
192
|
+
if not table:
|
|
193
|
+
raise ValueError(f"datamart '{mart_name}': empty table name")
|
|
194
|
+
return _DuckDBTableRef(db_path=mart.location, table=table, crs=mart.crs)
|
|
195
|
+
return resolve_source(mart.uri_for(table))
|
|
171
196
|
|
|
172
197
|
# GeoNode: geonode://<instance>/<dataset> reads via the instance's
|
|
173
198
|
# GeoServer WFS endpoint (reuses the OGC fetcher).
|
|
@@ -330,6 +355,52 @@ def _scan_to_gdf(
|
|
|
330
355
|
return gdf
|
|
331
356
|
|
|
332
357
|
|
|
358
|
+
def _duckdb_table_to_gdf(
|
|
359
|
+
db_path: str, table: str, *, crs: str | None, bbox: BBox | None
|
|
360
|
+
) -> gpd.GeoDataFrame:
|
|
361
|
+
"""Read a table from a DuckDB database file into a GeoDataFrame.
|
|
362
|
+
|
|
363
|
+
Attaches *db_path* **read-only** (so concurrent readers don't contend on
|
|
364
|
+
a write lock) onto an ephemeral in-memory session, then selects *table*.
|
|
365
|
+
When *bbox* is given an ``ST_Intersects`` envelope predicate is pushed
|
|
366
|
+
into the query against a ``geom`` column (matching the convention of
|
|
367
|
+
:func:`_scan_to_gdf`).
|
|
368
|
+
"""
|
|
369
|
+
from gispulse.persistence.duckdb_engine import DuckDBSession
|
|
370
|
+
|
|
371
|
+
# ATTACH cannot be wrapped in the SELECT sub-query DuckDBSession.sql
|
|
372
|
+
# builds, so run it separately on the connection, then SELECT the table
|
|
373
|
+
# — that SELECT *is* wrappable, so geometry/CRS decoding still works.
|
|
374
|
+
db_lit = db_path.replace("'", "''")
|
|
375
|
+
rel = f"_mart.{_quote_ident(table)}"
|
|
376
|
+
query = f"SELECT * FROM {rel}"
|
|
377
|
+
if bbox is not None:
|
|
378
|
+
minx, miny, maxx, maxy = bbox
|
|
379
|
+
query = (
|
|
380
|
+
f"SELECT * FROM {rel} "
|
|
381
|
+
f"WHERE ST_Intersects(geom, ST_MakeEnvelope("
|
|
382
|
+
f"{minx}, {miny}, {maxx}, {maxy}))"
|
|
383
|
+
)
|
|
384
|
+
|
|
385
|
+
session = DuckDBSession()
|
|
386
|
+
session.open()
|
|
387
|
+
try:
|
|
388
|
+
session.conn.execute(f"ATTACH '{db_lit}' AS _mart (READ_ONLY)")
|
|
389
|
+
gdf = session.sql(query)
|
|
390
|
+
finally:
|
|
391
|
+
session.close()
|
|
392
|
+
|
|
393
|
+
if crs and gdf.crs is None:
|
|
394
|
+
gdf = gdf.set_crs(crs)
|
|
395
|
+
return gdf
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
def _quote_ident(identifier: str) -> str:
|
|
399
|
+
"""Quote a SQL identifier (double-quote, doubling embedded quotes)."""
|
|
400
|
+
escaped = identifier.replace('"', '""')
|
|
401
|
+
return f'"{escaped}"'
|
|
402
|
+
|
|
403
|
+
|
|
333
404
|
# ---------------------------------------------------------------------------
|
|
334
405
|
# Public entry point
|
|
335
406
|
# ---------------------------------------------------------------------------
|
|
@@ -380,6 +451,15 @@ def load(
|
|
|
380
451
|
|
|
381
452
|
resolved = resolve_source(source)
|
|
382
453
|
|
|
454
|
+
# --- DuckDB-file datamart table ---------------------------------------
|
|
455
|
+
if isinstance(resolved, _DuckDBTableRef):
|
|
456
|
+
if lazy:
|
|
457
|
+
log.debug("loader_lazy_ignored_for_duckdb_table", table=resolved.table)
|
|
458
|
+
gdf = _duckdb_table_to_gdf(
|
|
459
|
+
resolved.db_path, resolved.table, crs=crs or resolved.crs, bbox=bbox
|
|
460
|
+
)
|
|
461
|
+
return gdf
|
|
462
|
+
|
|
383
463
|
# --- Local file path ---------------------------------------------------
|
|
384
464
|
if isinstance(resolved, Path):
|
|
385
465
|
if lazy:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gispulse
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.1
|
|
4
4
|
Summary: Modular geospatial engine with business rules, triggers, and dual operating modes (DuckDB/PostGIS)
|
|
5
5
|
Author-email: ImagoData <contact@imagodata.com>
|
|
6
6
|
License-Expression: AGPL-3.0-or-later
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/http/middleware/metrics_middleware.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/http/routers/portal_datasets_router.py
RENAMED
|
File without changes
|
{gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/http/routers/portal_features_router.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{gispulse-2.2.0 → gispulse-2.2.1}/src/gispulse/adapters/http/routers/portal_upload_router.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|