MapView Polygon
This example shows how to add Polygons in MapView
-
Add Polygon from a set of Latlng
-
Query Visible Polygon in Rect
-
Set Polygon stroke
-
Set Polygon color

For all code examples, refer to Android Maps SDK Code Examples
activity_polygon.xml view source
1<?xml version="1.0" encoding="utf-8"?>
2<androidx.coordinatorlayout.widget.CoordinatorLayout 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 tools:context=".PolygonActivity">
8
9 <ai.nextbillion.maps.core.MapView
10 android:id="@+id/map_view"
11 android:layout_width="match_parent"
12 android:layout_height="match_parent"
13 app:nbmap_cameraTargetLat="53.5493093866953"
14 app:nbmap_cameraTargetLng="10.031835837897463"
15 app:nbmap_cameraZoom="10"
16 app:nbmap_uiAttribution="false" />
17
18 <androidx.core.widget.NestedScrollView
19 android:id="@+id/bottomSheet"
20 android:layout_width="match_parent"
21 android:layout_height="wrap_content"
22 android:background="@color/white"
23 app:behavior_hideable="true"
24 app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
25
26 <LinearLayout
27 android:layout_width="match_parent"
28 android:layout_height="wrap_content"
29 android:orientation="vertical">
30
31 <TextView
32 android:layout_width="68dp"
33 android:layout_height="1dp"
34 android:layout_gravity="center_horizontal"
35 android:layout_marginTop="13dp"
36 android:background="@drawable/nbmap_radius_10_grey_bg" />
37
38 <TextView
39 android:layout_width="wrap_content"
40 android:layout_height="wrap_content"
41 android:layout_marginTop="13dp"
42 android:paddingHorizontal="16dp"
43 android:text="@string/polygonSetting"
44 android:textColor="@color/black"
45 android:textSize="24sp" />
46
47 <ai.nextbillion.view.SettingSwitchView
48 android:id="@+id/pointEnable"
49 android:layout_width="match_parent"
50 android:layout_height="wrap_content"
51 android:layout_marginLeft="16dp"
52 android:layout_marginRight="16dp" />
53
54 <ai.nextbillion.view.ColorSelectorView
55 android:id="@+id/polygon_color"
56 android:layout_width="match_parent"
57 android:layout_height="wrap_content"
58 android:layout_marginTop="10dp" />
59
60 <ai.nextbillion.view.ColorSelectorView
61 android:id="@+id/polygon_stroke_Color"
62 android:layout_width="match_parent"
63 android:layout_height="wrap_content"
64 android:layout_marginTop="10dp" />
65
66 <ai.nextbillion.view.SliderBarView
67 android:id="@+id/lineWidth"
68 android:layout_width="match_parent"
69 android:layout_height="wrap_content"
70 android:layout_marginBottom="10dp" />
71
72 </LinearLayout>
73
74
75 </androidx.core.widget.NestedScrollView>
76
77 <com.google.android.material.floatingactionbutton.FloatingActionButton
78 android:id="@+id/fb_clean"
79 android:layout_width="wrap_content"
80 android:layout_height="wrap_content"
81 android:layout_gravity="right"
82 android:layout_marginTop="120dp"
83 android:layout_marginRight="16dp"
84 android:backgroundTint="@color/purple_200"
85 android:src="@android:drawable/ic_menu_close_clear_cancel"
86 android:tooltipText="Clean"
87 app:fabSize="mini" />
88
89 <TextView
90 android:id="@+id/fb_check"
91 android:layout_width="40dp"
92 android:layout_height="40dp"
93 android:layout_gravity="right"
94 android:layout_marginTop="170dp"
95 android:layout_marginRight="16dp"
96 android:background="@drawable/shape_circle_40_md_grey_400"
97 android:gravity="center"
98 android:text="@string/polygonStrokeCheck" />
99
100
101 <com.google.android.material.floatingactionbutton.FloatingActionButton
102 android:id="@+id/fb_expand"
103 android:layout_width="wrap_content"
104 android:layout_height="wrap_content"
105 android:layout_gravity="right|bottom"
106 android:layout_marginRight="16dp"
107 android:layout_marginBottom="56dp"
108 android:backgroundTint="@color/purple_200"
109 android:src="@android:drawable/ic_dialog_info"
110 android:tooltipText="Create"
111 android:visibility="gone"
112 app:fabSize="mini" />
113
114 <ImageView
115 android:id="@+id/iv_back"
116 android:layout_width="40dp"
117 android:layout_height="40dp"
118 android:layout_gravity="top|left"
119 android:layout_marginLeft="16dp"
120 android:layout_marginTop="16dp"
121 android:background="@drawable/circle_white_bg"
122 android:src="@drawable/icon_back"
123 app:tint="@color/color_back_icon" />
124
125</androidx.coordinatorlayout.widget.CoordinatorLayout>
PolygonActivity view source
1public class PolygonActivity extends AppCompatActivity implements View.OnClickListener {
2
3 private NextbillionMap mMap;
4 private MapView mapView;
5 private BottomSheetBehavior bottomSheetBehavior;
6 private Polygon polygon;
7 // for stroke
8 private Polyline polyline;
9
10 // polygon
11 private List<LatLng> polygonPoints = new ArrayList<>();
12 private List<Marker> polygonMarkers = new ArrayList<>();
13 private LatLngBounds latLngBounds;
14 private Marker clickMarker;
15
16 // menu
17 private ColorSelectorView polygonColorSelectorView;
18 private ColorSelectorView polygonStrokeColorSelectorView;
19 private SliderBarView polygonStrokeWidthSliderBarView;
20 private SettingSwitchView pointSettingSwitchView;
21 private FloatingActionButton fbInfoFloatingActionButton;
22 private TextView fbNoticeButton;
23
24 private String polygonColor = "#1E58A5";
25 private String polygonStrokeColor = "#FF0000";
26 private float stockWidth = 2;
27 private int polygonIndex = 1;
28 private boolean showPolygonVertex = true;
29
30 @Override
31 protected void onCreate(Bundle savedInstanceState) {
32 super.onCreate(savedInstanceState);
33 setContentView(R.layout.activity_polygon);
34 mapView = findViewById(R.id.map_view);
35 mapView.onCreate(savedInstanceState);
36 mapView.getMapAsync(this::onMapSync);
37 // button
38 findViewById(R.id.fb_clean).setOnClickListener(this);
39 fbInfoFloatingActionButton = findViewById(R.id.fb_expand);
40 fbInfoFloatingActionButton.setOnClickListener(this);
41 fbNoticeButton = findViewById(R.id.fb_check);
42 fbNoticeButton.setOnClickListener(this);
43 // menu
44 polygonColorSelectorView = findViewById(R.id.polygon_color);
45 polygonStrokeColorSelectorView = findViewById(R.id.polygon_stroke_Color);
46 polygonStrokeWidthSliderBarView = findViewById(R.id.lineWidth);
47 pointSettingSwitchView = findViewById(R.id.pointEnable);
48 findViewById(R.id.iv_back).setOnClickListener(this);
49 initData();
50 initBottomSheet();
51 }
52
53 private void initData() {
54 polygonColorSelectorView.setTitle(getResources().getString(R.string.polygonColor));
55 polygonColorSelectorView.initColor(polygonColor);
56 polygonStrokeColorSelectorView.setTitle(getResources().getString(R.string.polygonStrokeColor));
57 polygonStrokeColorSelectorView.initColor(polygonStrokeColor);
58 polygonStrokeWidthSliderBarView.setTitle(getResources().getString(R.string.polygonStrokeWidth));
59 polygonStrokeWidthSliderBarView.initSeekBar(2, 0, 20, "px", 1);
60 pointSettingSwitchView.setTitle(getResources().getString(R.string.polygon_point_show));
61 pointSettingSwitchView.defaultValue(true);
62 polygonColorSelectorView.setOnColorChangedListener(new ColorSelectorView.OnColorChangedListener() {
63 @Override
64 public void onColorChanged(@NonNull String color) {
65 polygonColor = color;
66 if (polygon != null) {
67 polygon.setFillColor(Color.parseColor(color));
68 }
69 }
70 });
71 polygonStrokeColorSelectorView.setOnColorChangedListener(new ColorSelectorView.OnColorChangedListener() {
72 @Override
73 public void onColorChanged(@NonNull String color) {
74 polygonStrokeColor = color;
75 int colorHex = Color.parseColor(color);
76 if (polyline != null) {
77 polyline.setColor(colorHex);
78 }
79 }
80 });
81 polygonStrokeWidthSliderBarView.setOnSliderChangedListener(new SliderBarView.OnSliderChangedListener() {
82 @Override
83 public void onSliderChanged(float value) {
84 stockWidth = value;
85 if (polyline != null) {
86 polyline.setWidth(value);
87 }
88 }
89 });
90
91 pointSettingSwitchView.setOnSwitchChangedLister(new SettingSwitchView.OnSwitchChangedLister() {
92 @Override
93 public void onSwitchChanged(boolean status) {
94 showPolygonVertex = status;
95 upDataMarkerBySwitchState();
96 }
97 });
98 }
99
100 private void upDataMarkerBySwitchState() {
101 if (polygonMarkers == null || polygonMarkers.isEmpty()) {
102 return;
103 }
104 for (Marker marker : polygonMarkers) {
105 marker.setIcon(IconFactory.getInstance(PolygonActivity.this)
106 .fromResource((showPolygonVertex || polygonPoints.size() < 3) ? R.mipmap.ic_blue_dot : R.mipmap.ic_marker_transparent));
107 }
108 }
109
110 private void onMapSync(NextbillionMap map) {
111 mMap = map;
112 addMapListener();
113 mMap.setStyle( Style.Builder().fromUri(StyleConstants.LIGHT))
114 }
115
116 private void addMapListener() {
117 mMap.addOnMapLongClickListener(new NextbillionMap.OnMapLongClickListener() {
118 @Override
119 public boolean onMapLongClick(@NonNull LatLng latLng) {
120 polygonPoints.add(latLng);
121 MarkerOptions markerOptions = new MarkerOptions()
122 .setTitle(String.valueOf(polygonIndex))
123 .position(latLng)
124 .setIcon(IconFactory.getInstance(PolygonActivity.this)
125 .fromResource((showPolygonVertex || polygonPoints.size() < 3) ? R.mipmap.ic_blue_dot : R.mipmap.ic_marker_transparent));
126 polygonMarkers.add(mMap.addMarker(markerOptions));
127 polygonIndex ++;
128 if (polygonPoints.size() > 2) {
129 createPolygon();
130 }
131 return false;
132 }
133 });
134 mMap.addOnMapClickListener(new NextbillionMap.OnMapClickListener() {
135 @Override
136 public boolean onMapClick(@NonNull LatLng latLng) {
137 if (clickMarker != null) {
138 mMap.removeMarker(clickMarker);
139 }
140 clickMarker = mMap.addMarker(latLng);
141 Point clickPoint = new Point(latLng.getLongitude(),latLng.getLatitude());
142 List<Point> polygonLngLatPoints = new ArrayList<>();
143 for (LatLng point : polygonPoints) {
144 polygonLngLatPoints.add(new Point(point.getLongitude(),point.getLatitude()));
145 }
146 boolean isPointInPolygon = PolygonUtils.isPointInPolygon(clickPoint,PolygonUtils.getPolygon(true,polygonLngLatPoints));
147 if (isPointInPolygon) {
148 Toast.makeText(PolygonActivity.this, "this point is in polygon", Toast.LENGTH_SHORT).show();
149 }
150 return false;
151 }
152 });
153 mMap.addOnMoveListener(new NextbillionMap.OnMoveListener() {
154 @Override
155 public void onMoveBegin(@NonNull MoveGestureDetector moveGestureDetector) {
156
157 }
158
159 @Override
160 public void onMove(@NonNull MoveGestureDetector moveGestureDetector) {
161
162 }
163
164 @Override
165 public void onMoveEnd(@NonNull MoveGestureDetector moveGestureDetector) {
166 boolean isPolygonInScreen = checkPolygonVisibility();
167 if (isPolygonInScreen) {
168 fbNoticeButton.setBackgroundResource(R.drawable.shape_circle_40_purple_200);
169 } else {
170 fbNoticeButton.setBackgroundResource(R.drawable.shape_circle_40_md_grey_400);
171 }
172 }
173 });
174 mMap.setOnMarkerClickListener(new NextbillionMap.OnMarkerClickListener() {
175 @Override
176 public boolean onMarkerClick(@NonNull Marker marker) {
177 if (marker == clickMarker) {
178 mMap.removeMarker(clickMarker);
179 clickMarker = null;
180 }
181 return false;
182 }
183 });
184 }
185
186 private boolean checkPolygonVisibility() {
187 if (mMap == null || mMap.getProjection() == null || latLngBounds == null) {
188 return false;
189 }
190 LatLngBounds isInScreen = mMap.getProjection().getVisibleRegion().latLngBounds.intersect(latLngBounds);
191 return isInScreen != null;
192 }
193
194 private void cleanPolygon() {
195 if (mMap == null || polygon == null) {
196 Toast.makeText(PolygonActivity.this, "No polygon need remove", Toast.LENGTH_SHORT).show();
197 return;
198 }
199 mMap.removePolygon(polygon);
200 polygon = null;
201 polygonPoints.clear();
202 latLngBounds = null;
203 // polygon marker
204 for (Marker marker : polygonMarkers) {
205 mMap.removeMarker(marker);
206 }
207 polygonMarkers.clear();
208
209 // line
210 if (polyline != null) {
211 mMap.removePolyline(polyline);
212 polyline = null;
213 }
214 }
215
216 private void createPolygon() {
217 // check map
218 if (mMap == null) {
219 Toast.makeText(this, getResources().getString(R.string.waite_notice), Toast.LENGTH_SHORT).show();
220 return;
221 }
222 // check data
223 if (polygonPoints.isEmpty()) {
224 Toast.makeText(this, getResources().getString(R.string.polygon_empty_point_notice), Toast.LENGTH_SHORT).show();
225 return;
226 }
227
228 if (polyline != null) {
229 mMap.removePolyline(polyline);
230 }
231
232 if (polygon != null) {
233 mMap.removePolygon(polygon);
234 }
235 // update marker show style
236 upDataMarkerBySwitchState();
237
238 // add polygon
239 latLngBounds = new LatLngBounds.Builder().includes(polygonPoints).build();
240 PolygonOptions polygonOptions = new PolygonOptions();
241 polygonOptions.addAll(polygonPoints)
242 .fillColor(Color.parseColor(polygonColor))
243 .strokeColor(Color.parseColor(polygonStrokeColor));
244 polygon = mMap.addPolygon(polygonOptions);
245
246 // add polygon stroke by polyline
247 List<LatLng> polylinePoints = new ArrayList<>(polygonPoints);
248 polylinePoints.add(polygonPoints.get(0));
249 polylinePoints.add(polygonPoints.get(1));
250 PolylineOptions polylineOptions = new PolylineOptions()
251 .addAll(polylinePoints)
252 .color(Color.parseColor(polygonStrokeColor))
253 .width(stockWidth);
254 polyline = mMap.addPolyline(polylineOptions);
255 // change check button color
256 fbNoticeButton.setBackgroundResource(R.drawable.shape_circle_40_purple_200);
257 }
258
259 private void initBottomSheet() {
260 bottomSheetBehavior = BottomSheetBehavior.from(findViewById(R.id.bottomSheet));
261 bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
262 bottomSheetBehavior.setPeekHeight((int) Utils.dpToPx(60));
263 bottomSheetBehavior.addBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
264 @Override
265 public void onStateChanged(@NonNull View bottomSheet, int newState) {
266 if (newState == BottomSheetBehavior.STATE_HIDDEN) {
267 fbInfoFloatingActionButton.setVisibility(View.VISIBLE);
268 } else {
269 fbInfoFloatingActionButton.setVisibility(View.GONE);
270 }
271 }
272
273 @Override
274 public void onSlide(@NonNull View bottomSheet, float slideOffset) {
275
276 }
277 });
278 }
279
280 ///////////////////////////////////////////////////////////////////////////
281 // Lifecycle
282 ///////////////////////////////////////////////////////////////////////////
283
284 @Override
285 protected void onStart() {
286 super.onStart();
287 mapView.onStart();
288 }
289
290 @Override
291 protected void onResume() {
292 super.onResume();
293 mapView.onResume();
294 }
295
296 @Override
297 protected void onPause() {
298 super.onPause();
299 mapView.onPause();
300 }
301
302 @Override
303 protected void onStop() {
304 super.onStop();
305 mapView.onStop();
306 }
307
308 @Override
309 protected void onSaveInstanceState(@NonNull Bundle outState) {
310 super.onSaveInstanceState(outState);
311 mapView.onSaveInstanceState(outState);
312 }
313
314 @Override
315 protected void onDestroy() {
316 super.onDestroy();
317 mapView.onDestroy();
318 }
319
320 @Override
321 public void onLowMemory() {
322 super.onLowMemory();
323 mapView.onLowMemory();
324 }
325
326 @Override
327 public void onClick(View v) {
328 if (v.getId() == R.id.fb_clean) {
329 cleanPolygon();
330 } else if (v.getId() == R.id.fb_expand) {
331 bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
332 } else if (v.getId() == R.id.fb_check) {
333 boolean isPolygonInScreen = checkPolygonVisibility();
334 String message = getResources().getString(R.string.polygin_visibility_notice);
335 if (!isPolygonInScreen) {
336 message = getResources().getString(R.string.polygon_invisibility_notice);
337 }
338 Toast.makeText(PolygonActivity.this, message, Toast.LENGTH_SHORT).show();
339 } else if (v.getId() == R.id.iv_back) {
340 finish();
341 }
342 }
343}
initData
-
Initialises the data for the polygon activity.
-
Sets up various UI components such as color selectors, slider bars, and switch views. It also defines listeners for these components to handle user interactions and update the corresponding data values.
addMapListener
-
Adds listeners to the map.
-
Sets up event listeners for various map interactions such as long clicks, clicks, moves, and marker clicks. These listeners perform actions such as adding markers, creating polygons, checking polygon visibility, and removing markers and polygons.
checkPolygonVisibility
- Checks if the polygon is currently visible on the map. It relies on the map's projection and the bounds of the polygon to determine if the polygon is within the visible region of the map. It returns a boolean value indicating the visibility status of the polygon.
createPolygon
- Creates a polygon on the map. It checks if the necessary data, such as the map and polygon points, are available. It removes any existing polyline and polygon from the map. It updates the marker show style based on the switch state. Then it adds a polygon and a polyline to the map using the provided polygon points, colors, and stroke width.
cleanPolygon
- Removes the polygon and related markers from the map. It checks if the map and polygon are available and removes them from the map. It also clears the polygon points, marker list, and polyline if they exist.
These methods are part of the PolygonActivity class and are used to initialize and manage the polygon functionality in the corresponding Android activity.
The code also includes various lifecycle methods (onStart, onResume, onPause, onStop, onSaveInstanceState, onDestroy, onLowMemory) that should be implemented when using the MapView to properly manage its lifecycle and handle configuration changes.
Note: The code uses the Nextbillion Maps SDK, which provides map-related functionalities. It also utilizes the LatLng class to represent geography.