Author: mpazos Date: 2011-12-08 11:57:35 -0800 (Thu, 08 Dec 2011) New Revision: 38412 Added: trunk/spike/mauro/ogr/ trunk/spike/mauro/ogr/.classpath trunk/spike/mauro/ogr/.project trunk/spike/mauro/ogr/.settings/ trunk/spike/mauro/ogr/.settings/org.eclipse.jdt.core.prefs trunk/spike/mauro/ogr/jnaerate.sh trunk/spike/mauro/ogr/old_ogr_code/ trunk/spike/mauro/ogr/pom.xml trunk/spike/mauro/ogr/src/ trunk/spike/mauro/ogr/src/main/ trunk/spike/mauro/ogr/src/main/java/ trunk/spike/mauro/ogr/src/main/java/org/ trunk/spike/mauro/ogr/src/main/java/org/geotools/ trunk/spike/mauro/ogr/src/main/java/org/geotools/data/ trunk/spike/mauro/ogr/src/main/java/org/geotools/data/ogr/ trunk/spike/mauro/ogr/src/main/java/org/geotools/data/ogr/FeatureMapper.java trunk/spike/mauro/ogr/src/main/java/org/geotools/data/ogr/FeatureTypeMapper.java trunk/spike/mauro/ogr/src/main/java/org/geotools/data/ogr/FilterToRestrictedWhere.java trunk/spike/mauro/ogr/src/main/java/org/geotools/data/ogr/GdalInit.java trunk/spike/mauro/ogr/src/main/java/org/geotools/data/ogr/GeometryMapper.java trunk/spike/mauro/ogr/src/main/java/org/geotools/data/ogr/OGRDataStore.java trunk/spike/mauro/ogr/src/main/java/org/geotools/data/ogr/OGRDataStoreFactory.java trunk/spike/mauro/ogr/src/main/java/org/geotools/data/ogr/OGRDirectFeatureWriter.java trunk/spike/mauro/ogr/src/main/java/org/geotools/data/ogr/OGRFeatureReader.java trunk/spike/mauro/ogr/src/main/java/org/geotools/data/ogr/OGRFeatureSource.java trunk/spike/mauro/ogr/src/main/java/org/geotools/data/ogr/OGRFeatureStore.java trunk/spike/mauro/ogr/src/main/java/org/geotools/data/ogr/OGRFilterTranslator.java trunk/spike/mauro/ogr/src/main/java/org/geotools/data/ogr/OGRUtils.java trunk/spike/mauro/ogr/src/main/java/org/geotools/data/ogr/bridj/ trunk/spike/mauro/ogr/src/main/java/org/geotools/data/ogr/bridj/BridjUtilities.java trunk/spike/mauro/ogr/src/main/java/org/geotools/data/ogr/bridj/CplErrorLibrary.java trunk/spike/mauro/ogr/src/main/java/org/geotools/data/ogr/bridj/OGREnvelope.java trunk/spike/mauro/ogr/src/main/java/org/geotools/data/ogr/bridj/OGRField.java trunk/spike/mauro/ogr/src/main/java/org/geotools/data/ogr/bridj/OgrLibrary.java trunk/spike/mauro/ogr/src/main/java/org/geotools/data/ogr/bridj/OsrLibrary.java trunk/spike/mauro/ogr/src/main/java/org/geotools/data/ogr/bridj/__locale_struct.java trunk/spike/mauro/ogr/src/main/resources/ trunk/spike/mauro/ogr/src/main/resources/META-INF/ trunk/spike/mauro/ogr/src/main/resources/META-INF/services/ trunk/spike/mauro/ogr/src/main/resources/META-INF/services/org.geotools.data.DataStoreFactorySpi trunk/spike/mauro/ogr/src/site/ trunk/spike/mauro/ogr/src/site/apt/ trunk/spike/mauro/ogr/src/site/apt/review.apt trunk/spike/mauro/ogr/src/test/ trunk/spike/mauro/ogr/src/test/java/ trunk/spike/mauro/ogr/src/test/java/org/ trunk/spike/mauro/ogr/src/test/java/org/geotools/ trunk/spike/mauro/ogr/src/test/java/org/geotools/data/ trunk/spike/mauro/ogr/src/test/java/org/geotools/data/ogr/ trunk/spike/mauro/ogr/src/test/java/org/geotools/data/ogr/DumpShapefile.java trunk/spike/mauro/ogr/src/test/java/org/geotools/data/ogr/GeometryMapperTest.java trunk/spike/mauro/ogr/src/test/java/org/geotools/data/ogr/OGRDataStoreFactoryTest.java trunk/spike/mauro/ogr/src/test/java/org/geotools/data/ogr/OGRDataStoreTest.java trunk/spike/mauro/ogr/src/test/java/org/geotools/data/ogr/OGRDataStoreWriter.java trunk/spike/mauro/ogr/src/test/java/org/geotools/data/ogr/OGRPeformanceTest.java trunk/spike/mauro/ogr/src/test/java/org/geotools/data/ogr/TabFileReader.java trunk/spike/mauro/ogr/src/test/java/org/geotools/data/ogr/TabFileWriter.java trunk/spike/mauro/ogr/src/test/java/org/geotools/data/ogr/TestCaseSupport.java trunk/spike/mauro/ogr/src/test/resources/ trunk/spike/mauro/ogr/src/test/resources/org/ trunk/spike/mauro/ogr/src/test/resources/org/geotools/ trunk/spike/mauro/ogr/src/test/resources/org/geotools/data/ trunk/spike/mauro/ogr/src/test/resources/org/geotools/data/ogr/ trunk/spike/mauro/ogr/src/test/resources/org/geotools/data/ogr/test-data/ trunk/spike/mauro/ogr/src/test/resources/org/geotools/data/ogr/test-data/keepme.txt Log: ogr spike Added: trunk/spike/mauro/ogr/.classpath =================================================================== --- trunk/spike/mauro/ogr/.classpath (rev 0) +++ trunk/spike/mauro/ogr/.classpath 2011-12-08 19:57:35 UTC (rev 38412) @@ -0,0 +1,31 @@ +<classpath> + <classpathentry kind="src" path="src/main/java"/> + <classpathentry kind="src" path="src/main/resources" excluding="**/*.java"/> + <classpathentry kind="src" path="src/test/java" output="target/test-classes"/> + <classpathentry kind="src" path="src/test/resources" output="target/test-classes" excluding="**/*.java"/> + <classpathentry kind="output" path="target/classes"/> + <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> + <classpathentry kind="var" path="M2_REPO/com/nativelibs4java/bridj/0.6/bridj-0.6-c-only.jar"/> + <classpathentry kind="var" path="M2_REPO/commons-pool/commons-pool/1.5.4/commons-pool-1.5.4.jar" sourcepath="M2_REPO/commons-pool/commons-pool/1.5.4/commons-pool-1.5.4-sources.jar"/> + <classpathentry kind="var" path="M2_REPO/org/geotools/gt-api/8-SNAPSHOT/gt-api-8-SNAPSHOT.jar" sourcepath="M2_REPO/org/geotools/gt-api/8-SNAPSHOT/gt-api-8-SNAPSHOT-sources.jar"/> + <classpathentry kind="var" path="M2_REPO/org/geotools/gt-data/8-SNAPSHOT/gt-data-8-SNAPSHOT.jar" sourcepath="M2_REPO/org/geotools/gt-data/8-SNAPSHOT/gt-data-8-SNAPSHOT-sources.jar"/> + <classpathentry kind="var" path="M2_REPO/org/geotools/gt-epsg-hsql/8-SNAPSHOT/gt-epsg-hsql-8-SNAPSHOT.jar" sourcepath="M2_REPO/org/geotools/gt-epsg-hsql/8-SNAPSHOT/gt-epsg-hsql-8-SNAPSHOT-sources.jar"/> + <classpathentry kind="var" path="M2_REPO/org/geotools/gt-main/8-SNAPSHOT/gt-main-8-SNAPSHOT.jar" sourcepath="M2_REPO/org/geotools/gt-main/8-SNAPSHOT/gt-main-8-SNAPSHOT-sources.jar"/> + <classpathentry kind="var" path="M2_REPO/org/geotools/gt-metadata/8-SNAPSHOT/gt-metadata-8-SNAPSHOT.jar" sourcepath="M2_REPO/org/geotools/gt-metadata/8-SNAPSHOT/gt-metadata-8-SNAPSHOT-sources.jar"/> + <classpathentry kind="var" path="M2_REPO/org/geotools/gt-opengis/8-SNAPSHOT/gt-opengis-8-SNAPSHOT.jar" sourcepath="M2_REPO/org/geotools/gt-opengis/8-SNAPSHOT/gt-opengis-8-SNAPSHOT-sources.jar"/> + <classpathentry kind="var" path="M2_REPO/org/geotools/gt-referencing/8-SNAPSHOT/gt-referencing-8-SNAPSHOT.jar" sourcepath="M2_REPO/org/geotools/gt-referencing/8-SNAPSHOT/gt-referencing-8-SNAPSHOT-sources.jar"/> + <classpathentry kind="var" path="M2_REPO/org/geotools/gt-sample-data/8-SNAPSHOT/gt-sample-data-8-SNAPSHOT.jar" sourcepath="M2_REPO/org/geotools/gt-sample-data/8-SNAPSHOT/gt-sample-data-8-SNAPSHOT-sources.jar"/> + <classpathentry kind="var" path="M2_REPO/org/geotools/gt-shapefile/8-SNAPSHOT/gt-shapefile-8-SNAPSHOT.jar" sourcepath="M2_REPO/org/geotools/gt-shapefile/8-SNAPSHOT/gt-shapefile-8-SNAPSHOT-sources.jar"/> + <classpathentry kind="var" path="M2_REPO/hsqldb/hsqldb/1.8.0.7/hsqldb-1.8.0.7.jar"/> + <classpathentry kind="var" path="M2_REPO/javax/media/jai_core/1.1.3/jai_core-1.1.3.jar"/> + <classpathentry kind="var" path="M2_REPO/jdom/jdom/1.0/jdom-1.0.jar" sourcepath="M2_REPO/jdom/jdom/1.0/jdom-1.0-sources.jar"/> + <classpathentry kind="var" path="M2_REPO/net/java/dev/jsr-275/jsr-275/1.0-beta-2/jsr-275-1.0-beta-2.jar" sourcepath="M2_REPO/net/java/dev/jsr-275/jsr-275/1.0-beta-2/jsr-275-1.0-beta-2-sources.jar"/> + <classpathentry kind="var" path="M2_REPO/com/vividsolutions/jts/1.12/jts-1.12.jar" sourcepath="M2_REPO/com/vividsolutions/jts/1.12/jts-1.12-sources.jar"/> + <classpathentry kind="var" path="M2_REPO/junit/junit/4.4/junit-4.4.jar" sourcepath="M2_REPO/junit/junit/4.4/junit-4.4-sources.jar"> + <attributes> + <attribute value="jar:file:/home/mauro/.m2/repository/junit/junit/4.4/junit-4.4-javadoc.jar!/" name="javadoc_location"/> + </attributes> + </classpathentry> + <classpathentry kind="var" path="M2_REPO/java3d/vecmath/1.3.2/vecmath-1.3.2.jar"/> + <classpathentry kind="var" path="M2_REPO/xerces/xercesImpl/2.7.1/xercesImpl-2.7.1.jar"/> +</classpath> \ No newline at end of file Property changes on: trunk/spike/mauro/ogr/.classpath ___________________________________________________________________ Added: svn:mime-type + text/xml Added: svn:keywords + Id Added: svn:eol-style + native Added: trunk/spike/mauro/ogr/.project =================================================================== --- trunk/spike/mauro/ogr/.project (rev 0) +++ trunk/spike/mauro/ogr/.project 2011-12-08 19:57:35 UTC (rev 38412) @@ -0,0 +1,13 @@ +<projectDescription> + <name>gt-ogr</name> + <comment>A datastore levearing OGR for reading/writing a slew of data formats</comment> + <projects/> + <buildSpec> + <buildCommand> + <name>org.eclipse.jdt.core.javabuilder</name> + </buildCommand> + </buildSpec> + <natures> + <nature>org.eclipse.jdt.core.javanature</nature> + </natures> +</projectDescription> \ No newline at end of file Property changes on: trunk/spike/mauro/ogr/.project ___________________________________________________________________ Added: svn:mime-type + text/xml Added: svn:keywords + Id Added: svn:eol-style + native Added: trunk/spike/mauro/ogr/.settings/org.eclipse.jdt.core.prefs =================================================================== --- trunk/spike/mauro/ogr/.settings/org.eclipse.jdt.core.prefs (rev 0) +++ trunk/spike/mauro/ogr/.settings/org.eclipse.jdt.core.prefs 2011-12-08 19:57:35 UTC (rev 38412) @@ -0,0 +1,5 @@ +#Mon Nov 28 12:30:30 CET 2011 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.source=1.6 +org.eclipse.jdt.core.compiler.compliance=1.6 Added: trunk/spike/mauro/ogr/jnaerate.sh =================================================================== --- trunk/spike/mauro/ogr/jnaerate.sh (rev 0) +++ trunk/spike/mauro/ogr/jnaerate.sh 2011-12-08 19:57:35 UTC (rev 38412) @@ -0,0 +1,3 @@ +export JNAERATOR=/home/aaime/devel/gdal/jnaerator-0.9.9-SNAPSHOT-shaded.jar +export GDAL_BASE=/home/aaime/devel/gdal/gdal-1.8.0 +java -jar $JNAERATOR -I. -I$GDAL_BASE/gcore -I$GDAL_BASEport -I$GDAL_BASEogr -package org.geotools.data.ogr.bridj -library osr $GDAL_BASE/ogr/ogr_srs_api.h -library ogr $GDAL_BASE/ogr/ogr_core.h $GDAL_BASE/ogr/ogr_api.h -library cplError $GDAL_BASE/port/cpl_error.h -o src/main/java -v -runtime BridJ -reification -nocpp -DCPL_DLL= -DCPL_STDCALL= -DCPL_C_START= -DCPL_C_END= -noComp -parseInOneChunk Property changes on: trunk/spike/mauro/ogr/jnaerate.sh ___________________________________________________________________ Added: svn:executable + * Added: svn:keywords + Id Added: svn:eol-style + native Added: trunk/spike/mauro/ogr/pom.xml =================================================================== --- trunk/spike/mauro/ogr/pom.xml (rev 0) +++ trunk/spike/mauro/ogr/pom.xml 2011-12-08 19:57:35 UTC (rev 38412) @@ -0,0 +1,137 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- ======================================================================= + Maven Project Configuration File + + The Geotools Project + http://www.geotools.org/ + + Version: $Id$ + ======================================================================= --> + <project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 + http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.geotools</groupId> + <artifactId>geotools</artifactId> + <version>8-SNAPSHOT</version> + </parent> + + <!-- =========================================================== --> + <!-- Module Description --> + <!-- =========================================================== --> + <groupId>org.geotools</groupId> + <artifactId>gt-ogr</artifactId> + <packaging>jar</packaging> + <name>OGR datastore module (Spike)</name> + + + + <scm> + <connection> + scm:svn:http://svn.geotools.org/trunk/modules/unsupported/ogr/ + </connection> + <url>http://svn.geotools.org/trunk/modules/unsupported/ogr/</url> + </scm> + + <description> + A datastore levearing OGR for reading/writing a slew of data formats + </description> + + <licenses> + <license> + <name>Lesser General Public License (LGPL)</name> + <url>http://www.gnu.org/copyleft/lesser.txt</url> + <distribution>repo</distribution> + </license> + </licenses> + + <repositories> + <repository> + <id>nativelibs4java-repo</id> + <name>NativeLibs4Java Maven Repository</name> + <url>http://nativelibs4java.sourceforge.net/maven</url> + </repository> + </repositories> + + + <!-- =========================================================== --> + <!-- Developers and Contributors --> + <!-- =========================================================== --> + <developers> + <developer> + <name>Andrea Aime</name> + <id>aaime</id> + <email>aa...@us...</email> + <organization>University of Modena and Reggio Emilia</organization> + <roles> + <role>Module Maintainer</role> + <role>Java Developer</role> + </roles> + </developer> + </developers> + + + <!-- =========================================================== --> + <!-- Dependency Management --> + <!-- =========================================================== --> + <dependencies> + <dependency> + <groupId>org.geotools</groupId> + <artifactId>gt-data</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>com.nativelibs4java</groupId> + <artifactId>bridj</artifactId> + <classifier>c-only</classifier> + <version>0.6</version> + + <exclusions> + <exclusion> + <groupId>com.nativelibs4java.thirdparty.com.google.android</groupId> + <artifactId>dx</artifactId> + </exclusion> + </exclusions> + </dependency> + + <!-- Test dependencies --> + <dependency> + <groupId>org.geotools</groupId> + <artifactId>gt-sample-data</artifactId> + <version>${project.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.geotools</groupId> + <artifactId>gt-epsg-hsql</artifactId> + <version>${project.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.geotools</groupId> + <artifactId>gt-shapefile</artifactId> + <version>${project.version}</version> + <scope>test</scope> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <systemProperties> + <property> + <name>java.library.path</name> + <value>${java.library.path}</value> + </property> + </systemProperties> + </configuration> + </plugin> + </plugins> + </build> + +</project> Property changes on: trunk/spike/mauro/ogr/pom.xml ___________________________________________________________________ Added: svn:mime-type + text/xml Added: svn:keywords + Id Added: svn:eol-style + native Added: trunk/spike/mauro/ogr/src/main/java/org/geotools/data/ogr/FeatureMapper.java =================================================================== --- trunk/spike/mauro/ogr/src/main/java/org/geotools/data/ogr/FeatureMapper.java (rev 0) +++ trunk/spike/mauro/ogr/src/main/java/org/geotools/data/ogr/FeatureMapper.java 2011-12-08 19:57:35 UTC (rev 38412) @@ -0,0 +1,373 @@ +/* + * GeoTools - The Open Source Java GIS Toolkit + * http://geotools.org + * + * (C) 2007-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.ogr; + +import static org.bridj.Pointer.*; +import static org.geotools.data.ogr.bridj.OgrLibrary.*; + +import java.io.IOException; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.text.DateFormat; +import java.text.DecimalFormatSymbols; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.TimeZone; + +import org.bridj.Pointer; +import org.geotools.data.DataSourceException; +import org.geotools.data.ogr.bridj.OgrLibrary.OGRFieldType; +import org.geotools.feature.simple.SimpleFeatureBuilder; +import org.geotools.util.Converters; +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 com.vividsolutions.jts.geom.Geometry; +import com.vividsolutions.jts.geom.GeometryFactory; +import com.vividsolutions.jts.geom.LineString; +import com.vividsolutions.jts.geom.MultiLineString; +import com.vividsolutions.jts.geom.MultiPolygon; +import com.vividsolutions.jts.geom.Polygon; + +/** + * Maps OGR features into Geotools ones, and vice versa. Chances are that if you need to update a + * decode method a simmetric modification will be needed in the encode method. This class is not + * thread safe, so each thread should create its own instance. + * + * @author Andrea Aime - OpenGeo + * + */ +class FeatureMapper { + + SimpleFeatureBuilder builder; + + SimpleFeatureType schema; + + GeometryMapper geomMapper; + + GeometryFactory geomFactory; + + /** + * The date time format used by OGR when getting/setting times using strings + */ + DateFormat dateTimeFormat = new SimpleDateFormat("yyyy/MM/dd hh:mm:ss"); + + DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd"); + + DateFormat timeFormat = new SimpleDateFormat("hh:mm:ss"); + + HashMap<String, Integer> attributeIndexes; + + /** + * TODO: this is subscepitble to changes to the Locale in Java that might not affect + * the C code... we should probably figure out a way to get the OS level locale? + */ + static final DecimalFormatSymbols DECIMAL_SYMBOLS = new DecimalFormatSymbols(); + + public FeatureMapper(SimpleFeatureType targetSchema, Pointer layer, GeometryFactory geomFactory) { + this.schema = targetSchema; + this.builder = new SimpleFeatureBuilder(schema); + this.geomMapper = new GeometryMapper.WKB(geomFactory); + this.geomFactory = geomFactory; + + attributeIndexes = new HashMap<String, Integer>(); + Pointer layerDefinition = OGR_L_GetLayerDefn(layer); + int size = OGR_FD_GetFieldCount(layerDefinition); + for(int i = 0; i < size; i++) { + Pointer field = OGR_FD_GetFieldDefn(layerDefinition, i); + Pointer<Byte> namePtr = OGR_Fld_GetNameRef(field); + String name = namePtr.getCString(); + if(targetSchema.getDescriptor(name) != null) { + attributeIndexes.put(name, i); + } + } + } + + /** + * Converts an OGR feature into a GeoTools one + * + * @param schema + * @param ogrFeature + * @return + * @throws IOException + */ + SimpleFeature convertOgrFeature(Pointer<?> ogrFeature) + throws IOException { + // Extract all attributes (do not assume any specific order, the feature + // type may have been re-ordered by the Query) + Object[] attributes = new Object[schema.getAttributeCount()]; + + // .. then extract each attribute using the attribute type to determine + // which extraction method to call + for (int i = 0; i < attributes.length; i++) { + AttributeDescriptor at = schema.getDescriptor(i); + builder.add(getOgrField(at, ogrFeature)); + } + + // .. gather the FID + String fid = convertOGRFID(schema, ogrFeature); + + // .. finally create the feature + return builder.buildFeature(fid); + } + + /** + * Turns a GeoTools feature into an OGR one + * + * @param feature + * @return + * @throws DataSourceException + */ + Pointer convertGTFeature(Pointer featureDefinition, SimpleFeature feature) + throws IOException { + // create a new empty OGR feature + Pointer ogrFeature = OGR_F_Create(featureDefinition); + + // go thru GeoTools feature attributes, and convert + SimpleFeatureType schema = feature.getFeatureType(); + for (int i = 0, j = 0; i < schema.getAttributeCount(); i++) { + Object attribute = feature.getAttribute(i); + if (attribute instanceof Geometry) { + // using setGeoemtryDirectly the feature becomes the owner of the generated + // OGR geometry and we don't have to .delete() it (it's faster, too) + Pointer geometry = geomMapper.parseGTGeometry((Geometry) attribute); + OGR_F_SetGeometryDirectly(ogrFeature, geometry); + } else { + setFieldValue(featureDefinition, ogrFeature, j, attribute); + j++; + } + } + + return ogrFeature; + } + + static void setFieldValue(Pointer featureDefinition, Pointer ogrFeature, int fieldIdx, + Object value) throws IOException { + if (value == null) { + OGR_F_UnsetField(ogrFeature, fieldIdx); + } else { + Pointer fieldDefinition = OGR_FD_GetFieldDefn(featureDefinition, fieldIdx); + long ogrType = OGR_Fld_GetType(fieldDefinition).value(); + if (ogrType == OGRFieldType.OFTInteger.value()) { + OGR_F_SetFieldInteger(ogrFeature, fieldIdx, ((Number) value).intValue()); + } else if (ogrType == OGRFieldType.OFTReal.value()) { + OGR_F_SetFieldDouble(ogrFeature, fieldIdx, ((Number) value).doubleValue()); + } else if (ogrType == OGRFieldType.OFTBinary.value()) { + byte[] attValue = (byte[]) value; + OGR_F_SetFieldBinary(ogrFeature, fieldIdx, attValue.length, pointerToBytes(attValue)); + } else if (ogrType == OGRFieldType.OFTDate.value()) { + Calendar cal = Calendar.getInstance(); + cal.setTime((Date) value); + int year = cal.get(Calendar.YEAR); + int month = cal.get(Calendar.MONTH); + int day = cal.get(Calendar.DAY_OF_MONTH); + OGR_F_SetFieldDateTime(ogrFeature, fieldIdx, year, month, day, 0, 0, 0, 0); + } else if (ogrType == OGRFieldType.OFTTime.value()) { + Calendar cal = Calendar.getInstance(); + cal.setTime((Date) value); + int hour = cal.get(Calendar.HOUR_OF_DAY); + int minute = cal.get(Calendar.MINUTE); + int second = cal.get(Calendar.SECOND); + OGR_F_SetFieldDateTime(ogrFeature, fieldIdx, 0, 0, 0, hour, minute, second, 0); + } else if (ogrType == OGRFieldType.OFTDateTime.value()) { + Calendar cal = Calendar.getInstance(); + cal.setTime((Date) value); + int year = cal.get(Calendar.YEAR); + int month = cal.get(Calendar.MONTH); + int day = cal.get(Calendar.DAY_OF_MONTH); + int hour = cal.get(Calendar.HOUR_OF_DAY); + int minute = cal.get(Calendar.MINUTE); + int second = cal.get(Calendar.SECOND); + OGR_F_SetFieldDateTime(ogrFeature, fieldIdx, year, month, day, hour, minute, second, 0); + } else { + // anything else we treat as a string + String str = Converters.convert(value, String.class); + OGR_F_SetFieldString(ogrFeature, fieldIdx, pointerToCString(str)); + } + } + } + + /** + * Turns line and polygon into multiline and multipolygon. This is a stop-gap measure to make + * things works against shapefiles, I've asked the GDAL mailing list on how to properly handle + * this in the meantime + * + * @param ogrGeometry + * @param ad + * @return + */ + Geometry fixGeometryType(Geometry ogrGeometry, AttributeDescriptor ad) { + if (MultiPolygon.class.equals(ad.getType())) { + if (ogrGeometry instanceof MultiPolygon) + return ogrGeometry; + else + return geomFactory.createMultiPolygon(new Polygon[] { (Polygon) ogrGeometry }); + } else if (MultiLineString.class.equals(ad.getType())) { + if (ogrGeometry instanceof MultiLineString) + return ogrGeometry; + else + return geomFactory + .createMultiLineString(new LineString[] { (LineString) ogrGeometry }); + } + return ogrGeometry; + + } + + + /** + * Reads the current feature's specified field using the most appropriate OGR field extraction + * method + * + * @param ad + * @return + */ + Object getOgrField(AttributeDescriptor ad, Pointer<?> ogrFeature) throws IOException { + if(ad instanceof GeometryDescriptor) { + // gets the geometry as a reference, we don't own it, we should not deallocate it + Pointer<?> ogrGeometry = OGR_F_GetGeometryRef(ogrFeature); + return fixGeometryType(geomMapper.parseOgrGeometry(ogrGeometry), ad); + } + + Integer idx = attributeIndexes.get(ad.getLocalName()); + + // check for null fields + if (idx == null || OGR_F_IsFieldSet(ogrFeature, idx) == 0) { + return null; + } + + // hum, ok try and parse it + Class clazz = ad.getType().getBinding(); + if (clazz.equals(String.class)) { + return OGR_F_GetFieldAsString(ogrFeature, idx).getCString(); + } else if (clazz.equals(Byte.class)) { + return (byte) OGR_F_GetFieldAsInteger(ogrFeature, idx); + } else if (clazz.equals(Short.class)) { + return (short) OGR_F_GetFieldAsInteger(ogrFeature, idx); + } else if (clazz.equals(Integer.class)) { + return OGR_F_GetFieldAsInteger(ogrFeature, idx); + } else if (clazz.equals(Long.class)) { + String value = OGR_F_GetFieldAsString(ogrFeature, idx).getCString(); + return new Long(value); + } else if (clazz.equals(BigInteger.class)) { + String value = OGR_F_GetFieldAsString(ogrFeature, idx).getCString(); + return new BigInteger(value); + } else if (clazz.equals(Double.class)) { + return OGR_F_GetFieldAsDouble(ogrFeature, idx); + } else if (clazz.equals(Float.class)) { + return (float) OGR_F_GetFieldAsDouble(ogrFeature, idx); + } else if (clazz.equals(BigDecimal.class)) { + String value = OGR_F_GetFieldAsString(ogrFeature, idx).getCString().trim(); + char separator = DECIMAL_SYMBOLS.getDecimalSeparator(); + if(separator != '.') { + value = value.replace(separator, '.'); + } + return new BigDecimal(value); + } else if (clazz.equals(java.sql.Date.class)) { + Calendar cal = getDateField(ogrFeature, idx); + cal.clear(Calendar.HOUR_OF_DAY); + cal.clear(Calendar.MINUTE); + cal.clear(Calendar.SECOND); + return new java.sql.Date(cal.getTimeInMillis()); + } else if (clazz.equals(java.sql.Time.class)) { + Calendar cal = getDateField(ogrFeature, idx); + cal.clear(Calendar.YEAR); + cal.clear(Calendar.MONTH); + cal.clear(Calendar.DAY_OF_MONTH); + return new java.sql.Time(cal.getTimeInMillis()); + } else if (clazz.equals(java.sql.Timestamp.class)) { + Calendar cal = getDateField(ogrFeature, idx); + return new java.sql.Time(cal.getTimeInMillis()); + } else if (clazz.equals(java.util.Date.class)) { + Calendar cal = getDateField(ogrFeature, idx); + return cal.getTime(); + } else { + throw new IllegalArgumentException("Don't know how to read " + clazz.getName() + + " fields"); + } + } + + /** + * Reads a date field from the OGR api + * @param ogrFeature + * @param idx + * @return + */ + private Calendar getDateField(Pointer<?> ogrFeature, Integer idx) { + Pointer<Integer> year = allocateInt(); + Pointer<Integer> month = allocateInt(); + Pointer<Integer> day = allocateInt(); + Pointer<Integer> hour = allocateInt(); + Pointer<Integer> minute = allocateInt(); + Pointer<Integer> second = allocateInt(); + Pointer<Integer> timeZone = allocateInt(); + + OGR_F_GetFieldAsDateTime(ogrFeature, idx, year, month, day, hour, minute, second, timeZone); + + Calendar cal = Calendar.getInstance(); + // from ogr_core.h + // 0=unknown, 1=localtime(ambiguous), 100=GMT, 104=GMT+1, 80=GMT-5, etc + int tz = timeZone.getInt(); + if(tz != 0 && tz != 1) { + int offset = tz - 100 / 4; + if(offset < 0) { + cal.setTimeZone(TimeZone.getTimeZone("GMT" + offset)); + } else if(offset == 0) { + cal.setTimeZone(TimeZone.getTimeZone("GMT")); + } else { + cal.setTimeZone(TimeZone.getTimeZone("GMT+" + offset)); + } + } + cal.clear(); + cal.set(Calendar.YEAR, year.getInt()); + cal.set(Calendar.MONTH, month.getInt()); + cal.set(Calendar.DAY_OF_MONTH, day.getInt()); + cal.set(Calendar.HOUR_OF_DAY, hour.getInt()); + cal.set(Calendar.MINUTE, minute.getInt()); + cal.set(Calendar.SECOND, second.getInt()); + return cal; + } + + /** + * Generates a GT2 feature id given its feature type and an OGR feature + * + * @param schema + * @param ogrFeature + * @return + */ + String convertOGRFID(SimpleFeatureType schema, Pointer<?> ogrFeature) { + long id = OGR_F_GetFID(ogrFeature); + return schema.getTypeName() + "." + id; + } + + /** + * Decodes a GT2 feature id into an OGR one + * + * @param feature + * @return + */ + long convertGTFID(SimpleFeature feature) { + String id = feature.getID(); + return Long.parseLong(id.substring(id.indexOf(".") + 1)); + } + +} Property changes on: trunk/spike/mauro/ogr/src/main/java/org/geotools/data/ogr/FeatureMapper.java ___________________________________________________________________ Added: svn:mime-type + text/plain Added: svn:keywords + Id Added: svn:eol-style + native Added: trunk/spike/mauro/ogr/src/main/java/org/geotools/data/ogr/FeatureTypeMapper.java =================================================================== --- trunk/spike/mauro/ogr/src/main/java/org/geotools/data/ogr/FeatureTypeMapper.java (rev 0) +++ trunk/spike/mauro/ogr/src/main/java/org/geotools/data/ogr/FeatureTypeMapper.java 2011-12-08 19:57:35 UTC (rev 38412) @@ -0,0 +1,376 @@ +/* + * GeoTools - The Open Source Java GIS Toolkit + * http://geotools.org + * + * (C) 2007-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.ogr; + +import static org.bridj.Pointer.*; +import static org.geotools.data.ogr.bridj.OgrLibrary.*; +import static org.geotools.data.ogr.bridj.OsrLibrary.*; + +import java.io.IOException; +import java.math.BigDecimal; +import java.math.BigInteger; + +import org.bridj.Pointer; +import org.bridj.ValuedEnum; +import org.geotools.data.ogr.bridj.OgrLibrary.OGRFieldType; +import org.geotools.data.ogr.bridj.OgrLibrary.OGRJustification; +import org.geotools.data.ogr.bridj.OgrLibrary.OGRwkbGeometryType; +import org.geotools.feature.FeatureTypes; +import org.geotools.feature.simple.SimpleFeatureTypeBuilder; +import org.geotools.feature.type.BasicFeatureTypes; +import org.geotools.referencing.CRS; +import org.opengis.feature.simple.SimpleFeatureType; +import org.opengis.feature.type.AttributeDescriptor; +import org.opengis.feature.type.GeometryDescriptor; +import org.opengis.referencing.crs.CoordinateReferenceSystem; + +import com.vividsolutions.jts.geom.Geometry; +import com.vividsolutions.jts.geom.GeometryCollection; +import com.vividsolutions.jts.geom.LineString; +import com.vividsolutions.jts.geom.LinearRing; +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; + +/** + * Helper mapping between geotools and OGR feature types + * + * @author Andrea Aime - GeoSolutions + */ +class FeatureTypeMapper { + + /** + * Returns the geotools feature type equivalent from the native OGR one + * + * @param layer + * @param typeName + * @param namespaceURI + * @return + * @throws IOException + */ + SimpleFeatureType getFeatureType(Pointer layer, String typeName, String namespaceURI) + throws IOException { + Pointer definition = null; + try { + // setup the builder + SimpleFeatureTypeBuilder tb = new SimpleFeatureTypeBuilder(); + tb.setName(typeName); + tb.setNamespaceURI(namespaceURI); + if (tb.getNamespaceURI() == null) { + tb.setNamespaceURI(BasicFeatureTypes.DEFAULT_NAMESPACE); + } + + // grab the layer definition + definition = OGR_L_GetLayerDefn(layer); + + // figure out the geometry + Class<? extends Geometry> geometryBinding = getGeometryBinding(definition); + if (geometryBinding != null) { + CoordinateReferenceSystem crs = getCRS(layer); + tb.add("the_geom", geometryBinding, crs); + } + + // get the non geometric fields + final int count = OGR_FD_GetFieldCount(definition); + for (int i = 0; i < count; i++) { + Pointer field = OGR_FD_GetFieldDefn(definition, i); + String name = OGR_Fld_GetNameRef(field).getCString(); + Class binding = getBinding(field); + int width = OGR_Fld_GetWidth(field); + if (width > 0) { + tb.length(width); + } + tb.add(name, binding); + } + + // compute a default parent feature type + if ((geometryBinding == Point.class) || (geometryBinding == MultiPoint.class)) { + tb.setSuperType(BasicFeatureTypes.POINT); + } else if ((geometryBinding == Polygon.class) + || (geometryBinding == MultiPolygon.class)) { + tb.setSuperType(BasicFeatureTypes.POLYGON); + } else if ((geometryBinding == LineString.class) + || (geometryBinding == MultiLineString.class)) { + tb.setSuperType(BasicFeatureTypes.LINE); + } + + return tb.buildFeatureType(); + } finally { + OGRUtils.releaseDefinition(definition); + } + } + + /** + * Maps the OGR field type to a java class + * + * @param field + * @return + */ + private Class getBinding(Pointer field) { + ValuedEnum<OGRFieldType> type = OGR_Fld_GetType(field); + int width = OGR_Fld_GetWidth(field); + long value = type.value(); + if (value == OGRFieldType.OFTInteger.value()) { + if (width <= 3) { + return Byte.class; + } else if (width <= 5) { + return Short.class; + } else if (width <= 9) { + return Integer.class; + } else if (width <= 19) { + return Long.class; + } else { + return BigDecimal.class; + } + } else if (value == OGRFieldType.OFTIntegerList.value()) { + return int[].class; + } else if (value == OGRFieldType.OFTReal.value()) { + if (width <= 12) { + return Float.class; + } else if (width <= 22) { + return Double.class; + } else { + return BigDecimal.class; + } + } else if (value == OGRFieldType.OFTRealList.value()) { + return double[].class; + } else if (value == OGRFieldType.OFTBinary.value()) { + return byte[].class; + } else if (value == OGRFieldType.OFTDate.value()) { + return java.sql.Date.class; + } else if (value == OGRFieldType.OFTTime.value()) { + return java.sql.Time.class; + } else if (value == OGRFieldType.OFTDateTime.value()) { + return java.sql.Timestamp.class; + } else { + // whatever else we'll map a string + return String.class; + } + } + + /** + * Returns a OGR field definition compatible with the specified attribute descriptor where: + * <ul> + * <li>width is the number of chars needed to format the strings equivalent of the number + * <li> + * <li>precision is the number of chars after decimal pont</li> + * <li>justification: right or left (in outputs)</li> + * </ul> + * + * @param ad + * @throws IOException + */ + public Pointer getOGRFieldDefinition(AttributeDescriptor ad) throws IOException { + final Class type = ad.getType().getBinding(); + final Pointer def; + Pointer<Byte> namePtr = pointerToCString(ad.getLocalName()); + if (Boolean.class.equals(type)) { + def = OGR_Fld_Create(namePtr, OGRFieldType.OFTString); + OGR_Fld_SetWidth(def, 5); + } else if (Byte.class.equals(type)) { + def = OGR_Fld_Create(namePtr, OGRFieldType.OFTInteger); + OGR_Fld_SetWidth(def, 3); + OGR_Fld_SetJustify(def, OGRJustification.OJRight); + } else if (Short.class.equals(type)) { + def = OGR_Fld_Create(namePtr, OGRFieldType.OFTInteger); + OGR_Fld_SetWidth(def, 5); + OGR_Fld_SetJustify(def, OGRJustification.OJRight); + } else if (Integer.class.equals(type)) { + def = OGR_Fld_Create(namePtr, OGRFieldType.OFTInteger); + OGR_Fld_SetWidth(def, 9); + OGR_Fld_SetJustify(def, OGRJustification.OJRight); + } else if (Long.class.equals(type)) { + def = OGR_Fld_Create(namePtr, OGRFieldType.OFTInteger); + OGR_Fld_SetWidth(def, 19); + OGR_Fld_SetJustify(def, OGRJustification.OJRight); + } else if (BigInteger.class.equals(type)) { + def = OGR_Fld_Create(namePtr, OGRFieldType.OFTInteger); + OGR_Fld_SetWidth(def, 32); + OGR_Fld_SetJustify(def, OGRJustification.OJRight); + } else if (BigDecimal.class.equals(type)) { + def = OGR_Fld_Create(namePtr, OGRFieldType.OFTReal); + OGR_Fld_SetWidth(def, 32); + OGR_Fld_SetPrecision(def, 15); + OGR_Fld_SetJustify(def, OGRJustification.OJRight); + } else if (Float.class.equals(type)) { + def = OGR_Fld_Create(namePtr, OGRFieldType.OFTReal); + OGR_Fld_SetWidth(def, 12); + OGR_Fld_SetPrecision(def, 7); + OGR_Fld_SetJustify(def, OGRJustification.OJRight); + } else if (Double.class.equals(type) || Number.class.isAssignableFrom(type)) { + def = OGR_Fld_Create(namePtr, OGRFieldType.OFTInteger); + OGR_Fld_SetWidth(def, 22); + OGR_Fld_SetPrecision(def, 16); + OGR_Fld_SetJustify(def, OGRJustification.OJRight); + } else if (String.class.equals(type)) { + def = OGR_Fld_Create(namePtr, OGRFieldType.OFTString); + int length = FeatureTypes.getFieldLength(ad); + if (length <= 0) { + length = 255; + } + OGR_Fld_SetWidth(def, length); + } else if (byte[].class.equals(type)) { + def = OGR_Fld_Create(namePtr, OGRFieldType.OFTBinary); + } else if (java.sql.Date.class.isAssignableFrom(type)) { + def = OGR_Fld_Create(namePtr, OGRFieldType.OFTDate); + } else if (java.sql.Time.class.isAssignableFrom(type)) { + def = OGR_Fld_Create(namePtr, OGRFieldType.OFTTime); + } else if (java.util.Date.class.isAssignableFrom(type)) { + def = OGR_Fld_Create(namePtr, OGRFieldType.OFTDateTime); + } else { + throw new IOException("Cannot map " + type + " to an OGR type"); + } + + return def; + } + + /** + * Returns the OGR geometry type constant given a geometry attribute type + * + * @param descriptor + * @return + * @throws IOException + */ + public ValuedEnum<OGRwkbGeometryType> getOGRGeometryType(GeometryDescriptor descriptor) + throws IOException { + Class binding = descriptor.getType().getBinding(); + if (GeometryCollection.class.isAssignableFrom(binding)) { + if (MultiPoint.class.isAssignableFrom(binding)) { + return OGRwkbGeometryType.wkbMultiPoint; + } else if (MultiLineString.class.isAssignableFrom(binding)) { + return OGRwkbGeometryType.wkbMultiLineString; + } else if (MultiPolygon.class.isAssignableFrom(binding)) { + return OGRwkbGeometryType.wkbMultiPolygon; + } else { + return OGRwkbGeometryType.wkbGeometryCollection; + } + } else { + if (Point.class.isAssignableFrom(binding)) { + return OGRwkbGeometryType.wkbPoint; + } else if (LinearRing.class.isAssignableFrom(binding)) { + return OGRwkbGeometryType.wkbLinearRing; + } else if (LineString.class.isAssignableFrom(binding)) { + return OGRwkbGeometryType.wkbLineString; + } else if (Polygon.class.isAssignableFrom(binding)) { + return OGRwkbGeometryType.wkbPolygon; + } else { + return OGRwkbGeometryType.wkbUnknown; + } + } + } + + /** + * Returns the JTS geometry type equivalent to the layer native one + * + * @param definition + * @return + * @throws IOException + */ + private Class<? extends Geometry> getGeometryBinding(Pointer definition) throws IOException { + ValuedEnum<OGRwkbGeometryType> gt = OGR_FD_GetGeomType(definition); + long value = gt.value(); + // for line and polygon we return multi in any case since OGR will declare simple for + // multigeom + // anyways and then return simple or multi in the actual geoemtries depending on + // what it finds + if (value == OGRwkbGeometryType.wkbPoint.value() + || value == OGRwkbGeometryType.wkbPoint25D.value()) { + return Point.class; + } else if (value == OGRwkbGeometryType.wkbLinearRing.value()) { + return LinearRing.class; + } else if (value == OGRwkbGeometryType.wkbLineString.value() + || value == OGRwkbGeometryType.wkbLineString25D.value() + || value == OGRwkbGeometryType.wkbMultiLineString.value() + || value == OGRwkbGeometryType.wkbMultiLineString25D.value()) { + return MultiLineString.class; + } else if (value == OGRwkbGeometryType.wkbPolygon.value() + || value == OGRwkbGeometryType.wkbPolygon25D.value() + || value == OGRwkbGeometryType.wkbMultiPolygon.value() + || value == OGRwkbGeometryType.wkbMultiPolygon25D.value()) { + return MultiPolygon.class; + } else if (value == OGRwkbGeometryType.wkbGeometryCollection.value() + || value == OGRwkbGeometryType.wkbGeometryCollection25D.value()) { + return GeometryCollection.class; + } else if (value == OGRwkbGeometryType.wkbNone.value()) { + return null; + } else if (value == OGRwkbGeometryType.wkbUnknown.value()) { + return Geometry.class; + } else { + throw new IOException("Unknown geometry type: " + value); + } + } + + /** + * Returns the GeoTools {@link CoordinateReferenceSystem} equivalent to the layer native one + * + * @param layer + * @return + * @throws IOException + */ + private CoordinateReferenceSystem getCRS(Pointer layer) throws IOException { + Pointer spatialReference = null; + CoordinateReferenceSystem crs = null; + try { + spatialReference = OGR_L_GetSpatialRef(layer); + if (spatialReference == null) { + return null; + } + + try { + Pointer<Byte> code = OSRGetAuthorityCode(spatialReference, pointerToCString("EPSG")); + if (code != null) { + String fullCode = "EPSG:" + code; + crs = CRS.decode(fullCode); + } + } catch (Exception e) { + // fine, the code might be unknown to out authority + } + if (crs == null) { + try { + Pointer<Pointer<Byte>> wktPtr = allocatePointer(Byte.class); + OSRExportToWkt(spatialReference, wktPtr); + String wkt = wktPtr.getPointer(Byte.class).getCString(); + crs = CRS.parseWKT(wkt); + } catch (Exception e) { + // the wkt might reference an unsupported projection + } + } + return crs; + } finally { + OGRUtils.releaseSpatialReference(spatialReference); + } + } + + /** + * Returns a Pointer to a OGR spatial reference object equivalent to the specified GeoTools CRS + * + * @param crs + * @return + */ + public Pointer getSpatialReference(CoordinateReferenceSystem crs) { + if (crs == null) { + return null; + } + + // use tostring to get a lenient wkt translation + String wkt = crs.toString(); + return OSRNewSpatialReference(pointerToCString(wkt)); + } + +} Property changes on: trunk/spike/mauro/ogr/src/main/java/org/geotools/data/ogr/FeatureTypeMapper.java ___________________________________________________________________ Added: svn:mime-type + text/plain Added: svn:keywords + Id Added: svn:eol-style + native Added: trunk/spike/mauro/ogr/src/main/java/org/geotools/data/ogr/FilterToRestrictedWhere.java =================================================================== --- trunk/spike/mauro/ogr/src/main/java/org/geotools/data/ogr/FilterToRestrictedWhere.java (rev 0) +++ trunk/spike/mauro/ogr/src/main/java/org/geotools/data/ogr/FilterToRestrictedWhere.java 2011-12-08 19:57:35 UTC (rev 38412) @@ -0,0 +1,543 @@ +/* + * GeoTools - The Open Source Java GIS Toolkit + * http://geotools.org + * + * (C) 2002-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.ogr; + +import java.io.IOException; +import java.io.StringWriter; +import java.util.Iterator; + +import org.geotools.factory.Hints; +import org.geotools.util.ConverterFactory; +import org.geotools.util.Converters; +import org.opengis.feature.simple.SimpleFeatureType; +import org.opengis.feature.type.AttributeDescriptor; +import org.opengis.filter.And; +import org.opengis.filter.BinaryComparisonOperator; +import org.opengis.filter.BinaryLogicOperator; +import org.opengis.filter.ExcludeFilter; +import org.opengis.filter.Filter; +import org.opengis.filter.FilterVisitor; +import org.opengis.filter.Id; +import org.opengis.filter.IncludeFilter; +import org.opengis.filter.Not; +import org.opengis.filter.Or; +import org.opengis.filter.PropertyIsBetween; +import org.opengis.filter.PropertyIsEqualTo; +import org.opengis.filter.PropertyIsGreaterThan; +import org.opengis.filter.PropertyIsGreaterThanOrEqualTo; +import org.opengis.filter.PropertyIsLessThan; +import org.opengis.filter.PropertyIsLessThanOrEqualTo; +import org.opengis.filter.PropertyIsLike; +import org.opengis.filter.PropertyIsNil; +import org.opengis.filter.PropertyIsNotEqualTo; +import org.opengis.filter.PropertyIsNull; +import org.opengis.filter.expression.Add; +import org.opengis.filter.expression.Divide; +import org.opengis.filter.expression.Expression; +import org.opengis.filter.expression.ExpressionVisitor; +import org.opengis.filter.expression.Function; +import org.opengis.filter.expression.Literal; +import org.opengis.filter.expression.Multiply; +import org.opengis.filter.expression.NilExpression; +import org.opengis.filter.expression.PropertyName; +import org.opengis.filter.expression.Subtract; +import org.opengis.filter.spatial.BBOX; +import org.opengis.filter.spatial.Beyond; +import org.opengis.filter.spatial.Contains; +import org.opengis.filter.spatial.Crosses; +import org.opengis.filter.spatial.DWithin; +import org.opengis.filter.spatial.Disjoint; +import org.opengis.filter.spatial.Equals; +import org.opengis.filter.spatial.Intersects; +import org.opengis.filter.spatial.Overlaps; +import org.opengis.filter.spatial.Touches; +import org.opengis.filter.spatial.Within; +import org.opengis.filter.temporal.After; +import org.opengis.filter.temporal.AnyInteracts; +import org.opengis.filter.temporal.Before; +import org.opengis.filter.temporal.Begins; +import org.opengis.filter.temporal.BegunBy; +import org.opengis.filter.temporal.During; +import org.opengis.filter.temporal.EndedBy; +import org.opengis.filter.temporal.Ends; +import org.opengis.filter.temporal.Meets; +import org.opengis.filter.temporal.MetBy; +import org.opengis.filter.temporal.OverlappedBy; +import org.opengis.filter.temporal.TContains; +import org.opengis.filter.temporal.TEquals; +import org.opengis.filter.temporal.TOverlaps; + +/** + * Encodes a compliant filter to the "restricted where" syntax supported by OGR: + * + * <pre> + * @condition@ = @field_name@ @binary_operator@ @value@ | "(" @condition@ ")" @binary_logical_operator@ + * "(" @condition@ ")" + * @binary_operator@ = "<" | ">" | "<=" | ">=" | "<>" | "=" + * @binary_logical_operator@ = "AND" | "OR" + * @field_name@ = @string_token@ + * @value@ = @string_token@ | @numeric_value@ | @string_value@ + * @string_value@ = "'" @characters@ "'" + * </pre> + * + * Implementation wise this is a widely cut down version of JDBC's module FilterToSQL + * + * @author Andrea Aime - GeoSolutions + */ +class FilterToRestrictedWhere implements FilterVisitor, ExpressionVisitor { + /** error message for exceptions */ + protected static final String IO_ERROR = "io problem writing filter"; + + /** the schema the encoder will be used to be encode sql for */ + protected SimpleFeatureType featureType; + + StringWriter out = new StringWriter(); + + public FilterToRestrictedWhere(SimpleFeatureType featureType) { + this.featureType = featureType; + } + + public String getRestrictedWhere() { + return out.toString(); + } + + /** + * Writes the SQL for the PropertyIsBetween Filter. + * + * @param filter the Filter to be visited. + * + * @throws RuntimeException for io exception with writer + */ + public Object visit(PropertyIsBetween filter, Object extraData) throws RuntimeException { + Expression expr = (Expression) filter.getExpression(); + Expression lowerbounds = (Expression) filter.getLowerBoundary(); + Expression upperbounds = (Expression) filter.getUpperBoundary(); + + Class context; + AttributeDescriptor attType = (AttributeDescriptor) expr.evaluate(featureType); + if (attType != null) { + context = attType.getType().getBinding(); + } else { + context = String.class; + } + + out.write("(("); + expr.accept(this, extraData); + out.write(">="); + lowerbounds.accept(this, context); + out.write(") AND ("); + expr.accept(this, extraData); + out.write("<="); + upperbounds.accept(this, context); + out.write("))"); + + return extraData; + } + + /** + * Write the SQL for an And filter + * + * @param filter the filter to visit + * @param extraData extra data (unused by this method) + * + */ + public Object visit(And filter, Object extraData) { + return visit((BinaryLogicOperator) filter, "AND"); + } + + /** + * Write the SQL for an Or filter + * + * @param filter the filter to visit + * @param extraData extra data (unused by this method) + * + */ + public Object visit(Or filter, Object extraData) { + return visit((BinaryLogicOperator) filter, "OR"); + } + + /** + * Common implementation for BinaryLogicOperator filters. This way they're all handled + * centrally. + * + * @param filter the logic statement to be turned into SQL. + * @param extraData extra filter data. Not modified directly by this method. + */ + protected Object visit(BinaryLogicOperator filter, Object extraData) { + String type = (String) extraData; + out.write("("); + + Iterator<Filter> list = filter.getChildren().iterator(); + while (list.hasNext()) { + list.next().accept(this, extraData); + + if (list.hasNext()) { + out.write(" " + type + " "); + } + } + out.write(")"); + return extraData; + } + + public Object visit(PropertyIsEqualTo filter, Object extraData) { + visitBinaryComparisonOperator((BinaryComparisonOperator) filter, "="); + return extraData; + } + + public Object visit(PropertyIsGreaterThanOrEqualTo filter, Object extraData) { + visitBinaryComparisonOperator((BinaryComparisonOperator) filter, ">="); + return extraData; + } + + public Object visit(PropertyIsGreaterThan filter, Object extraData) { + visitBinaryComparisonOperator((BinaryComparisonOperator) filter, ">"); + return extraData; + } + + public Object visit(PropertyIsLessThan filter, Object extraData) { + visitBinaryComparisonOperator((BinaryComparisonOperator) filter, "<"); + return extraData; + } + + public Object visit(PropertyIsLessThanOrEqualTo filter, Object extraData) { + visitBinaryComparisonOperator((BinaryComparisonOperator) filter, "<="); + return extraData; + } + + public Object visit(PropertyIsNotEqualTo filter, Object extraData) { + visitBinaryComparisonOperator((BinaryComparisonOperator) filter, "!="); + return extraData; + } + + protected void visitBinaryComparisonOperator(BinaryComparisonOperator filter, Object extraData) + throws RuntimeException { + Expression left = filter.getExpression1(); + Expression right = filter.getExpression2(); + + // see if we can get some indication on how to evaluate literals + Class leftContext = null, rightContext = null; + if (left instanceof PropertyName) { + AttributeDescriptor attType = (AttributeDescriptor) left.evaluate(featureType); + if (attType != null) { + rightContext = attType.getType().getBinding(); + } + } + if (rightContext == null && right instanceof PropertyName) { + AttributeDescriptor attType = (AttributeDescriptor) right.evaluate(featureType); + if (attType != null) { + leftContext = attType.getType().getBinding(); + } + } + + String type = (String) extraData; + left.accept(this, leftContext); + out.write(" " + type + " "); + right.accept(this, rightContext); + } + + public Object visit(PropertyName expression, Object extraData) throws RuntimeException { + AttributeDescriptor attribute = null; + try { + attribute = (AttributeDescriptor) expression.evaluate(featureType); + } catch (Exception e) { + // just log and fall back on just encoding propertyName straight up + String msg = "Error occured mapping " + expression + " to feature type"; + } + String name = null; + if (attribute != null) { + name = attribute.getLocalName(); + } else { + name = expression.getPropertyName(); + } + out.write(name); + + return extraData; + } + + /** + * Export the contents of a Literal Expresion + * + * @param expression the Literal to export + * + * @throws RuntimeException for io exception with writer + */ + public Object visit(Literal expression, Object context) throws RuntimeException { + // type to convert the literal to + Class target = null; + if (context instanceof Class) { + target = (Class) context; + } + + // evaluate the expression + Object literal = evaluateLiteral(expression, target); + writeLiteral(literal); + return context; + } + + protected Object evaluateLiteral(Literal expression, Class target) { + Object literal = null; + + // HACK: let expression figure out the right value for numbers, + // since the context is almost always improperly set and the + // numeric converters try to force floating points to integrals + // JD: the above is no longer true, so instead do a safe conversion + if (target != null) { + // use the target type + if (Number.class.isAssignableFrom(target)) { + literal = Converters.convert(expression.evaluate(null), target, new Hints( + ConverterFactory.SAFE_CONVERSION, true)); + } else { + literal = expression.evaluate(null, target); + } + } + // if the target was n... [truncated message content] |