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);
}
}
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));
}
}
Aggregations