Skip to content

Commit 6bfcde1

Browse files
committed
OGC API Processes spullenboel
1 parent ef9c6c8 commit 6bfcde1

14 files changed

Lines changed: 2733 additions & 0 deletions

File tree

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.geojson
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# docker file voor ogc wps (pygeopapi)
2+
#
3+
4+
# FROM geopython/pygeoapi:latest
5+
FROM geopython/pygeoapi:0.19.0
6+
7+
WORKDIR /pygeoapi
8+
9+
LABEL author='Niels Hoffmann'
10+
LABEL name='ogc api processes workshop'
11+
12+
ADD requirements.txt /pygeoapi/
13+
RUN python3 -m pip install --no-cache-dir -r requirements.txt
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# PygeoAPI Demo server
2+
3+
Starten met `docker compose up`
4+
5+
- pygeoapi.config.yml is de configuratie file die, samen met de plugins/process en /data folder gemapt zijn naar de docker container.
6+
7+
als de container gestart is, is de server te benaderen op http://localhost:5000/
8+
9+
---
10+
pygeoapi publiceert een docker image. In deze folder is een eigen dockerfile gedefinieerd op basis van dit image omdat een van de processes aanvullende python packages vereist.
11+
12+
#### client.ipynb
13+
14+
Dit is een Jupyter Notebook waarmee de Pygeoapi OGC API Processes aangeroepen worden.
15+
16+
Benodigde packages:
17+
- jupyter notebook
18+
- requests
19+
- json
20+
- geopandas
21+
- folium (voor de kaartjes, zonder kan evt ook)
22+
23+
---
24+
disclaimer: configuratie van Pygeoapi en het client notebook zijn ten behoeve van demonstratie en leerdoeleinden. Zeker niet geschikt om een productie installatie mee te doen!

05 OGC API Processes/handson/pygeoapi/client.ipynb

Lines changed: 1304 additions & 0 deletions
Large diffs are not rendered by default.

05 OGC API Processes/handson/pygeoapi/data/bomen_sloten.geojson

Lines changed: 765 additions & 0 deletions
Large diffs are not rendered by default.

05 OGC API Processes/handson/pygeoapi/data/knmi_meetstations.geojson

Lines changed: 83 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
2+
3+
services:
4+
pygeoapi:
5+
build: .
6+
# image: pygeo-opentel:latest
7+
8+
container_name: pygeoapi
9+
10+
ports:
11+
- 5000:80
12+
13+
volumes:
14+
- ./pygeoapi.config.yml:/pygeoapi/local.config.yml
15+
- ./data:/data
16+
- ./plugins/process/squared.py:/pygeoapi/pygeoapi/process/squared.py
17+
- ./plugins/process/localoutlier.py:/pygeoapi/pygeoapi/process/localoutlier.py
18+
- ./plugins/process/aanvraag.py:/pygeoapi/pygeoapi/process/aanvraag.py
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
from os import makedirs, path
2+
from logging import getLogger
3+
4+
5+
from pygeoapi.process.base import BaseProcessor, ProcessorExecuteError
6+
7+
import geopandas as gpd
8+
import numpy as np
9+
10+
PROCESS_METADATA = {
11+
'version': '0.1',
12+
'id': 'aanvraag',
13+
'title': 'Aanvraag kapvergunning',
14+
'description': 'Demo om persoonsgegeven en object samen te loggen',
15+
'keywords': ['aanvraag', 'kapvergunning', 'otel demo'],
16+
"jobControlOptions": [
17+
"sync-execute"
18+
],
19+
20+
'inputs':{
21+
'dataset':{
22+
'title': 'Dataset',
23+
'description': 'geojson dataset of points, in one CRS. ',
24+
"schema": { "type": "string", "format": "url" },
25+
'minOccurs': 1,
26+
'maxOccurs': 1,
27+
'keywords': ['geojson ogc api features', 'point data']
28+
},
29+
'object_id':{
30+
'title': 'The object id for which a permit is requested',
31+
'description': 'The object id for which a permit is requested.',
32+
'minOccurs': 0,
33+
'maxOccurs': 1,
34+
'schema': {
35+
'oneOf': ['integer'],
36+
# 'defaultValue': 20,
37+
}
38+
},
39+
'subject_id':{
40+
'title': 'The subject id',
41+
'description': 'ID of the person requesting the permit',
42+
'minOccurs': 0,
43+
'maxOccurs': 1,
44+
'schema': {
45+
'oneOf': ['string'],
46+
# 'defaultValue': 30,
47+
},
48+
},
49+
},
50+
'outputs': {
51+
'output_dataset':{
52+
'title': 'Output Dataset',
53+
'description': 'output',
54+
'schema': {
55+
'type': 'object',
56+
'contentMediaType': 'application/json'
57+
}
58+
},
59+
},
60+
'example': {"inputs":
61+
{
62+
"dataset": "http://localhost/collections/bomen/items?f=json&limit=1000",
63+
"object_id": 2069296,
64+
"subject_id": "Meneer van Eik"
65+
}
66+
}
67+
}
68+
69+
class AanvraagProcessor(BaseProcessor):
70+
71+
72+
def __init__(self, processor_def):
73+
"""
74+
Initialize object
75+
76+
:param processor_def: provider definition
77+
78+
:returns: pygeoapi.process.aanvraag.AanvraagProcessor
79+
"""
80+
81+
super().__init__(processor_def, PROCESS_METADATA)
82+
83+
def execute(self, data):
84+
85+
obj_id = int(data.get('object_id'))
86+
subj_name = data.get('subject_id', '')
87+
dataset = data.get("dataset")
88+
89+
if dataset is None:
90+
raise ProcessorExecuteError('Cannot process without input dataset')
91+
92+
gdf = gpd.read_file(dataset)
93+
94+
95+
gdf['kap_aanvraag'] = np.where(gdf['id'].astype(int) == obj_id, subj_name, '0')
96+
gdf_out = gdf[gdf['kap_aanvraag'] != '0']
97+
98+
gdf_out = gdf_out[['id','leaf_type','geometry','species:nl','kap_aanvraag']]
99+
mimetype = 'application/geo+json'
100+
outputs = {
101+
'id': 'output_dataset',
102+
'value': gdf_out.to_json()
103+
}
104+
105+
return mimetype, outputs
106+
107+
def __repr__(self):
108+
return '<AanvraagProcessor> {}'.format(self.name)
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
from os import makedirs, path
2+
from logging import getLogger
3+
4+
5+
from pygeoapi.process.base import BaseProcessor, ProcessorExecuteError
6+
7+
import geopandas as gpd
8+
from numpy import vstack
9+
from sklearn.neighbors import LocalOutlierFactor
10+
11+
LOGGER = getLogger(__name__)
12+
13+
PROCESS_METADATA = {
14+
'version': '0.1',
15+
'id': 'localoutlier',
16+
'title': 'Local outlier factor (LOF)',
17+
'description': 'The local outlier factor (LOF) algorithm computes a score indicating the degree of abnormality of each input (observation), in a set of such observations. It measures the local density deviation of a given data point with respect to its neighbors. It considers as outliers the samples that have a substantially lower density than their neighbors.',
18+
'keywords': ['local outliter factor', 'LOF', 'outlier detection'],
19+
"jobControlOptions": [
20+
"sync-execute"
21+
],
22+
'links': [{
23+
'type': 'text/html',
24+
'rel': 'canonical',
25+
'title': 'information',
26+
'href': 'https://scikit-learn.org/stable/modules/outlier_detection.html#local-outlier-factor',
27+
'hreflang': 'en-US'
28+
}],
29+
'inputs':{
30+
'dataset':{
31+
'title': 'Dataset',
32+
'description': 'geojson dataset of points, in one CRS, for which LOF scores should be computed. ',
33+
"schema": { "type": "string", "format": "url" },
34+
'minOccurs': 1,
35+
'maxOccurs': 1,
36+
'keywords': ['geojson ogc api features', 'point data']
37+
},
38+
'n_neighbors':{
39+
'title': 'Number of neighbors',
40+
'description': 'Number of neighbors to use by default for `kneighbors` queries. If `n_neighbors` is larger than the number of samples provided, all samples will be used.',
41+
'minOccurs': 0,
42+
'maxOccurs': 1,
43+
'schema': {
44+
'oneOf': ['integer'],
45+
'defaultValue': 20,
46+
}
47+
},
48+
'leaf_size':{
49+
'title': 'Leaf size',
50+
'description': 'Leaf size passed to BallTree or KDTree. This can affect the speed of the construction and query, as well as the memory required to store the tree. The optimal value depends on the nature of the problem.',
51+
'minOccurs': 0,
52+
'maxOccurs': 1,
53+
'schema': {
54+
'oneOf': ['integer'],
55+
'defaultValue': 30,
56+
},
57+
}
58+
},
59+
'output_column':{
60+
'title': 'Output column name',
61+
'description': 'Name of the column in which to store output metric. If this column exists, an error will be thrown',
62+
'minOccurs': 0,
63+
'maxOccurs': 1,
64+
'schema': {
65+
'oneOf': ['string'],
66+
'defaultValue': 'abnormality',
67+
},
68+
},
69+
'outputs': {
70+
'output_dataset':{
71+
'title': 'Output Dataset',
72+
'description': 'output',
73+
'schema': {
74+
'type': 'object',
75+
'contentMediaType': 'application/json'
76+
}
77+
},
78+
},
79+
'example': {}
80+
}
81+
82+
# Parameters that are NOT passed directly to sklearn.neighbors.LocalOutlierFactor
83+
LOF_OMIT = ['training_dataset', 'dataset', 'output_column']
84+
85+
class LOFProcessor(BaseProcessor):
86+
"""Local outlier factor (LOF) processor"""
87+
88+
def __init__(self, processor_def):
89+
"""
90+
Initialize object
91+
92+
:param processor_def: provider definition
93+
94+
:returns: pygeoapi.process.localoutlier.LOFProcessor
95+
"""
96+
97+
super().__init__(processor_def, PROCESS_METADATA)
98+
99+
def execute(self, data):
100+
101+
data['p'] = int(data.get('p', 2))
102+
data['leaf_size'] = int(data.get('leaf_size', 30))
103+
data['n_neighbors'] = int(data.get('n_neighbors', 20))
104+
colName = data.get('output_column', 'abnormality')
105+
dataset = data.get("dataset")
106+
107+
if dataset is None:
108+
raise ProcessorExecuteError('Cannot process without input dataset')
109+
110+
#setup the sklearn classifier
111+
clf = LocalOutlierFactor(
112+
novelty=False,
113+
**{k:v for k,v in data.items()
114+
if k not in LOF_OMIT}
115+
)
116+
predictMethod = clf.fit_predict
117+
118+
gdf = gpd.read_file(dataset)
119+
X = vstack([gdf.geometry.x, gdf.geometry.y]).T
120+
121+
#perform the actual classification
122+
y_pred = predictMethod(X)
123+
if colName in gdf.columns:
124+
raise Exception(f'{colName} exists in input and will not be overwritten')
125+
gdf[colName] = y_pred
126+
127+
#timestamp does not serialize properly to json, so for now do a subset as workaround
128+
gdf_out = gdf[['STN','TYPE','geometry','abnormality']]
129+
mimetype = 'application/geo+json'
130+
outputs = {
131+
'id': 'output_dataset',
132+
'value': gdf_out.to_json()
133+
}
134+
135+
return mimetype, outputs
136+
137+
def __repr__(self):
138+
return '<LOFProcessor> {}'.format(self.name)

0 commit comments

Comments
 (0)