001: /*
002: * The JTS Topology Suite is a collection of Java classes that
003: * implement the fundamental operations required to validate a given
004: * geo-spatial data set to a known topological specification.
005: *
006: * Copyright (C) 2001 Vivid Solutions
007: *
008: * This library is free software; you can redistribute it and/or
009: * modify it under the terms of the GNU Lesser General Public
010: * License as published by the Free Software Foundation; either
011: * version 2.1 of the License, or (at your option) any later version.
012: *
013: * This library is distributed in the hope that it will be useful,
014: * but WITHOUT ANY WARRANTY; without even the implied warranty of
015: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
016: * Lesser General Public License for more details.
017: *
018: * You should have received a copy of the GNU Lesser General Public
019: * License along with this library; if not, write to the Free Software
020: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
021: *
022: * For more information, contact:
023: *
024: * Vivid Solutions
025: * Suite #1A
026: * 2328 Government Street
027: * Victoria BC V8T 5G5
028: * Canada
029: *
030: * (250)385-6040
031: * www.vividsolutions.com
032: */
033: /*
034: * Geotools2 - OpenSource mapping toolkit
035: * http://geotools.org
036: * (C) 2003, Geotools Project Managment Committee (PMC)
037: *
038: * This library is free software; you can redistribute it and/or
039: * modify it under the terms of the GNU Lesser General Public
040: * License as published by the Free Software Foundation;
041: * version 2.1 of the License.
042: *
043: * This library is distributed in the hope that it will be useful,
044: * but WITHOUT ANY WARRANTY; without even the implied warranty of
045: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
046: * Lesser General Public License for more details.
047: *
048: */
049: package com.vividsolutions.jts.io.oracle;
050:
051: import java.sql.SQLException;
052: import java.util.*;
053:
054: import oracle.sql.*;
055:
056: import com.vividsolutions.jts.algorithm.CGAlgorithms;
057: import com.vividsolutions.jts.geom.*;
058:
059: /**
060: * Creates a {@link Geometry} from an Oracle MDSYS.GEOMETRY object.
061: *
062: * A {@link GeometryFactory} may be provided, otherwise
063: * a default one will be used.
064: * The provided GeometryFactory will be used, with the exception of the SRID field.
065: * This will be extracted from the Geometry.
066: * <p>
067: * If a PrecisionModel is supplied it is the callers's responsibility
068: * to ensure that it matches the precision of the incoming data.
069: * If a lower precision for the data is required, a subsequent
070: * process must be run on the data to reduce its precision.
071: *
072: * @version 9i
073: * @author David Zwiers, Vivid Solutions.
074: */
075: public class OraReader {
076: private GeometryFactory geometryFactory;
077:
078: public static final int NULL_DIMENSION = -1;
079: private int dimension = -1;
080:
081: /**
082: * Creates a new reader, with a default GeometryFactory.
083: *
084: * @see #OraReader(GeometryFactory)
085: */
086: public OraReader() {
087: this (new GeometryFactory());
088: }
089:
090: /**
091: * Creates a new reader, with the supplied {@link GeometryFactory}.
092: * It is assumed that the supplied {@link PrecisionModel}
093: * matches the precision of the incoming data -
094: * coordinates are <b>not</b> made precise.
095: *
096: * @param gf
097: * A non-null geometry factory for later use.
098: * @throw NullPointerException when the geometry factory is null.
099: */
100: public OraReader(GeometryFactory gf) {
101: if (gf == null)
102: throw new NullPointerException(
103: "Geometry Factory may not be Null");
104: this .geometryFactory = gf;
105: }
106:
107: /**
108: * This method will attempt to create a JTS Geometry for the MDSYS.GEOMETRY
109: * provided. The Type of gemetry returned will depend on the input datum,
110: * where the Geometry type is specified within the STRUCT.
111: *
112: * @param struct The MDSYS.GEOMETRY Object to decode
113: * @return A JTS Geometry if one could be created, null otherwise
114: * @throws SQLException When a read error occured within the struct
115: */
116: public Geometry read(STRUCT struct) throws SQLException {
117:
118: // Note Returning null for null Datum
119: if (struct == null)
120: return null;
121:
122: Datum data[] = struct.getOracleAttributes();
123: int gType = asInteger(data[0], 0);
124: int SRID = asInteger(data[1], Constants.SRID_NULL);
125: double point[] = asDoubleArray((STRUCT) data[2], Double.NaN);
126: int elemInfo[] = asIntArray((ARRAY) data[3], 0);
127: double ordinates[] = asDoubleArray((ARRAY) data[4], Double.NaN);
128: GeometryFactory gf = geometryFactory;
129: if (geometryFactory.getSRID() != SRID) {
130: // clone it and use the geom's srid
131: gf = new GeometryFactory(geometryFactory
132: .getPrecisionModel(), SRID, geometryFactory
133: .getCoordinateSequenceFactory());
134: }
135:
136: return create(gf, gType, point, elemInfo, ordinates);
137: }
138:
139: /**
140: * Decode geometry from provided SDO encoded information.
141: *
142: * <p></p>
143: *
144: * @param gf Used to construct returned Geometry
145: * @param gType SDO_GTEMPLATE represents dimension, LRS, and geometry type
146: * @param point
147: * @param elemInfo
148: * @param ordinates
149: *
150: * @return Geometry as encoded
151: */
152: private Geometry create(GeometryFactory gf, int gType,
153: double[] point, int[] elemInfo, double[] ordinates) {
154:
155: int lrs = (gType % 1000) / 100;
156:
157: // find the dimension: represented by the smaller of the two dimensions
158: int dim = 0;
159: if (dimension != NULL_DIMENSION) {
160: dim = dimension;
161: } else {
162: dim = Math.min(gType / 1000, gf
163: .getCoordinateSequenceFactory().create(0, 0)
164: .getDimension());
165: }
166:
167: if (dim < 2) {
168: throw new IllegalArgumentException(
169: "Dimension D:"
170: + dim
171: + " is not valid for JTS. "
172: + "Either specify a dimension or use Oracle Locator Version 9i or later");
173: }
174:
175: // extract the geometry template type
176: // this is represented as the rightmost two digits
177: int geomTemplate = gType - (dim * 1000) - (lrs * 100);
178:
179: CoordinateSequence coords = null;
180:
181: if (lrs == 0 && geomTemplate == 1 && point != null
182: && elemInfo == null) {
183: // Single Point Type Optimization
184: coords = coordinates(gf.getCoordinateSequenceFactory(),
185: dim, lrs, geomTemplate, point);
186: elemInfo = new int[] { 1, Constants.SDO_ETYPE.POINT, 1 };
187: } else {
188: coords = coordinates(gf.getCoordinateSequenceFactory(),
189: dim, lrs, geomTemplate, ordinates);
190: }
191:
192: switch (geomTemplate) {
193: case Constants.SDO_GTEMPLATE.POINT:
194: return createPoint(gf, dim, lrs, elemInfo, 0, coords);
195:
196: case Constants.SDO_GTEMPLATE.LINE:
197: return createLine(gf, dim, lrs, elemInfo, 0, coords);
198:
199: case Constants.SDO_GTEMPLATE.POLYGON:
200: return createPolygon(gf, dim, lrs, elemInfo, 0, coords);
201:
202: case Constants.SDO_GTEMPLATE.MULTIPOINT:
203: return createMultiPoint(gf, dim, lrs, elemInfo, 0, coords);
204:
205: case Constants.SDO_GTEMPLATE.MULTILINE:
206: return createMultiLine(gf, dim, lrs, elemInfo, 0, coords,
207: -1);
208:
209: case Constants.SDO_GTEMPLATE.MULTIPOLYGON:
210: return createMultiPolygon(gf, dim, lrs, elemInfo, 0,
211: coords, -1);
212:
213: case Constants.SDO_GTEMPLATE.COLLECTION:
214: return createCollection(gf, dim, lrs, elemInfo, 0, coords,
215: -1);
216:
217: default:
218: return null;
219: }
220: }
221:
222: /**
223: * Construct CoordinateList as described by GTYPE.
224: *
225: * The number of ordinates per coordinate are taken to be lrs+dim, and the
226: * number of ordinates should be a multiple of this value.
227:
228: * In the Special case of GTYPE 2001 and a three ordinates are interpreted
229: * as a single Coordinate rather than an error.
230: *
231: * @param f CoordinateSequenceFactory used to encode ordiantes for JTS
232: * @param ordinates
233: *
234: * @return protected
235: *
236: * @throws IllegalArgumentException
237: */
238: private CoordinateSequence coordinates(CoordinateSequenceFactory f,
239: int dim, int lrs, int gtemplate, double[] ordinates) {
240: if ((ordinates == null) || (ordinates.length == 0)) {
241: return f.create(new Coordinate[0]);
242: }
243:
244: // POINT_TYPE Special Case
245: //
246: if ((dim == 2) && (lrs == 0) && (gtemplate == 01)
247: && (ordinates.length == 3)) {
248: return f.create(new Coordinate[] { new Coordinate(
249: ordinates[0], ordinates[1], ordinates[2]), });
250: }
251:
252: int len = dim + lrs;
253:
254: if ((len == 0 && ordinates.length != 0)
255: || (len != 0 && ((ordinates.length % len) != 0))) {
256: throw new IllegalArgumentException("Dimension D:" + dim
257: + " and L:" + lrs + " denote Coordinates " + "of "
258: + len + " ordinates. This cannot be resolved with"
259: + "an ordinate array of length " + ordinates.length);
260: }
261:
262: int length = (len == 0 ? 0 : ordinates.length / len);
263:
264: // we would have to ask for a dimension which represents all the requested
265: // dimension and measures from a mask array in the future
266: CoordinateSequence cs = f.create(length, dim);
267:
268: int actualDim = cs.getDimension();
269: for (int i = 0; i < length; i++) {
270: int j = 0;
271: // in the future change this condition to include ignored dimensions from mask array
272: for (; j < actualDim && j < dim; j++) {
273: cs.setOrdinate(i, j, ordinates[i * len + j]);
274: // may not always want to inc. j when we have a mask array
275: }
276: // in the future change this condition to include ignored dimensions from mask array
277: for (int d = j; j < actualDim && (j - d) < lrs; j++) {
278: cs.setOrdinate(i, j, ordinates[i * len + j]);
279: // may not always want to inc. j when we have a mask array
280: }
281: }
282: return cs;
283: }
284:
285: /**
286: * Create MultiGeometry as encoded by elemInfo.
287: *
288: * @param gf Used to construct MultiLineString
289: * @param elemInfo Interpretation of coords
290: * @param elemIndex Triplet in elemInfo to process as a Polygon
291: * @param coords Coordinates to interpret using elemInfo
292: * @param numGeom Number of triplets (or -1 for rest)
293: *
294: * @return GeometryCollection
295: *
296: * @throws IllegalArgumentException DWhen faced with an encoding error
297: */
298: private GeometryCollection createCollection(GeometryFactory gf,
299: int dim, int lrs, int[] elemInfo, int elemIndex,
300: CoordinateSequence coords, int numGeom) {
301:
302: int sOffset = StartingOffset(elemInfo, elemIndex);
303:
304: int length = coords.size() * dim;
305:
306: if (!(sOffset <= length))
307: throw new IllegalArgumentException(
308: "ELEM_INFO STARTING_OFFSET " + sOffset
309: + " inconsistent with ORDINATES length "
310: + coords.size());
311:
312: int endTriplet = (numGeom != -1) ? elemIndex + numGeom
313: : elemInfo.length / 3 + 1;
314:
315: List list = new LinkedList();
316: int etype;
317: int interpretation;
318: Geometry geom;
319:
320: boolean cont = true;
321: for (int i = elemIndex; cont && i < endTriplet; i++) {
322: etype = eType(elemInfo, i);
323: interpretation = interpretation(elemInfo, i);
324:
325: switch (etype) {
326: case -1:
327: cont = false; // We are the of the list - get out of here
328:
329: case Constants.SDO_ETYPE.POINT:
330:
331: if (interpretation == 1) {
332: geom = createPoint(gf, dim, lrs, elemInfo, i,
333: coords);
334: } else if (interpretation > 1) {
335: geom = createMultiPoint(gf, dim, lrs, elemInfo, i,
336: coords);
337: } else {
338: throw new IllegalArgumentException(
339: "ETYPE.POINT requires INTERPRETATION >= 1");
340: }
341:
342: break;
343:
344: case Constants.SDO_ETYPE.LINE:
345: geom = createLine(gf, dim, lrs, elemInfo, i, coords);
346:
347: break;
348:
349: case Constants.SDO_ETYPE.POLYGON:
350: case Constants.SDO_ETYPE.POLYGON_EXTERIOR:
351: geom = createPolygon(gf, dim, lrs, elemInfo, i, coords);
352: i += ((Polygon) geom).getNumInteriorRing();
353:
354: break;
355:
356: case Constants.SDO_ETYPE.POLYGON_INTERIOR:
357: throw new IllegalArgumentException(
358: "ETYPE 2003 (Polygon Interior) no expected in a GeometryCollection"
359: + "(2003 is used to represent polygon holes, in a 1003 polygon exterior)");
360:
361: default:
362: throw new IllegalArgumentException(
363: "ETYPE "
364: + etype
365: + " not representable as a JTS Geometry."
366: + "(Custom and Compound Straight and Curved Geometries not supported)");
367: }
368:
369: list.add(geom);
370: }
371:
372: GeometryCollection geoms = gf
373: .createGeometryCollection((Geometry[]) list
374: .toArray(new Geometry[list.size()]));
375:
376: return geoms;
377: }
378:
379: /**
380: * Create MultiPolygon as encoded by elemInfo.
381: *
382: *
383: * @param gf Used to construct MultiLineString
384: * @param elemInfo Interpretation of coords
385: * @param elemIndex Triplet in elemInfo to process as a Polygon
386: * @param coords Coordinates to interpret using elemInfo
387: * @param numGeom Number of triplets (or -1 for rest)
388: *
389: * @return MultiPolygon
390: */
391: private MultiPolygon createMultiPolygon(GeometryFactory gf,
392: int dim, int lrs, int[] elemInfo, int elemIndex,
393: CoordinateSequence coords, int numGeom) {
394:
395: int sOffset = StartingOffset(elemInfo, elemIndex);
396: int etype = eType(elemInfo, elemIndex);
397: int interpretation = interpretation(elemInfo, elemIndex);
398:
399: int length = coords.size() * dim;
400:
401: if (!(sOffset >= 1) || !(sOffset <= length))
402: throw new IllegalArgumentException(
403: "ELEM_INFO STARTING_OFFSET " + sOffset
404: + " inconsistent with ORDINATES length "
405: + coords.size());
406: if (!(etype == Constants.SDO_ETYPE.POLYGON)
407: && !(etype == Constants.SDO_ETYPE.POLYGON_EXTERIOR))
408: throw new IllegalArgumentException(
409: "ETYPE "
410: + etype
411: + " inconsistent with expected POLYGON or POLYGON_EXTERIOR");
412: if (interpretation != 1 && interpretation != 3) {
413: return null;
414: }
415:
416: int endTriplet = (numGeom != -1) ? elemIndex + numGeom
417: : (elemInfo.length / 3) + 1;
418:
419: List list = new LinkedList();
420: boolean cont = true;
421:
422: for (int i = elemIndex; cont && i < endTriplet
423: && (etype = eType(elemInfo, i)) != -1; i++) {
424: if ((etype == Constants.SDO_ETYPE.POLYGON)
425: || (etype == Constants.SDO_ETYPE.POLYGON_EXTERIOR)) {
426: Polygon poly = createPolygon(gf, dim, lrs, elemInfo, i,
427: coords);
428: i += poly.getNumInteriorRing(); // skip interior rings
429: list.add(poly);
430: } else { // not a Polygon - get out here
431: cont = false;
432: }
433: }
434:
435: MultiPolygon polys = gf.createMultiPolygon((Polygon[]) list
436: .toArray(new Polygon[list.size()]));
437:
438: return polys;
439: }
440:
441: /**
442: * Create MultiLineString as encoded by elemInfo.
443: *
444: *
445: * @param gf Used to construct MultiLineString
446: * @param elemInfo Interpretation of coords
447: * @param elemIndex Triplet in elemInfo to process as a Polygon
448: * @param coords Coordinates to interpret using elemInfo
449: * @param numGeom Number of triplets (or -1 for rest)
450: *
451: * @return MultiLineString
452: */
453: private MultiLineString createMultiLine(GeometryFactory gf,
454: int dim, int lrs, int[] elemInfo, int elemIndex,
455: CoordinateSequence coords, int numGeom) {
456:
457: int sOffset = StartingOffset(elemInfo, elemIndex);
458: int etype = eType(elemInfo, elemIndex);
459: int interpretation = interpretation(elemInfo, elemIndex);
460:
461: int length = coords.size() * dim;
462:
463: if (!(sOffset >= 1) || !(sOffset <= length))
464: throw new IllegalArgumentException(
465: "ELEM_INFO STARTING_OFFSET " + sOffset
466: + " inconsistent with ORDINATES length "
467: + coords.size());
468: if (!(etype == Constants.SDO_ETYPE.LINE))
469: throw new IllegalArgumentException("ETYPE " + etype
470: + " inconsistent with expected LINE");
471: if (!(interpretation == 1)) {
472: // we cannot represent INTERPRETATION > 1
473: return null;
474: }
475:
476: int endTriplet = (numGeom != -1) ? (elemIndex + numGeom)
477: : (elemInfo.length / 3);
478:
479: List list = new LinkedList();
480:
481: boolean cont = true;
482: for (int i = elemIndex; cont && i < endTriplet
483: && (etype = eType(elemInfo, i)) != -1; i++) {
484: if (etype == Constants.SDO_ETYPE.LINE) {
485: list.add(createLine(gf, dim, lrs, elemInfo, i, coords));
486: } else { // not a LineString - get out of here
487: cont = false;
488: }
489: }
490:
491: MultiLineString lines = gf
492: .createMultiLineString((LineString[]) list
493: .toArray(new LineString[list.size()]));
494:
495: return lines;
496: }
497:
498: /**
499: * Create MultiPoint as encoded by elemInfo.
500: *
501: *
502: * @param gf Used to construct polygon
503: * @param elemInfo Interpretation of coords
504: * @param elemIndex Triplet in elemInfo to process as a Polygon
505: * @param coords Coordinates to interpret using elemInfo
506: *
507: * @return MultiPoint
508: */
509: private MultiPoint createMultiPoint(GeometryFactory gf, int dim,
510: int lrs, int[] elemInfo, int elemIndex,
511: CoordinateSequence coords) {
512:
513: int sOffset = StartingOffset(elemInfo, elemIndex);
514: int etype = eType(elemInfo, elemIndex);
515: int interpretation = interpretation(elemInfo, elemIndex);
516:
517: if (!(sOffset >= 1) || !(sOffset <= coords.size()))
518: throw new IllegalArgumentException(
519: "ELEM_INFO STARTING_OFFSET " + sOffset
520: + " inconsistent with ORDINATES length "
521: + coords.size());
522: if (!(etype == Constants.SDO_ETYPE.POINT))
523: throw new IllegalArgumentException("ETYPE " + etype
524: + " inconsistent with expected POINT");
525: if (!(interpretation > 1)) {
526: return null;
527: }
528:
529: int len = dim + lrs;
530:
531: int start = (sOffset - 1) / len;
532: int end = start + interpretation;
533:
534: MultiPoint points = gf.createMultiPoint(subList(gf
535: .getCoordinateSequenceFactory(), coords, start, end));
536:
537: return points;
538: }
539:
540: /**
541: * Create Polygon as encoded.
542: *
543: * @see #interpretation(int[], int)
544: *
545: * @param gf Used to construct polygon
546: * @param elemInfo Interpretation of coords
547: * @param elemIndex Triplet in elemInfo to process as a Polygon
548: * @param coords Coordinates to interpret using elemInfo
549: *
550: * @return Polygon as encoded by elemInfo, or null when faced with and
551: * encoding that can not be captured by JTS
552: * @throws IllegalArgumentException When faced with an invalid SDO encoding
553: */
554: private Polygon createPolygon(GeometryFactory gf, int dim, int lrs,
555: int[] elemInfo, int elemIndex, CoordinateSequence coords) {
556:
557: int sOffset = StartingOffset(elemInfo, elemIndex);
558: int etype = eType(elemInfo, elemIndex);
559: int interpretation = interpretation(elemInfo, elemIndex);
560:
561: if (!(1 <= sOffset && sOffset <= (coords.size() * dim))) {
562: throw new IllegalArgumentException(
563: "ELEM_INFO STARTING_OFFSET " + sOffset
564: + "inconsistent with COORDINATES length "
565: + (coords.size() * dim));
566: }
567:
568: if (!(etype == Constants.SDO_ETYPE.POLYGON)
569: && !(etype == Constants.SDO_ETYPE.POLYGON_EXTERIOR)) {
570: throw new IllegalArgumentException(
571: "ETYPE "
572: + etype
573: + " inconsistent with expected POLYGON or POLYGON_EXTERIOR");
574: }
575: if (!(interpretation == 1) && !(interpretation == 3)) {
576: return null;
577: }
578:
579: LinearRing exteriorRing = createLinearRing(gf, dim, lrs,
580: elemInfo, elemIndex, coords);
581:
582: List rings = new LinkedList();
583:
584: boolean cont = true;
585: for (int i = elemIndex + 1; cont
586: && (etype = eType(elemInfo, i)) != -1; i++) {
587: if (etype == Constants.SDO_ETYPE.POLYGON_INTERIOR) {
588: rings.add(createLinearRing(gf, dim, lrs, elemInfo, i,
589: coords));
590: } else if (etype == Constants.SDO_ETYPE.POLYGON) { // need to test Clockwiseness of Ring to see if it is
591: // interior or not - (use POLYGON_INTERIOR to avoid pain)
592:
593: LinearRing ring = createLinearRing(gf, dim, lrs,
594: elemInfo, i, coords);
595:
596: if (CGAlgorithms.isCCW(ring.getCoordinates())) { // it is an Interior Hole
597: rings.add(ring);
598: } else { // it is the next Polygon! - get out of here
599: cont = false;
600: }
601: } else { // not a LinearRing - get out of here
602: cont = false;
603: }
604: }
605:
606: Polygon poly = gf.createPolygon(exteriorRing,
607: (LinearRing[]) rings.toArray(new LinearRing[rings
608: .size()]));
609:
610: return poly;
611: }
612:
613: /**
614: * Create Linear Ring for exterior/interior polygon ELEM_INFO triplets.
615: *
616: * @param gf
617: * @param elemInfo
618: * @param elemIndex
619: * @param coords
620: *
621: * @return LinearRing
622: *
623: * @throws IllegalArgumentException If circle, or curve is requested
624: */
625: private LinearRing createLinearRing(GeometryFactory gf, int dim,
626: int lrs, int[] elemInfo, int elemIndex,
627: CoordinateSequence coords) {
628:
629: int sOffset = StartingOffset(elemInfo, elemIndex);
630: int etype = eType(elemInfo, elemIndex);
631: int interpretation = interpretation(elemInfo, elemIndex);
632: int length = coords.size() * dim;
633:
634: if (!(sOffset <= length))
635: throw new IllegalArgumentException(
636: "ELEM_INFO STARTING_OFFSET " + sOffset
637: + " inconsistent with ORDINATES length "
638: + coords.size());
639: if (!(etype == Constants.SDO_ETYPE.POLYGON)
640: && !(etype == Constants.SDO_ETYPE.POLYGON_EXTERIOR)
641: && !(etype == Constants.SDO_ETYPE.POLYGON_INTERIOR)) {
642: throw new IllegalArgumentException(
643: "ETYPE "
644: + etype
645: + " inconsistent with expected POLYGON, POLYGON_EXTERIOR or POLYGON_INTERIOR");
646: }
647: if (!(interpretation == 1) && !(interpretation == 3)) {
648: return null;
649: }
650: LinearRing ring;
651:
652: int len = (dim + lrs);
653: int start = (sOffset - 1) / len;
654: int eOffset = StartingOffset(elemInfo, elemIndex + 1); // -1 for end
655: int end = (eOffset != -1) ? ((eOffset - 1) / len) : coords
656: .size();
657:
658: if (interpretation == 1) {
659: ring = gf
660: .createLinearRing(subList(gf
661: .getCoordinateSequenceFactory(), coords,
662: start, end));
663: } else { // interpretation == 3
664: // rectangle does not maintain measures
665: //
666: CoordinateSequence ext = subList(gf
667: .getCoordinateSequenceFactory(), coords, start, end);
668: Coordinate min = ext.getCoordinate(0);
669: Coordinate max = ext.getCoordinate(1);
670: ring = gf.createLinearRing(new Coordinate[] { min,
671: new Coordinate(max.x, min.y), max,
672: new Coordinate(min.x, max.y), min });
673: }
674:
675: return ring;
676: }
677:
678: /**
679: * Create LineString as encoded.
680: *
681: * @param gf
682: * @param elemInfo
683: * @param coords
684: *
685: * @return LineString
686: *
687: * @throws IllegalArgumentException If asked to create a curve
688: */
689: private LineString createLine(GeometryFactory gf, int dim, int lrs,
690: int[] elemInfo, int elemIndex, CoordinateSequence coords) {
691:
692: int sOffset = StartingOffset(elemInfo, elemIndex);
693: int etype = eType(elemInfo, elemIndex);
694: int interpretation = interpretation(elemInfo, elemIndex);
695:
696: if (etype != Constants.SDO_ETYPE.LINE)
697: return null;
698:
699: if (interpretation != 1) {
700: throw new IllegalArgumentException(
701: "ELEM_INFO INTERPRETAION "
702: + interpretation
703: + " not supported"
704: + "by JTS LineString. Straight edges"
705: + "( ELEM_INFO INTERPRETAION 1) is supported");
706: }
707:
708: int len = (dim + lrs);
709: int start = (sOffset - 1) / len;
710: int eOffset = StartingOffset(elemInfo, elemIndex + 1); // -1 for end
711: int end = (eOffset != -1) ? ((eOffset - 1) / len) : coords
712: .size();
713:
714: LineString line = gf.createLineString(subList(gf
715: .getCoordinateSequenceFactory(), coords, start, end));
716:
717: return line;
718: }
719:
720: /**
721: * Create Point as encoded.
722: *
723: * @param gf
724: * @param dim The number of Dimensions
725: * @param elemInfo
726: * @param elemIndex
727: * @param coords
728: *
729: * @return Point
730: */
731: private Point createPoint(GeometryFactory gf, int dim, int lrs,
732: int[] elemInfo, int elemIndex, CoordinateSequence coords) {
733: int sOffset = StartingOffset(elemInfo, elemIndex);
734: int etype = eType(elemInfo, elemIndex);
735: int interpretation = interpretation(elemInfo, elemIndex);
736:
737: if (!(sOffset >= 1) || !(sOffset <= coords.size()))
738: throw new IllegalArgumentException(
739: "ELEM_INFO STARTING_OFFSET " + sOffset
740: + " inconsistent with ORDINATES length "
741: + coords.size());
742: if (etype != Constants.SDO_ETYPE.POINT)
743: throw new IllegalArgumentException("ETYPE " + etype
744: + " inconsistent with expected POINT");
745: if (interpretation != 1) {
746: return null;
747: }
748:
749: int len = (dim + lrs);
750: int start = (sOffset - 1) / len;
751: int eOffset = StartingOffset(elemInfo, elemIndex + 1); // -1 for end
752:
753: Point point = null;
754: if ((sOffset == 1) && (eOffset == -1)) {
755: // Use all Coordinates
756: point = gf.createPoint(coords);
757: } else {
758: int end = (eOffset != -1) ? ((eOffset - 1) / len) : coords
759: .size();
760: point = gf
761: .createPoint(subList(gf
762: .getCoordinateSequenceFactory(), coords,
763: start, end));
764: }
765:
766: return point;
767: }
768:
769: /**
770: * Version of List.subList() that returns a CoordinateSequence.
771: *
772: * <p>
773: * Returns from start (inclusive) to end (exlusive):
774: * </p>
775: *
776: * @param factory Manages CoordinateSequences for JTS
777: * @param coords coords to sublist
778: * @param start starting offset
779: * @param end upper bound of sublist
780: *
781: * @return CoordianteSequence
782: */
783: private CoordinateSequence subList(
784: CoordinateSequenceFactory factory,
785: CoordinateSequence coords, int start, int end) {
786: if ((start == 0) && (end == coords.size())) {
787: return coords;
788: }
789:
790: if (coords instanceof List) {
791: List sublist = ((List) coords).subList(start, end);
792:
793: if (sublist instanceof CoordinateSequence) {
794: return (CoordinateSequence) sublist;
795: }
796: }
797:
798: CoordinateList list = new CoordinateList(coords
799: .toCoordinateArray());
800:
801: Coordinate[] array = new Coordinate[end - start];
802: int index = 0;
803:
804: for (Iterator i = list.subList(start, end).iterator(); i
805: .hasNext(); index++) {
806: array[index] = (Coordinate) i.next();
807: }
808:
809: return factory.create(array);
810: }
811:
812: /**
813: * ETYPE access for the elemInfo triplet indicated.
814: * <p>
815: * @see Constants.SDO_ETYPE for an indication of possible values
816: *
817: * @param elemInfo
818: * @param tripletIndex
819: * @return ETYPE for indicated triplet
820: */
821: private int eType(int[] elemInfo, int tripletIndex) {
822: if (((tripletIndex * 3) + 1) >= elemInfo.length) {
823: return -1;
824: }
825:
826: return elemInfo[(tripletIndex * 3) + 1];
827: }
828:
829: /**
830: * Accesses the interpretation value for the current geometry
831: *
832: * JTS valid interpretation is: 1 for strait edges, 3 for rectangle
833: *
834: * Other interpretations include: 2 for arcs, 4 for circles
835: *
836: * mostly useful for polygons
837: *
838: * @param elemInfo
839: * @param tripletIndex
840: * @return Starting Offset for the ordinates of the geometry
841: */
842: private int interpretation(int[] elemInfo, int tripletIndex) {
843: if (((tripletIndex * 3) + 2) >= elemInfo.length) {
844: return -1;
845: }
846:
847: return elemInfo[(tripletIndex * 3) + 2];
848: }
849:
850: /**
851: * Accesses the starting index in the ordinate array for the current geometry
852: *
853: * mostly useful for polygons
854: *
855: * @param elemInfo
856: * @param tripletIndex
857: * @return Starting Offset for the ordinates of the geometry
858: */
859: private int StartingOffset(int[] elemInfo, int tripletIndex) {
860: if (((tripletIndex * 3) + 0) >= elemInfo.length) {
861: return -1;
862: }
863:
864: return elemInfo[(tripletIndex * 3) + 0];
865: }
866:
867: /** Presents datum as an int */
868: private int asInteger(Datum datum, final int DEFAULT)
869: throws SQLException {
870: if (datum == null)
871: return DEFAULT;
872: return ((NUMBER) datum).intValue();
873: }
874:
875: /** Presents datum as a double */
876: private double asDouble(Datum datum, final double DEFAULT) {
877: if (datum == null)
878: return DEFAULT;
879: return ((NUMBER) datum).doubleValue();
880: }
881:
882: /** Presents struct as a double[] */
883: private double[] asDoubleArray(STRUCT struct, final double DEFAULT)
884: throws SQLException {
885: if (struct == null)
886: return null;
887: return asDoubleArray(struct.getOracleAttributes(), DEFAULT);
888: }
889:
890: /** Presents array as a double[] */
891: private double[] asDoubleArray(ARRAY array, final double DEFAULT)
892: throws SQLException {
893: if (array == null)
894: return null;
895: if (DEFAULT == 0)
896: return array.getDoubleArray();
897:
898: return asDoubleArray(array.getOracleArray(), DEFAULT);
899: }
900:
901: /** Presents Datum[] as a double[] */
902: private double[] asDoubleArray(Datum data[], final double DEFAULT) {
903: if (data == null)
904: return null;
905: double array[] = new double[data.length];
906: for (int i = 0; i < data.length; i++) {
907: array[i] = asDouble(data[i], DEFAULT);
908: }
909: return array;
910: }
911:
912: private int[] asIntArray(ARRAY array, int DEFAULT)
913: throws SQLException {
914: if (array == null)
915: return null;
916: if (DEFAULT == 0)
917: return array.getIntArray();
918:
919: return asIntArray(array.getOracleArray(), DEFAULT);
920: }
921:
922: /** Presents Datum[] as a int[] */
923: private int[] asIntArray(Datum data[], final int DEFAULT)
924: throws SQLException {
925: if (data == null)
926: return null;
927: int array[] = new int[data.length];
928: for (int i = 0; i < data.length; i++) {
929: array[i] = asInteger(data[i], DEFAULT);
930: }
931: return array;
932: }
933:
934: public int getDimension() {
935: return dimension;
936: }
937:
938: public void setDimension(int dimension) {
939: this.dimension = dimension;
940: }
941:
942: }
|