1. Hinweis
Zusammenfassung
In diesem Codelab erfahren Sie, wie Sie Daten aus der Google Maps Platform verwenden, um Orte in der Nähe in Augmented Reality (AR) auf Android-Geräten anzuzeigen.
Vorbereitung
- Grundkenntnisse der Android-Entwicklung mit Android Studio
- Kotlin-Vorkenntnisse
Lerninhalte
- Fordern Sie die Berechtigung des Nutzers an, auf die Kamera und den Standort des Geräts zuzugreifen.
- Integrieren Sie die Places API, um Orte in der Nähe des Gerätestandorts abzurufen.
- ARCore integrieren, um horizontale Ebenen zu finden, damit virtuelle Objekte mit Sceneform im 3D-Raum verankert und platziert werden können.
- Erfassen Sie Informationen zur Position des Geräts im Raum mit SensorManager und verwenden Sie die Maps SDK for Android-Dienstprogrammbibliothek, um virtuelle Objekte in der richtigen Ausrichtung zu positionieren.
Voraussetzungen
- Android Studio 2020.3.1 oder höher
- Ein Entwicklungscomputer, der OpenGL ES 3.0 oder höher unterstützt
- Ein Gerät mit ARCore-Unterstützung oder ein ARCore-fähiger Android-Emulator (eine Anleitung finden Sie im nächsten Schritt)
2. Einrichten
Android Studio
In diesem Codelab wird Android 10.0 (API-Level 29) verwendet. Außerdem müssen die Google Play-Dienste in Android Studio installiert sein. Führen Sie die folgenden Schritte aus, um beide Abhängigkeiten zu installieren:
- Rufen Sie den SDK-Manager auf, indem Sie auf Tools > SDK-Manager klicken.
- Prüfen Sie, ob Android 10.0 installiert ist. Falls nicht, installieren Sie es, indem Sie das Kästchen neben Android 10.0 (Q) anklicken, dann auf OK und schließlich noch einmal auf OK im angezeigten Dialogfeld klicken.
- Installieren Sie schließlich die Google Play-Dienste, indem Sie den Tab SDK Tools aufrufen, das Kästchen neben Google Play Services anklicken, auf OK klicken und dann im angezeigten Dialogfeld noch einmal OK auswählen**.**
Erforderliche APIs
Aktivieren Sie in Schritt 3 des folgenden Abschnitts das Maps SDK for Android und die Places API für dieses Codelab.
Einstieg in die Google Maps Platform
Wenn Sie die Google Maps Platform noch nicht verwendet haben, folgen Sie der Anleitung für die ersten Schritte mit der Google Maps Platform oder sehen Sie sich die Playlist „Erste Schritte mit der Google Maps Platform“ an, um die folgenden Schritte auszuführen:
- Erstellen Sie ein Rechnungskonto.
- Projekt erstellen
- Aktivieren Sie die APIs und SDKs der Google Maps Platform, die im vorherigen Abschnitt aufgeführt sind.
- Generieren Sie einen API-Schlüssel.
Optional: Android Emulator
Wenn Sie kein ARCore-kompatibles Gerät haben, können Sie alternativ den Android-Emulator verwenden, um eine AR-Szene zu simulieren und den Standort Ihres Geräts zu fälschen. Da Sie in dieser Übung auch Sceneform verwenden, müssen Sie die Schritte unter „Emulator für Sceneform konfigurieren“ ausführen.
3. Schnelleinstieg
Damit Sie so schnell wie möglich loslegen können, finden Sie hier einige Startcodes, die Ihnen helfen, diesem Codelab zu folgen. Sie können direkt zur Lösung springen. Wenn Sie alle Schritte sehen möchten, lesen Sie einfach weiter.
Sie können das Repository klonen, wenn Sie git
installiert haben.
git clone https://github.com/googlecodelabs/display-nearby-places-ar-android.git
Alternativ können Sie auf die Schaltfläche unten klicken, um den Quellcode herunterzuladen.
Öffnen Sie nach dem Abrufen des Codes das Projekt im Verzeichnis starter
.
4. Projektübersicht
Sehen Sie sich den Code an, den Sie im vorherigen Schritt heruntergeladen haben. In diesem Repository sollte sich ein einzelnes Modul namens app
befinden, das das Paket com.google.codelabs.findnearbyplacesar
enthält.
AndroidManifest.xml
Die folgenden Attribute werden in der Datei AndroidManifest.xml
deklariert, damit Sie die in diesem Codelab erforderlichen Funktionen verwenden können:
<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" />
Für uses-permission
, das angibt, welche Berechtigungen vom Nutzer erteilt werden müssen, bevor diese Funktionen verwendet werden können, werden die folgenden deklariert:
android.permission.INTERNET
: Damit Ihre App Netzwerkoperationen ausführen und Daten über das Internet abrufen kann, z. B. Ortsinformationen über die Places API.android.permission.CAMERA
: Der Zugriff auf die Kamera ist erforderlich, damit Sie die Kamera des Geräts verwenden können, um Objekte in Augmented Reality anzuzeigen.android.permission.ACCESS_FINE_LOCATION
: Der Standortzugriff ist erforderlich, damit Sie Orte in der Nähe des Gerätestandorts abrufen können.
Für uses-feature
, das angibt, welche Hardwarefunktionen für diese App erforderlich sind, wird Folgendes deklariert:
- OpenGL ES-Version 3.0 ist erforderlich.
- Ein ARCore-kompatibles Gerät ist erforderlich.
Außerdem werden dem Anwendungsobjekt die folgenden Metadaten-Tags hinzugefügt:
<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>
Der erste Metadateneintrag gibt an, dass ARCore für die Ausführung dieser App erforderlich ist. Der zweite Eintrag zeigt, wie Sie Ihren Google Maps Platform-API-Schlüssel für das Maps SDK for Android angeben.
build.gradle
In build.gradle
werden die folgenden zusätzlichen Abhängigkeiten angegeben:
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"
}
Hier finden Sie eine kurze Beschreibung der einzelnen Abhängigkeiten:
- Die Bibliotheken mit der Gruppen-ID
com.google.android.gms
, nämlichplay-services-location
undplay-services-maps
, werden verwendet, um auf Standortinformationen des Geräts und auf Funktionen im Zusammenhang mit Google Maps zuzugreifen. com.google.maps.android:maps-utils-ktx
ist die Bibliothek mit Kotlin-Erweiterungen (KTX) für die Maps SDK for Android-Dienstprogrammbibliothek. Funktionen dieser Bibliothek werden verwendet, um virtuelle Objekte später im realen Raum zu positionieren.com.google.ar.sceneform.ux:sceneform-ux
ist die Sceneform-Bibliothek, mit der Sie realistische 3D-Szenen rendern können, ohne OpenGL lernen zu müssen.- Die Abhängigkeiten innerhalb der Gruppen-ID
com.squareup.retrofit2
sind die Retrofit-Abhängigkeiten, mit denen Sie schnell einen HTTP-Client für die Interaktion mit der Places API schreiben können.
Projektstruktur
Hier finden Sie die folgenden Pakete und Dateien:
- **api**: Dieses Paket enthält Klassen, die für die Interaktion mit der Places API über Retrofit verwendet werden.
- **ar**: Dieses Paket enthält alle Dateien, die mit ARCore zusammenhängen.
- **model**: Dieses Paket enthält eine einzelne Datenklasse
Place
, die verwendet wird, um einen einzelnen Ort zu kapseln, der von der Places API zurückgegeben wird. - MainActivity.kt: Dies ist die einzige
Activity
in Ihrer App, in der eine Karte und eine Kameraansicht angezeigt werden.
5. Szene einrichten
Sehen Sie sich die Kernkomponenten der App an, beginnend mit den Augmented-Reality-Elementen.
MainActivity
enthält ein SupportMapFragment
-Element, das für die Darstellung des Kartenobjekts zuständig ist, und eine Unterklasse von ArFragment
– PlacesArFragment
–, die für die Darstellung der Augmented Reality-Szene zuständig ist.
Augmented Reality-Einrichtung
Neben der Darstellung der Augmented Reality-Szene kümmert sich PlacesArFragment
auch darum, die Kameraberechtigung vom Nutzer anzufordern, falls sie noch nicht erteilt wurde. Zusätzliche Berechtigungen können auch durch Überschreiben der Methode getAdditionalPermissions
angefordert werden. Da Sie auch die Berechtigung zur Standortermittlung benötigen, geben Sie das an und überschreiben Sie die getAdditionalPermissions
-Methode:
class PlacesArFragment : ArFragment() {
override fun getAdditionalPermissions(): Array<String> =
listOf(Manifest.permission.ACCESS_FINE_LOCATION)
.toTypedArray()
}
Ausführen
Öffnen Sie den Gerüstcode im Verzeichnis starter
in Android Studio. Wenn Sie in der Symbolleiste auf Ausführen > „app“ ausführen klicken und die App auf Ihrem Gerät oder Emulator bereitstellen, werden Sie zuerst aufgefordert, die Berechtigungen für Standort und Kamera zu aktivieren. Klicken Sie auf Zulassen. Daraufhin sollten Sie eine Kameraansicht und eine Kartenansicht nebeneinander sehen, wie hier:
Ebenen erkennen
Wenn Sie sich mit der Kamera in Ihrer Umgebung umsehen, werden Sie möglicherweise einige weiße Punkte auf horizontalen Oberflächen bemerken, ähnlich wie die weißen Punkte auf dem Teppich auf diesem Bild.
Diese weißen Punkte sind Richtlinien von ARCore, die darauf hinweisen, dass eine horizontale Ebene erkannt wurde. Mit diesen erkannten Ebenen können Sie einen sogenannten „Anker“ erstellen, um virtuelle Objekte im realen Raum zu positionieren.
Weitere Informationen zu ARCore und dazu, wie die Umgebung erkannt wird
6. Orte in der Nähe abrufen
Als Nächstes müssen Sie auf den aktuellen Standort des Geräts zugreifen und ihn anzeigen. Anschließend rufen Sie mit der Places API Orte in der Nähe ab.
Maps-Einrichtung
Google Maps Platform-API-Schlüssel
Sie haben bereits einen Google Maps Platform-API-Schlüssel erstellt, um die Places API abzufragen und das Maps SDK for Android zu verwenden. Öffnen Sie die Datei gradle.properties
und ersetzen Sie den String "YOUR API KEY HERE"
durch den API-Schlüssel, den Sie erstellt haben.
Gerätestandort auf der Karte anzeigen
Nachdem Sie den API-Schlüssel hinzugefügt haben, fügen Sie der Karte einen Orientierungshelfer hinzu, damit Nutzer sehen, wo sie sich auf der Karte befinden. Rufen Sie dazu die Methode setUpMaps
auf und legen Sie innerhalb des mapFragment.getMapAsync
-Aufrufs googleMap.isMyLocationEnabled
auf true.
fest. Dadurch wird der blaue Punkt auf der Karte angezeigt.
private fun setUpMaps() {
mapFragment.getMapAsync { googleMap ->
googleMap.isMyLocationEnabled = true
// ...
}
}
Aktuellen Standort abrufen
Wenn Sie den Standort des Geräts abrufen möchten, müssen Sie die Klasse FusedLocationProviderClient
verwenden. Eine Instanz davon wurde bereits in der Methode onCreate
von MainActivity
abgerufen. Um dieses Objekt zu verwenden, füllen Sie die Methode getCurrentLocation
aus, die ein Lambda-Argument akzeptiert, damit dem Aufrufer dieser Methode ein Standort übergeben werden kann.
Um diese Methode abzuschließen, können Sie auf die Property lastLocation
des Objekts FusedLocationProviderClient
zugreifen und dann addOnSuccessListener
hinzufügen:
fusedLocationClient.lastLocation.addOnSuccessListener { location ->
currentLocation = location
onSuccess(location)
}.addOnFailureListener {
Log.e(TAG, "Could not get location")
}
Die Methode getCurrentLocation
wird in der Lambda-Funktion aufgerufen, die in getMapAsync
in der Methode setUpMaps
bereitgestellt wird, aus der die Orte in der Nähe abgerufen werden.
Orte-Netzwerkanruf starten
Beachten Sie, dass im Methodenaufruf getNearbyPlaces
die folgenden Parameter an die Methode placesServices.nearbyPlaces
übergeben werden: ein API-Schlüssel, der Standort des Geräts, ein Radius in Metern (der auf 2 km festgelegt ist) und ein Ortstyp (der derzeit auf park
festgelegt ist).
val apiKey = "YOUR API KEY"
placesService.nearbyPlaces(
apiKey = apiKey,
location = "${location.latitude},${location.longitude}",
radiusInMeters = 2000,
placeType = "park"
)
Um den Netzwerkaufruf abzuschließen, übergeben Sie den API-Schlüssel, den Sie in der Datei gradle.properties
definiert haben. Das folgende Code-Snippet ist in Ihrer build.gradle
-Datei unter der Konfiguration android > defaultConfig definiert:
android {
defaultConfig {
resValue "string", "google_maps_key", (project.findProperty("GOOGLE_MAPS_API_KEY") ?: "")
}
}
Dadurch wird der Stringressourcenwert google_maps_key
zur Build-Zeit verfügbar.
Um den Netzwerkaufruf abzuschließen, können Sie diese String-Ressource einfach über getString
für das Context
-Objekt lesen.
val apiKey = this.getString(R.string.google_maps_key)
7. Orte in AR
Bisher haben Sie Folgendes getan:
- Beim ersten Ausführen der App wurden Kamera- und Standortberechtigungen vom Nutzer angefordert.
- ARCore einrichten, um horizontale Ebenen zu erfassen
- Maps SDK mit Ihrem API-Schlüssel einrichten
- Aktuellen Standort des Geräts erhalten
- Orte in der Nähe (insbesondere Parks) mit der Places API abgerufen
Im letzten Schritt dieser Übung müssen Sie die abgerufenen Orte in Augmented Reality positionieren.
Szenenanalyse
ARCore kann die reale Umgebung über die Kamera des Geräts erfassen, indem in jedem Bildframe interessante und eindeutige Punkte, sogenannte Feature-Punkte, erkannt werden. Wenn diese Feature-Punkte gruppiert sind und auf einer gemeinsamen horizontalen Ebene liegen, z. B. auf Tischen und Böden, kann ARCore diese Funktion der App als horizontale Ebene zur Verfügung stellen.
Wie Sie bereits gesehen haben, werden weiße Punkte angezeigt, um den Nutzer zu führen, wenn eine Ebene erkannt wurde.
Anker hinzufügen
Sobald eine Ebene erkannt wurde, können Sie ein Objekt namens Anker daran anfügen. Mit einem Anker können Sie virtuelle Objekte platzieren und dafür sorgen, dass sie an derselben Position im Raum bleiben. Ändern Sie den Code so, dass ein Anker angehängt wird, sobald eine Ebene erkannt wurde.
In setUpAr
ist ein OnTapArPlaneListener
an das PlacesArFragment
angehängt. Dieser Listener wird aufgerufen, wenn in der AR-Szene auf eine Ebene getippt wird. In diesem Aufruf können Sie ein Anchor
- und ein AnchorNode
-Objekt aus dem bereitgestellten HitResult
im Listener erstellen:
arFragment.setOnTapArPlaneListener { hitResult, _, _ ->
val anchor = hitResult.createAnchor()
anchorNode = AnchorNode(anchor)
anchorNode?.setParent(arFragment.arSceneView.scene)
addPlaces(anchorNode!!)
}
Im AnchorNode
-Objekt werden untergeordnete Knotenobjekte (PlaceNode
-Instanzen) in der Szene angehängt, was im addPlaces
-Methodenaufruf erfolgt.
Ausführen
Wenn Sie die App mit den oben genannten Änderungen ausführen, schauen Sie sich um, bis ein Flugzeug erkannt wird. Tippe auf die weißen Punkte, die ein Flugzeug anzeigen. Nun sollten auf der Karte Markierungen für alle Parks in Ihrer Nähe angezeigt werden. Die virtuellen Objekte sind jedoch an dem erstellten Anker fixiert und nicht relativ zu den Parks platziert.
Im letzten Schritt korrigieren Sie dies mithilfe der Maps SDK for Android-Dienstprogrammbibliothek und des SensorManager auf dem Gerät.
8. Orte positionieren
Damit das Symbol für den virtuellen Ort in Augmented Reality in der richtigen Richtung positioniert werden kann, benötigen Sie zwei Informationen:
- Wo der geografische Norden ist
- Der Winkel zwischen Norden und jedem Ort
Norden bestimmen
Norden kann mithilfe der auf dem Gerät verfügbaren Positionssensoren (geomagnetisch und Beschleunigungsmesser) bestimmt werden. Mit diesen beiden Sensoren können Sie Echtzeitinformationen zur Position des Geräts im Raum erfassen. Weitere Informationen zu Positionssensoren finden Sie unter Ausrichtung des Geräts berechnen.
Um auf diese Sensoren zuzugreifen, müssen Sie zuerst eine SensorManager
abrufen und dann eine SensorEventListener
für diese Sensoren registrieren. Diese Schritte werden bereits in den Lebenszyklusmethoden von MainActivity
ausgeführt:
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)
}
In der Methode onSensorChanged
wird ein SensorEvent
-Objekt bereitgestellt, das Details zu den Daten eines bestimmten Sensors enthält, wenn sich diese im Laufe der Zeit ändern. Fügen Sie dieser Methode den folgenden Code hinzu:
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)
}
Mit dem Code oben wird der Sensortyp geprüft und je nach Typ wird der entsprechende Sensorwert aktualisiert (entweder der Beschleunigungsmesser- oder der Magnetometerwert). Anhand dieser Sensormessungen kann nun der Wert für die Anzahl der Grad von Norden relativ zum Gerät bestimmt werden (d.h. der Wert von orientationAngles[0]
).
Sphärische Überschrift
Nachdem Norden bestimmt wurde, muss als Nächstes der Winkel zwischen Norden und jedem Ort ermittelt werden. Anhand dieser Informationen werden die Orte dann in der Augmented Reality in der richtigen Richtung positioniert.
Zur Berechnung des Kurses verwenden Sie die Maps SDK for Android-Dienstprogrammbibliothek, die einige Hilfsfunktionen zur Berechnung von Entfernungen und Kursen über sphärische Geometrie enthält. Weitere Informationen zur Bibliothek
Als Nächstes verwenden Sie die Methode sphericalHeading
in der Dienstprogrammbibliothek, mit der die Richtung zwischen zwei LatLng
-Objekten berechnet wird. Diese Informationen werden in der getPositionVector
-Methode benötigt, die in Place.kt
definiert ist. Diese Methode gibt letztendlich ein Vector3
-Objekt zurück, das dann von jedem PlaceNode
als seine lokale Position im AR-Raum verwendet wird.
Ersetzen Sie die Überschriftendefinition in dieser Methode durch Folgendes:
val heading = latLng.sphericalHeading(placeLatLng)
Dadurch sollte die folgende Methodendefinition entstehen:
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)
}
Lokale Position
Der letzte Schritt, um Orte in AR richtig auszurichten, besteht darin, das Ergebnis von getPositionVector
zu verwenden, wenn PlaceNode
-Objekte der Szene hinzugefügt werden. Rufen Sie addPlaces
in MainActivity
auf, direkt unter der Zeile, in der das übergeordnete Element für jedes placeNode
festgelegt ist (direkt unter placeNode.setParent(anchorNode)
). Legen Sie den localPosition
des placeNode
auf das Ergebnis des Aufrufs von getPositionVector
fest:
val placeNode = PlaceNode(this, place)
placeNode.setParent(anchorNode)
placeNode.localPosition = place.getPositionVector(orientationAngles[0], currentLocation.latLng)
Standardmäßig wird mit der Methode getPositionVector
der y-Abstand des Knotens auf 1 Meter festgelegt, wie durch den y
-Wert in der Methode getPositionVector
angegeben. Wenn Sie diesen Abstand anpassen möchten, z. B. auf 2 Meter, können Sie den Wert entsprechend ändern.
Durch diese Änderung sollten hinzugefügte PlaceNode
-Objekte jetzt in der richtigen Richtung ausgerichtet sein. Führen Sie die App jetzt aus, um das Ergebnis zu sehen.
9. Glückwunsch
Herzlichen Glückwunsch, dass Sie es bis hierher geschafft haben!