From 59c93fe544d8247aaff01a11e8e3b9bd8f5a4a9f Mon Sep 17 00:00:00 2001 From: monsieurtanuki Date: Sun, 25 Jan 2026 10:38:01 +0100 Subject: [PATCH 1/4] fix: throwing when LatLng is NaN --- lib/src/geo/crs.dart | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/lib/src/geo/crs.dart b/lib/src/geo/crs.dart index b33e06c63..ce2f09ef0 100644 --- a/lib/src/geo/crs.dart +++ b/lib/src/geo/crs.dart @@ -53,7 +53,7 @@ abstract class Crs { /// Similar to [latLngToXY] but converts the XY coordinates to an [Offset]. Offset latLngToOffset(LatLng latlng, double zoom) { - final (x, y) = latLngToXY(latlng, scale(zoom)); + final (x, y) = latLngToXY(checkLatLng(latlng), scale(zoom)); return Offset(x, y); } @@ -72,6 +72,16 @@ abstract class Crs { /// Whether this CRS supports repeating worlds: repeated (feature) layers and /// unbounded horizontal scrolling along the longitude axis bool get replicatesWorldLongitude => false; + + /// Throws if [latlng] is NaN, which may cause memory leak. + /// cf. https://github.com/fleaflet/flutter_map/issues/2178 + @protected + LatLng checkLatLng(LatLng latlng) { + if (latlng.latitude.isNaN || latlng.longitude.isNaN) { + throw Exception('LatLng is Nan: $latlng'); + } + return latlng; + } } /// Internal base class for CRS with a single zoom-level independent transformation. @@ -104,7 +114,7 @@ abstract class CrsWithStaticTransformation extends Crs { @override (double, double) latLngToXY(LatLng latlng, double scale) { - final (x, y) = projection.projectXY(latlng); + final (x, y) = projection.projectXY(checkLatLng(latlng)); return _transformation.transform(x, y, scale); } @@ -164,15 +174,18 @@ class Epsg3857 extends CrsWithStaticTransformation { ); @override - (double, double) latLngToXY(LatLng latlng, double scale) => - _transformation.transform( - SphericalMercator.projectLng(latlng.longitude), - SphericalMercator.projectLat(latlng.latitude), - scale, - ); + (double, double) latLngToXY(LatLng latlng, double scale) { + checkLatLng(latlng); + return _transformation.transform( + SphericalMercator.projectLng(latlng.longitude), + SphericalMercator.projectLat(latlng.latitude), + scale, + ); + } @override Offset latLngToOffset(LatLng latlng, double zoom) { + checkLatLng(latlng); final (x, y) = _transformation.transform( SphericalMercator.projectLng(latlng.longitude), SphericalMercator.projectLat(latlng.latitude), @@ -276,7 +289,7 @@ class Proj4Crs extends Crs { /// map point. @override (double, double) latLngToXY(LatLng latlng, double scale) { - final (x, y) = projection.projectXY(latlng); + final (x, y) = projection.projectXY(checkLatLng(latlng)); final transformation = _getTransformationByZoom(zoom(scale)); return transformation.transform(x, y, scale); } From c5424cc2990ac5a4451a264503d8183626d41608 Mon Sep 17 00:00:00 2001 From: monsieurtanuki Date: Mon, 23 Feb 2026 17:43:46 +0100 Subject: [PATCH 2/4] fix: throwing when LatLng is not finite --- lib/src/geo/crs.dart | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/src/geo/crs.dart b/lib/src/geo/crs.dart index ce2f09ef0..f4dc608e7 100644 --- a/lib/src/geo/crs.dart +++ b/lib/src/geo/crs.dart @@ -73,12 +73,13 @@ abstract class Crs { /// unbounded horizontal scrolling along the longitude axis bool get replicatesWorldLongitude => false; - /// Throws if [latlng] is NaN, which may cause memory leak. + /// Throws if [latlng] is not finite (e.g. either NaN or infinite), which may + /// cause memory leak. /// cf. https://github.com/fleaflet/flutter_map/issues/2178 @protected LatLng checkLatLng(LatLng latlng) { - if (latlng.latitude.isNaN || latlng.longitude.isNaN) { - throw Exception('LatLng is Nan: $latlng'); + if (!(latlng.latitude.isFinite && latlng.longitude.isFinite)) { + throw Exception('LatLng is not finite: $latlng'); } return latlng; } From a474de18bf76b8df3bf9e954d3eecba405c52a37 Mon Sep 17 00:00:00 2001 From: monsieurtanuki Date: Mon, 23 Feb 2026 19:37:39 +0100 Subject: [PATCH 3/4] unrelated fix for lint parameter_assignments --- lib/src/gestures/map_interactive_viewer.dart | 9 +++++++-- .../native/workers/tile_and_size_monitor_writer.dart | 5 +++-- lib/src/map/camera/camera_fit.dart | 6 +++--- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/lib/src/gestures/map_interactive_viewer.dart b/lib/src/gestures/map_interactive_viewer.dart index 67bff82fa..25b2a04e4 100644 --- a/lib/src/gestures/map_interactive_viewer.dart +++ b/lib/src/gestures/map_interactive_viewer.dart @@ -114,6 +114,7 @@ class MapInteractiveViewerState extends State late var _keyboardPanAnimationPrevZoom = _camera.zoom; // to detect changes late double _keyboardPanAnimationMaxVelocity; + double _keyboardPanAnimationMaxVelocityCalculator(double zoom) => _interactionOptions.keyboardOptions.maxPanVelocity?.call(zoom) ?? 5 * math.log(0.15 * zoom + 1) + 1; @@ -124,7 +125,9 @@ class MapInteractiveViewerState extends State // Shortcuts MapCamera get _camera => widget.controller.camera; + MapOptions get _options => widget.controller.options; + InteractionOptions get _interactionOptions => _options.interactionOptions; @override @@ -1243,7 +1246,8 @@ class MapInteractiveViewerState extends State yield initManagerListeners( manager: _keyboardZoomAnimationManager, sum: _NumInfiniteSumAnimation.new, - onTick: (value) { + onTick: (valueParameter) { + num value = valueParameter; if (_isZoomLeaping.value) { value *= keyboardOptions.zoomLeapVelocityMultiplier; } @@ -1260,7 +1264,8 @@ class MapInteractiveViewerState extends State yield initManagerListeners( manager: _keyboardRotateAnimationManager, sum: _NumInfiniteSumAnimation.new, - onTick: (value) { + onTick: (valueParameter) { + num value = valueParameter; if (_isRotateLeaping.value) { value *= keyboardOptions.rotateLeapVelocityMultiplier; } diff --git a/lib/src/layer/tile_layer/tile_provider/network/caching/built_in/impl/native/workers/tile_and_size_monitor_writer.dart b/lib/src/layer/tile_layer/tile_provider/network/caching/built_in/impl/native/workers/tile_and_size_monitor_writer.dart index 7e2a67117..2c6a35e38 100644 --- a/lib/src/layer/tile_layer/tile_provider/network/caching/built_in/impl/native/workers/tile_and_size_monitor_writer.dart +++ b/lib/src/layer/tile_layer/tile_provider/network/caching/built_in/impl/native/workers/tile_and_size_monitor_writer.dart @@ -144,8 +144,9 @@ Future tileAndSizeMonitorWriterWorker( void writeTile({ required final String path, required final CachedMapTileMetadata metadata, - Uint8List? tileBytes, + Uint8List? tileBytesParameter, }) { + Uint8List? tileBytes = tileBytesParameter; final tileFile = File(path); final initialTileFileExists = tileFile.existsSync(); final initialTileFileLength = @@ -315,7 +316,7 @@ Future tileAndSizeMonitorWriterWorker( :final CachedMapTileMetadata metadata, :final Uint8List? tileBytes, )) { - writeTile(path: path, metadata: metadata, tileBytes: tileBytes); + writeTile(path: path, metadata: metadata, tileBytesParameter: tileBytes); } else if (val == false) { disableSizeMonitor(); } else if (val == null) { diff --git a/lib/src/map/camera/camera_fit.dart b/lib/src/map/camera/camera_fit.dart index 939c6351f..3e794afe1 100644 --- a/lib/src/map/camera/camera_fit.dart +++ b/lib/src/map/camera/camera_fit.dart @@ -231,7 +231,7 @@ class FitInsideBounds extends CameraFit { ).size; final scale = _rectInRotRectScale( - angleRad: camera.rotationRad, + angleRadParameter: camera.rotationRad, smallRectHalfWidth: cameraSize.width / 2.0, smallRectHalfHeight: cameraSize.height / 2.0, bigRectHalfWidth: projectedBoundsSize.width / 2.0, @@ -305,13 +305,13 @@ class FitInsideBounds extends CameraFit { /// /// This algorithm has been adapted from https://stackoverflow.com/a/75907251 static double _rectInRotRectScale({ - required double angleRad, + required double angleRadParameter, required double smallRectHalfWidth, required double smallRectHalfHeight, required double bigRectHalfWidth, required double bigRectHalfHeight, }) { - angleRad = _normalize(angleRad, 0, 2.0 * math.pi); + final angleRad = _normalize(angleRadParameter, 0, 2.0 * math.pi); var kmin = double.infinity; final quadrant = (2.0 * angleRad / math.pi).floor(); if (quadrant.isOdd) { From d0e88bf730e78683cfc42e7dc68b1339647bc8ed Mon Sep 17 00:00:00 2001 From: monsieurtanuki Date: Tue, 24 Feb 2026 09:18:39 +0100 Subject: [PATCH 4/4] pana threshold fix --- .github/workflows/branch.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/branch.yml b/.github/workflows/branch.yml index 6fbe6185e..920e35566 100644 --- a/.github/workflows/branch.yml +++ b/.github/workflows/branch.yml @@ -23,7 +23,8 @@ jobs: - name: Install pana run: dart pub global activate pana - name: Check package score - run: pana --exit-code-threshold 0 . + # we cannot reach the top score: we use logger that doesn't support wasm, so -10 + run: pana --exit-code-threshold 10 . analyse-code: name: "Analyse Code"