private static String generateMessage(TileCoord coord, Set<VectorTileFeatureForCmp> features0,
      Set<VectorTileFeatureForCmp> features1) {
      boolean featureCountMatches = features0.size() == features1.size();
      return """
        <<<
        feature diff on coord %s - featureCountMatches: %b (%d vs %d)

        additional in db0
        ---
        %s

        additional in db1
        ---
        %s
        >>>
        """.formatted(
        coord, featureCountMatches, features0.size(), features1.size(),
        getDiffJoined(features0, features1, "\n"),
        getDiffJoined(features1, features0, "\n"));
    }
Wrapper around {@link VectorTile.Feature} to compare vector tiles.
<ul>
<li>{@link VectorTile.Feature#id()} won't be compared
<li>{@link VectorTile.Feature#layer()} will be compared
<li>{@link VectorTile.Feature#geometry()} gets normalized for comparing
<li>{@link VectorTile.Feature#tags()} will be compared except for the rank attribute since the value produced by
planetiler is not stable and differs on every run (at least for parks)
<li>{@link VectorTile.Feature#group()} will be compared
</ul>
planetiler-core/src/main/java/com/onthegomap/planetiler/mbtiles/Compare.java