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.views.MapView
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() {
override fun onCreate(savedInstanceState: Bundle?) {
@ -31,53 +41,111 @@ class MainActivity : ComponentActivity() {
@Composable
fun OsmMapScreen() {
// State untuk Tipe Peta (Poin 3: Switching Map Views)
// Kita pakai Boolean: True = Normal, False = Topografi/Lainnya
val context = LocalContext.current
val scope = rememberCoroutineScope() // Untuk menjalankan proses background (Geocoding)
// State
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(
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 = {
ExtendedFloatingActionButton(
onClick = { isNormalMode = !isNormalMode }
) {
Text(text = if (isNormalMode) "Mode Topografi" else "Mode Normal")
Text(text = if (isNormalMode) "Mode Topo" else "Mode Normal")
}
}
) { paddingValues ->
Box(modifier = Modifier.padding(paddingValues).fillMaxSize()) {
// Poin 1: Menampilkan Peta OSM menggunakan AndroidView
AndroidView(
modifier = Modifier.fillMaxSize(),
factory = { context ->
// Inisialisasi MapView (Versi OSM)
MapView(context).apply {
setMultiTouchControls(true) // Poin 2: Mengaktifkan Zoom dengan jari (pinch)
// Set titik awal (Contoh: Monas)
// Beda istilah: Google pakai LatLng, OSM pakai GeoPoint
factory = { ctx ->
MapView(ctx).apply {
setMultiTouchControls(true)
val startPoint = GeoPoint(-6.175392, 106.827153)
controller.setZoom(18.0) // Level zoom
controller.setZoom(18.0)
controller.setCenter(startPoint)
// Tambahkan Marker sederhana
val marker = Marker(this)
marker.position = startPoint
marker.setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_BOTTOM)
marker.title = "Monas"
overlays.add(marker)
// --- LOGIKA SENTUH PETA (Poin 4) ---
val eventReceiver = object : MapEventsReceiver {
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.title = "Lokasi Dipilih"
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 ->
// Kode disini dijalankan setiap kali ada perubahan State (isNormalMode berubah)
// Poin 3: Logika ganti tipe peta
if (isNormalMode) {
view.setTileSource(TileSourceFactory.MAPNIK) // Tampilan Standar OSM
view.setTileSource(TileSourceFactory.MAPNIK)
} else {
view.setTileSource(TileSourceFactory.USGS_TOPO) // Tampilan Topografi (Gratis)
// Catatan: OSM tidak punya satelit gratis yang sebagus Google,
// jadi kita pakai Topo atau Public Transport sebagai alternatif tampilan.
view.setTileSource(TileSourceFactory.USGS_TOPO)
}
}
)