@@ -10,6 +10,52 @@ namespace operations {
1010
1111using linestr = std::vector<geometry::Coord>;
1212
13+ bool segmentIntersectsGridBounds (const geometry::Line &line,
14+ const grid::Grid &raster) {
15+
16+ auto xy0 = line.start ;
17+ auto xy1 = line.end ;
18+ const double x0 = xy0.x ;
19+ const double y0 = xy0.y ;
20+ const double x1 = xy1.x ;
21+ const double y1 = xy1.y ;
22+
23+ auto xymin = raster.grid_to_world * geometry::Coord (0.0 , 0.0 );
24+ auto xymax =
25+ raster.grid_to_world * geometry::Coord (raster.ncols , raster.nrows );
26+ const double xmin = xymin.x ;
27+ const double ymin = xymin.y ;
28+ const double xmax = xymax.x ;
29+ const double ymax = xymax.y ;
30+
31+ auto inside = [&](const double &x, const double &y) {
32+ return x >= xmin && x <= xmax && y >= ymin && y <= ymax;
33+ };
34+
35+ if (inside (x0, y0) || inside (x1, y1)) {
36+ return true ;
37+ }
38+
39+ auto crosses_x = [&](const double &xtest) {
40+ if (x0 <= xtest && xtest <= x1) {
41+ const double ycross = y0 + (xtest - x0) * (y1 - y0) / (x1 - x0);
42+ return ymin <= ycross && ycross <= ymax;
43+ }
44+ return false ;
45+ };
46+
47+ auto crosses_y = [&](const double &ytest) {
48+ if (y0 <= ytest && ytest <= y1) {
49+ const double xcross = x0 + (ytest - y0) * (x1 - x0) / (y1 - y0);
50+ return xmin <= xcross && xcross <= xmax;
51+ }
52+ return false ;
53+ };
54+
55+ return crosses_x (xmin) || crosses_x (xmax) || crosses_y (ymin) ||
56+ crosses_y (ymax);
57+ }
58+
1359// / Piecewise decomposition of a linestring according to intersection points
1460std::vector<linestr> split_linestr (linestr linestring, linestr intersections) {
1561 // Add line start point
@@ -28,27 +74,49 @@ std::vector<linestr> split_linestr(linestr linestring, linestr intersections) {
2874
2975// / Find intersection points of a linestring with a raster grid
3076std::vector<linestr>
31- findIntersectionsLineString (geometry::LineString linestring,
32- grid::Grid raster ) {
77+ findIntersectionsLineString (geometry::LineString linestring, grid::Grid raster,
78+ bool bounded ) {
3379 linestr coords = linestring.coordinates ;
3480
3581 std::vector<linestr> allsplits;
3682 linestr linestr_piece;
3783 for (std::size_t i = 0 ; i < coords.size () - 1 ; i++) {
3884 geometry::Line line (coords.at (i), coords.at (i + 1 ));
3985
40- // If the line starts and ends in different cells, it needs to be cleaned.
41- if (raster.cellIndex (line.start ) != raster.cellIndex (line.end )) {
86+ bool single_cell =
87+ raster.cellIndices (line.start ) == raster.cellIndices (line.end );
88+
89+ // If the line starts and ends in the same cell,
90+ // or (bounded and (segment does not intersect overall grid bounds))
91+ if (single_cell ||
92+ (bounded && !segmentIntersectsGridBounds (line, raster))) {
93+ // then don't split, just push back the coordinate
94+ linestr_piece.push_back (coords.at (i));
95+ } else {
96+ // otherwise do split this straight-line segment
4297 linestr intersections = raster.findIntersections (line);
98+ // if only splitting within grid bounds, filter the intersections
99+ if (bounded) {
100+ linestr filtered;
101+ filtered.reserve (intersections.size ());
102+ for (std::size_t idx = 0 ; idx < intersections.size (); ++idx) {
103+ const auto &coordinate = intersections[idx];
104+ bool endpoint = (idx == 0 ) || (idx == intersections.size () - 1 );
105+ // keep the endpoints as original coordinates in the linestring
106+ // and keep any split intersections from within the grid bounds
107+ if (endpoint || pointInBounds (coordinate, raster)) {
108+ filtered.push_back (coordinate);
109+ }
110+ }
111+ intersections = std::move (filtered);
112+ }
43113 std::vector<linestr> splits = split_linestr (linestr_piece, intersections);
44114 allsplits.insert (allsplits.end (), splits.begin (), splits.end ());
45115 if (line.end == intersections.back ()) {
46116 linestr_piece = {};
47117 } else {
48118 linestr_piece = {intersections.back ()};
49119 }
50- } else {
51- linestr_piece.push_back (coords.at (i));
52120 }
53121 }
54122
@@ -57,9 +125,16 @@ findIntersectionsLineString(geometry::LineString linestring,
57125 allsplits.push_back (linestr_piece);
58126 }
59127
60- return ( allsplits) ;
128+ return allsplits;
61129}
62130
131+ bool pointInBounds (const geometry::Coord &pt, const grid::Grid &raster) {
132+ auto ll = raster.grid_to_world * geometry::Coord (0.0 , 0.0 );
133+ auto ur = raster.grid_to_world * geometry::Coord (raster.ncols , raster.nrows );
134+
135+ return pt.x >= ll.x && pt.x <= ur.x && pt.y >= ll.y && pt.y <= ur.y ;
136+ };
137+
63138bool isOnGridLine (geometry::Coord point, Direction direction, double level,
64139 double cellSize) {
65140 switch (direction) {
@@ -81,7 +156,7 @@ bool isOnGridLine(geometry::Coord point, Direction direction, double level,
81156// >>-----x----o-----o----- (don't include x)
82157// /.\ |..../
83158//
84- // TODO figure out what to do when some portion of the boundary is already
159+ // figure out what to do when some portion of the boundary is already
85160// along the grid line. This is a legitimate case for odd number of crossings:
86161//
87162// |......|
0 commit comments