Isis 3.0 Programmer Reference
Back | Home
CubeIoHandler.cpp
Go to the documentation of this file.
1 
24 #include "IsisDebug.h"
25 #include "CubeIoHandler.h"
26 
27 #include <algorithm>
28 #include <cmath>
29 #include <iomanip>
30 
31 #include <QDebug>
32 #include <QFile>
33 #include <QList>
34 #include <QListIterator>
35 #include <QMapIterator>
36 #include <QMutex>
37 #include <QPair>
38 #include <QRect>
39 #include <QTime>
40 
41 #include "Area3D.h"
42 #include "Brick.h"
43 #include "CubeCachingAlgorithm.h"
44 #include "Displacement.h"
45 #include "Distance.h"
46 #include "Endian.h"
47 #include "EndianSwapper.h"
48 #include "IException.h"
49 #include "IString.h"
50 #include "PixelType.h"
51 #include "Preference.h"
52 #include "Pvl.h"
53 #include "PvlGroup.h"
54 #include "PvlObject.h"
55 #include "RawCubeChunk.h"
57 #include "SpecialPixel.h"
58 #include "Statistics.h"
59 
60 using namespace std;
61 
62 namespace Isis {
79  CubeIoHandler::CubeIoHandler(QFile * dataFile,
80  const QList<int> *virtualBandList, const Pvl &label, bool alreadyOnDisk) {
81  m_byteSwapper = NULL;
82  m_cachingAlgorithms = NULL;
83  m_dataIsOnDiskMap = NULL;
84  m_rawData = NULL;
85  m_virtualBands = NULL;
86  m_nullChunkData = NULL;
87  m_lastProcessByLineChunks = NULL;
88  m_writeCache = NULL;
89  m_ioThreadPool = NULL;
90  m_writeThreadMutex = NULL;
91 
92  try {
93  if (!dataFile) {
94  IString msg = "Cannot create a CubeIoHandler with a NULL data file";
95  throw IException(IException::Programmer, msg, _FILEINFO_);
96  }
97 
98  m_cachingAlgorithms = new QList<CubeCachingAlgorithm *>;
99 
100  PvlGroup &performancePrefs =
101  Preference::Preferences().findGroup("Performance");
102  IString cubeWritePerfOpt = performancePrefs["CubeWriteThread"][0];
103  m_useOptimizedCubeWrite = (cubeWritePerfOpt.DownCase() == "optimized");
104  if ((m_useOptimizedCubeWrite && !alreadyOnDisk) ||
105  cubeWritePerfOpt.DownCase() == "always") {
106  m_ioThreadPool = new QThreadPool;
107  m_ioThreadPool->setMaxThreadCount(1);
108  }
109 
110  m_consecutiveOverflowCount = 0;
111  m_lastOperationWasWrite = false;
112  m_rawData = new QMap<int, RawCubeChunk *>;
113  m_writeCache = new QPair< QMutex *, QList<Buffer *> >;
114  m_writeCache->first = new QMutex;
115  m_writeThreadMutex = new QMutex;
116 
117  m_idealFlushSize = 32;
118 
119  m_cachingAlgorithms->append(new RegionalCachingAlgorithm);
120 
121  m_dataFile = dataFile;
122 
123  const PvlObject &core = label.findObject("IsisCube").findObject("Core");
124  const PvlGroup &pixelGroup = core.findGroup("Pixels");
125 
126  QString byteOrderStr = pixelGroup.findKeyword("ByteOrder")[0];
127  m_byteSwapper = new EndianSwapper(
128  byteOrderStr.toUpper());
129  m_base = pixelGroup.findKeyword("Base");
130  m_multiplier = pixelGroup.findKeyword("Multiplier");
131  m_pixelType = PixelTypeEnumeration(pixelGroup.findKeyword("Type"));
132 
133  // If the byte swapper isn't going to do anything, then get rid of it
134  // because it's quicker to check for a NULL byte swapper member than to
135  // call a swap that won't do anything.
136  if(!m_byteSwapper->willSwap()) {
137  delete m_byteSwapper;
138  m_byteSwapper = NULL;
139  }
140 
141  const PvlGroup &dimensions = core.findGroup("Dimensions");
142  m_numSamples = dimensions.findKeyword("Samples");
143  m_numLines = dimensions.findKeyword("Lines");
144  m_numBands = dimensions.findKeyword("Bands");
145 
146  m_startByte = (int)core.findKeyword("StartByte") - 1;
147 
148  m_samplesInChunk = -1;
149  m_linesInChunk = -1;
150  m_bandsInChunk = -1;
151 
152  if(!alreadyOnDisk) {
153  m_dataIsOnDiskMap = new QMap<int, bool>;
154  }
155 
156  setVirtualBands(virtualBandList);
157  }
158  catch(IException &e) {
159  IString msg = "Constructing CubeIoHandler failed";
160  throw IException(e, IException::Programmer, msg, _FILEINFO_);
161  }
162  catch(...) {
163  IString msg = "Constructing CubeIoHandler failed";
164  throw IException(IException::Programmer, msg, _FILEINFO_);
165  }
166  }
167 
168 
173  CubeIoHandler::~CubeIoHandler() {
174  ASSERT( m_rawData ? m_rawData->size() == 0 : 1 );
175 
176  if (m_ioThreadPool)
177  m_ioThreadPool->waitForDone();
178 
179  delete m_ioThreadPool;
180  m_ioThreadPool = NULL;
181 
182  delete m_dataIsOnDiskMap;
183  m_dataIsOnDiskMap = NULL;
184 
185  if (m_cachingAlgorithms) {
186  QListIterator<CubeCachingAlgorithm *> it(*m_cachingAlgorithms);
187  while (it.hasNext()) {
188  delete it.next();
189  }
190  delete m_cachingAlgorithms;
191  m_cachingAlgorithms = NULL;
192  }
193 
194  if (m_rawData) {
195  QMapIterator<int, RawCubeChunk *> it(*m_rawData);
196  while (it.hasNext()) {
197  // Unwritten data here means it cannot be written :(
198  ASSERT(0);
199  it.next();
200 
201  if(it.value())
202  delete it.value();
203  }
204  delete m_rawData;
205  m_rawData = NULL;
206  }
207 
208  if (m_writeCache) {
209  delete m_writeCache->first;
210  m_writeCache->first = NULL;
211 
212  for (int i = 0; i < m_writeCache->second.size(); i++) {
213  delete m_writeCache->second[i];
214  }
215  m_writeCache->second.clear();
216 
217  delete m_writeCache;
218  m_writeCache = NULL;
219  }
220 
221  delete m_byteSwapper;
222  m_byteSwapper = NULL;
223 
224  delete m_virtualBands;
225  m_virtualBands = NULL;
226 
227  delete m_nullChunkData;
228  m_nullChunkData = NULL;
229 
230  delete m_lastProcessByLineChunks;
231  m_lastProcessByLineChunks = NULL;
232 
233  delete m_writeThreadMutex;
234  m_writeThreadMutex = NULL;
235  }
236 
237 
246  void CubeIoHandler::read(Buffer &bufferToFill) const {
247  // We need to record the current chunk count size so we can use
248  // it to evaluate if the cache should be minimized
249  int lastChunkCount = m_rawData->size();
250 
251  if (m_lastOperationWasWrite) {
252  // Do the remaining writes
253  flushWriteCache(true);
254 
255  m_lastOperationWasWrite = false;
256 
257  // Stop backgrounding writes now, we don't want to keep incurring this
258  // penalty.
259  if (m_useOptimizedCubeWrite) {
260  delete m_ioThreadPool;
261  m_ioThreadPool = NULL;
262  }
263  }
264 
265  QMutexLocker lock(m_writeThreadMutex);
266 
267  // NON-THREADED CUBE READ
268  QList<RawCubeChunk *> cubeChunks;
269  QList<int > chunkBands;
270 
271  int bufferSampleCount = bufferToFill.SampleDimension();
272  int bufferLineCount = bufferToFill.LineDimension();
273  int bufferBandCount = bufferToFill.BandDimension();
274 
275  // our chunk dimensions are same as buffer shape dimensions
276  if (bufferSampleCount == m_samplesInChunk &&
277  bufferLineCount == m_linesInChunk &&
278  bufferBandCount == m_bandsInChunk) {
279  int bufferStartSample = bufferToFill.Sample();
280  int bufferStartLine = bufferToFill.Line();
281  int bufferStartBand = bufferToFill.Band();
282 
283  int bufferEndSample = bufferStartSample + bufferSampleCount - 1;
284  int bufferEndLine = bufferStartLine + bufferLineCount - 1;
285  int bufferEndBand = bufferStartBand + bufferBandCount - 1;
286 
287  // make sure we access the correct band
288  int startBand = bufferStartBand - 1;
289  if (m_virtualBands)
290  startBand = m_virtualBands->at(bufferStartBand - 1);
291 
292  int expectedChunkIndex =
293  ((bufferStartSample - 1) / getSampleCountInChunk()) +
294  ((bufferStartLine - 1) / getLineCountInChunk()) *
295  getChunkCountInSampleDimension() +
296  ((startBand - 1) / getBandCountInChunk()) *
297  getChunkCountInSampleDimension() *
298  getChunkCountInLineDimension();
299 
300  int chunkStartSample, chunkStartLine, chunkStartBand,
301  chunkEndSample, chunkEndLine, chunkEndBand;
302 
303  getChunkPlacement(expectedChunkIndex,
304  chunkStartSample, chunkStartLine, chunkStartBand,
305  chunkEndSample, chunkEndLine, chunkEndBand);
306 
307  if (chunkStartSample == bufferStartSample &&
308  chunkStartLine == bufferStartLine &&
309  chunkStartBand == bufferStartBand &&
310  chunkEndSample == bufferEndSample &&
311  chunkEndLine == bufferEndLine &&
312  chunkEndBand == bufferEndBand) {
313  cubeChunks.append(getChunk(expectedChunkIndex, true));
314  chunkBands.append(cubeChunks.last()->getStartBand());
315  }
316  }
317 
318  if (cubeChunks.empty()) {
319  // We can't guarantee our cube chunks will encompass the buffer
320  // if the buffer goes beyond the cube bounds.
321  for(int i = 0; i < bufferToFill.size(); i++) {
322  bufferToFill[i] = Null;
323  }
324 
326  chunkInfo = findCubeChunks(
327  bufferToFill.Sample(), bufferToFill.SampleDimension(),
328  bufferToFill.Line(), bufferToFill.LineDimension(),
329  bufferToFill.Band(), bufferToFill.BandDimension());
330  cubeChunks = chunkInfo.first;
331  chunkBands = chunkInfo.second;
332  }
333 
334  for (int i = 0; i < cubeChunks.size(); i++) {
335  writeIntoDouble(*cubeChunks[i], bufferToFill, chunkBands[i]);
336  }
337 
338  // Minimize the cache if it changed in size
339  if (lastChunkCount != m_rawData->size()) {
340  minimizeCache(cubeChunks, bufferToFill);
341  }
342  }
343 
344 
354  void CubeIoHandler::write(const Buffer &bufferToWrite) {
355  m_lastOperationWasWrite = true;
356 
357  if (m_ioThreadPool) {
358  // THREADED CUBE WRITE
359  Buffer * copy = new Buffer(bufferToWrite);
360  {
361  QMutexLocker locker(m_writeCache->first);
362  m_writeCache->second.append(copy);
363  }
364 
365  flushWriteCache();
366  }
367  else {
368  QMutexLocker lock(m_writeThreadMutex);
369  // NON-THREADED CUBE WRITE
370  synchronousWrite(bufferToWrite);
371  }
372  }
373 
374 
384  void CubeIoHandler::addCachingAlgorithm(CubeCachingAlgorithm *algorithm) {
385  m_cachingAlgorithms->prepend(algorithm);
386  }
387 
388 
399  void CubeIoHandler::clearCache(bool blockForWriteCache) const {
400  if (blockForWriteCache) {
401  // Start the rest of the writes
402  flushWriteCache(true);
403  }
404 
405  // If this map is allocated, then this is a brand new cube and we need to
406  // make sure it's filled with data or NULLs.
407  if(m_dataIsOnDiskMap) {
408  writeNullDataToDisk();
409  }
410 
411  // This should be allocated. This is a list of the cached cube data.
412  // Write it all to disk.
413  if (m_rawData) {
414  QMapIterator<int, RawCubeChunk *> it(*m_rawData);
415  while (it.hasNext()) {
416  it.next();
417 
418  if(it.value()) {
419  if(it.value()->isDirty()) {
420  (const_cast<CubeIoHandler *>(this))->writeRaw(*it.value());
421  }
422 
423  delete it.value();
424  }
425  }
426 
427  m_rawData->clear();
428  }
429 
430  if(m_lastProcessByLineChunks) {
431  delete m_lastProcessByLineChunks;
432  m_lastProcessByLineChunks = NULL;
433  }
434  }
435 
436 
441  BigInt CubeIoHandler::getDataSize() const {
442  return (BigInt)getChunkCountInSampleDimension() *
443  (BigInt)getChunkCountInLineDimension() *
444  (BigInt)getChunkCountInBandDimension() *
445  (BigInt)getBytesPerChunk();
446  }
447 
448 
458  void CubeIoHandler::setVirtualBands(const QList<int> *virtualBandList) {
459  if(m_virtualBands) {
460  delete m_virtualBands;
461  m_virtualBands = NULL;
462  }
463 
464  if(virtualBandList && !virtualBandList->empty())
465  m_virtualBands = new QList<int>(*virtualBandList);
466  }
467 
475  QMutex *CubeIoHandler::dataFileMutex() {
476  return m_writeThreadMutex;
477  }
478 
482  int CubeIoHandler::bandCount() const {
483  return m_numBands;
484  }
485 
486 
490  int CubeIoHandler::getBandCountInChunk() const {
491  return m_bandsInChunk;
492  }
493 
494 
502  BigInt CubeIoHandler::getBytesPerChunk() const {
503  return m_samplesInChunk * m_linesInChunk * m_bandsInChunk *
504  SizeOf(m_pixelType);
505  }
506 
507 
512  int CubeIoHandler::getChunkCountInBandDimension() const {
513  return (int)ceil((double)m_numBands / (double)m_bandsInChunk);
514  }
515 
516 
521  int CubeIoHandler::getChunkCountInLineDimension() const {
522  return (int)ceil((double)m_numLines / (double)m_linesInChunk);
523  }
524 
525 
530  int CubeIoHandler::getChunkCountInSampleDimension() const {
531  return (int)ceil((double)m_numSamples / (double)m_samplesInChunk);
532  }
533 
534 
546  int CubeIoHandler::getChunkIndex(const RawCubeChunk &chunk) const {
547 // ASSERT(chunk.getStartSample() <= sampleCount());
548 // ASSERT(chunk.getStartLine() <= lineCount());
549 // ASSERT(chunk.getStartBand() <= bandCount());
550 
551  int sampleIndex = (chunk.getStartSample() - 1) / getSampleCountInChunk();
552  int lineIndex = (chunk.getStartLine() - 1) / getLineCountInChunk();
553  int bandIndex = (chunk.getStartBand() - 1) / getBandCountInChunk();
554 
555  int indexInBand =
556  sampleIndex + lineIndex * getChunkCountInSampleDimension();
557  int indexOffsetToBand = bandIndex * getChunkCountInSampleDimension() *
558  getChunkCountInLineDimension();
559 
560  return indexOffsetToBand + indexInBand;
561  }
562 
563 
567  BigInt CubeIoHandler::getDataStartByte() const {
568  return m_startByte;
569  }
570 
571 
578  QFile * CubeIoHandler::getDataFile() {
579  return m_dataFile;
580  }
581 
582 
587  int CubeIoHandler::lineCount() const {
588  return m_numLines;
589  }
590 
591 
595  int CubeIoHandler::getLineCountInChunk() const {
596  return m_linesInChunk;
597  }
598 
599 
603  PixelType CubeIoHandler::pixelType() const {
604  return m_pixelType;
605  }
606 
607 
612  int CubeIoHandler::sampleCount() const {
613  return m_numSamples;
614  }
615 
616 
620  int CubeIoHandler::getSampleCountInChunk() const {
621  return m_samplesInChunk;
622  }
623 
624 
637  void CubeIoHandler::setChunkSizes(
638  int numSamples, int numLines, int numBands) {
639  bool success = false;
640  IString msg;
641 
642  if(m_samplesInChunk != -1 || m_linesInChunk != -1 || m_bandsInChunk != -1) {
643  IString msg = "You cannot change the chunk sizes once set";
644  }
645  else if(numSamples < 1) {
646  msg = "Negative and zero chunk sizes are not supported, samples per chunk"
647  " cannot be [" + IString(numSamples) + "]";
648  }
649  else if(numLines < 1) {
650  msg = "Negative and zero chunk sizes are not supported, lines per chunk "
651  "cannot be [" + IString(numLines) + "]";
652  }
653  else if(numBands < 1) {
654  msg = "Negative and zero chunk sizes are not supported, lines per chunk "
655  "cannot be [" + IString(numBands) + "]";
656  }
657  else {
658  success = true;
659  }
660 
661  if(success) {
662  m_samplesInChunk = numSamples;
663  m_linesInChunk = numLines;
664  m_bandsInChunk = numBands;
665 
666  if(m_dataIsOnDiskMap) {
667  m_dataFile->resize(getDataStartByte() + getDataSize());
668  }
669  else if(m_dataFile->size() < getDataStartByte() + getDataSize()) {
670  success = false;
671  msg = "File size [" + IString((BigInt)m_dataFile->size()) +
672  " bytes] not big enough to hold data [" +
673  IString(getDataStartByte() + getDataSize()) + " bytes] where the "
674  "offset to the cube data is [" + IString(getDataStartByte()) +
675  " bytes]";
676  }
677  }
678  else {
679  throw IException(IException::Programmer, msg, _FILEINFO_);
680  }
681  }
682 
683 
690  void CubeIoHandler::blockUntilThreadPoolEmpty() const {
691  if (m_ioThreadPool) {
692  QMutexLocker lock(m_writeThreadMutex);
693  }
694  }
695 
696 
704  bool CubeIoHandler::bufferLessThan(Buffer * const &lhs, Buffer * const &rhs) {
705  bool lessThan = false;
706 
707  // If there is any overlap then we need to return false due to it being
708  // ambiguous.
709  Area3D lhsArea(
710  Displacement(lhs->Sample(), Displacement::Pixels),
711  Displacement(lhs->Line(), Displacement::Pixels),
712  Displacement(lhs->Band(), Displacement::Pixels),
713  Distance(lhs->SampleDimension() - 1, Distance::Pixels),
714  Distance(lhs->LineDimension() - 1, Distance::Pixels),
715  Distance(lhs->BandDimension() - 1, Distance::Pixels));
716  Area3D rhsArea(
717  Displacement(rhs->Sample(), Displacement::Pixels),
718  Displacement(rhs->Line(), Displacement::Pixels),
719  Displacement(rhs->Band(), Displacement::Pixels),
720  Distance(rhs->SampleDimension() - 1, Distance::Pixels),
721  Distance(rhs->LineDimension() - 1, Distance::Pixels),
722  Distance(rhs->BandDimension() - 1, Distance::Pixels));
723 
724  if (!lhsArea.intersect(rhsArea).isValid()) {
725  if (lhs->Band() != rhs->Band()) {
726  lessThan = lhs->Band() < rhs->Band();
727  }
728  else if (lhs->Line() != rhs->Line()) {
729  lessThan = lhs->Line() < rhs->Line();
730  }
731  else if (lhs->Sample() != rhs->Sample()) {
732  lessThan = lhs->Sample() < rhs->Sample();
733  }
734  }
735 
736  return lessThan;
737  }
738 
739 
753  QPair< QList<RawCubeChunk *>, QList<int> > CubeIoHandler::findCubeChunks(int startSample,
754  int numSamples, int startLine, int numLines, int startBand,
755  int numBands) const {
756  QList<RawCubeChunk *> results;
757  QList<int> resultBands;
758 /************************************************************************CHANGED THIS!!!!!!!!******/
759  int lastBand = startBand + numBands - 1;
760 // int lastBand = min(startBand + numBands - 1,
761 // bandCount());
762 /************************************************************************CHANGED THIS!!!!!!!!******/
763 
764  QRect areaInBand(
765  QPoint(max(startSample, 1),
766  max(startLine, 1)),
767  QPoint(min(startSample + numSamples - 1,
768  sampleCount()),
769  min(startLine + numLines - 1,
770  lineCount())));
771 
772  // We are considering only 1 band at a time.. we can't use m_bandsInChunk
773  // because of virtual bands, but every extra loop should not need extra
774  // IO.
775  for(int band = startBand; band <= lastBand; band ++) {
776  // This is the user-requested area in this band
777  QRect areaLeftInBand(areaInBand);
778 
779  int actualBand = band;
780 
781  if(m_virtualBands) {
782  if (band < 1 || band > m_virtualBands->size())
783  actualBand = 0;
784  else
785  actualBand = (m_virtualBands->at(band - 1) - 1) / m_bandsInChunk + 1;
786  }
787  // We will be consuming areaLeftInBand until we've got all of the area
788  // requested.
789  while(!areaLeftInBand.isEmpty()) {
837  int areaStartLine = areaLeftInBand.top();
838  int areaStartSample = areaLeftInBand.left();
839 
840  int initialChunkXPos = (areaStartSample - 1) / m_samplesInChunk;
841  int initialChunkYPos = (areaStartLine - 1) / m_linesInChunk;
842  int initialChunkZPos = (actualBand - 1) / m_bandsInChunk;
843  int initialChunkBand = initialChunkZPos * m_bandsInChunk + 1;
844 
845 
846  QRect chunkRect(initialChunkXPos * m_samplesInChunk + 1,
847  initialChunkYPos * m_linesInChunk + 1,
848  m_samplesInChunk, m_linesInChunk);
849 
850  // The chunk rectangle must intersect the remaining area that is in the
851  // current band, and the chunk's initial band must be between 1 and the
852  // number of physical bands in the cube (inclusive).
853  while(chunkRect.intersects(areaLeftInBand) &&
854  (initialChunkBand >= 1 && initialChunkBand <= bandCount())) {
855  int chunkXPos = (chunkRect.left() - 1) / m_samplesInChunk;
856  int chunkYPos = (chunkRect.top() - 1) / m_linesInChunk;
857  int chunkZPos = initialChunkZPos;
858 
859  // We now have an X,Y,Z position for the chunk. What's its index?
860  int chunkIndex = chunkXPos +
861  (chunkYPos * getChunkCountInSampleDimension()) +
862  (chunkZPos * getChunkCountInSampleDimension() *
863  getChunkCountInLineDimension());
864 
865  RawCubeChunk * newChunk = getChunk(chunkIndex, true);
866 
867  results.append(newChunk);
868  resultBands.append(band);
869 
870  chunkRect.moveLeft(chunkRect.right() + 1);
871  }
872 
873  areaLeftInBand.setTop(chunkRect.bottom() + 1);
874  }
875  }
876 
877  return QPair< QList<RawCubeChunk *>, QList<int> >(results, resultBands);
878  }
879 
880 
898  void CubeIoHandler::findIntersection(
899  const RawCubeChunk &cube1, const Buffer &cube2,
900  int &startX, int &startY, int &startZ,
901  int &endX, int &endY, int &endZ) const {
902  // So we have 2 3D "cubes" (not Cube cubes but 3d areas) we need to
903  // intersect in order to figure out what chunk data goes into the output
904  // buffer.
905 
906  // To find the band range we actually have to map all of the bands from
907  // virtual bands to physical bands
908  int startVBand = cube2.Band();
909  int endVBand = startVBand + cube2.BandDimension() - 1;
910 
911  int startPhysicalBand = 0;
912  int endPhysicalBand = 0;
913 
914  bool startVBandFound = false;
915  for(int virtualBand = startVBand; virtualBand <= endVBand; virtualBand ++) {
916  int physicalBand = virtualBand;
917 
918  bool bandExists = true;
919  if(m_virtualBands) {
920  if (virtualBand < 1 || virtualBand > m_virtualBands->size())
921  bandExists = false;
922  else {
923  physicalBand = m_virtualBands->at(virtualBand - 1);
924  }
925  }
926 
927  if (bandExists) {
928  if(!startVBandFound) {
929  startPhysicalBand = physicalBand;
930  endPhysicalBand = physicalBand;
931  startVBandFound = true;
932  }
933  else {
934  if(physicalBand < startPhysicalBand)
935  startPhysicalBand = physicalBand;
936 
937  if(physicalBand > endPhysicalBand)
938  endPhysicalBand = physicalBand;
939  }
940  }
941  }
942 
943  startX = max(cube1.getStartSample(), cube2.Sample());
944  startY = max(cube1.getStartLine(), cube2.Line());
945  startZ = max(cube1.getStartBand(), startPhysicalBand);
946  endX = min(cube1.getStartSample() + cube1.sampleCount() - 1,
947  cube2.Sample() + cube2.SampleDimension() - 1);
948  endY = min(cube1.getStartLine() + cube1.lineCount() - 1,
949  cube2.Line() + cube2.LineDimension() - 1);
950  endZ = min(cube1.getStartBand() + cube1.bandCount() - 1,
951  endPhysicalBand);
952  }
953 
954 
965  void CubeIoHandler::flushWriteCache(bool force) const {
966  if (m_ioThreadPool) {
967  bool shouldFlush = m_writeCache->second.size() >= m_idealFlushSize ||
968  force;
969  bool cacheOverflowing =
970  (m_writeCache->second.size() > m_idealFlushSize * 10);
971  bool shouldAndCanFlush = false;
972  bool forceStart = force;
973 
974  if (shouldFlush) {
975  shouldAndCanFlush = m_writeThreadMutex->tryLock();
976  if (shouldAndCanFlush) {
977  m_writeThreadMutex->unlock();
978  }
979  }
980 
981  if (cacheOverflowing && !shouldAndCanFlush) {
982  forceStart = true;
983  m_consecutiveOverflowCount++;
984  }
985 
986  if (forceStart) {
987  blockUntilThreadPoolEmpty();
988 
989  if (m_writeCache->second.size() != 0) {
990  m_idealFlushSize = m_writeCache->second.size();
991  shouldFlush = true;
992  shouldAndCanFlush = true;
993  }
994  }
995  else if (!cacheOverflowing && shouldAndCanFlush) {
996  m_consecutiveOverflowCount = 0;
997  }
998 
999  if (cacheOverflowing && m_useOptimizedCubeWrite) {
1000  blockUntilThreadPoolEmpty();
1001 
1002  // If the process is very I/O bound, then write caching isn't helping
1003  // anything. In fact, it hurts, so turn it off.
1004  if (m_consecutiveOverflowCount > 10) {
1005  delete m_ioThreadPool;
1006  m_ioThreadPool = NULL;
1007  }
1008 
1009  // Write it all synchronously.
1010  foreach (Buffer *bufferToWrite, m_writeCache->second) {
1011  const_cast<CubeIoHandler *>(this)->synchronousWrite(*bufferToWrite);
1012  delete bufferToWrite;
1013  }
1014 
1015  m_writeCache->second.clear();
1016  }
1017 
1018  if (shouldAndCanFlush && m_ioThreadPool) {
1019  QMutexLocker locker(m_writeCache->first);
1020  QRunnable *writer = new BufferToChunkWriter(
1021  const_cast<CubeIoHandler *>(this), m_writeCache->second);
1022 
1023  m_ioThreadPool->start(writer);
1024 
1025  m_writeCache->second.clear();
1026  m_lastOperationWasWrite = true;
1027  }
1028 
1029  if (force) {
1030  blockUntilThreadPoolEmpty();
1031  }
1032  }
1033  }
1034 
1035 
1042  void CubeIoHandler::freeChunk(RawCubeChunk *chunkToFree) const {
1043  if(chunkToFree && m_rawData) {
1044  int chunkIndex = getChunkIndex(*chunkToFree);
1045 
1046  m_rawData->erase(m_rawData->find(chunkIndex));
1047 
1048  if(chunkToFree->isDirty())
1049  (const_cast<CubeIoHandler *>(this))->writeRaw(*chunkToFree);
1050 
1051  delete chunkToFree;
1052 
1053  if(m_lastProcessByLineChunks) {
1054  delete m_lastProcessByLineChunks;
1055  m_lastProcessByLineChunks = NULL;
1056  }
1057  }
1058  }
1059 
1060 
1070  RawCubeChunk *CubeIoHandler::getChunk(int chunkIndex,
1071  bool allocateIfNecessary) const {
1072  RawCubeChunk *chunk = NULL;
1073 
1074  if(m_rawData) {
1075  chunk = m_rawData->value(chunkIndex);
1076  }
1077 
1078  if(allocateIfNecessary && !chunk) {
1079  if(m_dataIsOnDiskMap && !(*m_dataIsOnDiskMap)[chunkIndex]) {
1080  chunk = getNullChunk(chunkIndex);
1081  (*m_dataIsOnDiskMap)[chunkIndex] = true;
1082  }
1083  else {
1084  int startSample;
1085  int startLine;
1086  int startBand;
1087  int endSample;
1088  int endLine;
1089  int endBand;
1090  getChunkPlacement(chunkIndex, startSample, startLine, startBand,
1091  endSample, endLine, endBand);
1092  chunk = new RawCubeChunk(startSample, startLine, startBand,
1093  endSample, endLine, endBand,
1094  getBytesPerChunk());
1095 
1096  (const_cast<CubeIoHandler *>(this))->readRaw(*chunk);
1097  chunk->setDirty(false);
1098  }
1099 
1100  (*m_rawData)[chunkIndex] = chunk;
1101  }
1102 
1103  return chunk;
1104  }
1105 
1106 
1111  int CubeIoHandler::getChunkCount() const {
1112  return getChunkCountInSampleDimension() *
1113  getChunkCountInLineDimension() *
1114  getChunkCountInBandDimension();
1115  }
1116 
1117 
1129  void CubeIoHandler::getChunkPlacement(int chunkIndex,
1130  int &startSample, int &startLine, int &startBand,
1131  int &endSample, int &endLine, int &endBand) const {
1132  int chunkSampleIndex = chunkIndex % getChunkCountInSampleDimension();
1133 
1134  chunkIndex =
1135  (chunkIndex - chunkSampleIndex) / getChunkCountInSampleDimension();
1136 
1137  int chunkLineIndex = chunkIndex % getChunkCountInLineDimension();
1138  chunkIndex = (chunkIndex - chunkLineIndex) / getChunkCountInLineDimension();
1139 
1140  int chunkBandIndex = chunkIndex;
1141 
1142  startSample = chunkSampleIndex * getSampleCountInChunk() + 1;
1143  endSample = startSample + getSampleCountInChunk() - 1;
1144  startLine = chunkLineIndex * getLineCountInChunk() + 1;
1145  endLine = startLine + getLineCountInChunk() - 1;
1146  startBand = chunkBandIndex * getBandCountInChunk() + 1;
1147  endBand = startBand + getBandCountInChunk() - 1;
1148  }
1149 
1150 
1161  RawCubeChunk *CubeIoHandler::getNullChunk(int chunkIndex) const {
1162  // Shouldn't ask for null chunks when the area has already been allocated
1163 // ASSERT(getChunk(chunkIndex) == NULL);
1164 
1165  int startSample = 0;
1166  int startLine = 0;
1167  int startBand = 0;
1168 
1169  int endSample = 0;
1170  int endLine = 0;
1171  int endBand = 0;
1172 
1173  getChunkPlacement(chunkIndex, startSample, startLine, startBand,
1174  endSample, endLine, endBand);
1175 
1176  RawCubeChunk *result = new RawCubeChunk(startSample, startLine, startBand,
1177  endSample, endLine, endBand, getBytesPerChunk());
1178 
1179  if(!m_nullChunkData) {
1180  // the pixel type doesn't really matter, so pick something small
1181  Brick nullBuffer(result->sampleCount(),
1182  result->lineCount(),
1183  result->bandCount(),
1184  UnsignedByte);
1185 
1186  nullBuffer.SetBasePosition(result->getStartSample(),
1187  result->getStartLine(),
1188  result->getStartBand());
1189  for(int i = 0; i < nullBuffer.size(); i++) {
1190  nullBuffer[i] = Null;
1191  }
1192 
1193  writeIntoRaw(nullBuffer, *result, result->getStartBand());
1194  m_nullChunkData = new QByteArray(result->getRawData());
1195  }
1196  else {
1197  result->setRawData(*m_nullChunkData);
1198  }
1199 
1200  result->setDirty(true);
1201  return result;
1202  }
1203 
1204 
1214  void CubeIoHandler::minimizeCache(const QList<RawCubeChunk *> &justUsed,
1215  const Buffer &justRequested) const {
1216  // Since we have a lock on the cache, no newly created threads can utilize
1217  // or access any cache data until we're done.
1218  if (m_rawData->size() * getBytesPerChunk() > 1 * 1024 * 1024 ||
1219  m_cachingAlgorithms->size() > 1) {
1220  bool algorithmAccepted = false;
1221 
1222  int algorithmIndex = 0;
1223  while(!algorithmAccepted &&
1224  algorithmIndex < m_cachingAlgorithms->size()) {
1225  CubeCachingAlgorithm *algorithm = (*m_cachingAlgorithms)[algorithmIndex];
1226 
1228  algorithm->recommendChunksToFree(m_rawData->values(), justUsed,
1229  justRequested);
1230 
1231  algorithmAccepted = result.algorithmUnderstoodData();
1232 
1233  if(algorithmAccepted) {
1234  QList<RawCubeChunk *> chunksToFree = result.getChunksToFree();
1235 
1236  RawCubeChunk *chunkToFree;
1237  foreach(chunkToFree, chunksToFree) {
1238  freeChunk(chunkToFree);
1239  }
1240  }
1241 
1242  algorithmIndex ++;
1243  }
1244 
1245  // Fall back - no algorithms liked us :(
1246  if(!algorithmAccepted && m_rawData->size() > 100) {
1247  // This (minimizeCache()) is typically executed in the Runnable thread.
1248  // We don't want to wait for ourselves.
1249  clearCache(false);
1250  }
1251  }
1252  }
1253 
1254 
1263  void CubeIoHandler::synchronousWrite(const Buffer &bufferToWrite) {
1264  QList<RawCubeChunk *> cubeChunks;
1265  QList<int> cubeChunkBands;
1266 
1267  int bufferSampleCount = bufferToWrite.SampleDimension();
1268  int bufferLineCount = bufferToWrite.LineDimension();
1269  int bufferBandCount = bufferToWrite.BandDimension();
1270 
1271  // process by line optimization
1272  if(m_lastProcessByLineChunks &&
1273  m_lastProcessByLineChunks->size()) {
1274  // Not optimized yet, let's see if we can optimize
1275  if(bufferToWrite.Sample() == 1 &&
1276  bufferSampleCount == sampleCount() &&
1277  bufferLineCount == 1 &&
1278  bufferBandCount == 1) {
1279  // We look like a process by line, are we using the same chunks as
1280  // before?
1281  int bufferLine = bufferToWrite.Line();
1282  int chunkStartLine =
1283  (*m_lastProcessByLineChunks)[0]->getStartLine();
1284  int chunkLines =
1285  (*m_lastProcessByLineChunks)[0]->lineCount();
1286  int bufferBand = bufferToWrite.Band();
1287  int chunkStartBand =
1288  (*m_lastProcessByLineChunks)[0]->getStartBand();
1289  int chunkBands =
1290  (*m_lastProcessByLineChunks)[0]->bandCount();
1291 
1292  if(bufferLine >= chunkStartLine &&
1293  bufferLine <= chunkStartLine + chunkLines - 1 &&
1294  bufferBand >= chunkStartBand &&
1295  bufferBand <= chunkStartBand + chunkBands - 1) {
1296  cubeChunks = *m_lastProcessByLineChunks;
1297  for (int i = 0; i < cubeChunks.size(); i++) {
1298  cubeChunkBands.append( cubeChunks[i]->getStartBand() );
1299  }
1300  }
1301  }
1302  }
1303  // Processing by chunk size
1304  else if (bufferSampleCount == m_samplesInChunk &&
1305  bufferLineCount == m_linesInChunk &&
1306  bufferBandCount == m_bandsInChunk) {
1307  int bufferStartSample = bufferToWrite.Sample();
1308  int bufferStartLine = bufferToWrite.Line();
1309  int bufferStartBand = bufferToWrite.Band();
1310 
1311  int bufferEndSample = bufferStartSample + bufferSampleCount - 1;
1312  int bufferEndLine = bufferStartLine + bufferLineCount - 1;
1313  int bufferEndBand = bufferStartBand + bufferBandCount - 1;
1314 
1315  int expectedChunkIndex =
1316  ((bufferStartSample - 1) / getSampleCountInChunk()) +
1317  ((bufferStartLine - 1) / getLineCountInChunk()) *
1318  getChunkCountInSampleDimension() +
1319  ((bufferStartBand - 1) / getBandCountInChunk()) *
1320  getChunkCountInSampleDimension() *
1321  getChunkCountInLineDimension();
1322 
1323  int chunkStartSample, chunkStartLine, chunkStartBand,
1324  chunkEndSample, chunkEndLine, chunkEndBand;
1325 
1326  getChunkPlacement(expectedChunkIndex,
1327  chunkStartSample, chunkStartLine, chunkStartBand,
1328  chunkEndSample, chunkEndLine, chunkEndBand);
1329 
1330  if (chunkStartSample == bufferStartSample &&
1331  chunkStartLine == bufferStartLine &&
1332  chunkStartBand == bufferStartBand &&
1333  chunkEndSample == bufferEndSample &&
1334  chunkEndLine == bufferEndLine &&
1335  chunkEndBand == bufferEndBand) {
1336  cubeChunks.append(getChunk(expectedChunkIndex, true));
1337  cubeChunkBands.append(cubeChunks.last()->getStartBand());
1338  }
1339  }
1340 
1342  if(cubeChunks.empty()) {
1343  chunkInfo = findCubeChunks(
1344  bufferToWrite.Sample(), bufferSampleCount,
1345  bufferToWrite.Line(), bufferLineCount,
1346  bufferToWrite.Band(), bufferBandCount);
1347  cubeChunks = chunkInfo.first;
1348  cubeChunkBands = chunkInfo.second;
1349  }
1350 
1351  // process by line optimization
1352  if(bufferToWrite.Sample() == 1 &&
1353  bufferSampleCount == sampleCount() &&
1354  bufferLineCount == 1 &&
1355  bufferBandCount == 1) {
1356  if(!m_lastProcessByLineChunks)
1357  m_lastProcessByLineChunks =
1358  new QList<RawCubeChunk *>(cubeChunks);
1359  else
1360  *m_lastProcessByLineChunks = cubeChunks;
1361  }
1362 
1363  for(int i = 0; i < cubeChunks.size(); i++) {
1364  writeIntoRaw(bufferToWrite, *cubeChunks[i], cubeChunkBands[i]);
1365  }
1366 
1367  minimizeCache(cubeChunks, bufferToWrite);
1368  }
1369 
1370 
1377  void CubeIoHandler::writeIntoDouble(const RawCubeChunk &chunk,
1378  Buffer &output, int index) const {
1379  // The code in this method is highly optimized. Even the order of the if
1380  // statements will have a significant impact on performance if changed.
1381  // Also, there is a lot of duplicate code in both writeIntoDouble(...) and
1382  // writeIntoRaw(...). This is needed for performance gain. Any function
1383  // or method calls from within the x loop cause significant performance
1384  // decreases.
1385  int startX = 0;
1386  int startY = 0;
1387  int startZ = 0;
1388 
1389  int endX = 0;
1390  int endY = 0;
1391  int endZ = 0;
1392 
1393  findIntersection(chunk, output, startX, startY, startZ, endX, endY, endZ);
1394 
1395  int bufferBand = output.Band();
1396  int bufferBands = output.BandDimension();
1397  int chunkStartSample = chunk.getStartSample();
1398  int chunkStartLine = chunk.getStartLine();
1399  int chunkStartBand = chunk.getStartBand();
1400  int chunkLineSize = chunk.sampleCount();
1401  int chunkBandSize = chunkLineSize * chunk.lineCount();
1402  //double *buffersDoubleBuf = output.p_buf;
1403  double *buffersDoubleBuf = output.DoubleBuffer();
1404  const char *chunkBuf = chunk.getRawData().data();
1405  char *buffersRawBuf = (char *)output.RawBuffer();
1406 
1407  for(int z = startZ; z <= endZ; z++) {
1408  const int &bandIntoChunk = z - chunkStartBand;
1409  int virtualBand = z;
1410 
1411  virtualBand = index;
1412 
1413  if(virtualBand != 0 && virtualBand >= bufferBand &&
1414  virtualBand <= bufferBand + bufferBands - 1) {
1415 
1416  for(int y = startY; y <= endY; y++) {
1417  const int &lineIntoChunk = y - chunkStartLine;
1418  int bufferIndex = output.Index(startX, y, virtualBand);
1419 
1420  for(int x = startX; x <= endX; x++) {
1421  const int &sampleIntoChunk = x - chunkStartSample;
1422 
1423  const int &chunkIndex = sampleIntoChunk +
1424  (chunkLineSize * lineIntoChunk) +
1425  (chunkBandSize * bandIntoChunk);
1426 
1427  double &bufferVal = buffersDoubleBuf[bufferIndex];
1428 
1429  if(m_pixelType == Real) {
1430  float raw = ((float *)chunkBuf)[chunkIndex];
1431  if(m_byteSwapper)
1432  raw = m_byteSwapper->Float(&raw);
1433 
1434  if(raw >= VALID_MIN4) {
1435  bufferVal = (double) raw;
1436  }
1437  else {
1438  if(raw == NULL4)
1439  bufferVal = NULL8;
1440  else if(raw == LOW_INSTR_SAT4)
1441  bufferVal = LOW_INSTR_SAT8;
1442  else if(raw == LOW_REPR_SAT4)
1443  bufferVal = LOW_REPR_SAT8;
1444  else if(raw == HIGH_INSTR_SAT4)
1445  bufferVal = HIGH_INSTR_SAT8;
1446  else if(raw == HIGH_REPR_SAT4)
1447  bufferVal = HIGH_REPR_SAT8;
1448  else
1449  bufferVal = LOW_REPR_SAT8;
1450  }
1451 
1452  ((float *)buffersRawBuf)[bufferIndex] = raw;
1453  }
1454 
1455  else if(m_pixelType == SignedWord) {
1456  short raw = ((short *)chunkBuf)[chunkIndex];
1457  if(m_byteSwapper)
1458  raw = m_byteSwapper->ShortInt(&raw);
1459 
1460  if(raw >= VALID_MIN2) {
1461  bufferVal = (double) raw * m_multiplier + m_base;
1462  }
1463  else {
1464  if(raw == NULL2)
1465  bufferVal = NULL8;
1466  else if(raw == LOW_INSTR_SAT2)
1467  bufferVal = LOW_INSTR_SAT8;
1468  else if(raw == LOW_REPR_SAT2)
1469  bufferVal = LOW_REPR_SAT8;
1470  else if(raw == HIGH_INSTR_SAT2)
1471  bufferVal = HIGH_INSTR_SAT8;
1472  else if(raw == HIGH_REPR_SAT2)
1473  bufferVal = HIGH_REPR_SAT8;
1474  else
1475  bufferVal = LOW_REPR_SAT8;
1476  }
1477 
1478  ((short *)buffersRawBuf)[bufferIndex] = raw;
1479  }
1480 
1481 
1482  else if(m_pixelType == UnsignedWord) {
1483  unsigned short raw = ((unsigned short *)chunkBuf)[chunkIndex];
1484  if(m_byteSwapper)
1485  raw = m_byteSwapper->UnsignedShortInt(&raw);
1486 
1487  if(raw >= VALID_MINU2) {
1488  bufferVal = (double) raw * m_multiplier + m_base;
1489  }
1490  else if (raw > VALID_MAXU2) {
1491  if(raw == HIGH_INSTR_SATU2)
1492  bufferVal = HIGH_INSTR_SAT8;
1493  else if(raw == HIGH_REPR_SATU2)
1494  bufferVal = HIGH_REPR_SAT8;
1495  else
1496  bufferVal = LOW_REPR_SAT8;
1497  }
1498  else {
1499  if(raw == NULLU2)
1500  bufferVal = NULL8;
1501  else if(raw == LOW_INSTR_SATU2)
1502  bufferVal = LOW_INSTR_SAT8;
1503  else if(raw == LOW_REPR_SATU2)
1504  bufferVal = LOW_REPR_SAT8;
1505  else
1506  bufferVal = LOW_REPR_SAT8;
1507  }
1508 
1509  ((unsigned short *)buffersRawBuf)[bufferIndex] = raw;
1510  }
1511 
1512  else if(m_pixelType == UnsignedByte) {
1513  unsigned char raw = ((unsigned char *)chunkBuf)[chunkIndex];
1514 
1515  if(raw == NULL1) {
1516  bufferVal = NULL8;
1517  }
1518  else if(raw == HIGH_REPR_SAT1) {
1519  bufferVal = HIGH_REPR_SAT8;
1520  }
1521  else {
1522  bufferVal = (double) raw * m_multiplier + m_base;
1523  }
1524 
1525  ((unsigned char *)buffersRawBuf)[bufferIndex] = raw;
1526  }
1527 
1528  bufferIndex ++;
1529  }
1530  }
1531  }
1532  }
1533  }
1534 
1535 
1542  void CubeIoHandler::writeIntoRaw(const Buffer &buffer, RawCubeChunk &output, int index)
1543  const {
1544  // The code in this method is highly optimized. Even the order of the if
1545  // statements will have a significant impact on performance if changed.
1546  // Also, there is a lot of duplicate code in both writeIntoDouble(...) and
1547  // writeIntoRaw(...). This is needed for performance gain. Any function
1548  // or method calls from within the x loop cause significant performance
1549  // decreases.
1550  int startX = 0;
1551  int startY = 0;
1552  int startZ = 0;
1553 
1554  int endX = 0;
1555  int endY = 0;
1556  int endZ = 0;
1557 
1558  output.setDirty(true);
1559  findIntersection(output, buffer, startX, startY, startZ, endX, endY, endZ);
1560 
1561  int bufferBand = buffer.Band();
1562  int bufferBands = buffer.BandDimension();
1563  int outputStartSample = output.getStartSample();
1564  int outputStartLine = output.getStartLine();
1565  int outputStartBand = output.getStartBand();
1566  int lineSize = output.sampleCount();
1567  int bandSize = lineSize * output.lineCount();
1568  double *buffersDoubleBuf = buffer.DoubleBuffer();
1569  char *chunkBuf = output.getRawData().data();
1570 
1571  for(int z = startZ; z <= endZ; z++) {
1572  const int &bandIntoChunk = z - outputStartBand;
1573  int virtualBand = index;
1574 
1575 
1576  if(m_virtualBands) {
1577  virtualBand = m_virtualBands->indexOf(virtualBand) + 1;
1578  }
1579 
1580  if(virtualBand != 0 && virtualBand >= bufferBand &&
1581  virtualBand <= bufferBand + bufferBands - 1) {
1582 
1583  for(int y = startY; y <= endY; y++) {
1584  const int &lineIntoChunk = y - outputStartLine;
1585  int bufferIndex = buffer.Index(startX, y, virtualBand);
1586 
1587  for(int x = startX; x <= endX; x++) {
1588  const int &sampleIntoChunk = x - outputStartSample;
1589 
1590  const int &chunkIndex = sampleIntoChunk +
1591  (lineSize * lineIntoChunk) + (bandSize * bandIntoChunk);
1592 
1593  double bufferVal = buffersDoubleBuf[bufferIndex];
1594 
1595  if(m_pixelType == Real) {
1596  float raw = 0;
1597 
1598  if(bufferVal >= VALID_MIN8) {
1599  double filePixelValueDbl = (bufferVal - m_base) /
1600  m_multiplier;
1601 
1602  if(filePixelValueDbl < (double) VALID_MIN4) {
1603  raw = LOW_REPR_SAT4;
1604  }
1605  else if(filePixelValueDbl > (double) VALID_MAX4) {
1606  raw = HIGH_REPR_SAT4;
1607  }
1608  else {
1609  raw = (float) filePixelValueDbl;
1610  }
1611  }
1612  else {
1613  if(bufferVal == NULL8)
1614  raw = NULL4;
1615  else if(bufferVal == LOW_INSTR_SAT8)
1616  raw = LOW_INSTR_SAT4;
1617  else if(bufferVal == LOW_REPR_SAT8)
1618  raw = LOW_REPR_SAT4;
1619  else if(bufferVal == HIGH_INSTR_SAT8)
1620  raw = HIGH_INSTR_SAT4;
1621  else if(bufferVal == HIGH_REPR_SAT8)
1622  raw = HIGH_REPR_SAT4;
1623  else
1624  raw = LOW_REPR_SAT4;
1625  }
1626  ((float *)chunkBuf)[chunkIndex] =
1627  m_byteSwapper ? m_byteSwapper->Float(&raw) : raw;
1628  }
1629  else if(m_pixelType == SignedWord) {
1630  short raw;
1631 
1632  if(bufferVal >= VALID_MIN8) {
1633  double filePixelValueDbl = (bufferVal - m_base) /
1634  m_multiplier;
1635  if(filePixelValueDbl < VALID_MIN2 - 0.5) {
1636  raw = LOW_REPR_SAT2;
1637  }
1638  if(filePixelValueDbl > VALID_MAX2 + 0.5) {
1639  raw = HIGH_REPR_SAT2;
1640  }
1641  else {
1642  int filePixelValue = (int)round(filePixelValueDbl);
1643 
1644  if(filePixelValue < VALID_MIN2) {
1645  raw = LOW_REPR_SAT2;
1646  }
1647  else if(filePixelValue > VALID_MAX2) {
1648  raw = HIGH_REPR_SAT2;
1649  }
1650  else {
1651  raw = filePixelValue;
1652  }
1653  }
1654  }
1655  else {
1656  if(bufferVal == NULL8)
1657  raw = NULL2;
1658  else if(bufferVal == LOW_INSTR_SAT8)
1659  raw = LOW_INSTR_SAT2;
1660  else if(bufferVal == LOW_REPR_SAT8)
1661  raw = LOW_REPR_SAT2;
1662  else if(bufferVal == HIGH_INSTR_SAT8)
1663  raw = HIGH_INSTR_SAT2;
1664  else if(bufferVal == HIGH_REPR_SAT8)
1665  raw = HIGH_REPR_SAT2;
1666  else
1667  raw = LOW_REPR_SAT2;
1668  }
1669 
1670  ((short *)chunkBuf)[chunkIndex] =
1671  m_byteSwapper ? m_byteSwapper->ShortInt(&raw) : raw;
1672  }
1673  else if(m_pixelType == UnsignedWord) {
1674  unsigned short raw;
1675 
1676  if(bufferVal >= VALID_MIN8) {
1677  double filePixelValueDbl = (bufferVal - m_base) /
1678  m_multiplier;
1679  if(filePixelValueDbl < VALID_MINU2 - 0.5) {
1680  raw = LOW_REPR_SATU2;
1681  }
1682  if(filePixelValueDbl > VALID_MAXU2 + 0.5) {
1683  raw = HIGH_REPR_SATU2;
1684  }
1685  else {
1686  int filePixelValue = (int)round(filePixelValueDbl);
1687 
1688  if(filePixelValue < VALID_MINU2) {
1689  raw = LOW_REPR_SATU2;
1690  }
1691  else if(filePixelValue > VALID_MAXU2) {
1692  raw = HIGH_REPR_SATU2;
1693  }
1694  else {
1695  raw = filePixelValue;
1696  }
1697  }
1698  }
1699  else {
1700  if(bufferVal == NULL8)
1701  raw = NULLU2;
1702  else if(bufferVal == LOW_INSTR_SAT8)
1703  raw = LOW_INSTR_SATU2;
1704  else if(bufferVal == LOW_REPR_SAT8)
1705  raw = LOW_REPR_SATU2;
1706  else if(bufferVal == HIGH_INSTR_SAT8)
1707  raw = HIGH_INSTR_SATU2;
1708  else if(bufferVal == HIGH_REPR_SAT8)
1709  raw = HIGH_REPR_SATU2;
1710  else
1711  raw = LOW_REPR_SATU2;
1712  }
1713 
1714  ((unsigned short *)chunkBuf)[chunkIndex] =
1715  m_byteSwapper ? m_byteSwapper->UnsignedShortInt(&raw) : raw;
1716  }
1717  else if(m_pixelType == UnsignedByte) {
1718  unsigned char raw;
1719 
1720  if(bufferVal >= VALID_MIN8) {
1721  double filePixelValueDbl = (bufferVal - m_base) /
1722  m_multiplier;
1723  if(filePixelValueDbl < VALID_MIN1 - 0.5) {
1724  raw = LOW_REPR_SAT1;
1725  }
1726  else if(filePixelValueDbl > VALID_MAX1 + 0.5) {
1727  raw = HIGH_REPR_SAT1;
1728  }
1729  else {
1730  int filePixelValue = (int)(filePixelValueDbl + 0.5);
1731  if(filePixelValue < VALID_MIN1) {
1732  raw = LOW_REPR_SAT1;
1733  }
1734  else if(filePixelValue > VALID_MAX1) {
1735  raw = HIGH_REPR_SAT1;
1736  }
1737  else {
1738  raw = (unsigned char)(filePixelValue);
1739  }
1740  }
1741  }
1742  else {
1743  if(bufferVal == NULL8)
1744  raw = NULL1;
1745  else if(bufferVal == LOW_INSTR_SAT8)
1746  raw = LOW_INSTR_SAT1;
1747  else if(bufferVal == LOW_REPR_SAT8)
1748  raw = LOW_REPR_SAT1;
1749  else if(bufferVal == HIGH_INSTR_SAT8)
1750  raw = HIGH_INSTR_SAT1;
1751  else if(bufferVal == HIGH_REPR_SAT8)
1752  raw = HIGH_REPR_SAT1;
1753  else
1754  raw = LOW_REPR_SAT1;
1755  }
1756 
1757  ((unsigned char *)chunkBuf)[chunkIndex] = raw;
1758  }
1759 
1760  bufferIndex ++;
1761  }
1762  }
1763  }
1764  }
1765  }
1766 
1767 
1771  void CubeIoHandler::writeNullDataToDisk() const {
1772  if(!m_dataIsOnDiskMap) {
1773  IString msg = "Cannot call CubeIoHandler::writeNullDataToDisk unless "
1774  "data is not already on disk (Cube::Create was called)";
1775  throw IException(IException::Programmer, msg, _FILEINFO_);
1776  }
1777 
1778  int numChunks = getChunkCount();
1779  for(int i = 0; i < numChunks; i++) {
1780  if(!(*m_dataIsOnDiskMap)[i]) {
1781  RawCubeChunk *nullChunk = getNullChunk(i);
1782  (const_cast<CubeIoHandler *>(this))->writeRaw(*nullChunk);
1783  (*m_dataIsOnDiskMap)[i] = true;
1784 
1785  delete nullChunk;
1786  nullChunk = NULL;
1787  }
1788  }
1789  }
1790 
1791 
1805  CubeIoHandler::BufferToChunkWriter::BufferToChunkWriter(
1806  CubeIoHandler * ioHandler, QList<Buffer *> buffersToWrite) {
1807  m_ioHandler = ioHandler;
1808  m_buffersToWrite = new QList<Buffer *>(buffersToWrite);
1809  m_timer = new QTime;
1810  m_timer->start();
1811 
1812  m_ioHandler->m_writeThreadMutex->lock();
1813  }
1814 
1815 
1826  CubeIoHandler::BufferToChunkWriter::~BufferToChunkWriter() {
1827  int elapsedMs = m_timer->elapsed();
1828  int idealFlushElapsedTime = 100; // ms
1829 
1830  // We want to aim our flush size at 100ms, so adjust accordingly to aim at
1831  // our target. This method seems to be extremely effective because
1832  // we maximize our I/O calls when caching is interfering and normalize
1833  // them otherwise.
1834  int msOffIdeal = elapsedMs - idealFlushElapsedTime;
1835  double percentOffIdeal = msOffIdeal / (double)idealFlushElapsedTime;
1836 
1837  // flush size is bounded to [32, 5000]
1838  int currentCacheSize = m_ioHandler->m_idealFlushSize;
1839  int desiredAdjustment = -1 * currentCacheSize * percentOffIdeal;
1840  int desiredCacheSize = (int)(currentCacheSize + desiredAdjustment);
1841  m_ioHandler->m_idealFlushSize = (int)(qMin(5000,
1842  qMax(32, desiredCacheSize)));
1843 
1844  delete m_timer;
1845  m_timer = NULL;
1846 
1847  m_ioHandler->m_writeThreadMutex->unlock();
1848  m_ioHandler = NULL;
1849 
1850  ASSERT(m_buffersToWrite->isEmpty());
1851  delete m_buffersToWrite;
1852  m_buffersToWrite = NULL;
1853  }
1854 
1855 
1861  void CubeIoHandler::BufferToChunkWriter::run() {
1862  // Sorting the buffers didn't seem to have a large positive impact on speed,
1863  // but does increase complexity so it's disabled.
1864 // QList<Buffer * > buffersToWrite(*m_buffersToWrite);
1865 // qSort(buffersToWrite.begin(), buffersToWrite.end(), bufferLessThan);
1866 
1867  // If the buffers have any overlap at all then we can't sort them and still
1868  // guarantee the last write() call makes it into the correct place. The
1869  // bufferLessThan is guaranteed to return false if there is overlap.
1870 // bool sortable = true;
1871 // for (int i = 1; sortable && i < buffersToWrite.size(); i++) {
1872 // if (!bufferLessThan(buffersToWrite[i - 1], buffersToWrite[i])) {
1873 // sortable = false;
1874 // }
1875 // }
1876 
1877 // if (!sortable) {
1878 // buffersToWrite = *m_buffersToWrite;
1879 // }
1880 
1881  foreach (Buffer * bufferToWrite, *m_buffersToWrite) {
1882  m_ioHandler->synchronousWrite(*bufferToWrite);
1883  delete bufferToWrite;
1884  }
1885 
1886  m_buffersToWrite->clear();
1887  m_ioHandler->m_dataFile->flush();
1888  }
1889 }
Buffer for reading and writing cube data.
Definition: Buffer.h:68
int Line(const int index=0) const
Returns the line position associated with a shape buffer index.
Definition: Buffer.cpp:161
void setRawData(QByteArray rawData)
Sets the chunk&#39;s raw data.
int getStartLine() const
Definition: RawCubeChunk.h:75
const double Null
Value for an Isis Null pixel.
Definition: SpecialPixel.h:109
int LineDimension() const
Returns the number of lines in the shape buffer.
Definition: Buffer.h:94
PvlGroupIterator findGroup(const QString &name, PvlGroupIterator beg, PvlGroupIterator end)
Find a group with the specified name, within these indexes.
Definition: PvlObject.h:141
Represents a 3D area (a 3D &quot;cube&quot;)
Definition: Area3D.h:41
int BandDimension() const
Returns the number of bands in the shape buffer.
Definition: Buffer.h:103
QList< RawCubeChunk * > getChunksToFree() const
bool isDirty() const
PvlObjectIterator findObject(const QString &name, PvlObjectIterator beg, PvlObjectIterator end)
Find the index of object with a specified name, between two indexes.
Definition: PvlObject.h:286
int SizeOf(Isis::PixelType pixelType)
Returns the number of bytes of the specified PixelType.
Definition: PixelType.h:62
Buffer for containing a three dimensional section of an image.
Definition: Brick.h:60
void SetBasePosition(const int start_sample, const int start_line, const int start_band)
This method is used to set the base position of the shape buffer.
Definition: Brick.h:112
double * DoubleBuffer() const
Returns the value of the shape buffer.
Definition: Buffer.h:153
int Sample(const int index=0) const
Returns the sample position associated with a shape buffer index.
Definition: Buffer.cpp:143
Unless noted otherwise, the portions of Isis written by the USGS are public domain.
Unless noted otherwise, the portions of Isis written by the USGS are public domain.
void setDirty(bool dirty)
Sets the chunk&#39;s dirty flag, indicating whether or not the chunk&#39;s data matches the data that is on d...
int lineCount() const
Definition: RawCubeChunk.h:96
Distance measurement, usually in meters.
Definition: Distance.h:47
PixelType
Enumerations for Isis Pixel Types.
Definition: PixelType.h:43
Handles converting buffers to and from disk.
void * RawBuffer() const
Returns a void pointer to the raw buffer.
Definition: Buffer.h:166
virtual CacheResult recommendChunksToFree(QList< RawCubeChunk * > allocated, QList< RawCubeChunk * > justUsed, const Buffer &justRequested)=0
Call this to determine which chunks should be freed from memory.
Unless noted otherwise, the portions of Isis written by the USGS are public domain.
Isis::PixelType PixelTypeEnumeration(const QString &type)
Returns PixelType enumeration given a string.
Definition: PixelType.h:105
Unless noted otherwise, the portions of Isis written by the USGS are public domain.
This algorithm recommends chunks to be freed that are not within the last IO.
bool algorithmUnderstoodData() const
If this is true, then the results (be them empty or not) should be considered valid.
int Band(const int index=0) const
Returns the band position associated with a shape buffer index.
Definition: Buffer.cpp:178
Area3D intersect(const Area3D &otherArea) const
Returns the intersection of this 3D area with another 3D area.
Definition: Area3D.cpp:460
QByteArray & getRawData() const
Definition: RawCubeChunk.h:53
PvlKeyword & findKeyword(const QString &kname, FindOptions opts)
Finds a keyword in the current PvlObject, or deeper inside other PvlObjects and Pvlgroups within this...
Definition: PvlObject.cpp:148
Contains multiple PvlContainers.
Definition: PvlGroup.h:57
#define _FILEINFO_
Macro for the filename and line number.
Definition: IException.h:38
int sampleCount() const
Definition: RawCubeChunk.h:89
A section of raw data on the disk.
Definition: RawCubeChunk.h:42
Byte swapper.
Definition: EndianSwapper.h:54
bool isValid() const
Returns true if all of the positions of the 3D area are valid (i.e.
Definition: Area3D.cpp:485
Container for cube-like labels.
Definition: Pvl.h:135
PvlKeyword & findKeyword(const QString &name)
Find a keyword with a specified name.
IString DownCase()
Converts all upper case letters in the object IString into lower case characters. ...
Definition: IString.cpp:659
int Index(const int i_samp, const int i_line, const int i_band) const
Given a sample, line, and band position, this returns the appropriate index in the shape buffer...
Definition: Buffer.cpp:213
int size() const
Returns the total number of pixels in the shape buffer.
Definition: Buffer.h:112
Displacement is a signed length, usually in meters.
Definition: Displacement.h:43
Isis exception class.
Definition: IException.h:99
Adds specific functionality to C++ strings.
Definition: IString.h:179
This stores the results of the caching algorithm.
int getStartBand() const
Definition: RawCubeChunk.h:82
This class is designed to handle write() asynchronously.
int getStartSample() const
Definition: RawCubeChunk.h:68
Contains Pvl Groups and Pvl Objects.
Definition: PvlObject.h:74
int bandCount() const
Definition: RawCubeChunk.h:103
This is the parent of the caching algorithms.
int SampleDimension() const
Returns the number of samples in the shape buffer.
Definition: Buffer.h:85

U.S. Department of the Interior | U.S. Geological Survey
ISIS | Privacy & Disclaimers | Astrogeology Research Program
To contact us, please post comments and questions on the ISIS Support Center
File Modified: 07/12/2023 23:17:10