Skip to content

Commit fd2c6a0

Browse files
committed
feat(library): add flexible polyline encoding and decoding
Adds FlexiblePolylineUtil to the library module, alongside the existing PolyUtil codec. The format supports configurable precision, a header that carries the precision with the data, and an optional third value per point. Also adds a demo activity exercising the decoder, registered under the Geometry category.
1 parent 82faf3f commit fd2c6a0

6 files changed

Lines changed: 884 additions & 0 deletions

File tree

‎demo/src/main/AndroidManifest.xml‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,9 @@
6565
<activity
6666
android:name=".PolyDecodeDemoActivity"
6767
android:exported="true" />
68+
<activity
69+
android:name=".FlexiblePolylineDemoActivity"
70+
android:exported="true" />
6871
<activity
6972
android:name=".PolySimplifyDemoActivity"
7073
android:exported="true" />
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
/*
2+
* Copyright 2026 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.google.maps.android.utils.demo
17+
18+
import android.widget.Toast
19+
import com.google.android.gms.maps.CameraUpdateFactory
20+
import com.google.android.gms.maps.model.BitmapDescriptorFactory
21+
import com.google.android.gms.maps.model.LatLng
22+
import com.google.android.gms.maps.model.MarkerOptions
23+
import com.google.android.gms.maps.model.PolylineOptions
24+
import com.google.maps.android.FlexiblePolylineUtil
25+
26+
/**
27+
* Demonstrates [FlexiblePolylineUtil] by decoding a flexible polyline and drawing it.
28+
*
29+
* [ENCODED_WALKWAY] encodes ten points along the Sydney Harbour Bridge walkway at seven
30+
* decimal places, with an altitude in metres at one decimal place. [FlexiblePolyline.toLatLngs]
31+
* supplies the path for [PolylineOptions]. The altitude is not accepted by [PolylineOptions],
32+
* so this demo reports it in the snippet of three markers instead.
33+
*/
34+
class FlexiblePolylineDemoActivity : BaseDemoActivity() {
35+
override fun startDemo(isRestore: Boolean) {
36+
val map = map ?: return
37+
val polyline = FlexiblePolylineUtil.decode(ENCODED_WALKWAY)
38+
val path = polyline.toLatLngs()
39+
40+
map.addPolyline(
41+
PolylineOptions()
42+
.addAll(path)
43+
.width(POLYLINE_WIDTH)
44+
.color(POLYLINE_COLOR),
45+
)
46+
47+
// Mark the first, highest and last points, and report their altitude as text.
48+
val withAltitude = polyline.points.filter { it.thirdDimensionValue != null }
49+
val labelled =
50+
listOfNotNull(
51+
withAltitude.firstOrNull()?.let { R.string.flexible_polyline_start to it },
52+
withAltitude.maxByOrNull { point -> point.thirdDimensionValue!! }
53+
?.let { R.string.flexible_polyline_highest to it },
54+
withAltitude.lastOrNull()?.let { R.string.flexible_polyline_end to it },
55+
)
56+
for ((titleResId, point) in labelled) {
57+
map.addMarker(
58+
MarkerOptions()
59+
.position(point.toLatLng())
60+
.title(getString(titleResId))
61+
.snippet(
62+
getString(
63+
R.string.flexible_polyline_altitude,
64+
point.thirdDimensionValue!!,
65+
),
66+
)
67+
.icon(
68+
BitmapDescriptorFactory.defaultMarker(
69+
BitmapDescriptorFactory.HUE_AZURE,
70+
),
71+
),
72+
)
73+
}
74+
75+
Toast.makeText(
76+
this,
77+
getString(
78+
R.string.flexible_polyline_summary,
79+
polyline.points.size,
80+
polyline.precision,
81+
polyline.thirdDimension.name.lowercase(),
82+
),
83+
Toast.LENGTH_LONG,
84+
).show()
85+
86+
if (!isRestore) {
87+
map.moveCamera(CameraUpdateFactory.newLatLngZoom(CAMERA_TARGET, CAMERA_ZOOM))
88+
}
89+
}
90+
91+
private companion object {
92+
/**
93+
* The Sydney Harbour Bridge walkway, encoded at precision 7 with an altitude in metres
94+
* at one decimal place. The alphabet is URL safe, so the string needs no escaping in
95+
* source, a URL or a JSON field. The Google format sample in [PolyDecodeDemoActivity]
96+
* contains four backslashes that are doubled in the Java literal.
97+
*/
98+
const val ENCODED_WALKWAY =
99+
"BnF__k0lU4kokk6CgFnzQnqDyGnzQvwD6HnzQvwDmHnzQvwDiEnzQvwDyBnzQvwDnBnzQvwDpFnzQvwDtIn" +
100+
"zQvwDtI"
101+
102+
val CAMERA_TARGET = LatLng(-33.85343, 151.21083)
103+
const val CAMERA_ZOOM = 15f
104+
const val POLYLINE_WIDTH = 12f
105+
const val POLYLINE_COLOR = 0xFF1A73E8.toInt()
106+
}
107+
}

‎demo/src/main/java/com/google/maps/android/utils/demo/MainActivity.kt‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ class MainActivity : ComponentActivity() {
121121
R.string.category_geometry,
122122
listOf(
123123
Demo(R.string.demo_title_poly_decode, PolyDecodeDemoActivity::class.java),
124+
Demo(R.string.demo_title_flexible_polyline, FlexiblePolylineDemoActivity::class.java),
124125
Demo(R.string.demo_title_poly_simplify, PolySimplifyDemoActivity::class.java),
125126
Demo(R.string.demo_title_polyline_progress, PolylineProgressDemoActivity::class.java),
126127
Demo(R.string.demo_title_spherical_distance, DistanceDemoActivity::class.java),

‎demo/src/main/res/values/strings.xml‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@
6969
<string name="demo_title_poly_decode">PolyUtil.decode</string>
7070
<string name="demo_title_poly_simplify">PolyUtil.simplify</string>
7171
<string name="demo_title_polyline_progress">Polyline Progress</string>
72+
<string name="demo_title_flexible_polyline">FlexiblePolylineUtil.decode</string>
7273
<string name="demo_title_spherical_distance">SphericalUtil.computeDistanceBetween</string>
7374

7475
<!-- Demo Titles: Utilities -->
@@ -143,4 +144,11 @@ Demonstrates how to dynamically control the visual layering (stacking order) of
143144
<b>Tie Resolution:</b><br/>
144145
• When two shapes of the same type share identical z-index values, the Google Maps SDK resolves their stacking order based on <b>insertion (creation) order</b>.
145146
]]></string>
147+
148+
<!-- Flexible Polyline demo -->
149+
<string name="flexible_polyline_summary">Points: %1$d · precision: %2$d · third dimension: %3$s</string>
150+
<string name="flexible_polyline_altitude">Altitude %1$.1f m (not rendered as height)</string>
151+
<string name="flexible_polyline_start">Start</string>
152+
<string name="flexible_polyline_highest">Highest point</string>
153+
<string name="flexible_polyline_end">End</string>
146154
</resources>

0 commit comments

Comments
 (0)