MapView Polyline

This example shows how to add PolyLines in MapView

  • Add Polyline from a set of Latlng

  • Set Polyline stroke

  • Set polyline colour

MapView Polyline

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

activity_polyline.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_uiAttribution="false"
14
app:nbmap_cameraTargetLat="53.5511"
15
app:nbmap_cameraTargetLng="9.9937"
16
app:nbmap_cameraZoom="12.5" />
17
18
<ImageView
19
android:id="@+id/iv_back"
20
android:layout_width="40dp"
21
android:layout_height="40dp"
22
android:layout_marginLeft="16dp"
23
android:layout_marginTop="16dp"
24
android:background="@drawable/circle_white_bg"
25
android:src="@drawable/icon_back"
26
app:tint="@color/color_back_icon"/>
27
28
<androidx.core.widget.NestedScrollView
29
android:id="@+id/bottomSheet"
30
android:layout_width="match_parent"
31
android:layout_height="wrap_content"
32
android:background="@color/white"
33
app:behavior_hideable="true"
34
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
35
36
<LinearLayout
37
android:layout_width="match_parent"
38
android:layout_height="wrap_content"
39
android:orientation="vertical">
40
41
<TextView
42
android:layout_width="68dp"
43
android:layout_height="4dp"
44
android:layout_gravity="center_horizontal"
45
android:layout_marginTop="13dp"
46
android:background="@drawable/nbmap_radius_10_grey_bg"
47
/>
48
49
<TextView
50
android:layout_width="wrap_content"
51
android:layout_height="wrap_content"
52
android:textColor="@color/black"
53
android:textSize="24sp"
54
android:paddingHorizontal="16dp"
55
android:layout_marginTop="13dp"
56
android:text="@string/polyLine"/>
57
58
<ai.nextbillion.view.SettingSwitchView
59
android:id="@+id/lineEnable"
60
android:visibility="gone"
61
android:layout_width="match_parent"
62
android:layout_height="wrap_content"/>
63
64
<ai.nextbillion.view.ColorSelectorView
65
android:id="@+id/lineColor"
66
android:layout_marginTop="10dp"
67
android:layout_width="match_parent"
68
android:layout_height="wrap_content"/>
69
70
<ai.nextbillion.view.SliderBarView
71
android:id="@+id/lineWidth"
72
android:layout_marginBottom="10dp"
73
android:layout_width="match_parent"
74
android:layout_height="wrap_content"/>
75
76
</LinearLayout>
77
78
79
</androidx.core.widget.NestedScrollView>
80
81
<LinearLayout
82
android:layout_width="match_parent"
83
android:layout_height="wrap_content"
84
app:layout_anchor="@id/bottomSheet"
85
android:paddingHorizontal="10dp"
86
android:paddingBottom="60dp"
87
android:orientation="horizontal" >
88
89
<Button
90
android:id="@+id/removeSingleLine"
91
android:layout_width="wrap_content"
92
android:layout_height="wrap_content"
93
android:textAllCaps="false"
94
android:textColor="@color/white"
95
android:text="@string/remove_single_line"
96
android:visibility="gone"
97
/>
98
99
<Space
100
android:layout_width="0dp"
101
android:layout_height="wrap_content"
102
android:layout_weight="1"/>
103
104
<Button
105
android:id="@+id/removeAllLine"
106
android:layout_width="wrap_content"
107
android:layout_height="wrap_content"
108
android:textColor="@color/white"
109
android:textAllCaps="false"
110
android:text="@string/remove_all_line"
111
android:visibility="gone"
112
/>
113
114
</LinearLayout>
115
116
</androidx.coordinatorlayout.widget.CoordinatorLayout>

PolylineActivity view source

1
class PolylineActivity : AppCompatActivity() {
2
private lateinit var mMap: NextbillionMap;
3
private lateinit var binding: ActivityPolylineBinding;
4
private lateinit var bottomSheetBehavior: BottomSheetBehavior<*>;
5
private var points: MutableList<LatLng> = mutableListOf();
6
private var polyline: Polyline? = null;
7
private var originMarker: Marker? = null;
8
private var lineColor = "#1E58A5";
9
private var lineWidth = 5f;
10
11
override fun onCreate(savedInstanceState: Bundle?) {
12
super.onCreate(savedInstanceState)
13
binding = ActivityPolylineBinding.inflate(LayoutInflater.from(this))
14
setContentView(binding.root)
15
binding.mapView.getMapAsync { map: NextbillionMap -> onMapSync(map) }
16
initBottomSheet()
17
binding.ivBack.setOnClickListener {
18
finish()
19
}
20
}
21
22
private fun onMapSync(map: NextbillionMap) {
23
showSnackBar()
24
mMap = map
25
mMap.addOnMapLongClickListener {
26
applyPolyline(it)
27
false
28
}
29
map.setStyle( Style.Builder().fromUri(StyleConstants.LIGHT))
30
}
31
32
private fun showSnackBar() {
33
val snackBar = Snackbar.make(binding.root,getString(R.string.snack_long_press),Snackbar.LENGTH_LONG)
34
snackBar.setTextColor(Color.WHITE)
35
snackBar.view.setBackgroundColor(Color.BLACK)
36
snackBar.show()
37
}
38
39
private fun applyPolyline(latLng: LatLng) {
40
points.add(latLng)
41
42
if (points.size < 2) {
43
originMarker = mMap.addMarker(latLng)
44
} else {
45
originMarker?.let {
46
mMap.removeMarker(it)
47
originMarker = null
48
}
49
recreatePoleLine()
50
}
51
52
updateFloatButtonStatus()
53
}
54
55
private fun removeSingleLine() {
56
if (points.size <= 2) {
57
removeAllPolyline()
58
return
59
} else {
60
points.removeLast()
61
recreatePoleLine()
62
}
63
}
64
65
private fun recreatePoleLine() {
66
polyline?.let {
67
mMap.removePolyline(it)
68
}
69
polyline = mMap.addPolyline(points, lineColor)
70
polyline?.width = lineWidth
71
animateBound(1f)
72
}
73
74
private fun removeAllPolyline() {
75
points.clear()
76
mMap.clear()
77
polyline = null
78
updateFloatButtonStatus()
79
}
80
81
private fun updateFloatButtonStatus() {
82
if (points.size < 2) {
83
bottomSheetBehavior.isHideable = true
84
bottomSheetBehavior.state = BottomSheetBehavior.STATE_HIDDEN
85
86
binding.removeSingleLine.visibility = View.GONE
87
binding.removeAllLine.visibility = View.GONE
88
} else {
89
bottomSheetBehavior.isHideable = false
90
bottomSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED
91
bottomSheetBehavior.peekHeight = dpToPx(this, 60.0)
92
93
binding.removeSingleLine.visibility = View.VISIBLE
94
binding.removeAllLine.visibility = View.VISIBLE
95
}
96
97
}
98
99
100
private fun initBottomSheet() {
101
binding.removeSingleLine.setOnClickListener {
102
removeSingleLine()
103
}
104
105
binding.removeAllLine.setOnClickListener {
106
removeAllPolyline()
107
}
108
109
binding.lineColor.setTitle(getString(R.string.lineColor))
110
binding.lineColor.initColor(lineColor)
111
binding.lineColor.setOnColorChangedListener(object : ColorSelectorView.OnColorChangedListener {
112
override fun onColorChanged(color: String) {
113
lineColor = color
114
polyline?.color = Color.parseColor(color)
115
}
116
})
117
118
binding.lineWidth.setTitle(getString(R.string.lineWidth))
119
binding.lineWidth.initSeekBar(5f, 1f, 15f, "", 1f)
120
binding.lineWidth.setOnSliderChangedListener(object : SliderBarView.OnSliderChangedListener {
121
override fun onSliderChanged(value: Float) {
122
lineWidth = value
123
polyline?.width = value
124
}
125
})
126
127
bottomSheetBehavior = BottomSheetBehavior.from(binding.bottomSheet)
128
bottomSheetBehavior.state = BottomSheetBehavior.STATE_HIDDEN
129
bottomSheetBehavior.addBottomSheetCallback(object :
130
BottomSheetBehavior.BottomSheetCallback() {
131
override fun onStateChanged(bottomSheet: View, newState: Int) {}
132
override fun onSlide(bottomSheet: View, slideOffset: Float) {
133
animateBound(slideOffset)
134
}
135
})
136
137
}
138
139
140
private fun animateBound(slideOffset: Float) {
141
if (points.size < 2) {
142
return
143
}
144
val bounds = LatLngBounds.Builder().includes(points).build()
145
animateCameraBox(bounds, createPadding(this, slideOffset))
146
}
147
148
private fun animateCameraBox(
149
bounds: LatLngBounds,
150
padding: IntArray
151
) {
152
val position = mMap.getCameraForLatLngBounds(bounds, padding)
153
position?.let {
154
val cameraUpdate = CameraUpdateFactory.newCameraPosition(position)
155
mMap.animateCamera(cameraUpdate)
156
}
157
158
}
159
160
private fun createPadding(context: Context, cameraBoundFactor: Float): IntArray {
161
val horPadding = dpToPx(context, 50.0)
162
val topPadding = dpToPx(context, 50.0)
163
val bottomPadding = (dpToPx(context, 260.0) * cameraBoundFactor).toInt()
164
return intArrayOf(horPadding, topPadding, horPadding, bottomPadding)
165
}
166
167
///////////////////////////////////////////////////////////////////////////
168
// Lifecycle
169
///////////////////////////////////////////////////////////////////////////
170
override fun onStart() {
171
super.onStart()
172
binding.mapView.onStart()
173
}
174
175
override fun onResume() {
176
super.onResume()
177
binding.mapView.onResume()
178
}
179
180
override fun onPause() {
181
super.onPause()
182
binding.mapView.onPause()
183
}
184
185
override fun onStop() {
186
super.onStop()
187
binding.mapView.onStop()
188
}
189
190
override fun onSaveInstanceState(outState: Bundle) {
191
super.onSaveInstanceState(outState)
192
binding.mapView.onSaveInstanceState(outState)
193
}
194
195
override fun onDestroy() {
196
super.onDestroy()
197
binding.mapView.onDestroy()
198
}
199
200
override fun onLowMemory() {
201
super.onLowMemory()
202
binding.mapView.onLowMemory()
203
}
204
205
fun dpToPx(context: Context, dp: Double): Int {
206
val scale = context.resources.displayMetrics.density
207
return (dp * scale + 0.5f).toInt()
208
}
209
}
  • applyPolyline(latLng: LatLng)

    • This method is used to apply a polyline on the map.

    • It takes a LatLng object as a parameter, representing a point on the polyline.

    • First, the point is added to the points list.

    • If the size of the points list is less than 2, it means there are not enough points to draw a polyline, so a marker is added to the map.

    • If the size of the points list is greater than or equal to 2, it means there are enough points to draw a polyline, so the previous polyline is removed and a new one is created.

    • Finally, the updateFloatButtonStatus method is called to update the status of the floating button.

  • removeSingleLine

    • This method is used to remove the last added point, thereby deleting the last segment of the polyline.

    • If the size of the points list is less than or equal to 2, it means there are not enough points to draw a polyline, so the removeAllPolyline method is called to remove all polylines and return.

    • If the size of the points list is greater than 2, the last point is removed, and a new polyline is created.

    • Finally, the updateFloatButtonStatus method is called to update the status of the floating button.

  • recreatePolyline

    • This method is used to recreate the polyline.

    • First, the previous polyline (if it exists) is removed.

    • A new polyline is created based on the points in the points list and assigned to the polyline variable.

    • The color of the polyline is set to the color specified by the lineColor variable.

    • The width of the polyline is set to the width specified by the lineWidth variable.

    • The animateBound method is called to fit the polyline's position.

  • removeAllPolyline

    • This method is used to remove all polylines.

    • The points list is cleared.

    • All markers and polylines on the map are cleared.

    • The polyline variable is set to null.

    • The updateFloatButtonStatus method is called to update the status of the floating button.

  • animateBound

    • This method is used to adjust the map's display area based on the position of the polyline.

    • If the size of the points list is less than 2, it means there are not enough points to draw a polyline, so it returns directly.

    • A LatLngBounds object is created based on the points in the points list.

    • The animateCameraBox method is called to animate the map's display area.

  • animateCameraBox

    • This method is used to animate the map's display area to include the specified boundary range.

    • A CameraPosition object is created based on the boundary range and padding.

    • If the CameraPosition object is not null, a CameraUpdate object is created, and the animateCamera method is used to animate the map's display area.

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.