/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2002-2011, 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.geoserver.data.geogit; import java.awt.RenderingHints; import java.awt.RenderingHints.Key; import java.io.IOException; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.Set; import org.geogit.api.ObjectId; import org.geogit.api.RevTree; import org.geogit.repository.Repository; import org.geogit.storage.ObjectDatabase; import org.geoserver.data.versioning.SimpleVersioningFeatureSource; import org.geotools.data.DataSourceException; import org.geotools.data.DefaultResourceInfo; 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.store.EmptyFeatureCollection; import org.geotools.factory.CommonFactoryFinder; import org.geotools.factory.Hints; import org.geotools.filter.spatial.DefaultCRSFilterVisitor; import org.geotools.filter.spatial.ReprojectingFilterVisitor; import org.geotools.geometry.jts.ReferencedEnvelope; import org.opengis.feature.simple.SimpleFeatureType; import org.opengis.feature.type.GeometryDescriptor; import org.opengis.feature.type.Name; import org.opengis.filter.Filter; import org.opengis.filter.FilterFactory2; import org.opengis.filter.sort.SortBy; import org.opengis.referencing.crs.CoordinateReferenceSystem; import com.google.common.base.Throwables; public class GeoGitFeatureSource implements SimpleVersioningFeatureSource { protected final SimpleFeatureType type; protected final GeoGitDataStore dataStore; /** * @see #getSupportedHints() */ private static final Set<Key> supportedHints = Collections .unmodifiableSet(new HashSet<Key>(Arrays.asList( Hints.FEATURE_DETACHED, Hints.JTS_GEOMETRY_FACTORY, Hints.JTS_COORDINATE_SEQUENCE_FACTORY) )); public GeoGitFeatureSource(final SimpleFeatureType type, final GeoGitDataStore dataStore) { this.type = type; this.dataStore = dataStore; } /** * @return the tree of feature references for this type */ public RevTree getCurrentVersion() { // assume HEAD is at MASTER try { final Name typeName = this.type.getName(); Repository repository = dataStore.getRepository(); RevTree typeTree = repository.getWorkingTree().getHeadVersion( typeName); return typeTree; } catch (Exception e) { throw new RuntimeException(e); } } /** * @see org.geotools.data.FeatureSource#getName() */ @Override public Name getName() { return type.getName(); } /** * @see org.geotools.data.FeatureSource#getInfo() */ @Override public ResourceInfo getInfo() { DefaultResourceInfo info = new DefaultResourceInfo(); ReferencedEnvelope bounds; try { bounds = getBounds(); if (bounds != null) { info.setBounds(bounds); info.setCRS(bounds.getCoordinateReferenceSystem()); } } catch (IOException e) { Throwables.propagate(e); } info.setName(getName().getLocalPart()); info.setDescription("GeoGit backed Feature Source"); return info; } /** * @see org.geotools.data.FeatureSource#getDataStore() */ @Override public GeoGitDataStore getDataStore() { return dataStore; } /** * @see org.geotools.data.FeatureSource#getQueryCapabilities() */ @Override public QueryCapabilities getQueryCapabilities() { QueryCapabilities caps = new QueryCapabilities() { /** * @see org.geotools.data.QueryCapabilities#isUseProvidedFIDSupported() * @return {@code true} */ @Override public boolean isUseProvidedFIDSupported() { return true; } /** * @see org.geotools.data.QueryCapabilities#supportsSorting(org.opengis.filter.sort.SortBy[]) * @return {@code false} */ @Override public boolean supportsSorting(SortBy[] sortAttributes) { return false; } }; return caps; } @Override public void addFeatureListener(FeatureListener listener) { // TODO Auto-generated method stub } @Override public void removeFeatureListener(FeatureListener listener) { // TODO Auto-generated method stub } /** * @see org.geotools.data.FeatureSource#getSchema() */ @Override public SimpleFeatureType getSchema() { return type; } /** * @see org.geotools.data.FeatureSource#getBounds() * @see #getBounds(Query) getBounds(Query.ALL) */ @Override public ReferencedEnvelope getBounds() throws IOException { return getBounds(Query.ALL); } /** * @see org.geotools.data.FeatureSource#getBounds(org.geotools.data.Query) */ @Override public ReferencedEnvelope getBounds(final Query query) throws IOException { // TODO optimize, please SimpleFeatureCollection features = getFeatures(query); ReferencedEnvelope bounds = features.getBounds(); return bounds; } @Override public int getCount(Query query) throws IOException { // TODO optimize, please SimpleFeatureCollection features = getFeatures(query); int size = features.size(); return size; } /** * GeoGit Features are always "detached", so we return the FEATURE_DETACHED * hint here, as well as the JTS related ones. * <p> * The JTS related hints supported are: * <ul> * <li>JTS_GEOMETRY_FACTORY * <li>JTS_COORDINATE_SEQUENCE_FACTORY * </ul> * Note, however, that if a {@link GeometryFactory} is provided through the * {@code JTS_GEOMETRY_FACTORY} hint, that very factory is used and takes * precedence over all the other ones. * </p> * * @see FeatureSource#getSupportedHints() * @see Hints#FEATURE_DETACHED * @see Hints#JTS_GEOMETRY_FACTORY * @see Hints#JTS_COORDINATE_SEQUENCE_FACTORY */ @Override public final Set<RenderingHints.Key> getSupportedHints() { return supportedHints; } /** * @see org.geotools.data.simple.SimpleFeatureSource#getFeatures() */ @Override public SimpleFeatureCollection getFeatures() throws IOException { return getFeatures(Query.ALL); } @Override public SimpleFeatureCollection getFeatures(final Filter filter) throws IOException { return getFeatures(new Query(getName().getLocalPart(), filter)); } @Override public SimpleFeatureCollection getFeatures(final Query query) throws IOException { Filter filter = query.getFilter(); if (filter == null) { filter = Filter.INCLUDE; } if (Filter.EXCLUDE.equals(filter)) { return new EmptyFeatureCollection(type); } final Query query2 = reprojectFilter(query); filter = query2.getFilter(); final ObjectDatabase lookupDatabase = dataStore.getRepository() .getIndex().getDatabase(); final RevTree typeTree = getCurrentVersion(); GeoGitSimpleFeatureCollection featureCollection; featureCollection = new GeoGitSimpleFeatureCollection(type, filter, lookupDatabase, typeTree); final int maxFeatures = query2.getMaxFeatures(); if (maxFeatures > 0 && maxFeatures != Query.DEFAULT_MAX) { featureCollection.setMaxFeatures(maxFeatures); } // if (null != query.getHints().get(Hints.JTS_GEOMETRY_FACTORY)) { // GeometryFactory gfac = (GeometryFactory) query.getHints().get( // Hints.JTS_GEOMETRY_FACTORY); // featureCollection.setGeometryFactory(gfac); // } else if (null != // query.getHints().get(Hints.JTS_COORDINATE_SEQUENCE_FACTORY)) { // CoordinateSequenceFactory csf = (CoordinateSequenceFactory) // query.getHints().get( // Hints.JTS_COORDINATE_SEQUENCE_FACTORY); // GeometryFactory gfac = new GeometryFactory(csf); // featureCollection.setGeometryFactory(gfac); // } return featureCollection; } /** * @return the id of the root tree. Defaults to the repository's root, but * {@link GeoGitFeatureStore} shall override to account for whether * there's a transaction in progress */ protected ObjectId getRootTreeId() { Repository repository = dataStore.getRepository(); ObjectId rootTreeId = repository.getRootTreeId(); return rootTreeId; } private Query reprojectFilter(Query query) throws IOException { final Filter originalFilter = query.getFilter() != null ? query .getFilter() : Filter.INCLUDE; if (Filter.INCLUDE.equals(originalFilter)) { return query; } final SimpleFeatureType nativeFeatureType = getSchema(); final GeometryDescriptor geom = nativeFeatureType .getGeometryDescriptor(); // if no geometry involved, no reprojection needed if (geom == null) { return query; } final FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2(null); try { CoordinateReferenceSystem nativeCRS = geom .getCoordinateReferenceSystem(); // now we apply a default to all geometries and bbox in the filter DefaultCRSFilterVisitor defaultCRSVisitor = new DefaultCRSFilterVisitor( ff, nativeCRS); final Filter defaultedFilter = (Filter) originalFilter.accept( defaultCRSVisitor, null); // and then we reproject all geometries so that the datastore // receives // them in the native projection system (or the forced one, in case // of force) ReprojectingFilterVisitor reprojectingVisitor = new ReprojectingFilterVisitor( ff, nativeFeatureType); final Filter reprojectedFilter = (Filter) defaultedFilter.accept( reprojectingVisitor, null); Query reprojectedQuery = new Query(query); reprojectedQuery.setFilter(reprojectedFilter); return reprojectedQuery; } catch (Exception e) { throw new DataSourceException( "Had troubles handling filter reprojection...", e); } } }