def configure(self): if self.var('_provider'): self.provider = self.var('_provider') else: self.provider = self.root.create_object(provider.Object, self.config, shared=True) s = self.var('title') or self.var('index') self.template = self.provider.print_template(s) if not self.template: raise gws.Error(f'print template {s!r} not found') uid = self.var('uid') or (gws.sha256(self.provider.path) + '_' + str(self.template.index)) self.set_uid(uid) self.map_position = self.var('mapPosition') for el in self.template.elements: if el.type == 'page' and el.size: self.page_size = el.size if el.type == 'map' and el.size: self.map_size = el.size self.map_position = el.position if not self.page_size or not self.map_size or not self.map_position: raise gws.Error('cannot read page or map size')
def parse_dict(d: dict, trusted, with_strict_mode) -> dict: res = dict(_DEFAULTS) for key, val in d.items(): if val is None: continue k = key.replace('-', '_') if k.startswith('__'): k = k[2:] fn = gws.get(_ParseFunctions, k) if not fn: err = f'style: invalid css property {key!r}' if with_strict_mode: raise gws.Error(err) else: gws.log.error(err) continue try: v = fn(val, trusted) if v is not None: res[k] = v except Exception as exc: err = f'style: invalid css value for {key!r}' if with_strict_mode: raise gws.Error(err) from exc else: gws.log.error(err) return res
def require_for(obj: gws.INode) -> Object: uid = obj.var('db') if uid: prov = obj.root.find(klass=Object, uid=uid) if not prov: raise gws.Error(f'{obj.uid}: db provider {uid!r} not found') else: prov = obj.root.find(klass=Object) if not prov: raise gws.Error(f'{obj.uid}: db provider postgres not found') return prov
def configure_table(self, cfg: gws.base.db.SqlTableConfig) -> gws.SqlTable: table = gws.SqlTable(name=cfg.get('name')) cols = self.describe(table) if not cols: raise gws.Error( f'table {table.name!r} not found or not accessible') cname = cfg.get('keyColumn') if cname: for col in cols: if col.name == cname: table.key_column = col break if not table.key_column: raise gws.Error( f'invalid keyColumn {cname!r} for table {table.name!r}') else: cs = [col for col in cols if col.is_key] if len(cs) == 1: table.key_column = cs[0] cname = cfg.get('geometryColumn') if cname: for col in cols: if col.name == cname and col.is_geometry: table.geometry_column = col break if not table.geometry_column: raise gws.Error( f'invalid geometryColumn {cname!r} for table {table.name!r}' ) else: cs = [col for col in cols if col.is_geometry] if len(cs) == 1: table.geometry_column = cs[0] if len(cs) > 1: table.geometry_column = cs[0] gws.log.debug( f'found multiple geometry columns for table {table.name!r}, using {table.geometry_column.name!r}' ) cname = cfg.get('searchColumn') if cname: for col in cols: if col.name == cname: table.search_column = col break if not table.search_column: raise gws.Error( f'invalid searchColumn {cname!r} for table {table.name!r}') return table
def _render_legend(self, kwargs): layers = self._legend_layers(kwargs) if not layers: raise gws.Error('no legend layers') lro = gws.gis.legend.render(gws.Legend(layers=layers)) if not lro: raise gws.Error('no legend output') img_path = gws.gis.legend.to_image_path(lro) if not img_path: raise gws.Error('no legend image path') self.legend_count += 1 return f'<img src="{img_path}"/>'
def _tag(geom, opts): typ = geom.type if typ == 'Point': return opts.ns + 'Point', _pos(geom, opts, False) if typ == 'LineString': return opts.ns + 'Curve', (opts.ns + 'segments', (opts.ns + 'LineStringSegment', _pos(geom, opts))) if typ == 'Polygon': return (opts.ns + 'Polygon', (opts.ns + 'exterior', (opts.ns + 'LinearRing', _pos(geom.exterior, opts))), [(opts.ns + 'interior', (opts.ns + 'LinearRing', _pos(r, opts))) for r in geom.interiors]) if typ == 'MultiPoint': return opts.ns + 'MultiPoint', [ (opts.ns + 'pointMember', _tag(p, opts)) for p in geom ] if typ == 'MultiLineString': return opts.ns + 'MultiCurve', [ (opts.ns + 'curveMember', _tag(p, opts)) for p in geom ] if typ == 'MultiPolygon': return opts.ns + 'MultiSurface', [ (opts.ns + 'surfaceMember', _tag(p, opts)) for p in geom ] raise gws.Error(f'cannot convert geometry type {typ!r} to GML')
def configure(self): uid = self.var('uid') or 'map' project = self.get_closest('gws.base.project') if project: uid = project.uid + '.' + uid self.set_uid(uid) p = self.var('crs') self.crs = gws.gis.crs.require(p) if p else gws.gis.crs.get3857() self.title = self.var('title') or self.uid self.resolutions = _DEFAULT_RESOLUTIONS self.init_resolution = _DEFAULT_RESOLUTIONS[-1] zoom = self.var('zoom') if zoom: self.resolutions = gws.gis.zoom.resolutions_from_config(zoom) self.init_resolution = gws.gis.zoom.init_resolution( zoom, self.resolutions) self.layers = self.create_children('gws.ext.layer', self.var('layers')) self.extent = _configure_extent(self, self.crs, None) if not self.extent: raise gws.Error(f'no extent found for {self.uid!r}') _set_default_extent(self, self.extent) self.center = self.var('center') or gws.gis.extent.center(self.extent) self.coordinate_precision = self.var('coordinatePrecision') if self.coordinate_precision is None: self.coordinate_precision = 2 if self.crs.units == 'm' else 7
def configure(self): # with reqSize=1 MP will request the same tile multiple times # reqSize=4 is more efficient, however, reqSize=1 yields the first tile faster # which is crucial when browsing non-cached low resoltions # so, let's use 1 as default, overridable in the config # # @TODO make MP cache network requests self.grid.reqSize = self.grid.reqSize or 1 self.url = self.var('url') p = self.var('service', default=gws.Data()) self.service = Service(crs=gws.gis.crs.get(p.crs) or gws.gis.crs.get3857(), origin=p.origin, tile_size=p.tileSize, extent=p.extent) if not self.service.extent: if self.service.crs.srid == gws.gis.crs.c3857: self.service.extent = gws.gis.crs.c3857_extent else: raise gws.Error( f'service extent required for crs {self.service.crs.srid!r}' )
def configure(self): self.provider = provider.create(self.root, self.config, shared=True) self.limit = self.var('limit') self.templates = gws.base.template.bundle.create( self.root, items=self.var('templates'), defaults=_DEFAULT_TEMPLATES, parent=self) self.print_template = self.templates.find(subject='print') self.ui = self.var('ui') p = self.var('export') if p: groups = p.groups or _DEFAULT_EXPORT_GROUPS elif self.ui.useExport: groups = _DEFAULT_EXPORT_GROUPS else: groups = [] self.export_groups = [ self.require_child(ExportGroup, g) for g in groups ] self.buchung = self.root.create_object(BuchungOptions, self.var('buchung')) self.eigentuemer = self.root.create_object(EigentuemerOptions, self.var('eigentuemer')) if self.eigentuemer.log_table: with self.provider.connection() as conn: if not conn.user_can('INSERT', self.eigentuemer.log_table): raise gws.Error( f'no INSERT acccess to {self.eigentuemer.log_table!r}')
def _configure_extent(obj, crs: gws.ICrs, default_ext): layers = gws.get(obj, 'layers') or [] # we have an explicit extent provided in the config config_ext = obj.var('extent') if config_ext: ext = gws.gis.extent.from_list(config_ext) if not ext: raise gws.Error(f'{obj.uid!r}: invalid extent {config_ext!r}') # configure sublayers using config_ext as a default for la in layers: _configure_extent(la, crs, ext) obj.extent = ext return obj.extent if layers: # no config extent, configure sublayers using the current default extent # set obj.extent to the sum of sublayers' extents layer_ext_list = [] for la in layers: layer_ext = _configure_extent(la, crs, default_ext) if layer_ext: layer_ext_list.append(layer_ext) if layer_ext_list: obj.extent = gws.gis.extent.merge(layer_ext_list) else: obj.extent = default_ext return obj.extent # obj is a leaf layer and has no configured extent # check if it has an own extent (from its source) own_bounds: gws.Bounds = gws.get(obj, 'own_bounds') if own_bounds: own_ext = own_bounds.extent buf = obj.var('extentBuffer', with_parent=True) if buf: own_ext = gws.gis.extent.buffer(own_ext, buf) own_ext = gws.gis.extent.transform(own_ext, own_bounds.crs, crs) obj.extent = own_ext return obj.extent # obj is a leaf layer and has neither configured nor own extent # try using the default extent if default_ext: obj.extent = default_ext return obj.extent # no extent can be computed, it will be set to the map extent later on return None
def compile(self): text = gws.read_file(self.path) if self.path else self.text try: g = {} exec(text, g) return g['main'] except Exception as exc: raise gws.Error( f'py load error: {exc!r} in {self.path!r}') from exc
def ping(): gws.log.debug(f'db: ping {self.uid!r}') try: with self.connection() as conn: conn.select_value('select 1 + 1') gws.log.debug(f'db connection {self.uid!r}: ok') except gws.lib.sql.postgres.Error as exc: raise gws.Error( f'cannot open db connection {self.uid!r}') from exc
def _from_wkb(g, crs): crsid = geos.lgeos.GEOSGetSRID(g._geom) if crsid: crs = gws.gis.crs.get(crsid) geos.lgeos.GEOSSetSRID(g._geom, 0) if not crs: raise gws.Error('missing or invalid crs for WKT') return Shape(g, crs)
def _attachment_name(): if res.attachment_name: return res.attachment_name if res.path: return os.path.basename(res.path) if res.mime: ext = gws.lib.mime.extension_for(res.mime) if ext: return 'download.' + ext raise gws.Error('missing attachment_name or mime type')
def _get_crs(self, js): p = js.get('crs') if not p: return if p.get('type') == 'name': crs = gws.gis.crs.get(gws.get(p, 'properties.name')) if crs: return crs raise gws.Error(f'unsupported geojson crs format')
def render(self, tri, notify=None): mp = tri.maps[0] mri = gws.MapRenderInput( background_color=mp.background_color, bbox=mp.bbox, center=mp.center, crs=tri.crs, dpi=tri.dpi, out_size=self.page_size, planes=mp.planes, rotation=mp.rotation, scale=mp.scale, ) notify = notify or (lambda a, b=None: None) notify('begin_print') notify('begin_page') notify('begin_map') mro = gws.gis.render.render_map(mri, notify) html = gws.gis.render.output_to_html_string(mro, wrap='fixed') notify('end_map') notify('end_page') notify('finalize_print') if not tri.out_mime or tri.out_mime == gws.lib.mime.HTML: notify('end_print') return gws.ContentResponse(mime=gws.lib.mime.HTML, content=html) if tri.out_mime == gws.lib.mime.PDF: res_path = gws.tempname('map.pdf') gws.lib.html2.render_to_pdf( html, out_path=res_path, page_size=self.page_size, ) notify('end_print') return gws.ContentResponse(path=res_path) if tri.out_mime == gws.lib.mime.PNG: res_path = gws.tempname('map.png') gws.lib.html2.render_to_png( html, out_path=res_path, page_size=self.page_size, ) notify('end_print') return gws.ContentResponse(path=res_path) raise gws.Error(f'invalid output mime: {tri.out_mime!r}')
def render(self, tri, notify=None): fn = self.compile() ctx = self.prepare_context(tri.context) if isinstance(ctx, dict): ctx = gws.Data(ctx) try: return fn(ctx) except Exception as exc: gws.log.exception() raise gws.Error(f'py error: {exc!r} path={self.path!r}') from exc
def union(shapes: t.List[gws.IShape]) -> gws.IShape: if not shapes: raise gws.Error('empty shape union') if len(shapes) == 1: return shapes[0] crs = shapes[0].crs shapes = [s.transformed_to(crs) for s in shapes] geom = shapely.ops.unary_union([getattr(s, 'geom') for s in shapes]) return Shape(geom, crs)
def from_wkt(s: str, crs: gws.ICrs = None) -> gws.IShape: if s.startswith('SRID='): # EWKT c = s.index(';') crsid = s[len('SRID='):c] s = s[c + 1:] crs = gws.gis.crs.get(crsid) if not crs: raise gws.Error('missing or invalid crs for WKT') geom = geos.WKTReader(geos.lgeos).read(s) return Shape(geom, crs)
def new_stored_session(self, typ: str, method: gws.IAuthMethod, user: gws.IUser) -> gws.IAuthSession: self.store.cleanup(self.session_life_time) uid = self.store.create(typ=typ, method_uid=method.uid, provider_uid=user.provider.uid, user_uid=user.uid, str_user=self.serialize_user(user)) sess = self.find_stored_session(uid) if not sess: raise gws.Error('failed to create a new session') return sess
def configure_layers(obj: gws.IOwsClient, provider_class, **filter_args): if obj.var('_provider'): obj.provider = obj.var('_provider') obj.source_layers = obj.var('_source_layers') else: obj.provider = obj.root.create_object(provider_class, obj.config, shared=True) slf = gws.merge(gws.gis.source.LayerFilter(level=1), filter_args, obj.var('sourceLayers')) obj.source_layers = gws.gis.source.filter_layers( obj.provider.source_layers, slf) if not obj.source_layers: raise gws.Error(f'no source layers found for {obj.uid!r}')
def render_map(mri: gws.MapRenderInput, notify: t.Callable = None) -> gws.MapRenderOutput: rd = _Renderer(mri=mri, mro=gws.MapRenderOutput(planes=[]), img_count=0, svg_count=0) # vectors always use PDF_DPI rd.vector_view = _map_view(mri.bbox, mri.center, mri.crs, units.PDF_DPI, mri.rotation, mri.scale, mri.out_size) rd.mro.view = rd.vector_view if mri.out_size[2] == units.PX: # if they want pixels, use PDF_PDI for rasters as well rd.raster_view = rd.vector_view elif mri.out_size[2] == units.MM: # if they want mm, rasters should use they own dpi raster_dpi = min(MAX_DPI, max(MIN_DPI, rd.mri.dpi)) rd.raster_view = _map_view(mri.bbox, mri.center, mri.crs, raster_dpi, mri.rotation, mri.scale, mri.out_size) else: raise gws.Error(f'invalid size {mri.out_size!r}') # NB: planes are top-to-bottom for p in reversed(mri.planes): if notify: notify('begin_plane', p) try: _render_plane(rd, p) except Exception: # swallow exceptions so that we still can render if some layer fails gws.log.exception('render: input plane failed') if notify: notify('end_plane', p) return rd.mro
def configure_source(self): gws.gis.ows.client.configure_layers(self, provider_module.Object) self.source_crs = gws.gis.crs.best_match( self.provider.force_crs or self.crs, gws.gis.source.supported_crs_list(self.source_layers)) if len(self.source_layers) > 1: gws.log.warn(f'multiple layers found for {self.uid!r}, using the first one') self.source_layer = self.source_layers[0] self.tile_matrix_set = self.get_tile_matrix_set_for_crs(self.source_crs) if not self.tile_matrix_set: raise gws.Error(f'no suitable tile matrix set found for layer={self.uid!r}') self.style_name = '' if self.source_layer.default_style: self.style_name = self.source_layer.default_style.name self.grid.reqSize = self.grid.reqSize or 1 return True
def render(self, tri, notify=None): notify = notify or _dummy_fn notify('begin_print') if self.root.application.developer_option('template.always_reload'): if self.path: self.text = gws.read_file(self.path) parser = _Parser() rt = _Runtime(self, tri, notify) html = self._do_render(self.text, self.prepare_context(tri.context), parser, rt) notify('finalize_print') mime = tri.out_mime if not mime and self.mimes: mime = self.mimes[0] if not mime: mime = gws.lib.mime.HTML if mime == gws.lib.mime.HTML: notify('end_print') return gws.ContentResponse(mime=mime, content=html) if mime == gws.lib.mime.PDF: res_path = self._finalize_pdf(tri, html, parser) notify('end_print') return gws.ContentResponse(path=res_path) if mime == gws.lib.mime.PNG: res_path = self._finalize_png(tri, html, parser) notify('end_print') return gws.ContentResponse(path=res_path) raise gws.Error(f'invalid output mime: {tri.out_mime!r}')
def configure(self): self.path = self.var('path') self.root.application.monitor.add_path(self.path) self.url = 'http://%s:%s' % (self.root.application.var( 'server.qgis.host'), self.root.application.var('server.qgis.port')) self.source_text = self._read(self.path) cc = caps.parse(self.source_text) self.metadata = cc.metadata self.print_templates = cc.print_templates self.properties = cc.properties self.source_layers = cc.source_layers self.version = cc.version self.force_crs = gws.gis.crs.get(self.var('forceCrs')) self.project_crs = cc.project_crs self.crs = self.force_crs or self.project_crs if not self.crs: raise gws.Error(f'unknown CRS for in {self.path!r}') self.direct_render = set(self.var('directRender', default=[])) self.direct_search = set(self.var('directSearch', default=[]))
def caps(self, p: CapsParams): """Print the capabilities of a service in JSON format""" protocol = None if p.type: protocol = p.type.lower() else: u = p.src.lower() for s in ('wms', 'wmts', 'wfs'): if s in u: protocol = s break if not protocol: raise gws.Error('unknown service') if p.src.startswith(('http:', 'https:')): xml = gws.gis.ows.request.get_text( p.src, protocol=t.cast(gws.OwsProtocol, protocol.upper()), verb=gws.OwsVerb.GetCapabilities) else: xml = gws.read_file(p.src) mod = gws.import_from_path( f'gws/plugin/ows_provider/{protocol}/caps.py') res = mod.parse(xml) js = gws.lib.json2.to_pretty_string(res, default=_caps_json) if p.out: gws.write_file(p.out, js) gws.log.info(f'saved to {p.out!r}') else: print(js)
def parse(xml: str) -> Caps: root_el = xml2.from_string(xml, sort_atts=True, strip_ns=True, to_lower=True) ver = root_el.attributes.get('version', '').split('-')[0] if not ver.startswith('3'): raise gws.Error(f'unsupported QGIS version {ver!r}') caps = Caps(version=ver) caps.properties = _properties(xml2.first(root_el, 'properties')) caps.metadata = _project_meta_from_props(caps.properties) caps.project_crs = gws.gis.crs.get( xml2.text(root_el, 'projectCrs spatialrefsys authid')) caps.print_templates = _layouts(root_el) map_layers = _map_layers(root_el, caps.properties) root_group = _map_layer_tree(xml2.first(root_el, 'layer-tree-group'), map_layers) caps.source_layers = gws.gis.source.check_layers(root_group.layers) return caps
def _configure_rule(self, r): rule = Rule(r) name = rule.get('name') title = rule.get('title') if not name: name = gws.to_uid(title) if title else rule.get('source') if not name: raise gws.Error('missing attribute name') rule.name = name rule.title = title or name rule.type = rule.get('type') or gws.AttributeType.str if not rule.editor: rule.editor = AttributeEditor( type=_DEFAULT_EDITOR.get(rule.type, 'str')) rule.validators = r.get('validators') or [] return rule
def run(root: gws.IRoot, uid): job = get(root, uid) if not job: raise gws.Error('invalid job_uid {uid!r}') gws.log.debug('running job', job.uid) job.run()
def require_helper(self, ext_type): for obj in self.helpers: if obj.ext_type == ext_type: return obj raise gws.Error(f'helper {ext_type!r} not found')