ガイド変更履歴HERE SDK API references
ガイド

例とユースケース

屋内地図はNavigateライセンスでのみ使用できます。

HERE SDKを使用して、屋内地図と施設を効果的に活用するための実用的な例とユースケースを確認してください。このセクションでは、屋内地図機能を最大限に活用するためのさまざまなトピックを取り上げます。

すべての屋内地図を一覧表示する

HERE SDK for iOS (Navigate) を使用すると、アカウントと選択したコレクションでアクセス可能なすべての民間施設を一覧表示できます。VenueMapには、施設識別子、施設ID、施設名を含むVenueInfo要素を保持するリストが含まれます。

let venueInfo:[VenueInfo]? = venueEngine?.venueMap.getVenueInfoList()
if let venueInfo = venueInfo {
  for venueInfo in venueInfo {
      print("Venue Identifier: \(venueInfo.venueIdentifier)." + " Venue Id: \(venueInfo.venueId)." + " Venue Name: \(venueInfo.venueName).")
  }
}

施設IDがUUIDの地図の場合、venueIdは0を返します。

施設を読み込んで表示する

HERE SDK for iOS (Navigate) を使用すると、IDごとに施設を読み込んで視覚化できます。現在の資格情報セットの施設IDを知っている必要があります。施設を読み込んで視覚化する方法は複数あります。

VenueMapには、マップに施設を追加する2つのメソッド、selectVenueAsync()addVenueAsync()があります。どちらのメソッドでも、getVenueService().addVenueToLoad()を使用してIDで施設を読み込み、それをマップに追加します。メソッドselectVenueAsync()では施設も選択します。

venueEngine.venueMap.selectVenueAsync(venueIdentifier: String);
venueEngine.venueMap.addVenueAsync(venueIdentifier: String);

int型の施設IDを使用するレガシーマップの場合、VenueMapは引き続きselectVenueAsync(venueID: Int)およびaddVenueAsync(venueID: Int)をサポートしており、施設IDで施設を読み込みます。

施設が読み込まれると、VenueServiceVenueDelegate.onGetVenueCompleted()メソッドを呼び出します。

// Delegate for the venue loading event.
extension ViewController: VenueDelegate {
    func onGetVenueCompleted(venueIdentifier: String, venueModel: VenueModel?, online: Bool, venueStyle: VenueStyle?) {
        if venueModel == nil {
            print("Loading of venue \(venueIdentifier) failed!")
        }
    }
}

int型の施設IDを使用するレガシーマップの場合、VenueServiceVenueListener.onGetVenueCompleted(venueID: Int, venueModel: VenueModel?, online: Bool, venueStyle: VenueStyle?)メソッドを呼び出します。

施設が正常に読み込まれると、addVenueAsync()メソッドを使用している場合は、VenueLifecycleDelegate.onVenueAdded()メソッドのみがトリガーされます。selectVenueAsync() メソッドを使用している場合は、VenueSelectionDelegate.onSelectedVenueChanged() メソッドもトリガーされます。

// Delegate for the venue selection event.
extension ViewController: VenueSelectionDelegate {
    func onSelectedVenueChanged(deselectedVenue: Venue?, selectedVenue: Venue?) {
        if let venueModel = selectedVenue?.venueModel {
            if moveToVenue {
                // Move camera to the selected venue.
                let center = GeoCoordinates(latitude: venueModel.center.latitude,
                                            longitude: venueModel.center.longitude,
                                            altitude: 500.0)
                mapView.camera.lookAt(point: center)
                // This functions is used to facilitate the toggling of topology visibility.
                // Setting isTopologyVisible property to true will render the topology on scene and false will lead to hide the topology.
                selectedVenue?.isTopologyVisible = true;
            }
        }
    }
}

VenueVenueMapから削除することもでき、これによりVenueLifecycleDelegate.onVenueRemoved(venueIdentifier: String)メソッドがトリガーされます。

venueEngine.venueMap.removeVenue(venue: venue)

int型の施設IDを使用するレガシーマップの場合、addVenueAsync()メソッドを使用するとVenueLifecycleListener.onVenueAdded()メソッドがトリガーされます。
int型の施設IDをVenueMapから削除すると、VenueLifecycleListener.onVenueRemoved(venueID: Int)がトリガーされます。

ラベルテキストの優先設定

施設のデフォルトのラベル テキスト設定を上書きできます。

VenueEngineが初期化されると、コールバックが呼び出されます。この時点から、VenueService にアクセスできるようになります。オプションのメソッド setLabeltextPreference() を呼び出して、レンダリング中にラベル テキストの設定を行うことができます。デフォルトのスタイル ラベル テキスト優先設定をオーバーライドすると、順序によって優先設定が定義されるリストとして次のオプションを設定できます。

  • "OCCUPANT_NAMES"
  • "SPACE_NAME"
  • "INTERNAL_ADDRESS"
  • "SPACE_TYPE_NAME"
  • "SPACE_CATEGORY_NAME"

これらは任意の順序で設定できます。たとえば、ラベル テキストの優先設定に "OCCUPANT_NAMES" が含まれない場合は、リストの順序に基づいて "SPACE_NAME" などに切り替わります。優先設定が見つからない場合は何も表示されません。

private func onVenueEngineInit() {
    // Get VenueService and VenueMap objects.
    let venueMap = venueEngine.venueMap
    let venueService = venueEngine.venueService

    // Add needed delegates.
    venueService.addServiceDelegate(self)
    venueService.addVenueDelegate(self)
    venueMap.addVenueSelectionDelegate(self)

    // Start VenueEngine. Once authentication is done, the authentication completion handler
    // will be triggered. Afterwards VenueEngine will start VenueService. Once VenueService
    // is initialized, VenueServiceListener.onInitializationCompleted method will be called.
    venueEngine.start(callback: {
        error, data in if let error = error {
            print("Failed to authenticate, reason: " + error.localizedDescription)
        }
    })

    if (hrn != "") {
        // Set platform catalog HRN
        venueService.setHrn(hrn: hrn)
    }

    // Set label text preference
    venueService.setLabeltextPreference(labelTextPref: LabelPref)
}

施設の図面と階を選択する

Venue オブジェクトを使用すると、施設の状態を制御できます。

Venue.selectedDrawing プロパティを使用すると、地図に表示される描画を取得および設定できます。新しい描画が選択されると、VenueDrawingSelectionDelegate.onDrawingSelected() メソッドがトリガーされます。

以下に、UITableView でアイテムをクリックしたときに描画を選択する方法の例を示します。

extension DrawingSwitcher: UITableViewDelegate {
    public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        let drawingIndex: Int = indexPath.row
        if let venue = venueMap?.selectedVenue {
            // Set the selected drawing when a user clicks on the item in the table view.
            let drawing: VenueDrawing = venue.venueModel.drawings[drawingIndex]
            venue.selectedDrawing = drawing
            ...
        }
    }
}

Venue.selectedLevelVenue.selectedLevelIndexVenue.selectedLevelZIndex プロパティを使用すると、地図に表示されるレベルを取得および設定できます。新しいレベルが選択されると、VenueLevelSelectionDelegate.onLevelSelected() メソッドがトリガーされます。

以下は、UITableViewの反転レベルリストに基づいてレベルを選択する方法の例を示しています。

extension LevelSwitcher: UITableViewDelegate {
    public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        // Rows in the LevelSwitcher's table view are presented in the reversed way
        currentLevelIndex = Int32(levels.count - indexPath.row - 1)
        updateLevel(currentLevelIndex)
    }
}
func updateLevel(_ levelIndex: Int32) {
    if let venue = venueMap?.selectedVenue {
        venue.selectedLevelIndex = currentLevelIndex
    }
}

描画とレベルを制御するUIスイッチャーの完全な例は、GitHubにある「IndoorMap」サンプルアプリで入手できます。

施設のスタイルをカスタマイズする

VenueGeometry オブジェクトのビジュアル スタイルを変更できます。ジオメトリー スタイル オブジェクトやラベル スタイル オブジェクトを作成して、Venue.setCustomStyle() メソッドに提供する必要があります。

// Create geometry and label styles for the selected geometry.
geometryStyle = VenueGeometryStyle(
    mainColor: selectedColor, outlineColor: selectedOutlineColor, outlineWidth: 1)
labelStyle = VenueLabelStyle(
    fillColor: selectedTextColor, outlineColor: selectedTextOutlineColor, outlineWidth: 1, maxFont: 28)
venue.setCustomStyle(geometries: [geometry], style: geometryStyle, labelStyle: labelStyle)

識別子でスペースを選択する

スペース、レベル、描画の ID は getIdentifier() を使用して抽出できます。スペースの場合は spaces.getIdentifier() を呼び出します。次に、これらの ID 値を使用して、getGeometryById(id:) でレベルや描画の特定のスペースを検索します。

var geometriesID : [String] = [];
var geometries : [VenueGeometry] = [];
for id in geometriesID
{
    VenueGeometry geometry = selectedVenue?.getSelectedDrawing().getGeometryById(id);
    geometries.append(geometry);
}
geometryStyle = VenueGeometryStyle(
    mainColor: selectedColor, outlineColor: selectedOutlineColor, outlineWidth: 1)
labelStyle = VenueLabelStyle(
    fillColor: selectedTextColor, outlineColor: selectedTextOutlineColor, outlineWidth: 1, maxFont: 28)
selectedVenue.setCustomStyle(geometries: geometries, style: geometryStyle, labelStyle: labelStyle)

施設のタップ ジェスチャーを処理する

施設オブジェクトはタップで選択できます。まず、タップ デリゲートを設定します。

// Create a venue tap handler and set it as default tap delegate.
mapView.gestures.tapDelegate = VenueTapHandler(venueEngine: venueEngine,
                                               mapView: mapView,
                                               geometryLabel: geometryNameLabel)

タップ デリゲート内では、タップされた地理座標を VenueMap.getGeometry()VenueMap.getVenue() メソッドのパラメーターとして使用できます。

public func onTap(origin: Point2D) {
    deselectGeometry()

    let venueMap = venueEngine.venueMap
    // Get geo coordinates of the tapped point.
    if let position = mapView.viewToGeoCoordinates(viewCoordinates: origin) {
        // If the tap point was inside a selected venue, try to pick a geometry inside.
        // Otherwise try to select an another venue, if the tap point was on top of one of them.
        if let selectedVenue = venueMap.selectedVenue, let geometry = venueMap.getGeometry(position: position) {
            onGeometryPicked(venue: selectedVenue, geometry: geometry)
        } else if let venue = venueMap.getVenue(position: position) {
            venueMap.selectedVenue = venue
        }
    }
}

func deselectGeometry() {
    // If a map marker is already on the screen, remove it.
    if let currentMarker = marker {
        mapView.mapScene.removeMapMarker(currentMarker)
    }
}

func onGeometryPicked(venue: Venue,
                      geometry: VenueGeometry) {
    // If the geomtry has an icon, add a map marker on top of the geometry.
    if geometry.lookupType == .icon {
        if let image = getMarkerImage() {
            marker = MapMarker(at: geometry.center,
                               image: image,
                               anchor: Anchor2D(horizontal: 0.5, vertical: 1.0))
            if let marker = marker {
                mapView.mapScene.addMapMarker(marker)
            }
        }
    }
}

選択した施設、描画、またはレベルが変更された場合は、タップされたジオメトリーの選択を解除することをお勧めします。

public init(venueEngine: VenueEngine, mapView: MapView, geometryLabel: UILabel) {
    ...
    let venueMap = venueEngine.venueMap
    venueMap.addVenueSelectionDelegate(self)
    venueMap.addDrawingSelectionDelegate(self)
    venueMap.addLevelSelectionDelegate(self)
}

deinit {
    let venueMap = venueEngine.venueMap
    venueMap.removeVenueSelectionDelegate(self)
    venueMap.removeDrawingSelectionDelegate(self)
    venueMap.removeLevelSelectionDelegate(self)
}
extension VenueTapHandler: VenueSelectionDelegate {
    public func onSelectedVenueChanged(deselectedVenue: Venue?, selectedVenue: Venue?) {
        self.deselectGeometry()
    }
}

extension VenueTapHandler: VenueDrawingSelectionDelegate {
    public func onDrawingSelected(venue: Venue, deselectedDrawing: VenueDrawing?, selectedDrawing: VenueDrawing) {
        self.deselectGeometry()
    }
}

extension VenueTapHandler: VenueLevelSelectionDelegate {
    public func onLevelSelected(venue: Venue, drawing: VenueDrawing, deselectedLevel: VenueLevel?, selectedLevel: VenueLevel) {
        self.deselectGeometry()
    }
}

施設でのマップタップイベントの使用法を示す完全な例は、GitHubにある「IndoorMap」サンプルアプリで入手できます。

屋内ルーティング

HERE SDK for iOS (Navigate) は施設内のルートを計算し視覚化する包括的な屋内ルーティング機能を提供します。このセクションでは、屋内ルーティング機能の設定方法と使用方法について説明します。

HEREベルリンオフィスの屋内ルート。

屋内ルートの計算

屋内ルートを計算するには、IndoorRoutingEngineを作成し、施設およびフロア情報を含む経由地を指定する必要があります。経由地は出発地点と到着地点ごとに作成します。

まず、次のようにルーティングエンジンを作成します。

let routingEngine = IndoorRoutingEngine(_: venueService)

開始地と目的地の経由地を作成します。屋内の場所には、次のように施設IDとフロアIDを指定します。

let startWaypoint = IndoorWaypoint(
    coordinates: position,
    venueId: String(venueModel.identifier),
    levelId: String(venue.selectedLevel.identifier))

let destinationWaypoint = IndoorWaypoint(
    coordinates: position,
    venueId: String(venueModel.identifier),
    levelId: String(venue.selectedLevel.identifier))

routingEngineを使用して次のようにルートを計算します。

let routeOptions = IndoorRouteOptions()
routingEngine?.calculateRoute(
    from: startWaypoint,
    to: destinationWaypoint,
    routeOptions: routeOptions) { error, routes in
        if error == nil, let routes = routes {
            let route = routes[0]
            // Use the calculated route
        }
    }

複数のフロアにまたがるルートの計算

屋内ルートは、施設内の複数のフロアにまたがることがあります。SDKはフロア間の移動を自動的に処理し、ルート内のフロア変更に関する情報を提供します。

屋内セクションの詳細およびフロア情報にアクセスするには、次のようにします。

for section in route.sections {
    // Check if section has indoor details
    if let indoorDetails = section.indoorSectionDetails {
        print("Indoor Section - Departure: \(indoorDetails.departurePlace.venueId ?? "")")
        print("Indoor Section - Arrival: \(indoorDetails.arrivalPlace.venueId ?? "")")
        
        // Iterate through indoor maneuvers
        for indoorManeuver in indoorDetails.indoorManeuvers {
            if let action = indoorManeuver.action {
                print("IndoorManeuver Action: \(action)")
            }
            print("IndoorManeuver Location Info: Level_Z_Index: \(indoorManeuver.levelZIndex)")
            
            // Check for level change data
            if let levelChangeData = indoorManeuver.indoorLevelChangeData {
                print("IndoorManeuver Level change using: \(levelChangeData.connector)")
                print("changeInLevel: \(levelChangeData.deltaZ)")
            }
        }
    }
}

IndoorLevelChangeDataは、次の情報を提供します。

  • connector:使用されるフロア間の接続手段の種類 (エレベーター、階段、エスカレーター、スロープなど)
  • deltaZ:フロアの変化量 (上方向は正、下方向は負)

ルート計算時のエラー

ルーティングコールバックでは、さまざまなエラーシナリオに対応するためにIndoorRoutingErrorパラメーターが提供されます。ルート計算中に発生した一部の問題はルート通知として報告され、その後IndoorRoutingErrorオブジェクトに変換されます。次の通知タイプがサポートされています。

  • noRouteFound:選択した経由地間にルートが見つかりません
  • couldNotMatchOrigin:出発地を特定できません
  • couldNotMatchDestination:目的地を特定できません

その他のエラーには、ネットワーク接続の問題、サービスエラー、リクエストの検証に関する問題があります。

routingEngine?.calculateRoute(
    from: startWaypoint,
    to: destinationWaypoint,
    routeOptions: routeOptions) { error, routes in
        if error == nil, let routes = routes {
            let route = routes[0]
            // Process the route
        } else {
            var errorMessage: String
            switch error {
                // Route notice errors - converted from route notices
                case .noRouteFound:
                    errorMessage = "No route found between selected waypoints"
                case .couldNotMatchOrigin:
                    errorMessage = "Origin could not be matched"
                case .couldNotMatchDestination:
                    errorMessage = "Destination could not be matched"
                case .mapNotFound:
                    errorMessage = "Requested map not found"
                case .parsingError:
                    errorMessage = "Routing response not in correct format"
                case .unknownError:
                    errorMessage = "Unknown error encountered"
                default:
                    errorMessage = "Unknown error encountered"
            }
            // Handle the error appropriately
            print(errorMessage)
        }
    }

フロア間の接続手段の回避オプション

屋内ルートの計算時に、ユーザーの設定やアクセシビリティ要件に基づいて、特定の種類のフロア間の接続手段を回避するようルーティングエンジンを設定できます。

let routeOptions = IndoorRouteOptions()

// Add features to avoid
routeOptions.indoorAvoidanceOptions.indoorFeatures.append(.elevator)
routeOptions.indoorAvoidanceOptions.indoorFeatures.append(.escalator)
routeOptions.indoorAvoidanceOptions.indoorFeatures.append(.stairs)

回避可能なフロア間の接続手段の種類は次のとおりです。

  • .elevator:エレベーター
  • .escalator:エスカレーター
  • .stairs:階段
  • .ramp:一般的なスロープ
  • .pedestrianRamp:歩行者専用スロープ
  • .driveRamp:車両用スロープ
  • .carLift:車両用リフト
  • .elevatorBank:エレベーター群
  • .connector:汎用接続手段

回避オプションを削除するには、次のようにします。

if let index = routeOptions.indoorAvoidanceOptions.indoorFeatures.firstIndex(of: .elevator) {
    routeOptions.indoorAvoidanceOptions.indoorFeatures.remove(at: index)
}

ルート設定

IndoorRouteOptionsオブジェクトを使用して、ルート計算の設定を行います。

ルート最適化モード

次のように、最速ルートと最短ルートから選択します。

let routeOptions = IndoorRouteOptions()

// For fastest route
routeOptions.routeOptions.optimizationMode = OptimizationMode.fastest

// For shortest route
routeOptions.routeOptions.optimizationMode = OptimizationMode.shortest

歩行者の歩行速度

歩行者ルートの歩行速度をカスタマイズできます。これは推定移動時間に影響します。速度はメートル/秒で指定し、有効範囲は0.5~2.0 m/sです。

let routeOptions = IndoorRouteOptions()

// Set walk speed (in meters per second)
// Valid range: 0.5 to 2.0 m/s
// Default is typically 1.0 m/s
routeOptions.speedInMetersPerSecond = 1.5

検証の例は次のとおりです。

var speed: Double = 1.2 // User input

// Validate and clamp the speed
if speed < 0.5 {
    speed = 0.5
} else if speed > 2.0 {
    speed = 2.0
}

routeOptions.speedInMetersPerSecond = speed

ターン・バイ・ターンアクション

屋内ルートでは、IndoorManeuverクラスを通じて詳細なターン・バイ・ターンの誘導情報が提供されます。

for section in route.sections {
    if let indoorDetails = section.indoorSectionDetails {
        for indoorManeuver in indoorDetails.indoorManeuvers {
            // Get the maneuver action
            if let action = indoorManeuver.action {
                print("Action: \(action)")
            }
            
            // Get the level information
            print("Level Z-Index: \(indoorManeuver.levelZIndex)")
            
            // Check for level change information
            if let levelChangeData = indoorManeuver.indoorLevelChangeData {
                print("Level change via: \(levelChangeData.connector)")
                print("Change in levels: \(levelChangeData.deltaZ)")
            }
            
            // Get space information (room/area details)
            if let spaceData = indoorManeuver.indoorSpaceData {
                print("Space Category: \(spaceData.spaceCategory)")
                print("Space Type: \(spaceData.spaceType)")
            }
        }
    }
}

IndoorManeuverは次を提供します。

  • Action:実行するアクションのタイプ
  • Level Z-Index:操作が行われるフロアの階層
  • Indoor Level Change Data:接続手段の種類や移動するフロア数など、フロア間の移動に関する情報
  • Indoor Space Data:進入または通過する空間に関する詳細 (カテゴリーやタイプなど)

ルートのETAと総距離

計算されたルートから、到着予測時刻 (ETA) と総距離を取得できます。

let route = routes[0]

// Get the total duration in seconds
let durationInSeconds = route.duration.seconds

// Get the total length in meters
let lengthInMeters = route.lengthInMeters

// Format for display
print("Route duration: \(durationInSeconds) seconds")
print("Route distance: \(lengthInMeters) meters")

// Convert to more readable format
let minutes = durationInSeconds / 60
let seconds = durationInSeconds % 60
let kilometers = Double(lengthInMeters) / 1000.0

print(String(format: "ETA: %d min %d sec", minutes, seconds))
print(String(format: "Distance: %.2f km", kilometers))

セクション単位の詳細は、次のとおりです。

for section in route.sections {
    let sectionDuration = section.duration.seconds
    let sectionLength = section.lengthInMeters
    
    print("Section duration: \(sectionDuration) seconds")
    print("Section distance: \(sectionLength) meters")
}

ルートのレンダリング

IndoorRoutingControllerは、地図上での屋内ルートの視覚化を処理します。

まず、次のようにコントローラーを作成します。

let routingController = IndoorRoutingController(_: venueMap, mapView: mapView)

ポリラインのレンダリング

地図上にルートを表示するには、showRoute()メソッドを使用します。

// Create route style
let routeStyle = IndoorRouteStyle()

// Show the route
routingController?.showRoute(route: route, style: routeStyle)

ルートを非表示にするには、次のようにします。

routingController?.hideRoute()

フロア変更アイコンの配置

さまざまなルート要素やフロア変更インジケーターのカスタムマーカーを設定します。

let routeStyle = IndoorRouteStyle()

// Set start and destination markers
let middleBottomAnchor = Anchor2D(horizontal: 0.5, vertical: 1.0)

if let startImage = UIImage(named: "ic_route_start.png"),
   let startPngData = startImage.pngData() {
    let markerImage = MapImage(pixelData: startPngData, imageFormat: .png)
    let startMarker = MapMarker(
        at: GeoCoordinates(latitude: 0.0, longitude: 0.0),
        image: markerImage,
        anchor: middleBottomAnchor)
    routeStyle.startMarker = startMarker
}

if let endImage = UIImage(named: "ic_route_end.png"),
   let endPngData = endImage.pngData() {
    let markerImage = MapImage(pixelData: endPngData, imageFormat: .png)
    let endMarker = MapMarker(
        at: GeoCoordinates(latitude: 0.0, longitude: 0.0),
        image: markerImage,
        anchor: middleBottomAnchor)
    routeStyle.destinationMarker = endMarker
}

// Set transport mode markers
if let walkImage = UIImage(named: "indoor_walk.png"),
   let walkPngData = walkImage.pngData() {
    let markerImage = MapImage(pixelData: walkPngData, imageFormat: .png)
    let walkMarker = MapMarker(
        at: GeoCoordinates(latitude: 0.0, longitude: 0.0),
        image: markerImage,
        anchor: Anchor2D(horizontal: 0.5, vertical: 0.5))
    routeStyle.walkMarker = walkMarker
}

if let driveImage = UIImage(named: "indoor_drive.png"),
   let drivePngData = driveImage.pngData() {
    let markerImage = MapImage(pixelData: drivePngData, imageFormat: .png)
    let driveMarker = MapMarker(
        at: GeoCoordinates(latitude: 0.0, longitude: 0.0),
        image: markerImage,
        anchor: Anchor2D(horizontal: 0.5, vertical: 0.5))
    routeStyle.driveMarker = driveMarker
}

次のように、方向インジケーター付きのフロア変更フィーチャーのマーカーを設定します。

// Configure markers for each level change feature
let features: [IndoorLevelChangeFeatures] = [
    .elevator,
    .escalator,
    .stairs,
    .ramp
]

for feature in features {
    // Create markers for up, down, and neutral directions
    let upMarker = createMarker(for: feature, deltaZ: 1)       // Going up
    let downMarker = createMarker(for: feature, deltaZ: -1)    // Going down
    let neutralMarker = createMarker(for: feature, deltaZ: 0)  // No vertical change
    
    routeStyle.setIndoorMarkersFor(
        feature: feature,
        upMarker: upMarker,
        downMarker: downMarker,
        exitMarker: neutralMarker)
}

次のように、フィーチャータイプと方向に基づいてマーカーを作成するヘルパーメソッドを定義します。

private func createMarker(for feature: IndoorLevelChangeFeatures, deltaZ: Int) -> MapMarker? {
    let imageName = getImageName(for: feature, deltaZ: deltaZ)
    
    guard let image = UIImage(named: imageName),
          let pngData = image.pngData() else {
        return nil
    }
    
    let markerImage = MapImage(pixelData: pngData, imageFormat: .png)
    return MapMarker(
        at: GeoCoordinates(latitude: 0.0, longitude: 0.0),
        image: markerImage,
        anchor: Anchor2D(horizontal: 0.5, vertical: 0.5))
}

private func getImageName(for feature: IndoorLevelChangeFeatures, deltaZ: Int) -> String {
    var baseName: String
    
    switch feature {
    case .elevator:
        baseName = "indoor_elevator"
    case .escalator:
        baseName = "indoor_escalator"
    case .stairs:
        baseName = "indoor_stairs"
    case .ramp:
        baseName = "indoor_ramp"
    default:
        return ""
    }
    
    if deltaZ > 0 {
        return "\(baseName)_up.png"
    } else if deltaZ < 0 {
        return "\(baseName)_down.png"
    } else {
        return "\(baseName).png"
    }
}

deltaZパラメーターは次のように方向を示します。

  • 1 (正):上のフロアに移動
  • -1 (負):下のフロアに移動
  • 0:上下方向のフロア変更なし

showRoute()メソッドはルートのレンダリングに関してカスタマイズの範囲が限定されています。フロアに関する高度なカスタマイズを適用する場合は、ルートの各座標間に描画されるMapPolylineを使用します。「地図上にルートを表示する」を参照してください。

施設を含む屋内ルーティングの使用方法を示す完全な例は、GitHubにある「IndoorMap」サンプルアプリで確認できます。


EN 日本語

HERE documentation

Find answers to your product and technical questions

Documentation

What's new

Videos

EN 日本語

HERE ドキュメント

製品や技術に関する質問の答えを見つけましょう。より多くの内容と最新の情報については、英語版をご覧ください。

ドキュメント

ダイナミックマップ

動的コンテンツ関連のAPIをアプリやサービスに活用して、ドライバーが安全・快適かつ予定どおりに目的地へ到着できるよう支援します。

地図とデータ

世界中を走行する多数のマッピング車両から得られる最新の位置情報データを活用し、精度の高い地図やカスタムレイヤーを構築できます。

最新情報

動画

(function () { const input = document.querySelector('input[data-typeahead]'); if (!input) return; // Prevent the form from submitting/navigating input.closest('form')?.addEventListener('submit', e => e.preventDefault()); input.addEventListener('input', function () { const q = this.value.trim().toLowerCase(); document.querySelectorAll('.nav-group-name').forEach(group => { let anyVisible = false; group.querySelectorAll('.nav-group-task').forEach(task => { const text = task.textContent.trim().toLowerCase(); const show = !q || text.includes(q); task.style.display = show ? '' : 'none'; if (show) anyVisible = true; }); // Hide the whole group header if nothing matches group.style.display = anyVisible || !q ? '' : 'none'; }); }); })(); (function () { function onTokenClick(event) { var link = event.target.closest('.sdk-for-ios .item .token'); if (!link) return; event.preventDefault(); console.log('token clicked', link.textContent.trim()); var item = link.closest('.item'); if (!item) return; var content = item.querySelector('.height-container'); if (!content) { console.log('no .height-container found for item', item); return; } var isHidden = window.getComputedStyle(content).display === 'none'; content.style.display = isHidden ? 'block' : 'none'; link.classList.toggle('token-open', isHidden); var href = link.getAttribute('href'); if (href) { if (history.pushState) history.pushState({}, '', href); else location.hash = href; } } function openHashTarget() { var hash = window.location.hash.slice(1); if (!hash) return; var anchor = document.querySelector('.sdk-for-ios a[name="' + hash + '"]'); if (!anchor) return; var item = anchor.closest('.item'); if (!item) return; var link = item.querySelector('.token'); var content = item.querySelector('.height-container'); if (!link || !content) return; content.style.display = 'block'; link.classList.add('token-open'); } function init() { console.log('HERE SDK accordion init'); openHashTarget(); } document.removeEventListener('click', onTokenClick); document.addEventListener('click', onTokenClick); if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } window.addEventListener('hashchange', openHashTarget); window.addEventListener('pageLoad', init); })();