示例#1
0
def getFrontPage(config):
    """ Returns a front page for the WMS, containing example links """
    doc = StringIO()
    doc.write("<html><head><title>%s</title></head>" % config.title)
    doc.write("<body><h1>%s</h1>" % config.title)
    doc.write("<p><a href=\"" + prefix + "?SERVICE=WMS&REQUEST=GetCapabilities\">Capabilities document</a></p>")
    doc.write("<p><a href=\"./godiva2.html\">Godiva2 interface</a></p>")
    doc.write("<h2>Datasets:</h2>")
    # Print a GetMap link for every dataset we have
    doc.write("<table border=\"1\"><tbody>")
    doc.write("<tr><th>Dataset</th>")
    for format in getmap.getSupportedImageFormats():
        doc.write("<th>%s</th>" % format)
    if config.allowFeatureInfo:
        doc.write("<th>FeatureInfo</th>")
    doc.write("</tr>")
    datasets = config.datasets
    for ds in datasets.keys():
        doc.write("<tr><th>%s</th>" % datasets[ds].title)
        vars = datareader.getVariableMetadata(datasets[ds].location)
        for format in getmap.getSupportedImageFormats():
            doc.write("<td>")
            for varID in vars.keys():
                doc.write("<a href=\"WMS.py?SERVICE=WMS&REQUEST=GetMap&VERSION=1.3.0&STYLES=&CRS=CRS:84&WIDTH=256&HEIGHT=256&FORMAT=%s" % format)
                doc.write("&LAYERS=%s%s%s" % (ds, wmsUtils.getLayerSeparator(), varID))
                bbox = vars[varID].bbox
                doc.write("&BBOX=%s,%s,%s,%s" % tuple([str(b) for b in bbox]))
                if vars[varID].tvalues is not None:
                    doc.write("&TIME=%s" % iso8601.tostring(vars[varID].tvalues[-1]))
                doc.write("\">%s</a><br />" % vars[varID].title)
            doc.write("</td>")
        if config.allowFeatureInfo:
            doc.write("<td>")
            if datasets[ds].queryable:
                for varID in vars.keys():
                    doc.write("<a href=\"WMS.py?SERVICE=WMS&REQUEST=GetFeatureInfo&VERSION=1.3.0&CRS=CRS:84&WIDTH=256&HEIGHT=256&INFO_FORMAT=text/xml")
                    doc.write("&QUERY_LAYERS=%s%s%s" % (ds, wmsUtils.getLayerSeparator(), varID))
                    bbox = vars[varID].bbox
                    doc.write("&BBOX=%s,%s,%s,%s" % tuple([str(b) for b in bbox]))
                    doc.write("&I=128&J=128")
                    if vars[varID].tvalues is not None:
                        doc.write("&TIME=%s" % iso8601.tostring(vars[varID].tvalues[-1]))
                    doc.write("\">%s</a><br />" % vars[varID].title)
            else:
                doc.write("Dataset not queryable")
            doc.write("</td>")
        doc.write("</tr>")
    doc.write("</tbody></table>")
    doc.write("</body></html>")
    s = doc.getvalue()
    doc.close()
    return s
示例#2
0
def getVariables(config, dataset):
    """ returns an HTML table containing a set of variables for the given dataset. """
    str = StringIO()
    str.write("<table cellspacing=\"0\"><tbody>")
    datasets = config.datasets
    vars = datareader.getVariableMetadata(datasets[dataset].location)
    for varID in vars.keys():
        str.write("<tr><td>")
        str.write("<a href=\"#\" onclick=\"javascript:variableSelected('%s', '%s')\">%s</a>" % (dataset, varID, vars[varID].title))
        str.write("</td></tr>")
    str.write("</tbody></table>")
    s = str.getvalue()
    str.close()
    return s
示例#3
0
def getVariableDetails(config, dataset, varID):
    """ returns an XML document containing the details of the given variable
        in the given dataset. """
    str = StringIO()
    datasets = config.datasets
    var = datareader.getVariableMetadata(datasets[dataset].location)[varID]
    str.write("<variableDetails dataset=\"%s\" variable=\"%s\" units=\"%s\">" % (dataset, var.title, var.units))
    str.write("<axes>")
    if var.zvalues is not None:
        str.write("<axis type=\"z\" units=\"%s\" positive=\"%d\">" % (var.zunits, var.zpositive))
        for z in var.zvalues:
            str.write("<value>%f</value>" % math.fabs(z))
        str.write("</axis>")
    str.write("</axes>")
    str.write("<range><min>%f</min><max>%f</max></range>" % (var.validMin, var.validMax))
    str.write("</variableDetails>")
    s = str.getvalue()
    str.close()
    return s
示例#4
0
def getTimesteps(config, dataset, varID, tIndex):
    """ Returns an HTML select box allowing the user to select a 
        set of times for a given day """
    datasets = config.datasets
    # Get an array of time axis values in seconds since the epoch
    tValues = datareader.getVariableMetadata(datasets[dataset].location)[varID].tvalues
    # TODO: is this the right thing to do here?
    if tValues is None:
        return ""
    str = StringIO()
    
    # We find a list of indices of timesteps that are on the same day
    # as the provided tIndex
    indices = {}
    reftime = time.gmtime(tValues[tIndex])
    indices[tIndex] = "%02d:%02d:%02d" % (reftime[3], reftime[4], reftime[5])
    for i in xrange(tIndex + 1, len(tValues)):
        t = time.gmtime(tValues[i])
        diff = _compareDays(reftime, t)
        if diff == 0:
            indices[i] = "%02d:%02d:%02d" % (t[3], t[4], t[5])
        else:
            break
    for i in xrange(tIndex - 1, -1, -1): # count backwards through the axis indices to zero
        t = time.gmtime(tValues[i])
        diff = _compareDays(reftime, t)
        if diff == 0:
            indices[i] = "%02d:%02d:%02d" % (t[3], t[4], t[5])
        else:
            break

    # Write the selection box with the timesteps
    str.write("<select id=\"tValues\" onchange=\"javascript:updateMap()\">")
    keys = indices.keys()
    keys.sort()
    for i in keys:
        str.write("<option value=\"%s\">%s</option>" % (iso8601.tostring(tValues[i]), indices[i]))
    str.write("</select>")

    s = str.getvalue()
    str.close()
    return s
示例#5
0
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
示例#6
0
def getCalendar(config, dataset, varID, dateTime):
    """ returns an HTML calendar for the given dataset and variable.
        dateTime is a string in ISO 8601 format with the required
        'focus time' """
    datasets = config.datasets
    # Get an array of time axis values in seconds since the epoch
    tValues = datareader.getVariableMetadata(datasets[dataset].location)[varID].tvalues
    # TODO: is this the right thing to do here?
    if tValues is None:
        return ""
    str = StringIO()
    prettyDateFormat = "%d %b %Y"

    # Find the closest time step to the given dateTime value
    # TODO: binary search would be more efficient
    reqTime = iso8601.parse(dateTime) # Gives seconds since the epoch
    diff = 1e20
    for i in xrange(len(tValues)):
        testDiff = math.fabs(tValues[i] - reqTime)
        if testDiff < diff:
            # Axis is monotonic so we should move closer and closer
            # to the nearest value
            diff = testDiff
            nearestIndex = i
        elif i > 0:
            # We've moved past the closest date
            break
    
    str.write("<root>")
    str.write("<nearestValue>%s</nearestValue>" % iso8601.tostring(tValues[nearestIndex]))
    str.write("<prettyNearestValue>%s</prettyNearestValue>" % time.strftime(prettyDateFormat, time.gmtime(tValues[nearestIndex])))
    str.write("<nearestIndex>%d</nearestIndex>" % nearestIndex)

    # create a struct_time tuple with zero timezone offset (i.e. GMT)
    nearesttime = time.gmtime(tValues[nearestIndex])

    # Now print out the calendar in HTML
    str.write("<calendar>")
    str.write("<table><tbody>")
    # Add the navigation buttons at the top of the month view
    str.write("<tr>")
    str.write("<td><a href=\"#\" onclick=\"javascript:setCalendar('%s','%s','%s'); return false\">&lt;&lt;</a></td>" % (dataset, varID, _getYearBefore(nearesttime)))
    str.write("<td><a href=\"#\" onclick=\"javascript:setCalendar('%s','%s','%s'); return false\">&lt;</a></td>" % (dataset, varID, _getMonthBefore(nearesttime)))
    str.write("<td colspan=\"3\">%s</td>" % _getHeading(nearesttime))
    str.write("<td><a href=\"#\" onclick=\"javascript:setCalendar('%s','%s','%s'); return false\">&gt;</a></td>" % (dataset, varID, _getMonthAfter(nearesttime)))
    str.write("<td><a href=\"#\" onclick=\"javascript:setCalendar('%s','%s','%s'); return false\">&gt;&gt;</a></td>" % (dataset, varID, _getYearAfter(nearesttime)))
    str.write("</tr>")
    # Add the day-of-week headings
    str.write("<tr><th>M</th><th>T</th><th>W</th><th>T</th><th>F</th><th>S</th><th>S</th></tr>")
    # Add the calendar body
    tValIndex = 0 # index in tvalues array
    for week in calendar.monthcalendar(nearesttime[0], nearesttime[1]):
        str.write("<tr>")
        for day in week:
            if day > 0:
                # Search through the t axis and find out whether we have
                # any data for this particular day
                found = 0
                calendarDay = (nearesttime[0], nearesttime[1], day, 0, 0, 0, 0, 0, 0)
                while not found and tValIndex < len(tValues):
                    axisDay = time.gmtime(tValues[tValIndex])
                    res = _compareDays(axisDay, calendarDay)
                    if res == 0:
                        found = 1 # Found data on this day
                    elif res < 0:
                        tValIndex = tValIndex + 1 # Date on axis is before target day
                    else:
                        break # Date on axis is after target day: no point searching further
                if found:
                    tValue = iso8601.tostring(tValues[tValIndex])
                    prettyTValue = time.strftime(prettyDateFormat, axisDay)
                    str.write("<td id=\"t%d\"><a href=\"#\" onclick=\"javascript:getTimesteps('%s','%s','%d','%s','%s'); return false\">%d</a></td>" % (tValIndex, dataset, varID, tValIndex, tValue, prettyTValue, day))
                else:
                    str.write("<td>%d</td>" % day)
            else:
                str.write("<td></td>")
        str.write("</tr>")

    str.write("</tbody></table>")
    str.write("</calendar>")
    str.write("</root>")

    s = str.getvalue()
    str.close()
    return s
示例#7
0
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&amp;REQUEST=GetMap" %
            (req.server.server_hostname, req.unparsed_uri.split("?")[0]))
        req.write("&amp;VERSION=%s" % wmsUtils.getWMSVersion())
        req.write("&amp;LAYERS=%s" % layers[0])
        if styles == ['']:
            req.write("&amp;STYLES=")
        else:
            req.write("&amp;STYLES=%s" % styles[i])
        # TODO: get the FORMAT string properly
        req.write("&amp;FORMAT=image/png&amp;CRS=CRS:84")
        bboxEls = tuple([str(f) for f in _getBbox(params)])
        req.write("&amp;BBOX=%s,%s,%s,%s" % bboxEls)
        if zValue != "":
            req.write("&amp;ELEVATION=%s" % zValue)
        if tValue != "":
            req.write("&amp;TIME=%s" % tValue)
        # TODO get width and height more intelligently
        req.write("&amp;WIDTH=500&amp;HEIGHT=500")
        if not (scaleMin == 0.0 and scaleMax == 0.0):
            # TODO add an auto-scaled layer
            req.write("&amp;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