Skip to content

Commit

Permalink
Initial write support for creating a collection from a GeoJSON spec
Browse files Browse the repository at this point in the history
  • Loading branch information
aaime committed Jun 22, 2017
1 parent c9a017f commit 8d01089
Show file tree
Hide file tree
Showing 9 changed files with 474 additions and 62 deletions.
Expand Up @@ -5,6 +5,7 @@
*/ */
package org.geoserver.opensearch.eo; package org.geoserver.opensearch.eo;


import java.util.ArrayList;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.NoSuchElementException; import java.util.NoSuchElementException;
Expand All @@ -29,10 +30,10 @@
* @source $URL$ * @source $URL$
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
class ListComplexFeatureCollection extends BaseFeatureCollection { public class ListComplexFeatureCollection extends BaseFeatureCollection {


/** wrapped list of features containing the contents */ /** wrapped list of features containing the contents */
private List<? extends Feature> list; private List<Feature> list;


/** Cached bounds */ /** Cached bounds */
private ReferencedEnvelope bounds = null; private ReferencedEnvelope bounds = null;
Expand All @@ -50,10 +51,22 @@ class ListComplexFeatureCollection extends BaseFeatureCollection {
* @param schema * @param schema
* @param list * @param list
*/ */
public ListComplexFeatureCollection(FeatureType schema, List<? extends Feature> list) { public ListComplexFeatureCollection(FeatureType schema, List<Feature> list) {
super(schema); super(schema);
this.list = list; this.list = list;
} }

/**
* Create a ListFeatureCollection around the provided feature.
*
* @param schema
* @param list
*/
public ListComplexFeatureCollection(Feature feature) {
super(feature.getType());
this.list = new ArrayList<>();
this.list.add(feature);
}


@Override @Override
public int size() { public int size() {
Expand Down
Expand Up @@ -4,50 +4,60 @@
*/ */
package org.geoserver.opensearch.eo.store; package org.geoserver.opensearch.eo.store;


import static org.geoserver.opensearch.eo.store.JDBCOpenSearchAccess.FF;
import static org.geoserver.opensearch.eo.store.OpenSearchAccess.METADATA_PROPERTY_NAME; import static org.geoserver.opensearch.eo.store.OpenSearchAccess.METADATA_PROPERTY_NAME;
import static org.geoserver.opensearch.eo.store.OpenSearchAccess.OGC_LINKS_PROPERTY_NAME; import static org.geoserver.opensearch.eo.store.OpenSearchAccess.OGC_LINKS_PROPERTY_NAME;
import static org.geoserver.opensearch.eo.store.JDBCOpenSearchAccess.FF;


import java.awt.RenderingHints.Key; import java.awt.RenderingHints.Key;
import java.io.IOException; import java.io.IOException;
import java.net.URI; import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.logging.Logger; import java.util.logging.Logger;
import java.util.stream.Stream;


import org.geotools.data.DataAccess; import org.geotools.data.DataAccess;
import org.geotools.data.DataSourceException; import org.geotools.data.DataSourceException;
import org.geotools.data.DataStore;
import org.geotools.data.DataUtilities;
import org.geotools.data.DefaultResourceInfo; import org.geotools.data.DefaultResourceInfo;
import org.geotools.data.FeatureListener; import org.geotools.data.FeatureListener;
import org.geotools.data.FeatureSource; import org.geotools.data.FeatureReader;
import org.geotools.data.FeatureStore;
import org.geotools.data.Join; import org.geotools.data.Join;
import org.geotools.data.Join.Type; import org.geotools.data.Join.Type;
import org.geotools.data.Query; import org.geotools.data.Query;
import org.geotools.data.QueryCapabilities; import org.geotools.data.QueryCapabilities;
import org.geotools.data.ResourceInfo; import org.geotools.data.ResourceInfo;
import org.geotools.data.Transaction;
import org.geotools.data.simple.SimpleFeatureCollection; import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.data.simple.SimpleFeatureSource; import org.geotools.data.simple.SimpleFeatureSource;
import org.geotools.data.simple.SimpleFeatureStore;
import org.geotools.data.store.EmptyFeatureCollection; import org.geotools.data.store.EmptyFeatureCollection;
import org.geotools.factory.CommonFactoryFinder; import org.geotools.factory.CommonFactoryFinder;
import org.geotools.feature.AttributeBuilder; import org.geotools.feature.AttributeBuilder;
import org.geotools.feature.ComplexFeatureBuilder; import org.geotools.feature.ComplexFeatureBuilder;
import org.geotools.feature.FeatureCollection; import org.geotools.feature.FeatureCollection;
import org.geotools.feature.FeatureIterator;
import org.geotools.feature.simple.SimpleFeatureBuilder; import org.geotools.feature.simple.SimpleFeatureBuilder;
import org.geotools.feature.simple.SimpleFeatureTypeBuilder; import org.geotools.feature.simple.SimpleFeatureTypeBuilder;
import org.geotools.geometry.jts.ReferencedEnvelope; import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.util.logging.Logging; import org.geotools.util.logging.Logging;
import org.opengis.feature.Attribute; import org.opengis.feature.Attribute;
import org.opengis.feature.Feature; import org.opengis.feature.Feature;
import org.opengis.feature.FeatureVisitor; import org.opengis.feature.FeatureFactory;
import org.opengis.feature.Property;
import org.opengis.feature.simple.SimpleFeature; import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType; import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.AttributeDescriptor; import org.opengis.feature.type.AttributeDescriptor;
import org.opengis.feature.type.FeatureType; import org.opengis.feature.type.FeatureType;
import org.opengis.feature.type.Name; import org.opengis.feature.type.Name;
import org.opengis.feature.type.PropertyDescriptor; import org.opengis.feature.type.PropertyDescriptor;
import org.opengis.filter.Filter; import org.opengis.filter.Filter;
import org.opengis.filter.FilterFactory2;
import org.opengis.filter.expression.PropertyName; import org.opengis.filter.expression.PropertyName;
import org.opengis.filter.identity.FeatureId; import org.opengis.filter.identity.FeatureId;
import org.opengis.filter.sort.SortBy; import org.opengis.filter.sort.SortBy;
Expand All @@ -58,9 +68,11 @@
* *
* @author Andrea Aime - GeoSolutions * @author Andrea Aime - GeoSolutions
*/ */
public abstract class AbstractMappingSource implements FeatureSource<FeatureType, Feature> { public abstract class AbstractMappingStore implements FeatureStore<FeatureType, Feature> {

static final FeatureFactory FEATURE_FACTORY = CommonFactoryFinder.getFeatureFactory(null);


static final Logger LOGGER = Logging.getLogger(AbstractMappingSource.class); static final Logger LOGGER = Logging.getLogger(AbstractMappingStore.class);


protected JDBCOpenSearchAccess openSearchAccess; protected JDBCOpenSearchAccess openSearchAccess;


Expand All @@ -72,7 +84,9 @@ public abstract class AbstractMappingSource implements FeatureSource<FeatureType


private SimpleFeatureType linkFeatureType; private SimpleFeatureType linkFeatureType;


public AbstractMappingSource(JDBCOpenSearchAccess openSearchAccess, private Transaction transaction;

public AbstractMappingStore(JDBCOpenSearchAccess openSearchAccess,
FeatureType collectionFeatureType) throws IOException { FeatureType collectionFeatureType) throws IOException {
this.openSearchAccess = openSearchAccess; this.openSearchAccess = openSearchAccess;
this.schema = collectionFeatureType; this.schema = collectionFeatureType;
Expand Down Expand Up @@ -127,9 +141,14 @@ public ResourceInfo getInfo() {
} }


/* /*
* + Returns the underlying delegate source * Returns the underlying delegate source
*/ */
protected abstract SimpleFeatureSource getDelegateCollectionSource() throws IOException; protected abstract SimpleFeatureSource getDelegateCollectionSource() throws IOException;

protected SimpleFeatureStore getDelegateCollectionStore() throws IOException {
SimpleFeatureStore fs = (SimpleFeatureStore) getDelegateCollectionSource();
return fs;
}


@Override @Override
public DataAccess<FeatureType, Feature> getDataStore() { public DataAccess<FeatureType, Feature> getDataStore() {
Expand Down Expand Up @@ -363,7 +382,7 @@ protected Feature mapToComplexFeature(PushbackFeatureIterator<SimpleFeature> it)
ComplexFeatureBuilder builder = new ComplexFeatureBuilder(schema); ComplexFeatureBuilder builder = new ComplexFeatureBuilder(schema);


// allow subclasses to perform custom mappings while reusing the common ones // allow subclasses to perform custom mappings while reusing the common ones
mapProperties(builder, fi); mapPropertiesToComplex(builder, fi);


// the OGC links can be more than one // the OGC links can be more than one
Object link = fi.getAttribute("link"); Object link = fi.getAttribute("link");
Expand Down Expand Up @@ -402,8 +421,8 @@ protected Feature mapToComplexFeature(PushbackFeatureIterator<SimpleFeature> it)
* @param builder * @param builder
* @param fi * @param fi
*/ */
protected void mapProperties(ComplexFeatureBuilder builder, SimpleFeature fi) { protected void mapPropertiesToComplex(ComplexFeatureBuilder builder, SimpleFeature fi) {
AttributeBuilder ab = new AttributeBuilder(CommonFactoryFinder.getFeatureFactory(null)); AttributeBuilder ab = new AttributeBuilder(FEATURE_FACTORY);
for (PropertyDescriptor pd : schema.getDescriptors()) { for (PropertyDescriptor pd : schema.getDescriptors()) {
if (!(pd instanceof AttributeDescriptor)) { if (!(pd instanceof AttributeDescriptor)) {
continue; continue;
Expand Down Expand Up @@ -432,5 +451,123 @@ protected void mapProperties(ComplexFeatureBuilder builder, SimpleFeature fi) {
} }


} }

/**
* Maps a complex feature back to one or more simple features
*
* @param it
* @return
* @throws IOException
*/
protected SimpleFeature mapToMainSimpleFeature(Feature feature) throws IOException {
// map the primary simple feature
final SimpleFeatureType simpleSchema = getDelegateCollectionSource().getSchema();
SimpleFeatureBuilder fb = new SimpleFeatureBuilder(simpleSchema);
for (PropertyDescriptor pd : schema.getDescriptors()) {
if (!(pd instanceof AttributeDescriptor)) {
continue;
}
String localName = (String) pd.getUserData().get(JDBCOpenSearchAccess.SOURCE_ATTRIBUTE);
if (localName == null) {
continue;
}
Property property = feature.getProperty(pd.getName());
if(property == null || property.getValue() == null) {
continue;
}
fb.set(localName, property.getValue());
}
SimpleFeature sf = fb.buildFeature(null);

return sf;
}

protected List<SimpleFeature> mapToSecondarySimpleFeatures(Feature feature) {
// TODO: handle OGC links, but for the moment the code uses modifyFeatures to add those
return Collections.emptyList();
}

@Override
public List<FeatureId> addFeatures(FeatureCollection<FeatureType, Feature> featureCollection)
throws IOException {
// silly implementation assuming there will be only one insert at a time (which is
// indeed the case for the current REST API), needs to be turned into a streaming
// approach in case we want to handle larger data volumes
final DataStore delegateStore = openSearchAccess.getDelegateStore();
List<FeatureId> result = new ArrayList<>();
try(FeatureIterator it = featureCollection.features()) {
Feature feature = it.next();
SimpleFeature simpleFeature = mapToMainSimpleFeature(feature);
SimpleFeatureStore store = getDelegateCollectionStore();
store.setTransaction(getTransaction());
List<FeatureId> ids = store.addFeatures(DataUtilities.collection(simpleFeature));
result.addAll(ids);

List<SimpleFeature> simpleFeatures = mapToSecondarySimpleFeatures(feature);
for (SimpleFeature sf : simpleFeatures) {
SimpleFeatureStore fs = (SimpleFeatureStore) delegateStore.getFeatureSource(sf.getType().getTypeName());
if(fs == null) {
throw new IOException("Could not find a delegate feature store for unmapped feature " + sf);
}
fs.setTransaction(getTransaction());
fs.addFeatures(DataUtilities.collection(sf));
}
}

return result;
}

@Override
public void removeFeatures(Filter filter) throws IOException {
MappingFilterVisitor visitor = new MappingFilterVisitor(propertyMapper);
Filter mappedFilter = (Filter) filter.accept(visitor, null);
SimpleFeatureStore store = getDelegateCollectionStore();
store.removeFeatures(mappedFilter);
}

@Override
public void modifyFeatures(AttributeDescriptor[] types, Object[] values, Filter filter)
throws IOException {
Name[] names = Stream.of(types).map(type -> type.getName()).toArray(size -> new Name[size]);
modifyFeatures(names, values, filter);
}

@Override
public void modifyFeatures(Name attributeName, Object attributeValue, Filter filter)
throws IOException {
modifyFeatures(new Name[] {attributeName}, new Object[] {attributeValue}, filter);
}

@Override
public void modifyFeatures(AttributeDescriptor type, Object value, Filter filter)
throws IOException {
modifyFeatures(type.getName(), value, filter);
}

@Override
public void modifyFeatures(Name[] attributeNames, Object[] attributeValues, Filter filter)
throws IOException {
throw new UnsupportedOperationException();

}

@Override
public void setFeatures(FeatureReader<FeatureType, Feature> reader) throws IOException {
throw new UnsupportedOperationException();
}

@Override
public void setTransaction(Transaction transaction) {
this.transaction = transaction;
}

@Override
public Transaction getTransaction() {
if(transaction == null) {
return Transaction.AUTO_COMMIT;
} else {
return this.transaction;
}
}


} }
Expand Up @@ -16,11 +16,11 @@
* *
* @author Andrea Aime - GeoSolutions * @author Andrea Aime - GeoSolutions
*/ */
public class JDBCCollectionFeatureSource extends AbstractMappingSource { public class JDBCCollectionFeatureStore extends AbstractMappingStore {


static final Logger LOGGER = Logging.getLogger(JDBCCollectionFeatureSource.class); static final Logger LOGGER = Logging.getLogger(JDBCCollectionFeatureStore.class);


public JDBCCollectionFeatureSource(JDBCOpenSearchAccess openSearchAccess, public JDBCCollectionFeatureStore(JDBCOpenSearchAccess openSearchAccess,
FeatureType collectionFeatureType) throws IOException { FeatureType collectionFeatureType) throws IOException {
super(openSearchAccess, collectionFeatureType); super(openSearchAccess, collectionFeatureType);
} }
Expand Down
Expand Up @@ -19,6 +19,7 @@
import org.geotools.data.DataStore; import org.geotools.data.DataStore;
import org.geotools.data.DataUtilities; import org.geotools.data.DataUtilities;
import org.geotools.data.FeatureSource; import org.geotools.data.FeatureSource;
import org.geotools.data.FeatureStore;
import org.geotools.data.Query; import org.geotools.data.Query;
import org.geotools.data.Repository; import org.geotools.data.Repository;
import org.geotools.data.ServiceInfo; import org.geotools.data.ServiceInfo;
Expand Down Expand Up @@ -474,7 +475,7 @@ public SimpleFeatureSource getCollectionGranulesSource(String collectionName) th


// is it a virtual collection? // is it a virtual collection?
if(!primaryTable) { if(!primaryTable) {
String cqlFilter = (String) collectionFeature.getAttribute("productcqlfilter"); String cqlFilter = (String) collectionFeature.getAttribute("productCqlFilter");
if(cqlFilter != null) { if(cqlFilter != null) {
try { try {
Filter filter = ECQL.toFilter(cqlFilter); Filter filter = ECQL.toFilter(cqlFilter);
Expand Down Expand Up @@ -531,12 +532,12 @@ private void checkName(String tableName, String lookup) {
} }
} }


public FeatureSource<FeatureType, Feature> getProductSource() throws IOException { public FeatureStore<FeatureType, Feature> getProductSource() throws IOException {
return new JDBCProductFeatureSource(this, productFeatureType); return new JDBCProductFeatureSource(this, productFeatureType);
} }


public FeatureSource<FeatureType, Feature> getCollectionSource() throws IOException { public FeatureStore<FeatureType, Feature> getCollectionSource() throws IOException {
return new JDBCCollectionFeatureSource(this, collectionFeatureType); return new JDBCCollectionFeatureStore(this, collectionFeatureType);
} }


@Override @Override
Expand Down
Expand Up @@ -27,7 +27,7 @@
* *
* @author Andrea Aime - GeoSolutions * @author Andrea Aime - GeoSolutions
*/ */
public class JDBCProductFeatureSource extends AbstractMappingSource { public class JDBCProductFeatureSource extends AbstractMappingStore {


static final Logger LOGGER = Logging.getLogger(JDBCProductFeatureSource.class); static final Logger LOGGER = Logging.getLogger(JDBCProductFeatureSource.class);


Expand Down Expand Up @@ -72,9 +72,9 @@ protected Query mapToSimpleCollectionQuery(Query query, boolean addJoins) throws
} }


@Override @Override
protected void mapProperties(ComplexFeatureBuilder builder, SimpleFeature fi) { protected void mapPropertiesToComplex(ComplexFeatureBuilder builder, SimpleFeature fi) {
// basic mappings // basic mappings
super.mapProperties(builder, fi); super.mapPropertiesToComplex(builder, fi);


// quicklook extraction // quicklook extraction
Object metadataValue = fi.getAttribute("quicklook"); Object metadataValue = fi.getAttribute("quicklook");
Expand Down

0 comments on commit 8d01089

Please sign in to comment.