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.HashMap;
022: import java.util.Iterator;
023: import java.util.List;
024: import java.util.Map;
025: import java.util.logging.Logger;
026:
027: import junit.framework.TestCase;
028:
029: import org.geotools.data.DataAccessFinder;
030: import org.geotools.data.DataStore;
031: import org.geotools.data.DataStoreFinder;
032: import org.geotools.data.DataUtilities;
033: import org.geotools.data.DefaultTransaction;
034: import org.geotools.data.FeatureWriter;
035: import org.geotools.data.Transaction;
036: import org.geotools.data.complex.config.EmfAppSchemaReader;
037: import org.geotools.data.feature.FeatureAccess;
038: import org.geotools.data.feature.FeatureSource2;
039: import org.geotools.data.postgis.PostgisDataStoreFactory;
040: import org.geotools.feature.iso.FeatureCollections;
041: import org.geotools.feature.iso.Types;
042: import org.geotools.filter.FilterFactoryImplNamespaceAware;
043: import org.geotools.filter.text.cql2.CQL;
044: import org.geotools.xlink.bindings.XLINK;
045: import org.opengis.feature.Attribute;
046: import org.opengis.feature.ComplexAttribute;
047: import org.opengis.feature.Feature;
048: import org.opengis.feature.FeatureCollection;
049: import org.opengis.feature.type.AttributeDescriptor;
050: import org.opengis.feature.type.FeatureType;
051: import org.opengis.feature.type.Name;
052: import org.opengis.filter.Filter;
053: import org.opengis.filter.FilterFactory;
054: import org.opengis.filter.expression.PropertyName;
055: import org.xml.sax.Attributes;
056: import org.xml.sax.helpers.NamespaceSupport;
057:
058: import com.vividsolutions.jts.geom.Coordinate;
059: import com.vividsolutions.jts.geom.GeometryFactory;
060:
061: /**
062: * DOCUMENT ME!
063: *
064: * @author Rob Atkinson
065: * @version $Id: TimeSeriesStressTest.java 29135 2008-02-07 19:49:09Z desruisseaux $
066: * @source $URL:
067: * 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 $
068: * @since 2.4
069: */
070: public class TimeSeriesStressTest extends TestCase {
071: private static final Logger LOGGER = org.geotools.util.logging.Logging
072: .getLogger(TimeSeriesStressTest.class.getPackage()
073: .getName());
074:
075: private static final String AWNS = "http://www.water.gov.au/awdip";
076:
077: private static final String CVNS = "http://www.opengis.net/cv/0.2.1";
078:
079: private static final String SANS = "http://www.opengis.net/sampling/1.0";
080:
081: private static final String OMNS = "http://www.opengis.net/om/1.0";
082:
083: private static final String SWENS = "http://www.opengis.net/swe/1.0.1";
084:
085: private static final String GMLNS = "http://www.opengis.net/gml";
086:
087: // private static final String GEONS = "http://www.seegrid.csiro.au/xml/geometry";
088:
089: final String schemaBase = "/test-data/";
090:
091: EmfAppSchemaReader reader;
092:
093: private FeatureSource2 source;
094:
095: /**
096: * DOCUMENT ME!
097: *
098: * @throws Exception
099: * DOCUMENT ME!
100: */
101: protected void setUp() throws Exception {
102: super .setUp();
103: reader = EmfAppSchemaReader.newInstance();
104:
105: // Logging.GEOTOOLS.forceMonolineConsoleOutput(Level.FINEST);
106: }
107:
108: /**
109: * DOCUMENT ME!
110: *
111: * @throws Exception
112: * DOCUMENT ME!
113: */
114: protected void tearDown() throws Exception {
115: super .tearDown();
116: }
117:
118: /**
119: * DOCUMENT ME!
120: *
121: * @param location
122: * schema location path discoverable through
123: * getClass().getResource()
124: *
125: * @throws IOException
126: * DOCUMENT ME!
127: */
128: private void loadSchema(String location) throws IOException {
129: // load needed GML types directly from the gml schemas
130: URL schemaLocation = getClass().getResource(location);
131: if (schemaLocation == null) {
132: schemaLocation = new URL(location);
133: }
134: assertNotNull(location, schemaLocation);
135: reader.parse(schemaLocation);
136: }
137:
138: private Name name(String ns, String localName) {
139: return Types.typeName(ns, localName);
140: }
141:
142: /**
143: * A rough stress test for timeseries mapping
144: *
145: * @throws Exception
146: */
147: public void testStressDataStore() throws Exception {
148: final Map dsParams = new HashMap();
149: String configLocation = schemaBase
150: + "TimeSeriesTest_properties2.xml";
151: final URL url = getClass().getResource(configLocation);
152:
153: dsParams.put("dbtype", "complex");
154: dsParams.put("url", url.toExternalForm());
155:
156: final Name typeName = new org.geotools.feature.Name(AWNS,
157: "SiteSinglePhenomTimeSeries");
158:
159: FeatureAccess mappingDataStore = (FeatureAccess) DataAccessFinder
160: .createAccess(dsParams);
161: assertNotNull(mappingDataStore);
162:
163: AttributeDescriptor attDesc = (AttributeDescriptor) mappingDataStore
164: .describe(typeName);
165: assertNotNull(attDesc);
166: assertTrue(attDesc.type() instanceof FeatureType);
167:
168: FeatureType fType = (FeatureType) attDesc.type();
169:
170: FeatureSource2 fSource = (FeatureSource2) mappingDataStore
171: .access(typeName);
172:
173: Filter filter = CQL.toFilter("gml:name = 'stat_id_3000'");
174: FeatureCollection features = (FeatureCollection) fSource
175: .content(filter);
176: final int expectedResults = 3000;
177: final int EXPECTED_RESULT_COUNT = 1;
178: final int numberOfRuns = 5;
179:
180: // LOGGER.info("stressing getCount()...");
181: final double countTime = stressCount(features,
182: EXPECTED_RESULT_COUNT, numberOfRuns);
183: LOGGER.info("getCount() agv time: " + countTime + "ms");
184:
185: LOGGER.info("Stressing getFeatures()...");
186: final double fetchTime = stressGetFeatures(features,
187: EXPECTED_RESULT_COUNT, numberOfRuns, expectedResults);
188: LOGGER.info("getFeatures() agv time: " + fetchTime + "ms");
189: }
190:
191: private double stressGetFeatures(final FeatureCollection features,
192: final int EXPECTED_RESULT_COUNT, final int numberOfRuns,
193: final int expectedResults) {
194: double cumulativeTime = 0;
195: StopWatch timer = new StopWatch();
196:
197: NamespaceSupport namespaces = new NamespaceSupport();
198: namespaces.declarePrefix("aw", AWNS);
199: namespaces.declarePrefix("om", OMNS);
200: namespaces.declarePrefix("swe", SWENS);
201: namespaces.declarePrefix("gml", GMLNS);
202: namespaces.declarePrefix("sa", SANS);
203: // TODO: use commonfactoryfinder or the mechanism choosed
204: // to pass namespace context to filter factory
205: FilterFactory ffac = new FilterFactoryImplNamespaceAware(
206: namespaces);
207:
208: Feature feature = null;
209: int count = 0;
210: final String phenomNamePath = "aw:relatedObservation/aw:PhenomenonTimeSeries/om:observedProperty/swe:Phenomenon/gml:name";
211:
212: for (int run = 0; run < numberOfRuns; run++) {
213: Iterator it = features.iterator();
214: count = 0;
215: timer.start();
216: for (; it.hasNext();) {
217: feature = (Feature) it.next();
218: count++;
219: }
220: timer.stop();
221: cumulativeTime += timer.time();
222: features.close(it);
223: }
224:
225: PropertyName gmlName = ffac.property("gml:name");
226: PropertyName phenomName = ffac.property(phenomNamePath);
227:
228: Object nameVal = gmlName.evaluate(feature, String.class);
229: assertNotNull("gml:name evaluated to null", nameVal);
230:
231: Object phenomNameVal = phenomName.evaluate(feature,
232: String.class);
233: assertNotNull(phenomNamePath + " evaluated to null",
234: phenomNameVal);
235:
236: PropertyName sampledFeatureName = ffac
237: .property("sa:sampledFeature");
238: Attribute sampledFeatureVal = (Attribute) sampledFeatureName
239: .evaluate(feature);
240: assertNotNull("sa:sampledFeature evaluated to null",
241: sampledFeatureVal);
242: assertNull(sampledFeatureVal.getValue());
243: Map attributes = (Map) sampledFeatureVal.getDescriptor()
244: .getUserData(Attributes.class);
245: assertNotNull(attributes);
246: Name xlinkTitle = name(XLINK.NAMESPACE, "title");
247: assertTrue(attributes.containsKey(xlinkTitle));
248: assertNotNull(attributes.get(xlinkTitle));
249:
250: Name xlinkHref = name(XLINK.NAMESPACE, "href");
251: assertTrue(attributes.containsKey(xlinkHref));
252: assertNotNull(attributes.get(xlinkHref));
253:
254: assertEquals(EXPECTED_RESULT_COUNT, count);
255:
256: // /
257: PropertyName elementName = ffac
258: .property("aw:relatedObservation/aw:PhenomenonTimeSeries/om:result/cv:CompactDiscreteTimeCoverage");
259: Object timeCovVal = elementName.evaluate(feature);
260: assertNotNull(
261: "aw:relatedObservation/aw:PhenomenonTimeSeries/om:result/cv:CompactDiscreteTimeCoverage",
262: timeCovVal);
263: assertTrue(timeCovVal instanceof Feature);
264: final List elements = (List) ((Feature) timeCovVal).getValue();
265: assertEquals(expectedResults, elements.size());
266:
267: ComplexAttribute element = (ComplexAttribute) elements.get(10);
268: assertNotNull(element);
269: Name compactTimeValuePairName = Types.typeName(CVNS,
270: "CompactTimeValuePair");
271: Name geometryName = Types.typeName(CVNS, "geometry");
272: Name valueName = Types.typeName(CVNS, "value");
273:
274: List compactTimes = element.get(compactTimeValuePairName);
275: assertNotNull(compactTimes);
276: assertEquals(1, compactTimes.size());
277:
278: ComplexAttribute compatTimeValuePair = (ComplexAttribute) compactTimes
279: .get(0);
280: List geometries = compatTimeValuePair.get(geometryName);
281: List values = compatTimeValuePair.get(valueName);
282:
283: assertNotNull(geometries);
284: assertNotNull(values);
285: assertEquals(1, geometries.size());
286: assertEquals(1, values.size());
287:
288: Attribute geom = (Attribute) geometries.get(0);
289: Attribute value = (Attribute) values.get(0);
290:
291: assertNotNull(geom.getValue());
292: assertNotNull(value.getValue());
293: assertNotNull(value.getDescriptor().getUserData(
294: Attributes.class));
295:
296: return cumulativeTime / numberOfRuns;
297: }
298:
299: /**
300: * Runs numberOfCycles + 1, the first run does not count, returns the avg
301: * time it took to count the features.
302: *
303: * @param features
304: * @param expectedFeatureCount
305: * @param numberOfCycles
306: * @return
307: */
308: private double stressCount(final FeatureCollection features,
309: final int expectedFeatureCount, final int numberOfCycles) {
310: double cumulativeTime = 0;
311:
312: StopWatch timer = new StopWatch();
313:
314: for (int i = 0; i < numberOfCycles; i++) {
315: timer.start();
316: // int size = ((Collection)features).size();
317: int size = FeatureCollections.getSize(features);
318: timer.stop();
319: assertEquals(expectedFeatureCount, size);
320:
321: // int resultCount = getCount(features);
322: // cumulativeTime += timer.time();
323: //
324: String msg = "be sure difference in result count is not due to different dataset.";
325: assertEquals(msg, expectedFeatureCount, size);
326: }
327:
328: return cumulativeTime / numberOfCycles;
329: }
330:
331: private int getCount(FeatureCollection features) {
332: Iterator iterator = features.iterator();
333: int count = 0;
334: try {
335: while (iterator.hasNext()) {
336: iterator.next();
337: count++;
338: }
339: } finally {
340: features.close(iterator);
341: }
342: return count;
343: }
344:
345: private static class StopWatch {
346: private long start;
347:
348: private long end = Long.MIN_VALUE;
349:
350: public void start() {
351: start = System.currentTimeMillis();
352: end = Long.MIN_VALUE;
353: }
354:
355: public void stop() {
356: end = System.currentTimeMillis();
357: }
358:
359: public long time() {
360: if (Long.MIN_VALUE == end) {
361: throw new IllegalStateException(
362: "call stop() before time()");
363: }
364: return end - start;
365: }
366: }
367:
368: private static void populateTable() throws Exception {
369: Map params = new HashMap();
370: params.put(PostgisDataStoreFactory.DBTYPE.key, "postgis");
371: params.put(PostgisDataStoreFactory.DATABASE.key, "postgis");
372: params.put(PostgisDataStoreFactory.HOST.key, "localhost");
373: params.put(PostgisDataStoreFactory.PORT.key, "5432");
374: params.put(PostgisDataStoreFactory.USER.key, "postgres");
375: params.put(PostgisDataStoreFactory.PASSWD.key, "postgres");
376:
377: final DataStore ds = DataStoreFinder.getDataStore(params);
378: final String typeSpec = "station_id:String,POSITION:Point,station_name:String,"
379: + "determinand_code:String,determinand_description:String,"
380: + "sample_time_position:java.util.Date,result:Double,units:String";
381: final org.geotools.feature.FeatureType schema = DataUtilities
382: .createType("TimeSeriesTest", typeSpec);
383: LOGGER.info("Creating schema " + schema);
384: ds.createSchema(schema);
385:
386: // the two fields grouped by
387: String station_id;
388: String determinand_code;
389: // put 1000, 2000, 3000, and 4000 groups of records
390: station_id = "stat_id_1000";
391: determinand_code = "det_code_1000";
392: populate(ds, schema, station_id, determinand_code, 1000);
393:
394: station_id = "stat_id_2000";
395: determinand_code = "det_code_2000";
396: populate(ds, schema, station_id, determinand_code, 2000);
397:
398: station_id = "stat_id_3000";
399: determinand_code = "det_code_3000";
400: populate(ds, schema, station_id, determinand_code, 3000);
401:
402: station_id = "stat_id_4000";
403: determinand_code = "det_code_4000";
404: populate(ds, schema, station_id, determinand_code, 4000);
405:
406: station_id = "stat_id_12000";
407: determinand_code = "det_code_12000";
408: populate(ds, schema, station_id, determinand_code, 12000);
409: }
410:
411: private static void populate(final DataStore ds,
412: final org.geotools.feature.FeatureType schema,
413: final String station_id, final String determinand_code,
414: final int featureCount) throws Exception {
415: org.geotools.feature.Feature feature;
416: GeometryFactory gf = new GeometryFactory();
417: LOGGER.info("Creating " + featureCount
418: + " features for station_id " + station_id);
419:
420: Transaction transaction = new DefaultTransaction();
421: FeatureWriter fw = ds.getFeatureWriterAppend(schema
422: .getTypeName(), transaction);
423: for (double i = 0; i < featureCount; i++) {
424: fw.hasNext();
425: feature = fw.next();
426: feature.setAttribute("station_id", station_id);
427: feature.setAttribute("determinand_code", determinand_code);
428: feature.setAttribute("POSITION", gf
429: .createPoint(new Coordinate(i / 1000D, i / 1000D)));
430: feature.setAttribute("station_name", "stat_name_" + i);
431: feature.setAttribute("determinand_description",
432: "determinand description " + i + " for "
433: + station_id);
434: feature.setAttribute("sample_time_position",
435: new java.util.Date());
436: feature.setAttribute("result", new Double(i / 1000));
437: fw.write();
438: if (i % 100 == 0) {
439: transaction.commit();
440: }
441: }
442: transaction.commit();
443: fw.close();
444: LOGGER.info(featureCount + " features added for " + station_id);
445: }
446:
447: public static void main(String[] argv) {
448: try {
449: populateTable();
450: } catch (Exception e) {
451: e.printStackTrace();
452: }
453: }
454: }
|