1 /******************************************************************************
2 * Copyright (c) 2014, Hobu Inc.
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, DATA, 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 #pragma once
36 
37 #include <pdal/DimDetail.hpp>
38 #include <pdal/DimType.hpp>
39 #include <pdal/Mesh.hpp>
40 #include <pdal/PointContainer.hpp>
41 #include <pdal/PointLayout.hpp>
42 #include <pdal/PointTable.hpp>
43 #include <pdal/PointRef.hpp>
44 
45 #include <memory>
46 #include <queue>
47 #include <set>
48 #include <deque>
49 
50 //#pragma warning(disable: 4244)  // conversion from 'type1' to 'type2', possible loss of data
51 
52 namespace pdal
53 {
54 namespace plang
55 {
56     class Invocation;
57 }
58 
59 struct PointViewLess;
60 class PointView;
61 class PointViewIter;
62 class KD2Index;
63 class KD3Index;
64 class BOX2D;
65 class BOX3D;
66 
67 struct RasterLimits;
68 template <class T> class Raster;
69 using Rasterd = Raster<double>;
70 
71 typedef std::shared_ptr<PointView> PointViewPtr;
72 typedef std::set<PointViewPtr, PointViewLess> PointViewSet;
73 
74 class PDAL_DLL PointView : public PointContainer
75 {
76     FRIEND_TEST(VoxelTest, center);
77     friend class Stage;
78     friend class plang::Invocation;
79     friend class PointIdxRef;
80     friend struct PointViewLess;
81 public:
82     PointView(const PointView&) = delete;
83     PointView& operator=(const PointView&) = delete;
84     PointView(PointTableRef pointTable);
85     PointView(PointTableRef pointTable, const SpatialReference& srs);
86     virtual ~PointView();
87 
88     PointViewIter begin();
89     PointViewIter end();
90 
id() const91     int id() const
92         { return m_id; }
93 
size() const94     point_count_t size() const
95         { return m_size; }
96 
empty() const97     bool empty() const
98         { return m_size == 0; }
99 
100     inline void appendPoint(const PointView& buffer, PointId id);
append(const PointView & buf)101     void append(const PointView& buf)
102     {
103         // We use size() instead of the index end because temp points
104         // might have been placed at the end of the buffer.
105         // We're essentially ditching temp points.
106         auto thisEnd = m_index.begin() + size();
107         auto bufEnd = buf.m_index.begin() + buf.size();
108         m_index.insert(thisEnd, buf.m_index.begin(), bufEnd);
109         m_size += buf.size();
110         clearTemps();
111     }
112 
113     /// Return a new point view with the same point table as this
114     /// point buffer.
makeNew() const115     PointViewPtr makeNew() const
116     {
117         return PointViewPtr(new PointView(m_pointTable, m_spatialReference));
118     }
119 
point(PointId id)120     PointRef point(PointId id)
121         { return PointRef(*this, id); }
122 
123     template<class T>
124     T getFieldAs(Dimension::Id dim, PointId pointIndex) const;
125 
126     inline void getField(char *pos, Dimension::Id d,
127         Dimension::Type type, PointId id) const;
128 
129     template<typename T>
130     void setField(Dimension::Id dim, PointId idx, T val);
131 
132     inline void setField(Dimension::Id dim, Dimension::Type type,
133         PointId idx, const void *val);
134 
135     template <typename T>
compare(Dimension::Id dim,PointId id1,PointId id2) const136     bool compare(Dimension::Id dim, PointId id1, PointId id2) const
137     {
138         return (getFieldInternal<T>(dim, id1) < getFieldInternal<T>(dim, id2));
139     }
140 
compare(Dimension::Id dim,PointId id1,PointId id2) const141     virtual bool compare(Dimension::Id dim, PointId id1, PointId id2) const
142     {
143         const Dimension::Detail *dd = layout()->dimDetail(dim);
144 
145         switch (dd->type())
146         {
147             case Dimension::Type::Float:
148                 return compare<float>(dim, id1, id2);
149                 break;
150             case Dimension::Type::Double:
151                 return compare<double>(dim, id1, id2);
152                 break;
153             case Dimension::Type::Signed8:
154                 return compare<int8_t>(dim, id1, id2);
155                 break;
156             case Dimension::Type::Signed16:
157                 return compare<int16_t>(dim, id1, id2);
158                 break;
159             case Dimension::Type::Signed32:
160                 return compare<int32_t>(dim, id1, id2);
161                 break;
162             case Dimension::Type::Signed64:
163                 return compare<int64_t>(dim, id1, id2);
164                 break;
165             case Dimension::Type::Unsigned8:
166                 return compare<uint8_t>(dim, id1, id2);
167                 break;
168             case Dimension::Type::Unsigned16:
169                 return compare<uint16_t>(dim, id1, id2);
170                 break;
171             case Dimension::Type::Unsigned32:
172                 return compare<uint32_t>(dim, id1, id2);
173                 break;
174             case Dimension::Type::Unsigned64:
175                 return compare<uint64_t>(dim, id1, id2);
176                 break;
177             case Dimension::Type::None:
178             default:
179                 return false;
180                 break;
181         }
182     }
183 
184     /*! @return a cumulated bounds of all points in the PointView.
185         \verbatim embed:rst
186         .. note::
187 
188             This method requires that an `X`, `Y`, and `Z` dimension be
189             available, and that it can be casted into a *double* data
190             type using the :cpp:func:`pdal::Dimension::applyScaling`
191             method. Otherwise, an exception will be thrown.
192         \endverbatim
193     */
194     void calculateBounds(BOX2D& box) const;
195     void calculateBounds(BOX3D& box) const;
196 
197     void dump(std::ostream& ostr) const;
hasDim(Dimension::Id id) const198     bool hasDim(Dimension::Id id) const
199         { return layout()->hasDim(id); }
dimName(Dimension::Id id) const200     std::string dimName(Dimension::Id id) const
201         { return layout()->dimName(id); }
dims() const202     Dimension::IdList dims() const
203         { return layout()->dims(); }
pointSize() const204     std::size_t pointSize() const
205         { return layout()->pointSize(); }
dimSize(Dimension::Id id) const206     std::size_t dimSize(Dimension::Id id) const
207         { return layout()->dimSize(id); }
dimType(Dimension::Id id) const208     Dimension::Type dimType(Dimension::Id id) const
209          { return layout()->dimType(id);}
dimTypes() const210     DimTypeList dimTypes() const
211         { return layout()->dimTypes(); }
layout() const212     PointLayoutPtr layout() const
213         { return m_layout; }
table() const214     inline PointTableRef table() const
215         { return m_pointTable;}
spatialReference() const216     SpatialReference spatialReference() const
217         { return m_spatialReference; }
218 
219     /// Fill a buffer with point data specified by the dimension list.
220     /// \param[in] dims  List of dimensions/types to retrieve.
221     /// \param[in] idx   Index of point to get.
222     /// \param[in] buf   Pointer to buffer to fill.
getPackedPoint(const DimTypeList & dims,PointId idx,char * buf) const223     void getPackedPoint(const DimTypeList& dims, PointId idx, char *buf) const
224     {
225         for (auto di = dims.begin(); di != dims.end(); ++di)
226         {
227             getField(buf, di->m_id, di->m_type, idx);
228             buf += Dimension::size(di->m_type);
229         }
230     }
231 
232     /// Load the point buffer from memory whose arrangement is specified
233     /// by the dimension list.
234     /// \param[in] dims  Dimension/types of data in packed order
235     /// \param[in] idx   Index of point to write.
236     /// \param[in] buf   Packed data buffer.
setPackedPoint(const DimTypeList & dims,PointId idx,const char * buf)237     void setPackedPoint(const DimTypeList& dims, PointId idx, const char *buf)
238     {
239         for (auto di = dims.begin(); di != dims.end(); ++di)
240         {
241             setField(di->m_id, di->m_type, idx, (const void *)buf);
242             buf += Dimension::size(di->m_type);
243         }
244     }
245 
246     /// Provides access to the memory storing the point data.  Though this
247     /// function is public, other access methods are safer and preferred.
getPoint(PointId id)248     char *getPoint(PointId id)
249         { return m_pointTable.getPoint(m_index[id]); }
250 
251     /// Provides access to the memory storing the point data.  Though this
252     /// function is public, other access methods are safer and preferred.
getOrAddPoint(PointId id)253     char *getOrAddPoint(PointId id)
254     {
255         if (id == size())
256         {
257             m_index.push_back(m_pointTable.addPoint());
258             ++m_size;
259             assert(m_temps.empty());
260         }
261 
262         return m_pointTable.getPoint(m_index.at(id));
263     }
264 
265     // The standard idiom is swapping with a stack-created empty queue, but
266     // that invokes the ctor and probably allocates.  We've probably only got
267     // one or two things in our queue, so just pop until we're empty.
clearTemps()268     void clearTemps()
269     {
270         while (!m_temps.empty())
271             m_temps.pop();
272     }
273     MetadataNode toMetadata() const;
274 
275     void invalidateProducts();
276 
277     /**
278       Creates a mesh with the specified name.
279 
280       \param name  Name of the mesh.
281       \return  Pointer to the new mesh.  Null is returned if the mesh
282           already exists.
283     */
284     TriangularMesh *createMesh(const std::string& name);
285 
286     /**
287       Get a pointer to a mesh.
288 
289       \param name  Name of the mesh.
290       \return  New mesh.  Null is returned if the mesh already exists.
291     */
292     TriangularMesh *mesh(const std::string& name = "");
293 
294     /**
295       Creates a raster with the specified name.
296 
297       \param name  Name of the raster.
298       \param limits  Limits of the raster to create.
299       \return  Pointer to the new raster.  Null is returned if the raster already exists.
300     */
301     Rasterd *createRaster(const std::string& name, const RasterLimits& limits,
302         double noData = 0);
303 
304     /**
305       Get a pointer to a raster.
306 
307       \param name  Name of the raster.
308       \return  Pointer to the New raster. Null
309     */
310     Rasterd *raster(const std::string& name = "");
311 
312     KD3Index& build3dIndex();
313     KD2Index& build2dIndex();
314 
315 protected:
316     PointTableRef m_pointTable;
317     PointLayoutPtr m_layout;
318     std::deque<PointId> m_index;
319     // The index might be larger than the size to support temporary point
320     // references.
321     point_count_t m_size;
322     int m_id;
323     std::queue<PointId> m_temps;
324     SpatialReference m_spatialReference;
325     std::map<std::string, std::unique_ptr<TriangularMesh>> m_meshes;
326     std::map<std::string, std::unique_ptr<Rasterd>> m_rasters;
327     std::unique_ptr<KD3Index> m_index3;
328     std::unique_ptr<KD2Index> m_index2;
329 
330 private:
331     static int m_lastId;
332 
333     PointId tableId(PointId idx);
334 
335     virtual void setFieldInternal(Dimension::Id dim, PointId idx,
336         const void *buf);
getFieldInternal(Dimension::Id dim,PointId idx,void * buf) const337     virtual void getFieldInternal(Dimension::Id dim, PointId idx,
338             void *buf) const
339         { m_pointTable.getFieldInternal(dim, m_index[idx], buf); }
swapItems(PointId id1,PointId id2)340     virtual void swapItems(PointId id1, PointId id2)
341     {
342         PointId temp = m_index[id2];
343         m_index[id2] = m_index[id1];
344         m_index[id1] = temp;
345     }
setItem(PointId dst,PointId src)346     virtual void setItem(PointId dst, PointId src)
347     {
348         m_index[dst] = m_index[src];
349     }
350 
351     template<class T>
352     T getFieldInternal(Dimension::Id dim, PointId pointIndex) const;
353     inline PointId getTemp(PointId id);
freeTemp(PointId id)354     void freeTemp(PointId id)
355         { m_temps.push(id); }
setSpatialReference(const SpatialReference & spatialRef)356     void setSpatialReference(const SpatialReference& spatialRef)
357         { m_spatialReference = spatialRef; }
358 
359     // For testing only.
index(PointId id) const360     PointId index(PointId id) const
361         { return m_index[id]; }
362 };
363 
364 struct PointViewLess
365 {
operator ()pdal::PointViewLess366     bool operator () (const PointViewPtr& p1, const PointViewPtr& p2) const
367         { return p1->m_id < p2->m_id; }
368 };
369 
370 template <class T>
getFieldInternal(Dimension::Id dim,PointId id) const371 T PointView::getFieldInternal(Dimension::Id dim, PointId id) const
372 {
373     T t;
374 
375     getFieldInternal(dim, id, &t);
376     return t;
377 }
378 
getField(char * pos,Dimension::Id d,Dimension::Type type,PointId id) const379 inline void PointView::getField(char *pos, Dimension::Id d,
380     Dimension::Type type, PointId id) const
381 {
382     Everything e;
383 
384     switch (type)
385     {
386     case Dimension::Type::Float:
387         e.f = getFieldAs<float>(d, id);
388         break;
389     case Dimension::Type::Double:
390         e.d = getFieldAs<double>(d, id);
391         break;
392     case Dimension::Type::Signed8:
393         e.s8 = getFieldAs<int8_t>(d, id);
394         break;
395     case Dimension::Type::Signed16:
396         e.s16 = getFieldAs<int16_t>(d, id);
397         break;
398     case Dimension::Type::Signed32:
399         e.s32 = getFieldAs<int32_t>(d, id);
400         break;
401     case Dimension::Type::Signed64:
402         e.s64 = getFieldAs<int64_t>(d, id);
403         break;
404     case Dimension::Type::Unsigned8:
405         e.u8 = getFieldAs<uint8_t>(d, id);
406         break;
407     case Dimension::Type::Unsigned16:
408         e.u16 = getFieldAs<uint16_t>(d, id);
409         break;
410     case Dimension::Type::Unsigned32:
411         e.u32 = getFieldAs<uint32_t>(d, id);
412         break;
413     case Dimension::Type::Unsigned64:
414         e.u64 = getFieldAs<uint64_t>(d, id);
415         break;
416     case Dimension::Type::None:
417         break;
418     }
419     memcpy(pos, &e, Dimension::size(type));
420 }
421 
setField(Dimension::Id dim,Dimension::Type type,PointId idx,const void * val)422 inline void PointView::setField(Dimension::Id dim,
423     Dimension::Type type, PointId idx, const void *val)
424 {
425     Everything e;
426 
427     memcpy(&e, val, Dimension::size(type));
428     switch (type)
429     {
430         case Dimension::Type::Float:
431             setField(dim, idx, e.f);
432             break;
433         case Dimension::Type::Double:
434             setField(dim, idx, e.d);
435             break;
436         case Dimension::Type::Signed8:
437             setField(dim, idx, e.s8);
438             break;
439         case Dimension::Type::Signed16:
440             setField(dim, idx, e.s16);
441             break;
442         case Dimension::Type::Signed32:
443             setField(dim, idx, e.s32);
444             break;
445         case Dimension::Type::Signed64:
446             setField(dim, idx, e.s64);
447             break;
448         case Dimension::Type::Unsigned8:
449             setField(dim, idx, e.u8);
450             break;
451         case Dimension::Type::Unsigned16:
452             setField(dim, idx, e.u16);
453             break;
454         case Dimension::Type::Unsigned32:
455             setField(dim, idx, e.u32);
456             break;
457         case Dimension::Type::Unsigned64:
458             setField(dim, idx, e.u64);
459             break;
460         case Dimension::Type::None:
461             break;
462     }
463 }
464 
465 template <class T>
getFieldAs(Dimension::Id dim,PointId pointIndex) const466 inline T PointView::getFieldAs(Dimension::Id dim,
467     PointId pointIndex) const
468 {
469     assert(pointIndex < m_size);
470     T retval;
471     bool ok = false;
472     const Dimension::Detail *dd = m_layout->dimDetail(dim);
473     Everything e;
474 
475     PointId rawIdx = m_index[pointIndex];
476     // Note that getFieldInternal() can't be hoisted out of the switch
477     // because we don't want to call it in the case where the dimension
478     // type isn't known.  A separate test could be made, but that *might*
479     // cost and this is an important code path.
480     switch (dd->type())
481     {
482     case Dimension::Type::Float:
483         m_pointTable.getFieldInternal(dim, rawIdx, &e);
484         ok = Utils::numericCast(e.f, retval);
485         break;
486     case Dimension::Type::Double:
487         m_pointTable.getFieldInternal(dim, rawIdx, &e);
488         ok = Utils::numericCast(e.d, retval);
489         break;
490     case Dimension::Type::Signed8:
491         m_pointTable.getFieldInternal(dim, rawIdx, &e);
492         ok = Utils::numericCast(e.s8, retval);
493         break;
494     case Dimension::Type::Signed16:
495         m_pointTable.getFieldInternal(dim, rawIdx, &e);
496         ok = Utils::numericCast(e.s16, retval);
497         break;
498     case Dimension::Type::Signed32:
499         m_pointTable.getFieldInternal(dim, rawIdx, &e);
500         ok = Utils::numericCast(e.s32, retval);
501         break;
502     case Dimension::Type::Signed64:
503         m_pointTable.getFieldInternal(dim, rawIdx, &e);
504         ok = Utils::numericCast(e.s64, retval);
505         break;
506     case Dimension::Type::Unsigned8:
507         m_pointTable.getFieldInternal(dim, rawIdx, &e);
508         ok = Utils::numericCast(e.u8, retval);
509         break;
510     case Dimension::Type::Unsigned16:
511         m_pointTable.getFieldInternal(dim, rawIdx, &e);
512         ok = Utils::numericCast(e.u16, retval);
513         break;
514     case Dimension::Type::Unsigned32:
515         m_pointTable.getFieldInternal(dim, rawIdx, &e);
516         ok = Utils::numericCast(e.u32, retval);
517         break;
518     case Dimension::Type::Unsigned64:
519         m_pointTable.getFieldInternal(dim, rawIdx, &e);
520         ok = Utils::numericCast(e.u64, retval);
521         break;
522     case Dimension::Type::None:
523     default:
524         ok = true;
525         retval = 0;
526         break;
527     } // switch
528 
529     if (!ok)
530     {
531         std::ostringstream oss;
532         oss << "Unable to fetch data and convert as requested: ";
533         oss << Dimension::name(dim) << ":" <<
534             Dimension::interpretationName(dd->type()) <<
535             "(" << Utils::toDouble(e, dd->type()) << ") -> " <<
536             Utils::typeidName<T>();
537         throw pdal_error(oss.str());
538     }
539 
540     return retval;
541 }
542 
543 
544 template<typename T>
setField(Dimension::Id dim,PointId idx,T val)545 void PointView::setField(Dimension::Id dim, PointId idx, T val)
546 {
547     const Dimension::Detail *dd = layout()->dimDetail(dim);
548 
549     Everything e;
550     bool ok = true;
551     switch (dd->type())
552     {
553     case Dimension::Type::Float:
554         ok = Utils::numericCast(val, e.f);
555         break;
556     case Dimension::Type::Double:
557         ok = Utils::numericCast(val, e.d);
558         break;
559     case Dimension::Type::Signed8:
560         ok = Utils::numericCast(val, e.s8);
561         break;
562     case Dimension::Type::Signed16:
563         ok = Utils::numericCast(val, e.s16);
564         break;
565     case Dimension::Type::Signed32:
566         ok = Utils::numericCast(val, e.s32);
567         break;
568     case Dimension::Type::Signed64:
569         ok = Utils::numericCast(val, e.s64);
570         break;
571     case Dimension::Type::Unsigned8:
572         ok = Utils::numericCast(val, e.u8);
573         break;
574     case Dimension::Type::Unsigned16:
575         ok = Utils::numericCast(val, e.u16);
576         break;
577     case Dimension::Type::Unsigned32:
578         ok = Utils::numericCast(val, e.u32);
579         break;
580     case Dimension::Type::Unsigned64:
581         ok = Utils::numericCast(val, e.u64);
582         break;
583     case Dimension::Type::None:
584         return;
585     }
586     if (ok)
587         m_pointTable.setFieldInternal(dim, tableId(idx), &e);
588     else
589     {
590         std::ostringstream oss;
591         oss << "Unable to set data and convert as requested: ";
592         oss << Dimension::name(dim) << ":" << Utils::typeidName<T>() <<
593             "(" << (double)val << ") -> " <<
594             Dimension::interpretationName(dd->type());
595         throw pdal_error(oss.str());
596     }
597 }
598 
appendPoint(const PointView & buffer,PointId id)599 inline void PointView::appendPoint(const PointView& buffer, PointId id)
600 {
601     // Invalid 'id' is a programmer error.
602     PointId rawId = buffer.m_index[id];
603     m_index.push_back(rawId);
604     m_size++;
605     assert(m_temps.empty());
606 }
607 
608 
609 // Make a temporary copy of a point by adding an entry to the index.
getTemp(PointId id)610 inline PointId PointView::getTemp(PointId id)
611 {
612     PointId newid;
613     if (m_temps.size())
614     {
615         newid = m_temps.front();
616         m_temps.pop();
617         m_index[newid] = m_index[id];
618     }
619     else
620     {
621         newid = (PointId)m_index.size();
622         m_index.push_back(m_index[id]);
623     }
624     return newid;
625 }
626 
627 PDAL_DLL std::ostream& operator<<(std::ostream& ostr, const PointView&);
628 
629 // PointViewIter
630 
631 class PointViewIter
632 {
633 private:
634     PointView *m_view;
635     PointId m_id;
636 
637 public:
638     using iterator_category = std::random_access_iterator_tag;
639     using value_type = PointRef;
640     using difference_type = ptrdiff_t;
641     using pointer = PointRef*;
642     using reference = PointRef;
643 
PointViewIter()644     PointViewIter()
645     {}
PointViewIter(PointView * view,PointId id)646     PointViewIter(PointView* view, PointId id) : m_view(view), m_id(id)
647     {}
648 
operator ++()649     PointViewIter& operator++()
650         { m_id++; return *this; }
operator ++(int)651     PointViewIter operator++(int)
652         { return PointViewIter(m_view, m_id++); }
operator --()653     PointViewIter& operator--()
654         { --m_id; return *this; }
operator --(int)655     PointViewIter operator--(int)
656         { return PointViewIter(m_view, m_id--); }
657 
operator +(const difference_type & n) const658     PointViewIter operator+(const difference_type& n) const
659         { return PointViewIter(m_view, m_id + n); }
operator +=(const difference_type & n)660     PointViewIter operator+=(const difference_type& n)
661         { m_id += n; return *this; }
operator -(const difference_type & n) const662     PointViewIter operator-(const difference_type& n) const
663         { return PointViewIter(m_view, m_id - n); }
operator -=(const difference_type & n)664     PointViewIter operator-=(const difference_type& n)
665         { m_id -= n; return *this; }
operator -(const PointViewIter & i) const666     difference_type operator-(const PointViewIter& i) const
667         { return static_cast<difference_type>(m_id - i.m_id); }
668 
operator ==(const PointViewIter & i) const669     bool operator==(const PointViewIter& i) const
670         { return m_id == i.m_id; }
operator !=(const PointViewIter & i) const671     bool operator!=(const PointViewIter& i) const
672         { return m_id != i.m_id; }
operator <(const PointViewIter & i) const673     bool operator<(const PointViewIter& i) const
674         { return m_id < i.m_id; }
operator <=(const PointViewIter & i) const675     bool operator<=(const PointViewIter& i) const
676         { return m_id <= i.m_id; }
operator >(const PointViewIter & i) const677     bool operator>(const PointViewIter& i) const
678         { return m_id > i.m_id; }
operator >=(const PointViewIter & i) const679     bool operator>=(const PointViewIter& i) const
680         { return m_id >= i.m_id; }
681 
operator *() const682     reference operator*() const
683         { return PointRef(*m_view, m_id); }
operator ->()684     pointer operator->()
685         { return nullptr; }
operator [](const difference_type & n) const686     reference operator[](const difference_type& n) const
687         { return PointRef(*m_view, m_id + n); }
688 };
689 
690 } // namespace pdal
691