diff options
Diffstat (limited to 'platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/CustomGeometrySource.java')
-rw-r--r-- | platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/CustomGeometrySource.java | 379 |
1 files changed, 0 insertions, 379 deletions
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/CustomGeometrySource.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/CustomGeometrySource.java deleted file mode 100644 index b75ccf5a9c..0000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/CustomGeometrySource.java +++ /dev/null @@ -1,379 +0,0 @@ -package com.mapbox.mapboxsdk.style.sources; - -import android.support.annotation.Keep; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.annotation.UiThread; -import android.support.annotation.WorkerThread; - -import com.mapbox.geojson.Feature; -import com.mapbox.geojson.FeatureCollection; -import com.mapbox.mapboxsdk.geometry.LatLngBounds; -import com.mapbox.mapboxsdk.style.expressions.Expression; - -import java.lang.ref.WeakReference; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.ThreadFactory; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; - -/** - * Custom Vector Source, allows using FeatureCollections. - * <p> - * CustomGeometrySource uses a coalescing model for frequent data updates targeting the same tile id, - * which means, that the in-progress request as well as the last scheduled request are guaranteed to finish. - * Any requests scheduled meanwhile can be canceled. - */ -public class CustomGeometrySource extends Source { - public static final String THREAD_PREFIX = "CustomGeom"; - public static final int THREAD_POOL_LIMIT = 4; - private static final AtomicInteger poolCount = new AtomicInteger(); - private final Lock executorLock = new ReentrantLock(); - private ThreadPoolExecutor executor; - private GeometryTileProvider provider; - private final Map<TileID, GeometryTileRequest> awaitingTasksMap = new HashMap<>(); - - /** - * A map containing in-progress requests targeting distinct tiles. - * A request is considered in-progress when it's started by the ThreadPoolExecutor. - * A request is marked as done when the data is passed from the JNI layer to the core, after the features conversion. - */ - private final Map<TileID, AtomicBoolean> inProgressTasksMap = new HashMap<>(); - - /** - * Create a CustomGeometrySource - * - * @param id The source id. - * @param provider The tile provider that returns geometry data for this source. - */ - @UiThread - public CustomGeometrySource(String id, GeometryTileProvider provider) { - this(id, new CustomGeometrySourceOptions(), provider); - } - - /** - * Create a CustomGeometrySource with non-default {@link CustomGeometrySourceOptions}. - * - * @param id The source id. - * @param options CustomGeometrySourceOptions. - * @param provider The tile provider that returns geometry data for this source. - */ - @UiThread - public CustomGeometrySource(String id, CustomGeometrySourceOptions options, - GeometryTileProvider provider) { - super(); - this.provider = provider; - initialize(id, options); - } - - /** - * Invalidate previously provided features within a given bounds at all zoom levels. - * Invoking this method will result in new requests to `GeometryTileProvider` for regions - * that contain, include, or intersect with the provided bounds. - * - * @param bounds The region in which features should be invalidated at all zoom levels - */ - public void invalidateRegion(LatLngBounds bounds) { - nativeInvalidateBounds(bounds); - } - - /** - * Invalidate the geometry contents of a specific tile. Invoking this method will result - * in new requests to `GeometryTileProvider` for visible tiles. - * - * @param zoomLevel Tile zoom level. - * @param x Tile X coordinate. - * @param y Tile Y coordinate. - */ - public void invalidateTile(int zoomLevel, int x, int y) { - nativeInvalidateTile(zoomLevel, x, y); - } - - /** - * Set or update geometry contents of a specific tile. Use this method to update tiles - * for which `GeometryTileProvider` was previously invoked. This method can be called from - * background threads. - * - * @param zoomLevel Tile zoom level. - * @param x Tile X coordinate. - * @param y Tile Y coordinate. - * @param data Feature collection for the tile. - */ - public void setTileData(int zoomLevel, int x, int y, FeatureCollection data) { - nativeSetTileData(zoomLevel, x, y, data); - } - - /** - * Queries the source for features. - * - * @param filter an optional filter expression to filter the returned Features - * @return the features - */ - @NonNull - public List<Feature> querySourceFeatures(@Nullable Expression filter) { - checkThread(); - Feature[] features = querySourceFeatures(filter != null ? filter.toArray() : null); - return features != null ? Arrays.asList(features) : new ArrayList<Feature>(); - } - - @Keep - protected native void initialize(String sourceId, Object options); - - @NonNull - @Keep - private native Feature[] querySourceFeatures(Object[] filter); - - @Keep - private native void nativeSetTileData(int z, int x, int y, FeatureCollection data); - - @Keep - private native void nativeInvalidateTile(int z, int x, int y); - - @Keep - private native void nativeInvalidateBounds(LatLngBounds bounds); - - @Override - @Keep - protected native void finalize() throws Throwable; - - private void setTileData(TileID tileId, FeatureCollection data) { - nativeSetTileData(tileId.z, tileId.x, tileId.y, data); - } - - /** - * Tile data request can come from a number of different threads. - * To remove race condition for requests targeting the same tile id we are first checking if there is a request - * already enqueued, if yes, we are replacing it. - * Otherwise, we are checking if there is an in-progress request, if yes, - * we are creating or replacing an awaiting request. - * If none of the above, we are enqueueing the request. - */ - @WorkerThread - @Keep - private void fetchTile(int z, int x, int y) { - AtomicBoolean cancelFlag = new AtomicBoolean(false); - TileID tileID = new TileID(z, x, y); - GeometryTileRequest request = - new GeometryTileRequest(tileID, provider, awaitingTasksMap, inProgressTasksMap, this, cancelFlag); - - synchronized (awaitingTasksMap) { - synchronized (inProgressTasksMap) { - if (executor.getQueue().contains(request)) { - executor.remove(request); - executeRequest(request); - } else if (inProgressTasksMap.containsKey(tileID)) { - awaitingTasksMap.put(tileID, request); - } else { - executeRequest(request); - } - } - } - } - - private void executeRequest(@NonNull GeometryTileRequest request) { - executorLock.lock(); - try { - if (executor != null && !executor.isShutdown()) { - executor.execute(request); - } - } finally { - executorLock.unlock(); - } - } - - /** - * We want to cancel only the oldest request, therefore, we are first checking if it's in progress, - * if not or if the currently in progress request has already been canceled, - * we are searching for any request in the executor's queue. - * Otherwise, we are removing an awaiting request targeting this tile id. - * <p> - * {@link GeometryTileRequest#equals(Object)} is overridden to cover only the tile id, - * therefore, we can use an empty request to search the executor's queue. - */ - @WorkerThread - @Keep - private void cancelTile(int z, int x, int y) { - TileID tileID = new TileID(z, x, y); - - synchronized (awaitingTasksMap) { - synchronized (inProgressTasksMap) { - AtomicBoolean cancelFlag = inProgressTasksMap.get(tileID); - // check if there is an in progress task - if (!(cancelFlag != null && cancelFlag.compareAndSet(false, true))) { - // if there is no tasks in progress or the in progress task was already cancelled, check the executor's queue - GeometryTileRequest emptyRequest = - new GeometryTileRequest(tileID, null, null, null, null, null); - if (!executor.getQueue().remove(emptyRequest)) { - // if there was no tasks in queue, remove from the awaiting map - awaitingTasksMap.remove(tileID); - } - } - } - } - } - - @Keep - private void startThreads() { - executorLock.lock(); - try { - if (executor != null && !executor.isShutdown()) { - executor.shutdownNow(); - } - - executor = new ThreadPoolExecutor(THREAD_POOL_LIMIT, THREAD_POOL_LIMIT, - 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), - new ThreadFactory() { - final AtomicInteger threadCount = new AtomicInteger(); - final int poolId = poolCount.getAndIncrement(); - - @NonNull - @Override - public Thread newThread(@NonNull Runnable runnable) { - return new Thread( - runnable, - String.format(Locale.US, "%s-%d-%d", THREAD_PREFIX, poolId, threadCount.getAndIncrement())); - } - }); - } finally { - executorLock.unlock(); - } - } - - @Keep - private void releaseThreads() { - executorLock.lock(); - try { - executor.shutdownNow(); - } finally { - executorLock.unlock(); - } - } - - @Keep - private boolean isCancelled(int z, int x, int y) { - return inProgressTasksMap.get(new TileID(z, x, y)).get(); - } - - static class TileID { - public int z; - public int x; - public int y; - - TileID(int _z, int _x, int _y) { - z = _z; - x = _x; - y = _y; - } - - public int hashCode() { - return Arrays.hashCode(new int[] {z, x, y}); - } - - public boolean equals(@Nullable Object object) { - if (object == this) { - return true; - } - - if (object == null || getClass() != object.getClass()) { - return false; - } - - if (object instanceof TileID) { - TileID other = (TileID) object; - return this.z == other.z && this.x == other.x && this.y == other.y; - } - return false; - } - } - - static class GeometryTileRequest implements Runnable { - private final TileID id; - private final GeometryTileProvider provider; - private final Map<TileID, GeometryTileRequest> awaiting; - private final Map<TileID, AtomicBoolean> inProgress; - @NonNull - private final WeakReference<CustomGeometrySource> sourceRef; - private final AtomicBoolean cancelled; - - GeometryTileRequest(TileID _id, GeometryTileProvider p, - Map<TileID, GeometryTileRequest> awaiting, - Map<TileID, AtomicBoolean> m, - CustomGeometrySource _source, AtomicBoolean _cancelled) { - id = _id; - provider = p; - this.awaiting = awaiting; - inProgress = m; - sourceRef = new WeakReference<>(_source); - cancelled = _cancelled; - } - - public void run() { - synchronized (awaiting) { - synchronized (inProgress) { - if (inProgress.containsKey(id)) { - // request targeting this tile id is already being processed, - // scenario that should occur only if the tile is being requested when - // another request is switching threads to execute - if (!awaiting.containsKey(id)) { - awaiting.put(id, this); - } - return; - } else { - inProgress.put(id, cancelled); - } - } - } - - if (!isCancelled()) { - FeatureCollection data = provider.getFeaturesForBounds(LatLngBounds.from(id.z, id.x, id.y), id.z); - CustomGeometrySource source = sourceRef.get(); - if (!isCancelled() && source != null && data != null) { - source.setTileData(id, data); - } - } - - synchronized (awaiting) { - synchronized (inProgress) { - inProgress.remove(id); - - // executing the next request targeting the same tile - if (awaiting.containsKey(id)) { - GeometryTileRequest queuedRequest = awaiting.get(id); - CustomGeometrySource source = sourceRef.get(); - if (source != null && queuedRequest != null) { - source.executor.execute(queuedRequest); - } - - awaiting.remove(id); - } - } - } - } - - private Boolean isCancelled() { - return cancelled.get(); - } - - @Override - public boolean equals(@Nullable Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - GeometryTileRequest request = (GeometryTileRequest) o; - return id.equals(request.id); - } - } -} |