Line data Source code
1 : ///////////////////////////////////////////////////////////////////////////////
2 : //
3 : // Project: C++ Test Suite for GDAL/OGR
4 : // Purpose: Test viewshed algorithm
5 : // Author: Andrew Bell
6 : //
7 : ///////////////////////////////////////////////////////////////////////////////
8 : /*
9 : * SPDX-License-Identifier: MIT
10 : ****************************************************************************/
11 :
12 : #include <algorithm>
13 : #include <array>
14 : #include <utility>
15 :
16 : #include <iomanip>
17 :
18 : #include "gdal_unit_test.h"
19 :
20 : #include "gtest_include.h"
21 :
22 : #include "viewshed/viewshed.h"
23 :
24 : namespace gdal
25 : {
26 : namespace viewshed
27 : {
28 :
29 : namespace
30 : {
31 : using Coord = std::pair<int, int>;
32 : using DatasetPtr = std::unique_ptr<GDALDataset>;
33 : using Transform = std::array<double, 6>;
34 : Transform identity{0, 1, 0, 0, 0, 1};
35 :
36 22 : Options stdOptions(int x, int y)
37 : {
38 22 : Options opts;
39 22 : opts.observer.x = x;
40 22 : opts.observer.y = y;
41 22 : opts.outputFilename = "none";
42 22 : opts.outputFormat = "mem";
43 22 : opts.curveCoeff = 0;
44 :
45 22 : return opts;
46 : }
47 :
48 5 : Options stdOptions(const Coord &observer)
49 : {
50 5 : return stdOptions(observer.first, observer.second);
51 : }
52 :
53 27 : DatasetPtr runViewshed(const int8_t *in, int xlen, int ylen,
54 : const Options &opts)
55 : {
56 54 : Viewshed v(opts);
57 :
58 27 : GDALDriver *driver = (GDALDriver *)GDALGetDriverByName("MEM");
59 27 : GDALDataset *dataset = driver->Create("", xlen, ylen, 1, GDT_Int8, nullptr);
60 27 : EXPECT_TRUE(dataset);
61 27 : dataset->SetGeoTransform(identity.data());
62 27 : GDALRasterBand *band = dataset->GetRasterBand(1);
63 27 : EXPECT_TRUE(band);
64 27 : CPLErr err = band->RasterIO(GF_Write, 0, 0, xlen, ylen, (int8_t *)in, xlen,
65 27 : ylen, GDT_Int8, 0, 0, nullptr);
66 27 : EXPECT_EQ(err, CE_None);
67 :
68 27 : EXPECT_TRUE(v.run(band));
69 54 : return v.output();
70 : }
71 :
72 : } // namespace
73 :
74 4 : TEST(Viewshed, min_max_mask)
75 : {
76 1 : const int xlen = 15;
77 1 : const int ylen = 15;
78 : std::array<int8_t, xlen * ylen> in;
79 1 : in.fill(0);
80 :
81 2 : SCOPED_TRACE("min_max_mask");
82 2 : Options opts(stdOptions(7, 7));
83 1 : opts.minDistance = 2;
84 1 : opts.maxDistance = 6;
85 :
86 2 : DatasetPtr output = runViewshed(in.data(), xlen, ylen, opts);
87 :
88 : std::array<int8_t, xlen * ylen> out;
89 1 : GDALRasterBand *band = output->GetRasterBand(1);
90 :
91 1 : int xOutLen = band->GetXSize();
92 1 : int yOutLen = band->GetYSize();
93 1 : EXPECT_EQ(xOutLen, 13);
94 1 : EXPECT_EQ(yOutLen, 13);
95 :
96 1 : CPLErr err = band->RasterIO(GF_Read, 0, 0, xOutLen, yOutLen, out.data(),
97 1 : xOutLen, yOutLen, GDT_Int8, 0, 0, nullptr);
98 1 : EXPECT_EQ(err, CE_None);
99 :
100 : // clang-format off
101 1 : std::array<int8_t, 13 * 13> expected{
102 : 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
103 : 0, 0, 0, 0, 0, 0, 127, 0, 0, 0, 0, 0, 0,
104 : 0, 0, 0, 127, 127, 127, 127, 127, 127, 127, 0, 0, 0,
105 : 0, 0, 127, 127, 127, 127, 127, 127, 127, 127, 127, 0, 0,
106 : 0, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 0,
107 : 0, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 0,
108 : 0, 127, 127, 127, 127, 0, 0, 0, 127, 127, 127, 127, 0,
109 : 127, 127, 127, 127, 127, 0, 0, 0, 127, 127, 127, 127, 127,
110 : 0, 127, 127, 127, 127, 0, 0, 0, 127, 127, 127, 127, 0,
111 : 0, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 0,
112 : 0, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 0,
113 : 0, 0, 127, 127, 127, 127, 127, 127, 127, 127, 127, 0, 0,
114 : 0, 0, 0, 127, 127, 127, 127, 127, 127, 127, 0, 0, 0
115 : };
116 : // clang-format on
117 :
118 1 : int8_t *o = out.data();
119 1 : int8_t *e = expected.data();
120 170 : for (size_t i = 0; i < 13 * 13; ++i)
121 169 : EXPECT_EQ(*e++, *o++);
122 :
123 : /**
124 : int8_t *p = out.data();
125 : for (int y = 0; y < yOutLen; ++y)
126 : {
127 : for (int x = 0; x < xOutLen; ++x)
128 : {
129 : char c;
130 : if (*p == 0)
131 : c = '*';
132 : else if (*p == 127)
133 : c = '.';
134 : else
135 : c = '?';
136 : std::cerr << c;
137 : p++;
138 : }
139 : std::cerr << "\n";
140 : }
141 : std::cerr << "\n";
142 : **/
143 1 : }
144 :
145 4 : TEST(Viewshed, angle)
146 : {
147 1 : const int xlen = 17;
148 1 : const int ylen = 17;
149 : std::array<int8_t, xlen * ylen> in;
150 1 : in.fill(0);
151 :
152 2 : SCOPED_TRACE("min_max_mask");
153 2 : Options opts(stdOptions(8, 8));
154 1 : opts.startAngle = 0;
155 1 : opts.endAngle = 30;
156 :
157 2 : DatasetPtr output = runViewshed(in.data(), xlen, ylen, opts);
158 :
159 : std::array<int8_t, xlen * ylen> out;
160 1 : GDALRasterBand *band = output->GetRasterBand(1);
161 :
162 1 : int xOutLen = band->GetXSize();
163 1 : int yOutLen = band->GetYSize();
164 1 : EXPECT_EQ(xOutLen, 6);
165 1 : EXPECT_EQ(yOutLen, 9);
166 1 : CPLErr err = band->RasterIO(GF_Read, 0, 0, xOutLen, yOutLen, out.data(),
167 1 : xOutLen, yOutLen, GDT_Int8, 0, 0, nullptr);
168 1 : EXPECT_EQ(err, CE_None);
169 :
170 : // clang-format off
171 1 : std::array<int8_t, 6 * 9> expected{
172 : 127, 127, 127, 127, 127, 127,
173 : 127, 127, 127, 127, 127, 0,
174 : 127, 127, 127, 127, 0, 0,
175 : 127, 127, 127, 127, 0, 0,
176 : 127, 127, 127, 0, 0, 0,
177 : 127, 127, 127, 0, 0, 0,
178 : 127, 127, 0, 0, 0, 0,
179 : 127, 127, 0, 0, 0, 0,
180 : 127, 0, 0, 0, 0, 0
181 : };
182 : // clang-format on
183 :
184 1 : int8_t *o = out.data();
185 1 : int8_t *e = expected.data();
186 55 : for (size_t i = 0; i < 6 * 9; ++i)
187 54 : EXPECT_EQ(*e++, *o++);
188 :
189 : /**
190 : int8_t *p = out.data();
191 : for (int y = 0; y < yOutLen; ++y)
192 : {
193 : for (int x = 0; x < xOutLen; ++x)
194 : {
195 : char c;
196 : if (*p == 0)
197 : c = '*';
198 : else if (*p == 127)
199 : c = '.';
200 : else
201 : c = '?';
202 : std::cerr << c;
203 : p++;
204 : }
205 : std::cerr << "\n";
206 : }
207 : std::cerr << "\n";
208 : **/
209 1 : }
210 :
211 4 : TEST(Viewshed, angle2)
212 : {
213 1 : const int xlen = 11;
214 1 : const int ylen = 11;
215 : std::array<int8_t, xlen * ylen> in;
216 1 : in.fill(0);
217 :
218 2 : SCOPED_TRACE("min_max_mask");
219 2 : Options opts(stdOptions(5, 5));
220 1 : opts.startAngle = 0;
221 1 : opts.endAngle = 300;
222 :
223 2 : DatasetPtr output = runViewshed(in.data(), xlen, ylen, opts);
224 :
225 : std::array<int8_t, xlen * ylen> out;
226 1 : GDALRasterBand *band = output->GetRasterBand(1);
227 :
228 1 : int xOutLen = band->GetXSize();
229 1 : int yOutLen = band->GetYSize();
230 1 : EXPECT_EQ(xOutLen, 11);
231 1 : EXPECT_EQ(yOutLen, 11);
232 1 : CPLErr err = band->RasterIO(GF_Read, 0, 0, xOutLen, yOutLen, out.data(),
233 1 : xOutLen, yOutLen, GDT_Int8, 0, 0, nullptr);
234 1 : EXPECT_EQ(err, CE_None);
235 :
236 : // clang-format off
237 1 : std::array<int8_t, 11 * 11> expected{
238 : 0, 0, 0, 0, 0, 127, 127, 127, 127, 127, 127, 0, 0, 0,
239 : 0, 0, 127, 127, 127, 127, 127, 127, 127, 0, 0, 0, 0, 127,
240 : 127, 127, 127, 127, 127, 127, 127, 127, 0, 0, 127, 127, 127, 127,
241 : 127, 127, 127, 127, 127, 127, 0, 127, 127, 127, 127, 127, 127, 127,
242 : 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
243 : 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
244 : 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
245 : 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
246 : 127, 127, 127, 127, 127, 127, 127, 127, 127,
247 : };
248 : // clang-format on
249 :
250 1 : int8_t *o = out.data();
251 1 : int8_t *e = expected.data();
252 122 : for (size_t i = 0; i < 11 * 11; ++i)
253 121 : EXPECT_EQ(*e++, *o++);
254 1 : }
255 :
256 4 : TEST(Viewshed, high_mask)
257 : {
258 1 : const int xlen = 15;
259 1 : const int ylen = 15;
260 : std::array<int8_t, xlen * ylen> in;
261 1 : in.fill(0);
262 1 : in[15 * 7 + 5] = 1;
263 1 : in[15 * 7 + 6] = 3;
264 1 : in[15 * 7 + 7] = 5;
265 1 : in[15 * 7 + 8] = 7;
266 1 : in[15 * 7 + 9] = 7;
267 1 : in[15 * 7 + 10] = 7;
268 1 : in[15 * 7 + 11] = 7;
269 1 : in[15 * 7 + 12] = 12;
270 1 : in[15 * 7 + 13] = 6;
271 1 : in[15 * 7 + 14] = 15;
272 :
273 2 : SCOPED_TRACE("high_mask");
274 2 : Options opts(stdOptions(3, 7));
275 :
276 1 : opts.highPitch = 45;
277 1 : opts.outOfRangeVal = 2;
278 1 : opts.visibleVal = 1;
279 1 : opts.invisibleVal = 0;
280 :
281 2 : DatasetPtr output = runViewshed(in.data(), xlen, ylen, opts);
282 :
283 : std::array<int8_t, xlen * ylen> out;
284 1 : GDALRasterBand *band = output->GetRasterBand(1);
285 :
286 1 : int xOutLen = band->GetXSize();
287 1 : int yOutLen = band->GetYSize();
288 1 : EXPECT_EQ(xOutLen, 15);
289 1 : EXPECT_EQ(yOutLen, 15);
290 1 : CPLErr err = band->RasterIO(GF_Read, 0, 0, xOutLen, yOutLen, out.data(),
291 1 : xOutLen, yOutLen, GDT_Int8, 0, 0, nullptr);
292 1 : EXPECT_EQ(err, CE_None);
293 :
294 : // clang-format off
295 1 : std::array<int8_t, 15 * 15> expected{
296 : 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0,
297 : 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0,
298 : 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
299 : 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
300 : 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
301 : 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
302 : 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
303 : 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 0, 2, 0, 2,
304 : 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
305 : 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
306 : 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
307 : 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
308 : 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
309 : 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0,
310 : 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0
311 : };
312 : // clang-format on
313 :
314 1 : int8_t *o = out.data();
315 1 : int8_t *e = expected.data();
316 16 : for (int y = 0; y < 15; ++y)
317 : {
318 240 : for (int x = 0; x < 15; ++x)
319 : {
320 225 : EXPECT_EQ(*e, *o);
321 225 : e++;
322 225 : o++;
323 : }
324 : }
325 1 : }
326 :
327 4 : TEST(Viewshed, low_mask)
328 : {
329 1 : const int xlen = 5;
330 1 : const int ylen = 5;
331 : std::array<int8_t, xlen * ylen> in;
332 1 : in.fill(0);
333 1 : in[12] = 5;
334 :
335 2 : SCOPED_TRACE("low_mask");
336 2 : Options opts(stdOptions(2, 2));
337 :
338 1 : opts.lowPitch = -45;
339 1 : opts.outputMode = OutputMode::DEM;
340 :
341 2 : DatasetPtr output = runViewshed(in.data(), xlen, ylen, opts);
342 :
343 : std::array<double, xlen * ylen> out;
344 1 : GDALRasterBand *band = output->GetRasterBand(1);
345 :
346 1 : int xOutLen = band->GetXSize();
347 1 : int yOutLen = band->GetYSize();
348 1 : CPLErr err = band->RasterIO(GF_Read, 0, 0, xOutLen, yOutLen, out.data(),
349 1 : xOutLen, yOutLen, GDT_Float64, 0, 0, nullptr);
350 1 : EXPECT_EQ(err, CE_None);
351 :
352 1 : std::array<double, 5 * 5> expected{2.17157, 2.76393, 3, 2.76393, 2.17157,
353 : 2.76393, 3.58579, 4, 3.58579, 2.76393,
354 : 3, 4, 5, 4, 3,
355 : 2.76393, 3.58579, 4, 3.58579, 2.76393,
356 : 2.17157, 2.76393, 3, 2.76393, 2.17157};
357 :
358 1 : const double *o = out.data();
359 1 : const double *e = expected.data();
360 26 : for (size_t i = 0; i < expected.size(); ++i)
361 : {
362 25 : EXPECT_NEAR(*o, *e, .00001);
363 25 : o++;
364 25 : e++;
365 : }
366 1 : }
367 :
368 4 : TEST(Viewshed, all_visible)
369 : {
370 : // clang-format off
371 1 : const int xlen = 3;
372 1 : const int ylen = 3;
373 1 : std::array<int8_t, xlen * ylen> in
374 : {
375 : 1, 2, 3,
376 : 4, 5, 6,
377 : 3, 2, 1
378 : };
379 : // clang-format on
380 :
381 2 : SCOPED_TRACE("all_visible");
382 2 : DatasetPtr output = runViewshed(in.data(), xlen, ylen, stdOptions(1, 1));
383 :
384 : std::array<uint8_t, xlen * ylen> out;
385 1 : GDALRasterBand *band = output->GetRasterBand(1);
386 1 : CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(), xlen,
387 1 : ylen, GDT_Int8, 0, 0, nullptr);
388 1 : EXPECT_EQ(err, CE_None);
389 :
390 10 : for (uint8_t o : out)
391 9 : EXPECT_EQ(o, 127);
392 1 : }
393 :
394 4 : TEST(Viewshed, simple_height)
395 : {
396 : // clang-format off
397 1 : const int xlen = 5;
398 1 : const int ylen = 5;
399 1 : std::array<int8_t, xlen * ylen> in
400 : {
401 : -1, 0, 1, 0, -1,
402 : -1, 2, 0, 4, -1,
403 : -1, 1, 0, -1, -1,
404 : 0, 3, 0, 2, 0,
405 : -1, 0, 0, 3, -1
406 : };
407 :
408 1 : std::array<double, xlen * ylen> observable
409 : {
410 : 4, 2, 1, 4, 8,
411 : 3, 2, 0, 4, 3,
412 : 2, 1, 0, -1, -1,
413 : 4, 3, 0, 2, 1,
414 : 6, 3, 0, 3, 4
415 : };
416 : // clang-format on
417 :
418 : {
419 2 : SCOPED_TRACE("simple_height:normal");
420 :
421 : DatasetPtr output =
422 2 : runViewshed(in.data(), xlen, ylen, stdOptions(2, 2));
423 :
424 : std::array<int8_t, xlen * ylen> out;
425 1 : GDALRasterBand *band = output->GetRasterBand(1);
426 1 : CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(), xlen,
427 1 : ylen, GDT_Int8, 0, 0, nullptr);
428 1 : EXPECT_EQ(err, CE_None);
429 :
430 : // We expect the cell to be observable when the input is higher than the observable
431 : // height.
432 : std::array<int8_t, xlen * ylen> expected;
433 26 : for (size_t i = 0; i < in.size(); ++i)
434 25 : expected[i] = (in[i] >= observable[i] ? 127 : 0);
435 :
436 1 : EXPECT_EQ(expected, out);
437 : }
438 :
439 : {
440 : std::array<double, xlen * ylen> dem;
441 2 : SCOPED_TRACE("simple_height:dem");
442 2 : Options opts = stdOptions(2, 2);
443 1 : opts.outputMode = OutputMode::DEM;
444 :
445 2 : DatasetPtr output = runViewshed(in.data(), xlen, ylen, opts);
446 :
447 1 : GDALRasterBand *band = output->GetRasterBand(1);
448 1 : CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, dem.data(), xlen,
449 1 : ylen, GDT_Float64, 0, 0, nullptr);
450 1 : EXPECT_EQ(err, CE_None);
451 :
452 1 : std::array<double, xlen *ylen> expected = observable;
453 : // Double equality is fine here as all the values are small integers.
454 1 : EXPECT_EQ(dem, expected);
455 : }
456 :
457 : {
458 : std::array<double, xlen * ylen> ground;
459 2 : SCOPED_TRACE("simple_height:ground");
460 2 : Options opts = stdOptions(2, 2);
461 1 : opts.outputMode = OutputMode::Ground;
462 2 : DatasetPtr output = runViewshed(in.data(), xlen, ylen, opts);
463 :
464 1 : GDALRasterBand *band = output->GetRasterBand(1);
465 1 : CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, ground.data(),
466 1 : xlen, ylen, GDT_Float64, 0, 0, nullptr);
467 1 : EXPECT_EQ(err, CE_None);
468 :
469 : std::array<double, xlen * ylen> expected;
470 26 : for (size_t i = 0; i < expected.size(); ++i)
471 25 : expected[i] = std::max(0.0, observable[i] - in[i]);
472 :
473 : // Double equality is fine here as all the values are small integers.
474 1 : EXPECT_EQ(expected, ground);
475 : }
476 1 : }
477 :
478 : // Addresses cases in #9501
479 4 : TEST(Viewshed, dem_vs_ground)
480 : {
481 : // Run gdal_viewshed on the input 8 x 1 array in both ground and dem mode and
482 : // verify the results are what are expected.
483 5 : auto run = [](const std::array<int8_t, 8> &in, Coord observer,
484 : const std::array<double, 8> &ground,
485 : const std::array<double, 8> &dem)
486 : {
487 5 : const int xlen = 8;
488 5 : const int ylen = 1;
489 :
490 : std::array<double, xlen * ylen> out;
491 10 : Options opts = stdOptions(observer);
492 5 : opts.outputMode = OutputMode::Ground;
493 :
494 : // Verify ground mode.
495 10 : DatasetPtr ds = runViewshed(in.data(), xlen, ylen, opts);
496 5 : GDALRasterBand *band = ds->GetRasterBand(1);
497 5 : CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(), xlen,
498 5 : ylen, GDT_Float64, 0, 0, nullptr);
499 5 : EXPECT_EQ(err, CE_None);
500 45 : for (size_t i = 0; i < ground.size(); ++i)
501 40 : EXPECT_DOUBLE_EQ(out[i], ground[i]);
502 :
503 : // Verify DEM mode.
504 5 : opts.outputMode = OutputMode::DEM;
505 5 : ds = runViewshed(in.data(), xlen, ylen, opts);
506 5 : band = ds->GetRasterBand(1);
507 5 : err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(), xlen, ylen,
508 : GDT_Float64, 0, 0, nullptr);
509 5 : EXPECT_EQ(err, CE_None);
510 45 : for (size_t i = 0; i < dem.size(); ++i)
511 40 : EXPECT_DOUBLE_EQ(out[i], dem[i]);
512 5 : };
513 :
514 : // Input / Observer / Minimum expected above ground / Minimum expected above zero (DEM)
515 1 : run({0, 0, 0, 1, 0, 0, 0, 0}, {2, 0}, {0, 0, 0, 0, 2, 3, 4, 5},
516 : {0, 0, 0, 1, 2, 3, 4, 5});
517 1 : run({1, 1, 0, 1, 0, 1, 2, 2}, {3, 0}, {0, 0, 0, 0, 0, 0, 0, 1 / 3.0},
518 : {1, 1, 0, 1, 0, 1, 2, 7 / 3.0});
519 1 : run({0, 0, 0, 1, 1, 0, 0, 0}, {0, 0},
520 : {0, 0, 0, 0, 1 / 3.0, 5 / 3.0, 6 / 3.0, 7 / 3.0},
521 : {0, 0, 0, 1, 4 / 3.0, 5 / 3.0, 6 / 3.0, 7 / 3.0});
522 1 : run({0, 0, 1, 2, 3, 4, 5, 6}, {0, 0}, {0, 0, 0, 0, 0, 0, 0, 0},
523 : {0, 0, 1, 2, 3, 4, 5, 6});
524 1 : run({0, 0, 1, 1, 3, 4, 5, 4}, {0, 0}, {0, 0, 0, .5, 0, 0, 0, 11 / 6.0},
525 : {0, 0, 1, 1.5, 3, 4, 5, 35 / 6.0});
526 1 : }
527 :
528 : // Test an observer to the right of the raster.
529 4 : TEST(Viewshed, oor_right)
530 : {
531 : // clang-format off
532 1 : const int xlen = 5;
533 1 : const int ylen = 3;
534 1 : std::array<int8_t, xlen * ylen> in
535 : {
536 : 1, 2, 0, 4, 1,
537 : 0, 0, 2, 1, 0,
538 : 1, 0, 0, 3, 3
539 : };
540 : // clang-format on
541 :
542 : {
543 2 : Options opts = stdOptions(6, 1);
544 1 : opts.outputMode = OutputMode::DEM;
545 2 : DatasetPtr ds = runViewshed(in.data(), xlen, ylen, opts);
546 1 : GDALRasterBand *band = ds->GetRasterBand(1);
547 : std::array<double, xlen * ylen> out;
548 1 : CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(), xlen,
549 1 : ylen, GDT_Float64, 0, 0, nullptr);
550 1 : EXPECT_EQ(err, CE_None);
551 :
552 : // clang-format off
553 1 : std::array<double, xlen * ylen> expected
554 : {
555 : 16 / 3.0, 29 / 6.0, 13 / 3.0, 4, 1,
556 : 3, 2.5, 2, 1, 0,
557 : 13 / 3.0, 23 / 6.0, 10 / 3.0, 3, 3
558 : };
559 : // clang-format on
560 :
561 16 : for (size_t i = 0; i < out.size(); ++i)
562 15 : EXPECT_DOUBLE_EQ(out[i], expected[i]);
563 : }
564 :
565 : {
566 2 : Options opts = stdOptions(6, 2);
567 1 : opts.outputMode = OutputMode::DEM;
568 2 : DatasetPtr ds = runViewshed(in.data(), xlen, ylen, opts);
569 1 : GDALRasterBand *band = ds->GetRasterBand(1);
570 : std::array<double, xlen * ylen> out;
571 1 : CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(), xlen,
572 1 : ylen, GDT_Float64, 0, 0, nullptr);
573 1 : EXPECT_EQ(err, CE_None);
574 :
575 : // clang-format off
576 1 : std::array<double, xlen * ylen> expected
577 : {
578 : 26 / 5.0, 17 / 4.0, 11 / 3.0, 4, 1,
579 : 6, 4.5, 3, 1.5, 0,
580 : 9, 7.5, 6, 4.5, 3
581 : };
582 : // clang-format on
583 :
584 16 : for (size_t i = 0; i < out.size(); ++i)
585 15 : EXPECT_DOUBLE_EQ(out[i], expected[i]);
586 : }
587 1 : }
588 :
589 : // Test an observer to the left of the raster.
590 4 : TEST(Viewshed, oor_left)
591 : {
592 : // clang-format off
593 1 : const int xlen = 5;
594 1 : const int ylen = 3;
595 1 : std::array<int8_t, xlen * ylen> in
596 : {
597 : 1, 2, 0, 4, 1,
598 : 0, 0, 2, 1, 0,
599 : 1, 0, 0, 3, 3
600 : };
601 : // clang-format on
602 :
603 : {
604 2 : Options opts = stdOptions(-2, 1);
605 1 : opts.outputMode = OutputMode::DEM;
606 2 : DatasetPtr ds = runViewshed(in.data(), xlen, ylen, opts);
607 1 : GDALRasterBand *band = ds->GetRasterBand(1);
608 : std::array<double, xlen * ylen> out;
609 1 : CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(), xlen,
610 1 : ylen, GDT_Float64, 0, 0, nullptr);
611 1 : EXPECT_EQ(err, CE_None);
612 :
613 : // clang-format off
614 1 : std::array<double, xlen * ylen> expected
615 : {
616 : 1, 2, 2, 4, 4.5,
617 : 0, 0, 2, 2.5, 3,
618 : 1, 1, 1, 3, 3.5
619 : };
620 : // clang-format on
621 :
622 16 : for (size_t i = 0; i < out.size(); ++i)
623 15 : EXPECT_DOUBLE_EQ(out[i], expected[i]);
624 : }
625 :
626 : {
627 2 : Options opts = stdOptions(-2, 2);
628 1 : opts.outputMode = OutputMode::DEM;
629 2 : DatasetPtr ds = runViewshed(in.data(), xlen, ylen, opts);
630 1 : GDALRasterBand *band = ds->GetRasterBand(1);
631 : std::array<double, xlen * ylen> out;
632 1 : CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(), xlen,
633 1 : ylen, GDT_Float64, 0, 0, nullptr);
634 1 : EXPECT_EQ(err, CE_None);
635 :
636 : // clang-format off
637 1 : std::array<double, xlen * ylen> expected
638 : {
639 : 1, 2, 5 / 3.0, 4, 4.2,
640 : 0, .5, 2, 2.5, 3.1,
641 : 1, 1.5, 2, 3, 3.6
642 : };
643 : // clang-format on
644 :
645 16 : for (size_t i = 0; i < out.size(); ++i)
646 15 : EXPECT_DOUBLE_EQ(out[i], expected[i]);
647 : }
648 1 : }
649 :
650 : // Test an observer above the raster
651 4 : TEST(Viewshed, oor_above)
652 : {
653 : // clang-format off
654 1 : const int xlen = 5;
655 1 : const int ylen = 3;
656 1 : std::array<int8_t, xlen * ylen> in
657 : {
658 : 1, 2, 0, 4, 1,
659 : 0, 0, 2, 1, 0,
660 : 1, 0, 0, 3, 3
661 : };
662 : // clang-format on
663 :
664 : {
665 2 : Options opts = stdOptions(2, -2);
666 1 : opts.outputMode = OutputMode::DEM;
667 2 : DatasetPtr ds = runViewshed(in.data(), xlen, ylen, opts);
668 1 : GDALRasterBand *band = ds->GetRasterBand(1);
669 : std::array<double, xlen * ylen> out;
670 1 : CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(), xlen,
671 1 : ylen, GDT_Float64, 0, 0, nullptr);
672 :
673 1 : EXPECT_EQ(err, CE_None);
674 :
675 : // clang-format off
676 1 : std::array<double, xlen * ylen> expected
677 : {
678 : 1, 2, 0, 4, 1,
679 : 2.5, 2, 2, 4, 4.5,
680 : 3, 8 / 3.0, 8 / 3.0, 14 / 3.0, 17 / 3.0
681 : };
682 : // clang-format on
683 :
684 16 : for (size_t i = 0; i < out.size(); ++i)
685 15 : EXPECT_DOUBLE_EQ(out[i], expected[i]);
686 : }
687 :
688 : {
689 2 : Options opts = stdOptions(-2, -2);
690 1 : opts.outputMode = OutputMode::DEM;
691 2 : DatasetPtr ds = runViewshed(in.data(), xlen, ylen, opts);
692 1 : GDALRasterBand *band = ds->GetRasterBand(1);
693 : std::array<double, xlen * ylen> out;
694 1 : CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(), xlen,
695 1 : ylen, GDT_Float64, 0, 0, nullptr);
696 1 : EXPECT_EQ(err, CE_None);
697 :
698 : // clang-format off
699 1 : std::array<double, xlen * ylen> expected
700 : {
701 : 1, 2, 0, 4, 1,
702 : 0, 1.5, 2.5, 1.25, 3.15,
703 : 1, 0.5, 2, 3, 3
704 : };
705 : // clang-format on
706 :
707 16 : for (size_t i = 0; i < out.size(); ++i)
708 15 : EXPECT_DOUBLE_EQ(out[i], expected[i]);
709 : }
710 1 : }
711 :
712 : // Test an observer below the raster
713 4 : TEST(Viewshed, oor_below)
714 : {
715 : // clang-format off
716 1 : const int xlen = 5;
717 1 : const int ylen = 3;
718 1 : std::array<int8_t, xlen * ylen> in
719 : {
720 : 1, 2, 0, 4, 1,
721 : 0, 0, 2, 1, 0,
722 : 1, 0, 0, 3, 3
723 : };
724 : // clang-format on
725 :
726 : {
727 2 : Options opts = stdOptions(2, 4);
728 1 : opts.outputMode = OutputMode::DEM;
729 2 : DatasetPtr ds = runViewshed(in.data(), xlen, ylen, opts);
730 1 : GDALRasterBand *band = ds->GetRasterBand(1);
731 : std::array<double, xlen * ylen> out;
732 1 : CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(), xlen,
733 1 : ylen, GDT_Float64, 0, 0, nullptr);
734 :
735 1 : EXPECT_EQ(err, CE_None);
736 :
737 : // clang-format off
738 1 : std::array<double, xlen * ylen> expected
739 : {
740 : 1, 2, 8 / 3.0, 4, 5,
741 : 0.5, 0, 2, 3, 4.5,
742 : 1, 0, 0, 3, 3
743 : };
744 : // clang-format on
745 :
746 16 : for (size_t i = 0; i < out.size(); ++i)
747 15 : EXPECT_DOUBLE_EQ(out[i], expected[i]);
748 : }
749 :
750 : {
751 2 : Options opts = stdOptions(6, 4);
752 1 : opts.outputMode = OutputMode::DEM;
753 2 : DatasetPtr ds = runViewshed(in.data(), xlen, ylen, opts);
754 1 : GDALRasterBand *band = ds->GetRasterBand(1);
755 : std::array<double, xlen * ylen> out;
756 1 : CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(), xlen,
757 1 : ylen, GDT_Float64, 0, 0, nullptr);
758 1 : EXPECT_EQ(err, CE_None);
759 :
760 : // clang-format off
761 1 : std::array<double, xlen * ylen> expected
762 : {
763 : 4.2, 6, 6, 4, 1,
764 : 1.35, 2.25, 4.5, 4.5, 0,
765 : 1, 0, 0, 3, 3
766 : };
767 : // clang-format on
768 :
769 16 : for (size_t i = 0; i < out.size(); ++i)
770 15 : EXPECT_DOUBLE_EQ(out[i], expected[i]);
771 : }
772 1 : }
773 :
774 : } // namespace viewshed
775 : } // namespace gdal
|