/**
* This program 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, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @author Gabriel Roldan (OpenGeo) 2010
*
*/
package org.geowebcache.diskquota.storage;
import java.math.BigInteger;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.geowebcache.GeoWebCacheException;
import org.geowebcache.diskquota.storage.PagePyramid.PageLevelInfo;
import org.geowebcache.grid.GridSubset;
import org.geowebcache.layer.TileLayer;
import org.geowebcache.layer.TileLayerDispatcher;
import org.geowebcache.mime.MimeType;
import org.geowebcache.storage.StorageBroker;
import org.geowebcache.storage.StorageException;
import org.geowebcache.storage.TileRange;
import org.springframework.util.Assert;
/**
* Supports the organization of tiles into groups (tile pages) for disk quota accounting purposes
*/
public class TilePageCalculator {
private static final Log log = LogFactory.getLog(TilePageCalculator.class);
private TileLayerDispatcher tld;
private StorageBroker sb;
public TilePageCalculator(final TileLayerDispatcher tld, final StorageBroker sb) {
this.tld = tld;
this.sb = sb;
}
public TilePage pageForTile(final TileSet tileSet, final long[] tileIndex) {
int[] pageIndexForTile = new int[3];
pageIndexForTile(tileSet, tileIndex, null);
String tileSetId = tileSet.getId();
int pageX = pageIndexForTile[0];
int pageY = pageIndexForTile[1];
byte zoomLevel = (byte) pageIndexForTile[2];
return new TilePage(tileSetId, pageX, pageY, zoomLevel);
}
public int[] pageIndexForTile(final TileSet tileSet, final long[] tileIndex,
int[] pageIndexTarget) {
Assert.notNull(tileSet);
Assert.notNull(tileIndex);
Assert.isTrue(pageIndexTarget != null && pageIndexTarget.length > 2);
PagePyramid pagePyramid = getPagePyramid(tileSet);
pagePyramid.pageIndexForTile(tileIndex[0], tileIndex[1], (int) tileIndex[2],
pageIndexTarget);
return pageIndexTarget;
}
private PagePyramid getPagePyramid(TileSet tileSet) {
PagePyramid pagePyramid = newPagePyramid(tileSet);
return pagePyramid;
}
public BigInteger getTilesPerPage(TileSet tileSet, int zoomLevel) {
PagePyramid pagePyramid = getPagePyramid(tileSet);
PageLevelInfo pageInfo = pagePyramid.getPageInfo(zoomLevel);
BigInteger tilesPerPage = pageInfo.tilesPerPage;
return tilesPerPage;
}
/**
* Returns a grid subset coverage range suitable for {@link TileRange}
*
* @param page
* @return {@code [minTileX, minTileY, maxTileX, maxTileY, zoomlevel]}
*/
public long[][] toGridCoverage(TileSet tileSet, TilePage page) {
PagePyramid pagePyramid = getPagePyramid(tileSet);
int pageX = page.getPageX();
int pageY = page.getPageY();
int level = page.getZoomLevel();
long[][] gridCoverage = pagePyramid.toGridCoverage(pageX, pageY, level);
return gridCoverage;
}
public Set<String> getLayerNames() {
return tld.getLayerNames();
}
public Set<TileSet> getTileSetsFor(final String layerName) {
return getTileSetsFor(layerName, Optional.empty(), Optional.empty(), Optional.empty());
}
public Set<TileSet> getTileSetsFor(final String layerName,
final Collection<GridSubset> gridSubSets, final List<MimeType> mimeTypes) {
return getTileSetsFor(layerName, Optional.of(gridSubSets), Optional.of(mimeTypes), Optional.empty());
}
@SuppressWarnings("unchecked")
public Set<TileSet> getTileSetsFor(
final String layerName,
final Optional<? extends Collection<GridSubset>> optGridSubsets,
final Optional<? extends Collection<MimeType>> optMimeTypes,
final Optional<? extends Collection<String>> optParameterIds) {
// If we don't have gridsets or mime types, we need to look up the layer object
final TileLayer tileLayer;
if(!(optGridSubsets.isPresent() && optMimeTypes.isPresent())) {
try {
tileLayer = tld.getTileLayer(layerName);
} catch (GeoWebCacheException e) {
throw new IllegalArgumentException(e);
}
} else {
tileLayer = null;
}
Collection<String> gridsetNames = getGridSubsetNames((Optional<Collection<GridSubset>>) optGridSubsets, tileLayer);
Collection<String> formatNames = getFormatNames((Optional<Collection<MimeType>>) optMimeTypes, tileLayer);
Collection<String> parameterIds = getParameterIds(layerName, (Optional<Collection<String>>) optParameterIds);
return
gridsetNames.stream()
.flatMap(gridset->
formatNames.stream()
.flatMap(format->
parameterIds.stream()
.map(parametersId->
new TileSet(layerName, gridset, format, parametersId)
)
)
)
.collect(Collectors.toSet());
}
private Collection<String> getGridSubsetNames(Optional<Collection<GridSubset>> gridSubsets, TileLayer tileLayer) {
return gridSubsets
.map(Collection::stream)
.map(stream->stream.map(GridSubset::getName))
.map(stream->stream.collect(Collectors.toSet()))
.orElseGet(()->tileLayer.getGridSubsets());
}
private Collection<String> getFormatNames(Optional<Collection<MimeType>> mimeTypes, TileLayer tileLayer) {
return mimeTypes
.map(Collection::stream)
.orElseGet(()->tileLayer.getMimeTypes().stream())
.map(MimeType::getFormat)
.collect(Collectors.toList());
}
private Collection<String> getParameterIds (String layerName, Optional<Collection<String>> parameterIds) {
return parameterIds.orElseGet(()->{
try {
return sb.getCachedParameterIds(layerName);
} catch (StorageException e) {
log.error("Error while retreiving cached parameter IDs for layer "+layerName, e);
return Collections.emptySet();
}
});
}
private PagePyramid newPagePyramid(final TileSet tileSet) {
final String layerName = tileSet.getLayerName();
final TileLayer tileLayer;
try {
tileLayer = tld.getTileLayer(layerName);
} catch (GeoWebCacheException e) {
throw new IllegalArgumentException(e);
}
final String gridsetId = tileSet.getGridsetId();
final GridSubset gridSubset = tileLayer.getGridSubset(gridsetId);
return newPagePyramid(gridSubset);
}
PagePyramid newPagePyramid(final GridSubset gridSubset) {
int zoomStart = gridSubset.getZoomStart();
int zoomStop = gridSubset.getZoomStop();
final long[][] coverages = gridSubset.getCoverages();
PagePyramid pagePyramid = new PagePyramid(coverages, zoomStart, zoomStop);
return pagePyramid;
}
}