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[15 * 7 + 5] = 1;
250 1 : in[15 * 7 + 6] = 3;
251 1 : in[15 * 7 + 7] = 5;
252 1 : in[15 * 7 + 8] = 7;
253 1 : in[15 * 7 + 9] = 7;
254 1 : in[15 * 7 + 10] = 7;
255 1 : in[15 * 7 + 11] = 7;
256 1 : in[15 * 7 + 12] = 12;
257 1 : in[15 * 7 + 13] = 6;
258 1 : in[15 * 7 + 14] = 15;
259 :
260 2 : SCOPED_TRACE("high_mask");
261 2 : Options opts(stdOptions(3, 7));
262 :
263 1 : opts.highPitch = 45;
264 1 : opts.outOfRangeVal = 2;
265 1 : opts.visibleVal = 1;
266 1 : opts.invisibleVal = 0;
267 :
268 2 : DatasetPtr output = runViewshed(in.data(), xlen, ylen, opts);
269 :
270 : std::array<int8_t, xlen * ylen> out;
271 1 : GDALRasterBand *band = output->GetRasterBand(1);
272 :
273 1 : int xOutLen = band->GetXSize();
274 1 : int yOutLen = band->GetYSize();
275 1 : EXPECT_EQ(xOutLen, 15);
276 1 : EXPECT_EQ(yOutLen, 15);
277 1 : CPLErr err = band->RasterIO(GF_Read, 0, 0, xOutLen, yOutLen, out.data(),
278 1 : xOutLen, yOutLen, GDT_Int8, 0, 0, nullptr);
279 1 : EXPECT_EQ(err, CE_None);
280 :
281 : // clang-format off
282 1 : std::array<int8_t, 15 * 15> expected{
283 : 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0,
284 : 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0,
285 : 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
286 : 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
287 : 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
288 : 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
289 : 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
290 : 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 0, 2, 0, 2,
291 : 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
292 : 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
293 : 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
294 : 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
295 : 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
296 : 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0,
297 : 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0
298 : };
299 : // clang-format on
300 :
301 1 : int8_t *o = out.data();
302 1 : int8_t *e = expected.data();
303 16 : for (int y = 0; y < 15; ++y)
304 : {
305 240 : for (int x = 0; x < 15; ++x)
306 : {
307 225 : EXPECT_EQ(*e, *o);
308 225 : e++;
309 225 : o++;
310 : }
311 : }
312 1 : }
313 :
314 4 : TEST(Viewshed, low_mask)
315 : {
316 1 : const int xlen = 5;
317 1 : const int ylen = 5;
318 : std::array<int8_t, xlen * ylen> in;
319 1 : in.fill(0);
320 1 : in[12] = 5;
321 :
322 2 : SCOPED_TRACE("low_mask");
323 2 : Options opts(stdOptions(2, 2));
324 :
325 1 : opts.lowPitch = -45;
326 1 : opts.outputMode = OutputMode::DEM;
327 :
328 2 : DatasetPtr output = runViewshed(in.data(), xlen, ylen, opts);
329 :
330 : std::array<double, xlen * ylen> out;
331 1 : GDALRasterBand *band = output->GetRasterBand(1);
332 :
333 1 : int xOutLen = band->GetXSize();
334 1 : int yOutLen = band->GetYSize();
335 1 : CPLErr err = band->RasterIO(GF_Read, 0, 0, xOutLen, yOutLen, out.data(),
336 1 : xOutLen, yOutLen, GDT_Float64, 0, 0, nullptr);
337 1 : EXPECT_EQ(err, CE_None);
338 :
339 1 : std::array<double, 5 * 5> expected{2.17157, 2.76393, 3, 2.76393, 2.17157,
340 : 2.76393, 3.58579, 4, 3.58579, 2.76393,
341 : 3, 4, 5, 4, 3,
342 : 2.76393, 3.58579, 4, 3.58579, 2.76393,
343 : 2.17157, 2.76393, 3, 2.76393, 2.17157};
344 :
345 1 : const double *o = out.data();
346 1 : const double *e = expected.data();
347 26 : for (size_t i = 0; i < expected.size(); ++i)
348 : {
349 25 : EXPECT_NEAR(*o, *e, .00001);
350 25 : o++;
351 25 : e++;
352 : }
353 1 : }
354 :
355 4 : TEST(Viewshed, all_visible)
356 : {
357 : // clang-format off
358 1 : const int xlen = 3;
359 1 : const int ylen = 3;
360 1 : std::array<int8_t, xlen * ylen> in
361 : {
362 : 1, 2, 3,
363 : 4, 5, 6,
364 : 3, 2, 1
365 : };
366 : // clang-format on
367 :
368 2 : SCOPED_TRACE("all_visible");
369 2 : DatasetPtr output = runViewshed(in.data(), xlen, ylen, stdOptions(1, 1));
370 :
371 : std::array<uint8_t, xlen * ylen> out;
372 1 : GDALRasterBand *band = output->GetRasterBand(1);
373 1 : CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(), xlen,
374 1 : ylen, GDT_Int8, 0, 0, nullptr);
375 1 : EXPECT_EQ(err, CE_None);
376 :
377 10 : for (uint8_t o : out)
378 9 : EXPECT_EQ(o, 127);
379 1 : }
380 :
381 4 : TEST(Viewshed, simple_height)
382 : {
383 : // clang-format off
384 1 : const int xlen = 5;
385 1 : const int ylen = 5;
386 1 : std::array<int8_t, xlen * ylen> in
387 : {
388 : -1, 0, 1, 0, -1,
389 : -1, 2, 0, 4, -1,
390 : -1, 1, 0, -1, -1,
391 : 0, 3, 0, 2, 0,
392 : -1, 0, 0, 3, -1
393 : };
394 :
395 1 : std::array<double, xlen * ylen> observable
396 : {
397 : 4, 2, 1, 4, 8,
398 : 3, 2, 0, 4, 3,
399 : 2, 1, 0, -1, -1,
400 : 4, 3, 0, 2, 1,
401 : 6, 3, 0, 3, 4
402 : };
403 : // clang-format on
404 :
405 : {
406 2 : SCOPED_TRACE("simple_height:normal");
407 :
408 : DatasetPtr output =
409 2 : runViewshed(in.data(), xlen, ylen, stdOptions(2, 2));
410 :
411 : std::array<int8_t, xlen * ylen> out;
412 1 : GDALRasterBand *band = output->GetRasterBand(1);
413 1 : CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(), xlen,
414 1 : ylen, GDT_Int8, 0, 0, nullptr);
415 1 : EXPECT_EQ(err, CE_None);
416 :
417 : // We expect the cell to be observable when the input is higher than the observable
418 : // height.
419 : std::array<int8_t, xlen * ylen> expected;
420 26 : for (size_t i = 0; i < in.size(); ++i)
421 25 : expected[i] = (in[i] >= observable[i] ? 127 : 0);
422 :
423 1 : EXPECT_EQ(expected, out);
424 : }
425 :
426 : {
427 : std::array<double, xlen * ylen> dem;
428 2 : SCOPED_TRACE("simple_height:dem");
429 2 : Options opts = stdOptions(2, 2);
430 1 : opts.outputMode = OutputMode::DEM;
431 :
432 2 : DatasetPtr output = runViewshed(in.data(), xlen, ylen, opts);
433 :
434 1 : GDALRasterBand *band = output->GetRasterBand(1);
435 1 : CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, dem.data(), xlen,
436 1 : ylen, GDT_Float64, 0, 0, nullptr);
437 1 : EXPECT_EQ(err, CE_None);
438 :
439 1 : std::array<double, xlen *ylen> expected = observable;
440 : // Double equality is fine here as all the values are small integers.
441 1 : EXPECT_EQ(dem, expected);
442 : }
443 :
444 : {
445 : std::array<double, xlen * ylen> ground;
446 2 : SCOPED_TRACE("simple_height:ground");
447 2 : Options opts = stdOptions(2, 2);
448 1 : opts.outputMode = OutputMode::Ground;
449 2 : DatasetPtr output = runViewshed(in.data(), xlen, ylen, opts);
450 :
451 1 : GDALRasterBand *band = output->GetRasterBand(1);
452 1 : CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, ground.data(),
453 1 : xlen, ylen, GDT_Float64, 0, 0, nullptr);
454 1 : EXPECT_EQ(err, CE_None);
455 :
456 : std::array<double, xlen * ylen> expected;
457 26 : for (size_t i = 0; i < expected.size(); ++i)
458 25 : expected[i] = std::max(0.0, observable[i] - in[i]);
459 :
460 : // Double equality is fine here as all the values are small integers.
461 1 : EXPECT_EQ(expected, ground);
462 : }
463 1 : }
464 :
465 : // Addresses cases in #9501
466 4 : TEST(Viewshed, dem_vs_ground)
467 : {
468 : // Run gdal_viewshed on the input 8 x 1 array in both ground and dem mode and
469 : // verify the results are what are expected.
470 5 : auto run = [](const std::array<int8_t, 8> &in, Coord observer,
471 : const std::array<double, 8> &ground,
472 : const std::array<double, 8> &dem)
473 : {
474 5 : const int xlen = 8;
475 5 : const int ylen = 1;
476 :
477 : std::array<double, xlen * ylen> out;
478 10 : Options opts = stdOptions(observer);
479 5 : opts.outputMode = OutputMode::Ground;
480 :
481 : // Verify ground mode.
482 10 : DatasetPtr ds = runViewshed(in.data(), xlen, ylen, opts);
483 5 : GDALRasterBand *band = ds->GetRasterBand(1);
484 5 : CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(), xlen,
485 5 : ylen, GDT_Float64, 0, 0, nullptr);
486 5 : EXPECT_EQ(err, CE_None);
487 45 : for (size_t i = 0; i < ground.size(); ++i)
488 40 : EXPECT_DOUBLE_EQ(out[i], ground[i]);
489 :
490 : // Verify DEM mode.
491 5 : opts.outputMode = OutputMode::DEM;
492 5 : ds = runViewshed(in.data(), xlen, ylen, opts);
493 5 : band = ds->GetRasterBand(1);
494 5 : err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(), xlen, ylen,
495 : GDT_Float64, 0, 0, nullptr);
496 5 : EXPECT_EQ(err, CE_None);
497 45 : for (size_t i = 0; i < dem.size(); ++i)
498 40 : EXPECT_DOUBLE_EQ(out[i], dem[i]);
499 5 : };
500 :
501 : // Input / Observer / Minimum expected above ground / Minimum expected above zero (DEM)
502 1 : run({0, 0, 0, 1, 0, 0, 0, 0}, {2, 0}, {0, 0, 0, 0, 2, 3, 4, 5},
503 : {0, 0, 0, 1, 2, 3, 4, 5});
504 1 : run({1, 1, 0, 1, 0, 1, 2, 2}, {3, 0}, {0, 0, 0, 0, 0, 0, 0, 1 / 3.0},
505 : {1, 1, 0, 1, 0, 1, 2, 7 / 3.0});
506 1 : run({0, 0, 0, 1, 1, 0, 0, 0}, {0, 0},
507 : {0, 0, 0, 0, 1 / 3.0, 5 / 3.0, 6 / 3.0, 7 / 3.0},
508 : {0, 0, 0, 1, 4 / 3.0, 5 / 3.0, 6 / 3.0, 7 / 3.0});
509 1 : run({0, 0, 1, 2, 3, 4, 5, 6}, {0, 0}, {0, 0, 0, 0, 0, 0, 0, 0},
510 : {0, 0, 1, 2, 3, 4, 5, 6});
511 1 : run({0, 0, 1, 1, 3, 4, 5, 4}, {0, 0}, {0, 0, 0, .5, 0, 0, 0, 11 / 6.0},
512 : {0, 0, 1, 1.5, 3, 4, 5, 35 / 6.0});
513 1 : }
514 :
515 : // Test an observer to the right of the raster.
516 4 : TEST(Viewshed, oor_right)
517 : {
518 : // clang-format off
519 1 : const int xlen = 5;
520 1 : const int ylen = 3;
521 1 : std::array<int8_t, xlen * ylen> in
522 : {
523 : 1, 2, 0, 4, 1,
524 : 0, 0, 2, 1, 0,
525 : 1, 0, 0, 3, 3
526 : };
527 : // clang-format on
528 :
529 : {
530 2 : Options opts = stdOptions(6, 1);
531 1 : opts.outputMode = OutputMode::DEM;
532 2 : DatasetPtr ds = runViewshed(in.data(), xlen, ylen, opts);
533 1 : GDALRasterBand *band = ds->GetRasterBand(1);
534 : std::array<double, xlen * ylen> out;
535 1 : CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(), xlen,
536 1 : ylen, GDT_Float64, 0, 0, nullptr);
537 1 : EXPECT_EQ(err, CE_None);
538 :
539 : // clang-format off
540 1 : std::array<double, xlen * ylen> expected
541 : {
542 : 16 / 3.0, 29 / 6.0, 13 / 3.0, 4, 1,
543 : 3, 2.5, 2, 1, 0,
544 : 13 / 3.0, 23 / 6.0, 10 / 3.0, 3, 3
545 : };
546 : // clang-format on
547 :
548 16 : for (size_t i = 0; i < out.size(); ++i)
549 15 : EXPECT_DOUBLE_EQ(out[i], expected[i]);
550 : }
551 :
552 : {
553 2 : Options opts = stdOptions(6, 2);
554 1 : opts.outputMode = OutputMode::DEM;
555 2 : DatasetPtr ds = runViewshed(in.data(), xlen, ylen, opts);
556 1 : GDALRasterBand *band = ds->GetRasterBand(1);
557 : std::array<double, xlen * ylen> out;
558 1 : CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(), xlen,
559 1 : ylen, GDT_Float64, 0, 0, nullptr);
560 1 : EXPECT_EQ(err, CE_None);
561 :
562 : // clang-format off
563 1 : std::array<double, xlen * ylen> expected
564 : {
565 : 26 / 5.0, 17 / 4.0, 11 / 3.0, 4, 1,
566 : 6, 4.5, 3, 1.5, 0,
567 : 9, 7.5, 6, 4.5, 3
568 : };
569 : // clang-format on
570 :
571 16 : for (size_t i = 0; i < out.size(); ++i)
572 15 : EXPECT_DOUBLE_EQ(out[i], expected[i]);
573 : }
574 1 : }
575 :
576 : // Test an observer to the left of the raster.
577 4 : TEST(Viewshed, oor_left)
578 : {
579 : // clang-format off
580 1 : const int xlen = 5;
581 1 : const int ylen = 3;
582 1 : std::array<int8_t, xlen * ylen> in
583 : {
584 : 1, 2, 0, 4, 1,
585 : 0, 0, 2, 1, 0,
586 : 1, 0, 0, 3, 3
587 : };
588 : // clang-format on
589 :
590 : {
591 2 : Options opts = stdOptions(-2, 1);
592 1 : opts.outputMode = OutputMode::DEM;
593 2 : DatasetPtr ds = runViewshed(in.data(), xlen, ylen, opts);
594 1 : GDALRasterBand *band = ds->GetRasterBand(1);
595 : std::array<double, xlen * ylen> out;
596 1 : CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(), xlen,
597 1 : ylen, GDT_Float64, 0, 0, nullptr);
598 1 : EXPECT_EQ(err, CE_None);
599 :
600 : // clang-format off
601 1 : std::array<double, xlen * ylen> expected
602 : {
603 : 1, 2, 2, 4, 4.5,
604 : 0, 0, 2, 2.5, 3,
605 : 1, 1, 1, 3, 3.5
606 : };
607 : // clang-format on
608 :
609 16 : for (size_t i = 0; i < out.size(); ++i)
610 15 : EXPECT_DOUBLE_EQ(out[i], expected[i]);
611 : }
612 :
613 : {
614 2 : Options opts = stdOptions(-2, 2);
615 1 : opts.outputMode = OutputMode::DEM;
616 2 : DatasetPtr ds = runViewshed(in.data(), xlen, ylen, opts);
617 1 : GDALRasterBand *band = ds->GetRasterBand(1);
618 : std::array<double, xlen * ylen> out;
619 1 : CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(), xlen,
620 1 : ylen, GDT_Float64, 0, 0, nullptr);
621 1 : EXPECT_EQ(err, CE_None);
622 :
623 : // clang-format off
624 1 : std::array<double, xlen * ylen> expected
625 : {
626 : 1, 2, 5 / 3.0, 4, 4.2,
627 : 0, .5, 2, 2.5, 3.1,
628 : 1, 1.5, 2, 3, 3.6
629 : };
630 : // clang-format on
631 :
632 16 : for (size_t i = 0; i < out.size(); ++i)
633 15 : EXPECT_DOUBLE_EQ(out[i], expected[i]);
634 : }
635 1 : }
636 :
637 : // Test an observer above the raster
638 4 : TEST(Viewshed, oor_above)
639 : {
640 : // clang-format off
641 1 : const int xlen = 5;
642 1 : const int ylen = 3;
643 1 : std::array<int8_t, xlen * ylen> in
644 : {
645 : 1, 2, 0, 4, 1,
646 : 0, 0, 2, 1, 0,
647 : 1, 0, 0, 3, 3
648 : };
649 : // clang-format on
650 :
651 : {
652 2 : Options opts = stdOptions(2, -2);
653 1 : opts.outputMode = OutputMode::DEM;
654 2 : DatasetPtr ds = runViewshed(in.data(), xlen, ylen, opts);
655 1 : GDALRasterBand *band = ds->GetRasterBand(1);
656 : std::array<double, xlen * ylen> out;
657 1 : CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(), xlen,
658 1 : ylen, GDT_Float64, 0, 0, nullptr);
659 :
660 1 : EXPECT_EQ(err, CE_None);
661 :
662 : // clang-format off
663 1 : std::array<double, xlen * ylen> expected
664 : {
665 : 1, 2, 0, 4, 1,
666 : 2.5, 2, 2, 4, 4.5,
667 : 3, 8 / 3.0, 8 / 3.0, 14 / 3.0, 17 / 3.0
668 : };
669 : // clang-format on
670 :
671 16 : for (size_t i = 0; i < out.size(); ++i)
672 15 : EXPECT_DOUBLE_EQ(out[i], expected[i]);
673 : }
674 :
675 : {
676 2 : Options opts = stdOptions(-2, -2);
677 1 : opts.outputMode = OutputMode::DEM;
678 2 : DatasetPtr ds = runViewshed(in.data(), xlen, ylen, opts);
679 1 : GDALRasterBand *band = ds->GetRasterBand(1);
680 : std::array<double, xlen * ylen> out;
681 1 : CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(), xlen,
682 1 : ylen, GDT_Float64, 0, 0, nullptr);
683 1 : EXPECT_EQ(err, CE_None);
684 :
685 : // clang-format off
686 1 : std::array<double, xlen * ylen> expected
687 : {
688 : 1, 2, 0, 4, 1,
689 : 0, 1.5, 2.5, 1.25, 3.15,
690 : 1, 0.5, 2, 3, 3
691 : };
692 : // clang-format on
693 :
694 16 : for (size_t i = 0; i < out.size(); ++i)
695 15 : EXPECT_DOUBLE_EQ(out[i], expected[i]);
696 : }
697 1 : }
698 :
699 : // Test an observer below the raster
700 4 : TEST(Viewshed, oor_below)
701 : {
702 : // clang-format off
703 1 : const int xlen = 5;
704 1 : const int ylen = 3;
705 1 : std::array<int8_t, xlen * ylen> in
706 : {
707 : 1, 2, 0, 4, 1,
708 : 0, 0, 2, 1, 0,
709 : 1, 0, 0, 3, 3
710 : };
711 : // clang-format on
712 :
713 : {
714 2 : Options opts = stdOptions(2, 4);
715 1 : opts.outputMode = OutputMode::DEM;
716 2 : DatasetPtr ds = runViewshed(in.data(), xlen, ylen, opts);
717 1 : GDALRasterBand *band = ds->GetRasterBand(1);
718 : std::array<double, xlen * ylen> out;
719 1 : CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(), xlen,
720 1 : ylen, GDT_Float64, 0, 0, nullptr);
721 :
722 1 : EXPECT_EQ(err, CE_None);
723 :
724 : // clang-format off
725 1 : std::array<double, xlen * ylen> expected
726 : {
727 : 1, 2, 8 / 3.0, 4, 5,
728 : 0.5, 0, 2, 3, 4.5,
729 : 1, 0, 0, 3, 3
730 : };
731 : // clang-format on
732 :
733 16 : for (size_t i = 0; i < out.size(); ++i)
734 15 : EXPECT_DOUBLE_EQ(out[i], expected[i]);
735 : }
736 :
737 : {
738 2 : Options opts = stdOptions(6, 4);
739 1 : opts.outputMode = OutputMode::DEM;
740 2 : DatasetPtr ds = runViewshed(in.data(), xlen, ylen, opts);
741 1 : GDALRasterBand *band = ds->GetRasterBand(1);
742 : std::array<double, xlen * ylen> out;
743 1 : CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(), xlen,
744 1 : ylen, GDT_Float64, 0, 0, nullptr);
745 1 : EXPECT_EQ(err, CE_None);
746 :
747 : // clang-format off
748 1 : std::array<double, xlen * ylen> expected
749 : {
750 : 4.2, 6, 6, 4, 1,
751 : 1.35, 2.25, 4.5, 4.5, 0,
752 : 1, 0, 0, 3, 3
753 : };
754 : // clang-format on
755 :
756 16 : for (size_t i = 0; i < out.size(); ++i)
757 15 : EXPECT_DOUBLE_EQ(out[i], expected[i]);
758 : }
759 1 : }
760 :
761 : } // namespace viewshed
762 : } // namespace gdal
|