max zoom per tile type

This commit is contained in:
stopflock
2025-10-05 13:04:48 -05:00
parent 5568173c6e
commit 5976ab4bab
5 changed files with 34 additions and 1 deletions

View File

@@ -191,6 +191,11 @@
"attribution": "Attribution",
"attributionHint": "© Map Provider",
"attributionRequired": "Attribution is required",
"maxZoom": "Max Zoom Level",
"maxZoomHint": "Maximum zoom level (1-19)",
"maxZoomRequired": "Max zoom is required",
"maxZoomInvalid": "Max zoom must be a number",
"maxZoomRange": "Max zoom must be between {} and {}",
"fetchPreview": "Fetch Preview",
"previewTileLoaded": "Preview tile loaded successfully",
"previewTileFailed": "Failed to fetch preview: {}",

View File

@@ -8,6 +8,7 @@ class TileType {
final String urlTemplate;
final String attribution;
final Uint8List? previewTile; // Single tile image data for preview
final int maxZoom; // Maximum zoom level for this tile type
const TileType({
required this.id,
@@ -15,6 +16,7 @@ class TileType {
required this.urlTemplate,
required this.attribution,
this.previewTile,
this.maxZoom = 18, // Default max zoom level
});
/// Create URL for a specific tile, replacing template variables
@@ -40,6 +42,7 @@ class TileType {
'urlTemplate': urlTemplate,
'attribution': attribution,
'previewTile': previewTile != null ? base64Encode(previewTile!) : null,
'maxZoom': maxZoom,
};
static TileType fromJson(Map<String, dynamic> json) => TileType(
@@ -50,6 +53,7 @@ class TileType {
previewTile: json['previewTile'] != null
? base64Decode(json['previewTile'])
: null,
maxZoom: json['maxZoom'] ?? 18, // Default to 18 if not specified
);
TileType copyWith({
@@ -58,12 +62,14 @@ class TileType {
String? urlTemplate,
String? attribution,
Uint8List? previewTile,
int? maxZoom,
}) => TileType(
id: id ?? this.id,
name: name ?? this.name,
urlTemplate: urlTemplate ?? this.urlTemplate,
attribution: attribution ?? this.attribution,
previewTile: previewTile ?? this.previewTile,
maxZoom: maxZoom ?? this.maxZoom,
);
@override
@@ -151,6 +157,7 @@ class DefaultTileProviders {
name: 'Street Map',
urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
attribution: '© OpenStreetMap contributors',
maxZoom: 19,
),
],
),

View File

@@ -256,6 +256,7 @@ class _TileTypeDialogState extends State<_TileTypeDialog> {
late final TextEditingController _nameController;
late final TextEditingController _urlController;
late final TextEditingController _attributionController;
late final TextEditingController _maxZoomController;
Uint8List? _previewTile;
bool _isLoadingPreview = false;
@@ -266,6 +267,7 @@ class _TileTypeDialogState extends State<_TileTypeDialog> {
_nameController = TextEditingController(text: tileType?.name ?? '');
_urlController = TextEditingController(text: tileType?.urlTemplate ?? '');
_attributionController = TextEditingController(text: tileType?.attribution ?? '');
_maxZoomController = TextEditingController(text: (tileType?.maxZoom ?? 18).toString());
_previewTile = tileType?.previewTile;
}
@@ -274,6 +276,7 @@ class _TileTypeDialogState extends State<_TileTypeDialog> {
_nameController.dispose();
_urlController.dispose();
_attributionController.dispose();
_maxZoomController.dispose();
super.dispose();
}
@@ -326,6 +329,22 @@ class _TileTypeDialogState extends State<_TileTypeDialog> {
validator: (value) => value?.trim().isEmpty == true ? locService.t('tileTypeEditor.attributionRequired') : null,
),
const SizedBox(height: 16),
TextFormField(
controller: _maxZoomController,
decoration: InputDecoration(
labelText: locService.t('tileTypeEditor.maxZoom'),
hintText: locService.t('tileTypeEditor.maxZoomHint'),
),
keyboardType: TextInputType.number,
validator: (value) {
if (value?.trim().isEmpty == true) return locService.t('tileTypeEditor.maxZoomRequired');
final zoom = int.tryParse(value!);
if (zoom == null) return locService.t('tileTypeEditor.maxZoomInvalid');
if (zoom < 1 || zoom > kAbsoluteMaxZoom) return locService.t('tileTypeEditor.maxZoomRange', params: ['1', kAbsoluteMaxZoom.toString()]);
return null;
},
),
const SizedBox(height: 16),
Row(
children: [
TextButton.icon(
@@ -425,6 +444,7 @@ class _TileTypeDialogState extends State<_TileTypeDialog> {
urlTemplate: _urlController.text.trim(),
attribution: _attributionController.text.trim(),
previewTile: _previewTile,
maxZoom: int.parse(_maxZoomController.text.trim()),
);
widget.onSave(tileType);

View File

@@ -78,6 +78,7 @@ class TileLayerManager {
return TileLayer(
urlTemplate: urlTemplate,
userAgentPackageName: 'me.deflock.deflockapp',
maxZoom: selectedTileType?.maxZoom?.toDouble() ?? 18.0,
tileProvider: NetworkTileProvider(
httpClient: _tileHttpClient,
// Enable flutter_map caching - cache busting handled by URL changes and FlutterMap key

View File

@@ -452,7 +452,7 @@ class MapViewState extends State<MapView> {
options: MapOptions(
initialCenter: _gpsController.currentLocation ?? _positionManager.initialLocation ?? LatLng(37.7749, -122.4194),
initialZoom: _positionManager.initialZoom ?? 15,
maxZoom: 19,
maxZoom: (appState.selectedTileType?.maxZoom ?? 18).toDouble(),
onPositionChanged: (pos, gesture) {
setState(() {}); // Instant UI update for zoom, etc.
if (gesture) widget.onUserGesture();