Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 83 additions & 2 deletions lib/src/layer/tile_layer/tile_layer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,23 @@ part 'wms_tile_layer_options.dart';
/// You should read up about the options by exploring each one, or visiting
/// https://docs.fleaflet.dev/usage/layers/tile-layer. Some are important to
/// avoid issues.

/// Strategy for selecting native zoom level when the target zoom is equidistant
/// from two available native zoom levels.
enum NativeZoomStrategy {
/// Prefer lower zoom level (better performance, lower quality)
preferLower,

/// Prefer higher zoom level (better quality, more bandwidth)
preferHigher,

/// Always round down to the nearest native zoom
alwaysLower,

/// Always round up to the nearest native zoom
alwaysHigher,
}

@immutable
class TileLayer extends StatefulWidget {
/// The URL template is a string that contains placeholders, which, when filled
Expand Down Expand Up @@ -113,6 +130,15 @@ class TileLayer extends StatefulWidget {
/// Otherwise, this should be specified.
late final int maxNativeZoom;

/// Native zoom levels (optional).
/// If specified, only these zoom levels will be used for fetching tiles.
/// For example: [4, 6, 8, 10] for JMA weather tiles.
late final List<int>? nativeZooms;

/// Strategy for selecting native zoom level when equidistant.
/// Defaults to [NativeZoomStrategy.preferLower] for better performance.
final NativeZoomStrategy nativeZoomStrategy;

/// If set to true, the zoom number used in tile URLs will be reversed
/// (`maxZoom - zoom` instead of `zoom`)
final bool zoomReverse;
Expand Down Expand Up @@ -241,6 +267,8 @@ class TileLayer extends StatefulWidget {
double maxZoom = double.infinity,
int minNativeZoom = 0,
int maxNativeZoom = 19,
this.nativeZooms,
this.nativeZoomStrategy = NativeZoomStrategy.preferLower,
this.zoomReverse = false,
double zoomOffset = 0.0,
this.additionalOptions = const {},
Expand Down Expand Up @@ -745,8 +773,61 @@ See:

/// Rounds the zoom to the nearest int and clamps it to the native zoom limits
/// if there are any.
int _clampToNativeZoom(double zoom) =>
zoom.round().clamp(widget.minNativeZoom, widget.maxNativeZoom);
int _clampToNativeZoom(double zoom) {
if (widget.nativeZooms != null && widget.nativeZooms!.isNotEmpty) {
final targetZoom = zoom.round();
final sortedZooms = List<int>.from(widget.nativeZooms!)..sort();

// Return as-is if exact match is found
if (sortedZooms.contains(targetZoom)) {
return targetZoom;
}

// Find the maximum value <= targetZoom and minimum value >= targetZoom
int? lowerZoom;
int? upperZoom;

for (final z in sortedZooms) {
if (z < targetZoom) {
lowerZoom = z;
} else if (z > targetZoom) {
upperZoom = z;
break;
}
}

// Handle boundary cases
if (lowerZoom == null) {
return sortedZooms.first;
} else if (upperZoom == null) {
return sortedZooms.last;
}

// Calculate distances
final lowerDiff = targetZoom - lowerZoom;
final upperDiff = upperZoom - targetZoom;

// Select based on strategy
switch (widget.nativeZoomStrategy) {
case NativeZoomStrategy.preferLower:
// Choose lower when distances are equal (default)
return lowerDiff <= upperDiff ? lowerZoom : upperZoom;

case NativeZoomStrategy.preferHigher:
// Choose higher when distances are equal
return lowerDiff < upperDiff ? lowerZoom : upperZoom;

case NativeZoomStrategy.alwaysLower:
// Always choose lower
return lowerZoom;

case NativeZoomStrategy.alwaysHigher:
// Always choose higher
return upperZoom;
}
}
return zoom.round().clamp(widget.minNativeZoom, widget.maxNativeZoom);
}

void _onTileLoadError(TileImage tile, Object error, StackTrace? stackTrace) {
debugPrint(error.toString());
Expand Down
Loading