OGC Context 1.0.0 compliance Test Suite
Introduction
This test suite is based on the following specifications
- OWS Context ATOM Encoding, version 1.0.0 [OGC 12-084]
- The Atom Syndication Format ( RFC4287)
- GEORSS ( OGC 06-050r3)
The schemas and rules were defined in Relax NG Compact Syntax (RNC) covering all the three specifications.
RELAX NG is a simple schema language for XML, based on [ RELAX] and [ TREX]. A RELAX NG schema specifies a pattern for the structure and content of an XML document. A RELAX NG schema thus identifies a class of XML documents consisting of those documents that match the pattern. A RELAX NG schema can be defined with two syntaxes (XML or on a non-XML Compact Syntax).
As the original Atom specification was defined in the Relax NG Compact Syntax it was decided to define the OWS Context ATOM Encoding following the same approach. This page reports the several schema implementation decisions and approaches to define the OWS classes to comply with the conformance classes.
The rnc and rng files are available at
A.1 Conformance Test Class: core
Fully Implements the Rules of Atom and OWC Context Classes
OWC:Context
The owc:Context defines a new set of elements on the atom:feed by allowing the presence new foreign elements of the dc, owc and georss namespaces.
# Define date time interval
datetimeInterval= xsd:string { pattern ="[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(\.[0-9]+)?(Z|[\+\-][0-9]{2}:[0-9]{2})?(/[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(\.[0-9]+)?(Z|[\+\-][0-9]{2}:[0-9]{2})|)"}
# Redefine atom:feed to include new owc and dc elements
atomFeedConstruct &=
owcDisplay?
& element dc:publisher { text } ?
& element dc:date { datetimeInterval }?
& georssWhere ?
And imposes a new rule for the atom:feed by stating that it needs to have a atom:category with @scheme='http://www.opengis.net/spec/owc/specReference'
s:rule [ context = "atom:feed"
s:assert [ test =
"atom:category[@scheme='http://www.opengis.net/spec/owc/specReference']"
"An atom:feed must have an atom:category with the OWC "
~ "specification reference identified with the "
~ " @scheme='http://www.opengis.net/spec/owc/specReference' "]]
]
OWC:Resource
The owc:Resource defines a new set of elements on the atom:feed by allowing the presence new foreign elements of the dc, owc and georss namespaces.
# Redefine atom:entry to include new owc and dc elements
atomEntryConstruct &=
owcOffering?
& element dc:publisher { text }?
& element dc:date { atomDateConstruct }?
& georssWhere ?
& element owc:minScaleDenominator { xsd:float }?
& element owc:maxScaleDenominator { xsd:float }?
And imposes a new rule on this element that it must have a human readable content element
s:rule [ context = "atom:entry"
s:assert [ test = "atom:content"
"An atom:entry must have one atom:content MUST have content "
~ "element in a format understandable by generic Atom readers"
~ " ( @type equal to 'html' is recommended)"]]
OWC:Offering
# owcOffering
owcOffering =
element owc:offering {
owcCommonAttributes,
attribute code { text },
( owcOperation*
& owcContent*
& owcStyleSet*
& extensionElement*)
}
OWC:Operation
The OWC:Operation contains always a code and href attribute. The first identifies the type of operation while the latter defines the HTTP endpoint.
Depending on the type of the HTTP verb defined on the method attribute (GET or POST) the element might have, respectively a owc:result or a owc:result and a owc:payload
# owcOperation
owcOperation =
element owc:operation {
owcCommonAttributes,
attribute code { text },
attribute href { owcURL },
attribute type { text }?,
( ( attribute method {"GET"} &
element owc:result{( owcContentConstruct )}?
& extensionElement* ) |
( attribute method {"POST"} &
element owc:result{( owcContentConstruct )}?
& element owc:request {( owcContentConstruct )}?
& extensionElement* ) )
}
OWC:Content
The OWC:Content class contain any type of text or XML. Or inline or by reference but it cannot have both.
# owcContent
owcContentConstruct =
owcCommonAttributes &
( ( attribute type { text },
attribute href { owcURL }) |
( attribute type { text },
( text | anyElement)* ) )
owcContent = element owc:content { owcContentConstruct }
OWC:StyleSet
owcStyleSet = element owc:styleSet { owcCommonAttributes,
attribute default { "0" | "1" } ?,
(element owc:name { text }
& element owc:title { text }
& element owc:abstract { text }?
& element owc:legendURL {
attribute type { text },
attribute href { owcHTTPURL }
}?
& owcContent?
& extensionElement*)
}
OWC:Display
# owc:display
owcDisplay = element owc:display {
(element owc:pixelWidth { xsd:integer }?
& element owc:pixelHeight { xsd:integer }?
& element owc:mmPerPixel { xsd:float }?)
& extensionElement*
}
Atom Syndication Format
The OWC Specification requires a new set of Atom foreign elements that we need now to define them as NOT foreign.
# Replacing definitions on RFC4287
include "rfc4287.rnc"{
# Redefine the Simple Extension to exclude owc:* elements
simpleExtensionElement = element * -
(atom:* | owc:* | dc:publisher | dc:date | georss:where) { text }
# Redefine the Structured Extension to exclude owc:* elements
structuredExtensionElement = element * -
(atom:* | owc:* | dc:publisher | dc:date | georss:where) {
(attribute * { text }+,(text|anyElement)*)
| (attribute * { text }*, (text?, anyElement+, (text|anyElement)*))}
}
GEORSS
The OWC:Context uses the georss:where element to express the spatial dimension of the Context document.
The original GEORSS specification was defined in XSD so it was necessary to define a Relax NG schema for this element
namespace georss="http://www.georss.org/georss"
namespace gml="http://www.opengis.net/gml"
georssWhere = element georss:where { gmlElement }
gmlElement = (gmlPointElement | gmlLineStringElement | gmlPolygonElement | gmlEnvelopeElement )
gmlPointElement =
element gml:Point {
element gml:pos { list { xsd:decimal, xsd:decimal }}
}
gmlLineStringElement =
element gml:LineString {
element gml:posList { list { (xsd:decimal, xsd:decimal)+ }}
}
gmlPolygonElement =
element gml:Polygon {
element gml:exterior {
element gml:LinearRing {
element gml:posList { list { (xsd:decimal, xsd:decimal)+ }}}}
}
gmlEnvelopeElement =
element gml:Envelope {
element gml:lowerCorner { list { xsd:decimal, xsd:decimal } }
& element gml:upperCorner { list { xsd:decimal, xsd:decimal } }
}
Validation Tools
There is a public repository for schema validation and conversion based on RELAX NG available at https://code.google.com/p/jing-trang/
There is simple test script in https://svn.opengeospatial.org/ogc-projects/cite/scripts/owsc/1.0/trunk/test.sh that executes
for ex in `ls web/examples/*.xml`
do java -jar resources/lib/relames/relames.jar resources/relaxng/owc.rng $ex
done
trang
trang converts between different schema languages for XML. It supports the following languages:
A schema written in any of the supported schema languages can be converted into any of the other supported schema languages, except that W3C? XML Schema is supported for output only, not for input.
To convert the Relax NG Compact syntax to Relax NG XML:
java -jar ../lib/trang/trang.jar -I rnc -O rng ../relaxng/owc.rnc ../relaxng/owc.rng
relames
The Sun Multi-Schema XML Validator Schematron add-on is a Java tool to validate XML documents against RELAX NG schemas annotated with
Schematron schemas. This tool supports embed Schematron constraints into RELAX NG schemas and it makes it easy to write many constraints which are difficult to achieve by RELAX NG alone.
To validate Context Documents with relames , enter the following command:
java -jar ../lib/relames/relames.jar ../relaxng/owc.rng ../../web/examples/meris_borders_users.atom
jing
This version of Jing implements
Jing also has experimental support for schema languages other than RELAX NG; specifically
To validate Context Documents with jing enter the following command:
java -jar ../lib/jing/bin/jing.jar -c ../relaxng/owc.rnc ../../web/examples/meris_borders_users.atom
Creation Tools
ows2owc.xsl
The ows2owc XSL Transformation file will make a OGC Context Document or Context Resource in Atom Encoding from a OGC Web Services GetCapabilities? document.
It currently supports:
- Web Map Specification (WMS) 1.1.1 and 1.3.0
- Web Feature Specification (WFS) 1.1.0
It accepts the following parameters
- now : Parameter with the current or desired update date to insert on the atom:updated element (Mandatory)
- bbox : Restrict Context file to a specific BBOX in the format: minlon, minlat, maxlon, maxlat (Optional)
- entry : Restrict Context file to a given layer or feature (Optional). If not present the entire Capabilities document will be processed
- iconheight : Height of the preview image (Optional) for WMS. Default value is 100
- mapheight : Height of the map image (Optional) for WMS. Default value is 500
- mode : The processing mode (Optional)
- if equal to 'feed' it will produce a valid ATOM feed (default)
- if equal to 'fragment' it will only produce the entry with the feature or layer. It must be used with the entry parameter.
Example:
xsltproc --stringparam entry "topp:member_map"
--stringparam now "`date +%Y-%m-%dT%H:%M:%S`"
--stringparam mode "fragment"
ows2owc.xsl
'http://meet.opengeospatial.org:8080/geoserver/ows?service=WFS&request=GetCapabilities'
creates the following file:
<entry xmlns="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:georss="http://www.georss.org/georss" xmlns:gml="http://www.opengis.net/gml" xmlns:owc="http://www.opengis.net/owc/1.0/" xmlns:ows="http://www.opengis.net/ows">
<id>http://meet.opengeospatial.org:8080/geoserver/wfstopp:member_map/</id>
<title>OGC members</title>
<author><name>Raj Singh</name></author>
<dc:publisher>Open Geospatial Consortium</dc:publisher>
<updated>2012-12-20T17:01:57Z</updated>
<dc:rights>Fee:NONE / Contraints:NONE</dc:rights>
<georss:where>
<gml:Polygon><gml:exterior><gml:LinearRing>
<gml:posList>-41.302 -157.862 -41.302 174.805 60.671 174.805 60.671 -157.862 -41.302 -157.862</gml:posList>
</gml:LinearRing></gml:exterior></gml:Polygon>
</georss:where>
<link rel="enclosure" type="text/xml; subtype=gml/3.1.1" title="WFS output for OGC members" href="http://meet.opengeospatial.org:8080/geoserver/wfs?SERVICE=WFS&VERSION=1.1.0&REQUEST=GetFeature&BBOX=-41.302,-157.862,60.671,174.805&OUTPUTFORMAT=text/xml; subtype=gml/3.1.1&TYPENAME=topp:member_map&MAXFEATURES=10"/>
<link rel="describedby" type="text/xml" title="Description of Features" href="http://meet.opengeospatial.org:8080/geoserver/wfs?SERVICE=WFS&VERSION=1.1.0&REQUEST=describeFeatureType&TYPENAME=topp:member_map"/>
<link rel="via" type="text/xml" title="Original GetCapabilities document" href="http://meet.opengeospatial.org:8080/geoserver/wfs?SERVICE=WFS&VERSION=1.1.0&REQUEST=GetCapabilities"/>
<content type="html">
<br/>
This resource is available from a OGC WFS Service (version 1.1.0) and it contains the following access points:
<ul><li>
<a href='http://meet.opengeospatial.org:8080/geoserver/wfs?SERVICE=WFS&VERSION=1.1.0&REQUEST=GetFeature&BBOX=-41.302,-157.862,60.671,174.805&OUTPUTFORMAT=text/xml; subtype=gml/3.1.1&TYPENAME=topp:member_map&MAXFEATURES=10'>
GetFeature </a> request in text/xml; subtype=gml/3.1.1 (atom:link[@rel="enclosure"])
</li> <li>
<a href='http://meet.opengeospatial.org:8080/geoserver/wfs?SERVICE=WFS&VERSION=1.1.0&REQUEST=GetCapabilities'>
GetCapabilities </a> request (atom:link[@rel="via"])
</li> </li> <li>
<a href='http://meet.opengeospatial.org:8080/geoserver/wfs?SERVICE=WFS&VERSION=1.1.0&REQUEST=describeFeatureType&TYPENAME=topp:member_map'>
Describe Feature </a> request for topp:member_map (atom:link[@rel="describedby"])
</li></ul>
<p style='font-size:small'>OGC Context CITE Testing XSLT (Extensible Stylesheet Language Transformations) by Terradue Srl.</p>
</content>
<owc:offering code="http://www.opengis.net/spec/owc/1.0/req/atom/wfs">
<owc:operation method="GET" code="GetCapabilities" href="http://meet.opengeospatial.org:8080/geoserver/wfs?SERVICE=WFS&VERSION=1.1.0&REQUEST=GetCapabilities"/>
<owc:operation method="GET" code="GetFeature" href="http://meet.opengeospatial.org:8080/geoserver/wfs?SERVICE=WFS&VERSION=1.1.0&REQUEST=GetFeature&BBOX=-41.302,-157.862,60.671,174.805&OUTPUTFORMAT=text/xml; subtype=gml/3.1.1&TYPENAME=topp:member_map&MAXFEATURES=10"/>
</owc:offering>
</entry>
This transformation file work by going for each layer or feature and creates a new Atom entry with a owc:offering element
<xsl:template match="wms:Layer | Layer | wfs:FeatureType">
<entry>
...
<owc:offering>
<xsl:attribute name="code"><xsl:value-of select="$offering_code"/></xsl:attribute>
<owc:operation method="GET" code="GetCapabilities">
<xsl:attribute name="href">
<xsl:value-of select="$get_capabilities_request"/>
</xsl:attribute>
</owc:operation>
<owc:operation method="GET">
<xsl:attribute name="code"><xsl:value-of select="$default_operation"/></xsl:attribute>
<xsl:attribute name="href"><xsl:value-of select="$get_request"/></xsl:attribute>
</owc:operation>
<xsl:for-each select="wms:Style | Style">
<owc:styleSet>
<owc:name><xsl:value-of select="wms:Name | Name"/></owc:name>
<owc:title><xsl:value-of select="wms:Title | Title"/></owc:title>
<owc:abstract><xsl:value-of select="wms:Abstract | Abstract"/></owc:abstract>
<owc:legendURL>
<xsl:attribute name="href">
<xsl:value-of select="wms:LegendURL/wms:OnlineResource/@xlink:href | LegendURL/OnlineResource/@xlink:href"/>
</xsl:attribute>
<xsl:attribute name="type">
<xsl:value-of select="wms:LegendURL/wms:Format | LegendURL/Format"/>
</xsl:attribute>
</owc:legendURL>
</owc:styleSet>
</xsl:for-each>
</owc:offering>
...
</entry>
To define the service GetCapabilities? request first it is necessary to obtain the service name, version and online resource for the serivce
<xsl:variable name="service_name"><xsl:choose>
<xsl:when test="/wfs:WFS_Capabilities">WFS</xsl:when>
<xsl:when test="/wms:WMS_Capabilities | /WMT_MS_Capabilities">WMS</xsl:when>
<xsl:otherwise>UNKNOWN</xsl:otherwise></xsl:choose>
</xsl:variable>
<xsl:variable name="version" select="/*/@version"/>
<xsl:variable name="capabilities_online_resource"
select="/wms:WMS_Capabilities/wms:Capability/wms:Request/wms:GetCapabilities/wms:DCPType/wms:HTTP/wms:Get/wms:OnlineResource/@xlink:href |
/WMT_MS_Capabilities/Capability/Request/GetCapabilities/DCPType/HTTP/Get/OnlineResource/@xlink:href |
/*/ows:OperationsMetadata/ows:Operation[@name='GetCapabilities']/ows:DCP/ows:HTTP/ows:Get/@xlink:href"/>
<xsl:variable name="get_capabilities_request">
<xsl:value-of select="$capabilities_online_resource"/>
<xsl:if test="substring-before($capabilities_online_resource,'?')=''">?</xsl:if>
<xsl:value-of select="concat('SERVICE=',$service_name,'&VERSION=',$version,'&REQUEST=GetCapabilities')"/>
</xsl:variable>
To define the entry's bounding box, the XSLT file will look for the layer's bounding box element value or value of its ancestors bounding box element. This way it solves the issue of nested layers in WMS or when spatial is defined only at the top most layer.
<xsl:variable name="maxX">
<xsl:choose>
<xsl:when test="$bbox!=''"><xsl:value-of select="$coords/*[3]"/></xsl:when>
<xsl:when test="ows:WGS84BoundingBox/ows:UpperCorner">
<xsl:value-of select="substring-before(ows:WGS84BoundingBox/ows:UpperCorner,' ')"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="ancestor-or-self::wms:Layer/wms:EX_GeographicBoundingBox/wms:eastBoundLongitude | ancestor-or-self::Layer/LatLonBoundingBox/@maxx"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="maxY">
<xsl:choose>
<xsl:when test="$bbox!=''"><xsl:value-of select="$coords/*[4]"/></xsl:when>
<xsl:when test="ows:WGS84BoundingBox/ows:UpperCorner">
<xsl:value-of select="substring-after(ows:WGS84BoundingBox/ows:UpperCorner,' ')"/>
</xsl:when>
<xsl:otherwise><xsl:value-of select="ancestor-or-self::wms:Layer/wms:EX_GeographicBoundingBox/wms:northBoundLatitude | ancestor-or-self::Layer/LatLonBoundingBox/@maxy"/></xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="minX">
<xsl:choose>
<xsl:when test="$bbox!=''"><xsl:value-of select="$coords/*[1]"/></xsl:when>
<xsl:when test="ows:WGS84BoundingBox/ows:LowerCorner">
<xsl:value-of select="substring-before(ows:WGS84BoundingBox/ows:LowerCorner,' ')"/>
</xsl:when>
<xsl:otherwise><xsl:value-of select="ancestor-or-self::wms:Layer/wms:EX_GeographicBoundingBox/wms:westBoundLongitude | ancestor-or-self::Layer/LatLonBoundingBox/@minx"/></xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="minY">
<xsl:choose>
<xsl:when test="$bbox!=''"><xsl:value-of select="$coords/*[2]"/></xsl:when>
<xsl:when test="ows:WGS84BoundingBox/ows:LowerCorner">
<xsl:value-of select="substring-after(ows:WGS84BoundingBox/ows:LowerCorner,' ')"/>
</xsl:when>
<xsl:otherwise><xsl:value-of select="ancestor-or-self::wms:Layer/wms:EX_GeographicBoundingBox/wms:southBoundLatitude | ancestor-or-self::Layer/LatLonBoundingBox/@miny"/></xsl:otherwise>
</xsl:choose>
</xsl:variable>
It uses then these values to define the entry's GEORSS element
<georss:where>
<gml:Polygon>
<gml:exterior>
<gml:LinearRing>
<gml:posList>
<xsl:value-of select="concat($minY,' ',$minX,' ',$minY,' ',$maxX,' ',$maxY,' ',$maxX,' ',$maxY,' ',$minX,' ',$minY,' ',$minX)"/>
</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</georss:where>
These values are also used to define the bbox request by checking the CRS code to use
<!-- preference for Plate Carre on element -->
<!-- if no crs available then check parent -->
<xsl:variable name="crs">
<xsl:choose>
<xsl:when test="count(wms:CRS[.='EPSG:4326'] | SRS[.='EPSG:4326'])!=0">EPSG:4326</xsl:when>
<xsl:when test="count(wms:CRS[.='CRS:84'] | SRS[.='CRS:84'])!=0">CRS:84</xsl:when>
<xsl:when test="count(wms:CRS[1] | SRS[1])!=0"><xsl:value-of select="wms:CRS[1] | SRS[1]"/></xsl:when>
<xsl:when test="count(ancestor::wms:Layer/wms:CRS[.='EPSG:4326'] | ancestor::Layer/SRS[.='EPSG:4326'])!=0">EPSG:4326</xsl:when>
<xsl:when test="count(ancestor::wms:Layer/wms:CRS[.='CRS:84'] | ancestor::Layer/SRS[.='CRS:84'])!=0">CRS:84</xsl:when>
<xsl:otherwise><xsl:value-of select="ancestor::wms:Layer/wms:CRS[1] | ancestor::Layer/SRS[1]"/></xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="bbox">
<xsl:choose>
<xsl:when test="($service_name='WMS' and $version='1.3.0' and $crs='EPSG:4326') or /wfs:WFS_Capabilities">
<xsl:value-of select="concat($minY,',',$minX,',',$maxY,',',$maxX)"/></xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat($minX,',',$minY,',',$maxX,',',$maxY)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
To create the request it is necessary to join all those parameters
<xsl:variable name="get_request">
<xsl:value-of select="$operation_online_resource"/><xsl:if test="substring-before($operation_online_resource,'?')=''">?</xsl:if><xsl:value-of select="concat('SERVICE=',$service_name, '&VERSION=',$version,'&REQUEST=',$default_operation,'&BBOX=',$bbox)"/><xsl:choose>
<xsl:when test="$service_name='WFS'"><xsl:value-of select="concat('&OUTPUTFORMAT=',$data_format,'&TYPENAME=',$name,'&MAXFEATURES=10')"/>
</xsl:when>
<xsl:when test="$service_name='WMS'"><xsl:value-of select="concat( $crsName, '=',$crs,'&WIDTH=',floor($map_height * $georatio),'&HEIGHT=',$map_height,'&LAYERS=',$name,'&FORMAT=',$data_format,'&BGCOLOR=0xffffff&TRANSPARENT=TRUE&EXCEPTIONS=',$exception_format)"/>
</xsl:when>
<xsl:otherwise/>
</xsl:choose>
</xsl:variable>
Access the full XSLT transformation in the trax github site.
#EOF