Examples
Getting started (3)Create a simple map viewDynamically build a map viewSupport map fragmentDynamic styling (30)Add a new layer below labelsHillshadingAdd a vector tile sourceAdd a WMS sourceShow time lapseAnimate marker positionAnimated image source (GIF)Icon update based on API responseChange a layer's colorChange a map's languageColor dependent on zoom levelClick to add photoIndoor MapMarker following routeSymbol layer iconsStyle with missing iconPicture in picturePulsing layer opacityButton interaction stylingOpacity fadeShow and hide layersSymbolLayer iconsAdjust text labelsMultiple text formatsUse an image sourceTransparent render surfaceAnimated icon movementVariable label placementAdjust a layer's opacityText anchor positionExtrusions (5)Adjust light location and colorDisplay 3D building height based on vector dataExtrude polygons for 3D indoor mappingRotate and tilt with 3D buildingsUse GeoJSON data to set extrusion heightCamera (5)Animate the map cameraFit camera in bounding boxRestrict map panningRotating cameraZoom to show a cluster's leavesData visualization (33)Display water depthBiometric fingerprintBaseball spray chartCalendar integrationCircle icon toggleCircleLayer clustersCircle radiusCircle to icon transitionCreate a line layerCreate hotspots from pointsData time lapseShow heatmap dataDraw a GeoJSON lineDraw a polygon with holesDraw a polygonDraw multiple geometriesJoin local JSON data with vector tile geometriesLine gradientLine behind moving iconMultiple expressionsIcon setting based on Feature propertyStyling heatmapsOutlined polygon holeSatellite land selectShared preferencesSpinning iconStyle circles categoricallyStyle lines using an identity property functionToggle collision detectionSymbol layer info windowSymbolLayer clusteringZoom-based icon switchUpdate a choropleth layer by zoom levelQuerying the map (3)Building outlineQuery a map featureSelect a buildingOffline (3)Cache managementA simple offline mapOffline managerMap style (4)Style attributionMapbox Studio styleDefault stylesLocal style or custom raster styleUser interaction (12)Click on single layerFeature countDrawing search areaSymbol layer icon size changeHighlighted lineInset mapBackground fogLocation pickerRecyclerView DirectionsRecyclerView interactionSearch again in an areaSnaking directionsImage generation (2)Snapshot NotificationShare snapshot imageDevice location (5)Track device locationLocation camera optionsCustomized location iconShow a user's location on a map fragmentShow a user's location

SymbolLayer clustering

activity_dds_image_clustering
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:mapbox="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.mapbox.mapboxandroiddemo.examples.dds.ImageClusteringActivity">
<com.mapbox.mapboxsdk.maps.MapView
android:id="@+id/mapView"
android:layout_width="match_parent"
android:layout_height="match_parent"
mapbox:mapbox_cameraTargetLat="2.69719"
mapbox:mapbox_cameraTargetLng="126.355705"
mapbox:mapbox_cameraZoom="4.119118"/>
</FrameLayout>
ImageClusteringActivity.java
package com.mapbox.mapboxandroiddemo.examples.dds;
import android.graphics.Color;
import android.os.Bundle;
import android.widget.Toast;
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.layers.TransitionOptions;
import com.mapbox.mapboxsdk.style.sources.GeoJsonOptions;
import com.mapbox.mapboxsdk.style.sources.GeoJsonSource;
import com.mapbox.mapboxsdk.utils.BitmapUtils;
import java.net.URI;
import java.net.URISyntaxException;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import timber.log.Timber;
import static com.mapbox.mapboxsdk.style.expressions.Expression.all;
import static com.mapbox.mapboxsdk.style.expressions.Expression.division;
import static com.mapbox.mapboxsdk.style.expressions.Expression.get;
import static com.mapbox.mapboxsdk.style.expressions.Expression.gte;
import static com.mapbox.mapboxsdk.style.expressions.Expression.has;
import static com.mapbox.mapboxsdk.style.expressions.Expression.literal;
import static com.mapbox.mapboxsdk.style.expressions.Expression.lt;
import static com.mapbox.mapboxsdk.style.expressions.Expression.toNumber;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconImage;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconSize;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textAllowOverlap;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textColor;
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;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textSize;
/**
* Use GeoJSON data with SymbolLayers to create a data clustering effect with icons rather than circles.
*/
public class ImageClusteringActivity extends AppCompatActivity implements OnMapReadyCallback {
private static final String CLUSTER_EARTHQUAKE_TRIANGLE_ICON_ID = "quake-triangle-icon-id";
private static final String SINGLE_EARTHQUAKE_TRIANGLE_ICON_ID = "single-quake-icon-id";
private static final String EARTHQUAKE_SOURCE_ID = "earthquakes";
private static final String POINT_COUNT = "point_count";
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_dds_image_clustering);
mapView = findViewById(R.id.mapView);
mapView.onCreate(savedInstanceState);
mapView.getMapAsync(this);
}
@Override
public void onMapReady(@NonNull MapboxMap mapboxMap) {
mapboxMap.setStyle(Style.LIGHT, new Style.OnStyleLoaded() {
@Override
public void onStyleLoaded(@NonNull Style style) {
// Disable any type of fading transition when icons collide on the map. This enhances the visual
// look of the data clustering together and breaking apart.
style.setTransition(new TransitionOptions(0, 0, false));
initLayerIcons(style);
addClusteredGeoJsonSource(style);
Toast.makeText(ImageClusteringActivity.this, R.string.zoom_map_in_and_out_instruction,
Toast.LENGTH_SHORT).show();
}
});
}
private void initLayerIcons(@NonNull Style loadedMapStyle) {
loadedMapStyle.addImage(SINGLE_EARTHQUAKE_TRIANGLE_ICON_ID, BitmapUtils.getBitmapFromDrawable(
getResources().getDrawable(R.drawable.single_quake_icon)));
loadedMapStyle.addImage(CLUSTER_EARTHQUAKE_TRIANGLE_ICON_ID, BitmapUtils.getBitmapFromDrawable(
getResources().getDrawable(R.drawable.earthquake_triangle)));
}
private void addClusteredGeoJsonSource(@NonNull Style loadedMapStyle) {
// Add a new source from the GeoJSON data and set the 'cluster' option to true.
try {
loadedMapStyle.addSource(
// Point to GeoJSON data. This example visualizes all M1.0+ earthquakes from
// 12/22/15 to 1/21/16 as logged by USGS' Earthquake hazards program.
new GeoJsonSource(EARTHQUAKE_SOURCE_ID,
new URI("https://www.mapbox.com/mapbox-gl-js/assets/earthquakes.geojson"),
new GeoJsonOptions()
.withCluster(true)
.withClusterMaxZoom(14)
.withClusterRadius(50)
)
);
} catch (URISyntaxException uriSyntaxException) {
Timber.e("Check the URL %s" , uriSyntaxException.getMessage());
}
SymbolLayer unclusteredSymbolLayer = new SymbolLayer("unclustered-points", EARTHQUAKE_SOURCE_ID).withProperties(
iconImage(SINGLE_EARTHQUAKE_TRIANGLE_ICON_ID),
iconSize(
division(
get("mag"), literal(4.0f)
)
)
);
unclusteredSymbolLayer.setFilter(has("mag"));
//Creating a SymbolLayer icon layer for single data/icon points
loadedMapStyle.addLayer(unclusteredSymbolLayer);
// Use the earthquakes GeoJSON source to create three point ranges.
int[] layers = new int[] {150, 20, 0};
for (int i = 0; i < layers.length; i++) {
//Add clusters' SymbolLayers images
SymbolLayer symbolLayer = new SymbolLayer("cluster-" + i, EARTHQUAKE_SOURCE_ID);
symbolLayer.setProperties(
iconImage(CLUSTER_EARTHQUAKE_TRIANGLE_ICON_ID)
);
Expression pointCount = toNumber(get(POINT_COUNT));
// Add a filter to the cluster layer that hides the icons based on "point_count"
symbolLayer.setFilter(
i == 0
? all(has(POINT_COUNT),
gte(pointCount, literal(layers[i]))
) : all(has(POINT_COUNT),
gte(pointCount, literal(layers[i])),
lt(pointCount, literal(layers[i - 1]))
)
);
loadedMapStyle.addLayer(symbolLayer);
}
//Add a SymbolLayer for the cluster data number point count
loadedMapStyle.addLayer(new SymbolLayer("count", EARTHQUAKE_SOURCE_ID).withProperties(
textField(Expression.toString(get(POINT_COUNT))),
textSize(12f),
textColor(Color.BLACK),
textIgnorePlacement(true),
// The .5f offset moves the data numbers down a little bit so that they're
// in the middle of the triangle cluster image
textOffset(new Float[] {0f, .5f}),
textAllowOverlap(true)
));
}
@Override
public void onStart() {
super.onStart();
mapView.onStart();
}
@Override
public void onResume() {
super.onResume();
mapView.onResume();
}
@Override
public void onPause() {
super.onPause();
mapView.onPause();
}
@Override
public void onStop() {
super.onStop();
mapView.onStop();
}
@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);
}
}
Was this page helpful?