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 30 : Options stdOptions(int x, int y)
37 : {
38 30 : Options opts;
39 30 : opts.observer.x = x;
40 30 : opts.observer.y = y;
41 30 : opts.outputFilename = "none";
42 30 : opts.outputFormat = "mem";
43 30 : opts.curveCoeff = 0;
44 :
45 30 : 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 8 : DatasetPtr runViewshed(const double *in, const double *sd, int xlen, int ylen,
73 : const Options &opts)
74 : {
75 16 : Viewshed v(opts);
76 :
77 8 : GDALDriver *driver = (GDALDriver *)GDALGetDriverByName("MEM");
78 : GDALDataset *dataset =
79 8 : driver->Create("", xlen, ylen, 2, GDT_Float32, nullptr);
80 8 : EXPECT_TRUE(dataset);
81 8 : dataset->SetGeoTransform(identity.data());
82 8 : GDALRasterBand *band = dataset->GetRasterBand(1);
83 8 : EXPECT_TRUE(band);
84 8 : CPLErr err = band->RasterIO(GF_Write, 0, 0, xlen, ylen, (void *)in, xlen,
85 8 : ylen, GDT_Float64, 0, 0, nullptr);
86 8 : EXPECT_EQ(err, CE_None);
87 8 : GDALRasterBand *sdBand = dataset->GetRasterBand(2);
88 8 : EXPECT_TRUE(sdBand);
89 8 : err = sdBand->RasterIO(GF_Write, 0, 0, xlen, ylen, (void *)sd, xlen, ylen,
90 : GDT_Float64, 0, 0, nullptr);
91 8 : EXPECT_EQ(err, CE_None);
92 :
93 8 : EXPECT_TRUE(v.run(band, sdBand));
94 16 : return v.output();
95 : }
96 :
97 : } // namespace
98 :
99 4 : TEST(Viewshed, min_max_mask)
100 : {
101 1 : const int xlen = 15;
102 1 : const int ylen = 15;
103 : std::array<int8_t, xlen * ylen> in;
104 1 : in.fill(0);
105 :
106 2 : SCOPED_TRACE("min_max_mask");
107 2 : Options opts(stdOptions(7, 7));
108 1 : opts.minDistance = 2;
109 1 : opts.maxDistance = 6;
110 :
111 2 : DatasetPtr output = runViewshed(in.data(), xlen, ylen, opts);
112 :
113 : std::array<int8_t, xlen * ylen> out;
114 1 : GDALRasterBand *band = output->GetRasterBand(1);
115 :
116 1 : int xOutLen = band->GetXSize();
117 1 : int yOutLen = band->GetYSize();
118 1 : EXPECT_EQ(xOutLen, 13);
119 1 : EXPECT_EQ(yOutLen, 13);
120 :
121 1 : CPLErr err = band->RasterIO(GF_Read, 0, 0, xOutLen, yOutLen, out.data(),
122 1 : xOutLen, yOutLen, GDT_Int8, 0, 0, nullptr);
123 1 : EXPECT_EQ(err, CE_None);
124 :
125 : //clang-format off
126 1 : std::array<int8_t, 13 * 13> expected{
127 : 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
128 : 0, 0, 0, 0, 0, 0, 127, 0, 0, 0, 0, 0, 0,
129 : 0, 0, 0, 127, 127, 127, 127, 127, 127, 127, 0, 0, 0,
130 : 0, 0, 127, 127, 127, 127, 127, 127, 127, 127, 127, 0, 0,
131 : 0, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 0,
132 : 0, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 0,
133 : 0, 127, 127, 127, 127, 0, 0, 0, 127, 127, 127, 127, 0,
134 : 127, 127, 127, 127, 127, 0, 0, 0, 127, 127, 127, 127, 127,
135 : 0, 127, 127, 127, 127, 0, 0, 0, 127, 127, 127, 127, 0,
136 : 0, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 0,
137 : 0, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 0,
138 : 0, 0, 127, 127, 127, 127, 127, 127, 127, 127, 127, 0, 0,
139 : 0, 0, 0, 127, 127, 127, 127, 127, 127, 127, 0, 0, 0};
140 : //clang-format on
141 :
142 1 : int8_t *o = out.data();
143 1 : int8_t *e = expected.data();
144 170 : for (size_t i = 0; i < 13 * 13; ++i)
145 169 : EXPECT_EQ(*e++, *o++);
146 :
147 : /**
148 : int8_t *p = out.data();
149 : for (int y = 0; y < yOutLen; ++y)
150 : {
151 : for (int x = 0; x < xOutLen; ++x)
152 : {
153 : char c;
154 : if (*p == 0)
155 : c = '*';
156 : else if (*p == 127)
157 : c = '.';
158 : else
159 : c = '?';
160 : std::cerr << c;
161 : p++;
162 : }
163 : std::cerr << "\n";
164 : }
165 : std::cerr << "\n";
166 : **/
167 1 : }
168 :
169 4 : TEST(Viewshed, angle)
170 : {
171 1 : const int xlen = 17;
172 1 : const int ylen = 17;
173 : std::array<int8_t, xlen * ylen> in;
174 1 : in.fill(0);
175 :
176 2 : SCOPED_TRACE("min_max_mask");
177 2 : Options opts(stdOptions(8, 8));
178 1 : opts.startAngle = 0;
179 1 : opts.endAngle = 30;
180 :
181 2 : DatasetPtr output = runViewshed(in.data(), xlen, ylen, opts);
182 :
183 : std::array<int8_t, xlen * ylen> out;
184 1 : GDALRasterBand *band = output->GetRasterBand(1);
185 :
186 1 : int xOutLen = band->GetXSize();
187 1 : int yOutLen = band->GetYSize();
188 1 : EXPECT_EQ(xOutLen, 6);
189 1 : EXPECT_EQ(yOutLen, 9);
190 1 : CPLErr err = band->RasterIO(GF_Read, 0, 0, xOutLen, yOutLen, out.data(),
191 1 : xOutLen, yOutLen, GDT_Int8, 0, 0, nullptr);
192 1 : EXPECT_EQ(err, CE_None);
193 :
194 : // clang-format off
195 1 : std::array<int8_t, 6 * 9> expected{
196 : 127, 127, 127, 127, 127, 127,
197 : 127, 127, 127, 127, 127, 0,
198 : 127, 127, 127, 127, 0, 0,
199 : 127, 127, 127, 127, 0, 0,
200 : 127, 127, 127, 0, 0, 0,
201 : 127, 127, 127, 0, 0, 0,
202 : 127, 127, 0, 0, 0, 0,
203 : 127, 127, 0, 0, 0, 0,
204 : 127, 0, 0, 0, 0, 0};
205 : // clang-format on
206 :
207 1 : int8_t *o = out.data();
208 1 : int8_t *e = expected.data();
209 55 : for (size_t i = 0; i < 6 * 9; ++i)
210 54 : EXPECT_EQ(*e++, *o++);
211 :
212 : /**
213 : int8_t *p = out.data();
214 : for (int y = 0; y < yOutLen; ++y)
215 : {
216 : for (int x = 0; x < xOutLen; ++x)
217 : {
218 : char c;
219 : if (*p == 0)
220 : c = '*';
221 : else if (*p == 127)
222 : c = '.';
223 : else
224 : c = '?';
225 : std::cerr << c;
226 : p++;
227 : }
228 : std::cerr << "\n";
229 : }
230 : std::cerr << "\n";
231 : **/
232 1 : }
233 :
234 4 : TEST(Viewshed, angle2)
235 : {
236 1 : const int xlen = 11;
237 1 : const int ylen = 11;
238 : std::array<int8_t, xlen * ylen> in;
239 1 : in.fill(0);
240 :
241 2 : SCOPED_TRACE("min_max_mask");
242 2 : Options opts(stdOptions(5, 5));
243 1 : opts.startAngle = 0;
244 1 : opts.endAngle = 300;
245 :
246 2 : DatasetPtr output = runViewshed(in.data(), xlen, ylen, opts);
247 :
248 : std::array<int8_t, xlen * ylen> out;
249 1 : GDALRasterBand *band = output->GetRasterBand(1);
250 :
251 1 : int xOutLen = band->GetXSize();
252 1 : int yOutLen = band->GetYSize();
253 1 : EXPECT_EQ(xOutLen, 11);
254 1 : EXPECT_EQ(yOutLen, 11);
255 1 : CPLErr err = band->RasterIO(GF_Read, 0, 0, xOutLen, yOutLen, out.data(),
256 1 : xOutLen, yOutLen, GDT_Int8, 0, 0, nullptr);
257 1 : EXPECT_EQ(err, CE_None);
258 :
259 : // clang-format off
260 1 : std::array<int8_t, 11 * 11> expected{
261 : 0, 0, 0, 0, 0, 127, 127, 127, 127, 127, 127,
262 : 0, 0, 0, 0, 0, 127, 127, 127, 127, 127, 127,
263 : 127, 0, 0, 0, 0, 127, 127, 127, 127, 127, 127,
264 : 127, 127, 127, 0, 0, 127, 127, 127, 127, 127, 127,
265 : 127, 127, 127, 127, 0, 127, 127, 127, 127, 127, 127,
266 : 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
267 : 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
268 : 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
269 : 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
270 : 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
271 : 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127
272 : };
273 : // clang-format on
274 :
275 1 : int8_t *o = out.data();
276 1 : int8_t *e = expected.data();
277 122 : for (size_t i = 0; i < 11 * 11; ++i)
278 121 : EXPECT_EQ(*e++, *o++);
279 1 : }
280 :
281 4 : TEST(Viewshed, high_mask)
282 : {
283 1 : const int xlen = 15;
284 1 : const int ylen = 15;
285 : std::array<int8_t, xlen * ylen> in;
286 1 : in.fill(0);
287 1 : in[15 * 7 + 5] = 1;
288 1 : in[15 * 7 + 6] = 3;
289 1 : in[15 * 7 + 7] = 5;
290 1 : in[15 * 7 + 8] = 7;
291 1 : in[15 * 7 + 9] = 7;
292 1 : in[15 * 7 + 10] = 7;
293 1 : in[15 * 7 + 11] = 7;
294 1 : in[15 * 7 + 12] = 12;
295 1 : in[15 * 7 + 13] = 6;
296 1 : in[15 * 7 + 14] = 15;
297 :
298 2 : SCOPED_TRACE("high_mask");
299 2 : Options opts(stdOptions(3, 7));
300 :
301 1 : opts.highPitch = 45;
302 1 : opts.outOfRangeVal = 2;
303 1 : opts.visibleVal = 1;
304 1 : opts.invisibleVal = 0;
305 :
306 2 : DatasetPtr output = runViewshed(in.data(), xlen, ylen, opts);
307 :
308 : std::array<int8_t, xlen * ylen> out;
309 1 : GDALRasterBand *band = output->GetRasterBand(1);
310 :
311 1 : int xOutLen = band->GetXSize();
312 1 : int yOutLen = band->GetYSize();
313 1 : EXPECT_EQ(xOutLen, 15);
314 1 : EXPECT_EQ(yOutLen, 15);
315 1 : CPLErr err = band->RasterIO(GF_Read, 0, 0, xOutLen, yOutLen, out.data(),
316 1 : xOutLen, yOutLen, GDT_Int8, 0, 0, nullptr);
317 1 : EXPECT_EQ(err, CE_None);
318 :
319 : // clang-format off
320 1 : std::array<int8_t, 15 * 15> expected{
321 : 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0,
322 : 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0,
323 : 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
324 : 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
325 : 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
326 : 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
327 : 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
328 : 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 0, 2, 0, 2,
329 : 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
330 : 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
331 : 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
332 : 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
333 : 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
334 : 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0,
335 : 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0
336 : };
337 : // clang-format on
338 :
339 1 : int8_t *o = out.data();
340 1 : int8_t *e = expected.data();
341 16 : for (int y = 0; y < 15; ++y)
342 : {
343 240 : for (int x = 0; x < 15; ++x)
344 : {
345 225 : EXPECT_EQ(*e, *o) << "X/Y exp/act = " << x << "/" << y << "/"
346 0 : << (int)*e << "/" << (int)*o << "!\n";
347 225 : e++;
348 225 : o++;
349 : }
350 : }
351 1 : }
352 :
353 4 : TEST(Viewshed, low_mask)
354 : {
355 1 : const int xlen = 5;
356 1 : const int ylen = 5;
357 : std::array<int8_t, xlen * ylen> in;
358 1 : in.fill(0);
359 1 : in[12] = 5;
360 :
361 2 : SCOPED_TRACE("low_mask");
362 2 : Options opts(stdOptions(2, 2));
363 :
364 1 : opts.lowPitch = -45;
365 1 : opts.outputMode = OutputMode::DEM;
366 :
367 2 : DatasetPtr output = runViewshed(in.data(), xlen, ylen, opts);
368 :
369 : std::array<double, xlen * ylen> out;
370 1 : GDALRasterBand *band = output->GetRasterBand(1);
371 :
372 1 : int xOutLen = band->GetXSize();
373 1 : int yOutLen = band->GetYSize();
374 1 : CPLErr err = band->RasterIO(GF_Read, 0, 0, xOutLen, yOutLen, out.data(),
375 1 : xOutLen, yOutLen, GDT_Float64, 0, 0, nullptr);
376 1 : EXPECT_EQ(err, CE_None);
377 :
378 1 : std::array<double, 5 * 5> expected{2.17157, 2.76393, 3, 2.76393, 2.17157,
379 : 2.76393, 3.58579, 4, 3.58579, 2.76393,
380 : 3, 4, 5, 4, 3,
381 : 2.76393, 3.58579, 4, 3.58579, 2.76393,
382 : 2.17157, 2.76393, 3, 2.76393, 2.17157};
383 :
384 1 : const double *o = out.data();
385 1 : const double *e = expected.data();
386 26 : for (size_t i = 0; i < expected.size(); ++i)
387 : {
388 25 : EXPECT_NEAR(*o, *e, .00001);
389 25 : o++;
390 25 : e++;
391 : }
392 1 : }
393 :
394 4 : TEST(Viewshed, all_visible)
395 : {
396 : // clang-format off
397 1 : const int xlen = 3;
398 1 : const int ylen = 3;
399 1 : std::array<int8_t, xlen * ylen> in
400 : {
401 : 1, 2, 3,
402 : 4, 5, 6,
403 : 3, 2, 1
404 : };
405 : // clang-format on
406 :
407 2 : SCOPED_TRACE("all_visible");
408 2 : DatasetPtr output = runViewshed(in.data(), xlen, ylen, stdOptions(1, 1));
409 :
410 : std::array<uint8_t, xlen * ylen> out;
411 1 : GDALRasterBand *band = output->GetRasterBand(1);
412 1 : CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(), xlen,
413 1 : ylen, GDT_Int8, 0, 0, nullptr);
414 1 : EXPECT_EQ(err, CE_None);
415 :
416 10 : for (uint8_t o : out)
417 9 : EXPECT_EQ(o, 127);
418 1 : }
419 :
420 4 : TEST(Viewshed, simple_height)
421 : {
422 : // clang-format off
423 1 : const int xlen = 5;
424 1 : const int ylen = 5;
425 1 : std::array<int8_t, xlen * ylen> in
426 : {
427 : -1, 0, 1, 0, -1,
428 : -1, 2, 0, 4, -1,
429 : -1, 1, 0, -1, -1,
430 : 0, 3, 0, 2, 0,
431 : -1, 0, 0, 3, -1
432 : };
433 :
434 1 : std::array<double, xlen * ylen> observable
435 : {
436 : 4, 2, 1, 4, 8,
437 : 3, 2, 0, 4, 3,
438 : 2, 1, 0, -1, -1,
439 : 4, 3, 0, 2, 1,
440 : 6, 3, 0, 3, 4
441 : };
442 : // clang-format on
443 :
444 : {
445 2 : SCOPED_TRACE("simple_height:normal");
446 :
447 : DatasetPtr output =
448 2 : runViewshed(in.data(), xlen, ylen, stdOptions(2, 2));
449 :
450 : std::array<int8_t, xlen * ylen> out;
451 1 : GDALRasterBand *band = output->GetRasterBand(1);
452 1 : CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(), xlen,
453 1 : ylen, GDT_Int8, 0, 0, nullptr);
454 1 : EXPECT_EQ(err, CE_None);
455 :
456 : // We expect the cell to be observable when the input is higher than the observable
457 : // height.
458 : std::array<int8_t, xlen * ylen> expected;
459 26 : for (size_t i = 0; i < in.size(); ++i)
460 25 : expected[i] = (in[i] >= observable[i] ? 127 : 0);
461 :
462 1 : EXPECT_EQ(expected, out);
463 : }
464 :
465 : {
466 : std::array<double, xlen * ylen> dem;
467 2 : SCOPED_TRACE("simple_height:dem");
468 2 : Options opts = stdOptions(2, 2);
469 1 : opts.outputMode = OutputMode::DEM;
470 :
471 2 : DatasetPtr output = runViewshed(in.data(), xlen, ylen, opts);
472 :
473 1 : GDALRasterBand *band = output->GetRasterBand(1);
474 1 : CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, dem.data(), xlen,
475 1 : ylen, GDT_Float64, 0, 0, nullptr);
476 1 : EXPECT_EQ(err, CE_None);
477 :
478 1 : std::array<double, xlen * ylen> expected = observable;
479 : // Double equality is fine here as all the values are small integers.
480 1 : EXPECT_EQ(dem, expected);
481 : }
482 :
483 : {
484 : std::array<double, xlen * ylen> ground;
485 2 : SCOPED_TRACE("simple_height:ground");
486 2 : Options opts = stdOptions(2, 2);
487 1 : opts.outputMode = OutputMode::Ground;
488 2 : DatasetPtr output = runViewshed(in.data(), xlen, ylen, opts);
489 :
490 1 : GDALRasterBand *band = output->GetRasterBand(1);
491 1 : CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, ground.data(),
492 1 : xlen, ylen, GDT_Float64, 0, 0, nullptr);
493 1 : EXPECT_EQ(err, CE_None);
494 :
495 : std::array<double, xlen * ylen> expected;
496 26 : for (size_t i = 0; i < expected.size(); ++i)
497 25 : expected[i] = std::max(0.0, observable[i] - in[i]);
498 :
499 : // Double equality is fine here as all the values are small integers.
500 1 : EXPECT_EQ(expected, ground);
501 : }
502 1 : }
503 :
504 : // Addresses cases in #9501
505 4 : TEST(Viewshed, dem_vs_ground)
506 : {
507 : // Run gdal_viewshed on the input 8 x 1 array in both ground and dem mode and
508 : // verify the results are what are expected.
509 5 : auto run = [](const std::array<int8_t, 8> &in, Coord observer,
510 : const std::array<double, 8> &ground,
511 : const std::array<double, 8> &dem)
512 : {
513 5 : const int xlen = 8;
514 5 : const int ylen = 1;
515 :
516 : std::array<double, xlen * ylen> out;
517 10 : Options opts = stdOptions(observer);
518 5 : opts.outputMode = OutputMode::Ground;
519 :
520 : // Verify ground mode.
521 10 : DatasetPtr ds = runViewshed(in.data(), xlen, ylen, opts);
522 5 : GDALRasterBand *band = ds->GetRasterBand(1);
523 5 : CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(), xlen,
524 5 : ylen, GDT_Float64, 0, 0, nullptr);
525 5 : EXPECT_EQ(err, CE_None);
526 45 : for (size_t i = 0; i < ground.size(); ++i)
527 40 : EXPECT_DOUBLE_EQ(out[i], ground[i]);
528 :
529 : // Verify DEM mode.
530 5 : opts.outputMode = OutputMode::DEM;
531 5 : ds = runViewshed(in.data(), xlen, ylen, opts);
532 5 : band = ds->GetRasterBand(1);
533 5 : err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(), xlen, ylen,
534 : GDT_Float64, 0, 0, nullptr);
535 5 : EXPECT_EQ(err, CE_None);
536 45 : for (size_t i = 0; i < dem.size(); ++i)
537 40 : EXPECT_DOUBLE_EQ(out[i], dem[i]);
538 5 : };
539 :
540 : // Input / Observer / Minimum expected above ground / Minimum expected above zero (DEM)
541 1 : run({0, 0, 0, 1, 0, 0, 0, 0}, {2, 0}, {0, 0, 0, 0, 2, 3, 4, 5},
542 : {0, 0, 0, 1, 2, 3, 4, 5});
543 1 : run({1, 1, 0, 1, 0, 1, 2, 2}, {3, 0}, {0, 0, 0, 0, 0, 0, 0, 1 / 3.0},
544 : {1, 1, 0, 1, 0, 1, 2, 7 / 3.0});
545 1 : run({0, 0, 0, 1, 1, 0, 0, 0}, {0, 0},
546 : {0, 0, 0, 0, 1 / 3.0, 5 / 3.0, 6 / 3.0, 7 / 3.0},
547 : {0, 0, 0, 1, 4 / 3.0, 5 / 3.0, 6 / 3.0, 7 / 3.0});
548 1 : run({0, 0, 1, 2, 3, 4, 5, 6}, {0, 0}, {0, 0, 0, 0, 0, 0, 0, 0},
549 : {0, 0, 1, 2, 3, 4, 5, 6});
550 1 : run({0, 0, 1, 1, 3, 4, 5, 4}, {0, 0}, {0, 0, 0, .5, 0, 0, 0, 11 / 6.0},
551 : {0, 0, 1, 1.5, 3, 4, 5, 35 / 6.0});
552 1 : }
553 :
554 : // Test an observer to the right of the raster.
555 4 : TEST(Viewshed, oor_right)
556 : {
557 : // clang-format off
558 1 : const int xlen = 5;
559 1 : const int ylen = 3;
560 1 : std::array<int8_t, xlen * ylen> in
561 : {
562 : 1, 2, 0, 4, 1,
563 : 0, 0, 2, 1, 0,
564 : 1, 0, 0, 3, 3
565 : };
566 : // clang-format on
567 :
568 : {
569 2 : Options opts = stdOptions(6, 1);
570 1 : opts.outputMode = OutputMode::DEM;
571 2 : DatasetPtr ds = runViewshed(in.data(), xlen, ylen, opts);
572 1 : GDALRasterBand *band = ds->GetRasterBand(1);
573 : std::array<double, xlen * ylen> out;
574 1 : CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(), xlen,
575 1 : ylen, GDT_Float64, 0, 0, nullptr);
576 1 : EXPECT_EQ(err, CE_None);
577 :
578 : // clang-format off
579 1 : std::array<double, xlen * ylen> expected
580 : {
581 : 16 / 3.0, 29 / 6.0, 13 / 3.0, 4, 1,
582 : 3, 2.5, 2, 1, 0,
583 : 13 / 3.0, 23 / 6.0, 10 / 3.0, 3, 3
584 : };
585 : // clang-format on
586 :
587 16 : for (size_t i = 0; i < out.size(); ++i)
588 15 : EXPECT_DOUBLE_EQ(out[i], expected[i]);
589 : }
590 :
591 : {
592 2 : Options opts = stdOptions(6, 2);
593 1 : opts.outputMode = OutputMode::DEM;
594 2 : DatasetPtr ds = runViewshed(in.data(), xlen, ylen, opts);
595 1 : GDALRasterBand *band = ds->GetRasterBand(1);
596 : std::array<double, xlen * ylen> out;
597 1 : CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(), xlen,
598 1 : ylen, GDT_Float64, 0, 0, nullptr);
599 1 : EXPECT_EQ(err, CE_None);
600 :
601 : // clang-format off
602 1 : std::array<double, xlen * ylen> expected
603 : {
604 : 26 / 5.0, 17 / 4.0, 11 / 3.0, 4, 1,
605 : 6, 4.5, 3, 1.5, 0,
606 : 9, 7.5, 6, 4.5, 3
607 : };
608 : // clang-format on
609 :
610 16 : for (size_t i = 0; i < out.size(); ++i)
611 15 : EXPECT_DOUBLE_EQ(out[i], expected[i]);
612 : }
613 1 : }
614 :
615 : // Test an observer to the left of the raster.
616 4 : TEST(Viewshed, oor_left)
617 : {
618 : // clang-format off
619 1 : const int xlen = 5;
620 1 : const int ylen = 3;
621 1 : std::array<int8_t, xlen * ylen> in
622 : {
623 : 1, 2, 0, 4, 1,
624 : 0, 0, 2, 1, 0,
625 : 1, 0, 0, 3, 3
626 : };
627 : // clang-format on
628 :
629 : {
630 2 : Options opts = stdOptions(-2, 1);
631 1 : opts.outputMode = OutputMode::DEM;
632 2 : DatasetPtr ds = runViewshed(in.data(), xlen, ylen, opts);
633 1 : GDALRasterBand *band = ds->GetRasterBand(1);
634 : std::array<double, xlen * ylen> out;
635 1 : CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(), xlen,
636 1 : ylen, GDT_Float64, 0, 0, nullptr);
637 1 : EXPECT_EQ(err, CE_None);
638 :
639 : // clang-format off
640 1 : std::array<double, xlen * ylen> expected
641 : {
642 : 1, 2, 2, 4, 4.5,
643 : 0, 0, 2, 2.5, 3,
644 : 1, 1, 1, 3, 3.5
645 : };
646 : // clang-format on
647 :
648 16 : for (size_t i = 0; i < out.size(); ++i)
649 15 : EXPECT_DOUBLE_EQ(out[i], expected[i]);
650 : }
651 :
652 : {
653 2 : Options opts = stdOptions(-2, 2);
654 1 : opts.outputMode = OutputMode::DEM;
655 2 : DatasetPtr ds = runViewshed(in.data(), xlen, ylen, opts);
656 1 : GDALRasterBand *band = ds->GetRasterBand(1);
657 : std::array<double, xlen * ylen> out;
658 1 : CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(), xlen,
659 1 : ylen, GDT_Float64, 0, 0, nullptr);
660 1 : EXPECT_EQ(err, CE_None);
661 :
662 : // clang-format off
663 1 : std::array<double, xlen * ylen> expected
664 : {
665 : 1, 2, 5 / 3.0, 4, 4.2,
666 : 0, .5, 2, 2.5, 3.1,
667 : 1, 1.5, 2, 3, 3.6
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 1 : }
675 :
676 : // Test an observer above the raster
677 4 : TEST(Viewshed, oor_above)
678 : {
679 : // clang-format off
680 1 : const int xlen = 5;
681 1 : const int ylen = 3;
682 1 : std::array<int8_t, xlen * ylen> in
683 : {
684 : 1, 2, 0, 4, 1,
685 : 0, 0, 2, 1, 0,
686 : 1, 0, 0, 3, 3
687 : };
688 : // clang-format on
689 :
690 : {
691 2 : Options opts = stdOptions(2, -2);
692 1 : opts.outputMode = OutputMode::DEM;
693 2 : DatasetPtr ds = runViewshed(in.data(), xlen, ylen, opts);
694 1 : GDALRasterBand *band = ds->GetRasterBand(1);
695 : std::array<double, xlen * ylen> out;
696 1 : CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(), xlen,
697 1 : ylen, GDT_Float64, 0, 0, nullptr);
698 :
699 1 : EXPECT_EQ(err, CE_None);
700 :
701 : // clang-format off
702 1 : std::array<double, xlen * ylen> expected
703 : {
704 : 1, 2, 0, 4, 1,
705 : 2.5, 2, 2, 4, 4.5,
706 : 3, 8 / 3.0, 8 / 3.0, 14 / 3.0, 17 / 3.0
707 : };
708 : // clang-format on
709 :
710 16 : for (size_t i = 0; i < out.size(); ++i)
711 15 : EXPECT_DOUBLE_EQ(out[i], expected[i]);
712 : }
713 :
714 : {
715 2 : Options opts = stdOptions(-2, -2);
716 1 : opts.outputMode = OutputMode::DEM;
717 2 : DatasetPtr ds = runViewshed(in.data(), xlen, ylen, opts);
718 1 : GDALRasterBand *band = ds->GetRasterBand(1);
719 : std::array<double, xlen * ylen> out;
720 1 : CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(), xlen,
721 1 : ylen, GDT_Float64, 0, 0, nullptr);
722 1 : EXPECT_EQ(err, CE_None);
723 :
724 : // clang-format off
725 1 : std::array<double, xlen * ylen> expected
726 : {
727 : 1, 2, 0, 4, 1,
728 : 0, 1.5, 2.5, 1.25, 3.15,
729 : 1, 0.5, 2, 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 1 : }
737 :
738 : // Test an observer below the raster
739 4 : TEST(Viewshed, oor_below)
740 : {
741 : // clang-format off
742 1 : const int xlen = 5;
743 1 : const int ylen = 3;
744 1 : std::array<int8_t, xlen * ylen> in
745 : {
746 : 1, 2, 0, 4, 1,
747 : 0, 0, 2, 1, 0,
748 : 1, 0, 0, 3, 3
749 : };
750 : // clang-format on
751 :
752 : {
753 2 : Options opts = stdOptions(2, 4);
754 1 : opts.outputMode = OutputMode::DEM;
755 2 : DatasetPtr ds = runViewshed(in.data(), xlen, ylen, opts);
756 1 : GDALRasterBand *band = ds->GetRasterBand(1);
757 : std::array<double, xlen * ylen> out;
758 1 : CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(), xlen,
759 1 : ylen, GDT_Float64, 0, 0, nullptr);
760 :
761 1 : EXPECT_EQ(err, CE_None);
762 :
763 : // clang-format off
764 1 : std::array<double, xlen * ylen> expected
765 : {
766 : 1, 2, 8 / 3.0, 4, 5,
767 : 0.5, 0, 2, 3, 4.5,
768 : 1, 0, 0, 3, 3
769 : };
770 : // clang-format on
771 :
772 16 : for (size_t i = 0; i < out.size(); ++i)
773 15 : EXPECT_DOUBLE_EQ(out[i], expected[i]);
774 : }
775 :
776 : {
777 2 : Options opts = stdOptions(6, 4);
778 1 : opts.outputMode = OutputMode::DEM;
779 2 : DatasetPtr ds = runViewshed(in.data(), xlen, ylen, opts);
780 1 : GDALRasterBand *band = ds->GetRasterBand(1);
781 : std::array<double, xlen * ylen> out;
782 1 : CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(), xlen,
783 1 : ylen, GDT_Float64, 0, 0, nullptr);
784 1 : EXPECT_EQ(err, CE_None);
785 :
786 : // clang-format off
787 1 : std::array<double, xlen * ylen> expected
788 : {
789 : 4.2, 6, 6, 4, 1,
790 : 1.35, 2.25, 4.5, 4.5, 0,
791 : 1, 0, 0, 3, 3
792 : };
793 : // clang-format on
794 :
795 16 : for (size_t i = 0; i < out.size(); ++i)
796 15 : EXPECT_DOUBLE_EQ(out[i], expected[i]);
797 : }
798 1 : }
799 :
800 : // Test a handling of SD raster right and left.
801 4 : TEST(Viewshed, sd)
802 : {
803 : {
804 : // clang-format off
805 1 : const int xlen = 8;
806 1 : const int ylen = 1;
807 1 : std::array<double, xlen * ylen> in
808 : {
809 : 0, 1, 1, 3.1, 1.5, 2.7, 3.7, 7.5
810 : };
811 :
812 1 : std::array<double, xlen * ylen> sd
813 : {
814 : 1, 100, .1, 100, .1, .1, 100, .1
815 : };
816 : // clang-format on
817 :
818 : {
819 2 : Options opts = stdOptions(0, 0);
820 1 : opts.outputMode = OutputMode::Normal;
821 2 : DatasetPtr ds = runViewshed(in.data(), sd.data(), xlen, ylen, opts);
822 1 : GDALRasterBand *band = ds->GetRasterBand(1);
823 : std::array<double, xlen * ylen> out;
824 1 : CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(),
825 1 : xlen, ylen, GDT_Float64, 0, 0, nullptr);
826 :
827 1 : EXPECT_EQ(err, CE_None);
828 :
829 : // clang-format off
830 1 : std::array<double, xlen * ylen> expected
831 : {
832 : 255, 255, 2, 255, 0, 2, 2, 255
833 : };
834 : // clang-format on
835 :
836 9 : for (size_t i = 0; i < out.size(); ++i)
837 8 : EXPECT_DOUBLE_EQ(out[i], expected[i])
838 0 : << "Error at position " << i << ".";
839 : }
840 : }
841 : {
842 : // clang-format off
843 1 : const int xlen = 8;
844 1 : const int ylen = 1;
845 1 : std::array<double, xlen * ylen> in
846 : {
847 : 7.5, 3.7, 2.7, 1.5, 3.1, 1, 1, 0
848 : };
849 :
850 1 : std::array<double, xlen * ylen> sd
851 : {
852 : .1, 100, .1, .1, 100, .1, 100, 1
853 : };
854 : // clang-format on
855 :
856 : {
857 2 : Options opts = stdOptions(7, 0);
858 1 : opts.outputMode = OutputMode::Normal;
859 2 : DatasetPtr ds = runViewshed(in.data(), sd.data(), xlen, ylen, opts);
860 1 : GDALRasterBand *band = ds->GetRasterBand(1);
861 : std::array<double, xlen * ylen> out;
862 1 : CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(),
863 1 : xlen, ylen, GDT_Float64, 0, 0, nullptr);
864 :
865 1 : EXPECT_EQ(err, CE_None);
866 :
867 : // clang-format off
868 1 : std::array<double, xlen * ylen> expected
869 : {
870 : 255, 2, 2, 0, 255, 2, 255, 255
871 : };
872 : // clang-format on
873 :
874 9 : for (size_t i = 0; i < out.size(); ++i)
875 8 : EXPECT_DOUBLE_EQ(out[i], expected[i])
876 0 : << "Error at position " << i << ".";
877 : }
878 : }
879 1 : }
880 :
881 : // Test a handling of SD raster up and down.
882 4 : TEST(Viewshed, sd_up_down)
883 : {
884 : // Up.
885 : {
886 : // clang-format off
887 1 : const int xlen = 1;
888 1 : const int ylen = 8;
889 1 : std::array<double, xlen * ylen> in
890 : {
891 : 0, 1, 1, 3.1, 1.5, 2.7, 3.7, 7.5
892 : };
893 :
894 1 : std::array<double, xlen * ylen> sd
895 : {
896 : 1, 100, .1, 100, .1, .1, 100, .1
897 : };
898 : // clang-format on
899 :
900 : {
901 2 : Options opts = stdOptions(0, 0);
902 1 : opts.outputMode = OutputMode::Normal;
903 2 : DatasetPtr ds = runViewshed(in.data(), sd.data(), xlen, ylen, opts);
904 1 : GDALRasterBand *band = ds->GetRasterBand(1);
905 : std::array<double, xlen * ylen> out;
906 1 : CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(),
907 1 : xlen, ylen, GDT_Float64, 0, 0, nullptr);
908 :
909 1 : EXPECT_EQ(err, CE_None);
910 :
911 : // clang-format off
912 1 : std::array<double, xlen * ylen> expected
913 : {
914 : 255, 255, 2, 255, 0, 2, 2, 255
915 : };
916 : // clang-format on
917 :
918 9 : for (size_t i = 0; i < out.size(); ++i)
919 8 : EXPECT_DOUBLE_EQ(out[i], expected[i])
920 0 : << "Error at position " << i << ".";
921 : }
922 : }
923 : // Down.
924 : {
925 : // clang-format off
926 1 : const int xlen = 1;
927 1 : const int ylen = 8;
928 1 : std::array<double, xlen * ylen> in
929 : {
930 : 7.5, 3.7, 2.7, 1.5, 3.1, 1, 1, 0
931 : };
932 :
933 1 : std::array<double, xlen * ylen> sd
934 : {
935 : .1, 100, .1, .1, 100, .1, 100, 1
936 : };
937 : // clang-format on
938 :
939 : {
940 2 : Options opts = stdOptions(0, 7);
941 1 : opts.outputMode = OutputMode::Normal;
942 2 : DatasetPtr ds = runViewshed(in.data(), sd.data(), xlen, ylen, opts);
943 1 : GDALRasterBand *band = ds->GetRasterBand(1);
944 : std::array<double, xlen * ylen> out;
945 1 : CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(),
946 1 : xlen, ylen, GDT_Float64, 0, 0, nullptr);
947 :
948 1 : EXPECT_EQ(err, CE_None);
949 :
950 : // clang-format off
951 1 : std::array<double, xlen * ylen> expected
952 : {
953 : 255, 2, 2, 0, 255, 2, 255, 255
954 : };
955 : // clang-format on
956 :
957 9 : for (size_t i = 0; i < out.size(); ++i)
958 8 : EXPECT_DOUBLE_EQ(out[i], expected[i]);
959 : }
960 : }
961 1 : }
962 :
963 : // Test SD raster
964 4 : TEST(Viewshed, sd_2)
965 : {
966 : // Right, down
967 : {
968 : // clang-format off
969 1 : const int xlen = 8;
970 1 : const int ylen = 2;
971 1 : std::array<double, xlen * ylen> in
972 : {
973 : 0, 1, 1, 3.1, 1.5, 2.7, 3.7, 7.5, // Row 0
974 : 0, 1.1, 1.4, 3.1, 1.5, 2.7, 3.7, 7.5 // Row 1
975 : };
976 :
977 1 : std::array<double, xlen * ylen> sd
978 : {
979 : 1, 100, .1, 100, .1, .1, 100, .1, // Row 0
980 : 1, 100, .1, 100, .1, .1, 100, .1 // Row 1
981 : };
982 : // clang-format on
983 :
984 : {
985 2 : Options opts = stdOptions(0, 0);
986 1 : opts.outputMode = OutputMode::Normal;
987 2 : DatasetPtr ds = runViewshed(in.data(), sd.data(), xlen, ylen, opts);
988 1 : GDALRasterBand *band = ds->GetRasterBand(1);
989 : std::array<double, xlen * ylen> out;
990 1 : CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(),
991 1 : xlen, ylen, GDT_Float64, 0, 0, nullptr);
992 :
993 1 : EXPECT_EQ(err, CE_None);
994 :
995 : // clang-format off
996 1 : std::array<double, xlen * ylen> expected
997 : {
998 : 255, 255, 2, 255, 0, 2, 2, 255,
999 : 255, 255, 2, 2, 0, 0, 2, 255
1000 : };
1001 : // clang-format on
1002 :
1003 1 : size_t i = 0;
1004 3 : for (int y = 0; y < ylen; y++)
1005 18 : for (int x = 0; x < xlen; x++)
1006 : {
1007 16 : EXPECT_DOUBLE_EQ(out[i], expected[i])
1008 0 : << "Mismatch at (" << x << ", " << y << ")";
1009 16 : i++;
1010 : }
1011 : }
1012 : }
1013 : // Right, up
1014 : {
1015 : // clang-format off
1016 1 : const int xlen = 8;
1017 1 : const int ylen = 2;
1018 1 : std::array<double, xlen * ylen> in
1019 : {
1020 : 0, 1.1, 1.4, 3.1, 1.5, 2.7, 3.7, 7.5, // Row 0
1021 : 0, 1, 1, 3.1, 1.5, 2.7, 3.7, 7.5 // Row 1
1022 : };
1023 :
1024 1 : std::array<double, xlen * ylen> sd
1025 : {
1026 : 1, 100, .1, 100, .1, .1, 100, .1, // Row 0
1027 : 1, 100, .1, 100, .1, .1, 100, .1 // Row 1
1028 : };
1029 : // clang-format on
1030 :
1031 : {
1032 2 : Options opts = stdOptions(0, 1);
1033 1 : opts.outputMode = OutputMode::Normal;
1034 2 : DatasetPtr ds = runViewshed(in.data(), sd.data(), xlen, ylen, opts);
1035 1 : GDALRasterBand *band = ds->GetRasterBand(1);
1036 : std::array<double, xlen * ylen> out;
1037 1 : CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(),
1038 1 : xlen, ylen, GDT_Float64, 0, 0, nullptr);
1039 :
1040 1 : EXPECT_EQ(err, CE_None);
1041 :
1042 : // clang-format off
1043 1 : std::array<double, xlen * ylen> expected
1044 : {
1045 : 255, 255, 2, 2, 0, 0, 2, 255,
1046 : 255, 255, 2, 255, 0, 2, 2, 255
1047 : };
1048 : // clang-format on
1049 :
1050 1 : size_t i = 0;
1051 3 : for (int y = 0; y < ylen; y++)
1052 18 : for (int x = 0; x < xlen; x++)
1053 : {
1054 16 : EXPECT_DOUBLE_EQ(out[i], expected[i])
1055 0 : << "Mismatch at (" << x << ", " << y << ")";
1056 16 : i++;
1057 : }
1058 : }
1059 : }
1060 :
1061 : // Left, down
1062 : {
1063 : // clang-format off
1064 1 : const int xlen = 8;
1065 1 : const int ylen = 2;
1066 1 : std::array<double, xlen * ylen> in
1067 : {
1068 : 7.5, 3.7, 2.7, 1.5, 3.1, 1, 1, 0, // Row 0
1069 : 7.5, 3.7, 2.7, 1.5, 3.1, 1.4, 1.1, 0 // Row 1
1070 : };
1071 :
1072 1 : std::array<double, xlen * ylen> sd
1073 : {
1074 : .1, 100, .1, .1, 100, .1, 100, 1, // Row 0
1075 : .1, 100, .1, .1, 100, .1, 100, 1 // Row 1
1076 : };
1077 : // clang-format on
1078 :
1079 : {
1080 2 : Options opts = stdOptions(7, 0);
1081 1 : opts.outputMode = OutputMode::Normal;
1082 2 : DatasetPtr ds = runViewshed(in.data(), sd.data(), xlen, ylen, opts);
1083 1 : GDALRasterBand *band = ds->GetRasterBand(1);
1084 : std::array<double, xlen * ylen> out;
1085 1 : CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(),
1086 1 : xlen, ylen, GDT_Float64, 0, 0, nullptr);
1087 :
1088 1 : EXPECT_EQ(err, CE_None);
1089 :
1090 : // clang-format off
1091 1 : std::array<double, xlen * ylen> expected
1092 : {
1093 : 255, 2, 2, 0, 255, 2, 255, 255, // Row 0
1094 : 255, 2, 0, 0, 2, 2, 255, 255 // Row 1
1095 : };
1096 : // clang-format on
1097 :
1098 1 : size_t i = 0;
1099 3 : for (int y = 0; y < ylen; y++)
1100 18 : for (int x = 0; x < xlen; x++)
1101 : {
1102 16 : EXPECT_DOUBLE_EQ(out[i], expected[i])
1103 0 : << "Mismatch at (" << x << ", " << y << ")";
1104 16 : i++;
1105 : }
1106 : }
1107 : }
1108 :
1109 : // Left, up
1110 : {
1111 : // clang-format off
1112 1 : const int xlen = 8;
1113 1 : const int ylen = 2;
1114 1 : std::array<double, xlen * ylen> in
1115 : {
1116 : 7.5, 3.7, 2.7, 1.5, 3.1, 1.4, 1.1, 0, // Row 0
1117 : 7.5, 3.7, 2.7, 1.5, 3.1, 1, 1, 0 // Row 1
1118 : };
1119 :
1120 1 : std::array<double, xlen * ylen> sd
1121 : {
1122 : .1, 100, .1, .1, 100, .1, 100, 1, // Row 0
1123 : .1, 100, .1, .1, 100, .1, 100, 1 // Row 1
1124 : };
1125 : // clang-format on
1126 :
1127 : {
1128 2 : Options opts = stdOptions(7, 1);
1129 1 : opts.outputMode = OutputMode::Normal;
1130 2 : DatasetPtr ds = runViewshed(in.data(), sd.data(), xlen, ylen, opts);
1131 1 : GDALRasterBand *band = ds->GetRasterBand(1);
1132 : std::array<double, xlen * ylen> out;
1133 1 : CPLErr err = band->RasterIO(GF_Read, 0, 0, xlen, ylen, out.data(),
1134 1 : xlen, ylen, GDT_Float64, 0, 0, nullptr);
1135 :
1136 1 : EXPECT_EQ(err, CE_None);
1137 :
1138 : // clang-format off
1139 1 : std::array<double, xlen * ylen> expected
1140 : {
1141 : 255, 2, 0, 0, 2, 2, 255, 255, // Row 0
1142 : 255, 2, 2, 0, 255, 2, 255, 255 // Row 1
1143 : };
1144 : // clang-format on
1145 :
1146 1 : size_t i = 0;
1147 3 : for (int y = 0; y < ylen; y++)
1148 18 : for (int x = 0; x < xlen; x++)
1149 : {
1150 16 : EXPECT_DOUBLE_EQ(out[i], expected[i])
1151 0 : << "Mismatch at (" << x << ", " << y << ")";
1152 16 : i++;
1153 : }
1154 : }
1155 : }
1156 1 : }
1157 :
1158 : } // namespace viewshed
1159 : } // namespace gdal
|