Skip to content

Commit b0f25fe

Browse files
committed
Fix a line clipping edge case
Fix the edge case when a line string enters the clip region from outside and has a node exactly on the clip region border. Before, the node on the border was skipped, which caused wrong line metrics estimation.
1 parent 1c0e989 commit b0f25fe

File tree

2 files changed

+50
-6
lines changed

2 files changed

+50
-6
lines changed

include/mapbox/geojsonvt/clip.hpp

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ class clipper {
115115
const auto& b = line[i + 1];
116116
const double ak = get<I>(a);
117117
const double bk = get<I>(b);
118+
const bool isLastSeg = (i == (len - 2));
118119

119120
if (lineMetrics) segLen = ::hypot((b.x - a.x), (b.y - a.y));
120121

@@ -135,9 +136,11 @@ class clipper {
135136
t = calc_progress<I>(a, b, k1);
136137
slice.emplace_back(intersect<I>(a, b, k1, t));
137138
if (lineMetrics) slice.segStart = lineLen + segLen * t;
139+
if (isLastSeg) slice.emplace_back(b); // last point
138140

139-
if (i == len - 2)
140-
slice.emplace_back(b); // last point
141+
} else if (bk == k1 && !isLastSeg) { // --->|.. |
142+
if (lineMetrics) slice.segStart = lineLen + segLen;
143+
slice.emplace_back(b);
141144
}
142145
} else if (ak > k2) {
143146
if (bk < k1) { // <--|-----|---
@@ -152,13 +155,16 @@ class clipper {
152155
slices.emplace_back(std::move(slice));
153156

154157
slice = newSlice(line);
158+
155159
} else if (bk < k2) { // | <--|---
156160
t = calc_progress<I>(a, b, k2);
157161
slice.emplace_back(intersect<I>(a, b, k2, t));
158162
if (lineMetrics) slice.segStart = lineLen + segLen * t;
163+
if (isLastSeg) slice.emplace_back(b); // last point
159164

160-
if (i == len - 2)
161-
slice.emplace_back(b); // last point
165+
} else if (bk == k2 && !isLastSeg) { // | ..|<---
166+
if (lineMetrics) slice.segStart = lineLen + segLen;
167+
slice.emplace_back(b);
162168
}
163169
} else {
164170
slice.emplace_back(a);
@@ -177,7 +183,7 @@ class clipper {
177183
slices.emplace_back(std::move(slice));
178184
slice = newSlice(line);
179185

180-
} else if (i == len - 2) { // | --> |
186+
} else if (isLastSeg) { // | --> |
181187
slice.emplace_back(b);
182188
}
183189
}
@@ -186,7 +192,7 @@ class clipper {
186192
}
187193

188194
if (!slice.empty()) { // add the final slice
189-
slice.segEnd = lineLen;
195+
if (lineMetrics) slice.segEnd = lineLen;
190196
slices.emplace_back(std::move(slice));
191197
}
192198
}

test/test.cpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -475,3 +475,41 @@ TEST(geoJSONToTile, Metrics) {
475475
double rightClipEnd = (rightProps.find("mapbox_clip_end")->second).get<double>();
476476
EXPECT_DOUBLE_EQ(rightClipEnd, 1.0);
477477
}
478+
479+
TEST(GeoJSONVT, ClipVertexOnTileBorder) {
480+
std::string data = R"geojson({
481+
"type": "Feature",
482+
"geometry": {
483+
"type": "LineString",
484+
"coordinates":[
485+
[-77.031373697916663,38.895516493055553],
486+
[-77.01416015625,38.887532552083336],
487+
[-76.99,38.87]
488+
]
489+
}
490+
})geojson"; // The second node is exactly on the (13, 2344, 3134) tile border.
491+
auto geojson = mapbox::geojson::parse(data);
492+
mapbox::geojsonvt::Options options;
493+
options.lineMetrics = true;
494+
options.buffer = 2048;
495+
options.extent = 8192;
496+
497+
const double kEpsilon = 1e-5;
498+
499+
mapbox::geojsonvt::GeoJSONVT index{geojson,options};
500+
501+
const Tile& tile = index.getTile(13, 2344, 3134);
502+
ASSERT_FALSE(tile.features.empty());
503+
const mapbox::geometry::line_string<int16_t> expected{
504+
{-2048, 2747}, {408, 5037}
505+
};
506+
const auto actual = tile.features[0].geometry.get<mapbox::geometry::line_string<int16_t>>();
507+
ASSERT_EQ(actual, expected);
508+
509+
// Check line metrics
510+
auto& props = tile.features[0].properties;
511+
double clipStart1 = (props.find("mapbox_clip_start")->second).get<double>();
512+
double clipEnd1 = (props.find("mapbox_clip_end")->second).get<double>();
513+
EXPECT_NEAR(0.660622, clipStart1, kEpsilon);
514+
EXPECT_NEAR(1.0, clipEnd1, kEpsilon);
515+
}

0 commit comments

Comments
 (0)