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