diff --git a/src/main/java/de/blau/android/layer/streetlevel/AbstractImageOverlay.java b/src/main/java/de/blau/android/layer/streetlevel/AbstractImageOverlay.java new file mode 100644 index 000000000..d6a8a28f8 --- /dev/null +++ b/src/main/java/de/blau/android/layer/streetlevel/AbstractImageOverlay.java @@ -0,0 +1,234 @@ +package de.blau.android.layer.streetlevel; + +import static de.blau.android.contract.Constants.LOG_TAG_LEN; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.List; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; + +import android.content.Context; +import android.graphics.Canvas; +import android.text.SpannableString; +import android.util.Log; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.content.ContextCompat; +import androidx.fragment.app.FragmentActivity; +import de.blau.android.Map; +import de.blau.android.osm.ViewBox; +import de.blau.android.resources.TileLayerSource; +import de.blau.android.resources.symbols.Mapillary; +import de.blau.android.util.DateFormatter; +import de.blau.android.util.FileUtil; +import de.blau.android.util.GeoJSONConstants; +import de.blau.android.util.mvt.VectorTileDecoder; +import de.blau.android.util.mvt.VectorTileRenderer; +import de.blau.android.util.mvt.style.Layer; +import de.blau.android.util.mvt.style.Style; +import de.blau.android.util.mvt.style.Symbol; +import de.blau.android.views.IMapView; + +public abstract class AbstractImageOverlay extends de.blau.android.layer.mvt.MapOverlay implements DateRangeInterface, SelectImageInterface { + + private static final int TAG_LEN = Math.min(LOG_TAG_LEN, AbstractImageOverlay.class.getSimpleName().length()); + private static final String DEBUG_TAG = AbstractImageOverlay.class.getSimpleName().substring(0, TAG_LEN); + + public static final int DEFAULT_MIN_ZOOM = 16; + public static final int DEFAULT_CACHE_SIZE = 100; + protected static final long ONE_MILLION = 1000000L; + + // mapbox gl style layer ids + protected static final String SELECTED_IMAGE_LAYER = "selected_image"; + + protected final SimpleDateFormat timestampFormat = DateFormatter.getUtcFormat("yyyy-MM-dd HH:mm:ssZ"); + + protected int minZoom = DEFAULT_MIN_ZOOM; + + /** Map this is an overlay of. */ + protected Map map = null; + + /** + * Download related stuff + */ + protected long cacheSize = DEFAULT_CACHE_SIZE * ONE_MILLION; + + protected JsonArray selectedFilter = null; + + /** + * Directory for caching images + */ + protected File cacheDir; + + private final String imageLayer; + private final String styleJson; + + /** + * Construct this layer + * + * @param map the Map object we are displayed on + */ + protected AbstractImageOverlay(@NonNull final Map map, @NonNull String tileId, @NonNull String imageLayer, @NonNull String styleJson) { + super(map, new VectorTileRenderer(), false); + this.setRendererInfo(TileLayerSource.get(map.getContext(), tileId, false)); + this.map = map; + final Context context = map.getContext(); + File[] storageDirs = ContextCompat.getExternalCacheDirs(context); + try { + cacheDir = FileUtil.getPublicDirectory(storageDirs.length > 1 && storageDirs[1] != null ? storageDirs[1] : storageDirs[0], getName()); + } catch (IOException e) { + Log.e(DEBUG_TAG, "Unable to create cache directory " + e.getMessage()); + } + this.imageLayer = imageLayer; + this.styleJson = styleJson; + setPrefs(map.getPrefs()); + + resetStyling(); + } + + @Override + public void onDraw(@NonNull Canvas c, @NonNull IMapView osmv) { + if (map.getZoomLevel() >= minZoom) { + super.onDraw(c, osmv); + } + } + + @Override + public List getClicked(final float x, final float y, final ViewBox viewBox) { + Style style = ((VectorTileRenderer) tileRenderer).getStyle(); + Layer layer = style.getLayer(imageLayer); + if (layer instanceof Symbol && map.getZoomLevel() < layer.getMinZoom()) { + return new ArrayList<>(); + } + List result = super.getClicked(x, y, viewBox); + // remove non image elements for now + if (result != null) { + for (VectorTileDecoder.Feature f : new ArrayList<>(result)) { + if (!isPoint(f)) { + result.remove(f); + } + } + } + Log.d(DEBUG_TAG, "getClicked found " + (result != null ? result.size() : "nothing")); + return result; + } + + @Override + public void invalidate() { + map.invalidate(); + } + + /** + * Check if this Feature geometry is a Point + * + * @param f the Feature + * @return true if the geometry is a Point + */ + protected boolean isPoint(@NonNull de.blau.android.util.mvt.VectorTileDecoder.Feature f) { + return GeoJSONConstants.POINT.equals(f.getGeometry().type()); + } + + /** + * Flush both the sequence and image cache + * + * @param ctx an Android Context + */ + public synchronized void flushCaches(@NonNull Context ctx) { + flushSequenceCache(ctx); + try { + Thread t = new Thread(null, () -> FileUtil.pruneCache(cacheDir, 0), "Image Cache Zapper"); + t.start(); + } catch (SecurityException | IllegalThreadStateException e) { + Log.e(DEBUG_TAG, "Unable to flush image cache " + e.getMessage()); + } + } + + @Override + public SpannableString getDescription(de.blau.android.util.mvt.VectorTileDecoder.Feature f) { + return getDescription(map.getContext(), f); + } + + @Override + public void resetStyling() { + try (InputStream is = map.getContext().getAssets().open(styleJson)) { + Style style = new Style(); + style.loadStyle(map.getContext(), is); + ((VectorTileRenderer) tileRenderer).setStyle(style); + Layer layer = style.getLayer(imageLayer); + if (layer instanceof Symbol) { + ((Symbol) layer).setSymbol(Mapillary.NAME, map.getDataStyle()); + layer.setColor(layer.getColor()); + } + layer = style.getLayer(SELECTED_IMAGE_LAYER); + if (layer instanceof Symbol) { + ((Symbol) layer).setSymbol(Mapillary.NAME, map.getDataStyle()); + layer.setColor(layer.getColor()); + selectedFilter = layer.getFilter(); + } + dirty(); + Log.d(DEBUG_TAG, "Loaded style successfully"); + } catch (IOException ioex) { + Log.d(DEBUG_TAG, "Reading default mapillary style failed"); + } + } + + /** + * Set a range for the capture date + * + * This manipulates the filter in the layer + * + * @param style style to change + * @param layerName the name of the layer to change + * @param start the lower bound for the capture date in ms since the epoch + * @param end the upper bound for the capture date in ms since the epoch + * @param optional format for string dates + */ + protected void setDateRange(@NonNull Style style, @NonNull String layerName, long start, long end, @Nullable SimpleDateFormat format) { + Layer layer = style.getLayer(layerName); + if (layer == null) { + Log.e(DEBUG_TAG, layerName + " not found"); + return; + } + JsonArray filter = layer.getFilter(); + if (filter != null && filter.size() == 3) { + setDateFilterValue(filter.get(1), start, format); + setDateFilterValue(filter.get(2), end, format); + return; + } + Log.e(DEBUG_TAG, "filter not found"); + } + + /** + * Set the value of a filter + * + * @param filter a JsonArray representing a filter + * @param value the value to set + * @param optional format for string dates + */ + protected abstract void setDateFilterValue(JsonElement filter, long value, @Nullable SimpleDateFormat format); + + @Override + public void flushTileCache(@Nullable final FragmentActivity activity, boolean all) { + super.flushTileCache(activity, all); + if (activity != null) { + flushSequenceCache(activity); + } + } + + /** + * Flush the sequence cache + * + * @param ctx an Android Context + */ + protected abstract void flushSequenceCache(@NonNull Context ctx); + + @Override + public int onDrawAttribution(@NonNull Canvas c, @NonNull IMapView osmv, int offset) { + return offset; + } +} \ No newline at end of file diff --git a/src/main/java/de/blau/android/layer/streetlevel/mapillary/AbstractSequenceFetcher.java b/src/main/java/de/blau/android/layer/streetlevel/AbstractSequenceFetcher.java similarity index 98% rename from src/main/java/de/blau/android/layer/streetlevel/mapillary/AbstractSequenceFetcher.java rename to src/main/java/de/blau/android/layer/streetlevel/AbstractSequenceFetcher.java index 59ebe3527..a0cf6793c 100644 --- a/src/main/java/de/blau/android/layer/streetlevel/mapillary/AbstractSequenceFetcher.java +++ b/src/main/java/de/blau/android/layer/streetlevel/AbstractSequenceFetcher.java @@ -1,4 +1,4 @@ -package de.blau.android.layer.streetlevel.mapillary; +package de.blau.android.layer.streetlevel; import static de.blau.android.contract.Constants.LOG_TAG_LEN; diff --git a/src/main/java/de/blau/android/layer/streetlevel/mapillary/MapillaryOverlay.java b/src/main/java/de/blau/android/layer/streetlevel/mapillary/MapillaryOverlay.java index 15e79c341..2f6e3bd93 100644 --- a/src/main/java/de/blau/android/layer/streetlevel/mapillary/MapillaryOverlay.java +++ b/src/main/java/de/blau/android/layer/streetlevel/mapillary/MapillaryOverlay.java @@ -2,10 +2,9 @@ import static de.blau.android.contract.Constants.LOG_TAG_LEN; -import java.io.File; import java.io.IOException; -import java.io.InputStream; import java.io.Serializable; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; @@ -18,13 +17,11 @@ import android.content.Context; import android.database.sqlite.SQLiteDatabase; -import android.graphics.Canvas; import android.os.Build; import android.text.SpannableString; import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.core.content.ContextCompat; import androidx.fragment.app.FragmentActivity; import de.blau.android.Map; import de.blau.android.R; @@ -32,44 +29,31 @@ import de.blau.android.contract.Urls; import de.blau.android.dialogs.DateRangeDialog; import de.blau.android.layer.LayerType; -import de.blau.android.layer.streetlevel.DateRangeInterface; -import de.blau.android.layer.streetlevel.SelectImageInterface; -import de.blau.android.osm.OsmParser; -import de.blau.android.osm.ViewBox; +import de.blau.android.layer.streetlevel.AbstractImageOverlay; +import de.blau.android.layer.streetlevel.AbstractSequenceFetcher; import de.blau.android.photos.MapillaryViewerActivity; import de.blau.android.photos.PhotoViewerFragment; import de.blau.android.prefs.Preferences; import de.blau.android.resources.KeyDatabaseHelper; import de.blau.android.resources.KeyDatabaseHelper.EntryType; -import de.blau.android.resources.TileLayerSource; -import de.blau.android.resources.symbols.Mapillary; -import de.blau.android.util.DateFormatter; -import de.blau.android.util.FileUtil; -import de.blau.android.util.GeoJSONConstants; import de.blau.android.util.SavingHelper; import de.blau.android.util.ScreenMessage; -import de.blau.android.util.mvt.VectorTileDecoder; import de.blau.android.util.mvt.VectorTileRenderer; import de.blau.android.util.mvt.style.Layer; import de.blau.android.util.mvt.style.Style; import de.blau.android.util.mvt.style.Symbol; -import de.blau.android.views.IMapView; -public class MapillaryOverlay extends de.blau.android.layer.mvt.MapOverlay implements DateRangeInterface, SelectImageInterface { +public class MapillaryOverlay extends AbstractImageOverlay { private static final int TAG_LEN = Math.min(LOG_TAG_LEN, MapillaryOverlay.class.getSimpleName().length()); private static final String DEBUG_TAG = MapillaryOverlay.class.getSimpleName().substring(0, TAG_LEN); - public static final String MAPILLARY_TILES_ID = "MAPILLARYV4"; - public static final int MAPILLARY_DEFAULT_MIN_ZOOM = 16; - public static final int MAPILLARY_DEFAULT_CACHE_SIZE = 100; - private static final long ONE_MILLION = 1000000L; + public static final String MAPILLARY_TILES_ID = "MAPILLARYV4"; // mapbox gl style layer ids - private static final String SELECTED_IMAGE_LAYER = "selected_image"; - private static final String IMAGE_LAYER = "image"; - private static final String OVERVIEW_LAYER = "overview"; - private static final String SEQUENCE_LAYER = "sequence"; + private static final String IMAGE_LAYER = "image"; + private static final String OVERVIEW_LAYER = "overview"; + private static final String SEQUENCE_LAYER = "sequence"; // mapillary API constants private static final String CAPTURED_AT_KEY = "captured_at"; @@ -101,56 +85,23 @@ static class State implements Serializable { private final String apiKey; - /** Map this is an overlay of. */ - private Map map = null; - - /** - * Download related stuff - */ - private long cacheSize = MAPILLARY_DEFAULT_CACHE_SIZE * ONE_MILLION; - - private JsonArray selectedFilter = null; - - /** - * Directory for caching mapillary images - */ - private File cacheDir; - /** * Construct this layer * * @param map the Map object we are displayed on */ public MapillaryOverlay(@NonNull final Map map) { - super(map, new VectorTileRenderer(), false); - this.setRendererInfo(TileLayerSource.get(map.getContext(), MAPILLARY_TILES_ID, false)); - this.map = map; + super(map, MAPILLARY_TILES_ID, IMAGE_LAYER, DEFAULT_MAPILLARY_STYLE_JSON); final Context context = map.getContext(); - File[] storageDirs = ContextCompat.getExternalCacheDirs(context); - try { - cacheDir = FileUtil.getPublicDirectory(storageDirs.length > 1 && storageDirs[1] != null ? storageDirs[1] : storageDirs[0], getName()); - } catch (IOException e) { - Log.e(DEBUG_TAG, "Unable to create cache directory " + e.getMessage()); - } try (KeyDatabaseHelper keys = new KeyDatabaseHelper(context); SQLiteDatabase db = keys.getReadableDatabase()) { apiKey = KeyDatabaseHelper.getKey(db, APIKEY_KEY, EntryType.API_KEY); if (apiKey == null) { ScreenMessage.toastTopError(context, context.getString(R.string.toast_api_key_missing, APIKEY_KEY)); } } - setPrefs(map.getPrefs()); - - resetStyling(); setDateRange(state.startDate, state.endDate); } - @Override - public void onDraw(@NonNull Canvas c, @NonNull IMapView osmv) { - if (map.getZoomLevel() >= MAPILLARY_DEFAULT_MIN_ZOOM) { - super.onDraw(c, osmv); - } - } - @Override public void onSaveState(@NonNull Context ctx) throws IOException { super.onSaveState(ctx); @@ -170,36 +121,11 @@ public boolean onRestoreState(@NonNull Context ctx) { return result; } - @Override - public List getClicked(final float x, final float y, final ViewBox viewBox) { - Style style = ((VectorTileRenderer) tileRenderer).getStyle(); - Layer layer = style.getLayer(IMAGE_LAYER); - if (layer instanceof Symbol && map.getZoomLevel() < layer.getMinZoom()) { - return new ArrayList<>(); - } - List result = super.getClicked(x, y, viewBox); - // remove non image elements for now - if (result != null) { - for (VectorTileDecoder.Feature f : new ArrayList<>(result)) { - if (!isPoint(f)) { - result.remove(f); - } - } - } - Log.d(DEBUG_TAG, "getClicked found " + (result != null ? result.size() : "nothing")); - return result; - } - @Override public String getName() { return map.getContext().getString(R.string.layer_mapillary); } - @Override - public void invalidate() { - map.invalidate(); - } - @Override public void onSelected(FragmentActivity activity, de.blau.android.util.mvt.VectorTileDecoder.Feature f) { if (isPoint(f)) { @@ -227,16 +153,6 @@ public void onSelected(FragmentActivity activity, de.blau.android.util.mvt.Vecto } } - /** - * Check if this Feature geometry is a Point - * - * @param f the Feature - * @return true if the geometry is a Point - */ - private boolean isPoint(@NonNull de.blau.android.util.mvt.VectorTileDecoder.Feature f) { - return GeoJSONConstants.POINT.equals(f.getGeometry().type()); - } - /** * Start the image viewer * @@ -367,67 +283,27 @@ public void selectImage(int pos) { } } - /** - * Flush both the sequence and image cache - * - * @param ctx an Android Context - */ - public synchronized void flushCaches(@NonNull Context ctx) { - flushSequenceCache(ctx); - try { - Thread t = new Thread(null, () -> FileUtil.pruneCache(cacheDir, 0), "Mapillary Image Cache Zapper"); - t.start(); - } catch (SecurityException | IllegalThreadStateException e) { - Log.e(DEBUG_TAG, "Unable to flush image cache " + e.getMessage()); - } - } - @Override public void setDateRange(long start, long end) { + state.startDate = start; + state.endDate = end; Style style = ((VectorTileRenderer) tileRenderer).getStyle(); - setDateRange(style.getLayer(IMAGE_LAYER), start, end); - setDateRange(style.getLayer(SEQUENCE_LAYER), start, end); - setDateRange(style.getLayer(OVERVIEW_LAYER), start, end); + setDateRange(style, IMAGE_LAYER, start, end, null); + setDateRange(style, SEQUENCE_LAYER, start, end, null); + setDateRange(style, OVERVIEW_LAYER, start, end, null); map.invalidate(); dirty(); } - /** - * Set a range for the capture date - * - * This manipulates the filter in the layer - * - * @param start the lower bound for the capture date in ms since the epoch - * @param end the upper bound for the capture date in ms since the epoch - */ - private void setDateRange(@NonNull Layer layer, long start, long end) { - state.startDate = start; - state.endDate = end; - JsonArray filter = layer.getFilter(); - if (filter != null && filter.size() == 3) { - setFilterValue(filter.get(1), start); - setFilterValue(filter.get(2), end); - } - } - - /** - * Set the value of a filter - * - * @param filter a JsonArray representing a filter - * @param value the value to set - */ - private void setFilterValue(JsonElement filter, long value) { + @Override + protected void setDateFilterValue(JsonElement filter, long value, @Nullable SimpleDateFormat format) { if (filter instanceof JsonArray && ((JsonArray) filter).size() == 3) { ((JsonArray) filter).set(2, new JsonPrimitive(value)); } } - /** - * Flush the sequence cache - * - * @param ctx an Android Context - */ - private void flushSequenceCache(@NonNull Context ctx) { + @Override + protected void flushSequenceCache(@NonNull Context ctx) { if (state != null) { state.imageId = 0; state.sequenceCache.clear(); @@ -436,47 +312,10 @@ private void flushSequenceCache(@NonNull Context ctx) { } } - @Override - public SpannableString getDescription(de.blau.android.util.mvt.VectorTileDecoder.Feature f) { - return getDescription(map.getContext(), f); - } - @Override public SpannableString getDescription(@NonNull Context context, de.blau.android.util.mvt.VectorTileDecoder.Feature f) { Long capturedAt = (Long) f.getAttributes().get(CAPTURED_AT_KEY); - return new SpannableString(context.getString(R.string.mapillary_image, DateFormatter.getUtcFormat(OsmParser.TIMESTAMP_FORMAT).format(capturedAt))); - } - - @Override - public void resetStyling() { - try (InputStream is = map.getContext().getAssets().open(DEFAULT_MAPILLARY_STYLE_JSON)) { - Style style = new Style(); - style.loadStyle(map.getContext(), is); - ((VectorTileRenderer) tileRenderer).setStyle(style); - Layer layer = style.getLayer(IMAGE_LAYER); - if (layer instanceof Symbol) { - ((Symbol) layer).setSymbol(Mapillary.NAME, map.getDataStyle()); - layer.setColor(layer.getColor()); - } - layer = style.getLayer(SELECTED_IMAGE_LAYER); - if (layer instanceof Symbol) { - ((Symbol) layer).setSymbol(Mapillary.NAME, map.getDataStyle()); - layer.setColor(layer.getColor()); - selectedFilter = layer.getFilter(); - } - dirty(); - Log.d(DEBUG_TAG, "Loaded successfully"); - } catch (IOException ioex) { - Log.d(DEBUG_TAG, "Reading default mapillary style failed"); - } - } - - @Override - public void flushTileCache(@Nullable final FragmentActivity activity, boolean all) { - super.flushTileCache(activity, all); - if (activity != null) { - flushSequenceCache(activity); - } + return new SpannableString(context.getString(R.string.mapillary_image, timestampFormat.format(capturedAt))); } @Override @@ -492,11 +331,6 @@ public LayerType getType() { return LayerType.MAPILLARY; } - @Override - public int onDrawAttribution(@NonNull Canvas c, @NonNull IMapView osmv, int offset) { - return offset; - } - /** * Show a dialog to set the displayed date range * diff --git a/src/main/java/de/blau/android/layer/streetlevel/panoramax/PanoramaxOverlay.java b/src/main/java/de/blau/android/layer/streetlevel/panoramax/PanoramaxOverlay.java index 88603a0b9..fd5c3c0a0 100644 --- a/src/main/java/de/blau/android/layer/streetlevel/panoramax/PanoramaxOverlay.java +++ b/src/main/java/de/blau/android/layer/streetlevel/panoramax/PanoramaxOverlay.java @@ -38,10 +38,10 @@ import de.blau.android.contract.Urls; import de.blau.android.dialogs.DateRangeDialog; import de.blau.android.layer.LayerType; +import de.blau.android.layer.streetlevel.AbstractImageOverlay; +import de.blau.android.layer.streetlevel.AbstractSequenceFetcher; import de.blau.android.layer.streetlevel.DateRangeInterface; import de.blau.android.layer.streetlevel.SelectImageInterface; -import de.blau.android.layer.streetlevel.mapillary.AbstractSequenceFetcher; -import de.blau.android.layer.streetlevel.mapillary.MapillaryOverlay; import de.blau.android.osm.OsmParser; import de.blau.android.osm.ViewBox; import de.blau.android.photos.MapillaryViewerActivity; @@ -68,20 +68,16 @@ import okhttp3.Response; import okhttp3.ResponseBody; -public class PanoramaxOverlay extends de.blau.android.layer.mvt.MapOverlay implements DateRangeInterface, SelectImageInterface { +public class PanoramaxOverlay extends AbstractImageOverlay { private static final int TAG_LEN = Math.min(LOG_TAG_LEN, PanoramaxOverlay.class.getSimpleName().length()); private static final String DEBUG_TAG = PanoramaxOverlay.class.getSimpleName().substring(0, TAG_LEN); - public static final String PANORAMAX_TILES_ID = "PANORAMAX"; - public static final int PANORAMAX_DEFAULT_MIN_ZOOM = 16; - public static final int PANORAMAX_DEFAULT_CACHE_SIZE = 100; - private static final long ONE_MILLION = 1000000L; + public static final String PANORAMAX_TILES_ID = "PANORAMAX"; // mapbox gl style layer ids - private static final String SELECTED_IMAGE_LAYER = "selected_image"; - private static final String IMAGE_LAYER = "pictures"; - private static final String SEQUENCE_LAYER = "sequences"; + private static final String IMAGE_LAYER = "pictures"; + private static final String SEQUENCE_LAYER = "sequences"; // panoramax tile API constants private static final String TIMESTAMP_KEY = "ts"; @@ -91,7 +87,6 @@ public class PanoramaxOverlay extends de.blau.android.layer.mvt.MapOverlay imple private static final String DEFAULT_PANORAMAX_STYLE_JSON = "panoramax-style.json"; /** this is the format used by panoramax */ - private final SimpleDateFormat timestampFormat = DateFormatter.getUtcFormat("yyyy-MM-dd HH:mm:ssZ"); private final SimpleDateFormat dateFormat = DateFormatter.getUtcFormat("yyyy-MM-dd"); private static final String API_PICTURES = "api/pictures/%s/hd.jpg"; @@ -114,50 +109,16 @@ static class State implements Serializable { private State state = new State(); private final SavingHelper savingHelper = new SavingHelper<>(); - /** Map this is an overlay of. */ - private Map map = null; - - /** - * Download related stuff - */ - private long cacheSize = PANORAMAX_DEFAULT_CACHE_SIZE * ONE_MILLION; - - private JsonArray selectedFilter = null; - - /** - * Directory for caching mapillary images - */ - private File cacheDir; - /** * Construct this layer * * @param map the Map object we are displayed on */ public PanoramaxOverlay(@NonNull final Map map) { - super(map, new VectorTileRenderer(), false); - this.setRendererInfo(TileLayerSource.get(map.getContext(), PANORAMAX_TILES_ID, false)); - this.map = map; - final Context context = map.getContext(); - File[] storageDirs = ContextCompat.getExternalCacheDirs(context); - try { - cacheDir = FileUtil.getPublicDirectory(storageDirs.length > 1 && storageDirs[1] != null ? storageDirs[1] : storageDirs[0], getName()); - } catch (IOException e) { - Log.e(DEBUG_TAG, "Unable to create cache directory " + e.getMessage()); - } - setPrefs(map.getPrefs()); - - resetStyling(); + super(map, PANORAMAX_TILES_ID, IMAGE_LAYER, DEFAULT_PANORAMAX_STYLE_JSON); setDateRange(state.startDate, state.endDate); } - @Override - public void onDraw(@NonNull Canvas c, @NonNull IMapView osmv) { - if (map.getZoomLevel() >= PANORAMAX_DEFAULT_MIN_ZOOM) { - super.onDraw(c, osmv); - } - } - @Override public void onSaveState(@NonNull Context ctx) throws IOException { super.onSaveState(ctx); @@ -177,36 +138,11 @@ public boolean onRestoreState(@NonNull Context ctx) { return result; } - @Override - public List getClicked(final float x, final float y, final ViewBox viewBox) { - Style style = ((VectorTileRenderer) tileRenderer).getStyle(); - Layer layer = style.getLayer(IMAGE_LAYER); - if (layer instanceof Symbol && map.getZoomLevel() < layer.getMinZoom()) { - return new ArrayList<>(); - } - List result = super.getClicked(x, y, viewBox); - // remove non image elements for now - if (result != null) { - for (VectorTileDecoder.Feature f : new ArrayList<>(result)) { - if (!isPoint(f)) { - result.remove(f); - } - } - } - Log.d(DEBUG_TAG, "getClicked found " + (result != null ? result.size() : "nothing")); - return result; - } - @Override public String getName() { return map.getContext().getString(R.string.layer_panoramax); } - @Override - public void invalidate() { - map.invalidate(); - } - @Override public void onSelected(FragmentActivity activity, de.blau.android.util.mvt.VectorTileDecoder.Feature f) { if (isPoint(f)) { @@ -233,16 +169,6 @@ public void onSelected(FragmentActivity activity, de.blau.android.util.mvt.Vecto } } - /** - * Check if this Feature geometry is a Point - * - * @param f the Feature - * @return true if the geometry is a Point - */ - private boolean isPoint(@NonNull de.blau.android.util.mvt.VectorTileDecoder.Feature f) { - return GeoJSONConstants.POINT.equals(f.getGeometry().type()); - } - /** * Start the image viewer * @@ -374,23 +300,10 @@ public void selectImage(int pos) { } } - /** - * Flush both the sequence and image cache - * - * @param ctx an Android Context - */ - public synchronized void flushCaches(@NonNull Context ctx) { - flushSequenceCache(ctx); - try { - Thread t = new Thread(null, () -> FileUtil.pruneCache(cacheDir, 0), "Mapillary Image Cache Zapper"); - t.start(); - } catch (SecurityException | IllegalThreadStateException e) { - Log.e(DEBUG_TAG, "Unable to flush image cache " + e.getMessage()); - } - } - @Override public void setDateRange(long start, long end) { + state.startDate = start; + state.endDate = end; Style style = ((VectorTileRenderer) tileRenderer).getStyle(); setDateRange(style, IMAGE_LAYER, start, end, timestampFormat); setDateRange(style, SEQUENCE_LAYER, start, end, dateFormat); @@ -398,100 +311,25 @@ public void setDateRange(long start, long end) { dirty(); } - /** - * Set a range for the capture date - * - * This manipulates the filter in the layer - * - * @param style style to change - * @param layerName the name of the layer to change - * @param start the lower bound for the capture date in ms since the epoch - * @param end the upper bound for the capture date in ms since the epoch - * @param format how to format the date/time value - */ - private void setDateRange(@NonNull Style style, @NonNull String layerName, long start, long end, @NonNull SimpleDateFormat format) { - Layer layer = style.getLayer(layerName); - if (layer == null) { - Log.e(DEBUG_TAG, layerName + " not found"); - return; - } - state.startDate = start; - state.endDate = end; - JsonArray filter = layer.getFilter(); - if (filter != null && filter.size() == 3) { - setDateFilterValue(filter.get(1), start, format); - setDateFilterValue(filter.get(2), end, format); - return; - } - Log.e(DEBUG_TAG, "filter not found"); - } - - /** - * Set the value of a date filter - * - * @param filter a JsonArray representing a filter - * @param value the value to set - * @param format how to format the date/time value - */ - private void setDateFilterValue(@NonNull JsonElement filter, long value, @NonNull SimpleDateFormat format) { - if (filter instanceof JsonArray && ((JsonArray) filter).size() == 3) { + @Override + protected void setDateFilterValue(@NonNull JsonElement filter, long value, @Nullable SimpleDateFormat format) { + if (filter instanceof JsonArray && ((JsonArray) filter).size() == 3 && format != null) { ((JsonArray) filter).set(2, new JsonPrimitive(format.format(value))); } } - /** - * Flush the sequence cache - * - * @param ctx an Android Context - */ - private void flushSequenceCache(@NonNull Context ctx) { - if (state != null) { - state.imageId = null; - state.sequenceCache.clear(); - state.sequenceId = null; - savingHelper.save(ctx, FILENAME, state, false); - } - } - - @Override - public SpannableString getDescription(de.blau.android.util.mvt.VectorTileDecoder.Feature f) { - return getDescription(map.getContext(), f); - } - @Override public SpannableString getDescription(@NonNull Context context, de.blau.android.util.mvt.VectorTileDecoder.Feature f) { return new SpannableString(context.getString(R.string.mapillary_image, f.getAttributes().get(TIMESTAMP_KEY))); } @Override - public void resetStyling() { - try (InputStream is = map.getContext().getAssets().open(DEFAULT_PANORAMAX_STYLE_JSON)) { - Style style = new Style(); - style.loadStyle(map.getContext(), is); - ((VectorTileRenderer) tileRenderer).setStyle(style); - Layer layer = style.getLayer(IMAGE_LAYER); - if (layer instanceof Symbol) { - ((Symbol) layer).setSymbol(Mapillary.NAME, map.getDataStyle()); - layer.setColor(layer.getColor()); - } - layer = style.getLayer(SELECTED_IMAGE_LAYER); - if (layer instanceof Symbol) { - ((Symbol) layer).setSymbol(Mapillary.NAME, map.getDataStyle()); - layer.setColor(layer.getColor()); - selectedFilter = layer.getFilter(); - } - dirty(); - Log.d(DEBUG_TAG, "Loaded style successfully"); - } catch (IOException ioex) { - Log.d(DEBUG_TAG, "Reading default mapillary style failed"); - } - } - - @Override - public void flushTileCache(@Nullable final FragmentActivity activity, boolean all) { - super.flushTileCache(activity, all); - if (activity != null) { - flushSequenceCache(activity); + protected void flushSequenceCache(@NonNull Context ctx) { + if (state != null) { + state.imageId = null; + state.sequenceCache.clear(); + state.sequenceId = null; + savingHelper.save(ctx, FILENAME, state, false); } } @@ -508,11 +346,6 @@ public LayerType getType() { return LayerType.PANORAMAX; } - @Override - public int onDrawAttribution(@NonNull Canvas c, @NonNull IMapView osmv, int offset) { - return offset; - } - /** * Show a dialog to set the displayed date range * diff --git a/src/main/java/de/blau/android/prefs/Preferences.java b/src/main/java/de/blau/android/prefs/Preferences.java index a27c45c31..624b20934 100755 --- a/src/main/java/de/blau/android/prefs/Preferences.java +++ b/src/main/java/de/blau/android/prefs/Preferences.java @@ -21,6 +21,7 @@ import de.blau.android.Map; import de.blau.android.R; import de.blau.android.contract.Urls; +import de.blau.android.layer.streetlevel.AbstractImageOverlay; import de.blau.android.osm.Server; import de.blau.android.presets.Preset; import de.blau.android.presets.PresetElementPath; @@ -191,7 +192,7 @@ public Preferences(@NonNull Context ctx) { tileCacheSize = getIntPref(R.string.config_tileCacheSize_key, 100); preferRemovableStorage = prefs.getBoolean(r.getString(R.string.config_preferRemovableStorage_key), true); - mapillaryCacheSize = getIntPref(R.string.config_mapillaryCacheSize_key, de.blau.android.layer.streetlevel.mapillary.MapillaryOverlay.MAPILLARY_DEFAULT_CACHE_SIZE); + mapillaryCacheSize = getIntPref(R.string.config_mapillaryCacheSize_key, AbstractImageOverlay.DEFAULT_CACHE_SIZE); downloadRadius = getIntPref(R.string.config_extTriggeredDownloadRadius_key, 50); maxDownloadSpeed = getIntPref(R.string.config_maxDownloadSpeed_key, 10); @@ -250,7 +251,7 @@ public Preferences(@NonNull Context ctx) { mapillarySequencesUrlV4 = prefs.getString(r.getString(R.string.config_mapillarySequencesUrlV4_key), Urls.DEFAULT_MAPILLARY_SEQUENCES_URL_V4); mapillaryImagesUrlV4 = prefs.getString(r.getString(R.string.config_mapillaryImagesUrlV4_key), Urls.DEFAULT_MAPILLARY_IMAGES_V4); - mapillaryMinZoom = getIntPref(R.string.config_mapillary_min_zoom_key, de.blau.android.layer.streetlevel.mapillary.MapillaryOverlay.MAPILLARY_DEFAULT_MIN_ZOOM); + mapillaryMinZoom = getIntPref(R.string.config_mapillary_min_zoom_key, AbstractImageOverlay.DEFAULT_MIN_ZOOM); panoramaxApiUrl = prefs.getString(r.getString(R.string.config_panoramaxApiUrl_key), Urls.DEFAULT_PANORAMAX_API_URL);