/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2004-2008, Open Source Geospatial Foundation (OSGeo) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. */ package org.geotools.geometry.jts; import java.util.ArrayList; import java.util.List; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.GeometryCollection; import com.vividsolutions.jts.geom.GeometryFactory; import com.vividsolutions.jts.geom.LineString; import com.vividsolutions.jts.geom.MultiLineString; import com.vividsolutions.jts.geom.MultiPoint; import com.vividsolutions.jts.geom.MultiPolygon; import com.vividsolutions.jts.geom.Point; import com.vividsolutions.jts.geom.Polygon; import com.vividsolutions.jts.geom.impl.PackedCoordinateSequence; import com.vividsolutions.jts.geom.impl.PackedCoordinateSequenceFactory; /** * A stateful geometry collector that will add all geometries into a single resulting geometry * collection with the following properties: * <ul> * <li>the elements of the resulting geometry are simple geometries, adding a geometry collection * will result in it being flattened</li> * <li>the resulting geometry collection type will match its contents, a generic geometry collection * will be used only in case of heterogeneous contents</li> * <li>all geometries will be cloned using the provided geometry factory (one based on a * {@link PackedCoordinateSequence} is used by default to reduce memory usage) * </ul> * * @author Andrea Aime - GeoSolutions * * * @source $URL: http://svn.osgeo.org/geotools/trunk/modules/library/main/src/main/java/org/geotools/geometry/jts/GeometryCollector.java $ */ public class GeometryCollector { List<Geometry> geometries = new ArrayList<Geometry>(); GeometryFactory factory = new GeometryFactory(new PackedCoordinateSequenceFactory()); long coordinates = 0; long maxCoordinates = -1; /** * Returns the maximum number of coordinates this collector is allowed to keep in the resulting * geometry * * @return */ public long getMaxCoordinates() { return maxCoordinates; } /** * Sets the maximum number of coordinates to be collected. By default is -1, no limit * * @param maxCoordinates */ public void setMaxCoordinates(long maxCoordinates) { this.maxCoordinates = maxCoordinates; } /** * Returns the geometry factory used to deep clone the geometries while collecting them (if null * no cloning will happen) * * @return */ public GeometryFactory getFactory() { return factory; } /** * Sets the geometry factory used to deep clone the geometries while collecting them. May be set * to null to avoid deep cloning. By default a geometry factory based on * {@link PackedCoordinateSequenceFactory} is used to minimize the memory usage * * @param factory */ public void setFactory(GeometryFactory factory) { this.factory = factory; } /** * Returns a geometry collection containing all of the geometries collected in the process * * @return */ public GeometryCollection collect() { // empty case, we return an empty collection (it's what JTS returns when a geometry // operation returns an empty result) if (geometries.isEmpty()) { return new GeometryCollection(null, factory == null ? new GeometryFactory() : factory); } // use or guess the geometry factory GeometryFactory gf = factory; if (gf == null) { gf = geometries.get(0).getFactory(); } if (gf == null) { gf = new GeometryFactory(); } // build the final collection Class collectionClass = guessCollectionType(); if (collectionClass == MultiPoint.class) { Point[] array = (Point[]) geometries.toArray(new Point[geometries.size()]); return gf.createMultiPoint(array); } else if (collectionClass == MultiPolygon.class) { Polygon[] array = (Polygon[]) geometries.toArray(new Polygon[geometries.size()]); return gf.createMultiPolygon(array); } else if (collectionClass == MultiLineString.class) { LineString[] array = (LineString[]) geometries .toArray(new LineString[geometries.size()]); return gf.createMultiLineString(array); } else { Geometry[] array = (Geometry[]) geometries.toArray(new Geometry[geometries.size()]); return gf.createGeometryCollection(array); } } private Class guessCollectionType() { // empty set? then we'll return an empty point collection if (geometries == null || geometries.size() == 0) { return GeometryCollection.class; } // see if all are of the same base geometric type Class result = baseType(geometries.get(0).getClass()); for (int i = 1; i < geometries.size(); i++) { Class curr = geometries.get(i).getClass(); if (curr != result && !(result.isAssignableFrom(curr))) { return GeometryCollection.class; } } // return the geometric collection associated with the base type if (result == Point.class) { return MultiPoint.class; } else if (result == LineString.class) { return MultiLineString.class; } else if (result == Polygon.class) { return MultiPolygon.class; } else { return GeometryCollection.class; } } private Class baseType(Class geometry) { if (Polygon.class.isAssignableFrom(geometry)) { return Polygon.class; } else if (LineString.class.isAssignableFrom(geometry)) { return LineString.class; } else if (Point.class.isAssignableFrom(geometry)) { return Point.class; } else { return geometry; } } /** * Adds a geometry to the collector * * @param g * @param result */ public void add(Geometry g) { if (g == null) { return; } else if (g instanceof GeometryCollection) { GeometryCollection gc = (GeometryCollection) g; for (int i = 0; i < gc.getNumGeometries(); i++) { add(gc.getGeometryN(i)); } } else { coordinates += g.getNumPoints(); if (maxCoordinates > 0 && coordinates > maxCoordinates) { throw new IllegalStateException( "Max number of collected ordinates has been exceeded. Current count is " + coordinates + ", max count is " + maxCoordinates); } // apply the geometry factory if possible (this ensures the proper coordinate sequence // usage) if (factory != null) { g = factory.createGeometry(g); } geometries.add(g); } } }