private Iterator<TileFeatures> groupIntoTiles(Iterator<SortableFeature> entries) {
    // entries are sorted by tile ID, so group consecutive entries in same tile into tiles
    if (!entries.hasNext()) {
      return Collections.emptyIterator();
    }

    /*
     * Features from sorter are ordered by tile, so iterate through features as long as
     * they are in the same tile and return that group.
     */
    SortableFeature firstFeature = entries.next();
    return new Iterator<>() {
      private SortableFeature lastFeature = firstFeature;
      private int lastTileId = extractTileFromKey(firstFeature.key());

      @Override
      public boolean hasNext() {
        return lastFeature != null;
      }

      @Override
      public TileFeatures next() {
        TileFeatures result = new TileFeatures(lastTileId);
        result.add(lastFeature);
        int lastTile = lastTileId;

        while (entries.hasNext()) {
          SortableFeature next = entries.next();
          lastFeature = next;
          lastTileId = extractTileFromKey(lastFeature.key());
          if (lastTile != lastTileId) {
            return result;
          }
          result.add(next);
        }
        lastFeature = null;
        return result;
      }
    };
  }
Reads temp features using {@code threads} parallel threads and merges into a sorted list.

@param threads The number of parallel read threads to spawn
@return a {@link Reader} with a handle to the new read threads that were spawned, and in {@link Iterable} that can
        be used to iterate over the results.
planetiler-core/src/main/java/com/onthegomap/planetiler/collection/FeatureGroup.java