From a45d33619893e9f7a3e947278a908f770b307884 Mon Sep 17 00:00:00 2001 From: Luc Maisonobe <luc@orekit.org> Date: Mon, 16 Feb 2015 17:41:33 +0100 Subject: [PATCH] Simplified tiles cache. --- .../org/orekit/rugged/raster/TilesCache.java | 324 ++---------------- .../orekit/rugged/raster/TilesCacheTest.java | 7 + 2 files changed, 36 insertions(+), 295 deletions(-) diff --git a/src/main/java/org/orekit/rugged/raster/TilesCache.java b/src/main/java/org/orekit/rugged/raster/TilesCache.java index d8174a1c..dbcc447c 100644 --- a/src/main/java/org/orekit/rugged/raster/TilesCache.java +++ b/src/main/java/org/orekit/rugged/raster/TilesCache.java @@ -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; } diff --git a/src/test/java/org/orekit/rugged/raster/TilesCacheTest.java b/src/test/java/org/orekit/rugged/raster/TilesCacheTest.java index 26d1f5f4..2863e5e7 100644 --- a/src/test/java/org/orekit/rugged/raster/TilesCacheTest.java +++ b/src/test/java/org/orekit/rugged/raster/TilesCacheTest.java @@ -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()); -- GitLab