1. Başlamadan önce
Özet (Abstract)
Bu codelab'de, Android'de artırılmış gerçeklik (AR) kullanarak yakındaki yerleri göstermek için Google Haritalar Platformu'ndaki verilerin nasıl kullanılacağı açıklanmaktadır.
Ön koşullar
- Android Studio ile Android geliştirme konusunda temel bilgi
- Kotlin hakkında bilgi
Neler öğreneceksiniz?
- Cihazın kamerasına ve konumuna erişmek için kullanıcıdan izin isteyin.
- Cihazın konumunun etrafındaki yerleri getirmek için Places API ile entegre edin.
- Sanal nesnelerin Sceneform kullanılarak 3D alana yerleştirilebilmesi ve sabitlenebilmesi için yatay düzlem yüzeylerini bulmak üzere ARCore ile entegrasyon.
- SensorManager kullanarak cihazın uzaydaki konumu hakkında bilgi toplayın ve sanal nesneleri doğru yöne yerleştirmek için Android için Haritalar SDK'sı Yardımcı Kitaplığı'nı kullanın.
Gerekenler
- Android Studio 2020.3.1 veya sonraki sürümler
- OpenGL ES 3.0 veya sonraki sürümleri destekleyen bir geliştirme makinesi
- ARCore destekli bir cihaz veya ARCore'un etkinleştirildiği bir Android emülatörü (talimatlar sonraki adımda verilmiştir)
2. Hazırlanın
Android Studio
Bu codelab'de Android 10.0 (API düzeyi 29) kullanılır ve Android Studio'da Google Play Hizmetleri'nin yüklü olması gerekir. Bu bağımlılıkların ikisini de yüklemek için aşağıdaki adımları tamamlayın:
- Tools (Araçlar) > SDK Manager'ı (SDK Yöneticisi) tıklayarak erişebileceğiniz SDK Yöneticisi'ne gidin.
- Android 10.0'ın yüklü olup olmadığını kontrol edin. Aksi takdirde, Android 10.0 (Q)'nun yanındaki onay kutusunu işaretleyip Tamam'ı, ardından açılan iletişim kutusunda tekrar Tamam'ı tıklayarak yükleyin.
- Son olarak, SDK Tools sekmesine giderek Google Play Hizmetleri'ni yükleyin, Google Play Hizmetleri'nin yanındaki onay kutusunu işaretleyin, Tamam'ı tıklayın ve ardından açılan iletişim kutusunda tekrar Tamam'ı seçin**.**
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 aşağıdaki adımları tamamlamak için Google Haritalar Platformu'nu Kullanmaya Başlama Kılavuzu'nu inceleyin veya Google Haritalar Platformu'nu Kullanmaya Başlama oynatma listesini izleyin:
- Faturalandırma hesabı oluşturun.
- Proje oluşturun.
- Google Haritalar Platformu API'lerini ve SDK'larını (önceki bölümde listelenmiştir) etkinleştirin.
- API anahtarı oluşturun.
İsteğe bağlı: Android Emulator
ARCore destekli bir cihazınız yoksa alternatif olarak Android Emulator'ı kullanarak bir AR sahnesini simüle edebilir ve cihazınızın konumunu sahte olarak ayarlayabilirsiniz. Bu alıştırmada Sceneform'u da kullanacağınız için "Configure the emulator to support Sceneform " (Sceneform'u destekleyecek şekilde emülatörü yapılandırma) bölümündeki adımları da uygulamanız gerekir.
3. Hızlı başlangıç
Mümkün olduğunca hızlı bir şekilde başlamanıza yardımcı olmak için bu codelab'i takip etmenize yardımcı olacak başlangıç kodunu aşağıda bulabilirsiniz. Çözüme geçebilirsiniz ancak tüm adımları görmek istiyorsanız okumaya devam edin.
git
yüklüyse depoyu klonlayabilirsiniz.
git clone https://github.com/googlecodelabs/display-nearby-places-ar-android.git
Alternatif olarak, kaynak kodunu 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 inceleyin. Bu deponun içinde, com.google.codelabs.findnearbyplacesar
paketini içeren app
adlı tek bir modül bulunur.
AndroidManifest.xml
Bu codelab'de gerekli özellikleri kullanabilmeniz için AndroidManifest.xml
dosyasında aşağıdaki özellikler tanımlanır:
<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 kullanıcının hangi izinleri vermesi gerektiğini belirten uses-permission
için aşağıdakiler beyan edilir:
android.permission.INTERNET
: Bu izin, uygulamanızın ağ işlemleri yapabilmesi ve internet üzerinden veri (ör. Places API aracılığıyla yer bilgileri) alabilmesi için gereklidir.android.permission.CAMERA
: Cihazın kamerasını kullanarak nesneleri artırılmış gerçeklikte görüntüleyebilmeniz için kamera erişimi gerekir.android.permission.ACCESS_FINE_LOCATION
—Cihazın konumuna göre yakındaki yerleri getirebilmeniz için konum erişimi gerekir.
Bu uygulamanın hangi donanım özelliklerine ihtiyaç duyduğunu belirten uses-feature
için aşağıdakiler beyan edilir:
- OpenGL ES sürümü 3.0 gereklidir.
- ARCore özellikli bir cihaz gereklidir.
Ayrıca, uygulama nesnesi altına aşağıdaki meta veri etiketleri 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 gerekli olduğunu belirtmek, ikincisi ise Google Haritalar Platformu API anahtarınızı Android için Haritalar SDK'sına nasıl sağlayacağınızı belirtmek içindir.
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"
}
Her bağımlılığın kısa açıklaması aşağıda verilmiştir:
com.google.android.gms
grup kimliğine sahip kitaplıklar (play-services-location
veplay-services-maps
) cihazın konum bilgilerine ve Google Haritalar ile ilgili işlevlere erişmek için kullanılır.com.google.maps.android:maps-utils-ktx
, Android için Haritalar SDK'sı Yardımcı Kitaplığı'nın Kotlin uzantıları (KTX) kitaplığıdır. Bu kitaplıkta, sanal nesneleri daha sonra gerçek alana yerleştirmek için işlevsellik kullanılır.com.google.ar.sceneform.ux:sceneform-ux
, OpenGL'yi öğrenmenize gerek kalmadan gerçekçi 3D sahneler oluşturmanızı sağlayan Sceneform kitaplığıdır.- Grup kimliği
com.squareup.retrofit2
içindeki bağımlılıklar, Places API ile etkileşim kurmak için hızlı bir şekilde HTTP istemcisi yazmanızı sağlayan Retrofit bağımlılıklarıdır.
Proje yapısı
Burada aşağıdaki paketleri ve dosyaları bulabilirsiniz:
- **api:**Bu paket, Retrofit kullanılarak 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çerir.
- **model:**Bu pakette, Places API tarafından döndürülen tek bir yeri kapsüllemek için kullanılan tek bir veri sınıfı
Place
bulunur. - MainActivity.kt: Bu, uygulamanızda bulunan tek
Activity
olup harita ve kamera görünümü gösterir.
5. Sahneyi ayarlama
Artırılmış gerçeklik parçalarıyla başlayarak uygulamanın temel bileşenlerini inceleyin.
MainActivity
, harita nesnesinin gösterilmesini sağlayan bir SupportMapFragment
ve artırılmış gerçeklik sahnesinin gösterilmesini sağlayan bir ArFragment
alt sınıfını—PlacesArFragment
—içerir.
Artırılmış gerçeklik kurulumu
PlacesArFragment
, artırılmış gerçeklik sahnesini göstermenin yanı sıra, daha önce verilmemişse kullanıcıdan kamera izni istemeyi de yönetir. Ek izinler, getAdditionalPermissions
yöntemi geçersiz kılınarak da istenebilir. Konum izni de vermeniz gerektiğinden bunu belirtin ve getAdditionalPermissions
yöntemini geçersiz kılın:
class PlacesArFragment : ArFragment() {
override fun getAdditionalPermissions(): Array<String> =
listOf(Manifest.permission.ACCESS_FINE_LOCATION)
.toTypedArray()
}
Çalıştırın
Android Studio'da starter
dizinindeki iskelet kodunu açın. Araç çubuğundan Run (Çalıştır) > Run ‘app' (Uygulamayı çalıştır) seçeneğini tıklayıp uygulamayı cihazınıza veya emülatörünüze dağıtırsanız önce konum ve kamera iznini etkinleştirmeniz istenir. İzin ver'i tıkladığınızda yan yana kamera görünümü ve harita görünümü gösterilir:
Uçakları algılama
Kameranızla bulunduğunuz ortamda etrafa bakarken yatay yüzeylerin üzerinde, bu resimdeki halının üzerindeki beyaz noktalara benzeyen birkaç beyaz nokta görebilirsiniz.
Bu beyaz noktalar, yatay bir düzlemin algılandığını belirtmek için ARCore tarafından sağlanan yönergelerdir. Bu düzlemler, sanal nesneleri gerçek alana yerleştirebilmeniz için "çapa" adı verilen bir öğe oluşturmanıza olanak tanır.
ARCore ve çevrenizi nasıl anladığı hakkında daha fazla bilgi için temel kavramları inceleyin.
6. Yakındaki yerleri görme
Ardından, cihazın mevcut konumuna erişip görüntülemeniz ve Places API'yi kullanarak yakındaki yerleri getirmeniz gerekir.
Haritalar kurulumu
Google Haritalar Platformu API anahtarı
Daha önce, Places API'yi sorgulamayı etkinleştirmek ve Android için Haritalar SDK'sını kullanabilmek için bir Google Haritalar Platformu API anahtarı oluşturmuştunuz. gradle.properties
dosyasını açın ve "YOUR API KEY HERE"
dizisini oluşturduğunuz API anahtarıyla değiştirin.
Cihaz konumunu haritada gösterme
API anahtarınızı ekledikten sonra, kullanıcıların haritaya göre nerede olduklarını anlamalarına yardımcı olmak için haritaya bir yardımcı ekleyin. Bunu yapmak için setUpMaps
yöntemine gidin ve mapFragment.getMapAsync
çağrısında googleMap.isMyLocationEnabled
değerini true.
olarak ayarlayın. Bu işlem, haritada mavi noktayı gösterir.
private fun setUpMaps() {
mapFragment.getMapAsync { googleMap ->
googleMap.isMyLocationEnabled = true
// ...
}
}
Geçerli konumu alma
Cihazın konumunu almak için FusedLocationProviderClient
sınıfını kullanmanız gerekir. Bunun bir örneğini alma işlemi, MainActivity
öğesinin onCreate
yönteminde zaten yapılmıştır. Bu nesneyi kullanmak için getCurrentLocation
yöntemini doldurun. Bu yöntem, bir lambda bağımsız değişkenini kabul eder. Böylece, bu yöntemin arayanına bir konum iletilebilir.
Bu yöntemi tamamlamak için FusedLocationProviderClient
nesnesinin lastLocation
özelliğine erişebilir ve ardından addOnSuccessListener
ekleyebilirsiniz:
fusedLocationClient.lastLocation.addOnSuccessListener { location ->
currentLocation = location
onSuccess(location)
}.addOnFailureListener {
Log.e(TAG, "Could not get location")
}
getCurrentLocation
yöntemi, yakındaki yerlerin getirildiği setUpMaps
yöntemindeki getMapAsync
içinde sağlanan lambda'dan çağrılır.
Yer ağı görüşmesi başlatma
getNearbyPlaces
yöntem çağrısında, placesServices.nearbyPlaces
yöntemine bir API anahtarı, cihazın konumu, metre cinsinden bir yarıçap (2 km olarak ayarlanır) ve bir yer türü (şu anda park
olarak ayarlanmıştır) parametrelerinin iletildiğini unutmayın.
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, dize kaynağı değerini google_maps_key
derleme zamanında kullanılabilir hale getirir.
Ağ çağrısını tamamlamak için Context
nesnesinde getString
aracılığıyla bu dize kaynağını okumanız yeterlidir.
val apiKey = this.getString(R.string.google_maps_key)
7. AR'de yerler
Şu ana kadar şunları yaptınız:
- Uygulama ilk kez çalıştırıldığında kullanıcıdan kamera ve konum izinleri isteniyor
- Yatay düzlemleri izlemeye başlamak için ARCore'u ayarlama
- Maps SDK'sını API anahtarınızla ayarlama
- Cihazın mevcut konumunu alma
- Places API'yi kullanarak yakındaki yerleri (özellikle parkları) getirme
Bu alıştırmayı tamamlamak için kalan adım, getirdiğiniz yerleri artırılmış gerçeklikte konumlandırmaktır.
Sahne yorumlama
ARCore, her görüntü karesinde özellik noktaları adı verilen ilginç ve belirgin noktaları algılayarak cihazın kamerası aracılığıyla gerçek dünyadaki sahneyi anlayabilir. Bu özellik noktaları kümelendiğinde ve masalar ile zeminler gibi ortak bir yatay düzlem üzerinde yer alıyormuş gibi göründüğünde ARCore, bu özelliği uygulama için yatay düzlem olarak kullanılabilir hale getirebilir.
Daha önce gördüğünüz gibi ARCore, bir düzlem algılandığında beyaz noktalar göstererek kullanıcıya rehberlik eder.
Bağlayıcı ekleme
Bir düzlem algılandığında anchor adı verilen bir nesne ekleyebilirsiniz. Bir sabitleme noktası aracılığıyla sanal nesneler yerleştirebilir ve bu nesnelerin uzayda aynı konumda kalmasını sağlayabilirsiniz. Bir uçak algılandığında eklemek için kodu değiştirebilirsiniz.
setUpAr
içinde, PlacesArFragment
öğesine OnTapArPlaneListener
eklenir. Bu dinleyici, AR sahnesinde bir düzleme her dokunulduğunda çağrılır. Bu görüşmede, dinleyicideki HitResult
kullanılarak ş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
örnekleri) ekleyeceğiniz yerdir.
Çalıştırın
Uygulamayı yukarıdaki değişikliklerle çalıştırırsanız bir uçak algılanana kadar etrafınıza bakın. Uçakları gösteren beyaz noktalara dokunun. Bu işlemi yaptığınızda, yakınınızdaki tüm parkların işaretlerini haritada görürsünüz. Ancak sanal nesnelerin, oluşturulan ve bu parkların uzaydaki konumuna göre yerleştirilmeyen tutturucuya yapıştığını fark edeceksiniz.
Son adımda, cihazda Android için Haritalar SDK'sı Yardımcı Program Kitaplığı ve SensorManager'ı kullanarak bu durumu düzelteceksiniz.
8. Yerleri konumlandırma
Sanal yer simgesini artırılmış gerçeklikte doğru bir yöne yerleştirebilmek için iki bilgiye ihtiyacınız vardır:
- Gerçek kuzeyin olduğu yer
- Kuzey ile her yer arasındaki açı
Kuzeyi belirleme
Kuzey, cihazda bulunan konum sensörleri (jeomanyetik ve ivme ölçer) kullanılarak belirlenebilir. Bu iki sensörü kullanarak cihazın uzaydaki konumu hakkında gerçek zamanlı bilgiler toplayabilirsiniz. Konum sensörleri hakkında daha fazla bilgi için Cihazın yönünü hesaplama başlıklı makaleyi inceleyin.
Bu sensörlere erişmek için SensorManager
edinmeniz ve ardından bu sensörlere SensorEventListener
kaydetmeniz gerekir. Bu adımlar, MainActivity
'nın yaşam döngüsü yöntemlerinde sizin için zaten yapılmıştır:
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, belirli bir sensörün verileriyle ilgili ayrıntıları içeren bir SensorEvent
nesnesi sağlanır. Şimdi bu yönteme aşağıdaki kodu 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 ölçümünü (ivme ölçer veya manyetometre ölçümü) günceller. Bu sensör okumaları kullanılarak cihazın kuzeye göre kaç derece döndüğü (yani orientationAngles[0]
değeri) belirlenebilir.
Küresel başlık
Kuzey belirlendikten sonraki adım, kuzey ile her yer arasındaki açıyı belirlemek ve ardından bu bilgileri kullanarak yerleri artırılmış gerçeklikte doğru yöne yerleştirmektir.
Başlığı hesaplamak için, küresel geometri aracılığıyla mesafeleri ve başlıkları hesaplamaya yönelik bir dizi yardımcı işlev içeren Android için Haritalar SDK'sı Yardımcı Kitaplığı'nı kullanacaksınız. Daha fazla bilgi için kitaplığa genel bakış başlıklı makaleyi okuyun.
Ardından, iki LatLng
nesnesi arasındaki yönü/açıyı hesaplayan yardımcı kitaplıktaki sphericalHeading
yöntemini kullanacaksınız. Bu bilgi, Place.kt
içinde tanımlanan getPositionVector
yöntemi için gereklidir. Bu yöntem sonuç olarak bir Vector3
nesnesi döndürür. Bu nesne daha sonra her PlaceNode
tarafından AR alanındaki yerel konumu olarak kullanılır.
Bu yöntemde başlık tanımını aşağıdakiyle değiştirin:
val heading = latLng.sphericalHeading(placeLatLng)
Bu işlem sonucunda aşağıdaki yöntem tanımı elde edilir:
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
Yerleri AR'de doğru şekilde yönlendirmek için son adım, sahneye PlaceNode
nesneleri eklenirken getPositionVector
sonucunu kullanmaktır. MainActivity
içinde addPlaces
'ya gidin. Bu bölüm, üst öğenin her placeNode
üzerinde ayarlandığı satırın hemen altında (placeNode.setParent(anchorNode)
'ün hemen altında) yer alır. placeNode
'nin localPosition
değerini, getPositionVector
'ı aşağıdaki gibi çağırarak elde edilen sonuç olarak ayarlayın:
val placeNode = PlaceNode(this, place)
placeNode.setParent(anchorNode)
placeNode.localPosition = place.getPositionVector(orientationAngles[0], currentLocation.latLng)
Varsayılan olarak, getPositionVector
yöntemi, getPositionVector
yöntemindeki y
değeriyle belirtildiği gibi düğümün y mesafesini 1 metreye ayarlar. Bu mesafeyi ayarlamak isterseniz (ör. 2 metre) değeri gerektiği gibi değiştirebilirsiniz.
Bu değişiklikle birlikte, eklenen PlaceNode
nesneleri artık doğru başlık yönünde olmalıdır. Şimdi uygulamayı çalıştırarak sonucu görebilirsiniz.
9. Tebrikler
Bu aşamaya ulaştığınız için tebrik ederiz.