Skip to content

Commit 6935ebd

Browse files
committed
- initial draft min viable SOS capability when responding to OGC formatted requests over IPC (Broadcasts specifically). See sample/TORGIListener for an example of an app querying TORGI and receiving SOS responses back based on data TORGI extracts from the GeoPackage.
- performance tweaks to heatmap (changed when the map is redrawn; allowed a longer map history; made the map squares a bit bigger)
1 parent b92431b commit 6935ebd

13 files changed

Lines changed: 283 additions & 45 deletions

File tree

sample/TORGIListener/app/src/main/java/tech/plugs/torgilistener/MainActivity.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,20 @@ protected void onCreate(Bundle savedInstanceState) {
2929
bDescribeSensor = findViewById(R.id.sendDescribeSensor);
3030
bGetCapabilities = findViewById(R.id.sendGetCapabilities);
3131
bGetObservations = findViewById(R.id.sendGetObservations);
32-
bDescribeSensor.setOnClickListener(view -> AbstractSOSBroadcastTransceiver.broadcast(MainActivity.this,AbstractSOSBroadcastTransceiver.getOperationDescribeSensor()));
33-
bGetCapabilities.setOnClickListener(view -> AbstractSOSBroadcastTransceiver.broadcast(MainActivity.this,AbstractSOSBroadcastTransceiver.getOperationGetCapabilities()));
34-
bGetObservations.setOnClickListener(view -> AbstractSOSBroadcastTransceiver.broadcast(MainActivity.this,AbstractSOSBroadcastTransceiver.getOperationGetObservations()));
32+
bDescribeSensor.setOnClickListener(view -> broadcast(AbstractSOSBroadcastTransceiver.getOperationDescribeSensor()));
33+
bGetCapabilities.setOnClickListener(view -> broadcast(AbstractSOSBroadcastTransceiver.getOperationGetCapabilities()));
34+
bGetObservations.setOnClickListener(view -> broadcast(AbstractSOSBroadcastTransceiver.getOperationGetObservations()));
3535
transceiver = new SampleTransceiver();
3636
IntentFilter intentFilter = new IntentFilter(AbstractSOSBroadcastTransceiver.ACTION_SOS);
3737
registerReceiver(transceiver, intentFilter);
3838

3939
}
4040

41+
private void broadcast(String value) {
42+
response.setText(getString(R.string.waiting_for_message));
43+
AbstractSOSBroadcastTransceiver.broadcast(MainActivity.this,value);
44+
}
45+
4146
@Override
4247
public void onResume() {
4348
super.onResume();

sample/TORGIListener/app/src/main/res/layout/activity_main.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
android:id="@+id/messgage"
2828
android:layout_width="match_parent"
2929
android:layout_height="wrap_content"
30-
android:text="Waiting for message"
30+
android:text="@string/waiting_for_message"
3131
android:layout_margin="4dp"
3232
android:background="@android:color/white"/>
3333

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
<resources>
22
<string name="app_name">TORGI Listener</string>
3+
<string name="waiting_for_message">Waiting for message…</string>
34
</resources>

torgi/src/main/java/org/sofwerx/torgi/Config.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,18 @@
66
import android.preference.PreferenceManager;
77

88
import java.io.File;
9+
import java.util.UUID;
910

1011
public class Config {
1112
public final static String PREFS_SAVE_DIR = "savedir";
1213
public final static String PREFS_PROCESS_EW = "processew";
14+
public final static String PREFS_UUID = "callsign";
1315

1416
private static Config instance = null;
1517
private String savedDir = null;
1618
private boolean processEWonboard = false;
1719
private SharedPreferences prefs = null;
20+
private String uuid = null;
1821
private Context context;
1922

2023
private Config(Context context) {
@@ -40,6 +43,17 @@ public boolean processEWOnboard() {
4043
return processEWonboard;
4144
}
4245

46+
public String getUuid() {
47+
if (uuid == null) {
48+
uuid = prefs.getString(PREFS_UUID,null);
49+
if (uuid == null) {
50+
uuid = UUID.randomUUID().toString();
51+
prefs.edit().putString(PREFS_UUID,uuid).apply();
52+
}
53+
}
54+
return uuid;
55+
}
56+
4357
public String getSavedDir() {
4458
if (savedDir == null) {
4559
/*savedDir = prefs.getString(PREFS_SAVE_DIR, null);

torgi/src/main/java/org/sofwerx/torgi/ogc/SOSHelper.java

Lines changed: 192 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,22 @@
11
package org.sofwerx.torgi.ogc;
22

3-
import java.text.ParseException;
3+
import android.content.Context;
4+
import android.location.Location;
5+
6+
import org.sofwerx.torgi.Config;
7+
import org.sofwerx.torgi.gnss.helper.GeoPackageSatDataHelper;
8+
9+
import java.io.StringWriter;
410
import java.text.SimpleDateFormat;
5-
import java.util.Date;
11+
import java.util.ArrayList;
612

713
//Built to comply with OGC SOS v2.0, see http://cite.opengeospatial.org/pub/cite/files/edu/sos/text/main.html
814
public class SOSHelper {
15+
private final static SimpleDateFormat dateFormatISO8601 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
16+
public final static String REQUEST_GET_OBSERVATION = "GetObservation";
17+
public final static String REQUEST_DESCRIBE_SENSOR = "DescribeSensor";
18+
public final static String REQUEST_GET_CAPABILITIES = "GetCapabilities";
19+
920
public static String getCapabilities() {
1021
XmlHelper helper = new XmlHelper(new XmlHelper.Tag(
1122
"sos", "Capabilities", null,
@@ -31,6 +42,183 @@ public static String getCapabilities() {
3142
return helper.toString();
3243
}
3344

34-
//TODO respond to DescribeSensor
35-
//TODO respond to GetObservation
45+
//Using the stringwriter based approach due to some XmlSerializer issue with a specific value in the om:result tag
46+
public static String getObservation(ArrayList<GeoPackageSatDataHelper> dps) {
47+
if ((dps == null) || dps.isEmpty())
48+
return null;
49+
StringWriter out = new StringWriter();
50+
out.append("<sos:GetObservationResponse xmlns:sams=\"http://www.opengis.net/samplingSpatial/2.0\">\n");
51+
for (GeoPackageSatDataHelper dp:dps) {
52+
out.append(getObservationTag(dp,2));
53+
}
54+
out.append("</sos:GetObservationResponse>");
55+
56+
return out.toString();
57+
}
58+
59+
//Using the text based approach due to some issue with a specific value in the om:result tag
60+
private static String getObservationTag(GeoPackageSatDataHelper dp, int leadSpaces) {
61+
if (dp == null)
62+
return null;
63+
StringWriter leadWriter = new StringWriter();
64+
if (leadSpaces > 0) {
65+
for (int i=0;i<leadSpaces;i++)
66+
leadWriter.append(' ');
67+
}
68+
final String lead = leadWriter.toString();
69+
StringWriter out = new StringWriter();
70+
out.append(lead+"<sos:observationData>\n");
71+
out.append(lead+" <om:OM_Observation gml:id="+dp.getId()+">\n");
72+
out.append(lead+" <om:type xlink:href=\"http://www.opengis.net/def/observationType/OGC-OM/2.0/OM_Measurement\"/>\n");
73+
out.append(lead+" <om:phenomenonTime>\n");
74+
out.append(lead+" <gml:TimeInstant gml:id=\"phenomenonTime_"+dp.getId()+"\">\n");
75+
out.append(lead+" <gml:timePosition>"+formatTime(dp.getMeassuredTime())+"</gml:timePosition>\n");
76+
out.append(lead+" </gml:TimeInstant>\n");
77+
out.append(lead+" </om:phenomenonTime>\n");
78+
out.append(lead+" <om:resultTime xlink:href=\"#phenomenonTime_"+dp.getId()+"\"/>\n");
79+
out.append(lead+" <om:result xmlns:ns=\"http://www.opengis.net/gml/3.2\" uom=\"dB-Hz\" xsi:type=\"ns:MeasureType\">"+Double.toString(dp.getCn0())+"</om:result>\n");
80+
out.append(lead+" </om:OM_Observation>\n");
81+
out.append(lead+"</sos:observationData>\n");
82+
return out.toString();
83+
}
84+
85+
/*public static String getObservation(ArrayList<GeoPackageSatDataHelper> dps) {
86+
if ((dps == null) || dps.isEmpty())
87+
return null;
88+
ArrayList<XmlHelper.Tag> dataTags = new ArrayList<>();
89+
for (GeoPackageSatDataHelper dp:dps) {
90+
dataTags.add(getObservationTag(dp));
91+
}
92+
XmlHelper.Tag[] dataTagsArray = null;
93+
final int MAX = 1;
94+
int size = dataTags.size();
95+
if (size > MAX)
96+
size = MAX;
97+
if (dataTags != null) {
98+
dataTagsArray = new XmlHelper.Tag[size];
99+
for (int i=0;i<size;i++) {
100+
dataTagsArray[i] = dataTags.get(i);
101+
}
102+
}
103+
XmlHelper helper = new XmlHelper(new XmlHelper.Tag(
104+
"sos","GetObservationResponse",null,
105+
new XmlHelper.Attribute[]{
106+
new XmlHelper.Attribute("xmlns","sams","http://www.opengis.net/samplingSpatial/2.0")},
107+
dataTagsArray));
108+
109+
return helper.toString();
110+
}
111+
112+
//TOGO having a value in the om:result tag is crashing for some reason so trying a different option
113+
private static XmlHelper.Tag getObservationTag(GeoPackageSatDataHelper dp) {
114+
if (dp == null)
115+
return null;
116+
return new XmlHelper.Tag(
117+
"sos","observationData",null,null,
118+
new XmlHelper.Tag[]{
119+
new XmlHelper.Tag(
120+
"om","OM_Observation",null,
121+
new XmlHelper.Attribute[]{
122+
new XmlHelper.Attribute("gml","id",Long.toString(dp.getId()))},
123+
new XmlHelper.Tag[]{
124+
new XmlHelper.Tag("om","type",null, new XmlHelper.Attribute[] {new XmlHelper.Attribute("xlink","href","http://www.opengis.net/def/observationType/OGC-OM/2.0/OM_Measurement")},null),
125+
new XmlHelper.Tag("om","phenomenonTime",null, null,
126+
new XmlHelper.Tag[]{new XmlHelper.Tag("gml","TimeInstant",null, new XmlHelper.Attribute[]{new XmlHelper.Attribute("gml","id","phenomenonTime_"+Long.toString(dp.getId()))},
127+
new XmlHelper.Tag[]{new XmlHelper.Tag("gml","timePosition",formatTime(dp.getMeassuredTime()), null, null)})}),
128+
new XmlHelper.Tag("om","resultTime",null, new XmlHelper.Attribute[] {new XmlHelper.Attribute("xlink","href","#phenomenonTime_"+Long.toString(dp.getId()))},null),
129+
new XmlHelper.Tag("om","result", Double.toString(dp.getCn0()),new XmlHelper.Attribute[]{
130+
new XmlHelper.Attribute("xmlns","ns","http://www.opengis.net/gml/3.2"),
131+
new XmlHelper.Attribute(null,"uom","dB-Hz"),
132+
new XmlHelper.Attribute("xsi","type","ns:MeasureType")},null)
133+
})
134+
}
135+
);
136+
} */
137+
138+
private static String formatTime(long time) {
139+
return dateFormatISO8601.format(time);
140+
}
141+
142+
public static String getDescribeSensor(Context context, Location last) {
143+
StringWriter out = new StringWriter();
144+
145+
out.append("<swes:DescribeSensorResponse>\n" +
146+
" <swes:procedureDescriptionFormat>http://www.opengis.net/sensorML/1.0.1</swes:procedureDescriptionFormat>\n" +
147+
" <swes:description>\n" +
148+
" <swes:SensorDescription>\n" +
149+
" <swes:validTime>\n" +
150+
" <gml:TimePeriod>\n" +
151+
" <gml:beginPosition>"+formatTime(System.currentTimeMillis())+"</gml:beginPosition>\n" +
152+
" <gml:endPosition indeterminatePosition=\"unknown\"/>\n" +
153+
" </gml:TimePeriod>\n" +
154+
" </swes:validTime>\n" +
155+
" <swes:data>\n" +
156+
" <sml:SensorML xmlns:sml=\"http://www.opengis.net/sensorML/1.0.1\" version=\"1.0.1\">\n" +
157+
" <sml:member>\n" +
158+
" <sml:System xmlns:xlink=\"http://www.w3.org/1999/xlink\" xmlns:gml=\"http://www.opengis.net/gml\" xmlns:swe=\"http://www.opengis.net/swe/1.0.1\" xmlns:sos=\"http://www.opengis.net/sos/2.0\">\n" +
159+
" <sml:identification>\n" +
160+
" <sml:IdentifierList>\n" +
161+
" <sml:identifier name=\"uniqueID\">\n" +
162+
" <sml:Term definition=\"urn:ogc:def:identifier:OGC:1.0:uniqueID\">\n" +
163+
" <sml:value>"+ Config.getInstance(context).getUuid()+"</sml:value>\n" +
164+
" </sml:Term>\n" +
165+
" </sml:identifier>\n" +
166+
" <sml:identifier name=\"longName\">\n" +
167+
" <sml:Term definition=\"urn:ogc:def:identifier:OGC:1.0:longName\">\n" +
168+
" <sml:value>TORGI instance "+Config.getInstance(context).getUuid()+"</sml:value>\n" +
169+
" </sml:Term>\n" +
170+
" </sml:identifier>\n" +
171+
" <sml:identifier name=\"shortName\">\n" +
172+
" <sml:Term definition=\"urn:ogc:def:identifier:OGC:1.0:shortName\">\n" +
173+
" <sml:value>TORGI</sml:value>\n" +
174+
" </sml:Term>\n" +
175+
" </sml:identifier>\n" +
176+
" </sml:IdentifierList>\n" +
177+
" </sml:identification>\n" +
178+
" <sml:validTime>\n" +
179+
" <gml:TimePeriod>\n" +
180+
" <gml:beginPosition>"+formatTime(System.currentTimeMillis())+"</gml:beginPosition>\n" +
181+
" <gml:endPosition indeterminatePosition=\"unknown\"/>\n" +
182+
" </gml:TimePeriod>\n" +
183+
" </sml:validTime>\n");
184+
if (last != null) {
185+
out.append(" <sml:position name=\"sensorPosition\">\n" +
186+
" <swe:Position referenceFrame=\"urn:ogc:def:crs:EPSG::4326\">\n" +
187+
" <swe:location>\n" +
188+
" <swe:Vector gml:id=\"STATION_LOCATION\">\n" +
189+
" <swe:coordinate name=\"easting\">\n" +
190+
" <swe:Quantity axisID=\"x\">\n" +
191+
" <swe:uom code=\"degree\"/>\n" +
192+
" <swe:value>"+Double.toString(last.getLongitude())+"</swe:value>\n" +
193+
" </swe:Quantity>\n" +
194+
" </swe:coordinate>\n" +
195+
" <swe:coordinate name=\"northing\">\n" +
196+
" <swe:Quantity axisID=\"y\">\n" +
197+
" <swe:uom code=\"degree\"/>\n" +
198+
" <swe:value>"+Double.toString(last.getLatitude())+"</swe:value>\n" +
199+
" </swe:Quantity>\n" +
200+
" </swe:coordinate>\n");
201+
if (last.hasAltitude()) {
202+
out.append(" <swe:coordinate name=\"altitude\">\n" +
203+
" <swe:Quantity axisID=\"z\">\n" +
204+
" <swe:uom code=\"m\"/>\n" +
205+
" <swe:value>52.0</swe:value>\n" +
206+
" </swe:Quantity>\n" +
207+
" </swe:coordinate>\n");
208+
}
209+
}
210+
out.append(" </swe:Vector>\n" +
211+
" </swe:location>\n" +
212+
" </swe:Position>\n" +
213+
" </sml:position>\n");
214+
out.append(" </sml:System>\n" +
215+
" </sml:member>\n" +
216+
" </sml:SensorML>\n" +
217+
" </swes:data>\n" +
218+
" </swes:SensorDescription>\n" +
219+
" </swes:description>\n" +
220+
"</swes:DescribeSensorResponse>");
221+
222+
return out.toString();
223+
}
36224
}

torgi/src/main/java/org/sofwerx/torgi/ogc/TorgiSOSBroadcastTransceiver.java

Lines changed: 7 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -65,36 +65,25 @@ private void parse(InputStream stream) {
6565
}
6666

6767
private void processParsing(XmlPullParser parser) throws IOException, XmlPullParserException{
68-
//TODO start parsing the choices
69-
/*ArrayList<Player> players = new ArrayList<>();
7068
int eventType = parser.getEventType();
71-
Player currentPlayer = null;
7269

7370
while (eventType != XmlPullParser.END_DOCUMENT) {
74-
String eltName = null;
71+
String eltName;
7572

7673
switch (eventType) {
7774
case XmlPullParser.START_TAG:
7875
eltName = parser.getName();
7976

80-
if ("player".equals(eltName)) {
81-
currentPlayer = new Player();
82-
players.add(currentPlayer);
83-
} else if (currentPlayer != null) {
84-
if ("name".equals(eltName)) {
85-
currentPlayer.name = parser.nextText();
86-
} else if ("age".equals(eltName)) {
87-
currentPlayer.age = parser.nextText();
88-
} else if ("position".equals(eltName)) {
89-
currentPlayer.position = parser.nextText();
90-
}
77+
if (SOSHelper.REQUEST_GET_OBSERVATION.equals(eltName)) {
78+
torgiService.onSOSRequestReceived(eltName);
79+
} else if (SOSHelper.REQUEST_DESCRIBE_SENSOR.equalsIgnoreCase(eltName)) {
80+
torgiService.onSOSRequestReceived(eltName);
81+
} else if (SOSHelper.REQUEST_GET_CAPABILITIES.equalsIgnoreCase(eltName)) {
82+
torgiService.onSOSRequestReceived(eltName);
9183
}
9284
break;
9385
}
94-
9586
eventType = parser.next();
9687
}
97-
98-
printPlayers(players);*/
9988
}
10089
}

torgi/src/main/java/org/sofwerx/torgi/ogc/XmlHelper.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ public XmlHelper(Tag root) {
1717
this.root = root;
1818
}
1919

20+
@Override
2021
public String toString() {
2122
if ((root != null) && (xmlS != null)) {
2223
try {

0 commit comments

Comments
 (0)