/*
* 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 java.io.IOException;
import java.io.StringWriter;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.URI;
import java.util.Arrays;
import java.util.Vector;
import org.gdal.gdal.gdal;
import org.gdal.ogr.DataSource;
import org.gdal.ogr.Driver;
import org.gdal.ogr.FeatureDefn;
import org.gdal.ogr.FieldDefn;
import org.gdal.ogr.Layer;
import org.gdal.ogr.ogr;
import org.gdal.osr.SpatialReference;
import org.geotools.data.AbstractDataStore;
import org.geotools.data.DataSourceException;
import org.geotools.data.FeatureReader;
import org.geotools.data.FeatureWriter;
import org.geotools.data.Query;
import org.geotools.data.Transaction;
import org.geotools.data.jdbc.FilterToSQL;
import org.geotools.factory.FactoryRegistryException;
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.FilterCapabilities;
import org.geotools.filter.visitor.PostPreProcessFilterSplittingVisitor;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.referencing.CRS;
import org.geotools.resources.Classes;
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.And;
import org.opengis.filter.Filter;
import org.opengis.filter.Or;
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.PropertyIsNotEqualTo;
import org.opengis.filter.expression.Expression;
import org.opengis.filter.expression.Literal;
import org.opengis.filter.expression.PropertyName;
import org.opengis.filter.spatial.BBOX;
import org.opengis.filter.spatial.BinarySpatialOperator;
import org.opengis.filter.spatial.Contains;
import org.opengis.filter.spatial.Crosses;
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.referencing.FactoryException;
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.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 datastore based on the the <a href="http://www.gdal.org/ogr">OGR</a> spatial data abstraction
* library
*
* @author Andrea Aime - OpenGeo
*
*
*
* @source $URL$
* http://svn.osgeo.org/geotools/trunk/modules/unsupported/ogr/src/main/java/org/geotools
* /data/ogr/OGRDataStore.java $
*/
public class OGRDataStore extends AbstractDataStore {
/** C compatible FALSE */
static final int FALSE = 0;
/** C compatible TRUE */
static final int TRUE = 1;
static final FilterCapabilities ATTRIBUTE_FILTER_CAPABILITIES;
static final FilterCapabilities GEOMETRY_FILTER_CAPABILITIES;
static {
ATTRIBUTE_FILTER_CAPABILITIES = new FilterCapabilities();
ATTRIBUTE_FILTER_CAPABILITIES.addType(PropertyIsEqualTo.class);
ATTRIBUTE_FILTER_CAPABILITIES.addType(PropertyIsNotEqualTo.class);
ATTRIBUTE_FILTER_CAPABILITIES.addType(PropertyIsGreaterThan.class);
ATTRIBUTE_FILTER_CAPABILITIES.addType(PropertyIsLessThan.class);
ATTRIBUTE_FILTER_CAPABILITIES.addType(PropertyIsGreaterThanOrEqualTo.class);
ATTRIBUTE_FILTER_CAPABILITIES.addType(PropertyIsLessThanOrEqualTo.class);
ATTRIBUTE_FILTER_CAPABILITIES.addType(Or.class);
ATTRIBUTE_FILTER_CAPABILITIES.addType(And.class);
// have the capabilities extract any filter that can use geometric intersection
// as its base
GEOMETRY_FILTER_CAPABILITIES = new FilterCapabilities();
GEOMETRY_FILTER_CAPABILITIES.addType(BBOX.class);
GEOMETRY_FILTER_CAPABILITIES.addType(Contains.class);
GEOMETRY_FILTER_CAPABILITIES.addType(Crosses.class);
GEOMETRY_FILTER_CAPABILITIES.addType(Equals.class);
GEOMETRY_FILTER_CAPABILITIES.addType(Intersects.class);
GEOMETRY_FILTER_CAPABILITIES.addType(Overlaps.class);
GEOMETRY_FILTER_CAPABILITIES.addType(Touches.class);
GEOMETRY_FILTER_CAPABILITIES.addType(Within.class);
}
/**
* The OGRwkbGeometryType enum from ogr_core.h, reduced to represent only 2D classes (I hope the
* 2.5D will be handled transparently by JTS)
*/
static final Class[] OGR_GEOM_TYPES = new Class[] { //
Geometry.class, // wkbUnknown = 0
Point.class, // wkbPoint = 1
MultiLineString.class, // wkbLineString = 2,
MultiPolygon.class, // wkbPolygon = 3,
MultiPoint.class, // wkbMultiPoint = 4,
MultiLineString.class, // wkbMultiLineString = 5,
MultiPolygon.class, // wkbMultiPolygon = 6,
GeometryCollection.class, // wkbGeometryCollection = 7
};
/**
* The source name that OGR should open and handle
*/
private String ogrSourceName;
/**
* Datastore namespace
*/
private URI namespace;
/**
* OGR driver to be used for the creation of new data sources
*/
private String ogrDriverName;
static {
// perform OGR format registration once
if (ogr.GetDriverCount() == 0)
ogr.RegisterAll();
}
/**
* Creates a new OGRDataStore
*
* @param ogrSourceName
* a references to the source that needs to be opened. May be a file system path, or
* a database reference. See the OGR driver documentation for valid formats of this
* string.
*/
public OGRDataStore(String ogrSourceName, String ogrDriverName, URI namespace)
throws IOException {
this.ogrSourceName = ogrSourceName;
this.ogrDriverName = ogrDriverName;
this.namespace = (namespace != null) ? namespace : FeatureTypes.DEFAULT_NAMESPACE;
int update = FALSE;
if (ogrDriverName == null) {
DataSource ds = getOGRDataSource(update);
ds.delete();
}
}
@Override
protected FeatureReader<SimpleFeatureType, SimpleFeature> getFeatureReader(String typeName)
throws IOException {
return getFeatureReader(typeName, Query.ALL, false);
}
protected FeatureReader<SimpleFeatureType, SimpleFeature> getFeatureReader(String typeName,
Query query) throws IOException {
return getFeatureReader(typeName, query, false);
}
protected FeatureReader<SimpleFeatureType, SimpleFeature> getFeatureReader(String typeName,
Query query, boolean openForUpdate)
throws IOException {
DataSource ds = getOGRDataSource(openForUpdate ? TRUE : FALSE);
Layer layer = getOGRLayer(ds, typeName);
// handle filtering if possible
SimpleFeatureType schema = getSchema(typeName);
Filter filter = query.getFilter();
org.gdal.ogr.Geometry spatialFilter = getSpatialFilter(schema, filter);
if(spatialFilter != null)
layer.SetSpatialFilter(spatialFilter);
String attributeFilter = getAttributeFilter(schema, filter);
if(attributeFilter != null)
layer.SetAttributeFilter(attributeFilter);
return new OGRFeatureReader(ds, layer, schema);
}
/**
* Parses the Geotools filter and tries to extract an intersecting geometry that
* can be used as the OGR spatial filter
* @param schema
* @param filter
* @return
*/
protected org.gdal.ogr.Geometry getSpatialFilter(SimpleFeatureType schema, Filter filter) {
// TODO: switch to the non deprecated splitter (that no one seems to be using)
PostPreProcessFilterSplittingVisitor visitor = new PostPreProcessFilterSplittingVisitor(GEOMETRY_FILTER_CAPABILITIES, schema, null);
filter.accept(visitor, null);
Filter preFilter = visitor.getFilterPre();
if(preFilter instanceof BinarySpatialOperator) {
BinarySpatialOperator bso = ((BinarySpatialOperator) preFilter);
Expression geomExpression = null;
if(bso.getExpression1() instanceof PropertyName && bso.getExpression2() instanceof Literal) {
geomExpression = bso.getExpression2();
} else if(bso.getExpression1() instanceof Literal && bso.getExpression2() instanceof PropertyName) {
geomExpression = bso.getExpression1();
}
if(geomExpression != null) {
Geometry geom = geomExpression.evaluate(null, Geometry.class);
if(geom != null)
return new GeometryMapper(geom.getFactory()).parseGTGeometry(geom);
}
}
return null;
}
/**
* Parses the GeoTools filter and tries to extract an SQL expression that can
* be used as the OGR attribute filter
* @param schema
* @param filter
* @return
*/
protected String getAttributeFilter(SimpleFeatureType schema, Filter filter) {
// TODO: switch to the non deprecated splitter (that no one seems to be using)
PostPreProcessFilterSplittingVisitor visitor = new PostPreProcessFilterSplittingVisitor(ATTRIBUTE_FILTER_CAPABILITIES, schema, null);
filter.accept(visitor, null);
Filter preFilter = visitor.getFilterPre();
if(preFilter != Filter.EXCLUDE && preFilter != Filter.INCLUDE) {
StringWriter writer = new StringWriter();
FilterToSQL sqlConverter = new FilterToSQL(writer);
preFilter.accept(sqlConverter, null);
return writer.getBuffer().toString();
}
return null;
}
public SimpleFeatureType getSchema(String typeName) throws IOException {
DataSource ds = getOGRDataSource(FALSE);
Layer layer = getOGRLayer(ds, typeName);
if (layer == null) {
ds.delete();
throw new IOException("No such type : " + typeName);
}
FeatureDefn featureDef = null;
try {
featureDef = layer.GetLayerDefn();
SimpleFeatureTypeBuilder builder = new SimpleFeatureTypeBuilder();
AttributeTypeBuilder abuilder = new AttributeTypeBuilder();
builder.setName(typeName);
builder.setNamespaceURI(namespace);
// handle the geometry
CoordinateReferenceSystem crs = getCRS(layer);
Class geomClass = getGeometryClass(featureDef.GetGeomType());
abuilder.setName(Classes.getShortName( geomClass ));
abuilder.setNillable(true);
abuilder.setCRS(crs);
abuilder.setBinding(geomClass);
GeometryType geometryType = abuilder.buildGeometryType();
builder.add(abuilder.buildDescriptor(
BasicFeatureTypes.GEOMETRY_ATTRIBUTE_NAME, geometryType));
// builder.add("the_geom", geomClass, crs);
// compute a default parent feature type
if ((geomClass == Point.class) || (geomClass == MultiPoint.class)) {
builder.setSuperType(BasicFeatureTypes.POINT);
} else if ((geomClass == Polygon.class) || (geomClass == MultiPolygon.class)) {
builder.setSuperType(BasicFeatureTypes.POLYGON);
} else if ((geomClass == LineString.class) || (geomClass == MultiLineString.class)) {
builder.setSuperType(BasicFeatureTypes.LINE);
}
// handle the other fields
for (int i = 1; i < featureDef.GetFieldCount() + 1; i++) {
FieldDefn fd = featureDef.GetFieldDefn(i - 1);
builder.length(fd.GetWidth());
builder.add(fd.GetNameRef(), getFieldClass(fd));
fd.delete();
}
// finally build the geometry type
return builder.buildFeatureType();
} catch (FactoryException e) {
throw new DataSourceException("Could not determine geometry SRS", e);
} catch (FactoryRegistryException e) {
throw new DataSourceException("Could not create feature type", e);
} finally {
if (featureDef != null)
featureDef.delete();
if (layer != null)
layer.delete();
if (ds != null)
ds.delete();
}
}
public String[] getTypeNames() throws IOException {
DataSource ds;
try {
ds = getOGRDataSource(FALSE);
} catch (IOException e) {
return new String[0];
}
String[] typeNames = new String[ds.GetLayerCount()];
for (int i = 0; i < typeNames.length; i++) {
Layer l = ds.GetLayerByIndex(i);
typeNames[i] = l.GetName();
l.delete();
}
ds.delete();
return typeNames;
}
protected int getCount(Query query) throws IOException {
if (!Filter.INCLUDE.equals(query.getFilter()))
return -1;
DataSource ds = getOGRDataSource(FALSE);
Layer l = getOGRLayer(ds, query.getTypeName());
if (l == null)
throw new IOException("Unknown feature type: " + query.getTypeName());
// go for the quick computation, return -1 otherwise
int count = l.GetFeatureCount(FALSE);
l.delete();
ds.delete();
return count;
}
protected FeatureWriter createFeatureWriter(String typeName, Transaction transaction)
throws IOException {
if (supportsInPlaceWrite(typeName)) {
OGRFeatureReader reader = (OGRFeatureReader) getFeatureReader(typeName, Query.ALL, true);
return new OGRDirectFeatureWriter(reader);
} else {
throw new UnsupportedOperationException(
"This file format does not support in place write, "
+ "can't perform updates/deletes");
}
}
public boolean supportsInPlaceWrite(String typeName) throws IOException {
// if it's not able to open the datasource in update mode, there's no
// change it'll be able to support in place write (no need to throw an
// exception in
// this case, thus the direct call instead of getOGRDataSource())
DataSource ds = ogr.OpenShared(ogrSourceName, TRUE);
if (ds == null)
return false;
Layer l = getOGRLayer(ds, typeName);
boolean retval = l.TestCapability(ogr.OLCDeleteFeature)
&& l.TestCapability(ogr.OLCRandomWrite) && l.TestCapability(ogr.OLCSequentialWrite);
l.delete();
ds.delete();
return retval;
}
// given up on this one. Some drivers tell you that you can write on a layer
// only if you opened in update mode. We could try to create a new data
// source,
// but unfortunately there is no way to have a generic ogr name that works
// for every data store. Finally, not all drivers that do write do support
// datastore deletion. So, in the end, either we can update directly an OGR
// datasource content, or we have to create a new one and roll our own
// mechanism to have it replace the old one (this includes moving and
// deleting
// the elements that make up a data store).
public boolean supportsWriteNewLayer(String typeName) throws IOException {
DataSource ds = getOGRDataSource(FALSE);
Layer l = getOGRLayer(ds, typeName);
boolean retval = ds.TestCapability(ogr.ODsCCreateLayer)
&& l.TestCapability(ogr.OLCSequentialWrite);
l.delete();
ds.delete();
return retval;
}
public ReferencedEnvelope getBounds(Query q) throws IOException {
if (!q.getFilter().equals(Filter.INCLUDE))
return null;
DataSource ds = getOGRDataSource(OGRDataStore.FALSE);
Layer l = getOGRLayer(ds, q.getTypeName());
try {
if (l.TestCapability(ogr.OLCFastGetExtent)) {
double[] bbox = new double[4];
// TODO: hum... going thru this forcing the computation is anyways
// faster than loading all the features just for the sake of
// getting out the bbox.... but we don't have this
// explicit middle ground in Geotools
l.GetExtent(bbox, OGRDataStore.FALSE);
CoordinateReferenceSystem crs = getSchema(q.getTypeName()).getCoordinateReferenceSystem();
return new ReferencedEnvelope(bbox[0], bbox[1], bbox[2], bbox[3], crs);
} else {
return null;
}
} finally {
if(l != null)
l.delete();
if(ds != null)
ds.delete();
}
}
public void createSchema(SimpleFeatureType schema) throws IOException {
// TODO: add a field to allow approximate definitions
createSchema(schema, false, null);
}
/**
* Creates a new OGR layer with provided schema and options
*
* @param schema
* the geotools schema
* @param approximateFields
* if true, OGR will try to create fields that are approximations of the required
* ones when an exact match cannt be provided
* @param options
* OGR data source/layer creation options
* @throws IOException
*/
public void createSchema(SimpleFeatureType schema, boolean approximateFields, String[] options)
throws IOException {
DataSource ds = null;
Layer l = null;
try {
// either open datasource, or try creating one
Vector optVector = options != null ? new Vector(Arrays.asList(options)) : null;
try {
ds = getOGRDataSource(TRUE);
} catch (IOException e) {
if (ogrDriverName != null) {
Driver d = ogr.GetDriverByName(ogrDriverName);
ds = d.CreateDataSource(ogrSourceName, optVector);
d.delete();
if (ds == null)
throw new IOException("Could not create OGR data source with driver "
+ ogrDriverName + " and options " + optVector);
} else {
throw new DataSourceException("Driver not provided, and could not "
+ "open data source neither");
}
}
// get the spatial reference corresponding to the default geometry
GeometryDescriptor geomType = schema.getGeometryDescriptor();
int ogrGeomType = getOGRGeometryType(geomType);
SpatialReference sr = null;
if (geomType.getCoordinateReferenceSystem() != null) {
// use tostring to get a lenient wkt translation
String wkt = geomType.getCoordinateReferenceSystem().toString();
sr = new SpatialReference(null);
if (sr.ImportFromWkt(wkt) != 0) {
sr = null;
LOGGER.warning("OGR could not parse the geometry WKT," + " detailed error is: "
+ gdal.GetLastErrorMsg() + "\n" + "WKT was: " + wkt);
}
}
// create the layer
l = ds.CreateLayer(schema.getTypeName(), sr, ogrGeomType, optVector);
if (l == null) {
throw new DataSourceException("Could not create the OGR layer: "
+ gdal.GetLastErrorMsg());
}
// create fields
for (int i = 0; i < schema.getAttributeCount(); i++) {
AttributeDescriptor ad = schema.getDescriptor(i);
if (ad == schema.getGeometryDescriptor())
continue;
FieldDefn definition = getOGRFieldDefinition(ad);
l.CreateField(definition, approximateFields ? TRUE : FALSE);
}
} finally {
if (l != null) {
l.delete();
}
if (ds != null)
ds.delete();
}
}
// ---------------------------------------------------------------------------------------
// PRIVATE SUPPORT METHODS
// ---------------------------------------------------------------------------------------
private FieldDefn getOGRFieldDefinition(AttributeDescriptor at) throws IOException {
final Class type = at.getType().getBinding();
final FieldDefn def;
// set type, width, precision and justification where:
// * width is the number of chars needed to format the strings
// equivalent of
// the number
// * precision is the number of chars after decimal pont
// * justification: right or left (in outputs)
// TODO: steal code from Shapefile data store to guess eventual size
// limitations
if (Boolean.class.equals(type)) {
def = new FieldDefn(at.getLocalName(), ogr.OFTString);
def.SetWidth(5);
} else if (Byte.class.equals(type)) {
def = new FieldDefn(at.getLocalName(), ogr.OFTInteger);
def.SetWidth(3);
def.SetJustify(ogr.OJRight);
} else if (Short.class.equals(type)) {
def = new FieldDefn(at.getLocalName(), ogr.OFTInteger);
def.SetWidth(5);
def.SetJustify(ogr.OJRight);
} else if (Integer.class.equals(type)) {
def = new FieldDefn(at.getLocalName(), ogr.OFTInteger);
def.SetWidth(9);
def.SetJustify(ogr.OJRight);
} else if (Long.class.equals(type)) {
def = new FieldDefn(at.getLocalName(), ogr.OFTInteger);
def.SetWidth(19);
def.SetJustify(ogr.OJRight);
} else if (BigInteger.class.equals(type)) {
def = new FieldDefn(at.getLocalName(), ogr.OFTInteger);
def.SetWidth(32);
def.SetJustify(ogr.OJRight);
} else if (BigDecimal.class.equals(type)) {
def = new FieldDefn(at.getLocalName(), ogr.OFTReal);
def.SetWidth(32);
def.SetPrecision(15);
def.SetJustify(ogr.OJRight);
} else if (Float.class.equals(type)) {
def = new FieldDefn(at.getLocalName(), ogr.OFTReal);
def.SetWidth(12);
def.SetPrecision(7);
def.SetJustify(ogr.OJRight);
} else if (Double.class.equals(type) || Number.class.isAssignableFrom(type)) {
def = new FieldDefn(at.getLocalName(), ogr.OFTReal);
def.SetWidth(22);
def.SetPrecision(16);
def.SetJustify(ogr.OJRight);
} else if (String.class.equals(type)) {
def = new FieldDefn(at.getLocalName(), ogr.OFTString);
def.SetWidth(255);
// TODO: do a serious attempt to cover blob and clob too
} else if (byte[].class.equals(type)) {
def = new FieldDefn(at.getLocalName(), ogr.OFTBinary);
// } else if (java.sql.Date.class.isAssignableFrom(type)) {
// def = new FieldDefn(at.getLocalName(), ogr.OFTDate);
// } else if (java.sql.Time.class.isAssignableFrom(type)) {
// def = new FieldDefn(at.getLocalName(), ogr.OFTTime);
} else if (java.util.Date.class.isAssignableFrom(type)) {
def = new FieldDefn(at.getLocalName(), ogr.OFTDateTime);
} else {
throw new IOException("Cannot map " + type + " to an OGR type");
}
return def;
}
/**
* Tries to open the specified source in either read only or read/write mode
*
* @param update
* open read/write if TRUE, otherwise open read only
*
* @return
* @throws IOException
*/
DataSource getOGRDataSource(int update) throws IOException {
DataSource ds = ogr.OpenShared(ogrSourceName, update);
if (ds == null)
throw new IOException("OGR could not open '" + ogrSourceName + "'");
return ds;
}
/**
* Internal utility method, returns a layer if available, otherwise closes the provided
* datasource and throws and exception stating the feature type is not available
*
* @param ds
* @param typeName
* @return
* @throws IOException
*/
Layer getOGRLayer(DataSource ds, String typeName) throws IOException {
Layer l = ds.GetLayerByName(typeName);
if (l == null) {
ds.delete();
throw new IOException("No feature type " + typeName);
}
return l;
}
/**
* Turns an ogrFieldType into the best corresponding class we can figure out
*
* @param ogrFieldType
* @return
*/
private Class getFieldClass(FieldDefn field) {
final int ogrFieldType = field.GetFieldType();
final int width = field.GetWidth();
if (ogrFieldType < 0)
throw new IllegalArgumentException("Can't have a negative type");
if (ogrFieldType == ogr.OFTInteger) {
if (width <= 9)
return Integer.class;
else if (width <= 19)
return Long.class;
else
return BigDecimal.class;
} else if (ogrFieldType == ogr.OFTReal) {
if (width < 13)
return Float.class;
else
return Double.class;
} else if (ogrFieldType == ogr.OFTDate || ogrFieldType == ogr.OFTTime
|| ogrFieldType == ogr.OFTDateTime) {
// return java.sql.Date.class;
// } else if (ogrFieldType == ogr.OFTTime) {
// return java.sql.Time.class;
// } else if (ogrFieldType == ogr.OFTDateTime) {
return java.util.Date.class;
} else {
return String.class;
}
}
/**
* Turns an OGR {@link SpatialReference} object into a {@link CoordinateReferenceSystem} one
* (using WKT parsing)
*
* @param reference
* @return
* @throws FactoryException
*/
private CoordinateReferenceSystem getCRS(Layer layer) throws FactoryException {
SpatialReference reference = layer.GetSpatialRef();
if (reference == null)
return null;
String[] wkt = new String[1];
reference.ExportToWkt(wkt);
reference.delete();
return CRS.parseWKT(wkt[0]);
}
/**
* Turns a wkbGeometryType into the best corresponding geometry class we can figure out
*
* @param wkbGeometryType
* @return
*/
private Class getGeometryClass(int wkbGeometryType) {
if (wkbGeometryType < 0)
throw new IllegalArgumentException("Can't have a negative type");
int mask25d = 0x80000000;
int unmaskedType = wkbGeometryType & ~mask25d;
if (wkbGeometryType >= OGR_GEOM_TYPES.length) {
LOGGER.warning("Could not recognize geometry type " + wkbGeometryType
+ ". Assuming it's a new type and handling as a string");
return Geometry.class;
}
return OGR_GEOM_TYPES[unmaskedType];
}
/**
* Returns the OGR geometry type constant given a geometry attribute type
*
* @param type
* @return
* @throws IOException
*/
private int getOGRGeometryType(GeometryDescriptor type) throws IOException {
Class<?> binding = type.getType().getBinding();
for (int i = 0; i < OGR_GEOM_TYPES.length; i++) {
if (binding.equals(OGR_GEOM_TYPES[i]))
return i;
}
throw new IOException("Could not map " + binding + " to an OGR geometry type");
}
}