Skip to content

Commit 5a7a35d

Browse files
committed
Add bitmap descriptor
1 parent 3ce498b commit 5a7a35d

5 files changed

Lines changed: 224 additions & 45 deletions

File tree

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/*
2+
* Copyright (c) 2025 Alexander Farber
3+
* SPDX-License-Identifier: MIT
4+
*
5+
* This file is part of the OpenMapView project (https://github.com/afarber/OpenMapView)
6+
*/
7+
8+
package de.afarber.openmapview
9+
10+
import android.graphics.Bitmap
11+
12+
/**
13+
* Represents a bitmap image used for marker icons.
14+
*
15+
* This sealed class provides different ways to specify marker icons:
16+
* - [DefaultMarker]: Default colored teardrop marker
17+
* - [BitmapMarker]: Custom bitmap object
18+
* - [ResourceMarker]: Bitmap from drawable resource
19+
* - [AssetMarker]: Bitmap from assets folder
20+
*/
21+
sealed class BitmapDescriptor {
22+
/**
23+
* Default teardrop marker with specified hue color.
24+
*
25+
* @property hue The hue value (0-360) on the HSV color wheel
26+
*/
27+
data class DefaultMarker(
28+
val hue: Float,
29+
) : BitmapDescriptor()
30+
31+
/**
32+
* Custom marker from a bitmap object.
33+
*
34+
* @property bitmap The bitmap to use as marker icon
35+
*/
36+
data class BitmapMarker(
37+
val bitmap: Bitmap,
38+
) : BitmapDescriptor()
39+
40+
/**
41+
* Marker from a drawable resource ID.
42+
*
43+
* @property resourceId The drawable resource ID (e.g., R.drawable.marker_icon)
44+
*/
45+
data class ResourceMarker(
46+
val resourceId: Int,
47+
) : BitmapDescriptor()
48+
49+
/**
50+
* Marker from an asset file.
51+
*
52+
* @property assetName The name of the asset file (e.g., "markers/custom_marker.png")
53+
*/
54+
data class AssetMarker(
55+
val assetName: String,
56+
) : BitmapDescriptor()
57+
}

openmapview/src/main/kotlin/de/afarber/openmapview/BitmapDescriptorFactory.kt

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ import android.graphics.Bitmap
1212
/**
1313
* Factory for creating marker icons.
1414
*
15-
* Provides predefined color constants and methods to generate colored marker icons.
15+
* Provides predefined color constants and methods to generate marker icon descriptors
16+
* from various sources (default colors, resources, assets, bitmaps).
1617
* Colors are specified using HSV hue values (0-360 degrees on the color wheel).
1718
*/
1819
object BitmapDescriptorFactory {
@@ -32,11 +33,35 @@ object BitmapDescriptorFactory {
3233
const val HUE_ROSE = 330f
3334

3435
/**
35-
* Creates a marker icon with the specified hue.
36+
* Creates a marker icon descriptor with the specified hue.
3637
*
3738
* @param hue The hue value (0-360) on the color wheel. Defaults to red (0).
3839
* 0=red, 120=green, 240=blue, etc.
39-
* @return A bitmap of the colored marker icon
40+
* @return A BitmapDescriptor for the default colored marker
4041
*/
41-
fun defaultMarker(hue: Float = HUE_RED): Bitmap = MarkerIconFactory.getDefaultIcon(hue)
42+
fun defaultMarker(hue: Float = HUE_RED): BitmapDescriptor = BitmapDescriptor.DefaultMarker(hue)
43+
44+
/**
45+
* Creates a marker icon descriptor from a drawable resource.
46+
*
47+
* @param resourceId The drawable resource ID (e.g., R.drawable.marker_icon)
48+
* @return A BitmapDescriptor for the resource marker
49+
*/
50+
fun fromResource(resourceId: Int): BitmapDescriptor = BitmapDescriptor.ResourceMarker(resourceId)
51+
52+
/**
53+
* Creates a marker icon descriptor from an asset file.
54+
*
55+
* @param assetName The name of the asset file (e.g., "markers/custom_marker.png")
56+
* @return A BitmapDescriptor for the asset marker
57+
*/
58+
fun fromAsset(assetName: String): BitmapDescriptor = BitmapDescriptor.AssetMarker(assetName)
59+
60+
/**
61+
* Creates a marker icon descriptor from a bitmap object.
62+
*
63+
* @param bitmap The bitmap to use as marker icon
64+
* @return A BitmapDescriptor for the bitmap marker
65+
*/
66+
fun fromBitmap(bitmap: Bitmap): BitmapDescriptor = BitmapDescriptor.BitmapMarker(bitmap)
4267
}

openmapview/src/main/kotlin/de/afarber/openmapview/MapController.kt

Lines changed: 46 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
package de.afarber.openmapview
99

1010
import android.content.Context
11+
import android.graphics.Bitmap
12+
import android.graphics.BitmapFactory
1113
import android.graphics.Canvas
1214
import android.graphics.Color
1315
import android.graphics.Paint
@@ -16,6 +18,7 @@ import kotlinx.coroutines.Dispatchers
1618
import kotlinx.coroutines.Job
1719
import kotlinx.coroutines.cancel
1820
import kotlinx.coroutines.launch
21+
import java.io.IOException
1922

2023
/**
2124
* Core controller managing map state, rendering, and interactions.
@@ -92,6 +95,44 @@ class MapController(
9295
color = Color.LTGRAY
9396
}
9497

98+
/**
99+
* Loads a bitmap from a BitmapDescriptor.
100+
*
101+
* @param descriptor The descriptor specifying the bitmap source
102+
* @return The loaded bitmap, or the default marker icon if loading fails
103+
*/
104+
private fun loadBitmap(descriptor: BitmapDescriptor?): Bitmap {
105+
if (descriptor == null) {
106+
return defaultMarkerIcon
107+
}
108+
109+
return when (descriptor) {
110+
is BitmapDescriptor.DefaultMarker -> {
111+
MarkerIconFactory.getDefaultIcon(descriptor.hue)
112+
}
113+
is BitmapDescriptor.BitmapMarker -> {
114+
descriptor.bitmap
115+
}
116+
is BitmapDescriptor.ResourceMarker -> {
117+
try {
118+
BitmapFactory.decodeResource(context.resources, descriptor.resourceId)
119+
?: defaultMarkerIcon
120+
} catch (e: Exception) {
121+
defaultMarkerIcon
122+
}
123+
}
124+
is BitmapDescriptor.AssetMarker -> {
125+
try {
126+
context.assets.open(descriptor.assetName).use { stream ->
127+
BitmapFactory.decodeStream(stream) ?: defaultMarkerIcon
128+
}
129+
} catch (e: IOException) {
130+
defaultMarkerIcon
131+
}
132+
}
133+
}
134+
}
135+
95136
/**
96137
* Sets the zoom level, clamping to valid range.
97138
*
@@ -740,7 +781,7 @@ class MapController(
740781
val screenY = (markerPixelY - centerPixelY + viewHeight / 2 - panOffsetY).toFloat()
741782

742783
// Get marker icon
743-
val icon = marker.icon ?: defaultMarkerIcon
784+
val icon = loadBitmap(marker.icon)
744785

745786
// Apply anchor point
746787
val anchorX = icon.width * marker.anchor.first
@@ -883,7 +924,7 @@ class MapController(
883924
val screenX = (markerPixelX - centerPixelX + viewWidth / 2 - panOffsetX).toFloat()
884925
val screenY = (markerPixelY - centerPixelY + viewHeight / 2 - panOffsetY).toFloat()
885926

886-
val icon = marker.icon ?: defaultMarkerIcon
927+
val icon = loadBitmap(marker.icon)
887928

888929
val anchorX = icon.width * marker.anchor.first
889930
val anchorY = icon.height * marker.anchor.second
@@ -963,7 +1004,7 @@ class MapController(
9631004
val boxWidth = maxWidth + padding * 2
9641005
val boxHeight = totalHeight + padding * 2
9651006

966-
val icon = marker.icon ?: defaultMarkerIcon
1007+
val icon = loadBitmap(marker.icon)
9671008
val markerHeight = icon.height * marker.anchor.second
9681009

9691010
val infoWindowX = screenX - boxWidth / 2
@@ -1152,7 +1193,7 @@ class MapController(
11521193
val screenX = (markerPixelX - centerPixelX + viewWidth / 2 - panOffsetX).toFloat()
11531194
val screenY = (markerPixelY - centerPixelY + viewHeight / 2 - panOffsetY).toFloat()
11541195

1155-
val icon = marker.icon ?: defaultMarkerIcon
1196+
val icon = loadBitmap(marker.icon)
11561197
val anchorX = icon.width * marker.anchor.first
11571198
val anchorY = icon.height * marker.anchor.second
11581199

@@ -1228,7 +1269,7 @@ class MapController(
12281269
val boxWidth = maxWidth + padding * 2
12291270
val boxHeight = totalHeight + padding * 2
12301271

1231-
val icon = marker.icon ?: defaultMarkerIcon
1272+
val icon = loadBitmap(marker.icon)
12321273
val markerHeight = icon.height * marker.anchor.second
12331274

12341275
val infoWindowX = screenX - boxWidth / 2

openmapview/src/main/kotlin/de/afarber/openmapview/Marker.kt

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,13 @@
77

88
package de.afarber.openmapview
99

10-
import android.graphics.Bitmap
11-
1210
/**
1311
* Represents a marker on the map at a specific geographic location.
1412
*
1513
* @property position The geographic location of the marker (mutable for dragging)
1614
* @property title Optional title text displayed when marker is clicked
1715
* @property snippet Optional snippet text displayed below the title
18-
* @property icon Custom icon bitmap. If null, a default marker icon will be used
16+
* @property icon Custom icon descriptor. If null, a default red marker icon will be used
1917
* @property anchor Anchor point for the marker icon. Default (0.5f, 1.0f) means
2018
* the marker is centered horizontally and anchored at the bottom
2119
* @property visible Whether the marker is visible. Default is true
@@ -28,7 +26,7 @@ data class Marker(
2826
var position: LatLng,
2927
val title: String? = null,
3028
val snippet: String? = null,
31-
val icon: Bitmap? = null,
29+
val icon: BitmapDescriptor? = null,
3230
val anchor: Pair<Float, Float> = Pair(0.5f, 1.0f),
3331
val visible: Boolean = true,
3432
val alpha: Float = 1.0f,

0 commit comments

Comments
 (0)