Skip to content
Snippets Groups Projects
Commit a45d3361 authored by Luc Maisonobe's avatar Luc Maisonobe
Browse files

Simplified tiles cache.

parent a2dd4d53
No related branches found
No related tags found
No related merge requests found
......@@ -16,18 +16,11 @@
*/
package org.orekit.rugged.raster;
import java.io.Serializable;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import org.apache.commons.math3.util.FastMath;
import org.orekit.rugged.errors.RuggedException;
import org.orekit.rugged.errors.RuggedMessages;
import org.orekit.rugged.raster.Tile.Location;
/** Cache for Digital Elevation Model {@link Tile tiles}.
* <p>
......@@ -44,14 +37,8 @@ public class TilesCache<T extends Tile> {
/** Updater for retrieving tiles data. */
private final TileUpdater updater;
/** Search optimized cache. */
private List<TilesStrip> searchCache;
/** Eviction queue. */
private T[] evictionQueue;
/** Index for next tile. */
private int next;
/** Cache. */
private final T[] tiles;
/** Simple constructor.
* @param factory factory for creating empty tiles
......@@ -60,12 +47,10 @@ public class TilesCache<T extends Tile> {
*/
public TilesCache(final TileFactory<T> factory, final TileUpdater updater, final int maxTiles) {
this.factory = factory;
this.updater = updater;
this.searchCache = new ArrayList<TilesStrip>();
this.updater = updater;
@SuppressWarnings("unchecked")
final T[] array = (T[]) Array.newInstance(Tile.class, maxTiles + 1);
this.evictionQueue = array;
this.next = 0;
final T[] array = (T[]) Array.newInstance(Tile.class, maxTiles);
this.tiles = array;
}
/** Get the tile covering a ground point.
......@@ -76,296 +61,45 @@ public class TilesCache<T extends Tile> {
*/
public T getTile(final double latitude, final double longitude)
throws RuggedException {
final T tile = getStrip(latitude, longitude).getTile(latitude, longitude);
if (tile.getLocation(latitude, longitude) != Tile.Location.HAS_INTERPOLATION_NEIGHBORS) {
// this should happen only if user set up an inconsistent TileUpdater
throw new RuggedException(RuggedMessages.TILE_WITHOUT_REQUIRED_NEIGHBORS_SELECTED,
FastMath.toDegrees(latitude),
FastMath.toDegrees(longitude));
}
return tile;
}
/** Create a tile covering a ground point.
* @param latitude latitude of the point
* @param longitude longitude of the point
* @return new tile covering the point
* @exception RuggedException if tile cannot be updated
*/
private T createTile(final double latitude, final double longitude)
throws RuggedException {
// create the tile and retrieve its data
final T tile = factory.createTile();
updater.updateTile(latitude, longitude, tile);
tile.tileUpdateCompleted();
return tile;
}
/** Append newly created tile at the end of the eviction queue.
* @param tile tile to append to queue
*/
private void appendToEvictionQueue(final T tile) {
evictionQueue[next] = tile;
next = (next + 1) % evictionQueue.length;
if (evictionQueue[next] != null) {
// the cache is full, we need to evict one tile
// from both the eviction cache and the search cache
for (final Iterator<TilesStrip> iterator = searchCache.iterator(); iterator.hasNext();) {
if (iterator.next().removeTile(evictionQueue[next])) {
evictionQueue[next] = null;
return;
}
}
}
}
/** Get a strip covering a ground point.
* @param latitude ground point latitude
* @param longitude ground point longitude
* @return strip covering the ground point
* @exception RuggedException if tile cannot be updated
*/
private TilesStrip getStrip(final double latitude, final double longitude)
throws RuggedException {
// look for a strip at the specified latitude
final int index = Collections.binarySearch(searchCache, new BasicLatitudeProvider(latitude),
new LatitudeComparator());
if (index >= 0) {
// rare case, the latitude is an exact maximum latitude for a strip
return searchCache.get(index);
} else {
final int insertionPoint = -(index + 1);
for (int i = 0; i < tiles.length; ++i) {
final T tile = tiles[i];
if (tile != null && tile.getLocation(latitude, longitude) == Tile.Location.HAS_INTERPOLATION_NEIGHBORS) {
// we have found the tile in the cache
if (insertionPoint < searchCache.size()) {
final TilesStrip strip = searchCache.get(insertionPoint);
if (strip.covers(latitude)) {
// we have found an existing strip
return strip;
// put it on the front as it becomes the most recently used
while (i > 0) {
tiles[i] = tiles[i - 1];
--i;
}
}
// no existing strip covers the specified latitude, we need to create a new one
final T tile = createTile(latitude, longitude);
final TilesStrip strip = new TilesStrip(tile);
searchCache.add(insertionPoint, strip);
appendToEvictionQueue(tile);
return strip;
}
}
/** Interface for retrieving latitude. */
private interface LatitudeProvider {
/** Get latitude.
* @return latitude
*/
double getLatitude();
}
/** Basic implementation of {@link LatitudeProvider}. */
private static class BasicLatitudeProvider implements LatitudeProvider {
/** Latitude. */
private final double latitude;
/** Simple constructor.
* @param latitude latitude
*/
public BasicLatitudeProvider(final double latitude) {
this.latitude = latitude;
}
/** {@inheritDoc} */
@Override
public double getLatitude() {
return latitude;
}
}
/** Interface for retrieving longitude. */
private interface LongitudeProvider {
tiles[0] = tile;
/** Get longitude.
* @return longitude
*/
double getLongitude();
}
/** Basic implementation of {@link LongitudeProvider}. */
private static class BasicLongitudeProvider implements LongitudeProvider {
/** Longitude. */
private final double longitude;
/** Simple constructor.
* @param longitude longitude
*/
public BasicLongitudeProvider(final double longitude) {
this.longitude = longitude;
}
/** {@inheritDoc} */
@Override
public double getLongitude() {
return longitude;
}
}
/** Strip of tiles for a given latitude. */
private class TilesStrip implements LatitudeProvider {
/** Minimum latitude. */
private final double minLatitude;
/** Maximum latitude. */
private final double maxLatitude;
/** Tiles list. */
private final List<TileDecorator> tiles;
/** Simple constructor.
* @param tile first tile to insert in the strip
*/
public TilesStrip(final T tile) {
minLatitude = tile.getMinimumLatitude();
maxLatitude = tile.getMaximumLatitude();
tiles = new ArrayList<TileDecorator>();
tiles.add(new TileDecorator(tile));
}
/** Check if the strip covers a specified latitude.
* @param latitude latitude to check
* @return true if the strip covers the latitude
*/
public boolean covers(final double latitude) {
return (minLatitude <= latitude) && (maxLatitude >= latitude);
}
/** {@inheritDoc} */
@Override
public double getLatitude() {
return maxLatitude;
}
/** Get a tile covering a ground point.
* @param latitude ground point latitude
* @param longitude ground point longitude
* @return strip covering the ground point
* @exception RuggedException if tile cannot be updated
*/
public T getTile(final double latitude, final double longitude)
throws RuggedException {
// look for a tile at the specified longitude
final int index = Collections.binarySearch(tiles, new BasicLongitudeProvider(longitude),
new LongitudeComparator());
if (index >= 0) {
// rare case, the longitude is an exact maximum longitude for a tile
return tiles.get(index).getTile();
} else {
final int insertionPoint = -(index + 1);
if (insertionPoint < tiles.size()) {
final T tile = tiles.get(insertionPoint).getTile();
if (tile.getLocation(latitude, longitude) == Location.HAS_INTERPOLATION_NEIGHBORS) {
// we have found an existing tile
return tile;
}
}
// no existing tile covers the specified ground point, we need to create a new one
final T tile = createTile(latitude, longitude);
tiles.add(insertionPoint, new TileDecorator(tile));
appendToEvictionQueue(tile);
return tile;
}
}
/** Remove a tile from the strip.
* @param tile tile to remove
* @return true if the tile has been removed
*/
public boolean removeTile(final Tile tile) {
for (final Iterator<TileDecorator> iterator = tiles.iterator(); iterator.hasNext();) {
if (iterator.next().getTile() == tile) {
iterator.remove();
return true;
}
}
return false;
}
}
/** Decorator for tiles, implementing {@link LongitudeProvider}. */
private class TileDecorator implements LongitudeProvider {
/** Underlying tile. */
private final T tile;
/** Simple constructor.
* @param tile tile to decorate
*/
public TileDecorator(final T tile) {
this.tile = tile;
}
/** Get the underlying tile.
* @return underlying tile
*/
public T getTile() {
return tile;
}
// none of the tiles in the cache covers the specified points
/** {@inheritDoc} */
@Override
public double getLongitude() {
return tile.getMaximumLongitude();
// make some room in the cache, possibly evicting the least recently used one
for (int i = tiles.length - 1; i > 0; --i) {
tiles[i] = tiles[i - 1];
}
}
/** Comparator for sorting with respect to latitude. */
private static class LatitudeComparator implements Comparator<LatitudeProvider>, Serializable {
/** Serializable UID. */
private static final long serialVersionUID = 20141212L;
// create the tile and retrieve its data
final T tile = factory.createTile();
updater.updateTile(latitude, longitude, tile);
tile.tileUpdateCompleted();
/** {@inheritDoc} */
@Override
public int compare(final LatitudeProvider o1, final LatitudeProvider o2) {
return Double.compare(o1.getLatitude(), o2.getLatitude());
if (tile.getLocation(latitude, longitude) != Tile.Location.HAS_INTERPOLATION_NEIGHBORS) {
// this should happen only if user set up an inconsistent TileUpdater
throw new RuggedException(RuggedMessages.TILE_WITHOUT_REQUIRED_NEIGHBORS_SELECTED,
FastMath.toDegrees(latitude),
FastMath.toDegrees(longitude));
}
}
/** Comparator for sorting with respect to longitude. */
private static class LongitudeComparator implements Comparator<LongitudeProvider>, Serializable {
/** Serializable UID. */
private static final long serialVersionUID = 20141212L;
/** {@inheritDoc} */
@Override
public int compare(final LongitudeProvider o1, final LongitudeProvider o2) {
return Double.compare(o1.getLongitude(), o2.getLongitude());
}
tiles[0] = tile;
return tile;
}
......
......@@ -65,6 +65,13 @@ public class TilesCacheTest {
}
Assert.assertEquals(12, factory.getCount());
// ensure the (0.0, 0.0) tile is the least recently used one
for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 3; ++j) {
cache.getTile(FastMath.toRadians(0.5 + j), FastMath.toRadians(0.5 + i));
}
}
// ask for one point outside of the covered area, to evict the (0.0, 0.0) tile
cache.getTile(FastMath.toRadians(20.5), FastMath.toRadians(30.5));
Assert.assertEquals(13, factory.getCount());
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment