Dit is een blogserie waarin functies voor het ontwikkelen van kaartapps met Flutter worden geïntroduceerd. Deze keer richten we ons op het weergeven van offline kaarten. We implementeren een functie om kaarten op het apparaat te downloaden en offline weer te geven. In dit artikel,Flutter kaart SDK (ArcGIS Maps SDK for Flutter) gebruiken we om deze functie te implementeren.
De voorbeeldcode is GitHub gepubliceerd, dus als je het snel wilt bekijken, kun je het downloaden. Hoe je de app uitvoert staat beschreven in de README . Om de voorbeelden uit te voeren heb je eenAPI-sleutel nodig (gratis) . De methode om de kaart- SDK te installeren en de kaart weer te geven vind je in het blogartikel "Laten we een kaartapp maken met Flutter!". In dit artikel richten we ons op de functie voor het weergeven van offline kaarten.
Offline kaarten (Vector Tile Layer) aanmaken
Deze keer maken we een functie waarmee je gegevens van een willekeurig gebied van de weergegeven kaart kunt downloaden. Eerst maken we een Vector Tile Layer die geschikt is voor downloaden zodat deze ook offline gebruikt kan worden.
1. Meld je aan bij ArcGIS Online met een ArcGIS Online account of een ArcGIS Location Platform account. Als je nog geen account hebt, kun je er gratis een aanmaken via de stappen bij .
2. Open de link bij , waar je een lijst ziet van de service ArcGIS Basemap Styles (Vector Tile Layer) .
3. Kies uit de items waarvan de naam "(for Developers)" bevat, bijvoorbeeld "World Street Map (wegenkaart)" of "World Topographic Map (topografische kaart)". Klik op de naam van het gekozen item en klik vervolgens linksonder op [] om details te bekijken.
4. Klik rechtsboven op [Vector Tile Style Editor bewerken] om de stijl aan te passen.'Vector Tile Style Editor' maakt het mogelijk om originele stijlen in te stellen afhankelijk van het gebruik van de kaart. Zie ook het artikel bij .
5. Klik linksboven op [Snelle bewerking] en verander de taalinstelling van Engels naar Japans onder [Taal].
6. [名前を付けて保存] をクリックして、[名前を付けてスタイルを保存] 画面を表示します。
7. タイトルに適当な名前を記入し、[次のレイヤーと共有:] を「すべてのユーザー (パブリック)」に変更して、[スタイルの保存] をクリックします。
8. Web ブラウザーに表示されている URL の英数字の部分が、このベクター タイル レイヤー スタイルの ID です。この ID を後の手順で使用します。
オフラインマップの表示機能の実装
続いて、オフラインマップを表示するアプリを開発していきます。
1. 「Flutter で地図アプリを作成してみよう!」のブログ記事の手順 6 で、マップビュー コントローラーに作成したマップを設定してマップの表示までできることを確認します。
パッケージの追加
2. デバイス内のパスの取得に path_provider パッケージを使用するため、pubspec.yaml の dependencies セクションにパッケージを追加します。
dependencies:
flutter:
sdk: flutter
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.8
arcgis_maps: ^200.8.0+4672
# 追加
path_provider: ^2.1.5
3. lib/main.dart を開き、既に書かれているインポート行の後に、下記のコードを追加して、この後の処理に必要なパッケージをインポートします。
import 'package:path_provider/path_provider.dart';
import 'dart:io';
変数の追加
4. _MyHomePageState クラスの最初に、使用する変数を追加します。
// オンライン時に表示するマップ オブジェクト格納用
ArcGISMap? _onlineMap;
// ベクター タイルを表示するためのレイヤー オブジェクト
ArcGISVectorTiledLayer? _vectorTiledLayer;
// ベクター タイルをダウンロードする際に使用するジョブ
ExportVectorTilesJob? _exportVectorTilesJob;
// ローディングバー (LinearProgressIndicator) の表示フラグ
bool _loading = false;
UI の作成
5. オンラインマップとオフラインマップの表示を切り替えるボタンを追加します。build Widget の中に既にある Expanded ウィジェットの下に、下記を追加します。
children: [
Expanded(
child: Stack(children: [
ArcGISMapView(
・・・省略・・・
])),
// 追加
Row(
mainAxisAlignment: MainAxisAlignment.center,
// オンラインマップとオフラインマップの表示を切り替えるボタンを追加します。
children: [
ElevatedButton(
onPressed: takeOnline,
child: const Text('オンライン表示'),
),
SizedBox(width: 5.0),
ElevatedButton(
onPressed: takeOffline,
child: const Text('オフラインマップ作成'),
),
]
)
6. マップ上にインジケーター (LinearProgressIndicator) を表示するようにコードを変更します。
// 変更前
child: ArcGISMapView(
controllerProvider: () => _mapViewController,
onMapViewReady: _onMapViewReady,
),
// 変更後
child: Stack(children: [
ArcGISMapView(
controllerProvider: () => _mapViewController,
onMapViewReady: _onMapViewReady),
Visibility(
visible: _loading,
child: SizedBox.expand(
child: Container(
margin: EdgeInsets.all(20),
child: Center(
child: LinearProgressIndicator(
minHeight: 20,
),
),
),
),
),
])
ダウンロード機能の実装
7. ベースマップを作成する既存のコードを、「オフラインマップ (ベクター タイル レイヤー) の作成」で作成したベクター タイル レイヤーを表示するベースマップに変更します。PortalItem.withPortalAndItemId コンストラクターの itemId には、「オフラインマップ (ベクター タイル レイヤー) の作成」の手順.8 で表示された ID を設定します。
// 変更前
// ベースマップのラベルを日本語表記にするためのパラメーターを設定します。
final bsp = BasemapStyleParameters();
bsp.specificLanguage = "ja";
final basemap = Basemap.withStyle(BasemapStyle.arcGISStreets, parameters: bsp);
// 変更後
// ポータルのアイテム ID からベクター タイル レイヤーを作成します。
final portal = Portal(
Uri.parse('https://www.arcgis.com'),
);
final portalItem = PortalItem.withPortalAndItemId(
portal: portal, itemId: "aa3f471a985641e094549ef472adec18");
_vectorTiledLayer = ArcGISVectorTiledLayer.withItem(portalItem);
// ベクター タイル レイヤーをベースマップのレイヤーにしてマップを作成します。
final basemap = Basemap.withBaseLayer(_vectorTiledLayer,);
8. ここからは、マップをダウンロードする処理を実装していきます。「オフラインマップ作成」ボタンをタップした時に実行される takeOffline 関数 (ElevatedButton の onPressed プロパティに設定) を作成します。
// 「オフラインマップ作成」ボタンを選択したときの処理
void takeOffline() async {
if (_mapViewController.arcGISMap != _onlineMap) return;
}
9. takeOffline 関数内にコードを追加していきます。まず、処理中を示すためのインジケーターを表示します。
// インジケーターを表示します。
setState(() => _loading = true);
10. ベクター タイルをダウンロードするための、ExportVectorTilesTask を作成します。このタスクは、ベクター タイルのソースデータ (.vtpk ファイル) とスタイル リソースをローカルにダウンロードするためのタスクです。
// ダウンロードしたベクター タイルを保存するためのディレクトリを準備します。
final directory = await getApplicationDocumentsDirectory();
final resourceDirectory = Directory(
'${directory.path}${Platform.pathSeparator}offline',
);
if (resourceDirectory.existsSync()) {
resourceDirectory.deleteSync(recursive: true);
}
resourceDirectory.createSync(recursive: true);
final resourceDirectoryPath = resourceDirectory.path;
final vtpkFile = File(
'$resourceDirectoryPath${Platform.pathSeparator}basemap.vtpk',
);
// ベクター タイル レイヤーの URL をパラメーターに設定してタスクを作成・ロードします。
final vectorTilesExportTask =
ExportVectorTilesTask.withUri(_vectorTiledLayer!.uri!);
await vectorTilesExportTask.load();
// 現在のマップの表示範囲を取得します (この範囲のベクター タイルをダウンロードします)。
final downloadArea = _mapViewController.visibleArea!.extent;
// タスク実行時に使用するパラメーターを作成します。
final exportVectorTilesParameters =
await vectorTilesExportTask.createDefaultExportVectorTilesParameters(
areaOfInterest: downloadArea,
maxScale: _mapViewController.scale,
);
// パラメーターとダウンロード先を設定してタスクのジョブ (ExportVectorTilesJob) を作成します。
_exportVectorTilesJob =
vectorTilesExportTask.exportVectorTilesWithItemResourceCache(
parameters: exportVectorTilesParameters,
vectorTileCacheUri: vtpkFile.uri,
itemResourceCacheUri: Uri.directory(resourceDirectoryPath),
);
11. ExportVectorTilesTask を実行して、ダウンロード処理を開始します。タスクを実行するには、タスクから作成したジョブ (ExportVectorTilesJob) の run メソッドを実行します。ジョブが成功したら、ダウンロードされたベクター タイルから、新たにオフライン用のマップを作成します。
try {
// ジョブを開始して、ジョブの結果を取得します。
final result = await _exportVectorTilesJob?.run();
// ジョブが成功したら、ダウンロードしたベクター タイルとベクター タイルのスタイルを取得します。
final vectorTilesCache = result?.vectorTileCache;
final itemResourceCache = result?.itemResourceCache;
if (vectorTilesCache == null || itemResourceCache == null) {
showErrorDialog('ベクター タイル キャッシュ または アイテム リソース キャッシュが無効です');
return;
}
// ダウンロードしたベクター タイルからベクター タイル レイヤーを作成します。
final localVectorTileLayer = ArcGISVectorTiledLayer.withVectorTileCache(
vectorTilesCache,
itemResourceCache: itemResourceCache,
);
// ベクター タイル レイヤーを追加したベースマップから、オフライン用のマップを作成します。
_mapViewController.arcGISMap = ArcGISMap.withBasemap(
Basemap.withBaseLayer(
localVectorTileLayer,
),
);
} on ArcGISException catch (e) {
showErrorDialog(e.message);
} finally {
_exportVectorTilesJob = null;
}
12. 最後にインジケーターを非表示にします。
// インジケーターを非表示にします。
setState(() {
_loading = false;
});
13. 続いて、エラー メッセージを表示するための showErrorDialog 関数を作成します。
void showErrorDialog(String message) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text('Info', style: Theme.of(context).textTheme.titleMedium),
content: Text(
'ベクター タイルのダウンロードに失敗しました:\n$message',
),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: const Text('閉じる'),
),
],
),
);
}
14. 「オンライン表示」ボタンをタップした時に実行される takeOnline 関数を作成します。現在表示しているマップをオフラインマップからオンラインマップに変更するために、_mapViewController.arcGISMap に _onlineMap 変数を設定します。
void takeOnline() {
_mapViewController.arcGISMap = _onlineMap;
}
15. 今の状態では、_onlineMap が空のため、マップの初期作成時に _onlineMap に現在のマップを保存します。_onMapViewReady 関数の最後に下記を追加します。
void _onMapViewReady() {
・・・省略・・・
_mapViewController.arcGISMap = map;
// 追加
// オンラインマップへの切替用に、現在のマップ状態を保存します。
_onlineMap = map;
}
16. 以上でダウンロード機能の実装は完了です。ビルドして任意の場所をダウンロードしてみてください (現在のマップの表示範囲のデータがダウンロードされます) 。ダウンロードが完了すると、その範囲のデータしか表示されていないことが分かります。
まとめ
本記事では、オフラインマップの表示機能を実装しました。ExportVectorTilesTask や ExportVectorTilesJob では、マップを任意のスケールでダウンロードしたり、ダウンロード処理を途中で停止/再開したり、処理の進捗状況を確認したりといった設定も可能です。 また、今回実装したようにベースマップをインターネットを介してダウンロードする方法以外にも、事前にパッケージ ファイルを作成しておき、そのローカル データをアプリで表示することも可能です。利用可能なオフライン機能については、こちらのページもご覧ください。
関連リンク