Android'de AR bölgesindeki yakındaki yerleri görüntüleme (Kotlin)

1. Başlamadan önce

Soyut

Bu codelab'de, yakındaki yerleri Android'de artırılmış gerçeklik (AR) ile göstermek için Google Haritalar Platformu'ndaki verileri nasıl kullanacağınızı öğretiyoruz.

2344909dd9a52c60.png

Ön koşullar

  • Android Studio'yu kullanarak Android geliştirmeyle ilgili temel bilgiler
  • Kotlin aşinalığı

Neler öğreneceksiniz?

  • Cihazın kamerasına ve konumuna erişmek için kullanıcıdan izin isteyin.
  • Cihazın konumunu etrafındaki yerleri getirmek için Places API ile entegrasyon yapın.
  • Yatay düzlem yüzeylerini bulmak için ARCore ile entegre edin. Böylece, sanal nesneler Sceneform kullanılarak sabitlenebilir ve 3D bir alana eklenebilir.
  • SensorManager'ı kullanarak cihazın uzaydaki konumu hakkında bilgi toplayın. Ayrıca, sanal nesneleri doğru başlığa yerleştirmek için Android Yardımcı Kitaplık Kitaplığı'nı kullanın.

Gerekenler

2. Hazırlanın

Android Studio

Bu codelab'de Android 10.0 (API düzeyi 29) kullanılmaktadır ve Android Studio'da Google Play hizmetlerinin yüklü olması gerekir. Bu bağımlılıkların her ikisini de yüklemek için aşağıdaki adımları tamamlayın:

  1. Araçlar > SDK Manager'ı tıklayarak erişebileceğiniz SDK Yöneticisi'ne gidin.

6c44a9cb9cf6c236.png

  1. Android 10.0'ın yüklü olup olmadığını kontrol edin. Açık değilse Android 10.0 (Q) seçeneğinin yanındaki onay kutusunu işaretleyip Tamam'ı tıkladıktan sonra açılan iletişim kutusunda tekrar Tamam'ı tıklayın.

368f17a974c75c73.png

  1. Son olarak, SDK Tools (SDK Araçları) sekmesine gidip Google Play hizmetleri'nin yanındaki onay kutusunu işaretleyin, Tamam'ı tıklayın ve açılan iletişim kutusunda tekrar OK'i (Tamam) seçin**.**

497a954b82242f4b.png

Gerekli API'ler

Aşağıdaki bölümün 3. adımında, bu codelab için Android için Haritalar SDK'sı ve Places API'yi etkinleştirin.

Google Haritalar Platformu'nu kullanmaya başlayın

Google Haritalar Platformu'nu daha önce kullanmadıysanız Google Haritalar Platformu'nu Kullanmaya Başlama rehberini izleyerek veya Google Haritalar Platformu oynatma listesini izleyerek aşağıdaki adımları tamamlayın:

  1. Faturalandırma hesabı oluşturun.
  2. Bir proje oluşturun.
  3. Google Haritalar Platformu API'lerini ve SDK'larını etkinleştirin (bir önceki bölümde listelenmiştir).
  4. API anahtarı oluşturun.

İsteğe bağlı: Android Emülatör

ARCore destekli bir cihazınız yoksa alternatif olarak AR sahnesi simüle etmek ve cihazınızın konumunu taklit etmek için Android Emülatör'ü kullanabilirsiniz. Bu egzersizde Sceneform da kullanacağınız göz önünde bulundurulduğunda, "Emülatörü "Sceneform"u destekleyecek şekilde yapılandırma" bölümündeki adımları da uygulamanız gerekir.

3. Hızlı başlangıç

En kısa sürede başlamanıza yardımcı olmak için aşağıda, bu codelab'i takip etmenize yardımcı olacak bazı başlangıç kodları verilmiştir. Çözüme geçebilirsiniz, ancak tüm adımları görmek isterseniz okumaya devam edin.

git yüklediyseniz kod deposunu klonlayabilirsiniz.

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

Alternatif olarak, kaynak kodu indirmek için aşağıdaki düğmeyi tıklayabilirsiniz.

Kodu aldıktan sonra starter dizininde bulunan projeyi açın.

4. Projeye genel bakış

Önceki adımda indirdiğiniz kodu keşfedin. Bu veri havuzunun içinde com.google.codelabs.findnearbyplacesar paketini içeren app adlı tek bir modül göreceksiniz.

AndroidManifest.xml

Bu codelab'de gerekli özellikleri kullanabilmeniz için AndroidManifest.xml dosyasında aşağıdaki özellikler beyan edilir:

<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" />

Bu özelliklerin kullanılabilmesi için önce kullanıcı tarafından hangi izinlerin verilmesi gerektiğini belirten uses-permission öğesinde şunlar beyan edilir:

  • android.permission.INTERNET: Böylece uygulamanızın ağ işlemleri yapabilir ve internet üzerinden Yerler API'si aracılığıyla yer bilgileri gibi verileri alabilirsiniz.
  • android.permission.CAMERA: Nesneleri artırılmış gerçeklikte görüntülemek için cihazın kamerasını kullanmak üzere kamera erişimi gereklidir.
  • android.permission.ACCESS_FINE_LOCATION: Yakındaki yerleri cihazın konumuna göre getirebilmeniz için konum erişimi gereklidir.

Bu uygulamanın ihtiyaç duyduğu donanım özelliklerini belirten uses-feature için aşağıdakiler bildirilir:

  • OpenGL ES sürüm 3.0 gereklidir.
  • ARCore uyumlu cihaz gerekiyor.

Ayrıca, aşağıdaki meta veri etiketleri uygulama nesnesinin altına eklenir:

<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>

İlk meta veri girişi, ARCore'un bu uygulamanın çalışması için bir gereklilik olduğunu, ikincisi ise Android için Haritalar SDK'sına Google Maps Platform API anahtarınızı nasıl sağladığınızı belirtmektir.

build.gradle

build.gradle içinde, aşağıdaki ek bağımlılıklar belirtilir:

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"
}

Aşağıda her bağımlının kısa bir açıklaması verilmiştir:

  • com.google.android.gms grup kimliğine sahip kitaplıklar (play-services-location ve play-services-maps) cihazın konum bilgilerine erişmek ve Google Haritalar ile ilgili işlevlere erişmek için kullanılır.
  • com.google.maps.android:maps-utils-ktx, Android Yardımcı Program Kitaplığı'nın Haritalar SDK'sı için Kotlin uzantıları (KTX) kitaplığıdır. İşlevsellik bu kitaplıkta daha sonra sanal nesneleri gerçek zamanlı olarak konumlandırmak için kullanılır.
  • com.google.ar.sceneform.ux:sceneform-ux, OpenGL'yi öğrenmek zorunda kalmadan gerçekçi 3D sahneler oluşturmanıza olanak tanıyan Sceneform kitaplığıdır.
  • com.squareup.retrofit2 grup kimliği içindeki bağımlılar, Places API ile etkileşim kurmak için hızlıca bir HTTP istemcisi yazmanıza olanak tanıyan Retrofit bağımlılıklarıdır.

Proje yapısı

Burada, aşağıdaki paketleri ve dosyaları bulabilirsiniz:

  • **api:**Bu paket, Retrofit kullanarak Places API ile etkileşimde bulunmak için kullanılan sınıfları içerir.
  • **ar—**Bu paket ARCore ile ilgili tüm dosyaları içermektedir.
  • **model—**Bu paket, tek bir yeri Place API'nin döndürdüğü şekilde doldurmak için kullanılan tek bir veri sınıfı Place içerir.
  • MainActivity.kt: Bu, uygulama içinde yer alan ve bir harita ile kamera görünümünü gösteren tek Activity.

5. Sahne ayarlama

Artırılmış gerçeklik parçalarıyla başlayarak uygulamanın temel bileşenlerini inceleyin.

MainActivity, harita nesnesinin işleneceği bir SupportMapFragment ve artırılmış gerçeklik sahnesinin gösterilmesini sağlayan bir ArFragmentPlacesArFragment öğesinin alt sınıfını içerir.

Artırılmış gerçeklik kurulumu

PlacesArFragment, artırılmış gerçeklik sahnesini görüntülemenin yanı sıra, daha önce verilmediği takdirde kullanıcıdan kamera izni istemeyi de yerine getirir. getAdditionalPermissions yöntemi geçersiz kılınarak ek izinler de istenebilir. Buna ek olarak, konum izni verilmesi içingetAdditionalPermissions

class PlacesArFragment : ArFragment() {

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

Çalıştır

Şimdi Android Studio'da starter dizinindeki iskelet kodunu açın. Araç çubuğundan Çalıştır > Çalıştır 'uygulamasını##39; seçeneğini tıklayıp uygulamayı cihazınıza veya emülatöre dağıtıyorsanız öncelikle konum ve kamera iznini etkinleştirmeniz istenir. Artık İzin ver'i tıklayabilirsiniz. Bunu yaptığınızda aşağıdaki gibi bir kamera görünümü ve bir harita görünümü görürsünüz:

e3e3073d5c86f427.png

Uçakları algılama

Kameranızın bulunduğu ortamda etrafa baktığınızda, bu resimdeki halı üzerinde beyaz noktalar gibi yatay yüzeylerin üzerine yerleştirilmiş birkaç beyaz nokta görebilirsiniz.

2a9b6ea7dcb2e249.png

Bu beyaz noktalar, yatay bir uçağın tespit edildiğini belirtmek için ARCore tarafından sağlanan yönergelerdir. Algılanan bu uçaklar, sanal nesneleri gerçek zamanlı olarak konumlandırabilmeniz için "sabit" adı verilen öğeler oluşturmanıza olanak tanır.

ARCore ve etrafınızdaki ortamı nasıl anladığı hakkında daha fazla bilgi edinmek için temel kavramları hakkında bilgi edinin.

6. Yakındaki yerleri alın

Daha sonra, cihazın mevcut konumuna erişip görüntülemeniz ve ardından Places API'sini kullanarak yakındaki yerleri getirmeniz gerekir.

Haritalar kurulumu

Google Maps Platform API anahtarı

Daha önce, Places API'yi sorgulamayı etkinleştirmek ve Android için Haritalar SDK'sını kullanabilmek amacıyla bir Google Maps Platform API anahtarı oluşturmuştunuz. Şimdi gradle.properties dosyasını açıp "YOUR API KEY HERE" dizesini oluşturduğunuz API anahtarıyla değiştirin.

Cihaz konumunu haritada göster

API anahtarınızı ekledikten sonra, kullanıcıları haritaya göre yönlendirmelerine yardımcı olmak için haritaya bir yardımcı ekleyin. Bunu yapmak için setUpMaps yöntemine gidin ve mapFragment.getMapAsync aramasının içinde googleMap.isMyLocationEnabled değerini true. olarak ayarlayın. Bunu yaptığınızda harita üzerinde mavi nokta gösterilir.

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

Mevcut konumu al

Cihazın konumunu almak için FusedLocationProviderClient sınıfını kullanmanız gerekir. Bunun bir MainActivity örneği, onCreate yönteminde zaten kazanılmış. Bu nesneden yararlanmak için bir lambda bağımsız değişkenini kabul eden getCurrentLocation yöntemini doldurun. Böylece bir yöntem, bu yöntemin arayanına iletilebilir.

Bu yöntemi tamamlamak için FusedLocationProviderClient nesnesinin lastLocation özelliğine erişebilir ve ardından aşağıdaki gibi bir addOnSuccessListener ekleyebilirsiniz:

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

getCurrentLocation yöntemi, getMapAsync içinde sağlanan lambdanın içinden, yakındaki yerlerin getirildiği setUpMaps yöntemiyle çağrılır.

Yer ağ çağrısını başlatma

getNearbyPlaces yöntemi çağrısında, aşağıdaki parametrelerin placesServices.nearbyPlaces yöntemine iletildiğini unutmayın: API anahtarı, cihazın konumu, metre cinsinden yarıçap (2 km olarak ayarlanır) ve bir yer türü (şu anda park olarak ayarlanmıştır).

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

Ağ çağrısını tamamlamak için gradle.properties dosyanızda tanımladığınız API anahtarını iletin. Aşağıdaki kod snippet'i build.gradle dosyanızda android > defaultConfig yapılandırması altında tanımlanır:

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

Bu işlem, derleme sırasında google_maps_key dize kaynağı değerini kullanılabilir hale getirir.

Ağ çağrısını tamamlamak için bu dize kaynağını getString nesnesini Context nesnesinde okuyabilirsiniz.

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

7. Artırılmış Gerçeklik'te yerler

Şimdiye kadar aşağıdakileri yaptınız:

  1. Uygulamayı ilk kez çalıştırırken kullanıcıdan kamera ve konum izinleri istendi
  2. Yatay uçakları izlemeye başlamak için ARCore'u kurun
  3. Haritalar SDK'sını API anahtarınızla ayarlama
  4. Cihazın mevcut konumu alındı
  5. Yerler API'si kullanılarak yakındaki yerler (özellikle parklar) getirildi

Bu egzersizi tamamlamak için kalan adım, artırılmış gerçeklik olarak getirdiğiniz yerleri konumlandırmaktır.

Sahne anlama

ARCore, her karede özellik noktaları adı verilen ilginç ve farklı noktaları tespit ederek cihazın kamerasıyla gerçek dünyadaki sahneyi anlayabilir. Bu özellik noktaları birleştirilip masalar ve zeminler gibi yaygın olarak kullanılan yatay bir düzlemde yattığında ARCore, bu özelliği uygulamada yatay düzlem olarak kullanabilir.

Daha önce gördüğünüz gibi, ARCore, beyaz nokta görüntüleyerek bir uçak algılandığında kullanıcıya yol göstermeye yardımcı olur.

2a9b6ea7dcb2e249.png

Sabitleme ekleme

Uçak algılandıktan sonra sabit adı verilen bir nesne ekleyebilirsiniz. Bir bağlantı aracı üzerinden, sanal nesneler yerleştirebilir ve bu nesnelerin uzayda aynı konumda kalacağını garanti edebilirsiniz. Devam edin ve uçağın algılandığı kodu eklemek için kodu değiştirin.

setUpAr içinde, PlacesArFragment öğesine bir OnTapArPlaneListener eklenir. Bu dinleyici, AR sahnesinde bir uça dokunulduğunda çağrılır. Bu görüşmede, dinleyicide sağlanan HitResult kaynağından şu şekilde bir Anchor ve AnchorNode oluşturabilirsiniz:

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

AnchorNode, addPlaces yöntem çağrısında işlenen sahnede alt düğüm nesnelerini (PlaceNode örnek) ekleyeceğiniz yerdir.

Çalıştır

Uygulamayı yukarıdaki değişikliklerle çalıştırırsanız bir uçak algılanana kadar çevrenize bakın. Şimdi uçağı gösteren beyaz noktalara dokunabilirsin. Bunu yaptığınızda artık harita üzerinde, en yakın parkların işaretçilerini göreceksiniz. Ancak sanal nesneler, oluşturulmuş çapada takılı kalır ve bu parkların uzayda bulunduğu yere göre yerleştirilmez.

f93eb87c98a0098d.png

Son adımda, cihazınızda Android Yardımcı Program Kitaplığı için Haritalar SDK'sı ve SensorManager'ı kullanarak bu sorunu düzeltebilirsiniz.

8. Yerleri konumlandırma

Sanal yer simgesini artırılmış gerçeklikte doğru bir başlıkta konumlandırabilmek için iki bilgi gerekir:

  • Doğru kuzey
  • Kuzey ve her yer arasındaki açı

Kuzeyi belirleme

Cihazda bulunan konum sensörleri (jeomanyetik ve ivme ölçer) kullanılarak Kuzey değeri belirlenebilir. Bu iki sensörü kullanarak cihazın uzaydaki konumuyla ilgili gerçek zamanlı bilgileri toplayabilirsiniz. Konum sensörleri hakkında daha fazla bilgi edinmek için Cihazın yönünü işleme başlıklı makaleyi inceleyin.

Bu sensörlere erişmek için önce SensorManager sensörü edinmeniz ve ardından bu sensörlere SensorEventListener kaydettirmeniz gerekir. Bu adımlar sizin için MainActivity'yaşam döngüsünde zaten yapılmış:

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)
}

onSensorChanged yönteminde, zaman içinde değişen belirli bir sensörle ilgili ayrıntıları içeren bir SensorEvent nesnesi sağlanır. Şimdi aşağıdaki kodu bu yönteme ekleyin:

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)
}

Yukarıdaki kod, sensör türünü kontrol eder ve türe bağlı olarak uygun sensör okuma işlemini (ivme ölçer veya manyetometre okuması) günceller. Bu sensör ölçümleri kullanılarak, kuzeyden cihaza göre kaç derecenin değeri artık belirlenebilir (yani orientationAngles[0] değeri).

Küresel başlık

Kuzey bölümü belirlendiğine göre sonraki adım, kuzey ile her yer arasındaki açıyı belirlemek ve daha sonra bu bilgileri artırılmış gerçeklikte doğru başlıkta konumlandırmak için kullanmaktır.

Başlığı hesaplamak için, geometrik biçimdeki mesafeleri ve başlıkları hesaplamak üzere kullanılan bir dizi yardımcı işlev içeren Android Yardımcı Program Kitaplığı'nın SDK'sını kullanırsınız. Daha fazla bilgi için kitaplığa genel bakış başlıklı makaleyi okuyun.

Ardından, yardımcı program kitaplığındaki sphericalHeading yöntemini kullanırsınız. Bu yöntem, iki LatLng nesnesi arasındaki başlığı/konumu hesaplar. Bu bilgi, Place.kt öğesinde tanımlanan getPositionVector yöntemi içinde gereklidir. Bu yöntem, daha sonra, her bir PlaceNode tarafından AR alanında yerel konumu olarak kullanılacak bir Vector3 nesnesi döndürür.

Devam edip bu yöntemdeki başlık tanımını aşağıdaki gibi değiştirin:

val heading = latLng.sphericalHeading(placeLatLng)

Bu, aşağıdaki yöntem tanımını gerektirir:

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)
}

Yerel konum

Artırılmış gerçeklikte (AR) yerleri doğru şekilde yönlendirmek için son adım, PlaceNode nesneleri sahneye eklenirken getPositionVector sonucunu kullanmaktır. Devam edip MainActivity içinde, her placeNode üzerinde ebeveynin bulunduğu satırın hemen altındaki addPlaces bölümüne gidin (placeNode.setParent(anchorNode) ifadesinin hemen altında). placeNode localPosition öğesini şu şekilde arayın:

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

Varsayılan olarak, getPositionVector yöntemi, düğümün y mesafesini getPositionVector yönteminde y değeriyle belirtildiği şekilde 1 metre olarak ayarlar. Örneğin, bu mesafeyi ayarlamak istiyorsanız 2 metreye gidip bu değeri gereken şekilde değiştirin.

Bu değişiklikle birlikte, eklenen PlaceNode nesneleri artık doğru başlığa odaklanmalıdır. Şimdi sonucu görmek için uygulamayı çalıştırın.

9. Tebrikler

Tebrikler!

Daha fazla bilgi