centroid2D method

Position? centroid2D({
  1. Dimensionality dimensionality = Dimensionality.areal,
  2. PositionScheme scheme = Position.scheme,
})

Returns the centroid of a geometry represented by this position series calculated in a cartesian 2D plane.

The centroid is - as by definition - a geometric center of mass of a geometry.

The centroid is computed according to dimensionality:

  • Dimensionality.volymetric: not supported, works as areal
  • Dimensionality.areal: weighted by the area with this position series representing a polygon with positions in the counterclockwise (CCW) order.
  • Dimensionality.linear: computed from midpoints of line segments that are weighted by the length of each line segment.
  • Dimensionality.punctual: the arithmetic mean of all separate positions in this series.

Returns null if a centroid position could not be calculated.

Use scheme to set the position scheme:

  • Position.scheme for generic position data (geographic, projected or any other), this is also the default
  • Projected.scheme for projected position data
  • Geographic.scheme for geographic position data

See also Centroid in Wikipedia.

Implementation

Position? centroid2D({
  Dimensionality dimensionality = Dimensionality.areal,
  PositionScheme scheme = Position.scheme,
}) {
  final topoDim = dimensionality.topologicalDimension;
  final posCount = positionCount;

  // Areal geometry (weighted by area triangles).
  if (topoDim >= 2 && posCount >= 3) {
    // See "Of a polygon" in https://en.wikipedia.org/wiki/Centroid
    var area = 0.0;
    var cx = 0.0;
    var cy = 0.0;
    var x1 = x(0);
    var y1 = y(0);
    for (var i = 1; i <= posCount; i++) {
      final isLast = i == posCount;
      final x2 = x(isLast ? 0 : i);
      final y2 = y(isLast ? 0 : i);
      final shoelace = x1 * y2 - x2 * y1;
      area += shoelace;
      cx += (x1 + x2) * shoelace;
      cy += (y1 + y2) * shoelace;
      x1 = x2;
      y1 = y2;
    }
    if (area.abs() > 0.0) {
      final area6 = 6.0 * (area / 2.0);
      return scheme.position.call(
        x: cx / area6,
        y: cy / area6,
      );
    }
  }

  // Linear geometry (weighted by line segments).
  if (topoDim >= 1 && posCount >= 2) {
    var length = 0.0;
    var cx = 0.0;
    var cy = 0.0;
    var x1 = x(0);
    var y1 = y(0);
    for (var i = 1; i < posCount; i++) {
      final x2 = x(i);
      final y2 = y(i);
      final dx = x2 - x1;
      final dy = y2 - y1;
      final segmentLength = math.sqrt(dx * dx + dy * dy);
      if (segmentLength > 0.0) {
        length += segmentLength;
        cx += segmentLength * (x1 + x2) / 2.0;
        cy += segmentLength * (y1 + y2) / 2.0;
      }
      x1 = x2;
      y1 = y2;
    }
    if (length > 0.0) {
      return scheme.position.call(
        x: cx / length,
        y: cy / length,
      );
    }
  }

  // Punctual geometry (arithmethic mean of all points).
  if (posCount >= 1) {
    var cx = 0.0;
    var cy = 0.0;
    for (var i = 0; i < posCount; i++) {
      cx += x(i);
      cy += y(i);
    }
    return scheme.position.call(
      x: cx / posCount,
      y: cy / posCount,
    );
  }

  // could not calculate
  return null;
}