You can store new observations in istSOS using various methods:
Even if you can use the csv2istsos.py script to facilitate the data loading, users may also use the SOS insertObservation request directly.
To execute the XML request from the interface:
- Open the requests test page: http://localhost/istsos/admin/requests
- Select the desired service instance
- Choose the “POST” option
- Paste into the field the InsertObservation xml
- Press “Send”
Note
Pay attention to the AssignedSensorId parameter: this according to the standard is returned by the system only when the sensor is registered. To access it, you can use administration interface, looking at the procedure metadata details.
Example
For example, a valid request for loading observations to a procedure named LOCARNO which is observing rainfall and temperature for the time inteval 2014-06-03T15:08:00Z/2014-06-03T15:48:00Z and specifying the respective qualityIndex for each measure, the request is:
<?xml version="1.0" encoding="UTF-8"?>
<sos:InsertObservation
xmlns:gml="http://www.opengis.net/gml"
xmlns:om="http://www.opengis.net/om/1.0"
xmlns:sos="http://www.opengis.net/sos/1.0"
xmlns:swe="http://www.opengis.net/swe"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://schemas.opengis.net/sos/1.0.0/sosAll.xsd"
service="SOS" version="1.0.0">
<sos:AssignedSensorId>xxxxxxxxxxxxxxxxxxxxxxxxxxx</sos:AssignedSensorId>
<om:Observation>
<om:procedure xlink:href="urn:ogc:def:procedure:x-istsos:1.0:LOCARNO"/>
<om:samplingTime>
<gml:TimePeriod>
<gml:beginPosition>2014-06-03T15:08:00Z</gml:beginPosition>
<gml:endPosition>2014-06-03T15:48:00Z</gml:endPosition>
</gml:TimePeriod>
</om:samplingTime>
<om:observedProperty>
<swe:CompositePhenomenon dimension="5">
<swe:component xlink:href="urn:ogc:def:parameter:x-istsos:1.0:time:iso8601"/>
<swe:component xlink:href="urn:ogc:def:parameter:x-istsos:1.0:meteo:air:rainfall"/>
<swe:component
xlink:href="urn:ogc:def:parameter:x-istsos:1.0:meteo:air:rainfall:qualityIndex"/>
<swe:component
xlink:href="urn:ogc:def:parameter:x-istsos:1.0:meteo:air:temperature"/>
<swe:component
xlink:href="urn:ogc:def:parameter:x-istsos:1.0:meteo:air:temperature:qualityIndex"/>
</swe:CompositePhenomenon>
</om:observedProperty>
<om:featureOfInterest xlink:href="urn:ogc:def:feature:x-istsos:1.0:Point:LOCARNO"/>
<om:result>
<swe:DataArray>
<swe:elementCount>
<swe:value>5</swe:value>
</swe:elementCount>
<swe:elementType name="SimpleDataArray">
<swe:DataRecord definition="urn:ogc:def:dataType:x-istsos:1.0:timeSeries">
<swe:field name="Time">
<swe:Time definition="urn:ogc:def:parameter:x-istsos:1.0:time:iso8601"/>
</swe:field>
<swe:field name="air-rainfall">
<swe:Quantity definition="urn:ogc:def:parameter:x-istsos:1.0:meteo:air:rainfall">
<swe:uom code="mm"/>
</swe:Quantity>
</swe:field>
<swe:field name="air-rainfall:qualityIndex">
<swe:Quantity definition="urn:ogc:def:parameter:x-istsos:1.0:meteo:air:rainfall:qualityIndex">
<swe:uom code="-"/>
</swe:Quantity>
</swe:field>
<swe:field name="air-temperature">
<swe:Quantity definition="urn:ogc:def:parameter:x-istsos:1.0:meteo:air:temperature">
<swe:uom code="\xc2\xb0C"/>
</swe:Quantity>
</swe:field>
<swe:field name="air-temperature:qualityIndex">
<swe:Quantity definition="urn:ogc:def:parameter:x-istsos:1.0:meteo:air:temperature:qualityIndex">
<swe:uom code="-"/>
</swe:Quantity>
</swe:field>
</swe:DataRecord>
</swe:elementType>
<swe:encoding>
<swe:TextBlock blockSeparator="@" decimalSeparator="." tokenSeparator=","/>
</swe:encoding>
<swe:values>
2014-06-03T14:10:00+0200,0.000000,200,20.000000,200@
2014-06-03T14:20:00+0200,0.000000,200,20.100000,200@
2014-06-03T14:30:00+0200,0.000000,200,20.200000,200@
2014-06-03T14:40:00+0200,0.000000,200,20.500000,200@
2014-06-03T14:50:00+0200,0.000000,200,20.500000,200@
2014-06-03T15:00:00+0200,0.000000,200,20.400000,200@
2014-06-03T15:10:00+0200,0.000000,200,20.400000,200@
2014-06-03T15:20:00+0200,0.100000,200,19.600000,200@
2014-06-03T15:30:00+0200,0.100000,200,19.100000,200@
2014-06-03T15:40:00+0200,0.000000,200,19.000000,200@
2014-06-03T15:50:00+0200,0.000000,200,20.600000,200
</swe:values>
</swe:DataArray>
</om:result>
</om:Observation>
</sos:InsertObservation>
Using this script you should prepare text files with sensor data formatted according to text/csv;subtype=istSOS. This format is a CSV represented by a header as the first line containing the URI names of the observed properties, the following lines contains the data.
Example of a text/csv;subtype=istSOS
File name: PROCEDURENAME_YYYYMMDDhhmmss.dat
urn:ogc:def:parameter:x-istsos:1.0:time:iso8601,urn:ogc:def:parameter:x-istsos:1.0:meteo:air:temperature
2013-01-01T00:10:00.000000+0100,0.446000
2013-01-01T00:20:00.000000+0100,0.862000
2013-01-01T00:30:00.000000+0100,0.932000
2013-01-01T00:40:00.000000+0100,0.384000
Note
Pay attention to the file name: there is a timestamp (YYYYMMDDhhmmss GMT+0:00). This parameter is used to force the endPosition in the sampling time of a procedure. This is particularly important when the procedure is an irregular time series.
Think of tipping bucket rain gauge, when there is no rain no data are sent. But updating the endPosition we will be able to know that the sensor is working and that there is no rain, instead of thinking that the sensor is not transmitting or that it is broken.
To load the prepared CSV you should run the csv2istsos.py command which is under the script folder of your installation location (e.g.: /usr/local/istsos)
Note
The “csv2istsos.py“ file, is a python script that makes use of the WA-REST features of istSOS to insert observations.
python scripts/csv2istsos.py --help
usage: csv2istsos.py [-h] [-v] [-t] -p procedures [procedures ...]
[-q quality index] [-u url] -s service
-w working directory [-e file extension]
[-usr user name] [-pwd password]
Import data from a csv file.
optional arguments:
-h, --help Show this help message and exit
-v, --verbose Activate verbose debug
-t, --test Use to test the command, deactivating the insert
observation operations.
-p procedures [procedures ...]
List of procedures to be aggregated.
-q quality index The quality index to set for all the measures of
the CSV file, if not set into the CSV.
(default: 100).
-u url IstSOS Server address IP (or domain name) used for
all request. (default: http://localhost:80/istsos).
-s service The name of the service instance.
-w working directory Working directory where the csv files are located.
-e file extension Extension of the CSV file. (default: .dat)
-usr user name
-pwd password
Example
For loading all the CSV files in the folder ~/Desktop/dataset referring to the sensor T_LUGANO of the SOS service named demo at the URL http://localhost/istsos
cd /usr/local/istsos
python scripts/csv2istsos.py -p T_LUGANO \
-u http://localhost/istsos -s demo \
-w ~/Desktop/dataset
Most of the sensor vendors doesn’t really care about interoperability, so sensor owners have to handle incredibly imaginative file formats. Luckily very often output files are in tabular data format (numbers and text) in plain text (CSV-like) that can be reverse engineered.
istSOS have some utility Python classes that can be used to implement a custom converter. In the next paragraphs there are some implementation that uses the raw2istsos.py base class.
If you want to implement something more specific just can take inspiration of the converters that we have prepared and implement one by yourself, and if you want to share we will be happy to add your converter inside the istSOS package.
File name: maggia_20150612112000.dat
File content:
2015-06-12 09:40:00,0.237
2015-06-12 09:50:00,0.234
2015-06-12 10:00:00,0.237
2015-06-12 10:10:00,0.236
2015-06-12 10:20:00,0.234
2015-06-12 10:30:00,0.237
2015-06-12 10:40:00,0.236
2015-06-12 10:50:00,0.232
2015-06-12 11:00:00,0.236
2015-06-12 11:10:00,0.232
2015-06-12 11:20:00,0.233
This is a quite an understandable format:
This characteristics can be configured like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | from scripts.converter import csv
# Configuring the Converter
conv = csv.CsvImporter('MAGGIA', {
"headrows": 0,
"separator": ",",
"filenamedate": {
"format": '%Y%m%d%H%M%S',
"remove": ['maggia_', '.dat']
},
"datetime": {
"column": 0,
"format": '%Y-%m-%d %H:%M:%S',
"tz": '+01:00'
},
"observations": [{
"observedProperty": "urn:ogc:def:parameter:x-istsos:1.0:river:water:height",
"column": 1
}]
},
'http://localhost/istsos', 'demo',
'/data/maggia', 'maggia_*.dat',
debug=True,
archivefolder='/data/archive/maggia'
)
# Converting raw data to text/csv;subtype=istSOS
if conv.execute():
# Send observation to istSOS
conv.csv2istsos()
|
Another example:
A procedure that measures two different observations.
File name: Calcaccia_A_1503261100.dat
Identificazione: Calcaccia A SN/TD: 739***/MTO-102 Firmware: FW1.18
Valore minimo 0.123 mWS Valore massimo 0.142 mWS Valore medio 0.536 mWS Pressure_Type="rel"
Ora Data Pressione [mWS] Conta-impulsi [mm]
09:50:00 26.03.2015 0.140 4.4
10:00:00 26.03.2015 0.137 4.4
10:10:00 26.03.2015 0.139 4.4
10:20:00 26.03.2015 0.141 4.4
10:30:00 26.03.2015 0.139 4.4
10:40:00 26.03.2015 0.139 4.4
10:50:00 26.03.2015 0.139 4.5
11:00:00 26.03.2015 0.139 4.5
Observing the content we can see:
This characteristics can be configured like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | from scripts.converter import csv
conv = csv.CsvImporter('CALCACCIA', {
"headrows": 3,
"separator": "\t",
"filenamedate": {
"format": '%y%m%d%H%M',
"remove": ['Calcaccia_A_','.dat']
},
"datetime": {
"tz": '+01:00',
"time": {
"column": 0,
"format": '%H:%M:%S'
},
"date": {
"column": 1,
"format": '%d.%m.%Y'
}
},
"observations": [{
"observedProperty": "urn:ogc:def:parameter:x-istsos:1.0:meteo:air:pressure",
"column": 2
},{
"observedProperty": "urn:ogc:def:parameter:x-istsos:1.0:pulse",
"column": 3
}]
},
'http://localhost/istsos', 'demo',
"/data/calcaccia", 'Calcaccia_A_*.dat',
debug=True,
archivefolder='/data/archive/calcaccia'
)
# Converting raw data to text/csv;subtype=istSOS
if conv.execute():
# Send observation to istSOS
conv.csv2istsos()
|
File name: 16_2015-07-08_182000.dat
File content:
101,2015,189,1800,13.85,41.5,30.3,53.54,979
101,2015,189,1810,13.85,41.45,29.11,61.27,979
99,2015,189,1811,27
99,2015,189,1812,40
99,2015,189,1813,43
99,2015,189,1814,37
99,2015,189,1815,31
101,2015,189,1820,13.85,40.99,26.2,81.3,980
In this file there are more than one observed property from a meteo station with multiple sensors.
Let’s analyse the output format:
Supponing that raw files are stored into a local folder (e.g. /data/lugano) and we want to insert some observations for the procedure T_LUGANO (air temperature) we should implement something like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | from scripts.converter import campbell
# Configuring the Converter
conv = campbell.CampbellImporter('T_LUGANO', {
'rowid': 101,
'observedProperty': 'urn:ogc:def:parameter:x-istsos:1.0:meteo:air:temperature',
'column': 6,
'date': [1,2,3],
'tz': '+01:00'
},
'http://localhost/istsos', 'demo',
'/data/lugano', '16_*',
debug=True,
archivefolder='/data/archive/lugano'
)
# Converting raw data to text/csv;subtype=istSOS
if conv.execute():
# Send observation to istSOS
conv.csv2istsos()
|
The CampbellImporter class (script/converter/campbell.py) is an extension of the basic class Converter (script/raw2istsos.py).
File name: 100538_WTemp.csv
File content:
100538 Vecchio Vedeggio;WTemp C
data_timemeas;data_valueend
2014-12-09 14:30:00;12.526
2014-12-09 14:40:00;12.608
2014-12-09 14:50:01;12.646
2014-12-09 15:00:00;12.667
100538 Vecchio Vedeggio;WTemp C
data_timemeas;data_valueend
2014-12-09 15:10:00;12.682
2014-12-09 15:20:01;12.696
2014-12-09 15:30:00;12.707
2014-12-09 15:40:01;12.714
2014-12-09 15:50:00;12.722
2014-12-09 16:00:00;12.728
This file contains only one observed property. The StsImporter class (script/sts.py) is specifically designed to import this kind of files. Rows that does not contains data are skipped during the importation.
1 2 3 4 5 6 7 8 9 10 11 | from scripts.converter import sts
cov = sts.StsImporter('WT_VVED_AGN', {
"tz": "+01:00"
},
'http://localhost/istsos', 'demo',
'/data/sts', '100538_WTemp.csv',
debug=True,
archivefolder='/data/archive/vved_agn'
)
if cov.execute():
cov.csv2istsos()
|
Kern file format uses some control characters (Start of Heading /SOH/, Start of Text /STX/, End Of Text /ETX/, end-of-transmission /EOT/, etc.).
File name: HBTIa-17_15_271730_10.csv
File content:
/SOH/TI 2015 271720 000010 HBTIa 17 CUCCIO PORLEZZA
Q 1 1 0 1 LIVELLO
Q 2 3 0 1 TEMPERATURA
Q 3 23 0 1 BATTERIA
/STX/D 271730 1 10 0.377 2 10 18.08 3 10 13.28
/ETX/EOT/
The KernImporter class (script/kern.py) is specialized to import this kind of files, handling all the control characters.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | from scripts.converter import kern
cov = kern.KernImporter('A_CUC_POR', {
"tz": "+01:00",
"observations": {
"observedProperty": "urn:ogc:def:parameter:x-istsos:1.0:river:water:height",
"column": 4
}
},
'http://localhost/istsos', 'sosraw',
"/data/kern", 'HBTIa-17_*',
debug=True,
archivefolder='/data/archive/cuc_por'
)
if cov.execute():
cov.csv2istsos()
|