Package org.geotools.data.view

Source Code of org.geotools.data.view.DefaultView

/*
*    GeoTools - The Open Source Java GIS Toolkit
*    http://geotools.org
*
*    (C) 2004-2008, 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.view;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.logging.Logger;

import org.geotools.data.DataSourceException;
import org.geotools.data.DataStore;
import org.geotools.data.DataUtilities;
import org.geotools.data.DefaultQuery;
import org.geotools.data.FeatureListener;
import org.geotools.data.FeatureLocking;
import org.geotools.data.FeatureSource;
import org.geotools.data.FeatureStore;
import org.geotools.data.Query;
import org.geotools.data.QueryCapabilities;
import org.geotools.data.ResourceInfo;
import org.geotools.data.crs.ForceCoordinateSystemFeatureResults;
import org.geotools.data.crs.ReprojectFeatureResults;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.data.simple.SimpleFeatureSource;
import org.geotools.factory.CommonFactoryFinder;
import org.geotools.feature.SchemaException;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.Name;
import org.opengis.filter.Filter;
import org.opengis.filter.FilterFactory;
import org.opengis.referencing.crs.CoordinateReferenceSystem;

/**
* Wrapper for SimpleFeatureSource constrained by a Query.
*
* <p>
* Support SimpleFeatureSource decorator that takes care of mapping a Query &
* SimpleFeatureSource with the schema and definition query configured for it.
* </p>
*
* <p>
* Because GeoServer requires that attributes always be returned in the same
* order we need a way to smoothly inforce this. Could we use this class to do
* so?
* </p>
* <p>
* WARNING: this class is a placeholder for ideas right now - it may not always
* impement FeatureSource.
* </p>
*
* @author Gabriel Rold�n
*
*
* @source $URL$
*/
public class DefaultView implements SimpleFeatureSource {

    /** Shared package logger */
    private static final Logger LOGGER = org.geotools.util.logging.Logging.getLogger("org.geotools.data.view");

    /** SimpleFeatureSource being served up */
    protected SimpleFeatureSource source;
    /**
     * Schema generated by provided constraintQuery
     */
    private SimpleFeatureType schema;

    /** Query provided as a constraint */
    private Query constraintQuery;

    /**
     * Creates a new GeoServerFeatureSource object.
     * <p>
     * Grabs the following from query:
     * <ul>
     * <li>typeName - only used if client does not supply
     * <li>cs - only used if client does not supply
     * <li>csForce - only used if client does not supply
     * <li>filter - combined with client filter
     * <li>propertyNames - combined with client filter (indicate property names
     * that *must* be included)
     * </ul>
     * </p>
     * Schema is generated based on this information.
     * </p>
     *
     * @param source
     *            a FeatureSource
     * @param query
     *            Filter used to limit results
     * @throws SchemaException
     */
    public DefaultView(SimpleFeatureSource source, Query query) throws SchemaException {
        this.source = source;
        this.constraintQuery = query;

        SimpleFeatureType origionalType = source.getSchema();

        CoordinateReferenceSystem cs = null;
        if (query.getCoordinateSystemReproject() != null) {
            cs = query.getCoordinateSystemReproject();
        } else if (query.getCoordinateSystem() != null) {
            cs = query.getCoordinateSystem();
        }
        schema = DataUtilities.createSubType(origionalType, query.getPropertyNames(), cs, query
                .getTypeName(), null);
    }

   
    /**
     * @see FeatureSource#getName()
     * @since 2.5
     */
    public Name getName() {
        return getSchema().getName();
    }

    /**
     * Factory that make the correct decorator for the provided featureSource.
     *
     * <p>
     * This factory method is public and will be used to create all required
     * subclasses. By comparison the constructors for this class have package
     * visibiliy.
     * </p>
     *
     * TODO: revisit this - I am not sure I want write access to views
     * (especially if they do reprojection).
     *
     * @param source
     * @param query
     *
     * @return @throws
     *         SchemaException
     */
    public static SimpleFeatureSource create(
            SimpleFeatureSource source, Query query)
            throws SchemaException {
        if (source instanceof FeatureLocking) {
            //  return new GeoServerFeatureLocking((FeatureLocking<SimpleFeatureType, SimpleFeature>) source,
            // schema, definitionQuery);
        } else if (source instanceof FeatureStore) {
            //return new GeoServerFeatureStore((SimpleFeatureStore) source, schema,
            // definitionQuery);
        }
        return new DefaultView(source, query);
    }

    /**
     * Takes a query and adapts it to match re definitionQuery filter configured
     * for a feature type. It won't handle coordinate system changes
     * <p>
     * Grabs the following from query:
     * <ul>
     * <li>typeName - only used if client does not supply
     * <li>filter - combined with client filter
     * <li>propertyNames - combined with client filter (indicate property names
     * that *must* be included)
     * </ul>
     * </p>
     *
     * @param query
     *            Query against this DataStore
     *
     * @return Query restricted to the limits of definitionQuery
     *
     * @throws IOException
     *             See DataSourceException
     * @throws DataSourceException
     *             If query could not meet the restrictions of definitionQuery
     */
    protected DefaultQuery makeDefinitionQuery(Query query) throws IOException {
        if ((query == Query.ALL) || query.equals(Query.ALL)) {
            return new DefaultQuery(constraintQuery);
        }

        try {
            String[] propNames = extractAllowedAttributes(query);

            String typeName = query.getTypeName();
            if (typeName == null) {
                typeName = constraintQuery.getTypeName();
            }

            URI namespace = query.getNamespace();
            if (namespace == null || namespace == Query.NO_NAMESPACE) {
                namespace = constraintQuery.getNamespace();
            }
            Filter filter = makeDefinitionFilter(query.getFilter());

            int maxFeatures = Math.min(query.getMaxFeatures(), constraintQuery.getMaxFeatures());

            String handle = query.getHandle();
            if (handle == null) {
                handle = constraintQuery.getHandle();
            } else if (constraintQuery.getHandle() != null) {
                handle = handle + "(" + constraintQuery.getHandle() + ")";
            }

            DefaultQuery defaultQuery = new DefaultQuery(typeName, namespace, filter, maxFeatures, propNames, handle);
            defaultQuery.setSortBy(query.getSortBy());
            return defaultQuery;
        } catch (Exception ex) {
            throw new DataSourceException(
                    "Could not restrict the query to the definition criteria: " + ex.getMessage(),
                    ex);
        }
    }

    /**
     * List of allowed attributes.
     *
     * <p>
     * Creates a list of FeatureTypeInfo's attribute names based on the
     * attributes requested by <code>query</code> and making sure they not
     * contain any non exposed attribute.
     * </p>
     *
     * <p>
     * Exposed attributes are those configured in the "attributes" element of
     * the FeatureTypeInfo's configuration
     * </p>
     *
     * @param query
     *            User's origional query
     *
     * @return List of allowed attribute types
     */
    private String[] extractAllowedAttributes(Query query) {
        String[] propNames = null;

        if (query.retrieveAllProperties()) {
            propNames = new String[schema.getAttributeCount()];

            for (int i = 0; i < schema.getAttributeCount(); i++) {
                propNames[i] = schema.getDescriptor(i).getLocalName();
            }
        } else {
            String[] queriedAtts = query.getPropertyNames();
            int queriedAttCount = queriedAtts.length;
            List allowedAtts = new LinkedList();

            for (int i = 0; i < queriedAttCount; i++) {
                if (schema.getDescriptor(queriedAtts[i]) != null) {
                    allowedAtts.add(queriedAtts[i]);
                } else {
                    LOGGER.info("queried a not allowed property: " + queriedAtts[i]
                            + ". Ommitting it from query");
                }
            }

            propNames = (String[]) allowedAtts.toArray(new String[allowedAtts.size()]);
        }

        return propNames;
    }

    /**
     * If a definition query has been configured for the FeatureTypeInfo, makes
     * and return a new Filter that contains both the query's filter and the
     * layer's definition one, by logic AND'ing them.
     *
     * @param filter
     *            Origional user supplied Filter
     *
     * @return Filter adjusted to the limitations of definitionQuery
     *
     * @throws DataSourceException
     *             If the filter could not meet the limitations of
     *             definitionQuery
     */
    protected Filter makeDefinitionFilter(Filter filter) throws DataSourceException {
        Filter newFilter = filter;
        Filter constraintFilter = constraintQuery.getFilter();
        try {
            if (constraintFilter != Filter.INCLUDE) {
                FilterFactory ff = CommonFactoryFinder.getFilterFactory(null);
                newFilter = ff.and(constraintFilter, filter);
            }
        } catch (Exception ex) {
            throw new DataSourceException("Can't create the constraint filter", ex);
        }
        return newFilter;
    }

    /**
     * Implement getDataStore.
     *
     * <p>
     * Description ...
     * </p>
     *
     * @return @see org.geotools.data.FeatureSource#getDataStore()
     */
    public DataStore getDataStore() {
        return (DataStore) source.getDataStore();
    }

    /**
     * Implement addFeatureListener.
     *
     * <p>
     * Description ...
     * </p>
     *
     * @param listener
     *
     * @see org.geotools.data.FeatureSource#addFeatureListener(org.geotools.data.FeatureListener)
     */
    public void addFeatureListener(FeatureListener listener) {
        source.addFeatureListener(listener);
    }

    /**
     * Implement removeFeatureListener.
     *
     * <p>
     * Description ...
     * </p>
     *
     * @param listener
     *
     * @see org.geotools.data.FeatureSource#removeFeatureListener(org.geotools.data.FeatureListener)
     */
    public void removeFeatureListener(FeatureListener listener) {
        source.removeFeatureListener(listener);
    }

    /**
     * Implement getFeatures.
     *
     * <p>
     * Description ...
     * </p>
     *
     * @param query
     *
     * @return @throws
     *         IOException
     *
     * @see org.geotools.data.FeatureSource#getFeatures(org.geotools.data.Query)
     */
    public SimpleFeatureCollection getFeatures(Query query) throws IOException {
        DefaultQuery mergedQuery = makeDefinitionQuery(query);
        SimpleFeatureCollection results = source.getFeatures(mergedQuery);

        // Get all the coordinate systems involved in the two queries
        CoordinateReferenceSystem cCs = constraintQuery.getCoordinateSystem();
        CoordinateReferenceSystem cCsr = constraintQuery.getCoordinateSystemReproject();
        CoordinateReferenceSystem qCs = query.getCoordinateSystem();
        CoordinateReferenceSystem qCsr = query.getCoordinateSystemReproject();

        /*
         * Here we create all the needed transformations. We assume for the
         * moment that the data stores are incapable of any kind of cs
         * transformation and neither capable of forcing cs. We also assume that
         * concatenating multiple forced and reprojected wrappers is inexpensive
         * since they are optimized to recognize each other and to avoid useless
         * object creation
         */
        try {
            if (qCsr != null && cCsr != null) {
                if (cCs != null)
                    results = new ForceCoordinateSystemFeatureResults(results, cCs);
                results = new ReprojectFeatureResults(results, cCsr);
                if (qCs != null)
                    results = new ForceCoordinateSystemFeatureResults(results, qCs);
                results = new ReprojectFeatureResults(results, qCsr);
            } else if (qCs != null && cCsr != null) {
                // complex case 2, reprojected then forced
                // mergedQuery.setCoordinateSystem(cCs);
                // mergedQuery.setCoordinateSystemReproject(cCsr);
                try {
                    if (cCs != null)
                        results = new ForceCoordinateSystemFeatureResults(results, cCs);
                    results = new ReprojectFeatureResults(source.getFeatures(mergedQuery), cCsr);

                    results = new ForceCoordinateSystemFeatureResults(results, qCs);
                } catch (SchemaException e) {
                    throw new DataSourceException("This should not happen", e);
                }
            } else {
                // easy case, we can just put toghether one forced cs and one
                // reprojection cs
                // in the mixed query and let it go

                // mergedQuery.setCoordinateSystem(qCs != null ? qCs : cCs);
                // mergedQuery.setCoordinateSystemReproject(qCsr != null ? qCsr
                // : cCsr);
                CoordinateReferenceSystem forcedCS = qCs != null ? qCs : cCs;
                CoordinateReferenceSystem reprojectCS = qCsr != null ? qCsr : cCsr;

                if (forcedCS != null)
                    results = new ForceCoordinateSystemFeatureResults(results, forcedCS);
                if (reprojectCS != null)
                    results = new ReprojectFeatureResults(results, reprojectCS);
            }
        } catch (IOException e) {
            throw e;
        } catch (Exception e) {
            throw new DataSourceException("A problem occurred while handling forced "
                    + "coordinate systems and reprojection", e);
        }

        return results;
    }

    /**
     * Implement getFeatures.
     *
     * <p>
     * Description ...
     * </p>
     *
     * @param filter
     *
     * @return @throws
     *         IOException
     */
    public SimpleFeatureCollection getFeatures(Filter filter) throws IOException {
        return getFeatures(new DefaultQuery(schema.getTypeName(),filter));
    }

    /**
     * Implement getFeatures.
     *
     * <p>
     * Description ...
     * </p>
     *
     * @return @throws
     *         IOException
     *
     * @see org.geotools.data.FeatureSource#getFeatures()
     */
    public SimpleFeatureCollection getFeatures() throws IOException {
        return getFeatures(Query.ALL);
    }

    /**
     * Implement getSchema.
     *
     * <p>
     * Description ...
     * </p>
     *
     * @return @see org.geotools.data.FeatureSource#getSchema()
     */
    public SimpleFeatureType getSchema() {
        return schema;
    }

    public ResourceInfo getInfo() {
        return new ResourceInfo(){
            final Set<String> words = new HashSet<String>();
            {
                words.add("features");
                words.add("view");
                words.add( DefaultView.this.getSchema().getTypeName() );
            }
            public ReferencedEnvelope getBounds() {
                try {
                    return DefaultView.this.getBounds();
                } catch (IOException e) {
                    return null;
                }
            }
            public CoordinateReferenceSystem getCRS() {
                return DefaultView.this.getSchema().getCoordinateReferenceSystem();
            }

            public String getDescription() {
                return null;
            }

            public Set<String> getKeywords() {
                return words;
            }

            public String getName() {
                return DefaultView.this.getSchema().getTypeName();
            }

            public URI getSchema() {
                Name name = DefaultView.this.getSchema().getName();
                URI namespace;
                try {
                    namespace = new URI( name.getNamespaceURI() );
                    return namespace;                   
                } catch (URISyntaxException e) {
                    return null;
                }               
            }

            public String getTitle() {
                Name name = DefaultView.this.getSchema().getName();
                return name.getLocalPart();
            }
           
        };
    }
   
    /**
     * Retrieves the total extent of this FeatureSource.
     *
     * <p>
     * Please note this extent will reflect the provided definitionQuery.
     * </p>
     *
     * @return Extent of this FeatureSource, or <code>null</code> if no
     *         optimizations exist.
     *
     * @throws IOException
     *             If bounds of definitionQuery
     */
    public ReferencedEnvelope getBounds() throws IOException {
        if (constraintQuery.getCoordinateSystemReproject() == null) {
            if (constraintQuery.getFilter() == null || constraintQuery.getFilter() == Filter.INCLUDE
                    || Filter.INCLUDE.equals(constraintQuery.getFilter())) {
                return source.getBounds();
            }
                return source.getBounds(constraintQuery);
           
        }
            // this will create a feature results that can reproject the
            // features, and will
            // properly compute the bouds
            return getFeatures().getBounds();
       
    }

    /**
     * Retrive the extent of the Query.
     *
     * <p>
     * This method provides access to an optimized getBounds opperation. If no
     * optimized opperation is available <code>null</code> will be returned.
     * </p>
     *
     * <p>
     * You may still make use of getFeatures( Query ).getCount() which will
     * return the correct answer (even if it has to itterate through all the
     * results to do so.
     * </p>
     *
     * @param query
     *            User's query
     *
     * @return Extend of Query or <code>null</code> if no optimization is
     *         available
     *
     * @throws IOException
     *             If a problem is encountered with source
     */
    public ReferencedEnvelope getBounds(Query query) throws IOException {
        if (constraintQuery.getCoordinateSystemReproject() == null) {
            try {
                query = makeDefinitionQuery(query);
            } catch (IOException ex) {
                return null;
            }

            return source.getBounds(query);
        }
            // this will create a feature results that can reproject the
            // features, and will
            // properly compute the bouds
            return getFeatures(query).getBounds();
    }

    /**
     * Adjust query and forward to source.
     *
     * <p>
     * This method provides access to an optimized getCount opperation. If no
     * optimized opperation is available <code>-1</code> will be returned.
     * </p>
     *
     * <p>
     * You may still make use of getFeatures( Query ).getCount() which will
     * return the correct answer (even if it has to itterate through all the
     * results to do so).
     * </p>
     *
     * @param query
     *            User's query.
     *
     * @return Number of Features for Query, or -1 if no optimization is
     *         available.
     */
    public int getCount(Query query) {
        try {
            query = makeDefinitionQuery(query);
        } catch (IOException ex) {
            return -1;
        }
        try {
            return source.getCount(query);
        } catch (IOException e) {
            return 0;
        }
    }
   
    public Set getSupportedHints() {
        return source.getSupportedHints();
    }


    public QueryCapabilities getQueryCapabilities() {
        return source.getQueryCapabilities();
    }
}
TOP

Related Classes of org.geotools.data.view.DefaultView

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.