Skip to content

Commit 2f4a86c

Browse files
committed
Add support to handle 'operator' Contact in SensorML. Plus additional error checking.
1 parent e925376 commit 2f4a86c

File tree

2 files changed

+34
-14
lines changed

2 files changed

+34
-14
lines changed

sensorml2iso/sensorml2iso.py

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,10 @@ def run(self):
130130
# obtain the stations DataFrame:
131131
stations_df = self.get_stations_df(self.service, self.stations)
132132

133+
if stations_df is None:
134+
self.log.write(u"\nNo valid SensorML documents obtained from SOS serivce. Verify service is compliant with the SOS profile [URL: {url}]".format(url=self.service))
135+
sys.exit("No valed SensorML documents obtained from SOS serivce. Verify service is compliant with the SOS profile [URL: {url}]".format(url=self.service))
136+
133137
# Assign EPSG:4326 CRS, retrieved from epsg.io
134138
# The OGC WKT crs string is available directly at http://epsg.io/4326.wkt
135139
# or http://spatialreference.org/ref/epsg/4326/ogcwkt/
@@ -154,9 +158,9 @@ def run(self):
154158
print("Date for determining active/inactive stations in SOS service: {active_date:%Y-%m-%d}".format(active_date=station_active_date))
155159
print("'Active' stations: %d / Total stations: %d" % (active_cnt, total_cnt))
156160
print("DataFrame sizes: Original(stations_df): {len_stations_df:2.0f}, Filtered: {len_filtered_stations_df:2.0f}".format(len_stations_df=len(stations_df), len_filtered_stations_df=len(filtered_stations_df)))
157-
self.log.write(u"Date for determining active/inactive stations in SOS service: {active_date:%Y-%m-%d}".format(active_date=station_active_date))
158-
self.log.write(u"'Active' stations: %d / Total stations: %d" % (active_cnt, total_cnt))
159-
self.log.write(u"DataFrame sizes: Original(stations_df): {len_stations_df:2.0f}, Filtered: {len_filtered_stations_df:2.0f}".format(len_stations_df=len(stations_df), len_filtered_stations_df=len(filtered_stations_df)))
161+
self.log.write(u"\nDate for determining active/inactive stations in SOS service: {active_date:%Y-%m-%d}".format(active_date=station_active_date))
162+
self.log.write(u"\n'Active' stations: %d / Total stations: %d" % (active_cnt, total_cnt))
163+
self.log.write(u"\nDataFrame sizes: Original(stations_df): {len_stations_df:2.0f}, Filtered: {len_filtered_stations_df:2.0f}".format(len_stations_df=len(stations_df), len_filtered_stations_df=len(filtered_stations_df)))
160164

161165
if self.verbose:
162166
# self.csv.write(unicode(stations_df[stations_df.ending > station_active_date.isoformat()].to_csv(encoding='utf-8')))
@@ -195,9 +199,9 @@ def get_stations_df(self, sos_url, station_urns_sel=None):
195199
try:
196200
sosgc = SensorObservationService(sos_url_params)
197201
except (ConnectionError, ReadTimeout) as e:
198-
self.log.write(u"Error: unable to connect to SOS service: {url} due to HTTP connection error.\n".format(url=sos_url_params))
199-
self.log.write(u"HTTP connection error: {err}.\n".format(err=str(e)))
200-
sys.exit("Error: unable to connect to SOS service: {url}. \nUnderlying HTTP connection error: {err}\n".format(url=sos_url_params, err=str(e)))
202+
self.log.write(u"\nError: unable to connect to SOS service: {url} due to HTTP connection error.".format(url=sos_url_params))
203+
self.log.write(u"\nHTTP connection error: {err}.".format(err=str(e)))
204+
sys.exit("\nError: unable to connect to SOS service: {url}. \nUnderlying HTTP connection error: {err}".format(url=sos_url_params, err=str(e)))
201205

202206
# vars to store returns from sos_collector.metadata_plus_exceptions function:
203207
sml_recs = {}
@@ -318,6 +322,13 @@ def get_stations_df(self, sos_url, station_urns_sel=None):
318322
role = contact.role.split('/')[-1]
319323
contacts_dct[role] = contact
320324

325+
# verify a 'publisher' Contact exists (template expects one):
326+
if "publisher" not in contacts_dct.keys():
327+
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())))
328+
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())))
329+
failures.append(station_urn)
330+
continue
331+
321332
sweQuants = system_el.findall(self.nsp('sml:outputs/sml:OutputList/sml:output/swe:Quantity'))
322333
quant_lst = [sweQuant.attrib['definition'] for sweQuant in sweQuants]
323334
parameter_lst = [sweQuant.split('/')[-1] for sweQuant in quant_lst]
@@ -458,18 +469,19 @@ def get_stations_df(self, sos_url, station_urns_sel=None):
458469
self.log.write(u"\n{station}".format(station=station_fail))
459470
print("{station}".format(station=station_fail))
460471

461-
stations_df = pd.DataFrame.from_records(station_recs, columns=station.keys())
462-
stations_df.index = stations_df['station_urn']
463-
464-
return stations_df
472+
if station_recs:
473+
stations_df = pd.DataFrame.from_records(station_recs, columns=station.keys())
474+
stations_df.index = stations_df['station_urn']
475+
return stations_df
476+
else:
477+
return None
465478

466479
def generate_iso(self, df):
467480
"""
468481
"""
469482

470483
# set up the Jinja2 template:
471484
env = Environment(loader=PackageLoader('sensorml2iso', 'templates'), trim_blocks=True, lstrip_blocks=True, autoescape=True)
472-
# env.filters['sixiteritems'] = six.iteritems
473485
template = env.get_template('sensorml_iso.xml')
474486

475487
for idx, station in df.iterrows():

sensorml2iso/templates/sensorml_iso.xml

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,14 @@
5656
</gmd:dateType>
5757
</gmd:CI_Date>
5858
</gmd:date>
59-
<gmd:citedResponsibleParty xlink:title="contacts_dct.publisher.organization">
59+
<gmd:citedResponsibleParty xlink:title="{{ contacts_dct.publisher.organization }}">
6060
{{ CI_ResponsibleParty(contacts_dct.publisher) }}
6161
</gmd:citedResponsibleParty>
62+
{% if 'operator' in contacts_dct %}
63+
<gmd:citedResponsibleParty xlink:title="{{ contacts_dct.operator.organization }}">
64+
{{ CI_ResponsibleParty(contacts_dct.operator, 'originator') }}
65+
</gmd:citedResponsibleParty>
66+
{% endif %}
6267
<gmd:presentationForm>
6368
<gmd:CI_PresentationFormCode codeList="http://www.isotc211.org/2005/resources/Codelist/gmxCodelists.xml#CI_PresentationFormCode" codeListValue="modelDigital">modelDigital</gmd:CI_PresentationFormCode>
6469
</gmd:presentationForm>
@@ -85,7 +90,7 @@ This station provides the following variables: {{ variables | replace("_", " ")
8590
<gmd:status>
8691
<gmd:MD_ProgressCode codeList="http://www.isotc211.org/2005/resources/Codelist/gmxCodelists.xml#MD_ProgressCode" codeListValue="onGoing">onGoing</gmd:MD_ProgressCode>
8792
</gmd:status>
88-
<gmd:pointOfContact xlink:title="contacts_dct.publisher.organization">
93+
<gmd:pointOfContact xlink:title="{{ contacts_dct.publisher.organization }}">
8994
{{ CI_ResponsibleParty(contacts_dct.publisher, 'pointOfContact') }}
9095
</gmd:pointOfContact>
9196
<gmd:resourceMaintenance>
@@ -183,7 +188,7 @@ This station provides the following variables: {{ variables | replace("_", " ")
183188
</gmd:CI_Date>
184189
</gmd:date>
185190
{% endif %}
186-
<gmd:citedResponsibleParty>
191+
<gmd:citedResponsibleParty xlink:title="{{ contacts_dct.publisher.organization }}">
187192
{{ CI_ResponsibleParty(contacts_dct.publisher) }}
188193
</gmd:citedResponsibleParty>
189194
</gmd:CI_Citation>
@@ -287,6 +292,9 @@ This station provides the following variables: {% for variable in variables %}{{
287292
<gco:CharacterString>{{ format }}</gco:CharacterString>
288293
</gmd:name>
289294
<gmd:version gco:nilReason="unknown"/>
295+
<gmd:specification>
296+
<gco:CharacterString>{{ format }}</gco:CharacterString>
297+
</gmd:specification>
290298
</gmd:MD_Format>
291299
</gmd:distributionFormat>
292300
{% endfor %}

0 commit comments

Comments
 (0)