mirror of
https://github.com/FoggedLens/deflock-app.git
synced 2026-05-19 06:58:29 +02:00
fix dup world area, storage estimate
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
Reference in New Issue
Block a user