Skip to content

Commit f78df7a

Browse files
authored
Fixed LineRegion tile generating bugs (#132)
Former-commit-id: 17aeab35399fca0223356b2b581245f3d7863431 [formerly cd1245f] Former-commit-id: 90c8e2ac3b6e5f899ad5be1dd693fea1f8726cad
1 parent d665b71 commit f78df7a

File tree

9 files changed

+75
-82
lines changed

9 files changed

+75
-82
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ Many thanks to my sponsors, no matter how much or how little they donated. Spons
1313

1414
# Changelog
1515

16+
## [8.0.1] - 2023/05/05
17+
18+
* Fixed bugs when generating tiles for `LineRegion`
19+
1620
## [8.0.0] - 2023/XX/XX
1721

1822
* Bulk downloading has been rewritten to use a new implementation that generates tile coordinates at the same time as downloading tiles

lib/src/bulk_download/downloader.dart

+7-3
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ Future<Stream<TileProgress>> bulkDownloader({
4141
: region.type == RegionType.circle
4242
? TilesGenerator.circleTiles
4343
: TilesGenerator.lineTiles,
44-
{'sendPort': recievePort.sendPort, ...generateTileLoopsInput(region)},
44+
{'sendPort': recievePort.sendPort, 'region': region},
4545
onExit: recievePort.sendPort,
4646
);
4747
final tileQueue = StreamQueue(
@@ -64,7 +64,7 @@ Future<Stream<TileProgress>> bulkDownloader({
6464
while (true) {
6565
requestTilePort.send(null);
6666

67-
final List<int>? value;
67+
final List<num>? value;
6868
try {
6969
value = await tileQueue.next;
7070
// ignore: avoid_catching_errors
@@ -100,7 +100,11 @@ Future<Stream<TileProgress>> bulkDownloader({
100100
break;
101101
}
102102

103-
final coord = TileCoordinates(value[0], value[1], value[2]);
103+
final coord = TileCoordinates(
104+
value[0].toInt(),
105+
value[1].toInt(),
106+
value[2].toInt(),
107+
);
104108

105109
final url = provider.getTileUrl(coord, region.options);
106110
final existingTile = await tiles.tiles.get(DatabaseTools.hash(url));

lib/src/bulk_download/tile_loops/count.dart

+22-31
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,17 @@
44
part of 'shared.dart';
55

66
class TilesCounter {
7-
static int rectangleTiles(Map<String, dynamic> input) {
8-
final LatLngBounds bounds = input['rectOutline'];
9-
final int minZoom = input['minZoom'];
10-
final int maxZoom = input['maxZoom'];
11-
final Crs crs = input['crs'];
12-
final CustomPoint<double> tileSize = input['tileSize'];
7+
static int rectangleTiles(DownloadableRegion region) {
8+
final tileSize = _getTileSize(region);
9+
final bounds = (region.originalRegion as RectangleRegion).bounds;
1310

1411
int numberOfTiles = 0;
15-
for (int zoomLvl = minZoom; zoomLvl <= maxZoom; zoomLvl++) {
16-
final CustomPoint<num> nwCustomPoint = crs
12+
for (int zoomLvl = region.minZoom; zoomLvl <= region.maxZoom; zoomLvl++) {
13+
final CustomPoint<num> nwCustomPoint = region.crs
1714
.latLngToPoint(bounds.northWest, zoomLvl.toDouble())
1815
.unscaleBy(tileSize)
1916
.floor();
20-
final CustomPoint<num> seCustomPoint = crs
17+
final CustomPoint<num> seCustomPoint = region.crs
2118
.latLngToPoint(bounds.southEast, zoomLvl.toDouble())
2219
.unscaleBy(tileSize)
2320
.ceil() -
@@ -29,30 +26,27 @@ class TilesCounter {
2926
return numberOfTiles;
3027
}
3128

32-
static int circleTiles(Map<String, dynamic> input) {
29+
static int circleTiles(DownloadableRegion region) {
3330
// This took some time and is fairly complicated, so this is the overall explanation:
3431
// 1. Given a `LatLng` for every x degrees on a circle's circumference, convert it into a tile number
3532
// 2. Using a `Map` per zoom level, record all the X values in it without duplicates
3633
// 3. Under the previous record, add all the Y values within the circle (ie. to opposite the X value)
3734
// 4. Loop over these XY values and add them to the list
3835
// Theoretically, this could have been done using the same method as `lineTiles`, but `lineTiles` was built after this algorithm and this makes more sense for a circle
3936

40-
final List<LatLng> circleOutline = input['circleOutline'];
41-
final int minZoom = input['minZoom'];
42-
final int maxZoom = input['maxZoom'];
43-
final Crs crs = input['crs'];
44-
final CustomPoint<double> tileSize = input['tileSize'];
37+
final tileSize = _getTileSize(region);
38+
final circleOutline = region.originalRegion.toOutline();
4539

4640
// Format: Map<z, Map<x, List<y>>>
4741
final Map<int, Map<int, List<int>>> outlineTileNums = {};
4842

4943
int numberOfTiles = 0;
5044

51-
for (int zoomLvl = minZoom; zoomLvl <= maxZoom; zoomLvl++) {
45+
for (int zoomLvl = region.minZoom; zoomLvl <= region.maxZoom; zoomLvl++) {
5246
outlineTileNums[zoomLvl] = <int, List<int>>{};
5347

5448
for (final LatLng node in circleOutline) {
55-
final CustomPoint<num> tile = crs
49+
final CustomPoint<num> tile = region.crs
5650
.latLngToPoint(node, zoomLvl.toDouble())
5751
.unscaleBy(tileSize)
5852
.floor();
@@ -82,7 +76,7 @@ class TilesCounter {
8276
return numberOfTiles;
8377
}
8478

85-
static int lineTiles(Map<String, dynamic> input) {
79+
static int lineTiles(DownloadableRegion region) {
8680
// This took some time and is fairly complicated, so this is the overall explanation:
8781
// 1. Given 4 `LatLng` points, create a 'straight' rectangle around the 'rotated' rectangle, that can be defined with just 2 `LatLng` points
8882
// 2. Convert the straight rectangle into tile numbers, and loop through the same as `rectangleTiles`
@@ -130,16 +124,13 @@ class TilesCounter {
130124
return true;
131125
}
132126

133-
final List<List<LatLng>> rects = input['lineOutline'];
134-
final int minZoom = input['minZoom'];
135-
final int maxZoom = input['maxZoom'];
136-
final Crs crs = input['crs'];
137-
final CustomPoint<double> tileSize = input['tileSize'];
127+
final tileSize = _getTileSize(region);
128+
final lineOutline = (region.originalRegion as LineRegion).toOutlines(1);
138129

139130
int numberOfTiles = 0;
140131

141-
for (int zoomLvl = minZoom; zoomLvl <= maxZoom; zoomLvl++) {
142-
for (final List<LatLng> rect in rects) {
132+
for (int zoomLvl = region.minZoom; zoomLvl <= region.maxZoom; zoomLvl++) {
133+
for (final rect in lineOutline) {
143134
final LatLng rrBottomLeft = rect[0];
144135
final LatLng rrBottomRight = rect[1];
145136
final LatLng rrTopRight = rect[2];
@@ -158,34 +149,34 @@ class TilesCounter {
158149
rrBottomRight.longitude,
159150
];
160151

161-
final CustomPoint<num> rrNorthWest = crs
152+
final CustomPoint<num> rrNorthWest = region.crs
162153
.latLngToPoint(rrTopLeft, zoomLvl.toDouble())
163154
.unscaleBy(tileSize)
164155
.floor();
165-
final CustomPoint<num> rrNorthEast = crs
156+
final CustomPoint<num> rrNorthEast = region.crs
166157
.latLngToPoint(rrTopRight, zoomLvl.toDouble())
167158
.unscaleBy(tileSize)
168159
.ceil() -
169160
const CustomPoint(1, 0);
170-
final CustomPoint<num> rrSouthWest = crs
161+
final CustomPoint<num> rrSouthWest = region.crs
171162
.latLngToPoint(rrBottomLeft, zoomLvl.toDouble())
172163
.unscaleBy(tileSize)
173164
.ceil() -
174165
const CustomPoint(0, 1);
175-
final CustomPoint<num> rrSouthEast = crs
166+
final CustomPoint<num> rrSouthEast = region.crs
176167
.latLngToPoint(rrBottomRight, zoomLvl.toDouble())
177168
.unscaleBy(tileSize)
178169
.ceil() -
179170
const CustomPoint(1, 1);
180171

181-
final CustomPoint<num> srNorthWest = crs
172+
final CustomPoint<num> srNorthWest = region.crs
182173
.latLngToPoint(
183174
LatLng(rrAllLat.max, rrAllLon.min),
184175
zoomLvl.toDouble(),
185176
)
186177
.unscaleBy(tileSize)
187178
.floor();
188-
final CustomPoint<num> srSouthEast = crs
179+
final CustomPoint<num> srSouthEast = region.crs
189180
.latLngToPoint(
190181
LatLng(rrAllLat.min, rrAllLon.max),
191182
zoomLvl.toDouble(),

lib/src/bulk_download/tile_loops/generate.dart

+38-33
Original file line numberDiff line numberDiff line change
@@ -6,23 +6,24 @@ part of 'shared.dart';
66
class TilesGenerator {
77
static Future<void> rectangleTiles(Map<String, dynamic> input) async {
88
final SendPort sendPort = input['sendPort'];
9-
final LatLngBounds bounds = input['rectOutline'];
10-
final int minZoom = input['minZoom'];
11-
final int maxZoom = input['maxZoom'];
12-
final Crs crs = input['crs'];
13-
final CustomPoint<double> tileSize = input['tileSize'];
9+
final DownloadableRegion region = input['region'];
10+
final originalRegion = region.originalRegion as RectangleRegion;
11+
12+
final tileSize = _getTileSize(region);
13+
final northWest = originalRegion.bounds.northWest;
14+
final southEast = originalRegion.bounds.southEast;
1415

1516
final recievePort = ReceivePort();
1617
sendPort.send(recievePort.sendPort);
1718
final requestQueue = StreamQueue(recievePort);
1819

19-
for (int zoomLvl = minZoom; zoomLvl <= maxZoom; zoomLvl++) {
20-
final CustomPoint<num> nwCustomPoint = crs
21-
.latLngToPoint(bounds.northWest, zoomLvl.toDouble())
20+
for (int zoomLvl = region.minZoom; zoomLvl <= region.maxZoom; zoomLvl++) {
21+
final CustomPoint<num> nwCustomPoint = region.crs
22+
.latLngToPoint(northWest, zoomLvl.toDouble())
2223
.unscaleBy(tileSize)
2324
.floor();
24-
final CustomPoint<num> seCustomPoint = crs
25-
.latLngToPoint(bounds.southEast, zoomLvl.toDouble())
25+
final CustomPoint<num> seCustomPoint = region.crs
26+
.latLngToPoint(southEast, zoomLvl.toDouble())
2627
.unscaleBy(tileSize)
2728
.ceil() -
2829
const CustomPoint(1, 1);
@@ -47,11 +48,10 @@ class TilesGenerator {
4748
// Theoretically, this could have been done using the same method as `lineTiles`, but `lineTiles` was built after this algorithm and this makes more sense for a circle
4849

4950
final SendPort sendPort = input['sendPort'];
50-
final List<LatLng> circleOutline = input['circleOutline'];
51-
final int minZoom = input['minZoom'];
52-
final int maxZoom = input['maxZoom'];
53-
final Crs crs = input['crs'];
54-
final CustomPoint<double> tileSize = input['tileSize'];
51+
final DownloadableRegion region = input['region'];
52+
53+
final tileSize = _getTileSize(region);
54+
final circleOutline = region.originalRegion.toOutline();
5555

5656
final recievePort = ReceivePort();
5757
sendPort.send(recievePort.sendPort);
@@ -60,11 +60,11 @@ class TilesGenerator {
6060
// Format: Map<z, Map<x, List<y>>>
6161
final Map<int, Map<int, List<int>>> outlineTileNums = {};
6262

63-
for (int zoomLvl = minZoom; zoomLvl <= maxZoom; zoomLvl++) {
63+
for (int zoomLvl = region.minZoom; zoomLvl <= region.maxZoom; zoomLvl++) {
6464
outlineTileNums[zoomLvl] = <int, List<int>>{};
6565

6666
for (final LatLng node in circleOutline) {
67-
final CustomPoint<num> tile = crs
67+
final CustomPoint<num> tile = region.crs
6868
.latLngToPoint(node, zoomLvl.toDouble())
6969
.unscaleBy(tileSize)
7070
.floor();
@@ -146,18 +146,19 @@ class TilesGenerator {
146146
}
147147

148148
final SendPort sendPort = input['sendPort'];
149-
final List<List<LatLng>> rects = input['lineOutline'];
150-
final int minZoom = input['minZoom'];
151-
final int maxZoom = input['maxZoom'];
152-
final Crs crs = input['crs'];
153-
final CustomPoint<double> tileSize = input['tileSize'];
149+
final DownloadableRegion region = input['region'];
150+
151+
final tileSize = _getTileSize(region);
152+
final lineOutline = (region.originalRegion as LineRegion).toOutlines(1);
154153

155154
final recievePort = ReceivePort();
156155
sendPort.send(recievePort.sendPort);
157156
final requestQueue = StreamQueue(recievePort);
158157

159-
for (double zoomLvl = minZoom.toDouble(); zoomLvl <= maxZoom; zoomLvl++) {
160-
for (final List<LatLng> rect in rects) {
158+
for (double zoomLvl = region.minZoom.toDouble();
159+
zoomLvl <= region.maxZoom;
160+
zoomLvl++) {
161+
for (final List<LatLng> rect in lineOutline) {
161162
final LatLng rrBottomLeft = rect[0];
162163
final LatLng rrBottomRight = rect[1];
163164
final LatLng rrTopRight = rect[2];
@@ -176,27 +177,31 @@ class TilesGenerator {
176177
rrBottomRight.longitude,
177178
];
178179

179-
final CustomPoint<num> rrNorthWest =
180-
crs.latLngToPoint(rrTopLeft, zoomLvl).unscaleBy(tileSize).floor();
181-
final CustomPoint<num> rrNorthEast =
182-
crs.latLngToPoint(rrTopRight, zoomLvl).unscaleBy(tileSize).ceil() -
183-
const CustomPoint(1, 0);
184-
final CustomPoint<num> rrSouthWest = crs
180+
final CustomPoint<num> rrNorthWest = region.crs
181+
.latLngToPoint(rrTopLeft, zoomLvl)
182+
.unscaleBy(tileSize)
183+
.floor();
184+
final CustomPoint<num> rrNorthEast = region.crs
185+
.latLngToPoint(rrTopRight, zoomLvl)
186+
.unscaleBy(tileSize)
187+
.ceil() -
188+
const CustomPoint(1, 0);
189+
final CustomPoint<num> rrSouthWest = region.crs
185190
.latLngToPoint(rrBottomLeft, zoomLvl)
186191
.unscaleBy(tileSize)
187192
.ceil() -
188193
const CustomPoint(0, 1);
189-
final CustomPoint<num> rrSouthEast = crs
194+
final CustomPoint<num> rrSouthEast = region.crs
190195
.latLngToPoint(rrBottomRight, zoomLvl)
191196
.unscaleBy(tileSize)
192197
.ceil() -
193198
const CustomPoint(1, 1);
194199

195-
final CustomPoint<num> srNorthWest = crs
200+
final CustomPoint<num> srNorthWest = region.crs
196201
.latLngToPoint(LatLng(rrAllLat.max, rrAllLon.min), zoomLvl)
197202
.unscaleBy(tileSize)
198203
.floor();
199-
final CustomPoint<num> srSouthEast = crs
204+
final CustomPoint<num> srSouthEast = region.crs
200205
.latLngToPoint(LatLng(rrAllLat.min, rrAllLon.max), zoomLvl)
201206
.unscaleBy(tileSize)
202207
.ceil() -

lib/src/bulk_download/tile_loops/shared.dart

+2-11
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import 'package:collection/collection.dart';
88
import 'package:flutter_map/flutter_map.dart';
99
import 'package:flutter_map/flutter_map.dart' hide Polygon;
1010
import 'package:latlong2/latlong.dart';
11-
import 'package:meta/meta.dart';
1211

1312
import '../../../flutter_map_tile_caching.dart';
1413

@@ -26,13 +25,5 @@ class _Polygon {
2625
List<CustomPoint<num>> get points => [nw, ne, se, sw];
2726
}
2827

29-
@internal
30-
Map<String, dynamic> generateTileLoopsInput(DownloadableRegion region) => {
31-
'rectOutline': LatLngBounds.fromPoints(region.points.cast()),
32-
'circleOutline': region.points,
33-
'lineOutline': region.points.slices(4).toList(),
34-
'minZoom': region.minZoom,
35-
'maxZoom': region.maxZoom,
36-
'crs': region.crs,
37-
'tileSize': CustomPoint(region.options.tileSize, region.options.tileSize),
38-
};
28+
CustomPoint<double> _getTileSize(DownloadableRegion region) =>
29+
CustomPoint(region.options.tileSize, region.options.tileSize);

lib/src/store/download.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ class DownloadManagement {
207207
: region.type == RegionType.circle
208208
? TilesCounter.circleTiles
209209
: TilesCounter.lineTiles,
210-
generateTileLoopsInput(region),
210+
region,
211211
);
212212

213213
/// Cancels the ongoing foreground download and recovery session (within the

prebuiltExampleApplications/AndroidApplication.apk.REMOVED.git-id

-1
This file was deleted.

prebuiltExampleApplications/WindowsApplication.exe.REMOVED.git-id

-1
This file was deleted.

pubspec.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name: flutter_map_tile_caching
22
description: Plugin for 'flutter_map' providing advanced caching functionality,
33
with ability to download map regions for offline use.
4-
version: 8.0.0
4+
version: 8.0.1
55
repository: https://github.com/JaffaKetchup/flutter_map_tile_caching
66
issue_tracker: https://github.com/JaffaKetchup/flutter_map_tile_caching/issues
77
documentation: https://fmtc.jaffaketchup.dev

0 commit comments

Comments
 (0)