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;
009: * version 2.1 of the License.
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.data;
017:
018: import java.io.IOException;
019: import java.util.NoSuchElementException;
020:
021: import junit.framework.TestCase;
022:
023: import org.geotools.feature.Feature;
024: import org.geotools.feature.FeatureType;
025: import org.geotools.feature.IllegalAttributeException;
026: import org.geotools.feature.SimpleFeature;
027: import org.geotools.filter.FidFilter;
028: import org.opengis.filter.Filter;
029: import org.geotools.filter.FilterFactory;
030: import org.geotools.filter.FilterFactoryFinder;
031:
032: import com.vividsolutions.jts.geom.Coordinate;
033: import com.vividsolutions.jts.geom.Envelope;
034: import com.vividsolutions.jts.geom.Geometry;
035: import com.vividsolutions.jts.geom.GeometryFactory;
036: import com.vividsolutions.jts.geom.LineString;
037: import com.vividsolutions.jts.geom.LinearRing;
038: import com.vividsolutions.jts.geom.MultiLineString;
039: import com.vividsolutions.jts.geom.Polygon;
040:
041: /**
042: * A set of constructs and utility methods used to test the data module.
043: * <p>
044: * By isolating a common set of {@link Feature}s, {@link FeatureType}s and {@link Filter}s
045: * we are able to reduce the amount of overhead in setting up new tests.
046: * </p>
047: * <p>
048: * We have also special cased {@link #assertEquals(Geometry, Geometry)} to work around
049: * {@code Geometry.equals( Object )} not working as expected.
050: * </p>
051: * <p>
052: * This code has been made part of the public {@code geotools.jar} to provide
053: * a starting point for test cases involving Data constructs.
054: * </p>
055: *
056: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/main/src/main/java/org/geotools/data/DataTestCase.java $
057: * @version $Id: DataTestCase.java 22600 2006-11-04 09:37:58Z jgarnett $
058: * @author Jody Garnett, Refractions Research
059: *
060: * @todo It should be possible to move this class in the {@code sample-data} module.
061: */
062: public class DataTestCase extends TestCase {
063: protected GeometryFactory gf;
064: protected FeatureType roadType; // road: id,geom,name
065: protected FeatureType subRoadType; // road: id,geom
066: protected SimpleFeature[] roadFeatures;
067: protected Envelope roadBounds;
068: protected Envelope rd12Bounds;
069: protected Filter rd1Filter;
070: protected Filter rd2Filter;
071: protected Filter rd12Filter;
072: protected Feature newRoad;
073:
074: protected FeatureType riverType; // river: id, geom, river, flow
075: protected FeatureType subRiverType; // river: river, flow
076: protected Feature[] riverFeatures;
077: protected Envelope riverBounds;
078: protected Filter rv1Filter;
079: protected Feature newRiver;
080:
081: protected FeatureType lakeType; // lake: id, geom, name
082: protected Feature[] lakeFeatures;
083: protected Envelope lakeBounds;
084: protected FilterFactory ff;
085:
086: /**
087: * Creates a default test case with the given name.
088: */
089: public DataTestCase(final String name) {
090: super (name);
091: }
092:
093: /**
094: * Invoked before a test is run. The default implementation invokes {@link #dataSetUp}.
095: */
096: protected void setUp() throws Exception {
097: ff = FilterFactoryFinder.createFilterFactory();
098: dataSetUp();
099: }
100:
101: /**
102: * Loads the data.
103: *
104: * @see #setUp()
105: */
106: protected void dataSetUp() throws Exception {
107: String namespace = getName();
108: roadType = DataUtilities.createType(namespace + ".road",
109: "id:0,geom:LineString,name:String");
110: subRoadType = DataUtilities.createType(namespace + "road",
111: "id:0,geom:LineString");
112: gf = new GeometryFactory();
113:
114: roadFeatures = new SimpleFeature[3];
115:
116: // 3,2
117: // 2,2 +-----+-----+ 4,2
118: // / rd1 \
119: // 1,1+ +5,1
120: roadFeatures[0] = (SimpleFeature) roadType.create(new Object[] {
121: new Integer(1),
122: line(new int[] { 1, 1, 2, 2, 4, 2, 5, 1 }), "r1", },
123: "road.rd1");
124:
125: // + 3,4
126: // + 3,3
127: // rd2 + 3,2
128: // |
129: // 3,0+
130: roadFeatures[1] = (SimpleFeature) roadType.create(new Object[] {
131: new Integer(2),
132: line(new int[] { 3, 0, 3, 2, 3, 3, 3, 4 }), "r2" },
133: "road.rd2");
134:
135: // rd3 + 5,3
136: // /
137: // 3,2 +----+ 4,2
138: roadFeatures[2] = (SimpleFeature) roadType.create(new Object[] {
139: new Integer(3), line(new int[] { 3, 2, 4, 2, 5, 3 }),
140: "r3" }, "road.rd3");
141: roadBounds = new Envelope();
142: roadBounds.expandToInclude(roadFeatures[0].getBounds());
143: roadBounds.expandToInclude(roadFeatures[1].getBounds());
144: roadBounds.expandToInclude(roadFeatures[2].getBounds());
145:
146: rd1Filter = ff.createFidFilter("road.rd1");
147: rd2Filter = ff.createFidFilter("road.rd2");
148:
149: FidFilter create = ff.createFidFilter();
150: create.addFid("road.rd1");
151: create.addFid("road.rd2");
152:
153: rd12Filter = create;
154:
155: rd12Bounds = new Envelope();
156: rd12Bounds.expandToInclude(roadFeatures[0].getBounds());
157: rd12Bounds.expandToInclude(roadFeatures[1].getBounds());
158: // + 2,3
159: // / rd4
160: // + 1,2
161: newRoad = roadType.create(new Object[] { new Integer(4),
162: line(new int[] { 1, 2, 2, 3 }), "r4" }, "road.rd4");
163:
164: riverType = DataUtilities.createType(namespace + ".river",
165: "id:0,geom:MultiLineString,river:String,flow:0.0");
166: subRiverType = DataUtilities.createType(namespace + ".river",
167: "river:String,flow:0.0");
168: gf = new GeometryFactory();
169: riverFeatures = new Feature[2];
170:
171: // 9,7 13,7
172: // +------+
173: // 5,5 /
174: // +---+ rv1
175: // 7,5 \
176: // 9,3 +----+ 11,3
177: riverFeatures[0] = riverType.create(
178: new Object[] {
179: new Integer(1),
180: lines(new int[][] { { 5, 5, 7, 4 },
181: { 7, 5, 9, 7, 13, 7 },
182: { 7, 5, 9, 3, 11, 3 } }), "rv1",
183: new Double(4.5) }, "river.rv1");
184:
185: // + 6,10
186: // /
187: // rv2+ 4,8
188: // |
189: // 4,6 +
190: riverFeatures[1] = riverType.create(new Object[] {
191: new Integer(2),
192: lines(new int[][] { { 4, 6, 4, 8, 6, 10 } }), "rv2",
193: new Double(3.0) }, "river.rv2");
194: riverBounds = new Envelope();
195: riverBounds.expandToInclude(riverFeatures[0].getBounds());
196: riverBounds.expandToInclude(riverFeatures[1].getBounds());
197:
198: rv1Filter = FilterFactoryFinder.createFilterFactory()
199: .createFidFilter("river.rv1");
200:
201: // 9,5 11,5
202: // +-----+
203: // rv3 \
204: // + 13,3
205: //
206: newRiver = riverType.create(new Object[] { new Integer(3),
207: lines(new int[][] { { 9, 5, 11, 5, 13, 3 } }), "rv3",
208: new Double(1.5) }, "river.rv3");
209:
210: lakeType = DataUtilities.createType(namespace + ".lake",
211: "id:0,geom:Polygon:nillable,name:String");
212: lakeFeatures = new Feature[1];
213: // + 14,8
214: // / \
215: // 12,6 + + 16,6
216: // \ |
217: // 14,4 +-+ 16,4
218: //
219: lakeFeatures[0] = lakeType.create(new Object[] {
220: new Integer(0),
221: polygon(new int[] { 12, 6, 14, 8, 16, 6, 16, 4, 14, 4,
222: 12, 6 }), "muddy" }, "lake.lk1");
223: lakeBounds = new Envelope();
224: lakeBounds.expandToInclude(lakeFeatures[0].getBounds());
225: }
226:
227: /**
228: * Set all data references to {@code null}, allowing garbage collection.
229: * This method is automatically invoked after each test.
230: */
231: protected void tearDown() throws Exception {
232: gf = null;
233: roadType = null;
234: subRoadType = null;
235: roadFeatures = null;
236: roadBounds = null;
237: rd1Filter = null;
238: rd2Filter = null;
239: newRoad = null;
240: riverType = null;
241: subRiverType = null;
242: riverFeatures = null;
243: riverBounds = null;
244: rv1Filter = null;
245: newRiver = null;
246: }
247:
248: /**
249: * Creates a line from the specified (<var>x</var>,<var>y</var>) coordinates.
250: * The coordinates are stored in a flat array.
251: */
252: public LineString line(int[] xy) {
253: Coordinate[] coords = new Coordinate[xy.length / 2];
254:
255: for (int i = 0; i < xy.length; i += 2) {
256: coords[i / 2] = new Coordinate(xy[i], xy[i + 1]);
257: }
258:
259: return gf.createLineString(coords);
260: }
261:
262: /**
263: * Creates a multiline from the specified (<var>x</var>,<var>y</var>) coordinates.
264: */
265: public MultiLineString lines(int[][] xy) {
266: LineString[] lines = new LineString[xy.length];
267:
268: for (int i = 0; i < xy.length; i++) {
269: lines[i] = line(xy[i]);
270: }
271:
272: return gf.createMultiLineString(lines);
273: }
274:
275: /**
276: * Creates a polygon from the specified (<var>x</var>,<var>y</var>) coordinates.
277: * The coordinates are stored in a flat array.
278: */
279: public Polygon polygon(int[] xy) {
280: LinearRing shell = ring(xy);
281: return gf.createPolygon(shell, null);
282: }
283:
284: /**
285: * Creates a line from the specified (<var>x</var>,<var>y</var>) coordinates and
286: * an arbitrary amount of holes.
287: */
288: public Polygon polygon(int[] xy, int[][] holes) {
289: if (holes == null || holes.length == 0) {
290: return polygon(xy);
291: }
292: LinearRing shell = ring(xy);
293:
294: LinearRing[] rings = new LinearRing[holes.length];
295:
296: for (int i = 0; i < xy.length; i++) {
297: rings[i] = ring(holes[i]);
298: }
299: return gf.createPolygon(shell, rings);
300: }
301:
302: /**
303: * Creates a ring from the specified (<var>x</var>,<var>y</var>) coordinates.
304: * The coordinates are stored in a flat array.
305: */
306: public LinearRing ring(int[] xy) {
307: Coordinate[] coords = new Coordinate[xy.length / 2];
308:
309: for (int i = 0; i < xy.length; i += 2) {
310: coords[i / 2] = new Coordinate(xy[i], xy[i + 1]);
311: }
312:
313: return gf.createLinearRing(coords);
314: }
315:
316: /**
317: * Compares two geometries for equality.
318: */
319: protected void assertEquals(Geometry expected, Geometry actual) {
320: if (expected == actual) {
321: return;
322: }
323: assertNotNull(expected);
324: assertNotNull(actual);
325: assertTrue(expected.equals(actual));
326: }
327:
328: /**
329: * Compares two geometries for equality.
330: */
331: protected void assertEquals(String message, Geometry expected,
332: Geometry actual) {
333: if (expected == actual) {
334: return;
335: }
336: assertNotNull(message, expected);
337: assertNotNull(message, actual);
338: assertTrue(message, expected.equals(actual));
339: }
340:
341: /**
342: * Counts the number of Features returned by the specified reader.
343: * <p>
344: * This method will close the reader.
345: * </p>
346: */
347: protected int count(FeatureReader reader) throws IOException {
348: if (reader == null) {
349: return -1;
350: }
351: int count = 0;
352: try {
353: while (reader.hasNext()) {
354: reader.next();
355: count++;
356: }
357: } catch (NoSuchElementException e) {
358: // bad dog!
359: throw new DataSourceException("hasNext() lied to me at:"
360: + count, e);
361: } catch (IllegalAttributeException e) {
362: throw new DataSourceException(
363: "next() could not understand feature at:" + count,
364: e);
365: } finally {
366: reader.close();
367: }
368: return count;
369: }
370:
371: /**
372: * Counts the number of Features in the specified writer.
373: * This method will close the writer.
374: */
375: protected int count(FeatureWriter writer)
376: throws NoSuchElementException, IOException,
377: IllegalAttributeException {
378: int count = 0;
379:
380: try {
381: while (writer.hasNext()) {
382: writer.next();
383: count++;
384: }
385: } finally {
386: writer.close();
387: }
388:
389: return count;
390: }
391: }
|