OGC Context 1.0.0 compliance Test Suite

Introduction

This test suite is based on the following specifications 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: It accepts the following parameters 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&amp;VERSION=1.1.0&amp;REQUEST=GetFeature&amp;BBOX=-41.302,-157.862,60.671,174.805&amp;OUTPUTFORMAT=text/xml; subtype=gml/3.1.1&amp;TYPENAME=topp:member_map&amp;MAXFEATURES=10"/>
  <link rel="describedby" type="text/xml" title="Description of Features" href="http://meet.opengeospatial.org:8080/geoserver/wfs?SERVICE=WFS&amp;VERSION=1.1.0&amp;REQUEST=describeFeatureType&amp;TYPENAME=topp:member_map"/>
  <link rel="via" type="text/xml" title="Original GetCapabilities document" href="http://meet.opengeospatial.org:8080/geoserver/wfs?SERVICE=WFS&amp;VERSION=1.1.0&amp;REQUEST=GetCapabilities"/>
  <content type="html">
    &lt;br/&gt;
    This resource is available from a OGC WFS Service (version 1.1.0) and it contains the following access points:
    &lt;ul&gt;&lt;li&gt;
    &lt;a href='http://meet.opengeospatial.org:8080/geoserver/wfs?SERVICE=WFS&amp;VERSION=1.1.0&amp;REQUEST=GetFeature&amp;BBOX=-41.302,-157.862,60.671,174.805&amp;OUTPUTFORMAT=text/xml; subtype=gml/3.1.1&amp;TYPENAME=topp:member_map&amp;MAXFEATURES=10'&gt;
    GetFeature &lt;/a&gt; request in text/xml; subtype=gml/3.1.1 (atom:link[@rel="enclosure"])
    &lt;/li&gt; &lt;li&gt;
    &lt;a href='http://meet.opengeospatial.org:8080/geoserver/wfs?SERVICE=WFS&amp;VERSION=1.1.0&amp;REQUEST=GetCapabilities'&gt;
    GetCapabilities &lt;/a&gt; request (atom:link[@rel="via"])
    &lt;/li&gt; &lt;/li&gt; &lt;li&gt;
    &lt;a href='http://meet.opengeospatial.org:8080/geoserver/wfs?SERVICE=WFS&amp;VERSION=1.1.0&amp;REQUEST=describeFeatureType&amp;TYPENAME=topp:member_map'&gt;
    Describe Feature &lt;/a&gt; request for topp:member_map (atom:link[@rel="describedby"])
    &lt;/li&gt;&lt;/ul&gt;
    
    &lt;p style='font-size:small'&gt;OGC Context CITE Testing XSLT (Extensible Stylesheet Language Transformations) by Terradue Srl.&lt;/p&gt;
    </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&amp;VERSION=1.1.0&amp;REQUEST=GetCapabilities"/>
    <owc:operation method="GET" code="GetFeature" href="http://meet.opengeospatial.org:8080/geoserver/wfs?SERVICE=WFS&amp;VERSION=1.1.0&amp;REQUEST=GetFeature&amp;BBOX=-41.302,-157.862,60.671,174.805&amp;OUTPUTFORMAT=text/xml; subtype=gml/3.1.1&amp;TYPENAME=topp:member_map&amp;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,'&amp;VERSION=',$version,'&amp;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, '&amp;VERSION=',$version,'&amp;REQUEST=',$default_operation,'&amp;BBOX=',$bbox)"/><xsl:choose>
            <xsl:when test="$service_name='WFS'"><xsl:value-of select="concat('&amp;OUTPUTFORMAT=',$data_format,'&amp;TYPENAME=',$name,'&amp;MAXFEATURES=10')"/>
            </xsl:when>
            <xsl:when test="$service_name='WMS'"><xsl:value-of select="concat( $crsName, '=',$crs,'&amp;WIDTH=',floor($map_height * $georatio),'&amp;HEIGHT=',$map_height,'&amp;LAYERS=',$name,'&amp;FORMAT=',$data_format,'&amp;BGCOLOR=0xffffff&amp;TRANSPARENT=TRUE&amp;EXCEPTIONS=',$exception_format)"/>
            </xsl:when>
            <xsl:otherwise/>
        </xsl:choose>
    </xsl:variable>

Access the full XSLT transformation in the trax github site.

#EOF