Android Studio Mapbox Layer Problem with both Symbol Layer and Circle Layer Crash

The attached code has several issues and I do not have 100% confidence but I think the crash is happened because you assign same Layer ID for both symbol layer and circle layer.

Did you see a crash dump similar to below? It says “Layer layer-id already exists”.

2020-11-06 22:54:49.324 7267-7267/com.example.sof95859 E/Mbgl-MapChangeReceiver: Exception in onDidFinishLoadingStyle com.mapbox.mapboxsdk.style.layers.CannotAddLayerException: Layer layer-id already exists at com.mapbox.mapboxsdk.maps.NativeMapView.nativeAddLayer(Native Method) at com.mapbox.mapboxsdk.maps.NativeMapView.addLayer(NativeMapView.java:858) at com.mapbox.mapboxsdk.maps.Style.addLayer(Style.java:191) at com.example.sof95859.MainActivity$1$1.onStyleLoaded(MainActivity.java:121) at com.mapbox.mapboxsdk.maps.MapboxMap.notifyStyleLoaded(MapboxMap.java:963) at com.mapbox.mapboxsdk.maps.MapboxMap.onFinishLoadingStyle(MapboxMap.java:225) at com.mapbox.mapboxsdk.maps.MapView$MapCallback.onDidFinishLoadingStyle(MapView.java:1373) at com.mapbox.mapboxsdk.maps.MapChangeReceiver.onDidFinishLoadingStyle(MapChangeReceiver.java:198) at com.mapbox.mapboxsdk.maps.NativeMapView.onDidFinishLoadingStyle(NativeMapView.java:1166) at android.os.MessageQueue.nativePollOnce(Native Method) at android.os.MessageQueue.next(MessageQueue.java:336) at android.os.Looper.loop(Looper.java:174) at android.app.ActivityThread.main(ActivityThread.java:7356) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)

I also attach working sample that accesses my sample layer.

public class MainActivity extends AppCompatActivity {
    private static final String TILESET_KEY = "yochi.6v4pssqn";
    private static final String VECTOR_SOURCE_ID = "vector-source";
    private static final String VECTOR_SOURCE_LAYER_ID = "sof95859-0wzvvn";
    private static final String TILESET_SYMBOL_LAYER_ID = "symbol-layer-id";
    private static final String TILESET_CIRCLE_LAYER_ID = "circle-layer-id";
    private static final int roundT_val = 2;
    private static final Expression COLOR_GREEN = rgb(0, 255, 0);

    private MapView mapView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Mapbox.getInstance(this, getString(R.string.mapbox_access_token));

        setContentView(R.layout.activity_main);

        mapView = (MapView) findViewById(R.id.mapView);
        mapView.onCreate(savedInstanceState);
        mapView.getMapAsync(new OnMapReadyCallback() {
            @Override
            public void onMapReady(@NonNull MapboxMap mapboxMap) {
                CameraUpdate update = CameraUpdateFactory.newLatLngBounds(
                        LatLngBounds.from(1, 1, -1, -1), 0, 0, 0 );
                mapboxMap.easeCamera(update);

                mapboxMap.setStyle(Style.MAPBOX_STREETS, new Style.OnStyleLoaded() {
                    @Override
                    public void onStyleLoaded(@NonNull Style style) {
                        // tileset.json API call did not work for me
                        style.addSource(new VectorSource(VECTOR_SOURCE_ID, "mapbox://" + TILESET_KEY));

                        // Asssign unique layer ID
                        SymbolLayer label =new SymbolLayer(TILESET_SYMBOL_LAYER_ID, VECTOR_SOURCE_ID);
                        // Set layer name by checking Tileset in Studio
                        label.setSourceLayer(VECTOR_SOURCE_LAYER_ID);
                        label.withProperties(
                                textField(get("gaslvl_PM")), //This contains the number values
                                textSize(17f),
                                textColor(Color.RED),
                                textVariableAnchor(
                                        new String[]{TEXT_ANCHOR_TOP, TEXT_ANCHOR_BOTTOM, TEXT_ANCHOR_LEFT, TEXT_ANCHOR_RIGHT}),
                                textJustify(TEXT_JUSTIFY_AUTO),
                                textRadialOffset(0.5f)
                        );
                        label.setFilter(all(
                                //This is to filter out the multiple rounds in the route of one full roundtrip.
                                //To prevent the same data from the same area to overlap.
                                eq(literal("roundtrip_number"), literal(roundT_val))));
                        style.addLayer(label);

                        CircleLayer circleLayer = new CircleLayer(TILESET_CIRCLE_LAYER_ID, VECTOR_SOURCE_ID);
                        // Set layer name by checking Tileset in Studio
                        circleLayer.setSourceLayer(VECTOR_SOURCE_LAYER_ID);
                        circleLayer.withProperties(
                                circleRadius(
                                        interpolate(
                                                exponential(1.75f),
                                                zoom(),
                                                stop(12, 2f),
                                                stop(22, 180f)
                                        )),
                                circleColor(
                                        match(
                                                // I think this is wrong
                                                get(TILESET_CIRCLE_LAYER_ID),rgb(0, 0, 0),
                                                stop("0", COLOR_GREEN),
                                                stop("1", COLOR_GREEN),
                                                stop("2", COLOR_GREEN),
                                                stop("3", COLOR_GREEN),
                                                stop("4", COLOR_GREEN)
                        // the number of parentheses is wrong
                        )));
                        circleLayer.setFilter(all(
                                eq(literal("roundtrip_number"), literal(roundT_val))
                                //eq(literal("date"), literal(Date.valueOf(date_val)))
                        ));
                        style.addLayer(circleLayer);

                    }
                });
            }
        });
    }

comments added

The important thing is “layerId” parameter for SymbolLayer, CircleLayer constructor is not the layer name in the tileset. You define any unique “layerId” in each constructor of SymbolLayer, CircleLayer etc…

You can use same data source for each layer by putting same Source ID in the second parameter of SymbolLayer, CircleLayer constructor. Please note that Source ID is also you define any Source ID in style.addSource.

Then to select which Layer should be used in each layer, you call label.setSourceLayer(VECTOR_SOURCE_LAYER_ID);

Here’s the summary of IDs

  • TILESET_KEY: Tileset ID provided by Studio
  • VECTOR_SOURCE_LAYER_ID: Layer Name provided by Studio
  • VECTOR_SOURCE_ID: Any source ID you can define
  • TILESET_SYMBOL_LAYER_ID, TILESET_CIRCLE_LAYER_ID: Any layer ID you can define

enter image description here

CLICK HERE to find out more related problems solutions.

Leave a Comment

Your email address will not be published.

Scroll to Top