org.geotools.data.collection.CollectionFeatureSource Maven / Gradle / Ivy
Show all versions of gt-main Show documentation
/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2019, 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.collection;
import java.awt.RenderingHints.Key;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.geotools.data.DataAccess;
import org.geotools.data.DataUtilities;
import org.geotools.data.FeatureListener;
import org.geotools.data.Query;
import org.geotools.data.QueryCapabilities;
import org.geotools.data.ResourceInfo;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.data.simple.SimpleFeatureSource;
import org.geotools.data.store.EmptyFeatureCollection;
import org.geotools.data.store.ReTypingFeatureCollection;
import org.geotools.data.store.ReprojectingFeatureCollection;
import org.geotools.feature.collection.DecoratingSimpleFeatureCollection;
import org.geotools.feature.collection.FilteringSimpleFeatureCollection;
import org.geotools.feature.collection.MaxSimpleFeatureCollection;
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 org.opengis.filter.Filter;
import org.opengis.filter.sort.SortBy;
/**
* This is a "port" of ContentFeatureSource to work with an iterator.
*
* To use this class please "wrap" CollectionFeatureSource around your choice of
* FeatureCollection.
*
*
* SimpleFeatureCollection collection = new ListFeatureCollection(schema);
* collection.add(feature1);
* collection.add(feature2);
* FeatureSource source = new CollectionFeatureSource(collection);
*
*
* Note to implementors: If you are performing "real I/O" please use ContentFeatureSource as it
* provides support for IOException.
*
* @author Jody
*/
public class CollectionFeatureSource implements SimpleFeatureSource {
protected SimpleFeatureCollection collection;
/** observers */
protected List listeners = null;
private QueryCapabilities capabilities;
private Set hints;
public CollectionFeatureSource(SimpleFeatureCollection collection) {
this.collection = collection;
}
public SimpleFeatureType getSchema() {
return collection.getSchema();
}
public synchronized void addFeatureListener(FeatureListener listener) {
if (listeners == null) {
listeners = Collections.synchronizedList(new ArrayList());
}
listeners.add(listener);
}
public synchronized void removeFeatureListener(FeatureListener listener) {
if (listeners == null) {
return;
}
listeners.remove(listener);
}
public ReferencedEnvelope getBounds() throws IOException {
return collection.getBounds();
}
public ReferencedEnvelope getBounds(Query query) throws IOException {
return getFeatures(query).getBounds();
}
public int getCount(Query query) throws IOException {
return getFeatures(query).size();
}
public DataAccess getDataStore() {
throw new UnsupportedOperationException("CollectionFeatureSource is an inmemory wrapper");
}
public ResourceInfo getInfo() {
throw new UnsupportedOperationException("CollectionFeatureSource is an inmemory wrapper");
}
public Name getName() {
return collection.getSchema().getName();
}
public synchronized QueryCapabilities getQueryCapabilities() {
if (capabilities == null) {
capabilities =
new QueryCapabilities() {
public boolean isOffsetSupported() {
return true;
}
public boolean isReliableFIDSupported() {
return true;
}
public boolean supportsSorting(
org.opengis.filter.sort.SortBy[] sortAttributes) {
return true;
}
};
}
return capabilities;
}
public synchronized Set getSupportedHints() {
if (hints == null) {
Set supports = new HashSet();
// supports.add( Hints.FEATURE_DETACHED );
hints = Collections.unmodifiableSet(supports);
}
return hints;
}
@Override
public String toString() {
StringBuilder buf = new StringBuilder();
buf.append("CollectionFeatureSource:");
buf.append(collection);
return buf.toString();
}
//
// GET FEATURES
// This forms the heart of the CollectionFeatureSource implementation
// Use: DataUtilities.mixQueries(this.query, query, "subCollection" ) as needed
//
public SimpleFeatureCollection getFeatures() throws IOException {
return getFeatures(Query.ALL);
}
public SimpleFeatureCollection getFeatures(Filter filter) {
Query query = new Query(getSchema().getTypeName(), filter);
return getFeatures(query);
}
public SimpleFeatureCollection getFeatures(Query query) {
query = DataUtilities.resolvePropertyNames(query, getSchema());
final int offset = query.getStartIndex() != null ? query.getStartIndex() : 0;
if (offset > 0 && query.getSortBy() == null) {
if (!getQueryCapabilities().supportsSorting(query.getSortBy())) {
throw new IllegalStateException(
"Feature source does not support this sorting "
+ "so there is no way a stable paging (offset/limit) can be performed");
}
Query copy = new Query(query);
copy.setSortBy(new SortBy[] {SortBy.NATURAL_ORDER});
query = copy;
}
SimpleFeatureCollection features = collection;
// step one: filter
if (query.getFilter() != null && query.getFilter().equals(Filter.EXCLUDE)) {
return new EmptyFeatureCollection(getSchema());
}
if (query.getFilter() != null && query.getFilter() != Filter.INCLUDE) {
features = new FilteringSimpleFeatureCollection(features, query.getFilter());
}
// step two: reproject
if (query.getCoordinateSystemReproject() != null) {
features =
new ReprojectingFeatureCollection(
features, query.getCoordinateSystemReproject());
}
// step two sort! (note this makes a sorted copy)
if (query.getSortBy() != null && query.getSortBy().length != 0) {
SimpleFeature array[] = features.toArray(new SimpleFeature[features.size()]);
// Arrays sort is stable (not resorting equal elements)
for (SortBy sortBy : query.getSortBy()) {
Comparator comparator = DataUtilities.sortComparator(sortBy);
Arrays.sort(array, comparator);
}
ArrayList list = new ArrayList(Arrays.asList(array));
features = new ListFeatureCollection(getSchema(), list);
}
// step three skip to start and return max number of fetaures
if (offset > 0 || !query.isMaxFeaturesUnlimited()) {
long max = Long.MAX_VALUE;
if (!query.isMaxFeaturesUnlimited()) {
max = query.getMaxFeatures();
}
features = new MaxSimpleFeatureCollection(features, offset, max);
}
// step four - retyping
// (It would be nice to do this earlier so as to not have all the baggage
// of unneeded attributes)
if (query.getPropertyNames() != Query.ALL_NAMES) {
// rebuild the type and wrap the reader
SimpleFeatureType schema = features.getSchema();
SimpleFeatureType target =
SimpleFeatureTypeBuilder.retype(schema, query.getPropertyNames());
// do an equals check because we may have needlessly retyped (that is,
// the subclass might be able to only partially retype)
if (!target.equals(schema)) {
features = new ReTypingFeatureCollection(features, target);
}
}
// Wrap up the results in a method that allows subCollection
return new SubCollection(query, features);
}
/**
* SubCollection for CollectionFeatureSource.
*
* Will route any calls refining the feature collection back to CollectionFeatureSource. This
* is based on the success of ContentFeatureCollection.
*
* @author Jody
*/
protected class SubCollection extends DecoratingSimpleFeatureCollection {
private Query query;
protected SubCollection(Query query, SimpleFeatureCollection features) {
super(features);
this.query = query;
}
public SimpleFeatureCollection subCollection(Filter filter) {
Query q = new Query(getSchema().getTypeName(), filter);
Query subQuery = DataUtilities.mixQueries(query, q, q.getHandle());
return CollectionFeatureSource.this.getFeatures(subQuery);
}
@Override
public SimpleFeatureCollection sort(SortBy order) {
Query q = new Query(getSchema().getTypeName());
q.setSortBy(new SortBy[] {order});
Query subQuery = DataUtilities.mixQueries(query, q, q.getHandle());
return CollectionFeatureSource.this.getFeatures(subQuery);
}
}
}