示例#1
0
    def _CheckLayers(self, layers_text):
        """Checks if the requested layer is available."""

        logger.debug("Processing the target layer")
        layer_names = layers_text.split(",")

        if 1 < len(layer_names):
            # We only handle a single layer at a time (jeffdonner is not sure
            # what handling multiple ones would mean - compositing them per
            # STYLES perhaps? Sending them in sequence, somehow?)
            logger.warning("Received request for multiple layers. "
                           "We only handle one at a time, the first.")

        # Just check the first, that's all we'll use.
        layer_name = layer_names[0]

        server_layers_by_name = self.layer_obj.GetLayers(
            utils.GetValue(self.parameters, "server-url"),
            utils.GetValue(self.parameters, "TargetPath"))

        self.requested_layer_obj = server_layers_by_name.get(layer_name, None)

        if not self.requested_layer_obj:
            logger.error("Layer %s doesn't exist", layer_name)
            self._ServiceExceptionImpl(
                _LAYER_NOT_DEFINED,
                "No layer matching \'%s\' found" % layer_name)

        # By this point we know it's there.
        logger.debug("Target layer: " + layer_name)
示例#2
0
    def _ExtractLayerObj(self, layer_name):

        server_layers_by_name = self.layer_obj.GetLayers(
            utils.GetValue(self.parameters, "server-url"),
            utils.GetValue(self.parameters, "TargetPath"))
        logger.debug("Server layers: %s ", pformat(server_layers_by_name))

        layer_obj = server_layers_by_name.get(layer_name, None)
        return layer_obj
示例#3
0
    def GenerateOutput(self):
        """Executes the operation and returns the image.

    Returns:
        The image composed of tiles.
    """
        logger.debug("Generating GetMapRequest response "
                     "for WMS request for version 1.3.0")

        # If "TargetPath" query parameter doesn't exist in the
        # input parameters, then send back "ServiceException"
        # to the client.

        target_path = utils.GetValue(self.parameters, "TargetPath")
        if not target_path:
            headers = _HEADERS_EXCEPTION
            response = ServiceException(None,
                                        "Target path is not specified.").Xml()
            return headers, response
        else:
            try:
                self._ProcessResponse()
            except ServiceException, e:
                headers = _HEADERS_EXCEPTION
                return headers, e.Xml()
示例#4
0
    def GenerateOutput(self):
        """Generate response for GetCapabilities request.

    Returns:
      headers: List of headers for the response.
      response: GetCapabilities response.
    """

        logger.debug("Processing GetCapabilities response for WMS v1.3.0")

        # If "TargetPath" query parameter doesn't exist in the
        # input parameters, then send back "ServiceException"
        # to the client.

        target_path = utils.GetValue(self.parameters, "TargetPath")

        if not target_path:
            headers = _HEADERS_EXCEPTION
            response = ServiceException(None,
                                        "Target path is not specified.").Xml()
        else:
            try:
                headers = [
                    ("Content-Disposition",
                     'inline; filename="wmsCapabilities-%s-google.xml"' %
                     _WMS_VERSION), ("Content-Type", _XML_CONTENT_TYPE)
                ]
                response = self._Xml()
            except ServiceException, e:
                headers = _HEADERS_EXCEPTION
                return headers, e.Xml()
示例#5
0
    def IsValidService(self, parameters):
        """Checks if the service is valid.

    Args:
      parameters: All of the query parameters (SERVICE, FORMAT etc), with
        the additions of this-endpoint.
    Returns:
      is_valid_service: If the service is valid.
    msg_to_client: Message sent back to the client.
    """
        logger.debug("Checking if the service is a valid one.")

        is_valid_service = False

        service = utils.GetValue(parameters, "service")
        request_type = utils.GetValue(parameters, "request")

        if service not in _SERVICES:
            if service is None:
                if request_type == "GetCapabilities":
                    # SERVICE parameter is mandatory for GetCapabilities request.
                    logger.error("No service parameter in the WMS request")
                    msg_to_client = ("MISSING SERVICE PARAMETER "
                                     "(expected something like WMS)")
                elif request_type == "GetMap":
                    # SERVICE parameter is optional for GetMap request.
                    is_valid_service = True
                    msg_to_client = "VALID SERVICE PARAMETER RECEIVED"
                else:
                    # Missing or no parameters in the WMS request.
                    logger.error("Invalid WMS request received")
                    msg_to_client = "INVALID WMS REQUEST RECEIVED"
            else:
                logger.error("Unknown service in the WMS request: \'%s\'",
                             service)
                msg_to_client = "BAD SERVICE PARAMETER (expected something like WMS)"
        else:
            is_valid_service = True
            msg_to_client = "VALID SERVICE PARAMETER RECEIVED"
            logger.debug("Valid service parameter received: \'%s\'", service)

        return is_valid_service, msg_to_client
示例#6
0
    def _EnsureRequiredParameters(self):
        """Mechanically produces a ServiceException if a required parm. is missing.

      Checks if the required parameter is available and is non-empty.
    """
        for reqd in self.required_param_names:
            # GIS clients send an empty value for "styles" parameter by default.
            # Empty values for styles parameter should be accepted.
            if reqd == "styles":
                continue
            if utils.GetValue(self.parameters, reqd) is None:
                error_message = "Missing required parameter: \'%s\'" % reqd
                logger.debug(error_message)
                self._ServiceException(None, error_message)
示例#7
0
    def IsValidVersion(self, parameters):
        """Checks if the version is a valid one.

    Args:
      parameters: All of the query parameters (SERVICE, FORMAT etc), with
        the additions of this-endpoint.
    Returns:
      is_valid_version: If the version is valid.
      msg_to_client: Message sent back to the client.
      version_handler: Version handler appriopriate to the WMS version.
    """

        logger.debug("Checking if the WMS version is supported.")

        is_valid_version = False

        version = utils.GetValue(parameters, "version")

        # TODO: Add support for 1.0.7, 1.1.0 and others.
        # The MapServer code has good examples of handling the minor
        # differences between versions.
        if version is None or version.startswith("1.3."):
            # Tolerate 1.3.x for no reason.
            # If there is no version parameter, we're supposed to reply with
            # the highest we support, ie 1.3.0
            version = _VERSION_V130
            # We cheat here - we don't use this version yet in the
            # supposedly common handling, but this lets us.
            parameters["version"] = _VERSION_V130

        version_handler = _SERVICE_HANDLERS.get(version, None)

        if version_handler is None:
            # Have a version, but it's something other than we support.
            # We can't return a ServiceException because we don't know what
            # format to send it in - it changes between versions.
            msg_to_client = "Version %s not supported" % version
            logger.error("WMS Version not supported: \'%s\'", version)
        else:
            is_valid_version = True
            msg_to_client = "Version %s supported" % version
            logger.debug("WMS Version supported: \'%s\'", version)

        return is_valid_version, msg_to_client, version_handler
示例#8
0
    def _CheckParameters(self):
        """Checks if required parameters are available in the request."""
        self._EnsureRequiredParameters()

        # presence is established - now we're looking in more detail.
        lite_checkers = {
            # version already checked by this point
            # request already checked by this point
            "layers": self._CheckLayers,
            "styles": self._CheckStyles,
            "crs": self._CheckCrs,
            "srs": self._CheckSrs,
            "bbox": self._CheckBbox,
            "width": self._CheckWidth,
            "height": self._CheckHeight,
            "format": self._CheckFormat
        }

        for name, checker in lite_checkers.iteritems():
            parameter_value = utils.GetValue(self.parameters, name)
            checker(parameter_value)
示例#9
0
    def GetRequestHandler(self, parameters, version_handler):
        """Get the request handler appropriate to the WMS request.

    Args:
      parameters: All of the query parameters (SERVICE, FORMAT etc), with
        the additions of 'this-endpoint'.
      version_handler: Version handler appropriate to the WMS version.
    Returns:
      req_hdlr_obj: Request handler.
    """
        req_hdlr_obj = None

        request_name = utils.GetValue(parameters, "request")
        request_handler = version_handler.get(request_name, None)

        if request_handler is None:
            req_hdlr_obj = version_handler["bad_service_handler"](request_name)
        else:
            req_hdlr_obj = request_handler(self.layer_obj, parameters)

        logger.debug("Type of WMS request: %s", type(req_hdlr_obj))

        return req_hdlr_obj
示例#10
0
    def SetLayers(self):
        """Set the layers for the capabilities xml."""

        # This outer, inaccessible layer is to give information just once;
        # the sub-layers inherit it.
        outer_layer = capabilities_wms.Layer(
            # 7.2.4.7.4 The layer is area-filling => opaque
            opaque=True,
            # 7.2.4.7.5 -- we can subset.
            noSubsets=False,
            # whether we support GetFeatureInfo
            queryable=False,
            # -- can't request it, this is just a container.
            Name=None,
            Title=_TITLE)

        server_layers_by_name = self.layer_obj.GetLayers(
            utils.GetValue(self.parameters, "server-url"),
            utils.GetValue(self.parameters, "TargetPath"))

        if not server_layers_by_name:
            # Raise ServiceException here.
            raise ServiceException(None, "Database type is not supported.")

        for layer_name, server_layer in server_layers_by_name.iteritems():
            proj = server_layer.projection
            wms_layer = capabilities_wms.Layer(
                # 7.2.4.7.4 - Even for vector maps we always get data from
                # the server, even if it's just a transparent tile.  By
                # jeffdonner's reading, this means that even the vector
                # layers are 'opaque'.
                opaque=True,
                # 7.2.4.7.5 - we can subset.
                noSubsets=False,
                queryable=False,
                Name=layer_name,
                Title=server_layer.label,
                # ex geo bounding box is required.
                EX_GeographicBoundingBox=capabilities_wms.
                EX_GeographicBoundingBox(
                    westBoundLongitude=-proj.MAX_LONGITUDE,
                    eastBoundLongitude=proj.MAX_LONGITUDE,
                    southBoundLatitude=-proj.MAX_LATITUDE,
                    northBoundLatitude=proj.MAX_LATITUDE))

            for epsg_name in proj.EPSG_NAMES:
                wms_layer.add_CRS(epsg_name)

            map_limits = proj.AdvertizedLogOuterBounds()
            bounding_boxes = []

            for epsg_name in proj.EPSG_NAMES:
                (min_x, min_y, max_x,
                 max_y) = self._GetMapLimitsForEpsg(map_limits, epsg_name)
                bounding_box_object = capabilities_wms.BoundingBox(
                    CRS=epsg_name,
                    minx=min_x,
                    miny=min_y,
                    maxx=max_x,
                    maxy=max_y)

                bounding_boxes.append(bounding_box_object)

            wms_layer.set_BoundingBox(bounding_boxes)

            outer_layer.add_Layer(wms_layer)

        self.capabilities_xml.get_Capability().set_Layer(outer_layer)
示例#11
0
  def SetLayers(self):
    """Set the layers for the capabilities xml."""

    # This outer, inaccessible layer is to give information just once;
    # the sub-layers inherit it.
    outer_layer = capabilities_wms.Layer(
        # If this is a containing layer it should be opaque=0
        opaque=0,
        cascaded=None,
        fixedHeight=None,
        fixedWidth=None,
        # 7.1.4.6.4 - we obviously can subset.
        noSubsets=0,
        queryable=0,
        # This is a containing layer.
        Name=None,
        Title=capabilities_wms.Title(_TITLE))

    server_layers_by_name = self.layer_obj.GetLayers(
        utils.GetValue(self.parameters, "server-url"),
        utils.GetValue(self.parameters, "TargetPath"))

    if not server_layers_by_name:
      # Raise ServiceException here.
      raise ServiceException(None, "Database type is not supported.")

    for layer_name, server_layer in server_layers_by_name.iteritems():
      wms_layer = capabilities_wms.Layer(
          # 7.1.4.6.3 - Even for vector maps we always get data from
          # the server, even if it's just a transparent tile.  By
          # jeffdonner's reading, this means that even the vector
          # layers are 'opaque'.
          opaque=1,
          cascaded=0,
          fixedHeight=None,
          fixedWidth=None,
          # We can subset.
          noSubsets=0,
          queryable=0,
          Name=layer_name,
          Title=capabilities_wms.Title(server_layer.label))

      proj = server_layer.projection
      for epsg_name in proj.EPSG_NAMES:
        wms_layer.add_SRS(capabilities_wms.SRS(epsg_name))

      wms_layer.set_LatLonBoundingBox(
          capabilities_wms.LatLonBoundingBox(
              minx=-proj.MAX_LONGITUDE,
              miny=-proj.MAX_LATITUDE,
              maxx=proj.MAX_LONGITUDE,
              maxy=proj.MAX_LATITUDE))
      map_limits = server_layer.projection.AdvertizedLogOuterBounds()
      wms_layer.set_BoundingBox([
          capabilities_wms.BoundingBox(
              SRS=epsg_name,
              minx=map_limits.x0,
              miny=map_limits.y0,
              maxx=map_limits.x1,
              maxy=map_limits.y1)
          for epsg_name in proj.EPSG_NAMES])

      outer_layer.add_Layer(wms_layer)

    self.capabilities_xml.get_Capability().set_Layer(outer_layer)
示例#12
0
    def GenerateOutputCommon(self):
        """Produces the WMS bitmap image. Used by both versions.

    Returns:
        The image composed of tiles.
    """
        logger.debug("Generating the bitmap image")

        image_format = utils.GetValue(self.parameters, "format")
        image_spec = image_specs.GetImageSpec(image_format)

        # TRANSPARENT parameter from GIS client's.
        # It can take "TRUE"/"FALSE" values.
        # Default value is "FALSE" as per spec.

        # GIS clients either send TRANSPARENT(=TRUE) attribute or don't send it
        # at all, if it's not required.
        # If this parameter is absent, GetValue() method returns None.
        # We default it to "FALSE" if this parameter is not available in the
        # GIS client requests.
        is_transparent = utils.GetValue(self.parameters, "transparent")
        if not is_transparent:
            is_transparent = "FALSE"

        # BGCOLOR parameter is a string that specifies the colour to be
        # used as the background (non-data) pixels of the map.
        # The format is 0xRRGGBB.The default value is 0xFFFFFF
        bgcolor = utils.GetValue(self.parameters, "bgcolor")
        if bgcolor:
            # Convert HEX string to python tuple.
            # Ignore the 0x at the beginning of the hex string.
            # Otherwise str.decode("hex") will throw
            # "TypeError: Non-hexadecimal digit found" error.
            if bgcolor[:2] == "0x":
                bgcolor = bgcolor[2:]
            bgcolor = tuple(ord(c) for c in bgcolor.decode("hex"))
        else:
            bgcolor = tiles.ALL_WHITE_PIXELS

        # Add the user requested image format, transparency
        # and bgcolor to the layer python object.
        self.requested_layer_obj.image_format = image_format
        self.requested_layer_obj.is_transparent = is_transparent
        self.requested_layer_obj.bgcolor = bgcolor

        im_user = tiles.ProduceImage(
            self.requested_layer_obj,
            # There's a weird border artifact that MapServer shows when we
            # ask for more than 360 laterally, and which natively we show
            # fine. It's probably not us, but there's room for doubt.  If
            # it was our calculations, it's likely the error would be most
            # extreme for a large final image, and few tiles.
            self.user_log_rect,
            self.user_width,
            self.user_height)

        buf = StringIO.StringIO()
        im_user.save(buf, image_spec.pil_format, **im_user.info)

        logger.debug("Image content type is :%s", image_spec.content_type)

        headers = [("Content-Type", image_spec.content_type)]

        logger.debug("Done generating the bitmap image")

        return headers, buf.getvalue()
示例#13
0
    def GenerateOutputCommon(self):
        """Produces the WMS bitmap image. Used by both versions.

    Returns:
        The image composed of tiles.
    """
        logger.debug("Generating the bitmap image")

        image_format = utils.GetValue(self.parameters, "format")
        image_spec = image_specs.GetImageSpec(image_format)
        logger.info(
            "WMS Request image_format: %s image_spec: %s will be applied to all layers.",
            image_format, image_spec)
        # TRANSPARENT parameter from GIS client's.
        # It can take "TRUE"/"FALSE" values.
        # Default value is "FALSE" as per spec.

        # GIS clients either send TRANSPARENT(=TRUE) attribute or don't send it
        # at all, if it's not required.
        # If this parameter is absent, GetValue() method returns None.
        # We default it to "FALSE" if this parameter is not available in the
        # GIS client requests.
        is_transparent = utils.GetValue(self.parameters, "transparent")
        if not is_transparent:
            is_transparent = "FALSE"

        # BGCOLOR parameter is a string that specifies the colour to be
        # used as the background (non-data) pixels of the map.
        # The format is 0xRRGGBB.The default value is 0xFFFFFF
        bgcolor = utils.GetValue(self.parameters, "bgcolor")
        if bgcolor:
            # Convert HEX string to python tuple.
            # Ignore the 0x at the beginning of the hex string.
            # Otherwise str.decode("hex") will throw
            # "TypeError: Non-hexadecimal digit found" error.
            if bgcolor[:2] == "0x":
                bgcolor = bgcolor[2:]
            bgcolor = tuple(ord(c) for c in bgcolor.decode("hex"))
        else:
            bgcolor = tiles.ALL_WHITE_PIXELS
        layer_names = self._ExtractLayerNames(
            utils.GetValue(self.parameters, 'layers'))
        logger.info("Loop through layers: %s ", (str(layer_names)))
        composite_image = None
        for layer_name in layer_names:
            # Add the user requested image format, transparency
            # and bgcolor to the layer python object.
            layer_obj = self._ExtractLayerObj(layer_name)
            logger.debug("Processing a layer:  %s   \n%s ", layer_name,
                         pformat(layer_obj))
            layer_obj.image_format = image_format
            layer_obj.is_transparent = is_transparent
            layer_obj.bgcolor = bgcolor
            # We'll assume that all WMS layers will have the same image_spec and projection
            # for
            im_user = tiles.ProduceImage(
                layer_obj,
                # There's a weird border artifact that MapServer shows when we
                # ask for more than 360 laterally, and which natively we show
                # fine. It's probably not us, but there's room for doubt.  If
                # it was our calculations, it's likely the error would be most
                # extreme for a large final image, and few tiles.
                self.user_log_rect,
                self.user_width,
                self.user_height)
            im_user = im_user.convert("RGBA")
            if composite_image is None:
                composite_image = im_user
            else:
                logger.debug("Adding layer %s to composite image...",
                             layer_name)
                composite_image.paste(im_user, (0, 0), im_user)
            buf = StringIO.StringIO()
            output_format = image_spec.pil_format
            composite_image.save(buf, image_spec.pil_format, **im_user.info)
        headers = [("Content-Type", image_spec.content_type)]
        return headers, buf.getvalue()