flutter_map_tile_caching
flutter_map_tile_caching copied to clipboard
[FEATURE] Add ability to retrieve raw bytes from internal `ImageProvider` (to enable partial compatibilty with vector tiles)
What do you want implemented?
I would be able to use vector tiles (.pbf) instead of Raster Tiles (.png). Indeed my company is rendering our map ourselves, and Vector tile takes much less computation power to render.
The idea is to be able to use url links like : "https://tiles.stadiamaps.com/data/openmaptiles/{z}/{x}/{y}.pbf?"
To provide the style in a json like file, and to have all the features the library already provide (cache & bulk dowloading).
What other alternatives are available?
There is already another library that handle Vector tiles, but the way it works I am pretty sure it is not compatible with this one.
I'll be happy to be wrong and that both library could be used at the same time.
The library is : https://github.com/greensopinion/flutter-vector-map-tiles
It does handle caching, but not bulk dowloading.
Can you provide any other information?
No response
Platforms Affected
Android, iOS
Severity
Annoying: Currently have to use workarounds
Requirements
- [X] I agree to follow this project's Code of Conduct
- [X] I am using the latest stable version of this package
- [X] I have checked for similar feature requests which may be duplicates
Hi @GaelleJoubert, That package is a plugin for FM, they are designed to work together.
@JaffaKetchup Thanks you for your answer.
I juste don't see yet how I would be able to make flutter-vector-map-tiles & flutter_map-tile-caching work together ...
Indeed, from what I see, flutter-vector-map-tiles provide a VectorTileLayer meant to be used instead of a TileLayer this way :
FlutterMap(
options: MapOptions(
center: LatLng(49.246292, -123.116226),
zoom: 10,
maxZoom: 15),
children: [
VectorTileLayer(
theme: _mapTheme()
tileProviders: TileProviders(
{'openmaptiles': _cachingTileProvider(_urlTemplate())}),
)
],
));
But the TileProvider it takes as an argument is a custum object, and not the same object that a TileLayer would take. So I cannot use the FMTC provider that I usually use this way in a TileLayer :
TileLayer(
tileProvider: storeMap.getTileProvider(FMTCProviderSettings),
urlTemplate: "https://tile.map.api-k.com/styles/topo/{z}/{x}/{y}.png",
),
Do you see a way to use both plugging together ? If yes I'll be happy to know how you would do. Thanks you very much for the help.
Ah my apologies @GaelleJoubert, I didn't see a reference to FMTC, so I assumed you were talking about FM.
Indeed they are incompatible.
Ah, yeah I thought so ... And there is no way, for now, to use Vector tiles with this plugging?
No way for now. PRs are always welcome (use 'next-version' branch if you do) :), but I'm unlikely to work to implement this.
PS. My fault for missing your actual point before, didn't realise this was an issue in my repo instead of FM. Apologies :)
Is there still plan to make this map_tile_caching work with vector_map_tile ?
There is nothing in the infrastructure and design that would be incompatible with vector tiles: they are bytes just like any other. Theoretically, all that is required is a compatible tile provider. I'll see what I can do to allow users to define this, because I don't want to add the dependency.
@JaffaKetchup I just tried but it doesn't seem as easy as you make it sound :) A tile provider doesn't provide bytes but an ImageProvider. Bytes are, actually, interpreted here:
https://github.com/JaffaKetchup/flutter_map_tile_caching/blob/dfee86c092fc2541b972632da626191cddbc1317/lib/src/providers/image_provider.dart#L97
To get the bytes from the store, FMTCBackendInternal.readTile() needs to be used - which not only isn't accessible, but would also mean that almost all features of FMTC are gone.
If there was a method getTileBytes(), an adapter could be implemented like this:
import 'dart:typed_data';
import 'package:flutter_map/flutter_map.dart';
import 'package:flutter_map_tile_caching/flutter_map_tile_caching.dart';
import 'package:vector_map_tiles/vector_map_tiles.dart';
import 'package:zs_connect/map/map.dart';
class FmtcVectorTileProvider extends VectorTileProvider {
final tileProvider = const FMTCStore(mapStoreName).getTileProvider();
@override
final int maximumZoom;
@override
final int minimumZoom;
final TileLayer tileLayer;
FmtcVectorTileProvider({
required String urlTemplate,
this.maximumZoom = 16,
this.minimumZoom = 1,
}) : tileLayer = TileLayer(urlTemplate: urlTemplate);
@override
Future<Uint8List> provide(TileIdentity tile) async {
final coordinates = TileCoordinates(tile.x, tile.y, tile.z);
// If there was a method that returned bytes, it could be called here
tileProvider.getTileBytes(coordinates, tileLayer);
}
}
What do you think? Did I miss something?
@micheljung You can try following something like this for now: https://github.com/Baseflow/flutter_cached_network_image/issues/714#issuecomment-1072493110. This will load the bytes from the ImageProvider. There's probably also other ways to do similar things.
Not sure how efficient/performant this is though!
I can look into adding a getBytes method to the provider, it might make sense.
Thank you for your quick reaction @JaffaKetchup. I tried this solution, but it seems like the Image detour corrupts the bytes so they can't be parsed as vector data anymore.
I'll give up at this point for now. For anyone interested, here's what I tried.
import 'dart:async';
import 'dart:typed_data';
import 'dart:ui';
import 'package:flutter/cupertino.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:flutter_map_tile_caching/flutter_map_tile_caching.dart';
import 'package:vector_map_tiles/vector_map_tiles.dart';
import 'package:zs_connect/map/map.dart';
class FmtcVectorTileProvider extends VectorTileProvider {
final tileProvider = const FMTCStore(mapStoreName).getTileProvider();
final VectorTileProvider delegate;
@override
int get maximumZoom => delegate.maximumZoom;
@override
int get minimumZoom => delegate.minimumZoom;
final TileLayer tileLayer;
FmtcVectorTileProvider({
required this.delegate,
required String urlTemplate,
}) : tileLayer = TileLayer(urlTemplate: urlTemplate);
@override
Future<Uint8List> provide(TileIdentity tile) async {
final coordinates = TileCoordinates(tile.x, tile.y, tile.z);
return await tileProvider.getImage(coordinates, tileLayer).getBytes();
}
}
extension ImageTool on ImageProvider {
Future<Uint8List> getBytes(
// also tried rawRgba
{ImageByteFormat format = ImageByteFormat.rawUnmodified}) async {
final Completer<Uint8List> completer = Completer<Uint8List>();
final ImageStreamListener listener = ImageStreamListener(
(imageInfo, synchronousCall) async {
final bytes = await imageInfo.image.toByteData(format: format);
if (!completer.isCompleted) {
completer.complete(bytes?.buffer.asUint8List());
}
},
);
final imageStream = resolve(ImageConfiguration.empty);
imageStream.addListener(listener);
final imageBytes = await completer.future;
imageStream.removeListener(listener);
return imageBytes;
}
}
[ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: InvalidProtocolBufferException: CodedBufferReader encountered a malformed varint.
#0 CodedBufferReader._readRawVarint32 (package:protobuf/src/protobuf/coded_buffer_reader.dart:232:5)
#1 CodedBufferReader.readUint32 (package:protobuf/src/protobuf/coded_buffer_reader.dart:122:23)
#2 CodedBufferReader.readTag (package:protobuf/src/protobuf/coded_buffer_reader.dart:161:16)
#3 _mergeFromCodedBufferReader (package:protobuf/src/protobuf/coded_buffer.dart:37:23)
#4 GeneratedMessage.mergeFromBuffer (package:protobuf/src/protobuf/generated_message.dart:192:5)
#5 new VectorTile.fromBuffer (package:vector_tile/raw/proto/vector_tile.pb.dart:340:128)
#6 VectorTile.fromBytes (package:vector_tile/vector_tile.dart:24:33)
#7 VectorTileReader.read.<anonymous closure> (package:vector_tile_renderer/src/vector_tile_reader.dart:10:25)
#8 Timeline.timeSync (dart:developer/timeline.dart:173:22)
#9 profileSync (package:vector_tile_renderer/src/profiling.dart:16:19)
#10 VectorTileReader.read (package:vector_tile_renderer/src/vector_tile_reader.dart:9:12)
#11 _createTile (package:vector_map_tiles/src/cache/vector_tile_loading_cache.dart:139:44)
#12 _Job.apply (package:executor_lib/src/queue_executor.dart:97:59)
#13 QueueExecutor._runOneAndReschedule (package:executor_lib/src/queue_executor.dart:52:34)
#14 _microtaskLoop (dart:async/schedule_microtask.dart:40:21)
#15 _startMicrotaskLoop (dart:async/schedule_microtask.dart:49:5)
I suppose that makes sense. I've re-opened this, as your suggestion would be relatively easy and shouldn't be exposing too much of the internals.