/* * 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)); } }