package org.geotools.data.aggregate; import static org.junit.Assert.*; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.Executors; import org.geotools.data.Query; import org.geotools.data.simple.SimpleFeatureCollection; import org.geotools.data.simple.SimpleFeatureIterator; import org.geotools.factory.CommonFactoryFinder; import org.geotools.feature.FeatureTypes; import org.geotools.feature.NameImpl; import org.geotools.feature.simple.SimpleFeatureTypeBuilder; import org.geotools.geometry.jts.ReferencedEnvelope; import org.geotools.referencing.CRS; import org.junit.Test; import org.opengis.feature.simple.SimpleFeature; import org.opengis.feature.simple.SimpleFeatureType; import org.opengis.filter.Filter; import org.opengis.filter.FilterFactory2; import org.opengis.filter.sort.SortBy; import org.opengis.filter.sort.SortOrder; /** * * * @source $URL$ */ public class AggregatingDataStoreTest extends AbstractAggregatingStoreTest { private static final String ROAD_SEGMENTS = "RoadSegments"; private static final String BASIC_POLYGONS = "BasicPolygons"; AggregatingDataStore store = new AggregatingDataStore(repository, Executors.newCachedThreadPool()); FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2(null); @Test public void testStoreConfiguration() throws IOException { store.autoConfigureStores(Arrays.asList("store1", "store2", "gt:store3")); // check we have the expected set of types, in the expected order Set<String> types = new LinkedHashSet<String>(); types.addAll(Arrays.asList(store1.getTypeNames())); types.addAll(Arrays.asList(store2.getTypeNames())); types.addAll(Arrays.asList(store3.getTypeNames())); String[] expected = (String[]) types.toArray(new String[types.size()]); assertArrayEquals(expected, store.getTypeNames()); // grab the BasicPolygon type map and check it looks fine AggregateTypeConfiguration config = store.getConfigurations().get(BASIC_POLYGONS); assertEquals(BASIC_POLYGONS, config.getName()); assertEquals(2, config.getSourceTypes().size()); assertSingleSourceType(BASIC_POLYGONS, new NameImpl("store1"), config); assertSingleSourceType(BASIC_POLYGONS, new NameImpl("store2"), config); // grab the Streams type map and check it looks fine config = store.getConfigurations().get("Streams"); assertEquals("Streams", config.getName()); assertEquals(1, config.getSourceTypes().size()); assertSingleSourceType("Streams", new NameImpl("store2"), config); // grab the RoadSegments type map and check it looks fine config = store.getConfigurations().get(ROAD_SEGMENTS); assertEquals(ROAD_SEGMENTS, config.getName()); assertEquals(2, config.getSourceTypes().size()); assertSingleSourceType(ROAD_SEGMENTS, new NameImpl("store1"), config); assertSingleSourceType(ROAD_SEGMENTS, new NameImpl("gt", "store3"), config); } @Test public void testFeatureType() throws IOException { store.autoConfigureStores(Arrays.asList("store1", "store2", "gt:store3")); assertEquals(store1.getSchema(BASIC_POLYGONS), store.getSchema(BASIC_POLYGONS)); assertEquals(store1.getSchema(ROAD_SEGMENTS), store.getSchema(ROAD_SEGMENTS)); // store2 has one property more in basic polygons store.resetConfiguration(); store.autoConfigureStores(Arrays.asList("store2", "gt:store3", "store1")); assertEquals(store2.getSchema(BASIC_POLYGONS), store.getSchema(BASIC_POLYGONS)); // store3 has one property less in road segments store.resetConfiguration(); store.autoConfigureStores(Arrays.asList("gt:store3", "store2", "store1")); assertEquals(store3.getSchema(ROAD_SEGMENTS), store.getSchema(ROAD_SEGMENTS)); } @Test public void testCount() throws IOException { // just the first store store.autoConfigureStores(Arrays.asList("store1")); assertEquals(3, store.getFeatureSource(BASIC_POLYGONS).getCount(Query.ALL)); // add all stores store.resetConfiguration(); store.autoConfigureStores(Arrays.asList("store1", "store2", "gt:store3")); assertEquals(4, store.getFeatureSource(BASIC_POLYGONS).getCount(Query.ALL)); } @Test public void testBoundAll() throws Exception { // just the first store store.autoConfigureStores(Arrays.asList("store1")); assertEquals(new ReferencedEnvelope(-2, 2, -1, 6, CRS.decode("EPSG:4326")), store .getFeatureSource(BASIC_POLYGONS).getBounds(Query.ALL)); // add all stores store.resetConfiguration(); store.autoConfigureStores(Arrays.asList("store1", "store2", "gt:store3")); assertEquals(new ReferencedEnvelope(-2, 4, -1, 6, CRS.decode("EPSG:4326")), store .getFeatureSource(BASIC_POLYGONS).getBounds(Query.ALL)); // get just one feature, it's only in the first store Filter eq1 = ff.equals(ff.property("ID"), ff.literal("two")); assertEquals(new ReferencedEnvelope(-2, 1, 3, 6, CRS.decode("EPSG:4326")), store .getFeatureSource(BASIC_POLYGONS).getBounds(new Query(null, eq1))); // get just one feature, it's only in the second store Filter eq2 = ff.equals(ff.property("ID"), ff.literal("four")); assertEquals(new ReferencedEnvelope(2, 4, 2, 4, CRS.decode("EPSG:4326")), store .getFeatureSource(BASIC_POLYGONS).getBounds(new Query(null, eq2))); } @Test public void testBoundMixed() throws Exception { AggregateTypeConfiguration config = new AggregateTypeConfiguration(BASIC_POLYGONS); config.addSourceType("store1", "BasicPolygons"); config.addSourceType("store2", "BasicPolygons4269"); store.addType(config); assertEquals(new ReferencedEnvelope(-2, 4, -1, 6, CRS.decode("EPSG:4326")), store .getFeatureSource(BASIC_POLYGONS).getBounds(Query.ALL)); } @Test public void testReadAll() throws Exception { store.autoConfigureStores(Arrays.asList("store1")); Map<String, SimpleFeature> features = collectFeatures(new Query(BASIC_POLYGONS)); assertEquals(3, features.size()); store.resetConfiguration(); store.autoConfigureStores(Arrays.asList("store1", "store2", "gt:store3")); features = collectFeatures(new Query("BasicPolygons")); assertSchema(features.values(), store1.getSchema(BASIC_POLYGONS)); assertEquals(4, features.size()); } @Test public void testReadSorted() throws Exception { store.autoConfigureStores(Arrays.asList("store1", "store2")); Query q = new Query(BASIC_POLYGONS); q.setSortBy(new SortBy[] { ff.sort("ID", SortOrder.DESCENDING) }); List<SimpleFeature> features = listFeatures(q); assertEquals(4, features.size()); String prev = null; for (SimpleFeature f : features) { String id = (String) f.getAttribute("ID"); if(prev != null) { assertTrue(prev.compareTo(id) >= 0); } prev = id; } } @Test public void testReadInvalidStore() throws Exception { store.resetConfiguration(); AggregateTypeConfiguration config = new AggregateTypeConfiguration(BASIC_POLYGONS); config.addSourceType("store1", BASIC_POLYGONS); config.addSourceType("store3", "ABCDE"); store.addType(config); try { store.getFeatureSource(BASIC_POLYGONS).getBounds(); fail("Should have thrown an exception, the store is not tolerant"); } catch (Exception e) { // fine } try { store.getFeatureSource(BASIC_POLYGONS).getCount(new Query(BASIC_POLYGONS)); fail("Should have thrown an exception, the store is not tolerant"); } catch (Exception e) { // fine } try { collectFeatures(new Query(BASIC_POLYGONS)); fail("Should have thrown an exception, the store is not tolerant"); } catch (Exception e) { // fine } } void assertSchema(Collection<SimpleFeature> features, SimpleFeatureType schema) { for (SimpleFeature sf : features) { assertEquals(schema, sf.getFeatureType()); } } @Test public void testReadFiltered() throws Exception { Query q = new Query(BASIC_POLYGONS); q.setFilter(ff.greater(ff.function("strLength", ff.property("ID")), ff.literal(3))); store.resetConfiguration(); store.autoConfigureStores(Arrays.asList("store1")); Map<String, SimpleFeature> features = collectFeatures(q); assertSchema(features.values(), store1.getSchema(BASIC_POLYGONS)); assertEquals(1, features.size()); store.resetConfiguration(); store.autoConfigureStores(Arrays.asList("store1", "store2", "gt:store3")); features = collectFeatures(q); assertSchema(features.values(), store1.getSchema(BASIC_POLYGONS)); assertEquals(2, features.size()); } @Test public void testReadFilteredManualConfig() throws Exception { Query q = new Query(BASIC_POLYGONS); q.setFilter(ff.greater(ff.function("strLength", ff.property("ID")), ff.literal(3))); store.resetConfiguration(); AggregateTypeConfiguration config = new AggregateTypeConfiguration(BASIC_POLYGONS); config.addSourceType("store1", BASIC_POLYGONS); store.addType(config); Map<String, SimpleFeature> features = collectFeatures(q); assertSchema(features.values(), store1.getSchema(BASIC_POLYGONS)); assertEquals(1, features.size()); config.addSourceType("store4", "BasicPolygons2"); features = collectFeatures(q); assertSchema(features.values(), store1.getSchema(BASIC_POLYGONS)); assertEquals(2, features.size()); } @Test public void testReadFilteredManualConfigInverted() throws Exception { Query q = new Query(BASIC_POLYGONS); q.setFilter(ff.greater(ff.function("strLength", ff.property("ID")), ff.literal(3))); store.resetConfiguration(); AggregateTypeConfiguration config = new AggregateTypeConfiguration(BASIC_POLYGONS); config.addSourceType("store4", "BasicPolygons2"); store.addType(config); Map<String, SimpleFeature> features = collectFeatures(q); SimpleFeatureTypeBuilder tb = new SimpleFeatureTypeBuilder(); tb.init(store4.getSchema("BasicPolygons2")); tb.setName(BASIC_POLYGONS); SimpleFeatureType expectedType = tb.buildFeatureType(); assertSchema(features.values(), expectedType); assertEquals(1, features.size()); config.addSourceType("store1", BASIC_POLYGONS); features = collectFeatures(q); assertSchema(features.values(), expectedType); assertEquals(2, features.size()); } @Test public void testFilterMissingAttribute() throws Exception { Query q = new Query(ROAD_SEGMENTS); q.setFilter(ff.equals(ff.property("NAME"), ff.literal("Main Street"))); store.resetConfiguration(); store.autoConfigureStores(Arrays.asList("store1")); Map<String, SimpleFeature> features = collectFeatures(q); assertSchema(features.values(), store1.getSchema(ROAD_SEGMENTS)); assertEquals(1, features.size()); // now we filter against a store that has the feature type, but misses the property store.resetConfiguration(); store.autoConfigureStores(Arrays.asList("store1", "store2", "gt:store3")); features = collectFeatures(q); assertSchema(features.values(), store1.getSchema(ROAD_SEGMENTS)); assertEquals(1, features.size()); } @Test public void testRetrieveMissingAttribute() throws Exception { Query q = new Query(ROAD_SEGMENTS); q.setPropertyNames(new String[] {"NAME"}); // now we filter against a store that has the feature type, but misses the property store.resetConfiguration(); store.autoConfigureStores(Arrays.asList("store1", "gt:store3")); Map<String, SimpleFeature> features = collectFeatures(q); assertSchema(features.values(), SimpleFeatureTypeBuilder.retype(store1.getSchema(ROAD_SEGMENTS), new String[] {"NAME"})); assertEquals(6, features.size()); System.out.println(features); SimpleFeature sf = features.get("RoadSegments.0.rs.1"); assertEquals("Route 5", sf.getAttribute("NAME")); sf = features.get("RoadSegments.1.rs.6"); assertNull(sf.getAttribute("NAME")); } private Map<String, SimpleFeature> collectFeatures(Query query) throws IOException { Map<String, SimpleFeature> result = new HashMap<String, SimpleFeature>(); SimpleFeatureCollection fc = store.getFeatureSource(query.getTypeName()).getFeatures(query); SimpleFeatureIterator fi = null; try { fi = fc.features(); while (fi.hasNext()) { SimpleFeature f = fi.next(); result.put(f.getID(), f); } } finally { if (fi != null) { fi.close(); } } return result; } private List<SimpleFeature> listFeatures(Query query) throws IOException { List<SimpleFeature> result = new ArrayList<SimpleFeature>(); SimpleFeatureCollection fc = store.getFeatureSource(query.getTypeName()).getFeatures(query); SimpleFeatureIterator fi = null; try { fi = fc.features(); while (fi.hasNext()) { SimpleFeature f = fi.next(); result.add(f); } } finally { if (fi != null) { fi.close(); } } return result; } }