fix dup world area, storage estimate

This commit is contained in:
stopflock
2025-08-07 09:22:17 -05:00
parent fff1096870
commit b1d29e443d
3 changed files with 76 additions and 10 deletions
+1 -1
View File
@@ -135,7 +135,7 @@ class _DownloadAreaDialogState extends State<DownloadAreaDialog> {
final worldTiles = OfflineAreaService().computeTileList(
OfflineAreaService().globalWorldBounds(), 1, 4);
final nTiles = allTiles.length + worldTiles.length;
const kbPerTile = 25; // Average PNG tile size
const kbPerTile = 6.5; // Empirically ~6.5kB average for OSM tiles at z=1-19
final totalMb = (nTiles * kbPerTile) / 1024.0;
setState(() {
_minZoom = minZoom;
+22 -5
View File
@@ -420,10 +420,8 @@ class _OfflineAreasSectionState extends State<_OfflineAreasSection> {
} else {
subtitle += '\nTiles: ${area.tilesTotal}';
}
subtitle += ' | Size: $diskStr';
if (area.status == OfflineAreaStatus.complete) {
subtitle += ' | Cameras: ${area.cameras.length}';
}
subtitle += '\nSize: $diskStr';
subtitle += '\nCameras: ${area.cameras.length}';
return Card(
child: ListTile(
leading: Icon(area.status == OfflineAreaStatus.complete
@@ -477,7 +475,26 @@ class _OfflineAreasSectionState extends State<_OfflineAreasSection> {
}
},
),
if (area.status != OfflineAreaStatus.downloading)
if (area.isPermanent)
IconButton(
icon: const Icon(Icons.refresh, color: Colors.blue),
tooltip: 'Refresh/re-download world tiles',
onPressed: () async {
// Trigger re-download for permanent area
await service.downloadArea(
id: area.id,
bounds: area.bounds,
minZoom: area.minZoom,
maxZoom: area.maxZoom,
directory: area.directory,
name: area.name,
onProgress: (progress) {},
onComplete: (status) {},
);
setState(() {});
},
)
else if (area.status != OfflineAreaStatus.downloading)
IconButton(
icon: const Icon(Icons.delete, color: Colors.red),
tooltip: 'Delete offline area',
+53 -4
View File
@@ -24,6 +24,7 @@ class OfflineArea {
int tilesTotal;
List<OsmCameraNode> cameras;
int sizeBytes; // Disk size in bytes
final bool isPermanent; // Not user-deletable if true
OfflineArea({
required this.id,
@@ -38,6 +39,7 @@ class OfflineArea {
this.tilesTotal = 0,
this.cameras = const [],
this.sizeBytes = 0,
this.isPermanent = false,
});
Map<String, dynamic> toJson() => {
@@ -56,6 +58,7 @@ class OfflineArea {
'tilesTotal': tilesTotal,
'cameras': cameras.map((c) => c.toJson()).toList(),
'sizeBytes': sizeBytes,
'isPermanent': isPermanent,
};
static OfflineArea fromJson(Map<String, dynamic> json) {
@@ -78,6 +81,7 @@ class OfflineArea {
cameras: (json['cameras'] as List? ?? [])
.map((e) => OsmCameraNode.fromJson(e)).toList(),
sizeBytes: json['sizeBytes'] ?? 0,
isPermanent: json['isPermanent'] ?? false,
);
}
}
@@ -105,9 +109,33 @@ class OfflineAreaService {
static final OfflineAreaService _instance = OfflineAreaService._();
factory OfflineAreaService() => _instance;
OfflineAreaService._() {
_initPermanentWorldArea();
_loadAreasFromDisk();
}
// Ensure permanent world area exists at all times
Future<void> _initPermanentWorldArea() async {
final dir = await getOfflineAreaDir();
final worldDir = "${dir.path}/world_z1_4";
final LatLngBounds worldBounds = globalWorldBounds();
// Check if already present
final existing = _areas.where((a) => a.isPermanent).toList();
if (existing.isEmpty) {
_areas.insert(0, OfflineArea(
id: 'permanent_world_z1_4',
name: 'World (zoom 1-4)',
bounds: worldBounds,
minZoom: 1,
maxZoom: 4,
directory: worldDir,
status: OfflineAreaStatus.complete, // Assume complete until proven otherwise on next app run
progress: 1.0,
isPermanent: true,
));
await saveAreasToDisk();
}
}
final List<OfflineArea> _areas = [];
/// Where offline area data/metadata lives
@@ -139,13 +167,27 @@ class OfflineAreaService {
void Function(OfflineAreaStatus status)? onComplete,
String? name,
}) async {
final area = OfflineArea(
// If area with same id exists, replace its contents, else add.
OfflineArea? area;
for (final a in _areas) {
if (a.id == id) { area = a; break; }
}
if (area != null) {
// Remove area and its files before creating fresh
_areas.remove(area);
final dirObj = Directory(area.directory);
if (await dirObj.exists()) {
await dirObj.delete(recursive: true);
}
}
area = OfflineArea(
id: id,
name: name ?? '',
name: name ?? area?.name ?? '',
bounds: bounds,
minZoom: minZoom,
maxZoom: maxZoom,
directory: directory,
isPermanent: area?.isPermanent ?? false,
);
_areas.add(area);
await saveAreasToDisk();
@@ -225,9 +267,16 @@ class OfflineAreaService {
final file = await _getMetadataPath();
if (!(await file.exists())) return;
final str = await file.readAsString();
final data = jsonDecode(str);
if (str.trim().isEmpty) return;
late final List data;
try {
data = jsonDecode(str);
} catch (e) {
debugPrint('Failed to parse offline areas json: $e');
return;
}
_areas.clear();
for (final areaJson in (data as List)) {
for (final areaJson in data) {
final area = OfflineArea.fromJson(areaJson);
// Check if directory still exists; adjust status if not
if (!Directory(area.directory).existsSync()) {