From 2614799b973f96c3c5bc1f169361bb62fc25d9b9 Mon Sep 17 00:00:00 2001 From: Ian Date: Wed, 2 Feb 2022 18:20:33 +0000 Subject: [PATCH 1/2] added option to use pxCache and also length check on Markers to avoid crash --- lib/src/layer/marker_layer.dart | 78 ++++++++++++++++++++++++--------- 1 file changed, 57 insertions(+), 21 deletions(-) diff --git a/lib/src/layer/marker_layer.dart b/lib/src/layer/marker_layer.dart index a34660d69..05ac95ce7 100644 --- a/lib/src/layer/marker_layer.dart +++ b/lib/src/layer/marker_layer.dart @@ -8,6 +8,10 @@ import 'package:latlong2/latlong.dart'; class MarkerLayerOptions extends LayerOptions { final List markers; + /// Do we want to cache projections for performance, caching can + /// introduce inaccuracies if the cache is out of date + final bool usePxCache; + /// If true markers will be counter rotated to the map rotation final bool? rotate; @@ -38,6 +42,7 @@ class MarkerLayerOptions extends LayerOptions { this.rotate = false, this.rotateOrigin, this.rotateAlignment = Alignment.center, + this.usePxCache = true, Stream? rebuild, }) : super(key: key, rebuild: rebuild); } @@ -191,10 +196,30 @@ class _MarkerLayerState extends State { var _pxCache = []; // Calling this every time markerOpts change should guarantee proper length - List generatePxCache() => List.generate( + List generatePxCache() { + if (widget.markerLayerOptions.usePxCache) { + return List.generate( widget.markerLayerOptions.markers.length, - (i) => widget.map.project(widget.markerLayerOptions.markers[i].point), + (i) => + widget.map.project(widget.markerLayerOptions.markers[i].point), ); + } + return []; + } + + bool updatePxCacheIfNeeded() { + var didUpdate = false; + /// markers may be modified, so update cache. Note, someone may + /// have not added to a cache, but modified, so this won't catch + /// this case. Parent widget setState should be called to call + /// didUpdateWidget to force a cache reload + + if(widget.markerLayerOptions.markers.length != _pxCache.length) { + _pxCache = generatePxCache(); + didUpdate = true; + } + return didUpdate; + } @override void initState() { @@ -211,18 +236,28 @@ class _MarkerLayerState extends State { @override Widget build(BuildContext context) { + return StreamBuilder( stream: widget.stream, // a Stream or null builder: (BuildContext context, AsyncSnapshot snapshot) { + + + var layerOptions = widget.markerLayerOptions; + var map = widget.map; + var usePxCache = layerOptions.usePxCache; var markers = []; - final sameZoom = widget.map.zoom == lastZoom; - for (var i = 0; i < widget.markerLayerOptions.markers.length; i++) { - var marker = widget.markerLayerOptions.markers[i]; + final sameZoom = map.zoom == lastZoom; + + var cacheUpdated = updatePxCacheIfNeeded(); + + for (var i = 0; i < layerOptions.markers.length; i++) { + var marker = layerOptions.markers[i]; // Decide whether to use cached point or calculate it var pxPoint = - sameZoom ? _pxCache[i] : widget.map.project(marker.point); - if (!sameZoom) { + usePxCache && (sameZoom || cacheUpdated) ? + _pxCache[i] : map.project(marker.point); + if (!sameZoom && usePxCache) { _pxCache[i] = pxPoint; } @@ -231,23 +266,23 @@ class _MarkerLayerState extends State { var sw = CustomPoint(pxPoint.x + width, pxPoint.y - height); var ne = CustomPoint(pxPoint.x - width, pxPoint.y + height); - if (!widget.map.pixelBounds.containsPartialBounds(Bounds(sw, ne))) { + if (!map.pixelBounds.containsPartialBounds(Bounds(sw, ne))) { continue; } - final pos = pxPoint - widget.map.getPixelOrigin(); + final pos = pxPoint - map.getPixelOrigin(); final markerWidget = - (marker.rotate ?? widget.markerLayerOptions.rotate ?? false) - // Counter rotated marker to the map rotation - ? Transform.rotate( - angle: -widget.map.rotationRad, - origin: marker.rotateOrigin ?? - widget.markerLayerOptions.rotateOrigin, - alignment: marker.rotateAlignment ?? - widget.markerLayerOptions.rotateAlignment, - child: marker.builder(context), - ) - : marker.builder(context); + (marker.rotate ?? layerOptions.rotate ?? false) + // Counter rotated marker to the map rotation + ? Transform.rotate( + angle: -map.rotationRad, + origin: marker.rotateOrigin ?? + layerOptions.rotateOrigin, + alignment: marker.rotateAlignment ?? + layerOptions.rotateAlignment, + child: marker.builder(context), + ) + : marker.builder(context); markers.add( Positioned( @@ -260,7 +295,7 @@ class _MarkerLayerState extends State { ), ); } - lastZoom = widget.map.zoom; + lastZoom = map.zoom; return Container( child: Stack( children: markers, @@ -270,3 +305,4 @@ class _MarkerLayerState extends State { ); } } + From 7a32d7d788d7848fc6e6ec47aff4c030cd3fbaad Mon Sep 17 00:00:00 2001 From: Ian Date: Sat, 26 Feb 2022 18:02:53 +0000 Subject: [PATCH 2/2] adjust comments as suggested --- lib/src/layer/marker_layer.dart | 40 ++++++++++++++------------------- 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/lib/src/layer/marker_layer.dart b/lib/src/layer/marker_layer.dart index 05ac95ce7..22b89e1bc 100644 --- a/lib/src/layer/marker_layer.dart +++ b/lib/src/layer/marker_layer.dart @@ -8,8 +8,8 @@ import 'package:latlong2/latlong.dart'; class MarkerLayerOptions extends LayerOptions { final List markers; - /// Do we want to cache projections for performance, caching can - /// introduce inaccuracies if the cache is out of date + /// Toggle marker position caching. Enabling will improve performance, but may introducen + /// errors when adding/removing markers. Default is enabled (`true`). final bool usePxCache; /// If true markers will be counter rotated to the map rotation @@ -195,13 +195,12 @@ class _MarkerLayerState extends State { // https://stackoverflow.com/questions/15943890/is-there-a-performance-benefit-in-using-fixed-length-lists-in-dart var _pxCache = []; - // Calling this every time markerOpts change should guarantee proper length + /// Calling this every time markerOpts change should guarantee proper length List generatePxCache() { if (widget.markerLayerOptions.usePxCache) { return List.generate( widget.markerLayerOptions.markers.length, - (i) => - widget.map.project(widget.markerLayerOptions.markers[i].point), + (i) => widget.map.project(widget.markerLayerOptions.markers[i].point), ); } return []; @@ -209,12 +208,13 @@ class _MarkerLayerState extends State { bool updatePxCacheIfNeeded() { var didUpdate = false; + /// markers may be modified, so update cache. Note, someone may /// have not added to a cache, but modified, so this won't catch /// this case. Parent widget setState should be called to call /// didUpdateWidget to force a cache reload - if(widget.markerLayerOptions.markers.length != _pxCache.length) { + if (widget.markerLayerOptions.markers.length != _pxCache.length) { _pxCache = generatePxCache(); didUpdate = true; } @@ -236,12 +236,9 @@ class _MarkerLayerState extends State { @override Widget build(BuildContext context) { - return StreamBuilder( stream: widget.stream, // a Stream or null builder: (BuildContext context, AsyncSnapshot snapshot) { - - var layerOptions = widget.markerLayerOptions; var map = widget.map; var usePxCache = layerOptions.usePxCache; @@ -254,9 +251,9 @@ class _MarkerLayerState extends State { var marker = layerOptions.markers[i]; // Decide whether to use cached point or calculate it - var pxPoint = - usePxCache && (sameZoom || cacheUpdated) ? - _pxCache[i] : map.project(marker.point); + var pxPoint = usePxCache && (sameZoom || cacheUpdated) + ? _pxCache[i] + : map.project(marker.point); if (!sameZoom && usePxCache) { _pxCache[i] = pxPoint; } @@ -271,17 +268,15 @@ class _MarkerLayerState extends State { } final pos = pxPoint - map.getPixelOrigin(); - final markerWidget = - (marker.rotate ?? layerOptions.rotate ?? false) - // Counter rotated marker to the map rotation + final markerWidget = (marker.rotate ?? layerOptions.rotate ?? false) + // Counter rotated marker to the map rotation ? Transform.rotate( - angle: -map.rotationRad, - origin: marker.rotateOrigin ?? - layerOptions.rotateOrigin, - alignment: marker.rotateAlignment ?? - layerOptions.rotateAlignment, - child: marker.builder(context), - ) + angle: -map.rotationRad, + origin: marker.rotateOrigin ?? layerOptions.rotateOrigin, + alignment: + marker.rotateAlignment ?? layerOptions.rotateAlignment, + child: marker.builder(context), + ) : marker.builder(context); markers.add( @@ -305,4 +300,3 @@ class _MarkerLayerState extends State { ); } } -