diff --git a/lib/src/geo/crs.dart b/lib/src/geo/crs.dart index f4dc608e7..6ad6f7d6e 100644 --- a/lib/src/geo/crs.dart +++ b/lib/src/geo/crs.dart @@ -351,6 +351,14 @@ class Proj4Crs extends Crs { if (scale == downScale) { return downZoom.toDouble(); } + // Scale is larger than the largest defined level: the requested zoom is + // beyond the deepest level this CRS knows about. Mirror the + // [double.negativeInfinity] returned above so callers (e.g. + // [FitBounds._getBoundsZoom]) can clamp to [maxZoom] rather than crashing + // on an out-of-range index access. + if (downZoom == _scales.length - 1) { + return double.infinity; + } // Interpolate final nextZoom = downZoom + 1; final nextScale = _scales[nextZoom]; diff --git a/test/geo/crs_test.dart b/test/geo/crs_test.dart new file mode 100644 index 000000000..5cbd209d9 --- /dev/null +++ b/test/geo/crs_test.dart @@ -0,0 +1,40 @@ +import 'package:flutter_map/flutter_map.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:proj4dart/proj4dart.dart' as proj4; + +void main() { + group('Proj4Crs.zoom', () { + final crs = Proj4Crs.fromFactory( + code: 'EPSG:3978', + proj4Projection: proj4.Projection.add( + 'EPSG:3978', + '+proj=lcc +lat_0=49 +lon_0=-95 +lat_1=49 +lat_2=77 +x_0=0 +y_0=0 ' + '+ellps=GRS80 +units=m +no_defs', + ), + resolutions: const [4096.0, 2048.0, 1024.0, 512.0, 256.0], + ); + + test('returns the array index when the scale matches a level exactly', () { + // Scale at zoom n equals 1 / resolutions[n]. + expect(crs.zoom(1 / 4096.0), 0); + expect(crs.zoom(1 / 256.0), 4); + }); + + test('interpolates between adjacent levels', () { + final lo = 1 / 2048.0; + final hi = 1 / 1024.0; + expect(crs.zoom((lo + hi) / 2), closeTo(1.5, 1e-9)); + }); + + test('returns -infinity when scale is below the coarsest level', () { + expect(crs.zoom(1 / 8192.0), double.negativeInfinity); + }); + + test('returns +infinity when scale exceeds the finest level', () { + // Regression test for fleaflet/flutter_map#1223: before the fix this + // threw RangeError when indexing _scales[length] during interpolation + // past the last level. + expect(crs.zoom(1 / 128.0), double.infinity); + }); + }); +}