Clean up polygon generation and clipping. Make sure winding order is correct.

This commit is contained in:
Eric Fischer 2015-10-07 16:52:52 -07:00
parent 7154c29459
commit 5dc9f50345
4 changed files with 109 additions and 14 deletions

View File

@ -123,7 +123,6 @@ void parse_geometry(int t, json_object *j, unsigned *bbox, long long *fpos, FILE
}
int within = geometry_within[t];
long long began = *fpos;
if (within >= 0) {
int i;
for (i = 0; i < j->length; i++) {
@ -187,9 +186,16 @@ void parse_geometry(int t, json_object *j, unsigned *bbox, long long *fpos, FILE
}
if (t == GEOM_POLYGON) {
if (*fpos != began) {
serialize_byte(out, VT_CLOSEPATH, fpos, fname);
}
// Note that this is not using the correct meaning of closepath.
//
// We are using it here to close an entire Polygon, to distinguish
// the Polygons within a MultiPolygon from each other.
//
// This will be undone in fix_polygon(), which needs to know which
// rings come from which Polygons so that it can make the winding order
// of the outer ring be the opposite of the order of the inner rings.
serialize_byte(out, VT_CLOSEPATH, fpos, fname);
}
}

View File

@ -298,6 +298,19 @@ static drawvec clip_poly1(drawvec &geom, int z, int detail, int buffer) {
}
if (out.size() > 0) {
// If the polygon begins and ends outside the edge,
// the starting and ending points will be left as the
// places where it intersects the edge. Need to add
// another point to close the loop.
if (out[0].x != out[out.size() - 1].x || out[0].y != out[out.size() - 1].y) {
out.push_back(out[0]);
}
if (out.size() < 3) {
fprintf(stderr, "Polygon degenerated to a line segment\n");
}
out[0].op = VT_MOVETO;
for (unsigned i = 1; i < out.size(); i++) {
out[i].op = VT_LINETO;
@ -318,7 +331,7 @@ drawvec clip_poly(drawvec &geom, int z, int detail, int buffer) {
if (geom[i].op == VT_MOVETO) {
unsigned j;
for (j = i + 1; j < geom.size(); j++) {
if (geom[j].op == VT_CLOSEPATH || geom[j].op == VT_MOVETO) {
if (geom[j].op != VT_LINETO) {
break;
}
}
@ -328,20 +341,20 @@ drawvec clip_poly(drawvec &geom, int z, int detail, int buffer) {
tmp.push_back(geom[k]);
}
tmp = clip_poly1(tmp, z, detail, buffer);
if (tmp.size() > 0) {
if (tmp[0].x != tmp[tmp.size() - 1].x || tmp[0].y != tmp[tmp.size() - 1].y) {
fprintf(stderr, "Internal error: Polygon ring not closed\n");
exit(EXIT_FAILURE);
}
}
for (unsigned k = 0; k < tmp.size(); k++) {
out.push_back(tmp[k]);
}
if (j >= geom.size() || geom[j].op == VT_CLOSEPATH) {
if (out.size() > 0 && out[out.size() - 1].op != VT_CLOSEPATH) {
out.push_back(draw(VT_CLOSEPATH, 0, 0));
}
i = j;
} else {
i = j - 1;
}
i = j - 1;
} else {
out.push_back(geom[i]);
fprintf(stderr, "Unexpected operation in polygon %d\n", (int) geom[i].op);
exit(EXIT_FAILURE);
}
}
@ -665,3 +678,74 @@ drawvec reorder_lines(drawvec &geom) {
return geom;
}
drawvec fix_polygon(drawvec &geom) {
int outer = 1;
drawvec out;
unsigned i;
for (i = 0; i < geom.size(); i++) {
if (geom[i].op == VT_CLOSEPATH) {
outer = 1;
} else if (geom[i].op == VT_MOVETO) {
// Find the end of the ring
unsigned j;
for (j = i + 1; j < geom.size(); j++) {
if (geom[j].op != VT_LINETO) {
break;
}
}
// Make a temporary copy of the ring.
// Close it if it isn't closed.
drawvec ring;
for (unsigned a = i; a < j; a++) {
ring.push_back(geom[a]);
}
if (j - i != 0 && (ring[0].x != ring[j - i - 1].x || ring[0].y != ring[j - i - 1].y)) {
ring.push_back(ring[0]);
}
// Reverse ring if winding order doesn't match
// inner/outer expectation
double area = 0;
for (unsigned k = 0; k < ring.size(); k++) {
area += (long double) ring[k].x * (long double) ring[(k + 1) % ring.size()].y;
area -= (long double) ring[k].y * (long double) ring[(k + 1) % ring.size()].x;
}
if ((area > 0) != outer) {
drawvec tmp;
for (int a = ring.size() - 1; a >= 0; a--) {
tmp.push_back(ring[a]);
}
ring = tmp;
}
// Copy ring into output, fixing the moveto/lineto ops if necessary because of
// reversal or closing
for (unsigned a = 0; a < ring.size(); a++) {
if (a == 0) {
out.push_back(draw(VT_MOVETO, ring[a].x, ring[a].y));
} else {
out.push_back(draw(VT_LINETO, ring[a].x, ring[a].y));
}
}
// Next ring or polygon begins on the non-lineto that ended this one
// and is not an outer ring unless there is a terminator first
i = j - 1;
outer = 0;
} else {
fprintf(stderr, "Internal error: polygon ring begins with %d, not moveto\n", geom[i].op);
exit(EXIT_FAILURE);
}
}
return out;
}

View File

@ -26,3 +26,4 @@ drawvec clip_lines(drawvec &geom, int z, int detail, long long buffer);
int quick_check(long long *bbox, int z, int detail, long long buffer);
drawvec simplify_lines(drawvec &geom, int z, int detail);
drawvec reorder_lines(drawvec &geom);
drawvec fix_polygon(drawvec &geom);

View File

@ -566,6 +566,10 @@ long long write_tile(char **geoms, char *metabase, char *stringpool, unsigned *f
original_features++;
if (z == 0 && t == VT_POLYGON) {
geom = fix_polygon(geom);
}
int quick = quick_check(bbox, z, line_detail, buffer);
if (quick == 0) {
continue;