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.tools;
034:
035: import java.util.Iterator;
036: import java.util.List;
037:
038: import org.apache.log4j.Logger;
039:
040: import com.vividsolutions.jts.geom.*;
041: import com.vividsolutions.jts.precision.EnhancedPrecisionOp;
042: import com.vividsolutions.jump.I18N;
043: import com.vividsolutions.jump.feature.*;
044: import com.vividsolutions.jump.task.TaskMonitor;
045:
046: /**
047: * Takes two FeatureCollections and returns their overlay, which is a new
048: * FeatureCollection containing the intersections of all pairs of input features.
049: */
050: public class OverlayEngine {
051: private static Logger LOG = Logger.getLogger(OverlayEngine.class);
052: private boolean splittingGeometryCollections = true;
053: private boolean allowingPolygonsOnly = true;
054:
055: /**
056: * Creates a new OverlayEngine.
057: */
058: public OverlayEngine() {
059: }
060:
061: /**
062: * Creates the overlay of the two datasets. The attributes from both datasets
063: * will be transferred to the overlay.
064: *
065: *@param a the first dataset involved in the overlay
066: *@param b the second dataset involved in the overlay
067: *@return intersections of all pairs of input features
068: */
069: public FeatureCollection overlay(FeatureCollection a,
070: FeatureCollection b, TaskMonitor monitor) {
071: return overlay(a, b, new AttributeMapping(a.getFeatureSchema(),
072: b.getFeatureSchema()), monitor);
073: }
074:
075: /**
076: * Creates the overlay of the two datasets. The attributes from the datasets
077: * will be transferred as specified by the AttributeMapping.
078: *
079: *@param a the first dataset involved in the overlay
080: *@param b the second dataset involved in the overlay
081: *@param mapping specifies which attributes are transferred
082: *@return intersections of all pairs of input features
083: */
084: public FeatureCollection overlay(FeatureCollection a,
085: FeatureCollection b, AttributeMapping mapping,
086: TaskMonitor monitor) {
087: monitor.allowCancellationRequests();
088: monitor
089: .report(I18N
090: .get("tools.OverlayEngine.indexing-second-feature-collection"));
091:
092: IndexedFeatureCollection indexedB = new IndexedFeatureCollection(
093: b);
094: monitor
095: .report(I18N
096: .get("tools.OverlayEngine.overlaying-feature-collections"));
097:
098: FeatureDataset overlay = new FeatureDataset(mapping
099: .createSchema("GEOMETRY"));
100: List aFeatures = a.getFeatures();
101:
102: for (int i = 0; (i < aFeatures.size())
103: && !monitor.isCancelRequested(); i++) {
104: Feature aFeature = (Feature) aFeatures.get(i);
105:
106: for (Iterator j = indexedB.query(
107: aFeature.getGeometry().getEnvelopeInternal())
108: .iterator(); j.hasNext()
109: && !monitor.isCancelRequested();) {
110: Feature bFeature = (Feature) j.next();
111: addIntersection(aFeature, bFeature, mapping, overlay,
112: monitor);
113: }
114:
115: monitor.report(i + 1, a.size(), "features");
116: }
117:
118: return overlay;
119: }
120:
121: private void addIntersection(Feature a, Feature b,
122: AttributeMapping mapping, FeatureCollection overlay,
123: TaskMonitor monitor) {
124: if (!a.getGeometry().getEnvelope().intersects(
125: b.getGeometry().getEnvelope())) {
126: return;
127: }
128:
129: Geometry intersection = null;
130:
131: try {
132: intersection = EnhancedPrecisionOp.intersection(a
133: .getGeometry(), b.getGeometry());
134: } catch (Exception ex) {
135: monitor.report(ex);
136: LOG.error(a.getGeometry());
137: LOG.error(b.getGeometry());
138: }
139:
140: if ((intersection == null) || intersection.isEmpty()) {
141: return;
142: }
143:
144: addFeature(intersection, overlay, mapping, a, b);
145: }
146:
147: protected void addFeature(Geometry intersection,
148: FeatureCollection overlay, AttributeMapping mapping,
149: Feature a, Feature b) {
150: if (splittingGeometryCollections
151: && intersection instanceof GeometryCollection) {
152: GeometryCollection gc = (GeometryCollection) intersection;
153:
154: for (int i = 0; i < gc.getNumGeometries(); i++) {
155: addFeature(gc.getGeometryN(i), overlay, mapping, a, b);
156: }
157:
158: return;
159: }
160:
161: if (allowingPolygonsOnly
162: && !(intersection instanceof Polygon || intersection instanceof MultiPolygon)) {
163: return;
164: }
165:
166: Feature feature = new BasicFeature(overlay.getFeatureSchema());
167: mapping.transferAttributes(a, b, feature);
168: feature.setGeometry(intersection);
169: overlay.add(feature);
170: }
171:
172: public void setSplittingGeometryCollections(
173: boolean splittingGeometryCollections) {
174: this .splittingGeometryCollections = splittingGeometryCollections;
175: }
176:
177: public void setAllowingPolygonsOnly(boolean allowingPolygonsOnly) {
178: this.allowingPolygonsOnly = allowingPolygonsOnly;
179: }
180: }
|