diff options
Diffstat (limited to 'platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayer.java')
-rw-r--r-- | platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayer.java | 398 |
1 files changed, 398 insertions, 0 deletions
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayer.java new file mode 100644 index 0000000000..0e7ce37a07 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayer.java @@ -0,0 +1,398 @@ +package com.mapbox.mapboxsdk.plugins.locationlayer; + +import android.graphics.Bitmap; +import android.graphics.PointF; +import android.support.annotation.ColorInt; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.mapbox.geojson.Feature; +import com.mapbox.geojson.Point; +import com.mapbox.mapboxsdk.geometry.LatLng; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.plugins.locationlayer.modes.RenderMode; +import com.mapbox.mapboxsdk.style.layers.Layer; +import com.mapbox.mapboxsdk.style.layers.SymbolLayer; +import com.mapbox.mapboxsdk.style.sources.GeoJsonSource; + +import java.util.ArrayList; +import java.util.List; + +import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.ACCURACY_LAYER; +import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.BACKGROUND_ICON; +import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.BACKGROUND_LAYER; +import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.BACKGROUND_STALE_ICON; +import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.BEARING_ICON; +import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.BEARING_LAYER; +import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.FOREGROUND_ICON; +import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.FOREGROUND_LAYER; +import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.FOREGROUND_STALE_ICON; +import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.LOCATION_SOURCE; +import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.PROPERTY_ACCURACY_ALPHA; +import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.PROPERTY_ACCURACY_COLOR; +import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.PROPERTY_ACCURACY_RADIUS; +import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.PROPERTY_BACKGROUND_ICON; +import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.PROPERTY_BACKGROUND_STALE_ICON; +import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.PROPERTY_BEARING_ICON; +import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.PROPERTY_COMPASS_BEARING; +import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.PROPERTY_FOREGROUND_ICON; +import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.PROPERTY_FOREGROUND_ICON_OFFSET; +import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.PROPERTY_FOREGROUND_STALE_ICON; +import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.PROPERTY_GPS_BEARING; +import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.PROPERTY_LOCATION_STALE; +import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.PROPERTY_SHADOW_ICON_OFFSET; +import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.SHADOW_ICON; +import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.SHADOW_LAYER; +import static com.mapbox.mapboxsdk.style.expressions.Expression.interpolate; +import static com.mapbox.mapboxsdk.style.expressions.Expression.linear; +import static com.mapbox.mapboxsdk.style.expressions.Expression.stop; +import static com.mapbox.mapboxsdk.style.expressions.Expression.zoom; +import static com.mapbox.mapboxsdk.style.layers.Property.NONE; +import static com.mapbox.mapboxsdk.style.layers.Property.VISIBLE; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.colorToRgbaString; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconSize; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.visibility; + +final class LocationLayer implements PluginAnimator.OnLayerAnimationsValuesChangeListener { + + @RenderMode.Mode + private int renderMode; + + private final MapboxMap mapboxMap; + private final LayerSourceProvider layerSourceProvider; + private final LayerBitmapProvider bitmapProvider; + private LocationLayerOptions options; + + private final List<String> layerMap = new ArrayList<>(); + private Feature locationFeature; + private GeoJsonSource locationSource; + + private boolean isHidden = true; + + LocationLayer(MapboxMap mapboxMap, LayerSourceProvider layerSourceProvider, + LayerFeatureProvider featureProvider, LayerBitmapProvider bitmapProvider, + LocationLayerOptions options) { + this.mapboxMap = mapboxMap; + this.layerSourceProvider = layerSourceProvider; + this.bitmapProvider = bitmapProvider; + this.locationFeature = featureProvider.generateLocationFeature(locationFeature, options); + initializeComponents(options); + setRenderMode(RenderMode.NORMAL); + } + + void initializeComponents(LocationLayerOptions options) { + addLocationSource(); + addLayers(options.layerBelow()); + applyStyle(options); + + if (isHidden) { + hide(); + } else { + show(); + } + } + + void applyStyle(@NonNull LocationLayerOptions options) { + this.options = options; + + float elevation = options.elevation(); + // Only add icon elevation if the values greater than 0. + if (elevation > 0) { + styleShadow(options); + } + styleForeground(options); + styleBackground(options); + styleBearing(options); + styleAccuracy(options.accuracyAlpha(), options.accuracyColor()); + styleScaling(options); + determineIconsSource(options); + } + + void setRenderMode(@RenderMode.Mode int renderMode) { + this.renderMode = renderMode; + + if (!isHidden) { + boolean isStale = locationFeature.getBooleanProperty(PROPERTY_LOCATION_STALE); + switch (renderMode) { + case RenderMode.NORMAL: + styleForeground(options); + setLayerVisibility(SHADOW_LAYER, true); + setLayerVisibility(FOREGROUND_LAYER, true); + setLayerVisibility(BACKGROUND_LAYER, true); + setLayerVisibility(ACCURACY_LAYER, !isStale); + setLayerVisibility(BEARING_LAYER, false); + break; + case RenderMode.COMPASS: + styleForeground(options); + setLayerVisibility(SHADOW_LAYER, true); + setLayerVisibility(FOREGROUND_LAYER, true); + setLayerVisibility(BACKGROUND_LAYER, true); + setLayerVisibility(ACCURACY_LAYER, !isStale); + setLayerVisibility(BEARING_LAYER, true); + break; + case RenderMode.GPS: + styleForeground(options); + setLayerVisibility(SHADOW_LAYER, false); + setLayerVisibility(FOREGROUND_LAYER, true); + setLayerVisibility(BACKGROUND_LAYER, true); + setLayerVisibility(ACCURACY_LAYER, false); + setLayerVisibility(BEARING_LAYER, false); + break; + default: + break; + } + + determineIconsSource(options); + } + } + + int getRenderMode() { + return renderMode; + } + + // + // Layer action + // + + void show() { + isHidden = false; + setRenderMode(renderMode); + } + + void hide() { + isHidden = true; + for (String layerId : layerMap) { + setLayerVisibility(layerId, false); + } + } + + void updateForegroundOffset(double tilt) { + JsonArray foregroundJsonArray = new JsonArray(); + foregroundJsonArray.add(0f); + foregroundJsonArray.add((float) (-0.05 * tilt)); + locationFeature.addProperty(PROPERTY_FOREGROUND_ICON_OFFSET, foregroundJsonArray); + + JsonArray backgroundJsonArray = new JsonArray(); + backgroundJsonArray.add(0f); + backgroundJsonArray.add((float) (0.05 * tilt)); + locationFeature.addProperty(PROPERTY_SHADOW_ICON_OFFSET, backgroundJsonArray); + + refreshSource(); + } + + void updateForegroundBearing(float bearing) { + if (renderMode != RenderMode.GPS) { + setBearingProperty(PROPERTY_GPS_BEARING, bearing); + } + } + + private void setLayerVisibility(String layerId, boolean visible) { + Layer layer = mapboxMap.getLayer(layerId); + if (layer != null) { + String targetVisibility = visible ? VISIBLE : NONE; + if (!layer.getVisibility().value.equals(targetVisibility)) { + layer.setProperties(visibility(visible ? VISIBLE : NONE)); + } + } + } + + private void addLayers(String idBelowLayer) { + addSymbolLayer(BEARING_LAYER, idBelowLayer); + addSymbolLayer(FOREGROUND_LAYER, BEARING_LAYER); + addSymbolLayer(BACKGROUND_LAYER, FOREGROUND_LAYER); + addSymbolLayer(SHADOW_LAYER, BACKGROUND_LAYER); + addAccuracyLayer(); + } + + private void addSymbolLayer(String layerId, String beforeLayerId) { + Layer layer = layerSourceProvider.generateLayer(layerId); + addLayerToMap(layer, beforeLayerId); + } + + private void addAccuracyLayer() { + Layer accuracyLayer = layerSourceProvider.generateAccuracyLayer(); + addLayerToMap(accuracyLayer, BACKGROUND_LAYER); + } + + private void addLayerToMap(Layer layer, @NonNull String idBelowLayer) { + mapboxMap.addLayerBelow(layer, idBelowLayer); + layerMap.add(layer.getId()); + } + + private void setBearingProperty(String propertyId, float bearing) { + locationFeature.addNumberProperty(propertyId, bearing); + refreshSource(); + } + + private void updateAccuracyRadius(float accuracy) { + if (renderMode == RenderMode.COMPASS || renderMode == RenderMode.NORMAL) { + locationFeature.addNumberProperty(PROPERTY_ACCURACY_RADIUS, accuracy); + refreshSource(); + } + } + + // + // Source actions + // + + private void addLocationSource() { + locationSource = layerSourceProvider.generateSource(locationFeature); + mapboxMap.addSource(locationSource); + } + + private void refreshSource() { + GeoJsonSource source = mapboxMap.getSourceAs(LOCATION_SOURCE); + if (source != null) { + locationSource.setGeoJson(locationFeature); + } + } + + private void setLocationPoint(Point locationPoint) { + JsonObject properties = locationFeature.properties(); + if (properties != null) { + locationFeature = Feature.fromGeometry(locationPoint, properties); + refreshSource(); + } + } + + // + // Styling + // + + private void styleBackground(LocationLayerOptions options) { + Bitmap backgroundBitmap = bitmapProvider.generateBitmap( + options.backgroundDrawable(), options.backgroundTintColor() + ); + Bitmap backgroundStaleBitmap = bitmapProvider.generateBitmap( + options.backgroundDrawableStale(), options.backgroundStaleTintColor() + ); + mapboxMap.addImage(BACKGROUND_ICON, backgroundBitmap); + mapboxMap.addImage(BACKGROUND_STALE_ICON, backgroundStaleBitmap); + } + + private void styleShadow(LocationLayerOptions options) { + mapboxMap.addImage(SHADOW_ICON, bitmapProvider.generateShadowBitmap(options)); + } + + private void styleBearing(LocationLayerOptions options) { + Bitmap bearingBitmap = bitmapProvider.generateBitmap(options.bearingDrawable(), options.bearingTintColor()); + mapboxMap.addImage(BEARING_ICON, bearingBitmap); + } + + private void styleAccuracy(float accuracyAlpha, @ColorInt int accuracyColor) { + locationFeature.addNumberProperty(PROPERTY_ACCURACY_ALPHA, accuracyAlpha); + locationFeature.addStringProperty(PROPERTY_ACCURACY_COLOR, colorToRgbaString(accuracyColor)); + refreshSource(); + } + + private void styleForeground(LocationLayerOptions options) { + Bitmap foregroundBitmap = bitmapProvider.generateBitmap( + options.foregroundDrawable(), options.foregroundTintColor() + ); + Bitmap foregroundBitmapStale = bitmapProvider.generateBitmap( + options.foregroundDrawableStale(), options.foregroundStaleTintColor() + ); + if (renderMode == RenderMode.GPS) { + foregroundBitmap = bitmapProvider.generateBitmap( + options.gpsDrawable(), options.foregroundTintColor() + ); + foregroundBitmapStale = bitmapProvider.generateBitmap( + options.gpsDrawable(), options.foregroundStaleTintColor() + ); + } + mapboxMap.addImage(FOREGROUND_ICON, foregroundBitmap); + mapboxMap.addImage(FOREGROUND_STALE_ICON, foregroundBitmapStale); + } + + private void styleScaling(LocationLayerOptions options) { + for (String layerId : layerMap) { + Layer layer = mapboxMap.getLayer(layerId); + if (layer != null && layer instanceof SymbolLayer) { + layer.setProperties( + iconSize( + interpolate(linear(), zoom(), + stop(options.minZoom(), options.minZoomIconScale()), + stop(options.maxZoom(), options.maxZoomIconScale()) + ) + ) + ); + } + } + } + + private void determineIconsSource(LocationLayerOptions options) { + String foregroundIconString = buildIconString( + renderMode == RenderMode.GPS ? options.gpsName() : options.foregroundName(), FOREGROUND_ICON); + String foregroundStaleIconString = buildIconString(options.foregroundStaleName(), FOREGROUND_STALE_ICON); + String backgroundIconString = buildIconString(options.backgroundName(), BACKGROUND_ICON); + String backgroundStaleIconString = buildIconString(options.backgroundStaleName(), BACKGROUND_STALE_ICON); + String bearingIconString = buildIconString(options.bearingName(), BEARING_ICON); + + locationFeature.addStringProperty(PROPERTY_FOREGROUND_ICON, foregroundIconString); + locationFeature.addStringProperty(PROPERTY_BACKGROUND_ICON, backgroundIconString); + locationFeature.addStringProperty(PROPERTY_FOREGROUND_STALE_ICON, foregroundStaleIconString); + locationFeature.addStringProperty(PROPERTY_BACKGROUND_STALE_ICON, backgroundStaleIconString); + locationFeature.addStringProperty(PROPERTY_BEARING_ICON, bearingIconString); + refreshSource(); + } + + private String buildIconString(@Nullable String bitmapName, @NonNull String drawableName) { + if (bitmapName != null) { + return bitmapName; + } + return drawableName; + } + + void setLocationsStale(boolean isStale) { + // If options has stale state disabled, just return here. + if (!options.enableStaleState()) { + return; + } + locationFeature.addBooleanProperty(PROPERTY_LOCATION_STALE, isStale); + refreshSource(); + if (renderMode != RenderMode.GPS) { + setLayerVisibility(ACCURACY_LAYER, !isStale); + } + } + + // + // Map click event + // + + boolean onMapClick(LatLng point) { + PointF screenLoc = mapboxMap.getProjection().toScreenLocation(point); + List<Feature> features = mapboxMap.queryRenderedFeatures(screenLoc, + BACKGROUND_LAYER, + FOREGROUND_LAYER, + BEARING_LAYER + ); + return !features.isEmpty(); + } + + @Override + public void onNewLatLngValue(LatLng latLng) { + Point point = Point.fromLngLat(latLng.getLongitude(), latLng.getLatitude()); + setLocationPoint(point); + } + + @Override + public void onNewGpsBearingValue(float gpsBearing) { + if (renderMode == RenderMode.GPS) { + setBearingProperty(PROPERTY_GPS_BEARING, gpsBearing); + } + } + + @Override + public void onNewCompassBearingValue(float compassBearing) { + if (renderMode == RenderMode.COMPASS) { + setBearingProperty(PROPERTY_COMPASS_BEARING, compassBearing); + } + } + + @Override + public void onNewAccuracyRadiusValue(float accuracyRadiusValue) { + updateAccuracyRadius(accuracyRadiusValue); + } +}
\ No newline at end of file |