Search in sources :

Example 1 with DynamicRangeSymbolizer

use of org.geotoolkit.display2d.ext.dynamicrange.DynamicRangeSymbolizer in project geotoolkit by Geomatys.

the class MapContextTileGenerator method generate.

@Override
public void generate(TileMatrixSet pyramid, Envelope env, NumberRange resolutions, ProcessListener listener) throws DataStoreException, InterruptedException {
    // check if we can optimize tiles generation
    boolean rasterOptimisation = true;
    search: for (MapLayer layer : MapBuilder.getLayers(sceneDef.getContext())) {
        final Style style = layer.getStyle();
        for (FeatureTypeStyle fts : style.featureTypeStyles()) {
            for (Rule rule : fts.rules()) {
                double scaleMin = rule.getMinScaleDenominator();
                double scaleMax = rule.getMaxScaleDenominator();
                // CanvasUtilities.computeSEScale
                if (scaleMin != 0.0 || scaleMax < 5.0E9) {
                    rasterOptimisation = false;
                    break search;
                }
                for (Symbolizer symbolizer : rule.symbolizers()) {
                    if (symbolizer instanceof RasterSymbolizer || symbolizer instanceof DynamicRangeSymbolizer) {
                    // ok
                    } else if (symbolizer instanceof PolygonSymbolizer) {
                        PolygonSymbolizer ps = (PolygonSymbolizer) symbolizer;
                        // check if we have a plain fill
                        Displacement displacement = ps.getDisplacement();
                        Fill fill = ps.getFill();
                        Stroke stroke = ps.getStroke();
                        Expression perpendicularOffset = ps.getPerpendicularOffset();
                        if (displacement != null) {
                            Double dx = doubleValue(displacement.getDisplacementX());
                            Double dy = doubleValue(displacement.getDisplacementX());
                            if ((dx != null && dx != 0.0) || (dy != null && dy != 0.0)) {
                                rasterOptimisation = false;
                                break search;
                            }
                        }
                        if (perpendicularOffset != null) {
                            Double off = doubleValue(perpendicularOffset);
                            if (off != null && off != 0.0) {
                                rasterOptimisation = false;
                                break search;
                            }
                        }
                        if (stroke != null) {
                            Double op = doubleValue(stroke.getOpacity());
                            Double wd = doubleValue(stroke.getWidth());
                            if ((op == null || op == 0.0) || (wd == null || wd == 0.0)) {
                            // not visible
                            } else {
                                rasterOptimisation = false;
                                break search;
                            }
                        }
                        if (fill != null) {
                            if (fill.getGraphicFill() != null) {
                                rasterOptimisation = false;
                                break search;
                            }
                        }
                    } else {
                        rasterOptimisation = false;
                        break search;
                    }
                }
            }
        }
    }
    if (rasterOptimisation) {
        /*
            We can generate the pyramid starting from the lowest level then going up
            using the previously generated level.
            */
        if (env != null) {
            try {
                CoordinateReferenceSystem targetCrs = pyramid.getCoordinateReferenceSystem();
                Envelope baseEnv = env;
                env = Envelopes.transform(env, targetCrs);
                double[] minres = new double[] { resolutions.getMinDouble(), resolutions.getMinDouble() };
                double[] maxres = new double[] { resolutions.getMaxDouble(), resolutions.getMaxDouble() };
                minres = ReferencingUtilities.convertResolution(baseEnv, minres, targetCrs, null);
                maxres = ReferencingUtilities.convertResolution(baseEnv, maxres, targetCrs, null);
                resolutions = NumberRange.create(minres[0], true, maxres[0], true);
            } catch (TransformException ex) {
                throw new DataStoreException(ex.getMessage(), ex);
            }
        }
        // generate lower level from data
        final TileMatrix[] mosaics = pyramid.getTileMatrices().toArray(new TileMatrix[0]);
        Arrays.sort(mosaics, (TileMatrix o1, TileMatrix o2) -> Double.compare(o1.getScale(), o2.getScale()));
        MapLayers parent = sceneDef.getContext();
        Hints hints = sceneDef.getHints();
        final long total = TileMatrices.countTiles(pyramid, env, resolutions);
        final AtomicLong al = new AtomicLong();
        // send an event only every few seconds
        final AtomicLong tempo = new AtomicLong(System.currentTimeMillis());
        final String msg = " / " + NumberFormat.getIntegerInstance(Locale.FRANCE).format(total);
        for (final TileMatrix mosaic : mosaics) {
            if (resolutions == null || resolutions.contains(mosaic.getScale())) {
                final Rectangle rect;
                try {
                    rect = TileMatrices.getTilesInEnvelope(mosaic, env);
                } catch (NoSuchDataException ex) {
                    continue;
                }
                final CanvasDef canvasDef = new CanvasDef();
                canvasDef.setBackground(this.canvasDef.getBackground());
                canvasDef.setEnvelope(mosaic.getEnvelope());
                final SceneDef sceneDef = new SceneDef(parent, hints);
                // one thread per line, the progressive image generates multiple tiles when drawing
                // this approach is more efficient from profiling result then using tile by tile
                // generation
                Stream<Tile> stream = LongStream.range(rect.y, rect.y + rect.height).parallel().boxed().flatMap((Long y) -> {
                    try {
                        final ProgressiveImage img = new ProgressiveImage(canvasDef, sceneDef, mosaic.getGridSize(), mosaic.getTileSize(), mosaic.getScale(), 0);
                        return img.generate(rect.x, rect.x + rect.width, y.intValue(), skipEmptyTiles);
                    } catch (Exception ex) {
                        LOGGER.log(Level.INFO, "Failed to generate a tile {0}", ex.getMessage());
                        LOGGER.log(Level.FINER, "Failed to generate a tile ", ex);
                        return Stream.empty();
                    }
                });
                if (listener != null) {
                    final NumberFormat format = NumberFormat.getIntegerInstance(Locale.FRANCE);
                    stream = stream.map((Tile t) -> {
                        long n = al.incrementAndGet();
                        if (n % 1000 == 0) {
                            final long time = System.currentTimeMillis();
                            if (tempo.updateAndGet((long operand) -> ((time - operand) > 3000) ? time : operand) == time) {
                                listener.progressing(new ProcessEvent(DUMMY, format.format(n) + msg, (float) ((((double) n) / ((double) total)) * 100.0)));
                            }
                        }
                        return t;
                    });
                }
                mosaic.writeTiles(stream, null);
                // last level event
                final NumberFormat format = NumberFormat.getIntegerInstance(Locale.FRANCE);
                long v = al.get();
                if (listener != null) {
                    listener.progressing(new ProcessEvent(DUMMY, format.format(v) + msg, (float) ((((double) v) / ((double) total)) * 100.0)));
                }
                // modify context
                final DefaultTileMatrixSet pm = new DefaultTileMatrixSet(pyramid.getCoordinateReferenceSystem());
                pm.getMosaicsInternal().add(mosaic);
                final InMemoryTiledGridCoverageResource r = new InMemoryTiledGridCoverageResource(NamesExt.create("test"));
                r.setSampleDimensions(sampleDimensions);
                r.getTileMatrixSets().add(pm);
                final MapLayers mc = MapBuilder.createContext();
                mc.getComponents().add(MapBuilder.createLayer(r));
                parent = mc;
                hints = new Hints(hints);
                hints.put(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
                hints.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
            }
        }
    } else {
        super.generate(pyramid, env, resolutions, listener);
    }
}
Also used : InMemoryTiledGridCoverageResource(org.geotoolkit.storage.memory.InMemoryTiledGridCoverageResource) Fill(org.opengis.style.Fill) Hints(org.geotoolkit.factory.Hints) RenderingHints(java.awt.RenderingHints) PolygonSymbolizer(org.opengis.style.PolygonSymbolizer) MapLayer(org.apache.sis.portrayal.MapLayer) Rectangle(java.awt.Rectangle) Envelope(org.opengis.geometry.Envelope) DefaultTileMatrixSet(org.geotoolkit.storage.multires.DefaultTileMatrixSet) DynamicRangeSymbolizer(org.geotoolkit.display2d.ext.dynamicrange.DynamicRangeSymbolizer) FeatureTypeStyle(org.opengis.style.FeatureTypeStyle) Style(org.opengis.style.Style) SceneDef(org.geotoolkit.display2d.service.SceneDef) CoordinateReferenceSystem(org.opengis.referencing.crs.CoordinateReferenceSystem) TileMatrix(org.geotoolkit.storage.multires.TileMatrix) Stroke(org.opengis.style.Stroke) DataStoreException(org.apache.sis.storage.DataStoreException) ProcessEvent(org.geotoolkit.process.ProcessEvent) TransformException(org.opengis.referencing.operation.TransformException) Tile(org.geotoolkit.storage.multires.Tile) DefaultImageTile(org.geotoolkit.storage.coverage.DefaultImageTile) ImageTile(org.geotoolkit.storage.coverage.ImageTile) NoSuchDataException(org.apache.sis.storage.NoSuchDataException) PolygonSymbolizer(org.opengis.style.PolygonSymbolizer) RasterSymbolizer(org.opengis.style.RasterSymbolizer) DynamicRangeSymbolizer(org.geotoolkit.display2d.ext.dynamicrange.DynamicRangeSymbolizer) Symbolizer(org.opengis.style.Symbolizer) Displacement(org.opengis.style.Displacement) PortrayalException(org.geotoolkit.display.PortrayalException) TransformException(org.opengis.referencing.operation.TransformException) NoSuchDataException(org.apache.sis.storage.NoSuchDataException) DataStoreException(org.apache.sis.storage.DataStoreException) IOException(java.io.IOException) RasterSymbolizer(org.opengis.style.RasterSymbolizer) AtomicLong(java.util.concurrent.atomic.AtomicLong) Expression(org.opengis.filter.Expression) AtomicLong(java.util.concurrent.atomic.AtomicLong) FeatureTypeStyle(org.opengis.style.FeatureTypeStyle) Rule(org.opengis.style.Rule) CanvasDef(org.geotoolkit.display2d.service.CanvasDef) MapLayers(org.apache.sis.portrayal.MapLayers) NumberFormat(java.text.NumberFormat)

Example 2 with DynamicRangeSymbolizer

use of org.geotoolkit.display2d.ext.dynamicrange.DynamicRangeSymbolizer in project geotoolkit by Geomatys.

the class GO2Utilities method inferStyle.

/**
 * Try to create a simple style from given statistics. For now, two cases exists:
 * <ol>
 *     <li>
 *         Less than 3 bands are detected. In this case, we simply create a color map over first band. The
 *         color map make use of standard deviation to compute interpolation boundaries.
 *     </li>
 *     <li>
 *         For 3 bands or more, we create a dynamic range RGB rendering based on first bands in the statistics. We
 *         define value boundary for each band using {@link #buildRanges(ImageStatistics, int) a custom empiric
 *         method removing extremums}.
 *     </li>
 * </ol>
 * @param stats The statistics to use for style creation. Can be null or empty, in which case nothing is returned.
 * @param forceAlpha A flag used only if given statistics contains more than 3 bands. Indicates that for RGB styling,
 *                   we must interpret 4th band as alpha component.
 * @return A style inferred from input statistics, or an empty shell if given argument does not contains enough
 * information.
 * @throws IOException It can happen when trying to access a color map definition on disk, but it fails.
 */
public static Optional<MutableStyle> inferStyle(final ImageStatistics stats, final boolean forceAlpha) throws IOException {
    if (stats == null)
        return Optional.empty();
    final ImageStatistics.Band[] bands = stats.getBands();
    if (bands == null || bands.length < 1)
        return Optional.empty();
    if (bands.length < 3) {
        LOGGER.log(Level.FINE, "applyColorMapStyle : fallBack way is choosen." + "GrayScale interpretation of the first coverage image band.");
        final ImageStatistics.Band band0 = bands[0];
        final Double bmin = band0.getMin();
        final Double bmax = band0.getMax();
        if (bmin == null || bmax == null || !Double.isFinite(bmin) || !Double.isFinite(bmax)) {
            LOGGER.log(Level.WARNING, "Image statistics object is present but contains null or non-finite extremas. Ignore it");
            return Optional.empty();
        }
        double palMin = bmin;
        double palMax = bmax;
        final Double mean = band0.getMean();
        final Double std = band0.getStd();
        if (mean != null && std != null && Double.isFinite(mean) && Double.isFinite(std)) {
            palMin = Math.max(bmin, mean - 2 * std);
            palMax = Math.min(bmax, mean + 2 * std);
        }
        if (!Double.isFinite(palMin) || !Double.isFinite(palMax)) {
            LOGGER.finest("Adapting rendering distribution failed. Fallback on input min/max");
            palMin = bmin;
            palMax = bmax;
        }
        assert Double.isFinite(palMin) : "Raster Style fallback : minimum value should be finite. min = " + palMin;
        assert Double.isFinite(palMax) : "Raster Style fallback : maximum value should be finite. max = " + palMax;
        assert palMin >= bmin;
        assert palMax <= bmax;
        final List<InterpolationPoint> values = new ArrayList<>();
        final double[] nodatas = band0.getNoData();
        if (nodatas != null)
            for (double nodata : nodatas) {
                values.add(STYLE_FACTORY.interpolationPoint(nodata, STYLE_FACTORY.literal(new Color(0, 0, 0, 0))));
            }
        values.add(STYLE_FACTORY.interpolationPoint(Float.NaN, STYLE_FACTORY.literal(new Color(0, 0, 0, 0))));
        // -- Color palette
        // Color[] colorsPal = PaletteFactory.getDefault().getColors("rainbow-t");
        Color[] colorsPal = PaletteFactory.getDefault().getColors("grayscale");
        assert colorsPal.length >= 2;
        if (colorsPal.length < 4) {
            final double percent_5 = (colorsPal.length == 3) ? 0.1 : 0.05;
            final Color[] colorsPalTemp = colorsPal;
            colorsPal = Arrays.copyOf(colorsPal, colorsPal.length + 2);
            System.arraycopy(colorsPalTemp, 2, colorsPal, 2, colorsPalTemp.length - 2);
            colorsPal[colorsPal.length - 1] = colorsPalTemp[colorsPalTemp.length - 1];
            colorsPal[1] = DefaultInterpolate.interpolate(colorsPalTemp[0], colorsPalTemp[1], percent_5);
            colorsPal[colorsPal.length - 2] = DefaultInterpolate.interpolate(colorsPalTemp[colorsPalTemp.length - 2], colorsPalTemp[colorsPalTemp.length - 1], 1 - percent_5);
        }
        // -- if difference between band minimum statistic and palette minimum,
        // -- define values between them as transparency
        values.add(STYLE_FACTORY.interpolationPoint(bmin, STYLE_FACTORY.literal(colorsPal[0])));
        assert colorsPal.length >= 4;
        // -- min and max transparency
        final double step = (palMax - palMin) / (colorsPal.length - 3);
        double currentVal = palMin;
        for (int c = 1; c <= colorsPal.length - 2; c++) {
            values.add(STYLE_FACTORY.interpolationPoint(currentVal, STYLE_FACTORY.literal(colorsPal[c])));
            currentVal += step;
        }
        assert StrictMath.abs(currentVal - step - palMax) < 1E-9;
        values.add(STYLE_FACTORY.interpolationPoint(bmax, STYLE_FACTORY.literal(colorsPal[colorsPal.length - 1])));
        final Expression function = STYLE_FACTORY.interpolateFunction(DEFAULT_CATEGORIZE_LOOKUP, values, Method.COLOR, Mode.LINEAR, DEFAULT_FALLBACK);
        final ColorMap colorMap = STYLE_FACTORY.colorMap(function);
        final RasterSymbolizer symbol = STYLE_FACTORY.rasterSymbolizer(band0.getName(), null, null, null, colorMap, null, null, null);
        return Optional.of(STYLE_FACTORY.style(symbol));
    } else {
        LOGGER.log(Level.FINE, "RGBStyle : fallBack way is choosen." + "RGB interpretation of the three first coverage image bands.");
        final int rgbNumBand = forceAlpha && bands.length > 3 ? 4 : 3;
        assert rgbNumBand <= bands.length;
        final double[][] ranges = buildRanges(stats, 4);
        final List<DRChannel> channels = new ArrayList<>();
        for (int i = 0; i < rgbNumBand; i++) {
            final DRChannel channel = new DRChannel();
            final String bandName = bands[i].getName();
            channel.setBand(bandName == null || bandName.isEmpty() ? Integer.toString(i) : bandName);
            channel.setColorSpaceComponent(DynamicRangeSymbolizer.DRChannel.RGBA_COMPONENTS[i]);
            DynamicRangeSymbolizer.DRBound drBound = new DynamicRangeSymbolizer.DRBound();
            drBound.setValue(FILTER_FACTORY.literal(ranges[i][0]));
            channel.setLower(drBound);
            drBound = new DynamicRangeSymbolizer.DRBound();
            drBound.setValue(FILTER_FACTORY.literal(ranges[i][1]));
            channel.setUpper(drBound);
            channels.add(channel);
        }
        final DynamicRangeSymbolizer drgb = new DynamicRangeSymbolizer();
        drgb.setChannels(channels);
        return Optional.of(STYLE_FACTORY.style(drgb));
    }
}
Also used : InterpolationPoint(org.geotoolkit.style.function.InterpolationPoint) DRChannel(org.geotoolkit.display2d.ext.dynamicrange.DynamicRangeSymbolizer.DRChannel) Color(java.awt.Color) ArrayList(java.util.ArrayList) MultiLineString(org.locationtech.jts.geom.MultiLineString) LineString(org.locationtech.jts.geom.LineString) MultiPoint(org.locationtech.jts.geom.MultiPoint) Point(org.locationtech.jts.geom.Point) Paint(java.awt.Paint) InterpolationPoint(org.geotoolkit.style.function.InterpolationPoint) DynamicRangeSymbolizer(org.geotoolkit.display2d.ext.dynamicrange.DynamicRangeSymbolizer) Expression(org.opengis.filter.Expression) ImageStatistics(org.geotoolkit.storage.coverage.ImageStatistics)

Aggregations

DynamicRangeSymbolizer (org.geotoolkit.display2d.ext.dynamicrange.DynamicRangeSymbolizer)2 Expression (org.opengis.filter.Expression)2 Color (java.awt.Color)1 Paint (java.awt.Paint)1 Rectangle (java.awt.Rectangle)1 RenderingHints (java.awt.RenderingHints)1 IOException (java.io.IOException)1 NumberFormat (java.text.NumberFormat)1 ArrayList (java.util.ArrayList)1 AtomicLong (java.util.concurrent.atomic.AtomicLong)1 MapLayer (org.apache.sis.portrayal.MapLayer)1 MapLayers (org.apache.sis.portrayal.MapLayers)1 DataStoreException (org.apache.sis.storage.DataStoreException)1 NoSuchDataException (org.apache.sis.storage.NoSuchDataException)1 PortrayalException (org.geotoolkit.display.PortrayalException)1 DRChannel (org.geotoolkit.display2d.ext.dynamicrange.DynamicRangeSymbolizer.DRChannel)1 CanvasDef (org.geotoolkit.display2d.service.CanvasDef)1 SceneDef (org.geotoolkit.display2d.service.SceneDef)1 Hints (org.geotoolkit.factory.Hints)1 ProcessEvent (org.geotoolkit.process.ProcessEvent)1