Google Maps에서 대량 발생한 마커를 클러스터링할 수 있게 되었습니다! -Marker Clustering -
TL; DR
Google Maps SDK for iOS에 Marker Clustering이 새롭게 등장 했으므로 시도해 보았습니다!
Marker Clustering에서 확대하면 Marker가 클러스터와 분리되고 축소되면 Marker가 클러스터됩니다. 이렇게 하면 대량의 Marker로 지도가 채워져 보기 어려워지는 문제가 해결됩니다.
환경
GMS를 Cocoapods로 설치
Cocoapods를 통해 Google Maps SDK for iOS 및 Google Maps SDK for iOS Utility Library을 설치합니다.
Podfileplatform :ios, '9.0'
target プロジェクト名 do
pod 'GoogleMaps'
pod 'Google-Maps-iOS-Utils'
end
Swift 프로젝트에서 사용할 준비
GMS는 Objective-C로 작성되었습니다.
Swift 프로젝트에서는 Bridging-Header를 만들고 GMUMarkerClustering을 가져옵니다.
GoogleMapsTrial-Bridging-Header.h#import <Google-Maps-iOS-Utils/GMUMarkerClustering.h>
AppDelegate에서 Google Maps API Key 설정을 잊지 않도록 합니다.
AppDelegate.swiftimport UIKit
import GoogleMaps
// Google Maps API Key
let APIKey = "YOUR_API_KEY"
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
GMSServices.provideAPIKey(APIKey)
let mapViewController = UINavigationController.init(rootViewController: MapViewController())
self.window = UIWindow(frame: UIScreen.main().bounds)
self.window?.rootViewController = mapViewController
self.window?.makeKeyAndVisible()
return true
}
}
GMUClusterItem 프로토콜에 적합한 Item 만들기
지도에 표시할 MarkerItem을 만듭니다.
MarkerItem.swiftclass MarkerItem: NSObject, GMUClusterItem {
var position: CLLocationCoordinate2D //必須
init(position: CLLocationCoordinate2D) {
self.position = position
}
}
Map을 표시하는 ViewController 만들기
MapViewController.swift
import UIKit
import GoogleMaps
// 新宿フロントタワー
let cameraLatitude = 35.695978
let cameraLongitude = 139.689340
class MapViewController: UIViewController, GMUClusterManagerDelegate, GMSMapViewDelegate {
private let camera = GMSCameraPosition.camera(withLatitude: cameraLatitude,
longitude: cameraLongitude, zoom: 15)
private let mapView = GMSMapView.init(frame: CGRect.zero)
private lazy var clusterManager: GMUClusterManager = {
let iconGenerator = GMUDefaultClusterIconGenerator()
let algorithm = GMUNonHierarchicalDistanceBasedAlgorithm()
let renderer = GMUDefaultClusterRenderer(mapView: self.mapView, clusterIconGenerator: iconGenerator)
return GMUClusterManager(map: self.mapView, algorithm: algorithm, renderer: renderer)
}()
override func viewDidLoad() {
super.viewDidLoad()
self.mapView.camera = camera
self.view = self.mapView
// Marker × 1000 ランダム座標を生成し、ClusterManagerにadd
for _ in 0...1000 {
let extent = 0.1
let latitude = cameraLatitude + extent * randomScale()
let longitude = cameraLongitude + extent * randomScale()
clusterManager.add(MarkerItem.init(position: CLLocationCoordinate2DMake(latitude, longitude)))
}
// MarkerItemをClusteringし、地図にプロット
clusterManager.cluster()
// GMUClusterManagerDelegate + GMSMapViewDelegateを設定
clusterManager.setDelegate(self, mapDelegate: self)
}
// MARK: - GMUMapViewDelegate
// Marker or Cluster Markerがタップされた
func mapView(_ mapView: GMSMapView, didTap marker: GMSMarker) -> Bool {
if marker.userData is MarkerItem {
debugPrint("ClusterのMarkerItemがタップされた")
} else {
debugPrint("通常のMarkerがタップされた")
}
return false
}
// MARK: - GMUClusterManagerDelegate
// Clusterがタップされたたら、Camera Positionを移動
func clusterManager(_ clusterManager: GMUClusterManager, didTap cluster: GMUCluster) {
let newCamera = GMSCameraPosition.camera(withTarget: cluster.position,
zoom: mapView.camera.zoom + 1)
let update = GMSCameraUpdate.setCamera(newCamera)
mapView.moveCamera(update)
}
private func randomScale() -> Double {
return Double(arc4random()) / Double(UINT32_MAX) * 2.0 - 1.0
}
}
사용 가능한 클러스터링 알고리즘
아직 문서가 보이지 않기 때문에 Jump to Definition으로 날아갔는데, 2 종류의 클러스터링 알고리즘을 확인.
다음 설명은 의역입니다. 덧붙여 서두의 gif는 GMUNonHierarchicalDistanceBasedAlgorithm
를 사용하고 있습니다.
// マップをグリッド状に分割
GMUGridBasedClusterAlgorithm
/*
非階層的クラスタリング
1. 追加順にitemsをイテレート(クラスター候補)
2. itemの中心点を持つクラスターを作成
3. 特定の距離圏内に含まれるすべてのitemをクラスターに追加
4. 別のクラスターに近ければ、既存クラスターから追加済みitemを移動させる
5. それらをクラスター候補から削除
各クラスターは最初のitemの中心点を持つ(複数itemの重心ではなく)
*/
GMUNonHierarchicalDistanceBasedAlgorithm
참고
이하의 문서, 소스 코드를 참고로 했습니다.
platform :ios, '9.0'
target プロジェクト名 do
pod 'GoogleMaps'
pod 'Google-Maps-iOS-Utils'
end
GMS는 Objective-C로 작성되었습니다.
Swift 프로젝트에서는 Bridging-Header를 만들고 GMUMarkerClustering을 가져옵니다.
GoogleMapsTrial-Bridging-Header.h
#import <Google-Maps-iOS-Utils/GMUMarkerClustering.h>
AppDelegate에서 Google Maps API Key 설정을 잊지 않도록 합니다.
AppDelegate.swift
import UIKit
import GoogleMaps
// Google Maps API Key
let APIKey = "YOUR_API_KEY"
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
GMSServices.provideAPIKey(APIKey)
let mapViewController = UINavigationController.init(rootViewController: MapViewController())
self.window = UIWindow(frame: UIScreen.main().bounds)
self.window?.rootViewController = mapViewController
self.window?.makeKeyAndVisible()
return true
}
}
GMUClusterItem 프로토콜에 적합한 Item 만들기
지도에 표시할 MarkerItem을 만듭니다.
MarkerItem.swiftclass MarkerItem: NSObject, GMUClusterItem {
var position: CLLocationCoordinate2D //必須
init(position: CLLocationCoordinate2D) {
self.position = position
}
}
Map을 표시하는 ViewController 만들기
MapViewController.swift
import UIKit
import GoogleMaps
// 新宿フロントタワー
let cameraLatitude = 35.695978
let cameraLongitude = 139.689340
class MapViewController: UIViewController, GMUClusterManagerDelegate, GMSMapViewDelegate {
private let camera = GMSCameraPosition.camera(withLatitude: cameraLatitude,
longitude: cameraLongitude, zoom: 15)
private let mapView = GMSMapView.init(frame: CGRect.zero)
private lazy var clusterManager: GMUClusterManager = {
let iconGenerator = GMUDefaultClusterIconGenerator()
let algorithm = GMUNonHierarchicalDistanceBasedAlgorithm()
let renderer = GMUDefaultClusterRenderer(mapView: self.mapView, clusterIconGenerator: iconGenerator)
return GMUClusterManager(map: self.mapView, algorithm: algorithm, renderer: renderer)
}()
override func viewDidLoad() {
super.viewDidLoad()
self.mapView.camera = camera
self.view = self.mapView
// Marker × 1000 ランダム座標を生成し、ClusterManagerにadd
for _ in 0...1000 {
let extent = 0.1
let latitude = cameraLatitude + extent * randomScale()
let longitude = cameraLongitude + extent * randomScale()
clusterManager.add(MarkerItem.init(position: CLLocationCoordinate2DMake(latitude, longitude)))
}
// MarkerItemをClusteringし、地図にプロット
clusterManager.cluster()
// GMUClusterManagerDelegate + GMSMapViewDelegateを設定
clusterManager.setDelegate(self, mapDelegate: self)
}
// MARK: - GMUMapViewDelegate
// Marker or Cluster Markerがタップされた
func mapView(_ mapView: GMSMapView, didTap marker: GMSMarker) -> Bool {
if marker.userData is MarkerItem {
debugPrint("ClusterのMarkerItemがタップされた")
} else {
debugPrint("通常のMarkerがタップされた")
}
return false
}
// MARK: - GMUClusterManagerDelegate
// Clusterがタップされたたら、Camera Positionを移動
func clusterManager(_ clusterManager: GMUClusterManager, didTap cluster: GMUCluster) {
let newCamera = GMSCameraPosition.camera(withTarget: cluster.position,
zoom: mapView.camera.zoom + 1)
let update = GMSCameraUpdate.setCamera(newCamera)
mapView.moveCamera(update)
}
private func randomScale() -> Double {
return Double(arc4random()) / Double(UINT32_MAX) * 2.0 - 1.0
}
}
사용 가능한 클러스터링 알고리즘
아직 문서가 보이지 않기 때문에 Jump to Definition으로 날아갔는데, 2 종류의 클러스터링 알고리즘을 확인.
다음 설명은 의역입니다. 덧붙여 서두의 gif는 GMUNonHierarchicalDistanceBasedAlgorithm
를 사용하고 있습니다.
// マップをグリッド状に分割
GMUGridBasedClusterAlgorithm
/*
非階層的クラスタリング
1. 追加順にitemsをイテレート(クラスター候補)
2. itemの中心点を持つクラスターを作成
3. 特定の距離圏内に含まれるすべてのitemをクラスターに追加
4. 別のクラスターに近ければ、既存クラスターから追加済みitemを移動させる
5. それらをクラスター候補から削除
各クラスターは最初のitemの中心点を持つ(複数itemの重心ではなく)
*/
GMUNonHierarchicalDistanceBasedAlgorithm
참고
이하의 문서, 소스 코드를 참고로 했습니다.
class MarkerItem: NSObject, GMUClusterItem {
var position: CLLocationCoordinate2D //必須
init(position: CLLocationCoordinate2D) {
self.position = position
}
}
import UIKit
import GoogleMaps
// 新宿フロントタワー
let cameraLatitude = 35.695978
let cameraLongitude = 139.689340
class MapViewController: UIViewController, GMUClusterManagerDelegate, GMSMapViewDelegate {
private let camera = GMSCameraPosition.camera(withLatitude: cameraLatitude,
longitude: cameraLongitude, zoom: 15)
private let mapView = GMSMapView.init(frame: CGRect.zero)
private lazy var clusterManager: GMUClusterManager = {
let iconGenerator = GMUDefaultClusterIconGenerator()
let algorithm = GMUNonHierarchicalDistanceBasedAlgorithm()
let renderer = GMUDefaultClusterRenderer(mapView: self.mapView, clusterIconGenerator: iconGenerator)
return GMUClusterManager(map: self.mapView, algorithm: algorithm, renderer: renderer)
}()
override func viewDidLoad() {
super.viewDidLoad()
self.mapView.camera = camera
self.view = self.mapView
// Marker × 1000 ランダム座標を生成し、ClusterManagerにadd
for _ in 0...1000 {
let extent = 0.1
let latitude = cameraLatitude + extent * randomScale()
let longitude = cameraLongitude + extent * randomScale()
clusterManager.add(MarkerItem.init(position: CLLocationCoordinate2DMake(latitude, longitude)))
}
// MarkerItemをClusteringし、地図にプロット
clusterManager.cluster()
// GMUClusterManagerDelegate + GMSMapViewDelegateを設定
clusterManager.setDelegate(self, mapDelegate: self)
}
// MARK: - GMUMapViewDelegate
// Marker or Cluster Markerがタップされた
func mapView(_ mapView: GMSMapView, didTap marker: GMSMarker) -> Bool {
if marker.userData is MarkerItem {
debugPrint("ClusterのMarkerItemがタップされた")
} else {
debugPrint("通常のMarkerがタップされた")
}
return false
}
// MARK: - GMUClusterManagerDelegate
// Clusterがタップされたたら、Camera Positionを移動
func clusterManager(_ clusterManager: GMUClusterManager, didTap cluster: GMUCluster) {
let newCamera = GMSCameraPosition.camera(withTarget: cluster.position,
zoom: mapView.camera.zoom + 1)
let update = GMSCameraUpdate.setCamera(newCamera)
mapView.moveCamera(update)
}
private func randomScale() -> Double {
return Double(arc4random()) / Double(UINT32_MAX) * 2.0 - 1.0
}
}
아직 문서가 보이지 않기 때문에 Jump to Definition으로 날아갔는데, 2 종류의 클러스터링 알고리즘을 확인.
다음 설명은 의역입니다. 덧붙여 서두의 gif는
GMUNonHierarchicalDistanceBasedAlgorithm
를 사용하고 있습니다.// マップをグリッド状に分割
GMUGridBasedClusterAlgorithm
/*
非階層的クラスタリング
1. 追加順にitemsをイテレート(クラスター候補)
2. itemの中心点を持つクラスターを作成
3. 特定の距離圏内に含まれるすべてのitemをクラスターに追加
4. 別のクラスターに近ければ、既存クラスターから追加済みitemを移動させる
5. それらをクラスター候補から削除
各クラスターは最初のitemの中心点を持つ(複数itemの重心ではなく)
*/
GMUNonHierarchicalDistanceBasedAlgorithm
참고
이하의 문서, 소스 코드를 참고로 했습니다.
Reference
이 문제에 관하여(Google Maps에서 대량 발생한 마커를 클러스터링할 수 있게 되었습니다! -Marker Clustering -), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://qiita.com/mshrwtnb/items/b7929275bd22868c7717텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)