Line data Source code
1 : /*
2 : * kearat.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 "kearat.h"
14 :
15 405 : KEARasterAttributeTable::KEARasterAttributeTable(
16 405 : kealib::KEAAttributeTable *poKEATable, KEARasterBand *poBand)
17 : {
18 405 : this->m_hMutex = CPLCreateMutex();
19 405 : CPLReleaseMutex(this->m_hMutex);
20 457 : for (size_t nColumnIndex = 0;
21 457 : nColumnIndex < poKEATable->getMaxGlobalColIdx(); nColumnIndex++)
22 : {
23 52 : kealib::KEAATTField sKEAField;
24 : try
25 : {
26 52 : sKEAField = poKEATable->getField(nColumnIndex);
27 : }
28 0 : catch (const kealib::KEAATTException &)
29 : {
30 : // pKEATable->getField raised exception because we have a missing
31 : // column
32 0 : continue;
33 : }
34 52 : m_aoFields.push_back(std::move(sKEAField));
35 : }
36 405 : m_poKEATable = poKEATable;
37 405 : m_poBand = poBand;
38 405 : }
39 :
40 810 : KEARasterAttributeTable::~KEARasterAttributeTable()
41 : {
42 : // can't just delete thanks to Windows
43 405 : kealib::KEAAttributeTable::destroyAttributeTable(m_poKEATable);
44 405 : CPLDestroyMutex(m_hMutex);
45 405 : m_hMutex = nullptr;
46 810 : }
47 :
48 1 : GDALDefaultRasterAttributeTable *KEARasterAttributeTable::Clone() const
49 : {
50 1 : const int nColCount = GetColumnCount();
51 1 : if (nColCount > 0 && GetRowCount() > RAT_MAX_ELEM_FOR_CLONE / nColCount)
52 0 : return nullptr;
53 :
54 2 : auto poRAT = std::make_unique<GDALDefaultRasterAttributeTable>();
55 :
56 11 : for (int iCol = 0; iCol < (int)m_aoFields.size(); iCol++)
57 : {
58 10 : const auto &sName = m_aoFields[iCol].name;
59 10 : const auto &sUsage = m_aoFields[iCol].usage;
60 : GDALRATFieldUsage eGDALUsage;
61 10 : if (sUsage == "PixelCount")
62 1 : eGDALUsage = GFU_PixelCount;
63 9 : else if (sUsage == "Name")
64 1 : eGDALUsage = GFU_Name;
65 8 : else if (sUsage == "Red")
66 1 : eGDALUsage = GFU_Red;
67 7 : else if (sUsage == "Green")
68 1 : eGDALUsage = GFU_Green;
69 6 : else if (sUsage == "Blue")
70 1 : eGDALUsage = GFU_Blue;
71 5 : else if (sUsage == "Alpha")
72 1 : eGDALUsage = GFU_Alpha;
73 : else
74 : {
75 : // don't recognise any other special names - generic column
76 4 : eGDALUsage = GFU_Generic;
77 : }
78 :
79 10 : const GDALRATFieldType eGDALType = GetTypeOfCol(iCol);
80 10 : poRAT->CreateColumn(sName.c_str(), eGDALType, eGDALUsage);
81 10 : poRAT->SetRowCount(static_cast<int>(m_poKEATable->getSize()));
82 :
83 10 : if (m_poKEATable->getSize() == 0)
84 0 : continue;
85 :
86 10 : switch (eGDALType)
87 : {
88 4 : case GFT_Integer:
89 : {
90 4 : int *panColData = (int *)VSI_MALLOC2_VERBOSE(
91 : sizeof(int), m_poKEATable->getSize());
92 4 : if (panColData == nullptr)
93 : {
94 0 : return nullptr;
95 : }
96 :
97 4 : if ((const_cast<KEARasterAttributeTable *>(this))
98 8 : ->ValuesIO(GF_Read, iCol, 0,
99 4 : static_cast<int>(m_poKEATable->getSize()),
100 4 : panColData) != CE_None)
101 : {
102 0 : CPLFree(panColData);
103 0 : return nullptr;
104 : }
105 :
106 8 : for (int iRow = 0; iRow < (int)m_poKEATable->getSize(); iRow++)
107 : {
108 4 : poRAT->SetValue(iRow, iCol, panColData[iRow]);
109 : }
110 4 : CPLFree(panColData);
111 4 : break;
112 : }
113 :
114 2 : case GFT_Real:
115 : {
116 2 : double *padfColData = (double *)VSI_MALLOC2_VERBOSE(
117 : sizeof(double), m_poKEATable->getSize());
118 2 : if (padfColData == nullptr)
119 : {
120 0 : return nullptr;
121 : }
122 2 : if ((const_cast<KEARasterAttributeTable *>(this))
123 4 : ->ValuesIO(GF_Read, iCol, 0,
124 2 : static_cast<int>(m_poKEATable->getSize()),
125 2 : padfColData) != CE_None)
126 : {
127 0 : CPLFree(padfColData);
128 0 : return nullptr;
129 : }
130 :
131 4 : for (int iRow = 0; iRow < (int)m_poKEATable->getSize(); iRow++)
132 : {
133 2 : poRAT->SetValue(iRow, iCol, padfColData[iRow]);
134 : }
135 2 : CPLFree(padfColData);
136 2 : break;
137 : }
138 :
139 3 : case GFT_String:
140 : {
141 3 : char **papszColData = (char **)VSI_MALLOC2_VERBOSE(
142 : sizeof(char *), m_poKEATable->getSize());
143 3 : if (papszColData == nullptr)
144 : {
145 0 : return nullptr;
146 : }
147 :
148 3 : if ((const_cast<KEARasterAttributeTable *>(this))
149 6 : ->ValuesIO(GF_Read, iCol, 0,
150 3 : static_cast<int>(m_poKEATable->getSize()),
151 3 : papszColData) != CE_None)
152 : {
153 0 : CPLFree(papszColData);
154 0 : return nullptr;
155 : }
156 :
157 6 : for (int iRow = 0; iRow < (int)m_poKEATable->getSize(); iRow++)
158 : {
159 3 : poRAT->SetValue(iRow, iCol, papszColData[iRow]);
160 3 : CPLFree(papszColData[iRow]);
161 : }
162 3 : CPLFree(papszColData);
163 3 : break;
164 : }
165 :
166 1 : case GFT_Boolean:
167 : {
168 1 : bool *pabColData = (bool *)VSI_MALLOC2_VERBOSE(
169 : sizeof(bool), m_poKEATable->getSize());
170 1 : if (pabColData == nullptr)
171 : {
172 0 : return nullptr;
173 : }
174 :
175 1 : if ((const_cast<KEARasterAttributeTable *>(this))
176 2 : ->ValuesIO(GF_Read, iCol, 0,
177 1 : static_cast<int>(m_poKEATable->getSize()),
178 1 : pabColData) != CE_None)
179 : {
180 0 : CPLFree(pabColData);
181 0 : return nullptr;
182 : }
183 :
184 2 : for (int iRow = 0; iRow < (int)m_poKEATable->getSize(); iRow++)
185 : {
186 1 : poRAT->SetValue(iRow, iCol, pabColData[iRow]);
187 : }
188 1 : CPLFree(pabColData);
189 1 : break;
190 : }
191 :
192 0 : case GFT_DateTime:
193 : case GFT_WKBGeometry:
194 0 : CPLAssert(false);
195 : break;
196 : }
197 : }
198 :
199 1 : poRAT->SetTableType(this->GetTableType());
200 :
201 1 : return poRAT.release();
202 : }
203 :
204 112 : int KEARasterAttributeTable::GetColumnCount() const
205 : {
206 112 : return (int)m_aoFields.size();
207 : }
208 :
209 97 : const char *KEARasterAttributeTable::GetNameOfCol(int nCol) const
210 : {
211 97 : if ((nCol < 0) || (nCol >= (int)m_aoFields.size()))
212 2 : return nullptr;
213 :
214 95 : return m_aoFields[nCol].name.c_str();
215 : }
216 :
217 49 : GDALRATFieldUsage KEARasterAttributeTable::GetUsageOfCol(int nCol) const
218 : {
219 49 : if ((nCol < 0) || (nCol >= (int)m_aoFields.size()))
220 2 : return GFU_Generic;
221 :
222 : GDALRATFieldUsage eGDALUsage;
223 47 : std::string keausage = m_aoFields[nCol].usage;
224 :
225 47 : if (keausage == "PixelCount")
226 3 : eGDALUsage = GFU_PixelCount;
227 44 : else if (keausage == "Name")
228 3 : eGDALUsage = GFU_Name;
229 41 : else if (keausage == "Red")
230 8 : eGDALUsage = GFU_Red;
231 33 : else if (keausage == "Green")
232 8 : eGDALUsage = GFU_Green;
233 25 : else if (keausage == "Blue")
234 8 : eGDALUsage = GFU_Blue;
235 17 : else if (keausage == "Alpha")
236 8 : eGDALUsage = GFU_Alpha;
237 : else
238 : {
239 : // don't recognise any other special names - generic column
240 9 : eGDALUsage = GFU_Generic;
241 : }
242 :
243 47 : return eGDALUsage;
244 : }
245 :
246 73 : GDALRATFieldType KEARasterAttributeTable::GetTypeOfCol(int nCol) const
247 : {
248 73 : if ((nCol < 0) || (nCol >= (int)m_aoFields.size()))
249 2 : return GFT_Integer;
250 :
251 : GDALRATFieldType eGDALType;
252 71 : switch (m_aoFields[nCol].dataType)
253 : {
254 5 : case kealib::kea_att_bool:
255 5 : eGDALType = GFT_Boolean;
256 5 : break;
257 40 : case kealib::kea_att_int:
258 40 : eGDALType = GFT_Integer;
259 40 : break;
260 11 : case kealib::kea_att_float:
261 11 : eGDALType = GFT_Real;
262 11 : break;
263 15 : case kealib::kea_att_string:
264 15 : eGDALType = GFT_String;
265 15 : break;
266 0 : default:
267 0 : eGDALType = GFT_Integer;
268 0 : break;
269 : }
270 71 : return eGDALType;
271 : }
272 :
273 7 : int KEARasterAttributeTable::GetColOfUsage(GDALRATFieldUsage eUsage) const
274 : {
275 : unsigned int i;
276 :
277 14 : std::string keausage;
278 7 : switch (eUsage)
279 : {
280 1 : case GFU_PixelCount:
281 1 : keausage = "PixelCount";
282 1 : break;
283 1 : case GFU_Name:
284 1 : keausage = "Name";
285 1 : break;
286 1 : case GFU_Red:
287 1 : keausage = "Red";
288 1 : break;
289 1 : case GFU_Green:
290 1 : keausage = "Green";
291 1 : break;
292 1 : case GFU_Blue:
293 1 : keausage = "Blue";
294 1 : break;
295 1 : case GFU_Alpha:
296 1 : keausage = "Alpha";
297 1 : break;
298 1 : default:
299 1 : keausage = "Generic";
300 1 : break;
301 : }
302 :
303 28 : for (i = 0; i < m_aoFields.size(); i++)
304 : {
305 28 : if (m_aoFields[i].usage == keausage)
306 7 : return i;
307 : }
308 0 : return -1;
309 : }
310 :
311 484 : int KEARasterAttributeTable::GetRowCount() const
312 : {
313 484 : return (int)m_poKEATable->getSize();
314 : }
315 :
316 7 : const char *KEARasterAttributeTable::GetValueAsString(int iRow,
317 : int iField) const
318 : {
319 : /// Let ValuesIO do the work.
320 : char *apszStrList[1];
321 7 : if ((const_cast<KEARasterAttributeTable *>(this))
322 7 : ->ValuesIO(GF_Read, iField, iRow, 1, apszStrList) != CE_None)
323 : {
324 3 : return "";
325 : }
326 :
327 : const_cast<KEARasterAttributeTable *>(this)->osWorkingResult =
328 4 : apszStrList[0];
329 4 : CPLFree(apszStrList[0]);
330 :
331 4 : return osWorkingResult;
332 : }
333 :
334 31 : int KEARasterAttributeTable::GetValueAsInt(int iRow, int iField) const
335 : {
336 : // Let ValuesIO do the work.
337 31 : int nValue = 0;
338 31 : if ((const_cast<KEARasterAttributeTable *>(this))
339 31 : ->ValuesIO(GF_Read, iField, iRow, 1, &nValue) != CE_None)
340 : {
341 3 : return 0;
342 : }
343 :
344 28 : return nValue;
345 : }
346 :
347 7 : double KEARasterAttributeTable::GetValueAsDouble(int iRow, int iField) const
348 : {
349 : // Let ValuesIO do the work.
350 7 : double dfValue = 0.0;
351 7 : if ((const_cast<KEARasterAttributeTable *>(this))
352 7 : ->ValuesIO(GF_Read, iField, iRow, 1, &dfValue) != CE_None)
353 : {
354 3 : return 0;
355 : }
356 :
357 4 : return dfValue;
358 : }
359 :
360 5 : bool KEARasterAttributeTable::GetValueAsBoolean(int iRow, int iField) const
361 : {
362 : // Let ValuesIO do the work.
363 5 : bool bValue = false;
364 5 : if ((const_cast<KEARasterAttributeTable *>(this))
365 5 : ->ValuesIO(GF_Read, iField, iRow, 1, &bValue) != CE_None)
366 : {
367 3 : return false;
368 : }
369 :
370 2 : return bValue;
371 : }
372 :
373 5 : GDALRATDateTime KEARasterAttributeTable::GetValueAsDateTime(int iRow,
374 : int iField) const
375 : {
376 : // Let ValuesIO do the work.
377 5 : GDALRATDateTime value;
378 5 : const_cast<KEARasterAttributeTable *>(this)->ValuesIO(GF_Read, iField, iRow,
379 : 1, &value);
380 5 : return value;
381 : }
382 :
383 : const GByte *
384 5 : KEARasterAttributeTable::GetValueAsWKBGeometry(int iRow, int iField,
385 : size_t &nWKBSize) const
386 : {
387 : // Let ValuesIO do the work.
388 5 : GByte *pabyWKB = nullptr;
389 5 : const_cast<KEARasterAttributeTable *>(this)->ValuesIO(
390 : GF_Read, iField, iRow, 1, &pabyWKB, &nWKBSize);
391 5 : if (nWKBSize)
392 1 : m_abyCachedWKB.assign(pabyWKB, pabyWKB + nWKBSize);
393 5 : VSIFree(pabyWKB);
394 5 : return nWKBSize ? m_abyCachedWKB.data() : nullptr;
395 : }
396 :
397 6 : CPLErr KEARasterAttributeTable::SetValue(int iRow, int iField,
398 : const char *pszValue)
399 : {
400 : // Let ValuesIO do the work.
401 6 : char *apszValues[1] = {const_cast<char *>(pszValue)};
402 12 : return ValuesIO(GF_Write, iField, iRow, 1, apszValues);
403 : }
404 :
405 6 : CPLErr KEARasterAttributeTable::SetValue(int iRow, int iField, double dfValue)
406 : {
407 : // Let ValuesIO do the work.
408 6 : return ValuesIO(GF_Write, iField, iRow, 1, &dfValue);
409 : }
410 :
411 42 : CPLErr KEARasterAttributeTable::SetValue(int iRow, int iField, int nValue)
412 : {
413 : // Let ValuesIO do the work.
414 42 : return ValuesIO(GF_Write, iField, iRow, 1, &nValue);
415 : }
416 :
417 4 : CPLErr KEARasterAttributeTable::SetValue(int iRow, int iField, bool bValue)
418 : {
419 : // Let ValuesIO do the work.
420 4 : return ValuesIO(GF_Write, iField, iRow, 1, &bValue);
421 : }
422 :
423 4 : CPLErr KEARasterAttributeTable::SetValue(int iRow, int iField,
424 : const GDALRATDateTime &sDateTime)
425 : {
426 : // Let ValuesIO do the work.
427 4 : return ValuesIO(GF_Write, iField, iRow, 1,
428 4 : const_cast<GDALRATDateTime *>(&sDateTime));
429 : }
430 :
431 4 : CPLErr KEARasterAttributeTable::SetValue(int iRow, int iField,
432 : const void *pabyWKB, size_t nWKBSize)
433 : {
434 : // Let ValuesIO do the work.
435 4 : const GByte **ppabyWKB = reinterpret_cast<const GByte **>(&pabyWKB);
436 4 : return ValuesIO(GF_Write, iField, iRow, 1, const_cast<GByte **>(ppabyWKB),
437 4 : &nWKBSize);
438 : }
439 :
440 33 : CPLErr KEARasterAttributeTable::ValuesIO(GDALRWFlag eRWFlag, int iField,
441 : int iStartRow, int iLength,
442 : double *pdfData)
443 : {
444 : /*if( ( eRWFlag == GF_Write ) && ( this->eAccess == GA_ReadOnly ) )
445 : {
446 : CPLError( CE_Failure, CPLE_NoWriteAccess,
447 : "Dataset not open in update mode");
448 : return CE_Failure;
449 : }*/
450 66 : CPLMutexHolderD(&m_hMutex);
451 :
452 33 : if (iField < 0 || iField >= (int)m_aoFields.size())
453 : {
454 1 : CPLError(CE_Failure, CPLE_AppDefined, "iField (%d) out of range.",
455 : iField);
456 :
457 1 : return CE_Failure;
458 : }
459 :
460 32 : if (iStartRow < 0 || (iStartRow + iLength) > (int)m_poKEATable->getSize())
461 : {
462 2 : CPLError(CE_Failure, CPLE_AppDefined,
463 : "iStartRow (%d) + iLength(%d) out of range.", iStartRow,
464 : iLength);
465 :
466 2 : return CE_Failure;
467 : }
468 :
469 30 : switch (m_aoFields[iField].dataType)
470 : {
471 3 : case kealib::kea_att_bool:
472 : case kealib::kea_att_int:
473 : {
474 : // allocate space for ints
475 3 : int *panColData = (int *)VSI_MALLOC2_VERBOSE(iLength, sizeof(int));
476 3 : if (panColData == nullptr)
477 : {
478 0 : return CE_Failure;
479 : }
480 :
481 3 : if (eRWFlag == GF_Write)
482 : {
483 : // copy the application supplied doubles to ints
484 4 : for (int i = 0; i < iLength; i++)
485 2 : panColData[i] = static_cast<int>(pdfData[i]);
486 : }
487 :
488 : // do the ValuesIO as ints
489 : CPLErr eVal =
490 3 : ValuesIO(eRWFlag, iField, iStartRow, iLength, panColData);
491 3 : if (eVal != CE_None)
492 : {
493 0 : CPLFree(panColData);
494 0 : return eVal;
495 : }
496 :
497 3 : if (eRWFlag == GF_Read)
498 : {
499 : // copy them back to doubles
500 2 : for (int i = 0; i < iLength; i++)
501 1 : pdfData[i] = panColData[i];
502 : }
503 :
504 3 : CPLFree(panColData);
505 : }
506 3 : break;
507 23 : case kealib::kea_att_float:
508 : {
509 : try
510 : {
511 23 : if (eRWFlag == GF_Read)
512 15 : m_poKEATable->getFloatFields(
513 15 : iStartRow, iLength, m_aoFields[iField].idx, pdfData);
514 : else
515 8 : m_poKEATable->setFloatFields(
516 8 : iStartRow, iLength, m_aoFields[iField].idx, pdfData);
517 : }
518 0 : catch (kealib::KEAException &e)
519 : {
520 0 : CPLError(CE_Failure, CPLE_AppDefined,
521 0 : "Failed to read/write attribute table: %s", e.what());
522 0 : return CE_Failure;
523 : }
524 : }
525 23 : break;
526 4 : case kealib::kea_att_string:
527 : {
528 : // allocate space for string pointers
529 : char **papszColData =
530 4 : (char **)VSI_MALLOC2_VERBOSE(iLength, sizeof(char *));
531 4 : if (papszColData == nullptr)
532 : {
533 0 : return CE_Failure;
534 : }
535 :
536 4 : if (eRWFlag == GF_Write)
537 : {
538 : // copy the application supplied doubles to strings
539 6 : for (int i = 0; i < iLength; i++)
540 : {
541 3 : osWorkingResult.Printf("%.16g", pdfData[i]);
542 3 : papszColData[i] = CPLStrdup(osWorkingResult);
543 : }
544 : }
545 :
546 : // do the ValuesIO as strings
547 : CPLErr eVal =
548 4 : ValuesIO(eRWFlag, iField, iStartRow, iLength, papszColData);
549 4 : if (eVal != CE_None)
550 : {
551 0 : if (eRWFlag == GF_Write)
552 : {
553 0 : for (int i = 0; i < iLength; i++)
554 0 : CPLFree(papszColData[i]);
555 : }
556 0 : CPLFree(papszColData);
557 0 : return eVal;
558 : }
559 :
560 4 : if (eRWFlag == GF_Read)
561 : {
562 : // copy them back to doubles
563 2 : for (int i = 0; i < iLength; i++)
564 1 : pdfData[i] = CPLAtof(papszColData[i]);
565 : }
566 :
567 : // either we allocated them for write, or they were allocated
568 : // by ValuesIO on read
569 8 : for (int i = 0; i < iLength; i++)
570 4 : CPLFree(papszColData[i]);
571 :
572 4 : CPLFree(papszColData);
573 : }
574 4 : break;
575 0 : default:
576 0 : break;
577 : }
578 30 : return CE_None;
579 : }
580 :
581 16 : CPLErr KEARasterAttributeTable::ValuesIO(GDALRWFlag eRWFlag, int iField,
582 : int iStartRow, int iLength,
583 : bool *pbData)
584 : {
585 : /*if( ( eRWFlag == GF_Write ) && ( this->eAccess == GA_ReadOnly ) )
586 : {
587 : CPLError( CE_Failure, CPLE_NoWriteAccess,
588 : "Dataset not open in update mode");
589 : return CE_Failure;
590 : }*/
591 32 : CPLMutexHolderD(&m_hMutex);
592 :
593 16 : if (iField < 0 || iField >= (int)m_aoFields.size())
594 : {
595 1 : CPLError(CE_Failure, CPLE_AppDefined, "iField (%d) out of range.",
596 : iField);
597 :
598 1 : return CE_Failure;
599 : }
600 :
601 15 : if (iStartRow < 0 || (iStartRow + iLength) > (int)m_poKEATable->getSize())
602 : {
603 2 : CPLError(CE_Failure, CPLE_AppDefined,
604 : "iStartRow (%d) + iLength(%d) out of range.", iStartRow,
605 : iLength);
606 :
607 2 : return CE_Failure;
608 : }
609 :
610 13 : switch (m_aoFields[iField].dataType)
611 : {
612 9 : case kealib::kea_att_bool:
613 : {
614 : try
615 : {
616 9 : if (eRWFlag == GF_Read)
617 4 : m_poKEATable->getBoolFields(iStartRow, iLength,
618 4 : m_aoFields[iField].idx, pbData);
619 : else
620 5 : m_poKEATable->setBoolFields(iStartRow, iLength,
621 5 : m_aoFields[iField].idx, pbData);
622 : }
623 0 : catch (kealib::KEAException &e)
624 : {
625 0 : CPLError(CE_Failure, CPLE_AppDefined,
626 0 : "Failed to read/write attribute table: %s", e.what());
627 0 : return CE_Failure;
628 : }
629 : }
630 9 : break;
631 0 : case kealib::kea_att_int:
632 : {
633 : // need to convert to/from int64_t
634 : int64_t *panColData =
635 0 : (int64_t *)VSI_MALLOC2_VERBOSE(iLength, sizeof(int64_t));
636 0 : if (panColData == nullptr)
637 : {
638 0 : return CE_Failure;
639 : }
640 :
641 0 : if (eRWFlag == GF_Write)
642 : {
643 : // copy the application supplied bools to int64t
644 0 : for (int i = 0; i < iLength; i++)
645 0 : panColData[i] = pbData[i];
646 : }
647 :
648 : try
649 : {
650 0 : if (eRWFlag == GF_Read)
651 0 : m_poKEATable->getIntFields(
652 0 : iStartRow, iLength, m_aoFields[iField].idx, panColData);
653 : else
654 0 : m_poKEATable->setIntFields(
655 0 : iStartRow, iLength, m_aoFields[iField].idx, panColData);
656 : }
657 0 : catch (kealib::KEAException &e)
658 : {
659 : // fprintf(stderr,"Failed to read/write attribute table: %s %d
660 : // %d %ld\n", e.what(), iStartRow, iLength,
661 : // m_poKEATable->getSize() );
662 0 : CPLError(CE_Failure, CPLE_AppDefined,
663 0 : "Failed to read/write attribute table: %s", e.what());
664 0 : return CE_Failure;
665 : }
666 :
667 0 : if (eRWFlag == GF_Read)
668 : {
669 : // copy them back to bools
670 0 : for (int i = 0; i < iLength; i++)
671 0 : pbData[i] = panColData[i] != 0;
672 : }
673 0 : CPLFree(panColData);
674 : }
675 0 : break;
676 2 : case kealib::kea_att_float:
677 : {
678 : // allocate space for doubles
679 : double *padfColData =
680 2 : (double *)VSI_MALLOC2_VERBOSE(iLength, sizeof(double));
681 2 : if (padfColData == nullptr)
682 : {
683 0 : return CE_Failure;
684 : }
685 :
686 2 : if (eRWFlag == GF_Write)
687 : {
688 : // copy the application supplied ints to doubles
689 2 : for (int i = 0; i < iLength; i++)
690 1 : padfColData[i] = pbData[i];
691 : }
692 :
693 : // do the ValuesIO as doubles
694 : CPLErr eVal =
695 2 : ValuesIO(eRWFlag, iField, iStartRow, iLength, padfColData);
696 2 : if (eVal != CE_None)
697 : {
698 0 : CPLFree(padfColData);
699 0 : return eVal;
700 : }
701 :
702 2 : if (eRWFlag == GF_Read)
703 : {
704 : // copy them back to ints
705 2 : for (int i = 0; i < iLength; i++)
706 1 : pbData[i] = padfColData[i] != 0;
707 : }
708 :
709 2 : CPLFree(padfColData);
710 : }
711 2 : break;
712 2 : case kealib::kea_att_string:
713 : {
714 : // allocate space for string pointers
715 : char **papszColData =
716 2 : (char **)VSI_MALLOC2_VERBOSE(iLength, sizeof(char *));
717 2 : if (papszColData == nullptr)
718 : {
719 0 : return CE_Failure;
720 : }
721 :
722 2 : if (eRWFlag == GF_Write)
723 : {
724 : // copy the application supplied ints to strings
725 4 : for (int i = 0; i < iLength; i++)
726 : {
727 2 : papszColData[i] = CPLStrdup(pbData[i] ? "true" : "false");
728 : }
729 : }
730 :
731 : // do the ValuesIO as strings
732 : CPLErr eVal =
733 2 : ValuesIO(eRWFlag, iField, iStartRow, iLength, papszColData);
734 2 : if (eVal != CE_None)
735 : {
736 0 : if (eRWFlag == GF_Write)
737 : {
738 0 : for (int i = 0; i < iLength; i++)
739 0 : CPLFree(papszColData[i]);
740 : }
741 0 : CPLFree(papszColData);
742 0 : return eVal;
743 : }
744 :
745 2 : if (eRWFlag == GF_Read)
746 : {
747 : // copy them back to ints
748 0 : for (int i = 0; i < iLength; i++)
749 0 : pbData[i] = CPLTestBool(papszColData[i]);
750 : }
751 :
752 : // either we allocated them for write, or they were allocated
753 : // by ValuesIO on read
754 4 : for (int i = 0; i < iLength; i++)
755 2 : CPLFree(papszColData[i]);
756 :
757 2 : CPLFree(papszColData);
758 : }
759 2 : break;
760 0 : default:
761 0 : break;
762 : }
763 13 : return CE_None;
764 : }
765 :
766 102 : CPLErr KEARasterAttributeTable::ValuesIO(GDALRWFlag eRWFlag, int iField,
767 : int iStartRow, int iLength,
768 : int *pnData)
769 : {
770 : /*if( ( eRWFlag == GF_Write ) && ( this->eAccess == GA_ReadOnly ) )
771 : {
772 : CPLError( CE_Failure, CPLE_NoWriteAccess,
773 : "Dataset not open in update mode");
774 : return CE_Failure;
775 : }*/
776 204 : CPLMutexHolderD(&m_hMutex);
777 :
778 102 : if (iField < 0 || iField >= (int)m_aoFields.size())
779 : {
780 1 : CPLError(CE_Failure, CPLE_AppDefined, "iField (%d) out of range.",
781 : iField);
782 :
783 1 : return CE_Failure;
784 : }
785 :
786 101 : if (iStartRow < 0 || (iStartRow + iLength) > (int)m_poKEATable->getSize())
787 : {
788 2 : CPLError(CE_Failure, CPLE_AppDefined,
789 : "iStartRow (%d) + iLength(%d) out of range.", iStartRow,
790 : iLength);
791 :
792 2 : return CE_Failure;
793 : }
794 :
795 99 : switch (m_aoFields[iField].dataType)
796 : {
797 2 : case kealib::kea_att_bool:
798 : {
799 : // need to convert to/from bools
800 : bool *panColData =
801 2 : (bool *)VSI_MALLOC2_VERBOSE(iLength, sizeof(bool));
802 2 : if (panColData == nullptr)
803 : {
804 0 : return CE_Failure;
805 : }
806 :
807 2 : if (eRWFlag == GF_Write)
808 : {
809 : // copy the application supplied ints to bools
810 4 : for (int i = 0; i < iLength; i++)
811 : {
812 2 : panColData[i] = (pnData[i] != 0);
813 : }
814 : }
815 :
816 : try
817 : {
818 2 : if (eRWFlag == GF_Read)
819 0 : m_poKEATable->getBoolFields(
820 0 : iStartRow, iLength, m_aoFields[iField].idx, panColData);
821 : else
822 2 : m_poKEATable->setBoolFields(
823 2 : iStartRow, iLength, m_aoFields[iField].idx, panColData);
824 : }
825 0 : catch (kealib::KEAException &e)
826 : {
827 0 : CPLError(CE_Failure, CPLE_AppDefined,
828 0 : "Failed to read/write attribute table: %s", e.what());
829 0 : return CE_Failure;
830 : }
831 :
832 2 : if (eRWFlag == GF_Read)
833 : {
834 : // copy them back to ints
835 0 : for (int i = 0; i < iLength; i++)
836 0 : pnData[i] = panColData[i] ? 1 : 0;
837 : }
838 2 : CPLFree(panColData);
839 : }
840 2 : break;
841 90 : case kealib::kea_att_int:
842 : {
843 : // need to convert to/from int64_t
844 : int64_t *panColData =
845 90 : (int64_t *)VSI_MALLOC2_VERBOSE(iLength, sizeof(int64_t));
846 90 : if (panColData == nullptr)
847 : {
848 0 : return CE_Failure;
849 : }
850 :
851 90 : if (eRWFlag == GF_Write)
852 : {
853 : // copy the application supplied ints to int64t
854 86 : for (int i = 0; i < iLength; i++)
855 43 : panColData[i] = pnData[i];
856 : }
857 :
858 : try
859 : {
860 90 : if (eRWFlag == GF_Read)
861 47 : m_poKEATable->getIntFields(
862 47 : iStartRow, iLength, m_aoFields[iField].idx, panColData);
863 : else
864 43 : m_poKEATable->setIntFields(
865 43 : iStartRow, iLength, m_aoFields[iField].idx, panColData);
866 : }
867 0 : catch (kealib::KEAException &e)
868 : {
869 : // fprintf(stderr,"Failed to read/write attribute table: %s %d
870 : // %d %ld\n", e.what(), iStartRow, iLength,
871 : // m_poKEATable->getSize() );
872 0 : CPLError(CE_Failure, CPLE_AppDefined,
873 0 : "Failed to read/write attribute table: %s", e.what());
874 0 : return CE_Failure;
875 : }
876 :
877 90 : if (eRWFlag == GF_Read)
878 : {
879 : // copy them back to ints
880 110 : for (int i = 0; i < iLength; i++)
881 63 : pnData[i] = static_cast<int>(panColData[i]);
882 : }
883 90 : CPLFree(panColData);
884 : }
885 90 : break;
886 3 : case kealib::kea_att_float:
887 : {
888 : // allocate space for doubles
889 : double *padfColData =
890 3 : (double *)VSI_MALLOC2_VERBOSE(iLength, sizeof(double));
891 3 : if (padfColData == nullptr)
892 : {
893 0 : return CE_Failure;
894 : }
895 :
896 3 : if (eRWFlag == GF_Write)
897 : {
898 : // copy the application supplied ints to doubles
899 2 : for (int i = 0; i < iLength; i++)
900 1 : padfColData[i] = pnData[i];
901 : }
902 :
903 : // do the ValuesIO as doubles
904 : CPLErr eVal =
905 3 : ValuesIO(eRWFlag, iField, iStartRow, iLength, padfColData);
906 3 : if (eVal != CE_None)
907 : {
908 0 : CPLFree(padfColData);
909 0 : return eVal;
910 : }
911 :
912 3 : if (eRWFlag == GF_Read)
913 : {
914 : // copy them back to ints
915 4 : for (int i = 0; i < iLength; i++)
916 2 : pnData[i] = static_cast<int>(padfColData[i]);
917 : }
918 :
919 3 : CPLFree(padfColData);
920 : }
921 3 : break;
922 4 : case kealib::kea_att_string:
923 : {
924 : // allocate space for string pointers
925 : char **papszColData =
926 4 : (char **)VSI_MALLOC2_VERBOSE(iLength, sizeof(char *));
927 4 : if (papszColData == nullptr)
928 : {
929 0 : return CE_Failure;
930 : }
931 :
932 4 : if (eRWFlag == GF_Write)
933 : {
934 : // copy the application supplied ints to strings
935 6 : for (int i = 0; i < iLength; i++)
936 : {
937 3 : osWorkingResult.Printf("%d", pnData[i]);
938 3 : papszColData[i] = CPLStrdup(osWorkingResult);
939 : }
940 : }
941 :
942 : // do the ValuesIO as strings
943 : CPLErr eVal =
944 4 : ValuesIO(eRWFlag, iField, iStartRow, iLength, papszColData);
945 4 : if (eVal != CE_None)
946 : {
947 0 : if (eRWFlag == GF_Write)
948 : {
949 0 : for (int i = 0; i < iLength; i++)
950 0 : CPLFree(papszColData[i]);
951 : }
952 0 : CPLFree(papszColData);
953 0 : return eVal;
954 : }
955 :
956 4 : if (eRWFlag == GF_Read)
957 : {
958 : // copy them back to ints
959 2 : for (int i = 0; i < iLength; i++)
960 1 : pnData[i] = atoi(papszColData[i]);
961 : }
962 :
963 : // either we allocated them for write, or they were allocated
964 : // by ValuesIO on read
965 8 : for (int i = 0; i < iLength; i++)
966 4 : CPLFree(papszColData[i]);
967 :
968 4 : CPLFree(papszColData);
969 : }
970 4 : break;
971 0 : default:
972 0 : break;
973 : }
974 99 : return CE_None;
975 : }
976 :
977 53 : CPLErr KEARasterAttributeTable::ValuesIO(GDALRWFlag eRWFlag, int iField,
978 : int iStartRow, int iLength,
979 : char **papszStrList)
980 : {
981 : /*if( ( eRWFlag == GF_Write ) && ( this->eAccess == GA_ReadOnly ) )
982 : {
983 : CPLError( CE_Failure, CPLE_NoWriteAccess,
984 : "Dataset not open in update mode");
985 : return CE_Failure;
986 : }*/
987 106 : CPLMutexHolderD(&m_hMutex);
988 :
989 53 : if (iField < 0 || iField >= (int)m_aoFields.size())
990 : {
991 3 : CPLError(CE_Failure, CPLE_AppDefined, "iField (%d) out of range.",
992 : iField);
993 :
994 3 : return CE_Failure;
995 : }
996 :
997 50 : if (iStartRow < 0 || (iStartRow + iLength) > (int)m_poKEATable->getSize())
998 : {
999 6 : CPLError(CE_Failure, CPLE_AppDefined,
1000 : "iStartRow (%d) + iLength(%d) out of range.", iStartRow,
1001 : iLength);
1002 :
1003 6 : return CE_Failure;
1004 : }
1005 :
1006 44 : switch (m_aoFields[iField].dataType)
1007 : {
1008 3 : case kealib::kea_att_bool:
1009 : {
1010 : // allocate space for bools
1011 : bool *pabColData =
1012 3 : (bool *)VSI_MALLOC2_VERBOSE(iLength, sizeof(bool));
1013 3 : if (pabColData == nullptr)
1014 : {
1015 0 : return CE_Failure;
1016 : }
1017 3 : if (eRWFlag == GF_Write)
1018 : {
1019 : // convert user supplied strings to ints
1020 6 : for (int i = 0; i < iLength; i++)
1021 3 : pabColData[i] = CPLTestBool(papszStrList[i]);
1022 : }
1023 :
1024 : // call values IO to read/write ints
1025 : CPLErr eVal =
1026 3 : ValuesIO(eRWFlag, iField, iStartRow, iLength, pabColData);
1027 3 : if (eVal != CE_None)
1028 : {
1029 0 : CPLFree(pabColData);
1030 0 : return eVal;
1031 : }
1032 :
1033 3 : if (eRWFlag == GF_Read)
1034 : {
1035 : // convert ints back to strings
1036 0 : for (int i = 0; i < iLength; i++)
1037 : {
1038 0 : papszStrList[i] =
1039 0 : CPLStrdup(pabColData[i] ? "true" : "false");
1040 : }
1041 : }
1042 3 : CPLFree(pabColData);
1043 : }
1044 3 : break;
1045 2 : case kealib::kea_att_int:
1046 : {
1047 : // allocate space for ints
1048 2 : int *panColData = (int *)VSI_MALLOC2_VERBOSE(iLength, sizeof(int));
1049 2 : if (panColData == nullptr)
1050 : {
1051 0 : return CE_Failure;
1052 : }
1053 2 : if (eRWFlag == GF_Write)
1054 : {
1055 : // convert user supplied strings to ints
1056 2 : for (int i = 0; i < iLength; i++)
1057 1 : panColData[i] = atoi(papszStrList[i]);
1058 : }
1059 :
1060 : // call values IO to read/write ints
1061 : CPLErr eVal =
1062 2 : ValuesIO(eRWFlag, iField, iStartRow, iLength, panColData);
1063 2 : if (eVal != CE_None)
1064 : {
1065 0 : CPLFree(panColData);
1066 0 : return eVal;
1067 : }
1068 :
1069 2 : if (eRWFlag == GF_Read)
1070 : {
1071 : // convert ints back to strings
1072 2 : for (int i = 0; i < iLength; i++)
1073 : {
1074 1 : osWorkingResult.Printf("%d", panColData[i]);
1075 1 : papszStrList[i] = CPLStrdup(osWorkingResult);
1076 : }
1077 : }
1078 2 : CPLFree(panColData);
1079 : }
1080 2 : break;
1081 7 : case kealib::kea_att_float:
1082 : {
1083 : // allocate space for doubles
1084 : double *padfColData =
1085 7 : (double *)VSI_MALLOC2_VERBOSE(iLength, sizeof(double));
1086 7 : if (padfColData == nullptr)
1087 : {
1088 0 : return CE_Failure;
1089 : }
1090 :
1091 7 : if (eRWFlag == GF_Write)
1092 : {
1093 : // convert user supplied strings to doubles
1094 6 : for (int i = 0; i < iLength; i++)
1095 3 : padfColData[i] = CPLAtof(papszStrList[i]);
1096 : }
1097 :
1098 : // call value IO to read/write doubles
1099 : CPLErr eVal =
1100 7 : ValuesIO(eRWFlag, iField, iStartRow, iLength, padfColData);
1101 7 : if (eVal != CE_None)
1102 : {
1103 0 : CPLFree(padfColData);
1104 0 : return eVal;
1105 : }
1106 :
1107 7 : if (eRWFlag == GF_Read)
1108 : {
1109 : // convert doubles back to strings
1110 8 : for (int i = 0; i < iLength; i++)
1111 : {
1112 4 : osWorkingResult.Printf("%.16g", padfColData[i]);
1113 4 : papszStrList[i] = CPLStrdup(osWorkingResult);
1114 : }
1115 : }
1116 7 : CPLFree(padfColData);
1117 : }
1118 7 : break;
1119 32 : case kealib::kea_att_string:
1120 : {
1121 : try
1122 : {
1123 32 : if (eRWFlag == GF_Read)
1124 : {
1125 28 : std::vector<std::string> aStrings;
1126 14 : m_poKEATable->getStringFields(
1127 14 : iStartRow, iLength, m_aoFields[iField].idx, &aStrings);
1128 14 : for (std::vector<std::string>::size_type i = 0;
1129 28 : i < aStrings.size(); i++)
1130 : {
1131 : // Copy using CPLStrdup so user can call CPLFree
1132 14 : papszStrList[i] = CPLStrdup(aStrings[i].c_str());
1133 : }
1134 : }
1135 : else
1136 : {
1137 : // need to convert to a vector first
1138 36 : std::vector<std::string> aStrings;
1139 36 : for (int i = 0; i < iLength; i++)
1140 : {
1141 18 : aStrings.push_back(papszStrList[i]);
1142 : }
1143 18 : m_poKEATable->setStringFields(
1144 18 : iStartRow, iLength, m_aoFields[iField].idx, &aStrings);
1145 : }
1146 : }
1147 0 : catch (kealib::KEAException &e)
1148 : {
1149 0 : CPLError(CE_Failure, CPLE_AppDefined,
1150 0 : "Failed to read/write attribute table: %s", e.what());
1151 0 : return CE_Failure;
1152 : }
1153 : }
1154 32 : break;
1155 0 : default:
1156 0 : break;
1157 : }
1158 44 : return CE_None;
1159 : }
1160 :
1161 9 : CPLErr KEARasterAttributeTable::ValuesIO(GDALRWFlag eRWFlag, int iField,
1162 : int iStartRow, int iLength,
1163 : GDALRATDateTime *psDateTime)
1164 : {
1165 9 : return ValuesIODateTimeFromIntoString(eRWFlag, iField, iStartRow, iLength,
1166 9 : psDateTime);
1167 : }
1168 :
1169 9 : CPLErr KEARasterAttributeTable::ValuesIO(GDALRWFlag eRWFlag, int iField,
1170 : int iStartRow, int iLength,
1171 : GByte **ppabyWKB, size_t *pnWKBSize)
1172 : {
1173 9 : return ValuesIOWKBGeometryFromIntoString(eRWFlag, iField, iStartRow,
1174 9 : iLength, ppabyWKB, pnWKBSize);
1175 : }
1176 :
1177 0 : int KEARasterAttributeTable::ChangesAreWrittenToFile()
1178 : {
1179 0 : return TRUE;
1180 : }
1181 :
1182 5 : void KEARasterAttributeTable::SetRowCount(int iCount)
1183 : {
1184 : /*if( this->eAccess == GA_ReadOnly )
1185 : {
1186 : CPLError( CE_Failure, CPLE_NoWriteAccess,
1187 : "Dataset not open in update mode");
1188 : return;
1189 : }*/
1190 :
1191 5 : if (iCount > (int)m_poKEATable->getSize())
1192 : {
1193 3 : m_poKEATable->addRows(iCount - m_poKEATable->getSize());
1194 : }
1195 : // can't shrink
1196 5 : }
1197 :
1198 18 : CPLErr KEARasterAttributeTable::CreateColumn(const char *pszFieldName,
1199 : GDALRATFieldType eFieldType,
1200 : GDALRATFieldUsage eFieldUsage)
1201 : {
1202 : /*if( this->eAccess == GA_ReadOnly )
1203 : {
1204 : CPLError( CE_Failure, CPLE_NoWriteAccess,
1205 : "Dataset not open in update mode");
1206 : return CE_Failure;
1207 : }*/
1208 36 : CPLMutexHolderD(&m_hMutex);
1209 :
1210 18 : const char *strUsage = "Generic";
1211 18 : switch (eFieldUsage)
1212 : {
1213 1 : case GFU_PixelCount:
1214 1 : strUsage = "PixelCount";
1215 1 : eFieldType = GFT_Real;
1216 1 : break;
1217 1 : case GFU_Name:
1218 1 : strUsage = "Name";
1219 1 : eFieldType = GFT_String;
1220 1 : break;
1221 3 : case GFU_Red:
1222 3 : strUsage = "Red";
1223 3 : eFieldType = GFT_Integer;
1224 3 : break;
1225 3 : case GFU_Green:
1226 3 : strUsage = "Green";
1227 3 : eFieldType = GFT_Integer;
1228 3 : break;
1229 3 : case GFU_Blue:
1230 3 : strUsage = "Blue";
1231 3 : eFieldType = GFT_Integer;
1232 3 : break;
1233 3 : case GFU_Alpha:
1234 3 : strUsage = "Alpha";
1235 3 : eFieldType = GFT_Integer;
1236 3 : break;
1237 4 : default:
1238 : // leave as "Generic"
1239 4 : break;
1240 : }
1241 :
1242 : try
1243 : {
1244 18 : switch (eFieldType)
1245 : {
1246 12 : case GFT_Integer:
1247 12 : m_poKEATable->addAttIntField(pszFieldName, 0, strUsage);
1248 12 : break;
1249 :
1250 1 : case GFT_Boolean:
1251 1 : m_poKEATable->addAttBoolField(pszFieldName, 0, strUsage);
1252 1 : break;
1253 :
1254 2 : case GFT_Real:
1255 2 : m_poKEATable->addAttFloatField(pszFieldName, 0, strUsage);
1256 2 : break;
1257 :
1258 3 : case GFT_String:
1259 : case GFT_DateTime:
1260 : case GFT_WKBGeometry:
1261 3 : m_poKEATable->addAttStringField(pszFieldName, "", strUsage);
1262 3 : break;
1263 : }
1264 :
1265 : // assume we can just grab this now
1266 18 : m_aoFields.push_back(m_poKEATable->getField(pszFieldName));
1267 : }
1268 0 : catch (kealib::KEAException &e)
1269 : {
1270 0 : CPLError(CE_Failure, CPLE_AppDefined, "Failed to add column: %s",
1271 0 : e.what());
1272 0 : return CE_Failure;
1273 : }
1274 :
1275 18 : return CE_None;
1276 : }
1277 :
1278 0 : CPLErr KEARasterAttributeTable::SetLinearBinning(double ldfRow0Min,
1279 : double ldfBinSize)
1280 : {
1281 0 : size_t nRows = m_poKEATable->getSize();
1282 :
1283 0 : osWorkingResult.Printf("%.16g", ldfRow0Min);
1284 0 : m_poBand->SetMetadataItem("STATISTICS_HISTOMIN", osWorkingResult);
1285 0 : osWorkingResult.Printf("%.16g", (nRows - 1) * ldfBinSize + ldfRow0Min);
1286 0 : m_poBand->SetMetadataItem("STATISTICS_HISTOMAX", osWorkingResult);
1287 :
1288 : // STATISTICS_HISTONUMBINS now returned by metadata
1289 :
1290 0 : return CE_None;
1291 : }
1292 :
1293 0 : int KEARasterAttributeTable::GetLinearBinning(double *pdfRow0Min,
1294 : double *pdfBinSize) const
1295 : {
1296 0 : const char *pszMin = m_poBand->GetMetadataItem("STATISTICS_HISTOMIN");
1297 0 : const char *pszMax = m_poBand->GetMetadataItem("STATISTICS_HISTOMAX");
1298 0 : if ((pszMin == nullptr) || (pszMax == nullptr))
1299 : {
1300 0 : return FALSE;
1301 : }
1302 0 : *pdfRow0Min = atof(pszMin);
1303 0 : *pdfBinSize = (atof(pszMax) - *pdfRow0Min) / (m_poKEATable->getSize() - 1);
1304 :
1305 0 : return TRUE;
1306 : }
1307 :
1308 0 : CPLXMLNode *KEARasterAttributeTable::Serialize() const
1309 : {
1310 0 : const int nColCount = GetColumnCount();
1311 0 : if (nColCount > 0 && GetRowCount() > RAT_MAX_ELEM_FOR_CLONE / nColCount)
1312 0 : return nullptr;
1313 :
1314 0 : return GDALRasterAttributeTable::Serialize();
1315 : }
1316 :
1317 1 : GDALRATTableType KEARasterAttributeTable::GetTableType() const
1318 : {
1319 1 : kealib::KEALayerType keaType = m_poBand->getLayerType();
1320 1 : if (keaType == kealib::kea_continuous)
1321 : {
1322 1 : return GRTT_ATHEMATIC;
1323 : }
1324 : else
1325 : {
1326 0 : return GRTT_THEMATIC;
1327 : }
1328 : }
1329 :
1330 : CPLErr
1331 0 : KEARasterAttributeTable::SetTableType(const GDALRATTableType eInTableType)
1332 : {
1333 0 : kealib::KEALayerType keaType = (eInTableType == GRTT_ATHEMATIC)
1334 0 : ? kealib::kea_continuous
1335 : : kealib::kea_thematic;
1336 : try
1337 : {
1338 0 : m_poBand->setLayerType(keaType);
1339 0 : return CE_None;
1340 : }
1341 0 : catch (const kealib::KEAIOException &)
1342 : {
1343 0 : return CE_Failure;
1344 : }
1345 : }
1346 :
1347 0 : void KEARasterAttributeTable::RemoveStatistics()
1348 : {
1349 : // TODO ?
1350 0 : }
|