Skip to content

Commit

Permalink
Simplified the sorting
Browse files Browse the repository at this point in the history
  • Loading branch information
Devon Tucker committed Aug 15, 2016
1 parent 99e7d42 commit 6c6af02
Showing 1 changed file with 40 additions and 86 deletions.
Expand Up @@ -14,7 +14,6 @@
import org.geotools.coverage.grid.GridCoverage2D;
import org.geotools.coverage.grid.GridCoverageFactory;
import org.geotools.coverage.processing.Operations;
import org.geotools.data.DataUtilities;
import org.geotools.factory.Hints;
import org.geotools.gce.imagemosaic.GranuleDescriptor;
import org.geotools.gce.imagemosaic.MergeBehavior;
Expand All @@ -27,7 +26,6 @@
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.image.ImageWorker;
import org.geotools.referencing.CRS;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.filter.sort.SortBy;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
Expand All @@ -37,10 +35,7 @@
* works by grouping together everything with a like CRS (and like SortBy property if supplied) and
* mosaicking them separately before forming a final mosaic.
*
* In order to provide accurate results this class groups not just by CRS but also by SortBy values
* before doing the submosaic. This means if you have a large range of values in the sort by key
* returned in the requested area along with a large number of granules requiring reprojection we
* may end up with a large number of resampling operations performed.
* This relies on the SortBy including CRS as a final SortBy clause
*/
class ReprojectingSubmosaicProducer extends BaseSubmosaicProducer {

Expand All @@ -51,26 +46,23 @@ class ReprojectingSubmosaicProducer extends BaseSubmosaicProducer {
// operations factory to use for resampling
private final Operations operations;

// the sort by requested
private final SortBy[] sortBy;

private CoordinateReferenceSystem targetCRS;

private RasterLayerResponse response;

private List<CRSBoundMosaicProducer> perMosaicProducers = new ArrayList<>();

private CoordinateReferenceSystem currentCRS;

private CRSBoundMosaicProducer currentSubmosaicProducer;

ReprojectingSubmosaicProducer(RasterLayerRequest request, RasterLayerResponse response,
RasterManager rasterManager, boolean dryRun) {
super(response, dryRun);
this.targetCRS = rasterManager.getConfiguration().getCrs();
this.response = response;
this.dryRun = dryRun;

Hints hints = rasterManager.getHints();
this.renderingHints = createRenderingHints(hints, request);
this.operations = new Operations(renderingHints);
this.sortBy = response.getSortBy();
}

private RenderingHints createRenderingHints(Hints hints, RasterLayerRequest request) {
Expand All @@ -84,37 +76,33 @@ private RenderingHints createRenderingHints(Hints hints, RasterLayerRequest requ

@Override
public boolean accept(GranuleDescriptor granuleDescriptor) {
String granuleCRSCode = getGranuleCRSCode(granuleDescriptor);
CoordinateReferenceSystem granuleCRS;
try {
granuleCRS = getCRS(granuleCRSCode);
} catch (FactoryException e) {
throw new IllegalStateException(e);
}

boolean accepted = false;
//see if we can find an existing submosaic producer that can handle this granule
for (CRSBoundMosaicProducer producer : perMosaicProducers) {
accepted = producer.accept(granuleDescriptor);
if (accepted) {
break;
}
}

//we didn't find any, so let's create one that can handle and add it to the list
if (!accepted) {
CRSBoundMosaicProducer producer = new CRSBoundMosaicProducer(rasterLayerResponse,
dryRun, granuleCRS, sortBy, granuleDescriptor);
perMosaicProducers.add(producer);
accepted = true;
if (this.currentCRS == null) {
//this is the first granule we're processing, let's set up the current producer and
//"forcefully" accept it with doAccept
this.currentCRS = granuleDescriptor.getGranuleEnvelope().getCoordinateReferenceSystem();
this.currentSubmosaicProducer = new CRSBoundMosaicProducer(
rasterLayerResponse,
dryRun,
currentCRS, granuleDescriptor);
perMosaicProducers.add(currentSubmosaicProducer);
return true;
} else {
//we have a current CRS group, either it matches or we need to create a new one
boolean accepted = currentSubmosaicProducer.accept(granuleDescriptor);
if (!accepted) {
//granule was rejected by the current producer, presumably because its CRS didn't
//match, we need to create a new one because we've moved on to the next
this.currentSubmosaicProducer = new CRSBoundMosaicProducer(
rasterLayerResponse,
dryRun,
currentCRS, granuleDescriptor);
perMosaicProducers.add(currentSubmosaicProducer);
accepted = currentSubmosaicProducer.acceptGranule(granuleDescriptor);
}
return accepted;
}

return accepted;
}

private static String getGranuleCRSCode(GranuleDescriptor granuleDescriptor) {
return (String) granuleDescriptor.getOriginator()
.getAttribute(CRSExtractor.DEFAULT_ATTRIBUTE_NAME);
}

protected static CoordinateReferenceSystem getCRS(String granuleCRSCode) throws FactoryException {
Expand Down Expand Up @@ -168,30 +156,19 @@ private MosaicElement reprojectMosaicElement(MosaicElement mosaicElement,
}

/**
* This submosaic producer takes a CRS and then only accepts granules that match that CRS. In
* order to properly support sort order + reprojection we group together granules based on CRS
* and also unique sort value combinations.
* This submosaic producer takes a CRS and then only accepts granules that match that CRS.
*
* For example, if the sort order is resolution and we have data in two CRSs (A and B) and two
* resolutions (say 250m and 500m) we will group together A-500m and B-500m, then A-250m and
* B-250m, mosaic each group separately, then mosaic all those results together.
*/
private static class CRSBoundMosaicProducer extends BaseSubmosaicProducer {

private final CoordinateReferenceSystem crs;

private final GranuleDescriptor templateDescriptor;

private final SortBy[] sortBy;

private ReferencedEnvelope submosaicBBOX;

public CRSBoundMosaicProducer(RasterLayerResponse rasterLayerResponse, boolean dryRun,
CoordinateReferenceSystem crs, SortBy[] sortBy, GranuleDescriptor templateDescriptor) {
CoordinateReferenceSystem crs, GranuleDescriptor templateDescriptor) {
super(rasterLayerResponse, dryRun);
this.crs = crs;
this.templateDescriptor = templateDescriptor;
this.sortBy = sortBy;

//always accept the template granule descriptor
this.doAccept(templateDescriptor);
Expand All @@ -210,41 +187,18 @@ public boolean accept(GranuleDescriptor granuleDescriptor) {
boolean shouldAccept = false;

//need to check that the granule matches CRS
try {
CoordinateReferenceSystem granuleCRS = getCRS(
getGranuleCRSCode(granuleDescriptor));
shouldAccept = CRS.equalsIgnoreMetadata(granuleCRS, this.crs);
} catch (FactoryException e) {
throw new IllegalArgumentException("Unable to create granule CRS from CRS code. "
+ "Cannot proceed with mosaicking.", e);
}
CoordinateReferenceSystem granuleCRS = granuleDescriptor.getGranuleEnvelope().getCoordinateReferenceSystem();
shouldAccept = CRS.equalsIgnoreMetadata(granuleCRS, this.crs);

//we need to see if the incoming granule matches the "template" feature this producer
//was initialized with.
if (sortBy != null) {
SimpleFeature granuleFeature = granuleDescriptor.getOriginator();
SimpleFeature templateFeature = templateDescriptor.getOriginator();
for (SortBy sortKey : sortBy) {
Object incomingAttribute = granuleFeature
.getAttribute(sortKey.getPropertyName().getPropertyName());
Object templateAttribute = templateFeature.getAttribute(
sortKey.getPropertyName().getPropertyName());

if (!DataUtilities.attributesEqual(incomingAttribute, templateAttribute)) {
shouldAccept = false;
break;
}
}
}

if (shouldAccept) {
return doAccept(granuleDescriptor);
}
else {
return false;
}
return shouldAccept && doAccept(granuleDescriptor);
}

/**
* This method skips any checking of the granuleDescriptor. Only used when we're sure this
* descriptor belongs in this producer
* @param granuleDescriptor
* @return
*/
private boolean doAccept(GranuleDescriptor granuleDescriptor) {
// Need to keep track of the eventual bounding box of this submosaic
if (this.submosaicBBOX == null) {
Expand Down

0 comments on commit 6c6af02

Please sign in to comment.