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

カスタムレイヤーを追加する

HERE SDKには、HERE Style EditorおよびStyleクラスを使用して地図をカスタマイズするためのさまざまなオプションが用意されています。その他のオプションについては、以下で説明します。

  • **カスタムラスターレイヤー:**ラスターデータソースを使用して、マップスキーム上にタイル画像をオーバーレイします。
  • **ポイント、ライン、ポリゴンのカスタムレイヤー:**ポイント、タイル、ポリゴンレイヤーを使用して、カスタマイズ可能なコンテンツを地図に追加します。
  • **ポイント、タイル、ポリゴンのカスタムレイヤーのタイルデータソース:**ポイント、タイル、ポリゴンのソースを提供することで、大規模なデータセットを効率的に管理および表示できます。

カスタムラスターレイヤーを追加する

カスタムラスターレイヤーを使用すると、HERE MapSchemeスタイルの上に独自のラスタータイルサービスを追加できます。これは、世界から選択したエリアにオーバーレイとして表示するタイルをホストする独自のサーバー、またはOpenStreetMapなどの公開タイルサーバーを使用できます。完全に不透明なマップ タイル オーバーレイおよび完全に透明なマップ タイル オーバーレイがサポートされています。また、複数のラスター レイヤーを地図に同時に追加できます。

これはこの機能のベータリリースであるため、いくつかのバグや予期しない動作が発生する可能性があります。関連するAPIは、廃止のプロセスを経ずに、新しいリリースに変更される可能性があります。

カスタム ラスター レイヤーを追加するには、RasterDataSource を作成する必要があります。RasterDataSource は、表示するラスター タイル データのソースを表します。その設定を変更することもできます。RasterDataSourceConfiguration を使用すると、URL、タイル処理スキーマ、ストレージ レベル、キャッシュ パラメーターなどのデータ ソースの設定を指定できます。

最後に、MapLayerBuilder を使用して MapLayer を作成し、レンダリング可能なマップ オーバーレイを地図に追加できます。

  • MapLayerVisibilityRangeを使用して、マップレイヤーのズームレベルを指定します。
  • MapLayerPriorityを使用して、MapLayerの描画順序を指定します。
  • MapContentTypeを使用して、MapLayerによって表示されるデータ型を指定します。
  • 必要に応じて、Style API を使用して、実行時の不透明度や明るさなどのプロパティを調整します。詳細については、カスタム レイヤーのスタイル ガイドを参照してください。

ラスター タイル イメージの場合は、MapContentType.rasterImage を使用します。

ピンチ、回転、ズームなど、地図のデフォルトのジェスチャー アクションは HERE ベクター マップの場合と同様にラスター タイルでも動作しますが、いくつかの違いがあります。たとえば、ラスター タイルはビットマップとして読み込まれるため、ラスター マップ タイルを回転させると、タイルに含まれているラベルと道路名もすべて回転します。

カスタム マップ スタイルまたはデフォルトのマップ スタイルを使用してマップ シーンを読み込む場合、地図はベクター タイルを使用してレンダリングされます。ベクター タイルでは、マップ情報が頂点とパスで構成されるベクター データとして表されるため、スケーラビリティとパフォーマンスが向上します。これとは対照的に、ラスター タイルは等間隔に配置された正方形であり、ピクセル情報のみを表すビットマップ画像で構成されます。なお、衛星マップ スタイルもラスターをベースとしています。

次のようにRasterDataSourceを作成します。

private func createRasterDataSource(dataSourceName: String) -> RasterDataSource {
    // Note: As an example, below is an URL template of an outdoor layer from thunderforest.com.
    // On their web page you can register a key. Without setting a valid API key, the tiles will
    // show a watermark.
    // More details on the terms of usage can be found here: https://www.thunderforest.com/terms/
    // For example, your application must have working links to https://www.thunderforest.com
    // and https://www.osm.org/copyright.
    // For the below template URL, please pay attention to the following attribution:
    // Maps © www.thunderforest.com, Data © www.osm.org/copyright.
    // Alternatively, choose another tile provider or use the (customizable) map styles provided by HERE.
    final templateUrl = 'https://tile.thunderforest.com/outdoors/{z}/{x}/{y}.png'

    let storageLevels: [Int32] = [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
    var rasterProviderConfig = RasterDataSourceConfiguration.Provider(
        urlProvider: TileUrlProviderFactory.fromXyzUrlTemplate(templateUrl)!,
        tilingScheme: TilingScheme.quadTreeMercator,
        storageLevels: storageLevels)

    // If you want to add transparent layers then set this to true.
    rasterProviderConfig.hasAlphaChannel = false

    // Raster tiles are stored in a separate cache on the device.
    let path = "cache/raster/mycustomlayer"
    let maxDiskSizeInBytes: Int64 = 1024 * 1024 * 128 // 128 MB
    let cacheConfig = RasterDataSourceConfiguration.Cache(path: path,
                                                          diskSize: maxDiskSizeInBytes)

    // Note that this will make the raster source already known to the passed map view.
    return RasterDataSource(context: mapView.mapContext,
                            configuration: RasterDataSourceConfiguration(name: dataSourceName,
                                                                         provider: rasterProviderConfig,
                                                                         cache: cacheConfig)
}

なお、必要に応じて次のような templateUrl の呼び出しに含めることもできます。

var rasterProviderConfig =
    RasterDataSourceConfiguration.Provider(tilingScheme: TilingScheme.quadTreeMercator,
                                           storageLevels: storageLevels,
                                           urlProvider: tileUrlRequestHandler)

...

// Handle tile requests for RasterDataSourceConfiguration.Provider.
private func tileUrlRequestHandler(x: Int32, y: Int32, level: Int32) -> String {
    return "https://tile.thunderforest.com/outdoors/\(level)/\(x)/\(y).png"
}

後者では、各タイル リクエストをリッスンするオプションが提供され、たとえば、必要に応じて、リクエストされたタイルをより詳細に制御できます。

なお、カスタム ラスター レイヤーは独自のキャッシュ ディレクトリを使用します。これにより、ベクター ベースのマップ データに使われるマップ キャッシュから独立できます。詳細については、「APIリファレンス」でRasterDataSourceConfigurationを参照してください。

このコードは、Thunderforest のタイル ソースを使用します。利用規約の詳細については、https://www.thunderforest.com/terms/ および https://www.osm.org/copyright を参照してください。

zoom 値は地図の現在のズーム レベルを表し、xTileyTile は水平のタイル番号と垂直のタイル番号を定義します。たとえば、標準の OSM 地図を表示するには、次のテンプレート URL を使用します。

let templateUrl = "https://a.tile.openstreetmap.org/{z}/{x}/{y}.png"

OSM 形式に従うその他のタイル サーバーはこちらにまとめてあります。なお、HERE SDK がサポートしているのはタイル サーバーのみです。ベクター データを提供するサーバーはサポートされていません。ベクター ベースのタイルは、HERE Style Editor および埋め込みマップ スタイル (上を参照) でのみ使用できます。

タイル ソースが作成されると、MapLayer を構築できます。

private func createMapLayer(dataSourceName: String) -> MapLayer {
    // The layer should be rendered on top of other layers including the "labels" layer
    // so that we don't overlap the raster layer over POI markers.
    let priority = MapLayerPriorityBuilder().renderedAfterLayer(named: "labels").build()

    // And it should be visible for all zoom levels. The minimum tilt level is 0 and maximum zoom level is 23.
    let range = MapLayerVisibilityRange(minimumZoomLevel: MapCameraLimits.minTilt, maximumZoomLevel: MapCameraLimits.maxZoomLevel)

    let mapLayer: MapLayer

    do {
        // Build and add the layer to the map.
        try mapLayer = MapLayerBuilder()
            .forMap(mapView.hereMap) // mandatory parameter
            .withName(dataSourceName + "Layer") // mandatory parameter
            .withDataSource(named: dataSourceName,
                            contentType: MapContentType.rasterImage)
            .withPriority(priority)
            .withVisibilityRange(range)
            .build()
        return mapLayer
    } catch let InstantiationException {
        fatalError("MapLayer creation failed Cause: \(InstantiationException)")
    }
}

上の例では「ラベル」レイヤーを参照しています。既存のレイヤーとその名前の詳細については、「API リファレンス」で MapLayerPriorityBuilder を参照してください。

最後に、可視性はレイヤーを有効または無効にすることで制御できます。一意の名前も指定する必要があります。各 RasterDataSource は 1 回のみ作成できます。

let dataSourceName = "myRasterDataSourceOutdoorStyle"
rasterDataSourceOutdoorStyle = createRasterDataSource(dataSourceName: dataSourceName)
rasterMapLayerOutdoorStyle = createMapLayer(dataSourceName: dataSourceName)

rasterMapLayerOutdoorStyle.setEnabled(true)

結果のレイヤーは次のようになります。

Screenshot: An outdoor map layer from Thunderforest.com shown on top of a map view.

上のスクリーンショットは、カスタム ラスター タイルを他の HERE SDK 機能と簡単に組み合わせることができることを示しています。たとえば、タイル サーバーのタイル データの上に複数の MapMarker インスタンスをレンダリングできます。

カスタム ラスター レイヤーの主な利点の 1 つは、透明なカスタム タイル ソースを上に配置して HERE マップ スタイルを簡単に拡張できることです。たとえば、天気予報データやその他の必要なデータを地図の上に表示できます。不透明なラスター タイル ソースを使用する場合は、空のベース マップ スタイルと組み合わせることをお勧めします。

空のベース マップ スタイルを使用しない場合、ラスター レイヤー タイルが読み込まれるまで、下層のマップ スキームが「透けて見える」状態となります。付属のサンプル アプリには、これがどのように見えるかが示されています。

他にも調整できるパラメーターがいくつかあります。

  • たとえば、アプリで複数のラスター レイヤーを使用する場合、MapLayerBuilder でレイヤーを作成するときに読み込みの優先順位を定義できます。これには整数値を指定できます。値が大きいレイヤーは、値が小さいレイヤーより前に読み込まれます。これはレイヤーが読み込まれる順番のみを制御します。
  • レイヤーのレンダリング方法を制御するのは MapLayerPriority です。たとえば、任意で renderedLast() を追加できます。これによりレイヤーは他の全レイヤーの上にレンダリングされます。
  • 透明なマップ スタイルを表示するには、rasterProviderConfig.hasAlphaChannel を true に設定します。
  • その他のパラメーターについては、「APIリファレンス」を参照してください。

地図をパンしている間の読み込み時間を短縮するもう1つのオプションは、機能設定を調整することです (Navigateでのみ使用可能)。

カスタムレイヤーを追加してライン、ポリゴン、ポイントを保持する

地図の外観をカスタマイズするもう1つの方法は、Styleクラスを使用して、独自のライン、ポリゴンおよびポイントカスタムレイヤーを追加することです。これにより、実行時に外観をより柔軟に更新できるようになります。ほとんどのユースケースでは、MapMarkerMapPolylineMapPolygonを代わりに使用することを検討してください。「マップアイテム」セクションを参照してください。大量のカスタムデータを扱う場合は、パフォーマンスとスケーラビリティを確保するために、代わりにライン、ポリゴン、ポイントのタイルソースを使用することを検討してください。

カスタムラインレイヤーを追加する

カスタム測地線を地図の外観に追加するには、HERE MapSchemeスタイルの上に独自のカスタムデータレイヤーを追加します。 測地線は地球の表面にある任意の2つのポイントを結ぶ最短線です。各ポイントは測地座標によって表されます。

これはこの機能のベータリリースであるため、いくつかのバグや予期しない動作が発生する可能性があります。関連するAPIは、廃止のプロセスを経ずに、新しいリリースに変更される可能性があります。

カスタム ラスター レイヤーを追加するには、LineDataSource を作成する必要があります。LineDataSource は表示する測地線データのソースを表します。 各測地線はカスタム データ属性のセットと関連付けし、地図の上に表示する際にそれを使用して各線の外観をカスタマイズできます。

LineDataSource に加えて、MapLayerBuilder を使用して MapLayer を作成し、レンダリング可能なマップ オーバーレイを地図に追加できます。

  • MapLayerVisibilityRangeを使用して、マップレイヤーのズームレベルを指定します。
  • MapLayerPriorityを使用して、MapLayerの描画順序を指定します。
  • MapContentTypeを使用して、MapLayerによって表示されるデータ型を指定します。測地線の場合は、MapContentType.LINEを使用します。
  • Style API を使用して、実行時に幅や色などのプロパティを調整します。詳細については、カスタム レイヤーのスタイル ガイドを参照してください。

次のようにLineDataSourceを作成します。

let myCustomLinesDataSourceName = "MyCustomLines"

private func createLineDataSource(dataSourceName : String) -> LineDataSource{
    // Creates a new LineDataSource, without custom data.
    // To also add custom lines while creating the data source, `LineDataSourceBuilder.withPolyline` API can be used.
    return LineDataSourceBuilder(mapView.mapContext).withName(dataSourceName).build()
}

ライン データ ソースを作成したら、カスタム測地線を追加できます。

private func addLinesToDataSource(linesDataSource : LineDataSource) {
    // Optional: Prepare a list of custom attributes.
    let lineAttributes = DataAttributesBuilder().with(name: "id", value: "my first line").build()

    // Add the line to data source.
    linesDataSource.add(LineDataBuilder().withGeometry(GeoPolyline(vertices: [GeoCoordinates(latitude: point1GeoLatitude, longitude: point1GeoLongitude), ...,
                                                                              GeoCoordinates(latitude: pointNGeoLatitude, longitude: pointNGeoLongitude)]))
                                         .withAttributes(lineAttributes)
                                         .build())

    // Repeat above steps to add more custom lines to the data source.
    // Alternatively, use `LineDataSource.add` to add multiple custom lines with a single call.
    ...
}

ライン レイヤーのカスタム スタイルを準備します。

以降の手順では、スタイル文字列のlayer値はMapLayerに付けた名前と一致している必要があります。この例で使われている dataSourceNameMyCustomLines で、レイヤー名は MyCustomLinesLayer です。

let myCustomLayerStyle = """
{
  "styles": [
    {
        "layer": "MyCustomLinesLayer",
        "technique": "line",
        "attr": {
            "width": 5.0,
            "color": "#ff0000ff"
        }
    }
  ]
}
"""

private func createCustomStyle() -> Style? {
    do {
        return try JsonStyleFactory.createFromString(myCustomLayerStyle)
    } catch let InstantiationException {
        // Custom exception handling
        ...
    }

    return nil
}

地図上に LineDataSource を表示するには、MapLayer を次のように構築します。

private func createMapLayer(dataSourceName: String, layerStyle: Style) -> MapLayer {
    // Set the layer to be rendered on top of other layers.
    let priority = MapLayerPriorityBuilder().renderedLast().build()
    // And it should be visible for all zoom levels. The minimum tilt level is 0 and maximum zoom level is 23.
    let range = MapLayerVisibilityRange(minimumZoomLevel: MapCameraLimits.minTilt, maximumZoomLevel: MapCameraLimits.maxZoomLevel)

    do {
        // Build and add the layer to the map.
        try mapLayer = MapLayerBuilder()
            .forMap(mapView.hereMap) // mandatory parameter
            .withName(dataSourceName + "Layer") // mandatory parameter
            .withDataSource(named: dataSourceName,
                            contentType: MapContentType.line)
            .withPriority(priority)
            .withVisibilityRange(range)
            .withStyle(layerStyle)
            .build()
        return mapLayer
    } catch let InstantiationException {
        fatalError("MapLayer creation failed Cause: \(InstantiationException)")
    }
}

カスタムポリゴンレイヤーを追加する

カスタム測地ポリゴンを地図の外観に追加するには、HERE MapSchemeスタイルの上に独自のカスタムデータレイヤーを追加します。 測地ポリゴンは外部測地境界線と、1つ以上の内部境界線 (任意) によって定義されます。各境界線は測地ポイントを結んだループで構成され、最初のポイントは最後のポイントと同じとなります。

これはこの機能のベータリリースであるため、いくつかのバグや予期しない動作が発生する可能性があります。関連するAPIは、廃止のプロセスを経ずに、新しいリリースに変更される可能性があります。

カスタム ポリゴン レイヤーを追加するには、PolygonDataSource を作成する必要があります。PolygonDataSource は表示する測地ポリゴン データのソースを表します。 各測地ポリゴンはカスタムデータ属性のセットと関連付けし、地図の上に表示する際にそれを使用して各ポリゴンの外観をカスタマイズできます。

PolygonDataSource に加えて、MapLayerBuilder を使用して MapLayer を作成し、レンダリング可能なマップ オーバーレイを地図に追加できます。

  • MapLayerVisibilityRangeを使用して、マップレイヤーのズームレベルを指定します。
  • MapLayerPriorityを使用して、MapLayerの描画順序を指定します。
  • MapContentTypeを使用して、MapLayerによって表示されるデータ型を指定します。測地ポリゴンの場合は、MapContentType.POLYGON を使用します。
  • Style API を使用して、実行時に色などのプロパティを調整します。詳細については、カスタム レイヤーのスタイル ガイドを参照してください。

次のようにPolygonDataSourceを作成します。

let myCustomPolygonsDataSourceName = "MyCustomPolygons"

private func createPolygonDataSource(dataSourceName : String) -> PolygonDataSource{
    // Creates a new PolygonDataSource, without custom data.
    // To also add custom polygons while creating the data source, `PolygonDataSourceBuilder.withPolygon` API can be used.
    return PolygonDataSourceBuilder(mapView.mapContext).withName(dataSourceName).build()
}

ポリゴン データ ソースを作成したら、カスタム測地ポリゴンを追加できます。

private func addPolygonsToDataSource(polygonsDataSource : PolygonDataSource) {
    // Optional: Prepare a list of custom attributes.
    let polygonAttributes = DataAttributesBuilder().with(name: "id", value: "my first polygon").build()

    // Add the polygon to data source.
    // Note: The point list must be closed and given in clockwise order.
    polygonsDataSource.add(PolygonDataBuilder().withGeometry(GeoPolygon(vertices: [GeoCoordinates(latitude: point1GeoLatitude, longitude: point1GeoLongitude),
        ...,
        GeoCoordinates(latitude: pointNGeoLatitude, longitude: pointNGeoLongitude),
        ...,
        GeoCoordinates(latitude: point1GeoLatitude, longitude: point1GeoLongitude)])))
        .withAttributes(polygonAttributes)
        .build())

    // Repeat above steps to add more custom polygons to the data source.
    // Alternatively, use `PolygonDataSource.add` to add multiple custom polygons with a single call.
    ...
}

ポリゴン レイヤーのカスタム スタイルを準備します。

以降の手順では、スタイル文字列のlayer値はMapLayerに付けた名前と一致している必要があります。この例で使われている dataSourceNameMyCustomPolygons で、レイヤー名は MyCustomPolygonsLayer です。

let myCustomLayerStyle = """
{
  "styles": [
    {
        "layer": "MyCustomPolygonsLayer",
        "technique": "polygon",
        "attr": {
            "color": "#ff0000ff"
        }
    }
  ]
}
"""

private func createCustomStyle() -> Style? {
    do {
        return try JsonStyleFactory.JsonStyleFactory.createFromString(myCustomLayerStyle)
    } catch let InstantiationException {
        // Custom exception handling
        ...
    }

    return nil
}

地図上に PolygonDataSource を表示するには、MapLayer を次のように構築します。

private func createMapLayer(dataSourceName: String, layerStyle: Style) -> MapLayer {
    // Set the layer to be rendered on top of other layers.
    let priority = MapLayerPriorityBuilder().renderedLast().build()
    // And it should be visible for all zoom levels. The minimum tilt level is 0 and maximum zoom level is 23.
    let range = MapLayerVisibilityRange(minimumZoomLevel: MapCameraLimits.minTilt, maximumZoomLevel: MapCameraLimits.maxZoomLevel)

    do {
        // Build and add the layer to the map.
        try mapLayer = MapLayerBuilder()
            .forMap(mapView.hereMap) // mandatory parameter
            .withName(dataSourceName + "Layer") // mandatory parameter
            .withDataSource(named: dataSourceName,
                            contentType: MapContentType.polygon)
            .withPriority(priority)
            .withVisibilityRange(range)
            .withStyle(layerStyle)
            .build()
        return mapLayer
    } catch let InstantiationException {
        fatalError("MapLayer creation failed Cause: \(InstantiationException)")
    }
}

カスタムポイントレイヤーを追加する

カスタム測地ポイントを地図の外観に追加するには、HERE MapSchemeスタイルの上に独自のカスタムデータレイヤーを追加します。

これはこの機能のベータリリースであるため、いくつかのバグや予期しない動作が発生する可能性があります。関連するAPIは、廃止のプロセスを経ずに、新しいリリースに変更される可能性があります。

カスタム ラスター レイヤーを追加するには、PointDataSource を作成する必要があります。PointDataSource は表示する測地ポイント データのソースを表します。 各測地ポイントはカスタム データ属性のセットと関連付けし、地図の上に表示する際にそれを使用して各ポイントの外観をカスタマイズできます。

PointDataSource に加えて、MapLayerBuilder を使用して MapLayer を作成し、レンダリング可能なマップ オーバーレイを地図に追加できます。

  • MapLayerVisibilityRangeを使用して、マップレイヤーのズームレベルを指定します。
  • MapLayerPriorityを使用して、MapLayerの描画順序を指定します。
  • MapContentTypeを使用して、MapLayerによって表示されるデータ型を指定します。測地ポイントの場合は、MapContentType.pointを使用します。
  • Style APIを使用して、実行時にアイコンなどのプロパティを調整します。詳細については、カスタム レイヤーのスタイル ガイドを参照してください。

次のようにPointDataSourceを作成します。

let myCustomPointsDataSourceName = "MyCustomPoints"

private func createPointDataSource(dataSourceName : String) -> PointDataSource{
    // Creates a new PointDataSource, without custom data.
    // To also add custom points while creating the data source, `PointDataSourceBuilder.withPoint` API can be used.
    return PointDataSourceBuilder(mapView.mapContext).withName(dataSourceName).build()
}

ポイント データ ソースを作成したら、カスタム測地ポイントを追加できます。

private func addPointsToDataSource(pointsDataSource : PointDataSource) {
    // Optional: Prepare a list of custom attributes.
    let pointAttributes = DataAttributesBuilder().with(name: "id", value: "my first point").build()

    // Add the point to data source.
    // Note: The point list must be closed and given in clockwise order.
    pointsDataSource.add(PointDataBuilder().withCoordinates(GeoCoordinates(latitude: pointGeoLatitude, longitude: pointGeoLongitude))
                                           .withAttributes(pointAttributes)
                                           .build())

    // Repeat above steps to add more custom points to the data source.
    // Alternatively, use `PointDataSource.add` to add multiple custom points with a single call.
    ...
}

ポイント レイヤーのカスタム スタイルを準備します。

以降の手順では、スタイル文字列のlayer値はMapLayerに付けた名前と一致している必要があります。この例で使われている dataSourceNameMyCustomPoints で、レイヤー名は MyCustomPointsLayer です。

let myCustomLayerStyle = """
{
  "styles": [
    {
        "layer": "MyCustomPointsLayer",
        "technique": "icon-text",
        "attr": {
            "icon-source": "my_icon.png"
        }
    }
  ]
}
"""

private func createCustomStyle() -> Style? {
    do {
        return try JsonStyleFactory.JsonStyleFactory.createFromString(myCustomLayerStyle)
    } catch let InstantiationException {
        // Custom exception handling
        ...
    }

    return nil
}

地図上に PointDataSource を表示するには、MapLayer を次のように構築します。

private func createMapLayer(dataSourceName: String, layerStyle: Style) -> MapLayer {
    // Set the layer to be rendered on top of other layers.
    let priority = MapLayerPriorityBuilder().renderedLast().build()
    // And it should be visible for all zoom levels. The minimum tilt level is 0 and maximum zoom level is 23.
    let range = MapLayerVisibilityRange(minimumZoomLevel: MapCameraLimits.minTilt, maximumZoomLevel: MapCameraLimits.maxZoomLevel)

    do {
        // Build and add the layer to the map.
        try mapLayer = MapLayerBuilder()
            .forMap(mapView.hereMap) // mandatory parameter
            .withName(dataSourceName + "Layer") // mandatory parameter
            .withDataSource(named: dataSourceName,
                            contentType: MapContentType.point)
            .withPriority(priority)
            .withVisibilityRange(range)
            .withStyle(layerStyle)
            .build()
        return mapLayer
    } catch let InstantiationException {
        fatalError("MapLayer creation failed Cause: \(InstantiationException)")
    }
}

カスタム レイヤーにカスタム データ属性を追加する

測地線などのカスタム オブジェクトは、属性のセットと関連付けできます。このような属性は一般的なキー/値のペアで、ディベロッパーが自由に定義できます。

たとえば、このようなユーザー定義の属性を使用して、各オブジェクトに一意の識別子をタグ付けできます。

let lineAttributes = DataAttributesBuilder().with(name: "id", value: "my first line").build()

また、スタイル式と合わせて使用して、地図の上に表示する際に各オブジェクトの外観をカスタマイズできます。

// Define a layer style string that would use per-line object width and color values
// that are stored inside line custom data attributes. For that purpose, the 'get' expression is used.
let myCustomLayerStyle = """
{
  "styles": [
    {
        "layer": "MyCustomLinesLayer",
        "technique": "line",
        "attr": {
            "width": ["get", "lineWidthInMeters"],
            "color": ["to-color", ["get", "lineColor"]]
        }
    }
  ]
}

// Define specific values for "lineWidth" and "lineColor" per line object.
let lineAttributes = DataAttributesBuilder().with(name: "lineWidthInMeters", value: 10.0)
                                            .with(name: "lineColor", value: "#ff0000ff").build();
...

カスタムタイルソースを追加する

ポイント、ライン、ポリゴンのタイルソースのサポートは高度な機能で、ユーザーが大量のカスタム地理空間データを統合して視覚化する必要があるシナリオ向けに設計されています。たとえば、世界中のEV充電ステーションや店舗の場所を視覚化する場合などです。MapMarkerMapPolylineなどのAPIは小規模なデータセットには十分ですが、大規模な使用ではメモリ消費量が多くなるため非効率になります。また、PointDataSourceなどのカスタムデータソースを使用する、ポイント、ライン、ポリゴンのカスタムレイヤーも大量のデータには適していません。代わりに、この章で説明するように、PointTileDataSourceLineTileDataSourcePolygonTileDataSourceの使用を検討してください。

データはタイル単位で読み込まれるため、データセット全体が一度にグローバルに読み込まれることはありません。

カスタムポイントタイルソースを追加する

カスタムポイントタイルソースを追加することにより、ポイントデータの追加レイヤーをデフォルトのHERE MapSchemeにレンダリングできるようになり、地図の外観をさらに改善できます。このアプローチにより、ポイントデータの形式がHERE SDKでネイティブにサポートされていない場合でも、ポイントデータのシームレスな統合が可能になります。

ポイントタイルデータソースは、施設情報 (POI)、リアルタイム車両追跡、イベントマーカーなどの場所の表示に使用できます。

これはこの機能のベータリリースであるため、いくつかのバグや予期しない動作が発生する可能性があります。関連するAPIは、廃止のプロセスを経ずに、新しいリリースに変更される可能性があります。

カスタムポイントタイルソースを追加するには、PointTileDataSourceを作成する必要があります。PointTileDataSourceによりポイントタイルソースの作成が容易になる他、PointTileSourceはリクエストされたTileKeyに基づき、カスタムポイントデータを提供するメカニズムを備えています。JSONベースのスタイルを使用してカスタムポイントタイルソースの外観を制御し、地図に統合します。

PointTileDataSourceに加えて、MapLayerBuilderを使用してMapLayerを作成し、レンダリング可能なマップオーバーレイを地図に追加できます。

  • MapLayerVisibilityRangeを使用して、マップレイヤーのズームレベルを指定します。
  • MapContentTypeを使用して、MapLayerによって表示されるデータ型を指定します。測地ポイントの場合は、MapContentType.POINTを使用します。
  • Style APIを使用して、実行時にアイコンなどのプロパティを調整します。

次のようにPointTileDataSourceを作成します。

private let mapView: MapView
private var pointMapLayer: MapLayer!
private var pointDataSource: PointTileDataSource!

init(_ mapView: MapView) {
    self.mapView = mapView

    let camera = mapView.camera
    let distanceInMeters = MapMeasure(kind: MapMeasure.Kind.distanceInMeters, value: 60 * 1000)
    camera.lookAt(point: GeoCoordinates(latitude: 52.518043, longitude: 13.405991),
                    zoom: distanceInMeters)

    let dataSourceName = "MyPointDataSource"
    pointDataSource = createPointTileDataSource(dataSourceName: dataSourceName)
    pointMapLayer = createMapLayer(dataSourceName: dataSourceName)

    // Load the map scene using a map scheme to render the map with.
    mapView.mapScene.loadScene(mapScheme: MapScheme.normalDay, completion: onLoadScene)
}

次のように、カスタムポイントデータソースを作成し、タイルを読み込みます。

private func createPointTileDataSource(dataSourceName: String) -> PointTileDataSource {
    // Create a PointTileDataSource using a local point tile source.
    // Note that this will make the point source already known to the passed map view.
    return PointTileDataSource.create(context: mapView.mapContext,
                                        name: dataSourceName,
                                        tileSource: LocalPointTileSource())
}

func loadTile(tileKey: TileKey, completionHandler: any PointTileSourceLoadResultHandler) -> (any TileSourceLoadTileRequestHandle)? {
    // For each tile, provide the tile geodetic center as a custom point, with a single
    // named attribute "pointText" containing the tile key representation as a string.
    let pointAttributes = DataAttributesBuilder()
        .with(name: "pointText", value: String(format: "Tile: (%d, %d, %d)", tileKey.x, tileKey.y, tileKey.level))
        .build()
    let tileData = PointDataBuilder().withCoordinates(getTileCenter(tileKey: tileKey))
                                        .withAttributes(pointAttributes)
                                        .build();

    completionHandler.loaded(tileKey: tileKey, data: [tileData],
                                metadata: TileSourceTileMetadata(dataVersion: dataVersion,
                                                                dataExpiryTimestamp: Date(timeIntervalSince1970: TimeInterval(0))))

    // No request handle is returned here since there is no asynchronous loading happening.
    return nil
}

次のように、ポイントタイルソースのカスタムスタイルを準備します。

以降の手順では、スタイル文字列のlayer値はMapLayerに付けた名前と一致している必要があります。この例で使われている dataSourceNameMyPointTileDataSource で、レイヤー名は MyPointTileDataSourceLayer です。

private let pointLayerStyle = """
{
    "styles": [
        {
            "layer": "MyPointDataSourceLayer",
            "technique": "icon-text",
            "attr": {
                "text-color": "#ff0000ff",
                "text-size": 30,
                "text": ["get", "pointText"]
            }
        }
    ]
}
"""

地図上に pointTileSource を表示するには、MapLayer を次のように構築します。

// Creates a MapLayer for displaying custom point tiles.
private func createMapLayer(dataSourceName: String) -> MapLayer {
    // The layer should be visible for all zoom levels. The minimum tilt level is 0 and maximum zoom level is 23.
    let range = MapLayerVisibilityRange(minimumZoomLevel: MapCameraLimits.minTilt, maximumZoomLevel: MapCameraLimits.maxZoomLevel)

    let mapLayer: MapLayer

    do {
        // Build and add the layer to the map.
        try mapLayer = MapLayerBuilder()
            .forMap(mapView.hereMap) // mandatory parameter
            .withName(dataSourceName + "Layer") // mandatory parameter
            .withDataSource(named: dataSourceName,
                            contentType: MapContentType.point)
            .withVisibilityRange(range)
            .withStyle(JsonStyleFactory.createFromString(pointLayerStyle)) // Creates a custom style for the point layer from the predefined JSON style string.
            .build()
        return mapLayer
    } catch let InstantiationException {
        fatalError("MapLayer creation failed Cause: \(InstantiationException)")
    }
}

最終的にポイントタイルソースは次のように表示されます。

Screenshot: Showing point tile source.

カスタムラインタイルソースを追加する

カスタムラインタイルソースを追加することにより、ラインデータの追加レイヤーをデフォルトのHERE MapSchemeの上に重ねてレンダリングできるようになり、地図の外観をさらに改善できます。このアプローチにより、ラインデータの形式がHERE SDKでネイティブにサポートされていない場合でも、ラインデータのシームレスな統合が可能になります。

ラインタイルデータソースは、未舗装道路、境界線などのレンダリングに最適です。

これはこの機能のベータリリースであるため、いくつかのバグや予期しない動作が発生する可能性があります。関連するAPIは、廃止のプロセスを経ずに、新しいリリースに変更される可能性があります。

カスタムラインタイルソースを追加するには、LineTileDataSourceを作成する必要があります。LineTileDataSourceによりラインタイルソースの作成が容易になる他、LineTileSourceはリクエストされたTileKeyに基づき、カスタムラインデータを提供するメカニズムを備えています。JSONベースのスタイルを使用してカスタムラインタイルソースの外観を制御し、地図に統合します。

LineTileDataSourceに加えて、MapLayerBuilderを使用してMapLayerを作成し、レンダリング可能なマップオーバーレイを地図に追加できます。

  • MapLayerVisibilityRangeを使用して、マップレイヤーのズームレベルを指定します。
  • MapContentTypeを使用して、MapLayerによって表示されるデータ型を指定します。測地線の場合は、MapContentType.LINEを使用します。
  • Style APIを使用して、実行時にアイコンなどのプロパティを調整します。

次のようにLineTileDataSourceを作成します。

private let mapView: MapView
private var lineMapLayer: MapLayer!
private var lineDataSource: LineTileDataSource!

init(_ mapView: MapView) {
    self.mapView = mapView

    let camera = mapView.camera
    let distanceInMeters = MapMeasure(kind: MapMeasure.Kind.distanceInMeters, value: 60 * 1000)
    camera.lookAt(point: GeoCoordinates(latitude: 52.518043, longitude: 13.405991),
                    zoom: distanceInMeters)

    let dataSourceName = "MyLineTileDataSource"
    lineDataSource = createLineTileDataSource(dataSourceName: dataSourceName)
    lineMapLayer = createMapLayer(dataSourceName: dataSourceName)

    if let lineMapLayer = lineMapLayer {
        lineMapLayer.setEnabled(false)
    }

    // Load the map scene using a map scheme to render the map with.
    mapView.mapScene.loadScene(mapScheme: MapScheme.normalDay, completion: onLoadScene)
}

次のように、カスタムラインデータソースを作成し、タイルを読み込みます。

private func createLineTileDataSource(dataSourceName: String) -> LineTileDataSource {
    // Create a LineTileDataSource using a local line tile source.
    return LineTileDataSource.create(context: mapView.mapContext,
                                        name: dataSourceName,
                                        tileSource: LocalLineTileSource())
}

func loadTile(tileKey: TileKey, completionHandler: any LineTileSourceLoadResultHandler) -> (any TileSourceLoadTileRequestHandle)? {
    do {
        let lineCoordinates = getTileBounds(tileKey: tileKey)
        let lineGeometry = try GeoPolyline(vertices: lineCoordinates)

        let tileData = LineDataBuilder()
            .withGeometry(lineGeometry)
            .withAttributes(DataAttributesBuilder()
                .with(name: "color", value: "#FF0000") // Example attribute for styling
                .build())
            .build()

        completionHandler.loaded(tileKey: tileKey, data: [tileData],
                                    metadata: TileSourceTileMetadata(dataVersion: dataVersion,
                                                                    dataExpiryTimestamp: Date(timeIntervalSince1970: TimeInterval(0))))
    } catch {
        print("Error creating GeoPolyline: \(error)")
        completionHandler.failed(tileKey)
        return nil
    }

    return nil
}

次のように、ラインタイルソースのカスタムスタイルを準備します。

以降の手順では、スタイル文字列のlayer値はMapLayerに付けた名前と一致している必要があります。この例で使われている dataSourceNameMyLineTileDataSource で、レイヤー名は MyLineTileDataSourceLayer です。

private let lineLayerStyle = """
{
    "styles": [
        {
            "layer": "MyLineTileDataSourceLayer",
            "technique": "line",
            "attr": {
                "color": "#FF0000",
                "width": ["world-scale", 5]
            }
        }
    ]
}
"""

地図上に lineTileSource を表示するには、MapLayer を次のように構築します。

// Creates a MapLayer for displaying custom line tiles.
private func createMapLayer(dataSourceName: String) -> MapLayer {
    // The layer should be visible for all zoom levels. The minimum tilt level is 0 and maximum zoom level is 23.
    let range = MapLayerVisibilityRange(minimumZoomLevel: MapCameraLimits.minTilt, maximumZoomLevel: MapCameraLimits.maxZoomLevel)

    let mapLayer: MapLayer

    do {
        try mapLayer = MapLayerBuilder()
            .forMap(mapView.hereMap)
            .withName(dataSourceName + "Layer")
            .withDataSource(named: dataSourceName,
                            contentType: MapContentType.line)
            .withVisibilityRange(range)
            .withStyle(JsonStyleFactory.createFromString(lineLayerStyle))
            .build()
        return mapLayer
    } catch let InstantiationException {
        fatalError("MapLayer creation failed Cause: \(InstantiationException)")
    }
}

最終的にポイントタイルソースは次のように表示されます。

Screenshot: Showing point tile source.

カスタムラスタータイルソースを追加する

カスタムラスタータイルソースを追加することにより、ラスターデータの追加レイヤーをデフォルトのHERE MapSchemeの上に重ねてレンダリングできるようになり、地図の外観をさらに改善できます。このアプローチにより、ラスターデータの形式がHERE SDKでネイティブにサポートされていない場合でも、ラスターデータのシームレスな統合が可能になります。

ラスタータイルソースを使うと、衛星画像、天気図などの画像ベースのデータをオーバーレイできます。

これはこの機能のベータリリースであるため、いくつかのバグや予期しない動作が発生する可能性があります。関連するAPIは、廃止のプロセスを経ずに、新しいリリースに変更される可能性があります。

カスタムラスタータイルソースを追加するには、RasterDataSourceを作成する必要があります。RasterDataSourceによりラスターデータソースの作成が容易になる他、RasterTileSourceはリクエストされたTileKeyに基づき、カスタムラスターデータを提供するメカニズムを備えています。JSONベースのスタイルを使用してカスタムラスタータイルソースの外観を制御し、地図に統合します。

RasterDataSourceに加えて、MapLayerBuilderを使用してMapLayerを作成し、レンダリング可能なマップオーバーレイを地図に追加できます。

  • MapLayerVisibilityRangeを使用して、マップレイヤーのズームレベルを指定します。
  • MapContentTypeを使用して、MapLayerによって表示されるデータ型を指定します。測地ラスターの場合は、MapContentType.RASTER_IMAGEを使用します。
  • Style APIを使用して、実行時にアイコンなどのプロパティを調整します。

次のようにRasterDataSourceを作成します。

private var mapView: MapView
private var rasterMapLayerStyle: MapLayer!
private var rasterDataSourceStyle: RasterDataSource!

init(_ mapView: MapView) {
    self.mapView = mapView

    let camera = mapView.camera
    let distanceInMeters = MapMeasure(kind: MapMeasure.Kind.distanceInMeters, value: 60 * 1000)
    camera.lookAt(point: GeoCoordinates(latitude: 52.518043, longitude: 13.405991),
                    zoom: distanceInMeters)

    // Load the map scene using a map scheme to render the map with.
    mapView.mapScene.loadScene(mapScheme: MapScheme.normalDay, completion: onLoadScene)

    let dataSourceName = "myRasterDataSourceStyle"
    rasterDataSourceStyle = createRasterDataSource(dataSourceName: dataSourceName)
    rasterMapLayerStyle = createMapLayer(dataSourceName: dataSourceName)

    // We want to start with the default map style.
    rasterMapLayerStyle.setEnabled(false)

    // Add a POI marker
    addPOIMapMarker(geoCoordinates: GeoCoordinates(latitude: 52.530932, longitude: 13.384915))
}

次のように、カスタムラスターデータソースを作成します。

private func createRasterDataSource(dataSourceName: String) -> RasterDataSource {
    // Create a RasterDataSource over a local raster tile source.
    // Note that this will make the raster source already known to the passed map view.
    return RasterDataSource(context: mapView.mapContext,
                            name: dataSourceName,
                            tileSource: LocalRasterTileSource())
}

@Override
func loadTile(tileKey: TileKey, completionHandler: any RasterTileSourceLoadResultHandler) -> (any TileSourceLoadTileRequestHandle)? {
    // Pick one of the local tile images, based on the tile key x component.

    completionHandler.loaded(tileKey: tileKey, data: tileData[Int(tileKey.x % Int32(tileData.count))],
                                metadata: TileSourceTileMetadata(dataVersion: dataVersion, dataExpiryTimestamp: Date(timeIntervalSince1970: TimeInterval(0))))

    // No request handle is returned here since there is no asynchronous loading happening.
    return nil
}

地図上に rasterTileSource を表示するには、MapLayer を次のように構築します。

private func createMapLayer(dataSourceName: String) -> MapLayer {
    // The layer should be rendered on top of other layers except the "labels" layer
    // so that we don't overlap the raster layer over POI markers.
    let priority = MapLayerPriorityBuilder().renderedBeforeLayer(named: "labels").build()

    // And it should be visible for all zoom levels. The minimum tilt level is 0 and maximum zoom level is 23.
    let range = MapLayerVisibilityRange(minimumZoomLevel: MapCameraLimits.minTilt, maximumZoomLevel: MapCameraLimits.maxZoomLevel)

    let mapLayer: MapLayer

    do {
        // Build and add the layer to the map.
        try mapLayer = MapLayerBuilder()
            .forMap(mapView.hereMap) // mandatory parameter
            .withName(dataSourceName + "Layer") // mandatory parameter
            .withDataSource(named: dataSourceName,
                            contentType: MapContentType.rasterImage)
            .withPriority(priority)
            .withVisibilityRange(range)
            .build()
        return mapLayer
    } catch let InstantiationException {
        fatalError("MapLayer creation failed Cause: \(InstantiationException)")
    }
}

最終的にラスタータイルソースは次のように表示されます。

Screenshot: Showing raster tile source.

注 (Navigateにのみ関連)

カスタム道路ジオメトリーまたはPOI (施設情報) を作成するには、HEREのサポートと、HEREとの追加契約が必要です。詳細については、HERE担当者にお問い合わせください。「カスタムマップカタログ」も参照してください。

サンプルアプリを試す

上のコードスニペットの多くは「CustomRasterLayers」サンプルアプリから取得できます。このサンプルアプリは、GitHubでお好みのプラットフォームのものを見つけることができます。

Related Topics


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); })();