Line data Source code
1 : /*
2 : * keacopy.cpp
3 : *
4 : * Created by Pete Bunting on 01/08/2012.
5 : * Copyright 2012 LibKEA. All rights reserved.
6 : *
7 : * This file is part of LibKEA.
8 : *
9 : * SPDX-License-Identifier: MIT
10 : *
11 : */
12 :
13 : #include <cmath>
14 : #include "gdal_priv.h"
15 : #include "gdal_rat.h"
16 :
17 : #include "keacopy.h"
18 :
19 : // Support functions for CreateCopy()
20 :
21 : // Copies GDAL Band to KEA Band if nOverview == -1
22 : // Otherwise it is assumed we are writing to the specified overview
23 89 : static bool KEACopyRasterData(GDALRasterBand *pBand,
24 : kealib::KEAImageIO *pImageIO, int nBand,
25 : int nOverview, int nTotalBands,
26 : GDALProgressFunc pfnProgress, void *pProgressData)
27 : {
28 : // get some info
29 89 : kealib::KEADataType eKeaType = pImageIO->getImageBandDataType(nBand);
30 : unsigned int nBlockSize;
31 89 : if (nOverview == -1)
32 88 : nBlockSize = pImageIO->getImageBlockSize(nBand);
33 : else
34 1 : nBlockSize = pImageIO->getOverviewBlockSize(nBand, nOverview);
35 :
36 89 : GDALDataType eGDALType = pBand->GetRasterDataType();
37 89 : unsigned int nXSize = pBand->GetXSize();
38 89 : unsigned int nYSize = pBand->GetYSize();
39 :
40 : // allocate some space
41 89 : int nPixelSize = GDALGetDataTypeSize(eGDALType) / 8;
42 89 : void *pData = VSIMalloc3(nPixelSize, nBlockSize, nBlockSize);
43 89 : if (pData == nullptr)
44 : {
45 0 : CPLError(CE_Failure, CPLE_AppDefined, "Unable to allocate memory");
46 0 : return false;
47 : }
48 : // for progress
49 89 : int nTotalBlocks =
50 89 : static_cast<int>(std::ceil((double)nXSize / (double)nBlockSize) *
51 89 : std::ceil((double)nYSize / (double)nBlockSize));
52 89 : int nBlocksComplete = 0;
53 89 : double dLastFraction = -1;
54 : // go through the image
55 179 : for (unsigned int nY = 0; nY < nYSize; nY += nBlockSize)
56 : {
57 : // adjust for edge blocks
58 90 : unsigned int nysize = nBlockSize;
59 90 : unsigned int nytotalsize = nY + nBlockSize;
60 90 : if (nytotalsize > nYSize)
61 1 : nysize -= (nytotalsize - nYSize);
62 182 : for (unsigned int nX = 0; nX < nXSize; nX += nBlockSize)
63 : {
64 : // adjust for edge blocks
65 92 : unsigned int nxsize = nBlockSize;
66 92 : unsigned int nxtotalsize = nX + nBlockSize;
67 92 : if (nxtotalsize > nXSize)
68 2 : nxsize -= (nxtotalsize - nXSize);
69 :
70 : // read in from GDAL
71 184 : if (pBand->RasterIO(GF_Read, nX, nY, nxsize, nysize, pData, nxsize,
72 : nysize, eGDALType, nPixelSize,
73 92 : nPixelSize * nBlockSize, nullptr) != CE_None)
74 : {
75 0 : CPLError(CE_Failure, CPLE_AppDefined,
76 : "Unable to read block at %d %d\n", nX, nY);
77 0 : return false;
78 : }
79 : // write out to KEA
80 92 : if (nOverview == -1)
81 91 : pImageIO->writeImageBlock2Band(nBand, pData, nX, nY, nxsize,
82 : nysize, nBlockSize, nBlockSize,
83 : eKeaType);
84 : else
85 1 : pImageIO->writeToOverview(nBand, nOverview, pData, nX, nY,
86 : nxsize, nysize, nBlockSize,
87 : nBlockSize, eKeaType);
88 :
89 : // progress
90 92 : nBlocksComplete++;
91 92 : if (nOverview == -1)
92 : {
93 91 : double dFraction =
94 91 : (((double)nBlocksComplete / (double)nTotalBlocks) /
95 91 : (double)nTotalBands) +
96 91 : ((double)(nBand - 1) * (1.0 / (double)nTotalBands));
97 91 : if (dFraction != dLastFraction)
98 : {
99 91 : if (!pfnProgress(dFraction, nullptr, pProgressData))
100 : {
101 0 : CPLFree(pData);
102 0 : return false;
103 : }
104 91 : dLastFraction = dFraction;
105 : }
106 : }
107 : }
108 : }
109 :
110 89 : CPLFree(pData);
111 89 : return true;
112 : }
113 :
114 : // copies the raster attribute table
115 88 : static void KEACopyRAT(GDALRasterBand *pBand, kealib::KEAImageIO *pImageIO,
116 : int nBand)
117 : {
118 88 : const GDALRasterAttributeTable *gdalAtt = pBand->GetDefaultRAT();
119 88 : if ((gdalAtt != nullptr) && (gdalAtt->GetRowCount() > 0))
120 : {
121 : // some operations depend on whether the input dataset is HFA
122 : int bInputHFA =
123 6 : pBand->GetDataset()->GetDriver() != nullptr &&
124 3 : EQUAL(pBand->GetDataset()->GetDriver()->GetDescription(), "HFA");
125 :
126 : kealib::KEAAttributeTable *keaAtt =
127 3 : pImageIO->getAttributeTable(kealib::kea_att_file, nBand);
128 :
129 : /*bool redDef = false;
130 : int redIdx = -1;
131 : bool greenDef = false;
132 : int greenIdx = -1;
133 : bool blueDef = false;
134 : int blueIdx = -1;
135 : bool alphaDef = false;
136 : int alphaIdx = -1;*/
137 :
138 3 : int numCols = gdalAtt->GetColumnCount();
139 : std::vector<kealib::KEAATTField *> *fields =
140 3 : new std::vector<kealib::KEAATTField *>();
141 : kealib::KEAATTField *field;
142 18 : for (int ni = 0; ni < numCols; ++ni)
143 : {
144 15 : field = new kealib::KEAATTField();
145 15 : field->name = gdalAtt->GetNameOfCol(ni);
146 :
147 15 : field->dataType = kealib::kea_att_string;
148 15 : switch (gdalAtt->GetTypeOfCol(ni))
149 : {
150 12 : case GFT_Integer:
151 12 : field->dataType = kealib::kea_att_int;
152 12 : break;
153 2 : case GFT_Real:
154 2 : field->dataType = kealib::kea_att_float;
155 2 : break;
156 1 : case GFT_String:
157 1 : field->dataType = kealib::kea_att_string;
158 1 : break;
159 0 : default:
160 : // leave as "kea_att_string"
161 0 : break;
162 : }
163 :
164 15 : if (bInputHFA && (field->name == "Histogram"))
165 : {
166 0 : field->usage = "PixelCount";
167 0 : field->dataType = kealib::kea_att_int;
168 : }
169 15 : else if (bInputHFA && (field->name == "Opacity"))
170 : {
171 0 : field->name = "Alpha";
172 0 : field->usage = "Alpha";
173 0 : field->dataType = kealib::kea_att_int;
174 : /*alphaDef = true;
175 : alphaIdx = ni;*/
176 : }
177 : else
178 : {
179 15 : field->usage = "Generic";
180 15 : switch (gdalAtt->GetUsageOfCol(ni))
181 : {
182 1 : case GFU_PixelCount:
183 1 : field->usage = "PixelCount";
184 1 : break;
185 1 : case GFU_Name:
186 1 : field->usage = "Name";
187 1 : break;
188 3 : case GFU_Red:
189 3 : field->usage = "Red";
190 3 : if (bInputHFA)
191 : {
192 0 : field->dataType = kealib::kea_att_int;
193 : /*redDef = true;
194 : redIdx = ni;*/
195 : }
196 3 : break;
197 3 : case GFU_Green:
198 3 : field->usage = "Green";
199 3 : if (bInputHFA)
200 : {
201 0 : field->dataType = kealib::kea_att_int;
202 : /*greenDef = true;
203 : greenIdx = ni;*/
204 : }
205 3 : break;
206 3 : case GFU_Blue:
207 3 : field->usage = "Blue";
208 3 : if (bInputHFA)
209 : {
210 0 : field->dataType = kealib::kea_att_int;
211 : /*blueDef = true;
212 : blueIdx = ni;*/
213 : }
214 3 : break;
215 3 : case GFU_Alpha:
216 3 : field->usage = "Alpha";
217 3 : break;
218 1 : default:
219 : // leave as "Generic"
220 1 : break;
221 : }
222 : }
223 :
224 15 : fields->push_back(field);
225 : }
226 :
227 : // This function will populate the field indexes used within
228 : // the KEA RAT.
229 3 : keaAtt->addFields(fields);
230 :
231 3 : int numRows = gdalAtt->GetRowCount();
232 3 : keaAtt->addRows(numRows);
233 :
234 3 : int *pnIntBuffer = new int[kealib::KEA_ATT_CHUNK_SIZE];
235 3 : int64_t *pnInt64Buffer = new int64_t[kealib::KEA_ATT_CHUNK_SIZE];
236 3 : double *pfDoubleBuffer = new double[kealib::KEA_ATT_CHUNK_SIZE];
237 6 : for (int ni = 0; ni < numRows; ni += kealib::KEA_ATT_CHUNK_SIZE)
238 : {
239 3 : int nLength = kealib::KEA_ATT_CHUNK_SIZE;
240 3 : if ((ni + nLength) > numRows)
241 : {
242 3 : nLength = numRows - ni;
243 : }
244 18 : for (int nj = 0; nj < numCols; ++nj)
245 : {
246 15 : field = fields->at(nj);
247 :
248 15 : switch (field->dataType)
249 : {
250 12 : case kealib::kea_att_int:
251 : ((GDALRasterAttributeTable *)gdalAtt)
252 12 : ->ValuesIO(GF_Read, nj, ni, nLength, pnIntBuffer);
253 40 : for (int i = 0; i < nLength; i++)
254 : {
255 28 : pnInt64Buffer[i] = pnIntBuffer[i];
256 : }
257 12 : keaAtt->setIntFields(ni, nLength, field->idx,
258 12 : pnInt64Buffer);
259 12 : break;
260 2 : case kealib::kea_att_float:
261 : ((GDALRasterAttributeTable *)gdalAtt)
262 2 : ->ValuesIO(GF_Read, nj, ni, nLength,
263 2 : pfDoubleBuffer);
264 2 : keaAtt->setFloatFields(ni, nLength, field->idx,
265 2 : pfDoubleBuffer);
266 2 : break;
267 1 : case kealib::kea_att_string:
268 : {
269 : char **papszColData =
270 1 : (char **)VSIMalloc2(nLength, sizeof(char *));
271 : ((GDALRasterAttributeTable *)gdalAtt)
272 1 : ->ValuesIO(GF_Read, nj, ni, nLength, papszColData);
273 :
274 2 : std::vector<std::string> aStringBuffer;
275 2 : for (int i = 0; i < nLength; i++)
276 : {
277 1 : aStringBuffer.push_back(papszColData[i]);
278 : }
279 :
280 2 : for (int i = 0; i < nLength; i++)
281 1 : CPLFree(papszColData[i]);
282 1 : CPLFree(papszColData);
283 :
284 1 : keaAtt->setStringFields(ni, nLength, field->idx,
285 1 : &aStringBuffer);
286 : }
287 1 : break;
288 0 : default:
289 : // Ignore as data type is not known or available from a
290 : // HFA/GDAL RAT."
291 0 : break;
292 : }
293 : }
294 : }
295 :
296 3 : delete[] pnIntBuffer;
297 3 : delete[] pnInt64Buffer;
298 3 : delete[] pfDoubleBuffer;
299 :
300 3 : delete keaAtt;
301 15 : for (std::vector<kealib::KEAATTField *>::iterator iterField =
302 3 : fields->begin();
303 33 : iterField != fields->end(); ++iterField)
304 : {
305 15 : delete *iterField;
306 : }
307 3 : delete fields;
308 : }
309 88 : }
310 :
311 : // copies the metadata
312 : // pass nBand == -1 to copy a dataset's metadata
313 : // or band index to copy a band's metadata
314 149 : static void KEACopyMetadata(GDALMajorObject *pObject,
315 : kealib::KEAImageIO *pImageIO, int nBand)
316 : {
317 149 : char **ppszMetadata = pObject->GetMetadata();
318 149 : if (ppszMetadata != nullptr)
319 : {
320 86 : int nCount = 0;
321 252 : while (ppszMetadata[nCount] != nullptr)
322 : {
323 166 : char *pszName = nullptr;
324 : const char *pszValue =
325 166 : CPLParseNameValue(ppszMetadata[nCount], &pszName);
326 166 : if (pszValue == nullptr)
327 12 : pszValue = "";
328 166 : if (pszName != nullptr)
329 : {
330 : // it is LAYER_TYPE and a Band? if so handle separately
331 154 : if ((nBand != -1) && EQUAL(pszName, "LAYER_TYPE"))
332 : {
333 55 : if (EQUAL(pszValue, "athematic"))
334 : {
335 53 : pImageIO->setImageBandLayerType(nBand,
336 : kealib::kea_continuous);
337 : }
338 : else
339 : {
340 2 : pImageIO->setImageBandLayerType(nBand,
341 : kealib::kea_thematic);
342 : }
343 : }
344 99 : else if ((nBand != -1) &&
345 84 : EQUAL(pszName, "STATISTICS_HISTOBINVALUES"))
346 : {
347 : // This gets copied across as part of the attributes
348 : // so ignore for now.
349 : }
350 : else
351 : {
352 : // write it into the image
353 99 : if (nBand != -1)
354 84 : pImageIO->setImageBandMetaData(nBand, pszName,
355 : pszValue);
356 : else
357 15 : pImageIO->setImageMetaData(pszName, pszValue);
358 : }
359 154 : CPLFree(pszName);
360 : }
361 166 : nCount++;
362 : }
363 : }
364 149 : }
365 :
366 : // copies the description over
367 88 : static void KEACopyDescription(GDALRasterBand *pBand,
368 : kealib::KEAImageIO *pImageIO, int nBand)
369 : {
370 88 : const char *pszDesc = pBand->GetDescription();
371 88 : pImageIO->setImageBandDescription(nBand, pszDesc);
372 88 : }
373 :
374 : // copies the no data value across
375 88 : static void KEACopyNoData(GDALRasterBand *pBand, kealib::KEAImageIO *pImageIO,
376 : int nBand)
377 : {
378 88 : int bSuccess = 0;
379 88 : double dNoData = pBand->GetNoDataValue(&bSuccess);
380 88 : if (bSuccess)
381 : {
382 17 : pImageIO->setNoDataValue(nBand, &dNoData, kealib::kea_64float);
383 : }
384 88 : }
385 :
386 88 : static bool KEACopyBand(GDALRasterBand *pBand, kealib::KEAImageIO *pImageIO,
387 : int nBand, int nTotalbands,
388 : GDALProgressFunc pfnProgress, void *pProgressData)
389 : {
390 : // first copy the raster data over
391 88 : if (!KEACopyRasterData(pBand, pImageIO, nBand, -1, nTotalbands, pfnProgress,
392 : pProgressData))
393 0 : return false;
394 :
395 : // are there any overviews?
396 88 : int nOverviews = pBand->GetOverviewCount();
397 89 : for (int nOverviewCount = 0; nOverviewCount < nOverviews; nOverviewCount++)
398 : {
399 1 : GDALRasterBand *pOverview = pBand->GetOverview(nOverviewCount);
400 1 : int nOverviewXSize = pOverview->GetXSize();
401 1 : int nOverviewYSize = pOverview->GetYSize();
402 1 : pImageIO->createOverview(nBand, nOverviewCount + 1, nOverviewXSize,
403 : nOverviewYSize);
404 1 : if (!KEACopyRasterData(pOverview, pImageIO, nBand, nOverviewCount + 1,
405 : nTotalbands, pfnProgress, pProgressData))
406 0 : return false;
407 : }
408 :
409 : // now metadata
410 88 : KEACopyMetadata(pBand, pImageIO, nBand);
411 :
412 : // and attributes
413 88 : KEACopyRAT(pBand, pImageIO, nBand);
414 :
415 : // and description
416 88 : KEACopyDescription(pBand, pImageIO, nBand);
417 :
418 : // and no data
419 88 : KEACopyNoData(pBand, pImageIO, nBand);
420 :
421 88 : return true;
422 : }
423 :
424 61 : static void KEACopySpatialInfo(GDALDataset *pDataset,
425 : kealib::KEAImageIO *pImageIO)
426 : {
427 61 : kealib::KEAImageSpatialInfo *pSpatialInfo = pImageIO->getSpatialInfo();
428 :
429 : double padfTransform[6];
430 61 : if (pDataset->GetGeoTransform(padfTransform) == CE_None)
431 : {
432 : // convert back from GDAL's array format
433 61 : pSpatialInfo->tlX = padfTransform[0];
434 61 : pSpatialInfo->xRes = padfTransform[1];
435 61 : pSpatialInfo->xRot = padfTransform[2];
436 61 : pSpatialInfo->tlY = padfTransform[3];
437 61 : pSpatialInfo->yRot = padfTransform[4];
438 61 : pSpatialInfo->yRes = padfTransform[5];
439 : }
440 :
441 61 : const char *pszProjection = pDataset->GetProjectionRef();
442 61 : pSpatialInfo->wktString = pszProjection;
443 :
444 61 : pImageIO->setSpatialInfo(pSpatialInfo);
445 61 : }
446 :
447 : // copies the GCP's across
448 61 : static void KEACopyGCPs(GDALDataset *pDataset, kealib::KEAImageIO *pImageIO)
449 : {
450 61 : int nGCPs = pDataset->GetGCPCount();
451 :
452 61 : if (nGCPs > 0)
453 : {
454 2 : std::vector<kealib::KEAImageGCP *> KEAGCPs;
455 1 : const GDAL_GCP *pGDALGCPs = pDataset->GetGCPs();
456 :
457 3 : for (int n = 0; n < nGCPs; n++)
458 : {
459 2 : kealib::KEAImageGCP *pGCP = new kealib::KEAImageGCP;
460 2 : pGCP->pszId = pGDALGCPs[n].pszId;
461 2 : pGCP->pszInfo = pGDALGCPs[n].pszInfo;
462 2 : pGCP->dfGCPPixel = pGDALGCPs[n].dfGCPPixel;
463 2 : pGCP->dfGCPLine = pGDALGCPs[n].dfGCPLine;
464 2 : pGCP->dfGCPX = pGDALGCPs[n].dfGCPX;
465 2 : pGCP->dfGCPY = pGDALGCPs[n].dfGCPY;
466 2 : pGCP->dfGCPZ = pGDALGCPs[n].dfGCPZ;
467 2 : KEAGCPs.push_back(pGCP);
468 : }
469 :
470 1 : const char *pszGCPProj = pDataset->GetGCPProjection();
471 : try
472 : {
473 1 : pImageIO->setGCPs(&KEAGCPs, pszGCPProj);
474 : }
475 0 : catch (const kealib::KEAException &)
476 : {
477 : }
478 :
479 3 : for (std::vector<kealib::KEAImageGCP *>::iterator itr = KEAGCPs.begin();
480 5 : itr != KEAGCPs.end(); ++itr)
481 : {
482 2 : delete (*itr);
483 : }
484 : }
485 61 : }
486 :
487 61 : bool KEACopyFile(GDALDataset *pDataset, kealib::KEAImageIO *pImageIO,
488 : GDALProgressFunc pfnProgress, void *pProgressData)
489 : {
490 : // Main function - copies pDataset to pImageIO
491 :
492 : // Copy across the spatial info.
493 61 : KEACopySpatialInfo(pDataset, pImageIO);
494 :
495 : // dataset metadata
496 61 : KEACopyMetadata(pDataset, pImageIO, -1);
497 :
498 : // GCPs
499 61 : KEACopyGCPs(pDataset, pImageIO);
500 :
501 : // now copy all the bands over
502 61 : int nBands = pDataset->GetRasterCount();
503 149 : for (int nBand = 0; nBand < nBands; nBand++)
504 : {
505 88 : GDALRasterBand *pBand = pDataset->GetRasterBand(nBand + 1);
506 88 : if (!KEACopyBand(pBand, pImageIO, nBand + 1, nBands, pfnProgress,
507 : pProgressData))
508 0 : return false;
509 : }
510 :
511 61 : pfnProgress(1.0, nullptr, pProgressData);
512 61 : return true;
513 : }
|