001: /*
002: * The JTS Topology Suite is a collection of Java classes that
003: * implement the fundamental operations required to validate a given
004: * geo-spatial data set to a known topological specification.
005: *
006: * Copyright (C) 2001 Vivid Solutions
007: *
008: * This library is free software; you can redistribute it and/or
009: * modify it under the terms of the GNU Lesser General Public
010: * License as published by the Free Software Foundation; either
011: * version 2.1 of the License, or (at your option) any later version.
012: *
013: * This library is distributed in the hope that it will be useful,
014: * but WITHOUT ANY WARRANTY; without even the implied warranty of
015: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
016: * Lesser General Public License for more details.
017: *
018: * You should have received a copy of the GNU Lesser General Public
019: * License along with this library; if not, write to the Free Software
020: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
021: *
022: * For more information, contact:
023: *
024: * Vivid Solutions
025: * Suite #1A
026: * 2328 Government Street
027: * Victoria BC V8T 5G5
028: * Canada
029: *
030: * (250)385-6040
031: * www.vividsolutions.com
032: */
033: package com.vividsolutions.jts.geom;
034:
035: import com.vividsolutions.jts.algorithm.CGAlgorithms;
036: import com.vividsolutions.jts.operation.BoundaryOp;
037:
038: /**
039: * Basic implementation of <code>LineString</code>.
040: *
041: *@version 1.7
042: */
043: public class LineString extends Geometry {
044: private static final long serialVersionUID = 3110669828065365560L;
045: /**
046: * The points of this <code>LineString</code>.
047: */
048: private CoordinateSequence points;
049:
050: /**
051: * Constructs a <code>LineString</code> with the given points.
052: *
053: *@param points the points of the linestring, or <code>null</code>
054: * to create the empty geometry. This array must not contain <code>null</code>
055: * elements. Consecutive points may not be equal.
056: *@param precisionModel the specification of the grid of allowable points
057: * for this <code>LineString</code>
058: *@param SRID the ID of the Spatial Reference System used by this
059: * <code>LineString</code>
060: */
061: /** @deprecated Use GeometryFactory instead */
062: public LineString(Coordinate points[],
063: PrecisionModel precisionModel, int SRID) {
064: super (new GeometryFactory(precisionModel, SRID));
065: init(getFactory().getCoordinateSequenceFactory().create(points));
066: }
067:
068: /**
069: *@param points the points of the linestring, or <code>null</code>
070: * to create the empty geometry. Consecutive points may not be equal.
071: */
072: public LineString(CoordinateSequence points, GeometryFactory factory) {
073: super (factory);
074: init(points);
075: }
076:
077: private void init(CoordinateSequence points) {
078: if (points == null) {
079: points = getFactory().getCoordinateSequenceFactory()
080: .create(new Coordinate[] {});
081: }
082: if (points.size() == 1) {
083: throw new IllegalArgumentException(
084: "point array must contain 0 or >1 elements");
085: }
086: this .points = points;
087: }
088:
089: public Coordinate[] getCoordinates() {
090: return points.toCoordinateArray();
091: }
092:
093: public CoordinateSequence getCoordinateSequence() {
094: return points;
095: }
096:
097: public Coordinate getCoordinateN(int n) {
098: return points.getCoordinate(n);
099: }
100:
101: public Coordinate getCoordinate() {
102: if (isEmpty())
103: return null;
104: return points.getCoordinate(0);
105: }
106:
107: public int getDimension() {
108: return 1;
109: }
110:
111: public int getBoundaryDimension() {
112: if (isClosed()) {
113: return Dimension.FALSE;
114: }
115: return 0;
116: }
117:
118: public boolean isEmpty() {
119: return points.size() == 0;
120: }
121:
122: public int getNumPoints() {
123: return points.size();
124: }
125:
126: public Point getPointN(int n) {
127: return getFactory().createPoint(points.getCoordinate(n));
128: }
129:
130: public Point getStartPoint() {
131: if (isEmpty()) {
132: return null;
133: }
134: return getPointN(0);
135: }
136:
137: public Point getEndPoint() {
138: if (isEmpty()) {
139: return null;
140: }
141: return getPointN(getNumPoints() - 1);
142: }
143:
144: public boolean isClosed() {
145: if (isEmpty()) {
146: return false;
147: }
148: return getCoordinateN(0).equals2D(
149: getCoordinateN(getNumPoints() - 1));
150: }
151:
152: public boolean isRing() {
153: return isClosed() && isSimple();
154: }
155:
156: public String getGeometryType() {
157: return "LineString";
158: }
159:
160: /**
161: * Returns the length of this <code>LineString</code>
162: *
163: *@return the area of the polygon
164: */
165: public double getLength() {
166: return CGAlgorithms.length(points);
167: }
168:
169: /**
170: * Gets the boundary of this geometry.
171: * The boundary of a lineal geometry is always a zero-dimensional geometry (which may be empty).
172: *
173: * @return the boundary geometry
174: * @see Geometry#getBoundary
175: */
176: public Geometry getBoundary() {
177: return (new BoundaryOp(this )).getBoundary();
178: }
179:
180: /**
181: * Creates a {@link LineString} whose coordinates are in the reverse
182: * order of this objects
183: *
184: * @return a {@link LineString} with coordinates in the reverse order
185: */
186: public LineString reverse() {
187: CoordinateSequence seq = (CoordinateSequence) points.clone();
188: CoordinateSequences.reverse(seq);
189: LineString revLine = getFactory().createLineString(seq);
190: return revLine;
191: }
192:
193: /**
194: * Returns true if the given point is a vertex of this <code>LineString</code>.
195: *
196: *@param pt the <code>Coordinate</code> to check
197: *@return <code>true</code> if <code>pt</code> is one of this <code>LineString</code>
198: * 's vertices
199: */
200: public boolean isCoordinate(Coordinate pt) {
201: for (int i = 0; i < points.size(); i++) {
202: if (points.getCoordinate(i).equals(pt)) {
203: return true;
204: }
205: }
206: return false;
207: }
208:
209: protected Envelope computeEnvelopeInternal() {
210: if (isEmpty()) {
211: return new Envelope();
212: }
213: return points.expandEnvelope(new Envelope());
214: }
215:
216: public boolean equalsExact(Geometry other, double tolerance) {
217: if (!isEquivalentClass(other)) {
218: return false;
219: }
220: LineString otherLineString = (LineString) other;
221: if (points.size() != otherLineString.points.size()) {
222: return false;
223: }
224: for (int i = 0; i < points.size(); i++) {
225: if (!equal(points.getCoordinate(i), otherLineString.points
226: .getCoordinate(i), tolerance)) {
227: return false;
228: }
229: }
230: return true;
231: }
232:
233: public void apply(CoordinateFilter filter) {
234: for (int i = 0; i < points.size(); i++) {
235: filter.filter(points.getCoordinate(i));
236: }
237: }
238:
239: public void apply(CoordinateSequenceFilter filter) {
240: if (points.size() == 0)
241: return;
242: for (int i = 0; i < points.size(); i++) {
243: filter.filter(points, i);
244: if (filter.isDone())
245: break;
246: }
247: if (filter.isGeometryChanged())
248: geometryChanged();
249: }
250:
251: public void apply(GeometryFilter filter) {
252: filter.filter(this );
253: }
254:
255: public void apply(GeometryComponentFilter filter) {
256: filter.filter(this );
257: }
258:
259: /**
260: * Creates and returns a full copy of this {@link LineString} object.
261: * (including all coordinates contained by it).
262: *
263: * @return a clone of this instance
264: */
265: public Object clone() {
266: LineString ls = (LineString) super .clone();
267: ls.points = (CoordinateSequence) points.clone();
268: return ls;
269: }
270:
271: /**
272: * Normalizes a LineString. A normalized linestring
273: * has the first point which is not equal to it's reflected point
274: * less than the reflected point.
275: */
276: public void normalize() {
277: for (int i = 0; i < points.size() / 2; i++) {
278: int j = points.size() - 1 - i;
279: // skip equal points on both ends
280: if (!points.getCoordinate(i)
281: .equals(points.getCoordinate(j))) {
282: if (points.getCoordinate(i).compareTo(
283: points.getCoordinate(j)) > 0) {
284: CoordinateArrays.reverse(getCoordinates());
285: }
286: return;
287: }
288: }
289: }
290:
291: protected boolean isEquivalentClass(Geometry other) {
292: return other instanceof LineString;
293: }
294:
295: protected int compareToSameClass(Object o) {
296: LineString line = (LineString) o;
297: // MD - optimized implementation
298: int i = 0;
299: int j = 0;
300: while (i < points.size() && j < line.points.size()) {
301: int comparison = points.getCoordinate(i).compareTo(
302: line.points.getCoordinate(j));
303: if (comparison != 0) {
304: return comparison;
305: }
306: i++;
307: j++;
308: }
309: if (i < points.size()) {
310: return 1;
311: }
312: if (j < line.points.size()) {
313: return -1;
314: }
315: return 0;
316: }
317:
318: protected int compareToSameClass(Object o,
319: CoordinateSequenceComparator comp) {
320: LineString line = (LineString) o;
321: return comp.compare(this.points, line.points);
322: }
323:
324: }
|