0001: /*
0002: * GeoTools - OpenSource mapping toolkit
0003: * http://geotools.org
0004: * (C) 2003-2006, Geotools Project Managment Committee (PMC)
0005: * (C) 2003 Refractions Research Inc.
0006: *
0007: * This library is free software; you can redistribute it and/or
0008: * modify it under the terms of the GNU Lesser General Public
0009: * License as published by the Free Software Foundation; either
0010: * version 2.1 of the License, or (at your option) any later version.
0011: *
0012: * This library is distributed in the hope that it will be useful,
0013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0015: * Lesser General Public License for more details.
0016: *
0017: * Refractions Research Inc. Can be found on the web at:
0018: * http://www.refractions.net/
0019: */
0020: package org.geotools.data.oracle.sdo;
0021:
0022: import java.lang.reflect.Array;
0023: import java.text.MessageFormat;
0024: import java.util.ArrayList;
0025: import java.util.Iterator;
0026: import java.util.LinkedList;
0027: import java.util.List;
0028: import java.util.logging.Logger;
0029:
0030: import com.vividsolutions.jts.algorithm.RobustCGAlgorithms;
0031: import com.vividsolutions.jts.geom.Coordinate;
0032: import com.vividsolutions.jts.geom.CoordinateSequence;
0033: import com.vividsolutions.jts.geom.CoordinateSequenceFactory;
0034: import com.vividsolutions.jts.geom.Envelope;
0035: import com.vividsolutions.jts.geom.Geometry;
0036: import com.vividsolutions.jts.geom.GeometryCollection;
0037: import com.vividsolutions.jts.geom.GeometryFactory;
0038: import com.vividsolutions.jts.geom.LineString;
0039: import com.vividsolutions.jts.geom.LinearRing;
0040: import com.vividsolutions.jts.geom.MultiLineString;
0041: import com.vividsolutions.jts.geom.MultiPoint;
0042: import com.vividsolutions.jts.geom.MultiPolygon;
0043: import com.vividsolutions.jts.geom.Point;
0044: import com.vividsolutions.jts.geom.Polygon;
0045:
0046: /**
0047: * Oracle Spatial Data Object utilities functions.
0048: *
0049: * <p>
0050: * Provide utility functions for working with JTS Geometries in terms Oracle
0051: * Spatial Data Objects
0052: * </p>
0053: *
0054: * <p>
0055: * This class can be used for normal JTS Geometry persistence with little fuss
0056: * and bother - please see GeometryConverter for an example of this.
0057: * <p>
0058: * With a little fuss and bother LRS information can also be handled.
0059: * Although it is very rare that JTS makes use of such things.
0060: * </p>
0061: * @see <a href="http://otn.oracle.com/pls/db10g/db10g.to_toc?pathname=appdev.101%2Fb10826%2Ftoc.htm&remark=portal+%28Unstructured+data%29">Spatial User's Guide (10.1)</a>
0062: * @author Jody Garnett, Refractions Reasearch Inc.
0063: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/unsupported/oracle-spatial/src/main/java/org/geotools/data/oracle/sdo/SDO.java $
0064: * @version CVS Version
0065: *
0066: * @see net.refractions.jspatial.jts
0067: */
0068: public final class SDO {
0069: private static final Logger LOGGER = org.geotools.util.logging.Logging
0070: .getLogger("org.geotools.data.oracle.sdo");
0071: public static final int SRID_NULL = -1;
0072:
0073: /** Used to test for Counter Clockwise or Clockwise Linear Rings */
0074: private static RobustCGAlgorithms clock = new RobustCGAlgorithms();
0075:
0076: //
0077: // Encoding Helper Functions
0078: //
0079:
0080: /**
0081: * Produce SDO_GTYPE representing provided Geometry.
0082: *
0083: * <p>
0084: * Encoding of Geometry type and dimension.
0085: * </p>
0086: *
0087: * <p>
0088: * SDO_GTYPE defined as for digits <code>[d][l][tt]</code>:
0089: * </p>
0090: *
0091: * <ul>
0092: * <li>
0093: * <b><code>d</code>:</b> number of dimensions (limited to 2,3, or 4)
0094: * </li>
0095: * <li>
0096: * <b><code>l</code>:</b> measure representation (ordinate 3 or 4) or 0 to
0097: * represent none/last
0098: * </li>
0099: * <li>
0100: * <b><code>tt:</code></b> See the TT. constants defined in this class
0101: * </li>
0102: * </ul>
0103: *
0104: * <p>
0105: * Definition provided by Oracle Spatial User�s Guide and Reference.
0106: * </p>
0107: *
0108: * @param geom
0109: *
0110: */
0111: public static int gType(Geometry geom) {
0112: int d = D(geom) * 1000;
0113: int l = L(geom) * 100;
0114: int tt = TT(geom);
0115:
0116: return d + l + tt;
0117: }
0118:
0119: /**
0120: * Return D as defined by SDO_GTYPE (either 2,3 or 4).
0121: *
0122: * <p>
0123: * For normal JTS Geometry this will be 2 or 3 depending if
0124: * geom.getCoordinate.z is Double.NaN.
0125: * </p>
0126: *
0127: * <p>
0128: * Subclasses may override as required.
0129: * </p>
0130: *
0131: * @param geom
0132: *
0133: * @return <code>3</code>
0134: */
0135: public static int D(Geometry geom) {
0136: CoordinateSequenceFactory f = geom.getFactory()
0137: .getCoordinateSequenceFactory();
0138:
0139: if (f instanceof CoordinateAccessFactory) {
0140: return ((CoordinateAccessFactory) f).getDimension();
0141: } else {
0142: //return 3;
0143: return Double.isNaN(geom.getCoordinate().z) ? 2 : 3;
0144: }
0145: }
0146:
0147: /**
0148: * Return L as defined by SDO_GTYPE (either 3,4 or 0).
0149: *
0150: * <p>
0151: * L represents support for LRS (Liniar Referencing System?). JTS Geometry
0152: * objects do not support LRS so this will be 0.
0153: * </p>
0154: *
0155: * <p>
0156: * Subclasses may override as required.
0157: * </p>
0158: *
0159: * @param geom
0160: *
0161: * @return <code>0</code>
0162: */
0163: public static int L(Geometry geom) {
0164: CoordinateSequenceFactory f = geom.getFactory()
0165: .getCoordinateSequenceFactory();
0166:
0167: if (f instanceof CoordinateAccessFactory) {
0168: return ((CoordinateAccessFactory) f).getDimension();
0169: } else {
0170: return 0;
0171: }
0172: }
0173:
0174: /**
0175: * Return TT as defined by SDO_GTYPE (represents geometry type).
0176: *
0177: * <p>
0178: * TT is used to represent the type of the JTS Geometry:
0179: * </p>
0180: * <pre><code>
0181: * <b>Value Geometry Type JTS Geometry</b>
0182: * 00 UNKNOWN_GEOMETRY null
0183: * 01 POINT Point
0184: * 02 LINE LineString
0185: * CURVE <i>not supported</i>
0186: * 03 POLYGON Polygon
0187: * 04 COLLECTION GeometryCollection
0188: * 05 MULTIPOINT MultiPoint
0189: * 06 MULTILINE MultiLineString
0190: * MULTICURVE <i>not supported</i>
0191: * 07 MULTIPOLYGON MultiPolygon
0192: * </code></pre>
0193: *
0194: * @param geom
0195: *
0196: * @return <code>TT</code> representing <code>geom</code>
0197: *
0198: * @throws IllegalArgumentException If SDO_GTYPE can not be represetned by
0199: * JTS
0200: */
0201: public static int TT(Geometry geom) {
0202: if (geom == null) {
0203: return TT.UNKNOWN; // UNKNOWN
0204: } else if (geom instanceof Point) {
0205: return TT.POINT;
0206: } else if (geom instanceof LineString) {
0207: return TT.LINE;
0208: } else if (geom instanceof Polygon) {
0209: return TT.POLYGON;
0210: } else if (geom instanceof MultiPoint) {
0211: return TT.MULTIPOINT;
0212: } else if (geom instanceof MultiLineString) {
0213: return TT.MULTILINE;
0214: } else if (geom instanceof MultiPolygon) {
0215: return TT.MULTIPOLYGON;
0216: } else if (geom instanceof GeometryCollection) {
0217: return TT.COLLECTION;
0218: }
0219:
0220: throw new IllegalArgumentException(
0221: "Cannot encode JTS "
0222: + geom.getGeometryType()
0223: + " as SDO_GTYPE "
0224: + "(Limitied to Point, Line, Polygon, GeometryCollection, MultiPoint,"
0225: + " MultiLineString and MultiPolygon)");
0226: }
0227:
0228: /**
0229: * Returns geometry SRID.
0230: *
0231: * <p>
0232: * SRID code representing Spatial Reference System. SRID number used must
0233: * be defined in the Oracle MDSYS.CS_SRS table.
0234: * </p>
0235: *
0236: * <p>
0237: * <code>SRID_NULL</code>represents lack of coordinate system.
0238: * </p>
0239: *
0240: * @param geom Geometry SRID Number (JTS14 uses GeometryFactor.getSRID() )
0241: *
0242: * @return <code>SRID</code> for provided geom
0243: */
0244: public static int SRID(Geometry geom) {
0245: return geom.getSRID();
0246: }
0247:
0248: /**
0249: * Return SDO_POINT_TYPE for geometry
0250: *
0251: * <p>
0252: * Will return non null for Point objects. <code>null</code> is returned
0253: * for all non point objects.
0254: * </p>
0255: *
0256: * <p>
0257: * You cannot use this with LRS Coordiantes
0258: * </p>
0259: *
0260: * <p>
0261: * Subclasses may wish to repress this method and force Points to be
0262: * represented using SDO_ORDINATES.
0263: * </p>
0264: *
0265: * @param geom
0266: *
0267: */
0268: public static double[] point(Geometry geom) {
0269: if (geom instanceof Point && (L(geom) == 0)) {
0270: Point point = (Point) geom;
0271: Coordinate coord = point.getCoordinate();
0272:
0273: return new double[] { coord.x, coord.y, coord.z };
0274: }
0275:
0276: // SDO_POINT_TYPE only used for non LRS Points
0277: return null;
0278: }
0279:
0280: /**
0281: * Return SDO_ELEM_INFO array for geometry
0282: *
0283: * <p>
0284: * Describes how to use Ordinates to represent Geometry.
0285: * </p>
0286: * <pre><code><b>
0287: * # Name Meaning</b>
0288: * 0 SDO_STARTING_OFFSET Offsets start at one
0289: * 1 SDO_ETYPE Describes how ordinates are ordered
0290: * 2 SDO_INTERPRETATION SDO_ETYPE: 4, 1005, or 2005
0291: * Number of triplets involved in compound geometry
0292: *
0293: * SDO_ETYPE: 1, 2, 1003, or 2003
0294: * Describes ordering of ordinates in geometry
0295: * </code></pre>
0296: *
0297: * <p>
0298: * For compound elements (SDO_ETYPE values 4 and 5) the last element of one
0299: * is the first element of the next.
0300: * </p>
0301: *
0302: * @param geom Geometry being represented
0303: *
0304: * @return Descriptionof Ordinates representation
0305: */
0306: public static int[] elemInfo(Geometry geom) {
0307: return elemInfo(geom, gType(geom));
0308: }
0309:
0310: public static int[] elemInfo(Geometry geom, final int GTYPE) {
0311: List list = new LinkedList();
0312: elemInfo(list, geom, 1, GTYPE);
0313:
0314: return intArray(list);
0315: }
0316:
0317: /**
0318: * Add to SDO_ELEM_INFO list for geometry and GTYPE.
0319: *
0320: * @param elemInfoList List used to gather SDO_ELEM_INFO
0321: * @param geom Geometry to encode
0322: * @param STARTING_OFFSET Starting offset in SDO_ORDINATES
0323: * @param GTYPE Encoding of dimension, measures and geometry type
0324: *
0325: * @throws IllegalArgumentException If geom cannot be encoded by ElemInfo
0326: */
0327: private static void elemInfo(List elemInfoList, Geometry geom,
0328: final int STARTING_OFFSET, final int GTYPE) {
0329: final int tt = TT(geom);
0330:
0331: switch (tt) {
0332: case TT.POINT:
0333: addElemInfo(elemInfoList, (Point) geom, STARTING_OFFSET);
0334:
0335: return;
0336:
0337: case TT.LINE:
0338: addElemInfo(elemInfoList, (LineString) geom,
0339: STARTING_OFFSET);
0340:
0341: return;
0342:
0343: case TT.POLYGON:
0344: addElemInfo(elemInfoList, (Polygon) geom, STARTING_OFFSET,
0345: GTYPE);
0346:
0347: return;
0348:
0349: case TT.MULTIPOINT:
0350: addElemInfo(elemInfoList, (MultiPoint) geom,
0351: STARTING_OFFSET);
0352:
0353: return;
0354:
0355: case TT.MULTILINE:
0356: addElemInfo(elemInfoList, (MultiLineString) geom,
0357: STARTING_OFFSET, GTYPE);
0358:
0359: return;
0360:
0361: case TT.MULTIPOLYGON:
0362: addElemInfo(elemInfoList, (MultiPolygon) geom,
0363: STARTING_OFFSET, GTYPE);
0364:
0365: return;
0366:
0367: case TT.COLLECTION:
0368: addElemInfo(elemInfoList, (GeometryCollection) geom,
0369: STARTING_OFFSET, GTYPE);
0370:
0371: return;
0372: }
0373:
0374: throw new IllegalArgumentException(
0375: "Cannot encode JTS "
0376: + geom.getGeometryType()
0377: + " as SDO_ELEM_INFO "
0378: + "(Limitied to Point, Line, Polygon, GeometryCollection, MultiPoint,"
0379: + " MultiLineString and MultiPolygon)");
0380: }
0381:
0382: /**
0383: * Not often called as POINT_TYPE prefered over ELEMINFO & ORDINATES.
0384: *
0385: * <p>
0386: * This method is included to allow for multigeometry encoding.
0387: * </p>
0388: *
0389: * @param elemInfoList List containing ELEM_INFO array
0390: * @param point Point to encode
0391: * @param STARTING_OFFSET Starting offset in SDO_ORDINATE array
0392: */
0393: private static void addElemInfo(List elemInfoList, Point point,
0394: final int STARTING_OFFSET) {
0395: addInt(elemInfoList, STARTING_OFFSET);
0396: addInt(elemInfoList, ETYPE.POINT);
0397: addInt(elemInfoList, 1); // INTERPRETATION single point
0398: }
0399:
0400: private static void addElemInfo(List elemInfoList, LineString line,
0401: final int STARTING_OFFSET) {
0402: addInt(elemInfoList, STARTING_OFFSET);
0403: addInt(elemInfoList, ETYPE.LINE);
0404: addInt(elemInfoList, 1); // INTERPRETATION straight edges
0405: }
0406:
0407: private static void addElemInfo(List elemInfoList, Polygon polygon,
0408: final int STARTING_OFFSET, final int GTYPE) {
0409: final int HOLES = polygon.getNumInteriorRing();
0410:
0411: if (HOLES == 0) {
0412: addInt(elemInfoList, STARTING_OFFSET);
0413: addInt(elemInfoList, elemInfoEType(polygon));
0414: addInt(elemInfoList, elemInfoInterpretation(polygon,
0415: ETYPE.POLYGON_EXTERIOR));
0416:
0417: return;
0418: }
0419:
0420: int LEN = D(GTYPE) + L(GTYPE);
0421: int offset = STARTING_OFFSET;
0422: LineString ring;
0423:
0424: ring = polygon.getExteriorRing();
0425: addInt(elemInfoList, offset);
0426: addInt(elemInfoList, elemInfoEType(polygon));
0427: addInt(elemInfoList, elemInfoInterpretation(polygon,
0428: ETYPE.POLYGON_EXTERIOR));
0429: offset += (ring.getNumPoints() * LEN);
0430:
0431: for (int i = 1; i <= HOLES; i++) {
0432: ring = polygon.getInteriorRingN(i - 1);
0433: addInt(elemInfoList, offset);
0434: addInt(elemInfoList, ETYPE.POLYGON_INTERIOR);
0435: addInt(elemInfoList, elemInfoInterpretation(ring,
0436: ETYPE.POLYGON_INTERIOR));
0437: offset += (ring.getNumPoints() * LEN);
0438: }
0439: }
0440:
0441: private static void addElemInfo(List elemInfoList,
0442: MultiPoint points, final int STARTING_OFFSET) {
0443: addInt(elemInfoList, STARTING_OFFSET);
0444: addInt(elemInfoList, ETYPE.POINT);
0445: addInt(elemInfoList,
0446: elemInfoInterpretation(points, ETYPE.POINT));
0447: }
0448:
0449: private static void addElemInfo(List elemInfoList,
0450: MultiLineString lines, final int STARTING_OFFSET,
0451: final int GTYPE) {
0452: LineString line;
0453: int offset = STARTING_OFFSET;
0454:
0455: int LEN = D(GTYPE) + L(GTYPE);
0456:
0457: for (int i = 0; i < lines.getNumGeometries(); i++) {
0458: line = (LineString) lines.getGeometryN(i);
0459: addElemInfo(elemInfoList, line, offset);
0460: offset += (line.getNumPoints() * LEN);
0461: }
0462: }
0463:
0464: private static void addElemInfo(List elemInfoList,
0465: MultiPolygon polys, final int STARTING_OFFSET,
0466: final int GTYPE) {
0467: Polygon poly;
0468: int offset = STARTING_OFFSET;
0469:
0470: int LEN = D(GTYPE) + L(GTYPE);
0471:
0472: for (int i = 0; i < polys.getNumGeometries(); i++) {
0473: poly = (Polygon) polys.getGeometryN(i);
0474: addElemInfo(elemInfoList, poly, offset, GTYPE);
0475: if (isRectangle(poly)) {
0476: offset += (2 * LEN);
0477: } else {
0478: offset += (poly.getNumPoints() * LEN);
0479: }
0480: }
0481: }
0482:
0483: private static void addElemInfo(List elemInfoList,
0484: GeometryCollection geoms, final int STARTING_OFFSET,
0485: final int GTYPE) {
0486: Geometry geom;
0487: int offset = STARTING_OFFSET;
0488: int LEN = D(GTYPE) + L(GTYPE);
0489:
0490: for (int i = 0; i < geoms.getNumGeometries(); i++) {
0491: geom = geoms.getGeometryN(i);
0492: elemInfo(elemInfoList, geom, offset, GTYPE);
0493: if (geom instanceof Polygon && isRectangle((Polygon) geom)) {
0494: offset += (2 * LEN);
0495: } else {
0496: offset += (geom.getNumPoints() * LEN);
0497: }
0498: }
0499: }
0500:
0501: /**
0502: * Adds contents of array to the list as Interger objects
0503: *
0504: * @param list List to append the contents of array to
0505: * @param array Array of ints to append
0506: */
0507: private static void addInts(List list, int[] array) {
0508: for (int i = 0; i < array.length; i++) {
0509: list.add(new Integer(array[i]));
0510: }
0511: }
0512:
0513: private static void addInt(List list, int i) {
0514: list.add(new Integer(i));
0515: }
0516:
0517: /**
0518: * Converts provied list to an int array
0519: *
0520: * @param list List to cast to an array type
0521: * @return array of ints
0522: */
0523: private static int[] intArray(List list) {
0524: int[] array = new int[list.size()];
0525: int offset = 0;
0526:
0527: for (Iterator i = list.iterator(); i.hasNext(); offset++) {
0528: array[offset] = ((Number) i.next()).intValue();
0529: }
0530:
0531: return array;
0532: }
0533:
0534: /**
0535: * Starting offset used by SDO_ORDINATES as stored in the SDO_ELEM_INFO
0536: * array.
0537: *
0538: * <p>
0539: * Starting offsets start from one.
0540: * </p>
0541: *
0542: * <p>
0543: * Describes ordinates as part of <code>SDO_ELEM_INFO</code> data type.
0544: * </p>
0545: *
0546: * @param geom
0547: *
0548: * @return <code>1</code> for non nested <code>geom</code>
0549: */
0550: public static int elemInfoStartingOffset(Geometry geom) {
0551: return 1;
0552: }
0553:
0554: /**
0555: * Produce <code>SDO_ETYPE</code> for geometry description as stored in the
0556: * <code>SDO_ELEM_INFO</code>.
0557: *
0558: * <p>
0559: * Describes how Ordinates are ordered:
0560: * </p>
0561: * <pre><code><b>
0562: * Value Elements Meaning</b>
0563: * 0 Custom Geometry (like spline)
0564: * 1 simple Point (or Points)
0565: * 2 simple Line (or Lines)
0566: * 3 polygon ring of unknown order (discouraged update to 1003 or 2003)
0567: * 1003 simple polygon ring (1 exterior counterclockwise order)
0568: * 2003 simple polygon ring (2 interior clockwise order)
0569: * 4 compound series defines a linestring
0570: * 5 compound series defines a polygon ring of unknown order (discouraged)
0571: * 1005 compound series defines exterior polygon ring (counterclockwise order)
0572: * 2005 compound series defines interior polygon ring (clockwise order)
0573: * </code></pre>
0574: *
0575: * <p>
0576: * Keep in mind:
0577: * </p>
0578: *
0579: * <ul>
0580: * <li>
0581: * <code>simple</code> elements are defined by a single triplet entry in
0582: * the <code>SDO_ELEM_INFO</code> array
0583: * </li>
0584: * <li>
0585: * <code>compound</code> elements are defined by a header triplet, and a
0586: * series of triplets for the parts. Elements in a compound element share
0587: * first and last points.
0588: * </li>
0589: * <li>
0590: * We are not allowed to mix 1 digit and 4 digit values for ETYPE and GTYPE
0591: * in a single geometry
0592: * </li>
0593: * </ul>
0594: *
0595: * <p>
0596: * This whole mess describes ordinates as part of
0597: * <code>SDO_ELEM_INFO</code> array. data type.
0598: * </p>
0599: *
0600: * @param geom Geometry being represented
0601: *
0602: * @return Descriptionof Ordinates representation
0603: *
0604: * @throws IllegalArgumentException
0605: */
0606: protected static int elemInfoEType(Geometry geom) {
0607: switch (TT(geom)) {
0608: case TT.UNKNOWN:
0609: return ETYPE.CUSTOM;
0610:
0611: case TT.POINT:
0612: return ETYPE.POINT;
0613:
0614: case TT.LINE:
0615: return ETYPE.LINE;
0616:
0617: case TT.POLYGON:
0618: return isExterior((Polygon) geom) ? ETYPE.POLYGON_EXTERIOR // cc order
0619: : ETYPE.POLYGON_INTERIOR; // ccw order
0620:
0621: default:
0622:
0623: // should never happen!
0624: throw new IllegalArgumentException(
0625: "Unknown encoding of SDO_GTYPE");
0626: }
0627: }
0628:
0629: /**
0630: * Produce <code>SDO_INTERPRETATION</code> for geometry description as
0631: * stored in the <code>SDO_ELEM_INFO</code>.
0632: *
0633: * <p>
0634: * Describes ordinates as part of <code>SDO_ELEM_INFO</code> array.
0635: * </p>
0636: *
0637: * <ul>
0638: * <li>
0639: * <b><code>compound</code> element:</b>(SDO_ETYPE 4, 1005, or 2005)<br>
0640: * Number of subsequent triplets are part of compound element
0641: * </li>
0642: * <li>
0643: * <b><code>simple</code> element:</b>(SDE_ELEM 1, 2, 1003, or 2003)<br>
0644: * Code defines how ordinates are interpreted (lines or curves)
0645: * </li>
0646: * </ul>
0647: *
0648: * <p>
0649: * SDO_INTERPRETAION Values: (from Table 2-2 in reference docs)
0650: * </p>
0651: * <pre><code><b>
0652: * SDO_ETYPE Value Meaning</b>
0653: * 0 anything Custom Geometry
0654: * 1 1 Point
0655: * 1 N > 1 N points
0656: * 2 1 LineString of straight lines
0657: * 2 2 LineString connected by circ arcs (start,any,end pt)
0658: * 1003/2003 1 Polygon Edged with straight lines
0659: * 1003/2003 2 Polygon Edged with circ arcs (start, any, end pt)
0660: * 1003/2003 3 Non SRID rectangle defined by (bottomleft,topright pt)
0661: * 1003/2003 4 Circle defined by three points on circumference
0662: * 4 N > 1 Compound Line String made of N (ETYPE=2) lines and arcs
0663: * 1005/2005 N > 1 Polygon defined by (ETYPE=2) lines and arcs
0664: *
0665: *
0666: * </code></pre>
0667: *
0668: * <p>
0669: * When playing with circular arcs (SDO_INTERPRETATION==2) arcs are defined
0670: * by three points. A start point, any point on the arc and the end point.
0671: * The last point of an arc is the start point of the next. When used to
0672: * describe a polygon (SDO_ETYPE==1003 or 2003) the first and last point
0673: * must be the same.
0674: * </p>
0675: *
0676: * @param geom
0677: *
0678: */
0679: public static int elemInfoInterpretation(Geometry geom) {
0680: return elemInfoInterpretation(geom, elemInfoEType(geom));
0681: }
0682:
0683: /**
0684: * Allows specification of <code>INTERPRETATION</code> used to interpret
0685: * <code>geom</code>.
0686: * <p>
0687: * Provides the INTERPRETATION value for the ELEM_INFO triplet
0688: * of (STARTING_OFFSET, ETYPE, INTERPRETATION).
0689: * </p>
0690: * @param geom Geometry to encode
0691: * @param etype ETYPE value requiring an INTERPREATION
0692: *
0693: * @return INTERPRETATION ELEM_INFO entry for geom given etype
0694: *
0695: * @throws IllegalArgumentException If asked to encode a curve
0696: */
0697: public static int elemInfoInterpretation(Geometry geom,
0698: final int etype) {
0699: switch (etype) {
0700: case ETYPE.CUSTOM: // customize for your own Geometries
0701: break;
0702:
0703: case ETYPE.POINT:
0704:
0705: if (geom instanceof Point) {
0706: return 1;
0707: }
0708:
0709: if (geom instanceof MultiPoint) {
0710: return ((MultiPoint) geom).getNumGeometries();
0711: }
0712:
0713: break;
0714:
0715: case ETYPE.LINE:
0716:
0717: if (isCurve((LineString) geom)) {
0718: return 2;
0719: }
0720:
0721: return 1;
0722:
0723: case ETYPE.POLYGON:
0724: case ETYPE.POLYGON_EXTERIOR:
0725: case ETYPE.POLYGON_INTERIOR:
0726:
0727: if (geom instanceof Polygon) {
0728: Polygon polygon = (Polygon) geom;
0729:
0730: if (isCurve(polygon)) {
0731: return 2;
0732: }
0733:
0734: if (isRectangle(polygon)) {
0735: return 3;
0736: }
0737:
0738: if (isCircle(polygon)) {
0739: return 4;
0740: }
0741: }
0742:
0743: return 1;
0744:
0745: case ETYPE.COMPOUND:
0746: throw new IllegalArgumentException(
0747: "JTS LineStrings are not composed of curves and lines.");
0748:
0749: case ETYPE.COMPOUND_POLYGON:
0750: case ETYPE.COMPOUND_POLYGON_INTERIOR:
0751: case ETYPE.COMPOUND_POLYGON_EXTERIOR:
0752: throw new IllegalArgumentException(
0753: "JTS Polygons are not composed of curves and lines.");
0754: }
0755:
0756: throw new IllegalArgumentException(
0757: "Cannot encode JTS "
0758: + geom.getGeometryType()
0759: + " as "
0760: + "SDO_INTERPRETATION (Limitied to Point, Line, Polygon, "
0761: + "GeometryCollection, MultiPoint, MultiLineString and MultiPolygon)");
0762: }
0763:
0764: /**
0765: * Produce <code>SDO_ORDINATES</code> for geometry.
0766: *
0767: * <p>
0768: * Please see SDO_ETYPE, SDO_INTERPRETATION and SDO_GTYPE for description
0769: * of how these ordinates are to be interpreted.
0770: * </p>
0771: *
0772: * <p>
0773: * Ordinates are ordered by Dimension are non null:
0774: * </p>
0775: *
0776: * <ul>
0777: * <li>
0778: * <p>
0779: * Two Dimensional:
0780: * </p>
0781: * {x1,y1,x2,y2,...}
0782: * </li>
0783: * <li>
0784: * <p>
0785: * Three Dimensional:
0786: * </p>
0787: * {x1,y1,z1,x2,y2,z2,...}
0788: * </li>
0789: * </ul>
0790: *
0791: * <p>
0792: * Spatial will siliently detect and ignore the following:
0793: * </p>
0794: *
0795: * <ul>
0796: * <li>
0797: * d001 point/d005 multipoint elements that are not SDO_ETYPE==1 points
0798: * </li>
0799: * <li>
0800: * d002 lines or curve/d006 multilines or multicurve elements that are not
0801: * SDO_ETYPE==2 lines or SDO_ETYPE==4 arcs
0802: * </li>
0803: * <li>
0804: * d003 polygon/d007 multipolygon elements that are not SDO_ETYPE==3
0805: * unordered polygon lines or SDO_ETYPE==5 unorderd compound polygon ring
0806: * are ignored
0807: * </li>
0808: * </ul>
0809: *
0810: * <p>
0811: * While Oracle is silient on these errors - all other errors will not be
0812: * detected!
0813: * </p>
0814: *
0815: * @param geom
0816: *
0817: */
0818: public static double[] ordinates(Geometry geom) {
0819: List list = new ArrayList();
0820:
0821: coordinates(list, geom);
0822:
0823: return ordinates(list, geom);
0824: }
0825:
0826: public static CoordinateSequence getCS(Geometry geom) {
0827: CoordinateSequence cs = null;
0828: switch (TT(geom)) {
0829: case TT.UNKNOWN:
0830: break; // extend with your own custom types
0831:
0832: case TT.POINT:
0833: //addCoordinates(list, (Point) geom);
0834:
0835: return cs;
0836:
0837: case TT.LINE:
0838: cs = getLineStringCS((LineString) geom);
0839:
0840: return cs;
0841:
0842: case TT.POLYGON:
0843: //addCoordinates(list, (Polygon) geom);
0844:
0845: return cs;
0846:
0847: case TT.COLLECTION:
0848: //addCoordinates(list, (GeometryCollection) geom);
0849:
0850: return cs;
0851:
0852: case TT.MULTIPOINT:
0853: //addCoordinates(list, (MultiPoint) geom);
0854:
0855: return cs;
0856:
0857: case TT.MULTILINE:
0858: //addCoordinates(list, (MultiLineString) geom);
0859:
0860: return cs;
0861:
0862: case TT.MULTIPOLYGON:
0863: //addCoordinates(list, (MultiPolygon) geom);
0864:
0865: return cs;
0866: }
0867:
0868: throw new IllegalArgumentException(
0869: "Cannot encode JTS "
0870: + geom.getGeometryType()
0871: + " as "
0872: + "SDO_ORDINATRES (Limitied to Point, Line, Polygon, "
0873: + "GeometryCollection, MultiPoint, MultiLineString and MultiPolygon)");
0874:
0875: }
0876:
0877: /**
0878: * getLineStringCS purpose.
0879: * <p>
0880: * Description ...
0881: * </p>
0882: * @param string
0883: */
0884: private static CoordinateSequence getLineStringCS(LineString ls) {
0885: if (ls.getCoordinateSequence() instanceof CoordinateAccess) {
0886: CoordinateAccess ca = (CoordinateAccess) ls
0887: .getCoordinateSequence();
0888: return ca;
0889: } else
0890: return null;
0891: }
0892:
0893: /**
0894: * Encode Geometry as described by GTYPE and ELEM_INFO
0895: * <p>
0896: * CoordinateSequence & CoordinateAccess wil be used to determine the
0897: * dimension, and the number of ordinates added.
0898: * </p>
0899: * @param list Flat list of Double
0900: * @param geom Geometry
0901: *
0902: * @throws IllegalArgumentException If geometry cannot be encoded
0903: */
0904: public static void coordinates(List list, Geometry geom) {
0905: switch (TT(geom)) {
0906: case TT.UNKNOWN:
0907: break; // extend with your own custom types
0908:
0909: case TT.POINT:
0910: addCoordinates(list, (Point) geom);
0911:
0912: return;
0913:
0914: case TT.LINE:
0915: addCoordinates(list, (LineString) geom);
0916:
0917: return;
0918:
0919: case TT.POLYGON:
0920: addCoordinates(list, (Polygon) geom);
0921:
0922: return;
0923:
0924: case TT.COLLECTION:
0925: addCoordinates(list, (GeometryCollection) geom);
0926:
0927: return;
0928:
0929: case TT.MULTIPOINT:
0930: addCoordinates(list, (MultiPoint) geom);
0931:
0932: return;
0933:
0934: case TT.MULTILINE:
0935: addCoordinates(list, (MultiLineString) geom);
0936:
0937: return;
0938:
0939: case TT.MULTIPOLYGON:
0940: addCoordinates(list, (MultiPolygon) geom);
0941:
0942: return;
0943: }
0944:
0945: throw new IllegalArgumentException(
0946: "Cannot encode JTS "
0947: + geom.getGeometryType()
0948: + " as "
0949: + "SDO_ORDINATRES (Limitied to Point, Line, Polygon, "
0950: + "GeometryCollection, MultiPoint, MultiLineString and MultiPolygon)");
0951: }
0952:
0953: /**
0954: * Adds a double array to list.
0955: *
0956: * <p>
0957: * The double array will contain all the ordinates in the Coordiante
0958: * sequence.
0959: * </p>
0960: *
0961: * @param list
0962: * @param sequence
0963: */
0964: private static void addCoordinates(List list,
0965: CoordinateSequence sequence) {
0966: double[] ords;
0967:
0968: if (sequence instanceof CoordinateAccess) {
0969: CoordinateAccess access = (CoordinateAccess) sequence;
0970:
0971: for (int i = 0; i < access.size(); i++) {
0972: list.add(ordinateArray(access, i));
0973: }
0974: } else {
0975: for (int i = 0; i < sequence.size(); i++) {
0976: list.add(ordinateArray(sequence.getCoordinate(i)));
0977: }
0978: }
0979: }
0980:
0981: private static double[] ordinateArray(Coordinate coord) {
0982: return new double[] { coord.x, coord.y, coord.z };
0983: }
0984:
0985: private static double[] ordinateArray(CoordinateAccess access,
0986: int index) {
0987: final int D = access.getDimension();
0988: final int L = access.getNumAttributes();
0989: final int LEN = D + L;
0990: double[] ords = new double[LEN];
0991:
0992: for (int i = 0; i < LEN; i++) {
0993: ords[i] = access.getOrdinate(index, i);
0994: }
0995:
0996: return ords;
0997: }
0998:
0999: /**
1000: *
1001: * ordinateArray purpose.
1002: * <p>
1003: * Description ...
1004: * </p>
1005: * @param access
1006: * @param index
1007: */
1008: private static double[] doubleOrdinateArray(
1009: CoordinateAccess access, int index) {
1010: final int D = access.getDimension();
1011: final int L = access.getNumAttributes();
1012: final int LEN = D + L;
1013: double[] ords = new double[LEN];
1014:
1015: for (int i = 0; i < LEN; i++) {
1016: ords[i] = access.getOrdinate(index, i);
1017: }
1018:
1019: return ords;
1020: }
1021:
1022: /**
1023: * Used with ELEM_INFO <code>( 1, 1, 1)</code>
1024: *
1025: * @param list List to add coordiantes to
1026: * @param point Point to be encoded
1027: */
1028: private static void addCoordinates(List list, Point point) {
1029: addCoordinates(list, point.getCoordinateSequence());
1030: }
1031:
1032: /**
1033: * Used with ELEM_INFO <code>(1, 2, 1)</code>
1034: *
1035: * @param list List to add coordiantes to
1036: * @param line LineString to be encoded
1037: */
1038: private static void addCoordinates(List list, LineString line) {
1039: addCoordinates(list, line.getCoordinateSequence());
1040: }
1041:
1042: /**
1043: * Used to addCoordinates based on polygon encoding.
1044: *
1045: * <p>
1046: * Elem Info Interpretations supported:
1047: *
1048: * <ul>
1049: * <li>
1050: * 3: Rectangle
1051: * </li>
1052: * <li>
1053: * 1: Standard (supports holes)
1054: * </li>
1055: * </ul>
1056: * </p>
1057: *
1058: * @param list List to add coordiantes to
1059: * @param polygon Polygon to be encoded
1060: */
1061: private static void addCoordinates(List list, Polygon polygon) {
1062: switch (elemInfoInterpretation(polygon)) {
1063: case 4: // circle not supported
1064: break;
1065:
1066: case 3:
1067: addCoordinatesInterpretation3(list, polygon);
1068: break;
1069:
1070: case 2: // curve not suppported
1071: break;
1072:
1073: case 1:
1074: addCoordinatesInterpretation1(list, polygon);
1075:
1076: break;
1077: }
1078: }
1079:
1080: /**
1081: * Rectangle ordinates for polygon.
1082: *
1083: * <p>
1084: * You should in sure that the provided <code>polygon</code> is a rectangle
1085: * using isRectangle( Polygon )
1086: * </p>
1087: *
1088: * <p></p>
1089: *
1090: * @param list List to add coordiantes to
1091: * @param polygon Polygon to be encoded
1092: */
1093: private static void addCoordinatesInterpretation3(List list,
1094: Polygon poly) {
1095: Envelope e = poly.getEnvelopeInternal();
1096: list.add(new double[] { e.getMinX(), e.getMinY() });
1097: list.add(new double[] { e.getMaxX(), e.getMaxY() });
1098: }
1099:
1100: /**
1101: * Add ordinates for polygon - with hole support.
1102: *
1103: * <p>
1104: * Ensure ordinates are added in the correct orientation as External or
1105: * Internal polygons.
1106: * </p>
1107: *
1108: * @param list List to add coordiantes to
1109: * @param polygon Polygon to be encoded
1110: */
1111: private static void addCoordinatesInterpretation1(List list,
1112: Polygon polygon) {
1113: int holes = polygon.getNumInteriorRing();
1114:
1115: addCoordinates(list, counterClockWise(polygon.getFactory()
1116: .getCoordinateSequenceFactory(), polygon
1117: .getExteriorRing().getCoordinateSequence()));
1118:
1119: for (int i = 0; i < holes; i++) {
1120: addCoordinates(list, clockWise(polygon.getFactory()
1121: .getCoordinateSequenceFactory(), polygon
1122: .getInteriorRingN(i).getCoordinateSequence()));
1123: }
1124: }
1125:
1126: private static void addCoordinates(List list, MultiPoint points) {
1127: for (int i = 0; i < points.getNumGeometries(); i++) {
1128: addCoordinates(list, (Point) points.getGeometryN(i));
1129: }
1130: }
1131:
1132: private static void addCoordinates(List list, MultiLineString lines) {
1133: for (int i = 0; i < lines.getNumGeometries(); i++) {
1134: addCoordinates(list, (LineString) lines.getGeometryN(i));
1135: }
1136: }
1137:
1138: private static void addCoordinates(List list, MultiPolygon polys) {
1139: for (int i = 0; i < polys.getNumGeometries(); i++) {
1140: addCoordinates(list, (Polygon) polys.getGeometryN(i));
1141: }
1142: }
1143:
1144: private static void addCoordinates(List list,
1145: GeometryCollection geoms) {
1146: Geometry geom;
1147:
1148: for (int i = 0; i < geoms.getNumGeometries(); i++) {
1149: geom = geoms.getGeometryN(i);
1150:
1151: if (geom instanceof Point) {
1152: addCoordinates(list, (Point) geom);
1153: } else if (geom instanceof LineString) {
1154: addCoordinates(list, (LineString) geom);
1155: } else if (geom instanceof Polygon) {
1156: addCoordinates(list, (Polygon) geom);
1157: } else if (geom instanceof MultiPoint) {
1158: addCoordinates(list, (MultiPoint) geom);
1159: } else if (geom instanceof MultiLineString) {
1160: addCoordinates(list, (MultiLineString) geom);
1161: } else if (geom instanceof MultiPolygon) {
1162: addCoordinates(list, (MultiPolygon) geom);
1163: } else if (geom instanceof GeometryCollection) {
1164: addCoordinates(list, (GeometryCollection) geom);
1165: }
1166: }
1167: }
1168:
1169: /**
1170: * Package up array of requested ordinate, regardless of geometry
1171: *
1172: * <p>
1173: * Example numbering: for (x y g m) dimension==2, measure==2
1174: * </p>
1175: *
1176: * <ul>
1177: * <li>
1178: * 0: x ordinate array
1179: * </li>
1180: * <li>
1181: * 1: y ordinate array
1182: * </li>
1183: * <li>
1184: * 2: g ordinate array
1185: * </li>
1186: * <li>
1187: * 3: m ordinate array
1188: * </li>
1189: * </ul>
1190: *
1191: *
1192: * @param coords
1193: * @param ordinate
1194: *
1195: */
1196: public static double[] ordinateArray(CoordinateSequence coords,
1197: int ordinate) {
1198: if (coords instanceof CoordinateAccess) {
1199: CoordinateAccess access = (CoordinateAccess) coords;
1200:
1201: return access.toOrdinateArray(ordinate);
1202: } else {
1203: final int LENGTH = coords.size();
1204: Coordinate c;
1205: double[] array = new double[LENGTH];
1206:
1207: if (ordinate == 0) {
1208: for (int i = 0; i < LENGTH; i++) {
1209: c = coords.getCoordinate(i);
1210: array[i] = (c != null) ? c.x : Double.NaN;
1211: }
1212: } else if (ordinate == 1) {
1213: for (int i = 0; i < LENGTH; i++) {
1214: c = coords.getCoordinate(i);
1215: array[i] = (c != null) ? c.y : Double.NaN;
1216: }
1217: } else if (ordinate == 2) {
1218: for (int i = 0; i < LENGTH; i++) {
1219: c = coords.getCoordinate(i);
1220: array[i] = (c != null) ? c.z : Double.NaN;
1221: }
1222: } else {
1223: // default to NaN
1224: for (int i = 0; i < LENGTH; i++) {
1225: array[i] = Double.NaN;
1226: }
1227: }
1228:
1229: return array;
1230: }
1231: }
1232:
1233: /**
1234: * Ordinate access.
1235: *
1236: * <p>
1237: * CoordianteAccess is required for additional ordinates.
1238: * </p>
1239: *
1240: * <p>
1241: * Ordinate limitied to:
1242: * </p>
1243: *
1244: * <ul>
1245: * <li>
1246: * 0: x ordinate array
1247: * </li>
1248: * <li>
1249: * 1: y ordinate array
1250: * </li>
1251: * <li>
1252: * 2: z ordinate array
1253: * </li>
1254: * <li>
1255: * 3: empty ordinate array
1256: * </li>
1257: * </ul>
1258: *
1259: *
1260: * @param array
1261: * @param ordinate
1262: *
1263: */
1264: public static double[] ordinateArray(Coordinate[] array,
1265: int ordinate) {
1266: if (array == null) {
1267: return null;
1268: }
1269:
1270: final int LENGTH = array.length;
1271: double[] ords = new double[LENGTH];
1272: Coordinate c;
1273:
1274: if (ordinate == 0) {
1275: for (int i = 0; i < LENGTH; i++) {
1276: c = array[i];
1277: ords[i] = (c != null) ? c.x : Double.NaN;
1278: }
1279: } else if (ordinate == 1) {
1280: for (int i = 0; i < LENGTH; i++) {
1281: c = array[i];
1282: ords[i] = (c != null) ? c.y : Double.NaN;
1283: }
1284: } else if (ordinate == 2) {
1285: for (int i = 0; i < LENGTH; i++) {
1286: c = array[i];
1287: ords[i] = (c != null) ? c.z : Double.NaN;
1288: }
1289: } else {
1290: // default to NaN
1291: for (int i = 0; i < LENGTH; i++) {
1292: ords[i] = Double.NaN;
1293: }
1294: }
1295:
1296: return ords;
1297: }
1298:
1299: public static double[] ordinateArray(List list, int ordinate) {
1300: if (list == null) {
1301: return null;
1302: }
1303:
1304: final int LENGTH = list.size();
1305: double[] ords = new double[LENGTH];
1306: Coordinate c;
1307:
1308: if (ordinate == 0) {
1309: for (int i = 0; i < LENGTH; i++) {
1310: c = (Coordinate) list.get(i);
1311: ords[i] = (c != null) ? c.x : Double.NaN;
1312: }
1313: } else if (ordinate == 1) {
1314: for (int i = 0; i < LENGTH; i++) {
1315: c = (Coordinate) list.get(i);
1316: ords[i] = (c != null) ? c.y : Double.NaN;
1317: }
1318: } else if (ordinate == 2) {
1319: for (int i = 0; i < LENGTH; i++) {
1320: c = (Coordinate) list.get(i);
1321: ords[i] = (c != null) ? c.z : Double.NaN;
1322: }
1323: } else {
1324: // default to NaN
1325: for (int i = 0; i < LENGTH; i++) {
1326: ords[i] = Double.NaN;
1327: }
1328: }
1329:
1330: return ords;
1331: }
1332:
1333: /**
1334: * Do not use me, I am broken
1335: * <p>
1336: * Do not use me, I am broken
1337: * </p>
1338: * @deprecated Do not use me, I am broken
1339: * @param list
1340: * @param ordinate
1341: */
1342: /*
1343: //TODO: check if I am correct
1344: public static Object[] attributeArray(List list, int ordinate) {
1345: if (list == null) {
1346: return null;
1347: }
1348:
1349: final int LENGTH = list.size();
1350: Object[] ords = new Object[LENGTH];
1351: Coordinate c;
1352: Double d;
1353: String s;
1354:
1355: if (ordinate == 0) {
1356: for (int i = 0; i < LENGTH; i++) {
1357: c = (Coordinate) list.get(i);
1358: ords[i] = (c != null) ? new Double(c.x) : new Double(Double.NaN);
1359: }
1360: } else if (ordinate == 1) {
1361: for (int i = 0; i < LENGTH; i++) {
1362: c = (Coordinate) list.get(i);
1363: ords[i] = (c != null) ? new Double(c.y) : new Double(Double.NaN);
1364: }
1365: } else if (ordinate == 2) {
1366: for (int i = 0; i < LENGTH; i++) {
1367: c = (Coordinate) list.get(i);
1368: ords[i] = (c != null) ? new Double(c.z) : new Double(Double.NaN);
1369: }
1370: }
1371: else if (ordinate == 3) { //BUG I am broken, do not use me our own Z
1372: for (int i = 0; i < LENGTH; i++) {
1373: c = (Coordinate) list.get(i);
1374: ords[i] = (c != null) ? new Double(Double.NaN) : new Double(Double.NaN);
1375: }
1376: }
1377: else if (ordinate == 4) { // our own T (a String)
1378: for (int i = 0; i < LENGTH; i++) {
1379: c = (Coordinate) list.get(i);
1380: ords[i] = (c != null) ? new Double(Double.NaN) : new Double(Double.NaN);
1381: }
1382: }else {
1383: // default to NaN
1384: for (int i = 0; i < LENGTH; i++) {
1385: ords[i] = list.get(i);
1386: }
1387: }
1388:
1389: return ords;
1390: }
1391: */
1392:
1393: /**
1394: * Package up <code>array</code> in correct manner for <code>geom</code>.
1395: *
1396: * <p>
1397: * Ordinates are placed into an array based on:
1398: * </p>
1399: *
1400: * <ul>
1401: * <li>
1402: * geometryGTypeD - chooses between 2d and 3d representation
1403: * </li>
1404: * <li>
1405: * geometryGTypeL - number of LRS measures
1406: * </li>
1407: * </ul>
1408: *
1409: *
1410: * @param list
1411: * @param geom
1412: *
1413: */
1414: public static double[] ordinates(List list, Geometry geom) {
1415: LOGGER.finest("ordinates D:" + D(geom));
1416: LOGGER.finest("ordinates L:" + L(geom));
1417:
1418: if (D(geom) == 3) {
1419: return ordinates3d(list, L(geom));
1420: } else {
1421: return ordinates2d(list, L(geom));
1422: }
1423: }
1424:
1425: /**
1426: * Ordinates (x,y,x1,y1,...) from coordiante list.
1427: *
1428: * <p>
1429: * No assumptions are made about the order
1430: * </p>
1431: *
1432: * @param list coordinate list
1433: * @return ordinate array
1434: */
1435: public static double[] ordinates2d(List list) {
1436: final int NUMBER = list.size();
1437: final int LEN = 2;
1438: double[] array = new double[NUMBER * LEN];
1439: double[] ords;
1440: int offset = 0;
1441:
1442: for (int i = 0; i < NUMBER; i++) {
1443: ords = (double[]) list.get(i);
1444:
1445: if (ords != null) {
1446: array[offset++] = ords[0];
1447: array[offset++] = ords[1];
1448: } else {
1449: array[offset++] = Double.NaN;
1450: array[offset++] = Double.NaN;
1451: }
1452: }
1453:
1454: return array;
1455: }
1456:
1457: /**
1458: * Ordinates (x,y,z,x2,y2,z2...) from coordiante[] array.
1459: *
1460: * @param list List of coordiante
1461: *
1462: * @return ordinate array
1463: */
1464: public static double[] ordinates3d(List list) {
1465: final int NUMBER = list.size();
1466: final int LEN = 3;
1467: double[] array = new double[NUMBER * LEN];
1468: double[] ords;
1469: int offset = 0;
1470:
1471: for (int i = 0; i < NUMBER; i++) {
1472: ords = (double[]) list.get(i);
1473:
1474: if (ords != null) {
1475: array[offset++] = ords[0];
1476: array[offset++] = ords[1];
1477: array[offset++] = ords[2];
1478: } else {
1479: array[offset++] = Double.NaN;
1480: array[offset++] = Double.NaN;
1481: array[offset++] = Double.NaN;
1482: }
1483: }
1484:
1485: return array;
1486: }
1487:
1488: /**
1489: * Ordinates (x,y,...id,x2,y2,...) from coordiante[] List.
1490: *
1491: * @param list coordiante list
1492: * @param L Dimension of ordinates required for representation
1493: *
1494: * @return ordinate array
1495: */
1496: public static double[] ordinates2d(List list, final int L) {
1497: if (L == 0) {
1498: return ordinates2d(list);
1499: }
1500:
1501: final int NUMBER = list.size();
1502: final int LEN = 2 + L;
1503: double[] array = new double[NUMBER * LEN];
1504: double[] ords;
1505:
1506: for (int i = 0; i < NUMBER; i++) {
1507: ords = (double[]) list.get(i);
1508:
1509: for (int j = 0; j < LEN; j++) {
1510: array[(i * LEN) + j] = ords[j];
1511: }
1512: }
1513:
1514: return array;
1515: }
1516:
1517: /**
1518: * Ordinates (x,y,z,...id,x2,y2,z2...) from coordiante[] array.
1519: *
1520: * @param list coordinate array to be represented as ordinates
1521: * @param L Dimension of ordinates required for representation
1522: *
1523: * @return ordinate array
1524: */
1525: public static double[] ordinates3d(List list, final int L) {
1526: if (L == 0) {
1527: return ordinates3d(list);
1528: }
1529:
1530: final int NUMBER = list.size();
1531: final int LEN = 3 + L;
1532: double[] array = new double[NUMBER * LEN];
1533: double[] ords;
1534:
1535: for (int i = 0; i < NUMBER; i++) {
1536: ords = (double[]) list.get(i);
1537:
1538: for (int j = 0; j < LEN; j++) {
1539: array[(i * LEN) + j] = ords[j];
1540: }
1541: }
1542:
1543: return array;
1544: }
1545:
1546: /**
1547: * Ensure Ring of Coordinates are in a counter clockwise order.
1548: *
1549: * <p>
1550: * If the Coordiante need to be reversed a copy will be returned.
1551: * </p>
1552: *
1553: * @param factory Factory to used to reverse CoordianteSequence
1554: * @param ring Ring of Coordinates
1555: *
1556: * @return coords in a CCW order
1557: */
1558: public static CoordinateSequence counterClockWise(
1559: CoordinateSequenceFactory factory, CoordinateSequence ring) {
1560: if (clock.isCCW(ring.toCoordinateArray())) {
1561: return ring;
1562: }
1563:
1564: return Coordinates.reverse(factory, ring);
1565: }
1566:
1567: /**
1568: * Ensure Ring of Coordinates are in a clockwise order.
1569: *
1570: * <p>
1571: * If the Coordiante need to be reversed a copy will be returned.
1572: * </p>
1573: *
1574: * @param factory Factory used to reverse CoordianteSequence
1575: * @param ring Ring of Coordinates
1576: *
1577: * @return coords in a CW order
1578: */
1579: private static CoordinateSequence clockWise(
1580: CoordinateSequenceFactory factory, CoordinateSequence ring) {
1581: if (!clock.isCCW(ring.toCoordinateArray())) {
1582: return ring;
1583: }
1584: return Coordinates.reverse(factory, ring);
1585: }
1586:
1587: /**
1588: * Reverse the clockwise orientation of the ring of Coordiantes.
1589: *
1590: * @param ring Ring of Coordinates
1591: *
1592: * @return coords Copy of <code>ring</code> in reversed order
1593: */
1594: private static Coordinate[] reverse(Coordinate[] ring) {
1595: int length = ring.length;
1596: Coordinate[] reverse = new Coordinate[length];
1597:
1598: for (int i = 0; i < length; i++) {
1599: reverse[i] = ring[length - i - 1];
1600: }
1601: return reverse;
1602: }
1603:
1604: // Utility Functions
1605: //
1606: //
1607:
1608: /**
1609: * Will need to tell if we are encoding a Polygon Exterior or Interior so
1610: * we can produce the correct encoding.
1611: *
1612: * @param poly Polygon to check
1613: *
1614: * @return <code>true</code> as we expect PolygonExteriors to be passed in
1615: */
1616: private static boolean isExterior(Polygon poly) {
1617: return true; // JTS polygons are always exterior
1618: }
1619:
1620: /**
1621: * We need to check if a <code>polygon</code> a cicle so we can produce the
1622: * correct encoding.
1623: *
1624: * @param polygon
1625: *
1626: * @return <code>true</code> if polygon is a circle
1627: */
1628: private static boolean isCircle(Polygon polygon) {
1629: return false; // JTS does not do cicles
1630: }
1631:
1632: /**
1633: * We need to check if a <code>polygon</code> a rectangle so we can produce
1634: * the correct encoding.
1635: *
1636: * <p>
1637: * Rectangles are only supported without a SRID!
1638: * </p>
1639: *
1640: * @param polygon
1641: *
1642: * @return <code>true</code> if polygon is SRID==0 and a rectangle
1643: */
1644: private static boolean isRectangle(Polygon polygon) {
1645: if (polygon.getFactory().getSRID() != SRID_NULL) {
1646: // Rectangles only valid in CAD applications
1647: // that do not have an SRID system
1648: //
1649: return false;
1650: }
1651:
1652: if (L(polygon) != 0) {
1653: // cannot support LRS on a rectangle
1654: return false;
1655: }
1656:
1657: Coordinate[] coords = polygon.getCoordinates();
1658:
1659: if (coords.length != 5) {
1660: return false;
1661: }
1662:
1663: if ((coords[0] == null) || (coords[1] == null)
1664: || (coords[2] == null) || (coords[3] == null)) {
1665: return false;
1666: }
1667:
1668: if (!coords[0].equals2D(coords[4])) {
1669: return false;
1670: }
1671:
1672: double x1 = coords[0].x;
1673: double y1 = coords[0].y;
1674: double x2 = coords[1].x;
1675: double y2 = coords[1].y;
1676: double x3 = coords[2].x;
1677: double y3 = coords[2].y;
1678: double x4 = coords[3].x;
1679: double y4 = coords[3].y;
1680:
1681: if ((x1 == x4) && (y1 == y2) && (x3 == x2) && (y3 == y4)) {
1682: // 1+-----+2
1683: // | |
1684: // 4+-----+3
1685: return true;
1686: }
1687:
1688: if ((x1 == x2) && (y1 == y4) && (x3 == x4) && (y3 == y2)) {
1689: // 2+-----+3
1690: // | |
1691: // 1+-----+4
1692: return true;
1693: }
1694:
1695: return false;
1696: }
1697:
1698: /**
1699: * We need to check if a <code>polygon</code> is defined with curves so we
1700: * can produce the correct encoding.
1701: *
1702: * <p></p>
1703: *
1704: * @param polygon
1705: *
1706: * @return <code>false</code> as JTS does not support curves
1707: */
1708: private static boolean isCurve(Polygon polygon) {
1709: return false;
1710: }
1711:
1712: /**
1713: * We need to check if a <code>lineString</code> is defined with curves so
1714: * we can produce the correct encoding.
1715: *
1716: * <p></p>
1717: *
1718: * @param lineString
1719: *
1720: * @return <code>false</code> as JTS does not support curves
1721: */
1722: private static boolean isCurve(LineString lineString) {
1723: return false;
1724: }
1725:
1726: // Decoding Helper Functions
1727: //
1728: //
1729: /**
1730: * Returns a range from a CoordinateList, based on ELEM_INFO triplets.
1731: *
1732: * @param factory Factory used for JTS
1733: * @param coords Coordinates
1734: * @param GTYPE Encoding of <b>D</b>imension, <b>L</b>RS and <b>TT</b>ype
1735: * @param elemInfo
1736: * @param triplet
1737: *
1738: */
1739: private static CoordinateSequence subList(
1740: CoordinateSequenceFactory factory,
1741: CoordinateSequence coords, int GTYPE, int[] elemInfo,
1742: int triplet) {
1743:
1744: final int STARTING_OFFSET = STARTING_OFFSET(elemInfo, triplet);
1745: final int ENDING_OFFSET = STARTING_OFFSET(elemInfo, triplet + 1); // -1 for end
1746:
1747: if ((STARTING_OFFSET == 1) && (ENDING_OFFSET == -1)) {
1748: // Use all Cordiantes
1749: return coords;
1750: }
1751: final int LEN = D(GTYPE) + L(GTYPE);
1752:
1753: int start = (STARTING_OFFSET - 1) / LEN;
1754: int end = (ENDING_OFFSET != -1) ? ((ENDING_OFFSET - 1) / LEN)
1755: : coords.size();
1756:
1757: return subList(factory, coords, start, end);
1758: }
1759:
1760: /**
1761: * Version of List.subList() that returns a CoordinateSequence.
1762: *
1763: * <p>
1764: * Returns from start (inclusive) to end (exlusive):
1765: * </p>
1766: *
1767: * <p>
1768: * Math speak: <code>[start,end)</code>
1769: * </p>
1770: *
1771: * @param factory Manages CoordinateSequences for JTS
1772: * @param coords coords to sublist
1773: * @param start starting offset
1774: * @param end upper bound of sublist
1775: *
1776: * @return CoordianteSequence
1777: */
1778: private static CoordinateSequence subList(
1779: CoordinateSequenceFactory factory,
1780: CoordinateSequence coords, int start, int end) {
1781: if ((start == 0) && (end == coords.size())) {
1782: return coords;
1783: }
1784:
1785: return Coordinates.subList(factory, coords, start, end);
1786: }
1787:
1788: private static LinearRing[] toInteriorRingArray(List list) {
1789: return (LinearRing[]) toArray(list, LinearRing.class);
1790:
1791: /*
1792: if( list == null ) return null;
1793: LinearRing array[] = new LinearRing[ list.size() ];
1794: int index=0;
1795: for( Iterator i=list.iterator(); i.hasNext(); index++ )
1796: {
1797: array[ index ] = (LinearRing) i.next();
1798: }
1799: return array;
1800: */
1801: }
1802:
1803: private static LineString[] toLineStringArray(List list) {
1804: return (LineString[]) toArray(list, LineString.class);
1805:
1806: /*
1807: if( list == null ) return null;
1808: LineString array[] = new LineString[ list.size() ];
1809: int index=0;
1810: for( Iterator i=list.iterator(); i.hasNext(); index++ )
1811: {
1812: array[ index ] = (LineString) i.next();
1813: }
1814: return array;
1815: */
1816: }
1817:
1818: private static Polygon[] toPolygonArray(List list) {
1819: return (Polygon[]) toArray(list, Polygon.class);
1820: }
1821:
1822: private static Geometry[] toGeometryArray(List list) {
1823: return (Geometry[]) toArray(list, Geometry.class);
1824: }
1825:
1826: /**
1827: * Useful function for converting to typed arrays for JTS API.
1828: *
1829: * <p>
1830: * Example:
1831: * </p>
1832: * <pre><code>
1833: * new MultiPoint( toArray( list, Coordiante.class ) );
1834: * </code></pre>
1835: *
1836: * @param list
1837: * @param type
1838: *
1839: */
1840: private static Object toArray(List list, Class type) {
1841: if (list == null) {
1842: return null;
1843: }
1844:
1845: Object array = Array.newInstance(type, list.size());
1846: int index = 0;
1847:
1848: for (Iterator i = list.iterator(); i.hasNext(); index++) {
1849: Array.set(array, index, i.next());
1850: }
1851:
1852: return array;
1853: }
1854:
1855: /**
1856: * Access D (for dimension) as encoded in GTYPE
1857: *
1858: * @param GTYPE DOCUMENT ME!
1859: *
1860: * @return DOCUMENT ME!
1861: */
1862: public static int D(final int GTYPE) {
1863: return GTYPE / 1000;
1864: }
1865:
1866: /**
1867: * Access L (for LRS) as encoded in GTYPE
1868: *
1869: * @param GTYPE DOCUMENT ME!
1870: *
1871: * @return DOCUMENT ME!
1872: */
1873: public static int L(final int GTYPE) {
1874: return (GTYPE - (D(GTYPE) * 1000)) / 100;
1875: }
1876:
1877: /**
1878: * Access TT (for geometry type) as encoded in GTYPE
1879: *
1880: * @param GTYPE DOCUMENT ME!
1881: *
1882: * @return DOCUMENT ME!
1883: */
1884: public static int TT(final int GTYPE) {
1885: return GTYPE - (D(GTYPE) * 1000) - (L(GTYPE) * 100);
1886: }
1887:
1888: /**
1889: * Access STARTING_OFFSET from elemInfo, or -1 if not available.
1890: *
1891: * <p></p>
1892: *
1893: * @param elemInfo DOCUMENT ME!
1894: * @param triplet DOCUMENT ME!
1895: *
1896: * @return DOCUMENT ME!
1897: */
1898: private static int STARTING_OFFSET(int[] elemInfo, int triplet) {
1899: if (((triplet * 3) + 0) >= elemInfo.length) {
1900: return -1;
1901: }
1902:
1903: return elemInfo[(triplet * 3) + 0];
1904: }
1905:
1906: /**
1907: * A version of assert that indicates range pre/post condition.
1908: * <p>
1909: * Works like assert exception IllegalArgumentException is thrown indicating this
1910: * is a required check.
1911: * </p>
1912: * <p>
1913: * Example phrased as a positive statement of the requirement to be met:
1914: * <pre><code>
1915: * ensure( "STARTING_OFFSET {1} must indicate a valid ordinate between {0} and {2}.
1916: * </code></pre>
1917: * </p>
1918: * @param condition MessageFormat pattern - positive statement of requirement
1919: * @param min minimum acceptable value ({0} in message format)
1920: * @param actual value supplied ({1} in message format)
1921: * @param max maximum acceptable value ({2} in message format)
1922: * @throws IllegalArgumentException unless min <= actual <= max
1923: */
1924: private static void ensure(String condition, int min, int actual,
1925: int max) {
1926: if (!(min <= actual && actual <= max)) {
1927: String msg = MessageFormat.format(condition, new Object[] {
1928: new Integer(min), new Integer(actual),
1929: new Integer(max) });
1930: throw new IllegalArgumentException(msg);
1931: }
1932: }
1933:
1934: /**
1935: * A version of assert that indicates range pre/post condition.
1936: * <p>
1937: * Works like assert exception IllegalArgumentException is thrown indicating this
1938: * is a required check.
1939: * </p>
1940: * <p>
1941: * Example phrased as a positive statement of the requirement to be met:
1942: * <pre><code>
1943: * ensure( "INTERPRETATION {0} must be on of {1}.
1944: * </code></pre>
1945: * </p>
1946: * @param condition MessageFormat pattern - positive statement of requirement
1947: * @param actual value supplied ({0} in message format)
1948: * @param set Array of acceptable values ({1} in message format)
1949: * @throws IllegalArgumentException unless actual is a member of set
1950: */
1951: private static void ensure(String condition, int actual, int[] set) {
1952: if (set == null)
1953: return; // don't apparently care
1954: for (int i = 0; i < set.length; i++) {
1955: if (set[i] == actual)
1956: return; // found it
1957: }
1958: StringBuffer array = new StringBuffer();
1959: for (int i = 0; i < set.length; i++) {
1960: array.append(set[i]);
1961: if (i < set.length) {
1962: array.append(",");
1963: }
1964: }
1965: String msg = MessageFormat.format(condition, new Object[] {
1966: new Integer(actual), array });
1967: throw new IllegalArgumentException(msg);
1968: }
1969:
1970: /** Returns the "length" of the ordinate array used for the
1971: * CoordianteSequence, GTYPE is used to determine the dimension.
1972: * <p>
1973: * This is most often used to check the STARTING_OFFSET value to ensure
1974: * that is falls within allowable bounds.
1975: * </p>
1976: * <p>
1977: * Example:<pre><code>
1978: * if (!(STARTING_OFFSET >= 1) ||
1979: * !(STARTING_OFFSET <= ordinateSize( coords, GTYPE ))){
1980: * throw new IllegalArgumentException(
1981: * "ELEM_INFO STARTING_OFFSET "+STARTING_OFFSET+
1982: * "inconsistent with COORDINATES length "+size( coords, GTYPE ) );
1983: * }
1984: * </code></pre>
1985: * </p>
1986: * @param coords
1987: * @param GTYPE
1988: */
1989: private static int ordinateSize(CoordinateSequence coords, int GTYPE) {
1990: if (coords == null) {
1991: return 0;
1992: }
1993: return coords.size() * D(GTYPE);
1994: }
1995:
1996: /**
1997: * ETYPE access for the elemInfo triplet indicated.
1998: * <p>
1999: * @see ETYPE for an indication of possible values
2000: *
2001: * @param elemInfo
2002: * @param triplet
2003: * @return ETYPE for indicated triplet
2004: */
2005: private static int ETYPE(int[] elemInfo, int triplet) {
2006: if (((triplet * 3) + 1) >= elemInfo.length) {
2007: return -1;
2008: }
2009:
2010: return elemInfo[(triplet * 3) + 1];
2011: }
2012:
2013: private static int INTERPRETATION(int[] elemInfo, int triplet) {
2014: if (((triplet * 3) + 2) >= elemInfo.length) {
2015: return -1;
2016: }
2017:
2018: return elemInfo[(triplet * 3) + 2];
2019: }
2020:
2021: /**
2022: * Coordiantes from <code>(x,y,x2,y2,...)</code> ordinates.
2023: *
2024: * @param ordinates DOCUMENT ME!
2025: *
2026: * @return DOCUMENT ME!
2027: */
2028: public static Coordinate[] asCoordinates(double[] ordinates) {
2029: return asCoordiantes(ordinates, 2);
2030: }
2031:
2032: /**
2033: * Coordiantes from a <code>(x,y,i3..,id,x2,y2...)</code> ordinates.
2034: *
2035: * @param ordinates DOCUMENT ME!
2036: * @param d DOCUMENT ME!
2037: *
2038: * @return DOCUMENT ME!
2039: */
2040: public static Coordinate[] asCoordiantes(double[] ordinates, int d) {
2041: int length = ordinates.length / d;
2042: Coordinate[] coords = new Coordinate[length];
2043:
2044: for (int i = 0; i < length; i++) {
2045: coords[i] = new Coordinate(ordinates[i * d],
2046: ordinates[(i * d) + 1]);
2047: }
2048:
2049: return coords;
2050: }
2051:
2052: /**
2053: * Construct CoordinateList as described by GTYPE.
2054: *
2055: * <p>
2056: * GTYPE encodes the following information:
2057: *
2058: * <ul>
2059: * <li>
2060: * D: Dimension of ordinates
2061: * </li>
2062: * <li>
2063: * L: Dimension of LRS measures
2064: * </li>
2065: * </ul>
2066: * </p>
2067: *
2068: * <p>
2069: * The number of ordinates per coordinate are taken to be L+D, and the
2070: * number of ordinates should be a multiple of this value.
2071: * </p>
2072: *
2073: * <p>
2074: * In the Special case of GTYPE 2001 and a three ordinates are interpreted
2075: * as a single Coordinate rather than an error.
2076: * </p>
2077: *
2078: * @param f CoordinateSequenceFactory used to encode ordiantes for JTS
2079: * @param GTYPE Encoding of <b>D</b>imension, <b>L</b>RS and <b>TT</b>ype
2080: * @param ordinates
2081: *
2082: *
2083: * @throws IllegalArgumentException DOCUMENT ME!
2084: */
2085: public static CoordinateSequence coordinates(
2086: CoordinateSequenceFactory f, final int GTYPE,
2087: double[] ordinates) {
2088: if ((ordinates == null) || (ordinates.length == 0)) {
2089: return f.create(new Coordinate[0]);
2090: }
2091:
2092: final int D = SDO.D(GTYPE);
2093: final int L = SDO.L(GTYPE);
2094: final int TT = SDO.TT(GTYPE);
2095:
2096: // POINT_TYPE Special Case
2097: //
2098: if ((D == 2) && (L == 0) && (TT == 01)
2099: && (ordinates.length == 3)) {
2100: return f.create(new Coordinate[] { new Coordinate(
2101: ordinates[0], ordinates[1], ordinates[2]), });
2102: }
2103:
2104: final int LEN = D + L;
2105:
2106: if ((ordinates.length % LEN) != 0) {
2107: throw new IllegalArgumentException("Dimension D:" + D
2108: + " and L:" + L + " denote Coordiantes " + "of "
2109: + LEN + " ordinates. This cannot be resolved with"
2110: + "an ordinate array of length " + ordinates.length);
2111: }
2112:
2113: final int LENGTH = ordinates.length / LEN;
2114:
2115: OrdinateList x = new OrdinateList(ordinates, 0, LEN);
2116: OrdinateList y = new OrdinateList(ordinates, 1, LEN);
2117: OrdinateList z = null;
2118:
2119: if (D == 3) {
2120: z = new OrdinateList(ordinates, 2, LEN);
2121: }
2122:
2123: if (L != 0) {
2124: OrdinateList[] m = new OrdinateList[L];
2125:
2126: for (int i = 0; i < L; i++) {
2127: m[i] = new OrdinateList(ordinates, D + i, LEN);
2128: }
2129:
2130: return coordiantes(f, x, y, z, m);
2131: } else {
2132: return coordiantes(f, x, y, z);
2133: }
2134: }
2135:
2136: /**
2137: * Construct CoordinateSequence with no LRS measures.
2138: *
2139: * <p>
2140: * To produce two dimension Coordiantes pass in <code>null</code> for z
2141: * </p>
2142: *
2143: * @param f DOCUMENT ME!
2144: * @param x DOCUMENT ME!
2145: * @param y DOCUMENT ME!
2146: * @param z DOCUMENT ME!
2147: *
2148: * @return DOCUMENT ME!
2149: */
2150: public static CoordinateSequence coordiantes(
2151: CoordinateSequenceFactory f, OrdinateList x,
2152: OrdinateList y, OrdinateList z) {
2153: final int LENGTH = x.size();
2154: Coordinate[] array = new Coordinate[LENGTH];
2155:
2156: if (z != null) {
2157: for (int i = 0; i < LENGTH; i++) {
2158: array[i] = new Coordinate(x.getDouble(i), y
2159: .getDouble(i), z.getDouble(i));
2160: }
2161: } else {
2162: for (int i = 0; i < LENGTH; i++) {
2163: array[i] = new Coordinate(x.getDouble(i), y
2164: .getDouble(i));
2165: }
2166: }
2167:
2168: return f.create(array);
2169: }
2170:
2171: /**
2172: * Construct CoordinateSequence with no LRS measures.
2173: *
2174: * <p>
2175: * To produce two dimension Coordiantes pass in <code>null</code> for z
2176: * </p>
2177: *
2178: * @param f DOCUMENT ME!
2179: * @param x DOCUMENT ME!
2180: * @param y DOCUMENT ME!
2181: * @param z DOCUMENT ME!
2182: *
2183: * @return DOCUMENT ME!
2184: */
2185: public static CoordinateSequence coordiantes(
2186: CoordinateSequenceFactory f, AttributeList x,
2187: AttributeList y, AttributeList z) {
2188: final int LENGTH = x.size();
2189: Coordinate[] array = new Coordinate[LENGTH];
2190:
2191: if (z != null) {
2192: for (int i = 0; i < LENGTH; i++) {
2193: array[i] = new Coordinate(x.getDouble(i), y
2194: .getDouble(i), z.getDouble(i));
2195: }
2196: } else {
2197: for (int i = 0; i < LENGTH; i++) {
2198: array[i] = new Coordinate(x.getDouble(i), y
2199: .getDouble(i));
2200: }
2201: }
2202:
2203: return f.create(array);
2204: }
2205:
2206: /**
2207: * Construct SpatialCoordiantes, with LRS measure information.
2208: *
2209: * <p>
2210: * To produce two dimension Coordiantes pass in <code>null</code> for z
2211: * </p>
2212: *
2213: * @param f DOCUMENT ME!
2214: * @param x x-ordinates
2215: * @param y y-ordinates
2216: * @param z z-ordinates, <code>null</code> for 2D
2217: * @param m column major measure information
2218: *
2219: * @return DOCUMENT ME!
2220: */
2221: public static CoordinateSequence coordiantes(
2222: CoordinateSequenceFactory f, OrdinateList x,
2223: OrdinateList y, OrdinateList z, OrdinateList[] m) {
2224: final int D = (z != null) ? 3 : 2;
2225: final int L = (m != null) ? m.length : 0;
2226:
2227: if (f instanceof CoordinateAccess && (L != 0)) {
2228: CoordinateAccessFactory factory = (CoordinateAccessFactory) f;
2229: double[][] xyz = new double[D][];
2230: double[][] measures = new double[L][];
2231:
2232: xyz[0] = x.toDoubleArray();
2233: xyz[1] = y.toDoubleArray();
2234:
2235: if (D == 3) {
2236: xyz[2] = z.toDoubleArray();
2237: }
2238:
2239: for (int i = 0; i < L; i++) {
2240: measures[i] = m[i].toDoubleArray();
2241: }
2242:
2243: return factory.create(xyz, measures);
2244: } else {
2245: return coordiantes(f, x, y, z);
2246: }
2247: }
2248:
2249: /**
2250: * Construct SpatialCoordiantes, with LRS measure information.
2251: *
2252: * <p>
2253: * To produce two dimension Coordiantes pass in <code>null</code> for z
2254: * </p>
2255: *
2256: * @param f DOCUMENT ME!
2257: * @param x x-ordinates
2258: * @param y y-ordinates
2259: * @param z z-ordinates, <code>null</code> for 2D
2260: * @param m column major measure information
2261: *
2262: * @return DOCUMENT ME!
2263: */
2264: public static CoordinateSequence coordiantes(
2265: CoordinateSequenceFactory f, AttributeList x,
2266: AttributeList y, AttributeList z, AttributeList[] m) {
2267: final int D = (z != null) ? 3 : 2;
2268: final int L = (m != null) ? m.length : 0;
2269:
2270: if (f instanceof CoordinateAccess && (L != 0)) {
2271: CoordinateAccessFactory factory = (CoordinateAccessFactory) f;
2272: double[][] xyz = new double[D][];
2273: Object[] measures = new Object[L];
2274:
2275: xyz[0] = x.toDoubleArray();
2276: xyz[1] = y.toDoubleArray();
2277:
2278: if (D == 3) {
2279: xyz[2] = z.toDoubleArray();
2280: }
2281:
2282: for (int i = 0; i < L; i++) {
2283: measures[i] = m[i].toObjectArray();
2284: }
2285:
2286: return factory.create(xyz, measures);
2287: } else {
2288: return coordiantes(f, x, y, z);
2289: }
2290: }
2291:
2292: /**
2293: * Decode geometry from provided SDO encoded information.
2294: *
2295: * <p></p>
2296: *
2297: * @param gf Used to construct returned Geometry
2298: * @param GTYPE SDO_GTYPE represents dimension, LRS, and geometry type
2299: * @param SRID SDO_SRID represents Spatial Reference System
2300: * @param point
2301: * @param elemInfo
2302: * @param ordinates
2303: *
2304: * @return Geometry as encoded
2305: */
2306: public static Geometry create(GeometryFactory gf, final int GTYPE,
2307: final int SRID, double[] point, int[] elemInfo,
2308: double[] ordinates) {
2309: final int L = SDO.L(GTYPE);
2310: final int TT = SDO.TT(GTYPE);
2311: double[] list;
2312: double[][] lists;
2313:
2314: CoordinateSequence coords;
2315:
2316: if ((L == 0) && (TT == 01) && (point != null)
2317: && (elemInfo == null)) {
2318: // Single Point Type Optimization
2319: coords = SDO.coordinates(gf.getCoordinateSequenceFactory(),
2320: GTYPE, point);
2321: elemInfo = new int[] { 1, ETYPE.POINT, 1 };
2322: } else {
2323: coords = SDO.coordinates(gf.getCoordinateSequenceFactory(),
2324: GTYPE, ordinates);
2325: }
2326:
2327: return create(gf, GTYPE, SRID, elemInfo, 0, coords, -1);
2328: }
2329:
2330: /**
2331: * Consturct geometry with SDO encoded information over a CoordinateList.
2332: *
2333: * <p>
2334: * Helpful when dealing construction Geometries with your own Coordiante
2335: * Types. The dimensionality specified in GTYPE will be used to interpret
2336: * the offsets in elemInfo.
2337: * </p>
2338: *
2339: * @param gf
2340: * @param GTYPE Encoding of <b>D</b>imension, <b>L</b>RS and <b>TT</b>ype
2341: * @param SRID
2342: * @param elemInfo
2343: * @param triplet DOCUMENT ME!
2344: * @param coords
2345: * @param N Number of triplets (-1 for unknown/don't care)
2346: *
2347: * @return Geometry as encoded, or null w/ log if it cannot be represented via JTS
2348: */
2349: public static Geometry create(GeometryFactory gf, final int GTYPE,
2350: final int SRID, final int[] elemInfo, final int triplet,
2351: CoordinateSequence coords, final int N) {
2352: switch (SDO.TT(GTYPE)) {
2353: case TT.POINT:
2354: return createPoint(gf, GTYPE, SRID, elemInfo, triplet,
2355: coords);
2356:
2357: case TT.LINE:
2358: return createLine(gf, GTYPE, SRID, elemInfo, triplet,
2359: coords);
2360:
2361: case TT.POLYGON:
2362: return createPolygon(gf, GTYPE, SRID, elemInfo, triplet,
2363: coords);
2364:
2365: case TT.MULTIPOINT:
2366: return createMultiPoint(gf, GTYPE, SRID, elemInfo, triplet,
2367: coords);
2368:
2369: case TT.MULTILINE:
2370: return createMultiLine(gf, GTYPE, SRID, elemInfo, triplet,
2371: coords, N);
2372:
2373: case TT.MULTIPOLYGON:
2374: return createMultiPolygon(gf, GTYPE, SRID, elemInfo,
2375: triplet, coords, N);
2376:
2377: case TT.COLLECTION:
2378: return createCollection(gf, GTYPE, SRID, elemInfo, triplet,
2379: coords, N);
2380:
2381: case TT.UNKNOWN:
2382: default:
2383: LOGGER
2384: .warning("Cannot represent provided SDO STRUCT (GTYPE ="
2385: + GTYPE + ") using JTS Geometry");
2386: return null;
2387: }
2388: }
2389:
2390: /**
2391: * Create Point as encoded.
2392: *
2393: * @param gf
2394: * @param GTYPE Encoding of <b>D</b>imension, <b>L</b>RS and <b>TT</b>ype
2395: * @param SRID
2396: * @param elemInfo
2397: * @param element
2398: * @param coords
2399: *
2400: * @return Point
2401: */
2402: private static Point createPoint(GeometryFactory gf,
2403: final int GTYPE, final int SRID, final int[] elemInfo,
2404: final int element, CoordinateSequence coords) {
2405: final int STARTING_OFFSET = STARTING_OFFSET(elemInfo, element);
2406: final int etype = ETYPE(elemInfo, element);
2407: final int INTERPRETATION = INTERPRETATION(elemInfo, element);
2408:
2409: if (!(STARTING_OFFSET >= 1)
2410: || !(STARTING_OFFSET <= coords.size()))
2411: throw new IllegalArgumentException(
2412: "ELEM_INFO STARTING_OFFSET " + STARTING_OFFSET
2413: + " inconsistent with ORDINATES length "
2414: + coords.size());
2415: if (etype != ETYPE.POINT)
2416: throw new IllegalArgumentException("ETYPE " + etype
2417: + " inconsistent with expected POINT");
2418: if (INTERPRETATION != 1) {
2419: LOGGER
2420: .warning("Could not create JTS Point with INTERPRETATION "
2421: + INTERPRETATION
2422: + " - we only expect 1 for a single point");
2423: return null;
2424: }
2425:
2426: Point point = new Point(subList(gf
2427: .getCoordinateSequenceFactory(), coords, GTYPE,
2428: elemInfo, element), gf);
2429:
2430: //Point point = gf.createPoint( coords.getCoordinate( index ) );
2431: point.setSRID(SRID);
2432:
2433: return point;
2434: }
2435:
2436: /**
2437: * Create LineString as encoded.
2438: *
2439: * @param gf
2440: * @param GTYPE Encoding of <b>D</b>imension, <b>L</b>RS and <b>TT</b>ype
2441: * @param SRID
2442: * @param elemInfo
2443: * @param triplet
2444: * @param coords
2445: *
2446: *
2447: * @throws IllegalArgumentException If asked to create a curve
2448: */
2449: private static LineString createLine(GeometryFactory gf,
2450: final int GTYPE, final int SRID, final int[] elemInfo,
2451: final int triplet, CoordinateSequence coords) {
2452: final int STARTING_OFFSET = STARTING_OFFSET(elemInfo, triplet);
2453: final int etype = ETYPE(elemInfo, triplet);
2454: final int INTERPRETATION = INTERPRETATION(elemInfo, triplet);
2455:
2456: if (etype != ETYPE.LINE)
2457: return null;
2458: if (INTERPRETATION != 1) {
2459: LOGGER
2460: .warning("Could not create JTS LineString with INTERPRETATION "
2461: + INTERPRETATION
2462: + " - we can only support 1 for straight edges");
2463: return null;
2464: }
2465:
2466: if (INTERPRETATION != 1) {
2467: // May be INTERPRETATION == 2 for curves
2468: throw new IllegalArgumentException(
2469: "ELEM_INFO INTERPRETAION "
2470: + INTERPRETATION
2471: + " not supported"
2472: + "by JTS LineString. Straight edges"
2473: + "( ELEM_INFO INTERPRETAION 1) is supported");
2474: }
2475:
2476: LineString line = new LineString(subList(gf
2477: .getCoordinateSequenceFactory(), coords, GTYPE,
2478: elemInfo, triplet), gf);
2479: line.setSRID(SRID);
2480:
2481: return line;
2482: }
2483:
2484: /**
2485: * Create Polygon as encoded.
2486: *
2487: * <p>
2488: * Encoded as a one or more triplets in elemInfo:
2489: * </p>
2490: *
2491: * <ul>
2492: * <li>
2493: * Exterior Polygon Ring: first triplet:
2494: *
2495: * <ul>
2496: * <li>
2497: * STARTING_OFFSET: position in ordinal ordinate array
2498: * </li>
2499: * <li>
2500: * ETYPE: 1003 (exterior) or 3 (polygon w/ counter clockwise ordinates)
2501: * </li>
2502: * <li>
2503: * INTERPRETATION: 1 for strait edges, 3 for rectanlge
2504: * </li>
2505: * </ul>
2506: *
2507: * </li>
2508: * <li>
2509: * Interior Polygon Ring(s): remaining triplets:
2510: *
2511: * <ul>
2512: * <li>
2513: * STARTING_OFFSET: position in ordinal ordinate array
2514: * </li>
2515: * <li>
2516: * ETYPE: 2003 (interior) or 3 (polygon w/ clockWise ordinates)
2517: * </li>
2518: * <li>
2519: * INTERPRETATION: 1 for strait edges, 3 for rectanlge
2520: * </li>
2521: * </ul>
2522: *
2523: * </li>
2524: * </ul>
2525: *
2526: * <p>
2527: * The polygon encoding will process subsequent 2003, or 3 triples with
2528: * clockwise ordering as interior holes.
2529: * </p>
2530: *
2531: * <p>
2532: * A subsequent triplet of any other type marks the end of the polygon.
2533: * </p>
2534: *
2535: * <p>
2536: * The dimensionality of GTYPE will be used to transalte the
2537: * <code>STARTING_OFFSET</code> provided by elemInfo into an index into
2538: * <code>coords</code>.
2539: * </p>
2540: *
2541: * @param gf Used to construct polygon
2542: * @param GTYPE Encoding of <b>D</b>imension, <b>L</b>RS and <b>TT</b>ype
2543: * @param SRID Spatial Reference System
2544: * @param elemInfo Interpretation of coords
2545: * @param triplet Triplet in elemInfo to process as a Polygon
2546: * @param coords Coordinates to interpret using elemInfo
2547: *
2548: * @return Polygon as encoded by elemInfo, or null when faced with and
2549: * encoding that can not be captured by JTS
2550: * @throws IllegalArgumentException When faced with an invalid SDO encoding
2551: */
2552: private static Polygon createPolygon(GeometryFactory gf,
2553: final int GTYPE, final int SRID, final int[] elemInfo,
2554: final int triplet, CoordinateSequence coords)
2555: throws IllegalArgumentException {
2556: final int STARTING_OFFSET = STARTING_OFFSET(elemInfo, triplet);
2557: final int eTYPE = ETYPE(elemInfo, triplet);
2558: final int INTERPRETATION = INTERPRETATION(elemInfo, triplet);
2559:
2560: ensure(
2561: "ELEM_INFO STARTING_OFFSET {1} must be in the range {0}..{1} of COORDINATES",
2562: 1, STARTING_OFFSET, ordinateSize(coords, GTYPE));
2563: if (!(1 <= STARTING_OFFSET && STARTING_OFFSET <= ordinateSize(
2564: coords, GTYPE))) {
2565: throw new IllegalArgumentException(
2566: "ELEM_INFO STARTING_OFFSET " + STARTING_OFFSET
2567: + "inconsistent with COORDINATES length "
2568: + ordinateSize(coords, GTYPE));
2569: }
2570: ensure(
2571: "ETYPE {0} must be expected POLYGON or POLYGON_EXTERIOR (one of {1})",
2572: eTYPE, new int[] { ETYPE.POLYGON,
2573: ETYPE.POLYGON_EXTERIOR });
2574: if (!(eTYPE == ETYPE.POLYGON)
2575: && !(eTYPE == ETYPE.POLYGON_EXTERIOR)) {
2576: throw new IllegalArgumentException(
2577: "ETYPE "
2578: + eTYPE
2579: + " inconsistent with expected POLYGON or POLYGON_EXTERIOR");
2580: }
2581: if (!(INTERPRETATION == 1) && !(INTERPRETATION == 3)) {
2582: LOGGER
2583: .warning("Could not create JTS Polygon with INTERPRETATION "
2584: + INTERPRETATION
2585: + " - we can only support 1 for straight edges, and 3 for rectangle");
2586: return null;
2587: }
2588:
2589: LinearRing exteriorRing = createLinearRing(gf, GTYPE, SRID,
2590: elemInfo, triplet, coords);
2591:
2592: List rings = new LinkedList();
2593: int etype;
2594: HOLES: for (int i = triplet + 1; (etype = ETYPE(elemInfo, i)) != -1; i++) {
2595: if (etype == ETYPE.POLYGON_INTERIOR) {
2596: rings.add(createLinearRing(gf, GTYPE, SRID, elemInfo,
2597: i, coords));
2598: } else if (etype == ETYPE.POLYGON) { // nead to test Clockwiseness of Ring to see if it is
2599: // interior or not - (use POLYGON_INTERIOR to avoid pain)
2600:
2601: LinearRing ring = createLinearRing(gf, GTYPE, SRID,
2602: elemInfo, i, coords);
2603:
2604: if (clock.isCCW(ring.getCoordinates())) { // it is an Interior Hole
2605: rings.add(ring);
2606: } else { // it is the next Polygon! - get out of here
2607:
2608: break HOLES;
2609: }
2610: } else { // not a LinearRing - get out of here
2611:
2612: break HOLES;
2613: }
2614: }
2615:
2616: Polygon poly = gf.createPolygon(exteriorRing,
2617: toInteriorRingArray(rings));
2618: poly.setSRID(SRID);
2619:
2620: return poly;
2621: }
2622:
2623: /**
2624: * Create Linear Ring for exterior/interior polygon ELEM_INFO triplets.
2625: *
2626: * <p>
2627: * Encoded as a single triplet in elemInfo:
2628: * </p>
2629: *
2630: * <ul>
2631: * <li>
2632: * STARTING_OFFSET: position in ordinal ordinate array
2633: * </li>
2634: * <li>
2635: * ETYPE: 1003 (exterior) or 2003 (interior) or 3 (unknown order)
2636: * </li>
2637: * <li>
2638: * INTERPRETATION: 1 for strait edges, 3 for rectanlge
2639: * </li>
2640: * </ul>
2641: *
2642: * <p>
2643: * The dimensionality of GTYPE will be used to transalte the
2644: * <code>STARTING_OFFSET</code> provided by elemInfo into an index into
2645: * <code>coords</code>.
2646: * </p>
2647: *
2648: * @param gf
2649: * @param GTYPE Encoding of <b>D</b>imension, <b>L</b>RS and <b>TT</b>ype
2650: * @param SRID
2651: * @param elemInfo
2652: * @param triplet
2653: * @param coords
2654: *
2655: * @return LinearRing
2656: *
2657: * @throws IllegalArgumentException If circle, or curve is requested
2658: */
2659: private static LinearRing createLinearRing(GeometryFactory gf,
2660: final int GTYPE, final int SRID, final int[] elemInfo,
2661: final int triplet, CoordinateSequence coords) {
2662:
2663: final int STARTING_OFFSET = STARTING_OFFSET(elemInfo, triplet);
2664: final int eTYPE = ETYPE(elemInfo, triplet);
2665: final int INTERPRETATION = INTERPRETATION(elemInfo, triplet);
2666: final int LENGTH = coords.size() * D(GTYPE);
2667:
2668: if (!(STARTING_OFFSET >= 1) || !(STARTING_OFFSET <= LENGTH))
2669: throw new IllegalArgumentException(
2670: "ELEM_INFO STARTING_OFFSET " + STARTING_OFFSET
2671: + " inconsistent with ORDINATES length "
2672: + coords.size());
2673: if (!(eTYPE == ETYPE.POLYGON)
2674: && !(eTYPE == ETYPE.POLYGON_EXTERIOR)
2675: && !(eTYPE == ETYPE.POLYGON_INTERIOR)) {
2676: throw new IllegalArgumentException(
2677: "ETYPE "
2678: + eTYPE
2679: + " inconsistent with expected POLYGON, POLYGON_EXTERIOR or POLYGON_INTERIOR");
2680: }
2681: if (!(INTERPRETATION == 1) && !(INTERPRETATION == 3)) {
2682: LOGGER
2683: .warning("Could not create LinearRing with INTERPRETATION "
2684: + INTERPRETATION
2685: + " - we can only support 1 for straight edges");
2686: return null;
2687: }
2688: LinearRing ring;
2689:
2690: if (INTERPRETATION == 1) {
2691: ring = gf.createLinearRing(subList(gf
2692: .getCoordinateSequenceFactory(), coords, GTYPE,
2693: elemInfo, triplet));
2694: } else if (INTERPRETATION == 3) {
2695: // rectangle does not maintain measures
2696: //
2697: CoordinateSequence ext = subList(gf
2698: .getCoordinateSequenceFactory(), coords, GTYPE,
2699: elemInfo, triplet);
2700: Coordinate min = ext.getCoordinate(0);
2701: Coordinate max = ext.getCoordinate(1);
2702: ring = gf.createLinearRing(new Coordinate[] { min,
2703: new Coordinate(max.x, min.y), max,
2704: new Coordinate(min.x, max.y), min });
2705: } else {
2706: // May be INTERPRETATION == 2 for curves, or 4 for circle
2707: //
2708: throw new IllegalArgumentException(
2709: "ELEM_INFO INTERPRETAION "
2710: + elemInfo[2]
2711: + " not supported"
2712: + "for JTS Polygon Linear Rings."
2713: + "ELEM_INFO INTERPRETAION 1 and 3 are supported");
2714: }
2715:
2716: ring.setSRID(SRID);
2717:
2718: return ring;
2719: }
2720:
2721: /**
2722: * Create MultiPoint as encoded by elemInfo.
2723: *
2724: * <p>
2725: * Encoded as a single triplet in elemInfo:
2726: * </p>
2727: *
2728: * <ul>
2729: * <li>
2730: * STARTING_OFFSET: position in ordinal ordinate array
2731: * </li>
2732: * <li>
2733: * ETYPE: 1 for Point
2734: * </li>
2735: * <li>
2736: * INTERPRETATION: number of points
2737: * </li>
2738: * </ul>
2739: *
2740: *
2741: * @param gf Used to construct polygon
2742: * @param GTYPE Encoding of <b>D</b>imension, <b>L</b>RS and <b>TT</b>ype
2743: * @param SRID Spatial Reference System
2744: * @param elemInfo Interpretation of coords
2745: * @param triplet Triplet in elemInfo to process as a Polygon
2746: * @param coords Coordinates to interpret using elemInfo
2747: *
2748: */
2749: private static MultiPoint createMultiPoint(GeometryFactory gf,
2750: final int GTYPE, final int SRID, final int[] elemInfo,
2751: final int triplet, CoordinateSequence coords) {
2752: final int STARTING_OFFSET = STARTING_OFFSET(elemInfo, triplet);
2753: final int eTYPE = ETYPE(elemInfo, triplet);
2754: final int INTERPRETATION = INTERPRETATION(elemInfo, triplet);
2755:
2756: if (!(STARTING_OFFSET >= 1)
2757: || !(STARTING_OFFSET <= coords.size()))
2758: throw new IllegalArgumentException(
2759: "ELEM_INFO STARTING_OFFSET " + STARTING_OFFSET
2760: + " inconsistent with ORDINATES length "
2761: + coords.size());
2762: if (!(eTYPE == ETYPE.POINT))
2763: throw new IllegalArgumentException("ETYPE " + eTYPE
2764: + " inconsistent with expected POINT");
2765: //CH- changed to >= 1, for GEOS-437, Jody and I looked at docs
2766: //and multipoint is a superset of point, so it should be fine,
2767: //for cases when there is just one point. Bart is testing.
2768: if (!(INTERPRETATION >= 1)) {
2769: LOGGER
2770: .warning("Could not create MultiPoint with INTERPRETATION "
2771: + INTERPRETATION
2772: + " - representing the number of points");
2773: return null;
2774: }
2775:
2776: final int LEN = D(GTYPE) + L(GTYPE);
2777:
2778: int start = (STARTING_OFFSET - 1) / LEN;
2779: int end = start + INTERPRETATION;
2780:
2781: MultiPoint points = gf.createMultiPoint(subList(gf
2782: .getCoordinateSequenceFactory(), coords, start, end));
2783: points.setSRID(SRID);
2784:
2785: return points;
2786: }
2787:
2788: /**
2789: * Create MultiLineString as encoded by elemInfo.
2790: *
2791: * <p>
2792: * Encoded as a series line of triplets in elemInfo:
2793: * </p>
2794: *
2795: * <ul>
2796: * <li>
2797: * STARTING_OFFSET: position in ordinal ordinate array
2798: * </li>
2799: * <li>
2800: * ETYPE: 2 for Line
2801: * </li>
2802: * <li>
2803: * INTERPRETATION: 1 for straight edges
2804: * </li>
2805: * </ul>
2806: *
2807: * <p></p>
2808: *
2809: * @param gf Used to construct MultiLineString
2810: * @param GTYPE Encoding of <b>D</b>imension, <b>L</b>RS and <b>TT</b>ype
2811: * @param SRID Spatial Reference System
2812: * @param elemInfo Interpretation of coords
2813: * @param triplet Triplet in elemInfo to process as a Polygon
2814: * @param coords Coordinates to interpret using elemInfo
2815: * @param N Number of triplets (or -1 for rest)
2816: *
2817: */
2818: private static MultiLineString createMultiLine(GeometryFactory gf,
2819: final int GTYPE, final int SRID, final int[] elemInfo,
2820: final int triplet, CoordinateSequence coords, final int N) {
2821: final int STARTING_OFFSET = STARTING_OFFSET(elemInfo, triplet);
2822: final int eTYPE = ETYPE(elemInfo, triplet);
2823: final int INTERPRETATION = INTERPRETATION(elemInfo, triplet);
2824:
2825: final int LENGTH = coords.size() * D(GTYPE);
2826:
2827: if (!(STARTING_OFFSET >= 1) || !(STARTING_OFFSET <= LENGTH))
2828: throw new IllegalArgumentException(
2829: "ELEM_INFO STARTING_OFFSET " + STARTING_OFFSET
2830: + " inconsistent with ORDINATES length "
2831: + coords.size());
2832: if (!(eTYPE == ETYPE.LINE))
2833: throw new IllegalArgumentException("ETYPE " + eTYPE
2834: + " inconsistent with expected LINE");
2835: if (!(INTERPRETATION == 1)) {
2836: // we cannot represent INTERPRETATION > 1
2837: LOGGER
2838: .warning("Could not create MultiLineString with INTERPRETATION "
2839: + INTERPRETATION
2840: + " - we can only represent 1 for straight edges");
2841: return null;
2842: }
2843:
2844: final int LEN = D(GTYPE) + L(GTYPE);
2845: final int endTriplet = (N != -1) ? (triplet + N)
2846: : (elemInfo.length / 3);
2847:
2848: List list = new LinkedList();
2849: int etype;
2850: LINES: // bad bad gotos jody
2851: for (int i = triplet; (i < endTriplet)
2852: && ((etype = ETYPE(elemInfo, i)) != -1); i++) {
2853: if (etype == ETYPE.LINE) {
2854: list.add(createLine(gf, GTYPE, SRID, elemInfo, i,
2855: coords));
2856: } else { // not a LinearString - get out of here
2857:
2858: break LINES; // goto LINES
2859: }
2860: }
2861:
2862: MultiLineString lines = gf
2863: .createMultiLineString(toLineStringArray(list));
2864: lines.setSRID(SRID);
2865:
2866: return lines;
2867: }
2868:
2869: /**
2870: * Create MultiPolygon as encoded by elemInfo.
2871: *
2872: * <p>
2873: * Encoded as a series polygon triplets in elemInfo:
2874: * </p>
2875: *
2876: * <ul>
2877: * <li>
2878: * STARTING_OFFSET: position in ordinal ordinate array
2879: * </li>
2880: * <li>
2881: * ETYPE: 2003 or 3 for Polygon
2882: * </li>
2883: * <li>
2884: * INTERPRETATION: 1 for straight edges, 3 for rectangle
2885: * </li>
2886: * </ul>
2887: *
2888: * <p></p>
2889: *
2890: * @param gf Used to construct MultiLineString
2891: * @param GTYPE Encoding of <b>D</b>imension, <b>L</b>RS and <b>TT</b>ype
2892: * @param SRID Spatial Reference System
2893: * @param elemInfo Interpretation of coords
2894: * @param triplet Triplet in elemInfo to process as a Polygon
2895: * @param coords Coordinates to interpret using elemInfo
2896: * @param N Number of triplets (or -1 for rest)
2897: *
2898: */
2899: private static MultiPolygon createMultiPolygon(GeometryFactory gf,
2900: final int GTYPE, final int SRID, final int[] elemInfo,
2901: final int triplet, CoordinateSequence coords, final int N) {
2902: final int STARTING_OFFSET = STARTING_OFFSET(elemInfo, triplet);
2903: final int eTYPE = ETYPE(elemInfo, triplet);
2904: final int INTERPRETATION = INTERPRETATION(elemInfo, triplet);
2905: final int LENGTH = coords.size() * D(GTYPE);
2906:
2907: if (!(STARTING_OFFSET >= 1) || !(STARTING_OFFSET <= LENGTH))
2908: throw new IllegalArgumentException(
2909: "ELEM_INFO STARTING_OFFSET " + STARTING_OFFSET
2910: + " inconsistent with ORDINATES length "
2911: + coords.size());
2912: if (!(eTYPE == ETYPE.POLYGON)
2913: && !(eTYPE == ETYPE.POLYGON_EXTERIOR))
2914: throw new IllegalArgumentException(
2915: "ETYPE "
2916: + eTYPE
2917: + " inconsistent with expected POLYGON or POLYGON_EXTERIOR");
2918: if (INTERPRETATION != 1 && INTERPRETATION != 3) {
2919: LOGGER
2920: .warning("Could not create MultiPolygon with INTERPRETATION "
2921: + INTERPRETATION
2922: + " - we can only represent 1 for straight edges, or 3 for rectangle");
2923: return null;
2924: }
2925: final int LEN = D(GTYPE) + L(GTYPE);
2926: final int endTriplet = (N != -1) ? (triplet + N)
2927: : ((elemInfo.length / 3) + 1);
2928:
2929: List list = new LinkedList();
2930: int etype;
2931: POLYGONS: for (int i = triplet; (i < endTriplet)
2932: && ((etype = ETYPE(elemInfo, i)) != -1); i++) {
2933: if ((etype == ETYPE.POLYGON)
2934: || (etype == ETYPE.POLYGON_EXTERIOR)) {
2935: Polygon poly = createPolygon(gf, GTYPE, SRID, elemInfo,
2936: i, coords);
2937: i += poly.getNumInteriorRing(); // skip interior rings
2938: list.add(poly);
2939: } else { // not a Polygon - get out here
2940:
2941: break POLYGONS;
2942: }
2943: }
2944:
2945: MultiPolygon polys = gf
2946: .createMultiPolygon(toPolygonArray(list));
2947: polys.setSRID(SRID);
2948:
2949: return polys;
2950: }
2951:
2952: /**
2953: * Create MultiPolygon as encoded by elemInfo.
2954: *
2955: * <p>
2956: * Encoded as a series polygon triplets in elemInfo:
2957: * </p>
2958: *
2959: * <ul>
2960: * <li>
2961: * STARTING_OFFSET: position in ordinal ordinate array
2962: * </li>
2963: * <li>
2964: * ETYPE: 2003 or 3 for Polygon
2965: * </li>
2966: * <li>
2967: * INTERPRETATION: 1 for straight edges, 2 for rectangle
2968: * </li>
2969: * </ul>
2970: *
2971: * <p></p>
2972: *
2973: * TODO: Confirm that createCollection is not getting cut&paste mistakes from polygonCollection
2974: *
2975: * @param gf Used to construct MultiLineString
2976: * @param GTYPE Encoding of <b>D</b>imension, <b>L</b>RS and <b>TT</b>ype
2977: * @param SRID Spatial Reference System
2978: * @param elemInfo Interpretation of coords
2979: * @param triplet Triplet in elemInfo to process as a Polygon
2980: * @param coords Coordinates to interpret using elemInfo
2981: * @param N Number of triplets (or -1 for rest)
2982: *
2983: * @return GeometryCollection
2984: *
2985: * @throws IllegalArgumentException DWhen faced with an encoding error
2986: */
2987: private static GeometryCollection createCollection(
2988: GeometryFactory gf, final int GTYPE, final int SRID,
2989: final int[] elemInfo, final int triplet,
2990: CoordinateSequence coords, final int N) {
2991: final int STARTING_OFFSET = STARTING_OFFSET(elemInfo, triplet);
2992: final int eTYPE = ETYPE(elemInfo, triplet);
2993: final int INTERPRETATION = INTERPRETATION(elemInfo, triplet);
2994:
2995: final int LENGTH = coords.size() * D(GTYPE);
2996:
2997: if (!(STARTING_OFFSET >= 1) || !(STARTING_OFFSET <= LENGTH))
2998: throw new IllegalArgumentException(
2999: "ELEM_INFO STARTING_OFFSET " + STARTING_OFFSET
3000: + " inconsistent with ORDINATES length "
3001: + coords.size());
3002:
3003: final int LEN = D(GTYPE) + L(GTYPE);
3004: final int endTriplet = (N != -1) ? (triplet + N)
3005: : ((elemInfo.length / 3) + 1);
3006:
3007: List list = new LinkedList();
3008: int etype;
3009: int interpretation;
3010: Geometry geom;
3011:
3012: GEOMETRYS: for (int i = triplet; i < endTriplet; i++) {
3013: etype = ETYPE(elemInfo, i);
3014: interpretation = INTERPRETATION(elemInfo, i);
3015:
3016: switch (etype) {
3017: case -1:
3018: break GEOMETRYS; // We are the of the list - get out of here
3019:
3020: case ETYPE.POINT:
3021:
3022: if (interpretation == 1) {
3023: geom = createPoint(gf, GTYPE, SRID, elemInfo, i,
3024: coords);
3025: } else if (interpretation > 1) {
3026: geom = createMultiPoint(gf, GTYPE, SRID, elemInfo,
3027: i, coords);
3028: } else {
3029: throw new IllegalArgumentException(
3030: "ETYPE.POINT requires INTERPRETATION >= 1");
3031: }
3032:
3033: break;
3034:
3035: case ETYPE.LINE:
3036: geom = createLine(gf, GTYPE, SRID, elemInfo, i, coords);
3037:
3038: break;
3039:
3040: case ETYPE.POLYGON:
3041: case ETYPE.POLYGON_EXTERIOR:
3042: geom = createPolygon(gf, GTYPE, SRID, elemInfo, i,
3043: coords);
3044: i += ((Polygon) geom).getNumInteriorRing();
3045:
3046: break;
3047:
3048: case ETYPE.POLYGON_INTERIOR:
3049: throw new IllegalArgumentException(
3050: "ETYPE 2003 (Polygon Interior) no expected in a GeometryCollection"
3051: + "(2003 is used to represent polygon holes, in a 1003 polygon exterior)");
3052:
3053: case ETYPE.CUSTOM:
3054: case ETYPE.COMPOUND:
3055: case ETYPE.COMPOUND_POLYGON:
3056: case ETYPE.COMPOUND_POLYGON_EXTERIOR:
3057: case ETYPE.COMPOUND_POLYGON_INTERIOR:
3058: default:
3059: throw new IllegalArgumentException(
3060: "ETYPE "
3061: + etype
3062: + " not representable as a JTS Geometry."
3063: + "(Custom and Compound Straight and Curved Geometries not supported)");
3064: }
3065:
3066: list.add(geom);
3067: }
3068:
3069: GeometryCollection geoms = gf
3070: .createGeometryCollection(toGeometryArray(list));
3071: geoms.setSRID(SRID);
3072:
3073: return geoms;
3074: }
3075: }
|