package org.geoserver.catalog.impl; import java.awt.image.BufferedImage; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.util.Arrays; import java.util.LinkedHashSet; import java.util.Properties; import javax.xml.namespace.QName; import junit.framework.Test; import org.apache.commons.io.FileUtils; import org.geoserver.catalog.Catalog; import org.geoserver.catalog.CatalogBuilder; import org.geoserver.catalog.CoverageInfo; import org.geoserver.catalog.CoverageStoreInfo; import org.geoserver.catalog.DataStoreInfo; import org.geoserver.catalog.FeatureTypeInfo; import org.geoserver.catalog.Keyword; import org.geoserver.catalog.LayerInfo; import org.geoserver.catalog.WMSLayerInfo; import org.geoserver.catalog.WMSStoreInfo; import org.geoserver.data.test.MockData; import org.geoserver.test.GeoServerTestSupport; import org.geoserver.test.RemoteOWSTestSupport; import org.geotools.coverage.grid.GridCoverage2D; import org.geotools.coverage.grid.GridCoverageFactory; import org.geotools.data.DataStore; import org.geotools.data.FeatureSource; import org.geotools.data.Query; import org.geotools.data.ResourceInfo; import org.geotools.feature.NameImpl; import org.geotools.gce.geotiff.GeoTiffWriter; import org.geotools.gce.imagemosaic.ImageMosaicFormat; import org.geotools.gce.imagemosaic.ImageMosaicReader; import org.geotools.geometry.jts.ReferencedEnvelope; import org.geotools.referencing.CRS; import org.geotools.referencing.crs.DefaultGeographicCRS; import org.opengis.feature.type.FeatureType; import org.opengis.feature.type.Name; import com.vividsolutions.jts.geom.Point; import static org.easymock.EasyMock.createMock; import static org.easymock.EasyMock.expect; import static org.easymock.EasyMock.isA; import static org.easymock.EasyMock.replay; public class CatalogBuilderTest extends GeoServerTestSupport { /** * This is a READ ONLY TEST so we can use one time setup */ public static Test suite() { return new OneTimeTestSetup(new CatalogBuilderTest()); } @Override protected void populateDataDirectory(MockData dataDirectory) throws Exception { super.populateDataDirectory(dataDirectory); dataDirectory.addWellKnownCoverageTypes(); } public void testFeatureTypeNoSRS() throws Exception { // build a feature type (it's already in the catalog, but we just want to // check it's built as expected // LINES is a feature type with a native SRS, so we want the bounds to be there Catalog cat = getCatalog(); CatalogBuilder cb = new CatalogBuilder(cat); cb.setStore(cat.getDataStoreByName(MockData.BRIDGES.getPrefix())); FeatureTypeInfo fti = cb.buildFeatureType(toName(MockData.BRIDGES)); // perform basic checks, this has no srs so no lat/lon bbox computation possible assertNull(fti.getSRS()); assertNull(fti.getNativeCRS()); assertNull(fti.getNativeBoundingBox()); assertNull(fti.getLatLonBoundingBox()); // force bounds computation cb.setupBounds(fti); assertNotNull(fti.getNativeBoundingBox()); assertNull(fti.getNativeBoundingBox().getCoordinateReferenceSystem()); assertNull(fti.getLatLonBoundingBox()); } public void testFeatureType() throws Exception { // build a feature type (it's already in the catalog, but we just want to // check it's built as expected // LINES is a feature type with a native SRS, so we want the bounds to be there Catalog cat = getCatalog(); CatalogBuilder cb = new CatalogBuilder(cat); cb.setStore(cat.getDataStoreByName(MockData.LINES.getPrefix())); FeatureTypeInfo fti = cb.buildFeatureType(toName(MockData.LINES)); // perform basic checks assertEquals("EPSG:32615", fti.getSRS()); assertEquals(CRS.decode("EPSG:32615", true), fti.getCRS()); assertNull(fti.getNativeBoundingBox()); assertNull(fti.getLatLonBoundingBox()); // force bounds computation cb.setupBounds(fti); assertNotNull(fti.getNativeBoundingBox()); assertNotNull(fti.getNativeBoundingBox().getCoordinateReferenceSystem()); assertNotNull(fti.getLatLonBoundingBox()); } public void testGeometryless() throws Exception { // build a feature type (it's already in the catalog, but we just want to // check it's built as expected // LINES is a feature type with a native SRS, so we want the bounds to be there Catalog cat = getCatalog(); CatalogBuilder cb = new CatalogBuilder(cat); cb.setStore(cat.getDataStoreByName(MockData.GEOMETRYLESS.getPrefix())); FeatureTypeInfo fti = cb.buildFeatureType(toName(MockData.GEOMETRYLESS)); LayerInfo layer = cb.buildLayer(fti); cb.setupBounds(fti); // perform basic checks assertNull(fti.getCRS()); // ... not so sure about this one, null would seem more natural assertTrue(fti.getNativeBoundingBox().isEmpty()); assertNull(fti.getLatLonBoundingBox()); assertNull(layer.getDefaultStyle()); } public void testCoverage() throws Exception { // build a feature type (it's already in the catalog, but we just want to // check it's built as expected // LINES is a feature type with a native SRS, so we want the bounds to be there Catalog cat = getCatalog(); CatalogBuilder cb = new CatalogBuilder(cat); cb.setStore(cat.getCoverageStoreByName(MockData.TASMANIA_DEM.getLocalPart())); CoverageInfo fti = cb.buildCoverage(); // perform basic checks assertEquals(CRS.decode("EPSG:4326", true), fti.getCRS()); assertEquals("EPSG:4326", fti.getSRS()); assertNotNull(fti.getNativeCRS()); assertNotNull(fti.getNativeBoundingBox()); assertNotNull(fti.getLatLonBoundingBox()); } public void testEmptyBounds() throws Exception { // test the bounds of a single point Catalog cat = getCatalog(); FeatureTypeInfo fti = cat.getFeatureTypeByName(getLayerId(MockData.POINTS)); assertEquals(Point.class, fti.getFeatureType().getGeometryDescriptor().getType() .getBinding()); assertEquals(1, fti.getFeatureSource(null, null).getCount(Query.ALL)); CatalogBuilder cb = new CatalogBuilder(cat); cb.setStore(cat.getStoreByName(MockData.CGF_PREFIX, DataStoreInfo.class)); FeatureTypeInfo built = cb.buildFeatureType(fti.getQualifiedName()); cb.setupBounds(built); assertTrue(built.getNativeBoundingBox().getWidth() > 0); assertTrue(built.getNativeBoundingBox().getHeight() > 0); } /** * Tests we can build properly the WMS store and the WMS layer * * @throws Exception */ public void testWMS() throws Exception { if (!RemoteOWSTestSupport.isRemoteWMSStatesAvailable(LOGGER)) { LOGGER.warning("Remote OWS tests disabled, skipping catalog builder wms tests"); return; } Catalog cat = getCatalog(); CatalogBuilder cb = new CatalogBuilder(cat); WMSStoreInfo wms = cb.buildWMSStore("demo"); wms.setCapabilitiesURL(RemoteOWSTestSupport.WMS_SERVER_URL + "service=WMS&request=GetCapabilities"); cat.save(wms); cb.setStore(wms); WMSLayerInfo wmsLayer = cb.buildWMSLayer("topp:states"); assertWMSLayer(wmsLayer); LayerInfo layer = cb.buildLayer(wmsLayer); assertEquals(LayerInfo.Type.WMS, layer.getType()); wmsLayer = cat.getFactory().createWMSLayer(); wmsLayer.setName("states"); wmsLayer.setNativeName("topp:states"); cb.initWMSLayer(wmsLayer); assertWMSLayer(wmsLayer); } void assertWMSLayer(WMSLayerInfo wmsLayer) throws Exception { assertEquals("states", wmsLayer.getName()); assertEquals("topp:states", wmsLayer.getNativeName()); assertEquals("EPSG:4326", wmsLayer.getSRS()); assertEquals("USA Population", wmsLayer.getTitle()); assertEquals("This is some census data on the states.", wmsLayer.getAbstract()); assertEquals(CRS.decode("EPSG:4326"), wmsLayer.getNativeCRS()); assertNotNull(wmsLayer.getNativeBoundingBox()); assertNotNull(wmsLayer.getLatLonBoundingBox()); assertFalse(wmsLayer.getKeywords().isEmpty()); } public void testLargeNDMosaic() throws Exception { // build a mosaic with 1025 files (the standard ulimit is 1024) File mosaic = new File("./target/largeMosaic"); try { createTimeMosaic(mosaic, 1025); // now configure a new store based on it Catalog cat = getCatalog(); CatalogBuilder cb = new CatalogBuilder(cat); CoverageStoreInfo store = cb.buildCoverageStore("largeMosaic"); store.setURL(mosaic.getAbsolutePath()); store.setType("ImageMosaic"); cat.add(store); // and configure also the coverage cb.setStore(store); CoverageInfo ci = cb.buildCoverage(); cat.add(ci); } finally { if(mosaic.exists() && mosaic.isDirectory()) { FileUtils.deleteDirectory(mosaic); } } } public void testMosaicParameters() throws Exception { // build a mosaic with 1025 files (the standard ulimit is 1024) File mosaic = new File("./target/smallMosaic"); try { createTimeMosaic(mosaic, 4); // now configure a new store based on it Catalog cat = getCatalog(); CatalogBuilder cb = new CatalogBuilder(cat); CoverageStoreInfo store = cb.buildCoverageStore("smallMosaic"); store.setURL(mosaic.getAbsolutePath()); store.setType("ImageMosaic"); cat.add(store); // and configure also the coverage cb.setStore(store); CoverageInfo ci = cb.buildCoverage(); cat.add(ci); // check the parameters have the default values System.out.println(ci.getParameters()); assertEquals(String.valueOf(-1), ci.getParameters().get(ImageMosaicFormat.MAX_ALLOWED_TILES.getName().toString())); assertEquals("", ci.getParameters().get(ImageMosaicFormat.FILTER.getName().toString())); } finally { if(mosaic.exists() && mosaic.isDirectory()) { FileUtils.deleteDirectory(mosaic); } } } private void createTimeMosaic(File mosaic, int fileCount) throws IOException, FileNotFoundException { if(mosaic.exists()) { if(mosaic.isDirectory()) { FileUtils.deleteDirectory(mosaic); } else { mosaic.delete(); } } mosaic.mkdir(); // build the reference coverage into a byte array GridCoverageFactory factory = new GridCoverageFactory(); BufferedImage bi = new BufferedImage(10, 10, BufferedImage.TYPE_4BYTE_ABGR); ReferencedEnvelope envelope = new ReferencedEnvelope(0, 10, 0, 10, DefaultGeographicCRS.WGS84); GridCoverage2D test = factory.create("test", bi, envelope); ByteArrayOutputStream bos = new ByteArrayOutputStream(); GeoTiffWriter writer = new GeoTiffWriter(bos); writer.write(test, null); // create the lot of files byte[] bytes = bos.toByteArray(); for(int i = 0; i < fileCount; i++) { String pad = ""; if(i < 10) { pad = "000"; } else if(i < 100) { pad = "00"; } else if(i < 1000){ pad = "0"; } File target = new File(mosaic, "tile_" +pad + i + ".tiff"); FileUtils.writeByteArrayToFile(target, bytes); } // create the mosaic indexer property file Properties p = new Properties(); p.put("ElevationAttribute", "elevation"); p.put("Schema", "*the_geom:Polygon,location:String,elevation:Integer"); p.put("PropertyCollectors", "IntegerFileNameExtractorSPI[elevationregex](elevation)"); FileOutputStream fos = new FileOutputStream(new File(mosaic, "indexer.properties")); p.store(fos, null); fos.close(); // and the regex itself p.clear(); p.put("regex", "(?<=_)(\\d{4})"); fos = new FileOutputStream(new File(mosaic, "elevationregex.properties")); p.store(fos, null); fos.close(); } public void testLookupSRSDetached() throws Exception { Catalog cat = getCatalog(); CatalogBuilder cb = new CatalogBuilder(cat); DataStoreInfo sf = cat.getDataStoreByName("sf"); FeatureSource fs = sf.getDataStore(null).getFeatureSource(toName(MockData.PRIMITIVEGEOFEATURE)); FeatureTypeInfo ft = cat.getFactory().createFeatureType(); ft.setNativeName("PrimitiveGeoFeature"); assertNull(ft.getSRS()); assertNull(ft.getCRS()); cb.lookupSRS(ft, fs, true); assertNotNull(ft.getSRS()); assertNotNull(ft.getCRS()); } public void testSetupBoundsDetached() throws Exception { Catalog cat = getCatalog(); CatalogBuilder cb = new CatalogBuilder(cat); DataStoreInfo sf = cat.getDataStoreByName("sf"); FeatureSource fs = sf.getDataStore(null).getFeatureSource(toName(MockData.PRIMITIVEGEOFEATURE)); FeatureTypeInfo ft = cat.getFactory().createFeatureType(); ft.setNativeName("PrimitiveGeoFeature"); assertNull(ft.getNativeBoundingBox()); assertNull(ft.getLatLonBoundingBox()); cb.lookupSRS(ft, fs, true); cb.setupBounds(ft, fs); assertNotNull(ft.getNativeBoundingBox()); assertNotNull(ft.getLatLonBoundingBox()); } public void testMetadataFromFeatueSource() throws Exception { CatalogBuilder cb = new CatalogBuilder(getCatalog()); cb.setStore(cb.buildDataStore("fooStore")); FeatureType ft = createMock(FeatureType.class); expect(ft.getName()).andReturn(new NameImpl("foo")).anyTimes(); expect(ft.getCoordinateReferenceSystem()).andReturn(null).anyTimes(); expect(ft.getGeometryDescriptor()).andReturn(null).anyTimes(); replay(ft); ResourceInfo rInfo = createMock(ResourceInfo.class); expect(rInfo.getTitle()).andReturn("foo title"); expect(rInfo.getDescription()).andReturn("foo description"); expect(rInfo.getKeywords()).andReturn( new LinkedHashSet<String>(Arrays.asList("foo", "bar", "baz"))).anyTimes(); replay(rInfo); FeatureSource fs = createMock(FeatureSource.class); expect(fs.getSchema()).andReturn(ft).anyTimes(); expect(fs.getInfo()).andReturn(rInfo).anyTimes(); replay(fs); FeatureTypeInfo ftInfo = cb.buildFeatureType(fs); assertEquals("foo title", ftInfo.getTitle()); assertEquals("foo description", ftInfo.getDescription()); assertTrue(ftInfo.getKeywords().contains(new Keyword("foo"))); assertTrue(ftInfo.getKeywords().contains(new Keyword("bar"))); assertTrue(ftInfo.getKeywords().contains(new Keyword("baz"))); } Name toName(QName qname) { return new NameImpl(qname.getNamespaceURI(), qname.getLocalPart()); } }