forked from fleaflet/flutter_map
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcircle_layer.dart
162 lines (145 loc) · 5.6 KB
/
circle_layer.dart
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
import 'dart:ui';
import 'package:flutter/widgets.dart';
import 'package:flutter_map/src/layer/general/mobile_layer_transformer.dart';
import 'package:flutter_map/src/map/camera/camera.dart';
import 'package:latlong2/latlong.dart' hide Path;
/// Immutable marker options for circle markers
@immutable
class CircleMarker {
final Key? key;
final LatLng point;
final double radius;
final Color color;
final double borderStrokeWidth;
final Color borderColor;
final bool useRadiusInMeter;
const CircleMarker({
required this.point,
required this.radius,
this.key,
this.useRadiusInMeter = false,
this.color = const Color(0xFF00FF00),
this.borderStrokeWidth = 0.0,
this.borderColor = const Color(0xFFFFFF00),
});
}
@immutable
class CircleLayer extends StatelessWidget {
final List<CircleMarker> circles;
const CircleLayer({super.key, required this.circles});
@override
Widget build(BuildContext context) {
final map = MapCamera.of(context);
return MobileLayerTransformer(
child: CustomPaint(
painter: CirclePainter(circles, map),
size: Size(map.size.x, map.size.y),
isComplex: true,
),
);
}
}
@immutable
class CirclePainter extends CustomPainter {
final List<CircleMarker> circles;
final MapCamera map;
const CirclePainter(this.circles, this.map);
@override
void paint(Canvas canvas, Size size) {
const distance = Distance();
final rect = Offset.zero & size;
canvas.clipRect(rect);
// Let's calculate all the points grouped by color and radius
final points = <Color, Map<double, List<Offset>>>{};
final pointsFilledBorder = <Color, Map<double, List<Offset>>>{};
final pointsBorder = <Color, Map<double, Map<double, List<Offset>>>>{};
for (final circle in circles) {
final offset = map.getOffsetFromOrigin(circle.point);
double radius = circle.radius;
if (circle.useRadiusInMeter) {
final r = distance.offset(circle.point, circle.radius, 180);
final delta = offset - map.getOffsetFromOrigin(r);
radius = delta.distance;
}
points[circle.color] ??= {};
points[circle.color]![radius] ??= [];
points[circle.color]![radius]!.add(offset);
if (circle.borderStrokeWidth > 0) {
// Check if color have some transparency or not
// As drawPoints is more efficient than drawCircle
if (circle.color.alpha == 0xFF) {
double radiusBorder = circle.radius + circle.borderStrokeWidth;
if (circle.useRadiusInMeter) {
final rBorder = distance.offset(circle.point, radiusBorder, 180);
final deltaBorder = offset - map.getOffsetFromOrigin(rBorder);
radiusBorder = deltaBorder.distance;
}
pointsFilledBorder[circle.borderColor] ??= {};
pointsFilledBorder[circle.borderColor]![radiusBorder] ??= [];
pointsFilledBorder[circle.borderColor]![radiusBorder]!.add(offset);
} else {
double realRadius = circle.radius;
if (circle.useRadiusInMeter) {
final rBorder = distance.offset(circle.point, realRadius, 180);
final deltaBorder = offset - map.getOffsetFromOrigin(rBorder);
realRadius = deltaBorder.distance;
}
pointsBorder[circle.borderColor] ??= {};
pointsBorder[circle.borderColor]![circle.borderStrokeWidth] ??= {};
pointsBorder[circle.borderColor]![circle.borderStrokeWidth]![
realRadius] ??= [];
pointsBorder[circle.borderColor]![circle.borderStrokeWidth]![
realRadius]!
.add(offset);
}
}
}
// Now that all the points are grouped, let's draw them
final paintBorder = Paint()..style = PaintingStyle.stroke;
for (final color in pointsBorder.keys) {
final paint = paintBorder..color = color;
for (final borderWidth in pointsBorder[color]!.keys) {
final pointsByRadius = pointsBorder[color]![borderWidth]!;
final radiusPaint = paint..strokeWidth = borderWidth;
for (final radius in pointsByRadius.keys) {
final pointsByRadiusColor = pointsByRadius[radius]!;
for (final offset in pointsByRadiusColor) {
_paintCircle(canvas, offset, radius, radiusPaint);
}
}
}
}
// Then the filled border in order to be under the circle
final paintPoint = Paint()
..isAntiAlias = false
..strokeCap = StrokeCap.round;
for (final color in pointsFilledBorder.keys) {
final paint = paintPoint..color = color;
final pointsByRadius = pointsFilledBorder[color]!;
for (final radius in pointsByRadius.keys) {
final pointsByRadiusColor = pointsByRadius[radius]!;
final radiusPaint = paint..strokeWidth = radius * 2;
_paintPoints(canvas, pointsByRadiusColor, radiusPaint);
}
}
// And then the circle
for (final color in points.keys) {
final paint = paintPoint..color = color;
final pointsByRadius = points[color]!;
for (final radius in pointsByRadius.keys) {
final pointsByRadiusColor = pointsByRadius[radius]!;
final radiusPaint = paint..strokeWidth = radius * 2;
_paintPoints(canvas, pointsByRadiusColor, radiusPaint);
}
}
}
void _paintPoints(Canvas canvas, List<Offset> offsets, Paint paint) {
canvas.drawPoints(PointMode.points, offsets, paint);
}
void _paintCircle(Canvas canvas, Offset offset, double radius, Paint paint) {
canvas.drawCircle(offset, radius, paint);
}
@override
bool shouldRepaint(CirclePainter oldDelegate) =>
circles != oldDelegate.circles || map != oldDelegate.map;
}