static List<LongArrayList> connectPolygonSegments(List<LongArrayList> outer) {
    LongObjectHashMap<LongArrayList> endpointIndex = Hppc.newLongObjectHashMap(outer.size() * 2);
    List<LongArrayList> completeRings = new ArrayList<>(outer.size());

    for (LongArrayList ids : outer) {
      if (ids.size() <= 1) {
        continue;
      }

      long first = ids.get(0);
      long last = ids.get(ids.size() - 1);

      // optimization - skip rings that are already closed (as long as they have enough points)
      if (first == last) {
        if (ids.size() >= 4) {
          completeRings.add(ids);
        }
        continue;
      }

      LongArrayList match, nextMatch;

      if ((match = endpointIndex.get(first)) != null) {
        endpointIndex.remove(first);
        if (first == match.get(0)) {
          reverseInPlace(match);
        }
        appendToSkipFirst(match, ids);
        if ((nextMatch = endpointIndex.get(last)) != null && nextMatch != match) {
          endpointIndex.remove(last);
          if (last == nextMatch.get(0)) {
            appendToSkipFirst(match, nextMatch);
          } else {
            appendToSkipFirst(match, reversedCopy(nextMatch));
          }
          endpointIndex.put(match.get(match.size() - 1), match);
        } else {
          endpointIndex.put(last, match);
        }
      } else if ((match = endpointIndex.get(last)) != null) {
        endpointIndex.remove(last);
        if (last == match.get(0)) {
          prependToSkipLast(match, ids);
        } else {
          appendToSkipFirst(match, reversedCopy(ids));
        }
        endpointIndex.put(first, match);
      } else {
        LongArrayList copy = new LongArrayList(ids);
        endpointIndex.put(first, copy);
        endpointIndex.put(last, copy);
      }
    }

    // iterate in sorted order to ensure the same input always produces the same output
    for (var entry : Hppc.sortedView(endpointIndex)) {
      LongArrayList value = entry.value;
      if (value.size() >= 4 && (value.get(0) == value.get(value.size() - 1) || entry.key == value.get(0))) {
        completeRings.add(value);
      }
    }
    return completeRings;
  }
Builds a multipolygon from linestrings containing the IDs of nodes in edge segments and a node ID location
provider.
<p>
Attempts to close rings where endpoints are within {@code minGap} units apart.

@param rings     edge segment node ID sequences
@param nodeCache node location provider
@param osmId     ID of this multipolygon
@param minGap    the minimum units apart to close a linestring
@return The new multipolygon
@throws GeometryException if building the polygon fails
planetiler-core/src/main/java/com/onthegomap/planetiler/reader/osm/OsmMultipolygon.java