Este es un artículo de blog en serie que presenta funciones útiles para el desarrollo de aplicaciones de mapas con Flutter. En esta ocasión, se trata de la visualización de mapas sin conexión. Implementaremos la función para descargar mapas en el dispositivo y mostrarlos sin conexión. En este artículo,Flutter SDK de mapas para (ArcGIS Maps SDK for Flutter) se utiliza para mostrar cómo implementar esta función.
El código de ejemplo está disponible en GitHub , así que si quieres revisarlo rápidamente, descárgalo. Las instrucciones para ejecutar la aplicación están descritas en README . Para ejecutar el ejemplo, necesitarás obtener una clave API (gratuita) . Para la instalación del SDK de mapas y la visualización del mapa, consulta el artículo del blog "SDK de mapas para Flutter". En este artículo nos centraremos en la función de visualización de mapas sin conexión.
Mapa sin conexión (capa vectorial de teselas) creación
En esta ocasión, crearemos una función para descargar datos del área deseada del mapa que se muestra en la aplicación. Primero, crearemos una capa vectorial de teselas compatible con descarga para uso sin conexión.
1. Inicia sesión en ArcGIS Online
2. Abre el enlace
3. De los elementos que tienen "(for Developers)" en el nombre, selecciona cualquier elemento como "World Street Map (mapa vial)" o "World Topographic Map (mapa topográfico)". Haz clic en el nombre del elemento seleccionado y luego haz clic en [Mostrar detalles] que aparece en la parte inferior izquierda.[Mostrar detalles] .
4. Haz clic en [Editar con Vector Tile Style Editor] que aparece en la parte superior derecha.[Vector Tile Style Editor editar con] .Vector Tile Style Editor, te permite personalizar la apariencia original según el uso del mapa. Para más detalles sobre el editor, consulta también el artículo aquí.
5. Haz clic en [Edición rápida] en la parte superior izquierda y cambia la configuración del [Idioma] de inglés a japonés.[Edición rápida] . Cambia el ajuste [Idioma] a japonés desde inglés.[Idioma] .
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,
);
// Configura los parámetros y el destino de descarga para crear la tarea (ExportVectorTilesJob).
_exportVectorTilesJob =
vectorTilesExportTask.exportVectorTilesWithItemResourceCache(
parameters: exportVectorTilesParameters,
vectorTileCacheUri: vtpkFile.uri,
itemResourceCacheUri: Uri.directory(resourceDirectoryPath),
);
11. Ejecuta ExportVectorTilesTask para iniciar la descarga. Para ejecutar la tarea, llama al método (ExportVectorTilesJob) creado desde la tarea. run . Si el trabajo tiene éxito, crea un nuevo mapa offline a partir de los tiles vectoriales descargados.
try {
// Inicia el trabajo y obtiene el resultado.
final result = await _exportVectorTilesJob?.run();
// Si el trabajo es exitoso, obtiene la caché de tiles vectoriales y los recursos del ítem descargados.
final vectorTilesCache = result?.vectorTileCache;
final itemResourceCache = result?.itemResourceCache;
if (vectorTilesCache == null || itemResourceCache == null) {
showErrorDialog('La caché de tiles vectoriales o la caché de recursos del ítem no son válidas');
return;
}
// Crea una capa de tiles vectoriales a partir de los tiles descargados.
final localVectorTileLayer = ArcGISVectorTiledLayer.withVectorTileCache(
vectorTilesCache,
itemResourceCache: itemResourceCache,
);
// Crea un mapa offline con el basemap que incluye la capa de tiles vectoriales.
_mapViewController.arcGISMap = ArcGISMap.withBasemap(
Basemap.withBaseLayer(
localVectorTileLayer,
),
);
} on ArcGISException catch (e) {
showErrorDialog(e.message);
} finally {
_exportVectorTilesJob = null;
}
12. Finalmente, oculta el indicador.
// Oculta el indicador.
setState(() {
_loading = false;
});
13. A continuación, crea la función para mostrar mensajes de error llamada showErrorDialog .
void showErrorDialog(String message) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text('Info', style: Theme.of(context).textTheme.titleMedium),
content: Text(
'No se pudo descargar el tile vectorial:\n$message',
),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: const Text('Cerrar'),
),
],
),
);
}
14. Crea la función takeOnline , que se ejecuta al pulsar el botón "Mostrar en línea". Para cambiar el mapa mostrado de offline a online, asigna la variable ' _mapViewController.arcGISMap ' con ' _onlineMap ' .
void takeOnline() {
_mapViewController.arcGISMap = _onlineMap;
}
15. Como ahora _onlineMap が空のため、マップの初期作成時に _onlineMap に現在のマップを保存します。_onMapViewReady 関数の最後に下記を追加します。
void _onMapViewReady() {
・・・省略・・・
_mapViewController.arcGISMap = map;
// 追加
// オンラインマップへの切替用に、現在のマップ状態を保存します。
_onlineMap = map;
}
16. 以上でダウンロード機能の実装は完了です。ビルドして任意の場所をダウンロードしてみてください (現在のマップの表示範囲のデータがダウンロードされます) 。ダウンロードが完了すると、その範囲のデータしか表示されていないことが分かります。
まとめ
本記事では、オフラインマップの表示機能を実装しました。ExportVectorTilesTask や ExportVectorTilesJob では、マップを任意のスケールでダウンロードしたり、ダウンロード処理を途中で停止/再開したり、処理の進捗状況を確認したりといった設定も可能です。 また、今回実装したようにベースマップをインターネットを介してダウンロードする方法以外にも、事前にパッケージ ファイルを作成しておき、そのローカル データをアプリで表示することも可能です。利用可能なオフライン機能については、こちらのページもご覧ください。
関連リンク