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