1 /******************************************************************************
2 * Copyright (c) 2011, Michael P. Gerlek (mpg@flaxen.com)
3 *
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following
8 * conditions are met:
9 *
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in
14 * the documentation and/or other materials provided
15 * with the distribution.
16 * * Neither the name of Hobu, Inc. or Flaxen Geo Consulting nor the
17 * names of its contributors may be used to endorse or promote
18 * products derived from this software without specific prior
19 * written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
28 * OF USE, view, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
32 * OF SUCH DAMAGE.
33 ****************************************************************************/
34
35 #include <pdal/pdal_test_main.hpp>
36
37 #include <array>
38 #include <random>
39
40 #include <pdal/PointView.hpp>
41 #include <pdal/PDALUtils.hpp>
42
43 #include "Support.hpp"
44
45 using namespace pdal;
46
makeTestView(PointTableRef table,point_count_t cnt=17)47 PointViewPtr makeTestView(PointTableRef table, point_count_t cnt = 17)
48 {
49 PointLayoutPtr layout(table.layout());
50
51 layout->registerDim(Dimension::Id::Classification);
52 layout->registerDim(Dimension::Id::X);
53 layout->registerDim(Dimension::Id::Y);
54
55 PointViewPtr view(new PointView(table));
56
57 // write the data into the view
58 for (PointId i = 0; i < cnt; i++)
59 {
60 const uint8_t x = static_cast<uint8_t>(i + 1);
61 const int32_t y = static_cast<int32_t>(i * 10);
62 const double z = static_cast<double>(i * 100);
63
64 view->setField(Dimension::Id::Classification, i, x);
65 view->setField(Dimension::Id::X, i, y);
66 view->setField(Dimension::Id::Y, i, z);
67 }
68 EXPECT_EQ(view->size(), cnt);
69 return view;
70 }
71
72
verifyTestView(const PointView & view,point_count_t cnt=17)73 void verifyTestView(const PointView& view, point_count_t cnt = 17)
74 {
75 // read the view back out
76 for (PointId i = 0; i < cnt; i++)
77 {
78 uint8_t x = view.getFieldAs<uint8_t>(
79 Dimension::Id::Classification, i);
80 int32_t y = view.getFieldAs<uint32_t>(Dimension::Id::X, i);
81 double z = view.getFieldAs<double>(Dimension::Id::Y, i);
82
83 EXPECT_EQ(x, (uint8_t)(i + 1));
84 EXPECT_EQ(y, (int32_t)(i * 10));
85 EXPECT_TRUE(Utils::compare_approx(z, static_cast<double>(i) * 100.0,
86 (std::numeric_limits<double>::min)()));
87 }
88 }
89
TEST(PointViewTest,getSet)90 TEST(PointViewTest, getSet)
91 {
92 PointTable table;
93 PointViewPtr view = makeTestView(table, 1);
94 verifyTestView(*view.get(), 1);
95 }
96
TEST(PointViewTest,getAsUint8)97 TEST(PointViewTest, getAsUint8)
98 {
99 PointTable table;
100 PointViewPtr view = makeTestView(table);
101
102 // read the view back out
103 for (int i = 0; i < 3; i++)
104 {
105 uint8_t x = view->getFieldAs<uint8_t>(Dimension::Id::Classification, i);
106 uint8_t y = view->getFieldAs<uint8_t>(Dimension::Id::X, i);
107 uint8_t z = view->getFieldAs<uint8_t>(Dimension::Id::Y, i);
108
109 EXPECT_EQ(x, i + 1u);
110 EXPECT_EQ(y, i * 10u);
111 EXPECT_EQ(z, i * 100u);
112 }
113
114 // read the view back out
115 for (int i = 3; i < 17; i++)
116 {
117 uint8_t x = view->getFieldAs<uint8_t>(Dimension::Id::Classification, i);
118 uint8_t y = view->getFieldAs<uint8_t>(Dimension::Id::X, i);
119 EXPECT_THROW(view->getFieldAs<uint8_t>(Dimension::Id::Y, i),
120 pdal_error);
121 EXPECT_EQ(x, i + 1u);
122 EXPECT_EQ(y, i * 10u);
123 }
124 }
125
TEST(PointViewTest,getAsInt32)126 TEST(PointViewTest, getAsInt32)
127 {
128 PointTable table;
129 PointViewPtr view = makeTestView(table);
130
131 // read the view back out
132 for (int i = 0; i < 17; i++)
133 {
134 int32_t x = view->getFieldAs<int32_t>(Dimension::Id::Classification, i);
135 int32_t y = view->getFieldAs<int32_t>(Dimension::Id::X, i);
136 int32_t z = view->getFieldAs<int32_t>(Dimension::Id::Y, i);
137
138 EXPECT_EQ(x, i + 1);
139 EXPECT_EQ(y, i * 10);
140 EXPECT_EQ(z, i * 100);
141 }
142 }
143
144
TEST(PointViewTest,getFloat)145 TEST(PointViewTest, getFloat)
146 {
147 PointTable table;
148 PointViewPtr view = makeTestView(table);
149
150 // read the view back out
151 for (int i = 0; i < 17; i++)
152 {
153 float x = view->getFieldAs<float>(Dimension::Id::Classification, i);
154 float y = view->getFieldAs<float>(Dimension::Id::X, i);
155 float z = view->getFieldAs<float>(Dimension::Id::Y, i);
156
157 EXPECT_FLOAT_EQ(x, i + 1.0f);
158 EXPECT_FLOAT_EQ(y, i * 10.0f);
159 EXPECT_FLOAT_EQ(z, i * 100.0f);
160 }
161 }
162
163
TEST(PointViewTest,bigfile)164 TEST(PointViewTest, bigfile)
165 {
166 PointTable table;
167
168 point_count_t NUM_PTS = 1000000;
169
170 PointLayoutPtr layout(table.layout());
171
172 layout->registerDim(Dimension::Id::X);
173 layout->registerDim(Dimension::Id::Y);
174 layout->registerDim(Dimension::Id::Z);
175
176 PointView view(table);
177
178 for (PointId id = 0; id < NUM_PTS; ++id)
179 {
180 view.setField(Dimension::Id::X, id, id);
181 view.setField(Dimension::Id::Y, id, 2 * id);
182 view.setField(Dimension::Id::Z, id, -(int)id);
183 }
184
185 for (PointId id = 0; id < NUM_PTS; ++id)
186 {
187 EXPECT_EQ(
188 view.getFieldAs<PointId>(Dimension::Id::X, id), id);
189 EXPECT_EQ(
190 view.getFieldAs<PointId>(Dimension::Id::Y, id), id * 2);
191 EXPECT_EQ(
192 view.getFieldAs<int>(Dimension::Id::Z, id), -(int)id);
193 }
194
195 // Test some random access.
196 std::unique_ptr<PointId[]> ids(new PointId[NUM_PTS]);
197 for (PointId idx = 0; idx < NUM_PTS; ++idx)
198 ids[idx] = idx;
199 // Do a bunch of random swaps.
200 std::default_random_engine generator;
201 std::uniform_int_distribution<PointId> distribution(0, NUM_PTS-1);
202 for (PointId idx = 0; idx < NUM_PTS; ++idx)
203 {
204 PointId y = distribution(generator);
205 PointId temp = std::move(ids[idx]);
206 ids[idx] = std::move(ids[y]);
207 ids[y] = std::move(temp);
208 }
209
210 for (PointId idx = 0; idx < NUM_PTS; ++idx)
211 {
212 PointId id = ids[idx];
213 view.setField(Dimension::Id::X, id, idx);
214 view.setField(Dimension::Id::Y, id, 2 * idx);
215 view.setField(Dimension::Id::Z, id, -(int)idx);
216 }
217
218 for (PointId idx = 0; idx < NUM_PTS; ++idx)
219 {
220 PointId id = ids[idx];
221 EXPECT_EQ(
222 view.getFieldAs<PointId>(Dimension::Id::X, id), idx);
223 EXPECT_EQ(
224 view.getFieldAs<PointId>(Dimension::Id::Y, id), idx * 2);
225 EXPECT_EQ(
226 view.getFieldAs<int>(Dimension::Id::Z, id), -(int)idx);
227 }
228 }
229
TEST(PointViewTest,order)230 TEST(PointViewTest, order)
231 {
232 PointTable table;
233
234 const size_t COUNT(1000);
235 std::array<PointViewPtr, COUNT> views;
236
237 std::random_device dev;
238 std::mt19937 generator(dev());
239
240 for (size_t i = 0; i < COUNT; ++i)
241 views[i] = PointViewPtr(new PointView(table));
242 std::shuffle(views.begin(), views.end(), generator);
243
244 PointViewSet set;
245 for (size_t i = 0; i < COUNT; ++i)
246 set.insert(views[i]);
247
248 PointViewSet::iterator pi;
249 for (auto si = set.begin(); si != set.end(); ++si)
250 {
251 if (si != set.begin())
252 EXPECT_TRUE((*pi)->id() < (*si)->id());
253 pi = si;
254 }
255 }
256
TEST(PointViewTest,issue1264)257 TEST(PointViewTest, issue1264)
258 {
259 PointTable t;
260 PointLayoutPtr layout(t.layout());
261 Dimension::Id foo = layout->assignDim("foo", Dimension::Type::Unsigned8);
262 Dimension::Id bar = layout->assignDim("bar", Dimension::Type::Signed8);
263 layout->finalize();
264
265 PointView v(t);
266 double d(250.0);
267 v.setField(foo, 0, d);
268 d = v.getFieldAs<double>(foo, 0);
269 EXPECT_DOUBLE_EQ(d, 250.0);
270 d = 123.0;
271 v.setField(bar, 0, d);
272 d = v.getFieldAs<double>(bar, 0);
273 EXPECT_DOUBLE_EQ(d, 123.0);
274 d = -120.23456;
275 v.setField(bar, 0, d);
276 d = v.getFieldAs<double>(bar, 0);
277 EXPECT_DOUBLE_EQ(d, -120.0);
278 d = 260.0;
279 EXPECT_THROW(v.setField(foo, 0, d), pdal_error);
280 }
281
TEST(PointViewTest,getFloatNan)282 TEST(PointViewTest, getFloatNan)
283 {
284 PointTable table;
285 PointLayoutPtr layout(table.layout());
286 layout->registerDim(Dimension::Id::ScanAngleRank, Dimension::Type::Float);
287 PointViewPtr view(new PointView(table));
288 const float scanAngleRank = std::numeric_limits<float>::quiet_NaN();
289 view->setField(Dimension::Id::ScanAngleRank, 0, scanAngleRank);
290 EXPECT_NO_THROW(view->getFieldAs<float>(Dimension::Id::ScanAngleRank, 0));
291 }
292
293 // Per discussions with @abellgithub (https://github.com/gadomski/PDAL/commit/c1d54e56e2de841d37f2a1b1c218ed723053f6a9#commitcomment-14415138)
294 // we only do bounds checking on `PointView`s when in debug mode.
295 #ifndef NDEBUG
TEST(PointViewDeathTest,out_of_bounds)296 TEST(PointViewDeathTest, out_of_bounds)
297 {
298 PointTable point_table;
299 auto point_view = makeTestView(point_table, 1);
300 EXPECT_DEATH(point_view->getFieldAs<uint8_t>(Dimension::Id::X, 1), "< m_size");
301 }
302 #endif
303