Skip to content

Commit 3d8e3a5

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 3d8e3a5

6 files changed

Lines changed: 890 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: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
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 HERE Flexible Polyline and drawing it.
28+
*
29+
* The sample string encodes the Sydney Harbour Bridge walkway with an altitude for every
30+
* point, at seven decimal places of coordinate precision. The demo makes two points:
31+
*
32+
* - the geometry draws exactly like any other path, because once decoded it is just a list of
33+
* [LatLng] values;
34+
* - the altitude cannot be drawn. A [com.google.android.gms.maps.model.Polyline] has no height,
35+
* so the third dimension is surfaced here as marker text instead, which is the honest
36+
* treatment for data the renderer cannot express.
37+
*/
38+
class FlexiblePolylineDemoActivity : BaseDemoActivity() {
39+
override fun startDemo(isRestore: Boolean) {
40+
val map = map ?: return
41+
val polyline = FlexiblePolylineUtil.decode(ENCODED_WALKWAY)
42+
val path = polyline.toLatLngs()
43+
44+
map.addPolyline(
45+
PolylineOptions()
46+
.addAll(path)
47+
.width(POLYLINE_WIDTH)
48+
.color(POLYLINE_COLOR),
49+
)
50+
51+
// The third dimension is metadata, not geometry: label the end points with it so the
52+
// altitude profile is visible without pretending the map can render height.
53+
val withAltitude = polyline.points.filter { it.thirdDimensionValue != null }
54+
val labelled =
55+
listOfNotNull(
56+
withAltitude.firstOrNull()?.let { R.string.flexible_polyline_start to it },
57+
withAltitude.maxByOrNull { point -> point.thirdDimensionValue!! }
58+
?.let { R.string.flexible_polyline_highest to it },
59+
withAltitude.lastOrNull()?.let { R.string.flexible_polyline_end to it },
60+
)
61+
for ((titleResId, point) in labelled) {
62+
map.addMarker(
63+
MarkerOptions()
64+
.position(point.toLatLng())
65+
.title(getString(titleResId))
66+
.snippet(
67+
getString(
68+
R.string.flexible_polyline_altitude,
69+
point.thirdDimensionValue!!,
70+
),
71+
)
72+
.icon(
73+
BitmapDescriptorFactory.defaultMarker(
74+
BitmapDescriptorFactory.HUE_AZURE,
75+
),
76+
),
77+
)
78+
}
79+
80+
Toast.makeText(
81+
this,
82+
getString(
83+
R.string.flexible_polyline_summary,
84+
polyline.points.size,
85+
polyline.precision,
86+
polyline.thirdDimension.name.lowercase(),
87+
),
88+
Toast.LENGTH_LONG,
89+
).show()
90+
91+
if (!isRestore) {
92+
map.moveCamera(CameraUpdateFactory.newLatLngZoom(CAMERA_TARGET, CAMERA_ZOOM))
93+
}
94+
}
95+
96+
private companion object {
97+
/**
98+
* The Sydney Harbour Bridge walkway, encoded at precision 7 with an altitude in metres
99+
* at one decimal place. Note that the string needs no escaping to sit in source, a URL
100+
* or a JSON field, unlike the Google format sample in [PolyDecodeDemoActivity].
101+
*/
102+
const val ENCODED_WALKWAY =
103+
"BnF__k0lU4kokk6CgFnzQnqDyGnzQvwD6HnzQvwDmHnzQvwDiEnzQvwDyBnzQvwDnBnzQvwDpFnzQvwDtIn" +
104+
"zQvwDtI"
105+
106+
val CAMERA_TARGET = LatLng(-33.85343, 151.21083)
107+
const val CAMERA_ZOOM = 15f
108+
const val POLYLINE_WIDTH = 12f
109+
const val POLYLINE_COLOR = 0xFF1A73E8.toInt()
110+
}
111+
}

‎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)