def test_sos_20_bom_gov_au():
    # Setup
    service = SensorObservationService(SERVICE_URL, version='2.0.0')
    id = service.identification
    # Check basic service metadata
    assert id.service == 'SOS'
    assert id.title == 'KISTERS KiWIS SOS2'
    assert id.keywords == []
    assert service.provider.name == 'Provider Name'
    assert service.provider.contact.name == 'Name'
    assert service.provider.contact.position is None
    assert len(service.operations) == 5
    assert service.get_operation_by_name('GetObservation').methods[0]['url'] == \
        'http://bom.gov.au/waterdata/services?datasource=0'
    response = service.get_observation(
        featureOfInterest='http://bom.gov.au/waterdata/services/stations/181.1',
        offerings=['http://bom.gov.au/waterdata/services/tstypes/Pat4_PC_1'],
        observedProperties=['http://bom.gov.au/waterdata/services/parameters/Water Course Discharge'],
        eventTime='om:phenomenonTime,2016-01-01T00:00:00+10/2016-03-05T00:00:00+10')
    # Process WaterML Response
    from owslib.etree import etree
    from owslib.swe.observation.sos200 import SOSGetObservationResponse
    from owslib.swe.observation.waterml2 import MeasurementTimeseriesObservation

    et = etree.fromstring(response)
    parsed_response = SOSGetObservationResponse(et)
    assert len(parsed_response.observations) > 0
    for o in parsed_response.observations:
        assert isinstance(o, MeasurementTimeseriesObservation)
Example #2
0
def sos_observation_xml(url, version='1.0.0', xml=None, offerings=[], 
                        responseFormat=None, observedProperties=[], 
                        eventTime=None, feature=None, allProperties=False):
    """Return the XML from a SOS GetObservation request.

    Parameters
    ----------
    url : string
        Full HTTP address of SOS
    version: string
        Version number of the SOS (e.g. 1.0.0)
    offerings : list
        selected offerings from SOS; defaults to all available
    responseFormat : string
        desire format for result data 
    observedProperties : list
        filters results for selected properties from SOS; defaults to first one
        (unless allProperties is True)
    eventTime : string
        filters results for a specified instant or period.
        Use ISO format YYYY-MM-DDTHH:mm:ss+-HH  Periods of time (start and end) 
        are separated by "/"; e.g. 2009-06-26T10:00:00+01/2009-06-26T11:00:00+01
    feature : string
        filters results for the ID of a feature_of_interest
    allProperties : boolean
        if allProperties is True, filters results for all properties (and
        ignores any items in the observedProperties)
    """
    # GetCapabilites of SOS
    _sos = SensorObservationService(url, version=version or '1.0.0', xml=xml or None) 
    # process any supplied offerings
    if offerings:
        for off in _sos.offerings:  # look for matching IDs
            _offerings = [off for off in _sos.offerings if off.id in offerings]
    else:
        _offerings = []
    # get offering IDs to be used
    offerings_objs = _offerings or  _sos.offerings
    sos_offerings = [off.id for off in offerings_objs]
    responseFormat = responseFormat or offerings_objs[0].response_formats[0]
    if not allProperties:
        observedProperties = observedProperties or [offering.observed_properties[0]]
    else:
        observedProperties = offering.observed_properties
    eventTime = eventTime

    if feature:
        return _sos.get_observation(
            offerings=sos_offerings, responseFormat=responseFormat,
            observedProperties=observedProperties, eventTime=eventTime,
            FEATUREOFINTEREST=feature)
    else:
        return _sos.get_observation(
            offerings=sos_offerings, responseFormat=responseFormat,
            observedProperties=observedProperties, eventTime=eventTime)
Example #3
0
class IoosSweSos(Collector):
    def __init__(self, url, xml=None):
        super(IoosSweSos,self).__init__()
        self.server = Sos(url, xml=xml)

    def metadata(self, **kwargs):
        callback = kwargs.get("feature_name_callback", None) or str
        kwargs['outputFormat'] = 'text/xml;subtype="sensorML/1.0.1"'

        responses = []
        if self.features is not None:
            for feature in self.features:
                kwargs['procedure'] = callback(feature)
                responses.append(SensorML(self.server.describe_sensor(**kwargs)))

        return responses

    def setup_params(self, **kwargs):
        params = kwargs

        # TODO: BBOX needs to be implemented as an OGC Filter.
        # This page has a good example: http://opendap.co-ops.nos.noaa.gov/ioos-dif-sos/index.jsp
        # Click on "Collection" in the POST example to see what it should look like.
        # Requires changing from GET to POST...
        if self.bbox is not None:
            print "BBOX requests for IOOS SWE SOS services are not yet implemented"

        if self.start_time is not None:
            params["eventTime"] = self.start_time.strftime('%Y-%m-%dT%H:%M:%SZ')
        if self.end_time is not None:
            params["eventTime"] += "/%s" % self.end_time.strftime('%Y-%m-%dT%H:%M:%SZ')

        if self.variables is None or len(self.variables) < 1:
            raise ValueError("You must set a filter for at least one variable (observedProperty)")
        else:
            ops = ",".join(self.variables)
            if isinstance(ops, basestring):
                ops = [ops]
            params["observedProperties"] = ops

        return params

    def collect(self, **kwargs):
        kwargs["responseFormat"] = 'text/xml;subtype="om/1.0.0/profiles/ioos_sos/1.0"'
        return IoosGetObservation(self.raw(**kwargs)).feature

    def raw(self, **kwargs):
        params = self.setup_params(**kwargs)
        return self.server.get_observation(**params)
Example #4
0
    def harvest(self):
        self.sos = SensorObservationService(self.service.get('url'))
        for offering in self.sos.offerings:
            # TODO: We assume an offering should only have one procedure here
            # which will be the case in sos 2.0, but may not be the case right now
            # on some non IOOS supported servers.
            uid = offering.procedures[0]
            sp_uid = uid.split(":")

            # List storing the stations that have already been processed in this SOS server.
            # This is kept and checked later to avoid servers that have the same stations in many offerings.
            processed = []

            # temnplate:  urn:ioos:type:authority:id
            # sample:     ioos:station:wmo:21414
            if sp_uid[2] == "station":   # Station Offering
                if not uid in processed:
                    self.process_station(uid)
                processed.append(uid)
            elif sp_uid[2] == "network": # Network Offering
                network_ds = IoosDescribeSensor(self.sos.describe_sensor(outputFormat='text/xml;subtype="sensorML/1.0.1/profiles/ioos_sos/1.0"', procedure=uid))
                # Iterate over stations in the network and process them individually
                for proc in network_ds.procedures:
                    if proc is not None and proc.split(":")[2] == "station":
                        if not proc in processed:
                            self.process_station(proc)
                        processed.append(proc)
Example #5
0
    def process_doc(self, doc):
        """
        Attempt to parse an xml string conforming to either an SOS or SensorML
        dataset and return the results
        """
        xml_doc = ET.fromstring(doc)
        if xml_doc.tag == "{http://www.opengis.net/sos/1.0}Capabilities":
            ds = SensorObservationService(None, xml=doc)
            # SensorObservationService does not store the etree doc root,
            # so maybe use monkey patching here for now?
            ds._root = xml_doc

        elif xml_doc.tag == "{http://www.opengis.net/sensorML/1.0.1}SensorML":
            ds = SensorML(xml_doc)
        else:
            raise ValueError("Unrecognized XML root element: {}".format(xml_doc.tag))
        return ds
Example #6
0
    def __init__(self, **kwargs):
        super(NdbcSos,self).__init__()
        if kwargs.get('test') is True:
            url = 'http://sdftest.ndbc.noaa.gov/sos/server.php'
        else:
            url = 'http://sdf.ndbc.noaa.gov/sos/server.php'

        self.server = Sos(url)
Example #7
0
class CoopsSos(Collector):
    def __init__(self, **kwargs):
        super(CoopsSos,self).__init__()
        url = 'http://opendap.co-ops.nos.noaa.gov/ioos-dif-sos/SOS'

        self.server = Sos(url)

    def get_metadata(self, **kwargs):
        response = self.server.describe_sensor(**kwargs)
        return IoosDescribeSensor(response)

    def get_describe_sensor_output_formats(self):
        return self.server.get_operation_by_name('DescribeSensor').parameters['outputFormat']['values']

    #def get_data(self, **kwargs):
    #    response = self.get_raw_data(**kwargs)
    #    return IoosDif(response)
        
    def get_raw_data(self, **kwargs):
        res = self.server.get_observation(**kwargs)
        if res[41:56] == "ExceptionReport":
            res = -1
        return res
Example #8
0
class NdbcSos(Collector):
    def __init__(self, **kwargs):
        super(NdbcSos,self).__init__()
        if kwargs.get('test') is True:
            url = 'http://sdftest.ndbc.noaa.gov/sos/server.php'
        else:
            url = 'http://sdf.ndbc.noaa.gov/sos/server.php'

        self.server = Sos(url)

    def get_metadata(self, **kwargs):
        response = self.server.describe_sensor(**kwargs)
        return IoosDescribeSensor(response)

    def get_describe_sensor_output_formats(self):
        return self.server.get_operation_by_name('DescribeSensor').parameters['outputFormat']['values']

    def get_data(self, **kwargs):
        response = self.get_raw_data(**kwargs)
        return IoosSwe(response)
        
    def get_raw_data(self, **kwargs):
        return self.server.get_observation(**kwargs)
Example #9
0
    def __init__(self, **kwargs):
        super(CoopsSos,self).__init__()
        url = 'http://opendap.co-ops.nos.noaa.gov/ioos-dif-sos/SOS'

        self.server = Sos(url)
# Accessing ncSOS with OWSLib

# <codecell>

from owslib.sos import SensorObservationService
import pdb
from owslib.etree import etree
import pandas as pd
import datetime as dt

# <codecell>

# ncSOS endpoint with time series data
#url='http://geoport-dev.whoi.edu/thredds/sos/usgs/data2/notebook/1211-A1H.cdf'
url='http://www.neracoos.org:8180/thredds/sos/UMO/DSG/SOS/A01/Accelerometer/HistoricRealtime/Agg.ncml'
sos = SensorObservationService(url)
contents = sos.contents

# <codecell>

sos.contents

# <codecell>

off = sos.offerings[1]
off.name

# <codecell>

off.response_formats
import datetime as dt
import numpy as np


# In[46]:

url = 'http://sdf.ndbc.noaa.gov/sos/server.php?request=GetCapabilities&service=SOS&version=1.0.0'
ndbc = SensorObservationService(url)


# In[47]:

# usgs woods hole
# buoy data (single current meter)
url='http://geoport-dev.whoi.edu/thredds/sos/usgs/data2/notebook/1211-AA.cdf'
usgs = SensorObservationService(url)
contents = usgs.contents


# In[48]:

usgs.contents


# In[49]:

off = usgs.offerings[1]
off.name


# In[50]:
Example #12
0
# <codecell>

from owslib.sos import SensorObservationService
from owslib.etree import etree
import pandas as pd
import datetime as dt
import matplotlib.pyplot as plt
import numpy as np
%matplotlib inline

# <codecell>

# usgs woods hole
# regular time series data 
url='http://geoport-dev.whoi.edu/thredds/sos/usgs/data2/emontgomery/stellwagen/CF-1.6/BUZZ_BAY/2651-A.cdf'
usgs = SensorObservationService(url)
contents = usgs.contents

# <codecell>

usgs.contents

# <codecell>

off = usgs.offerings[1]
off.name

# <codecell>

off.response_formats
Example #13
0
 def __init__(self, url, xml=None):
     super(IoosSweSos, self).__init__()
     self.server = Sos(url, xml=xml)
Example #14
0
class IoosSweSos(Collector):
    def __init__(self, url, xml=None):
        super(IoosSweSos, self).__init__()
        self.server = Sos(url, xml=xml)

    def metadata(self,
                 output_format=None,
                 feature_name_callback=None,
                 **kwargs):
        """
        Gets SensorML objects for all procedures in your filtered features.

        You should override the default output_format for servers that do not
        respond properly.
        """
        callback = feature_name_callback or str
        if output_format is None:
            output_format = (
                'text/xml; subtype="sensorML/1.0.1/profiles/ioos_sos/1.0"')

        responses = []
        if self.features is not None:
            for feature in self.features:
                ds_kwargs = kwargs.copy()
                ds_kwargs.update({
                    "outputFormat": output_format,
                    "procedure": callback(feature),
                })

                responses.append(
                    SensorML(self.server.describe_sensor(**ds_kwargs)))

        return responses

    def metadata_plus_exceptions(self,
                                 output_format=None,
                                 feature_name_callback=None,
                                 **kwargs):
        """
        Gets SensorML objects for all procedures in your filtered features.

        Return two dictionaries for service responses keyed by 'feature':
            responses: values are SOS DescribeSensor response text
            response_failures: values are exception text content furnished from ServiceException, ExceptionReport

        You should override the default output_format for servers that do not
        respond properly.
        """
        callback = feature_name_callback or str
        if output_format is None:
            output_format = (
                'text/xml; subtype="sensorML/1.0.1/profiles/ioos_sos/1.0"')

        responses = {}
        response_failures = {}
        if self.features is not None:
            for feature in self.features:
                ds_kwargs = kwargs.copy()
                ds_kwargs.update({
                    "outputFormat": output_format,
                    "procedure": callback(feature),
                })
                try:
                    responses[feature] = SensorML(
                        self.server.describe_sensor(**ds_kwargs))
                except (ServiceException, ExceptionReport) as e:
                    response_failures[feature] = str(e)

        return (responses, response_failures)

    def setup_params(self, **kwargs):
        params = kwargs

        if self.bbox is not None:
            params["featureOfInterest"] = "BBOX:%s,%s,%s,%s" % (
                self.bbox[0],
                self.bbox[1],
                self.bbox[2],
                self.bbox[3],
            )

        if self.start_time is not None:
            params["eventTime"] = self.start_time.strftime(
                "%Y-%m-%dT%H:%M:%SZ")
        if self.end_time is not None:
            params["eventTime"] += "/%s" % self.end_time.strftime(
                "%Y-%m-%dT%H:%M:%SZ")

        if self.variables is None or len(self.variables) < 1:
            raise ValueError(
                "You must set a filter for at least one variable (observedProperty)"
            )
        else:
            ops = ",".join(self.variables)
            if isinstance(ops, string_types):
                ops = [ops]
            params["observedProperties"] = ops

        return params

    def collect(self, **kwargs):
        # there is an unfortunate difference in how 52N and ncSOS handle the response format.
        # 52N expects subtype, ncSOS expects schema.
        # consult the observed properties and getcaps to figure out which should be used if none passed
        if "responseFormat" not in kwargs:

            # iterate offerings and see if we need to change to subtype
            off_dict = {off.name: off for off in self.server.offerings}

            response_format = None

            for offering in kwargs.get("offerings", []):
                if offering not in off_dict:
                    continue

                ioos_formats = [
                    rf for rf in off_dict[offering].response_formats
                    if "ioos_sos/1.0" in rf
                ]
                if not len(ioos_formats):
                    raise Exception(
                        "No ioos_sos/1.0 response format found for offering {}"
                        .format(offering))

                if response_format != ioos_formats[0]:
                    response_format = ioos_formats[0]

            kwargs["responseFormat"] = response_format

        return IoosGetObservation(self.raw(**kwargs)).observations

    def raw(self, **kwargs):
        params = self.setup_params(**kwargs)
        return self.server.get_observation(**params)
Example #15
0
    def get_stations_df(self, sos_url, station_urns_sel=None):
        """ Returns a Pandas Dataframe
        """
        # oFrmts: IOOS SOS OutputFormat strings (first is compliant to the IOOS SOS spec,
        # second is to accommodate NDBC).  More info here:
        # http://ioos.github.io/sos-guidelines/doc/wsdd/sos_wsdd_github_notoc/#describesensor-request:638e0b263020c13a76a55332bd966dbe
        oFrmts = [
            'text/xml; subtype="sensorML/1.0.1/profiles/ioos_sos/1.0"',
            'text/xml;subtype="sensorML/1.0.1"'
        ]
        params = {
            'service': 'SOS',
            'request': 'GetCapabilities',
            'acceptVersions': '1.0.0'
        }
        sos_url_params = sos_url + '?' + urlencode(params)
        # sos_url_params_quoted = quote(sos_url_params,"/=:")
        # sos_url_params_unquoted = unquote(sos_url_params)

        try:
            sosgc = SensorObservationService(sos_url_params)
        except (ConnectionError, ReadTimeout) as e:
            self.log.write(
                u"\nError: unable to connect to SOS service: {url} due to HTTP connection error."
                .format(url=sos_url_params))
            self.log.write(
                u"\nHTTP connection error: {err}.".format(err=str(e)))
            sys.exit(
                "\nError: unable to connect to SOS service: {url}. \nUnderlying HTTP connection error: {err}"
                .format(url=sos_url_params, err=str(e)))

        # vars to store returns from sos_collector.metadata_plus_exceptions function:
        sml_recs = {}
        sml_errors = {}
        describe_sensor_url = {}

        # leverage Pyoos Collector to query for all available stations and obtain SensorML
        # (if station subset not passed in --stations param)
        if station_urns_sel is not None:
            station_urns = station_urns_sel
        else:
            sos_collector = IoosSweSos(sos_url)
            station_urns = [
                urn.name for urn in sos_collector.server.offerings
                if 'network' not in urn.name.split(':')
            ]
            sos_collector.features = station_urns

            # write out stations in SOS that will be handled:
            if self.verbose:
                self.log.write(u"\nStations to process for SOS: {sos}".format(
                    sos=sos_url_params))
                print("Stations to process for SOS: {sos}".format(
                    sos=sos_url_params))
                for feature in sos_collector.features:
                    self.log.write(u"\n - {feature}".format(feature=feature))
                    print(" - {feature}".format(feature=feature))

            # iterate over possible oFrmts expected of the various SOS services (IOOS SOS 1.0, NDBC):
            # for fmt in reversed(oFrmts):
            for fmt in oFrmts:
                try:
                    sml_recs, sml_errors = sos_collector.metadata_plus_exceptions(
                        output_format=fmt, timeout=200)
                    # if no valid SensorML docs returned, try next oFrmt:
                    if not sml_recs:
                        continue
                    else:
                        # assign correct DescribeSensor url (use sos_collector.feature rather than sml_recs.keys() to
                        # create DescribeSensor URLs for failures to record in logs):
                        for station in sos_collector.features:
                            describe_sensor_url[
                                station] = self.generate_describe_sensor_url(
                                    sosgc, procedure=station, oFrmt=fmt)

                        # report on errors returned from metadata_plus_exceptions:
                        if sml_errors:
                            if self.verbose:
                                for station, msg in iteritems(sml_errors):
                                    self.log.write(
                                        u"\nSOS DescribeSensor error returned for: {station}, skipping. Error msg: {msg}"
                                        .format(station=station, msg=msg))
                                    print(
                                        "SOS DescribeSensor error returned for: {station}, skipping. Error msg: {msg}"
                                        .format(station=station, msg=msg))
                        else:
                            self.log.write(
                                u"\nSuccess, no errors returned from DescribeSensor requests in service: {sos}"
                                .format(sos=sos_url_params))
                            print(
                                "Success, no errors returned from DescribeSensor requests in service: {sos}"
                                .format(sos=sos_url_params))
                    break
                # ServiceException shouldn't be thrown by metadata_plus_exceptions function, but handle regardless by attempting next oFrmt:
                except ServiceException as e:
                    continue

        station_recs = []
        failures = []
        # generate Pandas DataFrame by populating 'station_recs' list by parsing SensorML strings:
        for station_idx, station_urn in enumerate(station_urns):
            if station_urns_sel is not None:
                # iterate oFrmts items for describe_sensor request (first is IOOS SOS spec-compliant, second is for NDBC SOS):
                for fmt in oFrmts:
                    try:
                        describe_sensor_url[
                            station_urn] = self.generate_describe_sensor_url(
                                sosgc, procedure=station_urn, oFrmt=fmt)
                        sml_str = sosgc.describe_sensor(procedure=station_urn,
                                                        outputFormat=fmt,
                                                        timeout=200)
                        break
                    except ServiceException as e:
                        sml_errors[station_urn] = str(e)
                        continue
                sml = SensorML(sml_str)

            else:
                # process valid SensorML responses, quietly pass on invalid stations (add to failures list for verbose reporting):
                try:
                    sml = sml_recs[station_urn]
                except KeyError:
                    self.log.write(
                        u"\n\nStation: {station} failed (no SensorML in sml_recs dict).  URL: {ds}"
                        .format(station=station_urn,
                                ds=describe_sensor_url[station_urn].replace(
                                    "&amp;", "&")))
                    print(
                        "Station: {station} failed (no SensorML in sml_recs dict).  URL: {ds}"
                        .format(station=station_urn,
                                ds=describe_sensor_url[station_urn].replace(
                                    "&amp;", "&")))
                    failures.append(station_urn)
                    continue

            if self.sos_type.lower() == 'ndbc':
                # later: add an error check
                sosgc_station_offering = sosgc.contents[
                    'station-' + station_urn.split(':')[-1]]
            else:
                sosgc_station_offering = None

            try:
                ds = IoosDescribeSensor(sml._root)
            except AttributeError:
                self.log.write(
                    u"\nInvalid SensorML passed to IoosDescribeSensor.  Check DescribeSensor request for : {station}, URL: "
                    .format(station=station,
                            ds=describe_sensor_url[station_urn].replace(
                                "&amp;", "&")))
                print(
                    "Invalid SensorML passed to IoosDescribeSensor.  Check DescribeSensor request for : {station}, URL: "
                    .format(station=station,
                            ds=describe_sensor_url[station_urn].replace(
                                "&amp;", "&")))

            station = OrderedDict()
            # debug:
            if self.verbose:
                self.log.write(u"\n\nProcessing station: {station}".format(
                    station=station_urn))
                print("Processing station: {station}".format(
                    station=station_urn))
                self.log.write("\n" +
                               etree.tostring(sml._root).decode('utf-8'))

            # assign 'pos' to GML point location (accommodate 'gml:coordinates' as used by NDBC if gml:Point not found):
            try:
                pos = testXMLValue(ds.system.location.find(self.nsp('gml:Point/gml:pos'))) \
                    if testXMLValue(ds.system.location.find(self.nsp('gml:Point/gml:pos'))) is not None \
                    else testXMLValue(ds.system.location.find(self.nsp('gml:Point/gml:coordinates')))
                station['lon'] = float(pos.split()[1])
                station['lat'] = float(pos.split()[0])
            except AttributeError as e:
                station['lon'] = None
                station['lat'] = None

            system_el = sml._root.findall(self.nsp('sml:member'))[0].find(
                self.nsp('sml:System'))

            # Parse the DocumentList into a dict storing documents by index value 'name' (may cause index duplication
            # errors but there is not enough information in SensorML for alternatives)
            # Assume that member corresponds to xlink:arcrole="urn:ogc:def:role:webPage"
            documents = system_el.findall(
                self.nsp('sml:documentation/sml:DocumentList/sml:member'))
            documents_dct = {}
            for d in documents:
                document = Documentation(d)
                name = testXMLAttribute(d, "name")
                # url = document.documents[0].url
                documents_dct[name] = document

            # obtain list of contacts (accommodate 'sml:contact' element repetition used by NDBC insead of  ContactList):
            contacts = system_el.findall(self.nsp('sml:contact/sml:ContactList/sml:member')) \
                if system_el.findall(self.nsp('sml:contact/sml:ContactList/sml:member')) \
                else system_el.findall(self.nsp('sml:contact'))
            contacts_dct = {}
            for c in contacts:
                contact = Contact(c)
                role = contact.role.split('/')[-1]
                contacts_dct[role] = contact

            # verify a 'publisher' Contact exists (template expects one):
            if "publisher" not in contacts_dct.keys():
                self.log.write(
                    u"\n\nStation: {station} skipped.  No \'http://mmisw.org/ont/ioos/definition/publisher\' Contact role defined in SensorML as required.  Roles defined: [{roles}]"
                    .format(station=station_urn,
                            roles=", ".join(contacts_dct.keys())))
                print(
                    "Station: {station} skipped.  No \'http://mmisw.org/ont/ioos/definition/publisher\' Contact role defined in SensorML as required.  Roles defined: [{roles}]"
                    .format(station=station_urn,
                            roles=", ".join(contacts_dct.keys())))
                failures.append(station_urn)
                continue

            sweQuants = system_el.findall(
                self.nsp('sml:outputs/sml:OutputList/sml:output/swe:Quantity'))
            quant_lst = [
                sweQuant.attrib['definition'] for sweQuant in sweQuants
            ]
            parameter_lst = [sweQuant.split('/')[-1] for sweQuant in quant_lst]

            # attempt to read beginPosition, if available, otherwise use current date
            # bc ISO requires date value in output location in template:
            beginPosition = testXMLValue(
                system_el.find(
                    self.nsp(
                        'sml:validTime/gml:TimePeriod/gml:beginPosition')))
            try:
                begin_service_date = parser.parse(beginPosition)
            except (AttributeError, TypeError) as e:
                begin_service_date = datetime.now(pytz.utc)

            station['station_urn'] = station_urn
            station['sos_url'] = sos_url_params
            station['describesensor_url'] = describe_sensor_url[station_urn]

            station['shortName'] = ds.shortName
            station['longName'] = ds.longName
            if self.sos_type.lower() == 'ndbc':
                station['wmoID'] = station_urn.split(':')[-1]
            else:
                station['wmoID'] = ds.get_ioos_def('wmoID', 'identifier', ont)
            station['serverName'] = self.server_name

            # Some capabilities-level metadata:
            station['title'] = sosgc.identification.title
            station['abstract'] = sosgc.identification.abstract
            station['keywords'] = sosgc.identification.keywords
            station['begin_service_date'] = begin_service_date

            # Beware that a station can have >1 classifier of the same type
            # This code does not accommodate that possibility
            station['platformType'] = ds.platformType
            station['parentNetwork'] = ds.get_ioos_def('parentNetwork',
                                                       'classifier', ont)
            station['sponsor'] = ds.get_ioos_def('sponsor', 'classifier', ont)

            # store some nested dictionaries in 'station' for appropriate SensorML sources:
            station['contacts_dct'] = contacts_dct
            station['documents_dct'] = documents_dct

            if self.sos_type.lower(
            ) == 'ndbc' and sosgc_station_offering is not None:
                station['starting'] = sosgc_station_offering.begin_position
                station['ending'] = sosgc_station_offering.end_position
            else:
                station['starting'] = ds.starting
                station['ending'] = ds.ending

            if self.sos_type.lower(
            ) == 'ndbc' and sosgc_station_offering is not None:
                station[
                    'variable_uris'] = sosgc_station_offering.observed_properties
                station['variables'] = [
                    var.split('/')[-1]
                    for var in sosgc_station_offering.observed_properties
                ]
                station['parameter_uris'] = ','.join(station['variable_uris'])
                station['parameters'] = ','.join(station['variables'])
            else:
                station['variable_uris'] = ds.variables
                station['variables'] = [
                    var.split('/')[-1] for var in ds.variables
                ]
                station['parameter_uris'] = ','.join(quant_lst)
                station['parameters'] = ','.join(parameter_lst)

            if self.verbose:
                for var in station['variable_uris']:
                    self.log.write(u"\nvariable: {var}".format(var=var))
                    print("variable: {var}".format(var=var))

            # print(sosgc.contents)
            # for id, offering in sosgc.contents.iteritems():
            #    print("sosgc.contents: {item}".format(item=id))

            # parse 'responseFormat' vals from SensorML:
            # response_formats = sosgc.contents[station_urn].response_formats
            response_formats = []
            for id, sosgc.content in sosgc.contents.items():
                if sosgc.content.name == station_urn:
                    response_formats = sosgc.content.response_formats
            # response_formats = [ sosgc.content.response_formats for id, sosgc.content in sosgc.contents.items()
            #                      if sosgc.content.name == station_urn ]

            # match responseFormats from SensorML (response_formats) against those passed in --response_formats parameter to
            # populate 'download_formats' list, that is then used to generate GetObservation requests for the template:
            # (default --response_formats values are: 'application/json,application/zip; subtype=x-netcdf' )
            download_formats = [
                response_format for response_format in response_formats
                if response_format in self.response_formats
            ]
            station['response_formats'] = response_formats
            station['download_formats'] = download_formats

            if self.verbose:
                for format in response_formats:
                    self.log.write(
                        u"\nresponseFormat: {format}".format(format=format))
                    print("responseFormat: {format}".format(format=format))
                for format in download_formats:
                    self.log.write(
                        u"\ndownloadFormats: {format}".format(format=format))
                    print("downloadFormats: {format}".format(format=format))

            # calculate event_time using self.getobs_req_hours:
            event_time_formatstr = "{begin:%Y-%m-%dT%H:%M:%S}{utc_code}/{end:%Y-%m-%dT%H:%M:%S}{utc_code}"
            utc_code = 'Z' if self.sos_type.lower() == 'ndbc' else None
            if station['starting'] is not None and station[
                    'ending'] is not None:
                event_time = event_time_formatstr.format(
                    begin=station['ending'] -
                    timedelta(hours=self.getobs_req_hours),
                    end=station['ending'],
                    utc_code=utc_code)
                if self.verbose:
                    self.log.write(
                        u"\nUsing starting/ending times from SensorML for eventTime"
                    )
                    print(
                        "Using starting/ending times from SensorML for eventTime"
                    )
                    self.log.write(
                        u"\nobservationTimeRange: starting: {start}, ending: {end}"
                        .format(start=station['starting'],
                                end=station['ending']))
                    print(
                        "observationTimeRange: starting: {start}, ending: {end}"
                        .format(start=station['starting'],
                                end=station['ending']))
            else:
                now = datetime.now(pytz.utc)
                then = now - timedelta(hours=self.getobs_req_hours)
                event_time = event_time_formatstr.format(begin=then,
                                                         end=now,
                                                         utc_code=utc_code)
                if self.verbose:
                    self.log.write(
                        u"\nNo 'observationTimeRange' present in SensorML.  Using present time for eventTime: then: {then:%Y-%m-%dT%H:%M:%S%z}, now: {now:%Y-%m-%dT%H:%M:%S%z}"
                        .format(then=then, now=now))
                    print(
                        "No 'observationTimeRange' present in SensorML.  Using present time for eventTime: then: {then:%Y-%m-%dT%H:%M:%S%z}, now: {now:%Y-%m-%dT%H:%M:%S%z}"
                        .format(then=then, now=now))

            if self.verbose:
                self.log.write(u"\neventTime: {time}".format(time=event_time))
                print("eventTime: {time}".format(time=event_time))

            # create a dict to store parameters for valid example GetObservation requests for station:
            getobs_req_dct = {}
            # populate a parameters dictionary for download links for each 'observedProperty' type
            # and secondly for each 'responseFormat' per observedProperty:
            getobs_params_base = {
                'service': 'SOS',
                'request': 'GetObservation',
                'version': '1.0.0',
                'offering': station_urn,
                'eventTime': event_time
            }
            for variable in station['variable_uris']:
                getobs_params = getobs_params_base.copy()
                getobs_params['observedProperty'] = variable
                variable = variable.split('/')[-1]
                for format in download_formats:
                    getobs_params['responseFormat'] = format
                    getobs_request_url_encoded = sos_url + '?' + urlencode(
                        getobs_params)
                    getobs_request_url = unquote(getobs_request_url_encoded)
                    getobs_req_dct[variable + '-' + format] = {
                        'variable':
                        variable,
                        'url':
                        getobs_request_url,
                        'format_type':
                        self.RESPONSE_FORMAT_TYPE_MAP.get(format, format),
                        'format_name':
                        self.RESPONSE_FORMAT_NAME_MAP.get(format, format)
                    }
                    if self.verbose:
                        self.log.write(
                            u"\ngetobs_request_url (var: {variable}): {getobs_request_url}"
                            .format(variable=variable.split("/")[-1],
                                    getobs_request_url=getobs_request_url))
                        print(
                            "getobs_request_url (var: {variable}): {getobs_request_url}"
                            .format(variable=variable.split("/")[-1],
                                    getobs_request_url=getobs_request_url))

            # ToDo: finish adding the 'getobs_req_dct' to the output template
            station['getobs_req_dct'] = getobs_req_dct

            station_recs.append(station)

        # extra debug for failed stations in verbose mode:
        if self.verbose:
            self.log.write(
                u"\n\n\nSOS DescribeSensor request errors recap.  Failed requests:"
            )
            print("SOS DescribeSensor request errors recap.  Failed requests:")
            for station_fail, msg in iteritems(sml_errors):
                self.log.write(
                    u"\n{station} - {msg}.  DescribeSensor URL: {ds}".format(
                        station=station_fail,
                        msg=msg,
                        ds=describe_sensor_url[station_fail].replace(
                            "&amp;", "&")))
                print("{station} - {msg}.  DescribeSensor URL: {ds}".format(
                    station=station_fail,
                    msg=msg,
                    ds=describe_sensor_url[station_fail].replace("&amp;",
                                                                 "&")))
            if failures:
                self.log.write(
                    u"\nStations in 'failures' list (should match DescribeSensor errors):"
                )
                print(
                    "Stations in 'failures' list (should match DescribeSensor errors):"
                )
                for station_fail in failures:
                    self.log.write(u"\n{station}".format(station=station_fail))
                    print("{station}".format(station=station_fail))

        if station_recs:
            stations_df = pd.DataFrame.from_records(station_recs,
                                                    columns=station.keys())
            stations_df.index = stations_df['station_urn']
            return stations_df
        else:
            return None
Example #16
0
class SosHarvest(Harvester):
    def __init__(self, service):
        Harvester.__init__(self, service)

    def harvest(self):
        self.sos = SensorObservationService(self.service.get('url'))
        for offering in self.sos.offerings:
            # TODO: We assume an offering should only have one procedure here
            # which will be the case in sos 2.0, but may not be the case right now
            # on some non IOOS supported servers.
            uid = offering.procedures[0]
            sp_uid = uid.split(":")

            # List storing the stations that have already been processed in this SOS server.
            # This is kept and checked later to avoid servers that have the same stations in many offerings.
            processed = []

            # temnplate:  urn:ioos:type:authority:id
            # sample:     ioos:station:wmo:21414
            if sp_uid[2] == "station":  # Station Offering
                if not uid in processed:
                    self.process_station(uid)
                processed.append(uid)
            elif sp_uid[2] == "network":  # Network Offering
                network_ds = IoosDescribeSensor(
                    self.sos.describe_sensor(
                        outputFormat=
                        'text/xml;subtype="sensorML/1.0.1/profiles/ioos_sos/1.0"',
                        procedure=uid))
                # Iterate over stations in the network and process them individually
                for proc in network_ds.procedures:
                    if proc is not None and proc.split(":")[2] == "station":
                        if not proc in processed:
                            self.process_station(proc)
                        processed.append(proc)

    def process_station(self, uid):
        """ Makes a DescribeSensor request based on a 'uid' parameter being a station procedure """

        GML_NS = "http://www.opengis.net/gml"
        XLINK_NS = "http://www.w3.org/1999/xlink"

        with app.app_context():

            metadata_value = etree.fromstring(
                self.sos.describe_sensor(
                    outputFormat=
                    'text/xml;subtype="sensorML/1.0.1/profiles/ioos_sos/1.0"',
                    procedure=uid))
            station_ds = IoosDescribeSensor(metadata_value)

            unique_id = station_ds.id
            if unique_id is None:
                app.logger.warn(
                    "Could not get a 'stationID' from the SensorML identifiers.  Looking for a definition of 'http://mmisw.org/ont/ioos/definition/stationID'"
                )
                return

            dataset = db.Dataset.find_one({'uid': unicode(unique_id)})
            if dataset is None:
                dataset = db.Dataset()
                dataset.uid = unicode(unique_id)

            # Find service reference in Dataset.services and remove (to replace it)
            tmp = dataset.services[:]
            for d in tmp:
                if d['service_id'] == self.service.get('_id'):
                    dataset.services.remove(d)

            # Parsing messages
            messages = []

            # NAME
            name = unicode_or_none(station_ds.shortName)
            if name is None:
                messages.append(
                    u"Could not get a 'shortName' from the SensorML identifiers.  Looking for a definition of 'http://mmisw.org/ont/ioos/definition/shortName'"
                )

            # DESCRIPTION
            description = unicode_or_none(station_ds.longName)
            if description is None:
                messages.append(
                    u"Could not get a 'longName' from the SensorML identifiers.  Looking for a definition of 'http://mmisw.org/ont/ioos/definition/longName'"
                )

            # PLATFORM TYPE
            asset_type = unicode_or_none(station_ds.platformType)
            if asset_type is None:
                messages.append(
                    u"Could not get a 'platformType' from the SensorML identifiers.  Looking for a definition of 'http://mmisw.org/ont/ioos/definition/platformType'"
                )

            # LOCATION is in GML
            gj = None
            loc = station_ds.location
            if loc is not None and loc.tag == "{%s}Point" % GML_NS:
                pos_element = loc.find("{%s}pos" % GML_NS)
                # strip out points
                positions = map(float, testXMLValue(pos_element).split(" "))
                crs = Crs(testXMLAttribute(pos_element, "srsName"))
                if crs.axisorder == "yx":
                    gj = json.loads(
                        geojson.dumps(
                            geojson.Point([positions[1], positions[0]])))
                else:
                    gj = json.loads(
                        geojson.dumps(
                            geojson.Point([positions[0], positions[1]])))
            else:
                messages.append(
                    u"Found an unrecognized child of the sml:location element and did not attempt to process it: %s"
                    % etree.tostring(loc).strip())

            service = {
                # Reset service
                'name': name,
                'description': description,
                'service_type': self.service.get('service_type'),
                'service_id': ObjectId(self.service.get('_id')),
                'data_provider': self.service.get('data_provider'),
                'metadata_type': u'sensorml',
                'metadata_value':
                unicode(etree.tostring(metadata_value)).strip(),
                'messages': map(unicode, messages),
                'keywords': map(unicode, sorted(station_ds.keywords)),
                'variables': map(unicode, sorted(station_ds.variables)),
                'asset_type': asset_type,
                'geojson': gj,
                'updated': datetime.utcnow()
            }

            dataset.services.append(service)
            dataset.updated = datetime.utcnow()
            dataset.save()
            return "Harvested"
# Accessing ncSOS with OWSLib

# <codecell>

from owslib.sos import SensorObservationService
import pdb
from owslib.etree import etree
import pandas as pd
import datetime as dt

# <codecell>

# usgs woods hole
# buoy data (ADCP)
url='http://geoport.whoi.edu/thredds/sos/usgs/data2/emontgomery/stellwagen/CF-1.6/HURRIRENE_BB/9141wh-a.nc'
usgs = SensorObservationService(url)
contents = usgs.contents

# <codecell>

usgs.contents

# <codecell>

off = usgs.offerings[1]
off.name

# <codecell>

off.response_formats
Example #18
0
class IoosSweSos(Collector):
    def __init__(self, url, xml=None):
        super(IoosSweSos, self).__init__()
        self.server = Sos(url, xml=xml)

    def metadata(self, output_format=None, feature_name_callback=None, **kwargs):
        """
        Gets SensorML objects for all procedures in your filtered features.

        You should override the default output_format for servers that do not
        respond properly.
        """
        callback = feature_name_callback or str
        if output_format is None:
            output_format = 'text/xml; subtype="sensorML/1.0.1/profiles/ioos_sos/1.0"'

        responses = []
        if self.features is not None:
            for feature in self.features:
                ds_kwargs = kwargs.copy()
                ds_kwargs.update({'outputFormat': output_format,
                                  'procedure'   : callback(feature)})

                responses.append(SensorML(self.server.describe_sensor(**ds_kwargs)))

        return responses

    def setup_params(self, **kwargs):
        params = kwargs

        if self.bbox is not None:
            params["featureOfInterest"] = "BBOX:%s,%s,%s,%s" % (self.bbox[0], self.bbox[1], self.bbox[2], self.bbox[3])

        if self.start_time is not None:
            params["eventTime"] = self.start_time.strftime('%Y-%m-%dT%H:%M:%SZ')
        if self.end_time is not None:
            params["eventTime"] += "/%s" % self.end_time.strftime('%Y-%m-%dT%H:%M:%SZ')

        if self.variables is None or len(self.variables) < 1:
            raise ValueError("You must set a filter for at least one variable (observedProperty)")
        else:
            ops = ",".join(self.variables)
            if isinstance(ops, basestring):
                ops = [ops]
            params["observedProperties"] = ops

        return params

    def collect(self, **kwargs):
        # there is an unfortunate difference in how 52N and ncSOS handle the response format.
        # 52N expects subtype, ncSOS expects schema.
        # consult the observed properties and getcaps to figure out which should be used if none passed
        if 'responseFormat' not in kwargs:

            # iterate offerings and see if we need to change to subtype
            off_dict = {off.name : off for off in self.server.offerings}

            response_format = None

            for offering in kwargs.get('offerings', []):
                if offering not in off_dict:
                    continue

                ioos_formats = [rf for rf in off_dict[offering].response_formats if 'ioos_sos/1.0' in rf]
                if not len(ioos_formats):
                    raise StandardError("No ioos_sos/1.0 response format found for offering %s" % offering)

                if response_format != ioos_formats[0]:
                    response_format = ioos_formats[0]

            kwargs["responseFormat"] = response_format

        return IoosGetObservation(self.raw(**kwargs)).observations

    def raw(self, **kwargs):
        params = self.setup_params(**kwargs)
        return self.server.get_observation(**params)
Example #19
0
class SosHarvest(Harvester):
    def __init__(self, service):
        Harvester.__init__(self, service)

    def harvest(self):
        self.sos = SensorObservationService(self.service.get('url'))
        for offering in self.sos.offerings:
            # TODO: We assume an offering should only have one procedure here
            # which will be the case in sos 2.0, but may not be the case right now
            # on some non IOOS supported servers.
            uid = offering.procedures[0]
            sp_uid = uid.split(":")

            # List storing the stations that have already been processed in this SOS server.
            # This is kept and checked later to avoid servers that have the same stations in many offerings.
            processed = []

            # temnplate:  urn:ioos:type:authority:id
            # sample:     ioos:station:wmo:21414
            if sp_uid[2] == "station":   # Station Offering
                if not uid in processed:
                    self.process_station(uid)
                processed.append(uid)
            elif sp_uid[2] == "network": # Network Offering
                network_ds = IoosDescribeSensor(self.sos.describe_sensor(outputFormat='text/xml;subtype="sensorML/1.0.1/profiles/ioos_sos/1.0"', procedure=uid))
                # Iterate over stations in the network and process them individually
                for proc in network_ds.procedures:
                    if proc is not None and proc.split(":")[2] == "station":
                        if not proc in processed:
                            self.process_station(proc)
                        processed.append(proc)

    def process_station(self, uid):
        """ Makes a DescribeSensor request based on a 'uid' parameter being a station procedure """

        GML_NS   = "http://www.opengis.net/gml"
        XLINK_NS = "http://www.w3.org/1999/xlink"

        with app.app_context():

            metadata_value = etree.fromstring(self.sos.describe_sensor(outputFormat='text/xml;subtype="sensorML/1.0.1/profiles/ioos_sos/1.0"', procedure=uid))
            station_ds     = IoosDescribeSensor(metadata_value)

            unique_id = station_ds.id
            if unique_id is None:
                app.logger.warn("Could not get a 'stationID' from the SensorML identifiers.  Looking for a definition of 'http://mmisw.org/ont/ioos/definition/stationID'")
                return

            dataset = db.Dataset.find_one( { 'uid' : unicode(unique_id) } )
            if dataset is None:
                dataset = db.Dataset()
                dataset.uid = unicode(unique_id)

            # Find service reference in Dataset.services and remove (to replace it)
            tmp = dataset.services[:]
            for d in tmp:
                if d['service_id'] == self.service.get('_id'):
                    dataset.services.remove(d)

            # Parsing messages
            messages = []

            # NAME
            name = unicode_or_none(station_ds.shortName)
            if name is None:
                messages.append(u"Could not get a 'shortName' from the SensorML identifiers.  Looking for a definition of 'http://mmisw.org/ont/ioos/definition/shortName'")

            # DESCRIPTION
            description = unicode_or_none(station_ds.longName)
            if description is None:
                messages.append(u"Could not get a 'longName' from the SensorML identifiers.  Looking for a definition of 'http://mmisw.org/ont/ioos/definition/longName'")

            # PLATFORM TYPE
            asset_type = unicode_or_none(station_ds.platformType)
            if asset_type is None:
                messages.append(u"Could not get a 'platformType' from the SensorML identifiers.  Looking for a definition of 'http://mmisw.org/ont/ioos/definition/platformType'")

            # LOCATION is in GML
            gj = None
            loc = station_ds.location
            if loc is not None and loc.tag == "{%s}Point" % GML_NS:
                pos_element = loc.find("{%s}pos" % GML_NS)
                # strip out points
                positions = map(float, testXMLValue(pos_element).split(" "))
                crs = Crs(testXMLAttribute(pos_element, "srsName"))
                if crs.axisorder == "yx":
                    gj = json.loads(geojson.dumps(geojson.Point([positions[1], positions[0]])))
                else:
                    gj = json.loads(geojson.dumps(geojson.Point([positions[0], positions[1]])))
            else:
                messages.append(u"Found an unrecognized child of the sml:location element and did not attempt to process it: %s" % etree.tostring(loc).strip())

            service = {
                # Reset service
                'name'              : name,
                'description'       : description,
                'service_type'      : self.service.get('service_type'),
                'service_id'        : ObjectId(self.service.get('_id')),
                'data_provider'     : self.service.get('data_provider'),
                'metadata_type'     : u'sensorml',
                'metadata_value'    : unicode(etree.tostring(metadata_value)).strip(),
                'messages'          : map(unicode, messages),
                'keywords'          : map(unicode, sorted(station_ds.keywords)),
                'variables'         : map(unicode, sorted(station_ds.variables)),
                'asset_type'        : asset_type,
                'geojson'           : gj,
                'updated'           : datetime.utcnow()
            }

            dataset.services.append(service)
            dataset.updated = datetime.utcnow()
            dataset.save()
            return "Harvested"
Example #20
0
    def get_stations_df(self, sos_url, station_urns_sel=None):
        """ Returns a GeoDataFrame
        """
        # LATER: ADD ERROR TEST/CATCH AFTER EACH WEB REQUEST
        oFrmt = 'text/xml; subtype="sensorML/1.0.1/profiles/ioos_sos/1.0"'
        params = {
            'service': 'SOS',
            'request': 'GetCapabilities',
            'acceptVersions': '1.0.0'
        }
        sos_url_params = sos_url + '?' + urlencode(params)
        sos_url_params_esc = sos_url_params.replace("&", "&amp;")
        # sos_url_params_quoted = quote(sos_url_params,"/=:")
        # sos_url_params_unquoted = unquote(sos_url_params)

        try:
            sosgc = SensorObservationService(sos_url_params)
        except (ConnectionError, ReadTimeout) as e:
            self.log.write(
                u"Error: unable to connect to SOS service: {url} due to HTTP connection error.\n"
                .format(url=sos_url_params))
            self.log.write(
                u"HTTP connection error: {err}.\n".format(err=e.message))
            sys.exit(
                "Error: unable to connect to SOS service: {url}. \nUnderlying HTTP connection error: {err}"
                .format(url=sos_url_params, err=e.message))

        if station_urns_sel is not None:
            station_urns = station_urns_sel
        else:
            sos_collector = IoosSweSos(sos_url)
            station_urns = [
                urn.name for urn in sos_collector.server.offerings
                if 'network' not in urn.name.split(':')
            ]
            sos_collector.features = station_urns
            sml_lst = sos_collector.metadata(timeout=200)

        station_recs = []
        for station_idx, station_urn in enumerate(station_urns):
            if station_urns_sel is not None:
                sml_str = sosgc.describe_sensor(procedure=station_urn,
                                                outputFormat=oFrmt,
                                                timeout=200)
                sml = SensorML(sml_str)

            else:
                sml = sml_lst[station_idx]

            # debug:
            # if self.verbose:
            #    self.log.write(unicode(etree.tostring(sml._root)))

            ds = IoosDescribeSensor(sml._root)

            pos = testXMLValue(
                ds.system.location.find(self.nsp('gml:Point/gml:pos')))

            system_el = sml._root.findall(self.nsp('sml:member'))[0].find(
                self.nsp('sml:System'))

            # Parse the DocumentList into a dict storing documents by index value 'name' (may cause index duplication
            # errors but there is not enough information in SensorML for alternatives)
            # Assume that member corresponds to xlink:arcrole="urn:ogc:def:role:webPage"
            documents = system_el.findall(
                self.nsp('sml:documentation/sml:DocumentList/sml:member'))
            documents_dct = {}
            for d in documents:
                document = Documentation(d)
                name = testXMLAttribute(d, "name")
                # url = document.documents[0].url
                documents_dct[name] = document

            contacts = system_el.findall(
                self.nsp('sml:contact/sml:ContactList/sml:member'))
            contacts_dct = {}
            for c in contacts:
                contact = Contact(c)
                role = contact.role.split('/')[-1]
                contacts_dct[role] = contact

            sweQuants = system_el.findall(
                self.nsp('sml:outputs/sml:OutputList/sml:output/swe:Quantity'))
            quant_lst = [
                sweQuant.attrib['definition'] for sweQuant in sweQuants
            ]
            parameter_lst = [sweQuant.split('/')[-1] for sweQuant in quant_lst]

            # attempt to read beginPosition, if available:
            beginPosition = testXMLValue(
                system_el.find(
                    self.nsp(
                        'sml:validTime/gml:TimePeriod/gml:beginPosition')))
            try:
                begin_service_date = parser.parse(beginPosition)
            except AttributeError as e:
                begin_service_date = None

            station = OrderedDict()
            station['station_urn'] = station_urn
            station['sos_url'] = sos_url_params_esc
            station['lon'] = float(pos.split()[1])
            station['lat'] = float(pos.split()[0])

            station['shortName'] = ds.shortName
            station['longName'] = ds.longName
            station['wmoID'] = ds.get_ioos_def('wmoID', 'identifier', ont)
            station['serverName'] = self.server_name

            # Some capabilities-level metadata:
            station['title'] = sosgc.identification.title
            station['abstract'] = sosgc.identification.abstract
            station['keywords'] = sosgc.identification.keywords
            station['begin_service_date'] = begin_service_date

            # Beware that a station can have >1 classifier of the same type
            # This code does not accommodate that possibility
            station['platformType'] = ds.platformType
            station['parentNetwork'] = ds.get_ioos_def('parentNetwork',
                                                       'classifier', ont)
            station['sponsor'] = ds.get_ioos_def('sponsor', 'classifier', ont)

            # store some nested dictionaries in 'station' for appopriate SensorML sources:
            station['contacts_dct'] = contacts_dct
            station['documents_dct'] = documents_dct

            # MW: the 'operator_' and 'publisher_' attributes can be removed bc they are not used
            # in the template code currently in favor of 'contacts_dct'
            # station['operatorSector'] = ds.get_ioos_def('operatorSector', 'classifier', ont)
            # station['operator_org'] = contacts_dct['operator'].organization
            # station['operator_country'] = contacts_dct['operator'].country
            # station['operator_url'] = contacts_dct['operator'].url
            # station['operator_email'] = contacts_dct['operator'].email

            # station['publisher'] = ds.get_ioos_def('publisher', 'classifier', ont)
            # station['publisher_org'] = contacts_dct['publisher'].organization
            # station['publisher_url'] = contacts_dct['publisher'].url
            # station_dct['publisher_email'] = contacts_dct['publisher'].electronicMailAddress

            station['starting'] = ds.starting
            station['ending'] = ds.ending
            # station['starting_isostr'] = datetime.isoformat(ds.starting)
            # station['ending_isostr'] = datetime.isoformat(ds.ending)

            station['parameter_uris'] = ','.join(quant_lst)
            station['parameters'] = ','.join(parameter_lst)
            station['variables'] = [var.split('/')[-1] for var in ds.variables]

            # debug:
            if self.verbose:
                self.log.write(u"\nProcessing station: {station}\n".format(
                    station=station_urn))
                print("\nProcessing station: {station}".format(
                    station=station_urn))
                for var in ds.variables:
                    self.log.write(u"variable: {var}\n".format(var=var))
                    print("variable: {var}".format(var=var))

            # print(sosgc.contents)
            # for id, offering in sosgc.contents.iteritems():
            #    print("sosgc.contents: {item}".format(item=id))

            # parse 'responseFormat' values and populate list:
            # response_formats = sosgc.contents[station_urn].response_formats
            response_formats = []
            for id, sosgc.content in sosgc.contents.items():
                if sosgc.content.name == station_urn:
                    response_formats = sosgc.content.response_formats
            # response_formats = [ sosgc.content.response_formats for id, sosgc.content in sosgc.contents.items() if sosgc.content.name == station_urn ]

            # subset responseFormats (response_formats) for download links matching those passed in --response_formats parameter
            # (or 'application/json,application/zip; subtype=x-netcdf' by default):
            download_formats = [
                response_format for response_format in response_formats
                if response_format in self.response_formats
            ]
            station['response_formats'] = response_formats
            station['download_formats'] = download_formats

            if self.verbose:
                for format in response_formats:
                    self.log.write(
                        u"responseFormat: {format}\n".format(format=format))
                    print("responseFormat: {format}".format(format=format))
                for format in download_formats:
                    self.log.write(
                        u"downloadFormats: {format}\n".format(format=format))
                    print("downloadFormats: {format}".format(format=format))

            # calculate event_time using self.getobs_req_hours:
            if ds.starting is not None and ds.ending is not None:
                event_time = "{begin:%Y-%m-%dT%H:%M:%S}/{end:%Y-%m-%dT%H:%M:%S}\n".format(
                    begin=ds.ending - timedelta(hours=self.getobs_req_hours),
                    end=ds.ending)
            else:
                now = datetime.now(pytz.utc)
                then = now - timedelta(hours=self.getobs_req_hours)
                event_time = "{begin:%Y-%m-%dT%H:%M:%S}/{end:%Y-%m-%dT%H:%M:%S}\n".format(
                    begin=then, end=now)
                if self.verbose:
                    self.log.write(
                        u"then: {then:%Y-%m-%dT%H:%M:%S%z}, now: {now:%Y-%m-%dT%H:%M:%S%z}\n"
                        .format(then=then, now=now))
                    print(
                        "then: {then:%Y-%m-%dT%H:%M:%S%z}, now: {now:%Y-%m-%dT%H:%M:%S%z}"
                        .format(then=then, now=now))

            if self.verbose:
                self.log.write(u"eventTime: {time}\n".format(time=event_time))
                print("eventTime: {time}".format(time=event_time))

            # create a dict to store parameters for valid example GetObservation requests for station:
            getobs_req_dct = {}
            # populate a parameters dictionary for download links for each 'observedProperty' type and secondly for each 'responseFormat' per observedProperty:
            getobs_params_base = {
                'service': 'SOS',
                'request': 'GetObservation',
                'version': '1.0.0',
                'offering': station_urn,
                'eventTime': event_time
            }
            for variable in ds.variables:
                getobs_params = getobs_params_base.copy()
                getobs_params['observedProperty'] = variable
                variable = variable.split('/')[-1]
                for format in download_formats:
                    getobs_params['responseFormat'] = format
                    getobs_request_url_encoded = sos_url + '?' + urlencode(
                        getobs_params)
                    getobs_request_url = unquote(getobs_request_url_encoded)
                    getobs_request_url_esc = getobs_request_url.replace(
                        "&", "&amp;")
                    getobs_req_dct[variable + '-' + format] = {
                        'variable': variable,
                        'url': getobs_request_url_esc,
                        'format_type': self.RESPONSE_FORMAT_TYPE_MAP[format],
                        'format_name': self.RESPONSE_FORMAT_NAME_MAP[format]
                    }
                    if self.verbose:
                        self.log.write(
                            u"getobs_request_url (var: {variable}): {getobs_request_url}\ngetobs_request_url_esc (var: {variable}): {getobs_request_url_esc}\n"
                            .format(
                                variable=variable.split("/")[-1],
                                getobs_request_url=getobs_request_url,
                                getobs_request_url_esc=getobs_request_url_esc))
                        print(
                            "getobs_request_url (var: {variable}): {getobs_request_url}\ngetobs_request_url_esc (var: {variable}): {getobs_request_url_esc}"
                            .format(
                                variable=variable.split("/")[-1],
                                getobs_request_url=getobs_request_url,
                                getobs_request_url_esc=getobs_request_url_esc))

            # ToDo: finish adding the 'getobs_req_dct' to the output template
            station['getobs_req_dct'] = getobs_req_dct

            station_recs.append(station)

        stations_df = pd.DataFrame.from_records(station_recs,
                                                columns=station.keys())
        stations_df.index = stations_df['station_urn']

        return stations_df
# <codecell>

from owslib.sos import SensorObservationService
from owslib.etree import etree
import pandas as pd
import datetime as dt
import matplotlib.pyplot as plt
import numpy as np
%matplotlib inline

# <codecell>

# usgs woods hole
# regular time series data 
url='http://geoport.whoi.edu/thredds/sos/usgs/data2/emontgomery/stellwagen/CF-1.6/BUZZ_BAY/2651-A.cdf'
usgs = SensorObservationService(url)
contents = usgs.contents

# <codecell>

usgs.contents

# <codecell>

off = usgs.offerings[1]
off.name

# <codecell>

off.response_formats
Example #22
0
def test_sos_caps():
    sos = SensorObservationService(SERVICE_URL)
    assert str(sos.contents['81102 - PM10']) == "Offering id: 81102 - PM10, name: Particulate Matter < 10 µm"
    assert repr(sos.contents['81102 - PM10']) == "<SosObservationOffering 'Particulate Matter < 10 µm'>"
Example #23
0
 def __init__(self, url, xml=None):
     super(IoosSweSos, self).__init__()
     self.server = Sos(url, xml=xml)
Example #24
0
# But can we formulate, request and process this same query (and others like it) using OWSlib?

# In[64]:

get_ipython().magic('matplotlib inline')
from owslib.sos import SensorObservationService
import pdb
from owslib.etree import etree
import pandas as pd
import datetime as dt
import numpy as np

# In[46]:

url = 'http://sdf.ndbc.noaa.gov/sos/server.php?request=GetCapabilities&service=SOS&version=1.0.0'
ndbc = SensorObservationService(url)

# In[47]:

# usgs woods hole
# buoy data (single current meter)
url = 'http://geoport-dev.whoi.edu/thredds/sos/usgs/data2/notebook/1211-AA.cdf'
usgs = SensorObservationService(url)
contents = usgs.contents

# In[48]:

usgs.contents

# In[49]: