mirror of
https://github.com/FoggedLens/deflock-app.git
synced 2026-07-07 21:27:57 +02:00
max zoom per tile type
This commit is contained in:
@@ -191,6 +191,11 @@
|
|||||||
"attribution": "Attribution",
|
"attribution": "Attribution",
|
||||||
"attributionHint": "© Map Provider",
|
"attributionHint": "© Map Provider",
|
||||||
"attributionRequired": "Attribution is required",
|
"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",
|
"fetchPreview": "Fetch Preview",
|
||||||
"previewTileLoaded": "Preview tile loaded successfully",
|
"previewTileLoaded": "Preview tile loaded successfully",
|
||||||
"previewTileFailed": "Failed to fetch preview: {}",
|
"previewTileFailed": "Failed to fetch preview: {}",
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ class TileType {
|
|||||||
final String urlTemplate;
|
final String urlTemplate;
|
||||||
final String attribution;
|
final String attribution;
|
||||||
final Uint8List? previewTile; // Single tile image data for preview
|
final Uint8List? previewTile; // Single tile image data for preview
|
||||||
|
final int maxZoom; // Maximum zoom level for this tile type
|
||||||
|
|
||||||
const TileType({
|
const TileType({
|
||||||
required this.id,
|
required this.id,
|
||||||
@@ -15,6 +16,7 @@ class TileType {
|
|||||||
required this.urlTemplate,
|
required this.urlTemplate,
|
||||||
required this.attribution,
|
required this.attribution,
|
||||||
this.previewTile,
|
this.previewTile,
|
||||||
|
this.maxZoom = 18, // Default max zoom level
|
||||||
});
|
});
|
||||||
|
|
||||||
/// Create URL for a specific tile, replacing template variables
|
/// Create URL for a specific tile, replacing template variables
|
||||||
@@ -40,6 +42,7 @@ class TileType {
|
|||||||
'urlTemplate': urlTemplate,
|
'urlTemplate': urlTemplate,
|
||||||
'attribution': attribution,
|
'attribution': attribution,
|
||||||
'previewTile': previewTile != null ? base64Encode(previewTile!) : null,
|
'previewTile': previewTile != null ? base64Encode(previewTile!) : null,
|
||||||
|
'maxZoom': maxZoom,
|
||||||
};
|
};
|
||||||
|
|
||||||
static TileType fromJson(Map<String, dynamic> json) => TileType(
|
static TileType fromJson(Map<String, dynamic> json) => TileType(
|
||||||
@@ -50,6 +53,7 @@ class TileType {
|
|||||||
previewTile: json['previewTile'] != null
|
previewTile: json['previewTile'] != null
|
||||||
? base64Decode(json['previewTile'])
|
? base64Decode(json['previewTile'])
|
||||||
: null,
|
: null,
|
||||||
|
maxZoom: json['maxZoom'] ?? 18, // Default to 18 if not specified
|
||||||
);
|
);
|
||||||
|
|
||||||
TileType copyWith({
|
TileType copyWith({
|
||||||
@@ -58,12 +62,14 @@ class TileType {
|
|||||||
String? urlTemplate,
|
String? urlTemplate,
|
||||||
String? attribution,
|
String? attribution,
|
||||||
Uint8List? previewTile,
|
Uint8List? previewTile,
|
||||||
|
int? maxZoom,
|
||||||
}) => TileType(
|
}) => TileType(
|
||||||
id: id ?? this.id,
|
id: id ?? this.id,
|
||||||
name: name ?? this.name,
|
name: name ?? this.name,
|
||||||
urlTemplate: urlTemplate ?? this.urlTemplate,
|
urlTemplate: urlTemplate ?? this.urlTemplate,
|
||||||
attribution: attribution ?? this.attribution,
|
attribution: attribution ?? this.attribution,
|
||||||
previewTile: previewTile ?? this.previewTile,
|
previewTile: previewTile ?? this.previewTile,
|
||||||
|
maxZoom: maxZoom ?? this.maxZoom,
|
||||||
);
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -151,6 +157,7 @@ class DefaultTileProviders {
|
|||||||
name: 'Street Map',
|
name: 'Street Map',
|
||||||
urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
|
urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
|
||||||
attribution: '© OpenStreetMap contributors',
|
attribution: '© OpenStreetMap contributors',
|
||||||
|
maxZoom: 19,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -256,6 +256,7 @@ class _TileTypeDialogState extends State<_TileTypeDialog> {
|
|||||||
late final TextEditingController _nameController;
|
late final TextEditingController _nameController;
|
||||||
late final TextEditingController _urlController;
|
late final TextEditingController _urlController;
|
||||||
late final TextEditingController _attributionController;
|
late final TextEditingController _attributionController;
|
||||||
|
late final TextEditingController _maxZoomController;
|
||||||
Uint8List? _previewTile;
|
Uint8List? _previewTile;
|
||||||
bool _isLoadingPreview = false;
|
bool _isLoadingPreview = false;
|
||||||
|
|
||||||
@@ -266,6 +267,7 @@ class _TileTypeDialogState extends State<_TileTypeDialog> {
|
|||||||
_nameController = TextEditingController(text: tileType?.name ?? '');
|
_nameController = TextEditingController(text: tileType?.name ?? '');
|
||||||
_urlController = TextEditingController(text: tileType?.urlTemplate ?? '');
|
_urlController = TextEditingController(text: tileType?.urlTemplate ?? '');
|
||||||
_attributionController = TextEditingController(text: tileType?.attribution ?? '');
|
_attributionController = TextEditingController(text: tileType?.attribution ?? '');
|
||||||
|
_maxZoomController = TextEditingController(text: (tileType?.maxZoom ?? 18).toString());
|
||||||
_previewTile = tileType?.previewTile;
|
_previewTile = tileType?.previewTile;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -274,6 +276,7 @@ class _TileTypeDialogState extends State<_TileTypeDialog> {
|
|||||||
_nameController.dispose();
|
_nameController.dispose();
|
||||||
_urlController.dispose();
|
_urlController.dispose();
|
||||||
_attributionController.dispose();
|
_attributionController.dispose();
|
||||||
|
_maxZoomController.dispose();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -326,6 +329,22 @@ class _TileTypeDialogState extends State<_TileTypeDialog> {
|
|||||||
validator: (value) => value?.trim().isEmpty == true ? locService.t('tileTypeEditor.attributionRequired') : null,
|
validator: (value) => value?.trim().isEmpty == true ? locService.t('tileTypeEditor.attributionRequired') : null,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
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(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
TextButton.icon(
|
TextButton.icon(
|
||||||
@@ -425,6 +444,7 @@ class _TileTypeDialogState extends State<_TileTypeDialog> {
|
|||||||
urlTemplate: _urlController.text.trim(),
|
urlTemplate: _urlController.text.trim(),
|
||||||
attribution: _attributionController.text.trim(),
|
attribution: _attributionController.text.trim(),
|
||||||
previewTile: _previewTile,
|
previewTile: _previewTile,
|
||||||
|
maxZoom: int.parse(_maxZoomController.text.trim()),
|
||||||
);
|
);
|
||||||
|
|
||||||
widget.onSave(tileType);
|
widget.onSave(tileType);
|
||||||
|
|||||||
@@ -78,6 +78,7 @@ class TileLayerManager {
|
|||||||
return TileLayer(
|
return TileLayer(
|
||||||
urlTemplate: urlTemplate,
|
urlTemplate: urlTemplate,
|
||||||
userAgentPackageName: 'me.deflock.deflockapp',
|
userAgentPackageName: 'me.deflock.deflockapp',
|
||||||
|
maxZoom: selectedTileType?.maxZoom?.toDouble() ?? 18.0,
|
||||||
tileProvider: NetworkTileProvider(
|
tileProvider: NetworkTileProvider(
|
||||||
httpClient: _tileHttpClient,
|
httpClient: _tileHttpClient,
|
||||||
// Enable flutter_map caching - cache busting handled by URL changes and FlutterMap key
|
// Enable flutter_map caching - cache busting handled by URL changes and FlutterMap key
|
||||||
|
|||||||
@@ -452,7 +452,7 @@ class MapViewState extends State<MapView> {
|
|||||||
options: MapOptions(
|
options: MapOptions(
|
||||||
initialCenter: _gpsController.currentLocation ?? _positionManager.initialLocation ?? LatLng(37.7749, -122.4194),
|
initialCenter: _gpsController.currentLocation ?? _positionManager.initialLocation ?? LatLng(37.7749, -122.4194),
|
||||||
initialZoom: _positionManager.initialZoom ?? 15,
|
initialZoom: _positionManager.initialZoom ?? 15,
|
||||||
maxZoom: 19,
|
maxZoom: (appState.selectedTileType?.maxZoom ?? 18).toDouble(),
|
||||||
onPositionChanged: (pos, gesture) {
|
onPositionChanged: (pos, gesture) {
|
||||||
setState(() {}); // Instant UI update for zoom, etc.
|
setState(() {}); // Instant UI update for zoom, etc.
|
||||||
if (gesture) widget.onUserGesture();
|
if (gesture) widget.onUserGesture();
|
||||||
|
|||||||
Reference in New Issue
Block a user