def _all_person(conn: AlkisConnection): dat = conn.data_schema rs = conn.select_from_ax('ax_anschrift', [ 'gml_id', 'ort_post', 'ortsteil', 'postleitzahlpostzustellung', 'strasse', 'hausnummer', 'telefon' ]) anschrift = {r['gml_id']: gws.compact(r) for r in rs} person = {} anrede = {1000: 'Frau', 2000: 'Herr', 3000: 'Firma'} rs = conn.select_from_ax('ax_person', [ 'gml_id', 'anrede', 'akademischergrad', 'geburtsdatum', 'nachnameoderfirma', 'vorname', 'hat' ]) for r in rs: hat = r.pop('hat', None) or [] for h in hat: if h in anschrift: r['anschrift'] = anschrift[h] break r['anrede'] = anrede.get(r['anrede']) person[r['gml_id']] = gws.compact(r) return person
def _all_buchungsblatt(conn: AlkisConnection): persons = _all_person(conn) blatts = {} rs = conn.select_from_ax('ax_buchungsblatt') for r in rs: bb = { 'gml_id': r['gml_id'], 'buchungsblattkennzeichen': r['buchungsblattkennzeichen'], 'buchungsblattnummermitbuchstabenerweiterung': r['buchungsblattnummermitbuchstabenerweiterung'], 'eigentuemer': [], } bb.update(resolver.places(conn, r)) bb.update(resolver.attributes(conn, 'ax_buchungsblatt', r)) blatts[r['gml_id']] = gws.compact(bb) rs = conn.select_from_ax('ax_namensnummer') for r in rs: if r['istbestandteilvon'] in blatts: eigentuemer = { 'anteil': _anteil(r), 'gml_id': r['gml_id'], 'laufendenummernachdin1421': r['laufendenummernachdin1421'], 'person': persons.get(r['benennt']) } eigentuemer.update(resolver.attributes(conn, 'ax_namensnummer', r)) blatts[r['istbestandteilvon']]['eigentuemer'].append( gws.compact(eigentuemer)) return blatts
def walk(sl: gws.SourceLayer, depth: int): if exclude_slf and gws.gis.source.layer_matches(sl, exclude_slf): return None cfg = None if not sl.is_group: # leaf layer cfg = leaf_layer_config_fn([sl]) elif flatten and sl.a_level >= flatten.level: # flattened group layer # NB use the absolute level to compute flatness, could also use relative (=depth) if flatten.useGroups: cfg = leaf_layer_config_fn([sl]) else: slf = gws.gis.source.LayerFilter(is_image=True) image_layers = gws.gis.source.filter_layers([sl], slf) if not image_layers: return None cfg = leaf_layer_config_fn(image_layers) else: # ordinary group layer sub_cfgs = gws.compact(walk(sub, depth + 1) for sub in sl.layers) if not sub_cfgs: return None cfg = { 'type': 'group', 'uid': gws.to_uid(sl.name), 'layers': sub_cfgs } if not cfg: return cfg = gws.merge( cfg, { 'uid': gws.to_uid(sl.name), 'title': sl.title, 'clientOptions': { 'visible': sl.is_visible, 'expanded': sl.is_expanded, }, 'opacity': sl.opacity or 1, }) if sl.scale_range: cfg['zoom'] = { 'minScale': sl.scale_range[0], 'maxScale': sl.scale_range[1], } for flt, cc in zip(custom_filters, custom_configs): if gws.gis.source.layer_matches(sl, flt): cfg = gws.deep_merge(vars(cc), cfg) cfg.pop('applyTo', None) return gws.compact(cfg)
def _map_layer_tree(el: gws.XmlElement, map_layers): visible = el.attributes.get('checked') != 'Qt::Unchecked' expanded = el.attributes.get('expanded') == '1' if el.name == 'layer-tree-group': title = el.attributes.get('name') # qgis doesn't write 'id' for groups but our generators might name = el.attributes.get('id') or title sl = gws.SourceLayer(title=title, name=name) sl.metadata = gws.lib.metadata.from_args(title=title, name=name) sl.is_visible = visible sl.is_expanded = expanded sl.is_group = True sl.is_queryable = False sl.is_image = False sl.layers = gws.compact( _map_layer_tree(c, map_layers) for c in el.children) return sl if el.name == 'layer-tree-layer': sl = map_layers.get(el.attributes.get('id')) if sl: sl.is_visible = visible sl.is_expanded = expanded sl.is_group = False sl.is_image = True return sl
def _metadata_dict(el: gws.XmlElement) -> dict: if not el: return {} d = { 'abstract': xml2.text(el, 'Abstract'), 'accessConstraints': xml2.text(el, 'AccessConstraints'), 'attribution': xml2.text(el, 'Attribution Title'), 'fees': xml2.text(el, 'Fees'), 'keywords': xml2.text_list(el, 'Keywords', 'KeywordList', deep=True), 'name': xml2.text(el, 'Name') or xml2.text(el, 'Identifier'), 'title': xml2.text(el, 'Title'), 'metaLinks': gws.compact(_parse_link(e) for e in xml2.all(el, 'MetadataURL')), } e = xml2.first(el, 'AuthorityURL') if e: d['authorityUrl'] = _parse_url(e) d['authorityName'] = xml2.attr(e, 'name') e = xml2.first(el, 'Identifier') if e: d['authorityIdentifier'] = e.text return gws.strip(d)
def _combine_outputs( lros: t.List[gws.LegendRenderOutput], options: dict = None) -> t.Optional[gws.LegendRenderOutput]: imgs = gws.compact(to_image(lro) for lro in lros) img = _combine_images(imgs, options) if not img: return None return gws.LegendRenderOutput(image=img, size=img.size)
def walk(sl, parent_path, level): if not sl: return sl.a_uid = gws.to_uid(sl.name or sl.metadata.get('title')) sl.a_path = parent_path + '/' + sl.a_uid sl.a_level = level sl.layers = gws.compact( walk(c, sl.a_path, level + 1) for c in (sl.layers or [])) return sl
def _add_font_atts(atts, sv, prefix=''): font_name = _font_name(sv, prefix) font_size = sv.get(prefix + 'font_size') or DEFAULT_FONT_SIZE atts.update( gws.compact({ 'font-family': font_name.split('-')[0], 'font-size': f'{font_size}px', 'font-weight': sv.get(prefix + 'font_weight'), 'font-style': sv.get(prefix + 'font_style'), }))
def _layouts(root_el: gws.XmlElement): tpls = [] for layout_el in xml2.all(root_el, 'Layouts Layout'): tpl = PrintTemplate( title=layout_el.attributes.get('name', ''), attributes=layout_el.attributes, index=len(tpls), elements=[], ) pc_el = xml2.first(layout_el, 'PageCollection') if pc_el: tpl.elements.extend( gws.compact(_layout_element(c) for c in pc_el.children)) tpl.elements.extend( gws.compact(_layout_element(c) for c in layout_el.children)) tpls.append(tpl) return tpls
def render(legend: gws.Legend, context: dict = None) -> t.Optional[gws.LegendRenderOutput]: if legend.path: img = gws.lib.image.from_path(legend.path) return gws.LegendRenderOutput(image=img, size=img.size) if legend.urls: lros = [] for url in legend.urls: try: res = gws.gis.ows.request.get_url(url, max_age=legend.cache_max_age) if not res.content_type.startswith('image/'): raise gws.gis.ows.error.Error( f'wrong content type {res.content_type!r}') img = gws.lib.image.from_bytes(res.content) lro = gws.LegendRenderOutput(image=img, size=img.size) lros.append(lro) except gws.gis.ows.error.Error: gws.log.exception( f'render_legend: download failed url={url!r}') # NB even if there's only one image, it's not a bad idea to run it through the image converter return _combine_outputs(gws.compact(lros), legend.options) if legend.template: # @TODO return html legends as html res = legend.template.render( gws.TemplateRenderInput(context=context, out_mime=gws.lib.mime.PNG)) img = gws.lib.image.from_path(res.path) return gws.LegendRenderOutput(image=img, size=img.size) if legend.layers: lros = gws.compact( la.render_legend_with_cache(context) for la in legend.layers if la.has_legend) return _combine_outputs(lros, legend.options) return None
def check_layers(layers) -> t.List[gws.SourceLayer]: def walk(sl, parent_path, level): if not sl: return sl.a_uid = gws.to_uid(sl.name or sl.metadata.get('title')) sl.a_path = parent_path + '/' + sl.a_uid sl.a_level = level sl.layers = gws.compact( walk(c, sl.a_path, level + 1) for c in (sl.layers or [])) return sl return gws.compact(walk(sl, '', 1) for sl in layers)
def _legend_layers(self, kwargs): if 'layers' in kwargs: user = self.tri.user or self.tpl.root.application.auth.guest_user return gws.compact( user.acquire('gws.ext.layer', uid) for uid in kwargs['layers'].split()) if not self.tri.maps: return if 'map' in kwargs: mp = self.tri.maps[int(kwargs['map'])] return mp.visible_layers return self.tri.maps[0].visible_layers
def _all_buchungsstelle(conn: AlkisConnection): blatts = _all_buchungsblatt(conn) stellen = {} rs = conn.select_from_ax('ax_buchungsstelle') for r in rs: bb = blatts.get(r['istbestandteilvon'], {}) st = { 'gml_id': r['gml_id'], 'beginnt': r['beginnt'], 'endet': r['endet'], 'laufendenummer': r['laufendenummer'], 'an': r['an'], 'eigentuemer': bb.get('eigentuemer', []), 'buchungsblatt': {k: v for k, v in bb.items() if k != 'eigentuemer'}, 'anteil': _anteil(r), } st.update(resolver.places(conn, r)) st.update(resolver.attributes(conn, 'ax_buchungsstelle', r)) stellen[r['gml_id']] = gws.compact(st) # resolve 'an' dependencies # Eine 'Buchungsstelle' verweist mit 'an' auf eine andere 'Buchungsstelle' auf einem anderen Buchungsblatt. # Die Buchungsstelle kann ein Recht (z.B. Erbbaurecht) oder einen Miteigentumsanteil 'an' der anderen Buchungsstelle haben # Die Relation zeigt stets vom begünstigten Recht zur belasteten Buchung # (z.B. Erbbaurecht hat ein Recht 'an' einem Grundstück). for r in stellen.values(): for gml_id in r.get('an', []): if gml_id is None: continue if gml_id not in stellen: gws.log.warn(f'invalid "an" reference: {gml_id!r}') continue target = stellen[gml_id] if '_an' not in target: target['_an'] = [] target['_an'].append(r['gml_id']) data = [] for r in stellen.values(): ls = _make_list(stellen, r, set()) data.append({'gml_id': r['gml_id'], 'stellen': indexer.to_json(ls)}) return data
def mapproxy_config(self, mc): m0 = self.tile_matrix_set.matrices[0] res = [ units.scale_to_res(m.scale) for m in self.tile_matrix_set.matrices ] grid_uid = mc.grid(gws.compact({ 'origin': 'nw', # nw = upper-left for WMTS 'bbox': m0.extent, 'res': res, 'srs': self.source_crs.epsg, 'tile_size': [m0.tile_width, m0.tile_height], })) src = self.mapproxy_back_cache_config(mc, self.get_tile_url(), grid_uid) self.mapproxy_layer_config(mc, src)
def leaf_layer_config(self, source_layers): default = { 'type': 'qgisflat', '_provider': self, '_source_layers': source_layers } if len(source_layers) > 1 or source_layers[0].is_group: return default sl = source_layers[0] ds = sl.data_source prov = ds.get('provider') if prov not in self.direct_render: return default if prov == 'wms': layers = ds.get('layers') if not layers: return return { 'type': 'wmsflat', 'sourceLayers': { 'names': ds['layers'] }, 'url': self.make_wms_url(ds['url'], ds['params']), } if prov == 'wmts': layers = ds.get('layers') if not layers: return return gws.compact({ 'type': 'wmts', 'url': ds['url'].split('?')[0], 'sourceLayer': ds['layers'][0], 'sourceStyle': (ds['options'] or {}).get('styles'), }) gws.log.warn(f'directRender not supported for {prov!r}') return default
def find_features(self, req: gws.IWebRequest, p: Params) -> Response: """Perform a search""" project = req.require_project(p.projectUid) bounds = gws.Bounds( crs=p.crs or project.map.crs, extent=p.bbox or project.map.extent, ) limit = self.limit if p.limit: limit = min(int(p.limit), self.limit) shapes = [] if p.shapes: shapes = [gws.gis.shape.from_props(s) for s in p.shapes] tolerance = None if p.tolerance: tolerance = gws.lib.units.parse(p.tolerance, default=gws.lib.units.PX) args = gws.SearchArgs( bounds=bounds, keyword=(p.keyword or '').strip(), layers=gws.compact( req.acquire('gws.ext.layer', uid) for uid in p.layerUids), limit=limit, project=project, resolution=p.resolution, shapes=shapes, tolerance=tolerance, ) found = runner.run(req, args) for f in found: # @TODO only pull specified props from a feature f.transform_to(args.bounds.crs) f.apply_templates() f.apply_data_model() return Response( features=[gws.props(f, req.user, context=self) for f in found])
def mapproxy_config(self, mc, options=None): # we use {x} like in Ol, mapproxy wants %(x)s url = re.sub(r'{([xyz])}', r'%(\1)s', self.url) grid_uid = mc.grid( gws.compact({ 'origin': self.service.origin, 'bbox': self.service.extent, # 'res': res, 'srs': self.service.crs.epsg, 'tile_size': [self.service.tile_size, self.service.tile_size], })) src = self.mapproxy_back_cache_config(mc, url, grid_uid) self.mapproxy_layer_config(mc, src)
def _parse_datasource(provider, source): if provider == 'wfs': params = _parse_datasource_uri(source) url = params.pop('url', '') if not url: return {} p = gws.lib.net.parse_url(url) typename = params.pop('typename', '') or p.params.get('typename') return {'url': url, 'typeName': typename, 'params': params} if provider == 'wms': options = {} for k, v in urllib.parse.parse_qs(source).items(): options[k] = v[0] if len(v) < 2 else v layers = [] if 'layers' in options: layers = options.pop('layers') # 'layers' must be a list if isinstance(layers, str): layers = [layers] url = options.pop('url', '') params = {} if url: url, params = _parse_url_with_qs(url) d = { 'url': url, 'options': options, 'params': params, 'layers': layers } if 'tileMatrixSet' in options: d['provider'] = 'wmts' return d if provider in ('gdal', 'ogr'): return {'path': source} if provider == 'postgres': return gws.compact(_parse_datasource_uri(source)) return {'source': source}
def layer_caps_tree(self, rd: Request) -> LayerCapsTree: """Return the root layer caps for a project.""" def enum(layer_uid, ancestors): layer: gws.ILayer = rd.req.acquire('gws.ext.layer', layer_uid) if not layer: return opts = self._layer_options(layer, level=len(ancestors) + 1) if not opts: return lc = LayerCaps() lc.ancestors = ancestors + [lc] if not layer.layers: self._populate_layer_caps(lc, layer, opts, []) tree.leaves.append(lc) return lc ch = gws.compact(enum(la.uid, lc.ancestors) for la in layer.layers) if ch: self._populate_layer_caps(lc, layer, opts, ch) return lc tree = LayerCapsTree(leaves=[], roots=[]) if not rd.project: return tree if not self.root_layer_uid: tree.roots = gws.compact(enum(la.uid, []) for la in rd.project.map.layers) return tree uid = self.root_layer_uid if '.' not in uid: uid = rd.project.map.uid + '.' + uid root = enum(uid, []) if root: tree.roots.append(root) return tree
def enum(layer_uid, ancestors): layer: gws.ILayer = rd.req.acquire('gws.ext.layer', layer_uid) if not layer: return opts = self._layer_options(layer, level=len(ancestors) + 1) if not opts: return lc = LayerCaps() lc.ancestors = ancestors + [lc] if not layer.layers: self._populate_layer_caps(lc, layer, opts, []) tree.leaves.append(lc) return lc ch = gws.compact(enum(la.uid, lc.ancestors) for la in layer.layers) if ch: self._populate_layer_caps(lc, layer, opts, ch) return lc
def mapproxy_config(self, mc, options=None): layers = [sl.name for sl in self.source_layers if sl.name] if not self.var('capsLayersBottomUp'): layers = reversed(layers) args = self.provider.operation_args(gws.OwsVerb.GetMap) req = gws.merge(args['params'], { 'transparent': True, 'layers': ','.join(layers), 'url': args['url'], }) source_uid = mc.source( gws.compact({ 'type': 'wms', 'supported_srs': [self.source_crs.epsg], 'concurrent_requests': self.var('maxRequests'), 'req': req })) self.mapproxy_layer_config(mc, source_uid)
def _collect_gebs(conn): rs = conn.select_from_ax('ax_gebaeude', [ 'gml_id', 'gebaeudefunktion', 'weiteregebaeudefunktion', 'name', 'bauweise', 'anzahlderoberirdischengeschosse', 'anzahlderunterirdischengeschosse', 'hochhaus', 'objekthoehe', 'dachform', 'zustand', 'geschossflaeche', 'grundflaeche', 'umbauterraum', 'lagezurerdoberflaeche', 'dachart', 'dachgeschossausbau', 'description', 'art', 'individualname', 'baujahr', 'wkb_geometry' ]) gebs = [] for r in rs: r = gws.compact(r) r.update(resolver.attributes(conn, 'ax_gebaeude', r)) gebs.append({ 'gml_id': r.pop('gml_id'), 'geom': r.pop('wkb_geometry'), 'attributes': indexer.to_json(r) }) return gebs
def mapproxy_back_cache_config(self, mc, url, grid_uid): source_uid = mc.source({ 'type': 'tile', 'url': url, 'grid': grid_uid, 'concurrent_requests': self.var('maxRequests', default=0) }) return mc.cache( gws.compact({ 'sources': [source_uid], 'grids': [grid_uid], 'cache': { 'type': 'file', 'directory_layout': 'mp' }, 'disable_storage': True, 'format': self.image_format, }))
def mapproxy_layer_config(self, mc, source_uid): mc.layer({'name': self.uid + '_NOCACHE', 'sources': [source_uid]}) res = [r for r in self.resolutions if r] if len(res) < 2: res = [res[0], res[0]] self.grid_uid = mc.grid( gws.compact({ 'origin': self.grid.origin, 'tile_size': [self.grid.tileSize, self.grid.tileSize], 'res': res, 'srs': self.map.crs.epsg, 'bbox': self.extent, })) meta_size = self.grid.reqSize or 4 front_cache_config = { 'sources': [source_uid], 'grids': [self.grid_uid], 'cache': { 'type': 'file', 'directory_layout': 'mp' }, 'meta_size': [meta_size, meta_size], 'meta_buffer': self.grid.reqBuffer, 'disable_storage': not self.has_cache, 'minimize_meta_requests': True, 'format': self.image_format, } self.cache_uid = mc.cache(front_cache_config) mc.layer({'name': self.uid, 'sources': [self.cache_uid]})
def shape_to_fragment(shape: gws.IShape, view: gws.MapView, style: gws.IStyle = None, label: str = None) -> t.List[gws.XmlElement]: if not shape: return [] geom = t.cast(gws.gis.shape.Shape, shape).geom if geom.is_empty: return [] trans = gws.gis.render.map_view_transformer(view) geom = shapely.ops.transform(trans, geom) if not style: return [_geometry(geom)] sv = style.values with_geometry = sv.with_geometry == 'all' with_label = label and _is_label_visible(view, sv) gt = _geom_type(geom) text = None if with_label: extra_y_offset = 0 if sv.label_offset_y is None: if gt == _TYPE_POINT: extra_y_offset = 12 if gt == _TYPE_LINESTRING: extra_y_offset = 6 text = _label(geom, label, sv, extra_y_offset) marker = None marker_id = None if with_geometry and sv.marker: marker_id = '_M' + gws.random_string(8) marker = _marker(marker_id, sv) icon = None atts: dict = {} if with_geometry and sv.icon: res = _parse_icon(sv.icon, view.dpi) if res: el, w, h = res x, y, w, h = _icon_size_position(geom, sv, w, h) atts = { 'x': f'{int(x)}', 'y': f'{int(y)}', 'width': f'{int(w)}', 'height': f'{int(h)}', } icon = xml2.element(name=el.name, attributes=gws.merge(el.attributes, atts), children=el.children) body = None if with_geometry: _add_paint_atts(atts, sv) if marker: atts['marker-start'] = atts['marker-mid'] = atts[ 'marker-end'] = f'url(#{marker_id})' if gt == _TYPE_POINT or gt == _TYPE_MULTIPOINT: atts['r'] = (sv.point_size or DEFAULT_POINT_SIZE) // 2 if gt == _TYPE_LINESTRING or gt == _TYPE_MUTLILINESTRING: atts['fill'] = 'none' body = _geometry(geom, atts) return gws.compact([marker, body, icon, text])
def _layer_tree_configuration( source_layers: t.List[gws.SourceLayer], roots_slf: gws.gis.source.LayerFilter, exclude_slf: gws.gis.source.LayerFilter, flatten: FlattenOption, custom_configs: t.List[types.CustomConfig], leaf_layer_config_fn: t.Callable, ) -> t.List[gws.Config]: ## def walk(sl: gws.SourceLayer, depth: int): if exclude_slf and gws.gis.source.layer_matches(sl, exclude_slf): return None cfg = None if not sl.is_group: # leaf layer cfg = leaf_layer_config_fn([sl]) elif flatten and sl.a_level >= flatten.level: # flattened group layer # NB use the absolute level to compute flatness, could also use relative (=depth) if flatten.useGroups: cfg = leaf_layer_config_fn([sl]) else: slf = gws.gis.source.LayerFilter(is_image=True) image_layers = gws.gis.source.filter_layers([sl], slf) if not image_layers: return None cfg = leaf_layer_config_fn(image_layers) else: # ordinary group layer sub_cfgs = gws.compact(walk(sub, depth + 1) for sub in sl.layers) if not sub_cfgs: return None cfg = { 'type': 'group', 'uid': gws.to_uid(sl.name), 'layers': sub_cfgs } if not cfg: return cfg = gws.merge( cfg, { 'uid': gws.to_uid(sl.name), 'title': sl.title, 'clientOptions': { 'visible': sl.is_visible, 'expanded': sl.is_expanded, }, 'opacity': sl.opacity or 1, }) if sl.scale_range: cfg['zoom'] = { 'minScale': sl.scale_range[0], 'maxScale': sl.scale_range[1], } for flt, cc in zip(custom_filters, custom_configs): if gws.gis.source.layer_matches(sl, flt): cfg = gws.deep_merge(vars(cc), cfg) cfg.pop('applyTo', None) return gws.compact(cfg) ## custom_filters = [ gws.gis.source.layer_filter_from_config(cc.applyTo) for cc in custom_configs ] # by default, take top-level layers as roots roots = gws.gis.source.filter_layers( source_layers, roots_slf or gws.gis.source.LayerFilter(level=1)) # make configs... return gws.compact(walk(sl, 0) for sl in roots)
def _sanitize(el: gws.XmlElement) -> t.Optional[gws.XmlElement]: if el.name in _ALLOWED_TAGS: return xml2.element(el.name, _sanitize_atts(el.attributes), gws.compact(_sanitize(c) for c in el.children))
def sanitize_element(el: gws.XmlElement) -> t.Optional[gws.XmlElement]: children = gws.compact(_sanitize(c) for c in el.children) if children: return xml2.tag('svg', _sanitize_atts(el.attributes), *children)