Coverage for gws-app/gws/plugin/gbd_geoservices/model.py: 0%
68 statements
« prev ^ index » next coverage.py v7.8.0, created at 2025-04-17 01:37 +0200
« prev ^ index » next coverage.py v7.8.0, created at 2025-04-17 01:37 +0200
1"""GBD Geoservices model."""
3import re
5import gws
6import gws.base.feature
7import gws.base.model
8import gws.base.shape
9import gws.gis.bounds
10import gws.gis.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
24class Object(gws.base.model.default_model.Object):
25 """GBD Geoservices model."""
27 apiKey: str
29 serviceUrl = 'https://geoservices.gbd-consult.de/search'
31 def configure(self):
32 self.apiKey = self.cfg('apiKey')
33 self.uidName = 'uid'
34 self.geometryName = 'geometry'
35 self.loadingStrategy = gws.FeatureLoadingStrategy.all
37 def props(self, user):
38 return gws.u.merge(
39 super().props(user),
40 canCreate=False,
41 canDelete=False,
42 canWrite=False,
43 )
45 def find_features(self, search, mc, **kwargs):
46 request = {
47 'page': 0,
48 'tags': {}
49 }
51 if search.shape:
52 geometry_tolerance = 0.0
54 if search.tolerance:
55 n, u = search.tolerance
56 geometry_tolerance = n * (search.resolution or 1) if u == 'px' else n
58 search_shape = search.shape.tolerance_polygon(geometry_tolerance)
59 request['viewbox'] = gws.gis.bounds.wgs_extent(search_shape.bounds())
61 kw = search.keyword or ''
62 use_address = False
63 if kw:
64 request['intersect'] = 1
65 # crude heuristics to check if this is an "address" or a "name"
66 if re.search(r'\s\d', kw):
67 request['address'] = kw
68 use_address = True
69 else:
70 request['name'] = kw
72 features = {}
74 res = self._query(request)
75 fs = res['results']['features']
77 for f in fs:
78 rec = gws.FeatureRecord(
79 uid=f['id'],
80 attributes={
81 k.replace(':', '_'): v
82 for k, v in sorted(f['properties'].items())
83 },
84 shape=gws.base.shape.from_geojson(f['geometry'], gws.gis.crs.WGS84, always_xy=True),
85 )
86 if search.shape and search.shape.type == gws.GeometryType.polygon and not rec['shape'].intersects(search.shape):
87 continue
89 a = rec['attributes']
91 if use_address and 'addr_housenumber' not in a:
92 continue
93 if not use_address and 'name' not in a:
94 continue
96 address = ' '.join([
97 a.get('addr_street', ''),
98 a.get('addr_housenumber', ''),
99 a.get('addr_postcode', ''),
100 a.get('addr_city', ''),
101 ])
102 address = ' '.join(address.split())
104 if use_address:
105 a['title'] = address
106 else:
107 a['title'] = a.get('name')
108 if address:
109 a['title'] += ' (' + address + ')'
111 features[a['title']] = self.feature_from_record(rec, mc)
113 return [f for _, f in sorted(features.items())]
115 def _query(self, request) -> dict:
116 try:
117 res = gws.lib.net.http_request(
118 self.serviceUrl,
119 method='POST',
120 headers={'x-api-key': self.apiKey},
121 json=request
122 )
123 return gws.lib.jsonx.from_string(res.text)
124 except gws.lib.net.Error as e:
125 gws.log.error('geoservices request error', e)
126 return {}