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

MapView Polygon

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

1
public 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.

© 2024 NextBillion.ai all rights reserved.