public static boolean isConvex(LinearRing ring) {
    double threshold = 1e-3;
    double minPointsToCheck = 10;
    CoordinateSequence seq = ring.getCoordinateSequence();
    int size = seq.size();
    if (size <= 3) {
      return false;
    }

    // ignore leading repeated points
    double c0x = seq.getX(0);
    double c0y = seq.getY(0);
    double c1x = Double.NaN, c1y = Double.NaN;
    int i;
    for (i = 1; i < size; i++) {
      c1x = seq.getX(i);
      c1y = seq.getY(i);
      if (c1x != c0x || c1y != c0y) {
        break;
      }
    }

    double dx1 = c1x - c0x;
    double dy1 = c1y - c0y;

    double negZ = 1e-20, posZ = 1e-20;

    // need to wrap around to make sure the triangle formed by last and first points does not change sign
    for (; i <= size + 1; i++) {
      // first and last point should be the same, so skip index 0
      int idx = i < size ? i : (i + 1 - size);
      double c2x = seq.getX(idx);
      double c2y = seq.getY(idx);

      double dx2 = c2x - c1x;
      double dy2 = c2y - c1y;
      double z = dx1 * dy2 - dy1 * dx2;

      double absZ = Math.abs(z);

      // look for sign changes in the triangles formed by sequential points
      // but, we want to allow for rounding errors and small concavities relative to the overall shape
      // so track the largest positive and negative threshold for triangle area and compare them once we
      // have enough points
      boolean extendedBounds = false;
      if (z < 0 && absZ > negZ) {
        negZ = absZ;
        extendedBounds = true;
      } else if (z > 0 && absZ > posZ) {
        posZ = absZ;
        extendedBounds = true;
      }

      if (i == minPointsToCheck || (i > minPointsToCheck && extendedBounds)) {
        double ratio = negZ < posZ ? negZ / posZ : posZ / negZ;
        if (ratio > threshold) {
          return false;
        }
      }

      c1x = c2x;
      c1y = c2y;
      dx1 = dx2;
      dy1 = dy2;
    }
    return (negZ < posZ ? negZ / posZ : posZ / negZ) < threshold;
  }
Returns {@code true} if the signed area of the triangle formed by 3 sequential points changes sign anywhere along
{@code ring}, ignoring repeated and collinear points.
planetiler-core/src/main/java/com/onthegomap/planetiler/geo/GeoUtils.java