/* * Geotoolkit - An Open Source Java GIS Toolkit * http://www.geotoolkit.org * * (C) 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.geotoolkit.data.shapefile; import java.io.IOException; import java.nio.charset.Charset; import org.apache.sis.internal.feature.AttributeConvention; import org.geotoolkit.data.dbf.DbaseFileHeader; import org.geotoolkit.data.dbf.DbaseFileReader; import org.geotoolkit.data.shapefile.lock.AccessManager; import org.geotoolkit.data.shapefile.shp.ShapefileReader; import org.apache.sis.storage.DataStoreException; import org.apache.sis.util.ArgumentChecks; import org.apache.sis.util.ObjectConverters; import org.opengis.feature.AttributeType; import org.opengis.feature.PropertyNotFoundException; /** * An AttributeReader implementation for Shapefile. Pretty straightforward. * <BR/>The default geometry is at position 0, and all dbf columns follow. * <BR/>The dbf file may not be necessary, if not, just pass null as the * DbaseFileReader * @module */ public class ShapefileAttributeReader { private final AccessManager locker; protected final AttributeType[] metaData; protected final boolean[] narrowing; protected final int[] attributIndex; protected ShapefileReader shp; protected DbaseFileReader dbf; protected DbaseFileReader.Row row; protected ShapefileReader.Record record; private boolean closed = false; //feature bbox must be bigger than this, otherwise shape geometry is only estimated private final boolean estimateRes; private final double estimateX; private final double estimateY; /** * Create the shapefile attribute reader * * @param locker - to aquiere different readers and writers. * @param atts - the attributes that we are going to read. * @param read3D - for shp reader, read 3d coordinate or not. * @param memoryMapped - for shp and dbf reader * @param resample - for shp reader, decimate coordinates while reading * @param readDBF - true to open a dbf reader * @param charset - for dbf reader * @param estimateRes - avoid reading geometry if under this resolution, * while return an approximate geometry */ public ShapefileAttributeReader(final AccessManager locker, final AttributeType[] atts, final boolean read3D, final boolean memoryMapped, final double[] resample, final boolean readDBF, final Charset charset, final double[] estimateRes) throws IOException, DataStoreException { ArgumentChecks.ensureNonNull("locker", locker); this.locker = locker; this.metaData = atts; this.shp = locker.getSHPReader(true, memoryMapped, read3D, resample); this.dbf = locker.getDBFReader(memoryMapped, charset); if(estimateRes != null){ this.estimateRes = true; this.estimateX = estimateRes[0]; this.estimateY = estimateRes[1]; }else{ this.estimateRes = false; this.estimateX = 0; this.estimateY = 0; } //ensure we don't have the identifier property for (int i=0; i<atts.length; i++) { if (AttributeConvention.contains(atts[i].getName())) { throw new DataStoreException("Atribute reader contains an invalid property : "+atts[i].getName()); } } //the attribut descriptor might define types that are mare restrictive //then what the readers can do. narrowing = new boolean[atts.length]; attributIndex = new int[atts.length]; if(dbf != null){ final DbaseFileHeader header = dbf.getHeader(); attLoop: for(int i=0;i<atts.length;i++){ final String attName = atts[i].getName().tip().toString(); //attribut field for(int k=0;k<header.getNumFields();k++){ final String fieldName = header.getFieldName(k); if(fieldName.equals(attName)){ narrowing[i] = (((AttributeType)atts[i]).getValueClass() != header.getFieldClass(k)); attributIndex[i] = k; continue attLoop; } } //geom field attributIndex[i] = -1; } }else{ if(atts.length != 1){ throw new IllegalStateException("Reader has been asked to read "+atts.length+" attributs, but no dbf reader given."); } //geom field attributIndex[0] = -1; } } public AccessManager getLocker() { return locker; } /** * {@inheritDoc } */ public void close() throws IOException { if(!closed){ closed = true; try { locker.disposeReaderAndWriters(); } finally { row = null; record = null; shp = null; dbf = null; } } } /** * {@inheritDoc } */ public boolean hasNext() throws IOException { if(shp.hasNext()){ if(dbf != null){ if(dbf.hasNext()){ return true; }else{ //shape files has more data then the dbf ? file or reader is corrupted throw new IOException("Shp has extra record"); } }else{ //no attributs, only shapes return true; } }else{ if(dbf != null){ if(!dbf.hasNext()){ return false; }else{ //dbf has more data then the shape ? file or reader is corrupted throw new IOException("Dbf has extra record"); } }else{ //no attributs, only shapes return false; } } } /** * {@inheritDoc } */ public void next() throws IOException { nextShape(); nextDbf(); } protected void nextShape() throws IOException { record = shp.nextRecord(); } protected void nextDbf() throws IOException { if (dbf != null) { row = dbf.next(); } } /** * {@inheritDoc } */ public Object read(final int param) throws IOException,IndexOutOfBoundsException { final int index = attributIndex[param]; if(index == -1){ if(estimateRes && !(Double.isNaN(record.maxX) || Double.isNaN(record.maxY) || Double.isNaN(record.minY) || Double.isNaN(record.minX)) && !(estimateX <= (record.maxX - record.minX) || estimateY <= (record.maxY - record.minY))){ //read estimated shape return record.estimatedShape(); }else{ //read full shape return record.shape(); } }else if(row != null) { if(narrowing[param]){ //must procede to a retype return ObjectConverters.convert(row.read(index), metaData[param].getValueClass()); }else{ return row.read(index); } } return null; } /** * {@inheritDoc } */ public void read(final Object[] buffer) throws IOException { for(int i=0;i<metaData.length;i++){ buffer[i] = read(i); } } public AttributeType[] getPropertyDescriptors() { return metaData; } public int getPropertyCount() { return metaData.length; } }