From 918c57d52d4a75989da836aaf5e5e0f93c1afdd8 Mon Sep 17 00:00:00 2001 From: Erica Fischer Date: Tue, 20 May 2025 12:05:30 -0700 Subject: [PATCH 01/12] Detect and defer duplicates --- serial.cpp | 2 +- tile.cpp | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/serial.cpp b/serial.cpp index 5afc2ed2f..c1c6cf13f 100644 --- a/serial.cpp +++ b/serial.cpp @@ -732,7 +732,7 @@ int serialize_feature(struct serialization_state *sst, serial_feature &sf, std:: } } - if (additional[A_DROP_DENSEST_AS_NEEDED] || + if (true || additional[A_DROP_DENSEST_AS_NEEDED] || additional[A_COALESCE_DENSEST_AS_NEEDED] || additional[A_CLUSTER_DENSEST_AS_NEEDED] || additional[A_CALCULATE_FEATURE_DENSITY] || diff --git a/tile.cpp b/tile.cpp index 312c289a4..c7566ca9a 100644 --- a/tile.cpp +++ b/tile.cpp @@ -1054,6 +1054,9 @@ static bool skip_next_feature(decompressor *geoms, std::atomic *geomp struct next_feature_state { unsigned long long previndex = 0; unsigned long long prev_not_dropped_index = 0; + + std::vector deferrals; + size_t which_deferral = 0; }; // This function is called repeatedly from write_tile() to retrieve the next feature @@ -1076,6 +1079,12 @@ static serial_feature next_feature(decompressor *geoms, std::atomic * } sf.t = -2; + + for (auto const fp : next_feature_state.deferrals) { + if (fp != NULL) { + fclose(fp); + } + } return sf; } @@ -1090,6 +1099,31 @@ static serial_feature next_feature(decompressor *geoms, std::atomic * sf = deserialize_feature(s, z, tx, ty, initial_x, initial_y); sf.stringpool = global_stringpool + pool_off[sf.segment]; + if (sf.index == next_feature_state.previndex) { + if (next_feature_state.which_deferral >= next_feature_state.deferrals.size()) { + std::string tmpname = "/tmp/XXXXXXXXXX"; + int fd = mkstemp((char *) tmpname.c_str()); + if (fd < 0) { + fprintf(stderr, "Can't create temporary file %zu for feature deferral: %s\n", next_feature_state.which_deferral, strerror(errno)); + exit(EXIT_OPEN); + } + FILE *fp = fdopen(fd, "wb+"); + if (fp == NULL) { + fprintf(stderr, "Can't reopen temporary file for feature deferral: %s\n", strerror(errno)); + exit(EXIT_OPEN); + } + next_feature_state.deferrals.resize(next_feature_state.which_deferral + 1); + next_feature_state.deferrals[next_feature_state.which_deferral] = fp; + } + + fwrite(&len, sizeof(len), 1, next_feature_state.deferrals[next_feature_state.which_deferral]); + fwrite(s.c_str(), 1, len, next_feature_state.deferrals[next_feature_state.which_deferral]); + next_feature_state.which_deferral++; + continue; + } else { + next_feature_state.which_deferral = 0; + } + // with fractional zoom level, so we can target a specific number // of features to keep with retain-points-multiplier, not just the // powers of the drop rate From 7bc75e60504de5c89b6b0f31a1b76d811de82aaa Mon Sep 17 00:00:00 2001 From: Erica Fischer Date: Tue, 20 May 2025 12:17:24 -0700 Subject: [PATCH 02/12] Reconstitute the deferred features after the main reader loop is done --- tile.cpp | 64 ++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 44 insertions(+), 20 deletions(-) diff --git a/tile.cpp b/tile.cpp index c7566ca9a..ae5740c82 100644 --- a/tile.cpp +++ b/tile.cpp @@ -1057,6 +1057,7 @@ struct next_feature_state { std::vector deferrals; size_t which_deferral = 0; + bool doing_deferrals = false; }; // This function is called repeatedly from write_tile() to retrieve the next feature @@ -1068,38 +1069,61 @@ static serial_feature next_feature(decompressor *geoms, std::atomic * while (1) { serial_feature sf; long long len; + std::string s; - if (geoms->deserialize_long_long(&len, geompos_in) == 0) { - fprintf(stderr, "Unexpected physical EOF in feature stream\n"); - exit(EXIT_READ); - } - if (len <= 0) { - if (compressed) { - geoms->end(geompos_in); + if (next_feature_state.doing_deferrals) { + if (next_feature_state.which_deferral >= next_feature_state.deferrals.size()) { + // done with direct deserialization, done with deferred deserialization + for (auto const fp : next_feature_state.deferrals) { + fclose(fp); + } + + sf.t = -2; + return sf; } - sf.t = -2; + if (fread(&len, sizeof(len), 1, next_feature_state.deferrals[next_feature_state.which_deferral]) != 1) { + next_feature_state.which_deferral++; + continue; + } - for (auto const fp : next_feature_state.deferrals) { - if (fp != NULL) { - fclose(fp); + s.resize(len); + if (fread((void *) s.c_str(), 1, len, next_feature_state.deferrals[next_feature_state.which_deferral]) != len) { + fprintf(stderr, "short read in deferred deserialization: %s\n", strerror(errno)); + exit(EXIT_READ); + } + } else { + if (geoms->deserialize_long_long(&len, geompos_in) == 0) { + fprintf(stderr, "Unexpected physical EOF in feature stream\n"); + exit(EXIT_READ); + } + if (len <= 0) { + if (compressed) { + geoms->end(geompos_in); + } + + next_feature_state.doing_deferrals = true; + next_feature_state.which_deferral = 0; + + for (auto const fp : next_feature_state.deferrals) { + rewind(fp); } + + continue; } - return sf; - } - std::string s; - s.resize(len); - size_t n = geoms->fread((void *) s.c_str(), sizeof(char), s.size(), geompos_in); - if (n != s.size()) { - fprintf(stderr, "Short read (%zu for %zu) from geometry\n", n, s.size()); - exit(EXIT_READ); + s.resize(len); + size_t n = geoms->fread((void *) s.c_str(), sizeof(char), s.size(), geompos_in); + if (n != s.size()) { + fprintf(stderr, "Short read (%zu for %zu) from geometry\n", n, s.size()); + exit(EXIT_READ); + } } sf = deserialize_feature(s, z, tx, ty, initial_x, initial_y); sf.stringpool = global_stringpool + pool_off[sf.segment]; - if (sf.index == next_feature_state.previndex) { + if (!next_feature_state.doing_deferrals && sf.index == next_feature_state.previndex) { if (next_feature_state.which_deferral >= next_feature_state.deferrals.size()) { std::string tmpname = "/tmp/XXXXXXXXXX"; int fd = mkstemp((char *) tmpname.c_str()); From d207d2628a8f94d1cfdacdbb858cd0ebea8875f1 Mon Sep 17 00:00:00 2001 From: Erica Fischer Date: Tue, 20 May 2025 12:23:03 -0700 Subject: [PATCH 03/12] Make it a command-line option --- main.cpp | 1 + options.hpp | 1 + serial.cpp | 3 ++- tile.cpp | 4 ++-- 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/main.cpp b/main.cpp index b419f9970..7d38cd818 100644 --- a/main.cpp +++ b/main.cpp @@ -3098,6 +3098,7 @@ int main(int argc, char **argv) { {"force-feature-limit", no_argument, &prevent[P_DYNAMIC_DROP], 1}, {"cluster-densest-as-needed", no_argument, &additional[A_CLUSTER_DENSEST_AS_NEEDED], 1}, {"keep-point-cluster-position", no_argument, &additional[A_KEEP_POINT_CLUSTER_POSITION], 1}, + {"distinguish-duplicates", no_argument, &additional[A_DISTINGUISH_DUPLICATES], 1}, {"Dropping tightly overlapping features", 0, 0, 0}, {"gamma", required_argument, 0, 'g'}, diff --git a/options.hpp b/options.hpp index 1ca5be350..83ffc2c69 100644 --- a/options.hpp +++ b/options.hpp @@ -29,6 +29,7 @@ #define A_VISVALINGAM ((int) 'v') #define A_DETECT_WRAPAROUND ((int) 'w') #define A_KEEP_POINT_CLUSTER_POSITION ((int) 'a') +#define A_DISTINGUISH_DUPLICATES ((int) 'u') #define P_TILE_COMPRESSION ((int) 'C') #define P_DUPLICATION ((int) 'D') diff --git a/serial.cpp b/serial.cpp index c1c6cf13f..2aaa2d7ce 100644 --- a/serial.cpp +++ b/serial.cpp @@ -732,7 +732,7 @@ int serialize_feature(struct serialization_state *sst, serial_feature &sf, std:: } } - if (true || additional[A_DROP_DENSEST_AS_NEEDED] || + if (additional[A_DROP_DENSEST_AS_NEEDED] || additional[A_COALESCE_DENSEST_AS_NEEDED] || additional[A_CLUSTER_DENSEST_AS_NEEDED] || additional[A_CALCULATE_FEATURE_DENSITY] || @@ -743,6 +743,7 @@ int serialize_feature(struct serialization_state *sst, serial_feature &sf, std:: prevent[P_DYNAMIC_DROP] || additional[A_INCREASE_GAMMA_AS_NEEDED] || additional[A_GENERATE_POLYGON_LABEL_POINTS] || + additional[A_DISTINGUISH_DUPLICATES] || sst->uses_gamma || retain_points_multiplier > 1 || preserve_multiplier_density_threshold > 0 || diff --git a/tile.cpp b/tile.cpp index ae5740c82..22ef2eb28 100644 --- a/tile.cpp +++ b/tile.cpp @@ -1088,7 +1088,7 @@ static serial_feature next_feature(decompressor *geoms, std::atomic * } s.resize(len); - if (fread((void *) s.c_str(), 1, len, next_feature_state.deferrals[next_feature_state.which_deferral]) != len) { + if (fread((void *) s.c_str(), 1, len, next_feature_state.deferrals[next_feature_state.which_deferral]) != (size_t) len) { fprintf(stderr, "short read in deferred deserialization: %s\n", strerror(errno)); exit(EXIT_READ); } @@ -1123,7 +1123,7 @@ static serial_feature next_feature(decompressor *geoms, std::atomic * sf = deserialize_feature(s, z, tx, ty, initial_x, initial_y); sf.stringpool = global_stringpool + pool_off[sf.segment]; - if (!next_feature_state.doing_deferrals && sf.index == next_feature_state.previndex) { + if (!next_feature_state.doing_deferrals && additional[A_DISTINGUISH_DUPLICATES] && sf.index == next_feature_state.previndex) { if (next_feature_state.which_deferral >= next_feature_state.deferrals.size()) { std::string tmpname = "/tmp/XXXXXXXXXX"; int fd = mkstemp((char *) tmpname.c_str()); From 3ab86d8a7b0b25c8d9f2c7b7a9828c86198999b1 Mon Sep 17 00:00:00 2001 From: Erica Fischer Date: Wed, 21 May 2025 11:01:32 -0700 Subject: [PATCH 04/12] Only defer duplicates in the first zoom. Limit deferral depth. Make tmpdir a global to keep from needing to pass it around --- main.cpp | 18 +++++++++--------- main.hpp | 1 + tile.cpp | 28 +++++++++++++++++++--------- tile.hpp | 2 +- 4 files changed, 30 insertions(+), 19 deletions(-) diff --git a/main.cpp b/main.cpp index 7d38cd818..5b053c963 100644 --- a/main.cpp +++ b/main.cpp @@ -99,6 +99,7 @@ int retain_points_multiplier = 1; std::vector unidecode_data; size_t maximum_string_attribute_length = 0; std::string accumulate_numeric; +const char *tmpdir = "/tmp"; std::vector order_by; bool order_reverse; @@ -740,7 +741,7 @@ void start_parsing(int fd, STREAM *fp, long long offset, long long len, std::ato parser_created = true; } -void radix1(int *geomfds_in, int *indexfds_in, int inputs, int prefix, int splits, long long mem, const char *tmpdir, long long *availfiles, FILE *geomfile, FILE *indexfile, std::atomic *geompos_out, long long *progress, long long *progress_max, long long *progress_reported, int maxzoom, int basezoom, double droprate, double gamma, struct drop_state *ds) { +void radix1(int *geomfds_in, int *indexfds_in, int inputs, int prefix, int splits, long long mem, long long *availfiles, FILE *geomfile, FILE *indexfile, std::atomic *geompos_out, long long *progress, long long *progress_max, long long *progress_reported, int maxzoom, int basezoom, double droprate, double gamma, struct drop_state *ds) { // Arranged as bits to facilitate subdividing again if a subdivided file is still huge int splitbits = log(splits) / log(2); splits = 1 << splitbits; @@ -1028,7 +1029,7 @@ void radix1(int *geomfds_in, int *indexfds_in, int inputs, int prefix, int split // counter backward but will be an honest estimate of the work remaining. *progress_max += geomst.st_size / 4; - radix1(&geomfds[i], &indexfds[i], 1, prefix + splitbits, *availfiles / 4, mem, tmpdir, availfiles, geomfile, indexfile, geompos_out, progress, progress_max, progress_reported, maxzoom, basezoom, droprate, gamma, ds); + radix1(&geomfds[i], &indexfds[i], 1, prefix + splitbits, *availfiles / 4, mem, availfiles, geomfile, indexfile, geompos_out, progress, progress_max, progress_reported, maxzoom, basezoom, droprate, gamma, ds); already_closed = 1; } } @@ -1063,7 +1064,7 @@ void prep_drop_states(struct drop_state *ds, int maxzoom, int basezoom, double d } } -void radix(std::vector &readers, int nreaders, FILE *geomfile, FILE *indexfile, const char *tmpdir, std::atomic *geompos, int maxzoom, int basezoom, double droprate, double gamma) { +void radix(std::vector &readers, int nreaders, FILE *geomfile, FILE *indexfile, std::atomic *geompos, int maxzoom, int basezoom, double droprate, double gamma) { // Run through the index and geometry for each reader, // splitting the contents out by index into as many // sub-files as we can write to simultaneously. @@ -1113,7 +1114,7 @@ void radix(std::vector &readers, int nreaders, FILE *geomfile, FI long long progress = 0, progress_max = geom_total, progress_reported = -1; long long availfiles_before = availfiles; - radix1(geomfds, indexfds, nreaders, 0, splits, mem, tmpdir, &availfiles, geomfile, indexfile, geompos, &progress, &progress_max, &progress_reported, maxzoom, basezoom, droprate, gamma, ds); + radix1(geomfds, indexfds, nreaders, 0, splits, mem, &availfiles, geomfile, indexfile, geompos, &progress, &progress_max, &progress_reported, maxzoom, basezoom, droprate, gamma, ds); if (availfiles - 2 * nreaders != availfiles_before) { fprintf(stderr, "Internal error: miscounted available file descriptors: %lld vs %lld\n", availfiles - 2 * nreaders, availfiles); @@ -1214,7 +1215,7 @@ double round_droprate(double r) { return std::round(r * 100000.0) / 100000.0; } -std::pair read_input(std::vector &sources, char *fname, int maxzoom, int minzoom, int basezoom, double basezoom_marker_width, sqlite3 *outdb, const char *outdir, std::set *exclude, std::set *include, int exclude_all, json_object *filter, double droprate, int buffer, const char *tmpdir, double gamma, int read_parallel, int forcetable, const char *attribution, bool uses_gamma, long long *file_bbox, long long *file_bbox1, long long *file_bbox2, const char *prefilter, const char *postfilter, const char *description, bool guess_maxzoom, bool guess_cluster_maxzoom, std::unordered_map const *attribute_types, const char *pgm, std::unordered_map const *attribute_accum, std::map const &attribute_descriptions, std::string const &commandline, int minimum_maxzoom) { +std::pair read_input(std::vector &sources, char *fname, int maxzoom, int minzoom, int basezoom, double basezoom_marker_width, sqlite3 *outdb, const char *outdir, std::set *exclude, std::set *include, int exclude_all, json_object *filter, double droprate, int buffer, double gamma, int read_parallel, int forcetable, const char *attribution, bool uses_gamma, long long *file_bbox, long long *file_bbox1, long long *file_bbox2, const char *prefilter, const char *postfilter, const char *description, bool guess_maxzoom, bool guess_cluster_maxzoom, std::unordered_map const *attribute_types, const char *pgm, std::unordered_map const *attribute_accum, std::map const &attribute_descriptions, std::string const &commandline, int minimum_maxzoom) { int ret = EXIT_SUCCESS; std::vector readers; @@ -2207,7 +2208,7 @@ std::pair read_input(std::vector &sources, char *fname, i serialize_uint(geomfile, ix, &geompos, fname); serialize_uint(geomfile, iy, &geompos, fname); - radix(readers, CPUS, geomfile, indexfile, tmpdir, &geompos, maxzoom, basezoom, droprate, gamma); + radix(readers, CPUS, geomfile, indexfile, &geompos, maxzoom, basezoom, droprate, gamma); /* end of tile */ serialize_ulong_long(geomfile, 0, &geompos, fname); // EOF @@ -2755,7 +2756,7 @@ std::pair read_input(std::vector &sources, char *fname, i std::atomic midx(0); std::atomic midy(0); std::vector strategies; - int written = traverse_zooms(fd, size, stringpool, &midx, &midy, maxzoom, minzoom, outdb, outdir, buffer, fname, tmpdir, gamma, full_detail, low_detail, min_detail, pool_off, initial_x, initial_y, simplification, maxzoom_simplification, layermaps, prefilter, postfilter, attribute_accum, filter, strategies, iz, shared_nodes_map, nodepos, shared_nodes_bloom, basezoom, droprate, unidecode_data); + int written = traverse_zooms(fd, size, stringpool, &midx, &midy, maxzoom, minzoom, outdb, outdir, buffer, fname, gamma, full_detail, low_detail, min_detail, pool_off, initial_x, initial_y, simplification, maxzoom_simplification, layermaps, prefilter, postfilter, attribute_accum, filter, strategies, iz, shared_nodes_map, nodepos, shared_nodes_bloom, basezoom, droprate, unidecode_data); if (maxzoom != written) { if (written > minzoom) { @@ -2991,7 +2992,6 @@ int main(int argc, char **argv) { double droprate = 2.5; double gamma = 0; int buffer = 5; - const char *tmpdir = "/tmp"; const char *attribution = NULL; std::vector sources; const char *prefilter = NULL; @@ -3833,7 +3833,7 @@ int main(int argc, char **argv) { auto input_ret = read_input(sources, name ? name : out_mbtiles ? out_mbtiles : out_dir, - maxzoom, minzoom, basezoom, basezoom_marker_width, outdb, out_dir, &exclude, &include, exclude_all, filter, droprate, buffer, tmpdir, gamma, read_parallel, forcetable, attribution, gamma != 0, file_bbox, file_bbox1, file_bbox2, prefilter, postfilter, description, guess_maxzoom, guess_cluster_maxzoom, &attribute_types, argv[0], &attribute_accum, attribute_descriptions, commandline, minimum_maxzoom); + maxzoom, minzoom, basezoom, basezoom_marker_width, outdb, out_dir, &exclude, &include, exclude_all, filter, droprate, buffer, gamma, read_parallel, forcetable, attribution, gamma != 0, file_bbox, file_bbox1, file_bbox2, prefilter, postfilter, description, guess_maxzoom, guess_cluster_maxzoom, &attribute_types, argv[0], &attribute_accum, attribute_descriptions, commandline, minimum_maxzoom); ret = std::get<0>(input_ret); diff --git a/main.hpp b/main.hpp index 714af5c31..08dad0658 100644 --- a/main.hpp +++ b/main.hpp @@ -54,6 +54,7 @@ extern int retain_points_multiplier; extern size_t maximum_string_attribute_length; extern std::string accumulate_numeric; extern unsigned long long preserve_multiplier_density_threshold; +extern const char *tmpdir; struct order_field { std::string name; diff --git a/tile.cpp b/tile.cpp index 22ef2eb28..68f65020f 100644 --- a/tile.cpp +++ b/tile.cpp @@ -904,6 +904,7 @@ struct write_tile_args { atomic_strategy *strategy = NULL; int zoom = -1; bool compressed; + bool first_zoom; node *shared_nodes_map; size_t nodepos; std::string const *shared_nodes_bloom; @@ -1063,7 +1064,7 @@ struct next_feature_state { // This function is called repeatedly from write_tile() to retrieve the next feature // from the input stream. If the stream is at an end, it returns a feature with the // geometry type set to -2. -static serial_feature next_feature(decompressor *geoms, std::atomic *geompos_in, int z, unsigned tx, unsigned ty, unsigned *initial_x, unsigned *initial_y, long long *original_features, long long *unclipped_features, int nextzoom, int maxzoom, int minzoom, int max_zoom_increment, size_t pass, std::atomic *along, long long alongminus, int buffer, std::atomic *within, compressor **geomfile, std::atomic *geompos, long long start_geompos[], std::atomic *oprogress, double todo, const char *fname, int child_shards, json_object *filter, const char *global_stringpool, long long *pool_off, std::vector> *layer_unmaps, bool first_time, bool compressed, multiplier_state *multiplier_state, std::shared_ptr &tile_stringpool, std::vector const &unidecode_data, next_feature_state &next_feature_state, double droprate) { +static serial_feature next_feature(decompressor *geoms, std::atomic *geompos_in, int z, unsigned tx, unsigned ty, unsigned *initial_x, unsigned *initial_y, long long *original_features, long long *unclipped_features, int nextzoom, int maxzoom, int minzoom, int max_zoom_increment, size_t pass, std::atomic *along, long long alongminus, int buffer, std::atomic *within, compressor **geomfile, std::atomic *geompos, long long start_geompos[], std::atomic *oprogress, double todo, const char *fname, int child_shards, json_object *filter, const char *global_stringpool, long long *pool_off, std::vector> *layer_unmaps, bool first_time, bool compressed, bool first_zoom, multiplier_state *multiplier_state, std::shared_ptr &tile_stringpool, std::vector const &unidecode_data, next_feature_state &next_feature_state, double droprate) { double extra_multiplier_zooms = log(retain_points_multiplier) / log(droprate); while (1) { @@ -1123,9 +1124,9 @@ static serial_feature next_feature(decompressor *geoms, std::atomic * sf = deserialize_feature(s, z, tx, ty, initial_x, initial_y); sf.stringpool = global_stringpool + pool_off[sf.segment]; - if (!next_feature_state.doing_deferrals && additional[A_DISTINGUISH_DUPLICATES] && sf.index == next_feature_state.previndex) { + if (!next_feature_state.doing_deferrals && first_zoom && additional[A_DISTINGUISH_DUPLICATES] && sf.index == next_feature_state.previndex) { if (next_feature_state.which_deferral >= next_feature_state.deferrals.size()) { - std::string tmpname = "/tmp/XXXXXXXXXX"; + std::string tmpname = std::string(tmpdir) + std::string("/deferralXXXXXXXXXX"); int fd = mkstemp((char *) tmpname.c_str()); if (fd < 0) { fprintf(stderr, "Can't create temporary file %zu for feature deferral: %s\n", next_feature_state.which_deferral, strerror(errno)); @@ -1142,7 +1143,13 @@ static serial_feature next_feature(decompressor *geoms, std::atomic * fwrite(&len, sizeof(len), 1, next_feature_state.deferrals[next_feature_state.which_deferral]); fwrite(s.c_str(), 1, len, next_feature_state.deferrals[next_feature_state.which_deferral]); - next_feature_state.which_deferral++; + + // limit the maximum depth of deferral, + // because even at z0 we will run out of file descriptors + // (and tile space) if there are large numbers of duplicates + if (next_feature_state.which_deferral < 50) { + next_feature_state.which_deferral++; + } continue; } else { next_feature_state.which_deferral = 0; @@ -1369,6 +1376,7 @@ struct run_prefilter_args { std::vector const *unidecode_data; bool first_time = false; bool compressed = false; + bool first_zoom = false; double droprate = 1; }; @@ -1380,7 +1388,7 @@ void *run_prefilter(void *v) { next_feature_state next_feature_state; while (1) { - serial_feature sf = next_feature(rpa->geoms, rpa->geompos_in, rpa->z, rpa->tx, rpa->ty, rpa->initial_x, rpa->initial_y, rpa->original_features, rpa->unclipped_features, rpa->nextzoom, rpa->maxzoom, rpa->minzoom, rpa->max_zoom_increment, rpa->pass, rpa->along, rpa->alongminus, rpa->buffer, rpa->within, rpa->geomfile, rpa->geompos, rpa->start_geompos, rpa->oprogress, rpa->todo, rpa->fname, rpa->child_shards, rpa->filter, rpa->global_stringpool, rpa->pool_off, rpa->layer_unmaps, rpa->first_time, rpa->compressed, &multiplier_state, tile_stringpool, *(rpa->unidecode_data), next_feature_state, rpa->droprate); + serial_feature sf = next_feature(rpa->geoms, rpa->geompos_in, rpa->z, rpa->tx, rpa->ty, rpa->initial_x, rpa->initial_y, rpa->original_features, rpa->unclipped_features, rpa->nextzoom, rpa->maxzoom, rpa->minzoom, rpa->max_zoom_increment, rpa->pass, rpa->along, rpa->alongminus, rpa->buffer, rpa->within, rpa->geomfile, rpa->geompos, rpa->start_geompos, rpa->oprogress, rpa->todo, rpa->fname, rpa->child_shards, rpa->filter, rpa->global_stringpool, rpa->pool_off, rpa->layer_unmaps, rpa->first_time, rpa->compressed, rpa->first_zoom, &multiplier_state, tile_stringpool, *(rpa->unidecode_data), next_feature_state, rpa->droprate); if (sf.t < 0) { break; } @@ -1676,7 +1684,7 @@ void skip_tile(decompressor *geoms, std::atomic *geompos_in, bool com } } -long long write_tile(decompressor *geoms, std::atomic *geompos_in, char *global_stringpool, int z, const unsigned tx, const unsigned ty, const int detail, int min_detail, sqlite3 *outdb, const char *outdir, int buffer, const char *fname, compressor **geomfile, std::atomic *geompos, int minzoom, int maxzoom, double todo, std::atomic *along, long long alongminus, double gamma, int child_shards, long long *pool_off, unsigned *initial_x, unsigned *initial_y, std::atomic *running, double simplification, std::vector> *layermaps, std::vector> *layer_unmaps, size_t tiling_seg, size_t pass, unsigned long long mingap, long long minextent, unsigned long long mindrop_sequence, const char *prefilter, const char *postfilter, json_object *filter, write_tile_args *arg, atomic_strategy *strategy_out, bool compressed_input, node *shared_nodes_map, size_t nodepos, std::string const &shared_nodes_bloom, std::vector const &unidecode_data, long long estimated_complexity, std::set &skip_children_out) { +long long write_tile(decompressor *geoms, std::atomic *geompos_in, char *global_stringpool, int z, const unsigned tx, const unsigned ty, const int detail, int min_detail, sqlite3 *outdb, const char *outdir, int buffer, const char *fname, compressor **geomfile, std::atomic *geompos, int minzoom, int maxzoom, double todo, std::atomic *along, long long alongminus, double gamma, int child_shards, long long *pool_off, unsigned *initial_x, unsigned *initial_y, std::atomic *running, double simplification, std::vector> *layermaps, std::vector> *layer_unmaps, size_t tiling_seg, size_t pass, unsigned long long mingap, long long minextent, unsigned long long mindrop_sequence, const char *prefilter, const char *postfilter, json_object *filter, write_tile_args *arg, atomic_strategy *strategy_out, bool compressed_input, bool first_zoom, node *shared_nodes_map, size_t nodepos, std::string const &shared_nodes_bloom, std::vector const &unidecode_data, long long estimated_complexity, std::set &skip_children_out) { double merge_fraction = 1; double mingap_fraction = 1; double minextent_fraction = 1; @@ -1855,6 +1863,7 @@ long long write_tile(decompressor *geoms, std::atomic *geompos_in, ch rpa.unidecode_data = &unidecode_data; rpa.first_time = first_time; rpa.compressed = compressed_input; + rpa.first_zoom = first_zoom; rpa.droprate = arg->droprate; // this does need to be a real thread, so we can pipe both to and from it @@ -1887,7 +1896,7 @@ long long write_tile(decompressor *geoms, std::atomic *geompos_in, ch ssize_t which_serial_feature = -1; if (prefilter == NULL) { - sf = next_feature(geoms, geompos_in, z, tx, ty, initial_x, initial_y, &original_features, &unclipped_features, nextzoom, maxzoom, minzoom, max_zoom_increment, pass, along, alongminus, buffer, within, geomfile, geompos, start_geompos, &oprogress, todo, fname, child_shards, filter, global_stringpool, pool_off, layer_unmaps, first_time, compressed_input, &multiplier_state, tile_stringpool, unidecode_data, next_feature_state, arg->droprate); + sf = next_feature(geoms, geompos_in, z, tx, ty, initial_x, initial_y, &original_features, &unclipped_features, nextzoom, maxzoom, minzoom, max_zoom_increment, pass, along, alongminus, buffer, within, geomfile, geompos, start_geompos, &oprogress, todo, fname, child_shards, filter, global_stringpool, pool_off, layer_unmaps, first_time, compressed_input, first_zoom, &multiplier_state, tile_stringpool, unidecode_data, next_feature_state, arg->droprate); } else { sf = parse_feature(prefilter_jp, z, tx, ty, layermaps, tiling_seg, layer_unmaps, postfilter != NULL, key_pool); } @@ -3075,7 +3084,7 @@ exit(EXIT_IMPOSSIBLE); len = 1; } else { arg->wrote_zoom = z; - len = write_tile(&dc, &geompos, arg->global_stringpool, z, x, y, z == arg->maxzoom ? arg->full_detail : arg->low_detail, arg->min_detail, arg->outdb, arg->outdir, arg->buffer, arg->fname, arg->geomfile, arg->geompos, arg->minzoom, arg->maxzoom, arg->todo, arg->along, geompos, arg->gamma, arg->child_shards, arg->pool_off, arg->initial_x, arg->initial_y, arg->running, arg->simplification, arg->layermaps, arg->layer_unmaps, arg->tiling_seg, arg->pass, arg->mingap, arg->minextent, arg->mindrop_sequence, arg->prefilter, arg->postfilter, arg->filter, arg, arg->strategy, arg->compressed, arg->shared_nodes_map, arg->nodepos, *(arg->shared_nodes_bloom), (*arg->unidecode_data), estimated_complexity, arg->skip_children_out); + len = write_tile(&dc, &geompos, arg->global_stringpool, z, x, y, z == arg->maxzoom ? arg->full_detail : arg->low_detail, arg->min_detail, arg->outdb, arg->outdir, arg->buffer, arg->fname, arg->geomfile, arg->geompos, arg->minzoom, arg->maxzoom, arg->todo, arg->along, geompos, arg->gamma, arg->child_shards, arg->pool_off, arg->initial_x, arg->initial_y, arg->running, arg->simplification, arg->layermaps, arg->layer_unmaps, arg->tiling_seg, arg->pass, arg->mingap, arg->minextent, arg->mindrop_sequence, arg->prefilter, arg->postfilter, arg->filter, arg, arg->strategy, arg->compressed, arg->first_zoom, arg->shared_nodes_map, arg->nodepos, *(arg->shared_nodes_bloom), (*arg->unidecode_data), estimated_complexity, arg->skip_children_out); } if (pthread_mutex_lock(&var_lock) != 0) { @@ -3141,7 +3150,7 @@ exit(EXIT_IMPOSSIBLE); return err_or_null; } -int traverse_zooms(int *geomfd, off_t *geom_size, char *global_stringpool, std::atomic *midx, std::atomic *midy, int &maxzoom, int minzoom, sqlite3 *outdb, const char *outdir, int buffer, const char *fname, const char *tmpdir, double gamma, int full_detail, int low_detail, int min_detail, long long *pool_off, unsigned *initial_x, unsigned *initial_y, double simplification, double maxzoom_simplification, std::vector> &layermaps, const char *prefilter, const char *postfilter, std::unordered_map const *attribute_accum, json_object *filter, std::vector &strategies, int iz, node *shared_nodes_map, size_t nodepos, std::string const &shared_nodes_bloom, int basezoom, double droprate, std::vector const &unidecode_data) { +int traverse_zooms(int *geomfd, off_t *geom_size, char *global_stringpool, std::atomic *midx, std::atomic *midy, int &maxzoom, int minzoom, sqlite3 *outdb, const char *outdir, int buffer, const char *fname, double gamma, int full_detail, int low_detail, int min_detail, long long *pool_off, unsigned *initial_x, unsigned *initial_y, double simplification, double maxzoom_simplification, std::vector> &layermaps, const char *prefilter, const char *postfilter, std::unordered_map const *attribute_accum, json_object *filter, std::vector &strategies, int iz, node *shared_nodes_map, size_t nodepos, std::string const &shared_nodes_bloom, int basezoom, double droprate, std::vector const &unidecode_data) { last_progress = 0; // The existing layermaps are one table per input thread. @@ -3333,6 +3342,7 @@ int traverse_zooms(int *geomfd, off_t *geom_size, char *global_stringpool, std:: args[thread].strategy = &strategy; args[thread].zoom = z; args[thread].compressed = (z != iz); + args[thread].first_zoom = (z == iz); args[thread].shared_nodes_map = shared_nodes_map; args[thread].nodepos = nodepos; args[thread].shared_nodes_bloom = &shared_nodes_bloom; diff --git a/tile.hpp b/tile.hpp index 5735d041b..dbf5eb51e 100644 --- a/tile.hpp +++ b/tile.hpp @@ -62,7 +62,7 @@ struct strategy { // long long write_tile(char **geom, char *stringpool, unsigned *file_bbox, int z, unsigned x, unsigned y, int detail, int min_detail, int basezoom, sqlite3 *outdb, const char *outdir, double droprate, int buffer, const char *fname, FILE **geomfile, int file_minzoom, int file_maxzoom, double todo, char *geomstart, long long along, double gamma, int nlayers, std::atomic *strategy); -int traverse_zooms(int *geomfd, off_t *geom_size, char *stringpool, std::atomic *midx, std::atomic *midy, int &maxzoom, int minzoom, sqlite3 *outdb, const char *outdir, int buffer, const char *fname, const char *tmpdir, double gamma, int full_detail, int low_detail, int min_detail, long long *pool_off, unsigned *initial_x, unsigned *initial_y, double simplification, double maxzoom_simplification, std::vector > &layermap, const char *prefilter, const char *postfilter, std::unordered_map const *attribute_accum, struct json_object *filter, std::vector &strategies, int iz, struct node *shared_nodes_map, size_t nodepos, std::string const &shared_nodes_bloom, int basezoom, double droprate, std::vector const &unidecode_data); +int traverse_zooms(int *geomfd, off_t *geom_size, char *stringpool, std::atomic *midx, std::atomic *midy, int &maxzoom, int minzoom, sqlite3 *outdb, const char *outdir, int buffer, const char *fname, double gamma, int full_detail, int low_detail, int min_detail, long long *pool_off, unsigned *initial_x, unsigned *initial_y, double simplification, double maxzoom_simplification, std::vector > &layermap, const char *prefilter, const char *postfilter, std::unordered_map const *attribute_accum, struct json_object *filter, std::vector &strategies, int iz, struct node *shared_nodes_map, size_t nodepos, std::string const &shared_nodes_bloom, int basezoom, double droprate, std::vector const &unidecode_data); int manage_gap(unsigned long long index, unsigned long long *previndex, double scale, double gamma, double *gap); From 36907b5bb21d8cc6a1bb35456017813eeeadec97 Mon Sep 17 00:00:00 2001 From: Erica Fischer Date: Wed, 21 May 2025 11:14:10 -0700 Subject: [PATCH 05/12] Add test --- tests/distinguish-duplicates/in.json | 183 ++++++ .../out/-z1_-b0_--distinguish-duplicates.json | 537 ++++++++++++++++++ 2 files changed, 720 insertions(+) create mode 100644 tests/distinguish-duplicates/in.json create mode 100644 tests/distinguish-duplicates/out/-z1_-b0_--distinguish-duplicates.json diff --git a/tests/distinguish-duplicates/in.json b/tests/distinguish-duplicates/in.json new file mode 100644 index 000000000..684aa0ef8 --- /dev/null +++ b/tests/distinguish-duplicates/in.json @@ -0,0 +1,183 @@ +{"type":"Feature","properties":{"a":0},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":0},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":0},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":1},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":1},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":1},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":2},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":2},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":2},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":3},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":3},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":3},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":4},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":4},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":4},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":5},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":5},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":5},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":6},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":6},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":6},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":7},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":7},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":7},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":8},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":8},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":8},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":9},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":9},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":9},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":10},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":10},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":10},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":11},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":11},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":11},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":12},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":12},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":12},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":13},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":13},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":13},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":14},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":14},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":14},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":15},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":15},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":15},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":16},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":16},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":16},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":17},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":17},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":17},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":18},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":18},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":18},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":19},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":19},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":19},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":20},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":20},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":20},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":21},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":21},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":21},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":22},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":22},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":22},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":23},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":23},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":23},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":24},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":24},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":24},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":25},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":25},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":25},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":26},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":26},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":26},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":27},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":27},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":27},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":28},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":28},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":28},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":29},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":29},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":29},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":30},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":30},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":30},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":31},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":31},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":31},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":32},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":32},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":32},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":33},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":33},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":33},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":34},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":34},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":34},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":35},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":35},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":35},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":36},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":36},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":36},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":37},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":37},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":37},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":38},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":38},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":38},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":39},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":39},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":39},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":40},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":40},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":40},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":41},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":41},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":41},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":42},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":42},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":42},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":43},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":43},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":43},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":44},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":44},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":44},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":45},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":45},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":45},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":46},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":46},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":46},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":47},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":47},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":47},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":48},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":48},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":48},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":49},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":49},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":49},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":50},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":50},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":50},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":51},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":51},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":51},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":52},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":52},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":52},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":53},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":53},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":53},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":54},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":54},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":54},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":55},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":55},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":55},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":56},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":56},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":56},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":57},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":57},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":57},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":58},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":58},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":58},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":59},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":59},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":59},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":60},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":60},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":60},"geometry":{"type":"Point","coordinates":[3,1]}} diff --git a/tests/distinguish-duplicates/out/-z1_-b0_--distinguish-duplicates.json b/tests/distinguish-duplicates/out/-z1_-b0_--distinguish-duplicates.json new file mode 100644 index 000000000..6cdf76b90 --- /dev/null +++ b/tests/distinguish-duplicates/out/-z1_-b0_--distinguish-duplicates.json @@ -0,0 +1,537 @@ +{ "type": "FeatureCollection", "properties": { +"antimeridian_adjusted_bounds": "1.000000,1.000000,3.000000,1.000000", +"bounds": "1.000000,1.000000,3.000000,1.000000", +"center": "3.000000,1.000000,1", +"description": "tests/distinguish-duplicates/out/-z1_-b0_--distinguish-duplicates.json.check.mbtiles", +"format": "pbf", +"generator_options": "./tippecanoe -q -a@ -f -o tests/distinguish-duplicates/out/-z1_-b0_--distinguish-duplicates.json.check.mbtiles -z1 -b0 --distinguish-duplicates tests/distinguish-duplicates/in.json", +"json": "{\"vector_layers\":[{\"id\":\"in\",\"description\":\"\",\"minzoom\":0,\"maxzoom\":1,\"fields\":{\"a\":\"Number\",\"b\":\"Number\",\"c\":\"Number\"}}],\"tilestats\":{\"layerCount\":1,\"layers\":[{\"layer\":\"in\",\"count\":183,\"geometry\":\"Point\",\"attributeCount\":3,\"attributes\":[{\"attribute\":\"a\",\"count\":61,\"type\":\"number\",\"values\":[0,1,10,11,12,13,14,15,16,17,18,19,2,20,21,22,23,24,25,26,27,28,29,3,30,31,32,33,34,35,36,37,38,39,4,40,41,42,43,44,45,46,47,48,49,5,50,51,52,53,54,55,56,57,58,59,6,60,7,8,9],\"min\":0,\"max\":60},{\"attribute\":\"b\",\"count\":61,\"type\":\"number\",\"values\":[0,1,10,11,12,13,14,15,16,17,18,19,2,20,21,22,23,24,25,26,27,28,29,3,30,31,32,33,34,35,36,37,38,39,4,40,41,42,43,44,45,46,47,48,49,5,50,51,52,53,54,55,56,57,58,59,6,60,7,8,9],\"min\":0,\"max\":60},{\"attribute\":\"c\",\"count\":61,\"type\":\"number\",\"values\":[0,1,10,11,12,13,14,15,16,17,18,19,2,20,21,22,23,24,25,26,27,28,29,3,30,31,32,33,34,35,36,37,38,39,4,40,41,42,43,44,45,46,47,48,49,5,50,51,52,53,54,55,56,57,58,59,6,60,7,8,9],\"min\":0,\"max\":60}]}]}}", +"maxzoom": "1", +"minzoom": "0", +"name": "tests/distinguish-duplicates/out/-z1_-b0_--distinguish-duplicates.json.check.mbtiles", +"strategies": "[{\"dropped_by_rate\":109},{}]", +"type": "overlay", +"version": "2" +}, "features": [ +{ "type": "FeatureCollection", "properties": { "zoom": 0, "x": 0, "y": 0 }, "features": [ +{ "type": "FeatureCollection", "properties": { "layer": "in", "version": 2, "extent": 4096 }, "features": [ +{ "type": "Feature", "properties": { "a": 0 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 0 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 1 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 2 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 2 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 3 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 4 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 5 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 6 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 7 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 7 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 8 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 9 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 10 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 11 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 12 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 12 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 13 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 14 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 15 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 16 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 17 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 17 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 18 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 19 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 20 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 21 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 22 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 22 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 23 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 24 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 25 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 26 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 27 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 27 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 28 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 29 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 30 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 31 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 32 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 32 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 33 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 34 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 35 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 36 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 37 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 37 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 38 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 39 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 40 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 41 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 42 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 42 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 43 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 44 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 45 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 46 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 47 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 47 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 48 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 49 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 50 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 52 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 54 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 57 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 59 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 51 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 53 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 56 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 58 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 52 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 55 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 57 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 60 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +] } +] } +, +{ "type": "FeatureCollection", "properties": { "zoom": 1, "x": 1, "y": 0 }, "features": [ +{ "type": "FeatureCollection", "properties": { "layer": "in", "version": 2, "extent": 4096 }, "features": [ +{ "type": "Feature", "properties": { "a": 0 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 0 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 0 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 1 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 1 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 1 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 2 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 2 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 2 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 3 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 3 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 3 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 4 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 4 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 4 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 5 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 5 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 5 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 6 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 6 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 6 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 7 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 7 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 7 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 8 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 8 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 8 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 9 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 9 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 9 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 10 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 10 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 10 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 11 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 11 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 11 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 12 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 12 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 12 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 13 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 13 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 13 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 14 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 14 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 14 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 15 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 15 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 15 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 16 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 16 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 16 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 17 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 17 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 17 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 18 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 18 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 18 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 19 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 19 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 19 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 20 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 20 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 20 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 21 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 21 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 21 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 22 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 22 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 22 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 23 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 23 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 23 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 24 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 24 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 24 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 25 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 25 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 25 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 26 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 26 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 26 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 27 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 27 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 27 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 28 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 28 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 28 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 29 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 29 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 29 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 30 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 30 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 30 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 31 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 31 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 31 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 32 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 32 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 32 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 33 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 33 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 33 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 34 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 34 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 34 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 35 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 35 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 35 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 36 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 36 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 36 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 37 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 37 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 37 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 38 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 38 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 38 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 39 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 39 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 39 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 40 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 40 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 40 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 41 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 41 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 41 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 42 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 42 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 42 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 43 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 43 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 43 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 44 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 44 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 44 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 45 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 45 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 45 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 46 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 46 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 46 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 47 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 47 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 47 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 48 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 48 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 48 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 49 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 49 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 49 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 50 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 50 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 50 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 51 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 52 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 53 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 54 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 55 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 56 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 57 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 58 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 59 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 60 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 51 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 52 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 53 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 54 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 55 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 56 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 57 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 58 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 59 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 60 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 51 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 52 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 53 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 54 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 55 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 56 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 57 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 58 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 59 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 60 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +] } +] } +] } From f7fb0686decdec7872bdac0f188ce3558b641316 Mon Sep 17 00:00:00 2001 From: Erica Fischer Date: Wed, 21 May 2025 11:36:21 -0700 Subject: [PATCH 06/12] Add documentation --- README.md | 7 ++++++- man/tippecanoe.1 | 18 ++++++++++++++---- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index bd0503cd8..0bc63f0cd 100644 --- a/README.md +++ b/README.md @@ -418,6 +418,7 @@ be reduced to the maximum that can be used with the specified _maxzoom_. `sum`, `product`, `mean`, `max`, `min`, `concat`, or `comma` to specify how the named _attribute_ is accumulated onto the attribute of the same name in a feature that does survive. The attributes and operations may also be specified as JSON keys and values: `--accumulate-attribute='{"attr": "operation", "attr2": "operation2"}'`. + * `--accumulate-numeric-attributes` _prefix_: Accumulate sum, count, min, and max for all numeric attributes into new attributes with the specified _prefix_. * `--set-attribute` _attribute_`:`_value_: Set the value of the specified _attribute_ in each feature to the specified _value_. This is mostly useful to give an attribute in each feature an initial value for `--accumulate-attribute`. The attributes and values may also be specified as JSON keys and values: `--set-attribute='{"attr": value, "attr2": value}'`. * `-pe` or `--empty-csv-columns-are-null`: Treat empty CSV columns as nulls rather than as empty strings. @@ -430,6 +431,7 @@ be reduced to the maximum that can be used with the specified _maxzoom_. * `-j` *filter* or `--feature-filter`=*filter*: Check features against a per-layer filter (as defined in the [Mapbox GL Style Specification](https://docs.mapbox.com/mapbox-gl-js/style-spec/#other-filter) or in a Felt filter specification still to be finalized) and only include those that match. Any features in layers that have no filter specified will be passed through. Filters for the layer `"*"` apply to all layers. The special variable `$zoom` refers to the current zoom level. * `-J` *filter-file* or `--feature-filter-file`=*filter-file*: Like `-j`, but read the filter from a file. + * `--unidecode-data` *file*: Specify transliterations that will be used when evaluating expressions, in the format from https://metacpan.org/pod/Text::Unidecode Example: to find the Natural Earth countries with low `scalerank` but high `LABELRANK`: @@ -468,14 +470,16 @@ the same layer, enclose them in an `all` expression so they will all be evaluate compensate for the larger marker, or `-Bf`*number* to allow at most *number* features in the densest tile. * `--retain-points-multiplier=`_multiple_: Retain the specified multiple of points instead of just the number of points that would ordinarily be retained by the drop rate. These can be thinned out later with the `-m` option to `tippecanoe-overzoom`. The start of each cluster is marked in the feature sequence by the `tippecanoe:retain_points_multiplier_first` attribute. The `--tile-size-limit` will also be extended at low zoom levels to allow for the multiplied features. * `--drop-denser=`_percentage_: When dropping dots at zoom levels below the base zoom, give the specified _percentage_ - preference to retaining points in sparse areas and dropping points in dense areas. + preference to retaining points in sparse areas and dropping points in dense areas. Use `--preserve-point-density-threshold` instead. * `--limit-base-zoom-to-maximum-zoom` or `-Pb`: Limit the guessed base zoom not to exceed the maxzoom, even if this would put more than the requested number of features in a base zoom tile. * `-al` or `--drop-lines`: Let "dot" dropping at lower zooms apply to lines too * `-ap` or `--drop-polygons`: Let "dot" dropping at lower zooms apply to polygons too * `-K` _distance_ or `--cluster-distance=`_distance_: Cluster points (as with `--cluster-densest-as-needed`, but without the experimental discovery process) that are approximately within _distance_ of each other. The units are tile coordinates within a nominally 256-pixel tile, so the maximum value of 255 allows only one feature per tile. Values around 10 are probably appropriate for typical marker sizes. See `--cluster-densest-as-needed` below for behavior. * `-k` _zoom_ or `--cluster-maxzoom=`_zoom_: Max zoom on which to cluster points if clustering is enabled. * `-kg` or `--cluster-maxzoom=g`: Set `--cluster-maxzoom=` to `maxzoom - 1` so that all features are visible at the maximum zoom level. + * `--keep-point-cluster-position`: Do not average the location of points as they are clustered. * `--preserve-point-density-threshold=`_level_: At the low zoom levels, do not reduce point density below the specified _level_, even if the specfied drop rate would normally call for it, so that low-density areas of the map do not appear blank. The unit is the distance between preserved points, as a fraction of the size of a tile. Values of 32 or 64 are probably appropriate for typical marker sizes. + * `--preserve-multiplier-density-threshold=`_level_: Preserve a minimum point density, but as `--retain-points-multiplier` features, not primary features. ### Dropping a fraction of features to keep under tile size limits @@ -487,6 +491,7 @@ the same layer, enclose them in an `all` expression so they will all be evaluate * `-aS` or `--coalesce-fraction-as-needed`: Dynamically combine a fraction of features from each zoom level into other nearby features to keep large tiles under the 500K size limit. (Again, mostly useful for polygons.) * `-pd` or `--force-feature-limit`: Dynamically drop some fraction of features from large tiles to keep them under the 500K size limit. It will probably look ugly at the tile boundaries. (This is like `-ad` but applies to each tile individually, not to the entire zoom level.) You probably don't want to use this. * `-aC` or `--cluster-densest-as-needed`: If a tile is too large, try to reduce its size by increasing the minimum spacing between features, and leaving one placeholder feature from each group. The remaining feature will be given a `"clustered": true` attribute to indicate that it represents a cluster, a `"point_count"` attribute to indicate the number of features that were clustered into it, and a `"sqrt_point_count"` attribute to indicate the relative width of a feature to represent the cluster. If the features being clustered are points, the representative feature will be located at the average of the original points' locations; otherwise, one of the original features will be left as the representative. + * `--distinguish-duplicates`: Treat sets of up to 50 exact duplicate locations as sub-layers, which will be accumulated or coalesced into other features of their own sub-layer instead of into their duplicates. ### Dropping tightly overlapping features diff --git a/man/tippecanoe.1 b/man/tippecanoe.1 index fb0c97a4c..24904fa4a 100644 --- a/man/tippecanoe.1 +++ b/man/tippecanoe.1 @@ -518,10 +518,12 @@ If the type is \fB\fCint\fR and the original attribute was floating\-point, it i that are dropped, coalesced\-as\-needed, or clustered. The \fIoperation\fP may be \fB\fCsum\fR, \fB\fCproduct\fR, \fB\fCmean\fR, \fB\fCmax\fR, \fB\fCmin\fR, \fB\fCconcat\fR, or \fB\fCcomma\fR to specify how the named \fIattribute\fP is accumulated onto the attribute of the same name in a feature that does survive. -The attributes and operations may also be specified as JSON keys and values: \fB\fC\-\-accumulate\-attribute='{"attr": "operation", "attr2", "operation2"}'\fR\&. +The attributes and operations may also be specified as JSON keys and values: \fB\fC\-\-accumulate\-attribute='{"attr": "operation", "attr2": "operation2"}'\fR\&. +.IP \(bu 2 +\fB\fC\-\-accumulate\-numeric\-attributes\fR \fIprefix\fP: Accumulate sum, count, min, and max for all numeric attributes into new attributes with the specified \fIprefix\fP\&. .IP \(bu 2 \fB\fC\-\-set\-attribute\fR \fIattribute\fP\fB\fC:\fR\fIvalue\fP: Set the value of the specified \fIattribute\fP in each feature to the specified \fIvalue\fP\&. This is mostly useful to give an attribute in each feature an initial value for \fB\fC\-\-accumulate\-attribute\fR\&. -The attributes and values may also be specified as JSON keys and values: \fB\fC\-\-set\-attribute='{"attr": value, "attr2", value}'\fR\&. +The attributes and values may also be specified as JSON keys and values: \fB\fC\-\-set\-attribute='{"attr": value, "attr2": value}'\fR\&. .IP \(bu 2 \fB\fC\-pe\fR or \fB\fC\-\-empty\-csv\-columns\-are\-null\fR: Treat empty CSV columns as nulls rather than as empty strings. .IP \(bu 2 @@ -539,6 +541,8 @@ The attributes and values may also be specified as JSON keys and values: \fB\fC\ \fB\fC\-j\fR \fIfilter\fP or \fB\fC\-\-feature\-filter\fR=\fIfilter\fP: Check features against a per\-layer filter (as defined in the Mapbox GL Style Specification \[la]https://docs.mapbox.com/mapbox-gl-js/style-spec/#other-filter\[ra] or in a Felt filter specification still to be finalized) and only include those that match. Any features in layers that have no filter specified will be passed through. Filters for the layer \fB\fC"*"\fR apply to all layers. The special variable \fB\fC$zoom\fR refers to the current zoom level. .IP \(bu 2 \fB\fC\-J\fR \fIfilter\-file\fP or \fB\fC\-\-feature\-filter\-file\fR=\fIfilter\-file\fP: Like \fB\fC\-j\fR, but read the filter from a file. +.IP \(bu 2 +\fB\fC\-\-unidecode\-data\fR \fIfile\fP: Specify transliterations that will be used when evaluating expressions, in the format from \[la]https://metacpan.org/pod/Text::Unidecode\[ra] .RE .PP Example: to find the Natural Earth countries with low \fB\fCscalerank\fR but high \fB\fCLABELRANK\fR: @@ -587,7 +591,7 @@ compensate for the larger marker, or \fB\fC\-Bf\fR\fInumber\fP to allow at most \fB\fC\-\-retain\-points\-multiplier=\fR\fImultiple\fP: Retain the specified multiple of points instead of just the number of points that would ordinarily be retained by the drop rate. These can be thinned out later with the \fB\fC\-m\fR option to \fB\fCtippecanoe\-overzoom\fR\&. The start of each cluster is marked in the feature sequence by the \fB\fCtippecanoe:retain_points_multiplier_first\fR attribute. The \fB\fC\-\-tile\-size\-limit\fR will also be extended at low zoom levels to allow for the multiplied features. .IP \(bu 2 \fB\fC\-\-drop\-denser=\fR\fIpercentage\fP: When dropping dots at zoom levels below the base zoom, give the specified \fIpercentage\fP -preference to retaining points in sparse areas and dropping points in dense areas. +preference to retaining points in sparse areas and dropping points in dense areas. Use \fB\fC\-\-preserve\-point\-density\-threshold\fR instead. .IP \(bu 2 \fB\fC\-\-limit\-base\-zoom\-to\-maximum\-zoom\fR or \fB\fC\-Pb\fR: Limit the guessed base zoom not to exceed the maxzoom, even if this would put more than the requested number of features in a base zoom tile. .IP \(bu 2 @@ -601,7 +605,11 @@ preference to retaining points in sparse areas and dropping points in dense area .IP \(bu 2 \fB\fC\-kg\fR or \fB\fC\-\-cluster\-maxzoom=g\fR: Set \fB\fC\-\-cluster\-maxzoom=\fR to \fB\fCmaxzoom \- 1\fR so that all features are visible at the maximum zoom level. .IP \(bu 2 +\fB\fC\-\-keep\-point\-cluster\-position\fR: Do not average the location of points as they are clustered. +.IP \(bu 2 \fB\fC\-\-preserve\-point\-density\-threshold=\fR\fIlevel\fP: At the low zoom levels, do not reduce point density below the specified \fIlevel\fP, even if the specfied drop rate would normally call for it, so that low\-density areas of the map do not appear blank. The unit is the distance between preserved points, as a fraction of the size of a tile. Values of 32 or 64 are probably appropriate for typical marker sizes. +.IP \(bu 2 +\fB\fC\-\-preserve\-multiplier\-density\-threshold=\fR\fIlevel\fP: Preserve a minimum point density, but as \fB\fC\-\-retain\-points\-multiplier\fR features, not primary features. .RE .SS Dropping a fraction of features to keep under tile size limits .RS @@ -621,11 +629,13 @@ preference to retaining points in sparse areas and dropping points in dense area \fB\fC\-pd\fR or \fB\fC\-\-force\-feature\-limit\fR: Dynamically drop some fraction of features from large tiles to keep them under the 500K size limit. It will probably look ugly at the tile boundaries. (This is like \fB\fC\-ad\fR but applies to each tile individually, not to the entire zoom level.) You probably don't want to use this. .IP \(bu 2 \fB\fC\-aC\fR or \fB\fC\-\-cluster\-densest\-as\-needed\fR: If a tile is too large, try to reduce its size by increasing the minimum spacing between features, and leaving one placeholder feature from each group. The remaining feature will be given a \fB\fC"clustered": true\fR attribute to indicate that it represents a cluster, a \fB\fC"point_count"\fR attribute to indicate the number of features that were clustered into it, and a \fB\fC"sqrt_point_count"\fR attribute to indicate the relative width of a feature to represent the cluster. If the features being clustered are points, the representative feature will be located at the average of the original points' locations; otherwise, one of the original features will be left as the representative. +.IP \(bu 2 +\fB\fC\-\-distinguish\-duplicates\fR: Treat sets of up to 50 exact duplicate locations as sub\-layers, which will be accumulated or coalesced into other features of their own sub\-layer instead of into their duplicates. .RE .SS Dropping tightly overlapping features .RS .IP \(bu 2 -\fB\fC\-g\fR \fIgamma\fP or \fB\fC\-\-gamma=_gamma\fR_: Rate at which especially dense dots are dropped (default 0, for no effect). A gamma of 2 reduces the number of dots less than a pixel apart to the square root of their original number. +\fB\fC\-g\fR \fIgamma\fP or \fB\fC\-\-gamma=\fR\fIgamma\fP: Rate at which especially dense dots are dropped (default 0, for no effect). A gamma of 2 reduces the number of dots less than a pixel apart to the square root of their original number. .IP \(bu 2 \fB\fC\-aG\fR or \fB\fC\-\-increase\-gamma\-as\-needed\fR: If a tile is too large, try to reduce it to under 500K by increasing the \fB\fC\-g\fR gamma. The discovered gamma applies to the entire zoom level. You probably want to use \fB\fC\-\-drop\-densest\-as\-needed\fR instead. .RE From f4717f765c6482b7bac41e291142a340831bdf3c Mon Sep 17 00:00:00 2001 From: Erica Fischer Date: Wed, 21 May 2025 12:06:08 -0700 Subject: [PATCH 07/12] Add more error checks --- tile.cpp | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/tile.cpp b/tile.cpp index 68f65020f..5c8e2053c 100644 --- a/tile.cpp +++ b/tile.cpp @@ -1076,7 +1076,10 @@ static serial_feature next_feature(decompressor *geoms, std::atomic * if (next_feature_state.which_deferral >= next_feature_state.deferrals.size()) { // done with direct deserialization, done with deferred deserialization for (auto const fp : next_feature_state.deferrals) { - fclose(fp); + if (fclose(fp) != 0) { + perror("fclose deferral"); + exit(EXIT_CLOSE); + } } sf.t = -2; @@ -1084,6 +1087,10 @@ static serial_feature next_feature(decompressor *geoms, std::atomic * } if (fread(&len, sizeof(len), 1, next_feature_state.deferrals[next_feature_state.which_deferral]) != 1) { + if (ferror(next_feature_state.deferrals[next_feature_state.which_deferral])) { + perror("read deferral"); + exit(EXIT_READ); + } next_feature_state.which_deferral++; continue; } @@ -1107,7 +1114,12 @@ static serial_feature next_feature(decompressor *geoms, std::atomic * next_feature_state.which_deferral = 0; for (auto const fp : next_feature_state.deferrals) { + errno = 0; rewind(fp); + if (errno != 0) { + perror("rewind deferral"); + exit(EXIT_SEEK); + } } continue; @@ -1141,8 +1153,14 @@ static serial_feature next_feature(decompressor *geoms, std::atomic * next_feature_state.deferrals[next_feature_state.which_deferral] = fp; } - fwrite(&len, sizeof(len), 1, next_feature_state.deferrals[next_feature_state.which_deferral]); - fwrite(s.c_str(), 1, len, next_feature_state.deferrals[next_feature_state.which_deferral]); + if (fwrite(&len, sizeof(len), 1, next_feature_state.deferrals[next_feature_state.which_deferral]) != 1) { + perror("write deferral length"); + exit(EXIT_WRITE); + } + if (fwrite(s.c_str(), 1, len, next_feature_state.deferrals[next_feature_state.which_deferral]) != (size_t) len) { + perror("write deferral"); + exit(EXIT_WRITE); + } // limit the maximum depth of deferral, // because even at z0 we will run out of file descriptors From f4a58f2805886c090c1a80258f3a887c5be4a15d Mon Sep 17 00:00:00 2001 From: Erica Fischer Date: Wed, 21 May 2025 13:18:01 -0700 Subject: [PATCH 08/12] It's a better test with no feature dropping by rate --- ...-z1_-r1_-b0_--distinguish-duplicates.json} | 226 +++++++++++++++++- 1 file changed, 222 insertions(+), 4 deletions(-) rename tests/distinguish-duplicates/out/{-z1_-b0_--distinguish-duplicates.json => -z1_-r1_-b0_--distinguish-duplicates.json} (70%) diff --git a/tests/distinguish-duplicates/out/-z1_-b0_--distinguish-duplicates.json b/tests/distinguish-duplicates/out/-z1_-r1_-b0_--distinguish-duplicates.json similarity index 70% rename from tests/distinguish-duplicates/out/-z1_-b0_--distinguish-duplicates.json rename to tests/distinguish-duplicates/out/-z1_-r1_-b0_--distinguish-duplicates.json index 6cdf76b90..4f4dcaca8 100644 --- a/tests/distinguish-duplicates/out/-z1_-b0_--distinguish-duplicates.json +++ b/tests/distinguish-duplicates/out/-z1_-r1_-b0_--distinguish-duplicates.json @@ -2,14 +2,14 @@ "antimeridian_adjusted_bounds": "1.000000,1.000000,3.000000,1.000000", "bounds": "1.000000,1.000000,3.000000,1.000000", "center": "3.000000,1.000000,1", -"description": "tests/distinguish-duplicates/out/-z1_-b0_--distinguish-duplicates.json.check.mbtiles", +"description": "tests/distinguish-duplicates/out/-z1_-r1_-b0_--distinguish-duplicates.json.check.mbtiles", "format": "pbf", -"generator_options": "./tippecanoe -q -a@ -f -o tests/distinguish-duplicates/out/-z1_-b0_--distinguish-duplicates.json.check.mbtiles -z1 -b0 --distinguish-duplicates tests/distinguish-duplicates/in.json", +"generator_options": "./tippecanoe -q -a@ -f -o tests/distinguish-duplicates/out/-z1_-r1_-b0_--distinguish-duplicates.json.check.mbtiles -z1 -r1 -b0 --distinguish-duplicates tests/distinguish-duplicates/in.json", "json": "{\"vector_layers\":[{\"id\":\"in\",\"description\":\"\",\"minzoom\":0,\"maxzoom\":1,\"fields\":{\"a\":\"Number\",\"b\":\"Number\",\"c\":\"Number\"}}],\"tilestats\":{\"layerCount\":1,\"layers\":[{\"layer\":\"in\",\"count\":183,\"geometry\":\"Point\",\"attributeCount\":3,\"attributes\":[{\"attribute\":\"a\",\"count\":61,\"type\":\"number\",\"values\":[0,1,10,11,12,13,14,15,16,17,18,19,2,20,21,22,23,24,25,26,27,28,29,3,30,31,32,33,34,35,36,37,38,39,4,40,41,42,43,44,45,46,47,48,49,5,50,51,52,53,54,55,56,57,58,59,6,60,7,8,9],\"min\":0,\"max\":60},{\"attribute\":\"b\",\"count\":61,\"type\":\"number\",\"values\":[0,1,10,11,12,13,14,15,16,17,18,19,2,20,21,22,23,24,25,26,27,28,29,3,30,31,32,33,34,35,36,37,38,39,4,40,41,42,43,44,45,46,47,48,49,5,50,51,52,53,54,55,56,57,58,59,6,60,7,8,9],\"min\":0,\"max\":60},{\"attribute\":\"c\",\"count\":61,\"type\":\"number\",\"values\":[0,1,10,11,12,13,14,15,16,17,18,19,2,20,21,22,23,24,25,26,27,28,29,3,30,31,32,33,34,35,36,37,38,39,4,40,41,42,43,44,45,46,47,48,49,5,50,51,52,53,54,55,56,57,58,59,6,60,7,8,9],\"min\":0,\"max\":60}]}]}}", "maxzoom": "1", "minzoom": "0", -"name": "tests/distinguish-duplicates/out/-z1_-b0_--distinguish-duplicates.json.check.mbtiles", -"strategies": "[{\"dropped_by_rate\":109},{}]", +"name": "tests/distinguish-duplicates/out/-z1_-r1_-b0_--distinguish-duplicates.json.check.mbtiles", +"tippecanoe_decisions": "{\"basezoom\":1,\"droprate\":1,\"retain_points_multiplier\":1}", "type": "overlay", "version": "2" }, "features": [ @@ -17,150 +17,368 @@ { "type": "FeatureCollection", "properties": { "layer": "in", "version": 2, "extent": 4096 }, "features": [ { "type": "Feature", "properties": { "a": 0 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } , +{ "type": "Feature", "properties": { "b": 0 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, { "type": "Feature", "properties": { "c": 0 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } , +{ "type": "Feature", "properties": { "a": 1 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, { "type": "Feature", "properties": { "b": 1 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } , +{ "type": "Feature", "properties": { "c": 1 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, { "type": "Feature", "properties": { "a": 2 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } , +{ "type": "Feature", "properties": { "b": 2 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, { "type": "Feature", "properties": { "c": 2 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } , +{ "type": "Feature", "properties": { "a": 3 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, { "type": "Feature", "properties": { "b": 3 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } , +{ "type": "Feature", "properties": { "c": 3 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, { "type": "Feature", "properties": { "a": 4 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } , +{ "type": "Feature", "properties": { "b": 4 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 4 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 5 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 5 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, { "type": "Feature", "properties": { "c": 5 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } , +{ "type": "Feature", "properties": { "a": 6 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, { "type": "Feature", "properties": { "b": 6 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } , +{ "type": "Feature", "properties": { "c": 6 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, { "type": "Feature", "properties": { "a": 7 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } , +{ "type": "Feature", "properties": { "b": 7 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, { "type": "Feature", "properties": { "c": 7 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } , +{ "type": "Feature", "properties": { "a": 8 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, { "type": "Feature", "properties": { "b": 8 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } , +{ "type": "Feature", "properties": { "c": 8 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, { "type": "Feature", "properties": { "a": 9 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } , +{ "type": "Feature", "properties": { "b": 9 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 9 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 10 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 10 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, { "type": "Feature", "properties": { "c": 10 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } , +{ "type": "Feature", "properties": { "a": 11 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, { "type": "Feature", "properties": { "b": 11 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } , +{ "type": "Feature", "properties": { "c": 11 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, { "type": "Feature", "properties": { "a": 12 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } , +{ "type": "Feature", "properties": { "b": 12 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, { "type": "Feature", "properties": { "c": 12 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } , +{ "type": "Feature", "properties": { "a": 13 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, { "type": "Feature", "properties": { "b": 13 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } , +{ "type": "Feature", "properties": { "c": 13 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, { "type": "Feature", "properties": { "a": 14 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } , +{ "type": "Feature", "properties": { "b": 14 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 14 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 15 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 15 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, { "type": "Feature", "properties": { "c": 15 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } , +{ "type": "Feature", "properties": { "a": 16 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, { "type": "Feature", "properties": { "b": 16 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } , +{ "type": "Feature", "properties": { "c": 16 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, { "type": "Feature", "properties": { "a": 17 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } , +{ "type": "Feature", "properties": { "b": 17 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, { "type": "Feature", "properties": { "c": 17 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } , +{ "type": "Feature", "properties": { "a": 18 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, { "type": "Feature", "properties": { "b": 18 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } , +{ "type": "Feature", "properties": { "c": 18 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, { "type": "Feature", "properties": { "a": 19 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } , +{ "type": "Feature", "properties": { "b": 19 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 19 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 20 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 20 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, { "type": "Feature", "properties": { "c": 20 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } , +{ "type": "Feature", "properties": { "a": 21 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, { "type": "Feature", "properties": { "b": 21 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } , +{ "type": "Feature", "properties": { "c": 21 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, { "type": "Feature", "properties": { "a": 22 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } , +{ "type": "Feature", "properties": { "b": 22 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, { "type": "Feature", "properties": { "c": 22 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } , +{ "type": "Feature", "properties": { "a": 23 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, { "type": "Feature", "properties": { "b": 23 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } , +{ "type": "Feature", "properties": { "c": 23 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, { "type": "Feature", "properties": { "a": 24 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } , +{ "type": "Feature", "properties": { "b": 24 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 24 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 25 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 25 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, { "type": "Feature", "properties": { "c": 25 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } , +{ "type": "Feature", "properties": { "a": 26 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, { "type": "Feature", "properties": { "b": 26 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } , +{ "type": "Feature", "properties": { "c": 26 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, { "type": "Feature", "properties": { "a": 27 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } , +{ "type": "Feature", "properties": { "b": 27 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, { "type": "Feature", "properties": { "c": 27 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } , +{ "type": "Feature", "properties": { "a": 28 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, { "type": "Feature", "properties": { "b": 28 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } , +{ "type": "Feature", "properties": { "c": 28 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, { "type": "Feature", "properties": { "a": 29 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } , +{ "type": "Feature", "properties": { "b": 29 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 29 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 30 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 30 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, { "type": "Feature", "properties": { "c": 30 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } , +{ "type": "Feature", "properties": { "a": 31 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, { "type": "Feature", "properties": { "b": 31 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } , +{ "type": "Feature", "properties": { "c": 31 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, { "type": "Feature", "properties": { "a": 32 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } , +{ "type": "Feature", "properties": { "b": 32 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, { "type": "Feature", "properties": { "c": 32 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } , +{ "type": "Feature", "properties": { "a": 33 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, { "type": "Feature", "properties": { "b": 33 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } , +{ "type": "Feature", "properties": { "c": 33 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, { "type": "Feature", "properties": { "a": 34 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } , +{ "type": "Feature", "properties": { "b": 34 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 34 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 35 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 35 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, { "type": "Feature", "properties": { "c": 35 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } , +{ "type": "Feature", "properties": { "a": 36 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, { "type": "Feature", "properties": { "b": 36 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } , +{ "type": "Feature", "properties": { "c": 36 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, { "type": "Feature", "properties": { "a": 37 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } , +{ "type": "Feature", "properties": { "b": 37 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, { "type": "Feature", "properties": { "c": 37 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } , +{ "type": "Feature", "properties": { "a": 38 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, { "type": "Feature", "properties": { "b": 38 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } , +{ "type": "Feature", "properties": { "c": 38 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, { "type": "Feature", "properties": { "a": 39 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } , +{ "type": "Feature", "properties": { "b": 39 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 39 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 40 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 40 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, { "type": "Feature", "properties": { "c": 40 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } , +{ "type": "Feature", "properties": { "a": 41 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, { "type": "Feature", "properties": { "b": 41 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } , +{ "type": "Feature", "properties": { "c": 41 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, { "type": "Feature", "properties": { "a": 42 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } , +{ "type": "Feature", "properties": { "b": 42 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, { "type": "Feature", "properties": { "c": 42 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } , +{ "type": "Feature", "properties": { "a": 43 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, { "type": "Feature", "properties": { "b": 43 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } , +{ "type": "Feature", "properties": { "c": 43 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, { "type": "Feature", "properties": { "a": 44 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } , +{ "type": "Feature", "properties": { "b": 44 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 44 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 45 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 45 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, { "type": "Feature", "properties": { "c": 45 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } , +{ "type": "Feature", "properties": { "a": 46 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, { "type": "Feature", "properties": { "b": 46 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } , +{ "type": "Feature", "properties": { "c": 46 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, { "type": "Feature", "properties": { "a": 47 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } , +{ "type": "Feature", "properties": { "b": 47 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, { "type": "Feature", "properties": { "c": 47 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } , +{ "type": "Feature", "properties": { "a": 48 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, { "type": "Feature", "properties": { "b": 48 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } , +{ "type": "Feature", "properties": { "c": 48 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, { "type": "Feature", "properties": { "a": 49 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } , +{ "type": "Feature", "properties": { "b": 49 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 49 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 50 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 50 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, { "type": "Feature", "properties": { "c": 50 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } , +{ "type": "Feature", "properties": { "a": 51 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, { "type": "Feature", "properties": { "a": 52 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } , +{ "type": "Feature", "properties": { "a": 53 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, { "type": "Feature", "properties": { "a": 54 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } , +{ "type": "Feature", "properties": { "a": 55 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 56 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, { "type": "Feature", "properties": { "a": 57 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } , +{ "type": "Feature", "properties": { "a": 58 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, { "type": "Feature", "properties": { "a": 59 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } , +{ "type": "Feature", "properties": { "a": 60 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, { "type": "Feature", "properties": { "b": 51 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } , +{ "type": "Feature", "properties": { "b": 52 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, { "type": "Feature", "properties": { "b": 53 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } , +{ "type": "Feature", "properties": { "b": 54 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 55 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, { "type": "Feature", "properties": { "b": 56 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } , +{ "type": "Feature", "properties": { "b": 57 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, { "type": "Feature", "properties": { "b": 58 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } , +{ "type": "Feature", "properties": { "b": 59 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 60 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 51 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, { "type": "Feature", "properties": { "c": 52 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } , +{ "type": "Feature", "properties": { "c": 53 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 54 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, { "type": "Feature", "properties": { "c": 55 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } , +{ "type": "Feature", "properties": { "c": 56 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, { "type": "Feature", "properties": { "c": 57 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } , +{ "type": "Feature", "properties": { "c": 58 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 59 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, { "type": "Feature", "properties": { "c": 60 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } ] } ] } From fc40c66551e72c7d07ac42ed19e31c4a22d8aa3b Mon Sep 17 00:00:00 2001 From: Erica Fischer Date: Wed, 21 May 2025 14:28:17 -0700 Subject: [PATCH 09/12] Bump version and changelog --- CHANGELOG.md | 4 ++++ version.hpp | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 62c074a6a..f56a86ea9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 2.79.0 + +* Add --distinguish-duplicates option + # 2.78.0 * Fix potential infinite loops in as-needed dropping and coalescing. diff --git a/version.hpp b/version.hpp index b9593e1d8..c8a13cad9 100644 --- a/version.hpp +++ b/version.hpp @@ -1,6 +1,6 @@ #ifndef VERSION_HPP #define VERSION_HPP -#define VERSION "v2.78.0" +#define VERSION "v2.79.0" #endif From fed221b28386185100fe9b70fd2fcede05c1c1c9 Mon Sep 17 00:00:00 2001 From: Erica Fischer Date: Fri, 31 Jul 2026 12:29:54 -0700 Subject: [PATCH 10/12] Guard against treating index 0 as a duplicate Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- tile.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tile.cpp b/tile.cpp index 1b2789e0d..a3d684870 100644 --- a/tile.cpp +++ b/tile.cpp @@ -1182,7 +1182,7 @@ static serial_feature next_feature(decompressor *geoms, std::atomic * sf = deserialize_feature(s, z, tx, ty, initial_x, initial_y); sf.stringpool = global_stringpool + pool_off[sf.segment]; - if (!next_feature_state.doing_deferrals && first_zoom && additional[A_DISTINGUISH_DUPLICATES] && sf.index == next_feature_state.previndex) { + if (!next_feature_state.doing_deferrals && *original_features > 0 && first_zoom && additional[A_DISTINGUISH_DUPLICATES] && sf.index == next_feature_state.previndex) { if (next_feature_state.which_deferral >= next_feature_state.deferrals.size()) { std::string tmpname = std::string(tmpdir) + std::string("/deferralXXXXXXXXXX"); int fd = mkstemp((char *) tmpname.c_str()); From 0d243dab1e61ea1d64bb00f8a83582479d6cf727 Mon Sep 17 00:00:00 2001 From: Erica Fischer Date: Fri, 31 Jul 2026 12:30:11 -0700 Subject: [PATCH 11/12] Remember to delete the temporary files Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- tile.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tile.cpp b/tile.cpp index a3d684870..51a1b33df 100644 --- a/tile.cpp +++ b/tile.cpp @@ -1184,12 +1184,16 @@ static serial_feature next_feature(decompressor *geoms, std::atomic * if (!next_feature_state.doing_deferrals && *original_features > 0 && first_zoom && additional[A_DISTINGUISH_DUPLICATES] && sf.index == next_feature_state.previndex) { if (next_feature_state.which_deferral >= next_feature_state.deferrals.size()) { - std::string tmpname = std::string(tmpdir) + std::string("/deferralXXXXXXXXXX"); - int fd = mkstemp((char *) tmpname.c_str()); + std::string tmpname = std::string(tmpdir) + "/deferral.XXXXXX"; + std::vector tmpl(tmpname.begin(), tmpname.end()); + tmpl.push_back('\0'); + + int fd = mkstemp_cloexec(tmpl.data()); if (fd < 0) { fprintf(stderr, "Can't create temporary file %zu for feature deferral: %s\n", next_feature_state.which_deferral, strerror(errno)); exit(EXIT_OPEN); } + unlink(tmpl.data()); FILE *fp = fdopen(fd, "wb+"); if (fp == NULL) { fprintf(stderr, "Can't reopen temporary file for feature deferral: %s\n", strerror(errno)); @@ -1197,7 +1201,6 @@ static serial_feature next_feature(decompressor *geoms, std::atomic * } next_feature_state.deferrals.resize(next_feature_state.which_deferral + 1); next_feature_state.deferrals[next_feature_state.which_deferral] = fp; - } if (fwrite(&len, sizeof(len), 1, next_feature_state.deferrals[next_feature_state.which_deferral]) != 1) { perror("write deferral length"); From ff48cbc76b4213962dee35ed79b0b8c7a4d514e5 Mon Sep 17 00:00:00 2001 From: Erica Fischer Date: Fri, 31 Jul 2026 12:35:31 -0700 Subject: [PATCH 12/12] Restore deleted closing brace --- tile.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/tile.cpp b/tile.cpp index 51a1b33df..4deb19f58 100644 --- a/tile.cpp +++ b/tile.cpp @@ -1201,6 +1201,7 @@ static serial_feature next_feature(decompressor *geoms, std::atomic * } next_feature_state.deferrals.resize(next_feature_state.which_deferral + 1); next_feature_state.deferrals[next_feature_state.which_deferral] = fp; + } if (fwrite(&len, sizeof(len), 1, next_feature_state.deferrals[next_feature_state.which_deferral]) != 1) { perror("write deferral length");