DescribeSensor/SensorML client access tests for IOOS SOS Milestone 1, using the AOOS 52North SOS endpoint

Access the AOOS SOS GetCapabilities, issue a sample DescribeSensor request, then parse parts of the response. Includes examples of testing the validity of controlled vocabulary terms vs MMI (using the approach described on this IPython notebook). This SOS test complements the one for GetObservations). 2013/11/4


In [60]:
from datetime import datetime
import urllib2
import json
import requests
from owslib.sos import SensorObservationService
from owslib.swe.sensor.sml import SensorML, EventMetadata

In [61]:
# AOOS SOS Tests (GetCap extracted from the AOOS 52N SOS Test Client)
aoossos_gc_url = "http://sos.aoos.org/sos/sos/kvp?service=SOS&request=GetCapabilities&AcceptVersions=1.0.0"
aoosxml = urllib2.urlopen(aoossos_gc_url).read()
aoosgc = SensorObservationService(None, xml=aoosxml)

In [62]:
# find wmo 46083 ndbc buoy offering, from the offerings list
ndbcfind = [offering for offering in aoosgc.offerings if '46083' in offering.name.lower()]
aoosoff1 = ndbcfind[0]
print aoosoff1.id, aoosoff1.name, aoosoff1.description
print aoosoff1.procedures


urn_ioos_station_wmo_46083 urn:ioos:station:wmo:46083 None
['urn:ioos:station:wmo:46083']

Interactions specific to DescribeSensor start here


In [63]:
descsen = aoosgc.get_operation_by_name('describesensor')
descsen.parameters


Out[63]:
{'outputFormat': {'values': ['text/xml; subtype="sensorML/1.0.1"',
   'text/xml; subtype="sensorML/1.0.1/profiles/ioos_sos/1.0"']}}

In [64]:
# Set parameters to be used in the DescribeSensor request
oFrmt = 'text/xml;subtype="sensorML/1.0.1/profiles/ioos_sos/1.0"'
procedure = aoosoff1.procedures[0]

In [65]:
response = aoosgc.describe_sensor(procedure=procedure, outputFormat=oFrmt)
print type(response), len(response)


<type 'str'> 32302

In [66]:
aoosoff1_sml_root = SensorML(response)

To see the structure of the IOOS SOS Milestone 1 SensorML response, see the reference implementation XML at http://code.google.com/p/ioostech/source/browse/trunk/templates/Milestone1.0/SML-DescribeSensor-Station.xml


In [67]:
print type(aoosoff1_sml_root), type(aoosoff1_sml_root.systems), len(aoosoff1_sml_root.systems)
aoosoff1_sml = aoosoff1_sml_root.systems[0]
print type(aoosoff1_sml)


<class 'owslib.swe.sensor.sml.SensorML'> <type 'list'> 1
<class 'owslib.swe.sensor.sml.SystemMetadata'>

In [68]:
print aoosoff1_sml.id, " || ", aoosoff1_sml.description
print "*** IDENTIFIERS:\n", aoosoff1_sml.identifiers
print "*** CLASSIFIERS:\n", aoosoff1_sml.classifiers
print "*** CONTACTS:\n", aoosoff1_sml.contacts


None  ||  Fairweather Grounds 92NM Southeast of Yakutat, AK
*** IDENTIFIERS:
{'stationID': <owslib.swe.sensor.sml.IdentifierMetadata object at 0x333b2d0>, 'shortName': <owslib.swe.sensor.sml.IdentifierMetadata object at 0x333b250>, 'uniqueID': <owslib.swe.sensor.sml.IdentifierMetadata object at 0x333b3d0>, 'longName': <owslib.swe.sensor.sml.IdentifierMetadata object at 0x333b350>}
*** CLASSIFIERS:
{'operatorSector': <owslib.swe.sensor.sml.ClassifierMetadata object at 0x333b550>, 'publisher': <owslib.swe.sensor.sml.ClassifierMetadata object at 0x333b4d0>, 'parentNetwork': <owslib.swe.sensor.sml.ClassifierMetadata object at 0x333b610>, 'platformType': <owslib.swe.sensor.sml.ClassifierMetadata object at 0x333b510>, 'sponsor': <owslib.swe.sensor.sml.ClassifierMetadata object at 0x333b5d0>}
*** CONTACTS:
{None: <owslib.swe.sensor.sml.ContactMetadata object at 0x333b310>}

Explore SensorML Identifiers


In [69]:
aoosoff1_sml.identifiers.keys()


Out[69]:
['stationID', 'shortName', 'uniqueID', 'longName']

In [70]:
print "stationID identifier information:"
print "   OWSLib object type:  ", type(aoosoff1_sml.identifiers['stationID'])
print "   name: ", aoosoff1_sml.identifiers['stationID'].name
print "   definition: ", aoosoff1_sml.identifiers['stationID'].definition
print "   value: ", aoosoff1_sml.identifiers['stationID'].value


stationID identifier information:
   OWSLib object type:   <class 'owslib.swe.sensor.sml.IdentifierMetadata'>
   name:  stationID
   definition:  http://mmisw.org/ont/ioos/definition/stationID
   value:  urn:ioos:station:wmo:46083

Explore SensorML Classifiers


In [71]:
aoosoff1_sml.classifiers.keys()


Out[71]:
['operatorSector', 'publisher', 'parentNetwork', 'platformType', 'sponsor']

In [72]:
def print_classifier_info(sml, classifier_name):
    classifier = sml.classifiers[classifier_name]
    print "%s classifier information:" % classifier.name
    print "   OWSLib object type:  ", type(classifier)
    print "   codespace: ", classifier.codespace
    print "   name: ", classifier.name
    print "   definition: ", classifier.definition
    print "   value: ", classifier.value
    return classifier

In [73]:
platformType = print_classifier_info(aoosoff1_sml, 'platformType')


platformType classifier information:
   OWSLib object type:   <class 'owslib.swe.sensor.sml.ClassifierMetadata'>
   codespace:  http://mmisw.org/ont/ioos/platform
   name:  platformType
   definition:  http://mmisw.org/ont/ioos/definition/platformType
   value:  FIXED MET STATION

In [74]:
publisher = print_classifier_info(aoosoff1_sml, 'publisher')


publisher classifier information:
   OWSLib object type:   <class 'owslib.swe.sensor.sml.ClassifierMetadata'>
   codespace:  http://mmisw.org/ont/ioos/organization
   name:  publisher
   definition:  http://mmisw.org/ont/ioos/definition/publisher
   value:  NDBC

In [75]:
operatorSector = print_classifier_info(aoosoff1_sml, 'operatorSector')


operatorSector classifier information:
   OWSLib object type:   <class 'owslib.swe.sensor.sml.ClassifierMetadata'>
   codespace:  http://mmisw.org/ont/ioos/sector
   name:  operatorSector
   definition:  http://mmisw.org/ont/ioos/definition/operatorSector
   value:  gov_federal

Test the validity of classifiers against MMI vocabularies

The MMI term testing function is taken (and illustrated) from my notebook at http://nbviewer.ipython.org/6514804


In [76]:
def get_term_ontmeta_jsonurl(termjsonurl):
    """ 
    Pass a complete URL for the json response corresponding to an ontology term.
    It can be from any ontology engine as long as the json is similar to MMI's.
    eg: 'http://mmisw.org/ont/cf/parameter/wind_speed.json'
    Parses the json response to return a regularized dictionary as well as the
    raw json response loaded as a dict.
    """
    
    # This is probably not applicable to other ontology repos beyond MMI...
    param_jsonstr = requests.get(termjsonurl).content
    # Validate the term URL
    if param_jsonstr == '[]':  # term URL does not exist; term is invalid
        return (None, None)
    
    #param_jsonstr = urllib2.urlopen(termjsonurl).read()    
    param_json = json.loads(param_jsonstr)
    param_extract_all_dct = dict([(param_el[1].split('/')[-1][:-1], param_el[2]) 
                                  for param_el in param_json])
    
    # Add handling of more IOOS vocabularies, as needed
    if 'cf/parameter' in termjsonurl:
        param_extract_dct = {'vocabulary': 'cf/parameter',
                             'definition': param_extract_all_dct['core#definition'],
                             'units':      param_extract_all_dct['canonical_units'],
                             'units_type': 'canonical units', 
                            }
    elif 'ioos/parameter' in termjsonurl:
        param_extract_dct = {'vocabulary': 'ioos/parameter',
                             'definition': param_extract_all_dct['Definition'],
                             'units':      param_extract_all_dct['Units'], 
                             'units_type': 'example or common units', 
                            }
    else:
        param_extract_dct = param_extract_all_dct
    
    return (param_extract_all_dct, param_extract_dct)

In [77]:
# Test for a term known to be valid
# Note that get_term_ontmeta_jsonurl() currently is tuned to do special extra handling
# only with "parameter" (observed properties) terms
term_url_json = operatorSector.codespace + '/' + operatorSector.value + '.json' 
print term_url_json
get_term_ontmeta_jsonurl(term_url_json)


http://mmisw.org/ont/ioos/sector/gov_federal.json
Out[77]:
({u'22-rdf-syntax-ns#type': u'<http://mmisw.org/ont/ioos/sector/Organization>',
  u'description': u'"Government-Federal"',
  u'name': u'"Government-Federal"',
  u'rdf-schema#label': u'"gov_federal"',
  u'term': u'"gov_federal"'},
 {u'22-rdf-syntax-ns#type': u'<http://mmisw.org/ont/ioos/sector/Organization>',
  u'description': u'"Government-Federal"',
  u'name': u'"Government-Federal"',
  u'rdf-schema#label': u'"gov_federal"',
  u'term': u'"gov_federal"'})

In [78]:
# Test for a term known to be invalid (doesn't exist on MMI vocabulary)
term_url_json = platformType.codespace + '/' + platformType.value + '.json' 
print term_url_json
get_term_ontmeta_jsonurl(term_url_json)


http://mmisw.org/ont/ioos/platform/FIXED MET STATION.json
Out[78]:
(None, None)

Explore SensorML components (including 'parameters')


In [79]:
# Looks like the sensorml doesn't have any "component" entries 
# (sensors / observed properties / parameters) yet ...
print type(aoosoff1_sml.components), len(aoosoff1_sml.components)


<type 'dict'> 0