Package org.geotools.data.shapefile

Source Code of org.geotools.data.shapefile.ShapefileFeatureSource

/*
*    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.geotools.data.shapefile;

import static org.geotools.data.shapefile.files.ShpFileType.*;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.ReadableByteChannel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.geotools.data.DataSourceException;
import org.geotools.data.EmptyFeatureReader;
import org.geotools.data.FeatureReader;
import org.geotools.data.FeatureSource;
import org.geotools.data.PrjFileReader;
import org.geotools.data.Query;
import org.geotools.data.ReTypeFeatureReader;
import org.geotools.data.shapefile.dbf.DbaseFileHeader;
import org.geotools.data.shapefile.dbf.DbaseFileReader;
import org.geotools.data.shapefile.fid.IndexedFidReader;
import org.geotools.data.shapefile.files.FileReader;
import org.geotools.data.shapefile.files.ShpFiles;
import org.geotools.data.shapefile.index.CloseableIterator;
import org.geotools.data.shapefile.index.Data;
import org.geotools.data.shapefile.index.TreeException;
import org.geotools.data.shapefile.shp.IndexFile;
import org.geotools.data.shapefile.shp.JTSUtilities;
import org.geotools.data.shapefile.shp.ShapefileHeader;
import org.geotools.data.shapefile.shp.ShapefileReader;
import org.geotools.data.store.ContentEntry;
import org.geotools.data.store.ContentFeatureSource;
import org.geotools.factory.Hints;
import org.geotools.factory.Hints.Key;
import org.geotools.feature.AttributeTypeBuilder;
import org.geotools.feature.FeatureTypes;
import org.geotools.feature.simple.SimpleFeatureTypeBuilder;
import org.geotools.feature.type.BasicFeatureTypes;
import org.geotools.filter.FilterAttributeExtractor;
import org.geotools.filter.visitor.ExtractBoundsFilterVisitor;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.renderer.ScreenMap;
import org.geotools.resources.Classes;
import org.geotools.util.logging.Logging;
import org.opengis.feature.FeatureVisitor;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.AttributeDescriptor;
import org.opengis.feature.type.GeometryDescriptor;
import org.opengis.feature.type.GeometryType;
import org.opengis.filter.Filter;
import org.opengis.filter.Id;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;

import com.vividsolutions.jts.geom.CoordinateSequenceFactory;
import com.vividsolutions.jts.geom.Envelope;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.LineString;
import com.vividsolutions.jts.geom.MultiLineString;
import com.vividsolutions.jts.geom.MultiPoint;
import com.vividsolutions.jts.geom.MultiPolygon;
import com.vividsolutions.jts.geom.Point;
import com.vividsolutions.jts.geom.Polygon;

/**
* A {@link FeatureSource} for shapefiles based on {@link ContentFeatureSource}
*
* @author Andrea Aime - GeoSolutions
*/
class ShapefileFeatureSource extends ContentFeatureSource {

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

    ShpFiles shpFiles;

    public ShapefileFeatureSource(ContentEntry entry, ShpFiles shpFiles) {
        super(entry, Query.ALL);
        this.shpFiles = shpFiles;
        HashSet<Key> hints = new HashSet<Hints.Key>();
        hints.add(Hints.FEATURE_DETACHED);
        hints.add(Hints.JTS_GEOMETRY_FACTORY);
        hints.add(Hints.JTS_COORDINATE_SEQUENCE_FACTORY);
        hints.add(Hints.GEOMETRY_DISTANCE);
        hints.add(Hints.SCREENMAP);
        this.hints = Collections.unmodifiableSet(hints);
    }

    @Override
    public ShapefileDataStore getDataStore() {
        return (ShapefileDataStore) super.getDataStore();
    }

    @Override
    protected boolean canFilter() {
        return true;
    }

    @Override
    protected boolean canRetype() {
        return true;
    }

    @Override
    protected ReferencedEnvelope getBoundsInternal(Query query) throws IOException {
        if (query.getFilter() != Filter.INCLUDE) {
            return null;
        }

        ReadableByteChannel in = null;
        try {
            ByteBuffer buffer = ByteBuffer.allocate(100);
            FileReader reader = new FileReader() {
                public String id() {
                    return "Shapefile Datastore's getBounds Method";
                }
            };

            in = shpFiles.getReadChannel(SHP, reader);
            try {
                in.read(buffer);
                buffer.flip();

                ShapefileHeader header = new ShapefileHeader();
                header.read(buffer, true);

                SimpleFeatureType schema = getSchema();
                ReferencedEnvelope bounds = new ReferencedEnvelope(
                        schema.getCoordinateReferenceSystem());
                bounds.include(header.minX(), header.minY());
                bounds.include(header.minX(), header.minY());

                Envelope env = new Envelope(header.minX(), header.maxX(), header.minY(),
                        header.maxY());

                CoordinateReferenceSystem crs = null;
                if (schema != null) {
                    crs = schema.getCoordinateReferenceSystem();
                }
                return new ReferencedEnvelope(env, crs);
            } finally {
                in.close();
            }

        } catch (IOException ioe) {
            throw new DataSourceException("Problem getting Bbox", ioe);
        } finally {
            try {
                if (in != null) {
                    in.close();
                }
            } catch (IOException ioe) {
                // do nothing
            }
        }
    }

    @Override
    protected int getCountInternal(Query query) throws IOException {
        if (query.getFilter() == Filter.INCLUDE) {
            IndexFile file = getDataStore().shpManager.openIndexFile();
            if (file != null) {
                try {
                    return file.getRecordCount();
                } finally {
                    file.close();
                }
            }

            // no Index file so use the number of shapefile records
            ShapefileReader reader = getDataStore().shpManager.openShapeReader(
                    new GeometryFactory(), false);
            int count = -1;

            try {
                count = reader.getCount(count);
            } catch (IOException e) {
                throw e;
            } finally {
                reader.close();
            }

            return count;

        }

        return -1;

    }

    @Override
    protected FeatureReader<SimpleFeatureType, SimpleFeature> getReaderInternal(Query q)
            throws IOException {
        SimpleFeatureType resultSchema = getResultSchema(q);
        SimpleFeatureType readSchema = getReadSchema(q);
        GeometryFactory geometryFactory = getGeometryFactory(q);

        // grab the target bbox, if any
        Envelope bbox = new ReferencedEnvelope();
        if (q.getFilter() != null) {
            bbox = (Envelope) q.getFilter().accept(ExtractBoundsFilterVisitor.BOUNDS_VISITOR, bbox);
        }

        // see if we can use indexing to speedup the data access
        Filter filter = q != null ? q.getFilter() : null;
        IndexManager indexManager = getDataStore().indexManager;
        CloseableIterator<Data> goodRecs = null;
        if (getDataStore().isFidIndexed() && filter instanceof Id && indexManager.hasFidIndex(false)) {
            Id fidFilter = (Id) filter;
            List<Data> records = indexManager.queryFidIndex(fidFilter);
            if (records != null) {
                goodRecs = new CloseableIteratorWrapper<Data>(records.iterator());
            }
        } else if (getDataStore().isIndexed() && !bbox.isNull()
                && !Double.isInfinite(bbox.getWidth()) && !Double.isInfinite(bbox.getHeight())) {
            try {
                if(indexManager.isSpatialIndexAvailable() || getDataStore().isIndexCreationEnabled()) {
                    goodRecs = indexManager.querySpatialIndex(bbox);
                }
            } catch (TreeException e) {
                throw new IOException("Error querying index: " + e.getMessage());
            }
        }
        // do we have anything to read at all? If not don't bother opening all the files
        if (goodRecs != null && !goodRecs.hasNext()) {
            LOGGER.log(Level.FINE, "Empty results for " + resultSchema.getName().getLocalPart()
                    + ", skipping read");
            goodRecs.close();
            return new EmptyFeatureReader<SimpleFeatureType, SimpleFeature>(resultSchema);
        }
       
        // get the .fix file reader, if we have a .fix file
        IndexedFidReader fidReader = null;
        if (getDataStore().isFidIndexed() && filter instanceof Id && indexManager.hasFidIndex(false)) {
            fidReader = new IndexedFidReader(shpFiles);
        }

        // setup the feature readers
        ShapefileSetManager shpManager = getDataStore().shpManager;
        ShapefileReader shapeReader = shpManager.openShapeReader(geometryFactory, goodRecs != null);
        DbaseFileReader dbfReader = null;
        List<AttributeDescriptor> attributes = readSchema.getAttributeDescriptors();
        if (attributes.size() < 1
                || (attributes.size() == 1 && readSchema.getGeometryDescriptor() != null)) {
            LOGGER.fine("The DBF file won't be opened since no attributes will be read from it");
        } else {
            dbfReader = shpManager.openDbfReader(goodRecs != null);
        }
        ShapefileFeatureReader reader;
        if (goodRecs != null) {
            reader = new IndexedShapefileFeatureReader(readSchema, shapeReader, dbfReader, fidReader,
                    goodRecs);
        } else {
            reader = new ShapefileFeatureReader(readSchema, shapeReader, dbfReader, fidReader);
        }
        if (filter != null && !Filter.INCLUDE.equals(filter)) {
            reader.setFilter(filter);
        }

        // setup the target bbox if any, and the generalization hints if available
        if (q != null) {
            if (bbox != null && !bbox.isNull()) {
                reader.setTargetBBox(bbox);
            }

            Hints hints = q.getHints();
            if (hints != null) {
                Number simplificationDistance = (Number) hints.get(Hints.GEOMETRY_DISTANCE);
                if (simplificationDistance != null) {
                    reader.setSimplificationDistance(simplificationDistance.doubleValue());
                }
                reader.setScreenMap((ScreenMap) hints.get(Hints.SCREENMAP));

                if (Boolean.TRUE.equals(hints.get(Hints.FEATURE_2D))) {
                    shapeReader.setFlatGeometry(true);
                }
            }

        }

        // do the retyping
        if(!FeatureTypes.equals(readSchema, resultSchema)) {
           return new ReTypeFeatureReader(reader, resultSchema);
        } else {
            return reader;
        }
    }

    SimpleFeatureType getResultSchema(Query q) {
        if (q.getPropertyNames() == null) {
            return getSchema();
        } else {
            return SimpleFeatureTypeBuilder.retype(getSchema(), q.getPropertyNames());
        }
    }
   
    SimpleFeatureType getReadSchema(Query q) {
        if (q.getPropertyNames() == null) {
            return getSchema();
        } else {
            LinkedHashSet<String> attributes = new LinkedHashSet<String>();
            attributes.addAll(Arrays.asList(q.getPropertyNames()));
            Filter filter = q.getFilter();
            if(filter != null && !Filter.INCLUDE.equals(filter)) {
                FilterAttributeExtractor fat = new FilterAttributeExtractor();
                filter.accept(fat, null);
                attributes.addAll(fat.getAttributeNameSet());
            }
           
            return SimpleFeatureTypeBuilder.retype(getSchema(), new ArrayList<String>(attributes));
        }
    }

    /**
     * Builds the most appropriate geometry factory depending on the available query hints
     *
     * @param query
     * @return
     */
    protected GeometryFactory getGeometryFactory(Query query) {
        // if no hints, use the default geometry factory
        if (query == null || query.getHints() == null) {
            return new GeometryFactory();
        }

        // grab a geometry factory... check for a special hint
        Hints hints = query.getHints();
        GeometryFactory geometryFactory = (GeometryFactory) hints.get(Hints.JTS_GEOMETRY_FACTORY);
        if (geometryFactory == null) {
            // look for a coordinate sequence factory
            CoordinateSequenceFactory csFactory = (CoordinateSequenceFactory) hints
                    .get(Hints.JTS_COORDINATE_SEQUENCE_FACTORY);

            if (csFactory != null) {
                geometryFactory = new GeometryFactory(csFactory);
            }
        }

        if (geometryFactory == null) {
            // fall back on the default one
            geometryFactory = new GeometryFactory();
        }
        return geometryFactory;
    }

    @Override
    protected SimpleFeatureType buildFeatureType() throws IOException {
        List<AttributeDescriptor> types = readAttributes();

        SimpleFeatureType parent = null;
        GeometryDescriptor geomDescriptor = (GeometryDescriptor) types.get(0);
        Class<?> geomBinding = geomDescriptor.getType().getBinding();

        if ((geomBinding == Point.class) || (geomBinding == MultiPoint.class)) {
            parent = BasicFeatureTypes.POINT;
        } else if ((geomBinding == Polygon.class) || (geomBinding == MultiPolygon.class)) {
            parent = BasicFeatureTypes.POLYGON;
        } else if ((geomBinding == LineString.class) || (geomBinding == MultiLineString.class)) {
            parent = BasicFeatureTypes.LINE;
        }

        SimpleFeatureTypeBuilder builder = new SimpleFeatureTypeBuilder();
        builder.setDefaultGeometry(geomDescriptor.getLocalName());
        builder.addAll(types);
        builder.setName(entry.getName());
        builder.setAbstract(false);
        if (parent != null) {
            builder.setSuperType(parent);
        }
        return builder.buildFeatureType();
    }

    /**
     * Create the AttributeDescriptor contained within this DataStore.
     *
     * @return List of new AttributeDescriptor
     * @throws IOException If AttributeType reading fails
     */
    protected List<AttributeDescriptor> readAttributes() throws IOException {
        ShapefileSetManager shpManager = getDataStore().shpManager;
        PrjFileReader prj = null;
        ShapefileReader shp = null;
        DbaseFileReader dbf = null;
        CoordinateReferenceSystem crs = null;

        AttributeTypeBuilder build = new AttributeTypeBuilder();
        List<AttributeDescriptor> attributes = new ArrayList<AttributeDescriptor>();
        try {
            shp = shpManager.openShapeReader(new GeometryFactory(), false);
            dbf = shpManager.openDbfReader(false);
            try {
                prj = shpManager.openPrjReader();

                if (prj != null) {
                    crs = prj.getCoordinateReferenceSystem();
                }
            } catch (FactoryException fe) {
                crs = null;
            }
           
            Class<?> geometryClass = JTSUtilities.findBestGeometryClass(shp.getHeader()
                    .getShapeType());
            build.setName(Classes.getShortName(geometryClass));
            build.setNillable(true);
            build.setCRS(crs);
            build.setBinding(geometryClass);

            GeometryType geometryType = build.buildGeometryType();
            attributes.add(build.buildDescriptor(BasicFeatureTypes.GEOMETRY_ATTRIBUTE_NAME,
                    geometryType));
            Set<String> usedNames = new HashSet<String>(); // record names in
            // case of
            // duplicates
            usedNames.add(BasicFeatureTypes.GEOMETRY_ATTRIBUTE_NAME);

            // take care of the case where no dbf and query wants all =>
            // geometry only
            if (dbf != null) {
                DbaseFileHeader header = dbf.getHeader();
                for (int i = 0, ii = header.getNumFields(); i < ii; i++) {
                    Class attributeClass = header.getFieldClass(i);
                    String name = header.getFieldName(i);
                    if (usedNames.contains(name)) {
                        String origional = name;
                        int count = 1;
                        name = name + count;
                        while (usedNames.contains(name)) {
                            count++;
                            name = origional + count;
                        }
                        build.addUserData(ShapefileDataStore.ORIGINAL_FIELD_NAME, origional);
                        build.addUserData(ShapefileDataStore.ORIGINAL_FIELD_DUPLICITY_COUNT, count);
                    }
                    usedNames.add(name);
                    int length = header.getFieldLength(i);

                    build.setNillable(true);
                    build.setLength(length);
                    build.setBinding(attributeClass);
                    attributes.add(build.buildDescriptor(name));
                }
            }
            return attributes;
        } finally {

            try {
                if (prj != null) {
                    prj.close();
                }
            } catch (IOException ioe) {
                // do nothing
            }
            try {
                if (dbf != null) {
                    dbf.close();
                }
            } catch (IOException ioe) {
                // do nothing
            }
            try {
                if (shp != null) {
                    shp.close();
                }
            } catch (IOException ioe) {
                // do nothing
            }
        }
    }

    @Override
    protected boolean handleVisitor(Query query, FeatureVisitor visitor) throws IOException {
        return super.handleVisitor(query, visitor);
    }

}
TOP

Related Classes of org.geotools.data.shapefile.ShapefileFeatureSource

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.