Google Maps には、ユーザが独自に地点とメモを登録して地図を作成する「マイマップ」という機能があります。
このマイマップに登録した地点情報を取得する API は 2016 年 9 月現在、公開されていません。
ただ、マイマップには KML フォーマットでエクスポートする機能が提供されており、これを利用すれば地点情報を取得することができます。
本記事では、この仕組みを利用した iOS アプリのサンプルを作成し、マイマップの地点情報を読み込み表示する方法を紹介します。
サンプルアプリのソースコードを GitHub で公開しています。
https://github.com/gaprot/OmosanMap-iOS
KML ファイルの URL 取得
マイマップに登録した地点情報が入った KML ファイルの URL を取得するには、以下のようにします。
- Web ブラウザで取得したいマイマップを開く
- マイマップ名の右にあるメニューから「 KML にエクスポート」を選択
- 「地図全体」を選択、「.KMZ ファイルではなく〜」のチェックを外して「ダウンロード」

このままだと KMZ ファイルがダウンロードされるだけなので、ダウンロード先 URL を取得するために以下を行います。
- Chrome の場合: ダウンロード履歴を開き、記述されている URL をコピー
- Safari の場合: ダウンロード履歴を開き、項目を右クリックして「アドレスをコピー」
注意(2017/04/20 追記) : Googleマイマップ KML の URL は、不定期に変更される場合があります。
そのため、記憶した URL が動作しなくなる場合がありますので、
そのような場合には URL の払い出しからやり直す必要があります。
KML の構造
詳細な仕様は
XML スキーマに譲りますが、KML は概ね下記のような構造になっています。
<?xml version='1.0' encoding='UTF-8'?>
<kml xmlns='http://www.opengis.net/kml/2.2'>
<Document>
<name>ドキュメント名</name>
<description><![CDATA[ドキュメント詳細]]></description>
<Folder>
<name>グループ名</name>
<Placemark>
<name>地点名</name>
<description><![CDATA[地点情報詳細]]></description>
<styleUrl>適用するスタイルの URL (アンカー)</styleUrl>
<Point>
<coordinates>緯度,経度,0.0</coordinates>
</Point>
</Placemark>
...以降、<Placemark>が続く...
</Folder>
...以降、<Folder>が続く...
<Style id='icon-ci-1-nodesc-normal'>
<IconStyle>
<Icon>
<href>地点マーカー画像へのパス</href>
</Icon>
<Color>マーカーの色(BBGGRRAA)</Color>
</IconStyle>
...
</Style>
...以降、<Style>が続く...
</Document>
</kml>
ひとまず
Folder
ノードごとに分類された
Placemark
ノードを読み取れば、マップビューに表示できそうです。
KML ファイルのダウンロードとパース
KML ファイルのダウンロードには
Alamofire を使用しています。
カスタムアイコンも含めて取得したいので、zip 圧縮された KMZ ファイルをダウンロードした後、
SSZipArchive で展開します。
// DocumentDataSource.swift
var directoryURL = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)[0]
var localURL: URL?
Alamofire.download(URLString) { (temporaryURL, response) in
// ファイルのダウンロード先を指定
guard let pathComponent = response.suggestedFilename else {
fatalError()
}
let destinationURL = directoryURL.appendingPathComponent(pathComponent)
let destinationPath = destinationURL.path
// 同名のファイルが残っている場合は削除しておく
if FileManager.default.fileExists(atPath: destinationPath) {
try! FileManager.default.removeItem(atPath: destinationPath)
}
localURL = destinationURL
return (destinationURL, [.createIntermediateDirectories])
}.response { response in
var result: Error?
defer {
handler(result)
}
if let error = response.error {
result = error
} else {
guard let localPath = localURL?.path else {
return
}
let kmlDirPath = localPath.replacingOccurrences(of: ".kmz", with: "")
self.basePath = kmlDirPath
if FileManager.default.fileExists(atPath: kmlDirPath) {
try! FileManager.default.removeItem(atPath: kmlDirPath)
}
// KMZファイルを展開
if SSZipArchive.unzipFile(atPath: localPath, toDestination: kmlDirPath) {
let kmlPath = kmlDirPath.appending("/doc.kml")
do {
try self.parseKML(path: kmlPath)
} catch let error {
result = error
}
if let kml = try? String(contentsOfFile: kmlPath, encoding: String.Encoding.utf8) {
print(kml)
}
} else {
// URLが間違っているとここに来ることがある
result = ErrorType.DownloadFailed
}
}
}
最後に
Ji という XML パーサで KML ファイルをパースし、地点情報を取得します。以下は
Document
ノードのパース例です。
// Document.swift
struct Document {
let name: String
let description: String
let folders: [Folder]
let styles: [String: Style]
}
extension Document
{
static func fromJiNode(node: JiNode) -> Document {
var name = ""
var description = ""
var folders: [Folder] = []
var styles: [String: Style] = [:]
for childNode in node.children {
guard let childNodeName = childNode.name?.lowercased() else {
continue
}
switch (childNodeName) {
case "name":
name = childNode.content ?? ""
case "description":
description = childNode.content ?? ""
case "folder":
folders.append(Folder.fromJiNode(node: childNode))
case "style":
let style = Style.fromJiNode(node: childNode)
styles[style.id] = style
default:
break
}
}
return Document(
name: name,
description: description,
folders: folders,
styles: styles
)
}
}
地点の表示とマーカー
Placemark
ノードに地点名や緯度経度が入っています。
ここから
MKPointAnnotation
等を生成し、
MKMapView#addAnnotation
すれば、地図上に地点が表示されるはずです。
なおマイマップには各地点のマーカーのスタイルを編集することができ、その情報は
Style
ノードに収められています。各
Placemark
ノードの
styleUrl
にその参照名が入っているので、そのスタイルを適用します。
// MapViewController.swift
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
// 自分の現在位置を表すアノテーションは除く
if annotation === mapView.userLocation {
return nil
}
guard let placemarkAnnotation = annotation as? PlacemarkAnnotation else {
fatalError()
}
let styleID = placemarkAnnotation.styleID
let identifier = "Pin"
let annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: identifier) ?? MKAnnotationView(annotation: annotation, reuseIdentifier: identifier)
annotationView.annotation = annotation
let defaultIconImage = UIImage(named: "pin")
if let iconURL = DocumentDataSource.shared.iconURL(for: styleID) {
self.imageDownloader.download(URLRequest(url: iconURL)) { response in
switch response.result {
case .success(var image):
if let color = DocumentDataSource.shared.iconColor(for: styleID) {
image = image.filteringColor(color: color)
}
annotationView.image = image
case .failure(_):
annotationView.image = defaultIconImage
}
}
} else {
annotationView.image = defaultIconImage
}
// アノテーションをタップしたら「吹き出し」を表示
// annotationのtitleとsubtitle、rightCalloutAccessoryViewが表示される
annotationView.canShowCallout = true
annotationView.rightCalloutAccessoryView = UIButton(type: .detailDisclosure)
return annotationView
}

地点の詳細情報
Placemark
ノードの
description
には HTML でフォーマットされた地点の詳細情報が入っています。以下のようにすれば、
UITextView
に表示させることができます。
// PlaceDetailViewController.swift
if
let descriptionTextData = placemark.descriptionText.data(using: String.Encoding.unicode),
let attributedDescriptionText = try? NSAttributedString(
data: descriptionTextData,
options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType],
documentAttributes: nil
) {
self.descriptionTextView.attributedText = attributedDescriptionText
}
なお添付画像の URL は
description
内に
img
タグとして含まれているので、これを抽出しサニタイズするとより良い見せ方ができると思います。

おわりに
このサンプルアプリは元々、弊社が表参道に移転した際に有志で作成していた「表参道ランチマップ」をアプリでも使えるようにしたいという要望から作られました。KML の URL を取得してしまえば好きなマイマップを表示できますので、色々な使い方ができるのではないかと思います。
Google マイマップの編集機能を CMS として考え、「サーバレス」でカスタムの地図アプリが実現できるところもユニークだと思いますので、様々なサービスに活用してもらえれば幸いです!
ギャップロを運営しているアップフロンティア株式会社では、一緒に働いてくれる仲間を随時、募集しています。
興味がある!一緒に働いてみたい!という方は下記よりご応募お待ちしております。
採用情報をみる