Merge 47741a6b70efee36febc697a1846cae84b95322e into 2daf84a6c10d22fcd4151fa9fcff9bea5407293a

This commit is contained in:
Erica Fischer 2024-03-09 21:44:55 -07:00 committed by GitHub
commit ce5bfa3e7f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 4031 additions and 11 deletions

View File

@ -507,6 +507,7 @@ the same layer, enclose them in an `all` expression so they will all be evaluate
* `-b` _pixels_ or `--buffer=`_pixels_: Buffer size where features are duplicated from adjacent tiles. Units are "screen pixels"—1/256th of the tile width or height. (default 5)
* `-pc` or `--no-clipping`: Don't clip features to the size of the tile. If a feature overlaps the tile's bounds or buffer at all, it is included completely. Be careful: this can produce very large tilesets, especially with large polygons.
* `-pD` or `--no-duplication`: As with `--no-clipping`, each feature is included intact instead of cut to tile boundaries. In addition, it is included only in a single tile per zoom level rather than potentially in multiple copies. Clients of the tileset must check adjacent tiles (possibly some distance away) to ensure they have all features.
* `-pF` or `--remove-filled`: Do not generate tiles that consist entirely of polygons that cover the entire extent of the tile. This is primarily useful for preventing the generation of large number of water or land cover tiles.
### Reordering features within each tile

View File

@ -77,6 +77,7 @@ size_t max_tile_features = 200000;
int cluster_distance = 0;
long justx = -1, justy = -1;
std::string attribute_for_id = "";
int fillzoom = -1;
int prevent[256];
int additional[256];
@ -2267,7 +2268,7 @@ int read_input(std::vector<source> &sources, char *fname, int maxzoom, int minzo
std::atomic<unsigned> midx(0);
std::atomic<unsigned> midy(0);
int written = traverse_zooms(fd, size, meta, stringpool, &midx, &midy, maxzoom, minzoom, outdb, outdir, buffer, fname, tmpdir, gamma, full_detail, low_detail, min_detail, meta_off, pool_off, initial_x, initial_y, simplification, layermaps, prefilter, postfilter, attribute_accum, filter);
int written = traverse_zooms(fd, size, meta, stringpool, &midx, &midy, maxzoom, minzoom, outdb, outdir, buffer, fname, tmpdir, gamma, full_detail, low_detail, min_detail, meta_off, pool_off, initial_x, initial_y, simplification, layermaps, prefilter, postfilter, attribute_accum, filter, &fillzoom);
if (maxzoom != written) {
if (written > minzoom) {
@ -2329,7 +2330,7 @@ int read_input(std::vector<source> &sources, char *fname, int maxzoom, int minzo
ai->second.maxzoom = maxzoom;
}
mbtiles_write_metadata(outdb, outdir, fname, minzoom, maxzoom, minlat, minlon, maxlat, maxlon, midlat, midlon, forcetable, attribution, merged_lm, true, description, !prevent[P_TILE_STATS], attribute_descriptions, "tippecanoe", commandline);
mbtiles_write_metadata(outdb, outdir, fname, minzoom, maxzoom, fillzoom, minlat, minlon, maxlat, maxlon, midlat, midlon, forcetable, attribution, merged_lm, true, description, !prevent[P_TILE_STATS], attribute_descriptions, "tippecanoe", commandline);
return ret;
}
@ -2579,6 +2580,7 @@ int main(int argc, char **argv) {
{"buffer", required_argument, 0, 'b'},
{"no-clipping", no_argument, &prevent[P_CLIPPING], 1},
{"no-duplication", no_argument, &prevent[P_DUPLICATION], 1},
{"remove-filled", no_argument, &prevent[P_FILLED], 1},
{"Reordering features within each tile", 0, 0, 0},
{"preserve-input-order", no_argument, &prevent[P_INPUT_ORDER], 1},

View File

@ -267,7 +267,7 @@ void tilestats(std::map<std::string, layermap_entry> const &layermap1, size_t el
state.json_end_hash();
}
void mbtiles_write_metadata(sqlite3 *outdb, const char *outdir, const char *fname, int minzoom, int maxzoom, double minlat, double minlon, double maxlat, double maxlon, double midlat, double midlon, int forcetable, const char *attribution, std::map<std::string, layermap_entry> const &layermap, bool vector, const char *description, bool do_tilestats, std::map<std::string, std::string> const &attribute_descriptions, std::string const &program, std::string const &commandline) {
void mbtiles_write_metadata(sqlite3 *outdb, const char *outdir, const char *fname, int minzoom, int maxzoom, int fillzoom, double minlat, double minlon, double maxlat, double maxlon, double midlat, double midlon, int forcetable, const char *attribution, std::map<std::string, layermap_entry> const &layermap, bool vector, const char *description, bool do_tilestats, std::map<std::string, std::string> const &attribute_descriptions, std::string const &program, std::string const &commandline) {
char *sql, *err;
sqlite3 *db = outdb;
@ -327,6 +327,17 @@ void mbtiles_write_metadata(sqlite3 *outdb, const char *outdir, const char *fnam
}
sqlite3_free(sql);
if (fillzoom >= 0) {
sql = sqlite3_mprintf("INSERT INTO metadata (name, value) VALUES ('fillzoom', %d);", fillzoom);
if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) {
fprintf(stderr, "set fillzoom: %s\n", err);
if (!forcetable) {
exit(EXIT_FAILURE);
}
}
sqlite3_free(sql);
}
sql = sqlite3_mprintf("INSERT INTO metadata (name, value) VALUES ('center', '%f,%f,%d');", midlon, midlat, maxzoom);
if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) {
fprintf(stderr, "set center: %s\n", err);

View File

@ -45,7 +45,7 @@ sqlite3 *mbtiles_open(char *dbname, char **argv, int forcetable);
void mbtiles_write_tile(sqlite3 *outdb, int z, int tx, int ty, const char *data, int size);
void mbtiles_write_metadata(sqlite3 *outdb, const char *outdir, const char *fname, int minzoom, int maxzoom, double minlat, double minlon, double maxlat, double maxlon, double midlat, double midlon, int forcetable, const char *attribution, std::map<std::string, layermap_entry> const &layermap, bool vector, const char *description, bool do_tilestats, std::map<std::string, std::string> const &attribute_descriptions, std::string const &program, std::string const &commandline);
void mbtiles_write_metadata(sqlite3 *outdb, const char *outdir, const char *fname, int minzoom, int maxzoom, int fillzoom, double minlat, double minlon, double maxlat, double maxlon, double midlat, double midlon, int forcetable, const char *attribution, std::map<std::string, layermap_entry> const &layermap, bool vector, const char *description, bool do_tilestats, std::map<std::string, std::string> const &attribute_descriptions, std::string const &program, std::string const &commandline);
void mbtiles_close(sqlite3 *outdb, const char *pgm);

View File

@ -30,6 +30,7 @@
#define P_SIMPLIFY_LOW ((int) 'S')
#define P_SIMPLIFY_SHARED_NODES ((int) 'n')
#define P_FEATURE_LIMIT ((int) 'f')
#define P_FILLED ((int) 'F')
#define P_KILOBYTE_LIMIT ((int) 'k')
#define P_DYNAMIC_DROP ((int) 'd')
#define P_INPUT_ORDER ((int) 'i')

File diff suppressed because one or more lines are too long

View File

@ -1124,7 +1124,7 @@ int main(int argc, char **argv) {
}
}
mbtiles_write_metadata(outdb, out_dir, name.c_str(), st.minzoom, st.maxzoom, st.minlat, st.minlon, st.maxlat, st.maxlon, st.midlat, st.midlon, 0, attribution.size() != 0 ? attribution.c_str() : NULL, layermap, true, description.c_str(), !pg, attribute_descriptions, "tile-join", generator_options);
mbtiles_write_metadata(outdb, out_dir, name.c_str(), st.minzoom, st.maxzoom, -1, st.minlat, st.minlon, st.maxlat, st.maxlon, st.midlat, st.midlon, 0, attribution.size() != 0 ? attribution.c_str() : NULL, layermap, true, description.c_str(), !pg, attribute_descriptions, "tile-join", generator_options);
if (outdb != NULL) {
mbtiles_close(outdb, argv[0]);

View File

@ -1221,6 +1221,7 @@ struct write_tile_args {
int wrote_zoom = 0;
size_t tiling_seg = 0;
struct json_object *filter = NULL;
bool removed_filled = false;
};
bool clip_to_tile(serial_feature &sf, int z, long long buffer) {
@ -1318,7 +1319,44 @@ void remove_attributes(serial_feature &sf, std::set<std::string> const &exclude_
}
}
serial_feature next_feature(FILE *geoms, std::atomic<long long> *geompos_in, char *metabase, long long *meta_off, 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, size_t passes, std::atomic<long long> *along, long long alongminus, int buffer, int *within, bool *first_time, FILE **geomfile, std::atomic<long long> *geompos, std::atomic<double> *oprogress, double todo, const char *fname, int child_shards, struct json_object *filter, const char *stringpool, long long *pool_off, std::vector<std::vector<std::string>> *layer_unmaps) {
bool is_filled(serial_feature &sf, int z) {
long long extent = 1LL << (32 - z);
if (prevent[P_FILLED]) {
if (sf.t != VT_POLYGON || sf.geometry.size() != 5) {
return false;
}
// It is a polygon with four sides.
// Are they all outside the tile boundary?
for (size_t j = 0; j + 1 < sf.geometry.size(); j++) {
if (sf.geometry[j].x == sf.geometry[j + 1].x && (sf.geometry[j].x < 0 || sf.geometry[j].x > extent)) {
// vertical line, outside the edge of the tile
if (!((sf.geometry[j].y < 0 && sf.geometry[j + 1].y > extent) ||
(sf.geometry[j + 1].y < 0 && sf.geometry[j].y > extent))) {
return false;
}
} else if (sf.geometry[j].y == sf.geometry[j + 1].y && (sf.geometry[j].y < 0 || sf.geometry[j].y > extent)) {
// horizontal line, outside the edge of the tile
if (!((sf.geometry[j].x < 0 && sf.geometry[j + 1].x > extent) ||
(sf.geometry[j + 1].x < 0 && sf.geometry[j].x > extent))) {
return false;
}
} else {
return false;
}
}
return true;
} else {
return false;
}
}
serial_feature next_feature(FILE *geoms, std::atomic<long long> *geompos_in, char *metabase, long long *meta_off, 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, size_t passes, std::atomic<long long> *along, long long alongminus, int buffer, int *within, bool *first_time, FILE **geomfile, std::atomic<long long> *geompos, std::atomic<double> *oprogress, double todo, const char *fname, int child_shards, struct json_object *filter, const char *stringpool, long long *pool_off, std::vector<std::vector<std::string>> *layer_unmaps, std::vector<serial_feature> *rewrite_queue, bool *is_rewriting) {
while (1) {
serial_feature sf = deserialize_feature(geoms, geompos_in, metabase, meta_off, z, tx, ty, initial_x, initial_y);
if (sf.t < 0) {
@ -1345,7 +1383,26 @@ serial_feature next_feature(FILE *geoms, std::atomic<long long> *geompos_in, cha
if (*first_time && pass == 1) { /* only write out the next zoom once, even if we retry */
if (sf.tippecanoe_maxzoom == -1 || sf.tippecanoe_maxzoom >= nextzoom) {
rewrite(sf.geometry, z, nextzoom, maxzoom, sf.bbox, tx, ty, buffer, within, geompos, geomfile, fname, sf.t, sf.layer, sf.metapos, sf.feature_minzoom, child_shards, max_zoom_increment, sf.seq, sf.tippecanoe_minzoom, sf.tippecanoe_maxzoom, sf.segment, initial_x, initial_y, sf.keys, sf.values, sf.has_id, sf.id, sf.index, sf.extent);
if (!*is_rewriting) {
if (is_filled(sf, z)) {
rewrite_queue->push_back(sf);
} else {
// Found a feature that we need to keep, so also push out all the
// filled features that we weren't sure about before.
*is_rewriting = true;
for (auto &rsf : *rewrite_queue) {
rewrite(rsf.geometry, z, nextzoom, maxzoom, rsf.bbox, tx, ty, buffer, within, geompos, geomfile, fname, rsf.t, rsf.layer, rsf.metapos, rsf.feature_minzoom, child_shards, max_zoom_increment, rsf.seq, rsf.tippecanoe_minzoom, rsf.tippecanoe_maxzoom, rsf.segment, initial_x, initial_y, rsf.keys, rsf.values, rsf.has_id, rsf.id, rsf.index, rsf.extent);
}
rewrite_queue->clear();
}
}
if (*is_rewriting) {
rewrite(sf.geometry, z, nextzoom, maxzoom, sf.bbox, tx, ty, buffer, within, geompos, geomfile, fname, sf.t, sf.layer, sf.metapos, sf.feature_minzoom, child_shards, max_zoom_increment, sf.seq, sf.tippecanoe_minzoom, sf.tippecanoe_maxzoom, sf.segment, initial_x, initial_y, sf.keys, sf.values, sf.has_id, sf.id, sf.index, sf.extent);
}
}
}
@ -1479,6 +1536,8 @@ struct run_prefilter_args {
long long *pool_off = NULL;
FILE *prefilter_fp = NULL;
struct json_object *filter = NULL;
std::vector<serial_feature> *rewrite_queue;
bool *is_rewriting;
};
void *run_prefilter(void *v) {
@ -1486,7 +1545,7 @@ void *run_prefilter(void *v) {
json_writer state(rpa->prefilter_fp);
while (1) {
serial_feature sf = next_feature(rpa->geoms, rpa->geompos_in, rpa->metabase, rpa->meta_off, 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->passes, rpa->along, rpa->alongminus, rpa->buffer, rpa->within, rpa->first_time, rpa->geomfile, rpa->geompos, rpa->oprogress, rpa->todo, rpa->fname, rpa->child_shards, rpa->filter, rpa->stringpool, rpa->pool_off, rpa->layer_unmaps);
serial_feature sf = next_feature(rpa->geoms, rpa->geompos_in, rpa->metabase, rpa->meta_off, 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->passes, rpa->along, rpa->alongminus, rpa->buffer, rpa->within, rpa->first_time, rpa->geomfile, rpa->geompos, rpa->oprogress, rpa->todo, rpa->fname, rpa->child_shards, rpa->filter, rpa->stringpool, rpa->pool_off, rpa->layer_unmaps, rpa->rewrite_queue, rpa->is_rewriting);
if (sf.t < 0) {
break;
}
@ -1747,6 +1806,9 @@ long long write_tile(FILE *geoms, std::atomic<long long> *geompos_in, char *meta
bool has_polygons = false;
bool first_time = true;
std::vector<serial_feature> rewrite_queue;
bool is_rewriting = false;
// This only loops if the tile data didn't fit, in which case the detail
// goes down and the progress indicator goes backward for the next try.
for (line_detail = detail; line_detail >= min_detail || line_detail == detail; line_detail--, oprogress = 0) {
@ -1839,6 +1901,8 @@ long long write_tile(FILE *geoms, std::atomic<long long> *geompos_in, char *meta
rpa.stringpool = stringpool;
rpa.pool_off = pool_off;
rpa.filter = filter;
rpa.rewrite_queue = &rewrite_queue;
rpa.is_rewriting = &is_rewriting;
if (pthread_create(&prefilter_writer, NULL, run_prefilter, &rpa) != 0) {
perror("pthread_create (prefilter writer)");
@ -1858,7 +1922,7 @@ long long write_tile(FILE *geoms, std::atomic<long long> *geompos_in, char *meta
ssize_t which_partial = -1;
if (prefilter == NULL) {
sf = next_feature(geoms, geompos_in, metabase, meta_off, z, tx, ty, initial_x, initial_y, &original_features, &unclipped_features, nextzoom, maxzoom, minzoom, max_zoom_increment, pass, passes, along, alongminus, buffer, within, &first_time, geomfile, geompos, &oprogress, todo, fname, child_shards, filter, stringpool, pool_off, layer_unmaps);
sf = next_feature(geoms, geompos_in, metabase, meta_off, z, tx, ty, initial_x, initial_y, &original_features, &unclipped_features, nextzoom, maxzoom, minzoom, max_zoom_increment, pass, passes, along, alongminus, buffer, within, &first_time, geomfile, geompos, &oprogress, todo, fname, child_shards, filter, stringpool, pool_off, layer_unmaps, &rewrite_queue, &is_rewriting);
} else {
sf = parse_feature(prefilter_jp, z, tx, ty, layermaps, tiling_seg, layer_unmaps, postfilter != NULL);
}
@ -2085,6 +2149,10 @@ long long write_tile(FILE *geoms, std::atomic<long long> *geompos_in, char *meta
}
}
if (first_time && pass == 1 && !is_rewriting && prevent[P_FILLED]) {
arg->removed_filled = true;
}
first_time = false;
bool merge_successful = true;
@ -2647,7 +2715,7 @@ void *run_thread(void *vargs) {
return NULL;
}
int traverse_zooms(int *geomfd, off_t *geom_size, char *metabase, char *stringpool, std::atomic<unsigned> *midx, std::atomic<unsigned> *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 *meta_off, long long *pool_off, unsigned *initial_x, unsigned *initial_y, double simplification, std::vector<std::map<std::string, layermap_entry>> &layermaps, const char *prefilter, const char *postfilter, std::map<std::string, attribute_op> const *attribute_accum, struct json_object *filter) {
int traverse_zooms(int *geomfd, off_t *geom_size, char *metabase, char *stringpool, std::atomic<unsigned> *midx, std::atomic<unsigned> *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 *meta_off, long long *pool_off, unsigned *initial_x, unsigned *initial_y, double simplification, std::vector<std::map<std::string, layermap_entry>> &layermaps, const char *prefilter, const char *postfilter, std::map<std::string, attribute_op> const *attribute_accum, struct json_object *filter, int *fillzoom) {
last_progress = 0;
// The existing layermaps are one table per input thread.
@ -2844,6 +2912,7 @@ int traverse_zooms(int *geomfd, off_t *geom_size, char *metabase, char *stringpo
args[thread].passes = 2 - start;
args[thread].wrote_zoom = -1;
args[thread].still_dropping = false;
args[thread].removed_filled = false;
if (pthread_create(&pthreads[thread], NULL, run_thread, &args[thread]) != 0) {
perror("pthread_create");
@ -2883,6 +2952,12 @@ int traverse_zooms(int *geomfd, off_t *geom_size, char *metabase, char *stringpo
if (additional[A_EXTEND_ZOOMS] && i == maxzoom && args[thread].still_dropping && maxzoom < MAX_ZOOM) {
maxzoom++;
}
if (args[thread].removed_filled && *fillzoom < 0) {
// Removing filled means that there will be tiles missing
// at the *next* zoom, so this zoom is OK to overzoom from
*fillzoom = i;
}
}
}

View File

@ -21,7 +21,7 @@ enum attribute_op {
long long write_tile(char **geom, char *metabase, 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);
int traverse_zooms(int *geomfd, off_t *geom_size, char *metabase, char *stringpool, std::atomic<unsigned> *midx, std::atomic<unsigned> *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 *meta_off, long long *pool_off, unsigned *initial_x, unsigned *initial_y, double simplification, std::vector<std::map<std::string, layermap_entry> > &layermap, const char *prefilter, const char *postfilter, std::map<std::string, attribute_op> const *attribute_accum, struct json_object *filter);
int traverse_zooms(int *geomfd, off_t *geom_size, char *metabase, char *stringpool, std::atomic<unsigned> *midx, std::atomic<unsigned> *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 *meta_off, long long *pool_off, unsigned *initial_x, unsigned *initial_y, double simplification, std::vector<std::map<std::string, layermap_entry> > &layermap, const char *prefilter, const char *postfilter, std::map<std::string, attribute_op> const *attribute_accum, struct json_object *filter, int *fillzoom);
int manage_gap(unsigned long long index, unsigned long long *previndex, double scale, double gamma, double *gap);