diff --git a/benchmark/crs.dart b/benchmark/crs.dart index c29c4e9ad..368369fa3 100644 --- a/benchmark/crs.dart +++ b/benchmark/crs.dart @@ -29,6 +29,7 @@ Future main() async { const N = 100000000; const crs = Epsg3857(); + final cachedCrs = Epsg3857Cached(); results.add(await timedRun('Concrete type: ${crs.code}.latLngToXY()', () { double x = 0; double y = 0; @@ -40,6 +41,20 @@ Future main() async { } return x + y; })); + results.add(await timedRun( + 'Concrete type (cached): ${cachedCrs.code}.latLngToXY()', + () { + double x = 0; + double y = 0; + for (int i = 0; i < N; ++i) { + final latlng = LatLng((i % 90).toDouble(), (i % 180).toDouble()); + final (cx, cy) = cachedCrs.latLngToXY(latlng, 1); + x += cx; + y += cy; + } + return x + y; + }, + )); results.add(await timedRun('Concrete type: ${crs.code}.latLngToOffset()', () { double x = 0; @@ -53,13 +68,30 @@ Future main() async { return x + y; })); - const crss = [ - Epsg3857(), - Epsg4326(), + results.add(await timedRun( + 'Concrete type (cached): ${cachedCrs.code}.latLngToOffset()', + () { + double x = 0; + double y = 0; + for (int i = 0; i < N; ++i) { + final latlng = LatLng((i % 90).toDouble(), (i % 180).toDouble()); + final p = cachedCrs.latLngToOffset(latlng, 1); + x += p.dx; + y += p.dy; + } + return x + y; + }, + )); + + final crss = <(String, Crs)>[ + ('EPSG:3857', const Epsg3857()), + ('EPSG:3857 (cached)', Epsg3857Cached()), + ('EPSG:4326', const Epsg4326()), + ('EPSG:4326 (cached)', Epsg4326Cached()), ]; - for (final crs in crss) { - results.add(await timedRun('${crs.code}.latLngToXY()', () { + for (final (label, crs) in crss) { + results.add(await timedRun('$label.latLngToXY()', () { double x = 0; double y = 0; for (int i = 0; i < N; ++i) { @@ -71,7 +103,7 @@ Future main() async { return x + y; })); - results.add(await timedRun('${crs.code}.latlngToPoint()', () { + results.add(await timedRun('$label.latlngToPoint()', () { double x = 0; double y = 0; for (int i = 0; i < N; ++i) { @@ -83,7 +115,7 @@ Future main() async { return x + y; })); - results.add(await timedRun('${crs.code}.pointToLatLng()', () { + results.add(await timedRun('$label.pointToLatLng()', () { double x = 0; double y = 0; for (int i = 0; i < N; ++i) { diff --git a/lib/src/geo/crs.dart b/lib/src/geo/crs.dart index f4dc608e7..3f7ded9a6 100644 --- a/lib/src/geo/crs.dart +++ b/lib/src/geo/crs.dart @@ -61,10 +61,10 @@ abstract class Crs { LatLng offsetToLatLng(Offset point, double zoom); /// Zoom to Scale function. - double scale(double zoom) => 256.0 * math.pow(2, zoom); + double scale(double zoom) => 256.0 * math.pow(2.0, zoom); /// Scale to Zoom function. - double zoom(double scale) => math.log(scale / 256) / math.ln2; + double zoom(double scale) => math.log(scale / 256.0) / math.ln2; /// Rescales the bounds to a given zoom value. Rect? getProjectedBounds(double zoom); @@ -85,6 +85,31 @@ abstract class Crs { } } +mixin _ScaleCacheMixin on Crs { + double _lastScaleZoom = double.nan; + double _lastScaleValue = double.nan; + double _lastZoomScale = double.nan; + double _lastZoomValue = double.nan; + + @override + double scale(double zoom) { + if (zoom == _lastScaleZoom) return _lastScaleValue; + final value = super.scale(zoom); + _lastScaleZoom = zoom; + _lastScaleValue = value; + return value; + } + + @override + double zoom(double scale) { + if (scale == _lastZoomScale) return _lastZoomValue; + final value = super.zoom(scale); + _lastZoomScale = scale; + _lastZoomValue = value; + return value; + } +} + /// Internal base class for CRS with a single zoom-level independent transformation. @immutable @internal @@ -159,6 +184,11 @@ class CrsSimple extends CrsWithStaticTransformation { ); } +/// Non-const CRS with cached scale/zoom. +class CrsSimpleCached extends CrsSimple with _ScaleCacheMixin { + CrsSimpleCached() : super(); +} + /// EPSG:3857, The most common CRS used for rendering maps. @immutable class Epsg3857 extends CrsWithStaticTransformation { @@ -199,6 +229,11 @@ class Epsg3857 extends CrsWithStaticTransformation { bool get replicatesWorldLongitude => true; } +/// Non-const CRS with cached scale/zoom. +class Epsg3857Cached extends Epsg3857 with _ScaleCacheMixin { + Epsg3857Cached() : super(); +} + /// EPSG:4326, A common CRS among GIS enthusiasts. /// Uses simple Equirectangular projection. @immutable @@ -214,6 +249,11 @@ class Epsg4326 extends CrsWithStaticTransformation { ); } +/// Non-const CRS with cached scale/zoom. +class Epsg4326Cached extends Epsg4326 with _ScaleCacheMixin { + Epsg4326Cached() : super(); +} + /// Custom CRS @immutable class Proj4Crs extends Crs {