001: package com.vividsolutions.jump.workbench.ui.plugin.analysis;
002:
003: import java.util.*;
004: import com.vividsolutions.jump.task.*;
005:
006: import com.vividsolutions.jts.geom.*;
007: import com.vividsolutions.jump.feature.*;
008:
009: /**
010: * Exceutes a spatial query with a given mask FeatureCollection, source FeatureCollection,
011: * and predicate.
012: * Ensures result does not contain duplicates.
013: *
014: * @author Martin Davis
015: * @version 1.2
016: */
017: public class SpatialJoinExecuter {
018: private FeatureCollection srcAFC;
019: private FeatureCollection srcBFC;
020:
021: private FeatureCollection queryFC;
022:
023: private boolean isExceptionThrown = false;
024:
025: private Geometry geoms[] = new Geometry[2];
026: private Set resultSet = new HashSet();
027:
028: public SpatialJoinExecuter(FeatureCollection srcAFC,
029: FeatureCollection srcBFC) {
030: this .srcAFC = srcAFC;
031: this .srcBFC = srcBFC;
032: }
033:
034: /**
035: * Gets the feature collection to query.
036: * A spatial index may be created if this would improve performance.
037: *
038: * @param func
039: * @return
040: */
041: private void createQueryFeatureCollection(GeometryPredicate pred) {
042: boolean buildIndex = false;
043: if (srcAFC.size() > 10)
044: buildIndex = true;
045: if (srcBFC.size() > 100)
046: buildIndex = true;
047: if (pred instanceof GeometryPredicate.DisjointPredicate)
048: buildIndex = false;
049:
050: if (buildIndex) {
051: queryFC = new IndexedFeatureCollection(srcBFC);
052: } else {
053: queryFC = srcBFC;
054: }
055: }
056:
057: private Iterator query(GeometryPredicate pred, double[] params,
058: Geometry gMask) {
059: Envelope queryEnv = gMask.getEnvelopeInternal();
060: // special hack for withinDistance
061: if (pred instanceof GeometryPredicate.WithinDistancePredicate) {
062: queryEnv.expandBy(params[0]);
063: }
064:
065: boolean useQuery = true;
066: if (pred instanceof GeometryPredicate.DisjointPredicate)
067: useQuery = false;
068:
069: Iterator queryIt = null;
070: if (useQuery) {
071: Collection queryResult = queryFC.query(queryEnv);
072: queryIt = queryResult.iterator();
073: } else {
074: queryIt = queryFC.iterator();
075: }
076: return queryIt;
077: }
078:
079: public boolean isExceptionThrown() {
080: return isExceptionThrown;
081: }
082:
083: private FeatureSchema createResultSchema() {
084: FeatureSchema resultFS = new FeatureSchema();
085: resultFS.addAttribute("GEOMETRY", AttributeType.GEOMETRY);
086: copyAttributesToSchema(srcAFC.getFeatureSchema(), "A_",
087: resultFS);
088: copyAttributesToSchema(srcBFC.getFeatureSchema(), "B_",
089: resultFS);
090: return resultFS;
091: }
092:
093: private void copyAttributesToSchema(FeatureSchema srcFS,
094: String prefix, FeatureSchema resultFS) {
095: for (int i = 0; i < srcFS.getAttributeCount(); i++) {
096: if (srcFS.getAttributeType(i) != AttributeType.GEOMETRY) {
097: resultFS.addAttribute(prefix
098: + srcFS.getAttributeName(i), srcFS
099: .getAttributeType(i));
100: }
101: }
102: }
103:
104: public FeatureCollection getResultFC() {
105: return new FeatureDataset(createResultSchema());
106: }
107:
108: private boolean isInResult(Feature f) {
109: return resultSet.contains(f);
110: }
111:
112: /**
113: * Computes geomSrc.func(geomMask)
114: *
115: * @param monitor
116: * @param func
117: * @param params
118: * @param resultFC
119: */
120: public void execute(TaskMonitor monitor, GeometryPredicate func,
121: double[] params, FeatureCollection resultFC) {
122: createQueryFeatureCollection(func);
123:
124: int total = srcAFC.size();
125: int count = 0;
126: for (Iterator iMask = srcAFC.iterator(); iMask.hasNext();) {
127:
128: monitor.report(count++, total, "features");
129: if (monitor.isCancelRequested())
130: return;
131:
132: Feature fMask = (Feature) iMask.next();
133: Geometry gMask = fMask.getGeometry();
134:
135: Iterator queryIt = query(func, params, gMask);
136: for (; queryIt.hasNext();) {
137: Feature fSrc = (Feature) queryIt.next();
138:
139: // optimization - if feature already in result no need to re-test
140: if (isInResult(fSrc))
141: continue;
142:
143: Geometry gSrc = fSrc.getGeometry();
144: geoms[0] = gSrc;
145: geoms[1] = gMask;
146: boolean isInResult = isTrue(func, gSrc, gMask, params);
147:
148: if (isInResult) {
149: addToResult(fSrc, fMask, resultFC);
150: }
151: }
152: }
153: }
154:
155: private void addToResult(Feature fA, Feature fB,
156: FeatureCollection resultFC) {
157: Feature fResult = new BasicFeature(resultFC.getFeatureSchema());
158: // for now just use A's geometry - in future make a geometry pair
159: fResult.setGeometry(fA.getGeometry());
160: // copyAttributesToFeature(fA, "A_", fResult);
161: // copyAttributesToFeature(fB, "B_", fResult);
162: // Ed Deen: Switched the above such that the right prefixes are used
163: // for the right features attribute names.
164: copyAttributesToFeature(fA, "B_", fResult);
165: copyAttributesToFeature(fB, "A_", fResult);
166: resultFC.add(fResult);
167: }
168:
169: private void copyAttributesToFeature(Feature fSrc, String prefix,
170: Feature fResult) {
171: FeatureSchema srcFS = fSrc.getSchema();
172: for (int i = 0; i < srcFS.getAttributeCount(); i++) {
173: if (srcFS.getAttributeType(i) != AttributeType.GEOMETRY) {
174: fResult.setAttribute(
175: prefix + srcFS.getAttributeName(i), fSrc
176: .getAttribute(i));
177: }
178: }
179: }
180:
181: private boolean isTrue(GeometryPredicate func, Geometry geom0,
182: Geometry geom1, double[] params) {
183: try {
184: return func.isTrue(geom0, geom1, params);
185: } catch (RuntimeException ex) {
186: // simply eat exceptions and report them by returning null
187: isExceptionThrown = true;
188: }
189: return false;
190:
191: }
192:
193: }
|