Merge pull request #187 from mapbox/earcut-polygon

Upgrade Clipper to 07b828b1 to fix self-intersecting polygons
This commit is contained in:
Eric Fischer 2016-04-26 14:26:10 -07:00
commit bd5dc4ad18
22 changed files with 1539 additions and 278 deletions

View File

@ -1,4 +1,4 @@
language: c
language: generic
sudo: false
@ -6,25 +6,24 @@ matrix:
include:
- os: linux
compiler: gcc
env: COVERAGE=gcov-4.9
env: COVERAGE=gcov-4.9 CC="gcc-4.9" CXX="g++-4.9"
addons:
apt:
sources: ['ubuntu-toolchain-r-test']
packages: [ 'g++-4.9', 'protobuf-compiler', 'libprotobuf-dev' ]
packages: [ 'g++-4.9' ]
- os: linux
compiler: clang
env: CC="clang-3.5" CXX="clang++-3.5"
addons:
apt:
packages: [ 'protobuf-compiler', 'libprotobuf-dev' ]
sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.5' ]
packages: [ 'clang-3.5' ]
- os: osx
compiler: clang
install:
- if [[ $(uname -s) == 'Darwin' ]]; then brew install protobuf; fi;
- if [ -n "${COVERAGE}" ]; then
export CXX=g++-4.9;
export CC=gcc-4.9;
export CXXFLAGS="--coverage -g";
export CFLAGS="--coverage -g";
export LDFLAGS="--coverage";

View File

@ -1,3 +1,7 @@
## 1.10.0
* Upgrade Clipper to fix potential crashes and improve polygon topology
## 1.9.16
* Switch to protozero as the library for reading and writing protocol buffers

View File

@ -60,6 +60,8 @@ clean:
indent:
clang-format -i -style="{BasedOnStyle: Google, IndentWidth: 8, UseTab: Always, AllowShortIfStatementsOnASingleLine: false, ColumnLimit: 0, ContinuationIndentWidth: 8, SpaceAfterCStyleCast: true, IndentCaseLabels: false, AllowShortBlocksOnASingleLine: false, AllowShortFunctionsOnASingleLine: false}" $(C) $(H)
geometry.o: clipper/clipper.hpp
TESTS = $(wildcard tests/*/out/*.json)
SPACE = $(NULL) $(NULL)
@ -67,7 +69,7 @@ test: tippecanoe tippecanoe-decode $(addsuffix .check,$(TESTS)) parallel-test pb
# Work around Makefile and filename punctuation limits: _ for space, @ for :, % for /
%.json.check:
./tippecanoe -f -o $@.mbtiles $(subst @,:,$(subst %,/,$(subst _, ,$(patsubst %.json.check,%,$(word 4,$(subst /, ,$@)))))) $(wildcard $(subst $(SPACE),/,$(wordlist 1,2,$(subst /, ,$@)))/*.json) < /dev/null
./tippecanoe -ad -f -o $@.mbtiles $(subst @,:,$(subst %,/,$(subst _, ,$(patsubst %.json.check,%,$(word 4,$(subst /, ,$@)))))) $(wildcard $(subst $(SPACE),/,$(wordlist 1,2,$(subst /, ,$@)))/*.json) < /dev/null
./tippecanoe-decode $@.mbtiles > $@.out
cmp $(patsubst %.check,%,$@) $@.out
rm $@.out $@.mbtiles

File diff suppressed because it is too large Load Diff

View File

@ -58,6 +58,7 @@
#include <ostream>
#include <functional>
#include <queue>
#include <unordered_map>
#if defined(CLIPPER_IMPL_INCLUDE)
#include CLIPPER_IMPL_INCLUDE
#endif
@ -77,7 +78,7 @@ enum PolyFillType { pftEvenOdd, pftNonZero, pftPositive, pftNegative };
static cInt const loRange = 0x7FFF;
static cInt const hiRange = 0x7FFF;
#else
typedef signed long long cInt;
typedef std::int64_t cInt;
static cInt const loRange = 0x3FFFFFFF;
static cInt const hiRange = 0x3FFFFFFFFFFFFFFFLL;
typedef signed long long long64; //used by Int128 class
@ -235,6 +236,7 @@ struct LocalMinimum;
struct OutPt;
struct OutRec;
struct Join;
struct OutPtIntersect;
typedef std::vector < OutRec* > PolyOutList;
typedef std::vector < TEdge* > EdgeList;
@ -377,6 +379,19 @@ private:
bool JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2);
void JoinCommonEdges();
void DoSimplePolygons();
bool FindIntersectLoop(std::unordered_multimap<int, OutPtIntersect> & dupeRec,
std::list<std::pair<int, OutPtIntersect> > & iList,
OutRec * outRec_parent,
int idx_origin,
int idx_prev,
std::set<int> & visited,
OutPt * orig_pt,
OutPt * prev_pt);
bool FixIntersects(std::unordered_multimap<int, OutPtIntersect> & dupeRec,
OutPt * op_j,
OutPt * op_k,
OutRec * outRec_j,
OutRec * outRec_k);
void FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec);
void FixupFirstLefts2(OutRec* InnerOutRec, OutRec* OuterOutRec);
void FixupFirstLefts3(OutRec* OldOutRec, OutRec* NewOutRec);

View File

@ -1,5 +1,6 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sqlite3.h>
#include <string>
@ -10,8 +11,6 @@
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
#include <google/protobuf/io/coded_stream.h>
#include "mvt.hh"
#include "tile.h"

View File

@ -2544,6 +2544,7 @@ int main(int argc, char **argv) {
{"reverse", no_argument, &additional[A_REVERSE], 1},
{"reorder", no_argument, &additional[A_REORDER], 1},
{"drop-lines", no_argument, &additional[A_LINE_DROP], 1},
{"check-polygons", no_argument, &additional[A_DEBUG_POLYGON], 1},
{"drop-polygons", no_argument, &additional[A_POLYGON_DROP], 1},
{"prefer-radix-sort", no_argument, &additional[A_PREFER_RADIX_SORT], 1},

View File

@ -18,6 +18,8 @@ extern "C" {
#include "projection.h"
}
static int pnpoly(drawvec &vert, size_t start, size_t nvert, long long testx, long long testy);
drawvec decode_geometry(FILE *meta, long long *geompos, int z, unsigned tx, unsigned ty, int detail, long long *bbox, unsigned initial_x, unsigned initial_y) {
drawvec out;
@ -204,36 +206,209 @@ drawvec shrink_lines(drawvec &geom, int z, int detail, int basezoom, long long *
}
#endif
static void decode_clipped(ClipperLib::PolyNode *t, drawvec &out) {
// To make the GeoJSON come out right, we need to do each of the
// outer rings followed by its children if any, and then go back
// to do any outer-ring children of those children as a new top level.
double get_area(drawvec &geom, int i, int j) {
double area = 0;
for (unsigned k = i; k < j; k++) {
area += (long double) geom[k].x * (long double) geom[i + ((k - i + 1) % (j - i))].y;
area -= (long double) geom[k].y * (long double) geom[i + ((k - i + 1) % (j - i))].x;
}
area /= 2;
return area;
}
void reverse_ring(drawvec &geom, int start, int end) {
drawvec tmp;
for (unsigned i = start; i < end; i++) {
tmp.push_back(geom[i]);
}
for (unsigned i = start; i < end; i++) {
geom[i] = tmp[end - 1 - i];
if (i == start) {
geom[i].op = VT_MOVETO;
} else if (i == end - 1) {
geom[i].op = VT_LINETO;
}
}
}
struct ring {
drawvec data;
long double area;
long long parent;
std::vector<size_t> children;
ring(drawvec &_data) {
data = _data;
area = get_area(_data, 0, _data.size());
parent = -1;
}
bool operator<(const ring &o) const {
if (fabs(this->area) < fabs(o.area)) {
return true;
} else {
return false;
}
}
};
static void decode_rings(ClipperLib::PolyNode *t, std::vector<ring> &out) {
// Supposedly outer ring
ClipperLib::Path p = t->Contour;
drawvec dv;
for (size_t i = 0; i < p.size(); i++) {
out.push_back(draw((i == 0) ? VT_MOVETO : VT_LINETO, p[i].X, p[i].Y));
dv.push_back(draw((i == 0) ? VT_MOVETO : VT_LINETO, p[i].X, p[i].Y));
}
if (p.size() > 0) {
out.push_back(draw(VT_LINETO, p[0].X, p[0].Y));
dv.push_back(draw(VT_LINETO, p[0].X, p[0].Y));
}
out.push_back(dv);
// Supposedly inner rings
for (int n = 0; n < t->ChildCount(); n++) {
ClipperLib::Path p = t->Childs[n]->Contour;
drawvec dv;
for (size_t i = 0; i < p.size(); i++) {
out.push_back(draw((i == 0) ? VT_MOVETO : VT_LINETO, p[i].X, p[i].Y));
dv.push_back(draw((i == 0) ? VT_MOVETO : VT_LINETO, p[i].X, p[i].Y));
}
if (p.size() > 0) {
out.push_back(draw(VT_LINETO, p[0].X, p[0].Y));
dv.push_back(draw(VT_LINETO, p[0].X, p[0].Y));
}
out.push_back(dv);
}
// Recurse to supposedly outer rings (children of the children)
for (int n = 0; n < t->ChildCount(); n++) {
for (int m = 0; m < t->Childs[n]->ChildCount(); m++) {
decode_clipped(t->Childs[n]->Childs[m], out);
decode_rings(t->Childs[n]->Childs[m], out);
}
}
}
static void decode_clipped(ClipperLib::PolyNode *t, drawvec &out) {
// The output of Clipper supposedly produces the outer rings
// as top level objects, with links to any inner-ring children
// they may have, each of which then has links to any outer rings
// that it has, and so on. This doesn't actually work.
// So instead, we pull out all the rings, sort them by absolute area,
// and go through them, looking for the
// smallest parent that contains a point from it, since we are
// guaranteed that at least one point in the polygon is strictly
// inside its parent (not on one of its boundary lines).
// Once we have that, we can run through the outer rings that have
// an even number of parents,
std::vector<ring> rings;
decode_rings(t, rings);
std::sort(rings.begin(), rings.end());
for (size_t i = 0; i < rings.size(); i++) {
for (size_t j = i + 1; j < rings.size(); j++) {
for (size_t k = 0; k < rings[i].data.size(); k++) {
if (pnpoly(rings[j].data, 0, rings[j].data.size(), rings[i].data[k].x, rings[i].data[k].y)) {
rings[i].parent = j;
rings[j].children.push_back(i);
goto nextring;
}
}
}
nextring:
;
}
for (size_t ii = rings.size(); ii > 0; ii--) {
size_t i = ii - 1;
if (rings[i].parent < 0) {
if (rings[i].area < 0) {
rings[i].area = -rings[i].area;
reverse_ring(rings[i].data, 0, rings[i].data.size());
}
} else {
if ((rings[i].area > 0) == (rings[rings[i].parent].area > 0)) {
rings[i].area = -rings[i].area;
reverse_ring(rings[i].data, 0, rings[i].data.size());
}
}
}
for (size_t ii = rings.size(); ii > 0; ii--) {
size_t i = ii - 1;
if (rings[i].area > 0) {
#if 0
fprintf(stderr, "ring area %Lf at %lld\n", rings[i].area, (long long) out.size());
#endif
for (size_t j = 0; j < rings[i].data.size(); j++) {
out.push_back(rings[i].data[j]);
}
for (size_t j = 0; j < rings[i].children.size(); j++) {
#if 0
fprintf(stderr, "ring area %Lf at %lld\n", rings[rings[i].children[j]].area, (long long) out.size());
#endif
for (size_t k = 0; k < rings[rings[i].children[j]].data.size(); k++) {
out.push_back(rings[rings[i].children[j]].data[k]);
}
rings[rings[i].children[j]].parent = -2;
}
} else if (rings[i].parent != -2) {
fprintf(stderr, "Found ring with child area but no parent %lld\n", (long long) i);
}
}
}
static void dump(drawvec &geom) {
ClipperLib::Clipper clipper(ClipperLib::ioStrictlySimple);
bool has_area = false;
for (size_t i = 0; i < geom.size(); i++) {
if (geom[i].op == VT_MOVETO) {
size_t j;
for (j = i + 1; j < geom.size(); j++) {
if (geom[j].op != VT_LINETO) {
break;
}
}
double area = get_area(geom, i, j);
if (area != 0) {
has_area = true;
}
ClipperLib::Path path;
printf("{ ClipperLib::Path path; ");
drawvec tmp;
for (size_t k = i; k < j; k++) {
printf("path.push_back(IntPoint(%lld,%lld)); ", geom[k].x, geom[k].y);
path.push_back(ClipperLib::IntPoint(geom[k].x, geom[k].y));
}
if (!clipper.AddPath(path, ClipperLib::ptSubject, true)) {
}
printf("clipper.AddPath(path, ClipperLib::ptSubject, true); }\n");
i = j - 1;
} else {
fprintf(stderr, "Unexpected operation in polygon %d\n", (int) geom[i].op);
exit(EXIT_FAILURE);
}
}
printf("clipper.Execute(ClipperLib::ctUnion, clipped));\n");
}
drawvec clean_or_clip_poly(drawvec &geom, int z, int detail, int buffer, bool clip) {
ClipperLib::Clipper clipper(ClipperLib::ioStrictlySimple);
@ -248,12 +423,7 @@ drawvec clean_or_clip_poly(drawvec &geom, int z, int detail, int buffer, bool cl
}
}
double area = 0;
for (size_t k = i; k < j; k++) {
area += (long double) geom[k].x * (long double) geom[i + ((k - i + 1) % (j - i))].y;
area -= (long double) geom[k].y * (long double) geom[i + ((k - i + 1) % (j - i))].x;
}
area = area / 2;
double area = get_area(geom, i, j);
if (area != 0) {
has_area = true;
}
@ -329,6 +499,102 @@ drawvec clean_or_clip_poly(drawvec &geom, int z, int detail, int buffer, bool cl
return out;
}
/* pnpoly:
Copyright (c) 1970-2003, Wm. Randolph Franklin
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimers.
Redistributions in binary form must reproduce the above copyright notice in the documentation and/or other materials provided with the distribution.
The name of W. Randolph Franklin may not be used to endorse or promote products derived from this Software without specific prior written permission.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
static int pnpoly(drawvec &vert, size_t start, size_t nvert, long long testx, long long testy) {
int i, j, c = 0;
for (i = 0, j = nvert - 1; i < nvert; j = i++) {
if (((vert[i + start].y > testy) != (vert[j + start].y > testy)) &&
(testx < (vert[j + start].x - vert[i + start].x) * (testy - vert[i + start].y) / (double) (vert[j + start].y - vert[i + start].y) + vert[i + start].x))
c = !c;
}
return c;
}
void check_polygon(drawvec &geom, drawvec &before) {
for (size_t i = 0; i + 1 < geom.size(); i++) {
for (size_t j = i + 1; j + 1 < geom.size(); j++) {
if (geom[i + 1].op == VT_LINETO && geom[j + 1].op == VT_LINETO) {
double s1_x = geom[i + 1].x - geom[i + 0].x;
double s1_y = geom[i + 1].y - geom[i + 0].y;
double s2_x = geom[j + 1].x - geom[j + 0].x;
double s2_y = geom[j + 1].y - geom[j + 0].y;
double s, t;
s = (-s1_y * (geom[i + 0].x - geom[j + 0].x) + s1_x * (geom[i + 0].y - geom[j + 0].y)) / (-s2_x * s1_y + s1_x * s2_y);
t = (s2_x * (geom[i + 0].y - geom[j + 0].y) - s2_y * (geom[i + 0].x - geom[j + 0].x)) / (-s2_x * s1_y + s1_x * s2_y);
if (t > 0 && t < 1 && s > 0 && s < 1) {
printf("Internal error: self-intersecting polygon. %lld,%lld to %lld,%lld intersects %lld,%lld to %lld,%lld\n",
geom[i + 0].x, geom[i + 0].y,
geom[i + 1].x, geom[i + 1].y,
geom[j + 0].x, geom[j + 0].y,
geom[j + 1].x, geom[j + 1].y);
dump(before);
}
}
}
}
size_t outer_start = -1;
size_t outer_len = 0;
for (size_t i = 0; i < geom.size(); i++) {
if (geom[i].op == VT_MOVETO) {
size_t j;
for (j = i + 1; j < geom.size(); j++) {
if (geom[j].op != VT_LINETO) {
break;
}
}
double area = get_area(geom, i, j);
#if 0
fprintf(stderr, "looking at %lld to %lld, area %f\n", (long long) i, (long long) j, area);
#endif
if (area > 0) {
outer_start = i;
outer_len = j - i;
} else {
for (size_t k = i; k < j; k++) {
if (!pnpoly(geom, outer_start, outer_len, geom[k].x, geom[k].y)) {
bool on_edge = false;
for (size_t l = outer_start; l < outer_start + outer_len; l++) {
if (geom[k].x == geom[l].x || geom[k].y == geom[l].y) {
on_edge = true;
break;
}
}
if (!on_edge) {
printf("%lld,%lld at %lld not in outer ring (%lld to %lld)\n", geom[k].x, geom[k].y, (long long) k, (long long) outer_start, (long long) (outer_start + outer_len));
dump(before);
#if 0
for (size_t l = outer_start; l < outer_start + outer_len; l++) {
fprintf(stderr, " %lld,%lld", geom[l].x, geom[l].y);
}
#endif
}
}
}
}
}
}
}
drawvec close_poly(drawvec &geom) {
drawvec out;
@ -532,12 +798,7 @@ drawvec reduce_tiny_poly(drawvec &geom, int z, int detail, bool *reduced, double
}
}
double area = 0;
for (size_t k = i; k < j; k++) {
area += (long double) geom[k].x * (long double) geom[i + ((k - i + 1) % (j - i))].y;
area -= (long double) geom[k].y * (long double) geom[i + ((k - i + 1) % (j - i))].x;
}
area = area / 2;
double area = get_area(geom, i, j);
// XXX There is an ambiguity here: If the area of a ring is 0 and it is followed by holes,
// we don't know whether the area-0 ring was a hole too or whether it was the outer ring
@ -931,12 +1192,7 @@ drawvec fix_polygon(drawvec &geom) {
// Reverse ring if winding order doesn't match
// inner/outer expectation
double area = 0;
for (size_t 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;
}
double area = get_area(ring, 0, ring.size());
if ((area > 0) != outer) {
drawvec tmp;
for (int a = ring.size() - 1; a >= 0; a--) {
@ -977,6 +1233,12 @@ std::vector<drawvec> chop_polygon(std::vector<drawvec> &geoms) {
for (size_t i = 0; i < geoms.size(); i++) {
if (geoms[i].size() > 700) {
static bool warned = false;
if (!warned) {
fprintf(stderr, "Warning: splitting up polygon with more than 700 sides\n");
warned = true;
}
long long midx = 0, midy = 0, count = 0;
long long maxx = LLONG_MIN, maxy = LLONG_MIN, minx = LLONG_MAX, miny = LLONG_MAX;

View File

@ -31,3 +31,4 @@ drawvec simplify_lines(drawvec &geom, int z, int detail, bool mark_tile_bounds);
drawvec reorder_lines(drawvec &geom);
drawvec fix_polygon(drawvec &geom);
std::vector<drawvec> chop_polygon(std::vector<drawvec> &geoms);
void check_polygon(drawvec &geom, drawvec &before);

View File

@ -12,7 +12,7 @@
}, "features": [
{ "type": "FeatureCollection", "properties": { "zoom": 0, "x": 0, "y": 0 }, "features": [
{ "type": "FeatureCollection", "properties": { "layer": "in" }, "features": [
{ "type": "Feature", "properties": { "boolean": true, "otherboolean": false, "stringify": "[\"yes\",27.00000000,27,1.4e27,{\"foo\":\"bar\"}]", "escape": "foo\u0001bar,ü\"\\/\u0008\u000c\u000a\u000d\u0009→", "prêt": "ready" }, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ 186.943359, 35.460670 ], [ 179.912109, 36.527295 ], [ 175.781250, 37.160317 ], [ 161.718750, 45.336702 ], [ 156.796875, 55.028022 ], [ 163.476562, 62.431074 ], [ 170.507812, 64.811557 ], [ 177.539062, 67.339861 ], [ 179.912109, 67.542167 ], [ 186.943359, 68.007571 ], [ 186.943359, 63.743631 ], [ 182.109375, 62.593341 ], [ 179.912109, 61.689872 ], [ 174.023438, 58.859224 ], [ 171.562500, 54.418930 ], [ 174.023438, 47.279229 ], [ 179.912109, 45.274886 ], [ 182.460938, 44.339565 ], [ 186.943359, 44.024422 ], [ 186.943359, 35.460670 ] ] ], [ [ [ -177.539062, 44.339565 ], [ -164.882812, 43.389082 ], [ -153.281250, 46.619261 ], [ -144.492188, 51.179343 ], [ -143.789062, 57.183902 ], [ -148.007812, 61.100789 ], [ -158.554688, 63.860036 ], [ -169.453125, 64.510643 ], [ -177.890625, 62.593341 ], [ -180.000000, 61.689872 ], [ -185.976562, 58.859224 ], [ -187.031250, 56.992883 ], [ -187.031250, 65.730626 ], [ -182.460938, 67.339861 ], [ -180.000000, 67.542167 ], [ -169.101562, 68.269387 ], [ -156.093750, 68.138852 ], [ -144.492188, 66.089364 ], [ -134.648438, 62.431074 ], [ -131.835938, 55.379110 ], [ -133.593750, 48.690960 ], [ -146.250000, 38.616870 ], [ -169.453125, 34.957995 ], [ -180.000000, 36.527295 ], [ -184.218750, 37.160317 ], [ -187.031250, 38.891033 ], [ -187.031250, 50.457504 ], [ -185.976562, 47.279229 ], [ -177.539062, 44.339565 ] ] ] ] } }
{ "type": "Feature", "properties": { "boolean": true, "otherboolean": false, "stringify": "[\"yes\",27.00000000,27,1.4e27,{\"foo\":\"bar\"}]", "escape": "foo\u0001bar,ü\"\\/\u0008\u000c\u000a\u000d\u0009→", "prêt": "ready" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -177.539062, 44.339565 ], [ -164.882812, 43.389082 ], [ -153.281250, 46.619261 ], [ -144.492188, 51.179343 ], [ -143.789062, 57.183902 ], [ -148.007812, 61.100789 ], [ -158.554688, 63.860036 ], [ -169.453125, 64.510643 ], [ -177.890625, 62.593341 ], [ -180.000000, 61.689872 ], [ -185.976562, 58.859224 ], [ -187.031250, 56.992883 ], [ -187.031250, 65.730626 ], [ -182.460938, 67.339861 ], [ -180.000000, 67.542167 ], [ -169.101562, 68.269387 ], [ -156.093750, 68.138852 ], [ -144.492188, 66.089364 ], [ -134.648438, 62.431074 ], [ -131.835938, 55.379110 ], [ -133.593750, 48.690960 ], [ -146.250000, 38.616870 ], [ -169.453125, 34.957995 ], [ -180.000000, 36.527295 ], [ -184.218750, 37.160317 ], [ -187.031250, 38.891033 ], [ -187.031250, 50.457504 ], [ -185.976562, 47.279229 ], [ -177.539062, 44.339565 ] ], [ [ 186.943359, 35.460670 ], [ 179.912109, 36.527295 ], [ 175.781250, 37.160317 ], [ 161.718750, 45.336702 ], [ 156.796875, 55.028022 ], [ 163.476562, 62.431074 ], [ 170.507812, 64.811557 ], [ 177.539062, 67.339861 ], [ 179.912109, 67.542167 ], [ 186.943359, 68.007571 ], [ 186.943359, 63.743631 ], [ 182.109375, 62.593341 ], [ 179.912109, 61.689872 ], [ 174.023438, 58.859224 ], [ 171.562500, 54.418930 ], [ 174.023438, 47.279229 ], [ 179.912109, 45.274886 ], [ 182.460938, 44.339565 ], [ 186.943359, 44.024422 ], [ 186.943359, 35.460670 ] ] ] } }
,
{ "type": "Feature", "properties": { "zoom": "z0-2" }, "geometry": { "type": "LineString", "coordinates": [ [ -112.851562, 55.178868 ], [ -117.773438, 44.590467 ], [ -104.414062, 51.179343 ] ] } }
] }
@ -204,13 +204,13 @@
,
{ "type": "FeatureCollection", "properties": { "zoom": 4, "x": 15, "y": 5 }, "features": [
{ "type": "FeatureCollection", "properties": { "layer": "in" }, "features": [
{ "type": "Feature", "properties": { "boolean": true, "otherboolean": false, "stringify": "[\"yes\",27.00000000,27,1.4e27,{\"foo\":\"bar\"}]", "escape": "foo\u0001bar,ü\"\\/\u0008\u000c\u000a\u000d\u0009→", "prêt": "ready" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 172.309570, 55.776573 ], [ 171.562500, 54.370959 ], [ 174.023438, 47.279229 ], [ 180.439453, 45.058001 ], [ 180.439453, 40.647304 ], [ 169.991455, 40.647304 ], [ 169.425659, 40.979898 ], [ 161.718750, 45.336702 ], [ 157.500000, 53.722717 ], [ 157.060547, 54.514704 ], [ 157.060547, 55.307264 ], [ 157.648315, 56.022948 ], [ 172.441406, 56.022948 ], [ 172.309570, 55.776573 ] ] ] } }
{ "type": "Feature", "properties": { "boolean": true, "otherboolean": false, "stringify": "[\"yes\",27.00000000,27,1.4e27,{\"foo\":\"bar\"}]", "escape": "foo\u0001bar,ü\"\\/\u0008\u000c\u000a\u000d\u0009→", "prêt": "ready" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 171.562500, 54.370959 ], [ 174.023438, 47.279229 ], [ 180.439453, 45.058001 ], [ 180.439453, 40.647304 ], [ 169.991455, 40.647304 ], [ 169.425659, 40.979898 ], [ 161.718750, 45.336702 ], [ 157.500000, 53.722717 ], [ 157.060547, 54.514704 ], [ 157.060547, 55.307264 ], [ 157.648315, 56.022948 ], [ 172.441406, 56.022948 ], [ 172.309570, 55.776573 ], [ 171.562500, 54.370959 ] ] ] } }
] }
] }
,
{ "type": "FeatureCollection", "properties": { "zoom": 4, "x": 15, "y": 4 }, "features": [
{ "type": "FeatureCollection", "properties": { "layer": "in" }, "features": [
{ "type": "Feature", "properties": { "boolean": true, "otherboolean": false, "stringify": "[\"yes\",27.00000000,27,1.4e27,{\"foo\":\"bar\"}]", "escape": "foo\u0001bar,ü\"\\/\u0008\u000c\u000a\u000d\u0009→", "prêt": "ready" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 180.000000, 61.650771 ], [ 174.023438, 58.816586 ], [ 172.309570, 55.776573 ], [ 172.172241, 55.528631 ], [ 157.241821, 55.528631 ], [ 157.500000, 55.841398 ], [ 163.476562, 62.431074 ], [ 170.507812, 64.776466 ], [ 175.187988, 66.513260 ], [ 175.676880, 66.687784 ], [ 180.439453, 66.687784 ], [ 180.439453, 61.850966 ], [ 180.000000, 61.650771 ] ] ] } }
{ "type": "Feature", "properties": { "boolean": true, "otherboolean": false, "stringify": "[\"yes\",27.00000000,27,1.4e27,{\"foo\":\"bar\"}]", "escape": "foo\u0001bar,ü\"\\/\u0008\u000c\u000a\u000d\u0009→", "prêt": "ready" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 174.023438, 58.816586 ], [ 172.309570, 55.776573 ], [ 172.172241, 55.528631 ], [ 157.241821, 55.528631 ], [ 157.500000, 55.841398 ], [ 163.476562, 62.431074 ], [ 170.507812, 64.776466 ], [ 175.187988, 66.513260 ], [ 175.676880, 66.687784 ], [ 180.439453, 66.687784 ], [ 180.439453, 61.850966 ], [ 180.000000, 61.650771 ], [ 174.023438, 58.816586 ] ] ] } }
] }
] }
,
@ -234,7 +234,7 @@
,
{ "type": "FeatureCollection", "properties": { "zoom": 5, "x": 0, "y": 9 }, "features": [
{ "type": "FeatureCollection", "properties": { "layer": "in" }, "features": [
{ "type": "Feature", "properties": { "boolean": true, "otherboolean": false, "stringify": "[\"yes\",27.00000000,27,1.4e27,{\"foo\":\"bar\"}]", "escape": "foo\u0001bar,ü\"\\/\u0008\u000c\u000a\u000d\u0009→", "prêt": "ready" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -179.868164, 61.710706 ], [ -180.219727, 61.551493 ], [ -180.219727, 61.710706 ], [ -179.868164, 61.710706 ] ] ] } }
{ "type": "Feature", "properties": { "boolean": true, "otherboolean": false, "stringify": "[\"yes\",27.00000000,27,1.4e27,{\"foo\":\"bar\"}]", "escape": "foo\u0001bar,ü\"\\/\u0008\u000c\u000a\u000d\u0009→", "prêt": "ready" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -180.219727, 61.710706 ], [ -179.868164, 61.710706 ], [ -180.219727, 61.551493 ], [ -180.219727, 61.710706 ] ] ] } }
] }
] }
,
@ -438,25 +438,25 @@
,
{ "type": "FeatureCollection", "properties": { "zoom": 5, "x": 31, "y": 11 }, "features": [
{ "type": "FeatureCollection", "properties": { "layer": "in" }, "features": [
{ "type": "Feature", "properties": { "boolean": true, "otherboolean": false, "stringify": "[\"yes\",27.00000000,27,1.4e27,{\"foo\":\"bar\"}]", "escape": "foo\u0001bar,ü\"\\/\u0008\u000c\u000a\u000d\u0009→", "prêt": "ready" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 173.485107, 48.922499 ], [ 174.023438, 47.279229 ], [ 180.219727, 45.135555 ], [ 180.219727, 40.813809 ], [ 169.708557, 40.813809 ], [ 169.425659, 40.979898 ], [ 168.750000, 41.376809 ], [ 168.530273, 41.504464 ], [ 168.530273, 49.066668 ], [ 173.435669, 49.066668 ], [ 173.485107, 48.922499 ] ] ] } }
{ "type": "Feature", "properties": { "boolean": true, "otherboolean": false, "stringify": "[\"yes\",27.00000000,27,1.4e27,{\"foo\":\"bar\"}]", "escape": "foo\u0001bar,ü\"\\/\u0008\u000c\u000a\u000d\u0009→", "prêt": "ready" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 174.023438, 47.279229 ], [ 180.219727, 45.135555 ], [ 180.219727, 40.813809 ], [ 169.708557, 40.813809 ], [ 169.425659, 40.979898 ], [ 168.750000, 41.376809 ], [ 168.530273, 41.504464 ], [ 168.530273, 49.066668 ], [ 173.435669, 49.066668 ], [ 173.485107, 48.922499 ], [ 174.023438, 47.279229 ] ] ] } }
] }
] }
,
{ "type": "FeatureCollection", "properties": { "zoom": 5, "x": 31, "y": 10 }, "features": [
{ "type": "FeatureCollection", "properties": { "layer": "in" }, "features": [
{ "type": "Feature", "properties": { "boolean": true, "otherboolean": false, "stringify": "[\"yes\",27.00000000,27,1.4e27,{\"foo\":\"bar\"}]", "escape": "foo\u0001bar,ü\"\\/\u0008\u000c\u000a\u000d\u0009→", "prêt": "ready" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 172.309570, 55.776573 ], [ 171.562500, 54.369359 ], [ 173.485107, 48.922499 ], [ 173.531799, 48.777913 ], [ 168.530273, 48.777913 ], [ 168.530273, 55.899956 ], [ 172.375488, 55.899956 ], [ 172.309570, 55.776573 ] ] ] } }
{ "type": "Feature", "properties": { "boolean": true, "otherboolean": false, "stringify": "[\"yes\",27.00000000,27,1.4e27,{\"foo\":\"bar\"}]", "escape": "foo\u0001bar,ü\"\\/\u0008\u000c\u000a\u000d\u0009→", "prêt": "ready" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 171.562500, 54.369359 ], [ 173.485107, 48.922499 ], [ 173.531799, 48.777913 ], [ 168.530273, 48.777913 ], [ 168.530273, 55.899956 ], [ 172.375488, 55.899956 ], [ 172.309570, 55.776573 ], [ 171.562500, 54.369359 ] ] ] } }
] }
] }
,
{ "type": "FeatureCollection", "properties": { "zoom": 5, "x": 31, "y": 9 }, "features": [
{ "type": "FeatureCollection", "properties": { "layer": "in" }, "features": [
{ "type": "Feature", "properties": { "boolean": true, "otherboolean": false, "stringify": "[\"yes\",27.00000000,27,1.4e27,{\"foo\":\"bar\"}]", "escape": "foo\u0001bar,ü\"\\/\u0008\u000c\u000a\u000d\u0009→", "prêt": "ready" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 179.901123, 61.606396 ], [ 174.023438, 58.815164 ], [ 172.306824, 55.776573 ], [ 172.240906, 55.652798 ], [ 168.530273, 55.652798 ], [ 168.530273, 61.710706 ], [ 180.131836, 61.710706 ], [ 179.901123, 61.606396 ] ] ] } }
{ "type": "Feature", "properties": { "boolean": true, "otherboolean": false, "stringify": "[\"yes\",27.00000000,27,1.4e27,{\"foo\":\"bar\"}]", "escape": "foo\u0001bar,ü\"\\/\u0008\u000c\u000a\u000d\u0009→", "prêt": "ready" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 174.023438, 58.815164 ], [ 172.306824, 55.776573 ], [ 172.240906, 55.652798 ], [ 168.530273, 55.652798 ], [ 168.530273, 61.710706 ], [ 180.131836, 61.710706 ], [ 179.901123, 61.606396 ], [ 174.023438, 58.815164 ] ] ] } }
] }
] }
,
{ "type": "FeatureCollection", "properties": { "zoom": 5, "x": 31, "y": 8 }, "features": [
{ "type": "FeatureCollection", "properties": { "layer": "in" }, "features": [
{ "type": "Feature", "properties": { "boolean": true, "otherboolean": false, "stringify": "[\"yes\",27.00000000,27,1.4e27,{\"foo\":\"bar\"}]", "escape": "foo\u0001bar,ü\"\\/\u0008\u000c\u000a\u000d\u0009→", "prêt": "ready" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 180.000000, 61.650771 ], [ 179.901123, 61.606396 ], [ 179.670410, 61.501734 ], [ 168.530273, 61.501734 ], [ 168.530273, 64.135775 ], [ 170.507812, 64.775296 ], [ 175.190735, 66.513260 ], [ 175.435181, 66.600676 ], [ 180.219727, 66.600676 ], [ 180.219727, 61.751031 ], [ 180.000000, 61.650771 ] ] ] } }
{ "type": "Feature", "properties": { "boolean": true, "otherboolean": false, "stringify": "[\"yes\",27.00000000,27,1.4e27,{\"foo\":\"bar\"}]", "escape": "foo\u0001bar,ü\"\\/\u0008\u000c\u000a\u000d\u0009→", "prêt": "ready" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 179.901123, 61.606396 ], [ 179.670410, 61.501734 ], [ 168.530273, 61.501734 ], [ 168.530273, 64.135775 ], [ 170.507812, 64.775296 ], [ 175.190735, 66.513260 ], [ 175.435181, 66.600676 ], [ 180.219727, 66.600676 ], [ 180.219727, 61.751031 ], [ 180.000000, 61.650771 ], [ 179.901123, 61.606396 ] ] ] } }
] }
] }
,

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -12,7 +12,7 @@
}, "features": [
{ "type": "FeatureCollection", "properties": { "zoom": 0, "x": 0, "y": 0 }, "features": [
{ "type": "FeatureCollection", "properties": { "layer": "in" }, "features": [
{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -3.076172, 61.015725 ], [ -15.380859, 52.908902 ], [ -10.019531, 50.007739 ], [ -5.009766, 8.059230 ], [ -17.050781, -21.943046 ], [ -75.058594, -30.977609 ], [ -118.037109, -8.928487 ], [ -135.000000, 29.075375 ], [ -113.027344, 53.014783 ], [ -111.884766, 53.383328 ], [ -117.070312, 60.020952 ], [ -113.027344, 69.005675 ], [ -93.076172, 75.004940 ], [ -74.003906, 79.004962 ], [ -33.046875, 79.004962 ], [ -3.076172, 61.015725 ] ], [ [ -111.884766, 53.383328 ], [ -103.007812, 39.027719 ], [ -71.015625, 29.075375 ], [ -37.001953, 34.016242 ], [ -15.380859, 52.908902 ], [ -28.037109, 59.040555 ], [ -81.035156, 63.035039 ], [ -111.884766, 53.383328 ] ] ] } }
{ "type": "Feature", "properties": { }, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ -103.007812, 39.027719 ], [ -71.015625, 29.075375 ], [ -37.001953, 34.016242 ], [ -15.380859, 52.908902 ], [ -10.019531, 50.007739 ], [ -5.009766, 8.059230 ], [ -17.050781, -21.943046 ], [ -75.058594, -30.977609 ], [ -118.037109, -8.928487 ], [ -135.000000, 29.075375 ], [ -113.027344, 53.014783 ], [ -111.884766, 53.383328 ], [ -103.007812, 39.027719 ] ] ], [ [ [ -117.070312, 60.020952 ], [ -113.027344, 69.005675 ], [ -93.076172, 75.004940 ], [ -74.003906, 79.004962 ], [ -33.046875, 79.004962 ], [ -3.076172, 61.015725 ], [ -15.380859, 52.908902 ], [ -28.037109, 59.040555 ], [ -81.035156, 63.035039 ], [ -111.884766, 53.383328 ], [ -117.070312, 60.020952 ] ] ] ] } }
] }
] }
] }

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -462,7 +462,14 @@ void *partial_feature_worker(void *v) {
// Scaling may have made the polygon degenerate.
// Give Clipper a chance to try to fix it.
for (size_t i = 0; i < geoms.size(); i++) {
drawvec before;
if (additional[A_DEBUG_POLYGON]) {
before = geoms[i];
}
geoms[i] = clean_or_clip_poly(geoms[i], 0, 0, 0, false);
if (additional[A_DEBUG_POLYGON]) {
check_polygon(geoms[i], before);
}
}
}

2
tile.h
View File

@ -50,6 +50,8 @@ static int additional_options[] = {
A_REORDER,
#define A_LINE_DROP ((int) 'l')
A_LINE_DROP,
#define A_DEBUG_POLYGON ((int) 'd')
A_DEBUG_POLYGON,
#define A_POLYGON_DROP ((int) 'p')
A_POLYGON_DROP,
#define A_PREFER_RADIX_SORT ((int) 'R')

View File

@ -1 +1 @@
#define VERSION "tippecanoe v1.9.16\n"
#define VERSION "tippecanoe v1.10.0\n"