001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2002-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.shapefile.indexed;
017:
018: import java.io.File;
019: import java.io.IOException;
020: import java.net.MalformedURLException;
021: import java.net.URL;
022: import java.net.URLDecoder;
023: import java.nio.charset.Charset;
024: import java.nio.charset.UnsupportedCharsetException;
025: import java.util.ArrayList;
026: import java.util.Arrays;
027: import java.util.Date;
028: import java.util.Iterator;
029:
030: import org.geotools.TestData;
031: import org.geotools.data.DataUtilities;
032: import org.geotools.data.DefaultQuery;
033: import org.geotools.data.DefaultTransaction;
034: import org.geotools.data.FeatureSource;
035: import org.geotools.data.FeatureStore;
036: import org.geotools.data.FeatureWriter;
037: import org.geotools.data.Query;
038: import org.geotools.data.Transaction;
039: import org.geotools.data.shapefile.ShapefileDataStore;
040: import org.geotools.factory.FactoryConfigurationError;
041: import org.geotools.feature.AttributeType;
042: import org.geotools.feature.AttributeTypeFactory;
043: import org.geotools.feature.Feature;
044: import org.geotools.feature.FeatureCollection;
045: import org.geotools.feature.FeatureCollections;
046: import org.geotools.feature.FeatureIterator;
047: import org.geotools.feature.FeatureType;
048: import org.geotools.feature.FeatureTypeFactory;
049: import org.geotools.feature.SimpleFeature;
050: import org.geotools.filter.Filter;
051: import org.geotools.filter.FilterFactory;
052: import org.geotools.filter.FilterFactoryFinder;
053: import org.geotools.filter.FilterType;
054: import org.geotools.filter.GeometryFilter;
055: import org.geotools.filter.IllegalFilterException;
056:
057: import com.vividsolutions.jts.geom.Coordinate;
058: import com.vividsolutions.jts.geom.Envelope;
059: import com.vividsolutions.jts.geom.Geometry;
060: import com.vividsolutions.jts.geom.GeometryCollection;
061: import com.vividsolutions.jts.geom.GeometryFactory;
062: import com.vividsolutions.jts.geom.PrecisionModel;
063:
064: /**
065: *
066: * @source $URL:
067: * http://svn.geotools.org/geotools/branches/shpLazyLoadingIndex/ext/shape/test/org/geotools/data/shapefile/indexed/ShapefileDataStoreTest.java $
068: * @version $Id: ShapefileDataStoreTest.java 29501 2008-02-28 02:36:47Z jgarnett $
069: * @author Ian Schneider
070: */
071: public class ShapefileDataStoreTest extends TestCaseSupport {
072: final static String STATE_POP = "shapes/statepop.shp";
073:
074: final static String STREAM = "shapes/stream.shp";
075:
076: final static String DANISH = "shapes/danish_point.shp";
077:
078: final static String CHINESE = "shapes/chinese_poly.shp";
079:
080: public ShapefileDataStoreTest(String testName) throws IOException {
081: super (testName);
082: }
083:
084: protected FeatureCollection loadFeatures(String resource, Query q)
085: throws Exception {
086: if (q == null) {
087: q = new DefaultQuery();
088: }
089:
090: URL url = TestData.url(resource);
091: IndexedShapefileDataStore s = new IndexedShapefileDataStore(url);
092: FeatureSource fs = s.getFeatureSource(s.getTypeNames()[0]);
093:
094: return fs.getFeatures(q);
095: }
096:
097: protected FeatureCollection loadFeatures(String resource,
098: Charset charset, Query q) throws Exception {
099: if (q == null)
100: q = new DefaultQuery();
101: URL url = TestData.url(resource);
102: ShapefileDataStore s = new IndexedShapefileDataStore(url, null,
103: false, true, IndexedShapefileDataStore.TREE_QIX,
104: charset);
105: FeatureSource fs = s.getFeatureSource(s.getTypeNames()[0]);
106: return fs.getFeatures(q);
107: }
108:
109: protected FeatureCollection loadFeatures(IndexedShapefileDataStore s)
110: throws Exception {
111: return s.getFeatureSource(s.getTypeNames()[0]).getFeatures();
112: }
113:
114: public void testLoad() throws Exception {
115: loadFeatures(STATE_POP, null);
116: }
117:
118: public void testLoadDanishChars() throws Exception {
119: FeatureCollection fc = loadFeatures(DANISH, null);
120: Feature first = fc.features().next();
121: // Charløtte, if you can read it with your OS charset
122: assertEquals("Charl\u00F8tte", first.getAttribute("TEKST1"));
123: }
124:
125: public void testLoadChineseChars() throws Exception {
126: try {
127: FeatureCollection fc = loadFeatures(CHINESE, Charset
128: .forName("GB18030"), null);
129: Feature first = fc.features().next();
130: String s = (String) first.getAttribute("NAME");
131: assertEquals("\u9ed1\u9f99\u6c5f\u7701", first
132: .getAttribute("NAME"));
133: } catch (UnsupportedCharsetException notInstalledInJRE) {
134: // this just means you have not installed
135: // chinese support into your JRE
136: // (as such it represents a bad configuration
137: // rather than a test failure)
138: // we only wanted to ensure that if you have Chinese support
139: // available - GeoTools can use it
140: }
141: }
142:
143: public void testSchema() throws Exception {
144: URL url = TestData.url(STATE_POP);
145: IndexedShapefileDataStore s = new IndexedShapefileDataStore(url);
146: FeatureType schema = s.getSchema(s.getTypeNames()[0]);
147: AttributeType[] types = schema.getAttributeTypes();
148: assertEquals("Number of Attributes", 253, types.length);
149: assertNotNull(schema.getDefaultGeometry().getCoordinateSystem());
150: }
151:
152: public void testSpacesInPath() throws Exception {
153: URL u = TestData.url(this , "folder with spaces/pointtest.shp");
154: File f = new File(URLDecoder.decode(u.getFile(), "UTF-8"));
155: assertTrue(f.exists());
156:
157: IndexedShapefileDataStore s = new IndexedShapefileDataStore(u);
158: loadFeatures(s);
159: }
160:
161: /**
162: * Test envelope versus old DataSource
163: */
164: public void testEnvelope() throws Exception {
165: FeatureCollection features = loadFeatures(STATE_POP, null);
166: testEnvelope(features, IndexedShapefileDataStore.TREE_GRX);
167: testEnvelope(features, IndexedShapefileDataStore.TREE_QIX);
168: testEnvelope(features, IndexedShapefileDataStore.TREE_NONE);
169: }
170:
171: private void testEnvelope(FeatureCollection features, byte treeType)
172: throws MalformedURLException, IOException {
173: IndexedShapefileDataStore s = new IndexedShapefileDataStore(
174: TestData.url(STATE_POP), null, true, true, treeType);
175: String typeName = s.getTypeNames()[0];
176: FeatureCollection all = s.getFeatureSource(typeName)
177: .getFeatures();
178:
179: assertEquals(features.getBounds(), all.getBounds());
180: }
181:
182: public void testCreateAndReadQIX() throws Exception {
183: File shpFile = copyShapefiles(STATE_POP);
184: URL url = shpFile.toURL();
185: String filename = url.getFile();
186: filename = filename.substring(0, filename.lastIndexOf("."));
187:
188: File file = new File(filename + ".qix");
189:
190: if (file.exists()) {
191: file.delete();
192: }
193: file.deleteOnExit();
194:
195: IndexedShapefileDataStore ds = new IndexedShapefileDataStore(
196: url, null, true, true,
197: IndexedShapefileDataStore.TREE_QIX);
198: FeatureCollection features = ds.getFeatureSource()
199: .getFeatures();
200: FeatureIterator indexIter = features.features();
201:
202: GeometryFactory factory = new GeometryFactory();
203: double area = Double.MAX_VALUE;
204: Feature smallestFeature = null;
205: while (indexIter.hasNext()) {
206: Feature newFeature = indexIter.next();
207:
208: double newArea = factory.toGeometry(newFeature.getBounds())
209: .getArea();
210:
211: if (smallestFeature == null || newArea < area) {
212: smallestFeature = newFeature;
213: area = newArea;
214: }
215: }
216: indexIter.close();
217:
218: IndexedShapefileDataStore ds2 = new IndexedShapefileDataStore(
219: url, null, false, false,
220: IndexedShapefileDataStore.TREE_NONE);
221:
222: Envelope newBounds = ds.getBounds(Query.ALL);
223: double dx = newBounds.getWidth() / 4;
224: double dy = newBounds.getHeight() / 4;
225: newBounds = new Envelope(newBounds.getMinX() + dx, newBounds
226: .getMaxX()
227: - dx, newBounds.getMinY() + dy, newBounds.getMaxY()
228: - dy);
229: performQueryComparison(ds, ds2, newBounds);
230: performQueryComparison(ds, ds2, smallestFeature.getBounds());
231:
232: assertTrue(file.exists());
233: }
234:
235: private ArrayList performQueryComparison(
236: IndexedShapefileDataStore indexedDS,
237: IndexedShapefileDataStore baselineDS, Envelope newBounds)
238: throws FactoryConfigurationError, IllegalFilterException,
239: IOException {
240: FeatureCollection features;
241: FeatureIterator indexIter;
242: FilterFactory fac = FilterFactoryFinder.createFilterFactory();
243: org.geotools.filter.BBoxExpression bbox = fac
244: .createBBoxExpression(newBounds);
245: org.geotools.filter.AttributeExpression attrExpression = fac
246: .createAttributeExpression(indexedDS.getSchema()
247: .getDefaultGeometry().getName());
248: GeometryFilter filter = fac
249: .createGeometryFilter(FilterType.GEOMETRY_BBOX);
250: filter.addLeftGeometry(attrExpression);
251: filter.addRightGeometry(bbox);
252: features = indexedDS.getFeatureSource().getFeatures(filter);
253: FeatureCollection features2 = baselineDS.getFeatureSource()
254: .getFeatures(filter);
255:
256: FeatureIterator baselineIter = features2.features();
257: indexIter = features.features();
258:
259: ArrayList baselineFeatures = new ArrayList();
260: ArrayList indexedFeatures = new ArrayList();
261:
262: try {
263: while (baselineIter.hasNext()) {
264: baselineFeatures.add(baselineIter.next());
265: }
266: while (indexIter.hasNext()) {
267: indexedFeatures.add(indexIter.next());
268: }
269: assertFalse(indexIter.hasNext());
270: assertFalse(baselineIter.hasNext());
271: assertEquals(baselineFeatures, indexedFeatures);
272: } finally {
273: indexIter.close();
274: baselineIter.close();
275: }
276: return indexedFeatures;
277: }
278:
279: public void testCreateAndReadGRX() throws Exception {
280: URL url = TestData.url(STATE_POP);
281: String filename = url.getFile();
282: filename = filename.substring(0, filename.lastIndexOf("."));
283:
284: File file = new File(filename + ".grx");
285:
286: if (file.exists()) {
287: file.delete();
288: }
289:
290: IndexedShapefileDataStore ds = new IndexedShapefileDataStore(
291: url, null, true, true,
292: IndexedShapefileDataStore.TREE_GRX);
293: FeatureCollection features = ds.getFeatureSource()
294: .getFeatures();
295: Iterator iter = features.iterator();
296:
297: while (iter.hasNext()) {
298: iter.next();
299: }
300:
301: // TODO: The following assertion fails
302: // assertTrue(file.exists());
303: }
304:
305: public void testLoadAndVerify() throws Exception {
306: FeatureCollection features = loadFeatures(STATE_POP, null);
307:
308: int count = features.size();
309: assertTrue("Got Features", count > 0);
310: //assertEquals("Number of Features loaded", 49, count); // FILE CORRECT
311: //assertEquals("Number of Features loaded", 3, count); // JAR WRONG
312:
313: FeatureType schema = firstFeature(features).getFeatureType();
314: assertNotNull(schema.getDefaultGeometry());
315: assertEquals("Number of Attributes", 253, schema
316: .getAttributeTypes().length);
317: assertEquals("Value of statename is wrong", firstFeature(
318: features).getAttribute("STATE_NAME"), "Illinois");
319: assertEquals("Value of land area is wrong",
320: ((Double) firstFeature(features)
321: .getAttribute("LAND_KM")).doubleValue(),
322: 143986.61, 0.001);
323: }
324:
325: private IndexedShapefileDataStore createDataStore(File f)
326: throws Exception {
327: FeatureCollection fc = createFeatureCollection();
328: f.createNewFile();
329:
330: IndexedShapefileDataStore sds = new IndexedShapefileDataStore(f
331: .toURL());
332: writeFeatures(sds, fc);
333:
334: return sds;
335: }
336:
337: private IndexedShapefileDataStore createDataStore()
338: throws Exception {
339: return createDataStore(getTempFile());
340: }
341:
342: /**
343: * Create a set of features, then remove every other one, updating the
344: * remaining. Test for removal and proper update after reloading...
345: */
346: public void testUpdating() throws Throwable {
347: try {
348: IndexedShapefileDataStore sds = createDataStore();
349: loadFeatures(sds);
350:
351: FeatureWriter writer = null;
352:
353: try {
354: writer = sds.getFeatureWriter(sds.getTypeNames()[0],
355: Filter.INCLUDE, Transaction.AUTO_COMMIT);
356:
357: while (writer.hasNext()) {
358: Feature feat = writer.next();
359: Byte b = (Byte) feat.getAttribute(1);
360:
361: if ((b.byteValue() % 2) == 0) {
362: writer.remove();
363: } else {
364: feat.setAttribute(1, new Byte((byte) -1));
365: }
366: }
367: } finally {
368: if (writer != null) {
369: writer.close();
370: }
371: }
372:
373: FeatureCollection fc = loadFeatures(sds);
374:
375: assertEquals(10, fc.size());
376:
377: for (FeatureIterator i = fc.features(); i.hasNext();) {
378: assertEquals(-1, ((Byte) i.next().getAttribute(1))
379: .byteValue());
380: }
381: } catch (Throwable t) {
382: if (System.getProperty("os.name").startsWith("Windows")) {
383: System.out.println("Ignore " + t
384: + " because you are on windows");
385:
386: return;
387: } else {
388: throw t;
389: }
390: }
391: }
392:
393: /**
394: * Create a test file, then continue removing the first entry until there
395: * are no features left.
396: */
397: public void testRemoveFromFrontAndClose() throws Throwable {
398: try {
399: IndexedShapefileDataStore sds = createDataStore();
400:
401: int idx = loadFeatures(sds).size();
402:
403: while (idx > 0) {
404: FeatureWriter writer = null;
405:
406: try {
407: writer = sds.getFeatureWriter(
408: sds.getTypeNames()[0], Filter.INCLUDE,
409: Transaction.AUTO_COMMIT);
410: writer.next();
411: writer.remove();
412: } finally {
413: if (writer != null) {
414: writer.close();
415: writer = null;
416: }
417: }
418:
419: assertEquals(--idx, loadFeatures(sds).size());
420: }
421: } catch (Throwable t) {
422: if (System.getProperty("os.name").startsWith("Windows")) {
423: System.out.println("Ignore " + t
424: + " because you are on windows");
425:
426: return;
427: } else {
428: throw t;
429: }
430: }
431: }
432:
433: /**
434: * Create a test file, then continue removing the last entry until there are
435: * no features left.
436: */
437: public void testRemoveFromBackAndClose() throws Throwable {
438: try {
439: IndexedShapefileDataStore sds = createDataStore();
440:
441: int idx = loadFeatures(sds).size();
442:
443: while (idx > 0) {
444: FeatureWriter writer = null;
445:
446: try {
447: writer = sds.getFeatureWriter(
448: sds.getTypeNames()[0], Filter.INCLUDE,
449: Transaction.AUTO_COMMIT);
450:
451: while (writer.hasNext()) {
452: writer.next();
453: }
454:
455: writer.remove();
456: } finally {
457: if (writer != null) {
458: writer.close();
459: writer = null;
460: }
461: }
462:
463: assertEquals(--idx, loadFeatures(sds).size());
464: }
465: } catch (Throwable t) {
466: if (System.getProperty("os.name").startsWith("Windows")) {
467: System.out.println("Ignore " + t
468: + " because you are on windows");
469:
470: return;
471: } else {
472: throw t;
473: }
474: }
475: }
476:
477: public void testTestTransaction() throws Exception {
478: IndexedShapefileDataStore sds = createDataStore();
479:
480: int idx = sds.getCount(Query.ALL);
481:
482: FeatureStore store = (FeatureStore) sds.getFeatureSource(sds
483: .getCurrentTypeName());
484:
485: Transaction transaction = new DefaultTransaction();
486: store.setTransaction(transaction);
487: Feature[] newFeatures1 = new Feature[1];
488: Feature[] newFeatures2 = new Feature[2];
489: GeometryFactory fac = new GeometryFactory();
490: newFeatures1[0] = DataUtilities.template(sds.getSchema());
491: newFeatures1[0].setDefaultGeometry(fac
492: .createPoint(new Coordinate(0, 0)));
493: newFeatures2[0] = DataUtilities.template(sds.getSchema());
494: newFeatures2[0].setDefaultGeometry(fac
495: .createPoint(new Coordinate(0, 0)));
496: newFeatures2[1] = DataUtilities.template(sds.getSchema());
497: newFeatures2[1].setDefaultGeometry(fac
498: .createPoint(new Coordinate(0, 0)));
499:
500: store.addFeatures(DataUtilities.collection(newFeatures1));
501: store.addFeatures(DataUtilities.collection(newFeatures2));
502: transaction.commit();
503: assertEquals(idx + 3, sds.getCount(Query.ALL));
504:
505: }
506:
507: private FeatureCollection createFeatureCollection()
508: throws Exception {
509: FeatureTypeFactory factory = FeatureTypeFactory
510: .newInstance("junk");
511: factory.addType(AttributeTypeFactory.newAttributeType("a",
512: Geometry.class));
513: factory.addType(AttributeTypeFactory.newAttributeType("b",
514: Byte.class));
515: factory.addType(AttributeTypeFactory.newAttributeType("c",
516: Short.class));
517: factory.addType(AttributeTypeFactory.newAttributeType("d",
518: Double.class));
519: factory.addType(AttributeTypeFactory.newAttributeType("e",
520: Float.class));
521: factory.addType(AttributeTypeFactory.newAttributeType("f",
522: String.class));
523: factory.addType(AttributeTypeFactory.newAttributeType("g",
524: Date.class));
525: factory.addType(AttributeTypeFactory.newAttributeType("h",
526: Boolean.class));
527: factory.addType(AttributeTypeFactory.newAttributeType("i",
528: Number.class));
529: factory.addType(AttributeTypeFactory.newAttributeType("j",
530: Long.class));
531:
532: FeatureType type = factory.getFeatureType();
533: FeatureCollection features = FeatureCollections.newCollection();
534:
535: for (int i = 0, ii = 20; i < ii; i++) {
536: features.add(type.create(new Object[] {
537: new GeometryFactory().createPoint(new Coordinate(1,
538: -1)), new Byte((byte) i),
539: new Short((short) i), new Double(i), new Float(i),
540: new String(i + " "), new Date(i),
541: new Boolean(true), new Integer(22),
542: new Long(1234567890123456789L) }));
543: }
544:
545: return features;
546: }
547:
548: public void testAttributesWriting() throws Exception {
549: FeatureCollection features = createFeatureCollection();
550: File tmpFile = getTempFile();
551: tmpFile.createNewFile();
552:
553: IndexedShapefileDataStore s = new IndexedShapefileDataStore(
554: tmpFile.toURL());
555: writeFeatures(s, features);
556: }
557:
558: public void testGeometriesWriting() throws Exception {
559: String[] wktResources = new String[] { "point", "multipoint",
560: "line", "multiline", "polygon", "multipolygon" };
561:
562: PrecisionModel pm = new PrecisionModel();
563:
564: for (int i = 0; i < wktResources.length; i++) {
565: Geometry geom = readGeometry(wktResources[i]);
566: String testName = wktResources[i];
567:
568: try {
569: runWriteReadTest(geom, false);
570: make3D(geom);
571: testName += "3d";
572: runWriteReadTest(geom, true);
573: } catch (Throwable e) {
574: throw new Exception("Error in " + testName, e);
575: }
576: }
577: }
578:
579: private void make3D(Geometry g) {
580: Coordinate[] c = g.getCoordinates();
581:
582: for (int i = 0, ii = c.length; i < ii; i++) {
583: c[i].z = 42 + i;
584: }
585: }
586:
587: private void writeFeatures(IndexedShapefileDataStore s,
588: FeatureCollection fc) throws Exception {
589: s.createSchema(fc.features().next().getFeatureType());
590:
591: FeatureWriter fw = s.getFeatureWriter(s.getTypeNames()[0],
592: Transaction.AUTO_COMMIT);
593: FeatureIterator it = fc.features();
594:
595: while (it.hasNext()) {
596: ((SimpleFeature) fw.next()).setAttributes(it.next()
597: .getAttributes(null));
598: fw.write();
599: }
600:
601: fw.close();
602: }
603:
604: private void runWriteReadTest(Geometry geom, boolean d3)
605: throws Exception {
606: // make features
607: FeatureTypeFactory factory = FeatureTypeFactory
608: .newInstance("junk");
609: factory.addType(AttributeTypeFactory.newAttributeType("a",
610: Geometry.class));
611:
612: FeatureType type = factory.getFeatureType();
613: FeatureCollection features = FeatureCollections.newCollection();
614:
615: for (int i = 0, ii = 20; i < ii; i++) {
616: features.add(type.create(new Object[] { geom.clone() }));
617: }
618:
619: // set up file
620: File tmpFile = getTempFile();
621: tmpFile.delete();
622:
623: // write features
624: IndexedShapefileDataStore s = new IndexedShapefileDataStore(
625: tmpFile.toURL());
626: s.createSchema(type);
627: writeFeatures(s, features);
628:
629: // read features
630: s = new IndexedShapefileDataStore(tmpFile.toURL());
631:
632: FeatureCollection fc = loadFeatures(s);
633: FeatureIterator fci = fc.features();
634:
635: // verify
636: while (fci.hasNext()) {
637: Feature f = fci.next();
638: Geometry fromShape = f.getDefaultGeometry();
639:
640: if (fromShape instanceof GeometryCollection) {
641: if (!(geom instanceof GeometryCollection)) {
642: fromShape = ((GeometryCollection) fromShape)
643: .getGeometryN(0);
644: }
645: }
646:
647: try {
648: Coordinate[] c1 = geom.getCoordinates();
649: Coordinate[] c2 = fromShape.getCoordinates();
650:
651: for (int cc = 0, ccc = c1.length; cc < ccc; cc++) {
652: if (d3) {
653: assertTrue(c1[cc].equals3D(c2[cc]));
654: } else {
655: assertTrue(c1[cc].equals2D(c2[cc]));
656: }
657: }
658: } catch (Throwable t) {
659: fail("Bogus : " + Arrays.asList(geom.getCoordinates())
660: + " : "
661: + Arrays.asList(fromShape.getCoordinates()));
662: }
663: }
664:
665: tmpFile.delete();
666: }
667:
668: public static void main(java.lang.String[] args) throws Exception {
669: junit.textui.TestRunner
670: .run(suite(ShapefileDataStoreTest.class));
671: }
672: }
|