Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUG] CircleMarker incorrectly fills with stroke color and has incorrect size #1688

Closed
vitalsh opened this issue Oct 10, 2023 · 13 comments · Fixed by #1692
Closed

[BUG] CircleMarker incorrectly fills with stroke color and has incorrect size #1688

vitalsh opened this issue Oct 10, 2023 · 13 comments · Fixed by #1692
Labels
bug This issue reports broken functionality or another error P: 1 (important)

Comments

@vitalsh
Copy link

vitalsh commented Oct 10, 2023

What is the bug?

CircleMarker fills the circle with either CircleMarker.color or CircleMarker.borderColor

How can we reproduce it?

Looks like any circle will be filled with the stroke color regardless of the settings/parameters

Do you have a potential solution?

circle_layer.dart : line 104 and 118
this line looks suspicious:
final radiusPaint = paint..strokeWidth = radius;
should probably be:
final radiusPaint = paint..strokeWidth = borderStrokeWidth;

Platforms

Any

Severity

Obtrusive: Prevents normal functioning but causes no errors in the console

@vitalsh vitalsh added bug This issue reports broken functionality or another error needs triage This new bug report needs reproducing and prioritizing labels Oct 10, 2023
@vitalsh vitalsh changed the title CircleMarker fills the circle with color CircleMarker fills the circle with color/borderColor Oct 10, 2023
@JaffaKetchup
Copy link
Member

Thanks for reporting, we'll look into it along with the other issues from v6.
Looks like this issue may have been introduced by #1679, heads up @Alexays (I'm happy to fix, but you might have a reason or way you want to, so just pinging).

@JaffaKetchup JaffaKetchup changed the title CircleMarker fills the circle with color/borderColor [BUG] CircleMarker incorrectly fills with stroke color Oct 10, 2023
@JaffaKetchup JaffaKetchup added P: 1 (important) and removed needs triage This new bug report needs reproducing and prioritizing labels Oct 10, 2023
@TesteurManiak
Copy link
Collaborator

TesteurManiak commented Oct 11, 2023

Seems to me like an issue with the base implementation, it uses drawPoints to paint the borders which won't work to create just an outline with empty center, drawCircle with a PaintingStyle.stroke should be the way to go.

@mootw
Copy link
Collaborator

mootw commented Oct 12, 2023

can confirm this issue, not sure why i did not pick it up during my review. i think im also seeing incorrect radius on the rendering too.

@JaffaKetchup
Copy link
Member

Potentially radius and diameter are being switched up.

@Alexays
Copy link
Contributor

Alexays commented Oct 15, 2023

I'm on holiday until Wednesday, I'll be able to look into it from then on, sorry for the breaking changes 🥲

@JaffaKetchup
Copy link
Member

Ah, no worries. I'll also take a look into it, but you just might have a preferred method.

@JaffaKetchup JaffaKetchup changed the title [BUG] CircleMarker incorrectly fills with stroke color [BUG] CircleMarker incorrectly fills with stroke color and has incorrect size Oct 15, 2023
@Alexays
Copy link
Contributor

Alexays commented Oct 15, 2023

I'll go using drawCircle only if the marker is not filled, in that way we preserve as much a we can the perf improvement of using drawPoint.

@JaffaKetchup
Copy link
Member

Not quite sure what you mean, but there's no rush at all!

@TesteurManiak
Copy link
Collaborator

@Alexays you should also use drawCircle if the marker is filled with color with an opacity inferior to 1.

@DevTello
Copy link

Hi, when do you plan to update the version with fixes? This bug is quite critical to me

@JaffaKetchup
Copy link
Member

Hi @DevTello, we'll release a hotfix version once we've resolved a couple more of the v6 bugs. You can use the 'master' branch for now.

@siunus
Copy link

siunus commented Oct 26, 2023

I just created this as a custom component with code from @Alexays
Just in case anyone needs it.

import 'dart:ui';
import 'package:flutter/widgets.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:latlong2/latlong.dart' hide Path;

/// Immutable marker options for circle markers
@immutable
class CustomCircleMarker {
  final Key? key;
  final LatLng point;
  final double radius;
  final Color color;
  final double borderStrokeWidth;
  final Color borderColor;
  final bool useRadiusInMeter;

  const CustomCircleMarker({
    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 CustomCircleLayer extends StatelessWidget {
  final List<CustomCircleMarker> circles;

  const CustomCircleLayer({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<CustomCircleMarker> 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) => false;
}

Then

CustomCircleLayer(
  circles: [
      CustomCircleMarker(
        point: LatLng(),
        radius: 75,
        color: Colors.green.withOpacity(0.5),
        borderStrokeWidth: 2,
        borderColor: Colors.green,
        useRadiusInMeter: true,
      ),
  ],
),

@JaffaKetchup
Copy link
Member

@siunus This is already available in the latest release!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug This issue reports broken functionality or another error P: 1 (important)
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants