def _create_qgis_projects(action, au_uids): gws.log.debug(f'create qgis projects for {au_uids!r}') dd = action.data_dir extents = _enum_extents(action, au_uids) layers = _enum_layers(action, au_uids) for au_uid in au_uids: path = f'{dd}/qgs/{au_uid}.qgs' ls = [la for la in layers if la['au_uid'] == au_uid] if not ls: os2.unlink(path) continue ext = extents.get(au_uid) if not ext: continue au_props = [au for au in action.au_list if au.uid == au_uid] if not au_props: continue res = action.qgis_template.render({ 'extent': ext, 'layers': ls, 'au': au_props[0], }) gws.write_file(path, res.content) gws.log.debug(f'created {path!r}')
def create_and_save(root: gws.IRoot): cfg = create(root) if not cfg: force = root.application.var('server.mapproxy.forceStart') if force: gws.log.warn('mapproxy: no configuration, using default') cfg = DEFAULT_CONFIG else: gws.log.warn('mapproxy: no configuration, not starting') gws.lib.os2.unlink(CONFIG_PATH) return cfg_str = yaml.dump(cfg) # make sure the config is ok before starting the server! test_path = CONFIG_PATH + '.test.yaml' gws.write_file(test_path, cfg_str) try: make_wsgi_app(test_path) except Exception as e: raise gws.ConfigurationError(f'MAPPROXY ERROR: {e!r}') from e gws.lib.os2.unlink(test_path) # write into the real config path gws.write_file(CONFIG_PATH, cfg_str)
def _worker(self, signo): with gws.lib.misc.lock(_lockfile) as ok: if not ok: try: pid = int(gws.read_file(_lockfile)) except: pid = None if not pid or not psutil.pid_exists(pid): gws.log.info( f'MONITOR: locked by dead pid={pid!r}, releasing') else: gws.log.info(f'MONITOR: locked by pid={pid!r}') return gws.write_file(_lockfile, str(os.getpid())) changed = self._poll() if not changed: return for path in changed: gws.log.info(f'MONITOR: changed {path!r}') # @TODO: smarter reload reconf = any(gws.APP_DIR not in path for path in changed) gws.log.info(f'MONITOR: begin reload (reconfigure={reconf})') if not self._reload(reconf): return # finally, reload ourselves gws.log.info(f'MONITOR: bye bye') control.reload('spool')
def environ(root: gws.IRoot): base_dir = gws.ensure_dir(gws.TMP_DIR + '/qqq') # it's all a bit blurry, but the server appears to read 'ini' from OPTIONS_DIR # while the app uses a profile # NB: for some reason, the profile path will be profiles/profiles/default (sic!) gws.ensure_dir('profiles', base_dir) gws.ensure_dir('profiles/default', base_dir) gws.ensure_dir('profiles/default/QGIS', base_dir) gws.ensure_dir('profiles/profiles', base_dir) gws.ensure_dir('profiles/profiles/default', base_dir) gws.ensure_dir('profiles/profiles/default/QGIS', base_dir) ini = _make_ini(root, base_dir) gws.write_file(base_dir + '/profiles/default/QGIS/QGIS3.ini', ini) gws.write_file(base_dir + '/profiles/profiles/default/QGIS/QGIS3.ini', ini) # server options, as documented on # see https://docs.qgis.org/testing/en/docs/user_manual/working_with_ogc/server/config.html#environment-variables server_env = { # not used here 'QGIS_PLUGINPATH': '', # not used here 'QGIS_SERVER_LOG_FILE': '', # see https://github.com/qgis/QGIS/pull/35738 'QGIS_SERVER_IGNORE_BAD_LAYERS': 'true', 'MAX_CACHE_LAYERS': root.application.var('server.qgis.maxCacheLayers'), 'QGIS_OPTIONS_PATH': base_dir + '/profiles/profiles/default', 'QGIS_SERVER_CACHE_DIRECTORY': gws.ensure_dir('servercache', base_dir), 'QGIS_SERVER_CACHE_SIZE': root.application.var('server.qgis.serverCacheSize'), 'QGIS_SERVER_LOG_LEVEL': root.application.var('server.qgis.serverLogLevel'), # 'QGIS_SERVER_MAX_THREADS': 4, # 'QGIS_SERVER_PARALLEL_RENDERING': 'false', } # qgis app options, mostly undocumented qgis_env = { 'QGIS_PREFIX_PATH': '/usr', 'QGIS_DEBUG': root.application.var('server.qgis.debug'), # 'QGIS_GLOBAL_SETTINGS_FILE': '/global_settings.ini', 'QGIS_CUSTOM_CONFIG_PATH': base_dir } # finally, there are lots of GDAL settings, some of those seem relevant # http://trac.osgeo.org/gdal/wiki/ConfigOptions gdal_env = { 'GDAL_FIX_ESRI_WKT': 'GEOGCS', 'GDAL_DEFAULT_WMS_CACHE_PATH': gws.ensure_dir('gdalcache', base_dir), } return gws.merge( server_env, qgis_env, gdal_env, )
def _inject_html_boxes(self, tri, boxes, project_path): # iterate the template XML tree and inject our boxes # see `caps.py` for the print layout structure root_el = xml2.from_path(self.provider.path) for layout_el in xml2.all(root_el, 'Layouts Layout'): for item_el in layout_el.children: uuid = item_el.attributes.get('uuid') if uuid in boxes: item_el.attributes['html'] = boxes[uuid] gws.write_file(project_path, xml2.to_string(root_el))
def caps(self, p: CapsParams): """Print the capabilities of a document in JSON format""" xml = gws.read_file(p.path) mod = gws.import_from_path(f'gws/plugin/qgis/caps.py') res = mod.parse(xml) js = gws.lib.json2.to_pretty_string(res, default=_caps_json, ascii=False) if p.out: gws.write_file(p.out, js) gws.log.info(f'saved to {p.out!r}') else: print(js)
def _enum_images(action): dd = action.data_dir images = [] for path in os2.find_files(f'{dd}/png', ext='png'): fn = _fnbody(path) converted_path = f'{dd}/cnv/{fn}.png' if os2.file_mtime(converted_path) < os2.file_mtime(path): try: # reduce the image palette (20-30 colors work just fine for scanned plans) gws.log.debug(f'converting {path!r}') img = PIL.Image.open(path) img = img.convert('RGBA') img = img.convert('P', palette=PIL.Image.ADAPTIVE, colors=action.image_quality) img.save(converted_path) os2.chown(converted_path) # copy the pgw along pgw = gws.read_file(f'{dd}/png/{fn}.pgw') gws.write_file(f'{dd}/cnv/{fn}.pgw', pgw) except Exception as e: gws.log.error(f'error converting {path!r}: {e}') continue try: palette = _image_palette(converted_path) except Exception as e: gws.log.error( f'error getting palette from {converted_path!r}: {e}') continue images.append({ 'uid': '_r_' + fn, 'fname': fn, 'path': converted_path, 'palette': palette }) return images
def find_features(self, args, source_layers): if not args.shapes: return [] shape = args.shapes[0] if shape.geometry_type != gws.GeometryType.point: return [] ps = gws.gis.ows.client.prepared_search( inverted_crs=self.inverted_crs, limit=args.limit, point=shape, protocol=self.protocol, protocol_version=self.version, request_crs=self.force_crs, request_crs_format=gws.CrsFormat.EPSG, source_layers=source_layers, ) params = gws.merge(ps.params, args.params) fmt = self.preferred_formats.get(gws.OwsVerb.GetFeatureInfo) if fmt: params.setdefault('INFO_FORMAT', fmt) op_args = self.operation_args(gws.OwsVerb.GetFeatureInfo, params=params) text = gws.gis.ows.request.get_text(**op_args) gws.write_file('/gws-var/res.xml', text) features = featureinfo.parse(text, crs=ps.request_crs, axis=ps.axis) if features is None: gws.log.debug(f'WMS NOT_PARSED params={params!r}') return [] gws.log.debug(f'WMS FOUND={len(features)} params={params!r}') return [f.transform_to(shape.crs) for f in features]
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 configure(self): self.crs = self.var('crs') self.db = t.cast( gws.ext.db.provider.postgres.Object, gws.base.db.require_provider(self, 'gws.ext.db.provider.postgres')) self.templates: t.List[gws.ITemplate] = gws.base.template.bundle(self, self.var('templates')) self.qgis_template: gws.ITemplate = gws.base.template.find(self.templates, subject='bplan.qgis') self.info_template: gws.ITemplate = gws.base.template.find(self.templates, subject='bplan.info') self.plan_table = self.db.configure_table(self.var('planTable')) self.meta_table = self.db.configure_table(self.var('metaTable')) self.data_dir = self.var('dataDir') self.au_list = self.var('administrativeUnits') self.type_list = self.var('planTypes') self.image_quality = self.var('imageQuality') p = self.var('exportDataModel') self.export_data_model: t.Optional[gws.IDataModel] = self.create_child('gws.base.model', p) if p else None for sub in 'png', 'pdf', 'cnv', 'qgs': gws.ensure_dir(self.data_dir + '/' + sub) self.key_col = 'plan_id' self.au_key_col = 'ags' self.au_name_col = 'gemeinde' self.type_col = 'typ' self.time_col = 'rechtskr' self.x_coord_col = 'utm_ost' self.y_coord_col = 'utm_nord' gws.write_file(_RELOAD_FILE, gws.random_string(16)) self.root.application.monitor.add_path(_RELOAD_FILE)
def _do_render(self, text, context, parser, runtime): def err(e, path, line): gws.log.warn( f'TEMPLATE: {e.__class__.__name__}:{e} in {path}:{line}') if self.root.application.developer_option('template.raise_errors'): err = None if self.root.application.developer_option('template.save_compiled'): gws.write_file( gws.VAR_DIR + '/debug_template_' + gws.to_uid(self.path) + '_' + gws.sha256(text), chartreux.translate(text, commands=parser, path=self.path or '<string>')) return chartreux.render( text, context, path=self.path or '<string>', error=err, runtime=runtime, commands=parser, )
def signal_reload(self, source): gws.log.debug(f'bplan reload signal {source!r}') gws.write_file(_RELOAD_FILE, gws.random_string(16))
def _save_intermediate(path, txt, ext): p = gws.lib.os2.parse_path(path) gws.write_file(f"{gws.CONFIG_DIR}/{p['name']}.parsed.{ext}", txt)
def save(self, path=None): path = path or self.path if not path: raise ValueError('no path') gws.write_file(path, str(self.bs))