Toto je blogový článek představující funkce použitelné při vývoji mapových aplikací ve Flutteru v sérii. Tentokrát se zaměříme na zobrazení offline mapy. Implementujeme funkci stahování mapy do zařízení a její zobrazení offline. V tomto článku se dozvíte,Flutter SDK pro mapy (ArcGIS Maps SDK for Flutter) a ukážeme, jak tuto funkci implementovat.
Ukázkový kód je na GitHubu zveřejněn, takže pokud chcete ihned vyzkoušet, stáhněte si ho. Jak spustit aplikaci je popsáno v README souboru. Pro spuštění ukázky je potřeba získat API klíč (zdarma) . Postup od zavedení SDK a zobrazení mapy najdete v článku „Vytvořme si mapovou aplikaci ve Flutteru!“. Tento článek se zaměřuje na funkci zobrazení offline mapy.
Offline mapa (vrstvy vektorových dlaždic) – vytvoření
Tentokrát vytvoříme funkci stahování dat libovolné oblasti zobrazené na mapě v aplikaci. Nejprve vytvoříme vrstvu vektorových dlaždic, která podporuje stahování pro použití offline.
1. Přihlaste se do ArcGIS Online ArcGIS Online účtu nebo do účtu ArcGIS Location Platform . Pokud účet nemáte, můžete si jej zdarma založit podle postupu zdeの手順から無償で作成できます。
2. こちらのリンクを開き、ArcGIS Basemap Styles サービス (ベクター タイル レイヤー) のリストを表示します。
3. 名前に「(for Developers)」が付いているアイテムの中から、World Street Map (道路地図) や World Topographic Map (地形図) 等の任意のアイテムを選びます。選択したアイテムの名前部分をクリックして、左下に表示される [詳細の表示] をクリックします。
4. 右上に表示される [Vector Tile Style Editor で編集] をクリックします。Vector Tile Style Editor を使用することで、マップの用途に応じてオリジナルの見た目を設定できます。エディターの詳細については、こちらの記事もご覧ください。
5. 左上の [クイック編集] をクリックして、[言語] の設定を英語から日本語に変更します。
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,
);
// Nastavte parametry a cílové umístění stahování a vytvořte úlohu úkolu (ExportVectorTilesJob).
_exportVectorTilesJob =
vectorTilesExportTask.exportVectorTilesWithItemResourceCache(
parameters: exportVectorTilesParameters,
vectorTileCacheUri: vtpkFile.uri,
itemResourceCacheUri: Uri.directory(resourceDirectoryPath),
);
11. Spusťte ExportVectorTilesTask pro zahájení stahování. Pro spuštění úkolu zavolejte metodu (ExportVectorTilesJob) run vytvořenou z úkolu. Po úspěšném dokončení úlohy vytvořte novou offline mapu ze stažených vektorových dlaždic.
try {
// Spusťte úlohu a získejte její výsledek.
final result = await _exportVectorTilesJob?.run();
// Pokud je úloha úspěšná, získejte cache vektorových dlaždic a cache zdrojů položek.
final vectorTilesCache = result?.vectorTileCache;
final itemResourceCache = result?.itemResourceCache;
if (vectorTilesCache == null || itemResourceCache == null) {
showErrorDialog('Cache vektorových dlaždic nebo cache zdrojů položek je neplatná');
return;
}
// Vytvořte vrstvu vektorových dlaždic ze stažených dat.
final localVectorTileLayer = ArcGISVectorTiledLayer.withVectorTileCache(
vectorTilesCache,
itemResourceCache: itemResourceCache,
);
// Vytvořte offline mapu s basemapou obsahující vrstvu vektorových dlaždic.
_mapViewController.arcGISMap = ArcGISMap.withBasemap(
Basemap.withBaseLayer(
localVectorTileLayer,
),
);
} on ArcGISException catch (e) {
showErrorDialog(e.message);
} finally {
_exportVectorTilesJob = null;
}
12. Nakonec skryjte indikátor načítání.
// Skryjte indikátor načítání.
setState(() {
_loading = false;
});
13. Dále vytvořte funkci pro zobrazování chybových zpráv showErrorDialog .
void showErrorDialog(String message) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text('Info', style: Theme.of(context).textTheme.titleMedium),
content: Text(
'Stahování vektorových dlaždic selhalo:\n$message',
),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: const Text('Zavřít'),
),
],
),
);
}
14. Vytvořte funkci takeOnline , která se spustí po klepnutí na tlačítko „Zobrazit online“. Pro přepnutí aktuální mapy z offline na online nastavte do 's _mapViewController.arcGISMap ' proměnnou ' _onlineMap ' .
_mapViewController.arcGISMap = _onlineMap;
}
15. 今の状態では、_onlineMap が空のため、マップの初期作成時に _onlineMap に現在のマップを保存します。_onMapViewReady 関数の最後に下記を追加します。
void _onMapViewReady() {
・・・省略・・・
_mapViewController.arcGISMap = map;
// 追加
// オンラインマップへの切替用に、現在のマップ状態を保存します。
_onlineMap = map;
}
16. 以上でダウンロード機能の実装は完了です。ビルドして任意の場所をダウンロードしてみてください (現在のマップの表示範囲のデータがダウンロードされます) 。ダウンロードが完了すると、その範囲のデータしか表示されていないことが分かります。
まとめ
本記事では、オフラインマップの表示機能を実装しました。ExportVectorTilesTask や ExportVectorTilesJob では、マップを任意のスケールでダウンロードしたり、ダウンロード処理を途中で停止/再開したり、処理の進捗状況を確認したりといった設定も可能です。 また、今回実装したようにベースマップをインターネットを介してダウンロードする方法以外にも、事前にパッケージ ファイルを作成しておき、そのローカル データをアプリで表示することも可能です。利用可能なオフライン機能については、こちらのページもご覧ください。
関連リンク