what3words とは
what3words とは住所などで表現される位置情報を、3つの単語で表現できるサービスです。
地球上を 3m × 3m のマスに区切り、その1マスを3つの単語で指定することができます。
「住所と違って何がいいの?」と思うかもしれませんが、
お花見の場所取りや、大型ショッピングセンターの特定の入り口など、
住所だけでは説明できない位置情報を短い3つの単語だけで指定することができます。
「それなら緯度経度の座標でいいやん」って思うかもしれませんが、
誰かに共有する際、緯度経度は不規則な数字であるため
コピー&ペースト以外の方法では共有が難しいというデメリットがあります。
what3word を使用すると、3つの単語を共有するだけで位置情報を共有できるので、
座標よりサクッと共有できるといったメリットがあります。
「住所とか緯度経度とか長いよね、カーナビの行き先指定とかめんどくさいし。
what3words を使うと3つの単語でサクッと位置情報を指定、共有できちゃうよ」
って感じです。
開発環境
- Xcode:11.1(11A1027)
- Swift 5
- iOS:13.1
下準備
こちらの手順に沿って進めていきます
API キーの取得
- こちらから利用登録をする
- ログイン後、Developer API Keys を選択
what3words のインストール
- CocoaPods
platform :ios, '9.0'
use_frameworks!
target 'MyApp' do
pod 'what3words', :git => 'https://github.com/what3words/w3w-swift-wrapper.git'
end
- Charthage
github "what3words/w3w-swift-wrapper"
セットアップ
- API Key のセット
AppDelegate.swift で API Key の登録を行います
import what3words
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
W3wGeocoder.setup(with: "input your API key")
return true
}
- what3words の API を使用したいところに以下を記述します
import what3words
import CoreLocation
以上で what3words を使用するための準備は完了です
what3words を使ってみた
今回は、
- 現在地の 3words を確認
- 検索欄から 3words を検索して位置を確認
の2つの機能を試してみようと思います
1. 現在地の 3words を確認
現在地の 3words を確認するために、まずは Google Maps SDK for iOS を使用し、
現在地の取得、表示を行います
Google Maps SDK for iOS を使用して現在地を地図上に表示にするまでの実装は別記事でまとめようと思います
import UIKit
import what3words
import GoogleMaps
class ViewController: UIViewController {
/// Google Map
let mapView: GMSMapView = {
let camera = GMSCameraPosition.camera(withLatitude: 35.6812226, longitude: 139.7670594, zoom: 12.0)
let view = GMSMapView.map(withFrame: CGRect.zero, camera: camera)
view.isMyLocationEnabled = true
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
/// 現在地の 3words を表示するラベル
let what3wordsLabel: UILabel = {
let label = UILabel()
label.numberOfLines = 1
label.textAlignment = .left
label.textColor = .black
label.font = UIFont.boldSystemFont(ofSize: 17.0)
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
let locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(mapView)
view.addSubview(what3wordsLabel)
searchBar.delegate = self
requestLoacion()
}
override func viewWillLayoutSubviews() {
setupSubViews()
}
private func setupSubViews() {
NSLayoutConstraint.activate([
what3wordsLabel.topAnchor.constraint(equalTo: view.top, constant: view.safeAreaInsets.top + 16.0),
what3wordsLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16.0),
mapView.topAnchor.constraint(equalTo: view.topAnchor),
mapView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
mapView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
mapView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
])
searchBar.delegate = self
}
private func requestLoacion() {
locationManager.delegate = self
locationManager.requestWhenInUseAuthorization()
}
}
extension ViewController: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
switch status {
case .authorizedAlways, .authorizedWhenInUse:
// 現在の位置情報を取得
locationManager.startUpdatingLocation()
case .denied, .notDetermined, .restricted:
print("許可されていません")
@unknown default:
fatalError()
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let currentLocation = locations.last?.coordinate else { return }
let camera = GMSCameraPosition(latitude: currentLocation.latitude,
longitude: currentLocation.longitude,
zoom: 17.0)
mapView.animate(to: camera)
W3wGeocoder.shared.convertTo3wa(coordinates: currentLocation, language: "ja", completion: { [weak self] (place, error) in
DispatchQueue.main.async {
// place.words に 位置情報から変換した 3words を取得できる
self?.what3wordsLabel.text = place?.words
}
})
locationManager.stopUpdatingLocation()
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print(error.localizedDescription)
}
}
2. 検索欄から 3words を検索して位置を確認
次に画面上に検索欄を表示させ、そこに 3words を入力し、Map 上にピンを立てるようなことをしたいと思います
what3words の検索 API の使用上、3つの単語は . で区切る必要があるようなので、
検索バーでは入力しやすいよう、スペース区切りで入力してもらって、それを . 区切りに変換するような処理を行なっています
class ViewController: UIViewController {
// 追記
/// what3words を入力する検索バー
let searchBar: UISearchBar = {
let view = UISearchBar()
view.placeholder = "スペース区切りで3つの単語を入れてください"
view.backgroundColor = .white
view.barTintColor = .black
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(mapView)
view.addSubview(what3wordsLabel)
view.addSubview(searchBar) // 追記
searchBar.delegate = self // 追記
requestLoacion()
}
override func viewWillLayoutSubviews() {
setupSubViews()
}
/// view の layout を指定
private func setupSubViews() {
// レイアウトを修正しています
NSLayoutConstraint.activate([
searchBar.topAnchor.constraint(equalTo: view.topAnchor, constant: view.safeAreaInsets.top + 16.0),
searchBar.leadingAnchor.constraint(equalTo: view.leadingAnchor),
searchBar.trailingAnchor.constraint(equalTo: view.trailingAnchor),
what3wordsLabel.topAnchor.constraint(equalTo: searchBar.bottomAnchor, constant: 16.0),
what3wordsLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16.0),
mapView.topAnchor.constraint(equalTo: searchBar.bottomAnchor),
mapView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
mapView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
mapView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
])
}
// ~~~~~~~~~省略~~~~~~~~~~~~
extension ViewController: UISearchBarDelegate {
/// 検索ボタンをタップした際に呼び出されるメソッド
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
guard let text = searchBar.text else { return }
// スペース区切りで入力したテキストを . 区切りに変換
let what3words = text.replacingOccurrences(of: " ", with: ".")
// ex) "ふつか そうさ ぜんぜん" で皇居へ移動します
W3wGeocoder.shared.convertToCoordinates(words: what3words, completion: { [weak self] (place, error) in
guard let location = place?.coordinates else { return }
let camera = GMSCameraPosition(latitude: location.latitude, longitude: location.longitude, zoom: 17.0)
let marker = GMSMarker(position: location)
marker.title = what3words
DispatchQueue.main.async {
marker.map = self?.mapView
self?.mapView.animate(to: camera)
}
})
}
}
コードの全容
import UIKit
import what3words
import GoogleMaps
class ViewController: UIViewController {
/// Google Map
let mapView: GMSMapView = {
let camera = GMSCameraPosition.camera(withLatitude: 35.6812226, longitude: 139.7670594, zoom: 12.0)
let view = GMSMapView.map(withFrame: CGRect.zero, camera: camera)
view.isMyLocationEnabled = true
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
/// 現在地の 3words を表示するラベル
let what3wordsLabel: UILabel = {
let label = UILabel()
label.numberOfLines = 1
label.textAlignment = .left
label.textColor = .black
label.font = UIFont.boldSystemFont(ofSize: 17.0)
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
/// what3words を入力する検索バー
let searchBar: UISearchBar = {
let view = UISearchBar()
view.placeholder = "スペース区切りで3つの単語を入れてください"
view.backgroundColor = .white
view.barTintColor = .black
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
let locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(mapView)
view.addSubview(what3wordsLabel)
view.addSubview(searchBar)
searchBar.delegate = self
requestLoacion()
}
override func viewWillLayoutSubviews() {
setupSubViews()
}
/// view の layout を指定
private func setupSubViews() {
NSLayoutConstraint.activate([
searchBar.topAnchor.constraint(equalTo: view.topAnchor, constant: view.safeAreaInsets.top + 16.0),
searchBar.leadingAnchor.constraint(equalTo: view.leadingAnchor),
searchBar.trailingAnchor.constraint(equalTo: view.trailingAnchor),
what3wordsLabel.topAnchor.constraint(equalTo: searchBar.bottomAnchor, constant: 16.0),
what3wordsLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16.0),
mapView.topAnchor.constraint(equalTo: searchBar.bottomAnchor),
mapView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
mapView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
mapView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
])
}
private func requestLoacion() {
locationManager.delegate = self
// 位置情報を取得
locationManager.requestWhenInUseAuthorization()
}
}
extension ViewController: CLLocationManagerDelegate {
/// 位置情報の取得の認可状態が更新された際に呼び出されるメソッド
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
switch status {
case .authorizedAlways, .authorizedWhenInUse:
locationManager.startUpdatingLocation()
case .denied, .notDetermined, .restricted:
print("許可されていません")
@unknown default:
fatalError()
}
}
/// 位置情報が更新された際に呼び出されるメソッド
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let currentLocation = locations.last?.coordinate else { return }
let camera = GMSCameraPosition(latitude: currentLocation.latitude,
longitude: currentLocation.longitude,
zoom: 17.0)
mapView.animate(to: camera)
W3wGeocoder.shared.convertTo3wa(coordinates: currentLocation, language: "ja", completion: { [weak self] (place, error) in
DispatchQueue.main.async {
// 現在の座標から変換した 3words をラベルに表示する
self?.what3wordsLabel.text = place?.words
}
})
locationManager.stopUpdatingLocation()
}
/// 位置情報の取得に失敗した際に呼び出されるメソッド
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print(error.localizedDescription)
}
}
extension ViewController: UISearchBarDelegate {
/// 検索ボタンをタップした際に呼び出されるメソッド
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
guard let text = searchBar.text else { return }
// スペース区切りで入力したテキストを . 区切りに変換
let what3words = text.replacingOccurrences(of: " ", with: ".")
// ex) "ふつか そうさ ぜんぜん" で皇居へ移動します
W3wGeocoder.shared.convertToCoordinates(words: what3words, completion: { [weak self] (place, error) in
guard let location = place?.coordinates else { return }
let camera = GMSCameraPosition(latitude: location.latitude, longitude: location.longitude, zoom: 17.0)
let marker = GMSMarker(position: location)
marker.title = what3words
DispatchQueue.main.async {
marker.map = self?.mapView
self?.mapView.animate(to: camera)
}
})
}
}