From fa3b8b3456d038a27f907d412c487eaec018517e Mon Sep 17 00:00:00 2001 From: stopflock Date: Mon, 11 Aug 2025 01:35:11 -0500 Subject: [PATCH] Offline areas download an extra ring of tiles at each zoom level --- lib/dev_config.dart | 3 ++ lib/screens/home_screen.dart | 34 ++++++++++++++++--- .../offline_areas/offline_tile_utils.dart | 16 +++++---- 3 files changed, 43 insertions(+), 10 deletions(-) diff --git a/lib/dev_config.dart b/lib/dev_config.dart index 21ac82d..79a22ea 100644 --- a/lib/dev_config.dart +++ b/lib/dev_config.dart @@ -23,3 +23,6 @@ const int kTileFetchSecondDelayMs = 15000; const int kTileFetchJitter2Ms = 4000; const int kTileFetchThirdDelayMs = 60000; const int kTileFetchJitter3Ms = 5000; + +// User download max zoom span (user can download up to kMaxUserDownloadZoomSpan zooms above min) +const int kMaxUserDownloadZoomSpan = 7; diff --git a/lib/screens/home_screen.dart b/lib/screens/home_screen.dart index ae30e45..38a0615 100644 --- a/lib/screens/home_screen.dart +++ b/lib/screens/home_screen.dart @@ -146,7 +146,32 @@ class _DownloadAreaDialogState extends State { Widget build(BuildContext context) { final bounds = widget.controller.camera.visibleBounds; final maxZoom = _zoom.toInt(); + double sliderMin; + double sliderMax; + int sliderDivisions; + double sliderValue; + // Generate slider min/max/divisions with clarity + if (_minZoom != null) { + sliderMin = _minZoom!.toDouble(); + } else { + sliderMin = 12.0; //fallback + } + if (_minZoom != null) { + final candidateMax = _minZoom! + kMaxUserDownloadZoomSpan; + sliderMax = candidateMax > 19 ? 19.0 : candidateMax.toDouble(); + } else { + sliderMax = 19.0; //fallback + } + if (_minZoom != null) { + final candidateMax = _minZoom! + kMaxUserDownloadZoomSpan; + int diff = (candidateMax > 19 ? 19 : candidateMax) - _minZoom!; + sliderDivisions = diff > 0 ? diff : 1; + } else { + sliderDivisions = 7; //fallback + } + sliderValue = _zoom.clamp(sliderMin, sliderMax); // We recompute estimates when the zoom slider changes + return AlertDialog( title: Row( children: const [ @@ -167,12 +192,13 @@ class _DownloadAreaDialogState extends State { Text('Z${_zoom.toStringAsFixed(0)}'), ], ), + Slider( - min: 12, - max: 19, - divisions: 7, + min: sliderMin, + max: sliderMax, + divisions: sliderDivisions, label: 'Z${_zoom.toStringAsFixed(0)}', - value: _zoom, + value: sliderValue, onChanged: (v) { setState(() => _zoom = v); WidgetsBinding.instance.addPostFrameCallback((_) => _recomputeEstimates()); diff --git a/lib/services/offline_areas/offline_tile_utils.dart b/lib/services/offline_areas/offline_tile_utils.dart index b532cba..071354e 100644 --- a/lib/services/offline_areas/offline_tile_utils.dart +++ b/lib/services/offline_areas/offline_tile_utils.dart @@ -22,12 +22,16 @@ Set> computeTileList(LatLngBounds bounds, int zMin, int zMax) { } for (int z = zMin; z <= zMax; z++) { final n = pow(2, z).toInt(); - final minTile = latLonToTile(latMin, lonMin, z); - final maxTile = latLonToTile(latMax, lonMax, z); - final minX = min(minTile[0], maxTile[0]); - final maxX = max(minTile[0], maxTile[0]); - final minY = min(minTile[1], maxTile[1]); - final maxY = max(minTile[1], maxTile[1]); + final minTileRaw = latLonToTileRaw(latMin, lonMin, z); + final maxTileRaw = latLonToTileRaw(latMax, lonMax, z); + int minX = min(minTileRaw[0].floor(), maxTileRaw[0].floor()) - 1; + int maxX = max(minTileRaw[0].ceil() - 1, maxTileRaw[0].ceil() - 1) + 1; + int minY = min(minTileRaw[1].floor(), maxTileRaw[1].floor()) - 1; + int maxY = max(minTileRaw[1].ceil() - 1, maxTileRaw[1].ceil() - 1) + 1; + minX = minX.clamp(0, n - 1); + maxX = maxX.clamp(0, n - 1); + minY = minY.clamp(0, n - 1); + maxY = maxY.clamp(0, n - 1); for (int x = minX; x <= maxX; x++) { for (int y = minY; y <= maxY; y++) { tiles.add([z, x, y]);