From 51be28707f59096e254b074b24ec86ccbb19749b Mon Sep 17 00:00:00 2001 From: Vladimir Agafonkin Date: Thu, 9 Jun 2016 18:18:55 +0300 Subject: [PATCH] rewind polygons for vt2 compliance --- src/clip.js | 14 ++++++++------ src/convert.js | 10 +++++++--- src/tile.js | 17 +++++++++++++++++ test/fixtures/feature-tiles.json | 2 +- 4 files changed, 33 insertions(+), 10 deletions(-) diff --git a/src/clip.js b/src/clip.js index f657ad8..3697c0d 100644 --- a/src/clip.js +++ b/src/clip.js @@ -78,6 +78,7 @@ function clipGeometry(geometry, k1, k2, axis, intersect, closed) { points = geometry[i], area = points.area, dist = points.dist, + outer = points.outer, len = points.length, a, j, last; @@ -93,7 +94,7 @@ function clipGeometry(geometry, k1, k2, axis, intersect, closed) { if ((bk > k2)) { // ---|-----|--> slice.push(intersect(a, b, k1), intersect(a, b, k2)); - if (!closed) slice = newSlice(slices, slice, area, dist); + if (!closed) slice = newSlice(slices, slice, area, dist, outer); } else if (bk >= k1) slice.push(intersect(a, b, k1)); // ---|--> | @@ -101,7 +102,7 @@ function clipGeometry(geometry, k1, k2, axis, intersect, closed) { if ((bk < k1)) { // <--|-----|--- slice.push(intersect(a, b, k2), intersect(a, b, k1)); - if (!closed) slice = newSlice(slices, slice, area, dist); + if (!closed) slice = newSlice(slices, slice, area, dist, outer); } else if (bk <= k2) slice.push(intersect(a, b, k2)); // | <--|--- @@ -111,11 +112,11 @@ function clipGeometry(geometry, k1, k2, axis, intersect, closed) { if (bk < k1) { // <--|--- | slice.push(intersect(a, b, k1)); - if (!closed) slice = newSlice(slices, slice, area, dist); + if (!closed) slice = newSlice(slices, slice, area, dist, outer); } else if (bk > k2) { // | ---|--> slice.push(intersect(a, b, k2)); - if (!closed) slice = newSlice(slices, slice, area, dist); + if (!closed) slice = newSlice(slices, slice, area, dist, outer); } // | --> | } @@ -132,18 +133,19 @@ function clipGeometry(geometry, k1, k2, axis, intersect, closed) { if (closed && last && (slice[0][0] !== last[0] || slice[0][1] !== last[1])) slice.push(slice[0]); // add the final slice - newSlice(slices, slice, area, dist); + newSlice(slices, slice, area, dist, outer); } return slices; } -function newSlice(slices, slice, area, dist) { +function newSlice(slices, slice, area, dist, outer) { if (slice.length) { // we don't recalculate the area/length of the unclipped geometry because the case where it goes // below the visibility threshold as a result of clipping is rare, so we avoid doing unnecessary work slice.area = area; slice.dist = dist; + if (outer !== undefined) slice.outer = outer; slices.push(slice); } diff --git a/src/convert.js b/src/convert.js index 26f0cbb..3a238ff 100644 --- a/src/convert.js +++ b/src/convert.js @@ -33,7 +33,7 @@ function convertFeature(features, feature, tolerance) { type = geom.type, coords = geom.coordinates, tags = feature.properties, - i, j, rings; + i, j, rings, projectedRing; if (type === 'Point') { features.push(create(tags, 1, [projectPoint(coords)])); @@ -47,7 +47,9 @@ function convertFeature(features, feature, tolerance) { } else if (type === 'MultiLineString' || type === 'Polygon') { rings = []; for (i = 0; i < coords.length; i++) { - rings.push(project(coords[i], tolerance)); + projectedRing = project(coords[i], tolerance); + if (type === 'Polygon') projectedRing.outer = (i === 0); + rings.push(projectedRing); } features.push(create(tags, type === 'Polygon' ? 3 : 2, rings)); @@ -55,7 +57,9 @@ function convertFeature(features, feature, tolerance) { rings = []; for (i = 0; i < coords.length; i++) { for (j = 0; j < coords[i].length; j++) { - rings.push(project(coords[i][j], tolerance)); + projectedRing = project(coords[i][j], tolerance); + projectedRing.outer = (j === 0); + rings.push(projectedRing); } } features.push(create(tags, 3, rings)); diff --git a/src/tile.js b/src/tile.js index 4e6ccb1..818383b 100644 --- a/src/tile.js +++ b/src/tile.js @@ -71,6 +71,8 @@ function addFeature(tile, feature, tolerance, noSimplify) { tile.numPoints++; } + if (type === 3) rewind(simplifiedRing, ring.outer); + simplified.push(simplifiedRing); } } @@ -83,3 +85,18 @@ function addFeature(tile, feature, tolerance, noSimplify) { }); } } + +function rewind(ring, clockwise) { + var area = signedArea(ring); + if (area < 0 === clockwise) ring.reverse(); +} + +function signedArea(ring) { + var sum = 0; + for (var i = 0, len = ring.length, j = len - 1, p1, p2; i < len; j = i++) { + p1 = ring[i]; + p2 = ring[j]; + sum += (p2[0] - p1[0]) * (p1[1] + p2[1]); + } + return sum; +} diff --git a/test/fixtures/feature-tiles.json b/test/fixtures/feature-tiles.json index 6014550..7da54d8 100644 --- a/test/fixtures/feature-tiles.json +++ b/test/fixtures/feature-tiles.json @@ -1 +1 @@ -{"z0-0-0":[{"geometry":[[[3186,2048],[3197,2048],[3197,2037],[3186,2037],[3186,2048]]],"type":3,"tags":{"prop0":"value0","prop1":{"this":"that"}}}]} +{"z0-0-0":[{"geometry":[[[3186,2048],[3186,2037],[3197,2037],[3197,2048],[3186,2048]]],"type":3,"tags":{"prop0":"value0","prop1":{"this":"that"}}}]}