Skip to content

Commit 374672d

Browse files
authored
fix: fixed multiple issues in PolygonLayer and PolylineLayer (#1925)
1 parent c1ae4fb commit 374672d

File tree

8 files changed

+46
-122
lines changed

8 files changed

+46
-122
lines changed

CHANGELOG.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,16 @@ Please consider [donating](https://docs.fleaflet.dev/supporters#support-us) or [
44

55
This CHANGELOG does not include every commit and/or PR - it is a hand picked selection of the most important ones. For a full list of changes, please check the GitHub repository releases/tags.
66

7+
## [7.0.2] - 2024/07/XX
8+
9+
> Note that this version causes a technically breaking change by removing `PolygonLayer.useDynamicUpdate` & `PolylineLayer.useDynamicUpdate`, introduced in v7.0.1. However, the implementations for these was broken on introduction, and their intended purpose no longer exists. Therefore, these should not have been used in any capacity, and should not affect any projects.
10+
11+
Contains the following user-affecting bug fixes:
12+
13+
- Fixed significant performance issues with `PolygonLayer` & `PolylineLayer` inadvertently introduced by v7.0.1 - [#1925](https://github.com/fleaflet/flutter_map/pull/1925) for [#1921](https://github.com/fleaflet/flutter_map/issues/1921)
14+
- Fixed bug where the holes of a `Polygon` would only appear if their winding direction was opposite to the direction of the `Polygon.points` - [#1925](https://github.com/fleaflet/flutter_map/pull/1925) for [#1924](https://github.com/fleaflet/flutter_map/issues/1924)
15+
- Relaxed constraint on 'package:logger' dependency - [#1922](https://github.com/fleaflet/flutter_map/pull/1922) for [#1916](https://github.com/fleaflet/flutter_map/issues/1916)
16+
717
## [7.0.1] - 2024/06/09
818

919
Contains the following user-affecting bug fixes:
@@ -62,10 +72,12 @@ Many thanks to these contributors (in no particular order):
6272
- @monsieurtanuki
6373
- ... and all the maintainers
6474

65-
## [6.2.0] - 2024/05/XX
75+
## [6.2.1] - 2024/05/27
6676

6777
> If possible, prefer to update directly to v7. This version is provided only to enable Flutter 3.22 compatibility without requiring a breaking change.
6878
79+
> v6.2.0 was retracted from pub.dev due to a mistake in the release preparation. For more information, see [this comment](https://github.com/fleaflet/flutter_map/pull/1891#issuecomment-2134069848). v6.2.1 is the replacement without the issues.
80+
6981
Contains the following user-affecting changes:
7082

7183
- Added support for Flutter 2.22 - [#1883](https://github.com/fleaflet/flutter_map/pull/1883)

example/lib/pages/polygon.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,8 @@ class _PolygonPageState extends State<PolygonPage> {
173173
(latlngs) => latlngs
174174
.map((latlng) =>
175175
LatLng(latlng.latitude - 6, latlng.longitude + 8))
176+
.toList()
177+
.reversed // Test that holes are always cut, no matter winding
176178
.toList(),
177179
)
178180
.toList(),

lib/src/layer/polygon_layer/painter.dart

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,11 @@ base class _PolygonPainter<R extends Object>
281281
// https://github.com/fleaflet/flutter_map/issues/1898.
282282
final holePointsList = polygon.holePointsList;
283283
if (holePointsList != null && holePointsList.isNotEmpty) {
284+
// See `Path.combine` comments below
285+
// Avoids failing to cut holes if the winding directions of the holes
286+
// and the normal points are the same
287+
filledPath.fillType = PathFillType.evenOdd;
288+
284289
final holeOffsetsList = List<List<Offset>>.generate(
285290
holePointsList.length,
286291
(i) => getOffsets(camera, origin, holePointsList[i]),
@@ -293,8 +298,9 @@ base class _PolygonPainter<R extends Object>
293298
// TODO: Potentially more efficient and may change the need to do
294299
// opacity checking - needs testing. However,
295300
// https://github.com/flutter/flutter/issues/44572 prevents this.
301+
// Also need to verify if `xor` or `difference` is preferred.
296302
/*filledPath = Path.combine(
297-
PathOperation.difference,
303+
PathOperation.xor,
298304
filledPath,
299305
Path()..addPolygon(holeOffsets, true),
300306
);*/

lib/src/layer/polygon_layer/polygon_layer.dart

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,6 @@ base class PolygonLayer<R extends Object>
7979
this.drawLabelsLast = false,
8080
this.hitNotifier,
8181
super.simplificationTolerance,
82-
super.useDynamicUpdate,
8382
}) : super();
8483

8584
@override

lib/src/layer/polyline_layer/polyline_layer.dart

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@ base class PolylineLayer<R extends Object>
5252
this.cullingMargin = 10,
5353
this.hitNotifier,
5454
this.minimumHitbox = 10,
55-
super.useDynamicUpdate,
5655
super.simplificationTolerance,
5756
}) : super();
5857

lib/src/layer/shared/layer_projection_simplification/state.dart

Lines changed: 23 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import 'dart:collection';
2-
31
import 'package:flutter/material.dart';
42
import 'package:flutter_map/flutter_map.dart';
53
import 'package:flutter_map/src/layer/shared/layer_projection_simplification/widget.dart';
@@ -46,9 +44,8 @@ mixin ProjectionSimplificationManagement<
4644
/// after [build] has been invoked.
4745
late Iterable<ProjectedElement> simplifiedElements;
4846

49-
final _cachedProjectedElements = SplayTreeMap<int, ProjectedElement>();
50-
final _cachedSimplifiedElements =
51-
<int, SplayTreeMap<int, ProjectedElement>>{};
47+
Iterable<ProjectedElement>? _cachedProjectedElements;
48+
final _cachedSimplifiedElements = <int, Iterable<ProjectedElement>>{};
5249

5350
double? _devicePixelRatio;
5451

@@ -57,88 +54,8 @@ mixin ProjectionSimplificationManagement<
5754
void didUpdateWidget(W oldWidget) {
5855
super.didUpdateWidget(oldWidget);
5956

60-
if (!widget.useDynamicUpdate) return;
61-
62-
final camera = MapCamera.of(context);
63-
64-
// If the simplification tolerance has changed, then clear all
65-
// simplifications to allow `build` to re-simplify.
66-
final hasSimplficationToleranceChanged =
67-
oldWidget.simplificationTolerance != widget.simplificationTolerance;
68-
if (hasSimplficationToleranceChanged) _cachedSimplifiedElements.clear();
69-
70-
final elements = getElements(widget);
71-
72-
// We specifically only use basic equality here, and not deep, since deep
73-
// will always be equal.
74-
if (getElements(oldWidget) == elements) return;
75-
76-
// Loop through all polygons in the new widget
77-
// If not in the projection cache, then re-project. Also, do the same for
78-
// the simplification cache, across all zoom levels for each polygon.
79-
// Then, remove all polygons no longer in the new widget from each cache.
80-
//
81-
// This is an O(n^3) operation, assuming n is the number of polygons
82-
// (assuming they are all similar, otherwise exact runtime will depend on
83-
// existing cache lengths, etc.). However, compared to previous versions, it
84-
// takes approximately the same duration, as it relieves the work from the
85-
// `build` method.
86-
for (final element in getElements(widget)) {
87-
final existingProjection = _cachedProjectedElements[element.hashCode];
88-
89-
if (existingProjection == null) {
90-
_cachedProjectedElements[element.hashCode] =
91-
projectElement(projection: camera.crs.projection, element: element);
92-
93-
if (hasSimplficationToleranceChanged) continue;
94-
95-
for (final MapEntry(key: zoomLvl, value: simplifiedElements)
96-
in _cachedSimplifiedElements.entries) {
97-
final simplificationTolerance = getEffectiveSimplificationTolerance(
98-
crs: camera.crs,
99-
zoom: zoomLvl,
100-
// When the tolerance changes, this method handles resetting and filling
101-
pixelTolerance: widget.simplificationTolerance,
102-
// When the DPR changes, the `build` method handles resetting and filling
103-
devicePixelRatio: MediaQuery.devicePixelRatioOf(context),
104-
);
105-
106-
final existingSimplification = simplifiedElements[element.hashCode];
107-
108-
if (existingSimplification == null) {
109-
_cachedSimplifiedElements[zoomLvl]![element.hashCode] =
110-
simplifyProjectedElement(
111-
projectedElement: _cachedProjectedElements[element.hashCode]!,
112-
tolerance: simplificationTolerance,
113-
);
114-
}
115-
}
116-
}
117-
}
118-
119-
_cachedProjectedElements
120-
.removeWhere((k, v) => !elements.map((p) => p.hashCode).contains(k));
121-
122-
for (final simplifiedElement in _cachedSimplifiedElements.values) {
123-
simplifiedElement
124-
.removeWhere((k, v) => !elements.map((p) => p.hashCode).contains(k));
125-
}
126-
}
127-
128-
@mustCallSuper
129-
@override
130-
void didChangeDependencies() {
131-
super.didChangeDependencies();
132-
133-
// Performed once only, at load - projects all initial polygons
134-
if (_cachedProjectedElements.isEmpty) {
135-
final camera = MapCamera.of(context);
136-
137-
for (final element in getElements(widget)) {
138-
_cachedProjectedElements[element.hashCode] =
139-
projectElement(projection: camera.crs.projection, element: element);
140-
}
141-
}
57+
_cachedProjectedElements = null;
58+
_cachedSimplifiedElements.clear();
14259
}
14360

14461
@mustBeOverridden
@@ -147,11 +64,22 @@ mixin ProjectionSimplificationManagement<
14764
Widget build(BuildContext context) {
14865
final camera = MapCamera.of(context);
14966

67+
final elements = getElements(widget);
68+
69+
final projected = _cachedProjectedElements ??= List.generate(
70+
elements.length,
71+
(i) => projectElement(
72+
projection: camera.crs.projection,
73+
element: elements.elementAt(i),
74+
),
75+
growable: false,
76+
);
77+
15078
// The `build` method handles initial simplification, re-simplification only
15179
// when the DPR has changed, and re-simplification implicitly when the
15280
// tolerance is changed (and the cache is emptied by `didUpdateWidget`).
15381
if (widget.simplificationTolerance == 0) {
154-
simplifiedElements = _cachedProjectedElements.values;
82+
simplifiedElements = projected;
15583
} else {
15684
// If the DPR has changed, invalidate the simplification cache
15785
final newDPR = MediaQuery.devicePixelRatioOf(context);
@@ -160,17 +88,13 @@ mixin ProjectionSimplificationManagement<
16088
_cachedSimplifiedElements.clear();
16189
}
16290

163-
simplifiedElements = (_cachedSimplifiedElements[camera.zoom.floor()] ??=
164-
SplayTreeMap.fromIterables(
165-
_cachedProjectedElements.keys,
166-
_simplifyElements(
167-
camera: camera,
168-
projectedElements: _cachedProjectedElements.values,
169-
pixelTolerance: widget.simplificationTolerance,
170-
devicePixelRatio: newDPR,
171-
),
172-
))
173-
.values;
91+
simplifiedElements =
92+
(_cachedSimplifiedElements[camera.zoom.floor()] ??= _simplifyElements(
93+
camera: camera,
94+
projectedElements: projected,
95+
pixelTolerance: widget.simplificationTolerance,
96+
devicePixelRatio: newDPR,
97+
));
17498
}
17599

176100
return Builder(

lib/src/layer/shared/layer_projection_simplification/widget.dart

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,11 @@
11
import 'package:flutter/material.dart';
2-
import 'package:flutter_map/flutter_map.dart';
32
import 'package:flutter_map/src/layer/shared/layer_projection_simplification/state.dart';
43

54
/// A [StatefulWidget] that includes the properties used by the [State] component
65
/// which mixes [ProjectionSimplificationManagement] in
76
@immutable
87
abstract base class ProjectionSimplificationManagementSupportedWidget
98
extends StatefulWidget {
10-
/// Whether to apply the auto-update algorithm to re-paint the necessary
11-
/// [Polygon]s when they change
12-
///
13-
/// It is recommended to leave this `true`, as default, otherwise changes to
14-
/// child polygons may not update. It will detect which polygons have changed,
15-
/// and only 'update' (re-project and re-simplify) those that are necessary.
16-
///
17-
/// However, where there are a large number of polygons, the majority (or more)
18-
/// of which change at the same time, then it is recommended to set this
19-
/// `false`. This will avoid a large unnecessary loop to detect changes, and
20-
/// is likely to improve performance on state changes. If `false`, then the
21-
/// layer will need to be manually rebuilt from scratch using new [Key]s
22-
/// whenever necessary. Do not use a [UniqueKey] : this will cause the entire
23-
/// widget to reset and rebuild every time the map camera changes.
24-
final bool useDynamicUpdate;
25-
269
/// Distance between two neighboring polyline points, in logical pixels scaled
2710
/// to floored zoom
2811
///
@@ -40,7 +23,6 @@ abstract base class ProjectionSimplificationManagementSupportedWidget
4023
/// necessary assertions are made.
4124
const ProjectionSimplificationManagementSupportedWidget({
4225
super.key,
43-
this.useDynamicUpdate = true,
4426
this.simplificationTolerance = 0.3,
4527
}) : assert(
4628
simplificationTolerance >= 0,

pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name: flutter_map
22
description: A versatile mapping package for Flutter, that's simple and easy to learn, yet completely customizable and configurable
3-
version: 7.0.1
3+
version: 7.0.2
44

55
repository: https://github.com/fleaflet/flutter_map
66
issue_tracker: https://github.com/fleaflet/flutter_map/issues

0 commit comments

Comments
 (0)