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