Skip to content

Commit

Permalink
OSEO, improved band selection, it is now possible to mix single band …
Browse files Browse the repository at this point in the history
…and multibands in the layer view
  • Loading branch information
aaime committed Nov 5, 2018
1 parent 26664ae commit f4c0923
Show file tree
Hide file tree
Showing 17 changed files with 540 additions and 270 deletions.
24 changes: 10 additions & 14 deletions doc/en/api/1.0.0/opensearch-eo.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -383,20 +383,16 @@ paths:
"layer": "test123",
"separateBands": true,
"bands": [
1,
2,
3,
4,
5,
6,
7,
8
],
"browseBands": [
4,
3,
2
],
"B02",
"B03",
"B04",
"B08"
],
"browseBands": [
"B08",
"B03",
"B02"
],
"heterogeneousCRS": true,
"mosaicCRS": "EPSG:4326"
}
Expand Down
36 changes: 20 additions & 16 deletions doc/en/user/source/community/opensearch-eo/automation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ the collection structure:
"workspace": "gs",
"layer": "test123",
"separateBands": false,
"browseBands": [1],
"browseBands": ["test123[0]"],
"heterogeneousCRS": false
}
Expand All @@ -180,21 +180,25 @@ the collection structure:
"workspace": "gs",
"layer": "test123",
"separateBands": true,
"bands": [
1,
2,
3,
4,
5,
6,
7,
8
],
"browseBands": [
4,
3,
2
],
"bands": [
"VNIR",
"QUALITY",
"CLOUDSHADOW",
"HAZE",
"SNOW"
],
"browseBands": [
"VNIR[0]", "VNIR[1]", "SNOW"
],
"heterogeneousCRS": true,
"mosaicCRS": "EPSG:4326"
}
In terms of band naming the "bands" parameter contains coverage names as used in the "band" column
of the granules table, in case a granule contains multiple bands, they can be referred by either
using the full name, in which case they will be all picked, or by using zero-based indexes like
``BANDNAME[INDEX]``, which allows to pick a particular band.

The same syntax is meant to be used in the ``browseBands`` property. In case the source is not
split band, the ``browseBands`` can still be used to select specific bands, using the layer
name as the coverage name, e.g. "test123[0]" to select the first band of the coverage.
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,6 @@
*/
package org.geoserver.opensearch.rest;

import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Properties;
import java.util.logging.Logger;
import javax.imageio.ImageReader;
import javax.imageio.spi.ImageReaderSpi;
import javax.imageio.stream.FileImageInputStream;
import javax.media.jai.ImageLayout;
import org.geoserver.catalog.CascadeDeleteVisitor;
import org.geoserver.catalog.Catalog;
import org.geoserver.catalog.CatalogBuilder;
Expand Down Expand Up @@ -72,6 +59,26 @@
import org.opengis.referencing.operation.TransformException;
import org.springframework.http.HttpStatus;

import java.awt.image.SampleModel;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.imageio.ImageReader;
import javax.imageio.spi.ImageReaderSpi;
import javax.imageio.stream.FileImageInputStream;
import javax.media.jai.ImageLayout;

/**
* Helper class with the responsibility of setting up and tearing down mosaics, coverage views,
* layers and styles for the collections.
Expand All @@ -82,6 +89,7 @@ class CollectionLayerManager {
static final Logger LOGGER = Logging.getLogger(CollectionLayerManager.class);
private static final String TIME_START = "timeStart";
static final Hints EXCLUDE_MOSAIC_HINTS = new Hints(Utils.EXCLUDE_MOSAIC, true);
public static final Pattern BAND_SPEC_PATTERN = Pattern.compile("([^\\[]+)(\\[(\\d+)\\])?");

Catalog catalog;

Expand Down Expand Up @@ -283,7 +291,8 @@ private void configureSeparateBandsMosaic(
createMosaicStore(cb, collection, layerConfiguration, relativePath);

// and finally the layer, with a coverage view associated to it
List<CoverageBand> coverageBands = buildCoverageBands(layerConfiguration);
List<CoverageBand> coverageBands =
buildCoverageBands(mosaicStoreInfo, layerConfiguration.getBands());
final String coverageName = layerConfiguration.getLayer();
final CoverageView coverageView = new CoverageView(coverageName, coverageBands);
CoverageInfo coverageInfo =
Expand All @@ -295,7 +304,7 @@ private void configureSeparateBandsMosaic(
catalog.add(layerInfo);

// configure the style if needed
createStyle(layerConfiguration, layerInfo);
createStyle(layerConfiguration, layerInfo, mosaicStoreInfo);
}

private double[][] getResolutionLevelsInCRS(
Expand Down Expand Up @@ -344,18 +353,64 @@ private double distance(double[] points, int base) {
return Math.sqrt(dx * dx + dy * dy);
}

private List<CoverageBand> buildCoverageBands(CollectionLayer collectionLayer) {
private List<CoverageBand> buildCoverageBands(CoverageStoreInfo mosaicStoreInfo, String[] bands)
throws IOException {
// get the coverage names for validation purposes
Set<String> coverageNames = new LinkedHashSet<>();
GridCoverage2DReader reader =
(GridCoverage2DReader) mosaicStoreInfo.getGridCoverageReader(null, null);
coverageNames.addAll(Arrays.asList(reader.getGridCoverageNames()));

// go through all the band selection specs
List<CoverageBand> result = new ArrayList<>();
String[] bands = collectionLayer.getBands();
int j = 0;
for (int i = 0; i < bands.length; i++) {
String band = bands[i];
CoverageBand cb =
new CoverageBand(
Collections.singletonList(new InputCoverageBand(band, "0")),
band,
i,
CompositionType.BAND_SELECT);
result.add(cb);

// the band name could be a straight coverage name, or refer to specific band indexes
Matcher matcher = BAND_SPEC_PATTERN.matcher(band);
if (!matcher.matches()) {
throw new RestException(
"Invalid band name specification, should be a band name as "
+ "a string without square brackets, or bandName[idx], but was "
+ band,
HttpStatus.BAD_REQUEST);
}
String coverageName = matcher.group(1);
if (!coverageNames.contains(coverageName)) {
throw new RestException(
"Could not find coverage named "
+ coverageName
+ ", the available ones are "
+ coverageNames,
HttpStatus.BAD_REQUEST);
}
SampleModel sm = reader.getImageLayout(coverageName).getSampleModel(null);

String bandIndexSpec = matcher.group(3);
if (bandIndexSpec == null) {
// getting all the bands in the coverage, how many do we have?
for (int b = 0; b < sm.getNumBands(); b++) {
CoverageBand cb =
new CoverageBand(
Collections.singletonList(
new InputCoverageBand(coverageName, String.valueOf(b))),
coverageName + ((sm.getNumBands() == 1) ? "" : "_" + b),
j++,
CompositionType.BAND_SELECT);
result.add(cb);
}
} else {
CoverageBand cb =
new CoverageBand(
Collections.singletonList(
new InputCoverageBand(
coverageName, String.valueOf(bandIndexSpec))),
coverageName + "_" + bandIndexSpec,
j++,
CompositionType.BAND_SELECT);
result.add(cb);
}
}
return result;
}
Expand Down Expand Up @@ -422,18 +477,19 @@ private void configureSimpleMosaic(
createDataStoreProperties(collection, mosaic);

CatalogBuilder cb = new CatalogBuilder(catalog);
createMosaicStore(cb, collection, layerConfiguration, relativePath);
CoverageStoreInfo mosaicStore =
createMosaicStore(cb, collection, layerConfiguration, relativePath);

// and then the layer
CoverageInfo coverageInfo = cb.buildCoverage(collection);
CoverageInfo coverageInfo = cb.buildCoverage(layerConfiguration.getLayer());
coverageInfo.setName(layerConfiguration.getLayer());
timeEnableResource(coverageInfo);
catalog.add(coverageInfo);
LayerInfo layerInfo = cb.buildLayer(coverageInfo);
catalog.add(layerInfo);

// configure the style if needed
createStyle(layerConfiguration, layerInfo);
createStyle(layerConfiguration, layerInfo, mosaicStore);
}

private void buildIndexer(
Expand All @@ -442,7 +498,7 @@ private void buildIndexer(
// prepare the mosaic configuration
Properties indexer = new Properties();
indexer.put("UseExistingSchema", "true");
indexer.put("Name", collection);
indexer.put("Name", layerConfiguration.getLayer());
indexer.put("TypeName", collection);
indexer.put("AbsolutePath", "true");
indexer.put("TimeAttribute", TIME_START);
Expand Down Expand Up @@ -479,25 +535,40 @@ private void timeEnableResource(ResourceInfo resource) {
resource.getMetadata().put(ResourceInfo.TIME, dimension);
}

private void createStyle(CollectionLayer layerConfiguration, LayerInfo layerInfo)
private void createStyle(
CollectionLayer layerConfiguration,
LayerInfo layerInfo,
CoverageStoreInfo mosaicStoreInfo)
throws IOException {
CoverageInfo ci = (CoverageInfo) layerInfo.getResource();
String[] bands = layerConfiguration.getBands();
String[] defaultBands =
ci.getDimensions().stream().map(d -> d.getName()).toArray(i -> new String[i]);
final String[] browseBands = layerConfiguration.getBrowseBands();
if (browseBands != null
&& browseBands.length > 0
&& !Arrays.equals(defaultBands, browseBands)) {
// get the band making up the layer, if not found, use the native ones
String[] bandSpecs = layerConfiguration.getBands();
if (bandSpecs == null) {
bandSpecs = mosaicStoreInfo.getGridCoverageReader(null, null).getGridCoverageNames();
}
final String[] browseBandSpecs = layerConfiguration.getBrowseBands();
if (browseBandSpecs != null
&& browseBandSpecs.length > 0
&& !Arrays.equals(bandSpecs, browseBandSpecs)) {

RasterSymbolizerBuilder rsb = new RasterSymbolizerBuilder();
if (browseBands.length == 1) {

List<CoverageBand> coverageBands = buildCoverageBands(mosaicStoreInfo, bandSpecs);
List<CoverageBand> browseBands = buildCoverageBands(mosaicStoreInfo, browseBandSpecs);

if (browseBands.size() == 1) {
ChannelSelectionBuilder cs = rsb.channelSelection();
cs.gray().channelName("" + getBandIndex(browseBands[0], bands, defaultBands));
} else if (browseBands.length == 3) {
cs.gray().channelName("" + getBandIndex(browseBands.get(0), coverageBands));
} else if (browseBands.size() == 3) {
ChannelSelectionBuilder cs = rsb.channelSelection();
cs.red().channelName("" + getBandIndex(browseBands[0], bands, defaultBands));
cs.green().channelName("" + getBandIndex(browseBands[1], bands, defaultBands));
cs.blue().channelName("" + getBandIndex(browseBands[2], bands, defaultBands));
cs.red().channelName("" + getBandIndex(browseBands.get(0), coverageBands));
cs.green().channelName("" + getBandIndex(browseBands.get(1), coverageBands));
cs.blue().channelName("" + getBandIndex(browseBands.get(2), coverageBands));
} else {
throw new RestException(
"Browse bands should select either 1 or 3 bands, but instead they created " +
+ browseBands.size() + " raster bands",
HttpStatus.PRECONDITION_FAILED);
}
Style style = rsb.buildStyle();
StyleInfo si = catalog.getFactory().createStyle();
Expand All @@ -516,23 +587,15 @@ private void createStyle(CollectionLayer layerConfiguration, LayerInfo layerInfo
}
}

private int getBandIndex(String band, String[] bands, String[] defaultBands) {
// using all native bands in a non split-multiband case
String[] lookup = bands;
if (bands == null || bands.length == 0) {
lookup = defaultBands;
}
private int getBandIndex(CoverageBand band, List<CoverageBand> bands) {
// lookup the band order in the split multiband case
for (int i = 0; i < lookup.length; i++) {
if (band.equals(lookup[i])) {
for (int i = 0; i < bands.size(); i++) {
if (band.equals(bands.get(i))) {
return i + 1;
}
}
throw new IllegalArgumentException(
"Could not find browse band "
+ band
+ " among the layer bands "
+ Arrays.toString(lookup));
"Could not find browse band " + band + " among the layer bands " + bands);
}

private void createDataStoreProperties(String collection, Resource mosaic) throws IOException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -479,13 +479,6 @@ private void validateLayer(CollectionLayer layer) {
"Invalid layer configuration, browse bands must be either "
+ "one (gray) or three (RGB)",
HttpStatus.BAD_REQUEST);
} else if (layer.getBands().length > 0
&& layer.getBrowseBands().length > 0
&& !containedFully(layer.getBrowseBands(), layer.getBands())) {
throw new RestException(
"Invalid layer configuration, browse bands contains entries "
+ "that are not part of the bands declaration",
HttpStatus.BAD_REQUEST);
}
}
// right now the mosaic can only be in 4326 because the granule table is in that CRS
Expand Down

0 comments on commit f4c0923

Please sign in to comment.