//$Header: /home/deegree/jail/deegreerepository/deegree/src/org/deegree/io/datastore/shape/ShapeDatastore.java,v 1.50 2006/11/16 08:55:32 mschneider Exp $
/*---------------- FILE HEADER ------------------------------------------
This file is part of deegree.
Copyright (C) 2001-2006 by:
EXSE, Department of Geography, University of Bonn
http://www.giub.uni-bonn.de/deegree/
lat/lon GmbH
http://www.lat-lon.de
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; either
version 2.1 of the License, or (at your option) any later version.
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.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Contact:
Andreas Poth
lat/lon GmbH
Aennchenstr. 19
53115 Bonn
Germany
E-Mail: poth@lat-lon.de
Prof. Dr. Klaus Greve
Department of Geography
University of Bonn
Meckenheimer Allee 166
53115 Bonn
Germany
E-Mail: greve@giub.uni-bonn.de
---------------------------------------------------------------------------*/
package org.deegree.io.datastore.shape;
import java.io.IOException;
import java.net.URL;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.deegree.framework.log.ILogger;
import org.deegree.framework.log.LoggerFactory;
import org.deegree.framework.util.IDGenerator;
import org.deegree.framework.xml.NamespaceContext;
import org.deegree.framework.xml.XMLTools;
import org.deegree.i18n.Messages;
import org.deegree.io.datastore.Datastore;
import org.deegree.io.datastore.DatastoreException;
import org.deegree.io.datastore.DatastoreTransaction;
import org.deegree.io.datastore.schema.MappedFeatureType;
import org.deegree.io.datastore.schema.MappedGMLSchema;
import org.deegree.io.datastore.schema.MappedGeometryPropertyType;
import org.deegree.io.datastore.schema.MappedPropertyType;
import org.deegree.io.datastore.schema.MappedSimplePropertyType;
import org.deegree.io.datastore.schema.content.MappingField;
import org.deegree.io.datastore.schema.content.SimpleContent;
import org.deegree.io.dbaseapi.DBaseException;
import org.deegree.io.dbaseapi.DBaseFile;
import org.deegree.io.shpapi.HasNoDBaseFileException;
import org.deegree.io.shpapi.ShapeFile;
import org.deegree.model.crs.CRSFactory;
import org.deegree.model.crs.CoordinateSystem;
import org.deegree.model.feature.Feature;
import org.deegree.model.feature.FeatureCollection;
import org.deegree.model.feature.FeatureFactory;
import org.deegree.model.feature.schema.FeatureType;
import org.deegree.model.feature.schema.PropertyType;
import org.deegree.model.filterencoding.ComplexFilter;
import org.deegree.model.filterencoding.Filter;
import org.deegree.model.filterencoding.FilterEvaluationException;
import org.deegree.model.filterencoding.FilterTools;
import org.deegree.model.spatialschema.Envelope;
import org.deegree.model.spatialschema.GeometryImpl;
import org.deegree.ogcbase.CommonNamespaces;
import org.deegree.ogcwebservices.wfs.operation.Query;
import org.w3c.dom.Element;
/**
* {@link Datastore} implementation that allows read access to ESRI shape files.
*
* @author <a href="mailto:deshmukh@lat-lon.de">Anup Deshmukh</a>
* @author <a href="mailto:schneider@lat-lon.de">Markus Schneider </a>
* @author last edited by: $Author: mschneider $
*
* @version $Revision: 1.50 $, $Date: 2006/11/16 08:55:32 $
*/
public class ShapeDatastore extends Datastore {
private static final ILogger LOG = LoggerFactory.getLogger( ShapeDatastore.class );
private static final NamespaceContext nsContext = CommonNamespaces.getNamespaceContext();
// keys: FeatureTypes, values: Property-To-column mappings
private Map<FeatureType, Map> ftMappings = new HashMap<FeatureType, Map>();
// NOTE: this is equal for all bound schemas
private URL shapeFileURL;
private String srsName;
/**
* Adds the given GML application schema to the set of schemas that are handled by this
* datastore instance.
* <p>
* Note that this method may be called several times for every GML schema that uses this
* datastore instance.
*
* @param schema
* GML application schema to bind
* @throws DatastoreException
*/
@Override
public void bindSchema( MappedGMLSchema schema )
throws DatastoreException {
super.bindSchema( schema );
validate( schema );
srsName = schema.getDefaultSRS().toString();
}
/**
* Performs a {@link Query} against the datastore.
*
* @param query
* query to be performed
* @param ft
* the root feature type that is queried
* @return requested feature instances
* @throws DatastoreException
*/
@Override
public FeatureCollection performQuery( Query query, MappedFeatureType ft )
throws DatastoreException {
query = transformQuery( query );
FeatureCollection result = null;
ShapeFile shapeFile = null;
int startPosition = -1;
int maxFeatures = -1;
int record = -1;
try {
LOG.logDebug( "Opening shapefile '" + shapeFileURL.getFile() + ".shp'." );
shapeFile = new ShapeFile( shapeFileURL.getFile() );
startPosition = query.getStartPosition();
maxFeatures = query.getMaxFeatures();
Filter filter = query.getFilter();
Envelope bbox = null;
if ( filter instanceof ComplexFilter ) {
Object[] objects = null;
try {
objects = FilterTools.extractFirstBBOX( (ComplexFilter) filter );
} catch ( Exception e ) {
LOG.logError( e.getMessage(), e );
String msg = Messages.getMessage( "DATASTORE_EXTRACTBBOX", record );
throw new DatastoreException( msg, e );
}
bbox = (Envelope) objects[0];
filter = (Filter) objects[1];
}
if ( bbox == null ) {
bbox = shapeFile.getFileMBR();
}
shapeFile.setFeatureType( ft, ftMappings.get( ft ) );
int[] idx = shapeFile.getGeoNumbersByRect( bbox );
//id=identity required
IDGenerator idg = IDGenerator.getInstance();
String id = ft.getName().getLocalName();
id += idg.generateUniqueID();
if ( idx != null ) {
// check parameters for sanity
if ( startPosition < 1 ) {
startPosition = 1;
}
if ( ( maxFeatures < 0 ) || ( maxFeatures >= idx.length ) ) {
maxFeatures = idx.length;
}
LOG.logDebug( "Generating ID '" + id + "' for the FeatureCollection." );
result = FeatureFactory.createFeatureCollection( id, idx.length );
// TODO: respect startposition
CoordinateSystem crs = CRSFactory.create( srsName );
for ( int i = 0; i < maxFeatures; i++ ) {
record = idx[i];
Feature feat = shapeFile.getFeatureByRecNo( idx[i] );
if ( filter == null || filter.evaluate( feat ) ) {
LOG.logDebug( "Adding feature '" + feat.getId() + "' to FeatureCollection." );
GeometryImpl geom = (GeometryImpl) feat.getDefaultGeometryPropertyValue();
geom.setCoordinateSystem( crs );
result.add( feat );
}
}
} else {
result = FeatureFactory.createFeatureCollection( id, 1 );
}
} catch ( IOException e ) {
LOG.logError( e.getMessage(), e );
String msg = Messages.getMessage( "DATASTORE_READINGFROMDBF", record );
throw new DatastoreException( msg, e );
} catch ( DBaseException e ) {
LOG.logError( e.getMessage(), e );
String msg = Messages.getMessage( "DATASTORE_READINGFROMDBF", record );
throw new DatastoreException( msg, e );
} catch ( HasNoDBaseFileException e ) {
LOG.logError( e.getMessage(), e );
String msg = Messages.getMessage( "DATASTORE_NODBASEFILE", record );
throw new DatastoreException( msg, e );
} catch ( FilterEvaluationException e ) {
throw new DatastoreException( e.getMessage(), e );
} catch ( Exception e ) {
LOG.logError( e.getMessage(), e );
String msg = Messages.getMessage( "DATASTORE_READINGFROMDBF", record );
throw new DatastoreException( msg, e );
} finally {
LOG.logDebug( "Closing shapefile." );
try {
shapeFile.close();
} catch ( Exception e ) {
String msg = Messages.getMessage( "DATASTORE_ERROR_CLOSING_SHAPEFILE",
this.shapeFileURL.getFile() );
throw new DatastoreException( msg );
}
}
// transform result to queried srs if necessary
String targetSrsName = query.getSrsName();
if (targetSrsName != null && !targetSrsName.equals(this.srsName)) {
result = transformResult( result, query.getSrsName() );
}
return result;
}
/**
* Performs a {@link Query} against the datastore (in the given transaction context).
* <p>
* NOTE: In ShapeDatastore, this method behaves exactly the same as
* #performQuery(Query,MappedFeatureType,DatastoreTransaction).
*
* @param query
* query to be performed
* @param rootFeatureType
* the root feature type that is queried
* @param context
* context (used to specify the JDBCConnection, for example)
* @return requested feature instances
* @throws DatastoreException
*/
@Override
public FeatureCollection performQuery( final Query query,
final MappedFeatureType rootFeatureType,
final DatastoreTransaction context )
throws DatastoreException {
return performQuery( query, rootFeatureType );
}
/**
* Validates the given {@link MappedGMLSchema} against the available columns in the
* referenced shape file.
*
* @param schema
* @throws DatastoreException
*/
private void validate( MappedGMLSchema schema )
throws DatastoreException {
Set<String> columnNames = determineShapeFileColumns( schema );
FeatureType[] featureTypes = schema.getFeatureTypes();
for ( int i = 0; i < featureTypes.length; i++ ) {
Map<PropertyType, String> ftMapping = getFTMapping( featureTypes[i], columnNames );
ftMappings.put( featureTypes[i], ftMapping );
}
}
private Map<PropertyType, String> getFTMapping( FeatureType ft, Set<String> columnNames )
throws DatastoreException {
Map<PropertyType, String> ftMapping = new HashMap<PropertyType, String>();
PropertyType[] properties = ft.getProperties();
for ( int i = 0; i < properties.length; i++ ) {
MappedPropertyType pt = (MappedPropertyType) properties[i];
if ( pt instanceof MappedSimplePropertyType ) {
SimpleContent content = ( (MappedSimplePropertyType) pt ).getContent();
if ( !( content instanceof MappingField ) ) {
String msg = Messages.getMessage( "DATASTORE_UNSUPPORTED_CONTENT", pt.getName() );
throw new DatastoreException( msg );
}
String field = ( (MappingField) content ).getField();
if ( !columnNames.contains( field ) ) {
String msg = Messages.getMessage( "DATASTORE_FIELDNOTFOUND", field,
pt.getName(), shapeFileURL.getFile(),
columnNames );
throw new DatastoreException( msg );
}
ftMapping.put( pt, field );
} else if ( pt instanceof MappedGeometryPropertyType ) {
// nothing to do
} else {
String msg = Messages.getMessage( "DATASTORE_NO_NESTED_FEATURE_TYPES", pt.getName() );
throw new DatastoreException( msg );
}
}
return ftMapping;
}
private Set<String> determineShapeFileColumns( MappedGMLSchema schema )
throws DatastoreException {
Set<String> columnNames = new HashSet<String>();
DBaseFile dbfFile = null;
try {
Element schemaRoot = schema.getDocument().getRootElement();
String shapePath = XMLTools.getNodeAsString(
schemaRoot,
"xs:annotation/xs:appinfo/deegreewfs:File/text()",
nsContext, null );
shapeFileURL = schema.getDocument().resolve( shapePath );
LOG.logDebug( "Opening dbf file '" + shapeFileURL + "'." );
dbfFile = new DBaseFile( shapeFileURL.getFile() );
String[] columns = dbfFile.getProperties();
for ( int i = 0; i < columns.length; i++ ) {
columnNames.add( columns[i] );
}
String s = "Successfully opened dbf file '" + shapeFileURL.getFile()
+ "' and retrieved the property columns.";
LOG.logInfo( s );
} catch ( Exception e ) {
LOG.logError( e.getMessage(), e );
throw new DatastoreException( Messages.getMessage( "DATASTORE_DBACCESSERROR" ) );
} finally {
if ( dbfFile != null ) {
dbfFile.close();
}
}
return columnNames;
}
/**
* Closes the datastore so it can free dependent resources.
*
* @throws DatastoreException
*/
@Override
public void close()
throws DatastoreException {
// TODO
}
}
/* ********************************************************************
Changes to this class. What the people have been up to:
$Log: ShapeDatastore.java,v $
Revision 1.50 2006/11/16 08:55:32 mschneider
Moved exceptions for unimplemented methods to abstract Datastore class.
Revision 1.49 2006/11/15 12:56:13 schmitz
Fixed nullpointerexception.
Revision 1.48 2006/11/09 17:38:38 mschneider
Added check if transformation is necessary.
Revision 1.47 2006/10/10 15:52:25 mschneider
Added comment because of startPosition parameter.
Revision 1.46 2006/09/27 20:30:20 mschneider
Activated transformation of query and result SRS.
Revision 1.45 2006/09/20 11:35:41 mschneider
Merged datastore related messages with org.deegree.18n.
Revision 1.44 2006/09/05 17:42:56 mschneider
Added @Override annotations. Fixed logging output of filenames.
Revision 1.43 2006/08/30 18:08:26 mschneider
Fixed exception chaining.
Revision 1.42 2006/08/30 17:40:44 mschneider
Removed System.out.println-calls.
Revision 1.41 2006/08/30 17:01:19 mschneider
Major cleanup and javadoc corrections.
Revision 1.40 2006/07/10 21:07:31 mschneider
Removed System.out.println's.
Revision 1.39 2006/06/07 14:55:58 poth
*** empty log message ***
Revision 1.38 2006/06/06 10:33:28 poth
bug fix
Revision 1.37 2006/06/06 07:57:27 poth
bug fix reading feature collection outside a shapes MBR / performance enhancement
Revision 1.36 2006/05/31 07:05:44 taddei
chnaged log level fom info to debug
Revision 1.35 2006/05/30 08:08:36 taddei
bug fix: datastore was returning no props, instead of all (when props not specified)
Revision 1.34 2006/05/29 20:42:42 poth
some code enhancements
Revision 1.33 2006/05/29 16:15:14 poth
bug fix - dbase file closed after reading column definitions
Revision 1.32 2006/05/29 12:31:57 taddei
fix for missing prefix
Revision 1.31 2006/05/26 14:52:17 taddei
bug fixes (srs, wfs:prefix)
Revision 1.30 2006/05/24 13:50:24 poth
bug fix creating feature
********************************************************************** */