Klik Peta & Ambil Lokasi, Geocoding/Reverse Geocoding untuk mendapat alamat

This commit is contained in:
202310715297 RAIHAN ARIQ MUZAKKI 2026-01-12 19:49:50 +07:00
parent a5b122d4e8
commit 575162379f

View File

@ -14,6 +14,16 @@ import org.osmdroid.tileprovider.tilesource.TileSourceFactory
import org.osmdroid.util.GeoPoint import org.osmdroid.util.GeoPoint
import org.osmdroid.views.MapView import org.osmdroid.views.MapView
import org.osmdroid.views.overlay.Marker import org.osmdroid.views.overlay.Marker
import android.location.Geocoder
import android.widget.Toast
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.osmdroid.events.MapEventsReceiver
import org.osmdroid.views.overlay.MapEventsOverlay
import java.util.Locale
class MainActivity : ComponentActivity() { class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
@ -31,53 +41,111 @@ class MainActivity : ComponentActivity() {
@Composable @Composable
fun OsmMapScreen() { fun OsmMapScreen() {
// State untuk Tipe Peta (Poin 3: Switching Map Views) val context = LocalContext.current
// Kita pakai Boolean: True = Normal, False = Topografi/Lainnya val scope = rememberCoroutineScope() // Untuk menjalankan proses background (Geocoding)
// State
var isNormalMode by remember { mutableStateOf(true) } var isNormalMode by remember { mutableStateOf(true) }
var selectedAddress by remember { mutableStateOf("Sentuh peta untuk cari alamat...") }
// Kita simpan referensi MapView agar bisa dimanipulasi (tambah marker) dari luar AndroidView
var mapViewRef by remember { mutableStateOf<MapView?>(null) }
Scaffold( Scaffold(
bottomBar = {
// Menampilkan Alamat Hasil Geocoding
Surface(
shadowElevation = 8.dp,
modifier = Modifier.fillMaxWidth()
) {
Text(
text = selectedAddress,
modifier = Modifier.padding(16.dp),
style = MaterialTheme.typography.bodyLarge
)
}
},
floatingActionButton = { floatingActionButton = {
ExtendedFloatingActionButton( ExtendedFloatingActionButton(
onClick = { isNormalMode = !isNormalMode } onClick = { isNormalMode = !isNormalMode }
) { ) {
Text(text = if (isNormalMode) "Mode Topografi" else "Mode Normal") Text(text = if (isNormalMode) "Mode Topo" else "Mode Normal")
} }
} }
) { paddingValues -> ) { paddingValues ->
Box(modifier = Modifier.padding(paddingValues).fillMaxSize()) { Box(modifier = Modifier.padding(paddingValues).fillMaxSize()) {
// Poin 1: Menampilkan Peta OSM menggunakan AndroidView
AndroidView( AndroidView(
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
factory = { context -> factory = { ctx ->
// Inisialisasi MapView (Versi OSM) MapView(ctx).apply {
MapView(context).apply { setMultiTouchControls(true)
setMultiTouchControls(true) // Poin 2: Mengaktifkan Zoom dengan jari (pinch)
// Set titik awal (Contoh: Monas)
// Beda istilah: Google pakai LatLng, OSM pakai GeoPoint
val startPoint = GeoPoint(-6.175392, 106.827153) val startPoint = GeoPoint(-6.175392, 106.827153)
controller.setZoom(18.0) // Level zoom controller.setZoom(18.0)
controller.setCenter(startPoint) controller.setCenter(startPoint)
// Tambahkan Marker sederhana // --- LOGIKA SENTUH PETA (Poin 4) ---
val marker = Marker(this) val eventReceiver = object : MapEventsReceiver {
marker.position = startPoint override fun singleTapConfirmedHelper(p: GeoPoint): Boolean {
// 1. Hapus marker lama (opsional, biar tidak penuh)
overlays.clear()
// Jangan lupa tambahkan ulang event overlay ini agar bisa diklik lagi
val eventsOverlay = MapEventsOverlay(this)
overlays.add(eventsOverlay)
// 2. Tambah Marker Baru di titik sentuh
val marker = Marker(this@apply)
marker.position = p
marker.setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_BOTTOM) marker.setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_BOTTOM)
marker.title = "Monas" marker.title = "Lokasi Dipilih"
overlays.add(marker) overlays.add(marker)
invalidate() // Refresh peta
// 3. Lakukan Geocoding (Poin 5)
scope.launch(Dispatchers.IO) {
try {
val geocoder = Geocoder(ctx, Locale.getDefault())
// Ambil max 1 hasil alamat
val addresses = geocoder.getFromLocation(p.latitude, p.longitude, 1)
val resultText = if (!addresses.isNullOrEmpty()) {
val address = addresses[0]
// Gabungkan baris alamat (misal: Jl. Sudirman, Jakarta)
address.getAddressLine(0)
} else {
"Alamat tidak ditemukan"
}
// Update UI harus di Main Thread
withContext(Dispatchers.Main) {
selectedAddress = resultText
marker.snippet = resultText
marker.showInfoWindow() // Tampilkan balon info otomatis
}
} catch (e: Exception) {
withContext(Dispatchers.Main) {
selectedAddress = "Error: Cek koneksi internet"
}
}
}
return true
}
override fun longPressHelper(p: GeoPoint?): Boolean = false
}
// Pasang "jaring" penangkap sentuhan ke peta
val eventsOverlay = MapEventsOverlay(eventReceiver)
overlays.add(eventsOverlay)
mapViewRef = this
} }
}, },
update = { view -> update = { view ->
// Kode disini dijalankan setiap kali ada perubahan State (isNormalMode berubah)
// Poin 3: Logika ganti tipe peta
if (isNormalMode) { if (isNormalMode) {
view.setTileSource(TileSourceFactory.MAPNIK) // Tampilan Standar OSM view.setTileSource(TileSourceFactory.MAPNIK)
} else { } else {
view.setTileSource(TileSourceFactory.USGS_TOPO) // Tampilan Topografi (Gratis) view.setTileSource(TileSourceFactory.USGS_TOPO)
// Catatan: OSM tidak punya satelit gratis yang sebagus Google,
// jadi kita pakai Topo atau Public Transport sebagai alternatif tampilan.
} }
} }
) )