Externalizing polygon shard detection (#146)

* Start of externalizing polygon shard detection

* Completely untested external quicksort

* Add unit test for external quicksort

* Remember to clean up temporary files

* Sort and scan the vertices

* Bring over more vertex logic

* Make nodes from vertices

* Checkpoint on switching over to global shared nodes

* Do the thing

* Revert unintended change to coalesced linestring behavior

* Take shared nodes into account in early simplification

* Let it do more sorting in memory

* Fix overnoding of collinear linestrings

* Remove duplicate nodes, since only duplicate vertices now matter

* Remember to delete temporary files

* Fix out of bounds memory access below, apparently

* Fix the actual undefined behavior

* Still running out of memory in one case. Find out where.

* Forgot the conditional

* Try again to make it not run out of memory

* Update version and changelog
This commit is contained in:
Erica Fischer
2023-10-03 10:53:12 -07:00
committed by GitHub
parent dcc3a11915
commit 26bf08deb2
18 changed files with 713 additions and 230 deletions
+122 -2
View File
@@ -393,6 +393,29 @@ static std::string strip_zeroes(std::string s) {
return s;
}
int nodecmp(const void *void1, const void *void2) {
node *n1 = (node *) void1;
node *n2 = (node *) void2;
if (n1->index < n2->index) {
return -1;
} else if (n1->index > n2->index) {
return 1;
}
return 0;
}
static void add_scaled_node(struct reader *r, serialization_state *sst, draw g) {
long long x = SHIFT_LEFT(g.x);
long long y = SHIFT_LEFT(g.y);
struct node n;
n.index = encode_quadkey((unsigned) x, (unsigned) y);
fwrite_check((char *) &n, sizeof(struct node), 1, r->nodefile, &r->nodepos, sst->fname);
}
// called from frontends
int serialize_feature(struct serialization_state *sst, serial_feature &sf) {
struct reader *r = &(*sst->readers)[sst->segment];
@@ -446,11 +469,12 @@ int serialize_feature(struct serialization_state *sst, serial_feature &sf) {
scaled_geometry = simple_clip_poly(scaled_geometry, SHIFT_RIGHT(c.minx), SHIFT_RIGHT(c.miny), SHIFT_RIGHT(c.maxx), SHIFT_RIGHT(c.maxy), prevent[P_SIMPLIFY_SHARED_NODES]);
} else if (sf.t == VT_LINE) {
scaled_geometry = clip_lines(scaled_geometry, SHIFT_RIGHT(c.minx), SHIFT_RIGHT(c.miny), SHIFT_RIGHT(c.maxx), SHIFT_RIGHT(c.maxy));
scaled_geometry = remove_noop(scaled_geometry, sf.t, 0);
} else if (sf.t == VT_POINT) {
scaled_geometry = clip_point(scaled_geometry, SHIFT_RIGHT(c.minx), SHIFT_RIGHT(c.miny), SHIFT_RIGHT(c.maxx), SHIFT_RIGHT(c.maxy));
}
scaled_geometry = remove_noop(scaled_geometry, sf.t, 0);
sf.bbox[0] = LLONG_MAX;
sf.bbox[1] = LLONG_MAX;
sf.bbox[2] = LLONG_MIN;
@@ -480,6 +504,100 @@ int serialize_feature(struct serialization_state *sst, serial_feature &sf) {
return 1;
}
if (prevent[P_SIMPLIFY_SHARED_NODES]) {
scaled_geometry = remove_noop(scaled_geometry, sf.t, 0);
if (sf.t == VT_POLYGON || sf.t == VT_LINE) {
for (size_t i = 0; i < scaled_geometry.size(); i++) {
if (scaled_geometry[i].op == VT_MOVETO) {
size_t j;
for (j = i + 1; j < scaled_geometry.size(); j++) {
if (scaled_geometry[j].op != VT_LINETO) {
break;
}
}
if (sf.t == VT_POLYGON && j - i >= 4) {
for (size_t k = i; k < j - 1; k++) {
// % (j - i - 1) because we don't want the duplicate last point
struct vertex v(
scaled_geometry[(k - i + 0) % (j - i - 1) + i],
scaled_geometry[(k - i + 1) % (j - i - 1) + i],
scaled_geometry[(k - i + 2) % (j - i - 1) + i]);
fwrite_check((char *) &v, sizeof(struct vertex), 1, r->vertexfile, &r->vertexpos, sst->fname);
}
} else if (sf.t == VT_LINE && j - i >= 2) {
for (size_t k = i; k + 2 < j; k++) {
struct vertex v(
scaled_geometry[k + 0],
scaled_geometry[k + 1],
scaled_geometry[k + 2]);
fwrite_check((char *) &v, sizeof(struct vertex), 1, r->vertexfile, &r->vertexpos, sst->fname);
}
}
// 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.
add_scaled_node(r, sst, scaled_geometry[i]);
if (sf.t == VT_LINE && j - i >= 2) {
// linestrings also need to preserve the last point
add_scaled_node(r, sst, scaled_geometry[j - 1]);
} else if (sf.t == VT_POLYGON && j - i >= 4) {
// 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 = scaled_geometry[k].x - scaled_geometry[i].x;
double yd = scaled_geometry[k].y - scaled_geometry[i].y;
double d = xd * xd + yd * yd;
if (d > far ||
((d == far) && (scaled_geometry[k] < scaled_geometry[which]))) {
far = d;
which = k;
}
}
add_scaled_node(r, sst, scaled_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;
size_t which2 = i;
for (size_t k = i + 1; k < j - 1; k++) {
double d = distance_from_line(scaled_geometry[k].x, scaled_geometry[k].y,
scaled_geometry[i].x, scaled_geometry[i].y,
scaled_geometry[which].x, scaled_geometry[which].y);
if ((d > far) ||
((d == far) && (scaled_geometry[k] < scaled_geometry[which2]))) {
far = d;
which2 = k;
}
}
add_scaled_node(r, sst, scaled_geometry[which2]);
}
i = j - 1;
}
}
}
}
if (!sf.has_id) {
if (additional[A_GENERATE_IDS]) {
sf.has_id = true;
@@ -583,7 +701,9 @@ int serialize_feature(struct serialization_state *sst, serial_feature &sf) {
for (size_t i = 0; i < scaled_geometry.size(); i++) {
ix += scaled_geometry[i].x + scaled_geometry[i].y;
}
ix = ix % scaled_geometry.size();
if (scaled_geometry.size() != 0) {
ix = ix % scaled_geometry.size();
}
// If off the edge of the plane, mask to bring it back into the addressable area
midx = SHIFT_LEFT(scaled_geometry[ix].x) & ((1LL << 32) - 1);