マップアイテムを追加する

HERE SDKを使用すると、マップポリラインやマーカーなど、いくつかのタイプのアイテムを地図に追加できます。これらの詳細については、以下のセクションで説明します。現在、次のマップ アイテムがサポートされています。

  • マップ ポリライン: 移動できないレンダリングされた線。
  • マップ矢印: 地図上に矢印インジケーターを表示する便利な方法。
  • マップ ポリゴン: 移動できないレンダリングされたシェイプ。
  • マップ円: ジオ ポリゴンとして定義できる、移動できないレンダリングされた円。
  • マップ マーカー: 地図上の特定のスポットを「マーク」するために固定できる画像。
  • マップ マーカー クラスタリング: ズーム レベルに応じてクラスター化できるマップ マーカーのグループ。
  • 埋め込み POI: 地図に埋め込まれた事前設定済みの公共交通機関の駅、レストラン、現金自動預け払い機、その他の施設などの選択可能な POI オブジェクト (Carto POI)。
  • マップ マーカー 3D: 指定した地理座標で地図上にレンダリングされた 3D シェイプ。
  • フラット マップ マーカー: 地図と一緒に回転および傾斜するフラット マップ マーカー。
  • 位置インジケーター: 地図上で現在のデバイス位置を示すための、事前定義済みアセット。
  • マップ表示ピン: 地図にネイティブな iOS の UIView レイアウトを表示する便利な方法。

ポリライン、ポリゴン、円は、現在のズーム レベルに基づいてサイズが調整されますが、マーカーとピンはズームした場合も変更されません。

どのマップ アイテムにも、地図からこれらのアイテムを選択できる便利な方法が備わっています。

マップ ポリラインを追加する

ポリラインは、地図上にルート ジオメトリーをレンダリングする場合などに便利です。作成方法を以下に示します。

private func createMapPolyline() -> MapPolyline {
    let coordinates = [GeoCoordinates(latitude: 52.53032, longitude: 13.37409),
                       GeoCoordinates(latitude: 52.5309, longitude: 13.3946),
                       GeoCoordinates(latitude: 52.53894, longitude: 13.39194),
                       GeoCoordinates(latitude: 52.54014, longitude: 13.37958)]

    // We are sure that the number of vertices is greater than two, so it will not crash.
    let geoPolyline = try! GeoPolyline(vertices: coordinates)
    let lineColor = UIColor(red: 0, green: 0.56, blue: 0.54, alpha: 0.63)
    let mapPolyline = nil
    let widthInPixels = 20.0
    do {
        let mapPolyline =  try MapPolyline(geometry: geoPolyline,
                                            representation: MapPolyline.SolidRepresentation(
                                            lineWidth: MapMeasureDependentRenderSize(
                                                sizeUnit: RenderSize.Unit.pixels,
                                                size: widthInPixels),
                                            color: lineColor,
                                            capShape: LineCap.round))

        mapView.mapScene.addMapPolyline(mapPolyline)
        mapPolylineList.append(mapPolyline)
    } catch let error {
        fatalError("Failed to render MapPolyline. Cause: \(error)")
    }

    return mapPolyline
}

MapPolylineは3つの要素で構成されます。

  • 地図上にポリラインを配置する場所を定義する2つ以上の地理座標のリスト。
  • この座標のリストを含むGeoPolyline
  • ポリラインの表示方法を定義する DashPatternLineCap などのスタイル パラメーター。

幾何学的な線は2つ以上の点で定義されるため、少なくとも2つのGeoCoordinatesを含む配列を作成する必要があります。これを行わなかった場合は、例外がスローされます。線の外観を変更するには、線の太さ (ピクセル単位) と色を設定します。スクリーンショットの例を参照してください。

1 つ以上のマップ ポリラインを作成したら、次のようにしてマップ シーンに追加できます。

mapScene = mapView.mapScene

mapPolyline = createMapPolyline()
mapScene.addMapPolyline(mapPolyline!)

マップポリラインのインスタンスがすでにマップシーンに添付されている場合は、さらにそれを追加しようとする試みは無視されます。

Screenshot: Showing a polyline.

マップビューでは1つのシーンのみが許可され、すべてのマップアイテムがそのシーンに直接配置されます。マップアイテムをグループ化する場合は、配列を使用してマップアイテムを整理し、個別に追加または削除できます。

mapPolyline を地図から直ちに削除するには、次のように呼び出します。

mapScene.removeMapPolyline(line)

MapPolyline アイテムは選択可能で、アイテムを選択するときに取得できる Metadata を保存できます。例については、以下のマップマーカーのセクションを参照してください。

MapPolylineの可視性は、現在のズームレベルに基づいて、以下に示すようにKindminimumZoomLevelmaximumZoomLevelで定義されるMapMeasureRangeアイテムのリストを設定することで調整できます。

func onEnableVisibilityRangesForPolyline() {
    var visibilityRanges: [MapMeasureRange] = []

    visibilityRanges.append(MapMeasureRange(kind: .zoomLevel, minimumValue: 1, maximumValue: 10))
    visibilityRanges.append(MapMeasureRange(kind: .zoomLevel, minimumValue: 11, maximumValue: 22))

    // Sets the visibility ranges for this map polyline based on zoom levels.
    // Each range is half-open: [minimumZoomLevel, maximumZoomLevel),
    // meaning the polyline is visible at minimumZoomLevel but not at maximumZoomLevel.
    // The polyline is rendered only when the map zoom level falls within any of the defined ranges.
    mapPolyline?.visibilityRanges = visibilityRanges
}

現在のところ、可視範囲でサポートされているのは、MapMeasure.Kind.ZOOM_LEVELのみです。その他の種類は無視されます。

描画順序を設定することで、どのアイテムを重ねて描画するかを指定できます。現時点では、描画順序の設定は、同じタイプのクラス内の順序のみを定義するため、他のマップアイテムとの関係には影響しません。

DrawOrderType を指定すると、エクスペリエンスをさらに最適化できます。たとえば、DrawOrderType.MAP_SCENE_ADDITION_ORDER_INDEPENDENT を使用すると、アイテムを追加するときの順序に描画順序が依存しないように指定できます。代わりに、同じ描画順序を持つ同じ種類の複数のマップ アイテムを任意の順序で描画でき、色などの類似属性を持つマップ アイテムをグループ化してまとめて描画することで、レンダリング パフォーマンスを向上させることができます。

マップ ポリラインは破線でレンダリングすることもできます。破線のパターン スタイルを設定するには、MapPolyline.DashImageRepresentation を使用します。

マップ矢印を追加する

マップ矢印は MapPolyline アイテムと同じように動作し、任意の数のポイントを持つポリラインで構成されていますが、端に矢印の先端が表示されます。マップ矢印は、ズーム レベルが 13 を超える場合にのみ表示されます。地図上で方向を示す場合、たとえば、ルートジオメトリーの一部にレンダリングするときなどに便利です。

private func createMapArrow() -> MapArrow {
    let coordinates = [GeoCoordinates(latitude: 52.53032, longitude: 13.37409),
                       GeoCoordinates(latitude: 52.5309, longitude: 13.3946),
                       GeoCoordinates(latitude: 52.53894, longitude: 13.39194),
                       GeoCoordinates(latitude: 52.54014, longitude: 13.37958)]

    // We are sure that the number of vertices is greater than two, so it will not crash.
    let geoPolyline = try! GeoPolyline(vertices: coordinates)
    let lineColor = UIColor(red: 0, green: 0.56, blue: 0.54, alpha: 0.63)
    let mapArrow = MapArrow(geometry: geoPolyline,
                            widthInPixels: 30,
                            color: lineColor)
    return mapArrow
}

MapArrowは3つの要素で構成されます。

  • 地図上にポリラインを配置する場所を定義する2つ以上の地理座標のリスト。
  • この座標のリストを含むGeoPolyline
  • 矢印の表示方法を定義する colorwidthInPixelsなどのスタイル パラメーター。

幾何学的な線は2つ以上の点で定義されるため、少なくとも2つのGeoCoordinatesを含む配列を作成する必要があります。これを行わなかった場合は、例外がスローされます。線の外観を変更するには、線の太さ (ピクセル単位) と色を設定します。スクリーンショットの例を参照してください。

1 つ以上のマップ矢印を作成したら、次のようにしてマップ シーンに追加できます。

mapScene = mapView.mapScene

mapArrow = createMapArrow()
mapScene.addMapArrow(mapArrow!)

マップ矢印のインスタンスがすでにマップ シーンに添付されている場合は、さらにそれを追加しようとする試みは無視されます。

Screenshot: Showing a map arrow.

マップビューでは1つのシーンのみが許可され、すべてのマップアイテムがそのシーンに直接配置されます。マップアイテムをグループ化する場合は、配列を使用してマップアイテムを整理し、個別に追加または削除できます。

mapArrow を地図から直ちに削除するには、次のように呼び出します。

mapScene.removeMapArrow(arrow)

MapArrow アイテムは選択可能で、アイテムを選択するときに取得できる Metadata を保存できます。例については、以下のマップマーカーのセクションを参照してください。

マップ ポリゴンを追加する

MapPolygon は、少なくとも 3 つの座標で構成される図形です。それ以外の場合は、レンダリングできません。MapPolyline と同様に、座標が接続されます。ポリゴンは、地図上の領域を強調表示するのに役立ちます。

座標の順序は重要です。

ポリゴンの作成方法については、次の例を参照してください。座標は、リスト内での時計回りの順序に基づいて接続されます。結果として得られるシェイプは、色で塗りつぶすことができます。

private func createMapPolygon() -> MapPolygon {
    // Note that a polygon requires a clockwise order of the coordinates.
    let coordinates = [GeoCoordinates(latitude: 52.54014, longitude: 13.37958),
                       GeoCoordinates(latitude: 52.53894, longitude: 13.39194),
                       GeoCoordinates(latitude: 52.5309, longitude: 13.3946),
                       GeoCoordinates(latitude: 52.53032, longitude: 13.37409)]

    // We are sure that the number of vertices is greater than three, so it will not crash.
    let geoPolygon = try! GeoPolygon(vertices: coordinates)
    let fillColor = UIColor(red: 0, green: 0.56, blue: 0.54, alpha: 0.63)
    let mapPolygon = MapPolygon(geometry: geoPolygon, color: fillColor)

    return mapPolygon
}

MapPolygonは3つの要素で構成されます。

  • 地図上にポリゴンを配置する場所を定義する 3 つ以上の地理座標のリスト。
  • この座標のリストを含むGeoPolygon
  • Color はポリゴン領域の塗りつぶし色を定義します。

ポリゴンは 3 つ以上のポイントによって定義されるため、少なくとも 3 つ GeoCoordinates を含む配列を作成する必要があります。これを行わなかった場合は、例外がスローされます。スクリーンショットの例を参照してください。

マップ ポリゴンを使用して、複雑な塗りつぶしシェイプや塗りつぶしなしのシェイプを作成できます。ただし、自己交差するポリゴンは、座標がリストの順序で結合されているため、望ましくない結果につながる可能性があります。別の方法として、複数のポリゴンを追加することや、目的のシェイプのアウトラインに表示されているとおりに座標を追加することができます。

マップ ポリラインとは異なり、マップ ポリゴンのアウトラインは、リストの最後の座標と最初の座標の間で自動的に接続されます。

1 つ以上のマップ ポリゴンを作成したら、次のようにしてマップ シーンに追加できます。

mapPolygon = createMapPolygon()
mapScene = mapView.mapScene
mapScene.addMapPolygon(mapPolygon!)

マップ ポリゴンがすでにマップ シーンに添付されている場合は、さらにそれを追加しようとする試みは無視されます。

Screenshot: Showing a polygon.

マップビューでは1つのシーンのみが許可され、すべてのマップアイテムがそのシーンに直接配置されます。マップアイテムをグループ化する場合は、配列を使用してマップアイテムを整理し、個別に追加または削除できます。

mapPolygon を地図から直ちに削除するには、次のように呼び出します。

mapScene.removeMapPolygon(area)

MapPolygon アイテムは選択可能で、アイテムを選択するときに取得できる Metadata を保存できます。例については、以下のマップマーカーのセクションを参照してください。

MapMeasureRangeアイテムのリストを設定すると、MapPolygonの表示を現在のズーム レベルに応じて調整できます。

描画順序を設定することで、どのアイテムを重ねて描画するかを指定できます。現時点では、描画順序の設定は、同じタイプのクラス内の順序のみを定義するため、他のマップアイテムとの関係には影響しません。

マップに円を追加する

円形は、地図上の領域を強調表示したり、透明なハローを描いたり、特定の地点を示したりするのに便利です。円は、技術的には三角形のポリゴン図形のシーケンスとしてレンダリングされます。

そのため、円は GeoCircle を使用した MapPolygon インスタンスとして作成できます。

private func createMapCircle() -> MapPolygon {
    let geoCircle = GeoCircle(center: GeoCoordinates(latitude: 52.530932, longitude: 13.384915),
                              radiusInMeters: 300.0)

    let geoPolygon = GeoPolygon(geoCircle: geoCircle)
    let fillColor = UIColor(red: 0, green: 0.56, blue: 0.54, alpha: 0.63)
    let mapPolygon = MapPolygon(geometry: geoPolygon, color: fillColor)

    return mapPolygon
}

円はポリゴンの特殊なシェイプであるため、前述の MapPolygon のセクションで示したように、円をマップ シーンに追加 (または削除) できます。

Screenshot: Showing a circle.

マップ マーカーを追加する

マップ マーカー使用して、地図上の位置を正確に指すことができます。マップ マーカーは常に、地図上にレンダリングされている他のアイテムの上に描画されます。

次のコードにより、マップ マーカーを地図に追加します。

guard
    let image = UIImage(named: "here_car.png"),
    let imageData = image.pngData() else {
        print("Error: Image not found.")
        return
}

let mapImage = MapImage(pixelData: imageData,
                        imageFormat: ImageFormat.png)
let mapMarker = MapMarker(at: geoCoordinates,
                          image: mapImage)

mapView.mapScene.addMapMarker(mapMarker)

この例では、PNG ("here_car.png") から UIImage を作成し、MapImage に変換します。その後、このMapImageMapMarkerインスタンスに設定します。

上記では、画像がデフォルトのピクセル サイズでロードされます。MapImage(imageData: Data, imageFormat: ImageFormat, width: UInt32, height: UInt32) を使用すると、カスタム サイズを設定できます。

HERE SDK for iOS は、透過性 (アルファ チャネル) の有無にかかわらず PNG リソースをサポートし、UIImage でネイティブにサポートされているその他すべての一般的なビットマップ リソース (PDF などのベクター グラフィックなど) をサポートします。オーバーロードされたコンストラクタを使用すると、SVG Tiny 形式でグラフィックを読み込むことができます。

静止画像のアセットとは対照的に、SVG アセットはその XML コンテンツのみを更新する必要があるため、さまざまなコンテンツを表示するのにも役立ちます。静止画像のアセットと SVG アセットは、その場で更新できます。別の方法として、より複雑なコンテンツについては、MapViewPins を使用することをお勧めします (以下を参照)。

画像を表示するには、MapMarkerをマップシーンに追加する必要があります。MapImage は、指定された geoCoordinates を中心として表示されます。

Screenshot: Showing randomly placed map images. The red map circle on top marks the location of the marker.

マーカーが地図に追加された後で「geoCoordinates」を更新することもできます。「mapMarker.coordinates」プロパティが更新されると、マーカーは直ちに新しい位置に表示されます。

マップ マーカーが地図に追加される時系列的な順序により、最初にレンダリングされるマーカーが決まります。これは、明示的な描画順序を設定することで調整できます。現時点では、描画順序の設定は、同じタイプのクラス内の順序のみを定義するため、他のマップアイテムとの関係には影響しません。

マップ マーカーを削除するには、次のように呼び出します。

mapView.mapScene.removeMapMarker(mapMarker)

[MapMarker]() タイプのマップ マーカーのリストを一度に削除することもできます。

mapView.mapScene.removeMapMarkers(mapMarkers)

同様に、addMapMarker(mapMarkers) を使用してバッチ呼び出しで複数のマップ マーカーを追加できます。

HERE SDK は、MapMarker アイテムの高度にカスタマイズ可能なアニメーションのサポートも提供しています。フェード イン/アウト アニメーションは、1 行のコードで適用できます。

// Optionally, enable a fade in-out animation.
let durationInSeconds: TimeInterval = 3
mapMarker.fadeDuration = durationInSeconds

マーカーが MapView に追加または削除されたときに、マーカーがアニメーションでフェードインまたはフェードアウトします。マーカーがビューポートに戻ったとき、つまり地図をパンしてマーカーが再び表示されるようになったときにも、アニメーションが再生されます。

マーカーを移動するなどの高度なカスタム アニメーションは、MapMarkerAnimation クラスを使用します。

MapMeasureRangeアイテムのリストを設定すると、MapMarkerの表示を現在のズーム レベルに応じて調整できます。

さらに多くの機能を利用でき、たとえば不透明度の値を設定してマーカーを透明にすることができます。

さまざまな解像度に対応する Retina サポート

SVG 画像は画質を損なうことなく拡大/縮小できますが、PNG などのピクセルベースの画像形式については、iOS プラットフォームでは @ 注釈を挿入して、ネイティブに Retina をサポートしています。

  • poi.png - デフォルト。アセットサイズの例:100 x 100 ピクセル。または、@1x を挿入できます。
  • [email protected] - Retina ディスプレイ用。2 倍のアセット サイズ:200 x 200 ピクセル。
  • [email protected] - Retina HD ディスプレイ用。3 倍のアセット サイズ:300 x 300 ピクセル。
  • ...

これに特定の HERE SDK コードは必要ありません。iOS はデバイスの画面とファイル名に応じて正しい画像を読み込みます。アプリでは、上記の MapMarker コード スニペットでも示したように、UIImage(named: "poi.png") で画像をネイティブに読み込むため、@ 注釈を省略すると、iOS が正しいアセットを読み込みます。結果として得られる画像は、画面上では同じサイズになりますが、大きな画面ではより高品質のアセットが表示されます。使用例については、MapItems のサンプル アプリをご覧ください。poi.imageset アセット フォルダーでさまざまな解像度を確認できます。このような方法は、より高い解像度で詳細を表示するアセットを作成した場合に理にかなっています。解像度に依存するアセットを省略すると、iOS は大きな画面ではぼやけて見える場合があるアセットを拡大/縮小できます。

アンカー付き POI マーカー

デフォルトでは、各画像は指定された位置を中心として配置されますが、一部のタイプのマーカーについては、この配置を変更する場合があります。例として POI マーカーがあります。通常、このマーカーは下中央部分で位置を指します。

そのため、画像がレンダリングされる場所をシフトする必要があります。デフォルトの中心は (0.5, 0.5) です。設定されたGeoCoordinatesの位置を、ビューの右下隅で指すようにするには、アンカーポイントを (1, 1) に設定します。

アンカー ポイントを使用すると、マーカーをレンダリングする場所を簡単に指定できます。左上隅は (0, 0) のアンカー ポイントと等しく、右下隅は (1, 1) のアンカー ポイントと等しくなります。ビューの大きさに関係なく、幅または高さの半分のポイントは常に0.5になります。これは、正規化されたテクスチャUV座標の概念に似ています。

Illustration: Normalized anchor points can specify any location within or outside a map marker.

位置を指すように POI をシフトする場合は、デフォルトの中央の位置 (0.5) はそのまま使用できますが、画像を上方向に 1 ポイント分シフトする必要があります。「1」は、画像の高さと同じ長さです。「1」よりも大きい値または「0」よりも小さい値を指定して、画像を任意の場所に移動することもできます。「2」は画像の 2 倍の高さを表します。

アンカー付き POI マーカーを地図に追加するには、次の例を参照してください。

guard
    let image = UIImage(named: "poi.png"),
    let imageData = image.pngData() else {
        print("Error: Image not found.")
        return
}

// The bottom, middle position should point to the location.
// By default, the anchor point is set to 0.5, 0.5.
let anchorPoint = Anchor2D(horizontal: 0.5, vertical: 1)
let mapMarker = MapMarker(at: geoCoordinates,
                          image: MapImage(pixelData: imageData,
                                          imageFormat: ImageFormat.png),
                          anchor: anchorPoint)

mapView.mapScene.addMapMarker(mapMarker)

上記の例では、"poi.png" という名前のカスタム POI 画像がさまざまな解像度でプロジェクトのアセット カタログに追加されています。iOS プラットフォームは、デバイスの表示密度に基づいて適切な画像解像度を選択します。その方法については、付属のサンプル アプリを参照してください。

Screenshot: Showing randomly placed map images. The red map circle on top marks the location of the marker.

ポリラインとは異なり、各 MapImage は地図の拡大/縮小率にかかわらず、サイズが維持されます。

テキスト付きのマップ マーカー

MapMarker では、アウトラインの色やテキストのサイズなど、カスタマイズ可能なスタイル オプションのある文字列を表示することもできます。

文字列は mapMarker.text = "Hello Text" を呼び出すことでいつでも設定できます。また、既存のマーカーのテキストを動的に更新することもできます。

オプションをリストとして設定し、テキストの有無にかかわらず複数のMapMarker項目が重なり合う場合にテキストを再配置する優先順位を定義できます。リストの最初の項目がデフォルトの配置を表します。ズーム アウトするなどしてマーカーが近づくと、重なり合うテキストはマーカーとテキストが完全に消えるまでリスト内の次の利用可能な位置に移動します。配置の優先順位が機能するには、mapMarker.isOverlapAllowed = false も呼び出す必要があります。デフォルトでは、マーカーは重なり合うことができ、テキストは下部にのみ表示されます。単一の配置オプションは、配置が変更されないことを意味します。

カスタムフォントを使用したマップマーカー

必要に応じて、TextStyleを使用してフォントのレンダリング方法をカスタマイズできます。さらに、テキストをレンダリングするためのカスタムフォントアセットを指定できます。これを行うには、AssetManagerでフォントをグローバルに登録してから、MapMarkerインスタンスに対してTextStyleを介してフォント名を設定します。カスタムフォントを追加するには、次の手順に従います。

  • カスタムフォントファイルをプロジェクトのアセットフォルダーに追加します。
  • AssetManagerクラスを使用し、assetManager.registerFont(...)メソッドを呼び出してフォントを登録します。
  • 登録すると、カスタムフォントをHERE SDK内で使用できるようになります。

次の例はカスタムフォントを登録する方法を示しています。

let fontFileName = "SimpleSans.ttf"
let assetManager = AssetsManager(mapView.mapContext)

// "SimpleSans" specifies the font name to be referenced wherever required.
assetManager.registerFont(fontName: "SimpleSans", fontPath: fontFileName)

サポートされているフォント形式については、APIリファレンスを参照してください。

Screenshot: Showing a map marker with text.

以下の例は、先ほど登録したカスタムフォントで使用可能なTextStyleオプションの利用方法を示しています。

let textStyleCurrent = mapMarker.textStyle
var textStyleNew = MapMarker.TextStyle()
let textSizeInPixels: Double = 30
let textOutlineSizeInPixels: Double = 5

// Placement priority is based on order. It is only effective when
// overlap is disallowed. The below setting will show the text
// at the bottom of the marker, but when the marker or the text overlaps
// then the text will swap to the top before the marker disappears completely.
// Note: By default, markers do not disappear when they overlap.
let placements: [MapMarker.TextStyle.Placement] = [.bottom, .top]
mapMarker.isOverlapAllowed = false

do {
    textStyleNew = try MapMarker.TextStyle(
        textSize: textSizeInPixels,
        textColor: textStyleCurrent.textColor,
        textOutlineSize: textOutlineSizeInPixels,
        textOutlineColor: textStyleCurrent.textOutlineColor,
        placements: placements,
        fontName: "SimpleSans"
    )
} catch let error as MapMarker.TextStyle.InstantiationError {
    // An error code will indicate what went wrong, for example, when negative values are set for text size.
    print("TextStyle: Error code: \(error.rawValue)")
} catch {
    // Handle any other errors that might be thrown
    print("An unexpected error occurred: \(error)")
}

mapMarker.text = "Hello Text"
mapMarker.textStyle = textStyleNew

異なる名前で複数のフォントを登録できます。同じフォント名での繰り返しの登録は無視されます。

マップマーカーを選択する

地図にマップ マーカーを追加したら、次のようにタップ ジェスチャー デリゲートを使用して、ユーザーがマップ マーカーをタップしたかどうかを確認できます。

// Conform to the TapDelegate protocol.
func onTap(origin: Point2D) {
    let originInPixels = Point2D(x: origin.x, y: origin.y);
    let sizeInPixels = Size2D(width: 1, height: 1);
    let rectangle = Rectangle2D(origin: originInPixels, size: sizeInPixels);

    // Creates a list of map content type from which the results will be picked.
    // The content type values can be mapContent, mapItems and customLayerData.
    var contentTypesToPickFrom = Array<MapScene.MapPickFilter.ContentType>();

    // mapContent is used when picking embedded carto POIs, traffic incidents, vehicle restriction etc.
    // mapItems is used when picking map items such as MapMarker, MapPolyline, MapPolygon etc.
    // Currently we need map markers so adding the mapItems filter.
    contentTypesToPickFrom.append(MapScene.MapPickFilter.ContentType.mapItems);
    let filter = MapScene.MapPickFilter(filter: contentTypesToPickFrom);

    mapView.pick(filter: filter, inside: rectangle, completion:onMapItemsPicked);
}

// Completion handler to receive picked map items.
func onMapItemsPicked(mapPickResults: MapPickResult?) {
    let pickedMapItems = mapPickResults?.mapItems;

    // Note that 3D map markers can't be picked yet. Only marker, polgon and polyline map items are pickable.
    guard let topmostMapMarker = pickedMapItems?.markers.first else {
        return
    }

    showDialog(title: "Map marker picked:", message: "Location: \(topmostMapMarker.coordinates)")
}

クラスは TapDelegate プロトコルに準拠しいる必要があり、クラスがデリゲートとして設定され、タップ イベントを受け取ることができる必要があります。タップ ジェスチャーが認識されたら、完了ハンドラーを設定して潜在的なアイテムを選択できるようにする必要があります。

慣例として、HERE SDK は、ジェスチャー イベントなどの繰り返し発生するイベントにプロトコルを使用します。1 回ごとに処理する必要がある単一のイベントには、完了ハンドラーの形式のコールバック関数が必要です。

タップ ジェスチャーが検出されたら、すぐに画面上のタップされた位置のビュー座標を使用して、その位置の周囲にあるマップ マーカーをマップ ビューに要求できます。ほとんどの場合、半径を 2 ピクセルに指定すれば十分です。すると、PickMapItemsResult により、MapPolylineMapMarker などの検出されたマップ アイテムにアクセスできるようになります。

同じタイプのアイテムを選択する場合、=== 演算子を使用してインスタンスを比較できます。

メタデータを追加する

多くの場合、ユーザーは表示されたマーカーを操作しようとします。たとえば、検索結果をタップしてレストランの詳細を確認しようとします。そのため、MapMarkerMetadata クラスのインスタンスを保持し、さまざまなタイプのデータを添付できます。カスタム タイプもサポートされます。

Metadata は複数のキー/valueのペアを保持できます。以下では、"key_poi" という名前の新しいキーを作成し、マーカーのタイプに関する情報を含む値として String を設定します。

let metadata = Metadata()
metadata.setString(key: "key_poi", value: "This is a POI.")
mapMarker.metadata = metadata

必要に応じて、任意の情報を設定できます。Metadata インスタンスのコンテンツを読もうとする場合は、キーに保存されているデータ (この例では "key_poi") を要求するだけです。

if let message = topmostMapMarker.metadata?.getString(key: "key_poi") {
    showDialog(title: "Map Marker picked", message: message)
    return
}

デフォルトでは、MapMarker インスタンスに Metadata は含まれないため、topmostMapMarker.metadatanil になる場合があります。キーによってアクセスされるデータも、Metadata オブジェクトにそのような情報が含まれない場合は nil になる場合があります。

情報が含まれる場合は、キーの "key_poi" に保存されている String を探し、ヘルパー メソッドを呼び出して、含まれている String をユーザーに提示します。好みに応じて任意の文字列をキーとして選択できますが、一意のキーを使用する必要があります。そうしないと、別のデータ項目に保存されているコンテンツが上書きされます。サンプルの完全なソース コードを確認するには、「MapMarker」のサンプル アプリを参照してください。

CustomMetadataValueインターフェースを使用して、カスタムオブジェクトをMetadataに保存することもできます。「検索」セクションで、検索結果のデータオブジェクトが全体として保存されている例を参照できます。

マップマーカークラスターを追加する

ズーム レベルによっては、複数の MapMarker アイテムが重なる場合があります。MapMarkerCluster クラスを使用すると、複数の MapMarker アイテムをグループ化して視覚的な混乱を減らすことができます。重複しているマーカーは、単一の画像表現に置き換えられます。このようなクラスター画像は選択でき、これにはその中に含まれるマーカーのみが含まれています。

次のコードは、複数の MapMarker アイテムを含むクラスターを作成する方法を示しています。

guard
    let image = UIImage(named: "green_square.png"),
    let imageData = image.pngData() else {
        print("Error: Image not found.")
        return
}

let clusterMapImage = MapImage(pixelData: imageData,
                               imageFormat: ImageFormat.png)

// Defines a text that indicates how many markers are included in the cluster.
var counterStyle = MapMarkerCluster.CounterStyle()
counterStyle.textColor = UIColor.black
counterStyle.fontSize = 40
counterStyle.maxCountNumber = 9
counterStyle.aboveMaxText = "+9"

let mapMarkerCluster = MapMarkerCluster(imageStyle: MapMarkerCluster.ImageStyle(image: clusterMapImage),
                                        counterStyle: counterStyle)
mapView.mapScene.addMapMarkerCluster(mapMarkerCluster)
mapMarkerClusters.append(mapMarkerCluster)

var index = 1
for _ in 1...10 {
    let indexString = String(index)
    mapMarkerCluster.addMapMarker(marker: createRandomMapMarkerInViewport(indexString))
    index = index + 1
}

複数の重複するマップ マーカーのクラスターを表すのに使用する MapImage を設定できます。

MapMarker アイテムは、すでに地図に表示されている MapMarkerCluster に対して、追加または削除することができます。すべてのマーカーを含むクラスターを削除する場合は、次のように呼び出します。

mapView.mapScene.removeMapMarkerCluster(mapMarkerCluster)

クラスターに含まれている MapMarker アイテムは、pickMapItemsResult.markers の一部ではありません。そのため、pickMapItems() の呼び出しから選択結果を受け取った後、以下に示すコードを使用してそれらを個別に選択できます (MapMarker のセクションを参照)。

if let groupingList = pickedMapItems?.clusteredMarkers {
    handlePickedMapMarkerClusters(groupingList)
}

...

private func handlePickedMapMarkerClusters(_ groupingList: [MapMarkerCluster.Grouping]) {
    guard let topmostGrouping = groupingList.first else {
        return
    }

    let clusterSize = topmostGrouping.markers.count
    if (clusterSize == 0) {
        // This cluster does not contain any MapMarker items.
        return
    }
    if (clusterSize == 1) {
        let metadata = getClusterMetadata(topmostGrouping.markers.first!)
        showDialog(title: "Map Marker picked", message: "This MapMarker belongs to a cluster. Metadata: \(metadata)")
    } else {
        var metadata = ""
        for mapMarker in topmostGrouping.markers {
            metadata += getClusterMetadata(mapMarker)
            metadata += " "
        }
        let metadataMessage = "Contained Metadata: " + metadata + ". "
        showDialog(title: "Map marker cluster picked",
                   message: "Number of contained markers in this cluster: \(clusterSize). \(metadataMessage) Total number of markers in this MapMarkerCluster: \(topmostGrouping.parent.markers.count)")
    }
}

private func getClusterMetadata(_ mapMarker: MapMarker) -> String {
    if let message = mapMarker.metadata?.getString(key: "key_cluster") {
         return message
    }
    return "No metadata."
}

このコードにより、クラスターが選択されたかどうかを検出し、含まれているマーカーについてユーザーに通知できます。また、markers リストに 1 つのアイテムのみが含まれている場合は、単一の MapMarker インスタンスを特定できます。

Several map marker cluster. The number indicates the contained marker.

通常、必要な MapMarkerCluster インスタンスは 1 つだけです。これを超えると、最上位のクラスターのみが処理されます。

マーカーの距離に応じて、複数のクラスター画像が同じMapMarkerClusterインスタンスに表示される場合があります。各画像には、クラスター化されたマーカーの総数のサブセットが含まれます。

GitHubの例については、「MapItems」アプリを参照してください。

3Dマップマーカーを追加する

HERE SDK を使用すると、カスタム 3D モデルを地図に追加できます。必要に応じて、これらのモデルをブレンド Color と一緒にテクスチャ化することができます。共通の .obj ファイル形式を使用して、3D モデルのジオメトリーを指定します。3D モデルは無料の Three.js オンライン エディターなどの、一般的な 3D モデリング ソフトウェアを使用して生成できます。

The obstacle.obj 3D object we will use for this example.

obj ファイル形式では、頂点、法線、テクスチャ座標、面を指定して、モデルのレンダリング方法を定義します。作成されたモデルは、その座標を更新することにより、地図内で動かすことができます。モデルの向きは、方位、ピッチ、ロールを更新することで変更できます。

HERE SDK ではマテリアル ファイル (*。mtl) およびマルチ テクスチャリングはサポートされないことに注意してください。

obj 形式で定義されたモデルとそのモデルをラップするテクスチャを作成したら、両方のファイルをドラッグ アンド ドロップでプロジェクトのアセットのディレクトリに追加します。

The obstacle.obj 3D object we will use for this example.

iOS の Bundle を使用して、リソースへのパスを取得します。

private func getResourceStringFromBundle(name: String, type: String) -> String {
    let bundle = Bundle(for: ViewController.self)
    let resourceUrl = bundle.url(forResource: name,
                                 withExtension: type)
    guard let resourceString = resourceUrl?.path else {
        fatalError("Error: Resource not found!")
    }

    return resourceString
}

これにより、ファイルが予期されたファイルの場所に存在しない場合は例外が返されます。

これで、次のコードを使用して MapMarker3D を地図に追加できるようになります。

let geometryFile = getResourceStringFromBundle(name: "obstacle", type: "obj")
let textureFile = getResourceStringFromBundle(name: "obstacle_texture", type: "png")

let mapMarker3DModel = MapMarker3DModel(geometryFilePath: geometryFile, textureFilePath: textureFile)
let mapMarker3D = MapMarker3D(at: geoCoordinates, model: mapMarker3DModel)
mapMarker3D.scale = 6
mapMarker3D.isDepthCheckEnabled = true

mapView.mapScene.addMapMarker3d(mapMarker3D)

上の例では、両方のアセット ファイルを、モデルを定義する MapMarker3DModel コンストラクタに渡します。同様に、MapImage 要素ですでに説明したとおり、利用可能な MapMarker3D コンストラクタのいずれかにモデルを渡すことができます。また、倍率を設定して、モデルが 6 倍の大きさで表示されるようにします。obj モデルは3D座標空間で定義されるため、長さの単位は含まれません。

MapMarker3D アイテムは、他のマップ アイテムと同じように MapScene を介して追加および削除できます。

テクスチャのサイズは1ピクセル以上である必要があります。透明なピクセルをテクスチャ画像として追加する場合は、テクスチャなしのモデルを、Color (3 番目のパラメーターとして MapMarker3DModel コンストラクタに渡すことができる) とブレンドすることができます。

以下に、表示例を示します。モデルは、指定された地理座標の中心に配置されます。モデルの中心は、その座標系の原点によって定義されます。

Two custom 3D map markers shown on a tilted map.

プロパティ isDepthCheckEnabled は、3D マーカーの頂点の深さをレンダリング中に考慮するかどうかを指定します。デフォルトでは、これは false に設定されています。

  • isDepthCheckEnabled が false に設定されている場合は、MapMarker3D アイテムは常に、地図に表示されている他のアイテムの上に描画されます。
  • isDepthCheckEnabledが true に設定されている場合は、3D マーカーが建物の形状などの他のマップ オブジェクトによって隠れる可能性があります。isDepthCheckEnabled メソッドは、内側と外側の領域があるトーラスのような複雑な 3D オブジェクトを描画するのに便利です。

2D MapMarker アイテムと同様に、MapMarker3D は地図を拡大/縮小してもサイズは変わりませんが、地図と一緒に回転します。

3D マーカーを地図に合わせて拡大/縮小させるにはどうすればよいでしょうか。

そのためには、RenderSize.Unit を受け取る MapMarker3D 用の簡易コンストラクタを使用します。たとえば、densityIndependentPixels を使用して拡大/縮小を禁止すると、結果として得られるマーカーはズーム レベルに依存しない、固定サイズで作成されます。拡大/縮小を許可するには、RenderSize.Unitmetersを使用します。

MapMeasureRangeアイテムのリストを設定すると、MapMarker3Dの表示を現在のズーム レベルに応じて調整できます。

MapMarker3D の透明度を変更する場合は、opacity 係数を指定します。この係数はマーカーのテクスチャのアルファ チャネルに適用されます。renderInternalsEnabledフラグを設定することで、前面に配置されたポリゴンによって隠された内部ジオメトリーをレンダリングするかどうかを指定できます。

フラット マップ マーカーを追加する

デフォルトでは、MapMarker は地図とともに回転せず、また地図を傾けても傾きません。これは、MapMarker3Dを使用してフラットにすることで変更できます。実際、フラット マップ マーカーは 3D 空間の Z 軸に 0 の長さを含む、MapMarker3D アイテムの特殊なケースです。

MapImage パラメーターを受け取る簡易コンストラクタを使用して、フラットな MapMarker3D オブジェクトを作成できます。拡大/縮小を禁止するには、RenderSize.UnitdensityIndependentPixels を使用します。結果として得られるマーカーは、ズーム レベルに依存しない、固定サイズで作成されます。拡大/縮小を許可するには、RenderSize.Unitmetersを使用します。

結果として得られるフラットマーカーは、地図とともに傾き、地図を回転すると一緒に回転します。

guard
  let image = UIImage(named: "poi.png"),
  let imageData = image.pngData() else {
      print("Error: Image not found.")
      return
}

// The default scale factor of the map marker is 1.0. For a scale of 2, the map marker becomes 2x larger.
// For a scale of 0.5, the map marker shrinks to half of its original size.
let scaleFactor: Double = 0.5

let mapImage:MapImage = MapImage(pixelData: imageData, imageFormat: ImageFormat.png)

// With densityIndependentPixels, the map marker will have a constant size on the screen regardless if the map is zoomed in or out.
let mapMarker3D: MapMarker3D = MapMarker3D(at: geoCoordinates, image: mapImage, scale: scaleFactor, unit: RenderSize.Unit.densityIndependentPixels)

mapView.mapScene.addMapMarker3d(mapMarker3D)

この例では、リソースから PNG ("poi.png") を読み込み、そこから MapImage を作成します。その後、このMapImageMapMarkerインスタンスに設定します。

画像を表示するには、MapMarkerをマップシーンに追加する必要があります。MapImage は、指定された geoCoordinates を中心として表示されます。

フラット 3D マーカーを地図に合わせて拡大/縮小させるにはどうすればよいでしょうか。

MapMarker3D を使用してフラット マップ マーカーを作成し、RenderSize.Unitmeters を使用してズーム イン/アウト時の拡大/縮小を許可します。

2D モデルからフラット 2D マップ マーカーを作成する方法

次の段落では、手書きの obj ファイルから 2D モデルを作成する方法を説明します。その結果、前のセクションと同じフラット マップ マーカーを作成できます。

以下に、2D の平面の例を示します。これは、X 軸と Y 軸方向に 2 単位分の大きさです。3D オブジェクトは簡単に拡大/縮小できるため、モデルのサイズは重要ではありません。この例では、座標系の原点は平面の下部にあります。このようにして、POI マーカー アセットを、指定された地理座標を指すテクスチャとしてラップできます。


v -1 2 0
v 1 2 0
v -1 0 0
v 1 0 0

vt 0 1
vt 1 1
vt 0 0
vt 1 0

vn 0 0 1
vn 0 0 1
vn 0 0 1
vn 0 0 1

f 1/1/1 3/3/3 2/2/2
f 3/3/3 4/4/4 2/2/2

この例では、上記の平面のモデル定義を "plane.obj" というテキスト ファイルに格納します。

The plane.obj 3D model we will use for this example.

平面は正方形であるため、POI 画像のサイズを透明な領域で左右に拡大し、画像が正方形になるようにしています。元の画像は正方形ではなく長方形であるため、平面にラップされると歪みが生じます。

obj 形式で定義されたモデルとそのモデルをラップするテクスチャを作成したら、両方のファイルをドラッグ アンド ドロップでプロジェクトのアセットのディレクトリに追加します。

The poi_texture.png texture we will use for this example.

次のコードを使用して、地図にフラット マーカーを追加します。

let geometryFile = getResourceStringFromBundle(name: "plane", type: "obj")

// The POI texture is a square, so we can easily wrap it onto the 2 x 2 plane model.
let textureFile = getResourceStringFromBundle(name: "poi_texture", type: "png")

let mapMarker3DModel = MapMarker3DModel(geometryFilePath: geometryFile, textureFilePath: textureFile)
let mapMarker3D = MapMarker3D(at: geoCoordinates, model: mapMarker3DModel)
// Scale marker. Note that we used a normalized length of 2 units in 3D space.
mapMarker3D.scale = 70

mapView.mapScene.addMapMarker3d(mapMarker3D)

getResourceStringFromBundle(name:type:) メソッドのコードは、前のセクションを参照してください。

3D 空間では 2 単位の正規化された長さを使用しているため、モデルを 60 倍に拡大します。結果については、下のスクリーンショットを参照してください。

Flat map markers - rotated and tilted together with the map.

フラット マーカーが指している位置を示す赤い円を追加しました。

フラット マップ マーカーは、地図を回転させると一緒に回転し、地図を傾けるとマップ マーカーも傾きます。これにより、2D モデルが平坦であっても 3D のような効果が得られます。デフォルトでは、フラット マップ マーカーのスケール値は地図のズーム レベルと一緒に変更されません。たとえば、地図をズーム アウトしてもマップ マーカーは表示されたままになります。フラット化されていない MapMarker の場合も同じです。

マーカーを地図と一緒に拡大/縮小させるには、前のセクションで示したように、フラット マップ マーカーを作成します。

位置情報インジケーターを追加する

3D マップ アイテムのもう 1 つのタイプとして、LocationIndicator があります。通常、地図上にはユーザーのデバイスの現在地 (進行方向を含む) を示すインスタンスが 1 つだけ追加されます。

HERE SDK では、さまざまなユースケース用に事前定義された 3D アセットが用意されています。必要に応じて、これらの定義済みの各スタイルを、独自のカスタマイズされた MapMarker3DModel に置き換えることができます。

すべてのマップ マーカーと同様に、マップ ビューのズーム レベルを変更しても、LocationIndicator は拡大/縮小されません。ただし、地図を傾けた場合は LocationIndicator が水平線方向に移動して小さくなります。

地図に LocationIndicator を追加する方法の例を見てみましょう。

private func addLocationIndicator(geoCoordinates: GeoCoordinates,
                                  style: LocationIndicator.IndicatorStyle) {
    let locationIndicator = LocationIndicator()
    locationIndicator.locationIndicatorStyle = style

    // A LocationIndicator is intended to mark the user's current location,
    // including a bearing direction.
    // For testing purposes, we create a Location object. Usually, you may want to get this from
    // a GPS sensor instead.
    var location = Location(coordinates: geoCoordinates)
    location.time = Date()
    location.bearingInDegrees = getRandom(min: 0, max: 360)

    locationIndicator.updateLocation(location)

    locationIndicator.enable(for: mapView)
}

上に示すとおり、アセットは必要ありません。代わりに IndicatorStyle が設定されています。

  • LocationIndicator.IndicatorStyle.navigation:ナビゲーションのユースケース用に設計されたアセット。
  • LocationIndicator.IndicatorStyle.pedestrian:座っているときや歩いているときなど、ハンドヘルドでの使用のために設計されたアセット。

どちらのスタイルも、現在の方位を示します。これは、ユーザーの進行方向を表します。この例では、0° ~ 360° の間のランダムな方位値を設定しています。0° の方位は北が上を示しているため、矢印は上を指しています。これは地図もデフォルトで北が上を指しているためです。

A location indicator for navigation use case.

ナビゲーション中の一般的なユーザー体験のアプローチは、ユーザーが運転している方向に地図を回転させて、方位が変化しても方向矢印が常に上を指すようにすることです。通常、ナビゲーション中の矢印は、ユーザーが走行中の道路の進行方向を指すことが期待されます。

歩行者のユースケースでは、ユーザーの現在地が焦点となります。そのため、方向を示す矢印ははるかに小さくなります。

A location indicator for pedestrian use case.

方位値の活用方法は、アプリケーションによって異なります。Location オブジェクトから取得された方位は移動方向を示す一方、アプリケーションでは真北を基準にして方位を設定する場合もあります (コンパス モード)。矢印が真北を指すようにするには、方位値を 0° に変更します。このような調整は、位置情報源から新しい Location の更新情報を受信するたびに実施する必要があります (以下を参照)。

通常、地図に設定される LocationIndicator のインスタンスは 1 つのみです。updateLocation() を呼び出すことでその場所を更新でき、setLocationIndicatorStyle() を呼び出すことで、そのスタイルをその場で更新できます。

各スタイルには、GPS 信号が失われたときのグレー状態など、さまざまな状態を定義する、いつかの関連付けられた MarkerType の値があります。LocationIndicator がカスタマイズされている場合は、すべてのタイプを設定することが重要です。そうしないと、状態を変更しても効果はありません。

次のコードスニペットは、非アクティブ状態を示す定義済みのグレーのタイプとデフォルト状態を切り替える方法を示しています。

let isActive = locationIndicator.isActive
// Toggle between active / inactive state.
locationIndicator.isActive = !isActive

精度のハローの色はsetHaloColor()メソッドを使用して設定できます。この機能は現在ベータ版であり、今後のリリースで動作が変更される可能性があります。
たとえば、次のコードスニペットでは、ハローの色を半透明の黄色に設定しています。

locationIndicator?.setHaloColor(locationIndicator?.locationIndicatorStyle ?? .pedestrian, color: UIColor(red: 1.0, green: 1.0, blue: 0.0, alpha: 0.3))

A location indicator with translucent yellow halo color.

RGBA値でハローの色と透明度を定義しており、0.30Fは透明度レベルを設定しています。現在のハローの色を取得するには、getHaloColor()メソッドを使用します。

LocationIndicatorを削除するには、次のように呼び出します。

// Remove indicator from map view.
locationIndicator.disable()

LocationIndicator はまだ選択することはできません。また、常に他のすべての要素の上に描画され、描画順序を設定する必要はありません。

ほとんどのユースケースでは、頻繁に更新される Location に基づいて、LocationIndicator を更新します。MapItems のサンプル アプリの一部として、分離された使用例を見ることができます。

LocationIndicator は、常に 0 に近い固定された高度でレンダリングされます。MapCameraを変更して、高い高度で地理座標を表示すると、問題が発生する可能性があります。MapCameraの角度が傾いていて、高度が高すぎると、この新たな視点が原因でLocationIndicatorがビューポートから消えることがあります。

HERE SDK (Navigate) の場合:
位置情報の取得に関するセクションを参照して、デバイスのGPSチップから実際のLocationの更新情報を取得する方法を確認してください。Positioningのサンプルアプリでは、LocationIndicatorを使用してこれを行う方法を示しています。
位置信号の水平精度を視覚化することもできます。上にあるLocationIndicatorでは上に精度のハローのカスタムカラーを設定できます。デフォルトでは、精度の視覚化は無効になっています。

NavigationCustom のサンプル アプリは、ナビゲーションが停止したときにカスタムの LocationIndicator や別のマーカー タイプに切り替える方法を示しています。

MapViewLifecycleListener の使用方法

MapViewLifecycleListener は、MapView のライフサイクルをリッスンする任意のクラスで実装できます。この実装により、次のイベントに対応します。

  • onAttach(to mapView: MapViewBase):マップ ビューに実装するオブジェクトを追加するときに呼び出されます。
  • onPause():実装するオブジェクトが添付されているマップ ビューが一時停止したとき (通常、アプリがバックグラウンドに移動したとき) に呼び出されます 。
  • onResume():実装するオブジェクトが添付されているマップ ビューが再開したとき (通常、アプリがフォアグラウンドに移動したとき) に呼び出されます。
  • onDetach(from mapView: MapViewBase):マップ ビューから実装するオブジェクトを削除するときに呼び出されます。
  • onDestroy():実装するオブジェクトが添付されているマップ ビューが破棄されたときに呼び出されます。

実装するオブジェクトは、次のように添付および削除できます。

  • オブジェクトを MapView ライフサイクルに添付する:mapView.addLifecycleDelegate(myObject)
  • オブジェクトを MapView ライフサイクルから削除する:mapView.removeLifecycleDelegate(myObject)

MapView からマップ アイテムを選択する方法

以下の表に、現在選択可能なマップ アイテムを選択する方法を示します。

マップ アイテム
選択方法
MapPolyline mapView.pick(filter: filter, inside: rectangle, completion:onMapItemsPicked)
MapPolygon mapView.pick(filter: filter, inside: rectangle, completion:onMapItemsPicked)
MapMarker mapView.pick(filter: filter, inside: rectangle, completion:onMapItemsPicked)
MapMarkerCluster.Grouping mapView.pick(filter: filter, inside: rectangle, completion:onMapItemsPicked)
埋め込みPOI mapView.pick(filter: filter, inside: rectangle, completion:onMapItemsPicked);
MapViewPin UIHostingController(rootView:PinTextView(..., onTap: onMapViewPinTapped)).view

mapView.pick()メソッドの詳細については、NavigateおよびExploreの「APIリファレンス」を参照してください。

マップビューピンを追加する

マップマーカーを使用すると、地図にアイテムをスムーズに配置できますが、マップピンではネイティブなプラットフォームビューを地図にピン留めできます。これは、ポップアップバブル (またはアニメーション化された情報バブル)、注釈、または追加情報を含むインタラクティブコントロールを表示する場合に便利です。たとえば、マップマーカーをタップすると、その場所に関する詳細情報のオーバーレイが表示されます。

マップピンは、複数のビューから構成でき、それぞれを通常のプラットフォームビューと同じように、ユーザー定義の制約、アニメーション、ネイティブジェスチャー処理に従って使用できます。

ビュー ピンはマップ ビューに添付されていますが、地図自体の一部ではありません。これは、ネイティブ ビューで構成されているためです。そのため、マップ ビューをパンすると、ピンはマップ ビューと一緒に移動しますが、わずかな遅延が発生します。この問題を回避するには、特に一度に複数のインスタンスを表示する場合は、代わりにMapMarkerを使用することを検討してください。

ビューは、構造体で定義することも、プログラムで作成することもできます。SwiftUIでカスタムピンビューを定義してアタッチする方法は次のとおりです。

struct PinTextView: View {
    let text: String
    let backgroundColor: Color
    let onTap: () -> Void

    var body: some View {
        Text(text)
            .font(.system(size: 17))
            .foregroundColor(.white)
            .frame(width: 200, height: 40)
            .background(backgroundColor)
            .cornerRadius(5)
            .onTapGesture {
                onTap()
            }
    }
}

PinTextViewは、ラベルやボタンなどのピンの内容を表します。次に、このビューをピンとして地図に追加するために、これをUIHostingControllerでラップします。これにより、SwiftUI ViewUIViewに変換されます。以下に例を示します。

let pinView = UIHostingController(rootView: PinTextView(
    text: "Centered ViewPin",
    backgroundColor: Color(red: 72/255, green: 218/255, blue: 208/255),
    onTap: onMapViewPinTapped
)).view

pinView?.frame = CGRect(x: 0, y: 0, width: 200, height: 40)

続いて、以下に示すように、onTapジェスチャーリスナーのハンドラーを追加します。

func onMapViewPinTapped() {
    print("Tapped on MapViewPin.")
}

これであと必要なのは、ビューを地図に追加するだけになりました。ポリゴンやマーカーなどの他のマップ アイテムと違い、ピンは地図に直接追加できます。

let viewPin = mapView.pinView(view: pinView!, to: geoCoordinates)

このメソッドは、ピンの制御 (アンカー ポイントの指定など) に使用できるプロキシ オブジェクトを返します。

mapViewに追加すると、ビュー (ネストされたすべてのサブビューを含む) は指定されたgeoCoordinatesの位置を中心として配置され、地図を動かすと、その位置が指定された地理的位置に自動的に調整されます。

スクリーンショット:AndroidのTextViewを示すマップピン。デフォルトでは、ビューは指定された位置を中心として配置されます。

ビューにアニメーションを適用したり、他のインタラクション リスナーをアタッチしたりすることもできます。ビューがマップビューに添付されると、他のiOSビューと同様に動作します。ただし、地図に固定されたままになり、パンまたはズーム時に一緒に移動します。マップポリラインやマップマーカーと同様に、ViewPinは地図を回転しても回転しません。

マップ ピンはいくつでも追加できますが、パフォーマンスを考慮してください。たとえば、地図上に複数の検索結果を表示する場合、マップ ピンはマップ マーカーよりもパフォーマンスが劣ります。

地図からピンを削除するには、次のように呼び出します。

mapView.unpinView(view: pinView)

複数の ViewPin インスタンスを追加した場合は、次のように呼び出すことで mapView オブジェクトからすべてのピンにアクセスできます。

let viewPins = mapView.viewPins

通常、マップ ピンは地図上の特定の場所について、追加のダイナミック コンテンツを表示するのに最適です。

アンカー付きマップピン

デフォルトでは、マップ ピンは指定された位置を中心として配置されます。しかし、下の領域を隠すことなくピンを使用するにはどうすればよいでしょうか。

この目的のために、任意のビューで行う場合と同じように、アンカーポイントを指定できます。

pinView?.layer.anchorPoint = CGPoint(x: 0.5, y: 1)

これにより、ビューはその位置の水平方向の中心に配置され、ビューの下部が指定された座標と一致します。下のスクリーンショットに表示されているように、マップ ピンは、マップ ピンの指定された中心位置を示すマップ円オブジェクトの上に配置されます。

Screenshot: An anchored map pin.

アンカー ポイントを使用すると、ビューをレンダリングする場所を簡単に指定できます。ビューの大きさに関係なく、幅または高さの半分のポイントは常に 0.5 になります。これは、正規化されたテクスチャ UV 座標の概念に似ています。

デフォルトでは、アンカー ポイントは (0.5, 0.5) で、ビューはその位置を中心としてレンダリングされます。ビューは任意のサイズにできるため、最大の幅および高さの値は「1」となります。ビューの寸法は、完全に拡張した後に計算されます。ビューの正確な寸法がわかっている場合は、最大値の 1 を基準にして、ビュー内の特定のポイントを簡単に計算できます。

オフセットを使用すると、X 軸および Y 軸に沿った移動を指定できますが、アンカー ポイントはビューなどの長方形の左上隅の相対位置を定義します。アンカーポイントは、ビューの境界との関連で、指定されたGeoCoordinatesの位置に中心を合わせるビューのポイントを定義します。

マップポリラインのグラデーションを有効にする

HERE SDKでは、グラデーションをレンダリングしてポリラインをカスタマイズできます。MapPolyline.lineColorMapPolyline.progressColor間の遷移を徐々にレンダリングできます。このグラデーション遷移の長さは、ズームレベルに依存するピクセル単位で定義され、MapPolyline.setProgressGradientLengthおよびMapPolyline.getProgressGradientLengthを使用して設定または取得できます。

let mapPolyline = createMapPolyline()

// Configure the progress color. Currently, cyan color is being used.
let progressColor = UIColor(red: 0, green: 1, blue: 1, alpha: 0.5)
mapPolyline.progressColor = progressColor

// Set the progress value, ranging from 0 to 1. For example, 0.40 represents 40% progress.
mapPolyline.progress = 0.40;

// Defines the gradient length using MapMeasureDependentRenderSize.
var gradientLength: MapMeasureDependentRenderSize
do {
    let widthInPixels: Double = 20.0
    gradientLength = try MapMeasureDependentRenderSize(
        sizeUnit: RenderSize.Unit.pixels,
        size: widthInPixels
    )
} catch let error {
    fatalError("Error while creating gradient length. Cause \(error)")
}

// Set the maximum gradient length between 'lineColor' and 'progressColor' in zoom-level-dependent pixels.
mapPolyline.progressGradientLength = gradientLength
mapScene.addMapPolyline(mapPolyline)

これにより、次のようなグラデーションポリラインが作成されます。

Screenshot: Gradient map polyline.

gradientLengthてサポートされているのはRenderSize.Unit.PIXELSのみで、MapMeasureDependentRenderSizeを設定する際にサポートされているのはMapMeasure.Kind.ZOOM_LEVELのみです。サポートされていないパラメーターはすべて無視されます。

ズームレベルに基づいてグラデーションの長さを変更するには、複数の値をMapMeasureDependentRenderSizeで使用します。デフォルトでは、グラデーション長はゼロピクセルの固定値です。

グラデーションはポリラインの長さに基づいてレンダリングされるため、実際のグラデーションはポリラインより短くなる場合があります。

サンプルアプリを試す

上記のコードスニペットはすべて、選択したプラットフォームのGitHubで見つけることができる「MapItems」サンプルアプリで確認できます。


このページは役に立ちましたか?