diff --git a/lib/src/layer/polygon_layer/label.dart b/lib/src/layer/polygon_layer/label.dart index 3f3684470..c8ec59393 100644 --- a/lib/src/layer/polygon_layer/label.dart +++ b/lib/src/layer/polygon_layer/label.dart @@ -6,42 +6,54 @@ import 'package:flutter_map/src/layer/polygon_layer/polygon_layer.dart'; import 'package:latlong2/latlong.dart'; import 'package:polylabel/polylabel.dart'; +class PolygonBounds { + final Offset min; + final Offset max; + + const PolygonBounds(this.min, this.max); +} + void Function(Canvas canvas)? buildLabelTextPainter({ + required math.Point mapSize, required Offset placementPoint, - required List points, - required String labelText, + required PolygonBounds bounds, + required TextPainter textPainter, required double rotationRad, required bool rotate, - required TextStyle labelStyle, required double padding, }) { - final textSpan = TextSpan(text: labelText, style: labelStyle); - final textPainter = TextPainter( - text: textSpan, - textAlign: TextAlign.center, - textDirection: TextDirection.ltr, - )..layout(); + final dx = placementPoint.dx; + final dy = placementPoint.dy; + final width = textPainter.width; + final height = textPainter.height; - final dx = placementPoint.dx - textPainter.width / 2; - final dy = placementPoint.dy - textPainter.height / 2; - - double maxDx = 0; - var minDx = double.infinity; - for (final point in points) { - maxDx = math.max(maxDx, point.dx); - minDx = math.min(minDx, point.dx); + // Cull labels where the polygon is still on the map but the label would not be. + if (dx + width / 2 < 0 || dx - width / 2 > mapSize.x) { + return null; + } + if (dy + height / 2 < 0 || dy - height / 2 > mapSize.y) { + return null; } - if (maxDx - minDx - padding > textPainter.width) { + // Note: I'm pretty sure this doesn't work for concave shapes. It would be more + // correct to evaluate the width of the polygon at the height of the label. + if (bounds.max.dx - bounds.min.dx - padding > width) { return (canvas) { if (rotate) { canvas.save(); - canvas.translate(placementPoint.dx, placementPoint.dy); + canvas.translate(dx, dy); canvas.rotate(-rotationRad); - canvas.translate(-placementPoint.dx, -placementPoint.dy); + canvas.translate(-dx, -dy); } - textPainter.paint(canvas, Offset(dx, dy)); + textPainter.paint( + canvas, + Offset( + dx - width / 2, + dy - height / 2, + ), + ); + if (rotate) { canvas.restore(); } diff --git a/lib/src/layer/polygon_layer/polygon_layer.dart b/lib/src/layer/polygon_layer/polygon_layer.dart index 7b1382808..f390d1070 100644 --- a/lib/src/layer/polygon_layer/polygon_layer.dart +++ b/lib/src/layer/polygon_layer/polygon_layer.dart @@ -53,6 +53,18 @@ class Polygon { LatLngBounds get boundingBox => _boundingBox ??= LatLngBounds.fromPoints(points); + TextPainter? _textPainter; + TextPainter? get textPainter { + if (label != null) { + return _textPainter ??= TextPainter( + text: TextSpan(text: label, style: labelStyle), + textAlign: TextAlign.center, + textDirection: TextDirection.ltr, + )..layout(); + } + return null; + } + Polygon({ required this.points, this.holePointsList, @@ -148,6 +160,13 @@ class PolygonPainter extends CustomPainter { int? _hash; + PolygonBounds getBounds(Polygon polygon) { + final bbox = polygon.boundingBox; + final min = map.getOffsetFromOrigin(bbox.southWest); + final max = map.getOffsetFromOrigin(bbox.northEast); + return PolygonBounds(min, max); + } + List getOffsets(List points) { return List.generate( points.length, @@ -241,7 +260,7 @@ class PolygonPainter extends CustomPainter { } } - if (polygonLabels && !drawLabelsLast && polygon.label != null) { + if (!drawLabelsLast && polygonLabels && polygon.textPainter != null) { // Labels are expensive because: // * they themselves cannot easily be pulled into our batched path // painting with the given text APIs @@ -252,10 +271,10 @@ class PolygonPainter extends CustomPainter { // The painter will be null if the layouting algorithm determined that // there isn't enough space. final painter = buildLabelTextPainter( + mapSize: map.size, placementPoint: map.getOffsetFromOrigin(polygon.labelPosition), - points: offsets, - labelText: polygon.label!, - labelStyle: polygon.labelStyle, + bounds: getBounds(polygon), + textPainter: polygon.textPainter!, rotationRad: map.rotationRad, rotate: polygon.rotateLabel, padding: 20, @@ -277,14 +296,13 @@ class PolygonPainter extends CustomPainter { if (polygon.points.isEmpty) { continue; } - final offsets = getOffsets(polygon.points); - - if (polygon.label != null) { + final textPainter = polygon.textPainter; + if (textPainter != null) { final painter = buildLabelTextPainter( + mapSize: map.size, placementPoint: map.getOffsetFromOrigin(polygon.labelPosition), - points: offsets, - labelText: polygon.label!, - labelStyle: polygon.labelStyle, + bounds: getBounds(polygon), + textPainter: textPainter, rotationRad: map.rotationRad, rotate: polygon.rotateLabel, padding: 20,