Menampilkan tempat-tempat terdekat dalam AR di Android (Kotlin)

1. Sebelum memulai

Abstrak

Codelab ini mengajari Anda cara menggunakan data dari Google Maps Platform untuk menampilkan tempat-tempat terdekat dalam augmented reality (AR).

2344909dd9a52c60.png

Prasyarat

  • Pemahaman dasar tentang pengembangan Android menggunakan Android Studio
  • Pemahaman tentang Kotlin

Yang akan Anda pelajari

  • Meminta izin dari pengguna untuk mengakses kamera dan lokasi perangkat.
  • Mengintegrasikan dengan Places API untuk mengambil data tempat-tempat terdekat di sekitar lokasi perangkat.
  • Mengintegrasikan dengan ARCore untuk menemukan permukaan bidang horizontal sehingga objek virtual dapat didasarkan dan ditempatkan dalam ruang 3D menggunakan Sceneform.
  • Mengumpulkan informasi tentang posisi perangkat di ruang menggunakan SensorManager dan menggunakan Library Utilitas Maps SDK for Android untuk menempatkan objek virtual pada arah tujuan yang benar.

Yang akan Anda perlukan

2. Memulai persiapan

Android Studio

Codelab ini menggunakan Android 10.0 (API level 29) dan mengharuskan Anda menginstal layanan Google Play di Android Studio. Untuk menginstal kedua dependensi ini, selesaikan langkah-langkah berikut:

  1. Buka SDK Manager, yang dapat Anda akses dengan mengklik Tools > SDK Manager.

6c44a9cb9cf6c236.png

  1. Periksa apakah Android 10.0 telah diinstal. Jika belum, instal dengan memilih kotak centang di samping Android 10.0 (Q), lalu klik OK, dan klik OK lagi pada dialog yang muncul.

368f17a974c75c73.png

  1. Terakhir, instal layanan Google Play dengan membuka tab SDK Tools, centang kotak di samping Google Play services, klik OK, lalu pilih OK lagi di dialog yang muncul**.**

497a954b82242f4b.png

API yang diperlukan

Pada Langkah 3 dari bagian berikut, aktifkan Maps SDK for Android dan Places API untuk codelab ini.

Memulai Google Maps Platform

Jika Anda belum pernah menggunakan Google Maps Platform, ikuti panduan Memulai Google Maps Platform atau tonton playlist Memulai Google Maps Platform untuk menyelesaikan langkah-langkah berikut:

  1. Membuat akun penagihan.
  2. Membuat project.
  3. Mengaktifkan API dan SDK Google Maps Platform (tercantum di bagian sebelumnya).
  4. Membuat kunci API.

Opsional: Android Emulator

Jika tidak memiliki perangkat yang mendukung ARCore, Anda dapat menggunakan Android Emulator untuk menyimulasikan tampilan AR serta memalsukan lokasi perangkat. Karena Anda juga akan menggunakan Sceneform dalam latihan ini, Anda juga harus memastikan untuk mengikuti langkah-langkah di bagian "Konfigurasikan emulator untuk mendukung Sceneform".

3. Mulai cepat

Untuk membantu Anda memulai secepatnya, berikut beberapa kode awal untuk membantu Anda mengikuti codelab ini. Anda dapat langsung ke bagian solusi, tetapi jika Anda ingin melihat semua langkah, baca semuanya.

Anda dapat menggandakan repositori jika sudah menginstal git.

git clone https://github.com/googlecodelabs/display-nearby-places-ar-android.git

Atau, Anda dapat mengklik tombol di bawah untuk mendownload kode sumber.

Setelah mendapatkan kode, lanjutkan dan buka project yang ada dalam direktori starter.

4. Ringkasan project

Pelajari kode yang Anda download dari langkah sebelumnya. Di dalam repositori ini, Anda akan menemukan satu modul bernama app, yang berisi paket com.google.codelabs.findnearbyplacesar.

AndroidManifest.xml

Atribut berikut dideklarasikan dalam file AndroidManifest.xml untuk memungkinkan Anda menggunakan fitur yang diperlukan pada codelab ini:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

<!-- Sceneform requires OpenGL ES 3.0 or later. -->
<uses-feature
   android:glEsVersion="0x00030000"
   android:required="true" />

<!-- Indicates that app requires ARCore ("AR Required"). Ensures the app is visible only in the Google Play Store on devices that support ARCore. For "AR Optional" apps remove this line. -->
<uses-feature android:name="android.hardware.camera.ar" />

Untuk uses-permission, yang menentukan izin mana yang harus diberikan oleh pengguna sebelum kemampuan tersebut dapat digunakan, hal-hal berikut dideklarasikan:

  • android.permission.INTERNET—kode ini dideklarasikan agar aplikasi Anda dapat melakukan operasi jaringan dan mengambil data melalui internet, seperti informasi tempat via Places API.
  • android.permission.CAMERA—akses kamera diperlukan agar Anda dapat menggunakan kamera perangkat untuk menampilkan objek dalam augmented reality.
  • android.permission.ACCESS_FINE_LOCATION—akses lokasi diperlukan agar Anda dapat mengambil data tempat-tempat terdekat berdasarkan lokasi perangkat.

Untuk uses-feature, yang menentukan fitur hardware mana yang diperlukan oleh aplikasi ini, hal-hal berikut dideklarasikan:

  • OpenGL ES versi 3.0 diperlukan.
  • Perangkat yang mendukung ARCore diperlukan.

Selain itu, tag metadata berikut ditambahkan di bagian objek aplikasi:

<application
  android:allowBackup="true"
  android:icon="@mipmap/ic_launcher"
  android:label="@string/app_name"
  android:roundIcon="@mipmap/ic_launcher_round"
  android:supportsRtl="true"
  android:theme="@style/AppTheme">

  <!--
     Indicates that this app requires Google Play Services for AR ("AR Required") and causes
     the Google Play Store to download and install Google Play Services for AR along with
     the app. For an "AR Optional" app, specify "optional" instead of "required".
  -->

  <meta-data
     android:name="com.google.ar.core"
     android:value="required" />

  <meta-data
     android:name="com.google.android.geo.API_KEY"
     android:value="@string/google_maps_key" />

  <!-- Additional elements here -->

</application>

Entri metadata pertama menunjukkan bahwa ARCore adalah persyaratan untuk menjalankan aplikasi ini dan yang kedua adalah cara Anda memberikan kunci API Google Maps Platform ke Maps SDK for Android.

build.gradle

Dalam build.gradle, dependensi tambahan berikut ditentukan:

dependencies {
    // Maps & Location
    implementation 'com.google.android.gms:play-services-location:17.0.0'
    implementation 'com.google.android.gms:play-services-maps:17.0.0'
    implementation 'com.google.maps.android:maps-utils-ktx:1.7.0'

    // ARCore
    implementation "com.google.ar.sceneform.ux:sceneform-ux:1.15.0"

    // Retrofit
    implementation "com.squareup.retrofit2:retrofit:2.7.1"
    implementation "com.squareup.retrofit2:converter-gson:2.7.1"
}

Berikut deskripsi singkat untuk masing-masing dependensi:

  • Library dengan ID grup com.google.android.gms, yaitu play-services-location dan play-services-maps, digunakan untuk mengakses informasi lokasi perangkat dan mengakses fungsi yang terkait dengan Google Maps.
  • com.google.maps.android:maps-utils-ktx adalah library ekstensi Kotlin (KTX) untuk Library Utilitas Maps SDK for Android. Fungsi akan digunakan dalam library ini untuk menempatkan objek virtual di ruang nyata nanti.
  • com.google.ar.sceneform.ux:sceneform-ux adalah library Sceneform, yang akan memungkinkan Anda merender tampilan 3D yang realistis tanpa harus mempelajari OpenGL.
  • Dependensi dalam ID grup com.squareup.retrofit2 adalah dependensi Retrofit, yang memungkinkan Anda menulis klien HTTP dengan cepat untuk berinteraksi dengan Places API.

Struktur project

Di sini, Anda akan menemukan paket dan file berikut:

  • **api—**paket ini berisi class yang digunakan untuk berinteraksi dengan Places API menggunakan Retrofit.
  • **ar—**paket ini berisi semua file yang terkait dengan ARCore.
  • **model—**paket ini berisi satu class data Place, yang digunakan untuk melakukan enkapsulasi satu tempat seperti yang ditampilkan oleh Places API.
  • MainActivity.kt—Ini adalah satu Activity yang terdapat dalam aplikasi Anda, yang akan menampilkan peta dan tampilan kamera.

5. Menyiapkan tampilan

Pahami komponen inti aplikasi, diawali dengan bagian-bagian augmented reality.

MainActivity berisi SupportMapFragment, yang akan menangani tampilan objek peta, dan subclass ArFragmentPlacesArFragment—yang menangani penayangan tampilan augmented reality.

Penyiapan augmented reality

Selain menampilkan tampilan augmented reality, PlacesArFragment juga akan menangani permintaan izin kamera dari pengguna, jika belum diberikan. Izin tambahan juga dapat diminta dengan mengganti metode getAdditionalPermissions. Karena Anda juga memerlukan izin akses lokasi dari pengguna, tentukan izin tersebut dan ganti metode getAdditionalPermissions:

class PlacesArFragment : ArFragment() {

   override fun getAdditionalPermissions(): Array<String> =
       listOf(Manifest.permission.ACCESS_FINE_LOCATION)
           .toTypedArray()
}

Menjalankan aplikasi

Lanjutkan dan buka kode kerangka di direktori starter pada Android Studio. Jika mengklik Run > Run 'app' dari toolbar dan men-deploy aplikasi ke perangkat atau emulator, Anda akan diminta untuk menyetujui izin akses lokasi dan kamera terlebih dahulu. Lanjutkan dan klik Allow, lalu setelah itu, Anda akan melihat tampilan kamera dan tampilan peta secara berdampingan seperti ini:

e3e3073d5c86f427.png

Mendeteksi bidang

Setelah melihat sekeliling Anda menggunakan kamera, Anda mungkin melihat beberapa titik putih terhampar di permukaan horizontal, seperti titik putih pada karpet di gambar ini.

2a9b6ea7dcb2e249.png

Titik putih ini adalah panduan yang disediakan oleh ARCore untuk menunjukkan bahwa bidang horizontal telah terdeteksi. Bidang yang terdeteksi ini memungkinkan Anda membuat "anchor" yang memungkinkan Anda menempatkan objek virtual di ruang nyata.

Untuk informasi selengkapnya tentang ARCore dan bagaimana ARCore memahami lingkungan di sekitar Anda, baca tentang konsep dasarnya.

6. Mendapatkan data tempat-tempat terdekat

Berikutnya, Anda perlu mengakses dan menampilkan lokasi perangkat saat ini, diikuti dengan mengambil data tempat-tempat terdekat menggunakan Places API.

Penyiapan Maps

Kunci API Google Maps Platform

Sebelumnya, Anda membuat kunci API Google Maps Platform untuk mengaktifkan pembuatan kueri Places API dan agar dapat menggunakan Maps SDK for Android. Lanjutkan dan buka file gradle.properties, lalu ganti string "YOUR API KEY HERE" dengan kunci API yang Anda buat.

Menampilkan lokasi perangkat di peta

Setelah Anda menambahkan kunci API, tambahkan bantuan di peta untuk membantu mengorientasikan posisi pengguna yang relatif terhadap peta. Untuk melakukannya, lihat metode setUpMaps dan di dalam panggilan mapFragment.getMapAsync, setel googleMap.isMyLocationEnabled ke true. Penyetelan tersebut akan menampilkan titik biru pada peta.

private fun setUpMaps() {
   mapFragment.getMapAsync { googleMap ->
       googleMap.isMyLocationEnabled = true
       // ...
   }
}

Mendapatkan lokasi saat ini

Untuk mendapatkan lokasi perangkat, Anda harus menggunakan class FusedLocationProviderClient. Mendapatkan instance ini sudah dilakukan dalam metode onCreate dari MainActivity. Untuk memanfaatkan objek ini, lengkapi metode getCurrentLocation, yang menerima argumen lambda sehingga lokasi dapat diteruskan ke pemanggil metode ini.

Untuk menyelesaikan metode ini, Anda dapat mengakses properti lastLocation dari objek FusedLocationProviderClient yang diikuti dengan menambahkan addOnSuccessListener, seperti:

fusedLocationClient.lastLocation.addOnSuccessListener { location ->
    currentLocation = location
    onSuccess(location)
}.addOnFailureListener {
    Log.e(TAG, "Could not get location")
}

Metode getCurrentLocation dipanggil dari dalam lambda yang disediakan di getMapAsync dalam metode setUpMaps, yang digunakan untuk mengambil data tempat-tempat terdekat.

Memulai panggilan jaringan tempat

Dalam panggilan metode getNearbyPlaces, perhatikan bahwa parameter berikut diteruskan ke metode placesServices.nearbyPlaces—kunci API, lokasi perangkat, radius dalam meter (yang ditetapkan ke 2 km), dan jenis tempat (saat ini ditetapkan ke park).

val apiKey = "YOUR API KEY"
placesService.nearbyPlaces(
   apiKey = apiKey,
   location = "${location.latitude},${location.longitude}",
   radiusInMeters = 2000,
   placeType = "park"
)

Untuk menyelesaikan panggilan jaringan, lanjutkan dan teruskan kunci API yang Anda tentukan di file gradle.properties. Cuplikan kode berikut ditentukan dalam file build.gradle Anda pada konfigurasi android > defaultConfig:

android {
   defaultConfig {
       resValue "string", "google_maps_key", (project.findProperty("GOOGLE_MAPS_API_KEY") ?: "")
   }
}

Tindakan ini akan membuat nilai resource string google_maps_key tersedia pada waktu build.

Untuk menyelesaikan panggilan jaringan, Anda dapat membaca resource string ini melalui getString pada objek Context.

val apiKey = this.getString(R.string.google_maps_key)

7. Tempat di AR

Hingga saat ini, Anda telah melakukan hal berikut:

  1. Meminta izin kamera dan akses lokasi dari pengguna saat pertama kali menjalankan aplikasi
  2. Menyiapkan ARCore untuk mulai melacak bidang horizontal
  3. Menyiapkan Maps SDK dengan kunci API Anda
  4. Mendapatkan lokasi perangkat saat ini
  5. Mengambil data tempat-tempat terdekat (khususnya taman) menggunakan Places API

Langkah selanjutnya untuk menyelesaikan latihan ini adalah dengan memosisikan tempat-tempat yang Anda ambil datanya di augmented reality.

Pemahaman tentang tampilan

ARCore dapat memahami tampilan dunia nyata melalui kamera perangkat dengan mendeteksi titik menarik dan berbeda yang disebut titik fitur di setiap frame gambar. Ketika titik fitur ini dikelompokkan dan tampak berada di bidang horizontal yang sama, seperti meja dan lantai, ARCore dapat menampilkan titik fitur ini di aplikasi sebagai bidang horizontal.

Seperti yang Anda lihat sebelumnya, ARCore membantu memandu pengguna saat bidang terdeteksi dengan menampilkan titik putih.

2a9b6ea7dcb2e249.png

Menambahkan anchor

Setelah bidang terdeteksi, Anda dapat menambahkan objek yang disebut anchor. Dengan menggunakan anchor, Anda dapat menempatkan objek virtual dan menjamin bahwa objek tersebut akan tetap berada di posisi yang sama di ruang nyata. Lanjutkan dan ubah kode untuk mengaitkan anchor setelah bidang terdeteksi.

Di setUpAr, OnTapArPlaneListener dikaitkan ke PlacesArFragment. Pemroses ini dipanggil setiap kali bidang terdeteksi di tampilan AR. Dalam panggilan ini, Anda dapat membuat Anchor dan AnchorNode dari HitResult yang disediakan di pemroses, seperti:

arFragment.setOnTapArPlaneListener { hitResult, _, _ ->
   val anchor = hitResult.createAnchor()
   anchorNode = AnchorNode(anchor)
   anchorNode?.setParent(arFragment.arSceneView.scene)
   addPlaces(anchorNode!!)
}

AnchorNode adalah tempat Anda akan menyertakan objek node turunan—instance PlaceNode—pada tampilan yang ditangani dalam panggilan metode addPlaces.

Menjalankan aplikasi

Jika Anda menjalankan aplikasi dengan modifikasi di atas, lihat di sekitar Anda hingga bidang terdeteksi. Lanjutkan dan ketuk titik putih yang menunjukkan sebuah bidang. Setelah melakukannya, Anda akan melihat penanda di peta untuk semua taman terdekat di sekitar Anda. Namun, jika Anda perhatikan, objek virtual tersebut tertahan di anchor yang dibuat dan tidak ditempatkan pada lokasi yang relatif terhadap tempat taman tersebut berada di ruang nyata.

f93eb87c98a0098d.png

Untuk langkah terakhir, Anda dapat memperbaikinya menggunakan Library Utilitas Maps SDK for Android dan SensorManager di perangkat.

8. Memosisikan tempat

Agar dapat menempatkan ikon tempat virtual dalam augmented reality pada arah tujuan yang akurat, Anda memerlukan dua informasi:

  • Arah utara sebenarnya
  • Sudut antara utara dan masing-masing tempat

Menentukan arah utara

Arah utara dapat ditentukan menggunakan sensor posisi (geomagnet dan akselerometer) yang tersedia di perangkat. Dengan menggunakan kedua sensor ini, Anda dapat mengumpulkan informasi real-time tentang posisi perangkat di ruang nyata. Untuk informasi selengkapnya tentang sensor posisi, baca Menghitung orientasi perangkat.

Untuk mengakses sensor ini, Anda harus mendapatkan SensorManager, yang diikuti dengan mendaftarkan SensorEventListener pada sensor tersebut. Langkah-langkah ini sudah dilakukan untuk Anda pada metode siklus proses MainActivity:

override fun onCreate(savedInstanceState: Bundle?) {
   super.onCreate(savedInstanceState)
   // ...
   sensorManager = getSystemService()!!
   // ...
}

override fun onResume() {
   super.onResume()
   sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD)?.also {
       sensorManager.registerListener(
           this,
           it,
           SensorManager.SENSOR_DELAY_NORMAL
       )
   }
   sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)?.also {
       sensorManager.registerListener(
           this,
           it,
           SensorManager.SENSOR_DELAY_NORMAL
       )
   }
}

override fun onPause() {
   super.onPause()
   sensorManager.unregisterListener(this)
}

Dalam metode onSensorChanged, objek SensorEvent disediakan, yang berisi detail tentang data sensor tertentu yang berubah dari waktu ke waktu. Lanjutkan dan tambahkan kode berikut ke dalam metode tersebut:

override fun onSensorChanged(event: SensorEvent?) {
   if (event == null) {
       return
   }
   if (event.sensor.type == Sensor.TYPE_ACCELEROMETER) {
       System.arraycopy(event.values, 0, accelerometerReading, 0, accelerometerReading.size)
   } else if (event.sensor.type == Sensor.TYPE_MAGNETIC_FIELD) {
       System.arraycopy(event.values, 0, magnetometerReading, 0, magnetometerReading.size)
   }

   // Update rotation matrix, which is needed to update orientation angles.
   SensorManager.getRotationMatrix(
       rotationMatrix,
       null,
       accelerometerReading,
       magnetometerReading
   )
   SensorManager.getOrientation(rotationMatrix, orientationAngles)
}

Kode di atas memeriksa jenis sensor dan, bergantung pada jenisnya, kode tersebut akan memperbarui pembacaan sensor yang sesuai (baik pembacaan akselerometer atau magnetometer). Dengan menggunakan pembacaan sensor ini, jumlah derajat dari utara relatif terhadap perangkat sekarang dapat ditentukan (yaitu, nilai orientationAngles[0]).

Arah tujuan sferikal

Karena utara telah ditentukan, langkah berikutnya adalah menentukan sudut antara utara dan masing-masing tempat, diikuti dengan menggunakan informasi tersebut untuk memosisikan tempat pada arah tujuan yang tepat di augmented reality.

Untuk menghitung arah tujuan, Anda akan menggunakan Library Utilitas Maps SDK for Android, yang berisi sejumlah fungsi bantuan untuk menghitung jarak dan arah tujuan melalui geometri sferikal. Untuk informasi selengkapnya, baca ringkasan library ini.

Selanjutnya, Anda akan menggunakan metode sphericalHeading di library utilitas, yang menghitung arah tujuan/sudut arah antara dua objek LatLng. Informasi ini diperlukan di dalam metode getPositionVector yang ditentukan di Place.kt. Metode ini pasti akan menampilkan objek Vector3, yang kemudian akan digunakan oleh setiap PlaceNode sebagai posisi lokalnya di ruang AR.

Lanjutkan dan ganti definisi arah tujuan dalam metode tersebut dengan kode berikut:

val heading = latLng.sphericalHeading(placeLatLng)

Tindakan ini akan menghasilkan definisi metode berikut:

fun Place.getPositionVector(azimuth: Float, latLng: LatLng): Vector3 {
   val placeLatLng = this.geometry.location.latLng
   val heading = latLng.sphericalHeading(placeLatLng)
   val r = -2f
   val x = r * sin(azimuth + heading).toFloat()
   val y = 1f
   val z = r * cos(azimuth + heading).toFloat()
   return Vector3(x, y, z)
}

Posisi lokal

Langkah terakhir untuk mengorientasikan tempat secara tepat di AR adalah dengan menggunakan hasil getPositionVector saat objek PlaceNode ditambahkan ke tampilan. Lanjutkan dan buka addPlaces di MainActivity, tepat di bawah baris tempat induk ditetapkan pada setiap placeNode (tepat di bawah placeNode.setParent(anchorNode)). Setel localPosition placeNode ke hasil pemanggilan getPositionVector seperti berikut:

val placeNode = PlaceNode(this, place)
placeNode.setParent(anchorNode)
placeNode.localPosition = place.getPositionVector(orientationAngles[0], currentLocation.latLng)

Secara default, metode getPositionVector menetapkan jarak y node menjadi 1 meter seperti yang ditetapkan oleh nilai y pada metode getPositionVector. Jika Anda ingin menyesuaikan jarak ini, misalnya 2 meter, lanjutkan dan ubah nilai tersebut sesuai kebutuhan.

Dengan perubahan ini, objek PlaceNode yang ditambahkan kini berorientasi ke arah tujuan yang benar. Sekarang lanjutkan dan jalankan aplikasi untuk melihat hasilnya.

9. Selamat

Selamat karena telah membaca sampai bagian ini.

Pelajari lebih lanjut