@Test public void testDrawIntepolation() throws Exception { MapContent mc = new MapContent(); ReferencedEnvelope reWgs = new ReferencedEnvelope(new Envelope(-180, 180, -90, 90), DefaultGeographicCRS.WGS84); BufferedImage testImage = new BufferedImage(200, 200, BufferedImage.TYPE_4BYTE_ABGR); GridCoverage2D testCoverage = new GridCoverageFactory().create("test", testImage, reWgs); GridCoverage2D coverage = new GridCoverage2D("test", testCoverage); // mocking a GridCoverageReader to wrap the testing coverage GridCoverage2DReader gridCoverageReader = Mockito.mock(GridCoverage2DReader.class); Mockito.when(gridCoverageReader.getOriginalEnvelope()).thenReturn(new GeneralEnvelope(reWgs)); Mockito.when(gridCoverageReader.getCoordinateReferenceSystem()) .thenReturn(DefaultGeographicCRS.WGS84); Mockito.when(gridCoverageReader.read(Mockito.any(GeneralParameterValue[].class))) .thenReturn(coverage); Layer layer = new FeatureLayer( FeatureUtilities.wrapGridCoverageReader( gridCoverageReader, new GeneralParameterValue[] {}), createRasterStyle()); layer .getUserData() .put( StreamingRenderer.BYLAYER_INTERPOLATION, Interpolation.getInstance(Interpolation.INTERP_BICUBIC)); mc.addLayer(layer); BufferedImage image = new BufferedImage(200, 200, BufferedImage.TYPE_4BYTE_ABGR); StreamingRenderer sr = new StreamingRenderer(); sr.setMapContent(mc); Graphics2D graphics = (Graphics2D) image.getGraphics(); sr.paint(graphics, new Rectangle(200, 200), reWgs); // test right interpolation hint is set on Graphics2D assertEquals( graphics.getRenderingHint(JAI.KEY_INTERPOLATION), Interpolation.getInstance(Interpolation.INTERP_BICUBIC)); layer .getUserData() .put( StreamingRenderer.BYLAYER_INTERPOLATION, Interpolation.getInstance(Interpolation.INTERP_NEAREST)); sr.paint(graphics, new Rectangle(200, 200), reWgs); // test right interpolation hint is set on Graphics2D assertEquals( graphics.getRenderingHint(JAI.KEY_INTERPOLATION), Interpolation.getInstance(Interpolation.INTERP_NEAREST)); }
/** * Test that point features are rendered at the expected image coordinates when the map is * rotated. StreamingRenderer * * @throws Exception */ @Test public void testRotatedTransform() throws Exception { // If we rotate the world rectangle + 90 degrees around (0,0), we get the screen rectangle final Rectangle screen = new Rectangle(0, 0, 100, 50); final Envelope world = new Envelope(0, 50, 0, -100); final AffineTransform worldToScreen = AffineTransform.getRotateInstance(Math.toRadians(90), 0, 0); DefaultFeatureCollection fc = new DefaultFeatureCollection(); fc.add(createPoint(0, 0)); fc.add(createPoint(world.getMaxX(), world.getMinY())); MapContext mapContext = new DefaultMapContext(DefaultGeographicCRS.WGS84); mapContext.addLayer((FeatureCollection) fc, createPointStyle()); BufferedImage image = new BufferedImage(screen.width, screen.height, BufferedImage.TYPE_4BYTE_ABGR); final StreamingRenderer sr = new StreamingRenderer(); sr.setContext(mapContext); sr.paint(image.createGraphics(), screen, worldToScreen); assertTrue("Pixel should be drawn at 0,0 ", image.getRGB(0, 0) != 0); assertTrue( "Pixel should not be drawn in image centre ", image.getRGB(screen.width / 2, screen.height / 2) == 0); assertTrue( "Pixel should be drawn at image max corner ", image.getRGB(screen.width - 1, screen.height - 1) != 0); }
public void testSkipProjectionErrors() throws Exception { // build map context MapContext mapContext = new DefaultMapContext(DefaultGeographicCRS.WGS84); mapContext.addLayer(createLineCollection(), createLineStyle()); // build projected envelope to work with (small one around the area of // validity of utm zone 1, which being a Gauss projection is a vertical // slice parallel to the central meridian, -177°) ReferencedEnvelope reWgs = new ReferencedEnvelope(new Envelope(-180, -170, 20, 40), DefaultGeographicCRS.WGS84); CoordinateReferenceSystem utm1N = CRS.decode("EPSG:32601"); System.out.println(CRS.getGeographicBoundingBox(utm1N)); ReferencedEnvelope reUtm = reWgs.transform(utm1N, true); BufferedImage image = new BufferedImage(200, 200, BufferedImage.TYPE_4BYTE_ABGR); // setup the renderer and listen for errors StreamingRenderer sr = new StreamingRenderer(); sr.setRendererHints(Collections.singletonMap(sr.OPTIMIZED_DATA_LOADING_KEY, Boolean.FALSE)); sr.setContext(mapContext); sr.addRenderListener( new RenderListener() { public void featureRenderer(SimpleFeature feature) {} public void errorOccurred(Exception e) { e.printStackTrace(); errors++; } }); errors = 0; sr.paint((Graphics2D) image.getGraphics(), new Rectangle(200, 200), reUtm); // we should get two errors since there are two features that cannot be // projected but the renderer itself should not throw exceptions assertEquals(2, errors); }
/* * https://osgeo-org.atlassian.net/browse/GEOT-5287 */ @Test public void testEmptyGeometryRendering() throws Exception { MapContent mc = new MapContent(); /* * We simulate reading empty geometries with this properties and mocking the capability to * filter, so that no filter layer is installed over our data and the empty geometry reaches * rendering code. These geometries are in EPSG:32717 because the 0,0 coordinate is in the * pole. */ File dir = new File(TestData.getResource(this, "empty-geom-rendering.properties").toURI()); PropertyDataStore dataStore = new PropertyDataStore(dir.getParentFile()) { @Override protected ContentFeatureSource createFeatureSource(ContentEntry entry) throws IOException { return new PropertyFeatureSource(entry, Query.ALL) { @Override protected boolean canFilter() { return true; } }; } }; /* * Set up the rendering of previous empty geometry */ StyleBuilder sb = new StyleBuilder(); Style style = sb.createStyle(sb.createPolygonSymbolizer()); Layer layer = new FeatureLayer(dataStore.getFeatureSource("empty-geom-rendering"), style); mc.addLayer(layer); StreamingRenderer sr = new StreamingRenderer(); sr.setMapContent(mc); BufferedImage img = new BufferedImage(40, 40, BufferedImage.TYPE_INT_ARGB); Graphics2D graphics = img.createGraphics(); Rectangle paintArea = new Rectangle(40, 40); // An EPSG:8357 extent on the EPSG:32717 area of application. double minx = -8929252.1; double maxx = -8708634.6; double miny = -491855.7; double maxy = -271204.3; ReferencedEnvelope referencedEnvelope = new ReferencedEnvelope( new Rectangle2D.Double(minx, miny, maxx - minx, maxy - miny), CRS.decode("EPSG:3857")); sr.addRenderListener( new RenderListener() { public void featureRenderer(SimpleFeature feature) {} public void errorOccurred(Exception e) { errors++; } }); errors = 0; sr.paint(graphics, paintArea, referencedEnvelope); assertTrue(errors == 0); }
@Test public void testEventAfterDrawing() throws Exception { // build map context MapContent mc = new MapContent(); mc.addLayer(new FeatureLayer(createLineCollection(), createLineStyle())); // build projected envelope to work with (small one around the area of // validity of utm zone 1, which being a Gauss projection is a vertical // slice parallel to the central meridian, -177°) ReferencedEnvelope reWgs = new ReferencedEnvelope(new Envelope(-180, -170, 20, 40), DefaultGeographicCRS.WGS84); CoordinateReferenceSystem utm1N = CRS.decode("EPSG:32601"); ReferencedEnvelope reUtm = reWgs.transform(utm1N, true); BufferedImage image = new BufferedImage(200, 200, BufferedImage.TYPE_4BYTE_ABGR); // setup the renderer and listen for errors final AtomicInteger commandsCount = new AtomicInteger(0); final BlockingQueue<RenderingRequest> queue = new ArrayBlockingQueue<RenderingRequest>(10) { @Override public void put(RenderingRequest e) throws InterruptedException { commandsCount.incrementAndGet(); super.put(e); } }; StreamingRenderer sr = new StreamingRenderer() { @Override protected BlockingQueue<RenderingRequest> getRequestsQueue() { return queue; } }; sr.setMapContent(mc); sr.addRenderListener( new RenderListener() { public void featureRenderer(SimpleFeature feature) { assertTrue(commandsCount.get() > 0); features++; } public void errorOccurred(Exception e) { errors++; } }); errors = 0; features = 0; sr.paint((Graphics2D) image.getGraphics(), new Rectangle(200, 200), reUtm); // we should get errors since there are two features that cannot be // projected but the renderer itself should not throw exceptions assertTrue(errors > 0); }
/** * Test that we don't have the geometry added twice by StreamingRenderer#findStyleAttributes when * geofence is filtering a layer. * * @throws Exception */ @Test public void testFindLineStyleAttributeWithAddedFilter() throws Exception { final List<Filter> filters = new ArrayList<Filter>(); SimpleFeatureSource testSource = new CollectionFeatureSource(createLineCollection()) { @Override public SimpleFeatureCollection getFeatures(Query query) { filters.add(query.getFilter()); return super.getFeatures(query); } }; Style style = createPointStyle(); MapContent mc = new MapContent(); FeatureLayer layer = new FeatureLayer(testSource, style); mc.addLayer(layer); StreamingRenderer sr = new StreamingRenderer(); sr.setMapContent(mc); ReferencedEnvelope envelope = new ReferencedEnvelope(0, 100, 0, 100, DefaultGeographicCRS.WGS84); // simulate geofence adding a bbox BBOX bbox = StreamingRenderer.filterFactory.bbox("", 30, 60, 30, 60, "WGS84"); StyleFactoryImpl sf = new StyleFactoryImpl(); Rule bboxRule = sf.createRule( new Symbolizer[0], new DescriptionImpl(), new Graphic[0], "bbox", bbox, false, 1e12, 0); style.featureTypeStyles().get(0).rules().add(bboxRule); BufferedImage bi = new BufferedImage(100, 100, BufferedImage.TYPE_3BYTE_BGR); Graphics2D graphics = bi.createGraphics(); try { sr.paint(graphics, new Rectangle(5, 5, 7, 7), envelope); } finally { graphics.dispose(); } // must have only one bbox, not two assertEquals(1, filters.size()); assertEquals(FastBBOX.class, filters.get(0).getClass()); }
@Test public void testRepeatedEnvelopeExpansion() throws Exception { final List<Filter> filters = new ArrayList<Filter>(); SimpleFeatureSource testSource = new CollectionFeatureSource(createLineCollection()) { @Override public SimpleFeatureCollection getFeatures(Query query) { filters.add(query.getFilter()); return super.getFeatures(query); } }; StyleBuilder sb = new StyleBuilder(); Style style20 = sb.createStyle(sb.createLineSymbolizer(20)); Style style10 = sb.createStyle(sb.createLineSymbolizer(10)); MapContent mc = new MapContent(); mc.addLayer(new FeatureLayer(testSource, style20)); mc.addLayer(new FeatureLayer(testSource, style10)); StreamingRenderer sr = new StreamingRenderer(); sr.setMapContent(mc); BufferedImage bi = new BufferedImage(100, 100, BufferedImage.TYPE_3BYTE_BGR); Graphics2D graphics = bi.createGraphics(); sr.paint( graphics, new Rectangle(0, 0, 100, 100), new ReferencedEnvelope(0, 100, 0, 100, DefaultGeographicCRS.WGS84)); graphics.dispose(); System.out.println(filters); assertEquals(2, filters.size()); Filter f1 = filters.get(0); assertTrue(f1 instanceof BBOX); BoundingBox bbox1 = ((BBOX) f1).getBounds(); ReferencedEnvelope expected = new ReferencedEnvelope(-11, 111, -11, 111, DefaultGeographicCRS.WGS84); assertEquals(expected, bbox1); Filter f2 = filters.get(1); assertTrue(f2 instanceof BBOX); BoundingBox bbox2 = ((BBOX) f2).getBounds(); assertEquals(new ReferencedEnvelope(-6, 106, -6, 106, DefaultGeographicCRS.WGS84), bbox2); }
@Test public void testScreenMapMemory() { // build a feature source with two zig-zag line occupying the same position LiteCoordinateSequence cs = new LiteCoordinateSequence(new double[] {0, 0, 1, 1, 2, 0, 3, 1, 4, 0}); SimpleFeature zigzag1 = SimpleFeatureBuilder.build( testLineFeatureType, new Object[] {gf.createLineString(cs)}, "zz1"); SimpleFeature zigzag2 = SimpleFeatureBuilder.build( testLineFeatureType, new Object[] {gf.createLineString(cs)}, "zz2"); DefaultFeatureCollection fc = new DefaultFeatureCollection(); fc.add(zigzag1); fc.add(zigzag2); SimpleFeatureSource zzSource = new CollectionFeatureSource(fc); // prepare the map MapContent mc = new MapContent(); StyleBuilder sb = new StyleBuilder(); mc.addLayer(new FeatureLayer(zzSource, sb.createStyle(sb.createLineSymbolizer()))); StreamingRenderer sr = new StreamingRenderer(); sr.setMapContent(mc); // collect rendered features final List<SimpleFeature> features = new ArrayList<SimpleFeature>(); RenderListener renderedFeaturesCollector = new RenderListener() { @Override public void featureRenderer(SimpleFeature feature) { features.add(feature); } @Override public void errorOccurred(Exception e) { // nothing to do } }; sr.addRenderListener(renderedFeaturesCollector); BufferedImage bi = new BufferedImage(1, 1, BufferedImage.TYPE_3BYTE_BGR); Graphics2D graphics = bi.createGraphics(); // have the lines be smaller than a 1/3 of a pixel sr.paint( graphics, new Rectangle(0, 0, 1, 1), new ReferencedEnvelope(0, 8, 0, 8, DefaultGeographicCRS.WGS84)); // check we only rendered one feature assertEquals(1, features.size()); assertEquals("zz1", features.get(0).getID()); // now have the lines be big enough to be painted instead features.clear(); sr.paint( graphics, new Rectangle(0, 0, 1, 1), new ReferencedEnvelope(0, 1, 0, 1, DefaultGeographicCRS.WGS84)); assertEquals(2, features.size()); assertEquals("zz1", features.get(0).getID()); assertEquals("zz2", features.get(1).getID()); graphics.dispose(); }
@Test public void testDeadlockOnException() throws Exception { ReferencedEnvelope reWgs = new ReferencedEnvelope(new Envelope(-180, 180, -90, 90), DefaultGeographicCRS.WGS84); // create the grid coverage that throws a OOM BufferedImage testImage = new BufferedImage(200, 200, BufferedImage.TYPE_4BYTE_ABGR); GridCoverage2D testCoverage = new GridCoverageFactory().create("test", testImage, reWgs); GridCoverage2D oomCoverage = new GridCoverage2D("test", testCoverage) { @Override public RenderedImage getRenderedImage() { throw new OutOfMemoryError("Boom!"); } }; // also have a collections of features to create the deadlock once the painter // thread is dead SimpleFeatureCollection lines = createLineCollection(); Style rasterStyle = createRasterStyle(); Style lineStyle = createLineStyle(); MapContent mapContent = new MapContent(); mapContent.addLayer(new GridCoverageLayer(oomCoverage, rasterStyle)); mapContent.addLayer(new FeatureLayer(lines, lineStyle)); final StreamingRenderer sr = new StreamingRenderer() { // makes it easy to reproduce the deadlock, just two features are sufficient protected BlockingQueue<RenderingRequest> getRequestsQueue() { return new RenderingBlockingQueue(1); } }; sr.setMapContent(mapContent); final List<Exception> exceptions = new ArrayList<Exception>(); sr.addRenderListener( new RenderListener() { public void featureRenderer(SimpleFeature feature) { features++; } public void errorOccurred(Exception e) { errors++; exceptions.add(e); } }); errors = 0; features = 0; BufferedImage image = new BufferedImage(200, 200, BufferedImage.TYPE_4BYTE_ABGR); sr.paint((Graphics2D) image.getGraphics(), new Rectangle(200, 200), reWgs); // all the lines should have been painted, the coverage reports as painted too // since the reporting happens in the main thread that does not error assertEquals(4, features); assertEquals(1, errors); assertTrue(exceptions.get(0).getCause() instanceof OutOfMemoryError); }
@Test public void testInfiniteLoopAvoidance() throws Exception { final Exception sentinel = new RuntimeException("This is the one that should be thrown in hasNext()"); // setup the mock necessary to have the renderer hit into the exception in hasNext() SimpleFeatureIterator it2 = createNiceMock(SimpleFeatureIterator.class); expect(it2.hasNext()).andThrow(sentinel).anyTimes(); replay(it2); SimpleFeatureCollection fc = createNiceMock(SimpleFeatureCollection.class); expect(fc.features()).andReturn(it2); expect(fc.size()).andReturn(200); expect(fc.getSchema()).andReturn(testLineFeatureType).anyTimes(); replay(fc); SimpleFeatureSource fs = createNiceMock(SimpleFeatureSource.class); expect(fs.getFeatures((Query) anyObject())).andReturn(fc); expect(fs.getSchema()).andReturn(testLineFeatureType).anyTimes(); expect(fs.getSupportedHints()).andReturn(new HashSet()).anyTimes(); replay(fs); // build map context MapContext mapContext = new DefaultMapContext(DefaultGeographicCRS.WGS84); mapContext.addLayer(fs, createLineStyle()); // setup the renderer and listen for errors final StreamingRenderer sr = new StreamingRenderer(); sr.setContext(mapContext); sr.addRenderListener( new RenderListener() { public void featureRenderer(SimpleFeature feature) { features++; } public void errorOccurred(Exception e) { errors++; if (errors > 2) { // we dont' want to block the loop in case of regression on this bug sr.stopRendering(); } // but we want to make sure we're getting Throwable t = e; while (t != sentinel && t.getCause() != null) t = t.getCause(); assertSame(sentinel, t); } }); errors = 0; features = 0; BufferedImage image = new BufferedImage(200, 200, BufferedImage.TYPE_4BYTE_ABGR); ReferencedEnvelope reWgs = new ReferencedEnvelope(new Envelope(-180, -170, 20, 40), DefaultGeographicCRS.WGS84); sr.paint((Graphics2D) image.getGraphics(), new Rectangle(200, 200), reWgs); // we should get two errors since there are two features that cannot be // projected but the renderer itself should not throw exceptions assertEquals(0, features); assertEquals(1, errors); }