#!/usr/bin/env python3 """Convert a template mapfile config to a full config. We do this rendering when the container is started to allow for complete control per mapserver instance (even though there is usually only one). """ import itertools import os import sys import jinja2 import yaml sectors_filter = os.getenv("WMS_SECTORS", "") if sectors_filter: sectors_filter = sectors_filter.split(" ") products_filter = os.getenv("WMS_PRODUCTS", "") if products_filter: products_filter = products_filter.split(" ") satellites_filter = os.getenv("WMS_PLATFORMS", "") if satellites_filter: satellites_filter = satellites_filter.split(" ") layer_base_dir = os.environ['LAYER_BASE_DIR'] def _get_connect_str(): pw_filename = "__POSTGRES_PASSWORD_FILE__" connect_str = "host=__POSTGRES_HOST__ " \ "port=__POSTGRES_PORT__ " \ "dbname=__POSTGRES_DBNAME__ " \ "user=__POSTGRES_USER__" if not os.path.isfile(pw_filename) or '__' in connect_str: print(pw_filename, os.path.isfile(pw_filename), connect_str) raise ValueError("Backend has not been configured properly " "and doesn't know how to communicate with the database. " "Please contact site administrator.") with open(pw_filename, 'r') as pw_file: password = pw_file.read().strip() connect_str += " password={}".format(password) return connect_str def main(): import argparse parser = argparse.ArgumentParser(description="Render mapfile template to complete mapfile config.") parser.add_argument("product_yaml_config", help="GeoSphere YAML configuration file. Matches the YAML used by GeoSphere API, " "but not all the information may be used.") parser.add_argument("template_fn", help="Template XML file to render") parser.add_argument('output_pattern', help='Format string for each output mapfiles. Should be per-sector per-processing level.') args = parser.parse_args() # Check if this script was properly sed'd with database information connect_params = _get_connect_str() if "__" not in "__POSTGRES_HOST__" else "" with open(args.product_yaml_config) as product_yaml: geosphere_config = yaml.load(product_yaml, Loader=yaml.SafeLoader) for template_info in template_info_from_config_and_db_info(geosphere_config, connect_params): output_fn = args.output_pattern.format(**template_info) render_mapfile_template_with_template_info(args.template_fn, output_fn, template_info) def template_info_from_config_and_db_info(geosphere_config: dict, connect_params: str): for satellite_info in geosphere_config["satellites"]: if satellites_filter and satellite_info["identifier"] not in satellites_filter: continue for instrument_info in satellite_info["instruments"]: for sector_info in instrument_info["sectors"]: if sectors_filter and sector_info["identifier"] not in sectors_filter: continue projection_info = projection_info_for_id(geosphere_config["projections"], sector_info["projection_id"]) product_infos_groups = per_processing_level_product_infos(sector_info["products"]) for processing_level, product_infos_for_plevel in product_infos_groups: # print(len(sector_info["products"])) # product_infos_for_plevel = list(product_infos_for_plevel) # print(processing_level, product_infos_for_plevel) product_infos = filter_products(product_infos_for_plevel, products_filter) template_info = { 'sector_info': sector_info, 'satellite_info': satellite_info, 'instrument_info': instrument_info, 'processing_level': processing_level, 'products': product_infos, 'layer_base_dir': layer_base_dir, 'projection': f"init=epsg:{projection_info['epsg_code']}", 'epsg_code': str(projection_info["epsg_code"]), 'postgis_connection_params': connect_params, 'postgis_geom_column': 'bbox_geometry', } yield template_info def projection_info_for_id(projection_infos, projection_id): return [proj_info for proj_info in projection_infos if proj_info["identifier"] == projection_id][0] def per_processing_level_product_infos(sector_product_infos): """Split a list of product information dictionaries into groups by ``processing_level``.""" yield from itertools.groupby(sector_product_infos, key=lambda product_info: product_info["processing_level"]) def filter_products(product_infos, product_identifiers_to_keep): if not product_identifiers_to_keep: return product_infos return [product_info for product_info in product_infos if product_info["identifier"] in product_identifiers_to_keep] def render_mapfile_template_with_template_info(template_fn, output_fn, template_info): with open(template_fn, 'r') as tmpl_file, open(output_fn, 'w') as mapfile: template = jinja2.Template(tmpl_file.read()) mapfile.write(template.render(template_info, env=os.environ)) if __name__ == "__main__": sys.exit(main())