diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3e57f93fe..d32199ba1 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -5,10 +5,7 @@ on: [push] jobs: test: runs-on: ubuntu-latest - strategy: - matrix: - version: ['Release', 'Debug'] steps: - uses: actions/checkout@v3 - - run: uname -a; BUILDTYPE=${{ matrix.version }} make - - run: make test \ No newline at end of file + - run: uname -a; make + - run: make test diff --git a/CHANGELOG.md b/CHANGELOG.md index c51372511..26bb202a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 2.47.0 + +* Stabilize feature order in tippecanoe-overzoom when --preserve-feature-order is specified but the sequence attribute is not present + # 2.46.0 * Polygon dust returns to having the attributes of the contributing feature nearest the placeholder instead of the contributing feature with the largest area. diff --git a/Makefile b/Makefile index 99d330bdc..1384daad0 100644 --- a/Makefile +++ b/Makefile @@ -278,6 +278,12 @@ overzoom-test: tippecanoe-overzoom ./tippecanoe-decode tests/pbf/13-1310-3166.pbf 13 1310 3166 > tests/pbf/13-1310-3166.pbf.json.check cmp tests/pbf/13-1310-3166.pbf.json.check tests/pbf/13-1310-3166.pbf.json rm tests/pbf/13-1310-3166.pbf tests/pbf/13-1310-3166.pbf.json.check + # Make sure feature order is stable + ./tippecanoe-overzoom --preserve-input-order -o tests/pbf/11-327-791-out.pbf tests/pbf/11-327-791.pbf 11/327/791 11/327/791 + ./tippecanoe-decode tests/pbf/11-327-791.pbf 11 327 791 > tests/pbf/11-327-791.json + ./tippecanoe-decode tests/pbf/11-327-791-out.pbf 11 327 791 > tests/pbf/11-327-791-out.json + cmp tests/pbf/11-327-791.json tests/pbf/11-327-791-out.json + rm tests/pbf/11-327-791.json tests/pbf/11-327-791-out.json tests/pbf/11-327-791-out.pbf # Different detail and buffer, and attribute stripping ./tippecanoe-overzoom -d8 -b30 -y NAME -y name -y scalerank -o tests/pbf/13-1310-3166-8-30.pbf tests/pbf/11-327-791.pbf 11/327/791 13/1310/3166 ./tippecanoe-decode tests/pbf/13-1310-3166-8-30.pbf 13 1310 3166 > tests/pbf/13-1310-3166-8-30.pbf.json.check diff --git a/clip.cpp b/clip.cpp index 4972afa24..0bfad160f 100644 --- a/clip.cpp +++ b/clip.cpp @@ -1026,7 +1026,7 @@ std::string overzoom(const mvt_tile &tile, int oz, int ox, int oy, int nz, int n } if (preserve_input_order) { - std::sort(outlayer.features.begin(), outlayer.features.end(), preservecmp); + std::stable_sort(outlayer.features.begin(), outlayer.features.end(), preservecmp); } if (outlayer.features.size() > 0) { diff --git a/dirtiles.cpp b/dirtiles.cpp index e88bb300b..98138bd5a 100644 --- a/dirtiles.cpp +++ b/dirtiles.cpp @@ -176,7 +176,7 @@ std::vector enumerate_dirtiles(const char *fname, int minzoom, int maxzoom) closedir(d1); } - std::sort(tiles.begin(), tiles.end()); + std::stable_sort(tiles.begin(), tiles.end()); return tiles; } diff --git a/geometry.cpp b/geometry.cpp index 2b4343d37..bb73fdf3f 100644 --- a/geometry.cpp +++ b/geometry.cpp @@ -1189,7 +1189,7 @@ drawvec polygon_to_anchor(const drawvec &geom) { std::vector candidates; for (size_t pass = 0; pass < 4; pass++) { - std::sort(points.begin(), points.end(), sorty_sorter(pass)); + std::stable_sort(points.begin(), points.end(), sorty_sorter(pass)); for (size_t i = 1; i < points.size(); i++) { double dx = points[i].x - points[i - 1].x; @@ -1213,7 +1213,7 @@ drawvec polygon_to_anchor(const drawvec &geom) { // segment, if we find one whose midpoint is inside the polygon and // far enough from any edge to be good enough, stop looking. - std::sort(candidates.begin(), candidates.end()); + std::stable_sort(candidates.begin(), candidates.end()); // only check the top 50 stride midpoints, since this list can be quite large for (size_t i = 0; i < candidates.size() && i < 50; i++) { double maybe_goodness = label_goodness(geom, candidates[i].x, candidates[i].y); diff --git a/main.cpp b/main.cpp index 152121840..fdac47770 100644 --- a/main.cpp +++ b/main.cpp @@ -2704,7 +2704,7 @@ std::pair read_input(std::vector &sources, char *fname, i } } - std::sort(ddv.begin(), ddv.end()); + std::stable_sort(ddv.begin(), ddv.end()); size_t i = 0; for (int z = 0; z <= basezoom; z++) { diff --git a/mvt.cpp b/mvt.cpp index b985188c4..04c1ef9dc 100644 --- a/mvt.cpp +++ b/mvt.cpp @@ -377,7 +377,7 @@ std::string mvt_tile::encode() { sorted_values.push_back(std::move(sv)); } - std::sort(sorted_values.begin(), sorted_values.end()); + std::stable_sort(sorted_values.begin(), sorted_values.end()); std::vector mapping; mapping.resize(sorted_values.size()); diff --git a/pmtiles_file.cpp b/pmtiles_file.cpp index 5cd78620d..e86e538b5 100644 --- a/pmtiles_file.cpp +++ b/pmtiles_file.cpp @@ -174,7 +174,7 @@ void mbtiles_map_image_to_pmtiles(char *fname, metadata m, bool tile_compression sqlite3_finalize(stmt); } - std::sort(tile_ids.begin(), tile_ids.end()); + std::stable_sort(tile_ids.begin(), tile_ids.end()); std::unordered_map> hash_to_offset_len; std::vector entries; @@ -263,7 +263,7 @@ void mbtiles_map_image_to_pmtiles(char *fname, metadata m, bool tile_compression // finalize PMTiles archive. { - std::sort(entries.begin(), entries.end(), pmtiles::entryv3_cmp()); + std::stable_sort(entries.begin(), entries.end(), pmtiles::entryv3_cmp()); std::string root_bytes; std::string leaves_bytes; diff --git a/serial.cpp b/serial.cpp index 83f114496..af405a8b3 100644 --- a/serial.cpp +++ b/serial.cpp @@ -604,7 +604,7 @@ int serialize_feature(struct serialization_state *sst, serial_feature &sf, std:: locs.push_back(encode_index(SHIFT_LEFT(scaled_geometry[i].x), SHIFT_LEFT(scaled_geometry[i].y))); } } - std::sort(locs.begin(), locs.end()); + std::stable_sort(locs.begin(), locs.end()); size_t n = 0; double sum = 0; for (size_t i = 1; i < locs.size(); i++) { diff --git a/shared_borders.cpp b/shared_borders.cpp index d79623763..2a1bd2eef 100644 --- a/shared_borders.cpp +++ b/shared_borders.cpp @@ -136,7 +136,7 @@ bool find_common_edges(std::vector &features, int z, int line_de } } - std::sort(edges.begin(), edges.end(), edgecmp_ring); + std::stable_sort(edges.begin(), edges.end(), edgecmp_ring); std::set necessaries; // Now mark all the points where the set of rings using the edge on one side @@ -397,7 +397,7 @@ bool find_common_edges(std::vector &features, int z, int line_de } } } - std::sort(order.begin(), order.end()); + std::stable_sort(order.begin(), order.end()); size_t merged = 0; for (size_t o = 0; o < order.size(); o++) { diff --git a/tile-join.cpp b/tile-join.cpp index c3e2b3fec..584dc7652 100644 --- a/tile-join.cpp +++ b/tile-join.cpp @@ -584,7 +584,7 @@ struct tileset_reader { next_overzoomed_tiles.clear(); } - std::sort(overzoomed_tiles.begin(), overzoomed_tiles.end(), tilecmp); + std::stable_sort(overzoomed_tiles.begin(), overzoomed_tiles.end(), tilecmp); overzoom_consumed_at_this_zoom = false; } diff --git a/tile.cpp b/tile.cpp index 033ec44d9..66fe121c9 100644 --- a/tile.cpp +++ b/tile.cpp @@ -426,7 +426,7 @@ static std::vector disassemble_multiplier_clusters(std::vector 1) { - std::sort(cluster.begin() + 1, cluster.end(), drop_sequence_cmp()); + std::stable_sort(cluster.begin() + 1, cluster.end(), drop_sequence_cmp()); } for (auto const &feature : cluster) { @@ -814,7 +814,7 @@ static unsigned long long choose_mingap(std::vector const &i // If there are no higher extents available, the tile has already been reduced as much as possible // and tippecanoe will exit with an error. static long long choose_minextent(std::vector &extents, double f, long long existing_extent) { - std::sort(extents.begin(), extents.end()); + std::stable_sort(extents.begin(), extents.end()); size_t ix = (extents.size() - 1) * (1 - f); while (ix + 1 < extents.size() && extents[ix] == existing_extent) { @@ -829,7 +829,7 @@ static unsigned long long choose_mindrop_sequence(std::vector *geompos_in, ch // Reorder and coalesce. // Sort back into input order or by attribute value - std::sort(shared_nodes.begin(), shared_nodes.end()); + std::stable_sort(shared_nodes.begin(), shared_nodes.end()); for (auto &kv : layers) { std::string const &layername = kv.first; @@ -2031,7 +2031,7 @@ long long write_tile(decompressor *geoms, std::atomic *geompos_in, ch // these will be smaller numbers, and avoid the problem of the // original sequence number varying based on how many reader threads // there were reading the input - std::sort(feature_sequences.begin(), feature_sequences.end()); + std::stable_sort(feature_sequences.begin(), feature_sequences.end()); for (size_t i = 0; i < feature_sequences.size(); i++) { size_t j = feature_sequences[i].second; serial_val sv(mvt_double, std::to_string(i)); @@ -2151,7 +2151,7 @@ long long write_tile(decompressor *geoms, std::atomic *geompos_in, ch std::vector &layer_features = features; if (additional[A_REORDER]) { - std::sort(layer_features.begin(), layer_features.end(), coalindexcmp_comparator()); + std::stable_sort(layer_features.begin(), layer_features.end(), coalindexcmp_comparator()); } if (additional[A_COALESCE]) { @@ -2215,13 +2215,13 @@ long long write_tile(decompressor *geoms, std::atomic *geompos_in, ch if (prevent[P_INPUT_ORDER]) { auto clustered = assemble_multiplier_clusters(layer_features); - std::sort(clustered.begin(), clustered.end(), preservecmp); + std::stable_sort(clustered.begin(), clustered.end(), preservecmp); layer_features = disassemble_multiplier_clusters(clustered); } if (order_by.size() != 0) { auto clustered = assemble_multiplier_clusters(layer_features); - std::sort(clustered.begin(), clustered.end(), ordercmp()); + std::stable_sort(clustered.begin(), clustered.end(), ordercmp()); layer_features = disassemble_multiplier_clusters(clustered); } diff --git a/version.hpp b/version.hpp index f091e508b..8f8040962 100644 --- a/version.hpp +++ b/version.hpp @@ -1,6 +1,6 @@ #ifndef VERSION_HPP #define VERSION_HPP -#define VERSION "v2.46.0" +#define VERSION "v2.47.0" #endif