Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FEATURE] Add ability to retrieve raw bytes from internal ImageProvider (to enable partial compatibilty with vector tiles) #84

Closed
3 tasks done
GaelleJoubert opened this issue Jan 18, 2023 · 11 comments · Fixed by #156
Labels
feature This issue requests a new feature

Comments

@GaelleJoubert
Copy link

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

@GaelleJoubert GaelleJoubert added the feature This issue requests a new feature label Jan 18, 2023
@JaffaKetchup
Copy link
Owner

Hi @GaelleJoubert,
That package is a plugin for FM, they are designed to work together.

@GaelleJoubert
Copy link
Author

@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.

@JaffaKetchup
Copy link
Owner

Ah my apologies @GaelleJoubert, I didn't see a reference to FMTC, so I assumed you were talking about FM.

Indeed they are incompatible.

@GaelleJoubert
Copy link
Author

Ah, yeah I thought so ...
And there is no way, for now, to use Vector tiles with this plugging?

@JaffaKetchup
Copy link
Owner

JaffaKetchup commented Jan 18, 2023

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 :)

@JaffaKetchup JaffaKetchup added the won't fix This issue will not be resolved label Jan 18, 2023
@JaffaKetchup JaffaKetchup closed this as not planned Won't fix, can't repro, duplicate, stale Jan 18, 2023
@supagon
Copy link

supagon commented Mar 29, 2024

Is there still plan to make this map_tile_caching work with vector_map_tile ?

@JaffaKetchup
Copy link
Owner

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.

@micheljung
Copy link

@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:

return decode(await ImmutableBuffer.fromUint8List(bytes));

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?

@JaffaKetchup
Copy link
Owner

@micheljung You can try following something like this for now: Baseflow/flutter_cached_network_image#714 (comment). 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.

@micheljung
Copy link

micheljung commented May 1, 2024

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)

@JaffaKetchup JaffaKetchup changed the title [FEATURE] Ability to use Vector Tiles [FEATURE] Add ability to retrieve raw bytes from internal ImageProvider (to enable partial compatibilty with vector tiles) May 1, 2024
@JaffaKetchup JaffaKetchup removed the won't fix This issue will not be resolved label May 1, 2024
@JaffaKetchup JaffaKetchup reopened this May 1, 2024
@JaffaKetchup
Copy link
Owner

JaffaKetchup commented May 1, 2024

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature This issue requests a new feature
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants