diff --git a/lib/src/layer/polygon_layer/polygon_layer.dart b/lib/src/layer/polygon_layer/polygon_layer.dart index 0bc3bff8b..3e99ca3a3 100644 --- a/lib/src/layer/polygon_layer/polygon_layer.dart +++ b/lib/src/layer/polygon_layer/polygon_layer.dart @@ -65,17 +65,24 @@ class _PolygonLayerState extends State { List<_ProjectedPolygon>? _cachedProjectedPolygons; final _cachedSimplifiedPolygons = >{}; + double? _devicePixelRatio; + @override void didUpdateWidget(PolygonLayer oldWidget) { super.didUpdateWidget(oldWidget); - // Reuse cache - if (widget.simplificationTolerance != 0 && - oldWidget.simplificationTolerance == widget.simplificationTolerance && - listEquals(oldWidget.polygons, widget.polygons)) return; - - _cachedSimplifiedPolygons.clear(); - _cachedProjectedPolygons = null; + if (!listEquals(oldWidget.polygons, widget.polygons)) { + // If the polylines have changed, then both the projections and the + // projection-dependendent simplifications must be invalidated + _cachedProjectedPolygons = null; + _cachedSimplifiedPolygons.clear(); + } else if (oldWidget.simplificationTolerance != + widget.simplificationTolerance) { + // If only the simplification tolerance has changed, this does not affect + // the projections (as that is done before simplification), so only + // invalidate the simplifications + _cachedSimplifiedPolygons.clear(); + } } @override @@ -91,14 +98,25 @@ class _PolygonLayerState extends State { growable: false, ); - final simplified = widget.simplificationTolerance <= 0 - ? projected - : _cachedSimplifiedPolygons[camera.zoom.floor()] ??= - _computeZoomLevelSimplification( - polygons: projected, - pixelTolerance: widget.simplificationTolerance, - camera: camera, - ); + late final List<_ProjectedPolygon> simplified; + if (widget.simplificationTolerance == 0) { + simplified = projected; + } else { + // If the DPR has changed, invalidate the simplification cache + final newDPR = MediaQuery.devicePixelRatioOf(context); + if (newDPR != _devicePixelRatio) { + _devicePixelRatio = newDPR; + _cachedSimplifiedPolygons.clear(); + } + + simplified = _cachedSimplifiedPolygons[camera.zoom.floor()] ??= + _computeZoomLevelSimplification( + camera: camera, + polygons: projected, + pixelTolerance: widget.simplificationTolerance, + devicePixelRatio: newDPR, + ); + } final culled = !widget.polygonCulling ? simplified @@ -122,14 +140,16 @@ class _PolygonLayerState extends State { } static List<_ProjectedPolygon> _computeZoomLevelSimplification({ + required MapCamera camera, required List<_ProjectedPolygon> polygons, required double pixelTolerance, - required MapCamera camera, + required double devicePixelRatio, }) { final tolerance = getEffectiveSimplificationTolerance( crs: camera.crs, zoom: camera.zoom.floor(), pixelTolerance: pixelTolerance, + devicePixelRatio: devicePixelRatio, ); return List<_ProjectedPolygon>.generate( diff --git a/lib/src/layer/polyline_layer/polyline_layer.dart b/lib/src/layer/polyline_layer/polyline_layer.dart index cc89a2baf..6be75f675 100644 --- a/lib/src/layer/polyline_layer/polyline_layer.dart +++ b/lib/src/layer/polyline_layer/polyline_layer.dart @@ -73,23 +73,27 @@ class PolylineLayer extends StatefulWidget { } class _PolylineLayerState extends State> { - List<_ProjectedPolyline>? _cachedProjectedPolylines; - final _cachedSimplifiedPolylines = >{}; + List<_ProjectedPolyline>? _cachedProjectedPolylines; + final _cachedSimplifiedPolylines = >>{}; - final _culledPolylines = - <_ProjectedPolyline>[]; // Avoids repetitive memory reallocation + double? _devicePixelRatio; @override void didUpdateWidget(PolylineLayer oldWidget) { super.didUpdateWidget(oldWidget); - // Reuse cache - if (widget.simplificationTolerance != 0 && - oldWidget.simplificationTolerance == widget.simplificationTolerance && - listEquals(oldWidget.polylines, widget.polylines)) return; - - _cachedSimplifiedPolylines.clear(); - _cachedProjectedPolylines = null; + if (!listEquals(oldWidget.polylines, widget.polylines)) { + // If the polylines have changed, then both the projections and the + // projection-dependendent simplifications must be invalidated + _cachedProjectedPolylines = null; + _cachedSimplifiedPolylines.clear(); + } else if (oldWidget.simplificationTolerance != + widget.simplificationTolerance) { + // If only the simplification tolerance has changed, this does not affect + // the projections (as that is done before simplification), so only + // invalidate the simplifications + _cachedSimplifiedPolylines.clear(); + } } @override @@ -105,14 +109,25 @@ class _PolylineLayerState extends State> { growable: false, ); - final simplified = widget.simplificationTolerance == 0 - ? projected - : _cachedSimplifiedPolylines[camera.zoom.floor()] ??= - _computeZoomLevelSimplification( - polylines: projected, - pixelTolerance: widget.simplificationTolerance, - camera: camera, - ); + late final List<_ProjectedPolyline> simplified; + if (widget.simplificationTolerance == 0) { + simplified = projected; + } else { + // If the DPR has changed, invalidate the simplification cache + final newDPR = MediaQuery.devicePixelRatioOf(context); + if (newDPR != _devicePixelRatio) { + _devicePixelRatio = newDPR; + _cachedSimplifiedPolylines.clear(); + } + + simplified = _cachedSimplifiedPolylines[camera.zoom.floor()] ??= + _computeZoomLevelSimplification( + camera: camera, + polylines: projected, + pixelTolerance: widget.simplificationTolerance, + devicePixelRatio: newDPR, + ); + } final culled = widget.cullingMargin == null ? simplified @@ -136,13 +151,13 @@ class _PolylineLayerState extends State> { ); } - List<_ProjectedPolyline> _aggressivelyCullPolylines({ + static List<_ProjectedPolyline> _aggressivelyCullPolylines({ required Projection projection, required List<_ProjectedPolyline> polylines, required MapCamera camera, required double cullingMargin, }) { - _culledPolylines.clear(); + final culledPolylines = <_ProjectedPolyline>[]; final bounds = camera.visibleBounds; final margin = cullingMargin / math.pow(2, camera.zoom); @@ -171,7 +186,7 @@ class _PolylineLayerState extends State> { // Gradient poylines cannot be easily segmented if (polyline.gradientColors != null) { - _culledPolylines.add(projectedPolyline); + culledPolylines.add(projectedPolyline); continue; } @@ -191,10 +206,12 @@ class _PolylineLayerState extends State> { } else { // If we cannot see this segment but have seen previous ones, flush the last polyline fragment. if (start != -1) { - _culledPolylines.add(_ProjectedPolyline._( - polyline: polyline, - points: projectedPolyline.points.sublist(start, i + 1), - )); + culledPolylines.add( + _ProjectedPolyline._( + polyline: polyline, + points: projectedPolyline.points.sublist(start, i + 1), + ), + ); // Reset start. start = -1; @@ -205,7 +222,7 @@ class _PolylineLayerState extends State> { // If the last segment was visible push that last visible polyline // fragment, which may also be the entire polyline if `start == 0`. if (containsSegment) { - _culledPolylines.add( + culledPolylines.add( start == 0 ? projectedPolyline : _ProjectedPolyline._( @@ -217,21 +234,24 @@ class _PolylineLayerState extends State> { } } - return _culledPolylines; + return culledPolylines; } - static List<_ProjectedPolyline> _computeZoomLevelSimplification({ - required List<_ProjectedPolyline> polylines, - required double pixelTolerance, + static List<_ProjectedPolyline> + _computeZoomLevelSimplification({ required MapCamera camera, + required List<_ProjectedPolyline> polylines, + required double pixelTolerance, + required double devicePixelRatio, }) { final tolerance = getEffectiveSimplificationTolerance( crs: camera.crs, zoom: camera.zoom.floor(), pixelTolerance: pixelTolerance, + devicePixelRatio: devicePixelRatio, ); - return List<_ProjectedPolyline>.generate( + return List<_ProjectedPolyline>.generate( polylines.length, (i) { final polyline = polylines[i]; diff --git a/lib/src/misc/simplify.dart b/lib/src/misc/simplify.dart index ea0828ec6..35c63cf3e 100644 --- a/lib/src/misc/simplify.dart +++ b/lib/src/misc/simplify.dart @@ -142,14 +142,16 @@ double getEffectiveSimplificationTolerance({ required Crs crs, required int zoom, required double pixelTolerance, + required double devicePixelRatio, }) { if (pixelTolerance <= 0) return 0; - final (x0, y0) = crs.untransform(0, 0, crs.scale(zoom.toDouble())); + final scale = crs.scale(zoom.toDouble()); + final (x0, y0) = crs.untransform(0, 0, scale); final (x1, y1) = crs.untransform( - pixelTolerance, - pixelTolerance, - crs.scale(zoom.toDouble()), + pixelTolerance * devicePixelRatio, + pixelTolerance * devicePixelRatio, + scale, ); final dx = x1 - x0;