【Flutter】地図機能関連
APIキーの隠蔽
iOS
- 環境変数用ファイルを作成(xcodeからファイルを追加)
import Foundation
struct Env {
static let googleMapApiKey = "<APIキー>"
}
- AppDelegate内で環境変数用ファイルにアクセスし取得
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GMSServices.provideAPIKey(Env.googleMapApiKey) // <---
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
- Environment.swiftをgitignore
Runner/Environment.swift
Android
- 環境変数用ファイルを作成
googleMap.apiKey=<APIキー>
- 環境変数を起動時にsecret.propertiesから読み込み
def secretProperties = new Properties()
def secretPropertiesFile = rootProject.file('secret.properties')
if (secretPropertiesFile.exists()) {
secretPropertiesFile.withReader('UTF-8') { reader ->
secretProperties.load(reader)
}
}
android{
...
defaultConfig{
...
manifestPlaceholders += [
googleMapApiKey: secretProperties.getProperty('googleMap.apiKey'),
]
...
}
}
- AndroidManifest内で変数に格納した環境変数を取得
- なぜかは分からないけど、applicationNameを削除する必要有り
+ <meta-data android:name="com.google.android.geo.API_KEY" android:value="${googleMapApiKey}"/>
- secret.propertiesをgitignore
secret.properties
GoogleMap
必要な要素
- GoogleMapController
- Position
- initialPosition
- cameraPosition
- LocationSettings
- Marker
パラメータ
- mapType
- initialCameraPosition
- myLocationEnabled
- onMapCreated
- onCameraMoveStarted:マップをドラッグして移動した時に呼び出される。
- onCameraMove:onCameraMoveStarted開始後に常に地図の中心の緯度と経度を取得できる。
- onCameraIdle:マップ移動の最後に一度呼ばれる。
- onTap:マップをタップした場所の緯度と経度が返される。
- circles (
Set<Circle>
):地図上に範囲を示すのに使用 - polygon(
Set<Polygon>
):マップ上に指定した形のポリゴンを表示 - polylines(
Set<Ployline>
):マップ上の区間に線を表示 - markers(
Set<Marker>
):マップ上にマーカーを設置
GoogleMapController
メソッド
- animateCamera(<CameraUpdate>)
- moveCamera(<CameraUpdate>)
クラス
-
CameraUpdate
- .newCameraPosition(<CameraPosition>)
- .newLatLng(<LatLng>)
-
CameraPosition
- target <LatLng>:緯度経度
- bearing <double>
- tilt <double>:傾き
- zoom <double>:ズーム
reference
Geolocator
- checkPermission
- requestPermission
- getPositionStream
- isLocationServiceEnabled
- getCurrentPosition
- distanceBetween
位置情報 with Firestore
- Firestoreで座標を管理する場合、
GeoPoint
という形で管理する GeoPoint
Widgetからカスタムマーカーを作る
アプローチ
-
BitmapDescriptor.fromBytes()
もしくはBitmapDescriptor.fromAssets()
を使って最終的にGoogleMapに渡す - その為にWidgetを一度画像化し、それをUint8ListのByteデータに変換して渡すというアプローチがある
解読
-
BitmapDescripter
はfromAsset
もしくはfromBytes
から生成することができる
BitmapDescriptor markerIcon = BitmapDescriptor.fromBytes(imageData);
-
imageData
はUint8List
の型
- マーカー化するwidgetのRenderObjectが存在する必要がある為、widgetを描画しなければならない
- 描画する選択肢としては、1) Stackで重ねる、2) Transform.translateで画面外で描画する、の2つのアプローチが存在する
- widgetを描画することで生成されるRenderObjectを再利用することができるようにリファレンスを取得する必要がある
-
RepaintBoundary
widgetを使うとラップしたwidgetの再描画を独立させることができる > これはもう少し勉強が必要。これは描画プロセスの中のLayerツリー構造が関係する模様。再描画の範囲を独立させるって感じかな。 - 先のRenderObjectに対するリファレンスを取得する為に、アイコンにしたいwidgetを
RepaintBoundary
でラップし、それにGlobalKeyを渡すことでGlobalKeyを通してアクセス可能にする - GlobalKeyの
currentContext.findRenderObject()
を使って、RenderRepaindBoundary
オブジェクトを取得する >>>
RenderRepaintBoundary boundary = iconKey.currentContext.findRenderObject();
-
RenderRepaintBoundary
が持つ、toImage()
メソッドを使って、renderObject
をImage
に変換
ui.Image image = await boundary.toImage(pixelRatio: 3.0);
- 今度はその
Image
をByteData
に変換
ByteData byteData = await image.toByteData(format: ui.ImageByteFormat.png);
- このByteDataをuint8Listに変換
var pngBytes = byteData.buffer.asUint8List();
-
BitmapDescriptor.fromBytes(imageData)
を使って、BitmapDescriptorに変換する
作成ステップ
- widgetを定義する
- そのwidgetをRepaintBoundaryでラップし、GlobalKeyを渡す
- GlobalKeyを通して、RenderRepaintBoundaryを取得する
- widgetからImageに変換
- ImageからByteDataに変換
- ByteDataからuint8Listに変換
- uint8ListからBitmapDescriptorに変換
参考
Mapbox
2つのバージョンが存在
- Mapbox GL JS v1と同世代
- Mapbox Maps SDK for Android v9
- Mapbox Maps SDK for iOS v6
- Mapbox GL JS v2と同世代
- Mapbox Maps SDK for Android v10
- Mapbox Maps SDK for iOS v10
パブリックトークン、シークレットトークンについて
Mapboxの利用をする際にはパブリックトークンとシークレットトークンの両方を使う必要がある。
Public token
:Read onlyの権限を有するトークン
Secret token
:操作権限や使用可能URLを設定する事ができるトークン
MapBoxのSDKのインストールにMapBoxからダウンロードする権限(DOWNLOADS:READ
)を付与したシークレットトークンが必要。
その後、ダウンロードしたMapBox SDKの利用にはRead only権限のパブリックトークンを使用する。
導入手順
- Packageのインストール
- パブリックトークンの設定
- iOS, Androidの設定
- MapWidgetの配置
1. Packageのインストール
1-1. シークレットトークンの発行
1-2. Android用の設定
ご自身のパソコンのHOMEディレクトリにある.gradle
ディレクトリにgrandle.properties
を作成し、以下の通り、シークレットトークンを設定
MAPBOX_DOWNLOADS_TOKEN=[発行したシークレットトークン]
1-3. iOS用の設定
ご自身のパソコンのHOMEディレクトリに.netrc
ファイルを作成し、以下の通り、読み込みコマンドを設定
machine api.mapbox.com
login mapbox
password [発行したシークレットトークン]
1-4. Packageのインストール
flutter pub add mapbox_maps_flutter
2. パブリックトークンの設定
MapboxOptions.setAccessToken(dotenv.env['MAPBOX_ACCESS_TOKEN'] ?? '');
3. iOS, Androidの設定
現在地取得の為のパーミッション設定をAndroid, iOS両方で設定
Android
AndroidManifest.xml
にて設定
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
iOS
Runner/Info.plist
にて設定
<key>NSLocationWhenInUseUsageDescription</key>
<string>[Your explanation here]</string>
reference
MapBoxの現在地表示
reference
Mapboxのピンマーカー配置
ピンマーカーはMapboxではAnnotationと呼ばれており、MapboxMap
クラスから生成できるPointAnnotationManager
クラスを使って、描画を行います。
final PointAnnotationManager? annotationMngr = await mapboxMap.annotations.createPointAnnotationManager();
生成手順
マーカーはPointAnnotationManager
のcreate()
メソッドを使ってマーカーオブジェクトであるPointAnnotationOptions
クラスを地図上に描画することが出来ます。
await annotationMngr?.create(PointAnnotationsOptions(...));
描画する画像はUint8List
型に変換し、PointAnnotationOptions
に渡す為、先に変換しておく必要があります。
final bytes = await rootBundle.load('assets/images/annotation_pin.png');
final list = bytes.buffer.asUint8List();
PointAnnotationOptions(
geometry: Point(
coordinates: Position(
0.381457,
6.687337,
)).toJson(),
textField: "custom-icon",
textOffset: [0.0, -2.0],
textColor: Colors.red.value,
iconSize: 1.3,
iconOffset: [0.0, -5.0],
symbolSortKey: 10,
image: list, // ⬅︎ こちらに渡す
)
またcreate(<PointAnnotationOptions>)
の代わりにcreateMulti(List<PointAnnotationOptions>)
を使うことで複数のマーカーを一度に描画させることも可能です。
var options = <PointAnnotationOptions>[];
for (var i = 0; i < 5; i++) {
options.add(PointAnnotationOptions(
geometry: createRandomPoint().toJson(), image: list));
}
annotationMngr?.createMulti(options);
reference
現在地取得
permission_handler
GeoLocator
Location
Reference