/**
* 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 Arne OpenGeo 2010
*/
package org.geowebcache.storage;
import java.util.concurrent.atomic.AtomicLong;
public class TileRangeIterator {
final private TileRange tr;
final private DiscontinuousTileRange dtr;
final private int metaX;
final private int metaY;
private AtomicLong tilesSkippedCount = new AtomicLong();
private AtomicLong tilesRenderedCount = new AtomicLong();
private volatile long[] lastGridLoc;
/**
* Note that the bounds of the tile range must already be expanded to the meta tile factors for
* this to work.
*
* @param tr
* @param metaTilingFactors
*/
public TileRangeIterator(TileRange tr, int[] metaTilingFactors) {
this.tr = tr;
this.metaX = metaTilingFactors[0];
this.metaY = metaTilingFactors[1];
if (tr instanceof DiscontinuousTileRange) {
dtr = (DiscontinuousTileRange) tr;
} else {
dtr = null;
}
}
/**
* Returns the underlying tile range
*
* @return
*/
public TileRange getTileRange() {
return tr;
}
/**
* This loops over all the possible metatile locations and returns a tile location within each
* metatile.
*
* If the TileRange object provided is a DiscontinuousTileRange implementation, each location is
* checked against the filter of that class.
*
* @param gridLoc as an optimization, re-use the previous gridLoc. It will be changed and used
* as the return value. The values passed in will not impact the result. For the first call,
* use a new 3 element array.
*
* @return {@code null} if there're no more tiles to return, the next grid location in the
* iterator otherwise. The array has three elements: {x,y,z}
*/
public synchronized long[] nextMetaGridLocation(final long[] gridLoc) {
long[] levelBounds;
long x;
long y;
int z;
// Figure out the starting point
if (lastGridLoc == null) {
z = tr.getZoomStart();
levelBounds = tr.rangeBounds(z);
x = levelBounds[0];
y = levelBounds[1];
} else {
z = (int) lastGridLoc[2];
levelBounds = tr.rangeBounds(z);
x = lastGridLoc[0] + metaX;
y = lastGridLoc[1];
}
// Loop over any remaining zoom levels
for (; z <= tr.getZoomStop(); z++) {
for (; y <= levelBounds[3]; y += metaY) {
for (; x <= levelBounds[2]; x += metaX) {
gridLoc[0] = x;
gridLoc[1] = y;
gridLoc[2] = z;
int tileCount = tilesForLocation(gridLoc, levelBounds);
if (checkGridLocation(gridLoc)) {
tilesRenderedCount.addAndGet(tileCount);
lastGridLoc = gridLoc.clone();
return gridLoc;
}
tilesSkippedCount.addAndGet(tileCount);
}
x = levelBounds[0];
}
// Get ready for the next level
if (z < tr.getZoomStop()) {// but be careful not to go out of index
levelBounds = tr.rangeBounds(z + 1);
x = levelBounds[0];
y = levelBounds[1];
}
}
return null;
}
/**
* Calculates the number of tiles covered by the meta tile for this grid location.
*
* @param gridLoc
* @param levelBounds
* @return
*/
private int tilesForLocation(long x, long y, long[] levelBounds) {
long boundsMaxX = levelBounds[2];
long boundsMaxY = levelBounds[3];
return (int) Math.min(metaX, 1 + (boundsMaxX - x))
* (int) Math.min(metaY, 1 + (boundsMaxY - y));
}
private int tilesForLocation(long[] gridLoc, long[] levelBounds) {
return tilesForLocation(gridLoc[0], gridLoc[1], levelBounds);
}
/**
* Checks whether this grid location, or any on the same meta tile, should be included according
* to the DiscontinuousTileRange
*
* @param gridLoc
* @return
*/
private boolean checkGridLocation(long[] gridLoc) {
if (dtr == null) {
return true;
} else {
long[] subIdx = new long[3];
subIdx[2] = gridLoc[2];
for (int i = 0; i < this.metaX; i++) {
for (int j = 0; j < this.metaY; j++) {
subIdx[0] = gridLoc[0] + i;
subIdx[1] = gridLoc[1] + j;
if (dtr.contains(subIdx)) {
return true;
}
}
}
}
return false;
}
}