/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2006-2008, Open Source Geospatial Foundation (OSGeo)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
package org.geotools.gce.imagemosaic;
import java.util.ArrayList;
import java.util.Collections;
import org.geotools.coverage.grid.io.OverviewPolicy;
import org.geotools.util.Utilities;
/**
* A class to handle overviews resolution levels. It stores overviews resolution levels information
* and suggests the level to be used depending on the current request and the {@link OverviewPolicy}.
*
* @author Simone Giannecchini, GeoSolutions SAS
* @author Daniele Romagnoli, GeoSolutions SAS
*/
final class OverviewsController {
final ArrayList<OverviewLevel> resolutionsLevels = new ArrayList<OverviewLevel>();
/**
* Constructor.
*
* @param highestRes The resolution values for the finest level, <b>This is treated as level 0.</b>
* @param numberOfOvervies number of overview levels.
* @param overviewsResolution resolutions for the various levels. <b>Implicitly, the index of the resolution is the index of the corresponding level.</b>
*/
public OverviewsController(
final double[] highestRes,
final int numberOfOvervies,
final double[][] overviewsResolution) {
// notice that we assume what follows:
// -highest resolution image is at level 0.
// -all the overviews share the same envelope
// -the aspect ratio for the overviews is constant
// -the provided resolutions are taken directly from the grid
resolutionsLevels.add(new OverviewLevel(1, highestRes[0], highestRes[1], 0));
if (numberOfOvervies > 0) {
for (int i = 0; i < overviewsResolution.length; i++) {
resolutionsLevels.add(new OverviewLevel(overviewsResolution[i][0] / highestRes[0],
overviewsResolution[i][0], overviewsResolution[i][1], i + 1));
}
Collections.sort(resolutionsLevels);
}
}
/**
* Given a specified {@link OverviewPolicy} and a {@link RasterLayerRequest}, suggest the proper
* overview level index.
* @param policy
* @param request
* @return the OverviewLevel index
*/
int pickOverviewLevel(final OverviewPolicy policy, final double[] requestedResolution) {
// //
//
// If this file has only
// one page we use decimation, otherwise we use the best page available.
// Future versions should use both.
//
// //
if (resolutionsLevels == null || resolutionsLevels.size() <= 0)
return 0;
// Now search for the best matching resolution.
// Check also for the "perfect match"... unlikely in practice unless someone
// tunes the clients to request exactly the resolution embedded in
// the overviews, something a perf sensitive person might do in fact
// requested scale factor for least reduced axis
final OverviewLevel max = (OverviewLevel) resolutionsLevels.get(0);
// the requested resolutions
final double requestedScaleFactorX;
final double requestedScaleFactorY;
if (requestedResolution != null) {
final double reqx = requestedResolution[0];
final double reqy = requestedResolution[1];
requestedScaleFactorX = reqx / max.resolutionX;
requestedScaleFactorY = reqy / max.resolutionY;
} else {
return 0;
}
final int leastReduceAxis = requestedScaleFactorX <= requestedScaleFactorY ? 0 : 1;
final double requestedScaleFactor = leastReduceAxis == 0 ? requestedScaleFactorX : requestedScaleFactorY;
// are we looking for a resolution even higher than the native one?
if (requestedScaleFactor <= 1) {
return max.imageChoice;
}
// are we looking for a resolution even lower than the smallest overview?
final OverviewLevel min = (OverviewLevel) resolutionsLevels.get(resolutionsLevels.size() - 1);
if (requestedScaleFactor >= min.scaleFactor) {
return min.imageChoice;
}
// Ok, so we know the overview is between min and max, skip the first
// and search for an overview with a resolution lower than the one requested,
// that one and the one from the previous step will bound the searched resolution
OverviewLevel prev = max;
final int size = resolutionsLevels.size();
for (int i = 1; i < size; i++) {
final OverviewLevel curr = resolutionsLevels.get(i);
// perfect match check
if (curr.scaleFactor == requestedScaleFactor) {
return curr.imageChoice;
}
// middle check. The first part of the condition should be sufficient, but
// there are cases where the x resolution is satisfied by the lowest resolution,
// the y by the one before the lowest (so the aspect ratio of the request is
// different than the one of the overviews), and we would end up going out of the
// loop since not even the lowest can "top" the request for one axis
if (curr.scaleFactor > requestedScaleFactor || i == size - 1) {
if (policy == OverviewPolicy.QUALITY) {
return prev.imageChoice;
} else if (policy == OverviewPolicy.SPEED) {
return curr.imageChoice;
} else if (requestedScaleFactor - prev.scaleFactor < curr.scaleFactor - requestedScaleFactor) {
return prev.imageChoice;
} else {
return curr.imageChoice;
}
}
prev = curr;
}
// fallback
return max.imageChoice;
}
/**
* Simple support class for sorting overview resolutions
*
* @author Andrea Aime
* @author Simone Giannecchini, GeoSolutions.
* @since 2.5
*/
static class OverviewLevel implements Comparable<OverviewLevel> {
double scaleFactor;
double resolutionX;
double resolutionY;
int imageChoice;
/**
*
* @param scaleFactor
* @param resolutionX
* @param resolutionY
* @param imageChoice
*/
public OverviewLevel(final double scaleFactor, final double resolutionX,
final double resolutionY, final int imageChoice) {
this.scaleFactor = scaleFactor;
this.resolutionX = resolutionX;
this.resolutionY = resolutionY;
this.imageChoice = imageChoice;
}
public int compareTo(final OverviewLevel other) {
if (scaleFactor > other.scaleFactor) {
return 1;
} else if (scaleFactor < other.scaleFactor) {
return -1;
} else {
return 0;
}
}
@Override
public String toString() {
return "OverviewLevel[Choice=" + imageChoice + ",scaleFactor="
+ scaleFactor + ",resX:" + resolutionX + ",resY:"
+ resolutionY + "]";
}
@Override
public int hashCode() {
int hash = Utilities.hash(imageChoice, 31);
hash = Utilities.hash(resolutionX, hash);
hash = Utilities.hash(resolutionY, hash);
hash = Utilities.hash(scaleFactor, hash);
return hash;
}
}
}