Initial commit
This commit is contained in:
126
lib/flutter_map_mbtiles_provider.dart
Normal file
126
lib/flutter_map_mbtiles_provider.dart
Normal file
@@ -0,0 +1,126 @@
|
||||
library flutter_map_mbtiles_provider;
|
||||
|
||||
import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_map/flutter_map.dart';
|
||||
// import 'package:flutter_map/src/layer/tile_provider/tile_provider.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:sqflite/sqflite.dart';
|
||||
|
||||
class MBTilesImageProvider extends TileProvider {
|
||||
final String asset;
|
||||
final File mbtilesFile;
|
||||
|
||||
Future<Database> database;
|
||||
Database _loadedDb;
|
||||
bool isDisposed = false;
|
||||
|
||||
MBTilesImageProvider._({this.asset, this.mbtilesFile}) {
|
||||
database = _loadMBTilesDatabase();
|
||||
}
|
||||
|
||||
factory MBTilesImageProvider.fromAsset(String asset) => MBTilesImageProvider._(asset: asset);
|
||||
|
||||
factory MBTilesImageProvider.fromFile(File mbtilesFile) => MBTilesImageProvider._(mbtilesFile: mbtilesFile);
|
||||
|
||||
Future<Database> _loadMBTilesDatabase() async {
|
||||
if (_loadedDb == null) {
|
||||
var file = mbtilesFile ?? await copyFileFromAssets();
|
||||
|
||||
_loadedDb = await openDatabase(file.path);
|
||||
|
||||
if (isDisposed) {
|
||||
await _loadedDb.close();
|
||||
_loadedDb = null;
|
||||
throw Exception('Tileprovider is already disposed');
|
||||
}
|
||||
}
|
||||
|
||||
return _loadedDb;
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
if (_loadedDb != null) {
|
||||
_loadedDb.close();
|
||||
_loadedDb = null;
|
||||
}
|
||||
isDisposed = true;
|
||||
}
|
||||
|
||||
Future<File> copyFileFromAssets() async {
|
||||
var tempDir = await getTemporaryDirectory();
|
||||
var filename = asset.split('/').last;
|
||||
var file = File('${tempDir.path}/$filename');
|
||||
|
||||
var data = await rootBundle.load(asset);
|
||||
file.writeAsBytesSync(data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes), flush: true);
|
||||
return file;
|
||||
}
|
||||
|
||||
@override
|
||||
ImageProvider getImage(Coords<num> coords, TileLayerOptions options) {
|
||||
var x = coords.x.round();
|
||||
var y = options.tms ? invertY(coords.y.round(), coords.z.round()) : coords.y.round();
|
||||
var z = coords.z.round();
|
||||
|
||||
return MBTileImage(
|
||||
database,
|
||||
Coords<int>(x, y)..z = z,
|
||||
mbtilesFile?.path ?? asset,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class MBTileImage extends ImageProvider<MBTileImage> {
|
||||
final Future<Database> database;
|
||||
final Coords<int> coords;
|
||||
final String filePath;
|
||||
|
||||
MBTileImage(this.database, this.coords, this.filePath);
|
||||
|
||||
@override
|
||||
ImageStreamCompleter load(MBTileImage key, decode) {
|
||||
return MultiFrameImageStreamCompleter(
|
||||
codec: _loadAsync(key),
|
||||
scale: 1,
|
||||
informationCollector: () sync* {
|
||||
yield DiagnosticsProperty<ImageProvider>('Image provider', this);
|
||||
yield DiagnosticsProperty<ImageProvider>('Image key', key);
|
||||
});
|
||||
}
|
||||
|
||||
Future<Codec> _loadAsync(MBTileImage key) async {
|
||||
assert(key == this);
|
||||
|
||||
final db = await key.database;
|
||||
List<Map> result = await db.rawQuery('select tile_data from tiles '
|
||||
'where zoom_level = ${coords.z} AND '
|
||||
'tile_column = ${coords.x} AND '
|
||||
'tile_row = ${coords.y} limit 1');
|
||||
final Uint8List bytes = result.isNotEmpty ? result.first['tile_data'] : null;
|
||||
|
||||
if (bytes == null) {
|
||||
return Future<Codec>.error('Failed to load tile for coords: $coords');
|
||||
}
|
||||
return await PaintingBinding.instance.instantiateImageCodec(bytes);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MBTileImage> obtainKey(ImageConfiguration configuration) {
|
||||
return SynchronousFuture(this);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => coords.hashCode;
|
||||
|
||||
@override
|
||||
bool operator ==(other) {
|
||||
return other is MBTileImage && coords == other.coords && filePath == other.filePath;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user