001: /*
002: * Geotools2 - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2004, 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: */
017:
018: package org.geotools.data.vpf;
019:
020: import java.io.File;
021: import java.io.IOException;
022: import java.util.HashMap;
023: import java.util.Iterator;
024: import java.util.List;
025: import java.util.Map;
026: import java.util.Vector;
027: import java.net.URI;
028:
029: import org.geotools.data.AbstractDataStore;
030: import org.geotools.data.FeatureReader;
031: import org.geotools.data.vpf.file.VPFFile;
032: import org.geotools.data.vpf.file.VPFFileFactory;
033: import org.geotools.data.vpf.ifc.FileConstants;
034: import org.geotools.data.vpf.ifc.VPFLibraryIfc;
035: import org.geotools.feature.Feature;
036: import org.geotools.feature.FeatureType;
037: import org.geotools.feature.IllegalAttributeException;
038: import org.geotools.feature.SchemaException;
039: import org.geotools.referencing.crs.DefaultGeographicCRS;
040: import org.opengis.referencing.crs.CoordinateReferenceSystem;
041:
042: /*
043: * A data store for a VPF library. A library is identified by
044: * an LHT file.
045: *
046: * VPF 101:
047: * Product: a profile of VPF data
048: * Examples include DNC, VMAP, and VITD.<b>
049: * Database: the world is separated into different databases.
050: * A database typically fits on one CD.<b>
051: * Library: each database is subdivided into libraries.
052: * Libraries may be organized into coverage groups
053: * which have different scales.
054: * The BROWSE library typically has information on the other libraries.<b>
055: * Coverage: associated feature types are grouped in coverages.
056: * Coverages share a common topological model.<b>
057: * Feature class: Feature types are grouped into classes.
058: * Feature types in a class share a common set of attributes
059: * and are stored together on disk. <b>
060: * Feature type: Feature types are denoted by a five-character FACC code.
061: * Feature types and feature classes are stored
062: * at the same directory level, inside their containing coverages.<b>
063: * File: VPF data is stored on disk in a complex hierarchy of flat files.
064: * The VPFFile AbstractDataStore can be used to extract the contents of
065: * an individual file, but this is typically useful only for testing.<b>
066: *
067: * Created on 19. april 2004, 14:53
068: *
069: * @author <a href="mailto:knuterik@onemap.org">Knut-Erik Johnsen</a>, Project OneMap
070: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/unsupported/vpf/src/main/java/org/geotools/data/vpf/VPFLibrary.java $
071: */
072: public class VPFLibrary extends AbstractDataStore implements
073: FileConstants, VPFLibraryIfc {
074:
075: /**
076: * Part of bounding box.
077: */
078: private final double xmin;
079: /**
080: * Part of bounding box.
081: */
082: private final double ymin;
083: /**
084: * Part of bounding box.
085: */
086: private final double xmax;
087: /**
088: * Part of bounding box.
089: */
090: private final double ymax;
091: /**
092: * The directory containing the library
093: */
094: private final File directory;
095: /**
096: * The name of the library
097: */
098: private final String libraryName;
099: /**
100: * The coverages that are in the library
101: */
102: private final List coverages = new Vector();
103: /**
104: * The coordinate reference system used through this library
105: */
106: private CoordinateReferenceSystem crs;
107: /**
108: * Signals if an error has already been logged for a CRS related exception
109: */
110: private boolean loggedCRSException = false;
111:
112: static URI DEFAULT_NAMESPACE;
113:
114: static {
115: try {
116: DEFAULT_NAMESPACE = new URI("http://www.vpf.org/default");
117: } catch (java.net.URISyntaxException urise) {
118: throw new RuntimeException(
119: "programmer error making default uri");
120: }
121: }
122:
123: /**
124: * The namespace to create FeatureTypes with. Set with a reasonable
125: * default of http://vpf.org/default.
126: */
127: private URI namespace = DEFAULT_NAMESPACE;
128:
129: /**
130: * Complete constructor
131: *
132: * @param libraryFeature a feature from the library attribute table
133: * @param dir the containing directory
134: *
135: * @throws IOException
136: * @throws SchemaException For problems making one of the feature classes as a FeatureType.
137: */
138: public VPFLibrary(Feature libraryFeature, File dir)
139: throws IOException, SchemaException {
140: xmin = ((Number) libraryFeature.getAttribute(FIELD_XMIN))
141: .doubleValue();
142: ymin = ((Number) libraryFeature.getAttribute(FIELD_YMIN))
143: .doubleValue();
144: xmax = ((Number) libraryFeature.getAttribute(FIELD_XMAX))
145: .doubleValue();
146: ymax = ((Number) libraryFeature.getAttribute(FIELD_YMAX))
147: .doubleValue();
148: libraryName = libraryFeature.getAttribute(FIELD_LIB_NAME)
149: .toString();
150: directory = new File(dir, libraryName);
151: setCoverages();
152: }
153:
154: /**
155: * Constructor that adds a namespace to the File only constructor. If
156: * using another constructor then use setNamespace(URI)
157: * ((javadocTODO: add the correct link to previous method.))
158: *
159: * @param dir the containing directory
160: * @throws IOException
161: * @throws SchemaException for problems making a featureType.
162: */
163: public VPFLibrary(File dir) throws IOException, SchemaException {
164: this (dir, DEFAULT_NAMESPACE);
165: }
166:
167: /**
168: * Constructor which defaults the containing database to null and looks up the first
169: * (and presumably only) entry in the library
170: * attribute table
171: * @param dir the containing directory
172: * @param namespace the namespace to create features with.
173: * @throws IOException
174: * @throws SchemaException For problems making one of the feature classes as a FeatureType.
175: */
176: public VPFLibrary(File dir, URI namespace) throws IOException,
177: SchemaException {
178: // read libraries info
179: String vpfTableName = new File(dir, LIBRARY_HEADER_TABLE)
180: .toString();
181: VPFFile lhtFile = VPFFileFactory.getInstance().getFile(
182: vpfTableName);
183: lhtFile.reset();
184: this .namespace = namespace;
185: try {
186: lhtFile.readFeature(); // check for errors
187: } catch (IllegalAttributeException exc) {
188: exc.printStackTrace();
189: throw new IOException(
190: "Illegal values in library attribute table");
191: }
192: xmin = -180;
193: ymin = -90;
194: xmax = 180;
195: ymax = 90;
196: directory = dir;
197: // What about looking up the LAT from the previous directory? Or, can you get what you need from the LHT?
198: String directoryName = directory.getPath();
199: libraryName = directoryName.substring(directoryName
200: .lastIndexOf(File.separator) + 1);
201: setCoverages();
202: }
203:
204: /**
205: * Determines the coverages contained by this library
206: * @throws IOException
207: * @throws SchemaException For problems making one of the feature classes as a FeatureType.
208: */
209: private void setCoverages() throws IOException, SchemaException {
210: VPFCoverage coverage;
211: Feature feature;
212: String directoryName;
213:
214: // I'd like to know why this if is here...
215: if (!directory.getName().equals("rference")) {
216: String vpfTableName = new File(directory,
217: COVERAGE_ATTRIBUTE_TABLE).toString();
218: VPFFile vpfFile = VPFFileFactory.getInstance().getFile(
219: vpfTableName);
220: // TableInputStream vpfTable = new TableInputStream(vpfTableName);
221: Iterator iter = vpfFile.readAllRows().iterator();
222: while (iter.hasNext()) {
223: feature = (Feature) iter.next();
224: directoryName = directory.getPath();
225: coverage = new VPFCoverage(this , feature,
226: directoryName, namespace);
227: coverages.add(coverage);
228: // Find the Tileref coverage, if any
229: if (coverage.getName().toLowerCase().equals("tileref")) {
230: createTilingSchema(coverage);
231: }
232: }
233: }
234: }
235:
236: /**
237: * Returns the coverages contained by the library
238: * @return a <code>List</code> value which contains VPFCoverage objects
239: */
240: public List getCoverages() {
241: return coverages;
242: }
243:
244: /** Getter for property xmax.
245: * @return Value of property xmax.
246: *
247: */
248: public double getXmax() {
249: return xmax;
250: }
251:
252: /** Getter for property xmin.
253: * @return Value of property xmin.
254: *
255: */
256: public double getXmin() {
257: return xmin;
258: }
259:
260: /** Getter for property ymax.
261: * @return Value of property ymax.
262: *
263: */
264: public double getYmax() {
265: return ymax;
266: }
267:
268: /** Getter for property ymin.
269: * @return Value of property ymin.
270: *
271: */
272: public double getYmin() {
273: return ymin;
274: }
275:
276: /*
277: * (non-Javadoc)
278: * @see java.lang.Object#toString()
279: */
280: public String toString() {
281: return "Dette er library : " + libraryName
282: + " with extensions:\n" + getXmin() + " " + getYmin()
283: + " - " + getXmax() + " " + getYmax() + "\n";
284: }
285:
286: /**
287: * A map containing the tiles used by this library
288: */
289: private final Map tileMap = new HashMap();
290:
291: /**
292: * Returns a map containing the tiles used by this library.
293: * The map has string keys and and string values.
294: * @return a <code>Map</code> value
295: */
296: public Map getTileMap() {
297: return tileMap;
298: }
299:
300: /**
301: * Generates the tile map for this coverage
302: * @param coverage a <code>VPFCoverage</code> which happens to be a TILEREF
303: * coverage
304: */
305: private void createTilingSchema(VPFCoverage coverage)
306: throws IOException {
307: // File tilefile = new File(directory, "tilereft.tft");
308:
309: VPFFeatureType tileType = (VPFFeatureType) coverage
310: .getFeatureTypes().get(0);
311: VPFFile tileFile = (VPFFile) tileType.getFeatureClass()
312: .getFileList().get(0);
313: Iterator rowsIter = tileFile.readAllRows().iterator();
314: while (rowsIter.hasNext()) {
315: Feature row = (Feature) rowsIter.next();
316: Short rowId = new Short(Short.parseShort(row.getAttribute(
317: "id").toString()));
318: String value = row.getAttribute(FIELD_TILE_NAME).toString();
319:
320: // Mangle tile directory from DOS style directory splits to a system
321: // specific form
322: String[] tmp = value.split("\\\\");
323: value = tmp[0];
324: for (int i = 1, ii = tmp.length; i < ii; i++) {
325: value = value.concat(File.separator).concat(tmp[i]);
326: }
327: tileMap.put(rowId, value);
328: }
329: }
330:
331: // HashMap hm = new HashMap();
332: //
333: // TableInputStream testInput = new TableInputStream(
334: // tilefile.getAbsolutePath());
335: // TableRow row = (TableRow) testInput.readRow();
336: // String tmp = null;
337: // StringBuffer buff = null;
338: //
339: // while (row != null) {
340: // tmp = row.get().toString().trim();
341: //
342: // if ((tmp != null) && (tmp.length() > 0)) {
343: // tmp = tmp.toLowerCase();
344: // buff = new StringBuffer();
345: // buff.append(tmp.charAt(0));
346: //
347: // for (int i = 1; i < tmp.length(); i++) {
348: // buff.append(File.separator);
349: // buff.append(tmp.charAt(i));
350: // }
351: //
352: // hm.put(row.get(FIELD_TILE_ID).toString().trim(),
353: // buff.toString());
354: //
355: //
356: // //System.out.println( new File( coverage, tmp.charAt(0) + File.separator + tmp.charAt(1) ).getAbsolutePath() );
357: // row = (TableRow) testInput.readRow();
358: // }
359: // }
360: //
361: // testInput.close();
362: // base.setTileMap(hm);
363: // }
364:
365: /* (non-Javadoc)
366: * @see org.geotools.data.AbstractDataStore#getTypeNames()
367: */
368: public String[] getTypeNames() {
369: // Get the type names for each coverage
370: String[] result = null;
371: int coveragesCount = coverages.size();
372: int featureTypesCount = 0;
373: int index = 0;
374: List[] coverageTypes = new List[coveragesCount];
375: for (int inx = 0; inx < coveragesCount; inx++) {
376: coverageTypes[inx] = ((VPFCoverage) coverages.get(inx))
377: .getFeatureTypes();
378: featureTypesCount += coverageTypes[inx].size();
379: }
380: result = new String[featureTypesCount];
381: for (int inx = 0; inx < coveragesCount; inx++) {
382: for (int jnx = 0; jnx < coverageTypes[inx].size(); jnx++) {
383: result[index] = ((FeatureType) coverageTypes[inx]
384: .get(jnx)).getTypeName();
385: index++;
386: }
387: }
388: return result;
389: }
390:
391: /* (non-Javadoc)
392: * @see org.geotools.data.AbstractDataStore#getSchema(java.lang.String)
393: */
394: public FeatureType getSchema(String typeName) {
395: // Look through all of the coverages to find a matching feature type
396: FeatureType result = null;
397: Iterator coverageIter = coverages.iterator();
398: Iterator featureTypesIter;
399: FeatureType temp;
400: boolean breakOut = false;
401: while (coverageIter.hasNext() && !breakOut) {
402: featureTypesIter = ((VPFCoverage) coverageIter.next())
403: .getFeatureTypes().iterator();
404: while (featureTypesIter.hasNext()) {
405: temp = (FeatureType) featureTypesIter.next();
406: if (temp.getTypeName().equals(typeName)) {
407: result = temp;
408: breakOut = true;
409: break;
410: }
411: }
412: }
413: return result;
414: }
415:
416: /* (non-Javadoc)
417: * @see org.geotools.data.AbstractDataStore#getFeatureReader(java.lang.String)
418: */
419: protected FeatureReader getFeatureReader(String typeName) {
420: // Find the appropriate feature type, make a reader for it, and reset its stream
421: FeatureReader result = null;
422: VPFFeatureType featureType = (VPFFeatureType) getSchema(typeName);
423: ((VPFFile) featureType.getFileList().get(0)).reset();
424: result = new VPFFeatureReader(featureType);
425: return result;
426: }
427:
428: /**
429: * Returns the coordinate reference system appropriate for this library.
430: * If the coordinate reference system cannot be determined null will be returned.
431: */
432: public CoordinateReferenceSystem getCoordinateReferenceSystem() {
433: if (crs == null) {
434: try {
435: // This is not really correct. It does some basic sanity checks
436: // (for GEO data type and WGE datum code), but basically assumes
437: // WGS84 data ("EPSG:4326").
438: String vpfTableName = new File(directory,
439: GEOGRAPHIC_REFERENCE_TABLE).toString();
440: VPFFile grtFile = VPFFileFactory.getInstance().getFile(
441: vpfTableName);
442: Feature grt = grtFile.getRowFromId("id", 1);
443: String dataType = String.valueOf(grt
444: .getAttribute("data_type"));
445:
446: if ("GEO".equalsIgnoreCase(dataType)) {
447: String geoDatumCode = String.valueOf(grt
448: .getAttribute("geo_datum_code"));
449: if ("WGE".equalsIgnoreCase(geoDatumCode)) {
450: crs = DefaultGeographicCRS.WGS84;
451: }
452: }
453: } catch (Exception ex) {
454: // Don't know what else can be done here, just dump it
455: if (!loggedCRSException) {
456: ex.printStackTrace();
457: loggedCRSException = true;
458: }
459: }
460: }
461: return crs;
462: }
463:
464: // public VPFFeatureClass getFeatureClass(String typename) {
465: // VPFFeatureClass tmp = null;
466: //
467: // if (coverages != null) {
468: // for (int i = 0; i < coverages.length; i++) {
469: // if (coverages[i] != null) {
470: // tmp = coverages[i].getFeatureClass(typename);
471: //
472: // if (tmp != null) {
473: // return tmp;
474: // }
475: // }
476: // }
477: // }
478: //
479: // return null;
480: // }
481: }
|