https://github.com/qwc-services/qwc-feature-info-service
QWC FeatureInfo Service
https://github.com/qwc-services/qwc-feature-info-service
Last synced: 4 months ago
JSON representation
QWC FeatureInfo Service
- Host: GitHub
- URL: https://github.com/qwc-services/qwc-feature-info-service
- Owner: qwc-services
- License: mit
- Created: 2020-03-09T15:12:19.000Z (about 5 years ago)
- Default Branch: master
- Last Pushed: 2024-11-14T15:19:29.000Z (5 months ago)
- Last Synced: 2024-11-14T16:27:45.931Z (5 months ago)
- Language: Python
- Size: 133 KB
- Stars: 1
- Watchers: 5
- Forks: 10
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
- jimsghstars - qwc-services/qwc-feature-info-service - QWC FeatureInfo Service (Python)
README
[](https://github.com/qwc-services/qwc-feature-info-service/actions)
[](https://hub.docker.com/r/sourcepole/qwc-feature-info-service)QWC FeatureInfo Service
=======================Query layers at a geographic position using an API based on WMS GetFeatureInfo.
The query is handled for each layer by its layer info provider configured in the config file.
Layer info providers:
* WMS GetFeatureInfo (default): forward info request to the QGIS Server
* DB Query: execute custom query SQL
* Custom info module: custom Python modules returning layer infoThe info results are each rendered into customizable HTML templates and returned as a GetFeatureInfoResponse XML.
Setup
-----The DB query uses a PostgreSQL connection service or connection to a PostGIS database.
This connection's user requires read access to the configured tables.### qwc_demo example
Uses PostgreSQL connection service `qwc_geodb` (GeoDB).
The user `qwc_service` requires read access to the configured tables
of the data layers from the QGIS project `qwc_demo.qgs`.Setup PostgreSQL connection service file `~/.pg_service.conf`:
```
[qwc_geodb]
host=localhost
port=5439
dbname=qwc_demo
user=qwc_service
password=qwc_service
sslmode=disable
```Configuration
-------------The static config and permission files are stored as JSON files in `$CONFIG_PATH` with subdirectories for each tenant,
e.g. `$CONFIG_PATH/default/*.json`. The default tenant name is `default`.### FeatureInfo Service config
* [JSON schema](schemas/qwc-feature-info-service.json)
* File location: `$CONFIG_PATH//featureInfoConfig.json`Note: Use the `qwc-config-generator` to automatically generate the resource configuration. Manually provided configuration is merged into the automatically generated configuration by service and layer name. I.e. the configuration for `countries` in the example below just specifies the info template, attributes configuration will completed by the config generator.
Example:
```json
{
"$schema": "https://raw.githubusercontent.com/qwc-services/qwc-feature-info-service/master/schemas/qwc-feature-info-service.json",
"service": "feature-info",
"config": {
"default_qgis_server_url": "http://localhost:8001/ows/"
},
"resources": {
"wms_services": [
{
"name": "qwc_demo",
"root_layer": {
"name": "qwc_demo",
"layers": [
{
"name": "edit_demo",
"title": "Edit Demo",
"layers": [
{
"name": "edit_points",
"title": "Edit Points",
"attributes": [
{
"name": "id"
},
{
"name": "name"
},
{
"name": "description"
},
{
"name": "num"
},
{
"name": "value"
},
{
"name": "type"
},
{
"name": "amount"
},
{
"name": "validated",
"format": "{\"t\": \"Yes\", \"f\": \"No\"}"
},
{
"name": "datetime"
},
{
"name": "geometry"
},
{
"name": "maptip"
}
]
}
]
},
{
"name": "countries",
"info_template": {
"type": "wms",
"wms_url": "http://localhost:8001/ows/qwc_demo",
"template": ""Demo Template
Pos: {{ x }}, {{ y }}
Name: {{ feature.Name }}
}
}
]
}
}
]
}
}
```Example `info_template` for WMS GetFeatureInfo:
```json
"info_template": {
"type": "wms",
"wms_url": "http://localhost:8001/ows/qwc_demo",
"template": ""Demo Template
Pos: {{ x }}, {{ y }}
Name: {{ feature.Name }}
}
```Example `info_template` for WMS GetFeatureInfo with template path:
```json
"info_template": {
"type": "wms",
"wms_url": "http://localhost:8001/ows/qwc_demo",
"template_path": "/info_templates/template.html"
}
```Example `info_template` for DB query:
```json
"info_template": {
"type": "sql",
"db_url": "postgresql:///?service=qwc_geodb",
"sql": "SELECT ogc_fid as _fid_, name, formal_en, pop_est, subregion, ST_AsText(wkb_geometry) as wkt_geom FROM qwc_geodb.ne_10m_admin_0_countries WHERE ST_Intersects(wkb_geometry, ST_GeomFromText(:geom, :srid)) LIMIT :feature_count;",
"template": ""Demo Template
Pos: {{ x }}, {{ y }}
Name: {{ feature.Name }}
}
```
Note: `x`, `y` and `geom` are passed as parameters to the SQL query. If a `GetFeatureInfo` request is being processed with a `filter_geom` parameter, `geom` will correspond to that parameter. Otherwise `geom` will be `POINT(x y)`.Example `info_template` for Custom info module:
```json
"info_template": {
"type": "module",
"module": "example",
"template": ""Demo Template
Pos: {{ x }}, {{ y }}
Name: {{ feature.Name }}
}
```#### Base64 encoded properties
The following config properties may also be set as Base64 encoded values instead:
* Default HTML info template: `default_info_template_base64`
* Formatting expression for converting attribute values: `format_base64`
* HTML template for info result: `template_base64`
* Query SQL for DB query: `sql_base64`Any plain text properties take precedence over their corresponding Base64 encoded property (e.g. `template_base64` is only used if `template` is not set).
### Permissions
* [JSON schema](https://github.com/qwc-services/qwc-services-core/blob/master/schemas/qwc-services-permissions.json)
* File location: `$CONFIG_PATH//permissions.json`Example:
```json
{
"$schema": "https://raw.githubusercontent.com/qwc-services/qwc-services-core/master/schemas/qwc-services-permissions.json",
"users": [
{
"name": "demo",
"groups": ["demo"],
"roles": []
}
],
"groups": [
{
"name": "demo",
"roles": ["demo"]
}
],
"roles": [
{
"role": "public",
"permissions": {
"wms_services": [
{
"name": "qwc_demo",
"ows_type": "WMS",
"layers": [
{
"name": "qwc_demo"
},
{
"name": "edit_demo"
},
{
"name": "edit_points",
"attributes": [
"id", "name", "description", "num", "value", "type", "amount", "validated",
"datetime", "geometry", "maptip"
]
},
{
"name": "countries",
"attributes": ["name", "formal_en", "pop_est", "subregion", "geometry"],
"info_template": true
}
]
}
]
}
}
]
}
```## HTML template
A HTML template can be provided for a layer in the config file.
The template must only contain the body content (without `head`, `script`, `body`).
The HTML can be styled using inline CSS, otherwise the CSS from the QWC viewer is used.This template can contain attribute value placeholders, in the form
{{ feature.attr }}
which are replaced with the respective values when the template is rendered (using [Jinja2](http://jinja.pocoo.org/)).
The following values are available in the template:* `x`, `y`, `crs`: Coordinates and CRS of info query
* `feature`: Feature with attributes from info result as properties, e.g. `feature.name`
* `fid`: Feature ID (if present)
* `bbox`: Feature bounding box as `[, , , ]` (if present)
* `geometry`: Feature geometry as WKT (if present)
* `layer`: Layer nameTo automatically detect hyperlinks in values and replace them as HTML links the following helper can be used in the template:
render_value(value)
Example:
```xml
Result at coordinates {{ x }}, {{ y }}
Name:
{{ feature.name }}
Description:
{{ feature.description }}
```### Default info template
Layers with no assigned info templates use WMS GetFeatureInfo with a default info template.
The default template can also optionally be configured as `default_info_template` in the config file.The InfoFeature `feature` available in the template also provides a list of its attributes:
feature._attributes = [
'name': ,
'value': ,
'alias': ,
'type': ,
'json_aliases':
]If an attribute value starts with `{` or `[` the service tries to parse it as JSON before rendering it in the template.
Default info template:
```xml
{% for attr in feature._attributes -%}
{% if attr['type'] == 'list' -%}
{# attribute is a list #}
{{ attr['alias'] }}
{%- for item in attr['value'] %}
{%- if item is mapping -%}
{# item is a dict #}
{% for key in item -%}
{% if not attr['json_aliases'] %}
{% set alias = key %}
{% elif key in attr['json_aliases'] %}
{% set alias = attr['json_aliases'][key] %}
{% endif %}
{% if alias %}
{{ alias }}
{{ render_value(item[key]) }}
{% endif %}
{%- endfor %}
{%- else -%}
{{ render_value(item) }}
{%- endif %}
{%- endfor %}
{%- elif attr['type'] in ['dict', 'OrderedDict'] -%}
{# attribute is a dict #}
{{ attr['alias'] }}
{% for key in attr['value'] -%}
{{ key }}
{{ render_value(attr['value'][key]) }}
{%- endfor %}
{%- else -%}
{# other attributes #}
{{ attr['alias'] }}
{{ render_value(attr['value']) }}
{%- endif %}
{%- endfor %}
```DB Query
--------In a DB Query the following values are replaced in the SQL:
* `:x`: X coordinate of query
* `:y`: Y coordinate of query
* `:srid`: SRID of query coordinates
* `:resolution`: Resolution in map units per pixel
* `:FI_POINT_TOLERANCE`: Tolerance for picking points, in pixels (default=16)
* `:FI_LINE_TOLERANCE`: Tolerance for picking lines, in pixels (default=8)
* `:FI_POLYGON_TOLERANCE`: Tolerance for picking polygons, in pixels (default=4)
* `:i`: X ordinate of query point on map, in pixels
* `:j`: Y ordinate of query point on map, in pixels
* `:height`: Height of map output, in pixels
* `:width`: Width of map output, in pixels
* `:bbox`: 'Bounding box for map extent as minx,miny,maxx,maxy'
* `:crs`: 'CRS for map extent'
* `:feature_count`: Max feature count
* `:with_geometry`: Whether to return geometries in response (default=1)
* `:with_maptip`: Whether to return maptip in response (default=1)The query may return the feature ID as `_fid_` and the WKT geometry as `wkt_geom`. All other selected columns are used as feature attributes.
Sample queries:
```sql
SELECT ogc_fid as _fid_, name, ...,
ST_AsText(wkb_geometry) as wkt_geom
FROM schema.table
WHERE ST_Intersects(wkb_geometry, ST_GeomFromText('POINT(:x :y)', :srid))
LIMIT :feature_count;
``````sql
SELECT ogc_fid as _fid_, name, ...,
ST_AsText(wkb_geometry) as wkt_geom
FROM schema.table
WHERE ST_Intersects(
wkb_geometry,
ST_Buffer(
ST_GeomFromText('POINT(:x :y)', :srid),
:resolution * :FI_POLYGON_TOLERANCE
)
)
LIMIT :feature_count;
```Custom info modules
-------------------Custom info modules can be placed in `./info_modules/custom//` and must provide the following method:
```python
def layer_info(layer, x, y, crs, params, identity)
```Input parameters:
* `layer` (str): Layer name
* `x` (float): X coordinate of query
* `y` (float): Y coordinate of query
* `crs` (str): CRS of query coordinates
* `params` (obj): FeatureInfo service params{
'i': ,
'j': ,
'height': ,
'width': ,
'bbox': '',
'crs': '',
'feature_count': ,
'with_geometry': ,
'with_maptip': ,
'FI_POINT_TOLERANCE': ,
'FI_LINE_TOLERANCE': ,
'FI_POLYGON_TOLERANCE': ,
'resolution':
}* `identity` (str): User name or Identity dict
Return info result as a dict:
{
'features': [
{
'id': , # optional
'attributes': [
{
'name': '',
'value':
}
],
'bbox': [, , , ], # optional
'geometry': '' # optional
}
]
}See [`./info_modules/custom/example/`](info_modules/custom/example/) for a sample implementation of a custom layer info module.
The custom info module can then be referenced in the `info_template` by its name (= directory name) in the service config.
Usage
-----Set the `CONFIG_PATH` environment variable to the path containing the service config and permission files when starting this service (default: `config`).
Base URL:
http://localhost:5015/
Service API:
http://localhost:5015/api/
Sample request:
curl 'http://localhost:5015/qwc_demo?layers=countries,edit_points&i=51&j=51&height=101&width=101&bbox=671639%2C5694018%2C1244689%2C6267068&crs=EPSG%3A3857'
Docker usage
------------To run this docker image you will need a PostGIS database and a running QGIS Server.
The following steps explain how to download the those services and how to run the `qwc-feature-info-service` with `docker-compose`.
**Step 1: Clone qwc-docker**
git clone https://github.com/qwc-services/qwc-docker
cd qwc-docker**Step 2: Create docker-compose.yml file**
cp docker-compose-example.yml docker-compose.yml
**Step 3: Start docker containers**
docker-compose up qwc-feature-info-service
For more information please visit: https://github.com/qwc-services/qwc-docker
Development
-----------Create a virtual environment:
virtualenv --python=/usr/bin/python3 --system-site-packages .venv
Without system packages:
python3 -m venv .venv
Activate virtual environment:
source .venv/bin/activate
Install requirements:
pip install -r requirements.txt
Start local service:
CONFIG_PATH=/PATH/TO/CONFIGS/ python src/server.py