215 lines
9.1 KiB
Swift
215 lines
9.1 KiB
Swift
//
|
|
// Copyright Amazon.com Inc. or its affiliates.
|
|
// All Rights Reserved.
|
|
//
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
//
|
|
|
|
import Amplify
|
|
import Foundation
|
|
|
|
import AWSLocation
|
|
|
|
extension AWSLocationGeoPlugin {
|
|
|
|
// MARK: - Search
|
|
|
|
/// Search for places or points of interest.
|
|
/// - Parameters:
|
|
/// - text: The place name or address to be used in the search. (case insensitive)
|
|
/// - area: The area (.near or .boundingBox) for the search. (optional)
|
|
/// - countries: Limits the search to the given a list of countries/regions. (optional)
|
|
/// - maxResults: The maximum number of results returned per request. (optional)
|
|
/// - placeIndexName: The name of the Place Index to query. (optional)
|
|
/// - Returns :
|
|
/// It returns a Geo.Place array.
|
|
/// - Throws:
|
|
/// `Geo.Error.accessDenied` if request authorization issue
|
|
/// `Geo.Error.serviceError` if service is down/resource not found/throttling/validation error
|
|
/// `Geo.Error.invalidConfiguration` if invalid configuration
|
|
/// `Geo.Error.networkError` if request failed or network unavailable
|
|
/// `Geo.Error.pluginError` if encapsulated error received by a dependent plugin
|
|
/// `Geo.Error.unknown` if error is unknown
|
|
public func search(for text: String,
|
|
options: Geo.SearchForTextOptions? = nil) async throws -> [Geo.Place] {
|
|
|
|
var request = SearchPlaceIndexForTextInput()
|
|
|
|
request.indexName = (options?.pluginOptions as? AWSLocationGeoPluginSearchOptions)?
|
|
.searchIndex ?? pluginConfig.defaultSearchIndex
|
|
|
|
guard request.indexName != nil else {
|
|
throw Geo.Error.invalidConfiguration(
|
|
GeoPluginErrorConstants.missingDefaultSearchIndex.errorDescription,
|
|
GeoPluginErrorConstants.missingDefaultSearchIndex.recoverySuggestion)
|
|
}
|
|
|
|
request.text = text
|
|
|
|
if let area = options?.area {
|
|
switch area {
|
|
case .near(let coordinates):
|
|
request.biasPosition = [coordinates.longitude,
|
|
coordinates.latitude]
|
|
case .within(let boundingBox):
|
|
request.filterBBox = [boundingBox.southwest.longitude,
|
|
boundingBox.southwest.latitude,
|
|
boundingBox.northeast.longitude,
|
|
boundingBox.northeast.latitude]
|
|
}
|
|
}
|
|
|
|
if let countries = options?.countries {
|
|
request.filterCountries = countries.map { country in
|
|
country.code
|
|
}
|
|
}
|
|
|
|
if let maxResults = options?.maxResults {
|
|
request.maxResults = maxResults as Int
|
|
}
|
|
|
|
do {
|
|
let response = try await locationService.searchPlaceIndex(forText: request)
|
|
var results = [LocationClientTypes.Place]()
|
|
if let responseResults = response.results {
|
|
results = responseResults.compactMap {
|
|
$0.place
|
|
}
|
|
}
|
|
|
|
let places: [Geo.Place] = results.compactMap {
|
|
guard let long = $0.geometry?.point?.first,
|
|
let lat = $0.geometry?.point?.last
|
|
else {
|
|
return nil
|
|
}
|
|
|
|
return Geo.Place(coordinates: Geo.Coordinates(latitude: lat, longitude: long),
|
|
label: $0.label,
|
|
addressNumber: $0.addressNumber,
|
|
street: $0.street,
|
|
municipality: $0.municipality,
|
|
neighborhood: $0.neighborhood,
|
|
region: $0.region,
|
|
subRegion: $0.subRegion,
|
|
postalCode: $0.postalCode,
|
|
country: $0.country)
|
|
}
|
|
return places
|
|
} catch {
|
|
let geoError = GeoErrorHelper.mapAWSLocationError(error)
|
|
throw geoError
|
|
}
|
|
}
|
|
|
|
/// Reverse geocodes a given pair of coordinates and returns a list of Places
|
|
/// closest to the specified position.
|
|
/// - Parameters:
|
|
/// - coordinates: Specifies a coordinate for the query.
|
|
/// - maxResults: The maximum number of results returned per request. (optional)
|
|
/// - placeIndexName: The name of the Place Index to query. (optional)
|
|
/// - Return value :
|
|
/// It returns a Geo.Place array.
|
|
/// - Throws:
|
|
/// `Geo.Error.accessDenied` if request authorization issue
|
|
/// `Geo.Error.serviceError` if service is down/resource not found/throttling/validation error
|
|
/// `Geo.Error.invalidConfiguration` if invalid configuration
|
|
/// `Geo.Error.networkError` if request failed or network unavailable
|
|
/// `Geo.Error.pluginError` if encapsulated error received by a dependent plugin
|
|
/// `Geo.Error.unknown` if error is unknown
|
|
public func search(for coordinates: Geo.Coordinates,
|
|
options: Geo.SearchForCoordinatesOptions? = nil) async throws -> [Geo.Place] {
|
|
|
|
var request = SearchPlaceIndexForPositionInput()
|
|
|
|
request.indexName = (options?.pluginOptions as? AWSLocationGeoPluginSearchOptions)?
|
|
.searchIndex ?? pluginConfig.defaultSearchIndex
|
|
|
|
guard request.indexName != nil else {
|
|
throw Geo.Error.invalidConfiguration(
|
|
GeoPluginErrorConstants.missingDefaultSearchIndex.errorDescription,
|
|
GeoPluginErrorConstants.missingDefaultSearchIndex.recoverySuggestion)
|
|
}
|
|
|
|
request.position = [coordinates.longitude,
|
|
coordinates.latitude]
|
|
|
|
if let maxResults = options?.maxResults {
|
|
request.maxResults = maxResults as Int
|
|
}
|
|
|
|
do {
|
|
let response = try await locationService.searchPlaceIndex(forPosition: request)
|
|
var results = [LocationClientTypes.Place]()
|
|
if let responseResults = response.results {
|
|
results = responseResults.compactMap {
|
|
$0.place
|
|
}
|
|
}
|
|
|
|
let places: [Geo.Place] = results.compactMap {
|
|
guard let long = $0.geometry?.point?.first,
|
|
let lat = $0.geometry?.point?.last
|
|
else {
|
|
return nil
|
|
}
|
|
|
|
return Geo.Place(coordinates: Geo.Coordinates(latitude: lat, longitude: long),
|
|
label: $0.label,
|
|
addressNumber: $0.addressNumber,
|
|
street: $0.street,
|
|
municipality: $0.municipality,
|
|
neighborhood: $0.neighborhood,
|
|
region: $0.region,
|
|
subRegion: $0.subRegion,
|
|
postalCode: $0.postalCode,
|
|
country: $0.country)
|
|
}
|
|
return places
|
|
} catch {
|
|
let geoError = GeoErrorHelper.mapAWSLocationError(error)
|
|
throw geoError
|
|
}
|
|
}
|
|
|
|
// MARK: - Maps
|
|
|
|
/// Retrieves metadata for available map resources.
|
|
/// - Returns: Metadata for all available map resources.
|
|
/// - Throws:
|
|
/// `Geo.Error.accessDenied` if request authorization issue
|
|
/// `Geo.Error.serviceError` if service is down/resource not found/throttling/validation error
|
|
/// `Geo.Error.invalidConfiguration` if invalid configuration
|
|
/// `Geo.Error.networkError` if request failed or network unavailable
|
|
/// `Geo.Error.pluginError` if encapsulated error received by a dependent plugin
|
|
/// `Geo.Error.unknown` if error is unknown
|
|
public func availableMaps() async throws -> [Geo.MapStyle] {
|
|
let mapStyles = Array(pluginConfig.maps.values)
|
|
guard !mapStyles.isEmpty else {
|
|
throw Geo.Error.invalidConfiguration(
|
|
GeoPluginErrorConstants.missingMaps.errorDescription,
|
|
GeoPluginErrorConstants.missingMaps.recoverySuggestion)
|
|
}
|
|
return mapStyles
|
|
}
|
|
|
|
/// Retrieves the default map resource.
|
|
/// - Returns: Metadata for the default map resource.
|
|
/// - Throws:
|
|
/// `Geo.Error.accessDenied` if request authorization issue
|
|
/// `Geo.Error.serviceError` if service is down/resource not found/throttling/validation error
|
|
/// `Geo.Error.invalidConfiguration` if invalid configuration
|
|
/// `Geo.Error.networkError` if request failed or network unavailable
|
|
/// `Geo.Error.pluginError` if encapsulated error received by a dependent plugin
|
|
/// `Geo.Error.unknown` if error is unknown
|
|
public func defaultMap() async throws -> Geo.MapStyle {
|
|
guard let mapName = pluginConfig.defaultMap, let mapStyle = pluginConfig.maps[mapName] else {
|
|
throw Geo.Error.invalidConfiguration(
|
|
GeoPluginErrorConstants.missingDefaultMap.errorDescription,
|
|
GeoPluginErrorConstants.missingDefaultMap.recoverySuggestion)
|
|
}
|
|
return mapStyle
|
|
}
|
|
}
|