001: /*
002: * Geotools2 - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2005-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: */
017: package org.geotools.data.complex;
018:
019: import java.io.IOException;
020: import java.net.URL;
021: import java.util.ArrayList;
022: import java.util.Calendar;
023: import java.util.Collections;
024: import java.util.Comparator;
025: import java.util.Date;
026: import java.util.HashMap;
027: import java.util.Iterator;
028: import java.util.List;
029: import java.util.Map;
030: import java.util.Set;
031: import java.util.Map.Entry;
032: import java.util.logging.Level;
033: import java.util.logging.Logger;
034:
035: import junit.framework.TestCase;
036:
037: import org.apache.xml.resolver.Catalog;
038: import org.apache.xml.resolver.tools.ResolvingXMLReader;
039: import org.geotools.data.DataAccessFinder;
040: import org.geotools.data.complex.config.ComplexDataStoreConfigurator;
041: import org.geotools.data.complex.config.ComplexDataStoreDTO;
042: import org.geotools.data.complex.config.EmfAppSchemaReader;
043: import org.geotools.data.complex.config.XMLConfigDigester;
044: import org.geotools.data.feature.FeatureAccess;
045: import org.geotools.data.feature.FeatureSource2;
046: import org.geotools.feature.iso.Types;
047: import org.geotools.filter.FilterFactoryImplNamespaceAware;
048: import org.geotools.gml3.bindings.GML;
049: import org.geotools.xlink.bindings.XLINK;
050: import org.opengis.feature.Attribute;
051: import org.opengis.feature.ComplexAttribute;
052: import org.opengis.feature.Feature;
053: import org.opengis.feature.FeatureCollection;
054: import org.opengis.feature.type.AttributeDescriptor;
055: import org.opengis.feature.type.AttributeType;
056: import org.opengis.feature.type.ComplexType;
057: import org.opengis.feature.type.FeatureType;
058: import org.opengis.feature.type.Name;
059: import org.opengis.filter.FilterFactory;
060: import org.opengis.filter.expression.Expression;
061: import org.opengis.filter.expression.PropertyName;
062: import org.xml.sax.Attributes;
063: import org.xml.sax.helpers.NamespaceSupport;
064:
065: /**
066: * DOCUMENT ME!
067: *
068: * @author Rob Atkinson
069: * @version $Id: TimeSeriesTest.java 29123 2008-02-07 03:44:12Z groldan $
070: * @source $URL:
071: * http://svn.geotools.org/geotools/branches/2.4.x/modules/unsupported/community-schemas/community-schema-ds/src/test/java/org/geotools/data/complex/TimeSeriesTest.java $
072: * @since 2.4
073: */
074: public class TimeSeriesTest extends TestCase {
075: private static final Logger LOGGER = org.geotools.util.logging.Logging
076: .getLogger(TimeSeriesTest.class.getPackage().getName());
077:
078: private static final String AWNS = "http://www.water.gov.au/awdip";
079:
080: private static final String CVNS = "http://www.opengis.net/cv/0.2.1";
081:
082: private static final String SANS = "http://www.opengis.net/sampling/1.0";
083:
084: private static final String OMNS = "http://www.opengis.net/om/1.0";
085:
086: private static final String SWENS = "http://www.opengis.net/swe/1.0.1";
087:
088: private static final String GMLNS = "http://www.opengis.net/gml";
089:
090: // private static final String GEONS =
091: // "http://www.seegrid.csiro.au/xml/geometry";
092:
093: final String schemaBase = "/test-data/";
094:
095: EmfAppSchemaReader reader;
096:
097: private FeatureSource2 source;
098:
099: /**
100: * DOCUMENT ME!
101: *
102: * @throws Exception
103: * DOCUMENT ME!
104: */
105: protected void setUp() throws Exception {
106: super .setUp();
107: reader = EmfAppSchemaReader.newInstance();
108:
109: // Logging.GEOTOOLS.forceMonolineConsoleOutput(Level.FINEST);
110: }
111:
112: /**
113: * DOCUMENT ME!
114: *
115: * @throws Exception
116: * DOCUMENT ME!
117: */
118: protected void tearDown() throws Exception {
119: super .tearDown();
120: }
121:
122: /**
123: * DOCUMENT ME!
124: *
125: * @param location
126: * schema location path discoverable through
127: * getClass().getResource()
128: *
129: * @throws IOException
130: * DOCUMENT ME!
131: */
132: private void loadSchema(URL location) throws IOException {
133: URL catalogLocation = getClass().getResource(
134: schemaBase + "observations.oasis.xml");
135: Catalog catalog = new ResolvingXMLReader().getCatalog();
136: catalog.getCatalogManager().setVerbosity(9);
137: catalog.parseCatalog(catalogLocation);
138:
139: reader.setCatalog(catalog);
140:
141: reader.parse(location);
142: }
143:
144: /**
145: * Tests if the schema-to-FM parsing code developed for complex datastore
146: * configuration loading can parse the GeoSciML types
147: *
148: * @throws Exception
149: */
150: public void testParseSchema() throws Exception {
151: try {
152: String schemaLocation = schemaBase
153: + "commonSchemas_new/awdip.xsd";
154: URL location = getClass().getResource(schemaLocation);
155: assertNotNull(location);
156: loadSchema(location);
157: } catch (Exception e) {
158: e.printStackTrace();
159: throw e;
160: }
161:
162: final Map typeRegistry = reader.getTypeRegistry();
163:
164: final Name typeName = Types.typeName(AWNS,
165: "SiteSinglePhenomTimeSeriesType");
166: final ComplexType testType = (ComplexType) typeRegistry
167: .get(typeName);
168:
169: List names = new ArrayList(typeRegistry.keySet());
170: Collections.sort(names, new Comparator() {
171: public int compare(Object o1, Object o2) {
172: return o1.toString().compareTo(o2.toString());
173: }
174: });
175:
176: assertNotNull(testType);
177: assertTrue(testType instanceof FeatureType);
178:
179: AttributeType super Type = testType.getSuper();
180: assertNotNull(super Type);
181:
182: Name super TypeName = Types.typeName(AWNS,
183: "SamplingSitePurposeType");
184: assertEquals(super TypeName, super Type.getName());
185: // assertTrue(superType instanceof FeatureType);
186:
187: // ensure all needed types were parsed and aren't just empty proxies
188: Map samplingProperties = new HashMap();
189:
190: // from gml:AbstractFeatureType
191: samplingProperties.put(name(GMLNS, "metaDataProperty"),
192: typeName(GMLNS, "MetaDataPropertyType"));
193: samplingProperties.put(name(GMLNS, "description"), typeName(
194: GMLNS, "StringOrRefType"));
195: samplingProperties.put(name(GMLNS, "name"), typeName(GMLNS,
196: "CodeType"));
197: samplingProperties.put(name(GMLNS, "boundedBy"), typeName(
198: GMLNS, "BoundingShapeType"));
199: samplingProperties.put(name(GMLNS, "location"), typeName(GMLNS,
200: "LocationPropertyType"));
201:
202: // aw:SamplingSiteType
203: samplingProperties.put(name(AWNS, "samplingRegimeType"), Types
204: .toTypeName(GML.CodeType));
205: samplingProperties.put(name(AWNS, "waterBodyType"), Types
206: .toTypeName(GML.CodeType));
207: samplingProperties.put(name(AWNS, "accessTypeCode"), Types
208: .toTypeName(GML.CodeType));
209:
210: // sa:SamplingPointType
211: samplingProperties.put(name(SANS, "position"), typeName(GMLNS,
212: "PointPropertyType"));
213:
214: // sa:SamplingFeatureType
215: samplingProperties.put(name(SANS, "relatedObservation"),
216: typeName(OMNS, "ObservationPropertyType"));
217: samplingProperties.put(name(SANS, "relatedSamplingFeature"),
218: typeName(SANS, "SamplingFeatureRelationPropertyType"));
219: samplingProperties.put(name(SANS, "sampledFeature"), typeName(
220: GMLNS, "FeaturePropertyType"));
221: samplingProperties.put(name(SANS, "surveyDetails"), typeName(
222: SANS, "SurveyProcedurePropertyType"));
223:
224: // sa:SiteSinglePhenomTimeSeriesType
225: samplingProperties.put(name(AWNS, "relatedObservation"),
226: typeName(AWNS, "PhenomenonTimeSeriesPropertyType"));
227:
228: assertPropertyNamesAndTypeNames(testType, samplingProperties);
229:
230: AttributeDescriptor relatedObservation = (AttributeDescriptor) Types
231: .descriptor(testType, name(AWNS, "relatedObservation"));
232: Map relatedObsProps = new HashMap();
233: relatedObsProps.put(name(AWNS, "PhenomenonTimeSeries"),
234: typeName(AWNS, "PhenomenonTimeSeriesType"));
235: ComplexType phenomenonTimeSeriesPropertyType = (ComplexType) relatedObservation
236: .type();
237:
238: assertPropertyNamesAndTypeNames(
239: phenomenonTimeSeriesPropertyType, relatedObsProps);
240:
241: AttributeDescriptor phenomenonTimeSeries = (AttributeDescriptor) Types
242: .descriptor(phenomenonTimeSeriesPropertyType, name(
243: AWNS, "PhenomenonTimeSeries"));
244: ComplexType phenomenonTimeSeriesType = (ComplexType) phenomenonTimeSeries
245: .type();
246: Map phenomenonTimeSeriesProps = new HashMap();
247: // from
248: // aw:WaterObservationType/om:TimeSeriesObsType/om:AbstractObservationType
249: // phenomenonTimeSeriesProps.put(name(OMNS, "procedure"), typeName(OMNS,
250: // "ObservationProcedurePropertyType"));
251: // phenomenonTimeSeriesProps.put(name(OMNS, "countParameter"),
252: // typeName(SWENS,
253: // "TypedCountType"));
254: // phenomenonTimeSeriesProps.put(name(OMNS, "measureParameter"),
255: // typeName(SWENS,
256: // "TypedMeasureType"));
257: // phenomenonTimeSeriesProps.put(name(OMNS, "termParameter"),
258: // typeName(SWENS,
259: // "TypedCategoryType"));
260: // phenomenonTimeSeriesProps.put(name(OMNS, "observedProperty"),
261: // typeName(SWENS,
262: // "PhenomenonPropertyType"));
263: //
264: // from PhenomenonTimeSeriesType
265: phenomenonTimeSeriesProps.put(name(AWNS, "result"), typeName(
266: CVNS, "CompactDiscreteTimeCoveragePropertyType"));
267:
268: assertPropertyNamesAndTypeNames(phenomenonTimeSeriesType,
269: phenomenonTimeSeriesProps);
270:
271: AttributeDescriptor observedProperty = (AttributeDescriptor) Types
272: .descriptor(phenomenonTimeSeriesType, name(OMNS,
273: "observedProperty"));
274:
275: ComplexType phenomenonPropertyType = (ComplexType) observedProperty
276: .type();
277:
278: assertPropertyNamesAndTypeNames(phenomenonPropertyType,
279: Collections.singletonMap(name(SWENS, "Phenomenon"),
280: typeName(SWENS, "PhenomenonType")));
281:
282: AttributeDescriptor phenomenon = (AttributeDescriptor) Types
283: .descriptor(phenomenonPropertyType, name(SWENS,
284: "Phenomenon"));
285: ComplexType phenomenonType = (ComplexType) phenomenon.type();
286: assertNotNull(phenomenonType.getSuper());
287: assertEquals(typeName(GMLNS, "DefinitionType"), phenomenonType
288: .getSuper().getName());
289:
290: Map phenomenonProps = new HashMap();
291: // from gml:DefinitionType
292: phenomenonProps.put(name(GMLNS, "metaDataProperty"), null);
293: phenomenonProps.put(name(GMLNS, "description"), null);
294: phenomenonProps.put(name(GMLNS, "name"), null);
295:
296: assertPropertyNamesAndTypeNames(phenomenonType, phenomenonProps);
297: }
298:
299: private void assertPropertyNamesAndTypeNames(
300: ComplexType parentType, Map expectedPropertiesAndTypes)
301: throws Exception {
302:
303: for (Iterator it = expectedPropertiesAndTypes.entrySet()
304: .iterator(); it.hasNext();) {
305: Map.Entry entry = (Entry) it.next();
306: Name dName = (Name) entry.getKey();
307: Name expectedDescriptorTypeName = (Name) entry.getValue();
308:
309: AttributeDescriptor d = (AttributeDescriptor) Types
310: .descriptor(parentType, dName);
311: assertNotNull("Descriptor " + dName
312: + " not found for type " + parentType.getName(), d);
313: AttributeType type;
314: try {
315: type = (AttributeType) d.type();
316: } catch (Exception e) {
317: LOGGER.log(Level.SEVERE, "type not parsed for "
318: + ((AttributeDescriptor) d).getName(), e);
319: throw e;
320: }
321: assertNotNull(type);
322: Name actualTypeName = type.getName();
323: assertNotNull(actualTypeName);
324: assertNotNull(type.getBinding());
325: if (expectedDescriptorTypeName != null) {
326: assertEquals("type mismatch for property " + dName,
327: expectedDescriptorTypeName, actualTypeName);
328: }
329: }
330: }
331:
332: private Name typeName(String ns, String localName) {
333: return Types.typeName(ns, localName);
334: }
335:
336: private Name name(String ns, String localName) {
337: return Types.typeName(ns, localName);
338: }
339:
340: public void testLoadMappingsConfig() throws Exception {
341: XMLConfigDigester reader = new XMLConfigDigester();
342: String configLocation = schemaBase
343: + "TimeSeriesTest_properties.xml";
344: URL url = getClass().getResource(configLocation);
345:
346: // configLocation =
347: // "file:/home/gabriel/svn/geoserver/trunk/configuration/community-schema-timeseries2/TimeSeriesTest_properties.xml";
348: // URL url = new URL(configLocation);
349:
350: ComplexDataStoreDTO config = reader.parse(url);
351:
352: Set mappings = ComplexDataStoreConfigurator
353: .buildMappings(config);
354:
355: assertNotNull(mappings);
356: assertEquals(1, mappings.size());
357:
358: FeatureTypeMapping mapping = (FeatureTypeMapping) mappings
359: .iterator().next();
360:
361: AttributeDescriptor targetFeature = mapping.getTargetFeature();
362: assertNotNull(targetFeature);
363: assertNotNull(targetFeature.type());
364: assertEquals(AWNS, targetFeature.getName().getNamespaceURI());
365: assertEquals("SiteSinglePhenomTimeSeries", targetFeature
366: .getName().getLocalPart());
367:
368: List attributeMappings = mapping.getAttributeMappings();
369: AttributeMapping attMapping = (AttributeMapping) attributeMappings
370: .get(0);
371: assertNotNull(attMapping);
372: assertEquals("aw:SiteSinglePhenomTimeSeries", attMapping
373: .getTargetXPath().toString());
374:
375: attMapping = (AttributeMapping) attributeMappings.get(1);
376: assertNotNull(attMapping);
377: // note the mapping says SiteSinglePhenomTimeSeries/gml:name[1] but
378: // attMapping.getTargetXPath().toString() results in a simplyfied form
379: assertEquals("gml:name", attMapping.getTargetXPath().toString());
380:
381: attMapping = (AttributeMapping) attributeMappings.get(2);
382: assertNotNull(attMapping);
383: assertEquals("sa:sampledFeature", attMapping.getTargetXPath()
384: .toString());
385: // this mapping has no source expression, just client properties
386: assertSame(Expression.NIL, attMapping.getSourceExpression());
387: assertSame(Expression.NIL, attMapping.getIdentifierExpression());
388: Map clientProperties = attMapping.getClientProperties();
389: assertEquals(2, clientProperties.size());
390:
391: Name clientPropName = name(XLINK.NAMESPACE, "title");
392: assertTrue("client property " + clientPropName + " not found",
393: clientProperties.containsKey(clientPropName));
394: clientPropName = name(XLINK.NAMESPACE, "href");
395: assertTrue("client property " + clientPropName + " not found",
396: clientProperties.containsKey(clientPropName));
397:
398: // now test the use of specific subtype overriding a general node type
399: attMapping = (AttributeMapping) attributeMappings.get(3);
400: assertNotNull(attMapping);
401: String expected = "aw:relatedObservation/aw:PhenomenonTimeSeries/om:observedProperty/swe:Phenomenon/gml:name";
402: String actual = attMapping.getTargetXPath().toString();
403: assertEquals(expected, actual);
404: }
405:
406: public void testDataStore() throws Exception {
407: FeatureAccess mappingDataStore;
408: final Name typeName = new org.geotools.feature.Name(AWNS,
409: "SiteSinglePhenomTimeSeries");
410: {
411: final Map dsParams = new HashMap();
412:
413: String configLocation = schemaBase
414: + "TimeSeriesTest_properties.xml";
415: final URL url = getClass().getResource(configLocation);
416: // configLocation =
417: // "file:/home/gabriel/svn/geoserver/trunk/configuration/community-schema-timeseries2/TimeSeriesTest_properties.xml";
418: // URL url = new URL(configLocation);
419:
420: dsParams.put("dbtype", "complex");
421: dsParams.put("url", url.toExternalForm());
422:
423: mappingDataStore = (FeatureAccess) DataAccessFinder
424: .createAccess(dsParams);
425: }
426: assertNotNull(mappingDataStore);
427: FeatureSource2 fSource;
428: {
429: AttributeDescriptor attDesc = (AttributeDescriptor) mappingDataStore
430: .describe(typeName);
431: assertNotNull(attDesc);
432: assertTrue(attDesc.type() instanceof FeatureType);
433:
434: FeatureType fType = (FeatureType) attDesc.type();
435:
436: fSource = (FeatureSource2) mappingDataStore
437: .access(typeName);
438: }
439: FeatureCollection features;
440: // make a getFeatures request with a nested properties filter.
441: // note that the expected result count is 6 - 3 sites x 2 phenomena
442: final int EXPECTED_RESULT_COUNT = 6;
443: {
444: features = (FeatureCollection) fSource.content();
445:
446: int resultCount = getCount(features);
447: String msg = "be sure difference in result count is not due to different dataset.";
448: assertEquals(msg, EXPECTED_RESULT_COUNT, resultCount);
449: }
450:
451: Feature feature;
452: int count = 0;
453:
454: FilterFactory ffac;
455: {
456: NamespaceSupport namespaces = new NamespaceSupport();
457:
458: namespaces.declarePrefix("aw", AWNS);
459: namespaces.declarePrefix("om", OMNS);
460: namespaces.declarePrefix("swe", SWENS);
461: namespaces.declarePrefix("gml", GMLNS);
462: namespaces.declarePrefix("sa", SANS);
463: // TODO: use commonfactoryfinder or the mechanism choosed
464: // to pass namespace context to filter factory
465: ffac = new FilterFactoryImplNamespaceAware(namespaces);
466: }
467:
468: final String phenomNamePath = "aw:relatedObservation/aw:PhenomenonTimeSeries/om:observedProperty/swe:Phenomenon/gml:name";
469: Iterator it = features.iterator();
470: for (; it.hasNext();) {
471: feature = (Feature) it.next();
472: count++;
473: {
474: PropertyName gmlName = ffac.property("gml:name");
475: PropertyName phenomName = ffac.property(phenomNamePath);
476:
477: Object nameVal = gmlName
478: .evaluate(feature, String.class);
479: assertNotNull("gml:name evaluated to null", nameVal);
480:
481: Object phenomNameVal = phenomName.evaluate(feature,
482: String.class);
483: assertNotNull(phenomNamePath + " evaluated to null",
484: phenomNameVal);
485: }
486: {
487: PropertyName sampledFeatureName = ffac
488: .property("sa:sampledFeature");
489: Attribute sampledFeatureVal = (Attribute) sampledFeatureName
490: .evaluate(feature);
491: assertNotNull("sa:sampledFeature evaluated to null",
492: sampledFeatureVal);
493: assertNull(sampledFeatureVal.getValue());
494: Map attributes = (Map) sampledFeatureVal
495: .getDescriptor().getUserData(Attributes.class);
496: assertNotNull(attributes);
497: Name xlinkTitle = name(XLINK.NAMESPACE, "title");
498: assertTrue(attributes.containsKey(xlinkTitle));
499: assertNotNull(attributes.get(xlinkTitle));
500:
501: Name xlinkHref = name(XLINK.NAMESPACE, "href");
502: assertTrue(attributes.containsKey(xlinkHref));
503: assertNotNull(attributes.get(xlinkHref));
504: }
505:
506: {
507: final String elementPath = "aw:relatedObservation/aw:PhenomenonTimeSeries/om:result/cv:CompactDiscreteTimeCoverage";
508: PropertyName elementName = ffac.property(elementPath);
509: Object timeCovVal = elementName.evaluate(feature);
510: assertNotNull(elementPath, timeCovVal);
511: assertTrue(timeCovVal instanceof Feature);
512: final List elements = (List) ((Feature) timeCovVal)
513: .getValue();
514: if (count == 1)
515: assertEquals(1, elements.size());
516: else if (count == 2) {
517: assertEquals(31, elements.size());
518:
519: Name compactTimeValuePairName = Types.typeName(
520: CVNS, "CompactTimeValuePair");
521: Name geometryName = Types
522: .typeName(CVNS, "geometry");
523: Name valueName = Types.typeName(CVNS, "value");
524:
525: ComplexAttribute element = (ComplexAttribute) elements
526: .get(21);
527: assertNotNull(element);
528:
529: List compactTimes = element
530: .get(compactTimeValuePairName);
531: assertNotNull(compactTimes);
532: assertEquals(1, compactTimes.size());
533:
534: ComplexAttribute compatTimeValuePair = (ComplexAttribute) compactTimes
535: .get(0);
536: List geometries = compatTimeValuePair
537: .get(geometryName);
538: List values = compatTimeValuePair.get(valueName);
539:
540: assertNotNull(geometries);
541: assertNotNull(values);
542: assertEquals(1, geometries.size());
543: assertEquals(1, values.size());
544:
545: Attribute geom = (Attribute) geometries.get(0);
546: Attribute value = (Attribute) values.get(0);
547:
548: assertNotNull(geom.getValue());
549: assertNotNull(value.getValue());
550:
551: Object valueContent = geom.getValue();
552: Date sampleTimePosition = (Date) valueContent;// 0=2007-01-01,
553: // 21=2007-01-22
554: Calendar cal = Calendar.getInstance();
555: cal.setTime(sampleTimePosition);
556: assertEquals(2007, cal.get(Calendar.YEAR));
557: assertEquals(Calendar.JANUARY, cal
558: .get(Calendar.MONTH));
559: assertEquals(22, cal.get(Calendar.DAY_OF_MONTH));
560: }
561: }
562:
563: }
564: features.close(it);
565:
566: assertEquals(EXPECTED_RESULT_COUNT, count);
567:
568: }
569:
570: private int getCount(FeatureCollection features) {
571: Iterator iterator = features.iterator();
572: int count = 0;
573: try {
574: while (iterator.hasNext()) {
575: iterator.next();
576: count++;
577: }
578: } finally {
579: features.close(iterator);
580: }
581: return count;
582: }
583: }
|