def write(self, f): """ writes this exception as XML using the f.write() method """ f.write('<?xml version="1.0" encoding="UTF-8"?>\n') f.write('<ServiceExceptionReport version="' + wmsUtils.getWMSVersion() + '"') f.write(' xmlns="http://www.opengis.net/ogc"') f.write(' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"') f.write( ' xsi:schemaLocation="http://www.opengis.net/ogc http://schemas.opengis.net/wms/1.3.0/exceptions_1_3_0.xsd">' ) f.write("<ServiceException") if self.code: f.write(' code="' + self.code + '"') f.write(">") if self.message: # Replace quotation marks with XML escape code f.write(self.message.replace('"', """)) f.write("</ServiceException>") f.write("</ServiceExceptionReport>")
def getCapabilities(req, params, config, lastUpdateTime): """ Returns the Capabilities document. req = mod_python request object or WMS.FakeModPythonRequest object params = wmsUtils.RequestParser object containing the request parameters config = ConfigParser object containing configuration info for this WMS lastUpdateTime = time at which cache of data and metadata was last updated """ version = params.getParamValue("version", "") format = params.getParamValue("format", "") # TODO: deal with version and format # Check the UPDATESEQUENCE (used for cache consistency) updatesequence = params.getParamValue("updatesequence", "") if updatesequence != "": try: us = iso8601.parse(updatesequence) if round(us) == round(lastUpdateTime): # Equal to the nearest second raise CurrentUpdateSequence(updatesequence) elif us > lastUpdateTime: raise InvalidUpdateSequence(updatesequence) except ValueError: # Client didn't supply a valid ISO8601 date # According to the spec, InvalidUpdateSequence is not the # right error code here so we use a generic exception raise WMSException("UPDATESEQUENCE must be a valid ISO8601 date") output = StringIO() output.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n") output.write("<WMS_Capabilities version=\"" + wmsUtils.getWMSVersion() + "\"") # UpdateSequence is accurate to the nearest second output.write(" updateSequence=\"%s\"" % iso8601.tostring(round(lastUpdateTime))) output.write(" xmlns=\"http://www.opengis.net/wms\"") output.write(" xmlns:xlink=\"http://www.w3.org/1999/xlink\"") # The next two lines should be commented out if you wish to load this document # in Cadcorp SIS from behind the University of Reading firewall output.write(" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"") output.write(" xsi:schemaLocation=\"http://www.opengis.net/wms http://schemas.opengis.net/wms/1.3.0/capabilities_1_3_0.xsd\"") output.write(">") output.write("<Service>") output.write("<Name>WMS</Name>") output.write("<Title>%s</Title>" % config.title) output.write("<Abstract>%s</Abstract>" % config.abstract) output.write("<KeywordList>") for keyword in config.keywords: output.write("<Keyword>%s</Keyword>" % keyword) output.write("</KeywordList>") output.write("<OnlineResource xlink:type=\"simple\" xlink:href=\"%s\"/>" % config.url) output.write("<ContactInformation>") output.write("<ContactPersonPrimary>") output.write("<ContactPerson>%s</ContactPerson>" % config.contactName) output.write("<ContactOrganization>%s</ContactOrganization>" % config.contactOrg) output.write("</ContactPersonPrimary>") output.write("<ContactVoiceTelephone>%s</ContactVoiceTelephone>" % config.contactTel) output.write("<ContactElectronicMailAddress>%s</ContactElectronicMailAddress>" % config.contactEmail) output.write("</ContactInformation>") output.write("<Fees>none</Fees>") output.write("<AccessConstraints>none</AccessConstraints>") output.write("<LayerLimit>%d</LayerLimit>" % getmap.getLayerLimit()) output.write("<MaxWidth>%d</MaxWidth>" % config.maxImageWidth) output.write("<MaxHeight>%d</MaxHeight>" % config.maxImageHeight) output.write("</Service>") output.write("<Capability>") output.write("<Request>") output.write("<GetCapabilities>") output.write("<Format>text/xml</Format>") url = "http://%s%s?" % (req.server.server_hostname, req.unparsed_uri.split("?")[0]) output.write("<DCPType><HTTP><Get><OnlineResource xlink:type=\"simple\" xlink:href=\"" + url + "\"/></Get></HTTP></DCPType>") output.write("</GetCapabilities>") output.write("<GetMap>") for format in getmap.getSupportedImageFormats(): output.write("<Format>%s</Format>" % format) output.write("<DCPType><HTTP><Get><OnlineResource xlink:type=\"simple\" xlink:href=\"" + url + "\"/></Get></HTTP></DCPType>") output.write("</GetMap>") if config.allowFeatureInfo: output.write("<GetFeatureInfo>") for format in getfeatureinfo.getSupportedFormats(): output.write("<Format>%s</Format>" % format) output.write("<DCPType><HTTP><Get><OnlineResource xlink:type=\"simple\" xlink:href=\"" + url + "\"/></Get></HTTP></DCPType>") output.write("</GetFeatureInfo>") output.write("</Request>") # TODO: support more exception types output.write("<Exception>") for ex_format in getmap.getSupportedExceptionFormats(): output.write("<Format>%s</Format>" % ex_format) output.write("</Exception>") # Write the top-level container layer output.write("<Layer>") output.write("<Title>%s</Title>" % config.title) # TODO: add styles for crs in grids.getSupportedCRSs().keys(): output.write("<CRS>" + crs + "</CRS>") # Now for the dataset layers datasets = config.datasets for dsid in datasets.keys(): # Write a container layer for this dataset. Container layers # do not have a Name output.write("<Layer>") output.write("<Title>%s</Title>" % datasets[dsid].title) # Now write the displayable data layers vars = datareader.getVariableMetadata(datasets[dsid].location) for vid in vars.keys(): output.write("<Layer") if config.allowFeatureInfo and datasets[dsid].queryable: output.write(" queryable=\"1\"") output.write(">") output.write("<Name>%s%s%s</Name>" % (dsid, wmsUtils.getLayerSeparator(), vid)) output.write("<Title>%s</Title>" % vars[vid].title) output.write("<Abstract>%s</Abstract>" % vars[vid].abstract) # Set the bounding box minLon, minLat, maxLon, maxLat = vars[vid].bbox output.write("<EX_GeographicBoundingBox>") output.write("<westBoundLongitude>%s</westBoundLongitude>" % str(minLon)) output.write("<eastBoundLongitude>%s</eastBoundLongitude>" % str(maxLon)) output.write("<southBoundLatitude>%s</southBoundLatitude>" % str(minLat)) output.write("<northBoundLatitude>%s</northBoundLatitude>" % str(maxLat)) output.write("</EX_GeographicBoundingBox>") output.write("<BoundingBox CRS=\"CRS:84\" ") output.write("minx=\"%f\" maxx=\"%f\" miny=\"%f\" maxy=\"%f\"/>" % (minLon, maxLon, minLat, maxLat)) # Set the level dimension if vars[vid].zvalues is not None: output.write("<Dimension name=\"elevation\" units=\"%s\"" % vars[vid].zunits) # Use the first value in the array as the default # If the default value is removed, you also need to edit # the data reading code (e.g. DataReader.java) to # disallow default z values output.write(" default=\"%s\">" % vars[vid].zvalues[0]) firstTime = 1 for z in vars[vid].zvalues: if firstTime: firstTime = 0 else: output.write(",") output.write(str(z)) output.write("</Dimension>") # Set the time dimension if vars[vid].tvalues is not None: output.write("<Dimension name=\"time\" units=\"ISO8601\">") # If we change this to support the "current" attribute # we must also change the data reading code firstTime = 1 for t in vars[vid].tvalues: if firstTime: firstTime = 0 else: output.write(",") output.write(iso8601.tostring(t)) output.write("</Dimension>") output.write("</Layer>") # end of variable Layer output.write("</Layer>") # end of dataset layer output.write("</Layer>") # end of top-level container layer output.write("</Capability>") output.write("</WMS_Capabilities>") req.content_type="text/xml" req.write(output.getvalue()) output.close() # Free the buffer return
def getMap(req, params, config): """ The GetMap operation. req = mod_python request object (or FakeModPythonRequestObject from Jython servlet) params = wmsUtils.RequestParser object containing the request parameters config = configuration object """ _checkVersion(params) # Checks the VERSION parameter layers = params.getParamValue("layers").split(",") if len(layers) > getLayerLimit(): raise WMSException("You may only request a maximum of " + str(getLayerLimit()) + " layer(s) simultaneously from this server") styles = params.getParamValue("styles").split(",") # We must either have one style per layer or else an empty parameter: "STYLES=" if len(styles) != len(layers) and styles != ['']: raise WMSException("You must request exactly one STYLE per layer, or use" + " the default style for each layer with STYLES=") for style in styles: if style != "": # TODO: handle styles properly raise StyleNotDefined(style) # RequestParser replaces pluses with spaces: we must change back # to parse the format correctly format = params.getParamValue("format").replace(" ", "+") if format not in getSupportedImageFormats(): raise InvalidFormat("image", format, "GetMap") exception_format = params.getParamValue("exceptions", "XML") if exception_format not in getSupportedExceptionFormats(): raise InvalidFormat("exception", exception_format, "GetMap") zValue = params.getParamValue("elevation", "") if len(zValue.split(",")) > 1 or len(zValue.split("/")) > 1: raise WMSException("You may only request a single value of ELEVATION") tValue = params.getParamValue("time", "") if len(tValue.split(",")) > 1 or len(tValue.split("/")) > 1: # TODO: support animations raise WMSException("You may only request a single value of TIME") # Get the requested transparency and background colour for the layer trans = params.getParamValue("transparent", "false").lower() if trans == "false": transparent = 0 elif trans == "true": transparent = 1 else: raise WMSException("The value of TRANSPARENT must be \"TRUE\" or \"FALSE\"") bgc = params.getParamValue("bgcolor", "0xFFFFFF") if len(bgc) != 8 or not bgc.startswith("0x"): raise WMSException("Invalid format for BGCOLOR") try: bgcolor = eval(bgc) # Parses hex string into an integer except: raise WMSException("Invalid format for BGCOLOR") # Get the extremes of the colour scale scaleMin, scaleMax = _getScale(params) # Get the percentage opacity of the map layer: another WMS extension opa = params.getParamValue("opacity", "100") try: opacity = int(opa) except: raise WMSException("The OPACITY parameter must be a valid number in the range 0 to 100 inclusive") if opacity < 0 or opacity > 100: raise WMSException("The OPACITY parameter must be a valid number in the range 0 to 100 inclusive") # Find the source of the requested data location, varID, queryable = _getLocationAndVariableID(layers, config.datasets) if format == _getGoogleEarthFormat(): # This is a special case: we don't actually render the image, # we just return a KML document containing a link to the image # Set a suggested filename in the header # "inline" means "don't force a download dialog box in web browser" req.headers_out["Content-Disposition"] = "inline; filename=%s.kml" % layers[0].replace("/", "_") req.content_type = format req.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n") req.write("<kml xmlns=\"http://earth.google.com/kml/2.0\">") req.write("<Folder>") req.write("<visibility>1</visibility>") req.write("<GroundOverlay>") # Get the variable metadata vars = datareader.getVariableMetadata(location) req.write("<name>%s</name>" % vars[varID].title) req.write("<description>%s</description>" % vars[varID].abstract) req.write("<visibility>1</visibility>") req.write("<Icon><href>") req.write("http://%s%s?SERVICE=WMS&REQUEST=GetMap" % (req.server.server_hostname, req.unparsed_uri.split("?")[0])) req.write("&VERSION=%s" % wmsUtils.getWMSVersion()) req.write("&LAYERS=%s" % layers[0]) if styles == ['']: req.write("&STYLES=") else: req.write("&STYLES=%s" % styles[i]) # TODO: get the FORMAT string properly req.write("&FORMAT=image/png&CRS=CRS:84") bboxEls = tuple([str(f) for f in _getBbox(params)]) req.write("&BBOX=%s,%s,%s,%s" % bboxEls) if zValue != "": req.write("&ELEVATION=%s" % zValue) if tValue != "": req.write("&TIME=%s" % tValue) # TODO get width and height more intelligently req.write("&WIDTH=500&HEIGHT=500") if not (scaleMin == 0.0 and scaleMax == 0.0): # TODO add an auto-scaled layer req.write("&SCALE=%s,%s" % (scaleMin, scaleMax)) req.write("</href></Icon>") req.write("<LatLonBox id=\"1\">") req.write("<west>%s</west><south>%s</south><east>%s</east><north>%s</north>" % bboxEls) req.write("<rotation>0</rotation>") req.write("</LatLonBox>") req.write("</GroundOverlay>") req.write("</Folder>") req.write("</kml>") else: # Generate a grid of lon,lat points, one for each image pixel grid = _getGrid(params, config) # Read the data for the image picData = datareader.readImageData(location, varID, tValue, zValue, grid, _getFillValue()) # TODO: cache the data array # Turn the data into an image and output to the client graphics.makePic(req, format, picData, grid.width, grid.height, _getFillValue(), transparent, bgcolor, opacity, scaleMin, scaleMax) return
def _checkVersion(params): """ Checks that the VERSION parameter exists and is correct """ version = params.getParamValue("version") if version != wmsUtils.getWMSVersion(): raise WMSException("VERSION must be %s" % wmsUtils.getWMSVersion())