153 lines
6.0 KiB
Swift
153 lines
6.0 KiB
Swift
import Foundation
|
|
|
|
public struct InlqGeocodingPlace: Codable {
|
|
public let id: String
|
|
public let name: String
|
|
public let nameAr: String?
|
|
public let nameEn: String?
|
|
public let category: String?
|
|
public let city: String?
|
|
public let neighbourhood: String?
|
|
public let address: String?
|
|
public let position: InlqLatLng
|
|
}
|
|
|
|
/**
|
|
* Commercial Geocoding & Reverse Geocoding Service calling Intaleq SaaS API on iOS (Swift).
|
|
*/
|
|
public class IntaleqGeocodingService {
|
|
|
|
public static let shared = IntaleqGeocodingService()
|
|
|
|
private init() {}
|
|
|
|
/**
|
|
* Search for places, addresses, and POIs by text query.
|
|
*/
|
|
public func search(
|
|
query: String,
|
|
proximity: InlqLatLng? = nil,
|
|
limit: Int = 10,
|
|
completion: @escaping (Result<[InlqGeocodingPlace], Error>) -> Void
|
|
) {
|
|
guard let encoded = query.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else {
|
|
completion(.failure(NSError(domain: "IntaleqMaps", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid query"])))
|
|
return
|
|
}
|
|
|
|
var urlStr = "\(IntaleqMaps.shared.baseUrl)/api/geocoding/search?q=\(encoded)&limit=\(limit)"
|
|
if let proximity = proximity {
|
|
urlStr += "&lat=\(proximity.latitude)&lng=\(proximity.longitude)"
|
|
}
|
|
|
|
guard let url = URL(string: urlStr) else {
|
|
completion(.failure(NSError(domain: "IntaleqMaps", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid URL"])))
|
|
return
|
|
}
|
|
|
|
var request = URLRequest(url: url)
|
|
request.httpMethod = "GET"
|
|
request.setValue(IntaleqMaps.shared.apiKey, forHTTPHeaderField: "x-api-key")
|
|
request.setValue("application/json", forHTTPHeaderField: "Accept")
|
|
|
|
URLSession.shared.dataTask(with: request) { data, _, error in
|
|
if let error = error {
|
|
completion(.failure(error))
|
|
return
|
|
}
|
|
|
|
guard let data = data else {
|
|
completion(.success([]))
|
|
return
|
|
}
|
|
|
|
do {
|
|
if let json = try JSONSerialization.jsonObject(with: data) as? [String: Any],
|
|
let features = json["features"] as? [[String: Any]] {
|
|
|
|
let places: [InlqGeocodingPlace] = features.compactMap { f in
|
|
guard let geom = f["geometry"] as? [String: Any],
|
|
let coords = geom["coordinates"] as? [Double],
|
|
coords.count >= 2 else { return nil }
|
|
|
|
let props = f["properties"] as? [String: Any] ?? [:]
|
|
return InlqGeocodingPlace(
|
|
id: f["id"] as? String ?? UUID().uuidString,
|
|
name: props["name"] as? String ?? "موقع محدد",
|
|
nameAr: props["name_ar"] as? String,
|
|
nameEn: props["name_en"] as? String,
|
|
category: props["category"] as? String,
|
|
city: props["city"] as? String,
|
|
neighbourhood: props["neighbourhood"] as? String,
|
|
address: props["address"] as? String,
|
|
position: InlqLatLng(latitude: coords[1], longitude: coords[0])
|
|
)
|
|
}
|
|
completion(.success(places))
|
|
} else {
|
|
completion(.success([]))
|
|
}
|
|
} catch {
|
|
completion(.failure(error))
|
|
}
|
|
}.resume()
|
|
}
|
|
|
|
/**
|
|
* Reverse geocode coordinate to get street and neighborhood name.
|
|
*/
|
|
public func reverseGeocode(
|
|
position: InlqLatLng,
|
|
completion: @escaping (Result<InlqGeocodingPlace?, Error>) -> Void
|
|
) {
|
|
let urlStr = "\(IntaleqMaps.shared.baseUrl)/api/geocoding/reverse?lat=\(position.latitude)&lng=\(position.longitude)"
|
|
guard let url = URL(string: urlStr) else {
|
|
completion(.failure(NSError(domain: "IntaleqMaps", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid URL"])))
|
|
return
|
|
}
|
|
|
|
var request = URLRequest(url: url)
|
|
request.httpMethod = "GET"
|
|
request.setValue(IntaleqMaps.shared.apiKey, forHTTPHeaderField: "x-api-key")
|
|
request.setValue("application/json", forHTTPHeaderField: "Accept")
|
|
|
|
URLSession.shared.dataTask(with: request) { data, _, error in
|
|
if let error = error {
|
|
completion(.failure(error))
|
|
return
|
|
}
|
|
|
|
guard let data = data else {
|
|
completion(.success(nil))
|
|
return
|
|
}
|
|
|
|
do {
|
|
if let json = try JSONSerialization.jsonObject(with: data) as? [String: Any],
|
|
let features = json["features"] as? [[String: Any]], let first = features.first,
|
|
let geom = first["geometry"] as? [String: Any],
|
|
let coords = geom["coordinates"] as? [Double], coords.count >= 2 {
|
|
|
|
let props = first["properties"] as? [String: Any] ?? [:]
|
|
let place = InlqGeocodingPlace(
|
|
id: first["id"] as? String ?? UUID().uuidString,
|
|
name: props["name"] as? String ?? "موقع محدد",
|
|
nameAr: props["name_ar"] as? String,
|
|
nameEn: props["name_en"] as? String,
|
|
category: props["category"] as? String,
|
|
city: props["city"] as? String,
|
|
neighbourhood: props["neighbourhood"] as? String,
|
|
address: props["address"] as? String,
|
|
position: InlqLatLng(latitude: coords[1], longitude: coords[0])
|
|
)
|
|
completion(.success(place))
|
|
} else {
|
|
completion(.success(nil))
|
|
}
|
|
} catch {
|
|
completion(.failure(error))
|
|
}
|
|
}.resume()
|
|
}
|
|
}
|