/*
* The Unified Mapping Platform (JUMP) is an extensible, interactive GUI
* for visualizing and manipulating spatial features with geometry and attributes.
*
* Copyright (C) 2003 Vivid Solutions
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* For more information, contact:
*
* Vivid Solutions
* Suite #1A
* 2328 Government Street
* Victoria BC V8T 5G5
* Canada
*
* (250)385-6040
* www.vividsolutions.com
*/
package com.vividsolutions.jump.tools;
import java.util.Iterator;
import java.util.List;
import org.apache.log4j.Logger;
import com.vividsolutions.jts.geom.*;
import com.vividsolutions.jts.precision.EnhancedPrecisionOp;
import com.vividsolutions.jump.I18N;
import com.vividsolutions.jump.feature.*;
import com.vividsolutions.jump.task.TaskMonitor;
/**
* Takes two FeatureCollections and returns their overlay, which is a new
* FeatureCollection containing the intersections of all pairs of input features.
*/
public class OverlayEngine {
private static Logger LOG = Logger.getLogger(OverlayEngine.class);
private boolean splittingGeometryCollections = true;
private boolean allowingPolygonsOnly = true;
/**
* Creates a new OverlayEngine.
*/
public OverlayEngine() {
}
/**
* Creates the overlay of the two datasets. The attributes from both datasets
* will be transferred to the overlay.
*
*@param a the first dataset involved in the overlay
*@param b the second dataset involved in the overlay
*@return intersections of all pairs of input features
*/
public FeatureCollection overlay(FeatureCollection a, FeatureCollection b,
TaskMonitor monitor) {
return overlay(a, b,
new AttributeMapping(a.getFeatureSchema(), b.getFeatureSchema()),
monitor);
}
/**
* Creates the overlay of the two datasets. The attributes from the datasets
* will be transferred as specified by the AttributeMapping.
*
*@param a the first dataset involved in the overlay
*@param b the second dataset involved in the overlay
*@param mapping specifies which attributes are transferred
*@return intersections of all pairs of input features
*/
public FeatureCollection overlay(FeatureCollection a, FeatureCollection b,
AttributeMapping mapping, TaskMonitor monitor) {
monitor.allowCancellationRequests();
monitor.report(I18N.get("tools.OverlayEngine.indexing-second-feature-collection"));
IndexedFeatureCollection indexedB = new IndexedFeatureCollection(b);
monitor.report(I18N.get("tools.OverlayEngine.overlaying-feature-collections"));
FeatureDataset overlay = new FeatureDataset(mapping.createSchema("GEOMETRY"));
List aFeatures = a.getFeatures();
for (int i = 0; (i < aFeatures.size()) && !monitor.isCancelRequested();
i++) {
Feature aFeature = (Feature) aFeatures.get(i);
for (Iterator j = indexedB.query(aFeature.getGeometry()
.getEnvelopeInternal())
.iterator();
j.hasNext() && !monitor.isCancelRequested();) {
Feature bFeature = (Feature) j.next();
addIntersection(aFeature, bFeature, mapping, overlay, monitor);
}
monitor.report(i + 1, a.size(), "features");
}
return overlay;
}
private void addIntersection(Feature a, Feature b,
AttributeMapping mapping, FeatureCollection overlay, TaskMonitor monitor) {
if (!a.getGeometry().getEnvelope().intersects(b.getGeometry()
.getEnvelope())) {
return;
}
Geometry intersection = null;
try {
intersection = EnhancedPrecisionOp.intersection(a.getGeometry(),
b.getGeometry());
} catch (Exception ex) {
monitor.report(ex);
LOG.error(a.getGeometry());
LOG.error(b.getGeometry());
}
if ((intersection == null) || intersection.isEmpty()) {
return;
}
addFeature(intersection, overlay, mapping, a, b);
}
protected void addFeature(Geometry intersection, FeatureCollection overlay,
AttributeMapping mapping, Feature a, Feature b) {
if (splittingGeometryCollections &&
intersection instanceof GeometryCollection) {
GeometryCollection gc = (GeometryCollection) intersection;
for (int i = 0; i < gc.getNumGeometries(); i++) {
addFeature(gc.getGeometryN(i), overlay, mapping, a, b);
}
return;
}
if (allowingPolygonsOnly && !(intersection instanceof Polygon || intersection instanceof MultiPolygon)) {
return;
}
Feature feature = new BasicFeature(overlay.getFeatureSchema());
mapping.transferAttributes(a, b, feature);
feature.setGeometry(intersection);
overlay.add(feature);
}
public void setSplittingGeometryCollections(
boolean splittingGeometryCollections) {
this.splittingGeometryCollections = splittingGeometryCollections;
}
public void setAllowingPolygonsOnly(boolean allowingPolygonsOnly) {
this.allowingPolygonsOnly = allowingPolygonsOnly;
}
}