001: /*
002: * The Unified Mapping Platform (JUMP) is an extensible, interactive GUI
003: * for visualizing and manipulating spatial features with geometry and attributes.
004: *
005: * Copyright (C) 2003 Vivid Solutions
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License
009: * as published by the Free Software Foundation; either version 2
010: * of the License, or (at your option) any later version.
011: *
012: * This program is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
015: * GNU General Public License for more details.
016: *
017: * You should have received a copy of the GNU General Public License
018: * along with this program; if not, write to the Free Software
019: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
020: *
021: * For more information, contact:
022: *
023: * Vivid Solutions
024: * Suite #1A
025: * 2328 Government Street
026: * Victoria BC V8T 5G5
027: * Canada
028: *
029: * (250)385-6040
030: * www.vividsolutions.com
031: */
032:
033: package com.vividsolutions.jump.util;
034:
035: import java.util.*;
036:
037: import com.vividsolutions.jts.algorithm.CGAlgorithms;
038: import com.vividsolutions.jts.algorithm.RobustCGAlgorithms;
039: import com.vividsolutions.jts.geom.*;
040: import com.vividsolutions.jts.util.Assert;
041:
042: /**
043: * Some utility functions for handling Coordinate arrays.
044: */
045: public class CoordinateArrays {
046: //<<TODO:REFACTORING>> JTS already has a class named CoordinateArrays.
047: //I wonder if we should collapse this class into that one. [Jon Aquino]
048: // MD - yep, at some point.
049: private static final CGAlgorithms cga = new RobustCGAlgorithms();
050: private final static Coordinate[] coordArrayType = new Coordinate[0];
051:
052: public static Coordinate[] toCoordinateArray(List coordList) {
053: return (Coordinate[]) coordList.toArray(coordArrayType);
054: }
055:
056: //<<TODO:REFACTORING>> This functionality is duplicated in
057: //the protected method Geometry#reversePointOrder. Perhaps we should
058: //make that method public and deprecate this method, or have this method
059: //delegate to the other. [Jon Aquino]
060: //MD: Geometry#reversePointOrder could delegate to this method. Can't do it other way around.
061: public static void reverse(Coordinate[] coord) {
062: int last = coord.length - 1;
063: int mid = last / 2;
064:
065: for (int i = 0; i <= mid; i++) {
066: Coordinate tmp = coord[i];
067: coord[i] = coord[last - i];
068: coord[last - i] = tmp;
069: }
070: }
071:
072: /**
073: * Converts an array of coordinates to a line or point, as appropriate.
074: * @param coords the coordinates of a line or point
075: * @param fact a factory used to create the Geometry
076: * @return a line if there is more than one coordinate; a point if there is
077: * just one coordinate; an empty point otherwise
078: */
079: public static Geometry toLineOrPoint(Coordinate[] coords,
080: GeometryFactory fact) {
081: if (coords.length > 1) {
082: return fact.createLineString(coords);
083: }
084:
085: if (coords.length == 1) {
086: return fact.createPoint(coords[0]);
087: }
088:
089: return fact.createPoint((Coordinate) null);
090: }
091:
092: public static boolean equals(Coordinate[] coord1,
093: Coordinate[] coord2) {
094: if (coord1 == coord2) {
095: return true;
096: }
097:
098: if ((coord1 == null) || (coord2 == null)) {
099: return false;
100: }
101:
102: if (coord1.length != coord2.length) {
103: return false;
104: }
105:
106: for (int i = 0; i < coord1.length; i++) {
107: if (!coord1[i].equals(coord2[i])) {
108: return false;
109: }
110: }
111:
112: return true;
113: }
114:
115: /**
116: * Converts a collection of coordinate arrays to a collection of geometries.
117: * @param coordArrays a collection of Coordinate[]
118: * @param fact a factory used to create the Geometries
119: * @return a collection of LineStrings and Points
120: */
121: public static List fromCoordinateArrays(List coordArrays,
122: GeometryFactory fact) {
123: List geomList = new ArrayList();
124:
125: for (Iterator i = coordArrays.iterator(); i.hasNext();) {
126: Coordinate[] coords = (Coordinate[]) i.next();
127: Geometry geom = toLineOrPoint(coords, fact);
128: geomList.add(geom);
129: }
130:
131: return geomList;
132: }
133:
134: /**
135: * Extract the coordinate arrays for a geometry into a List.
136: * @param g the Geometry to extract from
137: * @param coordArrayList the List to add the coordinate arrays to
138: * @param orientPolygons whether or not the arrays in the List should be
139: * oriented (clockwise for the shell, counterclockwise for the holes)
140: */
141: public static void addCoordinateArrays(Geometry g,
142: boolean orientPolygons, List coordArrayList) {
143: if (g.getDimension() <= 0) {
144: return;
145: } else if (g instanceof LineString) {
146: LineString l = (LineString) g;
147: coordArrayList.add(l.getCoordinates());
148: } else if (g instanceof Polygon) {
149: Polygon poly = (Polygon) g;
150: Coordinate[] shell = poly.getExteriorRing()
151: .getCoordinates();
152:
153: if (orientPolygons) {
154: shell = ensureOrientation(shell, CGAlgorithms.CLOCKWISE);
155: }
156:
157: coordArrayList.add(shell);
158:
159: for (int i = 0; i < poly.getNumInteriorRing(); i++) {
160: Coordinate[] hole = poly.getInteriorRingN(i)
161: .getCoordinates();
162:
163: if (orientPolygons) {
164: hole = ensureOrientation(hole,
165: CGAlgorithms.COUNTERCLOCKWISE);
166: }
167:
168: coordArrayList.add(hole);
169: }
170: } else if (g instanceof GeometryCollection) {
171: GeometryCollection gc = (GeometryCollection) g;
172:
173: for (int i = 0; i < gc.getNumGeometries(); i++) {
174: addCoordinateArrays(gc.getGeometryN(i), orientPolygons,
175: coordArrayList);
176: }
177: } else {
178: Assert.shouldNeverReachHere("Geometry of type "
179: + g.getClass().getName() + " not handled");
180: }
181: }
182:
183: /**
184: * Sets the orientation of an array of coordinates.
185: * @param coord the coordinates to inspect
186: * @param desiredOrientation CGAlgorithms.CLOCKWISE or CGAlgorithms.COUNTERCLOCKWISE
187: * @return a new array with entries in reverse order, if the orientation is
188: * incorrect; otherwise, the original array
189: */
190: public static Coordinate[] ensureOrientation(Coordinate[] coord,
191: int desiredOrientation) {
192: if (coord.length == 0) {
193: return coord;
194: }
195:
196: int orientation = cga.isCCW(coord) ? CGAlgorithms.COUNTERCLOCKWISE
197: : CGAlgorithms.CLOCKWISE;
198:
199: if (orientation != desiredOrientation) {
200: Coordinate[] reverse = (Coordinate[]) coord.clone();
201: CoordinateArrays.reverse(reverse);
202:
203: return reverse;
204: }
205:
206: return coord;
207: }
208:
209: /**
210: * Extract the coordinate arrays for a geometry.
211: * Polygons will be checked to ensure their rings are oriented correctly.
212: * Note: coordinates from Points or MultiPoints will not be extracted.
213: * @param g the Geometry to extract from
214: * @param orientPolygons ensure that Polygons are correctly oriented
215: * @return a list of Coordinate[]
216: */
217: public static List toCoordinateArrays(Geometry g,
218: boolean orientPolygons) {
219: List coordArrayList = new ArrayList();
220: addCoordinateArrays(g, orientPolygons, coordArrayList);
221:
222: return coordArrayList;
223: }
224: }
|