001:
002: /*
003: * The JTS Topology Suite is a collection of Java classes that
004: * implement the fundamental operations required to validate a given
005: * geo-spatial data set to a known topological specification.
006: *
007: * Copyright (C) 2001 Vivid Solutions
008: *
009: * This library is free software; you can redistribute it and/or
010: * modify it under the terms of the GNU Lesser General Public
011: * License as published by the Free Software Foundation; either
012: * version 2.1 of the License, or (at your option) any later version.
013: *
014: * This library is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
017: * Lesser General Public License for more details.
018: *
019: * You should have received a copy of the GNU Lesser General Public
020: * License along with this library; if not, write to the Free Software
021: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
022: *
023: * For more information, contact:
024: *
025: * Vivid Solutions
026: * Suite #1A
027: * 2328 Government Street
028: * Victoria BC V8T 5G5
029: * Canada
030: *
031: * (250)385-6040
032: * www.vividsolutions.com
033: */
034: package com.vividsolutions.jts.geom;
035:
036: import java.util.*;
037: import java.io.Serializable;
038: import com.vividsolutions.jts.geom.impl.*;
039: import com.vividsolutions.jts.geom.util.*;
040: import com.vividsolutions.jts.util.Assert;
041:
042: /**
043: * Supplies a set of utility methods for building Geometry objects from lists
044: * of Coordinates.
045: * <p>
046: * Note that the factory constructor methods do <b>not</b> change the input coordinates in any way.
047: * In particular, they are not rounded to the supplied <tt>PrecisionModel</tt>.
048: * It is assumed that input Coordinates meet the given precision.
049: *
050: *
051: * @version 1.7
052: */
053: public class GeometryFactory implements Serializable {
054: private static final long serialVersionUID = -6820524753094095635L;
055: private PrecisionModel precisionModel;
056:
057: private CoordinateSequenceFactory coordinateSequenceFactory;
058:
059: public static Point createPointFromInternalCoord(Coordinate coord,
060: Geometry exemplar) {
061: exemplar.getPrecisionModel().makePrecise(coord);
062: return exemplar.getFactory().createPoint(coord);
063: }
064:
065: /**
066: * Constructs a GeometryFactory that generates Geometries having the given
067: * PrecisionModel, spatial-reference ID, and CoordinateSequence implementation.
068: */
069: public GeometryFactory(PrecisionModel precisionModel, int SRID,
070: CoordinateSequenceFactory coordinateSequenceFactory) {
071: this .precisionModel = precisionModel;
072: this .coordinateSequenceFactory = coordinateSequenceFactory;
073: this .SRID = SRID;
074: }
075:
076: /**
077: * Constructs a GeometryFactory that generates Geometries having the given
078: * CoordinateSequence implementation, a double-precision floating PrecisionModel and a
079: * spatial-reference ID of 0.
080: */
081: public GeometryFactory(
082: CoordinateSequenceFactory coordinateSequenceFactory) {
083: this (new PrecisionModel(), 0, coordinateSequenceFactory);
084: }
085:
086: /**
087: * Constructs a GeometryFactory that generates Geometries having the given
088: * {@link PrecisionModel} and the default CoordinateSequence
089: * implementation.
090: *
091: * @param precisionModel the PrecisionModel to use
092: */
093: public GeometryFactory(PrecisionModel precisionModel) {
094: this (precisionModel, 0, getDefaultCoordinateSequenceFactory());
095: }
096:
097: /**
098: * Constructs a GeometryFactory that generates Geometries having the given
099: * {@link PrecisionModel} and spatial-reference ID, and the default CoordinateSequence
100: * implementation.
101: *
102: * @param precisionModel the PrecisionModel to use
103: * @param SRID the SRID to use
104: */
105: public GeometryFactory(PrecisionModel precisionModel, int SRID) {
106: this (precisionModel, SRID,
107: getDefaultCoordinateSequenceFactory());
108: }
109:
110: /**
111: * Constructs a GeometryFactory that generates Geometries having a floating
112: * PrecisionModel and a spatial-reference ID of 0.
113: */
114: public GeometryFactory() {
115: this (new PrecisionModel(), 0);
116: }
117:
118: private static CoordinateSequenceFactory getDefaultCoordinateSequenceFactory() {
119: return CoordinateArraySequenceFactory.instance();
120: }
121:
122: /**
123: * Converts the <code>List</code> to an array.
124: *
125: *@param points the <code>List</code> of Points to convert
126: *@return the <code>List</code> in array format
127: */
128: public static Point[] toPointArray(Collection points) {
129: Point[] pointArray = new Point[points.size()];
130: return (Point[]) points.toArray(pointArray);
131: }
132:
133: /**
134: * Converts the <code>List</code> to an array.
135: *
136: *@param geometries the list of <code>Geometry's</code> to convert
137: *@return the <code>List</code> in array format
138: */
139: public static Geometry[] toGeometryArray(Collection geometries) {
140: if (geometries == null)
141: return null;
142: Geometry[] geometryArray = new Geometry[geometries.size()];
143: return (Geometry[]) geometries.toArray(geometryArray);
144: }
145:
146: /**
147: * Converts the <code>List</code> to an array.
148: *
149: *@param linearRings the <code>List</code> of LinearRings to convert
150: *@return the <code>List</code> in array format
151: */
152: public static LinearRing[] toLinearRingArray(Collection linearRings) {
153: LinearRing[] linearRingArray = new LinearRing[linearRings
154: .size()];
155: return (LinearRing[]) linearRings.toArray(linearRingArray);
156: }
157:
158: /**
159: * Converts the <code>List</code> to an array.
160: *
161: *@param lineStrings the <code>List</code> of LineStrings to convert
162: *@return the <code>List</code> in array format
163: */
164: public static LineString[] toLineStringArray(Collection lineStrings) {
165: LineString[] lineStringArray = new LineString[lineStrings
166: .size()];
167: return (LineString[]) lineStrings.toArray(lineStringArray);
168: }
169:
170: /**
171: * Converts the <code>List</code> to an array.
172: *
173: *@param polygons the <code>List</code> of Polygons to convert
174: *@return the <code>List</code> in array format
175: */
176: public static Polygon[] toPolygonArray(Collection polygons) {
177: Polygon[] polygonArray = new Polygon[polygons.size()];
178: return (Polygon[]) polygons.toArray(polygonArray);
179: }
180:
181: /**
182: * Converts the <code>List</code> to an array.
183: *
184: *@param multiPolygons the <code>List</code> of MultiPolygons to convert
185: *@return the <code>List</code> in array format
186: */
187: public static MultiPolygon[] toMultiPolygonArray(
188: Collection multiPolygons) {
189: MultiPolygon[] multiPolygonArray = new MultiPolygon[multiPolygons
190: .size()];
191: return (MultiPolygon[]) multiPolygons
192: .toArray(multiPolygonArray);
193: }
194:
195: /**
196: * Converts the <code>List</code> to an array.
197: *
198: *@param multiLineStrings the <code>List</code> of MultiLineStrings to convert
199: *@return the <code>List</code> in array format
200: */
201: public static MultiLineString[] toMultiLineStringArray(
202: Collection multiLineStrings) {
203: MultiLineString[] multiLineStringArray = new MultiLineString[multiLineStrings
204: .size()];
205: return (MultiLineString[]) multiLineStrings
206: .toArray(multiLineStringArray);
207: }
208:
209: /**
210: * Converts the <code>List</code> to an array.
211: *
212: *@param multiPoints the <code>List</code> of MultiPoints to convert
213: *@return the <code>List</code> in array format
214: */
215: public static MultiPoint[] toMultiPointArray(Collection multiPoints) {
216: MultiPoint[] multiPointArray = new MultiPoint[multiPoints
217: .size()];
218: return (MultiPoint[]) multiPoints.toArray(multiPointArray);
219: }
220:
221: /**
222: * If the <code>Envelope</code> is a null <code>Envelope</code>, returns an
223: * empty <code>Point</code>. If the <code>Envelope</code> is a point, returns
224: * a non-empty <code>Point</code>. If the <code>Envelope</code> is a
225: * rectangle, returns a <code>Polygon</code> whose points are (minx, miny),
226: * (maxx, miny), (maxx, maxy), (minx, maxy), (minx, miny).
227: *
228: *@param envelope the <code>Envelope</code> to convert to a <code>Geometry</code>
229: *@param precisionModel the specification of the grid of allowable points
230: * for the new <code>Geometry</code>
231: *@param SRID the ID of the Spatial Reference System used by the
232: * <code>Envelope</code>
233: *@return an empty <code>Point</code> (for null <code>Envelope</code>
234: * s), a <code>Point</code> (when min x = max x and min y = max y) or a
235: * <code>Polygon</code> (in all other cases)
236: *@throws <code> TopologyException</code> if <code>coordinates</code>
237: * is not a closed linestring, that is, if the first and last coordinates
238: * are not equal
239: */
240: public Geometry toGeometry(Envelope envelope) {
241: if (envelope.isNull()) {
242: return createPoint((CoordinateSequence) null);
243: }
244: if (envelope.getMinX() == envelope.getMaxX()
245: && envelope.getMinY() == envelope.getMaxY()) {
246: return createPoint(new Coordinate(envelope.getMinX(),
247: envelope.getMinY()));
248: }
249: return createPolygon(
250: createLinearRing(new Coordinate[] {
251: new Coordinate(envelope.getMinX(), envelope
252: .getMinY()),
253: new Coordinate(envelope.getMaxX(), envelope
254: .getMinY()),
255: new Coordinate(envelope.getMaxX(), envelope
256: .getMaxY()),
257: new Coordinate(envelope.getMinX(), envelope
258: .getMaxY()),
259: new Coordinate(envelope.getMinX(), envelope
260: .getMinY()) }), null);
261: }
262:
263: /**
264: * Returns the PrecisionModel that Geometries created by this factory
265: * will be associated with.
266: */
267: public PrecisionModel getPrecisionModel() {
268: return precisionModel;
269: }
270:
271: /**
272: * Creates a Point using the given Coordinate; a null Coordinate will create
273: * an empty Geometry.
274: */
275: public Point createPoint(Coordinate coordinate) {
276: return createPoint(coordinate != null ? getCoordinateSequenceFactory()
277: .create(new Coordinate[] { coordinate })
278: : null);
279: }
280:
281: /**
282: * Creates a Point using the given CoordinateSequence; a null or empty
283: * CoordinateSequence will create an empty Point.
284: */
285: public Point createPoint(CoordinateSequence coordinates) {
286: return new Point(coordinates, this );
287: }
288:
289: /**
290: * Creates a MultiLineString using the given LineStrings; a null or empty
291: * array will create an empty MultiLineString.
292: * @param lineStrings LineStrings, each of which may be empty but not null
293: */
294: public MultiLineString createMultiLineString(
295: LineString[] lineStrings) {
296: return new MultiLineString(lineStrings, this );
297: }
298:
299: /**
300: * Creates a GeometryCollection using the given Geometries; a null or empty
301: * array will create an empty GeometryCollection.
302: * @param geometries Geometries, each of which may be empty but not null
303: */
304: public GeometryCollection createGeometryCollection(
305: Geometry[] geometries) {
306: return new GeometryCollection(geometries, this );
307: }
308:
309: /**
310: * Creates a MultiPolygon using the given Polygons; a null or empty array
311: * will create an empty Polygon. The polygons must conform to the
312: * assertions specified in the <A
313: * HREF="http://www.opengis.org/techno/specs.htm">OpenGIS Simple Features
314: * Specification for SQL</A>.
315: *
316: * @param polygons
317: * Polygons, each of which may be empty but not null
318: */
319: public MultiPolygon createMultiPolygon(Polygon[] polygons) {
320: return new MultiPolygon(polygons, this );
321: }
322:
323: /**
324: * Creates a LinearRing using the given Coordinates; a null or empty array will
325: * create an empty LinearRing. The points must form a closed and simple
326: * linestring. Consecutive points must not be equal.
327: * @param coordinates an array without null elements, or an empty array, or null
328: */
329: public LinearRing createLinearRing(Coordinate[] coordinates) {
330: return createLinearRing(coordinates != null ? getCoordinateSequenceFactory()
331: .create(coordinates)
332: : null);
333: }
334:
335: /**
336: * Creates a LinearRing using the given CoordinateSequence; a null or empty CoordinateSequence will
337: * create an empty LinearRing. The points must form a closed and simple
338: * linestring. Consecutive points must not be equal.
339: * @param coordinates a CoordinateSequence possibly empty, or null
340: */
341: public LinearRing createLinearRing(CoordinateSequence coordinates) {
342: return new LinearRing(coordinates, this );
343: }
344:
345: /**
346: * Creates a MultiPoint using the given Points.
347: * A null or empty array will create an empty MultiPoint.
348: *
349: * @param coordinates an array (without null elements), or an empty array, or <code>null</code>
350: * @return a MultiPoint object
351: */
352: public MultiPoint createMultiPoint(Point[] point) {
353: return new MultiPoint(point, this );
354: }
355:
356: /**
357: * Creates a {@link MultiPoint} using the given {@link Coordinate}s.
358: * A null or empty array will create an empty MultiPoint.
359: *
360: * @param coordinates an array (without null elements), or an empty array, or <code>null</code>
361: * @return a MultiPoint object
362: */
363: public MultiPoint createMultiPoint(Coordinate[] coordinates) {
364: return createMultiPoint(coordinates != null ? getCoordinateSequenceFactory()
365: .create(coordinates)
366: : null);
367: }
368:
369: /**
370: * Creates a MultiPoint using the given CoordinateSequence.
371: * A a null or empty CoordinateSequence will create an empty MultiPoint.
372: *
373: * @param coordinates a CoordinateSequence (possibly empty), or <code>null</code>
374: * @return a MultiPoint object
375: */
376: public MultiPoint createMultiPoint(CoordinateSequence coordinates) {
377: if (coordinates == null) {
378: return createMultiPoint(new Point[0]);
379: }
380: Point[] points = new Point[coordinates.size()];
381: for (int i = 0; i < coordinates.size(); i++) {
382: points[i] = createPoint(coordinates.getCoordinate(i));
383: }
384: return createMultiPoint(points);
385: }
386:
387: /**
388: * Constructs a <code>Polygon</code> with the given exterior boundary and
389: * interior boundaries.
390: *
391: * @param shell
392: * the outer boundary of the new <code>Polygon</code>, or
393: * <code>null</code> or an empty <code>LinearRing</code> if
394: * the empty geometry is to be created.
395: * @param holes
396: * the inner boundaries of the new <code>Polygon</code>, or
397: * <code>null</code> or empty <code>LinearRing</code> s if
398: * the empty geometry is to be created.
399: */
400: public Polygon createPolygon(LinearRing shell, LinearRing[] holes) {
401: return new Polygon(shell, holes, this );
402: }
403:
404: /**
405: * Build an appropriate <code>Geometry</code>, <code>MultiGeometry</code>, or
406: * <code>GeometryCollection</code> to contain the <code>Geometry</code>s in
407: * it.
408: * For example:<br>
409: *
410: * <ul>
411: * <li> If <code>geomList</code> contains a single <code>Polygon</code>,
412: * the <code>Polygon</code> is returned.
413: * <li> If <code>geomList</code> contains several <code>Polygon</code>s, a
414: * <code>MultiPolygon</code> is returned.
415: * <li> If <code>geomList</code> contains some <code>Polygon</code>s and
416: * some <code>LineString</code>s, a <code>GeometryCollection</code> is
417: * returned.
418: * <li> If <code>geomList</code> is empty, an empty <code>GeometryCollection</code>
419: * is returned
420: * </ul>
421: *
422: * Note that this method does not "flatten" Geometries in the input, and hence if
423: * any MultiGeometries are contained in the input a GeometryCollection containing
424: * them will be returned.
425: *
426: *@param geomList the <code>Geometry</code>s to combine
427: *@return a <code>Geometry</code> of the "smallest", "most
428: * type-specific" class that can contain the elements of <code>geomList</code>
429: * .
430: */
431: public Geometry buildGeometry(Collection geomList) {
432: Class geomClass = null;
433: boolean isHeterogeneous = false;
434: boolean hasGeometryCollection = false;
435: for (Iterator i = geomList.iterator(); i.hasNext();) {
436: Geometry geom = (Geometry) i.next();
437: Class partClass = geom.getClass();
438: if (geomClass == null) {
439: geomClass = partClass;
440: }
441: if (partClass != geomClass) {
442: isHeterogeneous = true;
443: }
444: if (geom instanceof GeometryCollection)
445: hasGeometryCollection = true;
446: }
447: // for the empty geometry, return an empty GeometryCollection
448: if (geomClass == null) {
449: return createGeometryCollection(null);
450: }
451: if (isHeterogeneous || hasGeometryCollection) {
452: return createGeometryCollection(toGeometryArray(geomList));
453: }
454: // at this point we know the collection is hetereogenous.
455: // Determine the type of the result from the first Geometry in the list
456: // this should always return a geometry, since otherwise an empty collection would have already been returned
457: Geometry geom0 = (Geometry) geomList.iterator().next();
458: boolean isCollection = geomList.size() > 1;
459: if (isCollection) {
460: if (geom0 instanceof Polygon) {
461: return createMultiPolygon(toPolygonArray(geomList));
462: } else if (geom0 instanceof LineString) {
463: return createMultiLineString(toLineStringArray(geomList));
464: } else if (geom0 instanceof Point) {
465: return createMultiPoint(toPointArray(geomList));
466: }
467: Assert.shouldNeverReachHere("Unhandled class: "
468: + geom0.getClass().getName());
469: }
470: return geom0;
471: }
472:
473: /**
474: * Creates a LineString using the given Coordinates; a null or empty array will
475: * create an empty LineString. Consecutive points must not be equal.
476: * @param coordinates an array without null elements, or an empty array, or null
477: */
478: public LineString createLineString(Coordinate[] coordinates) {
479: return createLineString(coordinates != null ? getCoordinateSequenceFactory()
480: .create(coordinates)
481: : null);
482: }
483:
484: /**
485: * Creates a LineString using the given CoordinateSequence; a null or empty CoordinateSequence will
486: * create an empty LineString. Consecutive points must not be equal.
487: * @param coordinates a CoordinateSequence possibly empty, or null
488: */
489: public LineString createLineString(CoordinateSequence coordinates) {
490: return new LineString(coordinates, this );
491: }
492:
493: /**
494: * @return a clone of g based on a CoordinateSequence created by this
495: * GeometryFactory's CoordinateSequenceFactory
496: */
497: public Geometry createGeometry(Geometry g) {
498: // could this be cached to make this more efficient? Or maybe it isn't enough overhead to bother
499: GeometryEditor editor = new GeometryEditor(this );
500: return editor.edit(g, new GeometryEditor.CoordinateOperation() {
501: public Coordinate[] edit(Coordinate[] coordinates,
502: Geometry geometry) {
503: return coordinates;
504: }
505: });
506: }
507:
508: public int getSRID() {
509: return SRID;
510: }
511:
512: private int SRID;
513:
514: public CoordinateSequenceFactory getCoordinateSequenceFactory() {
515: return coordinateSequenceFactory;
516: }
517:
518: }
|