Skip to content

Commit

Permalink
[GEOS-8249]: heterogeneous resolution coverageView completion
Browse files Browse the repository at this point in the history
  • Loading branch information
aaime committed Oct 6, 2017
1 parent 8ba473d commit fc86e94
Show file tree
Hide file tree
Showing 9 changed files with 198 additions and 151 deletions.
38 changes: 31 additions & 7 deletions doc/en/user/source/data/raster/coverageview.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,24 @@
Coverage Views
==============

Starting with GeoServer 2.6.0, You can define a new raster layer as a Coverage View.
Starting with GeoServer 2.6.0, You can define a new raster layer as a Coverage View.
Coverage Views allow defining a View made of different bands originally available inside coverages (either bands of the same coverage or different coverages) of the same Coverage Store.

Creating a Coverage View
------------------------

In order to create a Coverage View the administrator invokes the :guilabel:`Create new layer` page.
When a Coverage store is selected, the usual list of coverages available for publication appears.
In order to create a Coverage View the administrator invokes the :guilabel:`Create new layer` page.
When a Coverage store is selected, the usual list of coverages available for publication appears.
A link :guilabel:`Configure new Coverage view...` also appears:

.. figure:: images/coverageviewnewlayer.png
:align: center

Selecting the :guilabel:`Configure new Coverage view...` link opens a new page where you can configure the coverage view:

.. figure:: images/newcoverageview.png
:align: center

The upper text box allows to specify the name to be assigned to this coverage view. (In the following picture we want to create as example, a **currents** view merging together both u and v components of the currents, which are exposed as separated 1band coverages).

.. figure:: images/coverageviewname.png
Expand All @@ -32,7 +32,7 @@ It is possible to specify which input coverage bands need to be put on the view
.. figure:: images/coverageviewselectbands.png
:align: center

Once selected, they needs to be added to the output bands of the coverage view, using the :guilabel:`add` button.
Once selected, they needs to be added to the output bands of the coverage view, using the :guilabel:`add` button.

.. figure:: images/coverageviewaddbands.png
:align: center
Expand All @@ -59,6 +59,31 @@ Once all the properties of the layer have been configured, by selecting the :gui
.. figure:: images/coverageviewavailablelayers.png
:align: center

Heterogeneous coverage views
----------------------------

In case the various coverages bound in the view have different resolution, the UI will present
two extra controls:

.. figure:: images/coverageviewhetero.png
:align: center

The **coverage envelope policy** defines how the bounding box of the output is calculated for metadata
purposes. Having different resolutions, the coverages are unlikely to share the same bounding box. The possible values are:

* **Intersect envelopes**: Use the intersection of all input coverage envelopes
* **Union envelopes**: Use the union of all input coverage envelopes

The **coverage resolution policy** defines which target resolution is used when generating outputs:

* **Best**: Use the best resolution available among the chosen bands (e.g., in a set having 60m, 20m and 10m the 10m resolution will be chosen)
* **Worst**: Use the worst resolution available among the chosen bands (e.g., in a set having 60m, 20m and 10m the 60m resolution will be chosen)

The coverage resolution policy is *context sensitive*. Assume the input is a 12 bands Sentinel 2 dataset at three different
resolution, 10, 20 and 30 meters, and a false color image is generated by performing a band selection in the SLD.
If the policy is *best* and the SLD selects only bands at 20 and 60 meters, the output will be at 20 meters instead of
10 meters.

Coverage View in action
-----------------------

Expand All @@ -67,4 +92,3 @@ the values of the bands composing the coverage view.

.. figure:: images/coverageviewlayerpreview.png
:align: center

Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public static enum EnvelopeCompositionType {
* Which Resolution to be used in composition
*/
public static enum SelectedResolution {
BEST, WORST, INDEX;
BEST, WORST;
}

/**
Expand Down
114 changes: 71 additions & 43 deletions src/main/src/main/java/org/geoserver/catalog/CoverageViewHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,15 @@
import org.geoserver.catalog.CoverageView.EnvelopeCompositionType;
import org.geoserver.catalog.CoverageView.InputCoverageBand;
import org.geoserver.catalog.CoverageView.SelectedResolution;
import org.geotools.coverage.grid.GridCoverage2D;
import org.geotools.coverage.grid.GridEnvelope2D;
import org.geotools.coverage.grid.io.GridCoverage2DReader;
import org.geotools.coverage.grid.io.OverviewPolicy;
import org.geotools.data.DataSourceException;
import org.geotools.geometry.GeneralEnvelope;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.referencing.CRS;
import org.geotools.referencing.operation.builder.GridToEnvelopeMapper;
import org.geotools.referencing.operation.transform.AffineTransform2D;
import org.geotools.referencing.operation.transform.ProjectiveTransform;
import org.opengis.coverage.grid.GridEnvelope;
import org.opengis.parameter.ParameterDescriptor;
Expand All @@ -38,18 +39,55 @@
class CoverageViewHandler {

/**
* Visit the a coverage and decide if its resolution is better than the ones
* previously visited
*/
abstract static class CoverageResolutionChooser {

double[] resolution;

boolean visit(GridCoverage2D coverage) throws IOException {
MathTransform2D mt = coverage.getGridGeometry().getGridToCRS2D();
// cannot do a comparison
if (!(mt instanceof AffineTransform2D)) {
return false;
}
AffineTransform2D at = (AffineTransform2D) mt;
double scaleX = Math.abs(at.getScaleX());
double scaleY = Math.abs(at.getScaleY());
if (resolution == null) {
this.resolution = new double[2];
this.resolution[0] = scaleX;
this.resolution[1] = scaleY;
return true;
}
if (compare(scaleX, scaleY, resolution)) {
this.resolution[0] = scaleX;
this.resolution[1] = scaleY;
return true;
} else {
return false;
}
}

protected abstract boolean compare(double scaleX, double scaleY, double[] resolution);
}

/**
* Visit the reader for a coverage composing the view
* and compute the resolution levels
*/
interface ResolutionComposer {
interface ReaderResolutionComposer {
void visit(GridCoverage2DReader reader) throws IOException;

double[][] getResolutionLevels();

String getReferenceName();

CoverageResolutionChooser getCoverageResolutionChooser();
}

abstract class AbstractResolutionComposer implements ResolutionComposer {
abstract class AbstractReaderResolutionComposer implements ReaderResolutionComposer {
double[][] resolution;

String referenceName = null;
Expand All @@ -68,7 +106,7 @@ public String getReferenceName() {
/**
* Implementation returning the Best resolution of the visited elements
*/
class BestResolutionComposer extends AbstractResolutionComposer {
class BestReaderResolutionComposer extends AbstractReaderResolutionComposer {

@Override
public void visit(GridCoverage2DReader reader) throws IOException {
Expand All @@ -83,12 +121,23 @@ public void visit(GridCoverage2DReader reader) throws IOException {
}
}
}

@Override
public CoverageResolutionChooser getCoverageResolutionChooser() {
return new CoverageResolutionChooser() {
@Override
protected boolean compare(double scaleX, double scaleY, double[] resolution) {
return scaleX < resolution[0] && scaleY < resolution[1];
}
};
}

}

/**
* Implementation returning the Worst resolution of the visited elements
*/
class WorstResolutionComposer extends AbstractResolutionComposer {
class WorstReaderResolutionComposer extends AbstractReaderResolutionComposer {

@Override
public void visit(GridCoverage2DReader reader) throws IOException {
Expand All @@ -103,33 +152,18 @@ public void visit(GridCoverage2DReader reader) throws IOException {
}
}
}
}

/**
* Implementation returning the resolution of the i-tx coverage of the view
*/
class IndexedResolutionComposer extends AbstractResolutionComposer {

private int selectedResolutionIndex;

private int visited = -1;

public IndexedResolutionComposer(int selectedResolutionIndex) {
this.selectedResolutionIndex = selectedResolutionIndex;
}

@Override
public void visit(GridCoverage2DReader reader) throws IOException {
visited++;
if (visited == selectedResolutionIndex) {
resolution = reader.getResolutionLevels();
referenceName = reader.getGridCoverageNames()[0];
}
public CoverageResolutionChooser getCoverageResolutionChooser() {
return new CoverageResolutionChooser() {
@Override
protected boolean compare(double scaleX, double scaleY, double[] resolution) {
return scaleX > resolution[0] && scaleY > resolution[1];
}
};
}
}

//TODO: Add support for imposed Resolution

/**
* Visit the reader for a coverage composing the view and compute the envelope.
*/
Expand Down Expand Up @@ -334,12 +368,12 @@ public boolean checkConsistency(GridCoverage2DReader reader) throws IOException
/** The coverageView definition */
private CoverageView coverageView;

private ResolutionComposer resolutionComposer;
private ReaderResolutionComposer resolutionComposer;

private EnvelopeComposer envelopeComposer;

public CoverageViewHandler(boolean supportHeterogeneousCoverages, GridCoverage2DReader delegate,
String referenceName, CoverageView coverageView) {
String referenceName, CoverageView coverageView) {
this.supportHeterogeneousCoverages = supportHeterogeneousCoverages;
this.delegate = delegate;
this.referenceName = referenceName;
Expand Down Expand Up @@ -409,10 +443,6 @@ public GridEnvelope getOriginalGridRange() {

}

public String getReferenceName() {
return referenceName;
}

public MathTransform getOriginalGridToWorld(PixelInCell pixInCell) {
if (homogeneousCoverages) {
return delegate.getOriginalGridToWorld(referenceName, pixInCell);
Expand All @@ -436,17 +466,15 @@ public MathTransform getOriginalGridToWorld(PixelInCell pixInCell) {
}


private ResolutionComposer initResolutionComposer() {
private ReaderResolutionComposer initResolutionComposer() {
SelectedResolution selectedResolution = coverageView.getSelectedResolution();
switch (selectedResolution) {
case WORST:
return new WorstResolutionComposer();
return new WorstReaderResolutionComposer();
case BEST:
return new BestResolutionComposer();
case INDEX:
return new IndexedResolutionComposer(coverageView.getSelectedResolutionIndex());
return new BestReaderResolutionComposer();
default:
return new BestResolutionComposer();
return new BestReaderResolutionComposer();
}
}

Expand All @@ -464,14 +492,14 @@ private EnvelopeComposer initEnvelopeComposer() {

public double[] getReadingResolutions(OverviewPolicy policy, double[] requestedResolution)
throws IOException {
// TODO
if (homogeneousCoverages) {
return delegate.getReadingResolutions(referenceName, policy, requestedResolution);
}
return delegate.getReadingResolutions(referenceName, policy, requestedResolution);
}

EnvelopeCompositionType getEnvelopeCompositionType() {
return coverageView.getEnvelopeCompositionType();
}

CoverageResolutionChooser getCoverageResolutionChooser() {
return resolutionComposer.getCoverageResolutionChooser();
}
}

0 comments on commit fc86e94

Please sign in to comment.