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