Skip to content

Commit 2997ef3

Browse files
authored
Normalize CRS handling (#2116)
* Normalize CRS handling * Normalize storage_crs * Update crs.py * Cleanup * Use CrsTransformSpec for all CRS conversion * Create `get_transform_from_spec` * Add additional tests * Reduce diff * Revert transform_bbox changes * Update documentation * Update CRS docs * Update plugins.rst * Fix comment spacing in configuration docs * Incorporate PR review feedback * Small change to verbiage
1 parent f64ddb7 commit 2997ef3

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+1136
-1140
lines changed

docs/source/configuration.rst

Lines changed: 43 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -48,37 +48,43 @@ For more information related to API design rules (the ``api_rules`` property in
4848
encoding: utf-8 # default server encoding
4949
language: en-US # default server language
5050
locale_dir: /path/to/translations
51-
gzip: false # default server config to gzip/compress responses to requests with gzip in the Accept-Encoding header
51+
ogc_schemas_location: /opt/schemas.opengis.net # optional local copy of https://schemas.opengis.net
52+
gzip: false # default server config to gzip/compress responses to requests with gzip in the Accept-Encoding header
5253
cors: true # boolean on whether server should support CORS
5354
pretty_print: true # whether JSON responses should be pretty-printed
55+
admin: false # whether to enable the Admin API
5456
55-
limits: # server limits on number of items to return. This property can also be defined at the resource level to override global server settings
57+
# server limits on number of items to return.
58+
# overridable when redefined in resource level configuration
59+
limits:
5660
default_items: 50
5761
max_items: 1000
5862
max_distance_x: 25
5963
max_distance_y: 25
6064
max_distance_units: m
6165
on_exceed: throttle # throttle or error (default=throttle)
6266
63-
admin: false # whether to enable the Admin API
64-
65-
# optional configuration to specify a different set of templates for HTML pages. Recommend using absolute paths. Omit this to use the default provided templates
66-
# This property can also be defined at the resource level to override global server settings for specific datasets
67-
templates: # optional configuration to specify a different set of templates for HTML pages. Recommend using absolute paths. Omit this to use the default provided templates
67+
# configuration to specify directory tree for HTML page templates
68+
# omit this to use the default pygeoapi templates
69+
# overridable when redefined in resource level configuration
70+
templates:
71+
# recommended to use absolute paths
6872
path: /path/to/jinja2/templates/folder # path to templates folder containing the Jinja2 template HTML files
6973
static: /path/to/static/folder # path to static folder containing css, js, images and other static files referenced by the template
7074
71-
map: # leaflet map setup for HTML pages
75+
# leaflet map setup for HTML template rendering
76+
map:
7277
url: https://tile.openstreetmap.org/{z}/{x}/{y}.png
7378
attribution: '&copy; <a href="https://openstreetmap.org/copyright">OpenStreetMap contributors</a>'
74-
ogc_schemas_location: /opt/schemas.opengis.net # local copy of https://schemas.opengis.net
7579
76-
manager: # optional OGC API - Processes asynchronous job management
80+
# optional OGC API - Processes asynchronous job management configuration
81+
manager:
7782
name: TinyDB # plugin name (see pygeoapi.plugin for supported process_manager's)
7883
connection: /tmp/pygeoapi-process-manager.db # connection info to store jobs (e.g. filepath)
7984
output_dir: /tmp/ # temporary file area for storing job results (files)
8085
81-
api_rules: # optional API design rules to which pygeoapi should adhere
86+
# optional API design rules to which pygeoapi should adhere
87+
api_rules:
8288
api_version: 1.2.3 # omit to use pygeoapi's software version
8389
strict_slashes: true # trailing slashes will not be allowed and result in a 404
8490
url_prefix: 'v{api_major}' # adds a /v1 prefix to all URL paths
@@ -95,8 +101,8 @@ The ``logging`` section provides directives for logging messages which are usefu
95101
logging:
96102
level: ERROR # the logging level (see https://docs.python.org/3/library/logging.html#logging-levels)
97103
logfile: /path/to/pygeoapi.log # the full file path to the logfile
98-
logformat: # example for milliseconds:'[%(asctime)s.%(msecs)03d] {%(pathname)s:%(lineno)d} %(levelname)s - %(message)s'
99-
dateformat: # example for milliseconds:'%Y-%m-%dT%H:%M:%S'
104+
logformat: # example for milliseconds:'[%(asctime)s.%(msecs)03d] {%(pathname)s:%(lineno)d} %(levelname)s - %(message)s'
105+
dateformat: # example for milliseconds:'%Y-%m-%dT%H:%M:%S'
100106
101107
.. note::
102108
If ``level`` is defined and ``logfile`` is undefined, logging messages are output to the server's ``stdout``.
@@ -112,11 +118,12 @@ The ``rotation`` supports rotation of disk log files. The ``logfile`` file is op
112118
logging:
113119
logfile: /path/to/pygeoapi.log # the full file path to the logfile
114120
rotation:
115-
mode: # [time|size]
116-
when: # [s|m|h|d|w0-w6|midnight]
121+
mode: # [time|size]
122+
when: # [s|m|h|d|w0-w6|midnight]
117123
interval:
118124
max_bytes:
119125
backup_count:
126+
120127
.. note::
121128
Rotation block is not mandatory and defined only when needed. The ``mode`` can be defined by size or time.
122129
For RotatingFileHandler_ set mode size and parameters max_bytes and backup_count.
@@ -198,13 +205,13 @@ default.
198205
keywords: # list of related keywords
199206
- observations
200207
- monitoring
201-
linked-data: # linked data configuration (see Linked Data section)
208+
linked-data: # linked data configuration (see Linked Data section)
202209
context:
203210
- datetime: https://schema.org/DateTime
204211
- vocab: https://example.com/vocab#
205212
stn_id: "vocab:stn_id"
206213
value: "vocab:value"
207-
links: # list of 1..n related links
214+
links: # list of 1..n related links
208215
- type: text/csv # MIME type
209216
rel: canonical # link relations per https://www.iana.org/assignments/link-relations/link-relations.xhtml
210217
title: data # title
@@ -219,38 +226,37 @@ default.
219226
end: 2007-10-30T08:57:29Z # end datetime in RFC3339
220227
trs: http://www.opengis.net/def/uom/ISO-8601/0/Gregorian # TRS
221228
providers: # list of 1..n required connections information
222-
# provider name
223-
# see pygeoapi.plugin for supported providers
224-
# for custom built plugins, use the import path (e.g. mypackage.provider.MyProvider)
225-
# see Plugins section for more information
226-
- type: feature # underlying data geospatial type: (allowed values are: feature, coverage, record, tile, edr)
227-
default: true # optional: if not specified, the first provider definition is considered the default
228-
name: CSV
229+
- type: feature # underlying data geospatial type. Allowed values are: feature, coverage, record, tile, edr
230+
name: CSV # required: plugin name or import path. See Plugins section for more information.
229231
data: tests/data/obs.csv # required: the data filesystem path or URL, depending on plugin setup
230232
id_field: id # required for vector data, the field corresponding to the ID
231-
uri_field: uri # optional field corresponding to the Uniform Resource Identifier (see Linked Data section)
232-
time_field: datetimestamp # optional field corresponding to the temporal property of the dataset
233-
title_field: foo # optional field of which property to display as title/label on HTML pages
234-
properties: # optional: only return the following properties, in order
233+
234+
# optional fields
235+
uri_field: uri # field corresponding to the Uniform Resource Identifier (see Linked Data section)
236+
time_field: datetimestamp # field corresponding to the temporal property of the dataset
237+
title_field: foo # field of which property to display as title/label on HTML pages
238+
default: true # if not specified, the first provider definition is considered the default
239+
properties: # if specified, return only the following properties, in order
235240
- stn_id
236241
- value
242+
format: # default format
243+
name: GeoJSON # required: format name
244+
mimetype: application/json # required: format mimetype
245+
options: # optional options to pass to provider (i.e. GDAL creation)
246+
option_name: option_value
247+
include_extra_query_parameters: false # include extra query parameters that are not part of the collection properties (default: false)
237248
# editable transactions: DO NOT ACTIVATE unless you have setup access control beyond pygeoapi
238249
editable: true # optional: if backend is writable, default is false
239250
# coordinate reference systems (CRS) section is optional
240251
# default CRSs are http://www.opengis.net/def/crs/OGC/1.3/CRS84 (coordinates without height)
241252
# and http://www.opengis.net/def/crs/OGC/1.3/CRS84h (coordinates with ellipsoidal height)
242-
crs: # supported coordinate reference systems (CRS) for 'crs' and 'bbox-crs' query parameters
253+
crs: # supported coordinate reference systems (CRS) for 'crs' and 'bbox-crs' query parameters
243254
- http://www.opengis.net/def/crs/EPSG/0/28992
244255
- http://www.opengis.net/def/crs/OGC/1.3/CRS84
245256
- http://www.opengis.net/def/crs/EPSG/0/4326
246-
storage_crs: http://www.opengis.net/def/crs/OGC/1.3/CRS84 # optional CRS in which data is stored, default: as 'crs' field
247-
storage_crs_coordinate_epoch: : 2017.23 # optional, if storage_crs is a dynamic coordinate reference system
248-
format: # optional default format
249-
name: GeoJSON # required: format name
250-
mimetype: application/json # required: format mimetype
251-
options: # optional options to pass to provider (i.e. GDAL creation)
252-
option_name: option_value
253-
include_extra_query_parameters: false # include extra query parameters that are not part of the collection properties (default: false)
257+
storage_crs: http://www.opengis.net/def/crs/OGC/1.3/CRS84 # optional CRS in which data is stored, default: as 'crs' field
258+
storage_crs_coordinate_epoch: 2017.23 # optional, if storage_crs is a dynamic coordinate reference system
259+
always_xy: false # optional should CRS respect axis ordering
254260
255261
hello-world: # name of process
256262
type: process # REQUIRED (collection, process, or stac-collection)

docs/source/crs.rst

Lines changed: 89 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,42 +3,98 @@
33
CRS support
44
===========
55

6-
pygeoapi supports the complete specification: `OGC API - Features - Part 2: Coordinate Reference Systems by Reference corrigendum`_.
7-
The specified CRS-related capabilities are available for all Feature data Providers.
6+
pygeoapi supports multiple Coordinate Reference Systems (CRS).
7+
This enables the import and export of any data according to dedicated projections.
8+
A "projection" is specified with a CRS identifier.
9+
In particular CRS support allows for the following:
10+
11+
* to specify the CRS in which the data is stored, in pygeoapi the `storage_crs` config option
12+
* to specify the list of valid CRS representations, in pygeoapi the `crs` config option
13+
* to publish these in the collection metadata
14+
* the `bbox-crs=` query parameter to indicate that the `bbox=` parameter is encoded in that CRS
15+
* the `crs=` query parameter for a collection or collection item
16+
* the HTTP response header `Content-Crs` denotes the CRS of the Feature(s) in the data returned
17+
18+
Although GeoJSON mandates WGS84 in longitude, latitude order, the client and server may still agree on other CRSs.
19+
20+
21+
Background
22+
----------
23+
24+
pygeoapi implements the `OGC API - Features - Part 2: Coordinate Reference Systems by Reference`_ specification.
25+
26+
Under the hood, pygeoapi uses the `pyproj`_ Python package.
27+
28+
.. note::
29+
30+
For more information on implementing CRS on custom plugins, see `Implementation`_.
31+
32+
CRS support exists for the following OGC APIs:
33+
34+
.. csv-table::
35+
:header: OGC API, bbox-crs, filter-crs, crs
36+
:align: left
37+
38+
:ref:`OGC API - Features<ogcapi-features>`,✅,✅,✅
39+
:ref:`OGC API - Maps<ogcapi-maps>`,✅,❌,❌
40+
:ref:`OGC API - Coverages<ogcapi-coverages>`,✅,❌,❌
841

942
Configuration
1043
-------------
1144

12-
For details visit the :ref:`configuration` section for Feature Providers. At this moment only the 'URI' CRS notation format is supported.
45+
The CRS of a collection is defined in the provider block of your resource.
46+
The configuration controls how the `crs` related query parameters behave.
47+
48+
49+
* ``crs`` - list of CRSs supported
50+
* ``storage_crs`` - CRS in which the data is stored (must be in `crs` list)
51+
* ``storage_crs_coordinate_epoch`` - epoch of `storage_crs` for a dynamic coordinate reference system
52+
* ``always_xy`` - CRS should ignore authority on axis order, disobeying `ISO-19111`_ (default: false)
53+
54+
.. note::
55+
bbox-crs and filter-crs are used to convert the request geometry to the configured ``storage_crs``.
56+
An error will be returned for any interaction with CRS not included in the configured ``crs`` list.
1357

58+
The per-Provider configuration fields are all optional,
59+
with the following as default configuration:
1460

15-
* `crs` - list of CRSs supported
16-
* `storage_crs` - CRS in which the data is stored (must be in `crs` list)
17-
* `storage_crs_coordinate_epoch` - epoch of `storage_crs` for a dynamic coordinate reference system
61+
.. code-block:: yaml
1862
63+
crs:
64+
- http://www.opengis.net/def/crs/OGC/1.3/CRS84
65+
- http://www.opengis.net/def/crs/OGC/1.3/CRS84h
66+
storage_crs: http://www.opengis.net/def/crs/OGC/1.3/CRS84
1967
20-
These per-Provider configuration fields are all optional. Default for CRS-values is `http://www.opengis.net/def/crs/OGC/1.3/CRS84`, so "WGS84" with lon/lat axis ordering.
21-
If the storage CRS of the spatial feature collection is a dynamic coordinate reference system,
22-
`storage_crs_coordinate_epoch` configures the coordinate epoch of the coordinates.
68+
.. note::
69+
Configuration is done with URI formats like http://www.opengis.net/def/crs/OGC/1.3/CRS84.
70+
Both `URI` and `URN` CRS notation format are supported.
71+
The `EPSG:` format like EPSG:4326 is outside the scope of the OGC standard.
2372

24-
There is also support for CRSs that support height like `http://www.opengis.net/def/crs/OGC/1.3/CRS84h`. In that case
25-
bbox parameters (see below) may contain 6 coordinates.
2673

2774
Metadata
2875
--------
2976

30-
The conformance class `http://www.opengis.net/spec/ogcapi-features-2/1.0/conf/crs` is present as a `conformsTo` field
31-
in the root landing page response.
77+
The conformance class http://www.opengis.net/spec/ogcapi-features-2/1.0/conf/crs is
78+
present as a `conformsTo` field in the root landing page response.
3279

3380
The configured CRSs, or their defaults, `crs` and `storageCrs` and optionally `storageCrsCoordinateEpoch` will be present in the "Describe Collection" response.
3481

82+
.. note::
83+
If the storage CRS of the spatial feature collection is a dynamic coordinate reference system,
84+
`storage_crs_coordinate_epoch` configures the coordinate epoch of the coordinates.
85+
86+
.. note::
87+
There is also support for CRSs that support height like `http://www.opengis.net/def/crs/OGC/1.3/CRS84h`. In that case
88+
bbox parameters (see below) may contain 6 coordinates.
89+
3590
Parameters
3691
----------
3792

3893
The `items` query supports the following parameters:
3994

40-
* `crs` - the CRS in which Features coordinates should be returned, also for the 'get single item' request
41-
* `bbox-crs` - the CRS of the `bbox` parameter (only for Providers that support the `bbox` parameter)
95+
* ``crs`` - the CRS in which Features coordinates should be returned, also for the 'get single item' request
96+
* ``bbox-crs`` - the CRS of the `bbox` parameter (for Providers that support the `bbox` parameter)
97+
* ``filter-crs`` - the CRS of the CQL filter expression (for Providers that support `CQL` filters)
4298

4399
If any or both of these parameters are specified, their CRS-value should be from the advertised CRS-list in the Collection metadata (see above).
44100

@@ -52,21 +108,29 @@ Note that the values of these parameters may need to be URL-encoded.
52108
Implementation
53109
--------------
54110

55-
CRS and BBOX CRS support is implemented for all Feature Providers. Some details may help understanding (performance) implications.
111+
CRS and BBOX CRS support is implemented for all Feature Providers.
112+
Some details may help understanding (performance) implications.
56113

57-
BBOX CRS Parameter
114+
bbox-crs Parameter
58115
^^^^^^^^^^^^^^^^^^
59116

60-
The `bbox-crs` parameter is handled at the common level of pygeoapi, thus transparent for Feature Providers.
61-
Obviously the Provider should support `bbox`.
62-
A transformation of the `bbox` parameter is performed
117+
The ``bbox-crs`` parameter is handled at the common level of pygeoapi.
118+
A transformation of the request `bbox` parameter is performed
63119
according to the `storage_crs` configuration. Then the (transformed) `bbox` is passed with the
64120
other query parameters to the Provider instance.
65121

66-
CRS Parameter
122+
filter-crs Parameter
123+
^^^^^^^^^^^^^^^^^^^^
124+
125+
The ``filter-crs`` parameter is handled at the common level of pygeoapi.
126+
A transformation of the request `CQL` filter is performed
127+
according to the `storage_crs` configuration. Then the (transformed) `filter` is passed with the
128+
other query parameters to the Provider instance.
129+
130+
crs Parameter
67131
^^^^^^^^^^^^^
68132

69-
When the value of the `crs` parameter differs from the Provider data Storage CRS, the response Feature coordinates
133+
When the value of the ``crs`` parameter differs from the Provider data Storage CRS, the response Feature coordinates
70134
need to be transformed to that CRS. As some Feature Providers like PostgreSQL or OGR may support native
71135
coordinate transformation, pygeoapi delegates transformation to those Providers, passing the `crs` with the other query parameters.
72136

@@ -245,4 +309,6 @@ Or you may specify both `crs` and `bbox-crs` and thus `bbox` in that CRS `http:/
245309
.
246310
.
247311
248-
.. _`OGC API - Features - Part 2: Coordinate Reference Systems by Reference corrigendum`: https://docs.opengeospatial.org/is/18-058r1/18-058r1.html
312+
.. _`ISO-19111`: https://docs.ogc.org/as/18-005r5/18-005r5.html
313+
.. _`OGC API - Features - Part 2: Coordinate Reference Systems by Reference`: https://docs.ogc.org/is/18-058r1/18-058r1.html
314+
.. _`pyproj`: https://pyproj4.github.io/pyproj

docs/source/plugins.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ In this section we will explain how pygeoapi provides plugin architecture for da
77

88
Plugin development requires knowledge of how to program in Python as well as Python's package/module system.
99

10+
.. seealso::
11+
:ref:`publishing` for configuration of default plugins.
12+
1013
Overview
1114
--------
1215

docs/source/publishing/ogcapi-coverages.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ The `Xarray`_ provider plugin reads and extracts `NetCDF`_ and `Zarr`_ data.
7777
time_field: time
7878
# optionally specify the coordinate reference system of your dataset
7979
# else pygeoapi assumes it is WGS84 (EPSG:4326).
80-
storage_crs: 4326
80+
storage_crs: http://www.opengis.net/def/crs/OGC/1.3/CRS84
8181
format:
8282
name: netcdf
8383
mimetype: application/x-netcdf

0 commit comments

Comments
 (0)