Skip to content
Snippets Groups Projects
render.py 5.42 KiB
Newer Older
#!/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 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__":