Skip to content

Commit

Permalink
Prepared for prerelease -dev.6
Browse files Browse the repository at this point in the history
Added `StackTrace` output to `TileLoadingInterceptorResult.error` (converted existing type to Record)
Improved documentation
Updated CHANGELOG
  • Loading branch information
JaffaKetchup committed Oct 24, 2024
1 parent 3d11ef7 commit 17f01c0
Show file tree
Hide file tree
Showing 14 changed files with 79 additions and 43 deletions.
16 changes: 10 additions & 6 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ This allows a new paradigm to be used: stores may now be treated as bulk downloa
Additionally, vector tiles are now supported in theory, as the internal caching/retrieval logic of the specialised `ImageProvider` has been exposed, although it is out of scope to fully implement support for it.

* Improvements to the browse caching logic and customizability
* Added support for using multiple stores simultaneously in the `FMTCTileProvider` (through `FMTCTileProvider.allStores` & `FMTCTileProvider.multipleStores` constructors)
* Added support for using multiple stores simultaneously in the `FMTCTileProvider` (through the `FMTCTileProvider.allStores` & `FMTCTileProvider.multipleStores` constructors)
* Added `FMTCTileProvider.getBytes` method to expose internal caching mechanisms for external use
* Added `BrowseStoreStrategy` for increased control over caching behaviour
* Added 'tile loading interceptor' feature (`FMTCTileProvider.tileLoadingInterceptor`) to track (eg. for debugging and logging) the internal tile loading mechanisms
Expand All @@ -36,21 +36,25 @@ Additionally, vector tiles are now supported in theory, as the internal caching/
* Replaced `FMTCBrowsingErrorHandler` with `BrowsingExceptionHandler`, which may now return bytes to be displayed instead of (re)throwing exception
* Replaced `obscureQueryParams` with more flexible `urlTransformer` (and static `FMTCTileProvider.urlTransformerOmitKeyValues` utility method to provide old behaviour with more customizability) - also applies to bulk downloading in `StoreDownload.startForeground`
* Removed `FMTCTileProviderSettings` & absorbed properties directly into `FMTCTileProvider`
* Performance of the internal tile image provider has been significantly improved when fetching images from the network URL
> There was a significant time loss due to attempting to handle the network request response as a stream of incoming bytes, which allowed for `chunkEvents` to be reported back to Flutter (allowing it to get progress updates on the state of the tile), but meant the bytes had to be collected and built manually. Removing this functionality allows the network requests to use more streamlined 'package:http' methods, which does not expose a stream of incoming bytes, meaning that bytes no longer have to be treated manually. This can save hundreds of milliseconds on tile loading - a significant time save of potentially up to ~50% in some cases!
* Improvements & additions to bulk downloadable `BaseRegion`s
* Improvements, additions, and removals for bulk downloadable `BaseRegion`s
* Added `MultiRegion`, which contains multiple other `BaseRegion`s
* Improved speed (by massive amounts) and accuracy & reduced memory consumption of `CircleRegion`'s tile generation & counting algorithm
* Deprecated `BaseRegion.(maybe)When` - this is easy to perform using a standard pattern-matched switch

And here's some smaller, but still remarkable, changes:
* Changes to bulk downloading
* `DownloadProgress.latestTileEvent` is now nullable

* Performance of the internal tile image provider has been significantly improved when fetching images from the network URL
There was a significant time loss due to attempting to handle the network request response as a stream of incoming bytes, which allowed for `chunkEvents` to be reported back to Flutter (allowing it to get progress updates on the state of the tile), but meant the bytes had to be collected and built manually. Removing this functionality allows the network requests to use more streamlined 'package:http' methods, which does not expose a stream of incoming bytes, meaning that bytes no longer have to be treated manually. This can save hundreds of milliseconds on tile loading - a significant time save of potentially up to ~50% in some cases!
* Exporting stores is now more stable, and has improved documentation
The method now works in a dedicated temporary environment and attempts to perform two different strategies to move/copy-and-delete the result to the specified directory at the end before failing. Improved documentation covers the potential pitfalls of permissions and now recommends exporting to an app directory, then using the system share functionality on some devices. It now also returns the number of exported tiles.

* Removed deprecated remnants from v9.*

* Other generic improvements (performance & stability)
* Other generic improvements (performance, stability, and documentation)

* Brand new example app to (partially!) showcase the new levels of flexibility and customizability

## [9.1.3] - 2024/08/19

Expand Down
4 changes: 0 additions & 4 deletions example/analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
include: ../analysis_options.yaml

analyzer:
exclude:
- old_lib/

linter:
rules:
public_member_api_docs: false
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class _ResultDisplay extends StatelessWidget {
style: const TextStyle(fontWeight: FontWeight.bold),
textAlign: TextAlign.center,
),
if (fmtcResult.error case final error?)
if (fmtcResult.error?.error case final error?)
Text(
error is FMTCBrowsingError
? '`${error.type.name}`'
Expand Down
3 changes: 2 additions & 1 deletion lib/src/backend/backend_access.dart
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ abstract mixin class FMTCBackendAccessThreadSafe {
static FMTCBackendInternalThreadSafe? _internal;

/// Provides access to the thread-seperate backend internals
/// ([FMTCBackendInternalThreadSafe]) globally with some level of access control
/// ([FMTCBackendInternalThreadSafe]) globally with some level of access
/// control
///
/// {@macro fmtc.backend.access}
@meta.internal
Expand Down
1 change: 0 additions & 1 deletion lib/src/backend/impls/objectbox/backend/internal.dart
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,6 @@ class _ObjectBoxBackendImpl implements FMTCObjectBoxBackendInternal {

// Efficienctly forward resulting stream, but add extra debug info to any
// errors
// TODO: verify
yield* controller.stream.handleError(
(err, stackTrace) => Error.throwWithStackTrace(
err,
Expand Down
15 changes: 14 additions & 1 deletion lib/src/bulk_download/external/download_progress.dart
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,20 @@ class DownloadProgress {
isComplete: false,
);

/// The result of the latest attempted tile
/// The result of the latest attempted tile, if applicable
///
/// `null` if the first tile has not yet been downloaded, or the download has
/// completed. The completion of a download may be considered to be reported
/// twice, although only the one with [isComplete] should be taken as the
/// final indicator: the last tile may (or may not) make all the statistics
/// 100%, but it will not set [isComplete]. An event is emitted after the
/// final tile, with no tile set, but [isComplete] set.
///
/// A similar [TileEvent] may be emitted multiple times sequentially, due to
/// fallback reporting (configurable in [StoreDownload.startForeground]). In
/// this event, this is not set `null`, but [TileEvent.isRepeat] will be set.
/// If this flag is set, the tile should not be counted as a new event in any
/// listener.
///
/// {@macro fmtc.tileevent.extraConsiderations}
final TileEvent? latestTileEvent;
Expand Down
8 changes: 4 additions & 4 deletions lib/src/providers/image_provider/image_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ class _FMTCImageProvider extends ImageProvider<_FMTCImageProvider> {
},
);

/// {@macro fmtc.imageProvider.getBytes}
/// {@macro fmtc.tileProvider.getBytes}
static Future<Uint8List> getBytes({
required TileCoordinates coords,
required TileLayer options,
Expand All @@ -82,7 +82,7 @@ class _FMTCImageProvider extends ImageProvider<_FMTCImageProvider> {
final currentTLIR =
provider.tileLoadingInterceptor != null ? _TLIRConstructor._() : null;

void close([Object? error]) {
void close([({Object error, StackTrace stackTrace})? error]) {
finishedLoadingBytes?.call();

if (key != null && error != null) {
Expand Down Expand Up @@ -126,14 +126,14 @@ class _FMTCImageProvider extends ImageProvider<_FMTCImageProvider> {
currentTLIR: currentTLIR,
);
} catch (err, stackTrace) {
close(err);
close((error: err, stackTrace: stackTrace));

if (err is FMTCBrowsingError) {
final handlerResult = provider.errorHandler?.call(err);
if (handlerResult != null) return handlerResult;
}

Error.throwWithStackTrace(err, stackTrace);
rethrow;
}

close();
Expand Down
4 changes: 2 additions & 2 deletions lib/src/providers/image_provider/internal_get_bytes.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Future<Uint8List> _internalGetBytes({
currentTLIR?.hitOrMiss = false;
if (provider.recordHitsAndMisses) {
FMTCBackendAccess.internal.registerHitOrMiss(
storeNames: provider._getSpecifiedStoresOrNull(), // TODO: Verify
storeNames: provider._getSpecifiedStoresOrNull(),
hit: false,
);
}
Expand Down Expand Up @@ -248,7 +248,7 @@ Future<Uint8List> _internalGetBytes({
if (createdIn.isEmpty) return;

FMTCBackendAccess.internal.removeOldestTilesAboveLimit(
storeNames: createdIn.toList(growable: false), // TODO: Verify
storeNames: createdIn.toList(growable: false),
);
});
}
Expand Down
4 changes: 2 additions & 2 deletions lib/src/providers/tile_loading_interceptor/result.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class _TLIRConstructor {
_TLIRConstructor._();

TileLoadingInterceptorResultPath? resultPath;
Object? error;
({Object error, StackTrace stackTrace})? error;
late String networkUrl;
late String storageSuitableUID;
List<String>? existingStores;
Expand Down Expand Up @@ -59,7 +59,7 @@ class TileLoadingInterceptorResult {
///
/// See [didComplete] for a boolean result. If `null`, see [resultPath] for the
/// exact result path.
final Object? error;
final ({Object error, StackTrace stackTrace})? error;

/// Indicates whether the tile completed loading successfully
///
Expand Down
2 changes: 1 addition & 1 deletion lib/src/providers/tile_provider/tile_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ class FMTCTileProvider extends TileProvider {
super.dispose();
}

/// {@template fmtc.imageProvider.getBytes}
/// {@template fmtc.tileProvider.getBytes}
/// Use FMTC's caching logic to get the bytes of the specific tile (at
/// [coords]) with the specified [TileLayer] options and [FMTCTileProvider]
/// provider
Expand Down
2 changes: 2 additions & 0 deletions lib/src/regions/base_region.dart
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ sealed class BaseRegion {

/// Generate the list of all the [LatLng]s forming the outline of this region
///
/// May not be supported on all region implementations.
///
/// Returns a `Iterable<LatLng>` which can be used anywhere.
Iterable<LatLng> toOutline();

Expand Down
21 changes: 19 additions & 2 deletions lib/src/regions/shapes/multi.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,25 @@ part of '../../../../flutter_map_tile_caching.dart';
/// A region formed from multiple other [BaseRegion]s
///
/// When downloading, each sub-region specified in [regions] is downloaded
/// consecutively. Overlaps are not resolved into single regions, so it is
/// recommended to enable `skipExistingTiles` in [StoreDownload.startForeground].
/// consecutively. The advantage of [MultiRegion] is that:
///
/// * it avoids repeating the expensive setup and teardown of a bulk download
/// between each sub-region
/// * the progress of the download is reported as a whole, so no additional
/// work is required to keep track of which download is currently being
/// performed and keep track of custom progress statistics
///
/// Overlaps and intersections are not (yet) compiled into single
/// [CustomPolygonRegion]s. Therefore, where regions are known to overlap:
///
/// * (particularly where regions are [RectangleRegion]s & [CustomPolygonRegion]s)
/// Use ['package:polybool'](https://pub.dev/packages/polybool) (a 3rd party
/// package in no way associated with FMTC) to take the `union` all polygons:
/// this will remove self-intersections, combine overlapping polygons into
/// single polygons, etc - this is best for efficiency.
///
/// * (particularly where multiple different other region types are used)
/// Enable `skipExistingTiles` in [StoreDownload.startForeground].
///
/// [MultiRegion]s may be nested.
///
Expand Down
30 changes: 17 additions & 13 deletions lib/src/store/download.dart
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,19 @@ class StoreDownload {
/// recovery session by default
///
/// > [!TIP]
/// > To check the number of tiles in a region before starting a download, use
/// > To count the number of tiles in a region before starting a download, use
/// > [check].
///
/// Streams a [DownloadProgress] object containing statistics and information
/// about the download's progression status, once per tile and at intervals
/// of no longer than [maxReportInterval] (after the first tile).
/// of no longer than [maxReportInterval].
///
/// The first event reports the download has started (setup is complete). The
/// last event indicates the download has completed (either sucessfully or
/// been cancelled). All events between these two emissions will be reporting
/// a new tile, or reporting the old tile, due to [maxReportInterval].
/// See the documentation on [DownloadProgress.latestTileEvent] &
/// [TileEvent.isRepeat] for more details.
///
/// ---
///
Expand Down Expand Up @@ -128,33 +135,31 @@ class StoreDownload {
UrlTransformer? urlTransformer,
Object instanceId = 0,
}) async* {
FMTCBackendAccess.internal; // Verify intialisation
FMTCBackendAccess.internal; // Verify initialisation

// Check input arguments for suitability
if (!(region.options.wmsOptions != null ||
region.options.urlTemplate != null)) {
throw ArgumentError(
"`.toDownloadable`'s `TileLayer` argument must specify an appropriate `urlTemplate` or `wmsOptions`",
"`.toDownloadable`'s `TileLayer` argument must specify an appropriate "
'`urlTemplate` or `wmsOptions`',
'region.options.urlTemplate',
);
}

if (parallelThreads < 1) {
throw ArgumentError.value(
parallelThreads,
'parallelThreads',
'must be 1 or greater',
);
}

if (maxBufferLength < 0) {
throw ArgumentError.value(
maxBufferLength,
'maxBufferLength',
'must be 0 or greater',
);
}

if ((rateLimit ?? 2) < 1) {
throw ArgumentError.value(
rateLimit,
Expand Down Expand Up @@ -263,14 +268,13 @@ class StoreDownload {
cancelCompleter.complete();
}

/// Check how many downloadable tiles are within a specified region
///
/// This does not include skipped sea tiles or skipped existing tiles, as those
/// are handled during download only.
/// Count the number of tiles within the specified region
///
/// Note that this does not require a valid/ready/existing store.
/// This does not include skipped sea tiles or skipped existing tiles, as
/// those are handled during a download (as the contents must be known).
///
/// Returns the number of tiles.
/// Note that this does not require an existing/ready store, or a sensical
/// [DownloadableRegion.options].
Future<int> check(DownloadableRegion region) => compute(
(region) => region.when(
rectangle: TileCounters.rectangleTiles,
Expand Down
10 changes: 5 additions & 5 deletions pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name: flutter_map_tile_caching
description: Plugin for 'flutter_map' providing advanced caching functionality,
with ability to download map regions for offline use.
version: 10.0.0-dev.5
version: 10.0.0-dev.6

repository: https://github.com/JaffaKetchup/flutter_map_tile_caching
issue_tracker: https://github.com/JaffaKetchup/flutter_map_tile_caching/issues
Expand Down Expand Up @@ -37,14 +37,14 @@ dependencies:
http: ^1.2.2
latlong2: ^0.9.1
meta: ^1.15.0
objectbox: ^4.0.2
objectbox_flutter_libs: ^4.0.2
objectbox: ^4.0.3
objectbox_flutter_libs: ^4.0.3
path: ^1.9.0
path_provider: ^2.1.4

dev_dependencies:
build_runner: ^2.4.12
objectbox_generator: ^4.0.2
build_runner: ^2.4.13
objectbox_generator: ^4.0.3
test: ^1.25.8

flutter: null
Expand Down

0 comments on commit 17f01c0

Please sign in to comment.