/*
* 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.math.BigDecimal;
import java.math.BigInteger;
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 {
OGR ogr;
public FeatureTypeMapper(OGR ogr) {
this.ogr = ogr;
}
/**
* Returns the geotools feature type equivalent from the native OGR one
*
* @param layer
* @param typeName
* @param namespaceURI
* @return
* @throws IOException
*/
SimpleFeatureType getFeatureType(Object layer, String typeName, String namespaceURI)
throws IOException {
Object 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.LayerGetLayerDefn(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.LayerGetFieldCount(definition);
for (int i = 0; i < count; i++) {
Object field = ogr.LayerGetFieldDefn(definition, i);
String name = ogr.FieldGetName(field);
Class binding = getBinding(field);
int width = ogr.FieldGetWidth(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 {
ogr.LayerReleaseLayerDefn(definition);
}
}
/**
* Maps the OGR field type to a java class
*
* @param field
* @return
*/
private Class getBinding(Object field) {
long type = ogr.FieldGetType(field);
int width = ogr.FieldGetWidth(field);
if (ogr.FieldIsIntegerType(type)) {
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 (ogr.FieldIsIntegerListType(type)) {
return int[].class;
} else if (ogr.FieldIsRealType(type)) {
if (width <= 12) {
return Float.class;
} else if (width <= 22) {
return Double.class;
} else {
return BigDecimal.class;
}
} else if (ogr.FieldIsRealListType(type)) {
return double[].class;
} else if (ogr.FieldIsBinaryType(type)) {
return byte[].class;
} else if (ogr.FieldIsDateType(type)) {
return java.sql.Date.class;
} else if (ogr.FieldIsTimeType(type)) {
return java.sql.Time.class;
} else if (ogr.FieldIsDateTimeType(type)) {
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 Object getOGRFieldDefinition(AttributeDescriptor ad) throws IOException {
final Class type = ad.getType().getBinding();
final Object def;
final String name = ad.getLocalName();
if (Boolean.class.equals(type)) {
def = ogr.CreateStringField(name);
ogr.FieldSetWidth(def, 5);
} else if (Byte.class.equals(type)) {
def = ogr.CreateIntegerField(name);
ogr.FieldSetWidth(def, 3);
ogr.FieldSetJustifyRight(def);
} else if (Short.class.equals(type)) {
def = ogr.CreateIntegerField(name);
ogr.FieldSetWidth(def, 5);
ogr.FieldSetJustifyRight(def);
} else if (Integer.class.equals(type)) {
def = ogr.CreateIntegerField(name);
ogr.FieldSetWidth(def, 9);
ogr.FieldSetJustifyRight(def);
} else if (Long.class.equals(type)) {
def = ogr.CreateIntegerField(name);
ogr.FieldSetWidth(def, 19);
ogr.FieldSetJustifyRight(def);
} else if (BigInteger.class.equals(type)) {
def = ogr.CreateIntegerField(name);
ogr.FieldSetWidth(def, 32);
ogr.FieldSetJustifyRight(def);
} else if (BigDecimal.class.equals(type)) {
def = ogr.CreateRealField(name);
ogr.FieldSetWidth(def, 32);
ogr.FieldSetPrecision(def, 15);
ogr.FieldSetJustifyRight(def);
} else if (Float.class.equals(type)) {
def = ogr.CreateRealField(name);
ogr.FieldSetWidth(def, 12);
ogr.FieldSetPrecision(def, 7);
ogr.FieldSetJustifyRight(def);
} else if (Double.class.equals(type) || Number.class.isAssignableFrom(type)) {
def = ogr.CreateRealField(name);
ogr.FieldSetWidth(def, 22);
ogr.FieldSetPrecision(def, 16);
ogr.FieldSetJustifyRight(def);
} else if (String.class.equals(type)) {
def = ogr.CreateStringField(name);
int length = FeatureTypes.getFieldLength(ad);
if (length <= 0) {
length = 255;
}
ogr.FieldSetWidth(def, length);
} else if (byte[].class.equals(type)) {
def = ogr.CreateBinaryField(name);
} else if (java.sql.Date.class.isAssignableFrom(type)) {
def = ogr.CreateDateField(name);
} else if (java.sql.Time.class.isAssignableFrom(type)) {
def = ogr.CreateTimeField(name);
} else if (java.util.Date.class.isAssignableFrom(type)) {
def = ogr.CreateDateTimeField(name);
} 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 long getOGRGeometryType(GeometryDescriptor descriptor)
throws IOException {
Class binding = descriptor.getType().getBinding();
if (GeometryCollection.class.isAssignableFrom(binding)) {
if (MultiPoint.class.isAssignableFrom(binding)) {
return ogr.GetMultiPointType();
} else if (MultiLineString.class.isAssignableFrom(binding)) {
return ogr.GetMultiLineStringType();
} else if (MultiPolygon.class.isAssignableFrom(binding)) {
return ogr.GetMultiPolygonType();
} else {
return ogr.GetGeometryCollectionType();
}
} else {
if (Point.class.isAssignableFrom(binding)) {
return ogr.GetPointType();
} else if (LinearRing.class.isAssignableFrom(binding)) {
return ogr.GetLinearRingType();
} else if (LineString.class.isAssignableFrom(binding)) {
return ogr.GetLineStringType();
} else if (Polygon.class.isAssignableFrom(binding)) {
return ogr.GetPolygonType();
} else {
return ogr.GetGeometryUnknownType();
}
}
}
/**
* Returns the JTS geometry type equivalent to the layer native one
*
* @param definition
* @return
* @throws IOException
*/
private Class<? extends Geometry> getGeometryBinding(Object definition) throws IOException {
long value = ogr.LayerGetGeometryType(definition);
// 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 == ogr.GetPointType()
|| value == ogr.GetPoint25DType()) {
return Point.class;
} else if (value == ogr.GetLinearRingType()) {
return LinearRing.class;
} else if (value == ogr.GetLineStringType()
|| value == ogr.GetLineString25DType()
|| value == ogr.GetMultiLineStringType()
|| value == ogr.GetMultiLineString25DType()) {
return MultiLineString.class;
} else if (value == ogr.GetPolygonType()
|| value == ogr.GetPolygon25DType()
|| value == ogr.GetMultiPolygonType()
|| value == ogr.GetMultiPolygon25DType()) {
return MultiPolygon.class;
} else if (value == ogr.GetGeometryCollectionType()
|| value == ogr.GetGeometryCollection25DType()) {
return GeometryCollection.class;
} else if (value == ogr.GetGeometryNoneType()) {
return null;
} else if (value == ogr.GetGeometryUnknownType()) {
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(Object layer) throws IOException {
Object spatialReference = null;
CoordinateReferenceSystem crs = null;
try {
spatialReference = ogr.LayerGetSpatialRef(layer);
if (spatialReference == null) {
return null;
}
try {
String code = ogr.SpatialRefGetAuthorityCode(spatialReference, "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 {
String wkt = ogr.SpatialRefExportToWkt(spatialReference);
crs = CRS.parseWKT(wkt);
} catch (Exception e) {
// the wkt might reference an unsupported projection
}
}
return crs;
} finally {
if (spatialReference != null) ogr.SpatialRefRelease(spatialReference);
}
}
/**
* Returns a Pointer to a OGR spatial reference object equivalent to the specified GeoTools CRS
*
* @param crs
* @return
*/
public Object getSpatialReference(CoordinateReferenceSystem crs) {
if (crs == null) {
return null;
}
// use tostring to get a lenient wkt translation
String wkt = crs.toString();
return ogr.NewSpatialRef(wkt);
}
}