1   /* Copyright 2013-2019 CS Systèmes d'Information
2    * Licensed to CS Systèmes d'Information (CS) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * CS licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *   http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.orekit.rugged.raster;
18  
19  import org.hipparchus.util.FastMath;
20  import java.lang.reflect.Array;
21  
22  import org.orekit.rugged.errors.DumpManager;
23  import org.orekit.rugged.errors.RuggedException;
24  import org.orekit.rugged.errors.RuggedMessages;
25  
26  /** Cache for Digital Elevation Model {@link Tile tiles}.
27   * <p>
28   * Beware, this cache is <em>not</em> thread-safe!
29   * </p>
30   * @param <T> Type of tiles.
31   * @author Luc Maisonobe
32   */
33  public class TilesCache<T extends Tile> {
34  
35      /** Factory for empty tiles. */
36      private final TileFactory<T> factory;
37  
38      /** Updater for retrieving tiles data. */
39      private final TileUpdater updater;
40  
41      /** Cache. */
42      private final T[] tiles;
43  
44      /** Simple constructor.
45       * @param factory factory for creating empty tiles
46       * @param updater updater for retrieving tiles data
47       * @param maxTiles maximum number of tiles stored simultaneously in the cache
48       */
49      public TilesCache(final TileFactory<T> factory, final TileUpdater updater, final int maxTiles) {
50          this.factory       = factory;
51          this.updater    = updater;
52          @SuppressWarnings("unchecked")
53          final T[] array = (T[]) Array.newInstance(Tile.class, maxTiles);
54          this.tiles = array;
55      }
56  
57      /** Get the tile covering a ground point.
58       * @param latitude ground point latitude
59       * @param longitude ground point longitude
60       * @return tile covering the ground point
61       */
62      public T getTile(final double latitude, final double longitude) {
63  
64          for (int i = 0; i < tiles.length; ++i) {
65              final T tile = tiles[i];
66              if (tile != null && tile.getLocation(latitude, longitude) == Tile.Location.HAS_INTERPOLATION_NEIGHBORS) {
67                  // we have found the tile in the cache
68  
69                  // put it on the front as it becomes the most recently used
70                  while (i > 0) {
71                      tiles[i] = tiles[i - 1];
72                      --i;
73                  }
74                  tiles[0] = tile;
75  
76                  return tile;
77  
78              }
79          }
80  
81          // none of the tiles in the cache covers the specified points
82  
83          // make some room in the cache, possibly evicting the least recently used one
84          for (int i = tiles.length - 1; i > 0; --i) {
85              tiles[i] = tiles[i - 1];
86          }
87  
88          // create the tile and retrieve its data
89          final T tile = factory.createTile();
90  
91          // In case dump is asked for, suspend the dump manager as we don't need to dump anything here
92          // For instance for SRTM DEM, the user needs to read Geoid data that are not useful in the dump
93          final Boolean wasSuspended = DumpManager.suspend();
94  
95          updater.updateTile(latitude, longitude, tile);
96  
97          // Resume the dump manager if necessary
98          DumpManager.resume(wasSuspended);
99  
100         tile.tileUpdateCompleted();
101 
102         if (tile.getLocation(latitude, longitude) != Tile.Location.HAS_INTERPOLATION_NEIGHBORS) {
103             // this should happen only if user set up an inconsistent TileUpdater
104             throw new RuggedException(RuggedMessages.TILE_WITHOUT_REQUIRED_NEIGHBORS_SELECTED,
105                                       FastMath.toDegrees(latitude),
106                                       FastMath.toDegrees(longitude));
107         }
108 
109         tiles[0] = tile;
110         return tile;
111 
112     }
113 
114 }