package com.vividsolutions.jump.workbench.ui.plugin.analysis; import java.util.*; import com.vividsolutions.jump.task.*; import com.vividsolutions.jts.geom.*; import com.vividsolutions.jump.feature.*; /** * Exceutes a spatial query with a given mask FeatureCollection, source FeatureCollection, * and predicate. * Ensures result does not contain duplicates. * * @author Martin Davis * @version 1.2 */ public class SpatialJoinExecuter { private FeatureCollection srcAFC; private FeatureCollection srcBFC; private FeatureCollection queryFC; private boolean isExceptionThrown = false; private Geometry geoms[] = new Geometry[2]; private Set resultSet = new HashSet(); public SpatialJoinExecuter(FeatureCollection srcAFC, FeatureCollection srcBFC) { this.srcAFC = srcAFC; this.srcBFC = srcBFC; } /** * Gets the feature collection to query. * A spatial index may be created if this would improve performance. * * @param func * @return */ private void createQueryFeatureCollection(GeometryPredicate pred) { boolean buildIndex = false; if (srcAFC.size() > 10) buildIndex = true; if (srcBFC.size() > 100) buildIndex = true; if (pred instanceof GeometryPredicate.DisjointPredicate) buildIndex = false; if (buildIndex) { queryFC = new IndexedFeatureCollection(srcBFC); } else { queryFC = srcBFC; } } private Iterator query(GeometryPredicate pred, double[] params, Geometry gMask) { Envelope queryEnv = gMask.getEnvelopeInternal(); // special hack for withinDistance if (pred instanceof GeometryPredicate.WithinDistancePredicate) { queryEnv.expandBy(params[0]); } boolean useQuery = true; if (pred instanceof GeometryPredicate.DisjointPredicate) useQuery = false; Iterator queryIt = null; if (useQuery) { Collection queryResult = queryFC.query(queryEnv); queryIt = queryResult.iterator(); } else { queryIt = queryFC.iterator(); } return queryIt; } public boolean isExceptionThrown() { return isExceptionThrown; } private FeatureSchema createResultSchema() { FeatureSchema resultFS = new FeatureSchema(); resultFS.addAttribute("GEOMETRY", AttributeType.GEOMETRY); copyAttributesToSchema(srcAFC.getFeatureSchema(), "A_", resultFS); copyAttributesToSchema(srcBFC.getFeatureSchema(), "B_", resultFS); return resultFS; } private void copyAttributesToSchema(FeatureSchema srcFS, String prefix, FeatureSchema resultFS) { for (int i = 0; i < srcFS.getAttributeCount(); i++) { if (srcFS.getAttributeType(i) != AttributeType.GEOMETRY) { resultFS.addAttribute(prefix + srcFS.getAttributeName(i), srcFS.getAttributeType(i)); } } } public FeatureCollection getResultFC() { return new FeatureDataset(createResultSchema()); } private boolean isInResult(Feature f) { return resultSet.contains(f); } /** * Computes geomSrc.func(geomMask) * * @param monitor * @param func * @param params * @param resultFC */ public void execute(TaskMonitor monitor, GeometryPredicate func, double[] params, FeatureCollection resultFC ) { createQueryFeatureCollection(func); int total = srcAFC.size(); int count = 0; for (Iterator iMask = srcAFC.iterator(); iMask.hasNext(); ) { monitor.report(count++, total, "features"); if (monitor.isCancelRequested()) return; Feature fMask = (Feature) iMask.next(); Geometry gMask = fMask.getGeometry(); Iterator queryIt = query(func, params, gMask); for (; queryIt.hasNext(); ) { Feature fSrc = (Feature) queryIt.next(); // optimization - if feature already in result no need to re-test if (isInResult(fSrc)) continue; Geometry gSrc = fSrc.getGeometry(); geoms[0] = gSrc; geoms[1] = gMask; boolean isInResult = isTrue(func, gSrc, gMask, params); if (isInResult) { addToResult(fSrc, fMask, resultFC); } } } } private void addToResult(Feature fA, Feature fB, FeatureCollection resultFC) { Feature fResult = new BasicFeature(resultFC.getFeatureSchema()); // for now just use A's geometry - in future make a geometry pair fResult.setGeometry(fA.getGeometry()); // copyAttributesToFeature(fA, "A_", fResult); // copyAttributesToFeature(fB, "B_", fResult); // Ed Deen: Switched the above such that the right prefixes are used // for the right features attribute names. copyAttributesToFeature(fA, "B_", fResult); copyAttributesToFeature(fB, "A_", fResult); resultFC.add(fResult); } private void copyAttributesToFeature(Feature fSrc, String prefix, Feature fResult) { FeatureSchema srcFS = fSrc.getSchema(); for (int i = 0; i < srcFS.getAttributeCount(); i++) { if (srcFS.getAttributeType(i) != AttributeType.GEOMETRY) { fResult.setAttribute(prefix + srcFS.getAttributeName(i), fSrc.getAttribute(i)); } } } private boolean isTrue(GeometryPredicate func, Geometry geom0, Geometry geom1, double[] params) { try { return func.isTrue(geom0, geom1, params); } catch (RuntimeException ex) { // simply eat exceptions and report them by returning null isExceptionThrown = true; } return false; } }