/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2002-2009, Open Source Geospatial Foundation (OSGeo)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
package org.geotools.data.aggregate;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Future;
import org.geotools.data.DataStore;
import org.geotools.data.DataUtilities;
import org.geotools.data.FeatureReader;
import org.geotools.data.Query;
import org.geotools.data.simple.SimpleFeatureReader;
import org.geotools.data.store.ContentEntry;
import org.geotools.data.store.ContentFeatureSource;
import org.geotools.feature.SchemaException;
import org.geotools.feature.simple.SimpleFeatureTypeBuilder;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.Name;
import com.vividsolutions.jts.geom.Envelope;
class AggregatingFeatureSource extends ContentFeatureSource {
/**
* The configuration for this feature type
*/
AggregateTypeConfiguration config;
public AggregatingFeatureSource(ContentEntry entry, AggregatingDataStore store,
AggregateTypeConfiguration config) {
super(entry, null);
this.config = config;
}
public AggregatingDataStore getStore() {
return (AggregatingDataStore) getEntry().getDataStore();
}
@Override
protected ReferencedEnvelope getBoundsInternal(Query query) throws IOException {
// schedule all the bound queries
AggregatingDataStore store = getStore();
List<Future<ReferencedEnvelope>> allBounds = new ArrayList<Future<ReferencedEnvelope>>();
for (SourceType st : config.getSourceTypes()) {
Future<ReferencedEnvelope> f = store.submit(new BoundsCallable(store, query, st
.getStoreName(), st.getTypeName()));
allBounds.add(f);
}
// aggregate the envelopes
ReferencedEnvelope result = new ReferencedEnvelope(getSchema()
.getCoordinateReferenceSystem());
for (Future<ReferencedEnvelope> future : allBounds) {
try {
ReferencedEnvelope bound = future.get();
if (bound != null) {
// the inputs might have slightly different crs, compensate
Envelope env = new Envelope(bound);
result.expandToInclude(env);
}
} catch (Exception e) {
throw new IOException("Failed to get the envelope from a delegate store", e);
}
}
if (result.isNull()) {
return null;
} else {
return result;
}
}
@Override
protected int getCountInternal(Query query) throws IOException {
// schedule all the counts
AggregatingDataStore store = getStore();
List<Future<Long>> counts = new ArrayList<Future<Long>>();
for (SourceType st : config.getSourceTypes()) {
Future<Long> f = store.submit(new CountCallable(store, query, st.getStoreName(), st.getTypeName()));
counts.add(f);
}
// aggregate the counts
long total = 0;
for (Future<Long> future : counts) {
try {
long count = future.get();
if (count > 0) {
total += count;
} else {
// one of the sources found it was too costly to count
return -1;
}
} catch (Exception e) {
throw new IOException("Failed to count on a delegate store", e);
}
}
return (int) total;
}
@Override
protected FeatureReader<SimpleFeatureType, SimpleFeature> getReaderInternal(Query query)
throws IOException {
try {
// get the target schema from the primary store
Query psQuery = new Query(query);
Name psName = config.getPrimarySourceType().getStoreName();
psQuery.setTypeName(config.getPrimarySourceType().getTypeName());
DataStore ps = getStore().getStore(psName, false);
SimpleFeatureType target = DataUtilities.createView(ps, psQuery).getSchema();
target = retypeNameSchema(target);
// schedule all the data retrieval operations
FeatureQueue queue = new FeatureQueue(config.getSourceTypes().size());
AggregatingDataStore store = getStore();
for (SourceType st : config.getSourceTypes()) {
FeatureCallable fc = new FeatureCallable(store, query, st.getStoreName(),
st.getTypeName(), queue, target);
queue.addSource(fc);
store.submit(fc);
}
// build a reader out of the queue
SimpleFeatureReader reader = new QueueReader(queue, target);
return reader;
} catch (SchemaException e) {
throw new IOException("Failed to compute target feature type", e);
}
}
@Override
protected SimpleFeatureType buildFeatureType() throws IOException {
Name ps = config.getPrimarySourceType().getStoreName();
DataStore store = getStore().getStore(ps, false);
SimpleFeatureType schema = store.getSchema(config.getPrimarySourceType().getTypeName());
if (schema == null) {
throw new IOException("Could not find feature type " + schema + " in the primary store");
}
schema = retypeNameSchema(schema);
return schema;
}
/**
* Given a source store schema it renames it and forces in the right namespace
*
* @param schema
* @return
*/
private SimpleFeatureType retypeNameSchema(SimpleFeatureType schema) {
SimpleFeatureTypeBuilder tb = new SimpleFeatureTypeBuilder();
tb.init(schema);
tb.setName(config.getName());
tb.setNamespaceURI(getStore().getNamespaceURI());
schema = tb.buildFeatureType();
return schema;
}
@Override
protected boolean canFilter() {
return true;
}
@Override
protected boolean canRetype() {
return true;
}
}