In this page

Custom Polygon Cluster

这篇文档目前尚未提供译文,将以原文展示。

This example shows how to add Polygon Cluster in MapView

  1. Add Cluster from GeoJson

  2. Aggregate a large number of coordinate points

For all code examples, refer to Android Maps SDK Code Examples

activity_polygon_cluster.xml view source

1<?xml version="1.0" encoding="utf-8"?>
2<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
3    xmlns:app="http://schemas.android.com/apk/res-auto"
4    xmlns:tools="http://schemas.android.com/tools"
5    android:layout_width="match_parent"
6    android:layout_height="match_parent"
7    android:orientation="vertical"
8    tools:context=".PolygonActivity">
9
10    <ai.nextbillion.maps.core.MapView
11        android:id="@id/map_view"
12        android:layout_width="match_parent"
13        android:layout_height="match_parent"
14        app:nbmap_uiAttribution="false"
15        app:nbmap_cameraTargetLat="1.3383500934590808"
16        app:nbmap_cameraTargetLng="103.80586766146754"
17        app:nbmap_cameraZoom="11" />
18
19    <com.google.android.material.floatingactionbutton.FloatingActionButton
20        android:id="@+id/fab_route"
21        android:layout_width="wrap_content"
22        android:layout_height="wrap_content"
23        android:layout_alignParentBottom="true"
24        android:layout_alignParentEnd="true"
25        android:layout_alignParentRight="true"
26        android:layout_marginBottom="@dimen/fab_margin"
27        android:layout_marginRight="@dimen/fab_margin"
28        android:layout_marginEnd="@dimen/fab_margin"
29        android:src="@drawable/ic_directions_bus_black"
30        app:backgroundTint="@android:color/white" />
31
32    <com.google.android.material.floatingactionbutton.FloatingActionButton
33        android:id="@+id/fab_style"
34        android:layout_width="wrap_content"
35        android:layout_height="wrap_content"
36        android:layout_above="@id/fab_route"
37        android:layout_alignParentEnd="true"
38        android:layout_alignParentRight="true"
39        android:layout_marginBottom="@dimen/fab_margin"
40        android:layout_marginRight="@dimen/fab_margin"
41        android:src="@drawable/ic_layers"
42        android:layout_marginEnd="@dimen/fab_margin"
43        app:backgroundTint="@color/purple_200"
44        android:visibility="gone"/>
45
46    <ImageView
47        android:id="@+id/back_ib"
48        android:layout_width="40dp"
49        android:layout_height="40dp"
50        android:layout_marginLeft="12dp"
51        android:layout_marginTop="30dp"
52        android:background="@drawable/circle_white_bg"
53        android:src="@drawable/icon_back"
54        app:tint="@color/color_back_icon"/>
55
56</RelativeLayout>

PolygonClusterActivity view source

1public class PolygonClusterActivity extends AppCompatActivity implements OnMapReadyCallback, NextbillionMap.OnMapClickListener, View.OnClickListener {
2
3   private static final String TAG = "PolygonClusterActivity";
4   public static final String SOURCE_ID = "bus_stop";
5   public static final String SOURCE_ID_CLUSTER = "bus_stop_cluster";
6   public static final String URL_BUS_ROUTES = "https://raw.githubusercontent.com/cheeaun/busrouter-sg/master/data/2/bus-stops.geojson";
7   public static final String LAYER_ID = "stops_layer";
8   private static final String TAXI = "taxi";
9   private ImageView backBtn;
10   private MapView mapView;
11   private NextbillionMap nextbillionMap;
12
13   private FloatingActionButton styleFab;
14   private FloatingActionButton routeFab;
15
16   private CircleLayer layer;
17   private GeoJsonSource source;
18
19   private int currentStyleIndex = 0;
20   private boolean isLoadingStyle = true;
21
22   @Override
23   protected void onCreate(Bundle savedInstanceState) {
24       super.onCreate(savedInstanceState);
25       setContentView(R.layout.activity_polygon_cluster);
26       mapView = findViewById(R.id.map_view);
27       backBtn = findViewById(R.id.back_ib);
28       mapView.onCreate(savedInstanceState);
29       mapView.getMapAsync(this);
30
31       backBtn.setOnClickListener(new View.OnClickListener() {
32           @Override
33           public void onClick(View v) {
34               finish();
35           }
36       });
37   }
38
39   @Override
40   public void onMapReady(@NonNull NextbillionMap nbMap) {
41       this.nextbillionMap = nbMap;
42       mapView.addOnDidFinishLoadingStyleListener(() -> {
43           Style style = nextbillionMap.getStyle();
44           style.addImage(TAXI,(BitmapDrawable)getResources().getDrawable(R.mipmap.beat_taxi));
45           addBusStopSource(style);
46           addBusStopCircleLayer(style);
47           initFloatingActionButtons();
48           isLoadingStyle = false;
49       });
50   }
51
52   @Override
53   public boolean onMapClick(@NonNull LatLng latLng) {
54
55       return false;
56   }
57
58
59   private void addBusStopSource(Style style) {
60       try {
61           source = new GeoJsonSource(SOURCE_ID, new URI(URL_BUS_ROUTES));
62       } catch (URISyntaxException exception) {
63           Log.e(TAG, "That's not an url... ");
64       }
65       style.addSource(source);
66   }
67
68   private void addBusStopCircleLayer(Style style) {
69       layer = new CircleLayer(LAYER_ID, SOURCE_ID);
70       layer.setProperties(
71               circleColor(Color.parseColor("#FF0000")),
72               circleRadius(2.0f)
73       );
74       style.addLayerBelow(layer, "waterway-label");
75   }
76
77   private void initFloatingActionButtons() {
78       routeFab = findViewById(R.id.fab_route);
79       routeFab.setColorFilter(ContextCompat.getColor(PolygonClusterActivity.this, R.color.purple_200));
80       routeFab.setOnClickListener(PolygonClusterActivity.this);
81
82       styleFab = findViewById(R.id.fab_style);
83       styleFab.setOnClickListener(PolygonClusterActivity.this);
84   }
85
86   @Override
87   public void onClick(View view) {
88       if (isLoadingStyle) {
89           return;
90       }
91
92       if (view.getId() == R.id.fab_route) {
93           showBusCluster();
94       } else if (view.getId() == R.id.fab_style) {
95           changeMapStyle();
96       }
97   }
98
99   private void showBusCluster() {
100       removeFabs();
101       removeOldSource();
102       addClusteredSource();
103   }
104
105   private void removeOldSource() {
106       nextbillionMap.getStyle().removeSource(SOURCE_ID);
107       nextbillionMap.getStyle().removeLayer(LAYER_ID);
108   }
109
110   private void addClusteredSource() {
111       try {
112           nextbillionMap.getStyle().addSource(
113                   new GeoJsonSource(SOURCE_ID_CLUSTER,
114                           new URI(URL_BUS_ROUTES),
115                           new GeoJsonOptions()
116                                   .withCluster(true)
117                                   .withClusterMaxZoom(14)
118                                   .withClusterRadius(50)
119                   )
120           );
121       } catch (URISyntaxException malformedUrlException) {
122           Log.e(TAG, "That's not an url... ");
123       }
124
125       // Add unclustered layer
126       int[][] layers = new int[][]{
127               new int[]{150, ResourcesCompat.getColor(getResources(), R.color.purple_200, getTheme())},
128               new int[]{20, ResourcesCompat.getColor(getResources(), R.color.colorAccent, getTheme())},
129               new int[]{0, ResourcesCompat.getColor(getResources(), R.color.color_4158ce, getTheme())}
130       };
131
132       SymbolLayer unclustered = new SymbolLayer("unclustered-points", SOURCE_ID_CLUSTER);
133       unclustered.setProperties(
134               iconImage(TAXI)
135       );
136
137       nextbillionMap.getStyle().addLayer(unclustered);
138
139       for (int i = 0; i < layers.length; i++) {
140           // Add some nice circles
141           CircleLayer circles = new CircleLayer("cluster-" + i, SOURCE_ID_CLUSTER);
142           circles.setProperties(
143                   circleColor(layers[i][1]),
144                   circleRadius(18f)
145           );
146
147           Expression pointCount = toNumber(get("point_count"));
148           circles.setFilter(
149                   i == 0
150                           ? all(has("point_count"),
151                           gte(pointCount, literal(layers[i][0]))
152                   ) : all(has("point_count"),
153                           gt(pointCount, literal(layers[i][0])),
154                           lt(pointCount, literal(layers[i - 1][0]))
155                   )
156           );
157           nextbillionMap.getStyle().addLayer(circles);
158       }
159
160       // Add the count labels
161       SymbolLayer count = new SymbolLayer("count", SOURCE_ID_CLUSTER);
162       count.setProperties(
163               textField(Expression.toString(get("point_count"))),
164               textSize(12f),
165               textColor(Color.WHITE),
166               textIgnorePlacement(true),
167               textAllowOverlap(true)
168       );
169       nextbillionMap.getStyle().addLayer(count);
170   }
171
172   private void removeFabs() {
173       routeFab.setVisibility(View.GONE);
174       styleFab.setVisibility(View.GONE);
175   }
176
177   private void changeMapStyle() {
178       isLoadingStyle = true;
179       removeBusStop();
180       loadNewStyle();
181   }
182
183   private void removeBusStop() {
184       nextbillionMap.getStyle().removeLayer(layer);
185       nextbillionMap.getStyle().removeSource(source);
186   }
187
188   private void loadNewStyle() {
189       nextbillionMap.setStyle(new Style.Builder().fromUri(getNextStyle()));
190   }
191
192   private void addBusStop() {
193       nextbillionMap.getStyle().addLayer(layer);
194       nextbillionMap.getStyle().addSource(source);
195   }
196
197   private String getNextStyle() {
198       currentStyleIndex++;
199       if (currentStyleIndex == Data.STYLES.length) {
200           currentStyleIndex = 0;
201       }
202       return Data.STYLES[currentStyleIndex];
203   }
204
205
206   ///////////////////////////////////////////////////////////////////////////
207   // Lifecycle
208   ///////////////////////////////////////////////////////////////////////////
209
210   @Override
211   protected void onStart() {
212       super.onStart();
213       mapView.onStart();
214   }
215
216   @Override
217   protected void onResume() {
218       super.onResume();
219       mapView.onResume();
220   }
221
222   @Override
223   protected void onPause() {
224       super.onPause();
225       mapView.onPause();
226   }
227
228   @Override
229   protected void onStop() {
230       super.onStop();
231       mapView.onStop();
232   }
233
234   @Override
235   protected void onSaveInstanceState(@NonNull Bundle outState) {
236       super.onSaveInstanceState(outState);
237       mapView.onSaveInstanceState(outState);
238   }
239
240   @Override
241   protected void onDestroy() {
242       super.onDestroy();
243       mapView.onDestroy();
244   }
245
246   @Override
247   public void onLowMemory() {
248       super.onLowMemory();
249       mapView.onLowMemory();
250   }
251
252   private static class Data {
253       private static final String[] STYLES = new String[]{
254               Style.NBMAP_STREETS,
255               Style.OUTDOORS,
256               Style.LIGHT,
257               Style.DARK,
258               Style.SATELLITE,
259               Style.SATELLITE_STREETS
260       };
261   }
262}
  1. onMapReady: This method is called when the map is ready to be used. It initializes the NextbillionMap object and adds listeners to the map-style loading event. Once the style finishes loading, it adds the bus stop source, bus stop circle layer and sets up the floating action buttons.

  2. addBusStopSource: This method adds a GeoJsonSource to the map style, representing the bus stop data source. It creates a GeoJsonSource object with the provided URL to a GeoJSON file containing bus stop information and adds it to the map style.

  3. addBusStopCircleLayer: This method adds a CircleLayer to the map style, representing the bus stop markers on the map. It creates a CircleLayer object with the specified layer ID and source ID, sets properties for the circle color and radius and adds the layer to the map style.

  4. addClusteredSource: This method adds a clustered source to the map style, representing a clustering of bus stops. It creates a GeoJsonSource object with the provided URL to a GeoJSON file, enables clustering with specified cluster options and adds the source to the map style. It also adds clustered and unclustered layers to represent the clusters and individual bus stops on the map.

  5. showBusCluster: This method is called when the user clicks on the "Route" floating action button. It removes the existing floating action buttons, removes the old bus stop source and layer from the map style, and adds a clustered source with clustered and unclustered layers to represent the bus stop clusters on the map.

没找到你要找的内容?