Placesの検索

世界中の数億の 施設情報(POI )とポイントアドレスを集めた HERE のグローバルデータセットをソースとする HERE SDK for iOS を使用すると、すばやく簡単に検索できます。 HERE SDK を使用すると、検索に関連するさまざまなタスクを 1 つ のSearchEngine内で解決できます。

  • placesの検索: この巨大なデータベースから、カテゴリ別、または検索語を設定して、世界中のPlacesを検索して発見できます。
  • Autosuggestを生成: 検索条件を入力して検索を完了するときに、Placesを検索します。
  • 住所をリバースジオコード: 特定の地理座標に属する住所を検索します。
  • 住所でのジオコード : 住所に属する地理座標を検索します。
  • ID で検索: HERE Place IDで特定されたPlacesを検索します。
  • ルートに沿って検索: ルート全体に沿ったPlacesを検索します。
  • ルートに沿ってカテゴリで検索: ルート全体のカテゴリに基づいてPlacesを検索します。 この機能はベータ版です。
  • 個人データの持ち込み( BYOD ): 独自の Places データを追加して、実行時に無条件の検索リクエストにアクセスできるようにします。
  • オフラインで検索: インターネットに接続できない場合は、OfflineSearchEngineに切り替えて、キャッシュ済みのマップ データまたは事前にロードされたオフライン地図データを検索できます。

すべての検索バリアントに共通するフィーチャーの1つは、検索する場所または領域を指定できることです。 領域の設定は、GeoBoxで指定された矩形領域 、またはGeoCircleで指定された円領域を通過することで行う ことができます。 指定した領域外にある潜在的な検索結果は、関連するグローバルな結果を除いて、優先度の低いものでランク付けされます。たとえば、ベルリンで「 Manhattan 」を検索する場合です。 基本となる検索アルゴリズムが最適化され、結果のリストを絞り込むことができるようになり、ユーザーに迅速でわかりやすい結果が提供されます。

注 : 各検索リクエストは非同期で実行されます。 オンライン接続が必要です。

HERE の位置情報サービスが提供する場所の大規模なデータベースは、 HERE SDK のSearchEngineで簡単に見つけることができます。 例を見てみましょう。 まず、新しい SearchEngine インスタンスを作成します。

do {
    try searchEngine = SearchEngine()
} catch let engineInstantiationError {
    fatalError("Failed to initialize SearchEngine. Cause: \(engineInstantiationError)")
}

新しい SearchEngine インスタンスを作成すると、上の図のように処理する必要があるエラーがスローされることがあります。 このようなエラーは、たとえば、 HERE SDK の初期化が事前に失敗した場合に発生することがあります。

Placesの検索

デバイスに表示されている現在のマップのセンター周りにあるすべての「ピザ」の場所を探しているとします。 検索を開始する前に、詳細をいくつか指定する必要があります。

let searchOptions = SearchOptions(languageCode: LanguageCode.enUs,
                                  maxItems: 30)

上記のコード スニペットから、目的のデータを保持する新しいオブジェクト SearchOptions を作成しました。

  • LanguageCodeを設定することで、返される検索結果の言語を指定できます。
  • maxItems は、応答で配信する結果アイテムの最大数を定義するように設定されています。 上の例では、結果を 30 に制限しています。 リクエストよりも多くの検索結果が見つかった場合、最も関連性の高い 30 件の検索結果のみが返されます。

現在のビューポイント内のすべての結果を検索するために、 1 つのボックスで検索を行います。 SearchEngine では、次の 3 つの方法で検索場所を指定できます。

  • GeoCoordinates検索場所:指定した座標系を中心に非同期の検索リクエストを実行し、近隣で最も関連性の高い検索結果を提供します。
  • GeoCircle エリア内を検索 : 上記と似ていますが、指定した円領域内で結果を検索します。この領域は、中心地理座標およびメートル単位の半径で定義されます。
  • GeoBox エリア内を検索 : 上記と同様ですが、指定した矩形領域内で結果を検索します。この領域は、パラメーターとして渡された南西および北東の座標によって定義されます。

One-Box検索 は、近隣のPlacesを探すのに最適です。 入力すると、さまざまな言語 ( ラテン語、キリル語、アラビア語、ギリシャ語など ) で自由形式のテキストを提供できます。

検索する語句と一緒に領域を指定できます。 たとえば queryString 、「 pizza 」のように設定できます。

let queryArea = TextQuery.Area(inBox: getMapViewGeoBox())
let textQuery = TextQuery(queryString, area: queryArea)

ここでは、 getMapViewGeoBox()のコードを削除しました。 ユースケースに適した任意のGeoBoxを作成して渡すことができます。 実装の可能性については、付属のサンプルアプリを参照してください。

できれば、指定したマップ範囲内の結果が返されます。 検索結果が見つからなかった場合は、グローバル検索結果が返されることがあります。 ただし、指定した検索場所に関係なく、主要都市や州などの関連するグローバルな結果が含まれる場合があります。

注 : クエリー文字列には、検索するコンテンツの任意のテキスト記述を含めることができます。 複数の検索語を使用して、コンマで区切って検索結果を絞り込むことができます。 「ピザショーシー通り」と「ピザ、ショーシー通り」は同じ結果をもたらします。通り「チャウゼー通り」にあるピザレストランのみが見つかります。 また、空のクエリ文字列を渡すとエラーになります。この場合、検索は失敗します。

最後に、非同期で検索を開始できます。

_ = searchEngine.search(textQuery: textQuery,
                        options: searchOptions,
                        completion: onSearchCompleted)

...

// Completion handler to receive search results.
func onSearchCompleted(error: SearchError?, items: [Place]?) {
    if let searchError = error {
        showDialog(title: "Search", message: "Error: \(searchError)")
        return
    }

    // If error is nil, it is guaranteed that the items will not be nil.
    showDialog(title: "Search in viewport for: 'Pizza'.",
               message: "Found  \(items!.count) results.")

    // Add a new marker for each search result on map.
    for searchResult in items! {
        //...
    }
}

また、完了ハンドラのインライン化には、終了条件式を使用できます。

_ = searchEngine.search(textQuery: textQuery,
                        options: searchOptions) { (searchError, searchResultItems) in
   // Handle results here.
}

もちろん、 HERE SDK で利用できる他のすべての完了処理についても同じことが可能です。 このガイドでは、関数 呼び出し式 を使用して、すべての種類の情報を保持することをお勧めします。

提供されているすべての search() メソッドが TaskHandle を返します。このメソッドは、進行中のコールのステータスの確認、またはコールのキャンセルに任意で使用できます。 必要でない場合は、上に示したように、そのままにしておくことができます。

結果を確認する前に、可能なSearchErrorを確認する必要があります。 たとえば、デバイスがオフラインの場合、検索項目のリストは nil になり、エラー 列挙型 (enum) が原因を示します。 この場合、ヘルパーメソッドshowDialog() を呼び出して、エラーの説明をユーザーに表示します。 showDialog() の実装の可能性は、付随する「検索」の例のソースコードからアクセスできます。このコードには、 HERE SDK 固有のコードは含まれていません。

エラーが発生した場合は事前に削除しておくため、検索アイテムリストの折り返しを安全に解除できます。

検索のレスポンスにはエラーまたは結果が含まれます。erroritems が同時に nil になることも、非 nil になることもありません。

ここで、結果を確認しましょう。 一致する結果が見つからない場合は、事前にエラーが検出されている可能性があります。

// If error is nil, it is guaranteed that the items will not be nil.
showDialog(title: "Search in viewport for: 'Pizza'.",
           message: "Found  \(items!.count) results.")

// Add a new marker for each search result on map.
for searchResult in items! {
    let metadata = Metadata()
    metadata.setCustomValue(key: "key_search_result", value: SearchResultMetadata(searchResult))
    // Note that geoCoordinates are always set, but can be nil for suggestions only.
    addPoiMapMarker(geoCoordinates: searchResult.geoCoordinates!, metadata: metadata)
}

...

// Class to store search results as Metadata.
private class SearchResultMetadata : CustomMetadataValue {

    var searchResult: Place

    init(_ searchResult: Place) {
        self.searchResult = searchResult
    }

    func getTag() -> String {
        return "SearchResult Metadata"
    }
}

最後に、結果のリストについて反復処理を行います。 各Placeには、検索結果を説明するさまざまなフィールドが含まれています。

この例では、マップにマーカーを追加するために、そのPlacesの場所に関心があります。 さらに、Metadata を保存できるオブジェクト SearchResultを作成します。

注 : Metadata オブジェクトには、マップ マーカーと結果データを簡単に関連付けることができるように、さまざまなデータ型を含めることができます。 このようにして、マップ マーカーに関連するすべての情報を 1 つのオブジェクトに保持できます。これは、ユーザーがマップ マーカーをタップした後などに、このデータを表示する場合に便利です。 上記 CustomMetadataValue のように、インターフェイスを実装することで、複雑なデータオブジェクトも保存できます。

addPoiMapMarker()の実装方法としては、付随する「検索」のサンプルソースコードからアクセス できます。このガイドの「マップマーカーについて」も参照してください。 選択したマップ マーカーを手元に置いておくと、前のステップで設定した情報Metadata を取得できます。

if let searchResultMetadata =
    topmostMapMarker.metadata?.getCustomValue(key: "key_search_result") as? SearchResultMetadata {

    let title = searchResultMetadata.searchResult.title
    let vicinity = searchResultMetadata.searchResult.address.addressText
    showDialog(title: "Picked Search Result",
               message: "Title: \(title), Vicinity: \(vicinity)")
    return
}

すべてのマップマーカーにMetadataが含まれているわけでは ありません。 事前にMetadataを設定していない場合、getMetadata()は nil を返します。 この例では、保存されているデータ "key_search_result" が nil でないかどうかを確認するだけで、データに検索データが含まれている必要があることがわかります。 その後、 目的のPlaceを保持するカスタムタイプSearchResultMetadata にダウンキャストできます。

使用可能なオプションのフィールドの詳細については、 API リファレンス を参照してください。

スクリーンショット: 選択した検索結果をタイトルと近隣で表示。

このセクションおよび以降のセクションのすべてのコードは Search、 GitHubのサンプルアプリの一部として入手できます。

places にズーム

上記のコードでは、GeoBox を使用 して、表示されているマップビューポイント を直接検索します。そのため、検索結果をズームする必要はありません。 GeoBoxの代わり に、CountryCode値のリストを渡すことで、 GeoCircleの内側、 GeoCorridorに沿って、または内側の国を囲む位置を検索することもできます ( 「自分の周りを検索」 ) 。 サポートされているすべてのタイプについては、TextQuery.Area を参照

検索領域が表示されているマップビューポイント と異なる場合 は、次のコードを使用してGeoCoordinatesの一覧からGeoBoxを作成し、結果をズームできます。 GeoBox からGeoBox.containing(geoCoordinates: geoCoordinatesList)を取得します。

// Set null values to keep the default map orientation.
camera.lookAt(area: geoBox,
              orientation: GeoOrientationUpdate(bearing: nil, tilt: nil))

これでカメラが即座に移動します。 必要に応じて、さまざまなアニメーションスタイルを適用して、カメラを目的の領域にフライすることもできます。 このMapCameraセクションを参照 して、 GitHubCameraKeyframeTracks サンプルアプリ を確認します。

追加のパディングを適用する場合は、viewRectangleを追加のパラメーターとして受け取るオーバーロードされたメソッドlookAt()を使用します。 矩形は、GeoBoxが表示されている内部のマップ ビュー を参照するピクセル単位で指定されます。

let origin = Point2D(5, 5)
let sizeInPixels = Size2D(width: mapView.viewportSize.width - 10, height: mapView.viewportSize.height - 10)
let paddedViewRectangle = Rectangle2D(origin: origin, size: sizeInPixels)

上記のコードでは、 GeoBox マップビューポイント に表示されている任意の周囲に 5 ピクセルのパディングを追加するために使用できる矩形を作成します。

Placesのカテゴリの検索

上記のようにTextQueryをしてキーワード検索を行う代わりに、カテゴリを検索して結果を予想されるカテゴリPlaceに限定することもできます。

カテゴリ ID は特定の形式に従い、 HERE platform では 700 を超えるカテゴリを使用できます。 HERE SDK には、カテゴリ検索を簡単に使用できるように定義済みの値のセットが用意されています。 必要に応じて、 xxx-xxxx-xxxx の形式でカスタムカテゴリ文字列を渡すこともできます。各グループは、 1 、 2 、 3 の各レベルのカテゴリを表します。 1 番目のレベルがメインカテゴリを表し、 3 番目のレベルが 2 番目のレベルのサブカテゴリのサブカテゴリを表します。 各カテゴリレベルは 、placesカテゴリシステムで番号として定義されます。

例として、「飲食」カテゴリまたは「ショッピングエレクトロニクス」カテゴリに属するすべての場所を以下から検索します。

private func searchForCategories() {
    let categoryList = [PlaceCategory(id: PlaceCategory.eatAndDrink),
                        PlaceCategory(id: PlaceCategory.shoppingElectronics)]
    let queryArea = CategoryQuery.Area(areaCenter: GeoCoordinates(latitude: 52.520798,
                                                                  longitude: 13.409408))
    let categoryQuery = CategoryQuery(categoryList, area: queryArea)
    let searchOptions = SearchOptions(languageCode: LanguageCode.enUs,
                                      maxItems: 30)

    _ = searchEngine.search(categoryQuery: categoryQuery,
                            options: searchOptions,
                            completion: onSearchCompleted)
}

public func onSearchCompleted(error: SearchError?, items: [Place]?) {
    if let searchError = error {
        print("Search Error: \(searchError)")
        return
    }

    // If error is nil, it is guaranteed that the items will not be nil.
    showDialog(title: "Search Result", message: "\(items!.count) result(s) found. See log for details.")

    for place in items! {
        let addressText = place.address.addressText
        print(addressText)
    }
}

PlaceCategory は、Stringを受け入れます。 HERE では、事前定義されたカテゴリ eatAndDrink およびshoppingElectronicsを使用し ます。 この値String には、placesカテゴリシステムで表される ID が含まれています。 ここでも、 SearchEngine のオーバーロードされた search() メソッドを使用して、カテゴリリストと場所を探す地理的座標を含む CategoryQuery オブジェクトを渡します。

Auto Suggestionの検索

ほとんどの場合、Places検索を提供するアプリケーションでは、ユーザーが希望する検索用語を編集可能なテキストフィールドコンポーネントに入力可能です。 入力中に、可能性のある用語の予測を取得できると便利です。

エンジンによって提示された提案がランク付けされ、最も関連性の高い用語が結果リストの一番上に表示されます。 たとえば、最初のリストアイテムを使用して、ユーザーが現在入力している検索語のAutoSuggestを提供できます。 または - ユーザーの入力中に更新された一致候補のリストを表示できます。 ユーザーは、提案のリストから適切なキーワードを選択して、選択した用語の新しい検索を開始できます。または 、タイトル近隣などの結果の詳細情報をすでに取得し てユーザーに提示できます。

HERE SDKは 、UIや完全に統合されたAutosuggestソリューションを提供していません。 このようなソリューションは、必要に応じてアプリケーションによって実装できます。 このフィーチャーSuggestionを使用すると、TextQueryの条件に基づいて結果 Place が得られます。 これらのPlacesから、タイトルテキスト(「Pizza XL」)またはその他の関連する場所情報(住所など)を使用して、ユーザーにフィードバックを提供できます。たとえば、クリック可能な完了結果を提案することができます。 しかし、そのようなソリューションはアプリケーションの個々の要件に依存し、プラットフォームAPIを使用してアプリケーション側で実装する必要があります。

通常のテキストクエリと比較して、候補の検索では、入力したクエリの用語について、優先度でランク付けされた高速な結果が提供されます。

エンジンを使用して提案を検索する方法を確認しましょう。

let centerGeoCoordinates = getMapViewCenter()
let autosuggestOptions = SearchOptions(languageCode: LanguageCode.enUs,
                                       maxItems: 5)

let queryArea = TextQuery.Area(areaCenter: centerGeoCoordinates)

// Simulate a user typing a search term.
_ = searchEngine.suggest(textQuery: TextQuery("p", area: queryArea),
                         options: autosuggestOptions,
                         completion: onSearchCompleted)

_ = searchEngine.suggest(textQuery: TextQuery("pi", area: queryArea),
                         options: autosuggestOptions,
                         completion: onSearchCompleted)

_ = searchEngine.suggest(textQuery: TextQuery("piz", area: queryArea),
                         options: autosuggestOptions,
                         completion: onSearchCompleted)

ヘルパーメソッド getMapViewCenter() は ここから削除され、付属のサンプルアプリで確認できます。 マップ ビューの中央に現在表示されているGeoCoordinatesを返すだけです。

新しいテキスト入力ごとに、次のリクエストを作成します。 ユーザーが「 Pizza 」と入力することを計画していると仮定します。まず「 p 」の結果を探し、次に「 pi 」の結果を探し、最後に「 piz 」の結果を探します。 ユーザーが実際に「pizza」を検索する場合は、 3 度目の呼び出しで十分な興味深い提案があるはずです。

SearchEngine の他の search() メソッドと同様に、 suggest() メソッドは TaskHandle を返します。このメソッドを使用すると、進行中のコールのステータスを確認したり、コールをキャンセルしたりできます。

結果の取得方法を確認しましょう。

// Completion handler to receive auto suggestion results.
func onSearchCompleted(error: SearchError?, items: [Suggestion]?) {
    if let searchError = error {
        print("Autosuggest Error: \(searchError)")
        return
    }

    // If error is nil, it is guaranteed that the items will not be nil.
    print("Autosuggest: Found \(items!.count) result(s).")

    for autosuggestResult in items! {
        var addressText = "Not a place."
        if let place = autosuggestResult.place {
            addressText = place.address.addressText
        }
        print("Autosuggest result: \(autosuggestResult.title), addressText: \(addressText)")
    }
}

ここでは、Suggestionで見つかったリストアイテムをログに記録する完了ハンドラを定義します 。 エラーがない場合、エンジンは結果のリストを保証します。エラーがない場合、結果は nil になります。

すべての提案があるわけではありません。 たとえば、「シスコ」などの一般的な用語を使用して、新しい検索に入力できます。 一般的な用語では、 結果Suggestion にオブジェクト Place は含まれませんが、特定の場所を参照せずにテキストを表すのは title だけです。 Suggestion 結果の使用可能なすべてのフィールドについては、『 API リファレンス 』を参照してください。

結果の順序はランク付けされますが、完了イベントが到着する順序については保証がありません。 そのため、まれに、「 PI 」の結果よりも前に「 Piz 」の結果が表示されることがあります。

地理座標から住所をリバースジオコード

これで、マップ上の特定の場所または地域でPlacesを検索する方法を確認できました。 ただし、場所のみがわかっている場合は、どうすればよいのでしょうか。 このユースケースで最も一般的なのは、マップで何らかのアクションを実行しているユーザーです。 たとえば、長押しジェスチャを使用すると、ユーザーがマップを操作した場所の緯度座標と経度座標が表示されます。 ユーザーはマップ上の場所を参照できますが、その場所に属する住所情報などの他の属性は把握できません。

リバースジオ コーディング が役立つ場所です。

関心のある場所は GeoCoordinates インスタンスで表されます。たとえば、マップをタップしたユーザーから取得できます。 その場所を「ジオコード」する方法については、次のコードを参照してください。

private func getAddressForCoordinates(geoCoordinates: GeoCoordinates) {
    // By default results are localized in EN_US.
    let reverseGeocodingOptions = SearchOptions(languageCode: LanguageCode.enGb,
                                                maxItems: 1)
    _ = searchEngine.search(coordinates: geoCoordinates,
                            options: reverseGeocodingOptions,
                            completion: onReverseGeocodingCompleted)
}

// Completion handler to receive reverse geocoding results.
func onReverseGeocodingCompleted(error: SearchError?, items: [Place]?) {
    if let searchError = error {
        showDialog(title: "ReverseGeocodingError", message: "Error: \(searchError)")
        return
    }

    // If error is nil, it is guaranteed that the place list will not be empty.
    let addressText = items!.first!.address.addressText
    showDialog(title: "Reverse geocoded address:", message: addressText)
}

SearchEngine によって提供される他の検索機能と同様に、必要な LanguageCode を設定するための SearchOptions インスタンスを提供する必要があります。 結果の住所の言語を指定します。 その後、エンジンのメソッドsearch()を呼び出して、渡された座標のアドレスをオンラインで検索できます。 デバイスがオフラインのときなど、エラーが発生した場合は、 SearchError は、エラーの原因を保持します。

リバースジオ コーディングのレスポンスには、エラーまたは結果のSearchErrorが含まれています。また、アイテムリストは、同時に nil にすることも、非nil にすることもできません。

Place インスタンス内に含まれるオブジェクトAddress はデータクラスで、国、市区町村、番地などの生の場所の住所を記述する複数のフィールド String が含まれています。 詳細については、 API リファレンス を参照してください。 読み取り可能なアドレス表現の受信のみに関心がある場合 addressTextは、上の例に示すようにににアクセスできます。 これは、その場所のタイトルを含む、最も関連性の高い住所の詳細Stringを含みます。

スクリーンショット: 住所に解決された印刷機の長い座標を表示。

リバース ジオ コーディングでは、特定の検索領域は必要ありません。 世界中の住所に座標を解決できます。

住所を位置情報にジオコード

Reverse ジオ コーディング を使用すると、未加工の座標から住所を取得できますが 、 フィワードジオ コーディング では逆の処理が行われ、道路名や都市などの住所の詳細を入力するだけで、未加工の座標やその他の位置情報を検索できます。

注 : ほとんどの場合、リバースジオ コーディングでは 1 つの結果しか得られませんが、ジオ コーディングでは 1 つ以上の結果が得られます。

ここでその方法をご案内します。 まず、検索したい場所の近くの座標を指定する必要があります。queryStringとして、正確な場所を見つけたい住所を設定します。

let query = AddressQuery(queryString, near: geoCoordinates)
let geocodingOptions = SearchOptions(languageCode: LanguageCode.deDe,
                                     maxItems: 25)
_ = searchEngine.search(addressQuery: query,
                        options: geocodingOptions,
                        completion: onGeocodingCompleted)

この例では、 HERE の Berlin HQ "Invalidenstra ß e 116" の通りの名前をクエリー文字列として渡します ( 任意で都市を指定します ) 。 これはドイツ語の通り名な deDe ので、ドイツの言語コードを入力します。 これにより、返される結果の言語も決まります。

注 : 結果は指定した場所から遠く離れていてもかまいませんが、指定した座標に近い結果が高い順にランク付けされ、可能であれば返されます。

次のステップでは、完了ハンドラを実装する必要があります。

// Completion handler to receive geocoding results.
func onGeocodingCompleted(error: SearchError?, items: [Place]?) {
    if let searchError = error {
        showDialog(title: "Geocoding", message: "Error: \(searchError)")
        return
    }

    // If error is nil, it is guaranteed that the items will not be nil.
    for geocodingResult in items! {
        //...
    }
}

完了ハンドラがエラーを受信しなかったことを確認した後 、要素Placeの一覧表を確認します。

searchError が nil の場合 、items の結果は nil ではないことが保証され、その逆も同様です。 そのため、オプションの一覧表をラップ解除しても安全です。

結果は 、未加工の座標を含むPlacesオブジェクトにラップ されます。また、Addressオブジェクトや、 HERE Places API 内の位置を特定する場所 ID など、その他の住所の詳細情報もラップされます。 以下では、一覧表について繰り返し、住所のテキストと座標を取得します。

for geocodingResult in items! {
    // Note that geoCoordinates are always set, but can be nil for suggestions only.
    let geoCoordinates = geocodingResult.geoCoordinates!
    let address = geocodingResult.address
    let locationDetails = address.addressText
        + ". Coordinates: \(geoCoordinates.latitude)"
        + ", \(geoCoordinates.longitude)"
    //...
}

ユーザーがマップからこのような結果を選択した場合の表示例については、以下のスクリーンショットを参照してください。 ご希望の場合は、添付の「検索」のサンプルアプリを参照して、住所のテキストを検索し、マップ上の見つかった場所にマップマーカーを配置する方法を確認してください。

スクリーンショット: 選択したジオ コーディング結果を表示。

ルート沿いの検索

SearchEngineは、長方形または円の領域で検索せずに、GeoPolylineおよび他のパラメーターで定義できるより複雑なGeoCorridor領域で検索する場合に、特殊な検索ケースをサポートします。

このような場合の最も一般的なシナリオは、 Route を検索してレストランを探すことです。 ルートオブジェクトがすでに計算されているとします。 経路の計算方法については、経路のセクションを参照してください。 TextQueryを指定すると、ルート全体を含む長方形の領域を簡単に定義できます。

let textQuery = TextQuery("restaurants", in: route.boundingBox)

ただし、より長いルートの場合、およびルートの形状によっては、route.boundingBox がルート全体を長方形の領域に包含する必要があるため、結果が実際のルートパスから非常に離れている場合があります。

HERE SDK は GeoCorridor 、ルートの実際の形状から検索領域を判断できるクラスを提供することで、より正確なソリューションを提供します。 この方法 では、パスの上または下にある検索結果のみが含まれます。

以下に、ルートに沿ってチャージステーションを検索する方法の例を示します。

private func searchAlongARoute(route: Route) {
    // We specify here that we only want to include results
    // within a max distance of xx meters from any point of the route.
    let routeCorridor = GeoCorridor(polyline: route.geometry.vertices,
                                    halfWidthInMeters: Int32(200))
    let queryArea = TextQuery.Area(inCorridor: routeCorridor, near: mapView.camera.state.targetCoordinates)
    let textQuery = TextQuery("charging station", area: queryArea)

    let searchOptions = SearchOptions(languageCode: LanguageCode.enUs,
                                      maxItems: 30)
    searchEngine.search(textQuery: textQuery,
                        options: searchOptions,
                        completion: onSearchCompleted)
}

// Completion handler to receive results for found charging stations along the route.
func onSearchCompleted(error: SearchError?, items: [Place]?) {
    if let searchError = error {
        if searchError == .polylineTooLong {
            // Increasing halfWidthInMeters will result in less precise results with the benefit of a less
            // complex route shape.
            print("Route too long or halfWidthInMeters too small.")
        } else {
            print("No charging stations found along the route. Error: \(searchError)")
        }
        return
    }

    // If error is nil, it is guaranteed that the items will not be nil.
    print("Search along route found \(items!.count) charging stations:")

    for place in items! {
        // ...
    }
}

ご覧の ように、GeoCorridorはルートGeoPolylinehalfWidthInMeters を必要とします。 このパラメータでは、ポリライン上の任意のポイントから廊下の端までの最も遠いエッジを定義します。 小さな値を指定すると、実際のルートに沿って非常に近いエリアが結果の廊下によって定義されます。

スクリーンショット: ルートに沿って見つかった充電ステーションを表示

ルートの始点と終点の座標では、コリドーは丸い形になります。一定の厚さの曲線が始点と終点に丸いエッジを持つようなものを想像してください。 上記のスクリーンショットと混同しないでください。ルートの始点と終点を示す緑色の円が表示されます。

特に長いルートの場合、内部的に検索アルゴリズムが検索経路の最適化を試行します。 ただし、ポリラインが長すぎる可能性があります。 上記のコード スニペットに示されているように、このケースをキャッチして、最終的にはより複雑でないルートの検索を再トリガすることにします。 これは halfWidthInMeters パラメータで制御できます。値を大きくすると、廊下の複雑さが軽減され、結果の精度が低下しますが、少なくともこの方法ではより多くの結果が得られます。

ルートの複雑さは、フードの下の複数の要因によって決まります。そのため、ルートの明確な長さは一般に指定できません。

エラーが発生しなかった場合は、上記のセクションにすでに示されているように結果のPlace を処理できます。

このセクションのすべてのコードは 、EVRouting GitHub のサンプルアプリの一部として入手できます。

個人データの持ち込み

HERE SDK は、場所を検索するときに、実行時に BYOD (個人データの持ち込み)をサポートします。 OfflineSearchEngineで検索できる場所にカスタムデータを挿入できます。 このような個人的な場所は、定期的な問い合わせで見つけることができます。 結果は、 HERE から直接送られてきた他の場所としてランク付けされます。

GeoPlace カスタムの Place データを含めることができる複数のインスタンスを作成できます。 これらのインスタンスは、データソースMyPlacesに追加できます。

let geoPlaces = [
    GeoPlace.makeMyPlace(title: "Pizza Pino", coordinates: GeoCoordinates(latitude: 52.518032, longitude: 13.420632)),
    GeoPlace.makeMyPlace(title: "PVR mdh", coordinates: GeoCoordinates(latitude: 52.51772, longitude: 13.42038)),
    GeoPlace.makeMyPlace(title: "Harley's bar", coordinates: GeoCoordinates(latitude: 52.51764, longitude: 13.42062))
]

myPlaces.addPlaces(places: geoPlaces) { taskOutcome in
    if (taskOutcome == TaskOutcome.completed) {
      // Task completed.
    } else {
      // Task cancelled.
    }
}

このデータソースは、次の場所OfflineSearchEngineに添付できます。

offlineSearchEngine.attach(dataSource: myPlaces) { taskOutcome in
    if (taskOutcome == TaskOutcome.completed) {
      // Task completed.
    } else {
      // Task cancelled.
    }
}

addPlaces()attach() は、アプリケーションによって一度に数千の場所が追加される可能性があるため、非同期で操作できます。 両方のメソッドがTaskHandleを返し、それにより操作をキャンセルできます。 操作が完了すると、イベントTaskOutcomeが通知されます。

以下に、そのような場所を見つける方法の例を示します。

let queryArea = TextQuery.Area(inCircle: GeoCircle(center: GeoCoordinates(latitude: 52.518032, longitude: 13.420632),
                                                   radiusInMeters: 100.0))
let textQuery = TextQuery("Pizza Pino", area: queryArea)
offlineSearchEngine.search(textQuery: textQuery, options: SearchOptions()) { (searchError, placeList) in
    // ...
}

インスタンス offlineSearchEngine がアクティブである限り、追加された場所はメモリに残ります。

SearchEngineに加えて、オフラインの使用例にも相当するものがあります。 OfflineSearchEngine :すでに上記で示したオンライン版と同じ方法で構築できます。

do {
    // Allows to search on already downloaded or cached map data.
    try offlineSearchEngine = OfflineSearchEngine()
} catch let engineInstantiationError {
    fatalError("Failed to initialize OfflineSearchEngine. Cause: \(engineInstantiationError)")
}

OfflineSearchEngineSearchEngineとほぼ同じインターフェイスを提供しますが、結果は、 HERE バックエンドサービスへの新しい要求を開始するのではなく、すでにダウンロードまたはキャッシュされているマップ データから取得されたものであるため、若干異なる場合があります。

この方法 では、たとえば、SearchEngineを使用しているときに受信したデータよりも古いデータを使用できます。 一方、このクラスでは、オンライン接続が不要なため、より迅速に結果を提供できます。

すでにキャッシュ されている か、または事前にロードされているオフラインの地図データのみを検索できます。 キャッシュされたマップ データのみを使用する場合、すべてのタイルがロードされているわけではありません。 この場合、これらのタイルもロードされるまで結果は見つかりません。 オフラインの地図では、この問題は発生しません。また、必要なマップ データがダウンロードした地域で利用可能であることが保証されます。 したがって、キャッシュされたマップ データに依存しないことをお勧めします。

使用可能 なOfflineSearchEngineインターフェイスのほとんどはSearchEngineで使用できますが 、それとは異なります。単に、すべてのオンライン機能がオフラインデータからもアクセスできるわけではありません。

また 、HERE Places API内の場所を特定するplaces ID は、オフライン検索結果とは異なります。

通常、自由形式な TextQueryであれば、オフラインのplacesは少なくとも半径 62.5 km 以内にあります。 首都はどこからでもグローバルに見つけることができます。 クエリ文字列に都市名などの専用の場所を追加すると、その場所の近くを検索します。

現在、日本ではオフラインでの検索はできません。また、検索結果は見つかりません。

以下に、OfflineSearchEngineで使用できるユースケースを示します 。 たとえば、移動中に接続が一時的に失われることがあります。 このような場合は、すでにダウンロードされているマップ データで検索することをお勧めします。

そのためには、まずデバイスの接続が失われていないかを確認する必要があります。 2 番目のステップとして、優先エンジンを使用できます。

if isDeviceConnected {
    _ = searchEngine.search(textQuery: textQuery,
                            options: searchOptions,
                            completion: onSearchCompleted)
} else {
    _ = offlineSearchEngine.search(textQuery: textQuery,
                                   options: searchOptions,
                                   completion: onSearchCompleted)
}

検索結果を処理するに onSearchCompleted は、同じハンドラを設定します ( 前述のとおり ) 。

同様に、アドレスをリバースジオコードすることもできます。

if isDeviceConnected {
    _ = searchEngine.search(coordinates: geoCoordinates,
                            options: reverseGeocodingOptions,
                            completion: onReverseGeocodingCompleted)
} else {
    _ = offlineSearchEngine.search(coordinates: geoCoordinates,
                                   options: reverseGeocodingOptions,
                                   completion: onReverseGeocodingCompleted)
}

または、次のものを使用して、アドレスをジオコードして地理座標に変換できます。

if isDeviceConnected {
    _ = searchEngine.search(addressQuery: query,
                            options: geocodingOptions,
                            completion: onGeocodingCompleted)
} else {
    _ = offlineSearchEngine.search(addressQuery: query,
                                   options: geocodingOptions,
                                   completion: onGeocodingCompleted)
}

デバイスの接続を確認するための可能な解決策は、最初に実際の接続を試みることによって実装できます。それが失敗した場合は、OfflineSearchEngine- またはその逆に切り替えることができます。まずオフライン検索を試すことができますが、マップ データが利用できない場合はオンラインで試すことができます。

このセクションのすべてのコードは 、SearchHybrid GitHub のサンプルアプリの一部として入手できます。

」に一致する結果は 件です

    」に一致する結果はありません