コンテンツにスキップ

Icons (Experimental)

mapconductor-icons モジュールは、プログラムによるスタイリングが可能なカスタム描画マーカーアイコンを提供します。この実験的モジュールは、実行時に色、サイズ、その他のプロパティをカスタマイズできるベクタースタイルのアイコンを提供します。

⚠️ 実験的モジュール: このモジュールは実験的であり、将来のバージョンで大幅に変更される可能性があります。本番環境での使用には注意してください。

icons モジュールは、Canvas 描画操作を使用して高品質なマーカーアイコンを作成し、以下を提供します:

  • スケーラブルベクターグラフィックス: アイコンはあらゆるサイズで滑らかにスケールします
  • 実行時カスタマイズ: 色、サイズ、プロパティを動的に変更できます
  • 最適化されたキャッシング: パフォーマンスのための自動ビットマップキャッシング
  • 一貫した外観: すべての地図SDKで同じビジュアルスタイル

build.gradle に icons モジュールを追加します:

dependencies {
implementation "com.mapconductor:mapconductor-icons"
// 必須: Bom モジュール
implementation "com.mapconductor:mapconductor-bom:$version"
// 必須: Core モジュール
implementation "com.mapconductor:core"
// 地図SDKを選択
implementation "com.mapconductor:for-googlemaps"
}

カスタマイズ可能な塗りつぶしとストロークを持つシンプルな円形マーカーアイコン:

import com.mapconductor.icons.CircleIcon
// Basic circle icon
val basicCircle = CircleIcon()
// Customized circle icon
val customCircle = CircleIcon(
fillColor = Color.Blue,
strokeColor = Color.White,
strokeWidth = 2.dp,
scale = 1.2f,
iconSize = 32.dp
)
  • fillColor: Color: 円の内部色(デフォルト: Color.Red
  • strokeColor: Color: 境界線の色(デフォルト: Color.White
  • strokeWidth: Dp: 境界線の太さ(デフォルト: Settings から)
  • scale: Float: サイズ倍率(デフォルト: 1.0f
  • iconSize: Dp: アイコンの基本サイズ(デフォルト: Settings から)
  • debug: Boolean: デバッグアウトラインを表示(デフォルト: false

ポールとカスタマイズ可能なフラグを持つフラグスタイルのマーカーアイコン:

import com.mapconductor.icons.FlagIcon
// Basic flag icon
val basicFlag = FlagIcon()
// Customized flag icon
val customFlag = FlagIcon(
fillColor = Color.Green,
strokeColor = Color.Black,
strokeWidth = 1.5.dp,
scale = 1.0f,
iconSize = 40.dp
)
  • fillColor: Color: フラグとポールの色(デフォルト: Color.Red
  • strokeColor: Color: アウトラインの色(デフォルト: Color.White
  • strokeWidth: Dp: アウトラインの太さ(デフォルト: Settings から)
  • scale: Float: サイズ倍率(デフォルト: 1.0f
  • iconSize: Dp: アイコンの基本サイズ(デフォルト: Settings から)
  • debug: Boolean: デバッグアウトラインを表示(デフォルト: false
@Composable
fun BasicIconExample() {
val circleIcon = CircleIcon(
fillColor = Color.Blue,
strokeColor = Color.White
)
val flagIcon = FlagIcon(
fillColor = Color.Red,
strokeColor = Color.Black
)
// GoogleMapView、MapboxMapView などの選択した地図SDKに置き換えてください
MapView(state = mapViewState) {
Marker(
position = GeoPoint.fromLatLong(37.7749, -122.4194),
icon = circleIcon
)
Marker(
position = GeoPoint.fromLatLong(37.7849, -122.4094),
icon = flagIcon
)
}
}
@Composable
fun DynamicIconExample() {
var iconColor by remember { mutableStateOf(Color.Red) }
var iconSize by remember { mutableStateOf(32.dp) }
val dynamicIcon = CircleIcon(
fillColor = iconColor,
strokeColor = Color.White,
iconSize = iconSize
)
Column {
// カラーピッカー
Row {
Button(onClick = { iconColor = Color.Red }) { Text("Red") }
Button(onClick = { iconColor = Color.Blue }) { Text("Blue") }
Button(onClick = { iconColor = Color.Green }) { Text("Green") }
}
// サイズスライダー
Slider(
value = iconSize.value,
onValueChange = { iconSize = it.dp },
valueRange = 16f..64f
)
Text("Size: ${iconSize.value.toInt()}dp")
// GoogleMapView、MapboxMapView などの選択した地図SDKに置き換えてください
MapView(state = mapViewState) {
Marker(
position = GeoPoint.fromLatLong(37.7749, -122.4194),
icon = dynamicIcon
)
}
}
}
@Composable
fun CategoryIconExample() {
data class POI(
val name: String,
val category: String,
val position: GeoPointInterface
) : java.io.Serializable
val pois = listOf(
POI("Restaurant", "food", GeoPoint.fromLatLong(37.7749, -122.4194)),
POI("Hotel", "lodging", GeoPoint.fromLatLong(37.7849, -122.4094)),
POI("Gas Station", "fuel", GeoPoint.fromLatLong(37.7649, -122.4294))
)
fun getIconForCategory(category: String) = when (category) {
"food" -> CircleIcon(fillColor = Color.Red, strokeColor = Color.White)
"lodging" -> FlagIcon(fillColor = Color.Blue, strokeColor = Color.White)
"fuel" -> CircleIcon(fillColor = Color.Yellow, strokeColor = Color.Black)
else -> CircleIcon(fillColor = Color.Gray, strokeColor = Color.White)
}
// GoogleMapView、MapboxMapView などの選択した地図SDKに置き換えてください
MapView(state = mapViewState) {
pois.forEach { poi ->
Marker(
position = poi.position,
icon = getIconForCategory(poi.category),
extra = poi.name
)
}
}
}
object IconTheme {
data class Theme(
val primaryColor: Color,
val secondaryColor: Color,
val strokeColor: Color,
val strokeWidth: Dp
) : java.io.Serializable
val light = Theme(
primaryColor = Color(0xFF2196F3),
secondaryColor = Color(0xFFFFFFFF),
strokeColor = Color(0xFF000000),
strokeWidth = 1.dp
)
val dark = Theme(
primaryColor = Color(0xFF1976D2),
secondaryColor = Color(0xFF424242),
strokeColor = Color(0xFFFFFFFF),
strokeWidth = 1.dp
)
}
@Composable
fun ThemedIconExample() {
val isDarkTheme = isSystemInDarkTheme()
val theme = if (isDarkTheme) IconTheme.dark else IconTheme.light
val themedCircle = CircleIcon(
fillColor = theme.primaryColor,
strokeColor = theme.strokeColor,
strokeWidth = theme.strokeWidth
)
val themedFlag = FlagIcon(
fillColor = theme.primaryColor,
strokeColor = theme.strokeColor,
strokeWidth = theme.strokeWidth
)
// GoogleMapView、MapboxMapView などの選択した地図SDKに置き換えてください
MapView(state = mapViewState) {
Marker(
position = GeoPoint.fromLatLong(37.7749, -122.4194),
icon = themedCircle
)
Marker(
position = GeoPoint.fromLatLong(37.7849, -122.4094),
icon = themedFlag
)
}
}
@Composable
fun AnimatedIconExample() {
var scale by remember { mutableStateOf(1.0f) }
var color by remember { mutableStateOf(Color.Red) }
// Animate scale
LaunchedEffect(Unit) {
while (true) {
animate(
initialValue = 1.0f,
targetValue = 1.5f,
animationSpec = tween(1000)
) { value, _ -> scale = value }
animate(
initialValue = 1.5f,
targetValue = 1.0f,
animationSpec = tween(1000)
) { value, _ -> scale = value }
}
}
// Animate color
LaunchedEffect(Unit) {
val colors = listOf(Color.Red, Color.Blue, Color.Green, Color.Yellow)
var index = 0
while (true) {
delay(2000)
index = (index + 1) % colors.size
color = colors[index]
}
}
val animatedIcon = CircleIcon(
fillColor = color,
strokeColor = Color.White,
scale = scale
)
// GoogleMapView、MapboxMapView などの選択した地図SDKに置き換えてください
MapView(state = mapViewState) {
Marker(
position = GeoPoint.fromLatLong(37.7749, -122.4194),
icon = animatedIcon
)
}
}

アイコンには、地理座標に対してどのように配置されるかを決定する特定のアンカーポイントがあります:

// CircleIcon: anchored at left-center (0.0, 0.5)
// FlagIcon: anchored near the base of the pole (0.176, 0.91)

情報ウィンドウ(地図SDKがサポートしている場合)は異なるポイントにアンカーされます:

// CircleIcon info window anchor: center of the circle (0.5, 0.5)
// FlagIcon info window anchor: top of the flag (0.5, 0.0)

パフォーマンスに関する考慮事項

Section titled “パフォーマンスに関する考慮事項”

アイコンは、プロパティハッシュに基づいてレンダリングされたビットマップを自動的にキャッシュします:

// These will share the same cached bitmap
val icon1 = CircleIcon(fillColor = Color.Red, strokeColor = Color.White)
val icon2 = CircleIcon(fillColor = Color.Red, strokeColor = Color.White)
// This will create a new cached bitmap
val icon3 = CircleIcon(fillColor = Color.Blue, strokeColor = Color.White)
  • キャッシュされたビットマップは自動的に管理されます
  • 同一のプロパティを持つアイコンはビットマップインスタンスを共有します
  • 大きなアイコンはより多くのメモリを使用します - 適切なサイズを使用してください

デバッグモードを有効にして、アイコンの境界とアンカーポイントを視覚化します:

@Composable
fun DebugIconExample() {
val debugIcon = CircleIcon(
fillColor = Color.Red,
strokeColor = Color.White,
debug = true // Show debug outline and crosshairs
)
// GoogleMapView、MapboxMapView などの選択した地図SDKに置き換えてください
MapView(state = mapViewState) {
Marker(
position = GeoPoint.fromLatLong(37.7749, -122.4194),
icon = debugIcon
)
}
}

デバッグモードは以下を表示します:

  • アイコンの境界矩形(黒いアウトライン)
  • 中心の十字線(黒い線)
  • 実際に描画されたコンテンツ

カスタムアイコンを作成するには、AbstractMarkerIcon を拡張します:

class CustomIcon(
private val fillColor: Color = Color.Blue,
override val scale: Float = 1.0f,
override val iconSize: Dp = 32.dp,
override val debug: Boolean = false
) : AbstractMarkerIcon() {
override val anchor: Offset = Offset(0.5f, 0.5f)
override val infoAnchor: Offset = Offset(0.5f, 0.0f)
override fun toBitmapIcon(): BitmapIcon {
val id = "custom_icon_${hashCode()}".hashCode()
BitmapIconCache.get(id)?.let { return it }
val canvasSize = ResourceProvider.dpToPx(iconSize.value * scale)
val bitmap = createBitmap(canvasSize.toInt(), canvasSize.toInt())
val canvas = Canvas(bitmap)
// Custom drawing code here
val paint = Paint().apply {
color = fillColor.toArgb()
style = Paint.Style.FILL
isAntiAlias = true
}
canvas.drawRect(0f, 0f, canvasSize.toFloat(), canvasSize.toFloat(), paint)
val result = BitmapIcon(
bitmap = bitmap,
anchor = anchor,
size = Size(canvasSize.toFloat(), canvasSize.toFloat())
)
BitmapIconCache.put(id, result)
return result
}
}
  1. 一貫したサイジング: 類似したマーカータイプには一貫したアイコンサイズを使用する
  2. 色のアクセシビリティ: 塗りつぶしとストロークの色の間に十分なコントラストを確保する
  3. パフォーマンス: キャッシングの恩恵を受けるために、同一のアイコンインスタンスを再利用する
  4. 適切なスケール: アイコンサイズを選択する際は、地図のズームレベルを考慮する
  5. 地図SDK間でのテスト: すべてのターゲット地図SDKでアイコンの外観を確認する
  1. 限定的なアイコンセット: 現在、CircleIcon と FlagIcon のみが利用可能
  2. 静的な形状: アイコンはプログラムで描画され、ベクターファイルからではありません
  3. 地図SDK間の違い: 地図SDK間でわずかなレンダリングの違いが発生する可能性があります
  4. メモリ使用量: 大きなアイコンや多くのユニークなアイコンバリエーションは、より多くのメモリを消費します

このモジュールは実験的であり、API が変更される可能性があります。移行時には:

  1. 特定のユースケースで十分にテストする
  2. 多数のユニークなアイコンを使用する場合はメモリ使用量を監視する
  3. 重要な機能にはフォールバックオプションを用意する
  4. モジュールの改善に役立つように問題を報告する

icons モジュールは、地図SDK間で統一された MapConductor API を維持しながら、カスタムマーカースタイリングの基盤を提供します。