Coverage for gws-app / gws / plugin / gbd_geoservices / model.py: 0%
62 statements
« prev ^ index » next coverage.py v7.13.4, created at 2026-03-03 10:12 +0100
« prev ^ index » next coverage.py v7.13.4, created at 2026-03-03 10:12 +0100
1"""GBD Geoservices model."""
3import re
5import gws
6import gws.base.feature
7import gws.base.model
8import gws.base.shape
9import gws.lib.bounds
10import gws.lib.crs
11import gws.gis.source
12import gws.lib.jsonx
13import gws.lib.net
15gws.ext.new.model('gbd_geoservices')
18class Config(gws.base.model.Config):
19 """GBD Geoservices model."""
21 apiKey: str
22 """API key for GBD Geoservices."""
25class Object(gws.base.model.default_model.Object):
26 """GBD Geoservices model."""
28 apiKey: str
30 serviceUrl = 'https://geoservices.gbd-consult.de/search'
32 def configure(self):
33 self.apiKey = self.cfg('apiKey')
34 self.uidName = 'uid'
35 self.geometryName = 'geometry'
36 self.loadingStrategy = gws.FeatureLoadingStrategy.all
38 def props(self, user):
39 return gws.u.merge(
40 super().props(user),
41 canCreate=False,
42 canDelete=False,
43 canWrite=False,
44 )
46 def find_features(self, search, mc, **kwargs):
47 request = {'page': 0, 'tags': {}}
49 if search.shape:
50 geometry_tolerance = 0.0
52 if search.tolerance:
53 n, u = search.tolerance
54 geometry_tolerance = n * (search.resolution or 1) if u == 'px' else n
56 search_shape = search.shape.tolerance_polygon(geometry_tolerance)
57 request['viewbox'] = gws.lib.bounds.wgs_extent(search_shape.bounds())
59 kw = search.keyword or ''
60 if kw:
61 request['intersect'] = 1
62 # crude heuristics to check if this is an "address" or a "name"
63 if re.search(r'\s\d', kw):
64 request['address'] = kw
65 else:
66 request['name'] = kw
68 features = {}
70 res = self._query(request)
72 for f in res['results']['features']:
73 a = {k.replace(':', '_'): v for k, v in sorted(f['properties'].items())}
74 shape = gws.base.shape.from_geojson(f['geometry'], gws.lib.crs.WGS84, always_xy=True)
76 if search.shape and search.shape.type == gws.GeometryType.polygon and not shape.intersects(search.shape):
77 continue
79 address = ' '.join(
80 [
81 a.get('addr_street') or '',
82 a.get('addr_housenumber') or '',
83 a.get('addr_postcode') or '',
84 a.get('addr_city') or '',
85 ]
86 )
88 a['address'] = ' '.join(address.split())
89 a['name'] = a.get('name') or ''
91 if a['name'] and a['address']:
92 a['title'] = a['name'] + ' (' + a['address'] + ')'
93 else:
94 a['title'] = a['name'] or a['address']
96 rec = gws.FeatureRecord(uid=f['id'], attributes=a, shape=shape)
97 features[a['title']] = self.feature_from_record(rec, mc)
99 return [f for _, f in sorted(features.items())]
101 def _query(self, request) -> dict:
102 try:
103 res = gws.lib.net.http_request(
104 self.serviceUrl,
105 method='POST',
106 headers={'x-api-key': self.apiKey},
107 json=request,
108 )
109 return gws.lib.jsonx.from_string(res.text)
110 except gws.lib.net.Error as e:
111 gws.log.error('geoservices request error', e)
112 return {}