/src/gdal/frmts/mrf/mrf_util.cpp
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) 2002-2012, California Institute of Technology. |
3 | | * All rights reserved. Based on Government Sponsored Research under contracts |
4 | | * NAS7-1407 and/or NAS7-03001. |
5 | | * |
6 | | * Redistribution and use in source and binary forms, with or without |
7 | | * modification, are permitted provided that the following conditions are met: |
8 | | * 1. Redistributions of source code must retain the above copyright notice, |
9 | | * this list of conditions and the following disclaimer. |
10 | | * 2. Redistributions in binary form must reproduce the above copyright |
11 | | * notice, this list of conditions and the following disclaimer in the |
12 | | * documentation and/or other materials provided with the distribution. |
13 | | * 3. Neither the name of the California Institute of Technology (Caltech), |
14 | | * its operating division the Jet Propulsion Laboratory (JPL), the National |
15 | | * Aeronautics and Space Administration (NASA), nor the names of its |
16 | | * contributors may be used to endorse or promote products derived from this |
17 | | * software without specific prior written permission. |
18 | | * |
19 | | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
20 | | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
21 | | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
22 | | * ARE DISCLAIMED. IN NO EVENT SHALL THE CALIFORNIA INSTITUTE OF TECHNOLOGY BE |
23 | | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
24 | | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
25 | | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
26 | | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
27 | | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
28 | | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
29 | | * POSSIBILITY OF SUCH DAMAGE. |
30 | | * |
31 | | * Copyright 2014-2021 Esri |
32 | | * |
33 | | * Licensed under the Apache License, Version 2.0 (the "License"); |
34 | | * you may not use this file except in compliance with the License. |
35 | | * You may obtain a copy of the License at |
36 | | * |
37 | | * http://www.apache.org/licenses/LICENSE-2.0 |
38 | | * |
39 | | * Unless required by applicable law or agreed to in writing, software |
40 | | * distributed under the License is distributed on an "AS IS" BASIS, |
41 | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
42 | | * See the License for the specific language governing permissions and |
43 | | * limitations under the License. |
44 | | * |
45 | | * Functions used by the driver, should have prototypes in the header file |
46 | | * |
47 | | * Author: Lucian Plesea |
48 | | */ |
49 | | |
50 | | #include "marfa.h" |
51 | | #include <zlib.h> |
52 | | #include <algorithm> |
53 | | #include <limits> |
54 | | #include "mrfdrivercore.h" |
55 | | #include "gdal_frmts.h" |
56 | | |
57 | | // LERC and QB3 only work on little endian machines |
58 | | #if defined(WORDS_BIGENDIAN) |
59 | | #undef LERC |
60 | | #undef QB3_SUPPORT |
61 | | #endif |
62 | | |
63 | | NAMESPACE_MRF_START |
64 | | |
65 | | // These have to be positionally in sync with the enums in marfa.h |
66 | | static const char *const ILC_N[] = { |
67 | | #ifdef HAVE_PNG |
68 | | "PNG", "PPNG", |
69 | | #endif |
70 | | #ifdef HAVE_JPEG |
71 | | "JPEG", |
72 | | #endif |
73 | | #if defined(HAVE_PNG) && defined(HAVE_JPEG) |
74 | | "JPNG", |
75 | | #endif |
76 | | "NONE", "DEFLATE", "TIF", |
77 | | #if defined(LERC) |
78 | | "LERC", |
79 | | #endif |
80 | | #if defined(ZSTD_SUPPORT) |
81 | | "ZSTD", |
82 | | #endif |
83 | | #if defined(QB3_SUPPORT) |
84 | | "QB3", |
85 | | #endif |
86 | | "Unknown"}; |
87 | | |
88 | | static const char *const ILC_E[] = { |
89 | | #ifdef HAVE_PNG |
90 | | ".ppg", ".ppg", |
91 | | #endif |
92 | | #ifdef HAVE_JPEG |
93 | | ".pjg", |
94 | | #endif |
95 | | #if defined(HAVE_PNG) && defined(HAVE_JPEG) |
96 | | ".pjp", |
97 | | #endif |
98 | | ".til", ".pzp", ".ptf", |
99 | | #if defined(LERC) |
100 | | ".lrc", |
101 | | #endif |
102 | | #if defined(ZSTD_SUPPORT) |
103 | | ".pzs", |
104 | | #endif |
105 | | #if defined(QB3_SUPPORT) |
106 | | ".pq3", |
107 | | #endif |
108 | | ""}; |
109 | | |
110 | | static const char *const ILO_N[] = {"PIXEL", "BAND", "LINE", "Unknown"}; |
111 | | |
112 | | char const *const *ILComp_Name = ILC_N; |
113 | | char const *const *ILComp_Ext = ILC_E; |
114 | | char const *const *ILOrder_Name = ILO_N; |
115 | | |
116 | | /** |
117 | | * Get the string for a compression type |
118 | | */ |
119 | | const char *CompName(ILCompression comp) |
120 | 15.4k | { |
121 | 15.4k | if (comp >= IL_ERR_COMP) |
122 | 0 | return ILComp_Name[IL_ERR_COMP]; |
123 | 15.4k | return ILComp_Name[comp]; |
124 | 15.4k | } |
125 | | |
126 | | /** |
127 | | * Get the string for an order type |
128 | | */ |
129 | | const char *OrderName(ILOrder val) |
130 | 11.7k | { |
131 | 11.7k | if (val >= IL_ERR_ORD) |
132 | 0 | return ILOrder_Name[IL_ERR_ORD]; |
133 | 11.7k | return ILOrder_Name[val]; |
134 | 11.7k | } |
135 | | |
136 | | ILCompression CompToken(const char *opt, ILCompression def) |
137 | 11.7k | { |
138 | 11.7k | int i; |
139 | 11.7k | if (nullptr == opt) |
140 | 0 | return def; |
141 | 48.6k | for (i = 0; ILCompression(i) < IL_ERR_COMP; i++) |
142 | 48.6k | if (EQUAL(opt, ILComp_Name[i])) |
143 | 11.7k | break; |
144 | 11.7k | if (IL_ERR_COMP == ILCompression(i)) |
145 | 6 | return def; |
146 | 11.7k | return ILCompression(i); |
147 | 11.7k | } |
148 | | |
149 | | /** |
150 | | * Find a compression token |
151 | | */ |
152 | | ILOrder OrderToken(const char *opt, ILOrder def) |
153 | 11.7k | { |
154 | 11.7k | int i; |
155 | 11.7k | if (nullptr == opt) |
156 | 0 | return def; |
157 | 11.9k | for (i = 0; ILOrder(i) < IL_ERR_ORD; i++) |
158 | 11.9k | if (EQUAL(opt, ILOrder_Name[i])) |
159 | 11.7k | break; |
160 | 11.7k | if (IL_ERR_ORD == ILOrder(i)) |
161 | 0 | return def; |
162 | 11.7k | return ILOrder(i); |
163 | 11.7k | } |
164 | | |
165 | | // |
166 | | // Inserters for ILSize and ILIdx types |
167 | | // |
168 | | std::ostream &operator<<(std::ostream &out, const ILSize &sz) |
169 | 0 | { |
170 | 0 | out << "X=" << sz.x << ",Y=" << sz.y << ",Z=" << sz.z << ",C=" << sz.c |
171 | 0 | << ",L=" << sz.l; |
172 | 0 | return out; |
173 | 0 | } |
174 | | |
175 | | std::ostream &operator<<(std::ostream &out, const ILIdx &t) |
176 | 0 | { |
177 | 0 | out << "offset=" << t.offset << ",size=" << t.size; |
178 | 0 | return out; |
179 | 0 | } |
180 | | |
181 | | // Define PPMW to enable this handy debug function |
182 | | #ifdef PPMW |
183 | | void ppmWrite(const char *fname, const char *data, const ILSize &sz) |
184 | | { |
185 | | FILE *fp = fopen(fname, "wb"); |
186 | | switch (sz.c) |
187 | | { |
188 | | case 4: // Strip the alpha |
189 | | fprintf(fp, "P6 %d %d 255\n", sz.x, sz.y); |
190 | | { |
191 | | char *d = (char *)data; |
192 | | for (int i = sz.x * sz.y; i; i--) |
193 | | { |
194 | | fwrite(d, 3, 1, fp); |
195 | | d += 4; |
196 | | } |
197 | | } |
198 | | break; |
199 | | case 3: |
200 | | fprintf(fp, "P6 %d %d 255\n", sz.x, sz.y); |
201 | | fwrite(data, sz.x * sz.y, 3, fp); |
202 | | break; |
203 | | case 1: |
204 | | fprintf(fp, "P5 %d %d 255\n", sz.x, sz.y); |
205 | | fwrite(data, sz.x, sz.y, fp); |
206 | | break; |
207 | | default: |
208 | | fprintf(stderr, "Can't write ppm file with %d bands\n", /*ok*/ |
209 | | sz.c); |
210 | | } |
211 | | fclose(fp); |
212 | | return; |
213 | | } |
214 | | #endif |
215 | | |
216 | | // Returns the size of the index for image and overlays |
217 | | // If scale is zero, only base image |
218 | | GIntBig IdxSize(const ILImage &full, const int scale) |
219 | 11.6k | { |
220 | 11.6k | ILImage img = full; |
221 | 11.6k | img.pagecount = pcount(img.size, img.pagesize); |
222 | 11.6k | GIntBig sz = img.pagecount.l; |
223 | 11.6k | while (scale != 0 && 1 != img.pagecount.x * img.pagecount.y) |
224 | 0 | { |
225 | 0 | img.size.x = pcount(img.size.x, scale); |
226 | 0 | img.size.y = pcount(img.size.y, scale); |
227 | 0 | img.pagecount = pcount(img.size, img.pagesize); |
228 | 0 | sz += img.pagecount.l; |
229 | 0 | } |
230 | | |
231 | 11.6k | if (sz > |
232 | 11.6k | std::numeric_limits<GIntBig>::max() / static_cast<int>(sizeof(ILIdx))) |
233 | 0 | { |
234 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "IdxSize: integer overflow"); |
235 | 0 | return 0; |
236 | 0 | } |
237 | 11.6k | return sz * sizeof(ILIdx); |
238 | 11.6k | } |
239 | | |
240 | | ILImage::ILImage() |
241 | 23.7k | : dataoffset(0), idxoffset(0), quality(85), pageSizeBytes(0), |
242 | 23.7k | size(ILSize(1, 1, 1, 1, 0)), pagesize(ILSize(384, 384, 1, 1, 0)), |
243 | 23.7k | pagecount(pcount(size, pagesize)), |
244 | | #ifdef HAVE_PNG |
245 | 23.7k | comp(IL_PNG), |
246 | | #else |
247 | | comp(IL_NONE), |
248 | | #endif |
249 | 23.7k | order(IL_Interleaved), nbo(false), hasNoData(FALSE), NoDataValue(0.0), |
250 | 23.7k | dt(GDT_Unknown), ci(GCI_Undefined) |
251 | 23.7k | { |
252 | 23.7k | } |
253 | | |
254 | | /** |
255 | | *\brief Get a file name by replacing the extension. |
256 | | * pass the data file name and the default extension starting with . |
257 | | * If name length is not sufficient, it returns the extension |
258 | | * If the input name is curl with parameters, the base file extension gets |
259 | | *changed and parameters are preserved. |
260 | | */ |
261 | | |
262 | | CPLString getFname(const CPLString &in, const char *ext) |
263 | 16.2k | { |
264 | 16.2k | if (strlen(in) < strlen(ext)) |
265 | 0 | return CPLString(ext); |
266 | | |
267 | 16.2k | CPLString ret(in); |
268 | | // Is it a web file with parameters? |
269 | 16.2k | size_t extlen = strlen(ext); |
270 | 16.2k | size_t qmark = ret.find_first_of('?'); |
271 | 16.2k | if (!(qmark != std::string::npos && 0 == in.find("/vsicurl/http") && |
272 | 0 | qmark >= extlen)) |
273 | 16.2k | qmark = ret.size(); |
274 | 16.2k | return ret.replace(qmark - extlen, extlen, ext); |
275 | 16.2k | } |
276 | | |
277 | | /** |
278 | | *\brief Get a file name, either from the configuration or from the default file |
279 | | *name If the token is not defined by CPLGetXMLValue, if the extension of the in |
280 | | *name is .xml, it returns the token with the extension changed to defext. |
281 | | * Otherwise it returns the token itself |
282 | | * It is pretty hard to separate local vs remote due to the gdal file name |
283 | | *ornaments Absolute file names start with: ?:/ or / |
284 | | * |
285 | | */ |
286 | | |
287 | | CPLString getFname(CPLXMLNode *node, const char *token, const CPLString &in, |
288 | | const char *def) |
289 | 23.4k | { |
290 | 23.4k | CPLString fn = CPLGetXMLValue(node, token, ""); |
291 | 23.4k | if (fn.empty()) // Not provided |
292 | 16.2k | return getFname(in, def); |
293 | 7.19k | size_t slashPos = fn.find_first_of("\\/"); |
294 | | |
295 | | // Does it look like an absolute path or we won't find the basename of 'in' |
296 | 7.19k | if (slashPos == 0 // Starts with slash |
297 | 3.59k | || (slashPos == 2 && fn[1] == ':') // Starts with disk letter column |
298 | | // Does not start with dots then slash |
299 | 3.59k | || (slashPos != fn.npos && slashPos != fn.find_first_not_of('.')) || |
300 | 3.59k | EQUALN(in, "<MRF_META>", 10) // XML string input |
301 | 3.59k | || in.find_first_of("\\/") == |
302 | 3.59k | in.npos) // We can't get a basename from 'in' |
303 | 3.59k | return fn; |
304 | | |
305 | | // Relative path, prepend the path from the in file name |
306 | 3.59k | return in.substr(0, in.find_last_of("\\/") + 1) + fn; |
307 | 7.19k | } |
308 | | |
309 | | /** |
310 | | *\brief Extracts a numerical value from a XML node |
311 | | * It works like CPLGetXMLValue except for the default value being |
312 | | * a number instead of a string |
313 | | */ |
314 | | |
315 | | double getXMLNum(CPLXMLNode *node, const char *pszPath, double def) |
316 | 84.4k | { |
317 | 84.4k | const char *textval = CPLGetXMLValue(node, pszPath, nullptr); |
318 | 84.4k | if (textval) |
319 | 43.0k | return atof(textval); |
320 | 41.4k | return def; |
321 | 84.4k | } |
322 | | |
323 | | // |
324 | | // Calculate offset of index, pos is in pages |
325 | | // |
326 | | |
327 | | GIntBig IdxOffset(const ILSize &pos, const ILImage &img) |
328 | 120k | { |
329 | 120k | return img.idxoffset + |
330 | 120k | sizeof(ILIdx) * |
331 | 120k | (pos.c + |
332 | 120k | img.pagecount.c * |
333 | 120k | (pos.x + img.pagecount.x * |
334 | 120k | (pos.y + img.pagecount.y * |
335 | 120k | static_cast<GIntBig>(pos.z)))); |
336 | 120k | } |
337 | | |
338 | | // Is compression type endianness dependent? |
339 | | bool is_Endianness_Dependent(GDALDataType dt, ILCompression comp) |
340 | 66.7k | { |
341 | | // Add here all endianness dependent compressions |
342 | 66.7k | if (IL_ZLIB == comp || IL_NONE == comp) |
343 | 13.8k | if (GDALGetDataTypeSizeBytes(dt) > 1) |
344 | 0 | return true; |
345 | 66.7k | return false; |
346 | 66.7k | } |
347 | | |
348 | | MRFRasterBand *newMRFRasterBand(MRFDataset *pDS, const ILImage &image, int b, |
349 | | int level) |
350 | 2.71M | { |
351 | 2.71M | MRFRasterBand *bnd = nullptr; |
352 | 2.71M | CPLErrorReset(); |
353 | 2.71M | switch (pDS->current.comp) |
354 | 2.71M | { |
355 | 0 | #ifdef HAVE_PNG |
356 | 0 | case IL_PPNG: // Uses the PNG code, just has a palette in each PNG |
357 | 6.55k | case IL_PNG: |
358 | 6.55k | bnd = new PNG_Band(pDS, image, b, level); |
359 | 6.55k | break; |
360 | 0 | #endif |
361 | 0 | #ifdef HAVE_JPEG |
362 | 6.33k | case IL_JPEG: |
363 | 6.33k | bnd = new JPEG_Band(pDS, image, b, level); |
364 | 6.33k | break; |
365 | 0 | #endif |
366 | 0 | #if defined(HAVE_PNG) && defined(HAVE_JPEG) |
367 | 0 | case IL_JPNG: |
368 | 0 | bnd = new JPNG_Band(pDS, image, b, level); |
369 | 0 | break; |
370 | 0 | #endif |
371 | 556 | case IL_NONE: |
372 | 556 | bnd = new Raw_Band(pDS, image, b, level); |
373 | 556 | break; |
374 | 0 | #if defined(LERC) |
375 | 2.68M | case IL_LERC: |
376 | 2.68M | bnd = new LERC_Band(pDS, image, b, level); |
377 | 2.68M | break; |
378 | 0 | #endif |
379 | | #if defined(QB3_SUPPORT) |
380 | | case IL_QB3: |
381 | | bnd = new QB3_Band(pDS, image, b, level); |
382 | | break; |
383 | | #endif |
384 | | // ZLIB is just raw + deflate |
385 | 683 | case IL_ZLIB: |
386 | 683 | bnd = new Raw_Band(pDS, image, b, level); |
387 | 683 | bnd->SetDeflate(1); |
388 | 683 | break; |
389 | | // Same for ZSTD |
390 | 0 | #if defined(ZSTD_SUPPORT) |
391 | 0 | case IL_ZSTD: |
392 | 0 | bnd = new Raw_Band(pDS, image, b, level); |
393 | 0 | bnd->SetZstd(1); |
394 | 0 | break; |
395 | 0 | #endif |
396 | 11.6k | case IL_TIF: |
397 | 11.6k | if (image.pageSizeBytes > INT_MAX - 1024) |
398 | 0 | return nullptr; |
399 | 11.6k | bnd = new TIF_Band(pDS, image, b, level); |
400 | 11.6k | break; |
401 | 0 | default: |
402 | 0 | CPLError(CE_Failure, CPLE_AssertionFailed, |
403 | 0 | "Unsupported MRF compression"); |
404 | 0 | return nullptr; |
405 | 2.71M | } |
406 | | |
407 | | // If something was flagged during band creation |
408 | 2.71M | if (CPLGetLastErrorNo() != CE_None) |
409 | 49 | { |
410 | 49 | delete bnd; |
411 | 49 | return nullptr; |
412 | 49 | } |
413 | | |
414 | | // Copy the RW mode from the dataset |
415 | 2.71M | bnd->SetAccess(pDS->eAccess); |
416 | 2.71M | return bnd; |
417 | 2.71M | } |
418 | | |
419 | | /** |
420 | | *\brief log in a given base |
421 | | */ |
422 | | double logbase(double val, double base) |
423 | 0 | { |
424 | 0 | return log(val) / log(base); |
425 | 0 | } |
426 | | |
427 | | /** |
428 | | *\brief Is logbase(val, base) an integer? |
429 | | * |
430 | | */ |
431 | | int IsPower(double value, double base) |
432 | 0 | { |
433 | 0 | double v = logbase(value, base); |
434 | 0 | return CPLIsEqual(v, int(v + 0.5)); |
435 | 0 | } |
436 | | |
437 | | /************************************************************************/ |
438 | | /* SearchXMLSiblings() */ |
439 | | /************************************************************************/ |
440 | | |
441 | | /** |
442 | | *\brief Search for a sibling of the root node with a given name. |
443 | | * |
444 | | * Searches only the next siblings of the node passed in for the named element |
445 | | *or attribute. If the first character of the pszElement is '=', the search |
446 | | *includes the psRoot node |
447 | | * |
448 | | * @param psRoot the root node to search. This should be a node of type |
449 | | * CXT_Element. NULL is safe. |
450 | | * |
451 | | * @param pszElement the name of the element or attribute to search for. |
452 | | * |
453 | | * @return The first matching node or NULL on failure. |
454 | | */ |
455 | | |
456 | | CPLXMLNode *SearchXMLSiblings(CPLXMLNode *psRoot, const char *pszElement) |
457 | 0 | { |
458 | 0 | if (psRoot == nullptr || pszElement == nullptr) |
459 | 0 | return nullptr; |
460 | | |
461 | | // If the strings starts with '=', skip it and test the root |
462 | | // If not, start testing with the next sibling |
463 | 0 | if (pszElement[0] == '=') |
464 | 0 | pszElement++; |
465 | 0 | else |
466 | 0 | psRoot = psRoot->psNext; |
467 | |
|
468 | 0 | for (; psRoot != nullptr; psRoot = psRoot->psNext) |
469 | 0 | if ((psRoot->eType == CXT_Element || psRoot->eType == CXT_Attribute) && |
470 | 0 | EQUAL(pszElement, psRoot->pszValue)) |
471 | 0 | return psRoot; |
472 | 0 | return nullptr; |
473 | 0 | } |
474 | | |
475 | | // |
476 | | // Print a double so it can be read with strod while preserving precision |
477 | | // Unfortunately this is not quite possible or portable enough at this time |
478 | | // |
479 | | CPLString PrintDouble(double d, const char *frmt) |
480 | 22.2k | { |
481 | 22.2k | CPLString res; |
482 | 22.2k | res.FormatC(d, nullptr); |
483 | 22.2k | if (CPLStrtod(res.c_str(), nullptr) == d) |
484 | 21.8k | return res; |
485 | | |
486 | | // This would be the right code with a C99 compiler that supports %a |
487 | | // readback in strod() |
488 | | // return CPLString().Printf("%a",d); |
489 | | |
490 | 346 | return CPLString().FormatC(d, frmt); |
491 | 22.2k | } |
492 | | |
493 | | void XMLSetAttributeVal(CPLXMLNode *parent, const char *pszName, |
494 | | const char *pszVal) |
495 | 22.2k | { |
496 | 22.2k | CPLCreateXMLNode(parent, CXT_Attribute, pszName); |
497 | 22.2k | CPLSetXMLValue(parent, pszName, pszVal); |
498 | 22.2k | } |
499 | | |
500 | | void XMLSetAttributeVal(CPLXMLNode *parent, const char *pszName, |
501 | | const double val, const char *frmt) |
502 | 22.2k | { |
503 | 22.2k | XMLSetAttributeVal(parent, pszName, PrintDouble(val, frmt)); |
504 | 22.2k | } |
505 | | |
506 | | CPLXMLNode *XMLSetAttributeVal(CPLXMLNode *parent, const char *pszName, |
507 | | const ILSize &sz, const char *frmt) |
508 | 7.41k | { |
509 | 7.41k | CPLXMLNode *node = CPLCreateXMLNode(parent, CXT_Element, pszName); |
510 | 7.41k | XMLSetAttributeVal(node, "x", sz.x, frmt); |
511 | 7.41k | XMLSetAttributeVal(node, "y", sz.y, frmt); |
512 | 7.41k | if (sz.z != 1) |
513 | 0 | XMLSetAttributeVal(node, "z", sz.z, frmt); |
514 | 7.41k | XMLSetAttributeVal(node, "c", sz.c, frmt); |
515 | 7.41k | return node; |
516 | 7.41k | } |
517 | | |
518 | | // |
519 | | // Prints a vector of doubles into a string and sets that string as the value of |
520 | | // an XML attribute If all values are the same, it only prints one |
521 | | // |
522 | | void XMLSetAttributeVal(CPLXMLNode *parent, const char *pszName, |
523 | | std::vector<double> const &values) |
524 | 0 | { |
525 | 0 | if (values.empty()) |
526 | 0 | return; |
527 | | |
528 | 0 | CPLString value; |
529 | 0 | double val = values[0]; |
530 | 0 | int single_val = true; |
531 | 0 | for (int i = 0; i < int(values.size()); i++) |
532 | 0 | { |
533 | 0 | if (val != values[i]) |
534 | 0 | single_val = false; |
535 | 0 | value.append(PrintDouble(values[i]) + " "); |
536 | 0 | } |
537 | 0 | value.pop_back(); // Cut the last space |
538 | 0 | if (single_val) |
539 | 0 | value = PrintDouble(values[0]); |
540 | 0 | CPLCreateXMLNode(parent, CXT_Attribute, pszName); |
541 | 0 | CPLSetXMLValue(parent, pszName, value); |
542 | 0 | } |
543 | | |
544 | | /** |
545 | | *\brief Verify or make a file that big |
546 | | * |
547 | | * @return true if size is OK or if extend succeeded |
548 | | */ |
549 | | |
550 | | int CheckFileSize(const char *fname, GIntBig sz, GDALAccess eAccess) |
551 | 0 | { |
552 | |
|
553 | 0 | VSIStatBufL statb; |
554 | 0 | if (VSIStatL(fname, &statb)) |
555 | 0 | return false; |
556 | 0 | if (statb.st_size >= sz) |
557 | 0 | return true; |
558 | | |
559 | | // Don't change anything unless updating |
560 | 0 | if (eAccess != GA_Update) |
561 | 0 | return false; |
562 | | |
563 | | // There is no ftruncate in VSI, only truncate() |
564 | 0 | VSILFILE *ifp = VSIFOpenL(fname, "r+b"); |
565 | 0 | if (ifp == nullptr) |
566 | 0 | return false; |
567 | | |
568 | 0 | int ret = VSIFTruncateL(ifp, sz); |
569 | 0 | VSIFCloseL(ifp); |
570 | 0 | return !ret; |
571 | 0 | } |
572 | | |
573 | | NAMESPACE_MRF_END |
574 | | |
575 | | /************************************************************************/ |
576 | | /* GDALRegister_MRF() */ |
577 | | /************************************************************************/ |
578 | | |
579 | | USING_NAMESPACE_MRF |
580 | | |
581 | | void GDALRegister_MRF() |
582 | 24 | { |
583 | 24 | if (GDALGetDriverByName(DRIVER_NAME) != nullptr) |
584 | 0 | return; |
585 | | |
586 | 24 | GDALDriver *driver = new GDALDriver(); |
587 | 24 | MRFDriverSetCommonMetadata(driver); |
588 | | |
589 | 24 | driver->SetMetadataItem( |
590 | 24 | GDAL_DMD_CREATIONOPTIONLIST, |
591 | 24 | "<CreationOptionList>" |
592 | 24 | " <Option name='COMPRESS' type='string-select' " |
593 | 24 | #ifdef HAVE_PNG |
594 | 24 | "default='PNG' description='PPNG = Palette PNG; DEFLATE = zlib '>" |
595 | | #else |
596 | | "default='NONE' description='DEFLATE = zlib '>" |
597 | | #endif |
598 | 24 | #ifdef HAVE_JPEG |
599 | 24 | " <Value>JPEG</Value>" |
600 | 24 | #endif |
601 | 24 | #ifdef HAVE_PNG |
602 | 24 | " <Value>PNG</Value>" |
603 | 24 | " <Value>PPNG</Value>" |
604 | 24 | #endif |
605 | 24 | #if defined(HAVE_JPEG) && defined(HAVE_PNG) |
606 | 24 | " <Value>JPNG</Value>" |
607 | 24 | #endif |
608 | 24 | " <Value>TIF</Value>" |
609 | 24 | " <Value>DEFLATE</Value>" |
610 | 24 | " <Value>NONE</Value>" |
611 | 24 | #if defined(LERC) |
612 | 24 | " <Value>LERC</Value>" |
613 | 24 | #endif |
614 | 24 | #if defined(ZSTD_SUPPORT) |
615 | 24 | " <Value>ZSTD</Value>" |
616 | 24 | #endif |
617 | | #if defined(QB3_SUPPORT) |
618 | | " <Value>QB3</Value>" |
619 | | #endif |
620 | 24 | " </Option>" |
621 | 24 | " <Option name='INTERLEAVE' type='string-select' default='PIXEL'>" |
622 | 24 | " <Value>PIXEL</Value>" |
623 | 24 | " <Value>BAND</Value>" |
624 | 24 | " </Option>\n" |
625 | 24 | " <Option name='ZSIZE' type='int' description='Third dimension size' " |
626 | 24 | "default='1'/>" |
627 | 24 | " <Option name='QUALITY' type='int' description='Compression " |
628 | 24 | "dependent control value, for JPEG best=99, bad=0, default=85'/>\n" |
629 | 24 | " <Option name='BLOCKSIZE' type='int' description='Block size, both " |
630 | 24 | "x and y, default 512'/>\n" |
631 | 24 | " <Option name='BLOCKXSIZE' type='int' description='Block x size, " |
632 | 24 | "default=512'/>\n" |
633 | 24 | " <Option name='BLOCKYSIZE' type='int' description='Block y size, " |
634 | 24 | "default=512'/>\n" |
635 | 24 | " <Option name='NETBYTEORDER' type='boolean' " |
636 | 24 | "description='Force endian for certain compress options, default is " |
637 | 24 | "host order'/>\n" |
638 | 24 | " <Option name='CACHEDSOURCE' type='string' " |
639 | 24 | "description='The source raster, if this is a cache'/>\n" |
640 | 24 | " <Option name='UNIFORM_SCALE' type='int' description='Scale of " |
641 | 24 | "overlays in MRF, usually 2'/>\n" |
642 | 24 | " <Option name='NOCOPY' type='boolean' description='Leave created " |
643 | 24 | "MRF empty, default=no'/>\n" |
644 | 24 | " <Option name='DATANAME' type='string' description='Data file " |
645 | 24 | "name'/>\n" |
646 | 24 | " <Option name='INDEXNAME' type='string' description='Index file " |
647 | 24 | "name'/>\n" |
648 | 24 | " <Option name='SPACING' type='int' " |
649 | 24 | "description='Leave this many unused bytes before each tile, " |
650 | 24 | "default=0'/>\n" |
651 | 24 | " <Option name='PHOTOMETRIC' type='string-select' default='DEFAULT' " |
652 | 24 | "description='Band interpretation, may affect block encoding'>\n" |
653 | 24 | " <Value>MULTISPECTRAL</Value>" |
654 | 24 | " <Value>RGB</Value>" |
655 | 24 | " <Value>YCC</Value>" |
656 | 24 | " </Option>\n" |
657 | 24 | " <Option name='OPTIONS' type='string' description='\n" |
658 | 24 | " Compression dependent parameters, space separated:\n" |
659 | 24 | #if defined(ZSTD_SUPPORT) |
660 | 24 | " ZSTD - boolean, enable libzstd as final stage, preferred over " |
661 | 24 | "DEFLATE\n" |
662 | 24 | #endif |
663 | 24 | " DEFLATE - boolean, enable zlib as final stage\n" |
664 | 24 | " GZ - boolean, for DEFLATE enable gzip headers instead of zlib " |
665 | 24 | "ones when using zlib\n" |
666 | 24 | " RAWZ - boolean, for DEFLATE disable all zlib headers\n" |
667 | 24 | " Z_STRATEGY - Z_HUFFMAN_ONLY | Z_FILTERED | Z_RLE | Z_FIXED: " |
668 | 24 | "restricts DEFLATE and PNG strategy\n" |
669 | 24 | #if defined(LERC) |
670 | 24 | " LERC_PREC - numeric, set LERC precision, defaults to 0.5 for " |
671 | 24 | "int and 0.001 for float\n" |
672 | 24 | " V1 - boolean, use LERC V1 (older) format\n" |
673 | 24 | " L2_VER - numeric, encode specific version of Lerc, default is " |
674 | 24 | "library default\n" |
675 | 24 | " except for single band or INTERLEAVE=BAND, when it " |
676 | 24 | "defaults to 2\n" |
677 | 24 | #endif |
678 | 24 | " OPTIMIZE - boolean, for JPEG, enables Huffman table " |
679 | 24 | "optimization\n" |
680 | | #if defined(BRUNSLI) |
681 | | " JFIF - boolean, for JPEG, disable brunsli encoding\n" |
682 | | #endif |
683 | 24 | "'/>" |
684 | 24 | "</CreationOptionList>\n"); |
685 | | |
686 | 24 | driver->pfnOpen = MRFDataset::Open; |
687 | 24 | driver->pfnCreateCopy = MRFDataset::CreateCopy; |
688 | 24 | driver->pfnCreate = MRFDataset::Create; |
689 | 24 | driver->pfnDelete = MRFDataset::Delete; |
690 | 24 | GetGDALDriverManager()->RegisterDriver(driver); |
691 | 24 | } |