コンテンツにスキップ

Marker Native Strategy (Experimental)

mapconductor-marker-native-strategy モジュールは、ネイティブC++の空間インデックスを使用した高性能なマーカー管理を提供します。この実験的モジュールは、大量のマーカー(10,000個以上)を持つアプリケーションのパフォーマンスを劇的に向上させます。

⚠️ 実験的モジュール: このモジュールは非常に実験的であり、ネイティブライブラリのサポートが必要です。本番環境での使用には細心の注意を払ってください。

marker native strategy は、Java ベースの空間インデックスを最適化された C++ 実装に置き換え、以下を提供します:

  • 90%のメモリ削減: 標準的なマーカー管理と比較して
  • ネイティブ空間クエリ: 最大パフォーマンスのための C++ 空間インデックス
  • 効率的なビューポートカリング: ビューポート内のマーカーのみをレンダリング
  • 並列処理: マルチスレッドマーカー操作
  • 最小限の Java オーバーヘッド: ネイティブコードを単一の真実の情報源として
  • 標準 MarkerManager: 1,000マーカーあたり約1MB
  • NativeMarkerManager: 1,000マーカーあたり約100KB
  • 最適化されたストレージ: エンティティストレージの重複なし
  • 標準空間クエリ: Java オーバーヘッドを伴う O(log n)
  • ネイティブ空間クエリ: C++ 最適化による O(log n)
  • 大規模データセット: 100,000以上のマーカーで10倍〜100倍のパフォーマンス向上

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

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

native strategy でも、地図上に描画するためには地図SDKごとの renderer/controller が必要です。 組み込みの地図SDKモジュールは MapServiceRegistry の capability として提供します(Map Service Registry)。

ネイティブライブラリのセットアップ

Section titled “ネイティブライブラリのセットアップ”

モジュールにはネイティブC++ライブラリが必要です。アプリが必要なABIをサポートしていることを確認してください:

android {
defaultConfig {
ndk {
abiFilters 'arm64-v8a', 'armeabi-v7a', 'x86', 'x86_64'
}
}
}

ネイティブ空間インデックスを使用した高性能マーカーマネージャー:

import com.mapconductor.marker.nativestrategy.NativeMarkerManager
import com.mapconductor.core.geocell.HexGeocell
// Create native marker manager
val nativeManager = NativeMarkerManager<ActualMarker>(
hexGeocell = HexGeocell.defaultGeocell()
)
// Use like standard MarkerManager
nativeManager.registerEntity(markerEntity)
val nearestMarker = nativeManager.findNearest(position)
val markersInBounds = nativeManager.findMarkersInBounds(bounds)

ネイティブレンダリングストラテジ

Section titled “ネイティブレンダリングストラテジ”

ネイティブインデックスによる並列マーカーレンダリング:

import com.mapconductor.marker.nativestrategy.SimpleNativeParallelStrategy
val strategy = SimpleNativeParallelStrategy<ActualMarker>(
expandMargin = 0.2, // Viewport expansion
maxConcurrency = 4, // Parallel threads
geocell = HexGeocell.defaultGeocell()
)

シンプルなネイティブマネージャー

Section titled “シンプルなネイティブマネージャー”
@Composable
fun BasicNativeExample() {
// Create native marker manager
val nativeManager = remember {
NativeMarkerManager<GoogleMapActualMarker>(
hexGeocell = HexGeocell.defaultGeocell()
)
}
// Add markers to native manager
LaunchedEffect(Unit) {
val markers = generateLargeMarkerDataset() // 10,000以上のマーカー
markers.forEach { markerData ->
val entity = MarkerEntityInterface(
state = MarkerState(
id = markerData.id,
position = markerData.position,
icon = DefaultIcon()
)
)
nativeManager.registerEntity(entity)
}
}
// GoogleMapView、MapboxMapView などの選択した地図SDKに置き換えてください
MapView(state = mapViewState) {
// Markers are managed by native strategy
// No need to manually add Marker composables
}
DisposableEffect(Unit) {
onDispose {
nativeManager.destroy() // Important: cleanup native resources
}
}
}
@Composable
fun NativePerformanceExample() {
val nativeManager = remember {
NativeMarkerManager<GoogleMapActualMarker>(
hexGeocell = HexGeocell.defaultGeocell()
)
}
var stats by remember { mutableStateOf<NativeMarkerManagerStats?>(null) }
// Monitor performance
LaunchedEffect(Unit) {
while (true) {
delay(5000) // Update every 5 seconds
stats = nativeManager.getNativeMemoryStats()
}
}
Column {
stats?.let { s ->
Text("Markers: ${s.entityCount}")
Text("Native Index: ${s.nativeIndexCount}")
Text("Memory: ${s.estimatedMemoryKB} KB")
Text("Pure Native: ${s.usesPureNativeIndex}")
}
// GoogleMapView、MapboxMapView などの選択した地図SDKに置き換えてください
MapView(state = mapViewState) {
// Native-managed markers
}
}
}
@Composable
fun ParallelRenderingExample() {
val parallelStrategy = remember {
SimpleNativeParallelStrategy<GoogleMapActualMarker>(
expandMargin = 0.3, // Larger viewport expansion
maxConcurrency = 6, // More parallel threads
geocell = HexGeocell(
baseHexSideLength = 1000.0, // Optimized cell size
zoom = 15.0
)
)
}
// Configure marker controller with parallel strategy
LaunchedEffect(mapViewState) {
mapViewState.getMapViewHolder()?.let { holder ->
// Setup parallel strategy with map controller
// Implementation depends on map provider
}
}
// GoogleMapView、MapboxMapView などの選択した地図SDKに置き換えてください
MapView(state = mapViewState) {
// Parallel-rendered markers
}
}
@Composable
fun DynamicNativeLoadingExample() {
val nativeManager = remember {
NativeMarkerManager<GoogleMapActualMarker>(
hexGeocell = HexGeocell.defaultGeocell()
)
}
var currentBounds by remember { mutableStateOf<GeoRectBounds?>(null) }
var visibleMarkers by remember { mutableStateOf<List<MarkerEntityInterface<GoogleMapActualMarker>>>(emptyList()) }
// Load markers dynamically based on viewport
LaunchedEffect(currentBounds) {
currentBounds?.let { bounds ->
// Native spatial query is extremely fast
visibleMarkers = nativeManager.findMarkersInBounds(bounds)
}
}
Column {
Text("Visible Markers: ${visibleMarkers.size}")
// GoogleMapView、MapboxMapView などの選択した地図SDKに置き換えてください
MapView(
state = mapViewState,
onCameraMove = { cameraPosition ->
currentBounds = cameraPosition.visibleRegion?.bounds
}
) {
// Only visible markers are processed
}
}
}

ネイティブストラテジによるクラスタリング

Section titled “ネイティブストラテジによるクラスタリング”
@Composable
fun NativeClusteringExample() {
val clusteringStrategy = remember {
NativeSpatialMarkerStrategy<GoogleMapActualMarker>(
clusteringEnabled = true,
clusterThreshold = 50, // Cluster when > 50 markers nearby
clusterRadius = 100.0, // 100-meter clustering radius
geocell = HexGeocell.defaultGeocell()
)
}
val nativeManager = remember {
NativeMarkerManager<GoogleMapActualMarker>(
hexGeocell = HexGeocell.defaultGeocell()
)
}
// Add large dataset for clustering
LaunchedEffect(Unit) {
val denseMarkers = generateDenseMarkerCluster(
center = GeoPoint.fromLatLong(37.7749, -122.4194),
count = 1000,
radiusMeters = 500.0 // 500 meters
)
denseMarkers.forEach { markerData ->
val entity = MarkerEntityInterface(
state = MarkerState(
id = markerData.id,
position = markerData.position,
icon = DefaultIcon(fillColor = Color.Blue, scale = 0.8f)
)
)
nativeManager.registerEntity(entity)
}
}
// GoogleMapView、MapboxMapView などの選択した地図SDKに置き換えてください
MapView(state = mapViewState) {
// Native clustering automatically groups nearby markers
}
}
fun performNativeQueries(nativeManager: NativeMarkerManager<ActualMarker>) {
val center = GeoPoint.fromLatLong(37.7749, -122.4194)
val bounds = GeoRectBounds(
southWest = GeoPoint.fromLatLong(37.7700, -122.4250),
northEast = GeoPoint.fromLatLong(37.7800, -122.4150)
)
// Native spatial queries are extremely fast
val nearestMarker = nativeManager.findNearest(center)
val boundedMarkers = nativeManager.findMarkersInBounds(bounds)
val totalMarkers = nativeManager.allEntities().size
// Memory and performance stats
val stats = nativeManager.getNativeMemoryStats()
println("Query performance: ${stats.nativeIndexCount} indexed markers")
println("Memory usage: ${stats.estimatedMemoryKB} KB")
}
suspend fun batchNativeOperations(nativeManager: NativeMarkerManager<ActualMarker>) {
val markersBatch = generateMarkerBatch(10000) // 10,000 markers
// Efficient batch registration
withContext(Dispatchers.Default) {
markersBatch.forEach { markerData ->
val entity = MarkerEntityInterface(
state = MarkerState(
id = markerData.id,
position = markerData.position,
icon = DefaultIcon()
)
)
nativeManager.registerEntity(entity)
}
}
// Verify registration
val totalMarkers = nativeManager.allEntities().size
println("Registered $totalMarkers markers in native index")
}
@Composable
fun ResourceManagementExample() {
val nativeManager = remember {
NativeMarkerManager<GoogleMapActualMarker>(
hexGeocell = HexGeocell.defaultGeocell()
)
}
// Proper cleanup is crucial for native resources
DisposableEffect(nativeManager) {
onDispose {
// Cleanup native resources
nativeManager.destroy()
}
}
// Monitor memory usage
var memoryStats by remember { mutableStateOf<NativeMarkerManagerStats?>(null) }
LaunchedEffect(Unit) {
while (true) {
delay(10000) // Check every 10 seconds
memoryStats = nativeManager.getNativeMemoryStats()
// Log memory usage for monitoring
memoryStats?.let { stats ->
if (stats.estimatedMemoryKB > 10000) { // > 10MB
println("High memory usage detected: ${stats.estimatedMemoryKB} KB")
}
}
}
}
// GoogleMapView、MapboxMapView などの選択した地図SDKに置き換えてください
MapView(state = mapViewState) {
// Native-managed markers
}
}
// Optimal configuration for large datasets
val optimizedGeocell = HexGeocell(
baseHexSideLength = 500.0, // Smaller cells for dense data
zoom = 18.0 // Higher resolution
)
val optimizedManager = NativeMarkerManager<ActualMarker>(
hexGeocell = optimizedGeocell
)
// Optimal parallel strategy
val optimizedStrategy = SimpleNativeParallelStrategy<ActualMarker>(
expandMargin = 0.1, // Smaller expansion for performance
maxConcurrency = Runtime.getRuntime().availableProcessors(),
geocell = optimizedGeocell
)
suspend fun benchmarkNativePerformance() {
val standardManager = MarkerManager<ActualMarker>(HexGeocell.defaultGeocell())
val nativeManager = NativeMarkerManager<ActualMarker>(HexGeocell.defaultGeocell())
val testMarkers = generateTestDataset(50000) // 50,000 markers
// Benchmark registration
val standardTime = measureTimeMillis {
testMarkers.forEach { standardManager.registerEntity(it) }
}
val nativeTime = measureTimeMillis {
testMarkers.forEach { nativeManager.registerEntity(it) }
}
// Benchmark spatial queries
val testBounds = GeoRectBounds(
southWest = GeoPoint.fromLatLong(37.7700, -122.4250),
northEast = GeoPoint.fromLatLong(37.7800, -122.4150)
)
val standardQueryTime = measureTimeMillis {
repeat(1000) { standardManager.findMarkersInBounds(testBounds) }
}
val nativeQueryTime = measureTimeMillis {
repeat(1000) { nativeManager.findMarkersInBounds(testBounds) }
}
println("Registration - Standard: ${standardTime}ms, Native: ${nativeTime}ms")
println("Queries - Standard: ${standardQueryTime}ms, Native: ${nativeQueryTime}ms")
}
  1. リソース管理: 使用後は必ず NativeMarkerManager で destroy() を呼び出す
  2. バッチ操作: 大規模データセットにはバッチ登録を使用する
  3. メモリ監視: 本番環境でネイティブメモリ使用量を監視する
  4. テスト: 特定のデータパターンで徹底的にテストする
  5. フォールバックストラテジ: サポートされていないデバイス向けに非ネイティブフォールバックを用意する
  1. プラットフォームサポート: ターゲットABI向けのネイティブライブラリサポートが必要
  2. メモリ管理: ネイティブメモリはガベージコレクトされない
  3. デバッグ: ネイティブクラッシュはJavaクラッシュよりもデバッグが困難
  4. バイナリサイズ: ネイティブライブラリによりAPKサイズが増加
  5. 互換性: すべてのデバイス/エミュレータで動作しない可能性がある

ネイティブライブラリ読み込みの問題

Section titled “ネイティブライブラリ読み込みの問題”
// Check native library availability
try {
val nativeManager = NativeMarkerManager<ActualMarker>(
hexGeocell = HexGeocell.defaultGeocell()
)
// Native library loaded successfully
} catch (e: UnsatisfiedLinkError) {
// Fallback to standard marker manager
val standardManager = MarkerManager<ActualMarker>(
hexGeocell = HexGeocell.defaultGeocell()
)
}
class MarkerActivity : ComponentActivity() {
private var nativeManager: NativeMarkerManager<ActualMarker>? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
nativeManager = NativeMarkerManager(HexGeocell.defaultGeocell())
}
override fun onDestroy() {
super.onDestroy()
// Critical: cleanup native resources
nativeManager?.destroy()
nativeManager = null
}
}

marker native strategy モジュールは、マーカーを多用するアプリケーションに大幅なパフォーマンス向上をもたらしますが、実験的な性質のため、慎重なリソース管理と徹底的なテストが必要です。