How to add custom marker on Mapbox in Android with title and description?

2.1k views Asked by At

i'd like to add title and description for each marker on my map. For now i add marker on map taking data from a server with a GET call and creating marker for each object in response.

public void onMapReady(@NonNull final MapboxMap mapboxMap) {

        MainActivity.this.mapboxMap = mapboxMap;

        StringRequest request = new StringRequest(url, new Response.Listener<String>() {
            @Override
            public void onResponse(String response) {
                try {

                    JSONArray jsonArray = new JSONArray(response);
                    List<Feature> symbolLayerIconFeatureList = new ArrayList<>();

                    for(int i = 0; i < jsonArray.length(); i++){
                        JSONObject crag = jsonArray.getJSONObject(i);

                        String description = crag.getString("descrizione")
                        String name = crag.getString("nome");
                        Double lng = crag.getDouble("longitudine");
                        Double lat = crag.getDouble("latitudine");

                        symbolLayerIconFeatureList.add(Feature.fromGeometry(
                               Point.fromLngLat(lng, lat)));

                    }

                    mapboxMap.setStyle(new Style.Builder().fromUri("mapbox://styles/mapbox/cjf4m44iw0uza2spb3q0a7s41")

                            .withImage(ICON_ID, BitmapFactory.decodeResource(
                                    MainActivity.this.getResources(), R.drawable.icona_falesia))

                            .withSource(new GeoJsonSource(SOURCE_ID,
                                    FeatureCollection.fromFeatures(symbolLayerIconFeatureList)))

                            .withLayer(new SymbolLayer(LAYER_ID, SOURCE_ID)
                                    .withProperties(
                                            iconImage(ICON_ID),
                                            iconAllowOverlap(true),
                                            iconIgnorePlacement(true)
                                    )
                            ), new Style.OnStyleLoaded() {
                        @Override
                        public void onStyleLoaded(@NonNull Style style) {

                        }
                    });
                } catch (JSONException e){
                    e.printStackTrace();
                }
            }
        }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                error.printStackTrace();
            }
        });

        mQueue.add(request);

    }

This is my function onMapReady where a get data and create marker. How can i add also title and a kind of description for each marker?

1

There are 1 answers

0
langsmith On BEST ANSWER

You'll also need to use textField(), textOffset(), textIgnorePlacement(), textAllowOverlap(), and textField() if you just want show text along with a SymbolLayer icon. Adding the name and description to each Feature is key to getting this working correctly.

I've modified https://docs.mapbox.com/android/maps/examples/marker-symbol-layer/ to create the GIF seen at https://i.imgur.com/5LzSzRf.mp4. You can look at what's going on in the code and then adjust it to match your implementation within the GET onResponse() callback.

package com.mapbox.mapboxandroiddemo.examples.styles;

import android.graphics.BitmapFactory;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;

import com.mapbox.geojson.Feature;
import com.mapbox.geojson.FeatureCollection;
import com.mapbox.geojson.Point;
import com.mapbox.mapboxandroiddemo.R;
import com.mapbox.mapboxsdk.Mapbox;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.maps.Style;
import com.mapbox.mapboxsdk.style.expressions.Expression;
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.style.expressions.Expression.get;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconAllowOverlap;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconIgnorePlacement;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconImage;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textAllowOverlap;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textField;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textIgnorePlacement;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textOffset;

/**
 * Display {@link SymbolLayer} icons on the map.
 */
public class BasicSymbolLayerActivity extends AppCompatActivity implements
        OnMapReadyCallback {

  private static final String SOURCE_ID = "SOURCE_ID";
  private static final String ICON_ID = "ICON_ID";
  private static final String LAYER_ID = "LAYER_ID";
  private MapView mapView;

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

    // Mapbox access token is configured here. This needs to be called either in your application
    // object or in the same activity which contains the mapview.
    Mapbox.getInstance(this, getString(R.string.access_token));

    // This contains the MapView in XML and needs to be called after the access token is configured.
    setContentView(R.layout.activity_style_basic_symbol_layer);

    mapView = findViewById(R.id.mapView);
    mapView.onCreate(savedInstanceState);
    mapView.getMapAsync(this);
  }

  @Override
  public void onMapReady(@NonNull final MapboxMap mapboxMap) {

    List<Feature> symbolLayerIconFeatureList = new ArrayList<>();

    Feature singleFeature = Feature.fromGeometry(Point.fromLngLat(-57.225365, -33.213144));
    singleFeature.addStringProperty("NAME_PROPERTY_KEY", "descrizione 1");
    singleFeature.addStringProperty("DESCRIPTION_PROPERTY_KEY", "nome 1");

    Feature secondFeature = Feature.fromGeometry(Point.fromLngLat(-54.14164, -33.981818));
    secondFeature.addStringProperty("NAME_PROPERTY_KEY", "descrizione 2");
    secondFeature.addStringProperty("DESCRIPTION_PROPERTY_KEY", "nome 2");

    Feature thirdFeature = Feature.fromGeometry(Point.fromLngLat(-56.990533, -30.583266));
    thirdFeature.addStringProperty("NAME_PROPERTY_KEY", "descrizione 3");
    thirdFeature.addStringProperty("DESCRIPTION_PROPERTY_KEY", "nome 3");

    symbolLayerIconFeatureList.add(singleFeature);
    symbolLayerIconFeatureList.add(secondFeature);
    symbolLayerIconFeatureList.add(thirdFeature);

    mapboxMap.setStyle(new Style.Builder().fromUri("mapbox://styles/mapbox/cjf4m44iw0uza2spb3q0a7s41")

            // Add the SymbolLayer icon image to the map style
            .withImage(ICON_ID, BitmapFactory.decodeResource(
                    BasicSymbolLayerActivity.this.getResources(), R.drawable.mapbox_marker_icon_default))

            // Adding a GeoJson source for the SymbolLayer icons.
            .withSource(new GeoJsonSource(SOURCE_ID,
                    FeatureCollection.fromFeatures(symbolLayerIconFeatureList)))

            // Adding the actual SymbolLayer to the map style. An offset is added that the bottom of the red
            // marker icon gets fixed to the coordinate, rather than the middle of the icon being fixed to
            // the coordinate point. This is offset is not always needed and is dependent on the image
            // that you use for the SymbolLayer icon.
            .withLayer(new SymbolLayer(LAYER_ID, SOURCE_ID)
                    .withProperties(
                            iconImage(ICON_ID),
                            iconAllowOverlap(true),
                            iconIgnorePlacement(true),
                            textOffset(new Float[]{0f,-2.5f}),
                            textIgnorePlacement(true),
                            textAllowOverlap(true),
                            textField(Expression.concat(get("NAME_PROPERTY_KEY"), Expression.literal("–"), get("DESCRIPTION_PROPERTY_KEY")))
                    )
            ), new Style.OnStyleLoaded() {
      @Override
      public void onStyleLoaded(@NonNull Style style) {

        // Map is set up and the style has loaded. Now you can add additional data or make other map adjustments.


      }
    });
  }

  @Override
  public void onResume() {
    super.onResume();
    mapView.onResume();
  }

  @Override
  protected void onStart() {
    super.onStart();
    mapView.onStart();
  }

  @Override
  protected void onStop() {
    super.onStop();
    mapView.onStop();
  }

  @Override
  public void onPause() {
    super.onPause();
    mapView.onPause();
  }

  @Override
  public void onLowMemory() {
    super.onLowMemory();
    mapView.onLowMemory();
  }

  @Override
  protected void onDestroy() {
    super.onDestroy();
    mapView.onDestroy();
  }

  @Override
  protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    mapView.onSaveInstanceState(outState);
  }
}

See https://docs.mapbox.com/android/maps/examples/symbol-layer-info-window/ if you want to create an info window.