mapbox-maps-flutter icon indicating copy to clipboard operation
mapbox-maps-flutter copied to clipboard

Overlapping Polygons Affect Opacity of Fill Color

Open kyrill-bo opened this issue 1 year ago • 1 comments

Problem: Currently, our system generates polygons comprising 360 vertices to display a red circle with a fill color set to red and an opacity of 10%. However, when these polygons overlap, the resulting opacity is not consistent. Overlapping polygons cause areas of the circle to become darker than the intended 10% opacity due to the additive effect of overlapping colors.

image

Expected Behavior: We expect the fill color opacity to remain consistent at 10% throughout the entire circle, regardless of overlapping polygons.

This is the current situation: image

What we need is: image

nonSmokingAreas[result[i].id] =
    (await nonSmokingAreaManager?.create(
  PolygonAnnotationOptions(
      fillColor: Colors.red.withOpacity(0.1).value,
      fillSortKey: 1,
      geometry: Polygon(
        coordinates: [
          List.generate(
            points.length,
            (index) => Position(
              points[index].lng,
              points[index].lat,
            ),
          ),
        ],
      ).toJson()),
))!;
import 'dart:math';

/// Generates a list of points representing a regular polygon around a given geographic coordinate (latitude and longitude) with a specified radius.
///
/// Parameters:
/// - `lat` (double): The latitude of the central position of the polygon.
/// - `lng` (double): The longitude of the central position of the polygon.
/// - `radius` (double): The radius of the polygon in meters.
///
/// Returns:
/// - `List<({double lat, double lng})>`: A list of tuples, where each tuple represents a point on the generated polygon. Each point is represented by latitude and longitude.
///
/// Algorithm:
/// The function uses an iterative method to generate the points of the polygon. It calculates the position of each point using the distance (`radius`) and the angle relative to the central position. It uses the `_translateCoordinate` function to compute the geographic coordinates of each point.
///
/// 1. Initialize an empty list `points` to store the generated points.
/// 2. Iterate from 0 to 11 (total of 36 iterations) for creating a regular polygon.
/// 3. Calculate the angle (`angle`) for each point using the index of the iteration.
/// 4. Calculate the displacement (`dx` and `dy`) relative to the radius and angle.
/// 5. Use the `_translateCoordinate` function to compute the geographic coordinates of each point and add them to the `points` list.
/// 6. Return the `points` list containing all the generated points of the polygon.
///
/// Notes:
/// - The algorithm assumes the Earth is a perfect sphere and uses a simple approximation for computing the coordinates.
/// - The function generates a regular polygon with 36 vertices, which corresponds to a regular pentagon. The number of vertices can be changed by adjusting the loop condition.
List<({double lat, double lng})> listgeneratePolygonPoints(
    double lat, double lng, double radius) {
  List<({double lat, double lng})> points = [];

  for (int i = 0; i < 360; i++) {
    double angle = (2 * pi * i) / 360;
    double dx = radius * cos(angle);
    double dy = radius * sin(angle);
    final point = _translateCoordinate((lat: lat, lng: lng), dx, dy);
    points.add(point);
  }

  return points;
}

/// Translates the geographic coordinates of a point based on a given origin position, displacement in latitude and longitude, and Earth radius.
///
/// Parameters:
/// - `coordinate` ({double lat, double lng}): The initial coordinates tuple containing latitude and longitude.
/// - `dx` (double): The displacement in longitude.
/// - `dy` (double): The displacement in latitude.
///
/// Returns:
/// - `({double lat, double lng})`: A tuple containing the computed geographic coordinates of the point resulting from the displacement from the origin position.
///
/// Algorithm:
/// 1. Calculate the new latitude component (`lat`) based on the displacement in latitude and the Earth radius.
/// 2. Calculate the new longitude component (`lon`) based on the displacement in longitude, Earth radius, and the cosine of the latitude of the origin position.
/// 3. Return a tuple containing the calculated latitude and longitude of the new point.
///
/// Notes:
/// - The function considers the curvature of the Earth and uses a simple formula to compute the new coordinates.
/// - It is assumed that the displacement (`dx` and `dy`) is relatively small, so the curvature of the Earth can be neglected.
({double lat, double lng}) _translateCoordinate(
    ({double lat, double lng}) coordinate, double dx, double dy) {
  const double earthRadius = 6378137; // Earth radius in meters
  double lat = coordinate.lat + (dy / earthRadius) * (180 / pi);
  double lon = coordinate.lng +
      (dx / earthRadius) * (180 / pi) / cos(coordinate.lat * pi / 180);
  return (lat: lat, lng: lon);
}

kyrill-bo avatar Mar 20 '24 15:03 kyrill-bo

It looks like an expected behaviour to me. But you could merge your circles before putting them on the map. The current turf release doesn't support union operation, but ~~you could find a branch with added support in their PRs~~ (upd: I've tested it, it's completely non-working), or alternatively, you could use polybool library. Also, depending on the amount of circles you have, you may want to run this computation in isolate to prevent UI freezes.

Disclaimer: I'm not a maintainer of this repo.

simplylizz avatar May 11 '24 08:05 simplylizz