Isis 3 Programmer Reference
CubeIoHandler.cpp
1 
6 /* SPDX-License-Identifier: CC0-1.0 */
7 
8 #include "IsisDebug.h"
9 #include "CubeIoHandler.h"
10 
11 #include <algorithm>
12 #include <cmath>
13 #include <iomanip>
14 
15 #include <QDebug>
16 #include <QFile>
17 #include <QList>
18 #include <QListIterator>
19 #include <QMapIterator>
20 #include <QMutex>
21 #include <QPair>
22 #include <QRect>
23 #include <QTime>
24 
25 #include "Area3D.h"
26 #include "Brick.h"
27 #include "CubeCachingAlgorithm.h"
28 #include "Displacement.h"
29 #include "Distance.h"
30 #include "Endian.h"
31 #include "EndianSwapper.h"
32 #include "IException.h"
33 #include "IString.h"
34 #include "PixelType.h"
35 #include "Preference.h"
36 #include "Pvl.h"
37 #include "PvlGroup.h"
38 #include "PvlObject.h"
39 #include "RawCubeChunk.h"
40 #include "RegionalCachingAlgorithm.h"
41 #include "SpecialPixel.h"
42 #include "Statistics.h"
43 
44 using namespace std;
45 
46 namespace Isis {
63  CubeIoHandler::CubeIoHandler(QFile * dataFile,
64  const QList<int> *virtualBandList, const Pvl &label, bool alreadyOnDisk) {
65  m_byteSwapper = NULL;
66  m_cachingAlgorithms = NULL;
67  m_dataIsOnDiskMap = NULL;
68  m_rawData = NULL;
69  m_virtualBands = NULL;
70  m_nullChunkData = NULL;
71  m_lastProcessByLineChunks = NULL;
72  m_writeCache = NULL;
73  m_ioThreadPool = NULL;
74  m_writeThreadMutex = NULL;
75 
76  try {
77  if (!dataFile) {
78  IString msg = "Cannot create a CubeIoHandler with a NULL data file";
79  throw IException(IException::Programmer, msg, _FILEINFO_);
80  }
81 
82  m_cachingAlgorithms = new QList<CubeCachingAlgorithm *>;
83 
84  PvlGroup &performancePrefs =
85  Preference::Preferences().findGroup("Performance");
86  IString cubeWritePerfOpt = performancePrefs["CubeWriteThread"][0];
87  m_useOptimizedCubeWrite = (cubeWritePerfOpt.DownCase() == "optimized");
88  if ((m_useOptimizedCubeWrite && !alreadyOnDisk) ||
89  cubeWritePerfOpt.DownCase() == "always") {
90  m_ioThreadPool = new QThreadPool;
91  m_ioThreadPool->setMaxThreadCount(1);
92  }
93 
94  m_consecutiveOverflowCount = 0;
95  m_lastOperationWasWrite = false;
96  m_rawData = new QMap<int, RawCubeChunk *>;
97  m_writeCache = new QPair< QMutex *, QList<Buffer *> >;
98  m_writeCache->first = new QMutex;
99  m_writeThreadMutex = new QMutex;
100 
101  m_idealFlushSize = 32;
102 
103  m_cachingAlgorithms->append(new RegionalCachingAlgorithm);
104 
105  m_dataFile = dataFile;
106 
107  const PvlObject &core = label.findObject("IsisCube").findObject("Core");
108  const PvlGroup &pixelGroup = core.findGroup("Pixels");
109 
110  QString byteOrderStr = pixelGroup.findKeyword("ByteOrder")[0];
111  m_byteSwapper = new EndianSwapper(
112  byteOrderStr.toUpper());
113  m_base = pixelGroup.findKeyword("Base");
114  m_multiplier = pixelGroup.findKeyword("Multiplier");
115  m_pixelType = PixelTypeEnumeration(pixelGroup.findKeyword("Type"));
116 
117  // If the byte swapper isn't going to do anything, then get rid of it
118  // because it's quicker to check for a NULL byte swapper member than to
119  // call a swap that won't do anything.
120  if(!m_byteSwapper->willSwap()) {
121  delete m_byteSwapper;
122  m_byteSwapper = NULL;
123  }
124 
125  const PvlGroup &dimensions = core.findGroup("Dimensions");
126  m_numSamples = dimensions.findKeyword("Samples");
127  m_numLines = dimensions.findKeyword("Lines");
128  m_numBands = dimensions.findKeyword("Bands");
129 
130  m_startByte = (int)core.findKeyword("StartByte") - 1;
131 
132  m_samplesInChunk = -1;
133  m_linesInChunk = -1;
134  m_bandsInChunk = -1;
135 
136  if(!alreadyOnDisk) {
137  m_dataIsOnDiskMap = new QMap<int, bool>;
138  }
139 
140  setVirtualBands(virtualBandList);
141  }
142  catch(IException &e) {
143  IString msg = "Constructing CubeIoHandler failed";
144  throw IException(e, IException::Programmer, msg, _FILEINFO_);
145  }
146  catch(...) {
147  IString msg = "Constructing CubeIoHandler failed";
148  throw IException(IException::Programmer, msg, _FILEINFO_);
149  }
150  }
151 
152 
157  CubeIoHandler::~CubeIoHandler() {
158  ASSERT( m_rawData ? m_rawData->size() == 0 : 1 );
159 
160  if (m_ioThreadPool)
161  m_ioThreadPool->waitForDone();
162 
163  delete m_ioThreadPool;
164  m_ioThreadPool = NULL;
165 
166  delete m_dataIsOnDiskMap;
167  m_dataIsOnDiskMap = NULL;
168 
169  if (m_cachingAlgorithms) {
170  QListIterator<CubeCachingAlgorithm *> it(*m_cachingAlgorithms);
171  while (it.hasNext()) {
172  delete it.next();
173  }
174  delete m_cachingAlgorithms;
175  m_cachingAlgorithms = NULL;
176  }
177 
178  if (m_rawData) {
179  QMapIterator<int, RawCubeChunk *> it(*m_rawData);
180  while (it.hasNext()) {
181  // Unwritten data here means it cannot be written :(
182  ASSERT(0);
183  it.next();
184 
185  if(it.value())
186  delete it.value();
187  }
188  delete m_rawData;
189  m_rawData = NULL;
190  }
191 
192  if (m_writeCache) {
193  delete m_writeCache->first;
194  m_writeCache->first = NULL;
195 
196  for (int i = 0; i < m_writeCache->second.size(); i++) {
197  delete m_writeCache->second[i];
198  }
199  m_writeCache->second.clear();
200 
201  delete m_writeCache;
202  m_writeCache = NULL;
203  }
204 
205  delete m_byteSwapper;
206  m_byteSwapper = NULL;
207 
208  delete m_virtualBands;
209  m_virtualBands = NULL;
210 
211  delete m_nullChunkData;
212  m_nullChunkData = NULL;
213 
214  delete m_lastProcessByLineChunks;
215  m_lastProcessByLineChunks = NULL;
216 
217  delete m_writeThreadMutex;
218  m_writeThreadMutex = NULL;
219  }
220 
221 
230  void CubeIoHandler::read(Buffer &bufferToFill) const {
231  // We need to record the current chunk count size so we can use
232  // it to evaluate if the cache should be minimized
233  int lastChunkCount = m_rawData->size();
234 
235  if (m_lastOperationWasWrite) {
236  // Do the remaining writes
237  flushWriteCache(true);
238 
239  m_lastOperationWasWrite = false;
240 
241  // Stop backgrounding writes now, we don't want to keep incurring this
242  // penalty.
243  if (m_useOptimizedCubeWrite) {
244  delete m_ioThreadPool;
245  m_ioThreadPool = NULL;
246  }
247  }
248 
249  QMutexLocker lock(m_writeThreadMutex);
250 
251  // NON-THREADED CUBE READ
252  QList<RawCubeChunk *> cubeChunks;
253  QList<int > chunkBands;
254 
255  int bufferSampleCount = bufferToFill.SampleDimension();
256  int bufferLineCount = bufferToFill.LineDimension();
257  int bufferBandCount = bufferToFill.BandDimension();
258 
259  // our chunk dimensions are same as buffer shape dimensions
260  if (bufferSampleCount == m_samplesInChunk &&
261  bufferLineCount == m_linesInChunk &&
262  bufferBandCount == m_bandsInChunk) {
263  int bufferStartSample = bufferToFill.Sample();
264  int bufferStartLine = bufferToFill.Line();
265  int bufferStartBand = bufferToFill.Band();
266 
267  int bufferEndSample = bufferStartSample + bufferSampleCount - 1;
268  int bufferEndLine = bufferStartLine + bufferLineCount - 1;
269  int bufferEndBand = bufferStartBand + bufferBandCount - 1;
270 
271  // make sure we access the correct band
272  int startBand = bufferStartBand - 1;
273  if (m_virtualBands)
274  startBand = m_virtualBands->at(bufferStartBand - 1);
275 
276  int expectedChunkIndex =
277  ((bufferStartSample - 1) / getSampleCountInChunk()) +
278  ((bufferStartLine - 1) / getLineCountInChunk()) *
279  getChunkCountInSampleDimension() +
280  ((startBand - 1) / getBandCountInChunk()) *
281  getChunkCountInSampleDimension() *
282  getChunkCountInLineDimension();
283 
284  int chunkStartSample, chunkStartLine, chunkStartBand,
285  chunkEndSample, chunkEndLine, chunkEndBand;
286 
287  getChunkPlacement(expectedChunkIndex,
288  chunkStartSample, chunkStartLine, chunkStartBand,
289  chunkEndSample, chunkEndLine, chunkEndBand);
290 
291  if (chunkStartSample == bufferStartSample &&
292  chunkStartLine == bufferStartLine &&
293  chunkStartBand == bufferStartBand &&
294  chunkEndSample == bufferEndSample &&
295  chunkEndLine == bufferEndLine &&
296  chunkEndBand == bufferEndBand) {
297  cubeChunks.append(getChunk(expectedChunkIndex, true));
298  chunkBands.append(cubeChunks.last()->getStartBand());
299  }
300  }
301 
302  if (cubeChunks.empty()) {
303  // We can't guarantee our cube chunks will encompass the buffer
304  // if the buffer goes beyond the cube bounds.
305  for(int i = 0; i < bufferToFill.size(); i++) {
306  bufferToFill[i] = Null;
307  }
308 
310  chunkInfo = findCubeChunks(
311  bufferToFill.Sample(), bufferToFill.SampleDimension(),
312  bufferToFill.Line(), bufferToFill.LineDimension(),
313  bufferToFill.Band(), bufferToFill.BandDimension());
314  cubeChunks = chunkInfo.first;
315  chunkBands = chunkInfo.second;
316  }
317 
318  for (int i = 0; i < cubeChunks.size(); i++) {
319  writeIntoDouble(*cubeChunks[i], bufferToFill, chunkBands[i]);
320  }
321 
322  // Minimize the cache if it changed in size
323  if (lastChunkCount != m_rawData->size()) {
324  minimizeCache(cubeChunks, bufferToFill);
325  }
326  }
327 
328 
338  void CubeIoHandler::write(const Buffer &bufferToWrite) {
339  m_lastOperationWasWrite = true;
340 
341  if (m_ioThreadPool) {
342  // THREADED CUBE WRITE
343  Buffer * copy = new Buffer(bufferToWrite);
344  {
345  QMutexLocker locker(m_writeCache->first);
346  m_writeCache->second.append(copy);
347  }
348 
349  flushWriteCache();
350  }
351  else {
352  QMutexLocker lock(m_writeThreadMutex);
353  // NON-THREADED CUBE WRITE
354  synchronousWrite(bufferToWrite);
355  }
356  }
357 
358 
368  void CubeIoHandler::addCachingAlgorithm(CubeCachingAlgorithm *algorithm) {
369  m_cachingAlgorithms->prepend(algorithm);
370  }
371 
372 
383  void CubeIoHandler::clearCache(bool blockForWriteCache) const {
384  if (blockForWriteCache) {
385  // Start the rest of the writes
386  flushWriteCache(true);
387  }
388 
389  // If this map is allocated, then this is a brand new cube and we need to
390  // make sure it's filled with data or NULLs.
391  if(m_dataIsOnDiskMap) {
392  writeNullDataToDisk();
393  }
394 
395  // This should be allocated. This is a list of the cached cube data.
396  // Write it all to disk.
397  if (m_rawData) {
398  QMapIterator<int, RawCubeChunk *> it(*m_rawData);
399  while (it.hasNext()) {
400  it.next();
401 
402  if(it.value()) {
403  if(it.value()->isDirty()) {
404  (const_cast<CubeIoHandler *>(this))->writeRaw(*it.value());
405  }
406 
407  delete it.value();
408  }
409  }
410 
411  m_rawData->clear();
412  }
413 
414  if(m_lastProcessByLineChunks) {
415  delete m_lastProcessByLineChunks;
416  m_lastProcessByLineChunks = NULL;
417  }
418  }
419 
420 
425  BigInt CubeIoHandler::getDataSize() const {
426  return (BigInt)getChunkCountInSampleDimension() *
427  (BigInt)getChunkCountInLineDimension() *
428  (BigInt)getChunkCountInBandDimension() *
429  (BigInt)getBytesPerChunk();
430  }
431 
432 
442  void CubeIoHandler::setVirtualBands(const QList<int> *virtualBandList) {
443  if(m_virtualBands) {
444  delete m_virtualBands;
445  m_virtualBands = NULL;
446  }
447 
448  if(virtualBandList && !virtualBandList->empty())
449  m_virtualBands = new QList<int>(*virtualBandList);
450  }
451 
459  QMutex *CubeIoHandler::dataFileMutex() {
460  return m_writeThreadMutex;
461  }
462 
466  int CubeIoHandler::bandCount() const {
467  return m_numBands;
468  }
469 
470 
474  int CubeIoHandler::getBandCountInChunk() const {
475  return m_bandsInChunk;
476  }
477 
478 
486  BigInt CubeIoHandler::getBytesPerChunk() const {
487  return m_samplesInChunk * m_linesInChunk * m_bandsInChunk *
488  SizeOf(m_pixelType);
489  }
490 
491 
496  int CubeIoHandler::getChunkCountInBandDimension() const {
497  return (int)ceil((double)m_numBands / (double)m_bandsInChunk);
498  }
499 
500 
505  int CubeIoHandler::getChunkCountInLineDimension() const {
506  return (int)ceil((double)m_numLines / (double)m_linesInChunk);
507  }
508 
509 
514  int CubeIoHandler::getChunkCountInSampleDimension() const {
515  return (int)ceil((double)m_numSamples / (double)m_samplesInChunk);
516  }
517 
518 
530  int CubeIoHandler::getChunkIndex(const RawCubeChunk &chunk) const {
531 // ASSERT(chunk.getStartSample() <= sampleCount());
532 // ASSERT(chunk.getStartLine() <= lineCount());
533 // ASSERT(chunk.getStartBand() <= bandCount());
534 
535  int sampleIndex = (chunk.getStartSample() - 1) / getSampleCountInChunk();
536  int lineIndex = (chunk.getStartLine() - 1) / getLineCountInChunk();
537  int bandIndex = (chunk.getStartBand() - 1) / getBandCountInChunk();
538 
539  int indexInBand =
540  sampleIndex + lineIndex * getChunkCountInSampleDimension();
541  int indexOffsetToBand = bandIndex * getChunkCountInSampleDimension() *
542  getChunkCountInLineDimension();
543 
544  return indexOffsetToBand + indexInBand;
545  }
546 
547 
551  BigInt CubeIoHandler::getDataStartByte() const {
552  return m_startByte;
553  }
554 
555 
562  QFile * CubeIoHandler::getDataFile() {
563  return m_dataFile;
564  }
565 
566 
571  int CubeIoHandler::lineCount() const {
572  return m_numLines;
573  }
574 
575 
579  int CubeIoHandler::getLineCountInChunk() const {
580  return m_linesInChunk;
581  }
582 
583 
587  PixelType CubeIoHandler::pixelType() const {
588  return m_pixelType;
589  }
590 
591 
596  int CubeIoHandler::sampleCount() const {
597  return m_numSamples;
598  }
599 
600 
604  int CubeIoHandler::getSampleCountInChunk() const {
605  return m_samplesInChunk;
606  }
607 
608 
621  void CubeIoHandler::setChunkSizes(
622  int numSamples, int numLines, int numBands) {
623  bool success = false;
624  IString msg;
625 
626  if(m_samplesInChunk != -1 || m_linesInChunk != -1 || m_bandsInChunk != -1) {
627  IString msg = "You cannot change the chunk sizes once set";
628  }
629  else if(numSamples < 1) {
630  msg = "Negative and zero chunk sizes are not supported, samples per chunk"
631  " cannot be [" + IString(numSamples) + "]";
632  }
633  else if(numLines < 1) {
634  msg = "Negative and zero chunk sizes are not supported, lines per chunk "
635  "cannot be [" + IString(numLines) + "]";
636  }
637  else if(numBands < 1) {
638  msg = "Negative and zero chunk sizes are not supported, lines per chunk "
639  "cannot be [" + IString(numBands) + "]";
640  }
641  else {
642  success = true;
643  }
644 
645  if(success) {
646  m_samplesInChunk = numSamples;
647  m_linesInChunk = numLines;
648  m_bandsInChunk = numBands;
649 
650  if(m_dataIsOnDiskMap) {
651  m_dataFile->resize(getDataStartByte() + getDataSize());
652  }
653  else if(m_dataFile->size() < getDataStartByte() + getDataSize()) {
654  success = false;
655  msg = "File size [" + IString((BigInt)m_dataFile->size()) +
656  " bytes] not big enough to hold data [" +
657  IString(getDataStartByte() + getDataSize()) + " bytes] where the "
658  "offset to the cube data is [" + IString(getDataStartByte()) +
659  " bytes]";
660  }
661  }
662  else {
663  throw IException(IException::Programmer, msg, _FILEINFO_);
664  }
665  }
666 
667 
674  void CubeIoHandler::blockUntilThreadPoolEmpty() const {
675  if (m_ioThreadPool) {
676  QMutexLocker lock(m_writeThreadMutex);
677  }
678  }
679 
680 
688  bool CubeIoHandler::bufferLessThan(Buffer * const &lhs, Buffer * const &rhs) {
689  bool lessThan = false;
690 
691  // If there is any overlap then we need to return false due to it being
692  // ambiguous.
693  Area3D lhsArea(
694  Displacement(lhs->Sample(), Displacement::Pixels),
695  Displacement(lhs->Line(), Displacement::Pixels),
696  Displacement(lhs->Band(), Displacement::Pixels),
697  Distance(lhs->SampleDimension() - 1, Distance::Pixels),
698  Distance(lhs->LineDimension() - 1, Distance::Pixels),
699  Distance(lhs->BandDimension() - 1, Distance::Pixels));
700  Area3D rhsArea(
701  Displacement(rhs->Sample(), Displacement::Pixels),
702  Displacement(rhs->Line(), Displacement::Pixels),
703  Displacement(rhs->Band(), Displacement::Pixels),
704  Distance(rhs->SampleDimension() - 1, Distance::Pixels),
705  Distance(rhs->LineDimension() - 1, Distance::Pixels),
706  Distance(rhs->BandDimension() - 1, Distance::Pixels));
707 
708  if (!lhsArea.intersect(rhsArea).isValid()) {
709  if (lhs->Band() != rhs->Band()) {
710  lessThan = lhs->Band() < rhs->Band();
711  }
712  else if (lhs->Line() != rhs->Line()) {
713  lessThan = lhs->Line() < rhs->Line();
714  }
715  else if (lhs->Sample() != rhs->Sample()) {
716  lessThan = lhs->Sample() < rhs->Sample();
717  }
718  }
719 
720  return lessThan;
721  }
722 
723 
737  QPair< QList<RawCubeChunk *>, QList<int> > CubeIoHandler::findCubeChunks(int startSample,
738  int numSamples, int startLine, int numLines, int startBand,
739  int numBands) const {
740  QList<RawCubeChunk *> results;
741  QList<int> resultBands;
742 /************************************************************************CHANGED THIS!!!!!!!!******/
743  int lastBand = startBand + numBands - 1;
744 // int lastBand = min(startBand + numBands - 1,
745 // bandCount());
746 /************************************************************************CHANGED THIS!!!!!!!!******/
747 
748  QRect areaInBand(
749  QPoint(max(startSample, 1),
750  max(startLine, 1)),
751  QPoint(min(startSample + numSamples - 1,
752  sampleCount()),
753  min(startLine + numLines - 1,
754  lineCount())));
755 
756  // We are considering only 1 band at a time.. we can't use m_bandsInChunk
757  // because of virtual bands, but every extra loop should not need extra
758  // IO.
759  for(int band = startBand; band <= lastBand; band ++) {
760  // This is the user-requested area in this band
761  QRect areaLeftInBand(areaInBand);
762 
763  int actualBand = band;
764 
765  if(m_virtualBands) {
766  if (band < 1 || band > m_virtualBands->size())
767  actualBand = 0;
768  else
769  actualBand = (m_virtualBands->at(band - 1) - 1) / m_bandsInChunk + 1;
770  }
771  // We will be consuming areaLeftInBand until we've got all of the area
772  // requested.
773  while(!areaLeftInBand.isEmpty()) {
821  int areaStartLine = areaLeftInBand.top();
822  int areaStartSample = areaLeftInBand.left();
823 
824  int initialChunkXPos = (areaStartSample - 1) / m_samplesInChunk;
825  int initialChunkYPos = (areaStartLine - 1) / m_linesInChunk;
826  int initialChunkZPos = (actualBand - 1) / m_bandsInChunk;
827  int initialChunkBand = initialChunkZPos * m_bandsInChunk + 1;
828 
829 
830  QRect chunkRect(initialChunkXPos * m_samplesInChunk + 1,
831  initialChunkYPos * m_linesInChunk + 1,
832  m_samplesInChunk, m_linesInChunk);
833 
834  // The chunk rectangle must intersect the remaining area that is in the
835  // current band, and the chunk's initial band must be between 1 and the
836  // number of physical bands in the cube (inclusive).
837  while(chunkRect.intersects(areaLeftInBand) &&
838  (initialChunkBand >= 1 && initialChunkBand <= bandCount())) {
839  int chunkXPos = (chunkRect.left() - 1) / m_samplesInChunk;
840  int chunkYPos = (chunkRect.top() - 1) / m_linesInChunk;
841  int chunkZPos = initialChunkZPos;
842 
843  // We now have an X,Y,Z position for the chunk. What's its index?
844  int chunkIndex = chunkXPos +
845  (chunkYPos * getChunkCountInSampleDimension()) +
846  (chunkZPos * getChunkCountInSampleDimension() *
847  getChunkCountInLineDimension());
848 
849  RawCubeChunk * newChunk = getChunk(chunkIndex, true);
850 
851  results.append(newChunk);
852  resultBands.append(band);
853 
854  chunkRect.moveLeft(chunkRect.right() + 1);
855  }
856 
857  areaLeftInBand.setTop(chunkRect.bottom() + 1);
858  }
859  }
860 
861  return QPair< QList<RawCubeChunk *>, QList<int> >(results, resultBands);
862  }
863 
864 
882  void CubeIoHandler::findIntersection(
883  const RawCubeChunk &cube1, const Buffer &cube2,
884  int &startX, int &startY, int &startZ,
885  int &endX, int &endY, int &endZ) const {
886  // So we have 2 3D "cubes" (not Cube cubes but 3d areas) we need to
887  // intersect in order to figure out what chunk data goes into the output
888  // buffer.
889 
890  // To find the band range we actually have to map all of the bands from
891  // virtual bands to physical bands
892  int startVBand = cube2.Band();
893  int endVBand = startVBand + cube2.BandDimension() - 1;
894 
895  int startPhysicalBand = 0;
896  int endPhysicalBand = 0;
897 
898  bool startVBandFound = false;
899  for(int virtualBand = startVBand; virtualBand <= endVBand; virtualBand ++) {
900  int physicalBand = virtualBand;
901 
902  bool bandExists = true;
903  if(m_virtualBands) {
904  if (virtualBand < 1 || virtualBand > m_virtualBands->size())
905  bandExists = false;
906  else {
907  physicalBand = m_virtualBands->at(virtualBand - 1);
908  }
909  }
910 
911  if (bandExists) {
912  if(!startVBandFound) {
913  startPhysicalBand = physicalBand;
914  endPhysicalBand = physicalBand;
915  startVBandFound = true;
916  }
917  else {
918  if(physicalBand < startPhysicalBand)
919  startPhysicalBand = physicalBand;
920 
921  if(physicalBand > endPhysicalBand)
922  endPhysicalBand = physicalBand;
923  }
924  }
925  }
926 
927  startX = max(cube1.getStartSample(), cube2.Sample());
928  startY = max(cube1.getStartLine(), cube2.Line());
929  startZ = max(cube1.getStartBand(), startPhysicalBand);
930  endX = min(cube1.getStartSample() + cube1.sampleCount() - 1,
931  cube2.Sample() + cube2.SampleDimension() - 1);
932  endY = min(cube1.getStartLine() + cube1.lineCount() - 1,
933  cube2.Line() + cube2.LineDimension() - 1);
934  endZ = min(cube1.getStartBand() + cube1.bandCount() - 1,
935  endPhysicalBand);
936  }
937 
938 
947  void CubeIoHandler::flushWriteCache(bool force) const {
948  if (m_ioThreadPool) {
949  bool shouldFlush = m_writeCache->second.size() >= m_idealFlushSize ||
950  force;
951  bool cacheOverflowing =
952  (m_writeCache->second.size() > m_idealFlushSize * 10);
953  bool shouldAndCanFlush = false;
954  bool forceStart = force;
955 
956  if (shouldFlush) {
957  shouldAndCanFlush = m_writeThreadMutex->tryLock();
958  if (shouldAndCanFlush) {
959  m_writeThreadMutex->unlock();
960  }
961  }
962 
963  if (cacheOverflowing && !shouldAndCanFlush) {
964  forceStart = true;
965  m_consecutiveOverflowCount++;
966  }
967 
968  if (forceStart) {
969  blockUntilThreadPoolEmpty();
970 
971  if (m_writeCache->second.size() != 0) {
972  m_idealFlushSize = m_writeCache->second.size();
973  shouldFlush = true;
974  shouldAndCanFlush = true;
975  }
976  }
977  else if (!cacheOverflowing && shouldAndCanFlush) {
978  m_consecutiveOverflowCount = 0;
979  }
980 
981  if (cacheOverflowing && m_useOptimizedCubeWrite) {
982  blockUntilThreadPoolEmpty();
983 
984  // If the process is very I/O bound, then write caching isn't helping
985  // anything. In fact, it hurts, so turn it off.
986  if (m_consecutiveOverflowCount > 10) {
987  delete m_ioThreadPool;
988  m_ioThreadPool = NULL;
989  }
990 
991  // Write it all synchronously.
992  foreach (Buffer *bufferToWrite, m_writeCache->second) {
993  const_cast<CubeIoHandler *>(this)->synchronousWrite(*bufferToWrite);
994  delete bufferToWrite;
995  }
996 
997  m_writeCache->second.clear();
998  }
999 
1000  if (shouldAndCanFlush && m_ioThreadPool) {
1001  QMutexLocker locker(m_writeCache->first);
1002  QRunnable *writer = new BufferToChunkWriter(
1003  const_cast<CubeIoHandler *>(this), m_writeCache->second);
1004 
1005  m_ioThreadPool->start(writer);
1006 
1007  m_writeCache->second.clear();
1008  m_lastOperationWasWrite = true;
1009  }
1010 
1011  if (force) {
1012  blockUntilThreadPoolEmpty();
1013  }
1014  }
1015  }
1016 
1017 
1024  void CubeIoHandler::freeChunk(RawCubeChunk *chunkToFree) const {
1025  if(chunkToFree && m_rawData) {
1026  int chunkIndex = getChunkIndex(*chunkToFree);
1027 
1028  m_rawData->erase(m_rawData->find(chunkIndex));
1029 
1030  if(chunkToFree->isDirty())
1031  (const_cast<CubeIoHandler *>(this))->writeRaw(*chunkToFree);
1032 
1033  delete chunkToFree;
1034 
1035  if(m_lastProcessByLineChunks) {
1036  delete m_lastProcessByLineChunks;
1037  m_lastProcessByLineChunks = NULL;
1038  }
1039  }
1040  }
1041 
1042 
1052  RawCubeChunk *CubeIoHandler::getChunk(int chunkIndex,
1053  bool allocateIfNecessary) const {
1054  RawCubeChunk *chunk = NULL;
1055 
1056  if(m_rawData) {
1057  chunk = m_rawData->value(chunkIndex);
1058  }
1059 
1060  if(allocateIfNecessary && !chunk) {
1061  if(m_dataIsOnDiskMap && !(*m_dataIsOnDiskMap)[chunkIndex]) {
1062  chunk = getNullChunk(chunkIndex);
1063  (*m_dataIsOnDiskMap)[chunkIndex] = true;
1064  }
1065  else {
1066  int startSample;
1067  int startLine;
1068  int startBand;
1069  int endSample;
1070  int endLine;
1071  int endBand;
1072  getChunkPlacement(chunkIndex, startSample, startLine, startBand,
1073  endSample, endLine, endBand);
1074  chunk = new RawCubeChunk(startSample, startLine, startBand,
1075  endSample, endLine, endBand,
1076  getBytesPerChunk());
1077 
1078  (const_cast<CubeIoHandler *>(this))->readRaw(*chunk);
1079  chunk->setDirty(false);
1080  }
1081 
1082  (*m_rawData)[chunkIndex] = chunk;
1083  }
1084 
1085  return chunk;
1086  }
1087 
1088 
1093  int CubeIoHandler::getChunkCount() const {
1094  return getChunkCountInSampleDimension() *
1095  getChunkCountInLineDimension() *
1096  getChunkCountInBandDimension();
1097  }
1098 
1099 
1111  void CubeIoHandler::getChunkPlacement(int chunkIndex,
1112  int &startSample, int &startLine, int &startBand,
1113  int &endSample, int &endLine, int &endBand) const {
1114  int chunkSampleIndex = chunkIndex % getChunkCountInSampleDimension();
1115 
1116  chunkIndex =
1117  (chunkIndex - chunkSampleIndex) / getChunkCountInSampleDimension();
1118 
1119  int chunkLineIndex = chunkIndex % getChunkCountInLineDimension();
1120  chunkIndex = (chunkIndex - chunkLineIndex) / getChunkCountInLineDimension();
1121 
1122  int chunkBandIndex = chunkIndex;
1123 
1124  startSample = chunkSampleIndex * getSampleCountInChunk() + 1;
1125  endSample = startSample + getSampleCountInChunk() - 1;
1126  startLine = chunkLineIndex * getLineCountInChunk() + 1;
1127  endLine = startLine + getLineCountInChunk() - 1;
1128  startBand = chunkBandIndex * getBandCountInChunk() + 1;
1129  endBand = startBand + getBandCountInChunk() - 1;
1130  }
1131 
1132 
1143  RawCubeChunk *CubeIoHandler::getNullChunk(int chunkIndex) const {
1144  // Shouldn't ask for null chunks when the area has already been allocated
1145 // ASSERT(getChunk(chunkIndex) == NULL);
1146 
1147  int startSample = 0;
1148  int startLine = 0;
1149  int startBand = 0;
1150 
1151  int endSample = 0;
1152  int endLine = 0;
1153  int endBand = 0;
1154 
1155  getChunkPlacement(chunkIndex, startSample, startLine, startBand,
1156  endSample, endLine, endBand);
1157 
1158  RawCubeChunk *result = new RawCubeChunk(startSample, startLine, startBand,
1159  endSample, endLine, endBand, getBytesPerChunk());
1160 
1161  if(!m_nullChunkData) {
1162  // the pixel type doesn't really matter, so pick something small
1163  Brick nullBuffer(result->sampleCount(),
1164  result->lineCount(),
1165  result->bandCount(),
1166  UnsignedByte);
1167 
1168  nullBuffer.SetBasePosition(result->getStartSample(),
1169  result->getStartLine(),
1170  result->getStartBand());
1171  for(int i = 0; i < nullBuffer.size(); i++) {
1172  nullBuffer[i] = Null;
1173  }
1174 
1175  writeIntoRaw(nullBuffer, *result, result->getStartBand());
1176  m_nullChunkData = new QByteArray(result->getRawData());
1177  }
1178  else {
1179  result->setRawData(*m_nullChunkData);
1180  }
1181 
1182  result->setDirty(true);
1183  return result;
1184  }
1185 
1186 
1196  void CubeIoHandler::minimizeCache(const QList<RawCubeChunk *> &justUsed,
1197  const Buffer &justRequested) const {
1198  // Since we have a lock on the cache, no newly created threads can utilize
1199  // or access any cache data until we're done.
1200  if (m_rawData->size() * getBytesPerChunk() > 1 * 1024 * 1024 ||
1201  m_cachingAlgorithms->size() > 1) {
1202  bool algorithmAccepted = false;
1203 
1204  int algorithmIndex = 0;
1205  while(!algorithmAccepted &&
1206  algorithmIndex < m_cachingAlgorithms->size()) {
1207  CubeCachingAlgorithm *algorithm = (*m_cachingAlgorithms)[algorithmIndex];
1208 
1210  algorithm->recommendChunksToFree(m_rawData->values(), justUsed,
1211  justRequested);
1212 
1213  algorithmAccepted = result.algorithmUnderstoodData();
1214 
1215  if(algorithmAccepted) {
1216  QList<RawCubeChunk *> chunksToFree = result.getChunksToFree();
1217 
1218  RawCubeChunk *chunkToFree;
1219  foreach(chunkToFree, chunksToFree) {
1220  freeChunk(chunkToFree);
1221  }
1222  }
1223 
1224  algorithmIndex ++;
1225  }
1226 
1227  // Fall back - no algorithms liked us :(
1228  if(!algorithmAccepted && m_rawData->size() > 100) {
1229  // This (minimizeCache()) is typically executed in the Runnable thread.
1230  // We don't want to wait for ourselves.
1231  clearCache(false);
1232  }
1233  }
1234  }
1235 
1236 
1245  void CubeIoHandler::synchronousWrite(const Buffer &bufferToWrite) {
1246  QList<RawCubeChunk *> cubeChunks;
1247  QList<int> cubeChunkBands;
1248 
1249  int bufferSampleCount = bufferToWrite.SampleDimension();
1250  int bufferLineCount = bufferToWrite.LineDimension();
1251  int bufferBandCount = bufferToWrite.BandDimension();
1252 
1253  // process by line optimization
1254  if(m_lastProcessByLineChunks &&
1255  m_lastProcessByLineChunks->size()) {
1256  // Not optimized yet, let's see if we can optimize
1257  if(bufferToWrite.Sample() == 1 &&
1258  bufferSampleCount == sampleCount() &&
1259  bufferLineCount == 1 &&
1260  bufferBandCount == 1) {
1261  // We look like a process by line, are we using the same chunks as
1262  // before?
1263  int bufferLine = bufferToWrite.Line();
1264  int chunkStartLine =
1265  (*m_lastProcessByLineChunks)[0]->getStartLine();
1266  int chunkLines =
1267  (*m_lastProcessByLineChunks)[0]->lineCount();
1268  int bufferBand = bufferToWrite.Band();
1269  int chunkStartBand =
1270  (*m_lastProcessByLineChunks)[0]->getStartBand();
1271  int chunkBands =
1272  (*m_lastProcessByLineChunks)[0]->bandCount();
1273 
1274  if(bufferLine >= chunkStartLine &&
1275  bufferLine <= chunkStartLine + chunkLines - 1 &&
1276  bufferBand >= chunkStartBand &&
1277  bufferBand <= chunkStartBand + chunkBands - 1) {
1278  cubeChunks = *m_lastProcessByLineChunks;
1279  for (int i = 0; i < cubeChunks.size(); i++) {
1280  cubeChunkBands.append( cubeChunks[i]->getStartBand() );
1281  }
1282  }
1283  }
1284  }
1285  // Processing by chunk size
1286  else if (bufferSampleCount == m_samplesInChunk &&
1287  bufferLineCount == m_linesInChunk &&
1288  bufferBandCount == m_bandsInChunk) {
1289  int bufferStartSample = bufferToWrite.Sample();
1290  int bufferStartLine = bufferToWrite.Line();
1291  int bufferStartBand = bufferToWrite.Band();
1292 
1293  int bufferEndSample = bufferStartSample + bufferSampleCount - 1;
1294  int bufferEndLine = bufferStartLine + bufferLineCount - 1;
1295  int bufferEndBand = bufferStartBand + bufferBandCount - 1;
1296 
1297  int expectedChunkIndex =
1298  ((bufferStartSample - 1) / getSampleCountInChunk()) +
1299  ((bufferStartLine - 1) / getLineCountInChunk()) *
1300  getChunkCountInSampleDimension() +
1301  ((bufferStartBand - 1) / getBandCountInChunk()) *
1302  getChunkCountInSampleDimension() *
1303  getChunkCountInLineDimension();
1304 
1305  int chunkStartSample, chunkStartLine, chunkStartBand,
1306  chunkEndSample, chunkEndLine, chunkEndBand;
1307 
1308  getChunkPlacement(expectedChunkIndex,
1309  chunkStartSample, chunkStartLine, chunkStartBand,
1310  chunkEndSample, chunkEndLine, chunkEndBand);
1311 
1312  if (chunkStartSample == bufferStartSample &&
1313  chunkStartLine == bufferStartLine &&
1314  chunkStartBand == bufferStartBand &&
1315  chunkEndSample == bufferEndSample &&
1316  chunkEndLine == bufferEndLine &&
1317  chunkEndBand == bufferEndBand) {
1318  cubeChunks.append(getChunk(expectedChunkIndex, true));
1319  cubeChunkBands.append(cubeChunks.last()->getStartBand());
1320  }
1321  }
1322 
1324  if(cubeChunks.empty()) {
1325  chunkInfo = findCubeChunks(
1326  bufferToWrite.Sample(), bufferSampleCount,
1327  bufferToWrite.Line(), bufferLineCount,
1328  bufferToWrite.Band(), bufferBandCount);
1329  cubeChunks = chunkInfo.first;
1330  cubeChunkBands = chunkInfo.second;
1331  }
1332 
1333  // process by line optimization
1334  if(bufferToWrite.Sample() == 1 &&
1335  bufferSampleCount == sampleCount() &&
1336  bufferLineCount == 1 &&
1337  bufferBandCount == 1) {
1338  if(!m_lastProcessByLineChunks)
1339  m_lastProcessByLineChunks =
1340  new QList<RawCubeChunk *>(cubeChunks);
1341  else
1342  *m_lastProcessByLineChunks = cubeChunks;
1343  }
1344 
1345  for(int i = 0; i < cubeChunks.size(); i++) {
1346  writeIntoRaw(bufferToWrite, *cubeChunks[i], cubeChunkBands[i]);
1347  }
1348 
1349  minimizeCache(cubeChunks, bufferToWrite);
1350  }
1351 
1352 
1360  void CubeIoHandler::writeIntoDouble(const RawCubeChunk &chunk,
1361  Buffer &output, int index) const {
1362  // The code in this method is highly optimized. Even the order of the if
1363  // statements will have a significant impact on performance if changed.
1364  // Also, there is a lot of duplicate code in both writeIntoDouble(...) and
1365  // writeIntoRaw(...). This is needed for performance gain. Any function
1366  // or method calls from within the x loop cause significant performance
1367  // decreases.
1368  int startX = 0;
1369  int startY = 0;
1370  int startZ = 0;
1371 
1372  int endX = 0;
1373  int endY = 0;
1374  int endZ = 0;
1375 
1376  findIntersection(chunk, output, startX, startY, startZ, endX, endY, endZ);
1377 
1378  int bufferBand = output.Band();
1379  int bufferBands = output.BandDimension();
1380  int chunkStartSample = chunk.getStartSample();
1381  int chunkStartLine = chunk.getStartLine();
1382  int chunkStartBand = chunk.getStartBand();
1383  int chunkLineSize = chunk.sampleCount();
1384  int chunkBandSize = chunkLineSize * chunk.lineCount();
1385  //double *buffersDoubleBuf = output.p_buf;
1386  double *buffersDoubleBuf = output.DoubleBuffer();
1387  const char *chunkBuf = chunk.getRawData().data();
1388  char *buffersRawBuf = (char *)output.RawBuffer();
1389 
1390  for(int z = startZ; z <= endZ; z++) {
1391  const int &bandIntoChunk = z - chunkStartBand;
1392  int virtualBand = z;
1393 
1394  virtualBand = index;
1395 
1396  if(virtualBand != 0 && virtualBand >= bufferBand &&
1397  virtualBand <= bufferBand + bufferBands - 1) {
1398 
1399  for(int y = startY; y <= endY; y++) {
1400  const int &lineIntoChunk = y - chunkStartLine;
1401  int bufferIndex = output.Index(startX, y, virtualBand);
1402 
1403  for(int x = startX; x <= endX; x++) {
1404  const int &sampleIntoChunk = x - chunkStartSample;
1405 
1406  const int &chunkIndex = sampleIntoChunk +
1407  (chunkLineSize * lineIntoChunk) +
1408  (chunkBandSize * bandIntoChunk);
1409 
1410  double &bufferVal = buffersDoubleBuf[bufferIndex];
1411 
1412  if(m_pixelType == Real) {
1413  float raw = ((float *)chunkBuf)[chunkIndex];
1414  if(m_byteSwapper)
1415  raw = m_byteSwapper->Float(&raw);
1416 
1417  if(raw >= VALID_MIN4) {
1418  bufferVal = (double) raw;
1419  }
1420  else {
1421  if(raw == NULL4)
1422  bufferVal = NULL8;
1423  else if(raw == LOW_INSTR_SAT4)
1424  bufferVal = LOW_INSTR_SAT8;
1425  else if(raw == LOW_REPR_SAT4)
1426  bufferVal = LOW_REPR_SAT8;
1427  else if(raw == HIGH_INSTR_SAT4)
1428  bufferVal = HIGH_INSTR_SAT8;
1429  else if(raw == HIGH_REPR_SAT4)
1430  bufferVal = HIGH_REPR_SAT8;
1431  else
1432  bufferVal = LOW_REPR_SAT8;
1433  }
1434 
1435  ((float *)buffersRawBuf)[bufferIndex] = raw;
1436  }
1437 
1438  else if(m_pixelType == SignedWord) {
1439  short raw = ((short *)chunkBuf)[chunkIndex];
1440  if(m_byteSwapper)
1441  raw = m_byteSwapper->ShortInt(&raw);
1442 
1443  if(raw >= VALID_MIN2) {
1444  bufferVal = (double) raw * m_multiplier + m_base;
1445  }
1446  else {
1447  if(raw == NULL2)
1448  bufferVal = NULL8;
1449  else if(raw == LOW_INSTR_SAT2)
1450  bufferVal = LOW_INSTR_SAT8;
1451  else if(raw == LOW_REPR_SAT2)
1452  bufferVal = LOW_REPR_SAT8;
1453  else if(raw == HIGH_INSTR_SAT2)
1454  bufferVal = HIGH_INSTR_SAT8;
1455  else if(raw == HIGH_REPR_SAT2)
1456  bufferVal = HIGH_REPR_SAT8;
1457  else
1458  bufferVal = LOW_REPR_SAT8;
1459  }
1460 
1461  ((short *)buffersRawBuf)[bufferIndex] = raw;
1462  }
1463 
1464 
1465  else if(m_pixelType == UnsignedWord) {
1466  unsigned short raw = ((unsigned short *)chunkBuf)[chunkIndex];
1467  if(m_byteSwapper)
1468  raw = m_byteSwapper->UnsignedShortInt(&raw);
1469 
1470  if(raw >= VALID_MINU2) {
1471  bufferVal = (double) raw * m_multiplier + m_base;
1472  }
1473  else if (raw > VALID_MAXU2) {
1474  if(raw == HIGH_INSTR_SATU2)
1475  bufferVal = HIGH_INSTR_SAT8;
1476  else if(raw == HIGH_REPR_SATU2)
1477  bufferVal = HIGH_REPR_SAT8;
1478  else
1479  bufferVal = LOW_REPR_SAT8;
1480  }
1481  else {
1482  if(raw == NULLU2)
1483  bufferVal = NULL8;
1484  else if(raw == LOW_INSTR_SATU2)
1485  bufferVal = LOW_INSTR_SAT8;
1486  else if(raw == LOW_REPR_SATU2)
1487  bufferVal = LOW_REPR_SAT8;
1488  else
1489  bufferVal = LOW_REPR_SAT8;
1490  }
1491 
1492  ((unsigned short *)buffersRawBuf)[bufferIndex] = raw;
1493  }
1494 
1495  else if(m_pixelType == UnsignedInteger) {
1496 
1497  unsigned int raw = ((unsigned int *)chunkBuf)[chunkIndex];
1498  if(m_byteSwapper)
1499  raw = m_byteSwapper->Uint32_t(&raw);
1500 
1501  if(raw >= VALID_MINUI4) {
1502  bufferVal = (double) raw * m_multiplier + m_base;
1503  }
1504  else if (raw > VALID_MAXUI4) {
1505  if(raw == HIGH_INSTR_SATUI4)
1506  bufferVal = HIGH_INSTR_SAT8;
1507  else if(raw == HIGH_REPR_SATUI4)
1508  bufferVal = HIGH_REPR_SAT8;
1509  else
1510  bufferVal = LOW_REPR_SAT8;
1511  }
1512  else {
1513  if(raw == NULLUI4)
1514  bufferVal = NULL8;
1515  else if(raw == LOW_INSTR_SATUI4)
1516  bufferVal = LOW_INSTR_SAT8;
1517  else if(raw == LOW_REPR_SATUI4)
1518  bufferVal = LOW_REPR_SAT8;
1519  else
1520  bufferVal = LOW_REPR_SAT8;
1521  }
1522 
1523  ((unsigned int *)buffersRawBuf)[bufferIndex] = raw;
1524 
1525 
1526 
1527  }
1528 
1529  else if(m_pixelType == UnsignedByte) {
1530  unsigned char raw = ((unsigned char *)chunkBuf)[chunkIndex];
1531 
1532  if(raw == NULL1) {
1533  bufferVal = NULL8;
1534  }
1535  else if(raw == HIGH_REPR_SAT1) {
1536  bufferVal = HIGH_REPR_SAT8;
1537  }
1538  else {
1539  bufferVal = (double) raw * m_multiplier + m_base;
1540  }
1541 
1542  ((unsigned char *)buffersRawBuf)[bufferIndex] = raw;
1543  }
1544 
1545  bufferIndex ++;
1546  }
1547  }
1548  }
1549  }
1550  }
1551 
1552 
1560  void CubeIoHandler::writeIntoRaw(const Buffer &buffer, RawCubeChunk &output, int index)
1561  const {
1562  // The code in this method is highly optimized. Even the order of the if
1563  // statements will have a significant impact on performance if changed.
1564  // Also, there is a lot of duplicate code in both writeIntoDouble(...) and
1565  // writeIntoRaw(...). This is needed for performance gain. Any function
1566  // or method calls from within the x loop cause significant performance
1567  // decreases.
1568  int startX = 0;
1569  int startY = 0;
1570  int startZ = 0;
1571 
1572  int endX = 0;
1573  int endY = 0;
1574  int endZ = 0;
1575 
1576  output.setDirty(true);
1577  findIntersection(output, buffer, startX, startY, startZ, endX, endY, endZ);
1578 
1579  int bufferBand = buffer.Band();
1580  int bufferBands = buffer.BandDimension();
1581  int outputStartSample = output.getStartSample();
1582  int outputStartLine = output.getStartLine();
1583  int outputStartBand = output.getStartBand();
1584  int lineSize = output.sampleCount();
1585  int bandSize = lineSize * output.lineCount();
1586  double *buffersDoubleBuf = buffer.DoubleBuffer();
1587  char *chunkBuf = output.getRawData().data();
1588 
1589  for(int z = startZ; z <= endZ; z++) {
1590  const int &bandIntoChunk = z - outputStartBand;
1591  int virtualBand = index;
1592 
1593 
1594  if(m_virtualBands) {
1595  virtualBand = m_virtualBands->indexOf(virtualBand) + 1;
1596  }
1597 
1598  if(virtualBand != 0 && virtualBand >= bufferBand &&
1599  virtualBand <= bufferBand + bufferBands - 1) {
1600 
1601  for(int y = startY; y <= endY; y++) {
1602  const int &lineIntoChunk = y - outputStartLine;
1603  int bufferIndex = buffer.Index(startX, y, virtualBand);
1604 
1605  for(int x = startX; x <= endX; x++) {
1606  const int &sampleIntoChunk = x - outputStartSample;
1607 
1608  const int &chunkIndex = sampleIntoChunk +
1609  (lineSize * lineIntoChunk) + (bandSize * bandIntoChunk);
1610 
1611  double bufferVal = buffersDoubleBuf[bufferIndex];
1612 
1613  if(m_pixelType == Real) {
1614  float raw = 0;
1615 
1616  if(bufferVal >= VALID_MIN8) {
1617  double filePixelValueDbl = (bufferVal - m_base) /
1618  m_multiplier;
1619 
1620  if(filePixelValueDbl < (double) VALID_MIN4) {
1621  raw = LOW_REPR_SAT4;
1622  }
1623  else if(filePixelValueDbl > (double) VALID_MAX4) {
1624  raw = HIGH_REPR_SAT4;
1625  }
1626  else {
1627  raw = (float) filePixelValueDbl;
1628  }
1629  }
1630  else {
1631  if(bufferVal == NULL8)
1632  raw = NULL4;
1633  else if(bufferVal == LOW_INSTR_SAT8)
1634  raw = LOW_INSTR_SAT4;
1635  else if(bufferVal == LOW_REPR_SAT8)
1636  raw = LOW_REPR_SAT4;
1637  else if(bufferVal == HIGH_INSTR_SAT8)
1638  raw = HIGH_INSTR_SAT4;
1639  else if(bufferVal == HIGH_REPR_SAT8)
1640  raw = HIGH_REPR_SAT4;
1641  else
1642  raw = LOW_REPR_SAT4;
1643  }
1644  ((float *)chunkBuf)[chunkIndex] =
1645  m_byteSwapper ? m_byteSwapper->Float(&raw) : raw;
1646  }
1647 
1648  else if(m_pixelType == SignedWord) {
1649  short raw;
1650 
1651  if(bufferVal >= VALID_MIN8) {
1652  double filePixelValueDbl = (bufferVal - m_base) /
1653  m_multiplier;
1654  if(filePixelValueDbl < VALID_MIN2 - 0.5) {
1655  raw = LOW_REPR_SAT2;
1656  }
1657  if(filePixelValueDbl > VALID_MAX2 + 0.5) {
1658  raw = HIGH_REPR_SAT2;
1659  }
1660  else {
1661  int filePixelValue = (int)round(filePixelValueDbl);
1662 
1663  if(filePixelValue < VALID_MIN2) {
1664  raw = LOW_REPR_SAT2;
1665  }
1666  else if(filePixelValue > VALID_MAX2) {
1667  raw = HIGH_REPR_SAT2;
1668  }
1669  else {
1670  raw = filePixelValue;
1671  }
1672  }
1673  }
1674  else {
1675  if(bufferVal == NULL8)
1676  raw = NULL2;
1677  else if(bufferVal == LOW_INSTR_SAT8)
1678  raw = LOW_INSTR_SAT2;
1679  else if(bufferVal == LOW_REPR_SAT8)
1680  raw = LOW_REPR_SAT2;
1681  else if(bufferVal == HIGH_INSTR_SAT8)
1682  raw = HIGH_INSTR_SAT2;
1683  else if(bufferVal == HIGH_REPR_SAT8)
1684  raw = HIGH_REPR_SAT2;
1685  else
1686  raw = LOW_REPR_SAT2;
1687  }
1688 
1689  ((short *)chunkBuf)[chunkIndex] =
1690  m_byteSwapper ? m_byteSwapper->ShortInt(&raw) : raw;
1691  }
1692 
1693  else if(m_pixelType == UnsignedInteger) {
1694 
1695  unsigned int raw;
1696 
1697  if(bufferVal >= VALID_MINUI4) {
1698  double filePixelValueDbl = (bufferVal - m_base) /
1699  m_multiplier;
1700  if(filePixelValueDbl < VALID_MINUI4 - 0.5) {
1701  raw = LOW_REPR_SATUI4;
1702  }
1703  if(filePixelValueDbl > VALID_MAXUI4) {
1704  raw = HIGH_REPR_SATUI4;
1705  }
1706  else {
1707  unsigned int filePixelValue = (unsigned int)round(filePixelValueDbl);
1708 
1709  if(filePixelValue < VALID_MINUI4) {
1710  raw = LOW_REPR_SATUI4;
1711  }
1712  else if(filePixelValue > VALID_MAXUI4) {
1713  raw = HIGH_REPR_SATUI4;
1714  }
1715  else {
1716  raw = filePixelValue;
1717  }
1718  }
1719  }
1720  else {
1721  if(bufferVal == NULL8)
1722  raw = NULLUI4;
1723  else if(bufferVal == LOW_INSTR_SAT8)
1724  raw = LOW_INSTR_SATUI4;
1725  else if(bufferVal == LOW_REPR_SAT8)
1726  raw = LOW_REPR_SATUI4;
1727  else if(bufferVal == HIGH_INSTR_SAT8)
1728  raw = HIGH_INSTR_SATUI4;
1729  else if(bufferVal == HIGH_REPR_SAT8)
1730  raw = HIGH_REPR_SATUI4;
1731  else
1732  raw = LOW_REPR_SATUI4;
1733  }
1734 
1735  ((unsigned int *)chunkBuf)[chunkIndex] =
1736  m_byteSwapper ? m_byteSwapper->Uint32_t(&raw) : raw;
1737 
1738  }
1739 
1740 
1741  else if(m_pixelType == UnsignedWord) {
1742  unsigned short raw;
1743 
1744  if(bufferVal >= VALID_MIN8) {
1745  double filePixelValueDbl = (bufferVal - m_base) /
1746  m_multiplier;
1747  if(filePixelValueDbl < VALID_MINU2 - 0.5) {
1748  raw = LOW_REPR_SATU2;
1749  }
1750  if(filePixelValueDbl > VALID_MAXU2 + 0.5) {
1751  raw = HIGH_REPR_SATU2;
1752  }
1753  else {
1754  int filePixelValue = (int)round(filePixelValueDbl);
1755 
1756  if(filePixelValue < VALID_MINU2) {
1757  raw = LOW_REPR_SATU2;
1758  }
1759  else if(filePixelValue > VALID_MAXU2) {
1760  raw = HIGH_REPR_SATU2;
1761  }
1762  else {
1763  raw = filePixelValue;
1764  }
1765  }
1766  }
1767  else {
1768  if(bufferVal == NULL8)
1769  raw = NULLU2;
1770  else if(bufferVal == LOW_INSTR_SAT8)
1771  raw = LOW_INSTR_SATU2;
1772  else if(bufferVal == LOW_REPR_SAT8)
1773  raw = LOW_REPR_SATU2;
1774  else if(bufferVal == HIGH_INSTR_SAT8)
1775  raw = HIGH_INSTR_SATU2;
1776  else if(bufferVal == HIGH_REPR_SAT8)
1777  raw = HIGH_REPR_SATU2;
1778  else
1779  raw = LOW_REPR_SATU2;
1780  }
1781 
1782  ((unsigned short *)chunkBuf)[chunkIndex] =
1783  m_byteSwapper ? m_byteSwapper->UnsignedShortInt(&raw) : raw;
1784  }
1785  else if(m_pixelType == UnsignedByte) {
1786  unsigned char raw;
1787 
1788  if(bufferVal >= VALID_MIN8) {
1789  double filePixelValueDbl = (bufferVal - m_base) /
1790  m_multiplier;
1791  if(filePixelValueDbl < VALID_MIN1 - 0.5) {
1792  raw = LOW_REPR_SAT1;
1793  }
1794  else if(filePixelValueDbl > VALID_MAX1 + 0.5) {
1795  raw = HIGH_REPR_SAT1;
1796  }
1797  else {
1798  int filePixelValue = (int)(filePixelValueDbl + 0.5);
1799  if(filePixelValue < VALID_MIN1) {
1800  raw = LOW_REPR_SAT1;
1801  }
1802  else if(filePixelValue > VALID_MAX1) {
1803  raw = HIGH_REPR_SAT1;
1804  }
1805  else {
1806  raw = (unsigned char)(filePixelValue);
1807  }
1808  }
1809  }
1810  else {
1811  if(bufferVal == NULL8)
1812  raw = NULL1;
1813  else if(bufferVal == LOW_INSTR_SAT8)
1814  raw = LOW_INSTR_SAT1;
1815  else if(bufferVal == LOW_REPR_SAT8)
1816  raw = LOW_REPR_SAT1;
1817  else if(bufferVal == HIGH_INSTR_SAT8)
1818  raw = HIGH_INSTR_SAT1;
1819  else if(bufferVal == HIGH_REPR_SAT8)
1820  raw = HIGH_REPR_SAT1;
1821  else
1822  raw = LOW_REPR_SAT1;
1823  }
1824 
1825  ((unsigned char *)chunkBuf)[chunkIndex] = raw;
1826  }
1827 
1828  bufferIndex ++;
1829  }
1830  }
1831  }
1832  }
1833  }
1834 
1835 
1839  void CubeIoHandler::writeNullDataToDisk() const {
1840  if(!m_dataIsOnDiskMap) {
1841  IString msg = "Cannot call CubeIoHandler::writeNullDataToDisk unless "
1842  "data is not already on disk (Cube::Create was called)";
1843  throw IException(IException::Programmer, msg, _FILEINFO_);
1844  }
1845 
1846  int numChunks = getChunkCount();
1847  for(int i = 0; i < numChunks; i++) {
1848  if(!(*m_dataIsOnDiskMap)[i]) {
1849  RawCubeChunk *nullChunk = getNullChunk(i);
1850  (const_cast<CubeIoHandler *>(this))->writeRaw(*nullChunk);
1851  (*m_dataIsOnDiskMap)[i] = true;
1852 
1853  delete nullChunk;
1854  nullChunk = NULL;
1855  }
1856  }
1857  }
1858 
1859 
1873  CubeIoHandler::BufferToChunkWriter::BufferToChunkWriter(
1874  CubeIoHandler * ioHandler, QList<Buffer *> buffersToWrite) {
1875  m_ioHandler = ioHandler;
1876  m_buffersToWrite = new QList<Buffer *>(buffersToWrite);
1877  m_timer = new QTime;
1878  m_timer->start();
1879 
1880  m_ioHandler->m_writeThreadMutex->lock();
1881  }
1882 
1883 
1894  CubeIoHandler::BufferToChunkWriter::~BufferToChunkWriter() {
1895  int elapsedMs = m_timer->elapsed();
1896  int idealFlushElapsedTime = 100; // ms
1897 
1898  // We want to aim our flush size at 100ms, so adjust accordingly to aim at
1899  // our target. This method seems to be extremely effective because
1900  // we maximize our I/O calls when caching is interfering and normalize
1901  // them otherwise.
1902  int msOffIdeal = elapsedMs - idealFlushElapsedTime;
1903  double percentOffIdeal = msOffIdeal / (double)idealFlushElapsedTime;
1904 
1905  // flush size is bounded to [32, 5000]
1906  int currentCacheSize = m_ioHandler->m_idealFlushSize;
1907  int desiredAdjustment = -1 * currentCacheSize * percentOffIdeal;
1908  int desiredCacheSize = (int)(currentCacheSize + desiredAdjustment);
1909  m_ioHandler->m_idealFlushSize = (int)(qMin(5000,
1910  qMax(32, desiredCacheSize)));
1911 
1912  delete m_timer;
1913  m_timer = NULL;
1914 
1915  m_ioHandler->m_writeThreadMutex->unlock();
1916  m_ioHandler = NULL;
1917 
1918  ASSERT(m_buffersToWrite->isEmpty());
1919  delete m_buffersToWrite;
1920  m_buffersToWrite = NULL;
1921  }
1922 
1923 
1929  void CubeIoHandler::BufferToChunkWriter::run() {
1930  // Sorting the buffers didn't seem to have a large positive impact on speed,
1931  // but does increase complexity so it's disabled.
1932 // QList<Buffer * > buffersToWrite(*m_buffersToWrite);
1933 // qSort(buffersToWrite.begin(), buffersToWrite.end(), bufferLessThan);
1934 
1935  // If the buffers have any overlap at all then we can't sort them and still
1936  // guarantee the last write() call makes it into the correct place. The
1937  // bufferLessThan is guaranteed to return false if there is overlap.
1938 // bool sortable = true;
1939 // for (int i = 1; sortable && i < buffersToWrite.size(); i++) {
1940 // if (!bufferLessThan(buffersToWrite[i - 1], buffersToWrite[i])) {
1941 // sortable = false;
1942 // }
1943 // }
1944 
1945 // if (!sortable) {
1946 // buffersToWrite = *m_buffersToWrite;
1947 // }
1948 
1949  foreach (Buffer * bufferToWrite, *m_buffersToWrite) {
1950  m_ioHandler->synchronousWrite(*bufferToWrite);
1951  delete bufferToWrite;
1952  }
1953 
1954  m_buffersToWrite->clear();
1955  m_ioHandler->m_dataFile->flush();
1956  }
1957 }
Isis::Brick::SetBasePosition
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:120
Isis::SizeOf
int SizeOf(Isis::PixelType pixelType)
Returns the number of bytes of the specified PixelType.
Definition: PixelType.h:46
Isis::RawCubeChunk::setDirty
void setDirty(bool dirty)
Sets the chunk's dirty flag, indicating whether or not the chunk's data matches the data that is on d...
Definition: RawCubeChunk.cpp:213
Isis::RawCubeChunk
A section of raw data on the disk.
Definition: RawCubeChunk.h:27
Isis::RawCubeChunk::getStartBand
int getStartBand() const
Definition: RawCubeChunk.h:67
Isis::CubeCachingAlgorithm::CacheResult::getChunksToFree
QList< RawCubeChunk * > getChunksToFree() const
Definition: CubeCachingAlgorithm.cpp:92
Isis::PvlObject::findGroup
PvlGroupIterator findGroup(const QString &name, PvlGroupIterator beg, PvlGroupIterator end)
Find a group with the specified name, within these indexes.
Definition: PvlObject.h:129
Isis::Buffer::SampleDimension
int SampleDimension() const
Returns the number of samples in the shape buffer.
Definition: Buffer.h:70
Isis::PvlObject
Contains Pvl Groups and Pvl Objects.
Definition: PvlObject.h:61
Isis::IString::DownCase
IString DownCase()
Converts all upper case letters in the object IString into lower case characters.
Definition: IString.cpp:644
Isis::RawCubeChunk::getStartLine
int getStartLine() const
Definition: RawCubeChunk.h:60
QList< int >
Isis::RawCubeChunk::sampleCount
int sampleCount() const
Definition: RawCubeChunk.h:74
Isis::RawCubeChunk::isDirty
bool isDirty() const
Definition: RawCubeChunk.cpp:90
Isis::Buffer::DoubleBuffer
double * DoubleBuffer() const
Returns the value of the shape buffer.
Definition: Buffer.h:138
Isis::RawCubeChunk::bandCount
int bandCount() const
Definition: RawCubeChunk.h:88
Isis::Area3D::isValid
bool isValid() const
Returns true if all of the positions of the 3D area are valid (i.e.
Definition: Area3D.cpp:489
Isis::Buffer::Index
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:197
Isis::RegionalCachingAlgorithm
This algorithm recommends chunks to be freed that are not within the last IO.
Definition: RegionalCachingAlgorithm.h:26
Isis::Pvl
Container for cube-like labels.
Definition: Pvl.h:119
Isis::RawCubeChunk::getRawData
QByteArray & getRawData() const
Definition: RawCubeChunk.h:38
Isis::Brick
Buffer for containing a three dimensional section of an image.
Definition: Brick.h:45
Isis::CubeCachingAlgorithm::CacheResult
This stores the results of the caching algorithm.
Definition: CubeCachingAlgorithm.h:45
Isis::CubeCachingAlgorithm::recommendChunksToFree
virtual CacheResult recommendChunksToFree(QList< RawCubeChunk * > allocated, QList< RawCubeChunk * > justUsed, const Buffer &justRequested)=0
Call this to determine which chunks should be freed from memory.
Isis::Buffer::RawBuffer
void * RawBuffer() const
Returns a void pointer to the raw buffer.
Definition: Buffer.h:151
Isis::Distance
Distance measurement, usually in meters.
Definition: Distance.h:34
Isis::Buffer
Buffer for reading and writing cube data.
Definition: Buffer.h:53
Isis::RawCubeChunk::lineCount
int lineCount() const
Definition: RawCubeChunk.h:81
Isis::Displacement
Displacement is a signed length, usually in meters.
Definition: Displacement.h:31
Isis::PvlGroup
Contains multiple PvlContainers.
Definition: PvlGroup.h:41
Isis::CubeCachingAlgorithm
This is the parent of the caching algorithms.
Definition: CubeCachingAlgorithm.h:31
Isis::Buffer::LineDimension
int LineDimension() const
Returns the number of lines in the shape buffer.
Definition: Buffer.h:79
Isis::CubeIoHandler
Handles converting buffers to and from disk.
Definition: CubeIoHandler.h:109
Isis::BigInt
long long int BigInt
Big int.
Definition: Constants.h:49
Isis::PvlObject::findObject
PvlObjectIterator findObject(const QString &name, PvlObjectIterator beg, PvlObjectIterator end)
Find the index of object with a specified name, between two indexes.
Definition: PvlObject.h:274
Isis::IException
Isis exception class.
Definition: IException.h:91
Isis::RawCubeChunk::setRawData
void setRawData(QByteArray rawData)
Sets the chunk's raw data.
Definition: RawCubeChunk.cpp:101
Isis::RawCubeChunk::getStartSample
int getStartSample() const
Definition: RawCubeChunk.h:53
Isis::Null
const double Null
Value for an Isis Null pixel.
Definition: SpecialPixel.h:95
Isis::Buffer::Band
int Band(const int index=0) const
Returns the band position associated with a shape buffer index.
Definition: Buffer.cpp:162
Isis::PixelType
PixelType
Enumerations for Isis Pixel Types.
Definition: PixelType.h:27
Isis::Area3D
Represents a 3D area (a 3D "cube")
Definition: Area3D.h:29
std
Namespace for the standard library.
Isis::CubeCachingAlgorithm::CacheResult::algorithmUnderstoodData
bool algorithmUnderstoodData() const
If this is true, then the results (be them empty or not) should be considered valid.
Definition: CubeCachingAlgorithm.cpp:83
QPair
This is free and unencumbered software released into the public domain.
Definition: CubeIoHandler.h:23
QRunnable
Isis::EndianSwapper
Byte swapper.
Definition: EndianSwapper.h:38
Isis::PixelTypeEnumeration
Isis::PixelType PixelTypeEnumeration(const QString &type)
Returns PixelType enumeration given a string.
Definition: PixelType.h:89
QMap
This is free and unencumbered software released into the public domain.
Definition: CubeIoHandler.h:22
Isis::CubeIoHandler::BufferToChunkWriter
This class is designed to handle write() asynchronously.
Definition: CubeIoHandler.h:185
Isis::Buffer::size
int size() const
Returns the total number of pixels in the shape buffer.
Definition: Buffer.h:97
Isis::PvlObject::findKeyword
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:177
Isis::IString
Adds specific functionality to C++ strings.
Definition: IString.h:165
Isis::PvlContainer::findKeyword
PvlKeyword & findKeyword(const QString &name)
Find a keyword with a specified name.
Definition: PvlContainer.cpp:62
Isis::Area3D::intersect
Area3D intersect(const Area3D &otherArea) const
Returns the intersection of this 3D area with another 3D area.
Definition: Area3D.cpp:462
Isis::Buffer::Sample
int Sample(const int index=0) const
Returns the sample position associated with a shape buffer index.
Definition: Buffer.cpp:127
Isis::Buffer::Line
int Line(const int index=0) const
Returns the line position associated with a shape buffer index.
Definition: Buffer.cpp:145
Isis::Buffer::BandDimension
int BandDimension() const
Returns the number of bands in the shape buffer.
Definition: Buffer.h:88
Isis
This is free and unencumbered software released into the public domain.
Definition: Apollo.h:16