Créer votre première carte 3D avec SwiftUI

1. Avant de commencer

Cet atelier de programmation vous explique comment créer une application Maps 3D dans SwiftUI à l'aide du SDK Maps 3D pour iOS.

Application affichant une carte 3D de San Francisco

Vous allez découvrir comment :

  • Contrôler la caméra pour afficher des lieux et effectuer un survol de la carte
  • Ajouter des repères et des modèles
  • Tracer des lignes et des polygones
  • Gérer les clics des utilisateurs sur les repères de lieu

Prérequis

  • Un projet Google Console avec la facturation activée
  • Une clé API, éventuellement limitée au SDK Maps 3D pour iOS.
  • Connaissances de base en développement iOS avec SwiftUI.

Objectifs de l'atelier

  • Configurer Xcode et importer le SDK à l'aide de Swift Package Manager
  • Configurer votre application pour utiliser une clé API
  • Ajouter une carte 3D de base à votre application
  • Contrôler la caméra pour voler vers des emplacements spécifiques et les contourner
  • Ajouter des repères, des lignes, des polygones et des modèles à votre carte

Prérequis

  • Xcode 15 ou version ultérieure

2. Configuration

Pour l'étape d'activation suivante, vous devez activer le SDK Maps 3D pour iOS.

Configurer Google Maps Platform

Si vous ne disposez pas encore d'un compte Google Cloud Platform et d'un projet pour lequel la facturation est activée, consultez le guide Premiers pas avec Google Maps Platform pour savoir comment créer un compte de facturation et un projet.

  1. Dans Cloud Console, cliquez sur le menu déroulant des projets, puis sélectionnez celui que vous souhaitez utiliser pour cet atelier de programmation.

  1. Activez les API et les SDK Google Maps Platform requis pour cet atelier de programmation dans Google Cloud Marketplace. Pour ce faire, suivez les étapes indiquées dans cette vidéo ou dans cette documentation.
  2. Générez une clé API sur la page Identifiants de Cloud Console. Vous pouvez suivre la procédure décrite dans cette vidéo ou dans cette documentation. Toutes les requêtes envoyées à Google Maps Platform nécessitent une clé API.

Activer le SDK Maps 3D pour iOS

Pour trouver le SDK Maps 3D pour iOS, utilisez le lien de menu Google Maps Platform > API et services dans la console.

Cliquez sur "Activer" pour activer l'API dans le projet sélectionné.

Activer le SDK Maps 3D dans la console Google

3. Créer une application SwiftUI de base

Remarque: Vous trouverez le code de solution pour chaque étape dans le dépôt de l'application exemple de l'atelier de programmation sur GitHub .

Créez une application dans Xcode.

Le code de cette étape se trouve dans le dossier GoogleMaps3DDemo sur GitHub.

Ouvrez Xcode et créez une application. Spécifiez SwiftUI.

Appelez votre application GoogleMaps3DDemo, avec un nom de package com.example.GoogleMaps3DDemo.

Importer la bibliothèque GoogleMaps3D dans votre projet

Ajoutez le SDK à votre projet à l'aide de Swift Package Manager.

Dans votre projet ou espace de travail Xcode, accédez à File > Add Package Dependencies (Fichier > Ajouter des dépendances de package). Saisissez https://github.com/googlemaps/ios-maps-3d-sdk comme URL, appuyez sur Entrée pour importer le package, puis cliquez sur "Ajouter un package".

Dans la fenêtre "Choisir les produits du package", vérifiez que GoogleMaps3D sera ajouté à votre cible principale désignée. Une fois cette étape terminée, cliquez sur "Ajouter un package".

Pour vérifier votre installation, accédez au volet "Général" de votre cible. Dans "Frameworks, bibliothèques et contenu intégré", vous devriez voir les packages installés. Vous pouvez également consulter la section "Dépendances de paquets" du Project Navigator pour vérifier le paquet et sa version.

Ajouter votre clé API

Vous pouvez coder en dur votre clé API dans l'application, mais ce n'est pas une bonne pratique. Ajouter un fichier de configuration vous permet de garder votre clé API secrète et d'éviter de la vérifier dans le contrôle des versions.

Créer un fichier de configuration dans le dossier racine du projet

Dans Xcode, assurez-vous d'afficher la fenêtre de l'explorateur de projets. Effectuez un clic droit sur la racine du projet, puis sélectionnez "New File from Template" (Nouveau fichier à partir d'un modèle). Faites défiler la page jusqu'à "Fichier de paramètres de configuration". Sélectionnez cette option, puis cliquez sur "Suivant". Attribuez un nom au fichier (Config.xcconfig) et assurez-vous que le dossier racine du projet est sélectionné. Cliquez sur "Créer" pour créer le fichier.

Dans l'éditeur, ajoutez une ligne au fichier de configuration comme suit: MAPS_API_KEY = YOUR_API_KEY

Remplacez YOUR_API_KEY par votre clé API.

Ajoutez ce paramètre à Info.plist.

Pour ce faire, sélectionnez la racine du projet, puis cliquez sur l'onglet "Infos".

Ajoutez une propriété appelée MAPS_API_KEY avec une valeur de $(MAPS_API_KEY).

L'exemple de code d'application contient un fichier Info.plist qui spécifie cette propriété.

Ajouter une carte

Ouvrez le fichier nommé GoogleMaps3DDemoApp.swift. Il s'agit du point d'entrée et de la navigation principale de votre application.

Il appelle ContentView(), qui affiche un message Hello World.

Ouvrez ContentView.swift dans l'éditeur.

Ajoutez une instruction import pour GoogleMaps3D.

Supprimez le code dans le bloc de code var body: some View {}. Déclarez un nouvel élément Map() dans body.

La configuration minimale requise pour initialiser un Map est un MapMode. Il existe deux valeurs possibles:

  • .hybrid : images satellite avec routes et libellés, ou
  • .satellite : images satellite uniquement.

.hybrid peut vous aider.

Votre fichier ContentView.swift devrait se présenter comme suit.

import GoogleMaps3D
import SwiftUI

@main
struct ContentView: View {
    var body: some View {
      Map(mode: .hybrid)
    }
}

Définissez votre clé API.

La clé API doit être définie avant l'initialisation de la carte.

Pour ce faire, définissez Map.apiKey dans le gestionnaire d'événements init() de tout View contenant une carte. Vous pouvez également le définir dans GoogleMaps3DDemoApp.swift avant qu'il n'appelle ContentView().

Dans GoogleMaps3DDemoApp.swift, définissez Map.apiKey dans le gestionnaire d'événements onAppear de WindowGroup.

Récupérer votre clé API à partir du fichier de configuration

Utilisez Bundle.main.infoDictionary pour accéder au paramètre MAPS_API_KEY que vous avez créé dans votre fichier de configuration.

import GoogleMaps3D
import SwiftUI

@main
struct GoogleMaps3DDemoApp: App {
  var body: some Scene {
    WindowGroup {
      ContentView()
    }
    .onAppear {
      guard let infoDictionary: [String: Any] = Bundle.main.infoDictionary else {
        fatalError("Info.plist not found")
      }
      guard let apiKey: String = infoDictionary["MAPS_API_KEY"] as? String else {
        fatalError("MAPS_API_KEY not set in Info.plist")
      }
      Map.apiKey = apiKey
    }
  }
}

Créez et exécutez votre application pour vérifier qu'elle se charge correctement. Une carte du globe devrait s'afficher.

Carte 3D montrant la Terre

4. Utiliser une caméra pour contrôler la vue de la carte

Créer un objet d'état de la caméra

Les vues de carte 3D sont contrôlées par la classe Camera. Au cours de cette étape, vous allez apprendre à spécifier l'emplacement, l'altitude, l'orientation, l'inclinaison, le roulis et la portée pour personnaliser la vue de la carte.

Vue cartographique 3D de San Francisco

Créer une classe Helpers pour stocker les paramètres de l'appareil photo

Ajoutez un fichier vide appelé MapHelpers.swift. Dans votre nouveau fichier, importez GoogleMaps3D et ajoutez une extension à la classe Camera. Ajoutez une variable appelée sanFrancisco. Initialisez cette variable en tant que nouvel objet Camera. Placez la caméra à latitude: 37.39, longitude: -122.08.

import GoogleMaps3D

extension Camera {
 public static var sanFrancisco: Camera = .init(latitude: 37.39, longitude: -122.08)
}

Ajouter une vue à votre application

Créez un fichier appelé CameraDemo.swift. Ajoutez au fichier la structure de base d'une nouvelle vue SwiftUI.

Ajoutez une variable @State appelée camera, de type Camera. Initialisez-la sur la caméra sanFrancisco que vous venez de définir.

L'utilisation de @State vous permet de lier la carte à l'état de la caméra et de l'utiliser comme source fiable.

@State var camera: Camera = .sanFrancisco

Modifiez l'appel de fonction Map() pour inclure une propriété camera. Utilisez la liaison d'état de l'appareil photo $camera pour initialiser la propriété camera sur votre objet @State de l'appareil photo (.sanFrancisco).

import SwiftUI
import GoogleMaps3D

struct CameraDemo: View {
  @State var camera: Camera = .sanFrancisco
  var body: some View {
    VStack {
      Map(camera: $camera, mode: .hybrid)
    }
  }
}

Ajouter une UI de navigation de base à votre application

Ajoutez un NavigationView au point d'entrée principal de l'application, GoogleMaps3DDemoApp.swift.

Les utilisateurs pourront ainsi voir une liste de démonstrations et cliquer sur chacune d'elles pour les ouvrir.

Modifiez GoogleMaps3DDemoApp.swift pour ajouter un NavigationView.

Ajoutez un List contenant deux déclarations NavigationLink.

Le premier NavigationLink doit ouvrir ContentView() avec une description Text Basic Map.

Le deuxième NavigationLink doit ouvrir CameraDemo().

...
      NavigationView {
        List {
          NavigationLink(destination: ContentView()) {
            Text("Basic Map")
          }
          NavigationLink(destination: CameraDemo()) {
            Text("Camera Demo")
          }
        }
      }
...

Ajouter un aperçu Xcode

Les aperçus sont une fonctionnalité Xcode puissante qui vous permet de voir et d'interagir avec votre application lorsque vous y apportez des modifications.

Pour ajouter un aperçu, ouvrez CameraDemo.swift. Ajoutez un bloc de code #Preview {} en dehors du struct.

#Preview {
 CameraDemo()
}

Ouvrez ou actualisez le volet d'aperçu dans Xcode. La carte devrait afficher San Francisco.

Configurer des vues 3D personnalisées

Vous pouvez spécifier des paramètres supplémentaires pour contrôler la caméra:

  • heading: azimut en degrés à partir du nord vers lequel pointer la caméra.
  • tilt: angle d'inclinaison en degrés, où 0 correspond à une vue directement au-dessus et 90 à une vue horizontale.
  • roll: angle de roulis autour du plan vertical de la caméra, en degrés
  • range: distance en mètres de la caméra par rapport à la latitude et à la longitude
  • altitude: hauteur de la caméra au-dessus du niveau de la mer

Si vous ne fournissez aucun de ces paramètres supplémentaires, des valeurs par défaut seront utilisées.

Pour que la vue de la caméra affiche plus de données 3D, définissez les paramètres initiaux pour afficher une vue plus rapprochée et inclinée.

Modifiez le Camera que vous avez défini dans MapHelpers.swift pour inclure des valeurs pour altitude, heading, tilt, roll et range.

public static var sanFrancisco: Camera = .init(
  latitude: 37.7845812,
  longitude: -122.3660241,
  altitude: 585,
  heading: 288.0,
  tilt: 75.0,
  roll: 0.0,
  range: 100)

Compilez et exécutez l'application pour voir et explorer la nouvelle vue 3D.

5. Animations de base de l'appareil photo

Jusqu'à présent, vous avez utilisé la caméra pour spécifier un seul emplacement avec une inclinaison, une altitude, un cap et une portée. À cette étape, vous allez apprendre à déplacer la vue de la caméra en animant ces propriétés d'un état initial à un nouvel état.

Carte 3D de Seattle

S'envoler vers un lieu

Vous allez utiliser la méthode Map.flyCameraTo() pour animer la caméra de l'emplacement initial vers un nouvel emplacement.

La méthode flyCameraTo() utilise un certain nombre de paramètres:

  • un Camera représentant l'emplacement de fin.
  • duration: durée de l'animation, en secondes.
  • trigger: objet observable qui déclenche l'animation lorsque son état change.
  • completion: code qui sera exécuté à la fin de l'animation.

Définir un lieu de destination

Ouvrez votre fichier MapHelpers.swift.

Définissez un nouvel objet de caméra pour afficher Seattle.

public static var seattle: Camera = .init(latitude:
47.6210296,longitude: -122.3496903, heading: 149.0, tilt: 77.0, roll: 0.0, range: 4000)

Ajoutez un bouton pour déclencher l'animation.

Ouvrez votre CameraDemo.swift. Déclarez une nouvelle variable booléenne dans struct.

Appelez-la animate avec une valeur initiale de false.

@State private var animate: Bool = false

Ajoutez un Button sous VStack. Button lance l'animation de la carte.

Attribuez à Button un Text approprié, tel que "Démarrer le vol".

import SwiftUI
import GoogleMaps3D

struct CameraDemo: View {
  @State var camera:Camera = .sanFrancisco
  @State private var animate: Bool = false

  var body: some View {
    VStack{
      Map(camera: $camera, mode: .hybrid)
      Button("Start Flying") {
      }
    }
  }
}

Dans la fermeture du bouton, ajoutez du code pour inverser l'état de la variable animate.

      Button("Start Flying") {
        animate.toggle()
      }

Lancez l'animation.

Ajoutez le code pour déclencher l'animation flyCameraTo() lorsque l'état de la variable animate change.

  var body: some View {
    VStack{
      Map(camera: $camera, mode: .hybrid)
        .flyCameraTo(
          .seattle,
          duration: 5,
          trigger: animate,
          completion: {  }
        )
      Button("Start Flying") {
        animate.toggle()
      }
    }
  }

Survoler un lieu

Vous pouvez effectuer un survol d'un emplacement à l'aide de la méthode Map.flyCameraAround(). Cette méthode utilise plusieurs paramètres:

  • un Camera définissant l'emplacement et la vue.
  • duration en secondes.
  • rounds: nombre de répétitions de l'animation.
  • trigger: objet observable qui déclenchera l'animation.
  • callback: code qui s'exécutera lorsque l'animation s'exécutera.

Définissez une nouvelle variable @State appelée flyAround, avec une valeur initiale de false.

Ensuite, ajoutez un appel à flyCameraAround() immédiatement après l'appel de la méthode flyCameraTo().

La durée du survol doit être relativement longue pour que le changement de vue soit fluide.

Veillez à déclencher l'animation flyCameraAround() en modifiant l'état de l'objet déclencheur lorsque flyCameraTo() est terminé.

Le code doit se présenter comme suit.

import SwiftUI
import GoogleMaps3D

struct CameraDemo: View {
  @State var camera:Camera = .sanFrancisco
  @State private var animate: Bool = false
  @State private var flyAround: Bool = false

  var body: some View {
    VStack{
      Map(camera: $camera, mode: .hybrid)
        .flyCameraTo(
          .seattle,
          duration: 5,
          trigger: animate,
          completion: { flyAround = true }
        )
        .flyCameraAround(
          .seattle,
          duration: 15,
          rounds: 0.5,
          trigger: flyAround,
          callback: {  }
        )
      Button("Start Flying") {
        animate.toggle()
      }
    }
  }
}

#Preview {
  CameraDemo()
}

Prévisualisez ou exécutez l'application pour voir que la caméra fait le tour de la destination une fois l'animation flyCameraTo() terminée.

6. Ajoutez un repère à votre carte.

Au cours de cette étape, vous allez apprendre à dessiner un repère sur la carte.

Vous allez créer un objet Marker et l'ajouter à votre carte. Le SDK utilisera une icône par défaut pour le repère. Enfin, vous allez ajuster l'altitude du repère et d'autres propriétés pour modifier son affichage.

Carte 3D montrant un repère

Créez une vue SwiftUI pour votre démonstration de repères.

Ajoutez un fichier Swift à votre projet. Nommez-le MarkerDemo.swift.

Ajoutez le contour d'une vue SwiftUI et initialisez la carte comme vous l'avez fait dans votre CameraDemo.

import SwiftUI
import GoogleMaps3D

struct MarkerDemo: View {
  @State var camera: Camera = .sanFrancisco
  var body: some View {
    VStack {
      Map(camera: $camera, mode: .hybrid)
    }
  }
}

Initialiser un objet Marker

Déclarez une nouvelle variable de repère appelée mapMarker. En haut du bloc de code struct dans MarkerDemo.swift.

Placez la définition sur la ligne sous votre déclaration camera. Cet exemple de code initialise toutes les propriétés disponibles.

  @State var mapMarker: Marker = .init(
    position: .init(
      latitude: 37.8044862,
      longitude: -122.4301493,
      altitude: 0.0),
    altitudeMode: .absolute,
    collisionBehavior: .required,
    extruded: false,
    drawsWhenOccluded: true,
    sizePreserved: true,
    zIndex: 0,
    label: "Test"
  )

Ajoutez le repère à votre carte.

Pour dessiner le repère, ajoutez-le à une route fermée appelée lors de la création de la carte.

struct MarkerDemo: View {
  @State var camera: Camera = .sanFrancisco
  var body: some View {
    VStack {
      Map(camera: $camera, mode: .hybrid) {
        mapMarker
      }
    }
  }
}

Ajoutez un NavigationLink à GoogleMaps3DDemoApp.swift avec une destination de MarkerDemo() et Text en le décrivant comme "Démo du repère".

...
      NavigationView {
        List {
          NavigationLink(destination: Map()) {
            Text("Basic Map")
          }
          NavigationLink(destination: CameraDemo()) {
            Text("Camera Demo")
          }
          NavigationLink(destination: MarkerDemo()) {
            Text("Marker Demo")
          }
        }
      }
...

Prévisualiser et exécuter votre application

Actualisez l'aperçu ou exécutez votre application pour voir le repère.

Repères extrudés

Vous pouvez placer des repères au-dessus du sol ou du maillage 3D à l'aide de altitude et altitudeMode.

Copiez la déclaration mapMarker dans MarkerDemo.swift dans une nouvelle variable Marker appelée extrudedMarker.

Définissez une valeur non nulle pour altitude. 50 est suffisant.

Remplacez altitudeMode par .relativeToMesh et définissez extruded sur true. Utilisez latitude et longitude dans l'extrait de code ci-dessous pour placer le repère au sommet d'un gratte-ciel.

  @State var extrudedMarker: Marker = .init(
    position: .init(
      latitude: 37.78980534,
      longitude:  -122.3969349,
      altitude: 50.0),
    altitudeMode: .relativeToMesh,
    collisionBehavior: .required,
    extruded: true,
    drawsWhenOccluded: true,
    sizePreserved: true,
    zIndex: 0,
    label: "Extruded"
  )

Exécutez ou prévisualisez à nouveau l'application. Le repère doit apparaître au sommet d'un bâtiment 3D.

7. Ajoutez un modèle à votre carte.

Vous pouvez ajouter un Model de la même manière qu'un Marker. Vous aurez besoin d'un fichier de modèle accessible par URL ou ajouté en tant que fichier local dans votre projet. Pour cette étape, nous allons utiliser un fichier local que vous pouvez télécharger à partir du dépôt GitHub de cet atelier de programmation.

Carte 3D de San Francisco avec un modèle de montgolfière

Ajouter un fichier de modèle à votre projet

Créez un dossier nommé Models dans votre projet Xcode.

Téléchargez le modèle à partir du dépôt d'exemples d'applications GitHub. Ajoutez-le à votre projet en le faisant glisser vers le nouveau dossier dans la vue du projet Xcode.

Assurez-vous de définir la cible comme cible principale de votre application.

Vérifiez les paramètres Build Phases > Copy Bundle Resources (Phases de compilation > Copier les ressources du bundle) de votre projet. Le fichier de modèle doit figurer dans la liste des ressources copiées dans le bundle. Si ce n'est pas le cas, cliquez sur + pour l'ajouter.

Ajoutez le modèle à votre application.

Créez un fichier SwiftUI appelé ModelDemo.swift.

Ajoutez des instructions import pour SwiftUI et GoogleMaps3D comme aux étapes précédentes.

Déclarez un Map dans un VStack dans votre body.

import SwiftUI
import GoogleMaps3D

struct ModelDemo: View {
  @State var camera: Camera = .sanFrancisco
  
  var body: some View {
    VStack {
      Map(camera: $camera, mode: .hybrid) {
        
      }
    }
  }
}

Obtenez le chemin d'accès au modèle à partir de votre bundle. Ajoutez le code à l'extérieur de struct.

private let fileUrl = Bundle.main.url(forResource: "balloon", withExtension: "glb")

Déclarez une variable pour votre modèle dans la structure.

Indiquez une valeur par défaut si fileUrl n'est pas fourni.

  @State var balloonModel: Model = .init(
    position: .init(
      latitude: 37.791376,
      longitude: -122.397571,
      altitude: 300.0),
    url: URL(fileURLWithPath: fileUrl?.relativePath ?? ""),
    altitudeMode: .absolute,
    scale: .init(x: 5, y: 5, z: 5),
    orientation: .init(heading: 0, tilt: 0, roll: 0)
  )

3. Utilisez le modèle avec votre carte.

Comme pour ajouter un Marker, il vous suffit de fournir la référence à votre Model dans la déclaration Map.

  var body: some View {
    VStack {
      Map(camera: $camera, mode: .hybrid) {
        balloonModel
      }
    }
  }

Prévisualiser et exécuter votre application

Ajoutez un NavigationLink à GoogleMaps3DDemoApp.swift, avec une destination ModelDemo() et le Text "Démo du modèle".

...
          NavigationLink(destination: ModelDemo()) {
            Text("Model Demo")
          }
...

Actualisez l'aperçu ou exécutez votre application pour afficher le modèle.

8. Tracez une ligne et un polygone sur votre carte.

Au cours de cette étape, vous allez apprendre à ajouter des lignes et des polygones à votre carte 3D.

Par souci de simplicité, vous allez définir les formes en tant que tableaux d'objets LatLngAltitude. Dans une application réelle, les données peuvent être chargées à partir d'un fichier, d'un appel d'API ou d'une base de données.

Carte 3D de San Francisco montrant deux polygones et une polyligne

Créez des objets de forme pour gérer les données de forme.

Ajoutez une définition Camera à MapHelpers.swift qui examine le centre-ville de San Francisco.

  public static var downtownSanFrancisco: Camera = .init(latitude: 37.7905, longitude: -122.3989, heading: 25, tilt: 71, range: 2500) 

Ajoutez un fichier nommé ShapesDemo.swift à votre projet. Ajoutez une struct appelée ShapesDemo qui implémente le protocole View, puis ajoutez-y un body.

struct ShapesDemo: View {
  @State var camera: Camera = .downtownSanFrancisco

  var body: some View {
    VStack {
      Map(camera: $camera, mode: .hybrid) {

      }
    }
  }
}

Les classes que vous utiliserez pour gérer les données de forme sont Polyline et Polygon. Ouvrez ShapesDemo.swift et ajoutez-les au struct comme suit.

var polyline: Polyline = .init(coordinates: [
    LatLngAltitude(latitude: 37.80515638571346, longitude: -122.4032569467164, altitude: 0),
    LatLngAltitude(latitude: 37.80337073509504, longitude: -122.4012878349353, altitude: 0),
    LatLngAltitude(latitude: 37.79925208843463, longitude: -122.3976697250461, altitude: 0),
    LatLngAltitude(latitude: 37.7989102378512, longitude: -122.3983408725656, altitude: 0),
    LatLngAltitude(latitude: 37.79887832784348, longitude: -122.3987094864192, altitude: 0),
    LatLngAltitude(latitude: 37.79786443410338, longitude: -122.4066878788802, altitude: 0),
    LatLngAltitude(latitude: 37.79549248916587, longitude: -122.4032992702785, altitude: 0),
    LatLngAltitude(latitude: 37.78861484290265, longitude: -122.4019489189814, altitude: 0),
    LatLngAltitude(latitude: 37.78618687561075, longitude: -122.398969592545, altitude: 0),
    LatLngAltitude(latitude: 37.7892310309145, longitude: -122.3951458683092, altitude: 0),
    LatLngAltitude(latitude: 37.7916358762409, longitude: -122.3981969390652, altitude: 0)
  ])
  .stroke(GoogleMaps3D.Polyline.StrokeStyle(
    strokeColor: UIColor(red: 0.09803921568627451, green: 0.403921568627451, blue: 0.8235294117647058, alpha: 1),
    strokeWidth: 10.0,
    outerColor: .white,
    outerWidth: 0.2
    ))
  .contour(GoogleMaps3D.Polyline.ContourStyle(isGeodesic: true))

  var originPolygon: Polygon = .init(outerCoordinates: [
    LatLngAltitude(latitude: 37.79165766856578, longitude:  -122.3983762901255, altitude: 300),
    LatLngAltitude(latitude: 37.7915324439261, longitude:  -122.3982171091383, altitude: 300),
    LatLngAltitude(latitude: 37.79166617650914, longitude:  -122.3980478493319, altitude: 300),
    LatLngAltitude(latitude: 37.79178986470217, longitude:  -122.3982041104199, altitude: 300),
    LatLngAltitude(latitude: 37.79165766856578, longitude:  -122.3983762901255, altitude: 300 )
  ],
  altitudeMode: .relativeToGround)
  .style(GoogleMaps3D.Polygon.StyleOptions(fillColor:.green, extruded: true) )

  var destinationPolygon: Polygon = .init(outerCoordinates: [
      LatLngAltitude(latitude: 37.80515661739527, longitude:  -122.4034307490334, altitude: 300),
      LatLngAltitude(latitude: 37.80503794515428, longitude:  -122.4032633416024, altitude: 300),
      LatLngAltitude(latitude: 37.80517850164195, longitude:  -122.4031056058006, altitude: 300),
      LatLngAltitude(latitude: 37.80529346901115, longitude:  -122.4032622466595, altitude: 300),
      LatLngAltitude(latitude: 37.80515661739527, longitude:  -122.4034307490334, altitude: 300 )
  ],
  altitudeMode: .relativeToGround)
  .style(GoogleMaps3D.Polygon.StyleOptions(fillColor:.red, extruded: true) )

Notez les paramètres d'initialisation utilisés.

  • altitudeMode: .relativeToGround permet d'extruder les polygones à une hauteur spécifique au-dessus du sol.
  • altitudeMode: .clampToGround permet de faire en sorte que la polyligne suive la forme de la surface de la Terre.
  • Les styles sont définis sur les objets Polygon en associant un appel de méthode à styleOptions() après l'appel de .init().

Ajouter les formes à la carte

Comme dans les étapes précédentes, les formes peuvent être ajoutées directement à la fermeture Map. Créez votre Map dans un VStack.

...
  var body: some View {
    VStack {
      Map(camera: $camera, mode: .hybrid) {
        polyline
        originPolygon
        destinationPolygon
      }
    }
  }
...

Prévisualiser et exécuter votre application

Ajoutez du code Preview et inspectez votre application dans le volet Preview (Aperçu) de Xcode.

#Preview {
  ShapesDemo()
}

Pour exécuter votre application, ajoutez un NavigationLink à GoogleMaps3DDemoApp.swift qui ouvre la nouvelle vue de démonstration.

...
          NavigationLink(destination: ShapesDemo()) {
            Text("Shapes Demo")
          }
...

Exécutez votre application et explorez les formes que vous avez ajoutées.

9. Gérer les événements d'appui sur les repères de lieu

Au cours de cette étape, vous allez apprendre à répondre aux pressions de l'utilisateur sur "Placer des repères".

Carte affichant une fenêtre pop-up avec un ID de lieu

Remarque: Pour afficher les repères de lieu sur la carte, vous devez définir MapMode sur .hybrid.

Pour gérer le tapotement, vous devez implémenter la méthode Map.onPlaceTap.

L'événement onPlaceTap fournit un objet PlaceTapInfo à partir duquel vous pouvez obtenir l'ID de lieu du repère de lieu sélectionné.

Vous pouvez utiliser l'identifiant de lieu pour rechercher d'autres informations à l'aide du SDK Places ou de l'API Places.

Ajouter une vue Swift

Ajoutez le code suivant à un nouveau fichier Swift nommé PlaceTapDemo.swift.

import GoogleMaps3D
import SwiftUI

struct PlaceTapDemo: View {
  @State var camera: Camera = .sanFrancisco
  @State var isPresented = false
  @State var tapInfo: PlaceTapInfo?

  var body: some View {
    Map(camera: $camera, mode: .hybrid)
      .onPlaceTap { tapInfo in
        self.tapInfo = tapInfo
        isPresented.toggle()
      }
      .alert(
        "Place tapped - \(tapInfo?.placeId ?? "nil")",
        isPresented: $isPresented,
        actions: { Button("OK") {} }
      )
  }
}
#Preview {
  PlaceTapDemo()
}

Prévisualiser et exécuter votre application

Ouvrez le volet d'aperçu pour prévisualiser l'application.

Pour exécuter l'application, ajoutez un NavigationLink à GoogleMaps3DDemoApp.swift.

...
          NavigationLink(destination: PlaceTapDemo()) {
            Text("Place Tap Demo")
          }
...

10. (Facultatif) Pour aller plus loin

Animations avancées de la caméra

Certains cas d'utilisation nécessitent une animation fluide sur une séquence ou une liste d'emplacements ou d'états de la caméra, par exemple dans un simulateur de vol ou lors de la relecture d'une randonnée ou d'une course.

Dans cette étape, vous allez apprendre à charger une liste d'emplacements à partir d'un fichier et à animer chaque emplacement dans l'ordre.

Vue cartographique 3D de l'approche d'Innsbruck

Chargez un fichier contenant une séquence d'établissements.

Téléchargez flightpath.json à partir du dépôt GitHub d'applications exemples.

Créez un dossier nommé JSON dans votre projet Xcode.

Faites glisser flightpath.json vers votre dossier JSON dans Xcode.

Définissez la cible comme étant la cible principale de votre application. Vérifiez que les paramètres de votre projet "Copier les ressources du bundle" incluent ce fichier.

Créez deux fichiers Swift dans votre application, nommés FlightPathData.swift et FlightDataLoader.swift.

Copiez le code suivant dans votre application. Ce code crée des structures et des classes qui lisent un fichier local appelé "flighpath.json" et le décodent en tant que fichier JSON.

Les structures FlightPathData et FlightPathLocation représentent la structure de données du fichier JSON en tant qu'objets Swift.

La classe FlightDataLoader lit les données du fichier et les décode. Il adopte le protocole ObservableObject pour permettre à votre application d'observer les modifications apportées à ses données.

Les données analysées sont exposées via une propriété publiée.

FlightPaths.swift

import GoogleMaps3D

struct FlightPathData: Decodable {
  let flight: [FlightPathLocation]
}

struct FlightPathLocation: Decodable {
  let timestamp: Int64
  let latitude: Double
  let longitude: Double
  let altitude: Double
  let bearing: Double
  let speed: Double
}

FlightDataLoader.swift

import Foundation

public class FlightDataLoader : ObservableObject {

  @Published var flightPathData: FlightPathData = FlightPathData(flight:[])
  @Published var isLoaded: Bool = false

  public init() {
    load("flightpath.json")
  }
  
  public func load(_ path: String) {
    if let url = Bundle.main.url(forResource: path, withExtension: nil){
      if let data = try? Data(contentsOf: url){
        let jsondecoder = JSONDecoder()
        do{
          let result = try jsondecoder.decode(FlightPathData.self, from: data)
          flightPathData = result
          isLoaded = true
        }
        catch {
          print("Error trying to load or parse the JSON file.")
        }
      }
    }
  }
}

Animer la caméra à chaque emplacement

Pour animer la caméra entre une séquence d'étapes, vous devez utiliser un KeyframeAnimator.

Chaque Keyframe sera créé en tant que CubicKeyframe, de sorte que les changements d'état de la caméra soient animés de manière fluide.

L'utilisation de flyCameraTo() entraînerait un "rebondissement" de la vue entre chaque emplacement.

Commencez par déclarer une caméra appelée "innsbruck" dans MapHelpers.swift.

public static var innsbruck: Camera = .init(
  latitude: 47.263,
  longitude: 11.3704,
  altitude: 640.08,
  heading: 237,
  tilt: 80.0,
  roll: 0.0,
  range: 200)

Configurez maintenant une nouvelle vue dans un fichier nommé FlyAlongRoute.swift.

Importez SwiftUI et GoogleMaps3D. Ajoutez un Map et un Button dans un VStack. Configurez Button pour activer/désactiver l'état de la variable booléenne animation.

Déclarez un objet State pour FlightDataLoader, qui chargera le fichier JSON lors de son initialisation.

import GoogleMaps3D
import SwiftUI

struct FlyAlongRoute: View {
  @State private var camera: Camera = .innsbruck
  @State private var flyToDuration: TimeInterval = 5
  @State var animation: Bool = true

  @StateObject var flightData: FlightDataLoader = FlightDataLoader()

  var body: some View {
    VStack {
      Map(camera: $camera, mode: .hybrid)
      Button("Fly Along Route"){
        animation.toggle()
      }
    }
  }
}

Créer les images clés

Le processus de base consiste à créer une fonction qui renvoie un nouveau frame dans la séquence d'animation. Chaque nouveau frame définit l'état de la caméra suivant que l'animateur doit animer. Une fois cette fonction créée, appelez-la avec chaque emplacement du fichier dans l'ordre.

Ajoutez deux fonctions à votre struct FlyAlongRoute. La fonction makeKeyFrame renvoie un CubicKeyframe avec un état de la caméra. La fonction makeCamera prend une étape de la séquence de données de vol et renvoie un objet Camera représentant l'étape.

func makeKeyFrame(step: FlightPathLocation) -> CubicKeyframe<Camera> {
  return CubicKeyframe(
    makeCamera(step: step),
    duration: flyToDuration
  )
}

func makeCamera(step: FlightPathLocation) -> Camera {
  return .init(
    latitude: step.latitude,
    longitude: step.longitude,
    altitude: step.altitude,
    heading: step.bearing,
    tilt: 75,
    roll: 0,
    range: 200
  )
}

Assembler l'animation

Appelez keyframeAnimator après l'initialisation de Map et définissez les valeurs initiales.

Vous aurez besoin d'un état initial de la caméra en fonction du premier emplacement du parcours de vol.

L'animation doit se déclencher en fonction d'un changement d'état d'une variable.

Le contenu keyframeAnimator doit être une carte.

La liste réelle des clés d'animation est générée en parcourant chaque emplacement du parcours de vol.

   VStack {
      Map(camera: $camera, mode: .hybrid)
        .keyframeAnimator(
          initialValue: makeCamera(step: flightData.flightPathData.flight[0]),
          trigger: animation,
          content: { view, value in
            Map(camera: .constant(value), mode: .hybrid)
          },
          keyframes: { _ in
            KeyframeTrack(content: {
              for i in  1...flightData.flightPathData.flight.count-1 {
                makeKeyFrame(step: flightData.flightPathData.flight[i])
              }
            })
          }
        )
   }

Prévisualisez et exécutez votre application.

Ouvrez le volet d'aperçu pour prévisualiser votre vue.

Ajoutez un NavigationLink avec une destination FlightPathDemo() à GoogleMaps3DDemoApp.swift, puis exécutez l'application pour la tester.

11. Félicitations

Vous avez créé une application qui :

  • Ajoute une carte 3D de base à votre application.
  • Ajoute des repères, des lignes, des polygones et des modèles à votre carte.
  • Implémente du code pour contrôler la caméra afin de survoler la carte et des emplacements spécifiques.

Ce que vous avez appris

  • Ajouter le package GoogleMaps3D à une application SwiftUI Xcode
  • Initialiser une carte 3D avec une clé API et une vue par défaut
  • Ajouter des repères, des modèles 3D, des lignes et des polygones à votre carte
  • Contrôler la caméra pour animer un mouvement vers un autre emplacement
  • Gérer les événements de clic sur les repères de lieu

Étape suivante

  • Consultez le guide du développeur pour en savoir plus sur ce que vous pouvez faire avec le SDK Maps 3D pour iOS.
  • Aidez-nous à créer le contenu qui vous semble le plus utile en répondant à l'enquête suivante:

Quels autres ateliers de programmation souhaiteriez-vous voir ?

Visualisation des données sur les cartes En savoir plus sur la personnalisation du style de mes cartes Concevoir des interactions 3D dans Google Maps