001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2003-2006, Geotools Project Managment Committee (PMC)
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation; either
009: * version 2.1 of the License, or (at your option) any later version.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: */
016: package org.geotools.data.oracle.sdo;
017:
018: import java.sql.ResultSetMetaData;
019: import java.sql.SQLException;
020: import java.util.Iterator;
021:
022: import oracle.jdbc.OracleConnection;
023: import oracle.sql.ARRAY;
024: import oracle.sql.ArrayDescriptor;
025: import oracle.sql.CHAR;
026: import oracle.sql.CharacterSet;
027: import oracle.sql.Datum;
028: import oracle.sql.NUMBER;
029: import oracle.sql.STRUCT;
030: import oracle.sql.StructDescriptor;
031:
032: import com.vividsolutions.jts.geom.Coordinate;
033: import com.vividsolutions.jts.geom.CoordinateList;
034: import com.vividsolutions.jts.geom.Geometry;
035: import com.vividsolutions.jts.geom.GeometryCollection;
036: import com.vividsolutions.jts.geom.GeometryFactory;
037: import com.vividsolutions.jts.geom.LineString;
038: import com.vividsolutions.jts.geom.MultiLineString;
039: import com.vividsolutions.jts.geom.MultiPoint;
040: import com.vividsolutions.jts.geom.MultiPolygon;
041: import com.vividsolutions.jts.geom.Point;
042: import com.vividsolutions.jts.geom.Polygon;
043:
044: /**
045: * Sample use of SDO class for simple JTS Geometry.
046: * <p>
047: * If needed I can make a LRSGeometryConverter that allows
048: * JTS Geometries with additional ordinates beyond xyz.
049: * </p>
050: * @author jgarnett
051: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/unsupported/oracle-spatial/src/main/java/org/geotools/data/oracle/sdo/GeometryConverter.java $
052: */
053: public class GeometryConverter {
054: protected OracleConnection connection;
055: GeometryFactory geometryFactory;
056:
057: public GeometryConverter(OracleConnection connection) {
058: this (connection, new GeometryFactory());
059: }
060:
061: public GeometryConverter(OracleConnection connection,
062: GeometryFactory geometryFactory) {
063: this .geometryFactory = geometryFactory;
064: this .connection = connection;
065: }
066:
067: public static final String DATATYPE = "MDSYS.SDO_GEOMETRY";
068:
069: /**
070: * Used to handle MDSYS.SDO_GEOMETRY.
071: *
072: * @return <code>MDSYS.SDO_GEOMETRY</code>
073: *
074: * @see net.refractions.jspatial.Converter#getDataType()
075: */
076: public String getDataTypeName() {
077: return DATATYPE;
078: }
079:
080: /**
081: * Ensure that obj is a JTS Geometry (2D or 3D) with no LRS measures.
082: * <p>
083: * This Converter does not support SpatialCoordinates</p>
084: *
085: * @param geom the Geometry to be converted
086: * @return <code>true</code> if <code>obj</code> is a JTS Geometry
087: * @see net.refractions.jspatial.Converter#isCapable(java.lang.Object)
088: */
089: public boolean isCapable(Geometry geom) {
090: if (geom == null)
091: return true;
092: if (geom instanceof Point || geom instanceof MultiPoint
093: || geom instanceof LineString
094: || geom instanceof MultiLineString
095: || geom instanceof Polygon
096: || geom instanceof MultiPolygon
097: || geom instanceof GeometryCollection) {
098: int d = SDO.D(geom);
099: int l = SDO.L(geom);
100: return l == 0 && (d == 2 || d == 3);
101: }
102: return false;
103: }
104:
105: /**
106: * Convert provided SDO_GEOMETRY to JTS Geometry.
107: * <p>
108: * Will return <code>null</code> as <code>null</code>.
109: * </p>
110: *
111: * @param sdoGeometry datum STRUCT to be converted to a double[]
112: *
113: * @return JTS <code>Geometry</code> representing the provided <code>datum</code>
114: * @throws SQLException
115: *
116: * @see net.refractions.jspatial.Converter#toObject(oracle.sql.STRUCT)
117: */
118: public Geometry asGeometry(STRUCT sdoGeometry) throws SQLException {
119: // Note Returning null for null Datum
120: if (sdoGeometry == null)
121: return null;
122: ResultSetMetaData meta = sdoGeometry.getDescriptor()
123: .getMetaData();
124:
125: Datum data[] = sdoGeometry.getOracleAttributes();
126: final int GTYPE = asInteger(data[0], 0);
127: final int SRID = asInteger(data[1], SDO.SRID_NULL);
128: final double POINT[] = asDoubleArray((STRUCT) data[2],
129: Double.NaN);
130: final int ELEMINFO[] = asIntArray((ARRAY) data[3], 0);
131: final double ORDINATES[] = asDoubleArray((ARRAY) data[4],
132: Double.NaN);
133: ;
134:
135: return SDO.create(geometryFactory, GTYPE, SRID, POINT,
136: ELEMINFO, ORDINATES);
137: }
138:
139: /**
140: * Used to convert double[] to SDO_ODINATE_ARRAY.
141: * <p>
142: * Will return <code>null</code> as an empty <code>SDO_GEOMETRY</code></p>
143: *
144: * @param geom Map to be represented as a STRUCT
145: * @return STRUCT representing provided Map
146: * @see net.refractions.jspatial.Converter#toDataType(java.lang.Object)
147: */
148: public STRUCT toSDO(Geometry geom) throws SQLException {
149: if (geom == null)
150: return asEmptyDataType();
151:
152: int gtype = SDO.gType(geom);
153: NUMBER SDO_GTYPE = new NUMBER(gtype);
154:
155: int srid = SDO.SRID(geom);
156: NUMBER SDO_SRID = (srid == SDO.SRID_NULL || srid == 0) ? null
157: : new NUMBER(srid);
158:
159: double[] point = SDO.point(geom);
160: STRUCT SDO_POINT;
161:
162: ARRAY SDO_ELEM_INFO;
163: ARRAY SDO_ORDINATES;
164:
165: if (point == null) {
166: int elemInfo[] = SDO.elemInfo(geom);
167: double ordinates[] = SDO.ordinates(geom);
168:
169: SDO_POINT = null;
170: SDO_ELEM_INFO = toARRAY(elemInfo,
171: "MDSYS.SDO_ELEM_INFO_ARRAY");
172: SDO_ORDINATES = toARRAY(ordinates,
173: "MDSYS.SDO_ORDINATE_ARRAY");
174: } else { // Point Optimization
175: Datum data[] = new Datum[] { toNUMBER(point[0]),
176: toNUMBER(point[1]), toNUMBER(point[2]), };
177: SDO_POINT = toSTRUCT(data, "MDSYS.SDO_POINT_TYPE");
178: SDO_ELEM_INFO = null;
179: SDO_ORDINATES = null;
180: }
181: Datum attributes[] = new Datum[] { SDO_GTYPE, SDO_SRID,
182: SDO_POINT, SDO_ELEM_INFO, SDO_ORDINATES };
183: return toSTRUCT(attributes, DATATYPE);
184: }
185:
186: /**
187: * Representation of <code>null</code> as an Empty <code>SDO_GEOMETRY</code>.
188: *
189: * @return <code>null</code> as a SDO_GEOMETRY
190: */
191: protected STRUCT asEmptyDataType() throws SQLException {
192: return toSTRUCT(null, DATATYPE);
193: }
194:
195: /** Convience method for STRUCT construction. */
196: protected final STRUCT toSTRUCT(Datum attributes[], String dataType)
197: throws SQLException {
198: if (dataType.startsWith("*.")) {
199: dataType = "DRA." + dataType.substring(2);//TODO here
200: }
201: StructDescriptor descriptor = StructDescriptor
202: .createDescriptor(dataType, connection);
203:
204: return new STRUCT(descriptor, connection, attributes);
205: }
206:
207: /**
208: * Convience method for ARRAY construction.
209: * <p>
210: * Compare and contrast with toORDINATE - which treats <code>Double.NaN</code>
211: * as<code>NULL</code></p>
212: */
213: protected final ARRAY toARRAY(double doubles[], String dataType)
214: throws SQLException {
215: ArrayDescriptor descriptor = ArrayDescriptor.createDescriptor(
216: dataType, connection);
217:
218: return new ARRAY(descriptor, connection, doubles);
219: }
220:
221: /**
222: * Convience method for ARRAY construction.
223: * <p>
224: * Forced to burn memory here - only way to actually place
225: * <code>NULL</code> numbers in the ordinate stream.</p>
226: * <ul>
227: * <li>JTS: records lack of data as <code>Double.NaN</code></li>
228: * <li>SDO: records lack of data as <code>NULL</code></li>
229: * </ul>
230: * <p>
231: * The alternative is to construct the array from a array of
232: * doubles, which does not record <code>NULL</code> NUMBERs.</p>
233: * <p>
234: * The results is an "MDSYS.SDO_ORDINATE_ARRAY"</p>
235: * <code><pre>
236: * list = c1(1,2,0), c2(3,4,Double.NaN)
237: * measures = {{5,6},{7,8}
238: *
239: * toORDINATE( list, measures, 2 )
240: * = (1,2,5,7, 3,4,6,8)
241: *
242: * toORDINATE( list, measures, 3 )
243: * = (1,2,0,5,7, 3,4,NULL,6,8)
244: *
245: * toORDINATE( list, null, 2 )
246: * = (1,2, 3,4)
247: * </pre></code>
248: *
249: * @param list CoordinateList to be represented
250: * @param measures Per Coordiante Measures, <code>null</code> if not required
251: * @param D Dimension of Coordinates (limited to 2d, 3d)
252: */
253: protected final ARRAY toORDINATE(CoordinateList list,
254: double measures[][], final int D) throws SQLException {
255: ArrayDescriptor descriptor = ArrayDescriptor.createDescriptor(
256: "MDSYS.SDO_ORDINATE_ARRAY", connection);
257:
258: final int LENGTH = measures != null ? measures.length : 0;
259: final int LEN = D + LENGTH;
260: Datum data[] = new Datum[list.size() * LEN];
261: int offset = 0;
262: int index = 0;
263: Coordinate coord;
264:
265: for (Iterator i = list.iterator(); i.hasNext(); index++) {
266: coord = (Coordinate) i.next();
267:
268: data[offset++] = toNUMBER(coord.x);
269: data[offset++] = toNUMBER(coord.y);
270: if (D == 3) {
271: data[offset++] = toNUMBER(coord.x);
272: }
273: for (int j = 0; j < LENGTH; j++) {
274: data[offset++] = toNUMBER(measures[j][index]);
275: }
276: }
277: return new ARRAY(descriptor, connection, data);
278: }
279:
280: protected final ARRAY toORDINATE(double ords[]) throws SQLException {
281: ArrayDescriptor descriptor = ArrayDescriptor.createDescriptor(
282: "MDSYS.SDO_ORDINATE_ARRAY", connection);
283:
284: final int LENGTH = ords.length;
285:
286: Datum data[] = new Datum[LENGTH];
287:
288: for (int i = 0; i < LENGTH; i++) {
289: data[i] = toNUMBER(ords[i]);
290: }
291: return new ARRAY(descriptor, connection, data);
292: }
293:
294: protected final ARRAY toATTRIBUTE(double ords[], String desc)
295: throws SQLException {
296: ArrayDescriptor descriptor = ArrayDescriptor.createDescriptor(
297: desc, connection);
298:
299: final int LENGTH = ords.length;
300:
301: Datum data[] = new Datum[LENGTH];
302:
303: for (int i = 0; i < LENGTH; i++) {
304: data[i] = toNUMBER(ords[i]);
305: }
306: return new ARRAY(descriptor, connection, data);
307: }
308:
309: /**
310: * Convience method for NUMBER construction.
311: * <p>
312: * Double.NaN is represented as <code>NULL</code> to agree
313: * with JTS use.</p>
314: */
315: protected final NUMBER toNUMBER(double number) throws SQLException {
316: if (Double.isNaN(number)) {
317: return null;
318: }
319: return new NUMBER(number);
320: }
321:
322: /**
323: * Convience method for ARRAY construction.
324: */
325: protected final ARRAY toARRAY(int ints[], String dataType)
326: throws SQLException {
327: ArrayDescriptor descriptor = ArrayDescriptor.createDescriptor(
328: dataType, connection);
329:
330: return new ARRAY(descriptor, connection, ints);
331: }
332:
333: /** Convience method for NUMBER construction */
334: protected final NUMBER toNUMBER(int number) {
335: return new NUMBER(number);
336: }
337:
338: /** Convience method for CHAR construction */
339: protected final CHAR toCHAR(String s) {
340:
341: // make sure if the string is larger than one character, only take the first character
342: if (s.length() > 1)
343: s = new String((new Character(s.charAt(0))).toString());
344: try {
345: //BUG: make sure I am correct
346: return new CHAR(s, CharacterSet
347: .make(CharacterSet.ISO_LATIN_1_CHARSET));
348: } catch (SQLException e) {
349: e.printStackTrace();
350: }
351: return null;
352: }
353:
354: //
355: // These functions present Datum as a Java type
356: //
357: /** Presents datum as an int */
358: protected int asInteger(Datum datum, final int DEFAULT)
359: throws SQLException {
360: if (datum == null)
361: return DEFAULT;
362: return ((NUMBER) datum).intValue();
363: }
364:
365: /** Presents datum as a double */
366: protected double asDouble(Datum datum, final double DEFAULT)
367: throws SQLException {
368: if (datum == null)
369: return DEFAULT;
370: return ((NUMBER) datum).doubleValue();
371: }
372:
373: /** Presents struct as a double[] */
374: protected double[] asDoubleArray(STRUCT struct, final double DEFAULT)
375: throws SQLException {
376: if (struct == null)
377: return null;
378: return asDoubleArray(struct.getOracleAttributes(), DEFAULT);
379: }
380:
381: /** Presents array as a double[] */
382: protected double[] asDoubleArray(ARRAY array, final double DEFAULT)
383: throws SQLException {
384: if (array == null)
385: return null;
386: if (DEFAULT == 0)
387: return array.getDoubleArray();
388:
389: return asDoubleArray(array.getOracleArray(), DEFAULT);
390: }
391:
392: /** Presents Datum[] as a double[] */
393: protected double[] asDoubleArray(Datum data[], final double DEFAULT)
394: throws SQLException {
395: if (data == null)
396: return null;
397: double array[] = new double[data.length];
398: for (int i = 0; i < data.length; i++) {
399: array[i] = asDouble(data[i], DEFAULT);
400: }
401: return array;
402: }
403:
404: protected int[] asIntArray(ARRAY array, int DEFAULT)
405: throws SQLException {
406: if (array == null)
407: return null;
408: if (DEFAULT == 0)
409: return array.getIntArray();
410:
411: return asIntArray(array.getOracleArray(), DEFAULT);
412: }
413:
414: /** Presents Datum[] as a int[] */
415: protected int[] asIntArray(Datum data[], final int DEFAULT)
416: throws SQLException {
417: if (data == null)
418: return null;
419: int array[] = new int[data.length];
420: for (int i = 0; i < data.length; i++) {
421: array[i] = asInteger(data[i], DEFAULT);
422: }
423: return array;
424: }
425: }
|