001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2003-2006, Geotools Project Managment Committee (PMC)
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation; either
009: * version 2.1 of the License, or (at your option) any later version.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: */
016: package org.geotools.feature.visitor;
017:
018: import java.io.IOException;
019: import java.util.ArrayList;
020: import java.util.HashSet;
021: import java.util.List;
022: import java.util.Set;
023:
024: import org.geotools.data.DataTestCase;
025: import org.geotools.data.DataUtilities;
026: import org.geotools.factory.CommonFactoryFinder;
027: import org.geotools.feature.FeatureCollection;
028: import org.geotools.feature.FeatureType;
029: import org.geotools.feature.SimpleFeature;
030: import org.geotools.feature.visitor.MaxVisitor.MaxResult;
031: import org.geotools.feature.visitor.MedianVisitor.MedianResult;
032: import org.geotools.feature.visitor.MinVisitor.MinResult;
033: import org.geotools.feature.visitor.UniqueVisitor.UniqueResult;
034: import org.geotools.filter.IllegalFilterException;
035: import org.opengis.filter.FilterFactory;
036: import org.opengis.filter.expression.Expression;
037:
038: import com.vividsolutions.jts.geom.Envelope;
039:
040: /**
041: * Purpose: these tests ensure the proper operation of feature visitation, with CalcResult merging too!
042: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/main/src/test/java/org/geotools/feature/visitor/VisitorCalculationTest.java $
043: */
044: public class VisitorCalculationTest extends DataTestCase {
045: FeatureCollection fc;
046: FeatureType ft;
047: FeatureCollection fc2;
048: FeatureType ft2;
049: FeatureCollection fc3;
050: FeatureType ft3;
051:
052: public VisitorCalculationTest(String arg0) {
053: super (arg0);
054: }
055:
056: protected void setUp() throws Exception {
057: super .setUp();
058: fc = DataUtilities.collection(roadFeatures);
059: fc2 = DataUtilities.collection(riverFeatures);
060: ft = roadType;
061: ft2 = riverType;
062:
063: //create our own fc3
064: FeatureType boringType = DataUtilities.createType("fc3.boring",
065: "id:0");
066: SimpleFeature[] boringFeatures = new SimpleFeature[100];
067: for (int i = 1; i <= 100; i++) {
068: boringFeatures[i - 1] = (SimpleFeature) boringType
069: .create(new Object[] { new Integer(i) });
070: }
071:
072: ft3 = boringType;
073: fc3 = DataUtilities.collection(boringFeatures);
074: }
075:
076: // test only the visitor functions themselves, and try the merge operation
077: public void testMin() throws IllegalFilterException, IOException {
078: //index 0 is the id field, so the data isn't terribly exciting (1,2,3).
079: MinVisitor minVisitor = new MinVisitor(0, ft);
080: fc.accepts(minVisitor, null);
081: MinVisitor minVisitor2 = new MinVisitor(0, ft2);
082: fc2.accepts(minVisitor2, null);
083: //1 is min
084: Object result = minVisitor.getResult().getValue();
085: int value = ((Integer) result).intValue();
086: assertEquals(1, value);
087: int value2 = minVisitor.getResult().toInt();
088: assertEquals(1, value2);
089: //min of 1 and 1 is 1
090: CalcResult minResult1 = minVisitor.getResult();
091: CalcResult minResult2 = minVisitor2.getResult();
092: CalcResult minResult3 = minResult1.merge(minResult2);
093: assertEquals(1, minResult3.toInt());
094: //test for destruction during merge
095: CalcResult minResult4 = new MinResult((Comparable) new Integer(
096: 10));
097: CalcResult minResult5 = minResult4.merge(minResult1);
098: assertEquals(1, minResult5.toInt());
099: assertEquals(10, minResult4.toInt());
100: //test negative result
101: CalcResult minResult6 = new MinResult((Comparable) new Integer(
102: -5));
103: CalcResult minResult7 = (MinResult) minResult1
104: .merge(minResult6);
105: assertEquals(-5, minResult7.toInt());
106: assertEquals(-5, minResult6.toInt());
107: //test a mock optimization
108: minVisitor.setValue(new Integer(-50));
109: minResult1 = minVisitor.getResult();
110: minResult7 = minResult7.merge(minResult1);
111: assertEquals(-50, minResult7.toInt());
112: //test varying data types
113: minVisitor.setValue(new Double(-100.0));
114: minResult1 = minVisitor.getResult();
115: minResult7 = minResult7.merge(minResult1);
116: assertEquals(-100.0, minResult7.toDouble(), 0);
117: assertEquals(-100, minResult7.toInt());
118: }
119:
120: public void testMax() throws IllegalFilterException, IOException {
121: //index 0 is the id field, so the data isn't terribly exciting
122: MaxVisitor maxVisitor = new MaxVisitor(0, ft);
123: fc.accepts(maxVisitor, null); //1,2,3
124: MaxVisitor maxVisitor2 = new MaxVisitor(3, ft2);
125: fc2.accepts(maxVisitor2, null); //3,4.5
126: //3 is max
127: int value1 = maxVisitor.getResult().toInt();
128: assertEquals(3, value1);
129: //4.5 is max
130: double value2 = maxVisitor2.getResult().toDouble();
131: assertEquals((double) 4.5, value2, 0);
132: //max of 3 and 4.5 is 4.5
133: CalcResult maxResult1 = (MaxResult) maxVisitor.getResult();
134: CalcResult maxResult2 = (MaxResult) maxVisitor2.getResult();
135: CalcResult maxResult3 = (MaxResult) maxResult1
136: .merge(maxResult2);
137: assertEquals((double) 4.5, maxResult3.toDouble(), 0);
138: //test for destruction during merge
139: CalcResult maxResult4 = new MaxResult(
140: (Comparable) new Double(2));
141: CalcResult maxResult5 = (MaxResult) maxResult4
142: .merge(maxResult1);
143: assertEquals(3, maxResult5.toDouble(), 0);
144: assertEquals(2, maxResult4.toDouble(), 0);
145: //test negative result
146: CalcResult maxResult6 = new MaxResult((Comparable) new Integer(
147: -5));
148: CalcResult maxResult7 = (MaxResult) maxResult1
149: .merge(maxResult6);
150: assertEquals(3, maxResult7.toDouble(), 0);
151: assertEquals(-5, maxResult6.toDouble(), 0);
152: //test a mock optimization
153: maxVisitor.setValue(new Double(544));
154: maxResult1 = maxVisitor.getResult();
155: maxResult7 = maxResult7.merge(maxResult1);
156: assertEquals(544, maxResult7.toDouble(), 0);
157: //test varying data types
158: maxVisitor.setValue(new Long(6453));
159: maxResult1 = maxVisitor.getResult();
160: maxResult7 = maxResult7.merge(maxResult1);
161: assertEquals(6453, maxResult7.toDouble(), 0);
162: assertEquals(6453, maxResult7.toInt());
163: }
164:
165: public void testMedian() throws IllegalFilterException, IOException {
166: MedianVisitor medianVisitor1 = new MedianVisitor(0, ft);
167: fc.accepts(medianVisitor1, null); //1,2,3
168: MedianVisitor medianVisitor2 = new MedianVisitor(0, ft2);
169: fc2.accepts(medianVisitor2, null); //3,4.5
170: //1,2,3 --> 2, 1,2 --> 1.5
171: CalcResult medianResult1 = medianVisitor1.getResult();
172: CalcResult medianResult2 = medianVisitor2.getResult();
173: assertEquals(2, medianResult1.toInt());
174: assertEquals(1.5, medianResult2.toDouble(), 0);
175: //1,1,2,2,3 --> 2
176: CalcResult medianResult3 = medianResult1.merge(medianResult2);
177: assertEquals(2, medianResult3.toDouble(), 0);
178: //test for destruction during merge
179: List vals = new ArrayList();
180: vals.add(new Double(2.5));
181: vals.add(new Double(3.5));
182: vals.add(new Double(4.5));
183: CalcResult medianResult4 = new MedianResult(vals);
184: CalcResult medianResult5 = medianResult4.merge(medianResult1);
185: assertEquals(2.75, medianResult5.toDouble(), 0);
186: assertEquals(3.5, medianResult4.toDouble(), 0);
187: //test a mock optimization
188: medianVisitor1.setValue(new Double(544));
189: medianResult1 = medianVisitor1.getResult();
190: try {
191: medianResult3 = medianResult5.merge(medianResult1);
192: fail(); //merge should fail
193: } catch (Exception e) {
194: assertEquals("Optimized median results cannot be merged.",
195: e.getMessage());
196: }
197: }
198:
199: public void testSum() throws IllegalFilterException, IOException {
200: SumVisitor sumVisitor = new SumVisitor(0, ft);
201: fc.accepts(sumVisitor, null); //1,2,3
202: SumVisitor sumVisitor2 = new SumVisitor(3, ft2);
203: fc2.accepts(sumVisitor2, null); //3,4.5
204: //6 is sum
205: int value1 = sumVisitor.getResult().toInt();
206: assertEquals(6, value1);
207: //7.5 is sum
208: double value2 = sumVisitor2.getResult().toDouble();
209: assertEquals((double) 7.5, value2, 0);
210: //sum of 6 and 7.5 is 13.5
211: CalcResult sumResult1 = sumVisitor.getResult();
212: CalcResult sumResult2 = sumVisitor2.getResult();
213: CalcResult sumResult3 = sumResult1.merge(sumResult2);
214: assertEquals((double) 13.5, sumResult3.toDouble(), 0);
215: //test a mock optimization
216: sumVisitor2.setValue(new Integer(-42));
217: CalcResult sumResult4 = sumVisitor2.getResult();
218: CalcResult sumResult5 = sumResult3.merge(sumResult4);
219: assertEquals(-28.5, sumResult5.toDouble(), 0);
220: //test for destruction during merge
221: assertEquals(13.5, sumResult3.toDouble(), 0);
222: assertEquals(-42.0, sumResult4.toDouble(), 0);
223: }
224:
225: public void testCount() throws IllegalFilterException, IOException {
226: CountVisitor countVisitor = new CountVisitor();
227: fc.accepts(countVisitor, null);
228: CountVisitor countVisitor2 = new CountVisitor();
229: fc2.accepts(countVisitor2, null);
230: //3 features
231: int value1 = countVisitor.getResult().toInt();
232: assertEquals(3, value1);
233: //2 features
234: int value2 = countVisitor2.getResult().toInt();
235: assertEquals(2, value2);
236: //merge = 5 features
237: CalcResult countResult1 = countVisitor.getResult();
238: CalcResult countResult2 = countVisitor2.getResult();
239: CalcResult countResult3 = countResult1.merge(countResult2);
240: assertEquals(5, countResult3.toInt());
241: //test a mock optimization
242: countVisitor.setValue(20);
243: CalcResult countResult4 = countVisitor.getResult();
244: assertEquals(20, countResult4.toInt());
245: //test for destruction during merge
246: CalcResult countResult5 = countResult4.merge(countResult3);
247: assertEquals(5, countResult3.toInt());
248: assertEquals(20, countResult4.toInt());
249: assertEquals(25, countResult5.toInt());
250: }
251:
252: public void testAverage() throws IllegalFilterException,
253: IOException {
254: AverageVisitor averageVisitor = new AverageVisitor(0, ft);
255: fc.accepts(averageVisitor, null); //1,2,3
256: AverageVisitor averageVisitor2 = new AverageVisitor(3, ft2);
257: fc2.accepts(averageVisitor2, null); //3,4.5
258: //2 is average
259: int value1 = averageVisitor.getResult().toInt();
260: assertEquals(2, value1);
261: //3.75 is average
262: double value2 = averageVisitor2.getResult().toDouble();
263: assertEquals((double) 3.75, value2, 0);
264: //average of 1,2,3,3,4.5 is 2.7
265: CalcResult averageResult1 = averageVisitor.getResult();
266: CalcResult averageResult2 = averageVisitor2.getResult();
267: CalcResult averageResult3 = averageResult1
268: .merge(averageResult2);
269: assertEquals((double) 2.7, averageResult3.toDouble(), 0);
270: //test for destruction during merge
271: assertEquals((double) 3.75, averageResult2.toDouble(), 0);
272: //test mock optimizations
273: averageVisitor2.setValue(5, new Integer(100)); //mergeable optimization
274: averageResult2 = averageVisitor2.getResult();
275: assertEquals(20, averageResult2.toInt());
276: averageResult3 = averageResult1.merge(averageResult2);
277: assertEquals((double) 13.25, averageResult3.toDouble(), 0);
278: averageVisitor2.setValue(new Double(15.4)); //un-mergeable optimization
279: averageResult2 = averageVisitor2.getResult();
280: assertEquals((double) 15.4, averageResult2.toDouble(), 0);
281: try {
282: averageResult3 = averageResult1.merge(averageResult2);
283: fail(); //merge should throw an exception
284: } catch (Exception e) {
285: assertEquals("Optimized average results cannot be merged.",
286: e.getMessage());
287: }
288: //throw a monkey in the wrench (combine number classes)
289: averageVisitor.setValue(5, new Integer(10));
290: averageResult1 = averageVisitor.getResult();
291: averageVisitor2.setValue(5, new Double(33.3));
292: averageResult2 = averageVisitor2.getResult();
293: averageResult3 = averageResult1.merge(averageResult2); //int + double --> double?
294: assertEquals((double) 4.33, averageResult3.toDouble(), 0);
295: }
296:
297: public void testUnique() throws IllegalFilterException, IOException {
298: UniqueVisitor uniqueVisitor = new UniqueVisitor(0, ft);
299: fc.accepts(uniqueVisitor, null);
300: UniqueVisitor uniqueVisitor2 = new UniqueVisitor(3, ft2);
301: fc2.accepts(uniqueVisitor2, null);
302: //1, 2, 3
303: Set value1 = uniqueVisitor.getResult().toSet();
304: assertEquals(3, value1.size()); //3 items in the set
305: //3.0, 4.5
306: Object[] value2 = uniqueVisitor2.getResult().toArray();
307: assertEquals(2, value2.length); //2 items in the set
308: //test a merge
309: CalcResult uniqueResult1 = uniqueVisitor.getResult();
310: CalcResult uniqueResult2 = uniqueVisitor2.getResult();
311: CalcResult uniqueResult3 = uniqueResult1.merge(uniqueResult2);
312: assertEquals(5, uniqueResult3.toSet().size()); //3 and 3.0 are different, so there are actually 5
313: //ensure merge was not destructive
314: assertEquals(3, uniqueResult1.toSet().size());
315: //test a merge with duplicate elements
316: Set anotherSet = new HashSet();
317: anotherSet.add(new Integer(2));
318: anotherSet.add(new Integer(4));
319: CalcResult uniqueResult4 = new UniqueResult(anotherSet);
320: CalcResult uniqueResult5 = uniqueResult1.merge(uniqueResult4); //1,2,3 + 2,4
321: assertEquals(4, uniqueResult5.toSet().size());
322: //mock optimization
323: uniqueVisitor.setValue(anotherSet);
324: uniqueResult1 = uniqueVisitor.getResult();
325: assertEquals(anotherSet, uniqueResult1.toSet());
326: //int + double --> ?
327: uniqueResult3 = uniqueResult2.merge(uniqueResult1);
328: Object[] array = uniqueResult3.toArray();
329: assertEquals(3.0, ((Double) array[0]).doubleValue(), 0);
330: assertEquals(2, ((Integer) array[1]).intValue(), 0);
331: assertEquals(4, ((Integer) array[2]).intValue(), 0);
332: assertEquals(4.5, ((Double) array[3]).doubleValue(), 0);
333: }
334:
335: public void testBounds() throws IOException {
336: BoundsVisitor boundsVisitor1 = new BoundsVisitor();
337: fc.accepts(boundsVisitor1, null);
338: BoundsVisitor boundsVisitor2 = new BoundsVisitor();
339: fc2.accepts(boundsVisitor2, null);
340: Envelope env1 = new Envelope(1, 5, 0, 4);
341: CalcResult boundsResult1 = boundsVisitor1.getResult();
342: assertEquals(env1, boundsResult1.toEnvelope());
343: Envelope env2 = new Envelope(4, 13, 3, 10);
344: CalcResult boundsResult2 = boundsVisitor2.getResult();
345: assertEquals(env2, boundsResult2.toEnvelope());
346: CalcResult boundsResult3 = boundsResult2.merge(boundsResult1);
347: Envelope env3 = new Envelope(1, 13, 0, 10);
348: assertEquals(env3, boundsResult3.toEnvelope());
349: }
350:
351: public void testQuantileList() throws Exception {
352: FilterFactory factory = CommonFactoryFinder
353: .getFilterFactory(null);
354: Expression expr = factory.property(ft.getAttributeType(0)
355: .getName());
356: QuantileListVisitor visitor = new QuantileListVisitor(expr, 2);
357: fc.accepts(visitor, null);
358: List[] qResult = (List[]) visitor.getResult().getValue();
359: assertEquals(2, qResult.length);
360: assertEquals(2, qResult[0].size());
361: assertEquals(1, qResult[1].size());
362: }
363:
364: public void testStandardDeviation() throws Exception {
365: FilterFactory factory = CommonFactoryFinder
366: .getFilterFactory(null);
367: Expression expr = factory.property(ft3.getAttributeType(0)
368: .getName());
369: AverageVisitor visit1 = new AverageVisitor(expr);
370: fc3.accepts(visit1, null);
371: double average = visit1.getResult().toDouble();
372: System.out.println("AV=" + average);
373: StandardDeviationVisitor visit2 = new StandardDeviationVisitor(
374: expr, average);
375: fc3.accepts(visit2, null);
376: assertEquals(28.86, visit2.getResult().toDouble(), 0.01); //TODO: verify std_dev(1..100) =~ 28.86
377: }
378:
379: //try merging a count and sum to get an average, both count+sum and sum+count
380: public void testCountSumMerge() throws IllegalFilterException,
381: IOException {
382: CountVisitor countVisitor = new CountVisitor();
383: fc2.accepts(countVisitor, null); //count = 2
384: SumVisitor sumVisitor = new SumVisitor(3, ft2);
385: fc2.accepts(sumVisitor, null); //sum = 7.5
386: CalcResult countResult = countVisitor.getResult();
387: CalcResult sumResult = sumVisitor.getResult();
388: CalcResult averageResult1 = countResult.merge(sumResult);
389: CalcResult averageResult2 = sumResult.merge(countResult);
390: //both average results were correct?
391: assertEquals((double) 3.75, averageResult1.toDouble(), 0);
392: assertEquals((double) 3.75, averageResult2.toDouble(), 0);
393: //neither sum nor count was destroyed?
394: assertEquals(2, countResult.toInt());
395: assertEquals((double) 7.5, sumResult.toDouble(), 0);
396: }
397:
398: //try merging 2 incompatible CalcResults and check for the exception
399: public void testBadMerge() throws IllegalFilterException,
400: IOException {
401: //count + max = boom!
402: CountVisitor countVisitor = new CountVisitor();
403: countVisitor.setValue(8);
404: CalcResult countResult = countVisitor.getResult();
405: MaxVisitor maxVisitor = new MaxVisitor((Expression) null);
406: maxVisitor.setValue(new Double(99));
407: CalcResult maxResult = maxVisitor.getResult();
408: try {
409: CalcResult boomResult = maxResult.merge(countResult);
410: fail(); //merge should throw an exception
411: } catch (Exception e) {
412: assertEquals("Parameter is not a compatible type", e
413: .getMessage());
414: }
415: }
416: }
|