Skip to content

Commit 15e65a8

Browse files
author
sensiasoft
committed
Added support for filtering observations by ROI + JUnit test
1 parent 0b5c19e commit 15e65a8

File tree

3 files changed

+187
-15
lines changed

3 files changed

+187
-15
lines changed

sensorhub-core/src/test/java/org/sensorhub/test/persistence/AbstractTestObsStorage.java

Lines changed: 99 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@
2020
import java.util.Collection;
2121
import java.util.HashSet;
2222
import java.util.Iterator;
23+
import java.util.LinkedHashMap;
2324
import java.util.List;
25+
import java.util.Map;
2426
import java.util.Set;
2527
import javax.xml.namespace.QName;
2628
import net.opengis.gml.v32.AbstractFeature;
@@ -39,6 +41,8 @@
3941
import org.sensorhub.test.TestUtils;
4042
import org.vast.ogc.gml.GenericFeatureImpl;
4143
import org.vast.util.Bbox;
44+
import com.vividsolutions.jts.geom.Coordinate;
45+
import com.vividsolutions.jts.geom.Geometry;
4246
import com.vividsolutions.jts.geom.GeometryFactory;
4347
import com.vividsolutions.jts.geom.Polygon;
4448

@@ -59,15 +63,18 @@ public abstract class AbstractTestObsStorage<StorageType extends IObsStorageModu
5963
static String FOI_UID_PREFIX = "urn:domain:features:foi";
6064
static int NUM_FOIS = 100;
6165
GMLFactory gmlFac = new GMLFactory(true);
66+
Map<String, AbstractFeature> allFeatures;
6267

6368
static String[] FOI_SET1_IDS = new String[]
6469
{
65-
FOI_UID_PREFIX + "001",
66-
FOI_UID_PREFIX + "002",
67-
FOI_UID_PREFIX + "003"
70+
FOI_UID_PREFIX + "1",
71+
FOI_UID_PREFIX + "2",
72+
FOI_UID_PREFIX + "3",
73+
FOI_UID_PREFIX + "4",
74+
FOI_UID_PREFIX + "15"
6875
};
6976

70-
static int[] FOI_SET1_STARTS = new int[] {0, 20, 60};
77+
static int[] FOI_SET1_STARTS = new int[] {0, 20, 25, 40, 60};
7178

7279

7380
String[] FOI_IDS = FOI_SET1_IDS;
@@ -77,6 +84,7 @@ public abstract class AbstractTestObsStorage<StorageType extends IObsStorageModu
7784
protected void addFoisToStorage() throws Exception
7885
{
7986
storage.setAutoCommit(false);
87+
allFeatures = new LinkedHashMap<String, AbstractFeature>(NUM_FOIS);
8088

8189
for (int foiNum = 1; foiNum <= NUM_FOIS; foiNum++)
8290
{
@@ -89,6 +97,7 @@ protected void addFoisToStorage() throws Exception
8997
Point p = gmlFac.newPoint();
9098
p.setPos(new double[] {foiNum, foiNum, 0.0});
9199
foi.setLocation(p);
100+
allFeatures.put(foi.getUniqueIdentifier(), foi);
92101
storage.storeFoi(producerID, foi);
93102
}
94103

@@ -278,6 +287,36 @@ protected IObsFilter buildFilterByFoiID(DataComponent recordDef, List<DataBlock>
278287
}
279288

280289

290+
protected IObsFilter buildFilterByRoi(DataComponent recordDef, List<DataBlock> dataList, final Polygon roi)
291+
{
292+
// get list of FOIs within roi
293+
ArrayList<Integer> foiIndexList = new ArrayList<Integer>();
294+
int fIndex = 0;
295+
for (String foiID: FOI_IDS)
296+
{
297+
AbstractFeature f = allFeatures.get(foiID);
298+
if (roi.intersects((Geometry)f.getLocation()))
299+
foiIndexList.add(fIndex);
300+
fIndex++;
301+
}
302+
303+
// then just filter dataList using list of indexes
304+
int i = 0;
305+
int[] foiIndexes = new int[foiIndexList.size()];
306+
for (int index: foiIndexList)
307+
foiIndexes[i++] = index;
308+
buildFilterByFoiID(recordDef, dataList, foiIndexes);
309+
310+
// generate filter
311+
IObsFilter filter = new ObsFilter(recordDef.getName()) {
312+
public Polygon getRoi() { return roi; }
313+
public Collection<String> getProducerIDs() {return producerFilterList; };
314+
};
315+
316+
return filter;
317+
}
318+
319+
281320
protected IObsFilter buildFilterByFoiIDAndTime(DataComponent recordDef, List<DataBlock> dataList, int[] foiIndexes, final double[] timeRange)
282321
{
283322
final Set<String> foiSet = new HashSet<String>();
@@ -346,7 +385,8 @@ protected void checkFilteredResults(IObsFilter filter, List<DataBlock> dataList)
346385
{
347386
IDataRecord dbRec = it2.next();
348387
TestUtils.assertEquals(dataList.get(i), dbRec.getData());
349-
assertTrue(filter.getFoiIDs().contains(((ObsKey)dbRec.getKey()).foiID));
388+
if (filter.getFoiIDs() != null)
389+
assertTrue(filter.getFoiIDs().contains(((ObsKey)dbRec.getKey()).foiID));
350390
i++;
351391
}
352392

@@ -460,6 +500,60 @@ public void testGetRecordsForMultipleFoiIDsAndTime() throws Exception
460500
testList.addAll(dataList);
461501
filter = buildFilterByFoiIDAndTime(recordDef, testList, new int[] {2, 1}, timeRange);
462502
checkFilteredResults(filter, testList);
503+
}
504+
505+
506+
@Test
507+
public void testGetRecordsByRoi() throws Exception
508+
{
509+
addFoisToStorage();
510+
511+
DataComponent recordDef = createDs2();
512+
List<DataBlock> dataList = addObservationsWithFoiToStorage(recordDef);
513+
List<DataBlock> testList = new ArrayList<DataBlock>(dataList.size());
514+
IObsFilter filter;
515+
Polygon roi;
516+
517+
// FOI 1
518+
testList.clear();
519+
testList.addAll(dataList);
520+
roi = new GeometryFactory().createPolygon(new Coordinate[] {
521+
new Coordinate(0.5, 0.5),
522+
new Coordinate(0.5, 1.5),
523+
new Coordinate(1.5, 1.5),
524+
new Coordinate(1.5, 0.5),
525+
new Coordinate(0.5, 0.5)
526+
});
527+
filter = buildFilterByRoi(recordDef, testList, roi);
528+
checkFilteredResults(filter, testList);
529+
530+
// FOIs 1 + 3
531+
testList.clear();
532+
testList.addAll(dataList);
533+
roi = new GeometryFactory().createPolygon(new Coordinate[] {
534+
new Coordinate(0.5, 0.5),
535+
new Coordinate(0.5, 3.5),
536+
new Coordinate(3.5, 3.5),
537+
new Coordinate(3.5, 2.5),
538+
new Coordinate(1.5, 2.5),
539+
new Coordinate(1.5, 0.5),
540+
new Coordinate(0.5, 0.5)
541+
});
542+
filter = buildFilterByRoi(recordDef, testList, roi);
543+
checkFilteredResults(filter, testList);
544+
545+
// FOIs 1-4
546+
testList.clear();
547+
testList.addAll(dataList);
548+
roi = new GeometryFactory().createPolygon(new Coordinate[] {
549+
new Coordinate(0.0, 0.0),
550+
new Coordinate(0.0, 4.0),
551+
new Coordinate(4.0, 4.0),
552+
new Coordinate(4.0, 0.0),
553+
new Coordinate(0.0, 0.0)
554+
});
555+
filter = buildFilterByRoi(recordDef, testList, roi);
556+
checkFilteredResults(filter, testList);
463557
}
464558

465559
}

sensorhub-storage-perst/src/main/java/org/sensorhub/impl/persistence/perst/FeatureStoreImpl.java

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
import org.sensorhub.api.persistence.IFeatureStorage;
2929
import org.vast.util.Bbox;
3030
import com.vividsolutions.jts.geom.Envelope;
31+
import com.vividsolutions.jts.geom.Geometry;
32+
import com.vividsolutions.jts.geom.Polygon;
3133

3234

3335
/**
@@ -74,7 +76,8 @@ public Bbox getFeaturesSpatialExtent()
7476

7577
public Iterator<String> getFeatureIDs(IFeatureFilter filter)
7678
{
77-
// TODO optimize implementation to avoid loading whole feature objects
79+
// could we optimize implementation to avoid loading whole feature objects?
80+
// -> not worth it since with spatial filter we need to read geometries anyway
7881

7982
final Iterator<AbstractFeature> it = getFeatures(filter);
8083

@@ -139,14 +142,49 @@ public void remove()
139142
// case of ROI
140143
if (filter.getRoi() != null)
141144
{
145+
final Polygon roi = filter.getRoi();
146+
142147
// iterate through spatial index using bounding rectangle
143-
// TODO filter on exact polygon geometry using JTS
144-
Envelope env = filter.getRoi().getEnvelopeInternal();
148+
Envelope env = roi.getEnvelopeInternal();
145149
double[] coords = new double[] {env.getMinX(), env.getMinY(), Double.NEGATIVE_INFINITY, env.getMaxX(), env.getMaxY(), Double.POSITIVE_INFINITY};
146-
return geoIndex.iterator(new RectangleRn(coords));
150+
final Iterator<AbstractFeature> it = geoIndex.iterator(new RectangleRn(coords));
151+
152+
// wrap with iterator to filter on exact polygon geometry using JTS
153+
Iterator<AbstractFeature> it2 = new Iterator<AbstractFeature>()
154+
{
155+
AbstractFeature nextFeature;
156+
157+
public boolean hasNext()
158+
{
159+
return (nextFeature != null);
160+
}
161+
162+
public AbstractFeature next()
163+
{
164+
AbstractFeature currentFeature = nextFeature;
165+
nextFeature = null;
166+
167+
while (nextFeature == null && it.hasNext())
168+
{
169+
AbstractFeature f = it.next();
170+
Geometry geom = (Geometry)f.getLocation();
171+
if (geom != null && roi.intersects(geom))
172+
nextFeature = f;
173+
}
174+
175+
return currentFeature;
176+
}
177+
178+
public void remove()
179+
{
180+
}
181+
};
182+
183+
it2.next();
184+
return it2;
147185
}
148186

149-
// TODO handle ROI + IDs?
187+
// TODO handle ROI + IDs but it's not very useful in practice
150188

151189
return idIndex.iterator();
152190
}

sensorhub-storage-perst/src/main/java/org/sensorhub/impl/persistence/perst/ObsSeriesImpl.java

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
package org.sensorhub.impl.persistence.perst;
1616

17+
import java.util.ArrayList;
1718
import java.util.Collection;
1819
import java.util.Iterator;
1920
import java.util.Map.Entry;
@@ -26,10 +27,13 @@
2627
import org.garret.perst.Key;
2728
import org.garret.perst.Storage;
2829
import org.sensorhub.api.persistence.DataKey;
30+
import org.sensorhub.api.persistence.FeatureFilter;
2931
import org.sensorhub.api.persistence.IDataFilter;
32+
import org.sensorhub.api.persistence.IFeatureFilter;
3033
import org.sensorhub.api.persistence.IObsFilter;
3134
import org.sensorhub.api.persistence.ObsKey;
3235
import org.sensorhub.impl.persistence.perst.FoiTimesStoreImpl.FoiTimePeriod;
36+
import com.vividsolutions.jts.geom.Polygon;
3337

3438

3539
/**
@@ -64,12 +68,44 @@ private ObsSeriesImpl() {}
6468
}
6569

6670

67-
Set<FoiTimePeriod> getFoiTimePeriods(IDataFilter filter)
71+
Set<FoiTimePeriod> getFoiTimePeriods(final IDataFilter filter)
6872
{
69-
// FOI ID list
73+
// extract FOI filters if any
7074
Collection<String> foiIDs = null;
75+
Polygon roi = null;
7176
if (filter instanceof IObsFilter)
77+
{
7278
foiIDs = ((IObsFilter)filter).getFoiIDs();
79+
roi = ((IObsFilter) filter).getRoi();
80+
}
81+
82+
// if using spatial filter, first get matching FOI IDs
83+
// and then follow normal process
84+
if (roi != null)
85+
{
86+
IFeatureFilter foiFilter = new FeatureFilter()
87+
{
88+
public Collection<String> getFeatureIDs()
89+
{
90+
return ((IObsFilter)filter).getFoiIDs();
91+
}
92+
93+
public Polygon getRoi()
94+
{
95+
return ((IObsFilter) filter).getRoi();
96+
}
97+
};
98+
99+
Iterator<String> foiIt = getFeatureStore().getFeatureIDs(foiFilter);
100+
Collection<String> allFoiIDs = new ArrayList<String>(100);
101+
if (foiIDs != null)
102+
allFoiIDs.addAll(foiIDs);
103+
while (foiIt.hasNext())
104+
allFoiIDs.add(foiIt.next());
105+
foiIDs = allFoiIDs;
106+
}
107+
108+
// get time periods for list of FOIs
73109
Set<FoiTimePeriod> foiTimes = foiTimesStore.getSortedFoiTimes(foiIDs);
74110

75111
// trim periods to filter time range if specified
@@ -94,8 +130,6 @@ Set<FoiTimePeriod> getFoiTimePeriods(IDataFilter filter)
94130
}
95131
}
96132

97-
// TODO FOI spatial filter
98-
99133
return foiTimes;
100134
}
101135

@@ -133,7 +167,7 @@ public final void remove()
133167

134168
protected IteratorWithFoi getEntryIterator(IDataFilter filter)
135169
{
136-
// FoI ID list
170+
// get time periods formatching FOIs
137171
final Set<FoiTimePeriod> foiTimePeriods = getFoiTimePeriods(filter);
138172

139173
// scan through each time range sequentially
@@ -174,6 +208,12 @@ public String getCurrentFoiID()
174208
}
175209
};
176210
}
211+
212+
213+
protected FeatureStoreImpl getFeatureStore()
214+
{
215+
return ((ObsStorageRoot)getStorage().getRoot()).featureStore;
216+
}
177217

178218

179219
@Override

0 commit comments

Comments
 (0)