mirror of
https://github.com/felt/tippecanoe.git
synced 2026-10-02 16:35:40 +02:00
Actually do the shared edge detection
This commit is contained in:
+9
-4
@@ -712,7 +712,7 @@ static std::vector<std::pair<double, double>> clip_poly1(std::vector<std::pair<d
|
||||
}
|
||||
|
||||
drawvec simple_clip_poly(drawvec &geom, long long minx, long long miny, long long maxx, long long maxy,
|
||||
long long ax, long long ay, long long bx, long long by, drawvec &shared_nodes) {
|
||||
long long ax, long long ay, long long bx, long long by, drawvec &edge_nodes) {
|
||||
drawvec out;
|
||||
|
||||
for (size_t i = 0; i < geom.size(); i++) {
|
||||
@@ -730,7 +730,7 @@ drawvec simple_clip_poly(drawvec &geom, long long minx, long long miny, long lon
|
||||
double y = geom[k].y;
|
||||
tmp.emplace_back(x, y);
|
||||
}
|
||||
tmp = clip_poly1(tmp, minx, miny, maxx, maxy, ax, ay, bx, by, shared_nodes);
|
||||
tmp = clip_poly1(tmp, minx, miny, maxx, maxy, ax, ay, bx, by, edge_nodes);
|
||||
if (tmp.size() > 0) {
|
||||
if (tmp[0].first != tmp[tmp.size() - 1].first || tmp[0].second != tmp[tmp.size() - 1].second) {
|
||||
fprintf(stderr, "Internal error: Polygon ring not closed\n");
|
||||
@@ -760,12 +760,12 @@ drawvec simple_clip_poly(drawvec &geom, long long minx, long long miny, long lon
|
||||
return simple_clip_poly(geom, minx, miny, maxx, maxy, minx, miny, maxx, maxy, dv);
|
||||
}
|
||||
|
||||
drawvec simple_clip_poly(drawvec &geom, int z, int buffer, drawvec &shared_nodes) {
|
||||
drawvec simple_clip_poly(drawvec &geom, int z, int buffer, drawvec &edge_nodes) {
|
||||
long long area = 1LL << (32 - z);
|
||||
long long clip_buffer = buffer * area / 256;
|
||||
|
||||
return simple_clip_poly(geom, -clip_buffer, -clip_buffer, area + clip_buffer, area + clip_buffer,
|
||||
0, 0, area, area, shared_nodes);
|
||||
0, 0, area, area, edge_nodes);
|
||||
}
|
||||
// @@@
|
||||
|
||||
@@ -1123,6 +1123,11 @@ drawvec simplify_lines(drawvec &geom, int z, int detail, bool mark_tile_bounds,
|
||||
int res = 1 << (32 - detail - z);
|
||||
long long area = 1LL << (32 - z);
|
||||
|
||||
if (prevent[P_SIMPLIFY_SHARED_NODES] && z != 0) {
|
||||
// additional vertices tagged as "retain" in write_tile when assembling shared nodes instead
|
||||
retain = 0;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < geom.size(); i++) {
|
||||
if (geom[i].op == VT_MOVETO) {
|
||||
geom[i].necessary = 1;
|
||||
|
||||
@@ -67,6 +67,7 @@ struct serial_feature {
|
||||
std::vector<serial_val> full_values{};
|
||||
std::string layername = "";
|
||||
bool dropped = false;
|
||||
drawvec edge_nodes;
|
||||
};
|
||||
|
||||
std::string serialize_feature(serial_feature *sf, long long wx, long long wy);
|
||||
|
||||
@@ -1290,6 +1290,40 @@ long long choose_minextent(std::vector<long long> &extents, double f) {
|
||||
return extents[(extents.size() - 1) * (1 - f)];
|
||||
}
|
||||
|
||||
struct joint {
|
||||
draw p1;
|
||||
draw mid;
|
||||
draw p2;
|
||||
|
||||
joint(draw one, draw hinge, draw two) {
|
||||
if (one < two) {
|
||||
p1 = one;
|
||||
p2 = two;
|
||||
} else {
|
||||
p1 = two;
|
||||
p2 = one;
|
||||
}
|
||||
|
||||
mid = hinge;
|
||||
}
|
||||
|
||||
bool operator<(const joint &o) const {
|
||||
if (mid < o.mid) {
|
||||
return true;
|
||||
} else if (mid == o.mid) {
|
||||
if (p1 < o.p1) {
|
||||
return true;
|
||||
} else if (p1 == o.p1) {
|
||||
if (p2 < o.p2) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
struct write_tile_args {
|
||||
struct task *tasks = NULL;
|
||||
char *stringpool = NULL;
|
||||
@@ -1343,7 +1377,7 @@ struct write_tile_args {
|
||||
bool compressed;
|
||||
};
|
||||
|
||||
bool clip_to_tile(serial_feature &sf, int z, long long buffer, drawvec &shared_nodes) {
|
||||
bool clip_to_tile(serial_feature &sf, int z, long long buffer) {
|
||||
int quick = quick_check(sf.bbox, z, buffer);
|
||||
|
||||
if (z == 0) {
|
||||
@@ -1389,7 +1423,7 @@ bool clip_to_tile(serial_feature &sf, int z, long long buffer, drawvec &shared_n
|
||||
clipped = clip_lines(sf.geometry, z, buffer);
|
||||
}
|
||||
if (sf.t == VT_POLYGON) {
|
||||
clipped = simple_clip_poly(sf.geometry, z, buffer, shared_nodes);
|
||||
clipped = simple_clip_poly(sf.geometry, z, buffer, sf.edge_nodes);
|
||||
}
|
||||
if (sf.t == VT_POINT) {
|
||||
clipped = clip_point(sf.geometry, z, buffer);
|
||||
@@ -1438,7 +1472,7 @@ void remove_attributes(serial_feature &sf, std::set<std::string> const &exclude_
|
||||
}
|
||||
}
|
||||
|
||||
serial_feature next_feature(decompressor *geoms, std::atomic<long long> *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<long long> *along, long long alongminus, int buffer, int *within, compressor **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 first_time, bool compressed, drawvec &shared_nodes) {
|
||||
serial_feature next_feature(decompressor *geoms, std::atomic<long long> *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<long long> *along, long long alongminus, int buffer, int *within, compressor **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 first_time, bool compressed) {
|
||||
while (1) {
|
||||
serial_feature sf;
|
||||
std::string s;
|
||||
@@ -1481,7 +1515,7 @@ serial_feature next_feature(decompressor *geoms, std::atomic<long long> *geompos
|
||||
|
||||
(*original_features)++;
|
||||
|
||||
if (clip_to_tile(sf, z, buffer, shared_nodes)) {
|
||||
if (clip_to_tile(sf, z, buffer)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1630,8 +1664,7 @@ void *run_prefilter(void *v) {
|
||||
json_writer state(rpa->prefilter_fp);
|
||||
|
||||
while (1) {
|
||||
drawvec dv; // don't have a way to track tile-edge nodes through prefilter
|
||||
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->oprogress, rpa->todo, rpa->fname, rpa->child_shards, rpa->filter, rpa->stringpool, rpa->pool_off, rpa->layer_unmaps, rpa->first_time, rpa->compressed, dv);
|
||||
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->oprogress, rpa->todo, rpa->fname, rpa->child_shards, rpa->filter, rpa->stringpool, rpa->pool_off, rpa->layer_unmaps, rpa->first_time, rpa->compressed);
|
||||
if (sf.t < 0) {
|
||||
break;
|
||||
}
|
||||
@@ -1937,6 +1970,7 @@ long long write_tile(decompressor *geoms, std::atomic<long long> *geompos_in, ch
|
||||
|
||||
double coalesced_area = 0;
|
||||
drawvec shared_nodes;
|
||||
std::vector<joint> shared_joints;
|
||||
|
||||
int tile_detail = line_detail;
|
||||
size_t skipped = 0;
|
||||
@@ -2044,7 +2078,7 @@ long long write_tile(decompressor *geoms, std::atomic<long long> *geompos_in, ch
|
||||
ssize_t which_partial = -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, &oprogress, todo, fname, child_shards, filter, stringpool, pool_off, layer_unmaps, first_time, compressed_input, shared_nodes);
|
||||
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, &oprogress, todo, fname, child_shards, filter, stringpool, pool_off, layer_unmaps, first_time, compressed_input);
|
||||
} else {
|
||||
sf = parse_feature(prefilter_jp, z, tx, ty, layermaps, tiling_seg, layer_unmaps, postfilter != NULL);
|
||||
}
|
||||
@@ -2223,6 +2257,83 @@ long long write_tile(decompressor *geoms, std::atomic<long long> *geompos_in, ch
|
||||
for (auto &g : sf.geometry) {
|
||||
shared_nodes.push_back(g);
|
||||
}
|
||||
} else if (sf.t == VT_POLYGON) {
|
||||
sf.geometry = remove_noop(sf.geometry, sf.t, 0);
|
||||
|
||||
for (size_t i = 0; i < sf.geometry.size(); i++) {
|
||||
if (sf.geometry[i].op == VT_MOVETO) {
|
||||
size_t j;
|
||||
for (j = i + 1; j < sf.geometry.size(); j++) {
|
||||
if (sf.geometry[j].op != VT_LINETO) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// j - 1 because we don't want the duplicate last point
|
||||
for (size_t k = i; k < j - 1; k++) {
|
||||
shared_joints.emplace_back(
|
||||
sf.geometry[k],
|
||||
sf.geometry[(k + 1 - i) % (j - 1 - i) + i],
|
||||
sf.geometry[(k + 2 - i) % (j - 1 - i) + i]);
|
||||
}
|
||||
|
||||
// since the starting point is never simplified away,
|
||||
// don't let it be simplified away in any other polygons either.
|
||||
// Needs to appear twice here so that the check below will see
|
||||
// it as appearing in multiple features.
|
||||
shared_nodes.push_back(sf.geometry[i]);
|
||||
shared_nodes.push_back(sf.geometry[i]);
|
||||
|
||||
// To avoid letting polygons get simplified away to nothing,
|
||||
// also keep the furthest-away point from the initial point
|
||||
// (which Douglas-Peucker simplification would keep anyway,
|
||||
// if its search weren't being split up by polygon side).
|
||||
|
||||
double far = 0;
|
||||
size_t which = i;
|
||||
for (size_t k = i + 1; k < j - 1; k++) {
|
||||
double xd = sf.geometry[k].x - sf.geometry[i].x;
|
||||
double yd = sf.geometry[k].y - sf.geometry[i].y;
|
||||
double d = xd * xd + yd * yd;
|
||||
if (d > far) {
|
||||
far = d;
|
||||
which = k;
|
||||
}
|
||||
}
|
||||
|
||||
shared_nodes.push_back(sf.geometry[which]);
|
||||
shared_nodes.push_back(sf.geometry[which]);
|
||||
|
||||
// And, likewise, the point most distant from those two points,
|
||||
// which probably would also be the one that Douglas-Peucker
|
||||
// would keep next.
|
||||
|
||||
far = 0;
|
||||
double which2 = i;
|
||||
|
||||
for (size_t k = i + 1; k < j - 1; k++) {
|
||||
double xd = sf.geometry[k].x - sf.geometry[i].x;
|
||||
double yd = sf.geometry[k].y - sf.geometry[i].y;
|
||||
double xd2 = sf.geometry[k].x - sf.geometry[which].x;
|
||||
double yd2 = sf.geometry[k].y - sf.geometry[which].y;
|
||||
double d = xd * xd + yd * yd + xd2 * xd2 + yd2 * yd2;
|
||||
if (d > far) {
|
||||
far = d;
|
||||
which2 = k;
|
||||
}
|
||||
}
|
||||
|
||||
shared_nodes.push_back(sf.geometry[which2]);
|
||||
shared_nodes.push_back(sf.geometry[which2]);
|
||||
|
||||
i = j - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &p : sf.edge_nodes) {
|
||||
shared_nodes.push_back(p);
|
||||
shared_nodes.push_back(p);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2306,6 +2417,25 @@ long long write_tile(decompressor *geoms, std::atomic<long long> *geompos_in, ch
|
||||
}
|
||||
|
||||
shared_nodes = just_shared_nodes;
|
||||
|
||||
std::sort(shared_joints.begin(), shared_joints.end());
|
||||
for (size_t i = 0; i + 1 < shared_joints.size(); i++) {
|
||||
if (shared_joints[i].mid == shared_joints[i + 1].mid) {
|
||||
// printf("%lld,%lld: ", shared_joints[i].mid.x, shared_joints[i].mid.y);
|
||||
if (shared_joints[i].p1 != shared_joints[i + 1].p1 ||
|
||||
shared_joints[i].p2 != shared_joints[i + 1].p2) {
|
||||
shared_nodes.push_back(shared_joints[i].mid);
|
||||
// printf("different\n");
|
||||
} else {
|
||||
// printf("same %lld,%lld to %lld,%lld\n", shared_joints[i].p1.x, shared_joints[i].p1.y, shared_joints[i].p2.x, shared_joints[i].p2.y);
|
||||
}
|
||||
} else {
|
||||
// printf("only once %lld,%lld\n", shared_joints[i].mid.x, shared_joints[i].mid.y);
|
||||
}
|
||||
}
|
||||
|
||||
std::sort(shared_nodes.begin(), shared_nodes.end());
|
||||
shared_joints.clear();
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < partials.size(); i++) {
|
||||
|
||||
Reference in New Issue
Block a user