1 //**************************************************************************************************
2 //
3 // OSSIM (http://trac.osgeo.org/ossim/)
4 //
5 // License:  LGPL -- See LICENSE.txt file in the top level directory for more details.
6 //
7 //**************************************************************************************************
8 // $Id$
9 
10 #include <ossim/point_cloud/ossimPointCloudImageHandler.h>
11 #include <ossim/point_cloud/ossimPointCloudSource.h>
12 #include <ossim/point_cloud/ossimPointCloudHandlerRegistry.h>
13 #include <ossim/base/ossimConstants.h>
14 #include <ossim/base/ossimCommon.h>
15 #include <ossim/base/ossimKeywordNames.h>
16 #include <ossim/base/ossimPreferences.h>
17 #include <ossim/base/ossimTrace.h>
18 #include <ossim/base/ossimIpt.h>
19 #include <ossim/base/ossimDpt.h>
20 #include <ossim/base/ossimGpt.h>
21 #include <ossim/base/ossimFilename.h>
22 #include <ossim/base/ossimKeywordlist.h>
23 #include <ossim/base/ossimEllipsoid.h>
24 #include <ossim/base/ossimDatum.h>
25 #include <ossim/base/ossimNumericProperty.h>
26 #include <ossim/base/ossimStringProperty.h>
27 #include <ossim/imaging/ossimImageDataFactory.h>
28 #include <ossim/projection/ossimEpsgProjectionFactory.h>
29 
30 using namespace std;
31 
32 static ossimTrace traceDebug("ossimPointCloudImageHandler:debug");
33 static const char* GSD_FACTOR_KW = "gsd_factor";
34 static const char* COMPONENT_KW = "component";
35 
36 // The member m_activeComponent should be one of the following strings. This is set either in a
37 // state KWL or by a call to setProperty(<"active_component", <string> >)
38 static const char* INTENSITY_KW = "INTENSITY";
39 static const char* HIGHEST_KW = "HIGHEST";
40 static const char* LOWEST_KW = "LOWEST";
41 static const char* RETURNS_KW = "RETURNS";
42 static const char* RGB_KW = "RGB";
43 
PcrBucket(const ossim_float32 * init_value,ossim_uint32 numBands)44 ossimPointCloudImageHandler::PcrBucket::PcrBucket(const ossim_float32* init_value,
45                                                   ossim_uint32 numBands)
46 :  m_numSamples (1)
47 {
48    m_bucket = new ossim_float32[numBands];
49    for (ossim_uint32 i=0; i<numBands; i++)
50       m_bucket[i] = init_value[i];
51 }
52 
53 
PcrBucket(const ossim_float32 & R,const ossim_float32 & G,const ossim_float32 & B)54 ossimPointCloudImageHandler::PcrBucket::PcrBucket(const ossim_float32& R,
55       const ossim_float32& G,
56       const ossim_float32& B)
57 :  m_numSamples (1)
58 {
59    m_bucket = new ossim_float32[3];
60    m_bucket[0] = R;
61    m_bucket[1] = G;
62    m_bucket[2] = B;
63 
64 }
65 
66 
PcrBucket(const ossim_float32 & init_value)67 ossimPointCloudImageHandler::PcrBucket::PcrBucket(const ossim_float32& init_value)
68 :  m_numSamples (1)
69 {
70    m_bucket = new ossim_float32[1];
71    m_bucket[0] = init_value;
72 }
73 
74 
~PcrBucket()75 ossimPointCloudImageHandler::PcrBucket::~PcrBucket()
76 {
77    delete [] m_bucket;
78 }
79 
80 
ossimPointCloudImageHandler()81 ossimPointCloudImageHandler::ossimPointCloudImageHandler()
82       : ossimImageHandler(),
83         m_maxPixel(1.0),
84         m_minPixel(0.0),
85         m_gsd(),
86         m_gsdFactor (1.0),
87         m_tile(0),
88         m_mutex(),
89         m_activeComponent(INTENSITY)
90 {
91    //---
92    // Nan out as can be set in several places, i.e. setProperty,
93    // loadState and initProjection.
94    //---
95    m_gsd.makeNan();
96 
97    m_componentNames.emplace_back(INTENSITY_KW);
98    m_componentNames.emplace_back(HIGHEST_KW);
99    m_componentNames.emplace_back(LOWEST_KW);
100    m_componentNames.emplace_back(RETURNS_KW);
101    m_componentNames.emplace_back(RGB_KW);
102 }
103 
~ossimPointCloudImageHandler()104 ossimPointCloudImageHandler::~ossimPointCloudImageHandler()
105 {
106    close();
107 }
108 
open()109 bool ossimPointCloudImageHandler::open()
110 {
111    close();
112 
113    // Need to utilize the Point Cloud handler registry to open the PC file:
114    m_pch = ossimPointCloudHandlerRegistry::instance()->open(theImageFile);
115    if (!m_pch.valid())
116       return false;
117 
118    getImageGeometry();
119    ossimImageHandler::completeOpen();
120 
121    // Needed here after open to make sure that min/max pixels are set for active component/entry
122    setCurrentEntry((ossim_uint32)m_activeComponent);
123 
124    return true;
125 }
126 
setPointCloudHandler(ossimPointCloudHandler * pch)127 bool ossimPointCloudImageHandler::setPointCloudHandler(ossimPointCloudHandler* pch)
128 {
129    close();
130 
131    // Need to utilize the Point Cloud handler registry to open the PC file:
132    m_pch = pch;
133    if (!m_pch.valid())
134       return false;
135 
136    getImageGeometry();
137    ossimImageHandler::completeOpen();
138 
139    // Needed here after open to make sure that min/max pixels are set for active component/entry
140    setCurrentEntry((ossim_uint32)m_activeComponent);
141 
142    return true;
143 }
144 
close()145 void ossimPointCloudImageHandler::close()
146 {
147    if (isOpen())
148    {
149       m_pch->close();
150       m_tile = 0;
151       ossimImageHandler::close();
152    }
153 }
154 
getImageGeometry()155 ossimRefPtr<ossimImageGeometry> ossimPointCloudImageHandler::getImageGeometry()
156 {
157    if (!isOpen())
158       return 0;
159 
160    if (theGeometry.valid())
161       return theGeometry;
162 
163    // Check for external geom (i.e., a *.geom file)
164    theGeometry = getExternalImageGeometry();
165    if (theGeometry.valid())
166       return theGeometry;
167 
168    theGeometry = new ossimImageGeometry();
169    ossimString epsgCode ("EPSG:4326");
170    ossimMapProjection* proj = dynamic_cast<ossimMapProjection*>( // NOLINT
171            ossimEpsgProjectionFactory::instance()->createProjection(epsgCode));
172    if (!proj)
173       return 0;
174    theGeometry->setProjection(proj);
175 
176    // Need to establish image bounds and optimal GSD given ground rect. Use this switch to also
177    // initialize the UL tiepoint of image projection:
178    ossimGrect bounds;
179    m_pch->getBounds(bounds);
180    proj->setOrigin(bounds.ul());
181    proj->setUlTiePoints(bounds.ul());
182 
183    // The GSD depends on the point density on the ground. count all final returns and
184    // assume they are evenly distributed over the bounding ground rect. Note that this can have
185    // up to a sqrt(2)X error if the collection was taken in a cardinal-diagonal direction.
186    // Also use this point loop to latch the ground quad vertices vertices.
187    ossim_uint32 numPulses = m_pch->getNumPoints(); // count of final returns
188    if (numPulses == 0)
189    {
190       // Not yet determined. Set GSD to NAN and expect it will be set later:
191       m_gsd.makeNan();
192    }
193    else if (m_gsd.hasNans())
194    {
195       ossimDpt delta (bounds.widthMeters(), bounds.heightMeters());
196       ossim_float64 gsd = sqrt(delta.x * delta.y / numPulses) * m_gsdFactor;
197       setGSD(gsd); // also recomputes the image size
198    }
199 
200    return theGeometry;
201 }
202 
getTile(const ossimIrect & tile_rect,ossim_uint32 resLevel)203 ossimRefPtr<ossimImageData> ossimPointCloudImageHandler::getTile(const ossimIrect& tile_rect,
204                                                                  ossim_uint32 resLevel)
205 {
206    if (!m_tile.valid())
207       initTile();
208 
209    // Image rectangle must be set prior to calling getTile.
210    m_tile->setImageRectangle(tile_rect);
211    if (!getTile(m_tile.get(), resLevel))
212    {
213       if (m_tile->getDataObjectStatus() != OSSIM_NULL)
214          m_tile->makeBlank();
215    }
216 
217    return m_tile;
218 }
219 
getTile(ossimImageData * result,ossim_uint32 resLevel)220 bool ossimPointCloudImageHandler::getTile(ossimImageData* result, ossim_uint32 resLevel)
221 {
222    // check for all systems go and valid args:
223    if (!m_pch.valid() || !result || (result->getScalarType() != OSSIM_FLOAT32)
224        || (result->getDataObjectStatus() == OSSIM_NULL) || m_gsd.hasNans())
225    {
226       return false;
227    }
228 
229    // Overviews achieved with GSD setting. This may be too slow.
230    ossimDpt gsd (m_gsd);
231    if (resLevel > 0)
232       getGSD(gsd, resLevel);
233 
234    // Establish the ground and image rects for this tile:
235    const ossimIrect img_tile_rect = result->getImageRectangle();
236    const ossimIpt tile_offset (img_tile_rect.ul());
237    const ossim_uint32 tile_width = img_tile_rect.width();
238    const ossim_uint32 tile_height = img_tile_rect.height();
239    const ossim_uint32 tile_size = img_tile_rect.area();
240 
241    ossimGpt gnd_ul, gnd_lr;
242    ossimDpt dpt_ul (img_tile_rect.ul().x - 0.5, img_tile_rect.ul().y - 0.5);
243    ossimDpt dpt_lr (img_tile_rect.lr().x + 0.5, img_tile_rect.lr().y + 0.5);
244    theGeometry->rnToWorld(dpt_ul, resLevel, gnd_ul);
245    theGeometry->rnToWorld(dpt_lr, resLevel, gnd_lr);
246    const ossimGrect gnd_rect (gnd_ul, gnd_lr);
247 
248    // Create array of buckets to store accumulated point data.
249    ossim_uint32 numBands = result->getNumberOfBands();
250    if (numBands > getNumberOfInputBands())
251    {
252       // This should never happen;
253       ossimNotify(ossimNotifyLevel_FATAL)
254             << "ossimPointCloudImageHandler::getTile() ERROR: \n"
255             << "More bands were requested than was available from the point cloud source. Returning "
256             << "blank tile." << endl;
257       result->makeBlank();
258       return false;
259    }
260    std::map<ossim_int32, PcrBucket*> accumulator;
261 
262    // initialize a point block with desired fields as requested in the reader properties
263    ossimPointBlock pointBlock (this);
264    pointBlock.setFieldCode(componentToFieldCode());
265    m_pch->rewind();
266 
267    ossimDpt ipt;
268    ossimGpt pos;
269 
270 #define USE_GETBLOCK
271 #ifdef USE_GETBLOCK
272    m_pch->getBlock(gnd_rect, pointBlock);
273    for (ossim_uint32 id=0; id<pointBlock.size(); ++id)
274    {
275       pos = pointBlock[id]->getPosition();
276       theGeometry->worldToRn(pos, resLevel, ipt);
277       ipt.x = ossim::round<double,double>(ipt.x) - tile_offset.x;
278       ipt.y = ossim::round<double,double>(ipt.y) - tile_offset.y;
279 
280       ossim_int32 bucketIndex = ipt.y*tile_width + ipt.x;
281       if ((bucketIndex >= 0) && (bucketIndex < (ossim_int32)tile_size))
282          addSample(accumulator, bucketIndex, pointBlock[id]);
283    }
284 
285 #else // using getFileBlock
286    ossim_uint32 numPoints = m_pch->getNumPoints();
287    if (numPoints > ossimPointCloudHandler::DEFAULT_BLOCK_SIZE)
288       numPoints = ossimPointCloudHandler::DEFAULT_BLOCK_SIZE;
289 
290    // Loop to read all point blocks:
291    do
292    {
293       pointBlock.clear();
294       m_pch->getNextFileBlock(pointBlock, numPoints);
295       //m_pch->normalizeBlock(pointBlock);
296 
297       for (ossim_uint32 id=0; id<pointBlock.size(); ++id)
298       {
299          // Check that each point in read block is inside the ROI before accumulating it:
300          pos = pointBlock[id]->getPosition();
301          if (gnd_rect.pointWithin(pos))
302          {
303             theGeometry->worldToRn(pos, resLevel, ipt);
304             ipt.x = ossim::round<double,double>(ipt.x) - tile_offset.x;
305             ipt.y = ossim::round<double,double>(ipt.y) - tile_offset.y;
306 
307             ossim_int32 bucketIndex = ipt.y*tile_width + ipt.x;
308             if ((bucketIndex >= 0) && (bucketIndex < (ossim_int32)tile_size))
309                addSample(accumulator, bucketIndex, pointBlock[id]);
310          }
311       }
312    } while (pointBlock.size() == numPoints);
313 #endif
314 
315    // Finished accumulating, need to normalize and fill the tile.
316    // We must always blank out the tile as we may not have a point for every pixel.
317    normalize(accumulator);
318    auto buf = new ossim_float32*[numBands];
319    std::map<ossim_int32, PcrBucket*>::iterator accum_iter;
320    ossim_float32 null_pixel = OSSIM_DEFAULT_NULL_PIX_FLOAT;
321    result->setNullPix(null_pixel);
322    for (ossim_uint32 band = 0; band < numBands; band++)
323    {
324       ossim_uint32 index = 0;
325       buf[band] = result->getFloatBuf(band);
326       for (ossim_uint32 y = 0; y < tile_height; y++)
327       {
328          for (ossim_uint32 x = 0; x < tile_width; x++)
329          {
330             accum_iter = accumulator.find(index);
331             if (accum_iter != accumulator.end())
332                buf[band][index] = accum_iter->second->m_bucket[band];
333             else
334                buf[band][index] = null_pixel;
335             ++index;
336          }
337       }
338    }
339 
340    delete [] buf;
341    buf = 0;
342 
343    auto pcr_iter = accumulator.begin();
344    while (pcr_iter != accumulator.end())
345    {
346       delete pcr_iter->second;
347       pcr_iter++;
348    }
349 
350    result->validate();
351    return true;
352 }
353 
addSample(std::map<ossim_int32,PcrBucket * > & accumulator,ossim_int32 index,const ossimPointRecord * sample)354 void ossimPointCloudImageHandler::addSample(std::map<ossim_int32, PcrBucket*>& accumulator,
355                                             ossim_int32 index,
356                                             const ossimPointRecord* sample)
357 {
358    if (sample == 0)
359       return;
360 
361    //cout << "sample: "<<*sample<<endl;//TODO: REMOVE DEBUG
362 
363    // Search map for exisiting point in that location:
364    auto iter = accumulator.find(index);
365    if (iter == accumulator.end())
366    {
367       // First hit. Initialize location with current sample:
368       if (m_activeComponent == INTENSITY)
369       {
370          accumulator[index] = new PcrBucket(sample->getField(ossimPointRecord::Intensity));
371       }
372       else if (m_activeComponent == RGB)
373       {
374          ossim_float32 color[3];
375          color[0] = sample->getField(ossimPointRecord::Red);
376          color[1] = sample->getField(ossimPointRecord::Green);
377          color[2] = sample->getField(ossimPointRecord::Blue);
378          accumulator[index] = new PcrBucket(color, 3);
379       }
380       else if ((m_activeComponent == LOWEST) || (m_activeComponent == HIGHEST))
381          accumulator[index] = new PcrBucket(sample->getPosition().hgt);
382       else if (m_activeComponent == RETURNS)
383          accumulator[index] = new PcrBucket(sample->getField(ossimPointRecord::NumberOfReturns));
384    }
385    else
386    {
387       // Not the first hit at this location, accumulate:
388       // First hit. Initialize location with current sample:
389       if (m_activeComponent == INTENSITY)
390       {
391          iter->second->m_bucket[0] += sample->getField(ossimPointRecord::Intensity);
392       }
393       else if (m_activeComponent == RGB)
394       {
395          iter->second->m_bucket[0] += sample->getField(ossimPointRecord::Red);
396          iter->second->m_bucket[1] += sample->getField(ossimPointRecord::Green);
397          iter->second->m_bucket[2] += sample->getField(ossimPointRecord::Blue);
398       }
399       else if ((m_activeComponent == HIGHEST) &&
400             (sample->getPosition().hgt > iter->second->m_bucket[0]))
401          iter->second->m_bucket[0] = sample->getPosition().hgt;
402       else if ((m_activeComponent == LOWEST) &&
403             (sample->getPosition().hgt < iter->second->m_bucket[0]))
404          iter->second->m_bucket[0] = sample->getPosition().hgt;
405       else if (m_activeComponent == RETURNS)
406          iter->second->m_bucket[0] += sample->getField(ossimPointRecord::NumberOfReturns);
407 
408       iter->second->m_numSamples++;
409    }
410 }
411 
normalize(std::map<ossim_int32,PcrBucket * > & accumulator)412 void ossimPointCloudImageHandler::normalize(std::map<ossim_int32, PcrBucket*>& accumulator)
413 {
414    // highest and lowest elevations latch extremes, no mean is computed but needs to be normalized
415    if ((m_activeComponent == LOWEST) || (m_activeComponent == HIGHEST) ||
416          (m_activeComponent == RETURNS))
417       return;
418 
419    int numBands = 1;
420    if (m_activeComponent == RGB)
421       numBands = 3;
422 
423    auto iter = accumulator.begin();
424    ossim_float32 avg;
425    while (iter != accumulator.end())
426    {
427       for (int i=0; i<numBands; i++)
428       {
429          avg = iter->second->m_bucket[i] / iter->second->m_numSamples;
430          iter->second->m_bucket[i] = avg;
431       }
432       iter++;
433    }
434 }
435 
getNumberOfInputBands() const436 ossim_uint32 ossimPointCloudImageHandler::getNumberOfInputBands() const
437 {
438    ossim_uint32 numBands = 0;
439    if (m_pch.valid())
440    {
441       if (m_activeComponent == INTENSITY)
442          numBands = 1;
443       else if (m_activeComponent == RGB)
444          numBands = 3;
445       else if ((m_activeComponent == LOWEST) || (m_activeComponent == HIGHEST))
446          numBands = 1;
447       else if (m_activeComponent == RETURNS)
448          numBands = 1;
449    }
450    return numBands;
451 }
452 
getNumberOfLines(ossim_uint32 resLevel) const453 ossim_uint32 ossimPointCloudImageHandler::getNumberOfLines(ossim_uint32 resLevel) const
454 {
455    ossim_uint32 result = 0;
456    if (isOpen() && theGeometry.valid())
457    {
458       ossimIpt image_size(theGeometry->getImageSize());
459       result = image_size.line;
460       if (resLevel)
461          result = (result >> resLevel);
462    }
463    return result;
464 }
465 
getNumberOfSamples(ossim_uint32 resLevel) const466 ossim_uint32 ossimPointCloudImageHandler::getNumberOfSamples(ossim_uint32 resLevel) const
467 {
468    ossim_uint32 result = 0;
469    if (isOpen() && theGeometry.valid())
470    {
471       ossimIpt image_size(theGeometry->getImageSize());
472       result = image_size.samp;
473       if (resLevel)
474          result = (result >> resLevel);
475    }
476    return result;
477 }
478 
getImageTileWidth() const479 ossim_uint32 ossimPointCloudImageHandler::getImageTileWidth() const
480 {
481    return getTileWidth();
482 }
483 
getImageTileHeight() const484 ossim_uint32 ossimPointCloudImageHandler::getImageTileHeight() const
485 {
486    return getTileHeight();
487 }
488 
getTileWidth() const489 ossim_uint32 ossimPointCloudImageHandler::getTileWidth() const
490 {
491    ossimIpt ipt;
492    ossim::defaultTileSize(ipt);
493    return ipt.x;
494 }
495 
getTileHeight() const496 ossim_uint32 ossimPointCloudImageHandler::getTileHeight() const
497 {
498    ossimIpt ipt;
499    ossim::defaultTileSize(ipt);
500    return ipt.y;
501 }
502 
getOutputScalarType() const503 ossimScalarType ossimPointCloudImageHandler::getOutputScalarType() const
504 {
505    return OSSIM_FLOAT32;
506 }
507 
getEntryList(std::vector<ossim_uint32> & entryList) const508 void ossimPointCloudImageHandler::getEntryList(std::vector<ossim_uint32>& entryList) const
509 {
510    entryList.clear();
511    for (ossim_uint32 i = 0; i < m_componentNames.size(); i++)
512    {
513       entryList.emplace_back(i);
514    }
515 }
516 
getEntryNames(std::vector<ossimString> & entryNames) const517 void ossimPointCloudImageHandler::getEntryNames(std::vector<ossimString>& entryNames) const
518 {
519    entryNames = m_componentNames;
520 }
521 
getCurrentEntry() const522 ossim_uint32 ossimPointCloudImageHandler::getCurrentEntry() const
523 {
524    return (ossim_uint32) m_activeComponent;
525 }
526 
setCurrentEntry(ossim_uint32 entryIdx)527 bool ossimPointCloudImageHandler::setCurrentEntry(ossim_uint32 entryIdx)
528 {
529    if (entryIdx >= NUM_COMPONENTS)
530       return false;
531 
532    m_activeComponent = (Components) entryIdx;
533    if (m_pch.valid() && m_pch->getMinPoint() && m_pch->getMaxPoint())
534    {
535       if (m_activeComponent == INTENSITY)
536       {
537          m_minPixel = m_pch->getMinPoint()->getField(ossimPointRecord::Intensity);
538          m_maxPixel = m_pch->getMaxPoint()->getField(ossimPointRecord::Intensity);
539       }
540       else if (m_activeComponent == RGB)
541       {
542          m_minPixel = m_pch->getMinPoint()->getField(ossimPointRecord::Red);
543          m_maxPixel = m_pch->getMaxPoint()->getField(ossimPointRecord::Red);
544       }
545       else if ((m_activeComponent == LOWEST) || (m_activeComponent == HIGHEST))
546       {
547          m_minPixel = m_pch->getMinPoint()->getPosition().hgt;
548          m_maxPixel = m_pch->getMaxPoint()->getPosition().hgt;
549       }
550       else if (m_activeComponent == RETURNS)
551       {
552          m_minPixel = 0;
553          m_maxPixel = m_pch->getMaxPoint()->getField(ossimPointRecord::NumberOfReturns);
554       }
555    }
556 
557    return true;
558 }
559 
getShortName() const560 ossimString ossimPointCloudImageHandler::getShortName() const
561 {
562    return ossimString("Point Cloud Image Handler");
563 }
564 
getLongName() const565 ossimString ossimPointCloudImageHandler::getLongName() const
566 {
567    return ossimString("ossim point cloud to image renderer");
568 }
569 
getMinPixelValue(ossim_uint32) const570 double ossimPointCloudImageHandler::getMinPixelValue(ossim_uint32 /* band */) const
571 {
572    return m_minPixel;
573 }
574 
getMaxPixelValue(ossim_uint32) const575 double ossimPointCloudImageHandler::getMaxPixelValue(ossim_uint32 /* band */) const
576 {
577    return m_maxPixel;
578 }
579 
getNullPixelValue(ossim_uint32) const580 double ossimPointCloudImageHandler::getNullPixelValue(ossim_uint32 /* band */) const
581 {
582    return OSSIM_DEFAULT_NULL_PIX_FLOAT;
583 }
584 
getNumberOfDecimationLevels() const585 ossim_uint32 ossimPointCloudImageHandler::getNumberOfDecimationLevels() const
586 {
587    // Can support any number of rlevels.
588    ossim_uint32 result = 1;
589    const ossim_uint32 STOP_DIMENSION = 16;
590    ossim_uint32 largestImageDimension =
591          getNumberOfSamples(0) > getNumberOfLines(0) ? getNumberOfSamples(0) : getNumberOfLines(0);
592    while (largestImageDimension > STOP_DIMENSION)
593    {
594       largestImageDimension /= 2;
595       ++result;
596    }
597    return result;
598 }
599 
initTile()600 void ossimPointCloudImageHandler::initTile()
601 {
602    const ossim_uint32 BANDS = getNumberOfOutputBands();
603 
604    m_tile = new ossimImageData(this, getOutputScalarType(), BANDS, getTileWidth(), getTileHeight());
605 
606    for (ossim_uint32 band = 0; band < BANDS; ++band)
607    {
608       m_tile->setMinPix(getMinPixelValue(band), band);
609       m_tile->setMaxPix(getMaxPixelValue(band), band);
610       m_tile->setNullPix(getNullPixelValue(band), band);
611    }
612 
613    m_tile->initialize();
614 }
615 
getGSD(ossimDpt & gsd,ossim_uint32 resLevel) const616 void ossimPointCloudImageHandler::getGSD(ossimDpt& gsd, ossim_uint32 resLevel) const
617 {
618    // std::pow(2.0, 0) returns 1.
619    ossim_float64 d = std::pow(2.0, static_cast<double>(resLevel));
620    gsd.x = m_gsd.x * d;
621    gsd.y = m_gsd.y * d;
622 }
623 
setGSD(const ossim_float64 & gsd)624 void ossimPointCloudImageHandler::setGSD(const ossim_float64& gsd)
625 {
626    if (ossim::isnan(gsd) || (gsd<=0.0) || !theGeometry.valid())
627          return;
628 
629    m_gsd = ossimDpt(gsd, gsd);
630    m_gsdFactor = 1.0; // resets after GSD adjusted
631 
632    ossimMapProjection* proj = dynamic_cast<ossimMapProjection*>(theGeometry->getProjection());
633    if (!proj)
634       return;
635 
636    proj->setMetersPerPixel(m_gsd);
637 
638    ossimGrect bounds;
639    m_pch->getBounds(bounds);
640 
641    ossimDpt ipt_ul, ipt_lr;
642    theGeometry->worldToLocal(bounds.ul(), ipt_ul);
643    theGeometry->worldToLocal(bounds.lr(), ipt_lr);
644    ossimIpt image_size;
645    image_size.x = ossim::round<ossim_int32,double>(ipt_lr.x - ipt_ul.x) + 1;
646    image_size.y = ossim::round<ossim_int32,double>(ipt_lr.y - ipt_ul.y) + 1;
647 
648    theGeometry->setImageSize(image_size);
649 }
650 
saveState(ossimKeywordlist & kwl,const char * prefix) const651 bool ossimPointCloudImageHandler::saveState(ossimKeywordlist& kwl, const char* prefix) const
652 {
653    static const char MODULE[] = "ossimPointCloudImageHandler::saveState()";
654 
655    ossimImageHandler::saveState(kwl, prefix);
656    if (kwl.getErrorStatus() == ossimErrorCodes::OSSIM_ERROR)
657    {
658       ossimNotify(ossimNotifyLevel_WARN) << MODULE
659             << " ERROR detected in keyword list!  State not saved." << std::endl;
660       return false;
661    }
662 
663    kwl.add(prefix, ossimKeywordNames::ENTRY_KW, (int) m_activeComponent, true);
664    kwl.add(prefix, ossimKeywordNames::METERS_PER_PIXEL_KW, m_gsd.x, true);
665 
666    return true;
667 }
668 
loadState(const ossimKeywordlist & kwl,const char * prefix)669 bool ossimPointCloudImageHandler::loadState(const ossimKeywordlist& kwl, const char* prefix)
670 {
671    static const char MODULE[] = "ossimPointCloudImageHandler::loadState()";
672    theDecimationFactors.clear();
673    if(traceDebug())
674       ossimNotify(ossimNotifyLevel_DEBUG)<< MODULE << " DEBUG: entered ..."<< std::endl;
675 
676    ossimImageHandler::loadState(kwl, prefix);
677    if (kwl.getErrorStatus() == ossimErrorCodes::OSSIM_ERROR)
678    {
679       ossimNotify(ossimNotifyLevel_WARN)<< MODULE
680             << "WARNING: error detected in keyword list!  State not load." << std::endl;
681       return false;
682    }
683 
684    m_activeComponent = INTENSITY;
685    ossimString value = kwl.find(prefix, ossimKeywordNames::ENTRY_KW);
686    if (!value.empty())
687    {
688       ossim_uint32 i = value.toUInt32();
689       if (i < NUM_COMPONENTS)
690          m_activeComponent = (Components) i;
691    }
692 
693    value = kwl.find(prefix, ossimKeywordNames::METERS_PER_PIXEL_KW);
694    if (!value.empty())
695       setGSD(value.toDouble());
696 
697    // The rest of the state is established by opening the file:
698    bool good_open = open();
699 
700    return good_open;
701 }
702 
getValidImageVertices(std::vector<ossimIpt> & validVertices,ossimVertexOrdering ordering,ossim_uint32 resLevel) const703 void ossimPointCloudImageHandler::getValidImageVertices(std::vector<ossimIpt>& validVertices,
704                                    ossimVertexOrdering ordering,
705                                    ossim_uint32 resLevel) const
706 {
707    validVertices.clear();
708    if (!m_pch.valid())
709       return;
710    int divisor = 1;
711    if (resLevel)
712       divisor = resLevel<<1;
713 
714    // Transform the world coords for the four vertices into image vertices:
715    ossimDpt r0Pt;
716    ossimGrect bounds;
717    m_pch->getBounds(bounds);
718    theGeometry->worldToLocal(bounds.ul(), r0Pt);
719    validVertices.emplace_back(r0Pt);
720    theGeometry->worldToLocal(bounds.ur(), r0Pt);
721    validVertices.emplace_back(r0Pt);
722    theGeometry->worldToLocal(bounds.lr(), r0Pt);
723    validVertices.emplace_back(r0Pt);
724    theGeometry->worldToLocal(bounds.ll(), r0Pt);
725    validVertices.emplace_back(r0Pt);
726 
727    if (ordering == OSSIM_COUNTERCLOCKWISE_ORDER)
728    {
729       for (int i=3; i>=0; i--)
730          validVertices.emplace_back(validVertices[i]/divisor);
731       validVertices.erase(validVertices.begin(), validVertices.begin()+4);
732    }
733 }
734 
setProperty(ossimRefPtr<ossimProperty> property)735 void ossimPointCloudImageHandler::setProperty(ossimRefPtr<ossimProperty> property)
736 {
737    if (!property.valid())
738       return;
739 
740    ossimString s;
741    property->valueToString(s);
742    if (s.empty())
743       return;
744 
745    // The user should select either explicit GSD or relative GSD factor, never both:
746    if ( property->getName() == ossimKeywordNames::METERS_PER_PIXEL_KW )
747    {
748       ossim_float64 gsd = s.toFloat64();
749       if (!ossim::isnan(gsd))
750          setGSD(gsd);
751    }
752    else if ( property->getName() == GSD_FACTOR_KW )
753    {
754       m_gsdFactor = s.toDouble();
755       if (!ossim::isnan(m_gsdFactor))
756       {
757          if (!m_gsd.hasNans())
758             setGSD(m_gsd.x * m_gsdFactor);
759       }
760       else
761          m_gsdFactor = 1.0;
762    }
763    else if ( property->getName() == ossimKeywordNames::ENTRY_KW )
764    {
765       m_activeComponent = (Components) s.toUInt32();
766    }
767    else if ( property->getName() == COMPONENT_KW )
768    {
769       for (int i=0; i<NUM_COMPONENTS; i++)
770       {
771          if (s.upcase() == m_componentNames[i])
772          {
773             m_activeComponent = (Components) i;
774             break;
775          }
776       }
777    }
778    else
779    {
780       ossimImageHandler::setProperty(property);
781    }
782 }
783 
getProperty(const ossimString & name) const784 ossimRefPtr<ossimProperty> ossimPointCloudImageHandler::getProperty(const ossimString& name)const
785 {
786    ossimRefPtr<ossimProperty> prop = 0;
787    if ( name == ossimKeywordNames::METERS_PER_PIXEL_KW )
788    {
789       ossimString value = ossimString::toString(m_gsd.x);
790       prop = new ossimStringProperty(name, value);
791    }
792    else if ( name == GSD_FACTOR_KW )
793    {
794       prop = new ossimNumericProperty(name, ossimString::toString(m_gsdFactor));
795    }
796    else if ( name == ossimKeywordNames::ENTRY_KW )
797    {
798       prop = new ossimNumericProperty(name, ossimString::toString((ossim_uint32) m_activeComponent));
799    }
800    else if ( name == COMPONENT_KW )
801    {
802       prop = new ossimStringProperty(name, m_componentNames[m_activeComponent]);
803    }
804    else
805    {
806       prop = ossimImageHandler::getProperty(name);
807    }
808    return prop;
809 }
810 
componentToFieldCode() const811 ossim_uint32 ossimPointCloudImageHandler::componentToFieldCode() const
812 {
813    ossim_uint32 field_code = 0;
814    switch (m_activeComponent)
815    {
816    case INTENSITY:
817       field_code = ossimPointRecord::Intensity;
818       break;
819    case RETURNS:
820       field_code = ossimPointRecord::NumberOfReturns;
821       break;
822    case RGB:
823       field_code = ossimPointRecord::Red | ossimPointRecord::Green | ossimPointRecord::Blue;
824       break;
825    default:
826       break;
827    }
828    return field_code;
829 }
830 
831 
832