iOS,AndroidにおけるMapBox
ライブラリを用いた地理院地図のハンドリング
第11回地理院地図パートナーネットワーク会議

応用技術株式会社
エンジニアリング本部 国土基盤情報部 建設情報ユニット

林 博文

会社紹介

応用技術株式会社

概要
2018年度にiOS,Androidのアプリ開発において、いくつかの理由により使用していたAppleMapView、GoogleMapsAPIをMapBoxライブラリに置き換え、地理院地図タイルを背景地図に変更した。
AppleMapView、GoogleMapsAPIとの特徴比較や変更した理由、MapBoxライブラリによる地理院地図ハンドリングの実装と効果について述べる。

スマートデバイス向けの地図アプリ開発● Appleが提供する「Apple Maps」、Googleが提供する「GoogleMaps API」が標準● 標準地図はカスタマイズができない=>GoogleMaps APIはできるようになった。
● ライセンシングや仕様に違いがある● そのほか「地理院地図」「OpenStreetMap」「Bing Maps」「Mapion」「Yahoo!地図」「ゼンリンいつもNAVI」「MapBox」等● SDKやAPIが準備されている

iOSとAndroid
iOS (iPadOS)Android

開発環境

XCodeAndroid Studio

開発言語

Objective-C++ / SwiftJava / Kotlin

ライブラリ

AppleMapView

GoogleMapsAPI

主要センサー

GPSTouch ID指紋認証センサー
気圧計
3軸ジャイロ
加速度センサー近接センサー
環境光センサーGPS
気圧センサー
ジャイロセンサー
加速度センサー近接センサー
周囲温度センサー
重力センサー
照度センサー
地磁気センサー
相対湿度センサー
歩行センサーライセンスの違いApple Mapkit

Google Maps API

デベロッパー契約に地図利用代金が含まれているため、特に利用料は必要なし。

ライセンス登録にカード番号が必要
1 か月 $200 分まで無料
Mobile Native Static Mapsは読み込み無制限
Mobile Native Dynamic Mapsは読み込み無制限
Dynamic Maps(Web)は最大 28,000 読み込み$200/
月+0～100,000$7.00 / 1000回+100,001～500,000 $5.60 / 1000回
そのほか、ルート、プレイスは別料金マップ種類の違い
Apple Mapkit

Google Maps API

standard 標準の地図

ROADMAP 市街地図

satellite 航空写真

SATELLITE 航空写真

hybrid 標準の地図＋航空写真

HYBRIDを表示

航空写真上に主要な道路の透明なレイヤ

satelliteFlyover 3D航空写真

TERRAIN

地形的特徴を持つ地図を表示

hybridFlyover 3D標準の地図＋航空写真

iOS,Androidの地図ハンドリング
iOS

Android

ライブラリの使用

MapKit.frameworkLinked を
Frameworks and Libraries に追加

app/build.gradle:
implementation'com.google.android.gms:playservices-maps:17.0.0'
Manifest:Map API キー

コード

storyboard:
MapKitViewを貼付け

layout:swiftコード:import MapKitを記載、UIViewControllerのクラス変数内に@IBOutlet weak var mapView:
MKMapView! とstoryboardのコントローラ参照を作成する。

SupportMapFragmentを配置してMapのActivityをコンテキストに指定する。
Activity:
ActivityのコールバックでMapのオブジェ
クト生成

iOS::AppleMapViewの特徴
レイヤ：
MKAnnotation プロトコル（アノテーションのコントロール）
MKOverlay プロトコル（地物、画像のコントロール）
図形：
CirclePolygonPolyline
Paths
Tiled Images
特徴：
Locationサービス（ルート表示、POI 検索、ジオコーディング）APIが利用できる。
lineやポイントのスタイルを、個別のオブジェクト単位で指定しておく方法がない・・・・Android::GoogleMapsAPIの特徴
レイヤ：
Markers
Info Windows
ShapesGround Overlays
Tile Overlays
図形：
Icons anchored to specific positions on the map (Markers).
Sets of line segments (Polylines).
Enclosed segments (Polygons).
Bitmap graphics anchored to specific positions on the map (Ground Overlays).
Sets of images which are displayed on top of the base map tiles (Tile Overlays).
特徴：
ラインやポイントのスタイルを、個別のオブジェクト単位で指定しておくメソッドがある。
多数のマーカーをまとめて表現できるクラスタをサポートする。
GeoJSON、KMLのインポートができる。
Locationサービス（ルート表示、POI 検索、ジオコーディング）APIは別料金・・・

地図の表現の差異●●●●

AppleMaps、GoogleMapsにはトンネルの表記がない
iOS端末、Android端末で同一内容の地図を提供可能なコントロールに切替える
地理院地図は災害時のコンテンツも迅速に提供される地図表現を選択できる
Apple Map

地理院地図

Google Maps

地図ライブラリの変更
iOSとAndroidで地図ハンドリングに関するコードを同じにしたい
タイルのズーム制限を回避したい
地図表現を紙地図に近くしたい。
カスタマイズ地図に柔軟に対応できるようにしたい
オフラインマップに対応したい
SDKの機能開発スピードとバグフィックス、ソースコード修正

MapBox SDK

https://github.com/mapbox/mapbox-gl-native

MapBox SDK + 地理院地図
地理院地図は地形情報や構造物の情報が適切に表現されており、フィールド調査において周囲の状況把握が容易。
オフラインマップを利用することができるので、通信回線の不安定な地域でのアプリ使用が可能。
MapBox SDKにより、iOS・Androidで同じの表示品質が得られる。

Apple Map地理院地図

MapBox SDK + 地理院地図
地理院地図の全国最新写真シームレスは一部色合いが異なったり抜けがあるが、新しいものは解像度はよい。
MapBox SDKでは、標準の最大ズーム率18よりも拡大することができる。

地理院地図ハンドリング実装
class MapViewController: UIViewController {gsi_stdmaplayer.json
var trackCurrentLocation = true
var shownInitialLocation : Bool = false{
var selectedArea : InspectionArea?
"version": 8,var sensingEventAnnotaions :[String:MGLPointAnnotation] = [:]"name": "Raster Tiles",@IBOutlet weak var controllBar: UIView!
@IBOutlet weak var mapTypeSelector: UISegmentedControl!
"sources": {@IBOutlet weak var mapView: MGLMapView!
"gsiStd": {var downloadDate : Date?
"type": "raster",var refreshState = true
"tiles": [let imagePinReuseId = "imagePin""https://cyberjapandata.gsi.go.jp/xyz/std/{z}/{x}/{y}.png" let pinReuseId = "areaPin"],
let mapStyleURL = URL(string: "https://ogicim.com/layer/gsi_stdmaplayer.json")!
let satelliteMapStyleURL = URL(string: "https://ogicim.com/layer/gsi_satellite.json")!
"tileSize": 256}
override func viewDidLoad() {super.viewDidLoad()},self.mapTypeSelector.layer.cornerRadius = 6.0
self.addAnnotations()"layers": [mapView.showsUserLocation = true{
mapView.styleURL = mapStyleURL
mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]"id": "gsiStd",
mapView.showsScale = true
mapView.zoomLevel = MainStore.state.setting.zoomLevel"type": "raster",
mapView.showsHeading = true
"source": "gsiStd",
mapView.showsUserHeadingIndicator = true
mapView.camera.heading = MainStore.state.setting.direction"paint": {"raster-fade-duration": 100
for gestureRecognizer in self.mapView.gestureRecognizers! {}if let _ = gestureRecognizer as? UIPanGestureRecognizer {gestureRecognizer.addTarget(self, action: #selector(self.panOnMap))}
break} else if let _ = gestureRecognizer as? UIPinchGestureRecognizer {]gestureRecognizer.addTarget(self, action: #selector(self.pinchOnMap))}
break}}

iOS::Swift地理院地図ハンドリング実装
package ogicim.mapapp
import android.Manifest<省略>
import com.mapbox.android.core.location.LocationEngineCallback<省略>
class MapActivity : AppCompatActivity() {private lateinit var mViewModel: MapViewModel
private var mMapView: MapView? = null
private val mMapMarkers: HashMap<String, Geofencing> = HashMap()
override fun onCreate(savedInstanceState: Bundle?) {}}Mapbox.getInstance(this, getString(R.string.access_token))super.onCreate(savedInstanceState)
setContentView(R.layout.activity_map)
mViewModel = ViewModelProviders.of(this, MapViewModel.Factory()).get(MapViewModel::class.java)mViewModel.populate()navigation_map.selectedItemId = R.id.navigation_map
navigation_map.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener)
mMapView = findViewById(R.id.mapView)
mMapView?.onCreate(savedInstanceState)
mMapView?.getMapAsync { mapboxMap ->if(1){mapboxMap.setStyle(getString(R.string.style_url)) { style -> setUpLocationComponent(style, mapboxMap) }showScaleBar(mapboxMap)
text_map.setOnClickListener { changeLayer(mapboxMap, false) }text_satellite.setOnClickListener { changeLayer(mapboxMap, true) }img_my_location.setOnClickListener {State.lastZoom = mapboxMap.cameraPosition.zoom
mapboxMap.locationComponent.lastKnownLocation?.also { loc ->mapboxMap.moveCamera(CameraUpdateFactory.newCameraPosition(CameraPosition.Builder().target(LatLng(loc.latitude, loc.longitude)).build()))}
if (State.isStartMaintenance) mapboxMap.locationComponent.cameraMode = CameraMode.TRACKING}
showMarkers(mapboxMap)}}

Android::Kothlin

AndroidはiOSと比較して、ステップが多くなる傾向がある。

MapBox SDK + 地理院地図利用の効果

カスタマイズ地図に柔軟に対応できる

MapBox SDK + 地理院地図利用の効果

3Dに対応しており、地図を傾けて表示することができる。

今後の課題①特定地域の地震、台風・豪雨等、火山 レイヤ対応②ベクタータイルの対応（ズームレベル18以上）③全国最新写真シームレス＋ベクタータイルレイヤ④オフラインマップ⑤3D表示のDEM対応⑥ズームレベル19以上の写真地図※実装の容易な順

最後に宣伝Geopaparazzi 6.0
SMASH

スマホのフィールド調査アプリ
調査レポートのカスタマイズとQGISやArcGISで作成した地図データが取り込める便利なアプリがバージョンア
ップして、レイヤー操作、3D表示、ベクトルタイル対応、iOS対応に進化