Beispiel #1
0
 def test_parse_iso_datetime(self):
     assert utils.parse_iso_datetime("2009-05-28T16:15:00") == datetime.datetime(2009, 5, 28, 16, 15)
Beispiel #2
0
    def produce_plot(self, query, mode):
        """
        Handler for a GetMap and GetVSec requests. Produces a plot with
        the parameters specified in the URL.

        # TODO: Handle multiple layers. (mr, 2010-06-09)
        # TODO: Cache the produced images: Check whether an image with the given
        #      parameters has already been produced. (mr, 2010-08-18)
        """
        logging.debug("GetMap/GetVSec request. Interpreting parameters..")

        # Evaluate query parameters:
        # =============================

        version = query.get("VERSION", "1.1.1")

        # Image size.
        width = query.get('WIDTH', 900)
        height = query.get('HEIGHT', 600)
        figsize = float(width if width != "" else 900), float(
            height if height != "" else 600)
        logging.debug("  requested image size = %sx%s", figsize[0], figsize[1])

        # Requested layers.
        layers = [
            layer for layer in query.get('LAYERS', '').strip().split(',')
            if layer
        ]
        layer = layers[0] if len(layers) > 0 else ''
        if layer.find(".") > 0:
            dataset, layer = layer.split(".")
        else:
            dataset = None
        logging.debug("  requested dataset = '%s', layer = '%s'", dataset,
                      layer)

        # Requested style(s).
        styles = [
            style
            for style in query.get('STYLES', 'default').strip().split(',')
            if style
        ]
        style = styles[0] if len(styles) > 0 else None
        logging.debug("  requested style = '%s'", style)

        # Forecast initialisation time.
        init_time = query.get('DIM_INIT_TIME')
        if init_time is not None:
            try:
                init_time = parse_iso_datetime(init_time)
            except ValueError:
                return self.create_service_exception(
                    code="InvalidDimensionValue",
                    text=
                    "DIM_INIT_TIME has wrong format (needs to be 2005-08-29T13:00:00Z)",
                    version=version)
        logging.debug("  requested initialisation time = '%s'", init_time)

        # Forecast valid time.
        valid_time = query.get('TIME')
        if valid_time is not None:
            try:
                valid_time = parse_iso_datetime(valid_time)
            except ValueError:
                return self.create_service_exception(
                    code="InvalidDimensionValue",
                    text=
                    "TIME has wrong format (needs to be 2005-08-29T13:00:00Z)",
                    version=version)
        logging.debug("  requested (valid) time = '%s'", valid_time)

        # Coordinate reference system.
        crs = query.get("CRS" if version == "1.3.0" else "SRS",
                        'EPSG:4326').lower()
        is_yx = version == "1.3.0" and crs.startswith("epsg") and int(
            crs[5:]) in axisorder_yx

        # Allow to request vertical sections via GetMap, if the specified CRS is of type "VERT:??".
        msg = None
        if crs.startswith('vert:logp'):
            mode = "getvsec"
        else:
            try:
                get_projection_params(crs)
            except ValueError:
                return self.create_service_exception(
                    code="InvalidSRS",
                    text=f"The requested CRS '{crs}' is not supported.",
                    version=version)
        logging.debug("  requested coordinate reference system = '%s'", crs)

        # Create a frameless figure (WMS) or one with title and legend
        # (MSS specific)? Default is WMS mode (frameless).
        noframe = query.get('FRAME', 'off').lower() == 'off'

        # Transparency.
        transparent = query.get('TRANSPARENT', 'false').lower() == 'true'
        if transparent:
            logging.debug("  requested transparent image")

        # Return format (image/png, text/xml, etc.).
        return_format = query.get('FORMAT', 'image/png').lower()
        logging.debug("  requested return format = '%s'", return_format)
        if return_format not in ["image/png", "text/xml"]:
            return self.create_service_exception(
                code="InvalidFORMAT",
                text=f"unsupported FORMAT: '{return_format}'",
                version=version)

        # 3) Check GetMap/GetVSec-specific parameters and produce
        #    the image with the corresponding section driver.
        # =======================================================
        if mode == "getmap":
            # Check requested layer.
            if (dataset not in self.hsec_layer_registry) or (
                    layer not in self.hsec_layer_registry[dataset]):
                return self.create_service_exception(
                    code="LayerNotDefined",
                    text=f"Invalid LAYER '{dataset}.{layer}' requested",
                    version=version)

            # Check if the layer requires time information and if they are given.
            if self.hsec_layer_registry[dataset][
                    layer].uses_inittime_dimension() and init_time is None:
                return self.create_service_exception(
                    code="MissingDimensionValue",
                    text=
                    "INIT_TIME not specified (use the DIM_INIT_TIME keyword)",
                    version=version)
            if self.hsec_layer_registry[dataset][
                    layer].uses_validtime_dimension() and valid_time is None:
                return self.create_service_exception(
                    code="MissingDimensionValue",
                    text="TIME not specified",
                    version=version)

            # Check if the requested coordinate system is supported.
            if not self.hsec_layer_registry[dataset][layer].support_epsg_code(
                    crs):
                return self.create_service_exception(
                    code="InvalidSRS",
                    text=f"The requested CRS '{crs}' is not supported.",
                    version=version)

            # Bounding box.
            try:
                if is_yx:
                    bbox = [
                        float(v) for v in query.get(
                            'BBOX', '-90,-180,90,180').split(',')
                    ]
                    bbox = (bbox[1], bbox[0], bbox[3], bbox[2])
                else:
                    bbox = [
                        float(v) for v in query.get(
                            'BBOX', '-180,-90,180,90').split(',')
                    ]

            except ValueError:
                return self.create_service_exception(
                    text=f"Invalid BBOX: {query.get('BBOX')}", version=version)

            # Vertical level, if applicable.
            level = query.get('ELEVATION')
            level = float(level) if level is not None else None
            layer_datatypes = self.hsec_layer_registry[dataset][
                layer].required_datatypes()
            if any(_x in layer_datatypes
                   for _x in ["pl", "al", "ml", "tl", "pv"]) and level is None:
                # Use the default value.
                level = -1
            elif ("sfc" in layer_datatypes) and \
                    all(_x not in layer_datatypes for _x in ["pl", "al", "ml", "tl", "pv"]) and \
                    level is not None:
                return self.create_service_exception(
                    text=
                    f"ELEVATION argument not applicable for layer '{layer}'. Please omit this argument.",
                    version=version)

            plot_driver = self.hsec_drivers[dataset]
            try:
                plot_driver.set_plot_parameters(
                    self.hsec_layer_registry[dataset][layer],
                    bbox=bbox,
                    level=level,
                    crs=crs,
                    init_time=init_time,
                    valid_time=valid_time,
                    style=style,
                    figsize=figsize,
                    noframe=noframe,
                    transparent=transparent,
                    return_format=return_format)
                image = plot_driver.plot()
            except (IOError, ValueError) as ex:
                logging.error("ERROR: %s %s", type(ex), ex)
                logging.debug("%s", traceback.format_exc())
                msg = "The data corresponding to your request is not available. Please check the " \
                      "times and/or levels you have specified.\n\n" \
                      f"Error message: '{ex}'"
                return self.create_service_exception(text=msg, version=version)

        elif mode == "getvsec":
            # Vertical secton path.
            path = query.get("PATH")
            if path is None:
                return self.create_service_exception(text="PATH not specified",
                                                     version=version)
            try:
                path = [float(v) for v in path.split(',')]
                path = [[lat, lon] for lat, lon in zip(path[0::2], path[1::2])]
            except ValueError:
                return self.create_service_exception(
                    text=f"Invalid PATH: {path}", version=version)
            logging.debug("VSEC PATH: %s", path)

            # Check requested layers.
            if (dataset not in self.vsec_layer_registry) or (
                    layer not in self.vsec_layer_registry[dataset]):
                return self.create_service_exception(
                    code="LayerNotDefined",
                    text=f"Invalid LAYER '{dataset}.{layer}' requested",
                    version=version)

            # Check if the layer requires time information and if they are given.
            if self.vsec_layer_registry[dataset][
                    layer].uses_inittime_dimension():
                if init_time is None:
                    return self.create_service_exception(
                        code="MissingDimensionValue",
                        text=
                        "INIT_TIME not specified (use the DIM_INIT_TIME keyword)",
                        version=version)
                if valid_time is None:
                    return self.create_service_exception(
                        code="MissingDimensionValue",
                        text="TIME not specified",
                        version=version)

            # Bounding box (num interp. points, p_bot, num labels, p_top).
            try:
                bbox = [
                    float(v)
                    for v in query.get("BBOX", "101,1050,10,180").split(",")
                ]
            except ValueError:
                return self.create_service_exception(
                    text=f"Invalid BBOX: {query.get('BBOX')}", version=version)

            plot_driver = self.vsec_drivers[dataset]
            try:
                plot_driver.set_plot_parameters(
                    plot_object=self.vsec_layer_registry[dataset][layer],
                    vsec_path=path,
                    vsec_numpoints=bbox[0],
                    vsec_path_connection="greatcircle",
                    vsec_numlabels=bbox[2],
                    init_time=init_time,
                    valid_time=valid_time,
                    style=style,
                    bbox=bbox,
                    figsize=figsize,
                    noframe=noframe,
                    transparent=transparent,
                    return_format=return_format)
                image = plot_driver.plot()
            except (IOError, ValueError) as ex:
                logging.error("ERROR: %s %s", type(ex), ex)
                msg = "The data corresponding to your request is not available. Please check the " \
                      "times and/or path you have specified.\n\n" \
                      f"Error message: {ex}"
                return self.create_service_exception(text=msg, version=version)

        # 4) Return the produced image.
        # =============================
        return image, return_format