Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: KmlSuperOverlay
4 : * Purpose: Implements write support for KML superoverlay - KMZ.
5 : * Author: Harsh Govind, harsh.govind@spadac.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2010, SPADAC Inc. <harsh.govind@spadac.com>
9 : * Copyright (c) 2010-2014, Even Rouault <even dot rouault at spatialys.com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #include "cpl_port.h"
15 : #include "kmlsuperoverlaydataset.h"
16 :
17 : #include <array>
18 : #include <cmath>
19 : #include <cstring>
20 : #include <algorithm>
21 : #include <fstream>
22 : #include <iostream>
23 : #include <sstream>
24 :
25 : #include "cpl_conv.h"
26 : #include "cpl_error.h"
27 : #include "cpl_string.h"
28 : #include "cpl_vsi.h"
29 : #include "gdal_frmts.h"
30 : #include "memdataset.h"
31 : #include "ogr_spatialref.h"
32 : #include "../vrt/gdal_vrt.h"
33 : #include "../vrt/vrtdataset.h"
34 :
35 : /************************************************************************/
36 : /* GenerateTiles() */
37 : /************************************************************************/
38 75 : static void GenerateTiles(const std::string &filename, CPL_UNUSED int zoom,
39 : int rxsize, int rysize, CPL_UNUSED int ix,
40 : CPL_UNUSED int iy, int rx, int ry, int dxsize,
41 : int dysize, int bands, GDALDataset *poSrcDs,
42 : GDALDriver *poOutputTileDriver, bool isJpegDriver)
43 : {
44 75 : GDALRasterBand *alphaBand = nullptr;
45 :
46 150 : std::vector<GByte> abyScanline(dxsize);
47 150 : std::vector<bool> hadnoData(dxsize);
48 :
49 75 : if (isJpegDriver && bands == 4)
50 41 : bands = 3;
51 :
52 : auto poTmpDataset = std::unique_ptr<GDALDataset>(
53 150 : MEMDataset::Create("", dxsize, dysize, bands, GDT_Byte, nullptr));
54 :
55 75 : if (!isJpegDriver) // Jpeg dataset only has one or three bands
56 : {
57 12 : if (bands < 4) // add transparency to files with one band or three
58 : // bands
59 : {
60 7 : poTmpDataset->AddBand(GDT_Byte);
61 : alphaBand =
62 7 : poTmpDataset->GetRasterBand(poTmpDataset->GetRasterCount());
63 : }
64 : }
65 :
66 75 : const int rowOffset = rysize / dysize;
67 75 : const int loopCount = rysize / rowOffset;
68 7045 : for (int row = 0; row < loopCount; row++)
69 : {
70 6970 : if (!isJpegDriver)
71 : {
72 643640 : for (int i = 0; i < dxsize; i++)
73 : {
74 641920 : hadnoData[i] = false;
75 : }
76 : }
77 :
78 22860 : for (int band = 1; band <= bands; band++)
79 : {
80 15890 : GDALRasterBand *poBand = poSrcDs->GetRasterBand(band);
81 15890 : int hasNoData = 0;
82 15890 : const double noDataValue = poBand->GetNoDataValue(&hasNoData);
83 :
84 15890 : int yOffset = ry + row * rowOffset;
85 15890 : CPLErr errTest = poBand->RasterIO(
86 15890 : GF_Read, rx, yOffset, rxsize, rowOffset, abyScanline.data(),
87 : dxsize, 1, GDT_Byte, 0, 0, nullptr);
88 :
89 15890 : const bool bReadFailed = (errTest == CE_Failure);
90 15890 : if (bReadFailed)
91 : {
92 0 : hasNoData = 1;
93 : }
94 :
95 : // fill the true or false for hadnoData array if the source data has
96 : // nodata value
97 15890 : if (!isJpegDriver)
98 : {
99 5480 : if (hasNoData == 1)
100 : {
101 0 : for (int j = 0; j < dxsize; j++)
102 : {
103 0 : double v = abyScanline[j];
104 0 : if (v == noDataValue || bReadFailed)
105 : {
106 0 : hadnoData[j] = true;
107 : }
108 : }
109 : }
110 : }
111 :
112 15890 : if (!bReadFailed)
113 : {
114 15890 : GDALRasterBand *poBandtmp = poTmpDataset->GetRasterBand(band);
115 15890 : CPL_IGNORE_RET_VAL(poBandtmp->RasterIO(
116 15890 : GF_Write, 0, row, dxsize, 1, abyScanline.data(), dxsize, 1,
117 : GDT_Byte, 0, 0, nullptr));
118 : }
119 : }
120 :
121 : // fill the values for alpha band
122 6970 : if (!isJpegDriver)
123 : {
124 1720 : if (alphaBand)
125 : {
126 561400 : for (int i = 0; i < dxsize; i++)
127 : {
128 560000 : if (hadnoData[i])
129 : {
130 0 : abyScanline[i] = 0;
131 : }
132 : else
133 : {
134 560000 : abyScanline[i] = 255;
135 : }
136 : }
137 :
138 1400 : CPL_IGNORE_RET_VAL(alphaBand->RasterIO(
139 1400 : GF_Write, 0, row, dxsize, 1, abyScanline.data(), dxsize, 1,
140 : GDT_Byte, 0, 0, nullptr));
141 : }
142 : }
143 : }
144 :
145 75 : CPLConfigOptionSetter oSetter("GDAL_OPEN_AFTER_COPY", "NO", false);
146 : /* to prevent CreateCopy() from calling QuietDelete() */
147 75 : const char *const apszOptions[] = {"@QUIET_DELETE_ON_CREATE_COPY=NO",
148 : nullptr};
149 75 : std::unique_ptr<GDALDataset>(
150 : poOutputTileDriver->CreateCopy(filename.c_str(), poTmpDataset.get(),
151 : FALSE, apszOptions, nullptr, nullptr));
152 75 : }
153 :
154 : /************************************************************************/
155 : /* GenerateRootKml() */
156 : /************************************************************************/
157 :
158 19 : static int GenerateRootKml(const char *filename, const char *kmlfilename,
159 : double north, double south, double east, double west,
160 : int tilesize, const char *pszOverlayName,
161 : const char *pszOverlayDescription)
162 : {
163 19 : VSILFILE *fp = VSIFOpenL(filename, "wb");
164 19 : if (fp == nullptr)
165 : {
166 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot create %s", filename);
167 0 : return FALSE;
168 : }
169 19 : int minlodpixels = tilesize / 2;
170 :
171 : const std::string osOverlayName = pszOverlayName
172 : ? std::string(pszOverlayName)
173 19 : : CPLGetBasenameSafe(kmlfilename);
174 :
175 : // If we have not written any features yet, output the layer's schema.
176 19 : VSIFPrintfL(fp, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
177 19 : VSIFPrintfL(fp, "<kml xmlns=\"http://www.opengis.net/kml/2.2\">\n");
178 19 : VSIFPrintfL(fp, "\t<Document>\n");
179 19 : char *pszEncoded = CPLEscapeString(osOverlayName.c_str(), -1, CPLES_XML);
180 19 : VSIFPrintfL(fp, "\t\t<name>%s</name>\n", pszEncoded);
181 19 : CPLFree(pszEncoded);
182 19 : if (pszOverlayDescription == nullptr)
183 : {
184 18 : VSIFPrintfL(fp, "\t\t<description></description>\n");
185 : }
186 : else
187 : {
188 1 : pszEncoded = CPLEscapeString(pszOverlayDescription, -1, CPLES_XML);
189 1 : VSIFPrintfL(fp, "\t\t<description>%s</description>\n", pszEncoded);
190 1 : CPLFree(pszEncoded);
191 : }
192 19 : VSIFPrintfL(fp, "\t\t<styleUrl>#hideChildrenStyle</styleUrl>\n");
193 19 : VSIFPrintfL(fp, "\t\t<Style id=\"hideChildrenStyle\">\n");
194 19 : VSIFPrintfL(fp, "\t\t\t<ListStyle id=\"hideChildren\">\n");
195 19 : VSIFPrintfL(fp, "\t\t\t\t<listItemType>checkHideChildren</listItemType>\n");
196 19 : VSIFPrintfL(fp, "\t\t\t</ListStyle>\n");
197 19 : VSIFPrintfL(fp, "\t\t</Style>\n");
198 : /*VSIFPrintfL(fp, "\t\t<Region>\n");
199 : VSIFPrintfL(fp, "\t\t\t<LatLonAltBox>\n");
200 : VSIFPrintfL(fp, "\t\t\t\t<north>%f</north>\n", north);
201 : VSIFPrintfL(fp, "\t\t\t\t<south>%f</south>\n", south);
202 : VSIFPrintfL(fp, "\t\t\t\t<east>%f</east>\n", east);
203 : VSIFPrintfL(fp, "\t\t\t\t<west>%f</west>\n", west);
204 : VSIFPrintfL(fp, "\t\t\t</LatLonAltBox>\n");
205 : VSIFPrintfL(fp, "\t\t</Region>\n");*/
206 19 : VSIFPrintfL(fp, "\t\t<NetworkLink>\n");
207 19 : VSIFPrintfL(fp, "\t\t\t<open>1</open>\n");
208 19 : VSIFPrintfL(fp, "\t\t\t<Region>\n");
209 19 : VSIFPrintfL(fp, "\t\t\t\t<LatLonAltBox>\n");
210 19 : VSIFPrintfL(fp, "\t\t\t\t\t<north>%f</north>\n", north);
211 19 : VSIFPrintfL(fp, "\t\t\t\t\t<south>%f</south>\n", south);
212 19 : VSIFPrintfL(fp, "\t\t\t\t\t<east>%f</east>\n", east);
213 19 : VSIFPrintfL(fp, "\t\t\t\t\t<west>%f</west>\n", west);
214 19 : VSIFPrintfL(fp, "\t\t\t\t</LatLonAltBox>\n");
215 19 : VSIFPrintfL(fp, "\t\t\t\t<Lod>\n");
216 19 : VSIFPrintfL(fp, "\t\t\t\t\t<minLodPixels>%d</minLodPixels>\n",
217 : minlodpixels);
218 19 : VSIFPrintfL(fp, "\t\t\t\t\t<maxLodPixels>-1</maxLodPixels>\n");
219 19 : VSIFPrintfL(fp, "\t\t\t\t</Lod>\n");
220 19 : VSIFPrintfL(fp, "\t\t\t</Region>\n");
221 19 : VSIFPrintfL(fp, "\t\t\t<Link>\n");
222 19 : VSIFPrintfL(fp, "\t\t\t\t<href>0/0/0.kml</href>\n");
223 19 : VSIFPrintfL(fp, "\t\t\t\t<viewRefreshMode>onRegion</viewRefreshMode>\n");
224 19 : VSIFPrintfL(fp, "\t\t\t</Link>\n");
225 19 : VSIFPrintfL(fp, "\t\t</NetworkLink>\n");
226 19 : VSIFPrintfL(fp, "\t</Document>\n");
227 19 : VSIFPrintfL(fp, "</kml>\n");
228 :
229 19 : VSIFCloseL(fp);
230 19 : return TRUE;
231 : }
232 :
233 : /************************************************************************/
234 : /* GenerateChildKml() */
235 : /************************************************************************/
236 :
237 75 : static int GenerateChildKml(
238 : const std::string &filename, int zoom, int ix, int iy, double zoomxpixel,
239 : double zoomypixel, int dxsize, int dysize, double south, double west,
240 : int xsize, int ysize, int maxzoom, OGRCoordinateTransformation *poTransform,
241 : const std::string &fileExt, bool fixAntiMeridian, const char *pszAltitude,
242 : const char *pszAltitudeMode,
243 : const std::vector<std::pair<std::pair<int, int>, bool>> &childTiles)
244 : {
245 75 : double tnorth = south + zoomypixel * ((iy + 1) * dysize);
246 75 : double tsouth = south + zoomypixel * (iy * dysize);
247 75 : double teast = west + zoomxpixel * ((ix + 1) * dxsize);
248 75 : double twest = west + zoomxpixel * ix * dxsize;
249 :
250 75 : double upperleftT = twest;
251 75 : double lowerleftT = twest;
252 :
253 75 : double rightbottomT = tsouth;
254 75 : double leftbottomT = tsouth;
255 :
256 75 : double lefttopT = tnorth;
257 75 : double righttopT = tnorth;
258 :
259 75 : double lowerrightT = teast;
260 75 : double upperrightT = teast;
261 :
262 75 : if (poTransform)
263 : {
264 10 : poTransform->Transform(1, &twest, &tsouth);
265 10 : poTransform->Transform(1, &teast, &tnorth);
266 :
267 10 : poTransform->Transform(1, &upperleftT, &lefttopT);
268 10 : poTransform->Transform(1, &upperrightT, &righttopT);
269 10 : poTransform->Transform(1, &lowerrightT, &rightbottomT);
270 10 : poTransform->Transform(1, &lowerleftT, &leftbottomT);
271 : }
272 :
273 75 : if (fixAntiMeridian && teast < twest)
274 : {
275 3 : teast += 360;
276 3 : lowerrightT += 360;
277 3 : upperrightT += 360;
278 : }
279 :
280 150 : std::vector<int> xchildren;
281 150 : std::vector<int> ychildern;
282 :
283 75 : int minLodPixels = 128;
284 75 : if (zoom == 0)
285 : {
286 19 : minLodPixels = 1;
287 : }
288 :
289 75 : int maxLodPix = -1;
290 75 : if (zoom < maxzoom)
291 : {
292 16 : double zareasize = pow(2.0, (maxzoom - zoom - 1)) * dxsize;
293 16 : double zareasize1 = pow(2.0, (maxzoom - zoom - 1)) * dysize;
294 16 : xchildren.push_back(ix * 2);
295 16 : int tmp = ix * 2 + 1;
296 16 : int tmp1 = static_cast<int>(ceil(xsize / zareasize));
297 16 : if (tmp < tmp1)
298 : {
299 16 : xchildren.push_back(ix * 2 + 1);
300 : }
301 16 : ychildern.push_back(iy * 2);
302 16 : tmp = iy * 2 + 1;
303 16 : tmp1 = static_cast<int>(ceil(ysize / zareasize1));
304 16 : if (tmp < tmp1)
305 : {
306 16 : ychildern.push_back(iy * 2 + 1);
307 : }
308 16 : maxLodPix = 2048;
309 :
310 16 : bool hasChildKML = false;
311 60 : for (const auto &kv : childTiles)
312 : {
313 49 : if (kv.second)
314 : {
315 5 : hasChildKML = true;
316 5 : break;
317 : }
318 : }
319 16 : if (!hasChildKML)
320 : {
321 : // no child KML files, so don't expire this one at any zoom.
322 11 : maxLodPix = -1;
323 : }
324 : }
325 :
326 75 : VSILFILE *fp = VSIFOpenL(filename.c_str(), "wb");
327 75 : if (fp == nullptr)
328 : {
329 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot create %s",
330 : filename.c_str());
331 0 : return FALSE;
332 : }
333 :
334 75 : VSIFPrintfL(fp, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
335 75 : VSIFPrintfL(fp, "<kml xmlns=\"http://www.opengis.net/kml/2.2\" "
336 : "xmlns:gx=\"http://www.google.com/kml/ext/2.2\">\n");
337 75 : VSIFPrintfL(fp, "\t<Document>\n");
338 75 : VSIFPrintfL(fp, "\t\t<name>%d/%d/%d.kml</name>\n", zoom, ix, iy);
339 75 : VSIFPrintfL(fp, "\t\t<styleUrl>#hideChildrenStyle</styleUrl>\n");
340 75 : VSIFPrintfL(fp, "\t\t<Style id=\"hideChildrenStyle\">\n");
341 75 : VSIFPrintfL(fp, "\t\t\t<ListStyle id=\"hideChildren\">\n");
342 75 : VSIFPrintfL(fp, "\t\t\t\t<listItemType>checkHideChildren</listItemType>\n");
343 75 : VSIFPrintfL(fp, "\t\t\t</ListStyle>\n");
344 75 : VSIFPrintfL(fp, "\t\t</Style>\n");
345 75 : VSIFPrintfL(fp, "\t\t<Region>\n");
346 75 : VSIFPrintfL(fp, "\t\t\t<LatLonAltBox>\n");
347 75 : VSIFPrintfL(fp, "\t\t\t\t<north>%f</north>\n", tnorth);
348 75 : VSIFPrintfL(fp, "\t\t\t\t<south>%f</south>\n", tsouth);
349 75 : VSIFPrintfL(fp, "\t\t\t\t<east>%f</east>\n", teast);
350 75 : VSIFPrintfL(fp, "\t\t\t\t<west>%f</west>\n", twest);
351 75 : VSIFPrintfL(fp, "\t\t\t</LatLonAltBox>\n");
352 75 : VSIFPrintfL(fp, "\t\t\t<Lod>\n");
353 75 : VSIFPrintfL(fp, "\t\t\t\t<minLodPixels>%d</minLodPixels>\n", minLodPixels);
354 75 : VSIFPrintfL(fp, "\t\t\t\t<maxLodPixels>%d</maxLodPixels>\n", maxLodPix);
355 75 : VSIFPrintfL(fp, "\t\t\t</Lod>\n");
356 75 : VSIFPrintfL(fp, "\t\t</Region>\n");
357 75 : VSIFPrintfL(fp, "\t\t<GroundOverlay>\n");
358 75 : VSIFPrintfL(fp, "\t\t\t<drawOrder>%d</drawOrder>\n", zoom);
359 75 : VSIFPrintfL(fp, "\t\t\t<Icon>\n");
360 75 : VSIFPrintfL(fp, "\t\t\t\t<href>%d%s</href>\n", iy, fileExt.c_str());
361 75 : VSIFPrintfL(fp, "\t\t\t</Icon>\n");
362 :
363 75 : if (pszAltitude != nullptr)
364 : {
365 5 : VSIFPrintfL(fp, "\t\t\t<altitude>%s</altitude>\n", pszAltitude);
366 : }
367 75 : if (pszAltitudeMode != nullptr &&
368 5 : (strcmp(pszAltitudeMode, "clampToGround") == 0 ||
369 5 : strcmp(pszAltitudeMode, "absolute") == 0))
370 : {
371 5 : VSIFPrintfL(fp, "\t\t\t<altitudeMode>%s</altitudeMode>\n",
372 : pszAltitudeMode);
373 : }
374 70 : else if (pszAltitudeMode != nullptr &&
375 0 : (strcmp(pszAltitudeMode, "relativeToSeaFloor") == 0 ||
376 0 : strcmp(pszAltitudeMode, "clampToSeaFloor") == 0))
377 : {
378 0 : VSIFPrintfL(fp, "\t\t\t<gx:altitudeMode>%s</gx:altitudeMode>\n",
379 : pszAltitudeMode);
380 : }
381 :
382 : /* When possible, use <LatLonBox>. I've noticed otherwise that */
383 : /* if using <gx:LatLonQuad> with extents of the size of a country or */
384 : /* continent, the overlay is really bad placed in GoogleEarth */
385 75 : if (lowerleftT == upperleftT && lowerrightT == upperrightT &&
386 70 : leftbottomT == rightbottomT && righttopT == lefttopT)
387 : {
388 70 : VSIFPrintfL(fp, "\t\t\t<LatLonBox>\n");
389 70 : VSIFPrintfL(fp, "\t\t\t\t<north>%f</north>\n", tnorth);
390 70 : VSIFPrintfL(fp, "\t\t\t\t<south>%f</south>\n", tsouth);
391 70 : VSIFPrintfL(fp, "\t\t\t\t<east>%f</east>\n", teast);
392 70 : VSIFPrintfL(fp, "\t\t\t\t<west>%f</west>\n", twest);
393 70 : VSIFPrintfL(fp, "\t\t\t</LatLonBox>\n");
394 : }
395 : else
396 : {
397 5 : VSIFPrintfL(fp, "\t\t\t<gx:LatLonQuad>\n");
398 5 : VSIFPrintfL(fp, "\t\t\t\t<coordinates>\n");
399 5 : VSIFPrintfL(fp, "\t\t\t\t\t%f,%f,0\n", lowerleftT, leftbottomT);
400 5 : VSIFPrintfL(fp, "\t\t\t\t\t%f,%f,0\n", lowerrightT, rightbottomT);
401 5 : VSIFPrintfL(fp, "\t\t\t\t\t%f,%f,0\n", upperrightT, righttopT);
402 5 : VSIFPrintfL(fp, "\t\t\t\t\t%f,%f,0\n", upperleftT, lefttopT);
403 5 : VSIFPrintfL(fp, "\t\t\t\t</coordinates>\n");
404 5 : VSIFPrintfL(fp, "\t\t\t</gx:LatLonQuad>\n");
405 : }
406 75 : VSIFPrintfL(fp, "\t\t</GroundOverlay>\n");
407 :
408 131 : for (const auto &kv : childTiles)
409 : {
410 56 : int cx = kv.first.first;
411 56 : int cy = kv.first.second;
412 :
413 56 : double cnorth = south + zoomypixel / 2 * ((cy + 1) * dysize);
414 56 : double csouth = south + zoomypixel / 2 * (cy * dysize);
415 56 : double ceast = west + zoomxpixel / 2 * ((cx + 1) * dxsize);
416 56 : double cwest = west + zoomxpixel / 2 * cx * dxsize;
417 :
418 56 : if (poTransform)
419 : {
420 8 : poTransform->Transform(1, &cwest, &csouth);
421 8 : poTransform->Transform(1, &ceast, &cnorth);
422 : }
423 :
424 56 : if (fixAntiMeridian && ceast < cwest)
425 : {
426 2 : ceast += 360;
427 : }
428 :
429 56 : VSIFPrintfL(fp, "\t\t<NetworkLink>\n");
430 56 : VSIFPrintfL(fp, "\t\t\t<name>%d/%d/%d%s</name>\n", zoom + 1, cx, cy,
431 : fileExt.c_str());
432 56 : VSIFPrintfL(fp, "\t\t\t<Region>\n");
433 56 : VSIFPrintfL(fp, "\t\t\t\t<Lod>\n");
434 56 : VSIFPrintfL(fp, "\t\t\t\t\t<minLodPixels>128</minLodPixels>\n");
435 56 : VSIFPrintfL(fp, "\t\t\t\t\t<maxLodPixels>-1</maxLodPixels>\n");
436 56 : VSIFPrintfL(fp, "\t\t\t\t</Lod>\n");
437 56 : VSIFPrintfL(fp, "\t\t\t\t<LatLonAltBox>\n");
438 56 : VSIFPrintfL(fp, "\t\t\t\t\t<north>%f</north>\n", cnorth);
439 56 : VSIFPrintfL(fp, "\t\t\t\t\t<south>%f</south>\n", csouth);
440 56 : VSIFPrintfL(fp, "\t\t\t\t\t<east>%f</east>\n", ceast);
441 56 : VSIFPrintfL(fp, "\t\t\t\t\t<west>%f</west>\n", cwest);
442 56 : VSIFPrintfL(fp, "\t\t\t\t</LatLonAltBox>\n");
443 56 : VSIFPrintfL(fp, "\t\t\t</Region>\n");
444 56 : VSIFPrintfL(fp, "\t\t\t<Link>\n");
445 56 : VSIFPrintfL(fp, "\t\t\t\t<href>../../%d/%d/%d.kml</href>\n", zoom + 1,
446 : cx, cy);
447 56 : VSIFPrintfL(fp,
448 : "\t\t\t\t<viewRefreshMode>onRegion</viewRefreshMode>\n");
449 56 : VSIFPrintfL(fp, "\t\t\t\t<viewFormat/>\n");
450 56 : VSIFPrintfL(fp, "\t\t\t</Link>\n");
451 56 : VSIFPrintfL(fp, "\t\t</NetworkLink>\n");
452 : }
453 :
454 75 : VSIFPrintfL(fp, "\t</Document>\n");
455 75 : VSIFPrintfL(fp, "</kml>\n");
456 75 : VSIFCloseL(fp);
457 :
458 75 : return TRUE;
459 : }
460 :
461 : /************************************************************************/
462 : /* DetectTransparency() */
463 : /************************************************************************/
464 85 : int KmlSuperOverlayReadDataset::DetectTransparency(int rxsize, int rysize,
465 : int rx, int ry, int dxsize,
466 : int dysize,
467 : GDALDataset *poSrcDs)
468 : {
469 85 : int bands = poSrcDs->GetRasterCount();
470 85 : int rowOffset = rysize / dysize;
471 85 : int loopCount = rysize / rowOffset;
472 85 : int hasNoData = 0;
473 85 : std::vector<GByte> abyScanline(dxsize);
474 :
475 85 : int flags = 0;
476 425 : for (int band = 1; band <= bands; band++)
477 : {
478 340 : GDALRasterBand *poBand = poSrcDs->GetRasterBand(band);
479 340 : int noDataValue = static_cast<int>(poBand->GetNoDataValue(&hasNoData));
480 :
481 340 : if (band < 4 && hasNoData)
482 : {
483 0 : for (int row = 0; row < loopCount; row++)
484 : {
485 0 : int yOffset = ry + row * rowOffset;
486 0 : CPL_IGNORE_RET_VAL(poBand->RasterIO(
487 0 : GF_Read, rx, yOffset, rxsize, rowOffset, abyScanline.data(),
488 : dxsize, 1, GDT_Byte, 0, 0, nullptr));
489 0 : for (int i = 0; i < dxsize; i++)
490 : {
491 0 : if (abyScanline[i] == noDataValue)
492 : {
493 0 : flags |= KMLSO_ContainsTransparentPixels;
494 : }
495 : else
496 : {
497 0 : flags |= KMLSO_ContainsOpaquePixels;
498 : }
499 : }
500 : // shortcut - if there are both types of pixels, flags is as
501 : // full as it is going to get.
502 : // so no point continuing, skip to the next band
503 0 : if ((flags & KMLSO_ContainsTransparentPixels) &&
504 0 : (flags & KMLSO_ContainsOpaquePixels))
505 : {
506 0 : break;
507 : }
508 0 : }
509 : }
510 340 : else if (band == 4)
511 : {
512 5525 : for (int row = 0; row < loopCount; row++)
513 : {
514 5440 : int yOffset = ry + row * rowOffset;
515 5440 : CPL_IGNORE_RET_VAL(poBand->RasterIO(
516 5440 : GF_Read, rx, yOffset, rxsize, rowOffset, abyScanline.data(),
517 : dxsize, 1, GDT_Byte, 0, 0, nullptr));
518 1398080 : for (int i = 0; i < dxsize; i++)
519 : {
520 1392640 : if (abyScanline[i] == 255)
521 : {
522 696320 : flags |= KMLSO_ContainsOpaquePixels;
523 : }
524 696320 : else if (abyScanline[i] == 0)
525 : {
526 696320 : flags |= KMLSO_ContainsTransparentPixels;
527 : }
528 : else
529 : {
530 0 : flags |= KMLSO_ContainsPartiallyTransparentPixels;
531 : }
532 : }
533 : }
534 : }
535 : }
536 170 : return flags;
537 : }
538 :
539 : /************************************************************************/
540 : /* CreateCopy() */
541 : /************************************************************************/
542 :
543 2 : class KmlSuperOverlayDummyDataset final : public GDALDataset
544 : {
545 : public:
546 1 : KmlSuperOverlayDummyDataset() = default;
547 : ~KmlSuperOverlayDummyDataset() override;
548 : };
549 :
550 : KmlSuperOverlayDummyDataset::~KmlSuperOverlayDummyDataset() = default;
551 :
552 : static GDALDataset *
553 24 : KmlSuperOverlayCreateCopy(const char *pszFilename, GDALDataset *poSrcDS,
554 : CPL_UNUSED int bStrict, char **papszOptions,
555 : GDALProgressFunc pfnProgress, void *pProgressData)
556 : {
557 24 : bool isKmz = false;
558 :
559 24 : if (pfnProgress == nullptr)
560 0 : pfnProgress = GDALDummyProgress;
561 :
562 24 : int bands = poSrcDS->GetRasterCount();
563 24 : if (bands != 1 && bands != 3 && bands != 4)
564 3 : return nullptr;
565 :
566 : // correct the file and get the directory
567 21 : char *output_dir = nullptr;
568 42 : std::string osFilename;
569 21 : if (pszFilename == nullptr)
570 : {
571 0 : output_dir = CPLGetCurrentDir();
572 0 : osFilename = CPLFormFilenameSafe(output_dir, "doc", "kml");
573 : }
574 : else
575 : {
576 21 : osFilename = pszFilename;
577 21 : const std::string osExtension = CPLGetExtensionSafe(pszFilename);
578 21 : const char *extension = osExtension.c_str();
579 21 : if (!EQUAL(extension, "kml") && !EQUAL(extension, "kmz"))
580 : {
581 0 : CPLError(CE_Failure, CPLE_None,
582 : "File extension should be kml or kmz.");
583 0 : return nullptr;
584 : }
585 21 : if (EQUAL(extension, "kmz"))
586 : {
587 17 : isKmz = true;
588 : }
589 :
590 21 : output_dir = CPLStrdup(CPLGetPathSafe(pszFilename).c_str());
591 21 : if (strcmp(output_dir, "") == 0)
592 : {
593 0 : CPLFree(output_dir);
594 0 : output_dir = CPLGetCurrentDir();
595 : }
596 : }
597 21 : pszFilename = osFilename.c_str();
598 :
599 42 : CPLString outDir = output_dir ? output_dir : "";
600 21 : CPLFree(output_dir);
601 21 : output_dir = nullptr;
602 :
603 21 : VSILFILE *zipHandle = nullptr;
604 21 : if (isKmz)
605 : {
606 17 : outDir = "/vsizip/";
607 17 : outDir += pszFilename;
608 17 : zipHandle = VSIFOpenL(outDir, "wb");
609 17 : if (zipHandle == nullptr)
610 : {
611 2 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot create %s",
612 : pszFilename);
613 2 : return nullptr;
614 : }
615 : }
616 :
617 19 : GDALDriver *poOutputTileDriver = nullptr;
618 19 : GDALDriver *poJpegOutputTileDriver = nullptr;
619 19 : GDALDriver *poPngOutputTileDriver = nullptr;
620 19 : bool isAutoDriver = false;
621 19 : bool isJpegDriver = false;
622 :
623 : const char *pszFormat =
624 19 : CSLFetchNameValueDef(papszOptions, "FORMAT", "JPEG");
625 19 : if (EQUAL(pszFormat, "AUTO"))
626 : {
627 1 : isAutoDriver = true;
628 : poJpegOutputTileDriver =
629 1 : GetGDALDriverManager()->GetDriverByName("JPEG");
630 1 : poPngOutputTileDriver = GetGDALDriverManager()->GetDriverByName("PNG");
631 : }
632 : else
633 : {
634 18 : poOutputTileDriver = GetGDALDriverManager()->GetDriverByName(pszFormat);
635 18 : if (EQUAL(pszFormat, "JPEG"))
636 : {
637 15 : isJpegDriver = true;
638 : }
639 : }
640 :
641 19 : if ((!isAutoDriver && poOutputTileDriver == nullptr) ||
642 1 : (isAutoDriver && (poJpegOutputTileDriver == nullptr ||
643 : poPngOutputTileDriver == nullptr)))
644 : {
645 0 : CPLError(CE_Failure, CPLE_None, "Image export driver was not found..");
646 0 : if (zipHandle != nullptr)
647 : {
648 0 : VSIFCloseL(zipHandle);
649 0 : VSIUnlink(pszFilename);
650 : }
651 0 : return nullptr;
652 : }
653 :
654 19 : int xsize = poSrcDS->GetRasterXSize();
655 19 : int ysize = poSrcDS->GetRasterYSize();
656 :
657 19 : double north = 0.0;
658 19 : double south = 0.0;
659 19 : double east = 0.0;
660 19 : double west = 0.0;
661 :
662 19 : GDALGeoTransform gt;
663 :
664 19 : if (poSrcDS->GetGeoTransform(gt) == CE_None)
665 : {
666 19 : north = gt[3];
667 19 : south = gt[3] + gt[5] * ysize;
668 19 : east = gt[0] + gt[1] * xsize;
669 19 : west = gt[0];
670 : }
671 :
672 19 : std::unique_ptr<OGRCoordinateTransformation> poTransform;
673 19 : const auto poSrcSRS = poSrcDS->GetSpatialRef();
674 19 : if (poSrcSRS && poSrcSRS->IsProjected())
675 : {
676 4 : OGRSpatialReference poLatLong;
677 2 : poLatLong.SetWellKnownGeogCS("WGS84");
678 2 : poLatLong.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
679 :
680 2 : poTransform.reset(
681 : OGRCreateCoordinateTransformation(poSrcSRS, &poLatLong));
682 2 : if (poTransform != nullptr)
683 : {
684 2 : poTransform->Transform(1, &west, &south);
685 2 : poTransform->Transform(1, &east, &north);
686 : }
687 : }
688 :
689 : const bool fixAntiMeridian =
690 19 : CPLFetchBool(papszOptions, "FIX_ANTIMERIDIAN", false);
691 19 : if (fixAntiMeridian && east < west)
692 : {
693 1 : east += 360;
694 : }
695 :
696 : // Zoom levels of the pyramid.
697 19 : int maxzoom = 0;
698 : int tilexsize;
699 : int tileysize;
700 : // Let the longer side determine the max zoom level and x/y tilesizes.
701 19 : if (xsize >= ysize)
702 : {
703 19 : double dtilexsize = xsize;
704 25 : while (dtilexsize > 400) // calculate x tile size
705 : {
706 6 : dtilexsize = dtilexsize / 2;
707 6 : maxzoom++;
708 : }
709 19 : tilexsize = static_cast<int>(dtilexsize);
710 19 : tileysize = static_cast<int>(dtilexsize * ysize / xsize);
711 : }
712 : else
713 : {
714 0 : double dtileysize = ysize;
715 0 : while (dtileysize > 400) // calculate y tile size
716 : {
717 0 : dtileysize = dtileysize / 2;
718 0 : maxzoom++;
719 : }
720 :
721 0 : tileysize = static_cast<int>(dtileysize);
722 0 : tilexsize = static_cast<int>(dtileysize * xsize / ysize);
723 : }
724 :
725 38 : std::vector<double> zoomxpixels;
726 38 : std::vector<double> zoomypixels;
727 44 : for (int zoom = 0; zoom < maxzoom + 1; zoom++)
728 : {
729 25 : zoomxpixels.push_back(gt[1] * pow(2.0, (maxzoom - zoom)));
730 : // zoomypixels.push_back(abs(gt[5]) * pow(2.0, (maxzoom -
731 : // zoom)));
732 25 : zoomypixels.push_back(fabs(gt[5]) * pow(2.0, (maxzoom - zoom)));
733 : }
734 :
735 38 : std::vector<std::string> fileVector;
736 : int nRet;
737 :
738 19 : const char *pszOverlayName = CSLFetchNameValue(papszOptions, "NAME");
739 : const char *pszOverlayDescription =
740 19 : CSLFetchNameValue(papszOptions, "DESCRIPTION");
741 :
742 19 : if (isKmz)
743 : {
744 : std::string tmpFileName =
745 30 : CPLFormFilenameSafe(outDir, "doc.kml", nullptr);
746 15 : nRet = GenerateRootKml(tmpFileName.c_str(), pszFilename, north, south,
747 : east, west, static_cast<int>(tilexsize),
748 : pszOverlayName, pszOverlayDescription);
749 15 : fileVector.push_back(std::move(tmpFileName));
750 : }
751 : else
752 : {
753 4 : nRet = GenerateRootKml(pszFilename, pszFilename, north, south, east,
754 : west, static_cast<int>(tilexsize),
755 : pszOverlayName, pszOverlayDescription);
756 : }
757 :
758 19 : if (nRet == FALSE)
759 : {
760 0 : if (zipHandle != nullptr)
761 : {
762 0 : VSIFCloseL(zipHandle);
763 0 : VSIUnlink(pszFilename);
764 : }
765 0 : return nullptr;
766 : }
767 :
768 19 : const char *pszAltitude = CSLFetchNameValue(papszOptions, "ALTITUDE");
769 : const char *pszAltitudeMode =
770 19 : CSLFetchNameValue(papszOptions, "ALTITUDEMODE");
771 19 : if (pszAltitudeMode != nullptr)
772 : {
773 1 : if (strcmp(pszAltitudeMode, "clampToGround") == 0)
774 : {
775 0 : pszAltitudeMode = nullptr;
776 0 : pszAltitude = nullptr;
777 : }
778 1 : else if (strcmp(pszAltitudeMode, "absolute") == 0)
779 : {
780 1 : if (pszAltitude == nullptr)
781 : {
782 0 : CPLError(CE_Warning, CPLE_AppDefined,
783 : "Using ALTITUDE=0 as default value");
784 0 : pszAltitude = "0";
785 : }
786 : }
787 0 : else if (strcmp(pszAltitudeMode, "relativeToSeaFloor") == 0)
788 : {
789 : /* nothing to do */
790 : }
791 0 : else if (strcmp(pszAltitudeMode, "clampToSeaFloor") == 0)
792 : {
793 0 : pszAltitude = nullptr;
794 : }
795 : else
796 : {
797 0 : CPLError(CE_Warning, CPLE_AppDefined,
798 : "Ignoring unhandled value of ALTITUDEMODE");
799 0 : pszAltitudeMode = nullptr;
800 0 : pszAltitude = nullptr;
801 : }
802 : }
803 :
804 : int zoom;
805 19 : int nTotalTiles = 0;
806 19 : int nTileCount = 0;
807 :
808 44 : for (zoom = maxzoom; zoom >= 0; --zoom)
809 : {
810 25 : const int rmaxxsize = tilexsize * (1 << (maxzoom - zoom));
811 25 : const int rmaxysize = tileysize * (1 << (maxzoom - zoom));
812 :
813 25 : const int xloop = std::max(1, static_cast<int>(xsize / rmaxxsize));
814 25 : const int yloop = std::max(1, static_cast<int>(ysize / rmaxysize));
815 25 : nTotalTiles += xloop * yloop;
816 : }
817 :
818 : // {(x, y): [((childx, childy), hasChildKML), ...], ...}
819 : std::map<std::pair<int, int>,
820 : std::vector<std::pair<std::pair<int, int>, bool>>>
821 38 : childTiles;
822 : std::map<std::pair<int, int>,
823 : std::vector<std::pair<std::pair<int, int>, bool>>>
824 38 : currentTiles;
825 19 : std::pair<int, int> childXYKey;
826 19 : std::pair<int, int> parentXYKey;
827 :
828 19 : const char *pszPathSep = VSIGetDirectorySeparator(outDir.c_str());
829 :
830 44 : for (zoom = maxzoom; zoom >= 0; --zoom)
831 : {
832 25 : const int rmaxxsize = tilexsize * (1 << (maxzoom - zoom));
833 25 : const int rmaxysize = tileysize * (1 << (maxzoom - zoom));
834 :
835 25 : const int xloop = std::max(1, static_cast<int>(xsize / rmaxxsize));
836 25 : const int yloop = std::max(1, static_cast<int>(ysize / rmaxysize));
837 :
838 50 : std::stringstream zoomStr;
839 25 : zoomStr << zoom;
840 :
841 50 : std::string zoomDir = outDir;
842 25 : zoomDir += pszPathSep;
843 25 : zoomDir += zoomStr.str();
844 25 : VSIMkdir(zoomDir.c_str(), 0775);
845 :
846 64 : for (int ix = 0; ix < xloop; ix++)
847 : {
848 39 : int rxsize = static_cast<int>(rmaxxsize);
849 39 : int rx = static_cast<int>(ix * rmaxxsize);
850 39 : int dxsize = static_cast<int>(rxsize / rmaxxsize * tilexsize);
851 :
852 78 : std::stringstream ixStr;
853 39 : ixStr << ix;
854 :
855 39 : zoomDir = outDir;
856 39 : zoomDir += pszPathSep;
857 39 : zoomDir += zoomStr.str();
858 39 : zoomDir += pszPathSep;
859 39 : zoomDir += ixStr.str();
860 39 : VSIMkdir(zoomDir.c_str(), 0775);
861 :
862 154 : for (int iy = 0; iy < yloop; iy++)
863 : {
864 115 : int rysize = static_cast<int>(rmaxysize);
865 115 : int ry = static_cast<int>(ysize - (iy * rmaxysize)) - rysize;
866 115 : int dysize = static_cast<int>(rysize / rmaxysize * tileysize);
867 :
868 115 : std::stringstream iyStr;
869 115 : iyStr << iy;
870 :
871 115 : if (isAutoDriver)
872 : {
873 85 : int flags = KmlSuperOverlayReadDataset::DetectTransparency(
874 : rxsize, rysize, rx, ry, dxsize, dysize, poSrcDS);
875 85 : if (flags & (KmlSuperOverlayReadDataset::
876 : KMLSO_ContainsPartiallyTransparentPixels |
877 : KmlSuperOverlayReadDataset::
878 : KMLSO_ContainsTransparentPixels))
879 : {
880 45 : if (!(flags &
881 : (KmlSuperOverlayReadDataset::
882 : KMLSO_ContainsPartiallyTransparentPixels |
883 : KmlSuperOverlayReadDataset::
884 : KMLSO_ContainsOpaquePixels)))
885 : {
886 : // don't bother creating empty tiles
887 40 : continue;
888 : }
889 5 : poOutputTileDriver = poPngOutputTileDriver;
890 5 : isJpegDriver = false;
891 : }
892 : else
893 : {
894 40 : poOutputTileDriver = poJpegOutputTileDriver;
895 40 : isJpegDriver = true;
896 : }
897 : }
898 :
899 150 : std::string fileExt = ".jpg";
900 75 : if (isJpegDriver == false)
901 : {
902 12 : fileExt = ".png";
903 : }
904 150 : std::string filename = zoomDir;
905 75 : filename += pszPathSep;
906 75 : filename += iyStr.str();
907 75 : filename += fileExt;
908 75 : if (isKmz)
909 : {
910 19 : fileVector.push_back(filename);
911 : }
912 :
913 75 : GenerateTiles(filename, zoom, rxsize, rysize, ix, iy, rx, ry,
914 : dxsize, dysize, bands, poSrcDS,
915 : poOutputTileDriver, isJpegDriver);
916 150 : std::string childKmlfile = zoomDir;
917 75 : childKmlfile += pszPathSep;
918 75 : childKmlfile += iyStr.str();
919 75 : childKmlfile += ".kml";
920 75 : if (isKmz)
921 : {
922 19 : fileVector.push_back(childKmlfile);
923 : }
924 :
925 75 : double tmpSouth = gt[3] + gt[5] * ysize;
926 75 : double zoomxpix = zoomxpixels[zoom];
927 75 : double zoomypix = zoomypixels[zoom];
928 75 : if (zoomxpix == 0)
929 : {
930 0 : zoomxpix = 1;
931 : }
932 :
933 75 : if (zoomypix == 0)
934 : {
935 0 : zoomypix = 1;
936 : }
937 :
938 75 : childXYKey = std::make_pair(ix, iy);
939 75 : parentXYKey = std::make_pair(ix / 2, iy / 2);
940 :
941 : // only create child KML if there are child tiles
942 75 : bool hasChildKML = !childTiles[childXYKey].empty();
943 75 : if (!currentTiles.count(parentXYKey))
944 : {
945 35 : currentTiles[parentXYKey] =
946 70 : std::vector<std::pair<std::pair<int, int>, bool>>();
947 : }
948 150 : currentTiles[parentXYKey].push_back(
949 75 : std::make_pair(std::make_pair(ix, iy), hasChildKML));
950 75 : GenerateChildKml(childKmlfile, zoom, ix, iy, zoomxpix, zoomypix,
951 75 : dxsize, dysize, tmpSouth, gt[0], xsize, ysize,
952 : maxzoom, poTransform.get(), fileExt,
953 : fixAntiMeridian, pszAltitude, pszAltitudeMode,
954 75 : childTiles[childXYKey]);
955 :
956 75 : nTileCount++;
957 75 : pfnProgress(1.0 * nTileCount / nTotalTiles, "", pProgressData);
958 : }
959 : }
960 25 : childTiles = currentTiles;
961 25 : currentTiles.clear();
962 : }
963 :
964 19 : if (zipHandle != nullptr)
965 : {
966 15 : VSIFCloseL(zipHandle);
967 : }
968 :
969 38 : GDALOpenInfo oOpenInfo(pszFilename, GA_ReadOnly);
970 : auto poDS = std::unique_ptr<GDALDataset>(
971 38 : KmlSuperOverlayReadDataset::Open(&oOpenInfo));
972 19 : if (!poDS)
973 1 : poDS = std::make_unique<KmlSuperOverlayDummyDataset>();
974 19 : return poDS.release();
975 : }
976 :
977 : /************************************************************************/
978 : /* KMLRemoveSlash() */
979 : /************************************************************************/
980 :
981 : /* replace "a/b/../c" pattern by "a/c" */
982 127 : static std::string KMLRemoveSlash(const char *pszPathIn)
983 : {
984 254 : std::string osRet(pszPathIn);
985 :
986 : while (true)
987 : {
988 135 : size_t nSlashDotDot = osRet.find("/../");
989 135 : if (nSlashDotDot == std::string::npos || nSlashDotDot == 0)
990 : break;
991 8 : size_t nPos = nSlashDotDot - 1;
992 16 : while (nPos > 0 && osRet[nPos] != '/')
993 8 : --nPos;
994 8 : if (nPos == 0)
995 0 : break;
996 16 : osRet = osRet.substr(0, nPos + 1) +
997 24 : osRet.substr(nSlashDotDot + strlen("/../"));
998 8 : }
999 127 : return osRet;
1000 : }
1001 :
1002 : /************************************************************************/
1003 : /* KmlSuperOverlayReadDataset() */
1004 : /************************************************************************/
1005 :
1006 48 : KmlSuperOverlayReadDataset::KmlSuperOverlayReadDataset()
1007 : {
1008 48 : m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
1009 48 : m_oSRS.importFromWkt(SRS_WKT_WGS84_LAT_LONG);
1010 48 : }
1011 :
1012 : /************************************************************************/
1013 : /* ~KmlSuperOverlayReadDataset() */
1014 : /************************************************************************/
1015 :
1016 96 : KmlSuperOverlayReadDataset::~KmlSuperOverlayReadDataset()
1017 :
1018 : {
1019 48 : if (psRoot != nullptr)
1020 38 : CPLDestroyXMLNode(psRoot);
1021 48 : KmlSuperOverlayReadDataset::CloseDependentDatasets();
1022 96 : }
1023 :
1024 : /************************************************************************/
1025 : /* CloseDependentDatasets() */
1026 : /************************************************************************/
1027 :
1028 48 : int KmlSuperOverlayReadDataset::CloseDependentDatasets()
1029 : {
1030 48 : int bRet = FALSE;
1031 48 : if (poDSIcon)
1032 : {
1033 38 : CPLString l_osFilename(poDSIcon->GetDescription());
1034 38 : poDSIcon.reset();
1035 38 : VSIUnlink(l_osFilename);
1036 38 : bRet = TRUE;
1037 : }
1038 :
1039 48 : LinkedDataset *psCur = psFirstLink;
1040 48 : psFirstLink = nullptr;
1041 48 : psLastLink = nullptr;
1042 :
1043 61 : while (psCur != nullptr)
1044 : {
1045 13 : LinkedDataset *psNext = psCur->psNext;
1046 13 : if (psCur->poDS != nullptr)
1047 : {
1048 13 : if (psCur->poDS->nRefCount == 1)
1049 13 : bRet = TRUE;
1050 13 : GDALClose(psCur->poDS);
1051 : }
1052 13 : delete psCur;
1053 13 : psCur = psNext;
1054 : }
1055 :
1056 48 : if (!m_apoOverviewDS.empty())
1057 : {
1058 8 : bRet = TRUE;
1059 8 : m_apoOverviewDS.clear();
1060 : }
1061 :
1062 48 : return bRet;
1063 : }
1064 :
1065 : /************************************************************************/
1066 : /* GetSpatialRef() */
1067 : /************************************************************************/
1068 :
1069 1 : const OGRSpatialReference *KmlSuperOverlayReadDataset::GetSpatialRef() const
1070 :
1071 : {
1072 1 : return &m_oSRS;
1073 : }
1074 :
1075 : /************************************************************************/
1076 : /* GetGeoTransform() */
1077 : /************************************************************************/
1078 :
1079 1 : CPLErr KmlSuperOverlayReadDataset::GetGeoTransform(GDALGeoTransform >) const
1080 : {
1081 1 : gt = m_gt;
1082 1 : return CE_None;
1083 : }
1084 :
1085 : /************************************************************************/
1086 : /* KmlSuperOverlayRasterBand() */
1087 : /************************************************************************/
1088 :
1089 192 : KmlSuperOverlayRasterBand::KmlSuperOverlayRasterBand(
1090 192 : KmlSuperOverlayReadDataset *poDSIn, int /* nBand*/)
1091 : {
1092 192 : nRasterXSize = poDSIn->nRasterXSize;
1093 192 : nRasterYSize = poDSIn->nRasterYSize;
1094 192 : eDataType = GDT_Byte;
1095 192 : nBlockXSize = 256;
1096 192 : nBlockYSize = 256;
1097 192 : }
1098 :
1099 : /************************************************************************/
1100 : /* IReadBlock() */
1101 : /************************************************************************/
1102 :
1103 8 : CPLErr KmlSuperOverlayRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff,
1104 : void *pData)
1105 : {
1106 8 : int nXOff = nBlockXOff * nBlockXSize;
1107 8 : int nYOff = nBlockYOff * nBlockYSize;
1108 8 : int nXSize = nBlockXSize;
1109 8 : int nYSize = nBlockYSize;
1110 8 : if (nXOff + nXSize > nRasterXSize)
1111 4 : nXSize = nRasterXSize - nXOff;
1112 8 : if (nYOff + nYSize > nRasterYSize)
1113 8 : nYSize = nRasterYSize - nYOff;
1114 :
1115 : GDALRasterIOExtraArg sExtraArg;
1116 8 : INIT_RASTERIO_EXTRA_ARG(sExtraArg);
1117 :
1118 16 : return IRasterIO(GF_Read, nXOff, nYOff, nXSize, nYSize, pData, nXSize,
1119 16 : nYSize, eDataType, 1, nBlockXSize, &sExtraArg);
1120 : }
1121 :
1122 : /************************************************************************/
1123 : /* GetColorInterpretation() */
1124 : /************************************************************************/
1125 :
1126 15 : GDALColorInterp KmlSuperOverlayRasterBand::GetColorInterpretation()
1127 : {
1128 15 : return static_cast<GDALColorInterp>(GCI_RedBand + nBand - 1);
1129 : }
1130 :
1131 : /************************************************************************/
1132 : /* IRasterIO() */
1133 : /************************************************************************/
1134 :
1135 50 : CPLErr KmlSuperOverlayRasterBand::IRasterIO(
1136 : GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
1137 : void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
1138 : GSpacing nPixelSpace, GSpacing nLineSpace, GDALRasterIOExtraArg *psExtraArg)
1139 : {
1140 : KmlSuperOverlayReadDataset *poGDS =
1141 50 : cpl::down_cast<KmlSuperOverlayReadDataset *>(poDS);
1142 :
1143 50 : return poGDS->IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData,
1144 : nBufXSize, nBufYSize, eBufType, 1, &nBand,
1145 50 : nPixelSpace, nLineSpace, 0, psExtraArg);
1146 : }
1147 :
1148 : /************************************************************************/
1149 : /* GetOverviewCount() */
1150 : /************************************************************************/
1151 :
1152 31 : int KmlSuperOverlayRasterBand::GetOverviewCount()
1153 : {
1154 : KmlSuperOverlayReadDataset *poGDS =
1155 31 : cpl::down_cast<KmlSuperOverlayReadDataset *>(poDS);
1156 :
1157 31 : return static_cast<int>(poGDS->m_apoOverviewDS.size());
1158 : }
1159 :
1160 : /************************************************************************/
1161 : /* GetOverview() */
1162 : /************************************************************************/
1163 :
1164 37 : GDALRasterBand *KmlSuperOverlayRasterBand::GetOverview(int iOvr)
1165 : {
1166 : KmlSuperOverlayReadDataset *poGDS =
1167 37 : cpl::down_cast<KmlSuperOverlayReadDataset *>(poDS);
1168 :
1169 37 : if (iOvr < 0 || iOvr >= static_cast<int>(poGDS->m_apoOverviewDS.size()))
1170 0 : return nullptr;
1171 :
1172 37 : return poGDS->m_apoOverviewDS[iOvr]->GetRasterBand(nBand);
1173 : }
1174 :
1175 : /************************************************************************/
1176 : /* KmlSuperOverlayGetBoundingBox() */
1177 : /************************************************************************/
1178 :
1179 118 : static bool KmlSuperOverlayGetBoundingBox(const CPLXMLNode *psNode,
1180 : std::array<double, 4> &adfExtents)
1181 : {
1182 118 : const CPLXMLNode *psBox = CPLGetXMLNode(psNode, "LatLonBox");
1183 118 : if (!psBox)
1184 77 : psBox = CPLGetXMLNode(psNode, "LatLonAltBox");
1185 118 : if (psBox)
1186 : {
1187 116 : const char *pszNorth = CPLGetXMLValue(psBox, "north", nullptr);
1188 116 : const char *pszSouth = CPLGetXMLValue(psBox, "south", nullptr);
1189 116 : const char *pszEast = CPLGetXMLValue(psBox, "east", nullptr);
1190 116 : const char *pszWest = CPLGetXMLValue(psBox, "west", nullptr);
1191 116 : if (pszNorth && pszSouth && pszEast && pszWest)
1192 : {
1193 116 : adfExtents[0] = CPLAtof(pszWest);
1194 116 : adfExtents[1] = CPLAtof(pszSouth);
1195 116 : adfExtents[2] = CPLAtof(pszEast);
1196 116 : adfExtents[3] = CPLAtof(pszNorth);
1197 :
1198 116 : return true;
1199 : }
1200 : }
1201 : else
1202 : {
1203 2 : const CPLXMLNode *psLatLonQuad = CPLGetXMLNode(psNode, "gx:LatLonQuad");
1204 2 : if (psLatLonQuad)
1205 : {
1206 : const CPLStringList aosTuples(CSLTokenizeString2(
1207 2 : CPLGetXMLValue(psLatLonQuad, "coordinates", ""), " \t\n\r", 0));
1208 2 : if (aosTuples.size() == 4)
1209 : {
1210 : const CPLStringList aosLL(
1211 2 : CSLTokenizeString2(aosTuples[0], ",", 0));
1212 : const CPLStringList aosLR(
1213 2 : CSLTokenizeString2(aosTuples[1], ",", 0));
1214 : const CPLStringList aosUR(
1215 2 : CSLTokenizeString2(aosTuples[2], ",", 0));
1216 : const CPLStringList aosUL(
1217 2 : CSLTokenizeString2(aosTuples[3], ",", 0));
1218 4 : if (aosLL.size() >= 2 && aosLR.size() >= 2 &&
1219 2 : aosUR.size() >= 2 && aosUL.size() >= 2 &&
1220 2 : strcmp(aosLL[0], aosUL[0]) == 0 &&
1221 1 : strcmp(aosLL[1], aosLR[1]) == 0 &&
1222 5 : strcmp(aosLR[0], aosUR[0]) == 0 &&
1223 1 : strcmp(aosUR[1], aosUL[1]) == 0)
1224 : {
1225 1 : adfExtents[0] = CPLAtof(aosLL[0]);
1226 1 : adfExtents[1] = CPLAtof(aosLL[1]);
1227 1 : adfExtents[2] = CPLAtof(aosUR[0]);
1228 1 : adfExtents[3] = CPLAtof(aosUR[1]);
1229 1 : return true;
1230 : }
1231 : }
1232 : }
1233 : }
1234 :
1235 1 : return false;
1236 : }
1237 :
1238 : /************************************************************************/
1239 : /* IRasterIO() */
1240 : /************************************************************************/
1241 :
1242 : class SubImageDesc
1243 : {
1244 : public:
1245 : GDALDataset *poDS = nullptr;
1246 : std::array<double, 4> adfExtents = {0, 0, 0, 0};
1247 : };
1248 :
1249 91 : CPLErr KmlSuperOverlayReadDataset::IRasterIO(
1250 : GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
1251 : void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
1252 : int nBandCount, BANDMAP_TYPE panBandMap, GSpacing nPixelSpace,
1253 : GSpacing nLineSpace, GSpacing nBandSpace, GDALRasterIOExtraArg *psExtraArg)
1254 : {
1255 91 : if (eRWFlag == GF_Write)
1256 0 : return CE_Failure;
1257 :
1258 91 : if (bIsOvr)
1259 : {
1260 : GDALRasterIOExtraArg sExtraArgs;
1261 1 : GDALCopyRasterIOExtraArg(&sExtraArgs, psExtraArg);
1262 1 : const int nOvrFactor = poParent->nFactor / nFactor;
1263 1 : if (sExtraArgs.bFloatingPointWindowValidity)
1264 : {
1265 0 : sExtraArgs.dfXOff *= nOvrFactor;
1266 0 : sExtraArgs.dfYOff *= nOvrFactor;
1267 0 : sExtraArgs.dfXSize *= nOvrFactor;
1268 0 : sExtraArgs.dfYSize *= nOvrFactor;
1269 : }
1270 1 : return poParent->IRasterIO(
1271 : eRWFlag, nXOff * nOvrFactor, nYOff * nOvrFactor,
1272 : nXSize * nOvrFactor, nYSize * nOvrFactor, pData, nBufXSize,
1273 : nBufYSize, eBufType, nBandCount, panBandMap, nPixelSpace,
1274 1 : nLineSpace, nBandSpace, &sExtraArgs);
1275 : }
1276 :
1277 90 : double dfXOff = 1.0 * nXOff / nFactor;
1278 90 : double dfYOff = 1.0 * nYOff / nFactor;
1279 90 : double dfXSize = 1.0 * nXSize / nFactor;
1280 90 : double dfYSize = 1.0 * nYSize / nFactor;
1281 :
1282 90 : int nIconCount = poDSIcon->GetRasterCount();
1283 :
1284 90 : if (nBufXSize > dfXSize || nBufYSize > dfYSize)
1285 : {
1286 28 : const double dfRequestXMin = m_gt[0] + nXOff * m_gt[1];
1287 28 : const double dfRequestXMax = m_gt[0] + (nXOff + nXSize) * m_gt[1];
1288 28 : const double dfRequestYMin = m_gt[3] + (nYOff + nYSize) * m_gt[5];
1289 28 : const double dfRequestYMax = m_gt[3] + nYOff * m_gt[5];
1290 :
1291 28 : const CPLXMLNode *psIter = psDocument->psChild;
1292 28 : std::vector<SubImageDesc> aoImages;
1293 28 : const double dfXRes = m_gt[1] * nFactor;
1294 28 : const double dfYRes = -m_gt[5] * nFactor;
1295 28 : double dfNewXRes = dfXRes;
1296 28 : double dfNewYRes = dfYRes;
1297 :
1298 242 : while (psIter != nullptr)
1299 : {
1300 214 : const CPLXMLNode *psRegion = nullptr;
1301 214 : const CPLXMLNode *psLink = nullptr;
1302 214 : std::array<double, 4> adfExtents = {0, 0, 0, 0};
1303 214 : const char *pszHref = nullptr;
1304 642 : if (psIter->eType == CXT_Element &&
1305 214 : strcmp(psIter->pszValue, "NetworkLink") == 0 &&
1306 74 : (psRegion = CPLGetXMLNode(psIter, "Region")) != nullptr &&
1307 74 : (psLink = CPLGetXMLNode(psIter, "Link")) != nullptr &&
1308 502 : KmlSuperOverlayGetBoundingBox(psRegion, adfExtents) &&
1309 74 : (pszHref = CPLGetXMLValue(psLink, "href", nullptr)) != nullptr)
1310 : {
1311 74 : if (dfRequestXMin < adfExtents[2] &&
1312 74 : dfRequestXMax > adfExtents[0] &&
1313 206 : dfRequestYMin < adfExtents[3] &&
1314 58 : dfRequestYMax > adfExtents[1])
1315 : {
1316 39 : CPLString osSubFilename;
1317 39 : if (STARTS_WITH(pszHref, "http"))
1318 : osSubFilename =
1319 0 : CPLSPrintf("/vsicurl_streaming/%s", pszHref);
1320 : else
1321 : {
1322 39 : const char *pszBaseFilename = osFilename.c_str();
1323 39 : if (EQUAL(CPLGetExtensionSafe(pszBaseFilename).c_str(),
1324 39 : "kmz") &&
1325 0 : !STARTS_WITH(pszBaseFilename, "/vsizip/"))
1326 : {
1327 0 : osSubFilename = "/vsizip/";
1328 0 : osSubFilename += CPLGetPathSafe(pszBaseFilename);
1329 0 : osSubFilename += "/";
1330 0 : osSubFilename += pszHref;
1331 : }
1332 : else
1333 : {
1334 117 : if ((STARTS_WITH(pszHref, "../../") &&
1335 39 : CPLHasPathTraversal(pszHref +
1336 78 : strlen("../../"))) ||
1337 39 : (!STARTS_WITH(pszHref, "../../") &&
1338 0 : CPLHasPathTraversal(pszHref)))
1339 : {
1340 0 : CPLError(CE_Failure, CPLE_AppDefined,
1341 : "Path traversal detected in %s",
1342 : pszHref);
1343 0 : return CE_Failure;
1344 : }
1345 39 : osSubFilename = CPLFormFilenameSafe(
1346 78 : CPLGetPathSafe(pszBaseFilename).c_str(),
1347 39 : pszHref, nullptr);
1348 : }
1349 39 : osSubFilename = KMLRemoveSlash(osSubFilename);
1350 : }
1351 :
1352 39 : KmlSuperOverlayReadDataset *poSubImageDS = nullptr;
1353 39 : if (EQUAL(CPLGetExtensionSafe(osSubFilename).c_str(),
1354 : "kml"))
1355 : {
1356 39 : KmlSuperOverlayReadDataset *poRoot =
1357 39 : poParent ? poParent : this;
1358 : LinkedDataset *psLinkDS =
1359 39 : poRoot->oMapChildren[osSubFilename];
1360 39 : if (psLinkDS == nullptr)
1361 : {
1362 13 : if (poRoot->oMapChildren.size() == 64)
1363 : {
1364 0 : psLinkDS = poRoot->psLastLink;
1365 0 : CPLAssert(psLinkDS);
1366 0 : poRoot->oMapChildren.erase(
1367 0 : psLinkDS->osSubFilename);
1368 0 : GDALClose(psLinkDS->poDS);
1369 0 : if (psLinkDS->psPrev != nullptr)
1370 : {
1371 0 : poRoot->psLastLink = psLinkDS->psPrev;
1372 0 : psLinkDS->psPrev->psNext = nullptr;
1373 : }
1374 : else
1375 : {
1376 0 : CPLAssert(psLinkDS == poRoot->psFirstLink);
1377 0 : poRoot->psFirstLink = nullptr;
1378 0 : poRoot->psLastLink = nullptr;
1379 : }
1380 : }
1381 : else
1382 13 : psLinkDS = new LinkedDataset();
1383 :
1384 13 : poRoot->oMapChildren[osSubFilename] = psLinkDS;
1385 : poSubImageDS =
1386 13 : cpl::down_cast<KmlSuperOverlayReadDataset *>(
1387 : KmlSuperOverlayReadDataset::Open(
1388 : osSubFilename, poRoot));
1389 13 : if (poSubImageDS)
1390 13 : poSubImageDS->MarkAsShared();
1391 : else
1392 0 : CPLDebug("KMLSuperOverlay", "Cannot open %s",
1393 : osSubFilename.c_str());
1394 13 : psLinkDS->osSubFilename = osSubFilename;
1395 13 : psLinkDS->poDS = poSubImageDS;
1396 13 : psLinkDS->psPrev = nullptr;
1397 13 : psLinkDS->psNext = poRoot->psFirstLink;
1398 13 : if (poRoot->psFirstLink != nullptr)
1399 : {
1400 8 : CPLAssert(poRoot->psFirstLink->psPrev ==
1401 : nullptr);
1402 8 : poRoot->psFirstLink->psPrev = psLinkDS;
1403 : }
1404 : else
1405 5 : poRoot->psLastLink = psLinkDS;
1406 13 : poRoot->psFirstLink = psLinkDS;
1407 : }
1408 : else
1409 : {
1410 26 : poSubImageDS = psLinkDS->poDS;
1411 26 : if (psLinkDS != poRoot->psFirstLink)
1412 : {
1413 26 : if (psLinkDS == poRoot->psLastLink)
1414 : {
1415 25 : poRoot->psLastLink = psLinkDS->psPrev;
1416 25 : CPLAssert(poRoot->psLastLink != nullptr);
1417 25 : poRoot->psLastLink->psNext = nullptr;
1418 : }
1419 : else
1420 1 : psLinkDS->psNext->psPrev = psLinkDS->psPrev;
1421 26 : CPLAssert(psLinkDS->psPrev != nullptr);
1422 26 : psLinkDS->psPrev->psNext = psLinkDS->psNext;
1423 26 : psLinkDS->psPrev = nullptr;
1424 26 : poRoot->psFirstLink->psPrev = psLinkDS;
1425 26 : psLinkDS->psNext = poRoot->psFirstLink;
1426 26 : poRoot->psFirstLink = psLinkDS;
1427 : }
1428 : }
1429 : }
1430 39 : if (poSubImageDS)
1431 : {
1432 39 : int nSubImageXSize = poSubImageDS->GetRasterXSize();
1433 39 : int nSubImageYSize = poSubImageDS->GetRasterYSize();
1434 39 : adfExtents[0] = poSubImageDS->m_gt[0];
1435 39 : adfExtents[1] = poSubImageDS->m_gt[3] +
1436 39 : nSubImageYSize * poSubImageDS->m_gt[5];
1437 39 : adfExtents[2] = poSubImageDS->m_gt[0] +
1438 39 : nSubImageXSize * poSubImageDS->m_gt[1];
1439 39 : adfExtents[3] = poSubImageDS->m_gt[3];
1440 :
1441 : double dfSubXRes =
1442 39 : (adfExtents[2] - adfExtents[0]) / nSubImageXSize;
1443 : double dfSubYRes =
1444 39 : (adfExtents[3] - adfExtents[1]) / nSubImageYSize;
1445 :
1446 39 : if (dfSubXRes < dfNewXRes)
1447 19 : dfNewXRes = dfSubXRes;
1448 39 : if (dfSubYRes < dfNewYRes)
1449 19 : dfNewYRes = dfSubYRes;
1450 :
1451 39 : SubImageDesc oImageDesc;
1452 39 : oImageDesc.poDS = poSubImageDS;
1453 39 : poSubImageDS->Reference();
1454 39 : oImageDesc.adfExtents = adfExtents;
1455 39 : aoImages.push_back(oImageDesc);
1456 : }
1457 39 : CPL_IGNORE_RET_VAL(osSubFilename);
1458 : }
1459 : }
1460 214 : psIter = psIter->psNext;
1461 : }
1462 :
1463 28 : if (dfNewXRes < dfXRes || dfNewYRes < dfYRes)
1464 : {
1465 19 : const double dfXFactor = dfXRes / dfNewXRes;
1466 19 : const double dfYFactor = dfYRes / dfNewYRes;
1467 : auto poVRTDS = std::make_unique<VRTDataset>(
1468 0 : static_cast<int>(nRasterXSize * dfXFactor + 0.5),
1469 19 : static_cast<int>(nRasterYSize * dfYFactor + 0.5));
1470 :
1471 95 : for (int iBandIdx = 0; iBandIdx < 4; iBandIdx++)
1472 : {
1473 76 : poVRTDS->AddBand(GDT_Byte, nullptr);
1474 :
1475 : auto poVRTBand = static_cast<VRTSourcedRasterBand *>(
1476 76 : poVRTDS->GetRasterBand(iBandIdx + 1));
1477 76 : const int nBand = iBandIdx + 1;
1478 76 : if (nBand <= nIconCount || (nIconCount == 1 && nBand != 4))
1479 : {
1480 74 : poVRTBand->AddSimpleSource(
1481 : poDSIcon->GetRasterBand(nBand <= nIconCount ? nBand
1482 : : 1),
1483 74 : 0, 0, nRasterXSize, nRasterYSize, 0, 0,
1484 74 : poVRTDS->GetRasterXSize(), poVRTDS->GetRasterYSize(),
1485 : nullptr, VRT_NODATA_UNSET);
1486 : }
1487 : else
1488 : {
1489 2 : poVRTBand->AddComplexSource(
1490 2 : poDSIcon->GetRasterBand(1), 0, 0, nRasterXSize,
1491 2 : nRasterYSize, 0, 0, poVRTDS->GetRasterXSize(),
1492 2 : poVRTDS->GetRasterYSize(), VRT_NODATA_UNSET, 0, 255);
1493 : }
1494 : }
1495 :
1496 58 : for (const auto &oImage : aoImages)
1497 : {
1498 : const int nDstXOff = static_cast<int>(
1499 39 : (oImage.adfExtents[0] - m_gt[0]) / dfNewXRes + 0.5);
1500 : const int nDstYOff = static_cast<int>(
1501 39 : (m_gt[3] - oImage.adfExtents[3]) / dfNewYRes + 0.5);
1502 : const int nDstXSize = static_cast<int>(
1503 39 : (oImage.adfExtents[2] - oImage.adfExtents[0]) / dfNewXRes +
1504 39 : 0.5);
1505 : const int nDstYSize = static_cast<int>(
1506 39 : (oImage.adfExtents[3] - oImage.adfExtents[1]) / dfNewYRes +
1507 39 : 0.5);
1508 :
1509 39 : const int nSrcBandCount = oImage.poDS->GetRasterCount();
1510 195 : for (int iBandIdx = 0; iBandIdx < 4; iBandIdx++)
1511 : {
1512 156 : const int nBand = iBandIdx + 1;
1513 : auto poVRTBand = static_cast<VRTSourcedRasterBand *>(
1514 156 : poVRTDS->GetRasterBand(iBandIdx + 1));
1515 :
1516 156 : if (nBand <= nSrcBandCount ||
1517 0 : (nSrcBandCount == 1 && nBand != 4))
1518 : {
1519 156 : poVRTBand->AddSimpleSource(
1520 156 : oImage.poDS->GetRasterBand(
1521 : nBand <= nSrcBandCount ? nBand : 1),
1522 156 : 0, 0, oImage.poDS->GetRasterXSize(),
1523 156 : oImage.poDS->GetRasterYSize(), nDstXOff, nDstYOff,
1524 : nDstXSize, nDstYSize, nullptr, VRT_NODATA_UNSET);
1525 : }
1526 : else
1527 : {
1528 0 : poVRTBand->AddComplexSource(
1529 0 : oImage.poDS->GetRasterBand(1), 0, 0,
1530 0 : oImage.poDS->GetRasterXSize(),
1531 0 : oImage.poDS->GetRasterYSize(), nDstXOff, nDstYOff,
1532 : nDstXSize, nDstYSize, VRT_NODATA_UNSET, 0, 255);
1533 : }
1534 : }
1535 : }
1536 :
1537 19 : int nReqXOff = static_cast<int>(dfXOff * dfXFactor + 0.5);
1538 19 : int nReqYOff = static_cast<int>(dfYOff * dfYFactor + 0.5);
1539 19 : int nReqXSize = static_cast<int>(dfXSize * dfXFactor + 0.5);
1540 19 : int nReqYSize = static_cast<int>(dfYSize * dfYFactor + 0.5);
1541 19 : if (nReqXOff + nReqXSize > poVRTDS->GetRasterXSize())
1542 0 : nReqXSize = poVRTDS->GetRasterXSize() - nReqXOff;
1543 19 : if (nReqYOff + nReqYSize > poVRTDS->GetRasterYSize())
1544 0 : nReqYSize = poVRTDS->GetRasterYSize() - nReqYOff;
1545 :
1546 : GDALRasterIOExtraArg sExtraArgs;
1547 19 : INIT_RASTERIO_EXTRA_ARG(sExtraArgs);
1548 : // cppcheck-suppress redundantAssignment
1549 19 : sExtraArgs.eResampleAlg = psExtraArg->eResampleAlg;
1550 19 : CPLErr eErr = poVRTDS->RasterIO(
1551 : eRWFlag, nReqXOff, nReqYOff, nReqXSize, nReqYSize, pData,
1552 : nBufXSize, nBufYSize, eBufType, nBandCount, panBandMap,
1553 : nPixelSpace, nLineSpace, nBandSpace, &sExtraArgs);
1554 :
1555 58 : for (auto &oImage : aoImages)
1556 : {
1557 39 : oImage.poDS->Dereference();
1558 : }
1559 :
1560 19 : return eErr;
1561 : }
1562 : }
1563 :
1564 71 : GDALProgressFunc pfnProgressGlobal = psExtraArg->pfnProgress;
1565 71 : void *pProgressDataGlobal = psExtraArg->pProgressData;
1566 71 : CPLErr eErr = CE_None;
1567 :
1568 148 : for (int iBandIdx = 0; iBandIdx < nBandCount && eErr == CE_None; iBandIdx++)
1569 : {
1570 77 : int nBand = panBandMap[iBandIdx];
1571 :
1572 77 : if ((nIconCount > 1 || nBand == 4) && nBand > nIconCount)
1573 : {
1574 0 : GByte nVal = (nBand == 4) ? 255 : 0;
1575 0 : for (int j = 0; j < nBufYSize; j++)
1576 : {
1577 0 : GDALCopyWords(&nVal, GDT_Byte, 0,
1578 0 : static_cast<GByte *>(pData) + j * nLineSpace +
1579 0 : iBandIdx * nBandSpace,
1580 : eBufType, static_cast<int>(nPixelSpace),
1581 : nBufXSize);
1582 : }
1583 0 : continue;
1584 : }
1585 :
1586 77 : int nIconBand = (nIconCount == 1) ? 1 : nBand;
1587 :
1588 77 : int nReqXOff = static_cast<int>(dfXOff + 0.5);
1589 77 : int nReqYOff = static_cast<int>(dfYOff + 0.5);
1590 77 : int nReqXSize = static_cast<int>(dfXSize + 0.5);
1591 77 : int nReqYSize = static_cast<int>(dfYSize + 0.5);
1592 77 : if (nReqXOff + nReqXSize > poDSIcon->GetRasterXSize())
1593 0 : nReqXSize = poDSIcon->GetRasterXSize() - nReqXOff;
1594 77 : if (nReqYOff + nReqYSize > poDSIcon->GetRasterYSize())
1595 0 : nReqYSize = poDSIcon->GetRasterYSize() - nReqYOff;
1596 :
1597 : GDALRasterIOExtraArg sExtraArgs;
1598 77 : INIT_RASTERIO_EXTRA_ARG(sExtraArgs);
1599 : // cppcheck-suppress redundantAssignment
1600 77 : sExtraArgs.eResampleAlg = psExtraArg->eResampleAlg;
1601 77 : sExtraArgs.pfnProgress = GDALScaledProgress;
1602 154 : sExtraArgs.pProgressData = GDALCreateScaledProgress(
1603 77 : 1.0 * iBandIdx / nBandCount, 1.0 * (iBandIdx + 1) / nBandCount,
1604 : pfnProgressGlobal, pProgressDataGlobal);
1605 :
1606 77 : eErr = poDSIcon->GetRasterBand(nIconBand)->RasterIO(
1607 : eRWFlag, nReqXOff, nReqYOff, nReqXSize, nReqYSize,
1608 77 : static_cast<GByte *>(pData) + nBandSpace * iBandIdx, nBufXSize,
1609 : nBufYSize, eBufType, nPixelSpace, nLineSpace, &sExtraArgs);
1610 :
1611 77 : GDALDestroyScaledProgress(sExtraArgs.pProgressData);
1612 : }
1613 :
1614 71 : psExtraArg->pfnProgress = pfnProgressGlobal;
1615 71 : psExtraArg->pProgressData = pProgressDataGlobal;
1616 :
1617 71 : return eErr;
1618 : }
1619 :
1620 : /************************************************************************/
1621 : /* KmlSuperOverlayFindRegionStart() */
1622 : /************************************************************************/
1623 :
1624 1462 : static int KmlSuperOverlayFindRegionStartInternal(CPLXMLNode *psNode,
1625 : CPLXMLNode **ppsRegion,
1626 : CPLXMLNode **ppsDocument,
1627 : CPLXMLNode **ppsGroundOverlay,
1628 : CPLXMLNode **ppsLink)
1629 : {
1630 1462 : CPLXMLNode *psRegion = nullptr;
1631 1462 : CPLXMLNode *psLink = nullptr;
1632 1462 : CPLXMLNode *psGroundOverlay = nullptr;
1633 2947 : if (strcmp(psNode->pszValue, "NetworkLink") == 0 &&
1634 1485 : (psRegion = CPLGetXMLNode(psNode, "Region")) != nullptr &&
1635 23 : (psLink = CPLGetXMLNode(psNode, "Link")) != nullptr)
1636 : {
1637 23 : *ppsRegion = psRegion;
1638 23 : *ppsLink = psLink;
1639 23 : return TRUE;
1640 : }
1641 4211 : if ((strcmp(psNode->pszValue, "Document") == 0 ||
1642 1333 : strcmp(psNode->pszValue, "Folder") == 0) &&
1643 2821 : (psRegion = CPLGetXMLNode(psNode, "Region")) != nullptr &&
1644 49 : (psGroundOverlay = CPLGetXMLNode(psNode, "GroundOverlay")) != nullptr)
1645 : {
1646 49 : *ppsDocument = psNode;
1647 49 : *ppsRegion = psRegion;
1648 49 : *ppsGroundOverlay = psGroundOverlay;
1649 49 : return TRUE;
1650 : }
1651 :
1652 1390 : CPLXMLNode *psIter = psNode->psChild;
1653 4094 : while (psIter != nullptr)
1654 : {
1655 2799 : if (psIter->eType == CXT_Element)
1656 : {
1657 1246 : if (KmlSuperOverlayFindRegionStartInternal(
1658 1246 : psIter, ppsRegion, ppsDocument, ppsGroundOverlay, ppsLink))
1659 95 : return TRUE;
1660 : }
1661 :
1662 2704 : psIter = psIter->psNext;
1663 : }
1664 :
1665 1295 : return FALSE;
1666 : }
1667 :
1668 108 : static int KmlSuperOverlayFindRegionStart(CPLXMLNode *psNode,
1669 : CPLXMLNode **ppsRegion,
1670 : CPLXMLNode **ppsDocument,
1671 : CPLXMLNode **ppsGroundOverlay,
1672 : CPLXMLNode **ppsLink)
1673 : {
1674 108 : CPLXMLNode *psIter = psNode;
1675 252 : while (psIter != nullptr)
1676 : {
1677 216 : if (psIter->eType == CXT_Element)
1678 : {
1679 216 : if (KmlSuperOverlayFindRegionStartInternal(
1680 216 : psIter, ppsRegion, ppsDocument, ppsGroundOverlay, ppsLink))
1681 72 : return TRUE;
1682 : }
1683 :
1684 144 : psIter = psIter->psNext;
1685 : }
1686 :
1687 36 : return FALSE;
1688 : }
1689 :
1690 : /************************************************************************/
1691 : /* Identify() */
1692 : /************************************************************************/
1693 :
1694 59329 : int KmlSuperOverlayReadDataset::Identify(GDALOpenInfo *poOpenInfo)
1695 :
1696 : {
1697 59329 : const char *pszExt = poOpenInfo->osExtension.c_str();
1698 59329 : if (EQUAL(pszExt, "kmz"))
1699 143 : return -1;
1700 59186 : if (poOpenInfo->nHeaderBytes == 0)
1701 54545 : return FALSE;
1702 4641 : if (
1703 : #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
1704 4641 : !EQUAL(pszExt, "kml") ||
1705 : #endif
1706 111 : strstr(reinterpret_cast<const char *>(poOpenInfo->pabyHeader),
1707 : "<kml") == nullptr)
1708 4530 : return FALSE;
1709 :
1710 245 : for (int i = 0; i < 2; i++)
1711 : {
1712 : // Leave below variable here as the TryToIngest() at end might
1713 : // invalidate it
1714 178 : const char *pszText =
1715 : reinterpret_cast<const char *>(poOpenInfo->pabyHeader);
1716 178 : if (strstr(pszText, "<NetworkLink>") != nullptr &&
1717 28 : strstr(pszText, "<Region>") != nullptr &&
1718 28 : strstr(pszText, "<Link>") != nullptr)
1719 18 : return TRUE;
1720 :
1721 160 : if (strstr(pszText, "<Document>") != nullptr &&
1722 22 : strstr(pszText, "<Region>") != nullptr &&
1723 18 : strstr(pszText, "<GroundOverlay>") != nullptr)
1724 18 : return TRUE;
1725 :
1726 142 : if (strstr(pszText, "<GroundOverlay>") != nullptr &&
1727 8 : strstr(pszText, "<Icon>") != nullptr &&
1728 8 : strstr(pszText, "<href>") != nullptr &&
1729 8 : (strstr(pszText, "<LatLonBox>") != nullptr ||
1730 2 : strstr(pszText, "<gx:LatLonQuad>") != nullptr))
1731 8 : return TRUE;
1732 :
1733 134 : if (i == 0 && !poOpenInfo->TryToIngest(1024 * 10))
1734 0 : return FALSE;
1735 : }
1736 :
1737 67 : return -1;
1738 : }
1739 :
1740 : /************************************************************************/
1741 : /* Open() */
1742 : /************************************************************************/
1743 :
1744 111 : GDALDataset *KmlSuperOverlayReadDataset::Open(GDALOpenInfo *poOpenInfo)
1745 :
1746 : {
1747 111 : if (Identify(poOpenInfo) == FALSE)
1748 0 : return nullptr;
1749 :
1750 111 : return Open(poOpenInfo->pszFilename);
1751 : }
1752 :
1753 : /************************************************************************/
1754 : /* KmlSuperOverlayLoadIcon() */
1755 : /************************************************************************/
1756 :
1757 : #define BUFFER_SIZE 20000000
1758 :
1759 : static std::unique_ptr<GDALDataset>
1760 38 : KmlSuperOverlayLoadIcon(const char *pszBaseFilename, const char *pszIcon)
1761 : {
1762 76 : const std::string osExt = CPLGetExtensionSafe(pszIcon);
1763 38 : const char *pszExt = osExt.c_str();
1764 38 : if (!EQUAL(pszExt, "png") && !EQUAL(pszExt, "jpg") &&
1765 0 : !EQUAL(pszExt, "jpeg"))
1766 : {
1767 0 : return nullptr;
1768 : }
1769 :
1770 76 : CPLString osSubFilename;
1771 38 : if (STARTS_WITH(pszIcon, "http"))
1772 0 : osSubFilename = CPLSPrintf("/vsicurl_streaming/%s", pszIcon);
1773 : else
1774 : {
1775 38 : if (CPLHasPathTraversal(pszIcon))
1776 : {
1777 0 : CPLError(CE_Failure, CPLE_AppDefined,
1778 : "Path traversal detected in %s", pszIcon);
1779 0 : return nullptr;
1780 : }
1781 38 : osSubFilename = CPLFormFilenameSafe(
1782 76 : CPLGetPathSafe(pszBaseFilename).c_str(), pszIcon, nullptr);
1783 38 : osSubFilename = KMLRemoveSlash(osSubFilename);
1784 : }
1785 :
1786 38 : VSILFILE *fp = VSIFOpenL(osSubFilename, "rb");
1787 38 : if (fp == nullptr)
1788 : {
1789 0 : return nullptr;
1790 : }
1791 38 : GByte *pabyBuffer = static_cast<GByte *>(VSIMalloc(BUFFER_SIZE));
1792 38 : if (pabyBuffer == nullptr)
1793 : {
1794 0 : VSIFCloseL(fp);
1795 0 : return nullptr;
1796 : }
1797 38 : const size_t nRead = VSIFReadL(pabyBuffer, 1, BUFFER_SIZE, fp);
1798 38 : VSIFCloseL(fp);
1799 38 : if (nRead == BUFFER_SIZE)
1800 : {
1801 0 : CPLFree(pabyBuffer);
1802 0 : return nullptr;
1803 : }
1804 :
1805 38 : osSubFilename = VSIMemGenerateHiddenFilename("kmlsuperoverlay");
1806 38 : VSIFCloseL(VSIFileFromMemBuffer(osSubFilename, pabyBuffer, nRead, TRUE));
1807 :
1808 : auto poDSIcon = std::unique_ptr<GDALDataset>(GDALDataset::Open(
1809 76 : osSubFilename, GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR));
1810 38 : if (!poDSIcon)
1811 : {
1812 0 : VSIUnlink(osSubFilename);
1813 0 : return nullptr;
1814 : }
1815 :
1816 38 : return poDSIcon;
1817 : }
1818 :
1819 : /************************************************************************/
1820 : /* KmlSuperOverlayComputeDepth() */
1821 : /************************************************************************/
1822 :
1823 35 : static bool KmlSuperOverlayComputeDepth(const std::string &osFilename,
1824 : CPLXMLNode *psDocument, int &nLevel)
1825 : {
1826 35 : CPLXMLNode *psIter = psDocument->psChild;
1827 210 : while (psIter != nullptr)
1828 : {
1829 185 : const char *pszHref = nullptr;
1830 555 : if (psIter->eType == CXT_Element &&
1831 185 : strcmp(psIter->pszValue, "NetworkLink") == 0 &&
1832 380 : CPLGetXMLNode(psIter, "Region") != nullptr &&
1833 10 : (pszHref = CPLGetXMLValue(psIter, "Link.href", nullptr)) != nullptr)
1834 : {
1835 10 : if (EQUAL(CPLGetExtensionSafe(pszHref).c_str(), "kml"))
1836 : {
1837 10 : CPLString osSubFilename;
1838 10 : if (STARTS_WITH(pszHref, "http"))
1839 : osSubFilename =
1840 0 : CPLSPrintf("/vsicurl_streaming/%s", pszHref);
1841 : else
1842 : {
1843 30 : if ((STARTS_WITH(pszHref, "../../") &&
1844 20 : CPLHasPathTraversal(pszHref + strlen("../../"))) ||
1845 10 : (!STARTS_WITH(pszHref, "../../") &&
1846 0 : CPLHasPathTraversal(pszHref)))
1847 : {
1848 0 : CPLError(CE_Failure, CPLE_AppDefined,
1849 : "Path traversal detected in %s", pszHref);
1850 0 : return false;
1851 : }
1852 :
1853 10 : osSubFilename = CPLFormFilenameSafe(
1854 20 : CPLGetPathSafe(osFilename.c_str()).c_str(), pszHref,
1855 10 : nullptr);
1856 10 : osSubFilename = KMLRemoveSlash(osSubFilename);
1857 : }
1858 :
1859 10 : VSILFILE *fp = VSIFOpenL(osSubFilename, "rb");
1860 10 : if (fp != nullptr)
1861 : {
1862 : char *pszBuffer = static_cast<char *>(
1863 10 : VSI_MALLOC_VERBOSE(BUFFER_SIZE + 1));
1864 10 : if (pszBuffer == nullptr)
1865 : {
1866 0 : VSIFCloseL(fp);
1867 0 : return false;
1868 : }
1869 : const size_t nRead =
1870 10 : VSIFReadL(pszBuffer, 1, BUFFER_SIZE, fp);
1871 10 : pszBuffer[nRead] = '\0';
1872 10 : VSIFCloseL(fp);
1873 10 : if (nRead == BUFFER_SIZE)
1874 : {
1875 0 : CPLFree(pszBuffer);
1876 : }
1877 : else
1878 : {
1879 10 : CPLXMLNode *psNode = CPLParseXMLString(pszBuffer);
1880 10 : CPLFree(pszBuffer);
1881 10 : if (psNode != nullptr)
1882 : {
1883 10 : CPLXMLNode *psRegion = nullptr;
1884 10 : CPLXMLNode *psNewDocument = nullptr;
1885 10 : CPLXMLNode *psGroundOverlay = nullptr;
1886 10 : CPLXMLNode *psLink = nullptr;
1887 10 : if (KmlSuperOverlayFindRegionStart(
1888 : psNode, &psRegion, &psNewDocument,
1889 10 : &psGroundOverlay, &psLink) &&
1890 10 : psNewDocument != nullptr && nLevel < 20)
1891 : {
1892 10 : nLevel++;
1893 10 : if (!KmlSuperOverlayComputeDepth(
1894 : osSubFilename, psNewDocument, nLevel))
1895 : {
1896 0 : CPLDestroyXMLNode(psNode);
1897 0 : return false;
1898 : }
1899 : }
1900 10 : CPLDestroyXMLNode(psNode);
1901 10 : break;
1902 : }
1903 : }
1904 : }
1905 : }
1906 : }
1907 175 : psIter = psIter->psNext;
1908 : }
1909 35 : return true;
1910 : }
1911 :
1912 : /************************************************************************/
1913 : /* KmlSingleDocRasterDataset */
1914 : /************************************************************************/
1915 :
1916 : class KmlSingleDocRasterRasterBand;
1917 :
1918 : struct KmlSingleDocRasterTilesDesc
1919 : {
1920 : int nMaxJ_i; /* i index at which a tile with max j is realized */
1921 : int nMaxJ_j; /* j index at which a tile with max j is realized */
1922 : int nMaxI_i; /* i index at which a tile with max i is realized */
1923 : int nMaxI_j; /* j index at which a tile with max i is realized */
1924 : char szExtJ[4]; /* extension of tile at which max j is realized */
1925 : char szExtI[4]; /* extension of tile at which max i is realized */
1926 : };
1927 :
1928 : class KmlSingleDocRasterDataset final : public GDALDataset
1929 : {
1930 : friend class KmlSingleDocRasterRasterBand;
1931 : OGRSpatialReference m_oSRS{};
1932 : CPLString osDirname{};
1933 : CPLString osNominalExt{};
1934 : std::unique_ptr<GDALDataset> poCurTileDS{};
1935 : std::array<double, 4> adfGlobalExtents = {0, 0, 0, 0};
1936 : GDALGeoTransform m_gt{};
1937 : std::vector<std::unique_ptr<KmlSingleDocRasterDataset>> m_apoOverviews{};
1938 : std::vector<KmlSingleDocRasterTilesDesc> aosDescs{};
1939 : int nLevel = 0;
1940 : int nTileSize = 0;
1941 : bool bHasBuiltOverviews = false;
1942 : bool bLockOtherBands = false;
1943 :
1944 : protected:
1945 : int CloseDependentDatasets() override;
1946 :
1947 : public:
1948 : KmlSingleDocRasterDataset();
1949 : ~KmlSingleDocRasterDataset() override;
1950 :
1951 1 : CPLErr GetGeoTransform(GDALGeoTransform >) const override
1952 : {
1953 1 : gt = m_gt;
1954 1 : return CE_None;
1955 : }
1956 :
1957 1 : const OGRSpatialReference *GetSpatialRef() const override
1958 : {
1959 1 : return &m_oSRS;
1960 : }
1961 :
1962 : void BuildOverviews();
1963 :
1964 : static GDALDataset *Open(const char *pszFilename,
1965 : const CPLString &osFilename, CPLXMLNode *psNode);
1966 : };
1967 :
1968 : /************************************************************************/
1969 : /* KmlSingleDocRasterRasterBand */
1970 : /************************************************************************/
1971 :
1972 : class KmlSingleDocRasterRasterBand final : public GDALRasterBand
1973 : {
1974 : public:
1975 : KmlSingleDocRasterRasterBand(KmlSingleDocRasterDataset *poDS, int nBand);
1976 :
1977 : CPLErr IReadBlock(int, int, void *) override;
1978 : GDALColorInterp GetColorInterpretation() override;
1979 :
1980 : int GetOverviewCount() override;
1981 : GDALRasterBand *GetOverview(int) override;
1982 : };
1983 :
1984 : /************************************************************************/
1985 : /* KmlSingleDocRasterDataset() */
1986 : /************************************************************************/
1987 :
1988 2 : KmlSingleDocRasterDataset::KmlSingleDocRasterDataset()
1989 : {
1990 2 : m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
1991 2 : m_oSRS.importFromWkt(SRS_WKT_WGS84_LAT_LONG);
1992 2 : }
1993 :
1994 : /************************************************************************/
1995 : /* ~KmlSingleDocRasterDataset() */
1996 : /************************************************************************/
1997 :
1998 4 : KmlSingleDocRasterDataset::~KmlSingleDocRasterDataset()
1999 : {
2000 2 : KmlSingleDocRasterDataset::CloseDependentDatasets();
2001 4 : }
2002 :
2003 : /************************************************************************/
2004 : /* CloseDependentDatasets() */
2005 : /************************************************************************/
2006 :
2007 2 : int KmlSingleDocRasterDataset::CloseDependentDatasets()
2008 : {
2009 2 : int bRet = FALSE;
2010 :
2011 2 : if (poCurTileDS)
2012 : {
2013 2 : bRet = TRUE;
2014 2 : poCurTileDS.reset();
2015 : }
2016 2 : if (!m_apoOverviews.empty())
2017 : {
2018 1 : bRet = TRUE;
2019 1 : m_apoOverviews.clear();
2020 : }
2021 :
2022 2 : return bRet;
2023 : }
2024 :
2025 : /************************************************************************/
2026 : /* KmlSingleDocGetDimensions() */
2027 : /************************************************************************/
2028 :
2029 2 : static bool KmlSingleDocGetDimensions(const CPLString &osDirname,
2030 : const KmlSingleDocRasterTilesDesc &oDesc,
2031 : int nLevel, int nTileSize, int &nXSize,
2032 : int &nYSize, int &nBands, int &bHasCT)
2033 : {
2034 : std::string osImageFilename = CPLFormFilenameSafe(
2035 : osDirname,
2036 2 : CPLSPrintf("kml_image_L%d_%d_%d", nLevel, oDesc.nMaxJ_j, oDesc.nMaxJ_i),
2037 4 : oDesc.szExtJ);
2038 : auto poImageDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
2039 4 : osImageFilename.c_str(), GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR));
2040 2 : if (!poImageDS)
2041 : {
2042 0 : return false;
2043 : }
2044 : int nRightXSize;
2045 2 : int nBottomYSize = poImageDS->GetRasterYSize();
2046 2 : nBands = poImageDS->GetRasterCount();
2047 3 : bHasCT = (nBands == 1 &&
2048 1 : poImageDS->GetRasterBand(1)->GetColorTable() != nullptr);
2049 2 : if (oDesc.nMaxJ_j == oDesc.nMaxI_j && oDesc.nMaxJ_i == oDesc.nMaxI_i)
2050 : {
2051 2 : nRightXSize = poImageDS->GetRasterXSize();
2052 : }
2053 : else
2054 : {
2055 : osImageFilename =
2056 0 : CPLFormFilenameSafe(osDirname,
2057 : CPLSPrintf("kml_image_L%d_%d_%d", nLevel,
2058 0 : oDesc.nMaxI_j, oDesc.nMaxI_i),
2059 0 : oDesc.szExtI);
2060 0 : poImageDS.reset(GDALDataset::Open(
2061 : osImageFilename.c_str(), GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR));
2062 0 : if (!poImageDS)
2063 : {
2064 0 : return false;
2065 : }
2066 0 : nRightXSize = poImageDS->GetRasterXSize();
2067 : }
2068 :
2069 2 : nXSize = nRightXSize + oDesc.nMaxI_i * nTileSize;
2070 2 : nYSize = nBottomYSize + oDesc.nMaxJ_j * nTileSize;
2071 2 : return (nXSize > 0 && nYSize > 0);
2072 : }
2073 :
2074 : /************************************************************************/
2075 : /* BuildOverviews() */
2076 : /************************************************************************/
2077 :
2078 2 : void KmlSingleDocRasterDataset::BuildOverviews()
2079 : {
2080 2 : if (bHasBuiltOverviews)
2081 1 : return;
2082 1 : bHasBuiltOverviews = TRUE;
2083 :
2084 2 : for (int k = 2; k <= static_cast<int>(aosDescs.size()); k++)
2085 : {
2086 : const KmlSingleDocRasterTilesDesc &oDesc =
2087 1 : aosDescs[aosDescs.size() - k];
2088 1 : int nXSize = 0;
2089 1 : int nYSize = 0;
2090 1 : int nTileBands = 0;
2091 1 : int bHasCT = FALSE;
2092 1 : if (!KmlSingleDocGetDimensions(
2093 1 : osDirname, oDesc, static_cast<int>(aosDescs.size()) - k + 1,
2094 : nTileSize, nXSize, nYSize, nTileBands, bHasCT))
2095 : {
2096 0 : break;
2097 : }
2098 :
2099 2 : auto poOvrDS = std::make_unique<KmlSingleDocRasterDataset>();
2100 1 : poOvrDS->nRasterXSize = nXSize;
2101 1 : poOvrDS->nRasterYSize = nYSize;
2102 1 : poOvrDS->nLevel = static_cast<int>(aosDescs.size()) - k + 1;
2103 1 : poOvrDS->nTileSize = nTileSize;
2104 1 : poOvrDS->osDirname = osDirname;
2105 1 : poOvrDS->osNominalExt = oDesc.szExtI;
2106 1 : poOvrDS->m_gt[0] = adfGlobalExtents[0];
2107 1 : poOvrDS->m_gt[1] =
2108 1 : (adfGlobalExtents[2] - adfGlobalExtents[0]) / poOvrDS->nRasterXSize;
2109 1 : poOvrDS->m_gt[2] = 0.0;
2110 1 : poOvrDS->m_gt[3] = adfGlobalExtents[3];
2111 1 : poOvrDS->m_gt[4] = 0.0;
2112 2 : poOvrDS->m_gt[5] = -(adfGlobalExtents[3] - adfGlobalExtents[1]) /
2113 1 : poOvrDS->nRasterXSize;
2114 5 : for (int iBand = 1; iBand <= nBands; iBand++)
2115 8 : poOvrDS->SetBand(iBand,
2116 4 : std::make_unique<KmlSingleDocRasterRasterBand>(
2117 8 : poOvrDS.get(), iBand));
2118 1 : poOvrDS->SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE");
2119 :
2120 1 : m_apoOverviews.push_back(std::move(poOvrDS));
2121 : }
2122 : }
2123 :
2124 : /************************************************************************/
2125 : /* KmlSingleDocRasterRasterBand() */
2126 : /************************************************************************/
2127 :
2128 8 : KmlSingleDocRasterRasterBand::KmlSingleDocRasterRasterBand(
2129 8 : KmlSingleDocRasterDataset *poDSIn, int nBandIn)
2130 : {
2131 8 : poDS = poDSIn;
2132 8 : nBand = nBandIn;
2133 8 : nBlockXSize = poDSIn->nTileSize;
2134 8 : nBlockYSize = poDSIn->nTileSize;
2135 8 : eDataType = GDT_Byte;
2136 8 : }
2137 :
2138 : /************************************************************************/
2139 : /* IReadBlock() */
2140 : /************************************************************************/
2141 :
2142 12 : CPLErr KmlSingleDocRasterRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff,
2143 : void *pImage)
2144 : {
2145 : KmlSingleDocRasterDataset *poGDS =
2146 12 : cpl::down_cast<KmlSingleDocRasterDataset *>(poDS);
2147 : const std::string osImageFilename =
2148 : CPLFormFilenameSafe(poGDS->osDirname,
2149 : CPLSPrintf("kml_image_L%d_%d_%d", poGDS->nLevel,
2150 : nBlockYOff, nBlockXOff),
2151 24 : poGDS->osNominalExt);
2152 22 : if (poGDS->poCurTileDS == nullptr ||
2153 10 : strcmp(CPLGetFilename(poGDS->poCurTileDS->GetDescription()),
2154 : CPLGetFilename(osImageFilename.c_str())) != 0)
2155 : {
2156 6 : CPLErrorHandlerPusher oErrorHandler(CPLQuietErrorHandler);
2157 3 : poGDS->poCurTileDS.reset(
2158 : GDALDataset::Open(osImageFilename.c_str(), GDAL_OF_RASTER));
2159 : }
2160 12 : GDALDataset *poImageDS = poGDS->poCurTileDS.get();
2161 12 : if (poImageDS == nullptr)
2162 : {
2163 0 : memset(pImage, 0, static_cast<size_t>(nBlockXSize) * nBlockYSize);
2164 0 : return CE_None;
2165 : }
2166 12 : int nXSize = poImageDS->GetRasterXSize();
2167 12 : int nYSize = poImageDS->GetRasterYSize();
2168 :
2169 12 : int nReqXSize = nBlockXSize;
2170 12 : if (nBlockXOff * nBlockXSize + nReqXSize > nRasterXSize)
2171 8 : nReqXSize = nRasterXSize - nBlockXOff * nBlockXSize;
2172 12 : int nReqYSize = nBlockYSize;
2173 12 : if (nBlockYOff * nBlockYSize + nReqYSize > nRasterYSize)
2174 12 : nReqYSize = nRasterYSize - nBlockYOff * nBlockYSize;
2175 :
2176 12 : if (nXSize != nReqXSize || nYSize != nReqYSize)
2177 : {
2178 0 : CPLDebug("KMLSUPEROVERLAY", "Tile %s, dimensions %dx%d, expected %dx%d",
2179 : osImageFilename.c_str(), nXSize, nYSize, nReqXSize, nReqYSize);
2180 0 : return CE_Failure;
2181 : }
2182 :
2183 12 : CPLErr eErr = CE_Failure;
2184 12 : if (poImageDS->GetRasterCount() == 1)
2185 : {
2186 : GDALColorTable *poColorTable =
2187 4 : poImageDS->GetRasterBand(1)->GetColorTable();
2188 4 : if (nBand == 4 && poColorTable == nullptr)
2189 : {
2190 : /* Add fake alpha band */
2191 0 : memset(pImage, 255, static_cast<size_t>(nBlockXSize) * nBlockYSize);
2192 0 : eErr = CE_None;
2193 : }
2194 : else
2195 : {
2196 4 : eErr = poImageDS->GetRasterBand(1)->RasterIO(
2197 : GF_Read, 0, 0, nXSize, nYSize, pImage, nXSize, nYSize, GDT_Byte,
2198 4 : 1, nBlockXSize, nullptr);
2199 :
2200 : /* Expand color table */
2201 4 : if (eErr == CE_None && poColorTable != nullptr)
2202 : {
2203 4 : GByte *pabyImage = static_cast<GByte *>(pImage);
2204 : int j, i;
2205 2276 : for (j = 0; j < nReqYSize; j++)
2206 : {
2207 147680 : for (i = 0; i < nReqXSize; i++)
2208 : {
2209 145408 : GByte nVal = pabyImage[j * nBlockXSize + i];
2210 : const GDALColorEntry *poEntry =
2211 145408 : poColorTable->GetColorEntry(nVal);
2212 145408 : if (poEntry != nullptr)
2213 : {
2214 145408 : if (nBand == 1)
2215 36352 : pabyImage[j * nBlockXSize + i] =
2216 36352 : static_cast<GByte>(poEntry->c1);
2217 109056 : else if (nBand == 2)
2218 36352 : pabyImage[j * nBlockXSize + i] =
2219 36352 : static_cast<GByte>(poEntry->c2);
2220 72704 : else if (nBand == 3)
2221 36352 : pabyImage[j * nBlockXSize + i] =
2222 36352 : static_cast<GByte>(poEntry->c3);
2223 : else
2224 36352 : pabyImage[j * nBlockXSize + i] =
2225 36352 : static_cast<GByte>(poEntry->c4);
2226 : }
2227 : }
2228 : }
2229 : }
2230 : }
2231 : }
2232 8 : else if (nBand <= poImageDS->GetRasterCount())
2233 : {
2234 16 : eErr = poImageDS->GetRasterBand(nBand)->RasterIO(
2235 : GF_Read, 0, 0, nXSize, nYSize, pImage, nXSize, nYSize, GDT_Byte, 1,
2236 8 : nBlockXSize, nullptr);
2237 : }
2238 0 : else if (nBand == 4 && poImageDS->GetRasterCount() == 3)
2239 : {
2240 : /* Add fake alpha band */
2241 0 : memset(pImage, 255, static_cast<size_t>(nBlockXSize) * nBlockYSize);
2242 0 : eErr = CE_None;
2243 : }
2244 :
2245 : /* Cache other bands */
2246 12 : if (!poGDS->bLockOtherBands)
2247 : {
2248 3 : poGDS->bLockOtherBands = TRUE;
2249 15 : for (int iBand = 1; iBand <= poGDS->nBands; iBand++)
2250 : {
2251 12 : if (iBand != nBand)
2252 : {
2253 : KmlSingleDocRasterRasterBand *poOtherBand =
2254 : static_cast<KmlSingleDocRasterRasterBand *>(
2255 9 : poGDS->GetRasterBand(iBand));
2256 : GDALRasterBlock *poBlock =
2257 9 : poOtherBand->GetLockedBlockRef(nBlockXOff, nBlockYOff);
2258 9 : if (poBlock == nullptr)
2259 0 : continue;
2260 9 : poBlock->DropLock();
2261 : }
2262 : }
2263 3 : poGDS->bLockOtherBands = FALSE;
2264 : }
2265 :
2266 12 : return eErr;
2267 : }
2268 :
2269 : /************************************************************************/
2270 : /* GetColorInterpretation() */
2271 : /************************************************************************/
2272 :
2273 4 : GDALColorInterp KmlSingleDocRasterRasterBand::GetColorInterpretation()
2274 : {
2275 4 : return static_cast<GDALColorInterp>(GCI_RedBand + nBand - 1);
2276 : }
2277 :
2278 : /************************************************************************/
2279 : /* GetOverviewCount() */
2280 : /************************************************************************/
2281 :
2282 1 : int KmlSingleDocRasterRasterBand::GetOverviewCount()
2283 : {
2284 : KmlSingleDocRasterDataset *poGDS =
2285 1 : cpl::down_cast<KmlSingleDocRasterDataset *>(poDS);
2286 1 : poGDS->BuildOverviews();
2287 :
2288 1 : return static_cast<int>(poGDS->m_apoOverviews.size());
2289 : }
2290 :
2291 : /************************************************************************/
2292 : /* GetOverview() */
2293 : /************************************************************************/
2294 :
2295 1 : GDALRasterBand *KmlSingleDocRasterRasterBand::GetOverview(int iOvr)
2296 : {
2297 : KmlSingleDocRasterDataset *poGDS =
2298 1 : cpl::down_cast<KmlSingleDocRasterDataset *>(poDS);
2299 1 : poGDS->BuildOverviews();
2300 :
2301 1 : if (iOvr < 0 || iOvr >= static_cast<int>(poGDS->m_apoOverviews.size()))
2302 0 : return nullptr;
2303 :
2304 1 : return poGDS->m_apoOverviews[iOvr]->GetRasterBand(nBand);
2305 : }
2306 :
2307 : /************************************************************************/
2308 : /* KmlSingleDocCollectTiles() */
2309 : /************************************************************************/
2310 :
2311 : static void
2312 60 : KmlSingleDocCollectTiles(CPLXMLNode *psNode,
2313 : std::vector<KmlSingleDocRasterTilesDesc> &aosDescs,
2314 : CPLString &osURLBase)
2315 : {
2316 60 : if (strcmp(psNode->pszValue, "href") == 0)
2317 : {
2318 : int level, j, i;
2319 : char szExt[4];
2320 3 : const char *pszHref = CPLGetXMLValue(psNode, "", "");
2321 3 : if (STARTS_WITH(pszHref, "http"))
2322 : {
2323 0 : osURLBase = CPLGetPathSafe(pszHref);
2324 : }
2325 3 : if (sscanf(CPLGetFilename(pszHref), "kml_image_L%d_%d_%d.%3s", &level,
2326 3 : &j, &i, szExt) == 4)
2327 : {
2328 3 : if (level > static_cast<int>(aosDescs.size()))
2329 : {
2330 : KmlSingleDocRasterTilesDesc sDesc;
2331 2 : while (level > static_cast<int>(aosDescs.size()) + 1)
2332 : {
2333 0 : sDesc.nMaxJ_i = -1;
2334 0 : sDesc.nMaxJ_j = -1;
2335 0 : sDesc.nMaxI_i = -1;
2336 0 : sDesc.nMaxI_j = -1;
2337 0 : strcpy(sDesc.szExtI, "");
2338 0 : strcpy(sDesc.szExtJ, "");
2339 0 : aosDescs.push_back(sDesc);
2340 : }
2341 :
2342 2 : sDesc.nMaxJ_j = j;
2343 2 : sDesc.nMaxJ_i = i;
2344 2 : strcpy(sDesc.szExtJ, szExt);
2345 2 : sDesc.nMaxI_j = j;
2346 2 : sDesc.nMaxI_i = i;
2347 2 : strcpy(sDesc.szExtI, szExt);
2348 2 : aosDescs.push_back(sDesc);
2349 : }
2350 : else
2351 : {
2352 : /* 2010_USACE_JALBTCX_Louisiana_Mississippi_Lidar.kmz has not a
2353 : * lower-right tile */
2354 : /* so the right most tile and the bottom most tile might be
2355 : * different */
2356 2 : if ((j > aosDescs[level - 1].nMaxJ_j) ||
2357 1 : (j == aosDescs[level - 1].nMaxJ_j &&
2358 1 : i > aosDescs[level - 1].nMaxJ_i))
2359 : {
2360 1 : aosDescs[level - 1].nMaxJ_j = j;
2361 1 : aosDescs[level - 1].nMaxJ_i = i;
2362 1 : strcpy(aosDescs[level - 1].szExtJ, szExt);
2363 : }
2364 1 : if (i > aosDescs[level - 1].nMaxI_i ||
2365 0 : (i == aosDescs[level - 1].nMaxI_i &&
2366 0 : j > aosDescs[level - 1].nMaxI_j))
2367 : {
2368 1 : aosDescs[level - 1].nMaxI_j = j;
2369 1 : aosDescs[level - 1].nMaxI_i = i;
2370 1 : strcpy(aosDescs[level - 1].szExtI, szExt);
2371 : }
2372 : }
2373 : }
2374 : }
2375 : else
2376 : {
2377 57 : CPLXMLNode *psIter = psNode->psChild;
2378 152 : while (psIter != nullptr)
2379 : {
2380 95 : if (psIter->eType == CXT_Element)
2381 59 : KmlSingleDocCollectTiles(psIter, aosDescs, osURLBase);
2382 95 : psIter = psIter->psNext;
2383 : }
2384 : }
2385 60 : }
2386 :
2387 : /************************************************************************/
2388 : /* Open() */
2389 : /************************************************************************/
2390 :
2391 : /* Read raster with a structure like
2392 : * http://opentopo.sdsc.edu/files/Haiti/NGA_Haiti_LiDAR2.kmz */
2393 : /* i.e. made of a doc.kml that list all tiles at all overview levels */
2394 : /* The tile name pattern is "kml_image_L{level}_{j}_{i}.{png|jpg}" */
2395 99 : GDALDataset *KmlSingleDocRasterDataset::Open(const char *pszFilename,
2396 : const CPLString &osFilename,
2397 : CPLXMLNode *psRoot)
2398 : {
2399 99 : CPLXMLNode *psRootFolder = CPLGetXMLNode(psRoot, "=kml.Document.Folder");
2400 99 : if (psRootFolder == nullptr)
2401 65 : return nullptr;
2402 34 : const char *pszRootFolderName = CPLGetXMLValue(psRootFolder, "name", "");
2403 34 : if (strcmp(pszRootFolderName, "kml_image_L1_0_0") != 0)
2404 33 : return nullptr;
2405 :
2406 1 : std::array<double, 4> adfGlobalExtents = {0, 0, 0, 0};
2407 1 : CPLXMLNode *psRegion = CPLGetXMLNode(psRootFolder, "Region");
2408 1 : if (psRegion == nullptr)
2409 0 : return nullptr;
2410 1 : if (!KmlSuperOverlayGetBoundingBox(psRegion, adfGlobalExtents))
2411 0 : return nullptr;
2412 :
2413 2 : std::vector<KmlSingleDocRasterTilesDesc> aosDescs;
2414 2 : CPLString osDirname = CPLGetPathSafe(osFilename);
2415 1 : KmlSingleDocCollectTiles(psRootFolder, aosDescs, osDirname);
2416 1 : if (aosDescs.empty())
2417 0 : return nullptr;
2418 3 : for (const auto &oDesc : aosDescs)
2419 : {
2420 2 : if (oDesc.nMaxJ_i < 0)
2421 0 : return nullptr;
2422 : }
2423 :
2424 : const std::string osImageFilename =
2425 : CPLFormFilenameSafe(osDirname,
2426 : CPLSPrintf("kml_image_L%d_%d_%d",
2427 1 : static_cast<int>(aosDescs.size()), 0, 0),
2428 2 : aosDescs.back().szExtI);
2429 : auto poImageDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
2430 2 : osImageFilename.c_str(), GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR));
2431 1 : if (poImageDS == nullptr)
2432 : {
2433 0 : return nullptr;
2434 : }
2435 1 : int nTileSize = poImageDS->GetRasterXSize();
2436 1 : if (nTileSize != poImageDS->GetRasterYSize())
2437 : {
2438 1 : nTileSize = 1024;
2439 : }
2440 :
2441 1 : const KmlSingleDocRasterTilesDesc &oDesc = aosDescs.back();
2442 1 : int nXSize = 0;
2443 1 : int nYSize = 0;
2444 1 : int nBands = 0;
2445 1 : int bHasCT = FALSE;
2446 1 : if (!KmlSingleDocGetDimensions(osDirname, oDesc,
2447 1 : static_cast<int>(aosDescs.size()), nTileSize,
2448 : nXSize, nYSize, nBands, bHasCT))
2449 : {
2450 0 : return nullptr;
2451 : }
2452 :
2453 2 : auto poDS = std::make_unique<KmlSingleDocRasterDataset>();
2454 1 : poDS->nRasterXSize = nXSize;
2455 1 : poDS->nRasterYSize = nYSize;
2456 1 : poDS->nLevel = static_cast<int>(aosDescs.size());
2457 1 : poDS->nTileSize = nTileSize;
2458 1 : poDS->osDirname = std::move(osDirname);
2459 1 : poDS->osNominalExt = oDesc.szExtI;
2460 1 : poDS->adfGlobalExtents = adfGlobalExtents;
2461 1 : poDS->m_gt[0] = adfGlobalExtents[0];
2462 1 : poDS->m_gt[1] =
2463 1 : (adfGlobalExtents[2] - adfGlobalExtents[0]) / poDS->nRasterXSize;
2464 1 : poDS->m_gt[2] = 0.0;
2465 1 : poDS->m_gt[3] = adfGlobalExtents[3];
2466 1 : poDS->m_gt[4] = 0.0;
2467 1 : poDS->m_gt[5] =
2468 1 : -(adfGlobalExtents[3] - adfGlobalExtents[1]) / poDS->nRasterYSize;
2469 1 : if (nBands == 1 && bHasCT)
2470 1 : nBands = 4;
2471 5 : for (int iBand = 1; iBand <= nBands; iBand++)
2472 8 : poDS->SetBand(iBand, std::make_unique<KmlSingleDocRasterRasterBand>(
2473 8 : poDS.get(), iBand));
2474 1 : poDS->SetDescription(pszFilename);
2475 1 : poDS->SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE");
2476 1 : poDS->aosDescs = std::move(aosDescs);
2477 :
2478 1 : return poDS.release();
2479 : }
2480 :
2481 : /************************************************************************/
2482 : /* KmlSingleOverlayRasterDataset */
2483 : /************************************************************************/
2484 :
2485 8 : class KmlSingleOverlayRasterDataset final : public VRTDataset
2486 : {
2487 : public:
2488 4 : KmlSingleOverlayRasterDataset(int nXSize, int nYSize)
2489 4 : : VRTDataset(nXSize, nYSize)
2490 : {
2491 4 : }
2492 :
2493 : ~KmlSingleOverlayRasterDataset() override;
2494 :
2495 : static GDALDataset *Open(const char *pszFilename,
2496 : const CPLString &osFilename, CPLXMLNode *psRoot);
2497 : };
2498 :
2499 : KmlSingleOverlayRasterDataset::~KmlSingleOverlayRasterDataset() = default;
2500 :
2501 : /************************************************************************/
2502 : /* Open() */
2503 : /************************************************************************/
2504 :
2505 : /* Read raster with a structure like https://trac.osgeo.org/gdal/ticket/6712 */
2506 : /* i.e. made of a doc.kml that has a single GroundOverlay */
2507 36 : GDALDataset *KmlSingleOverlayRasterDataset::Open(const char *pszFilename,
2508 : const CPLString &osFilename,
2509 : CPLXMLNode *psRoot)
2510 : {
2511 36 : CPLXMLNode *psGO = CPLGetXMLNode(psRoot, "=kml.GroundOverlay");
2512 36 : if (psGO == nullptr)
2513 : {
2514 : // Otherwise look for kml.Document.Folder.GroundOverlay if there's
2515 : // a single occurrence of Folder and GroundOverlay
2516 34 : auto psDoc = CPLGetXMLNode(psRoot, "=kml.Document");
2517 34 : if (psDoc == nullptr)
2518 : {
2519 0 : return nullptr;
2520 : }
2521 34 : CPLXMLNode *psFolder = nullptr;
2522 116 : for (auto psIter = psDoc->psChild; psIter; psIter = psIter->psNext)
2523 : {
2524 98 : if (psIter->eType == CXT_Element &&
2525 66 : strcmp(psIter->pszValue, "Folder") == 0)
2526 : {
2527 49 : if (psFolder == nullptr)
2528 33 : psFolder = psIter;
2529 : else
2530 16 : return nullptr;
2531 : }
2532 : }
2533 :
2534 : // folder is not mandatory -- some kml have a structure
2535 : // kml.Document.GroundOverlay
2536 18 : CPLXMLNode *psParent = psFolder != nullptr ? psFolder : psDoc;
2537 36 : for (auto psIter = psParent->psChild; psIter; psIter = psIter->psNext)
2538 : {
2539 18 : if (psIter->eType == CXT_Element &&
2540 18 : strcmp(psIter->pszValue, "GroundOverlay") == 0)
2541 : {
2542 2 : if (psGO == nullptr)
2543 2 : psGO = psIter;
2544 : else
2545 0 : return nullptr;
2546 : }
2547 : }
2548 18 : if (psGO == nullptr)
2549 : {
2550 16 : return nullptr;
2551 : }
2552 : }
2553 :
2554 4 : const char *pszHref = CPLGetXMLValue(psGO, "Icon.href", nullptr);
2555 4 : if (pszHref == nullptr)
2556 0 : return nullptr;
2557 4 : std::array<double, 4> adfExtents = {0, 0, 0, 0};
2558 4 : if (!KmlSuperOverlayGetBoundingBox(psGO, adfExtents))
2559 0 : return nullptr;
2560 12 : if ((STARTS_WITH(pszHref, "../") &&
2561 8 : CPLHasPathTraversal(pszHref + strlen("../"))) ||
2562 4 : (!STARTS_WITH(pszHref, "../") && CPLHasPathTraversal(pszHref)))
2563 : {
2564 0 : CPLError(CE_Failure, CPLE_AppDefined, "Path traversal detected in %s",
2565 : pszHref);
2566 0 : return nullptr;
2567 : }
2568 : const std::string osImageFilename = CPLFormFilenameSafe(
2569 8 : CPLGetPathSafe(osFilename).c_str(), pszHref, nullptr);
2570 4 : GDALDataset *poImageDS = GDALDataset::FromHandle(
2571 : GDALOpenShared(osImageFilename.c_str(), GA_ReadOnly));
2572 4 : if (poImageDS == nullptr)
2573 0 : return nullptr;
2574 :
2575 : auto poDS = std::make_unique<KmlSingleOverlayRasterDataset>(
2576 8 : poImageDS->GetRasterXSize(), poImageDS->GetRasterYSize());
2577 12 : for (int i = 1; i <= poImageDS->GetRasterCount(); ++i)
2578 : {
2579 8 : poDS->AddBand(GDT_Byte, nullptr);
2580 :
2581 8 : auto poImageBand = poImageDS->GetRasterBand(i);
2582 : auto poVRTBand =
2583 8 : static_cast<VRTSourcedRasterBand *>(poDS->GetRasterBand(i));
2584 32 : poVRTBand->AddSimpleSource(
2585 8 : poImageBand, 0, 0, poImageDS->GetRasterXSize(),
2586 8 : poImageDS->GetRasterYSize(), 0, 0, poImageDS->GetRasterXSize(),
2587 8 : poImageDS->GetRasterYSize(), nullptr, VRT_NODATA_UNSET);
2588 :
2589 8 : poVRTBand->SetColorInterpretation(
2590 8 : poImageBand->GetColorInterpretation());
2591 :
2592 8 : const auto poCT = poImageBand->GetColorTable();
2593 8 : if (poCT)
2594 2 : poVRTBand->SetColorTable(poCT);
2595 : }
2596 4 : poImageDS->Dereference();
2597 : GDALGeoTransform gt{
2598 4 : adfExtents[0],
2599 4 : (adfExtents[2] - adfExtents[0]) / poImageDS->GetRasterXSize(),
2600 : 0,
2601 4 : adfExtents[3],
2602 : 0,
2603 8 : -(adfExtents[3] - adfExtents[1]) / poImageDS->GetRasterYSize()};
2604 4 : poDS->SetGeoTransform(gt);
2605 4 : poDS->SetProjection(SRS_WKT_WGS84_LAT_LONG);
2606 4 : poDS->SetWritable(false);
2607 4 : poDS->SetDescription(pszFilename);
2608 :
2609 4 : return poDS.release();
2610 : }
2611 :
2612 : /************************************************************************/
2613 : /* Open() */
2614 : /************************************************************************/
2615 :
2616 : GDALDataset *
2617 147 : KmlSuperOverlayReadDataset::Open(const char *pszFilename,
2618 : KmlSuperOverlayReadDataset *poParent, int nRec)
2619 :
2620 : {
2621 147 : if (nRec == 2)
2622 0 : return nullptr;
2623 294 : CPLString osFilename(pszFilename);
2624 147 : if (EQUAL(CPLGetExtensionSafe(pszFilename).c_str(), "kmz"))
2625 : {
2626 65 : if (!STARTS_WITH(pszFilename, "/vsizip/"))
2627 65 : osFilename = CPLSPrintf("/vsizip/%s", pszFilename);
2628 65 : char **papszFiles = VSIReadDir(osFilename);
2629 65 : if (papszFiles == nullptr)
2630 48 : return nullptr;
2631 17 : char **papszIter = papszFiles;
2632 17 : for (; *papszIter != nullptr; papszIter++)
2633 : {
2634 17 : if (EQUAL(CPLGetExtensionSafe(*papszIter).c_str(), "kml"))
2635 : {
2636 : osFilename =
2637 17 : CPLFormFilenameSafe(osFilename, *papszIter, nullptr);
2638 17 : osFilename = KMLRemoveSlash(osFilename);
2639 17 : break;
2640 : }
2641 : }
2642 17 : CSLDestroy(papszFiles);
2643 : }
2644 99 : VSILFILE *fp = VSIFOpenL(osFilename, "rb");
2645 99 : if (fp == nullptr)
2646 0 : return nullptr;
2647 99 : char *pszBuffer = static_cast<char *>(VSI_MALLOC_VERBOSE(BUFFER_SIZE + 1));
2648 99 : if (pszBuffer == nullptr)
2649 : {
2650 0 : VSIFCloseL(fp);
2651 0 : return nullptr;
2652 : }
2653 99 : const size_t nRead = VSIFReadL(pszBuffer, 1, BUFFER_SIZE, fp);
2654 99 : pszBuffer[nRead] = '\0';
2655 99 : VSIFCloseL(fp);
2656 99 : if (nRead == BUFFER_SIZE)
2657 : {
2658 0 : CPLFree(pszBuffer);
2659 0 : return nullptr;
2660 : }
2661 :
2662 99 : CPLXMLNode *psNode = CPLParseXMLString(pszBuffer);
2663 99 : CPLFree(pszBuffer);
2664 99 : if (psNode == nullptr)
2665 0 : return nullptr;
2666 :
2667 : GDALDataset *psSingleDocDS =
2668 99 : KmlSingleDocRasterDataset::Open(pszFilename, osFilename, psNode);
2669 99 : if (psSingleDocDS != nullptr)
2670 : {
2671 1 : CPLDestroyXMLNode(psNode);
2672 1 : return psSingleDocDS;
2673 : }
2674 :
2675 98 : CPLXMLNode *psRegion = nullptr;
2676 98 : CPLXMLNode *psDocument = nullptr;
2677 98 : CPLXMLNode *psGroundOverlay = nullptr;
2678 98 : CPLXMLNode *psLink = nullptr;
2679 98 : if (!KmlSuperOverlayFindRegionStart(psNode, &psRegion, &psDocument,
2680 : &psGroundOverlay, &psLink))
2681 : {
2682 : // If we didn't find a super overlay, this still could be a valid kml
2683 : // containing a single overlay. Test for that now. (Note that we need to
2684 : // test first for super overlay in order to avoid false positive matches
2685 : // of super overlay datasets to single overlay datasets)
2686 36 : GDALDataset *psSingleOverlayDS = KmlSingleOverlayRasterDataset::Open(
2687 : pszFilename, osFilename, psNode);
2688 36 : CPLDestroyXMLNode(psNode);
2689 36 : return psSingleOverlayDS;
2690 : }
2691 :
2692 62 : if (psLink != nullptr)
2693 : {
2694 23 : const char *pszHref = CPLGetXMLValue(psLink, "href", nullptr);
2695 46 : if (pszHref == nullptr ||
2696 46 : !EQUAL(CPLGetExtensionSafe(pszHref).c_str(), "kml"))
2697 : {
2698 0 : CPLDestroyXMLNode(psNode);
2699 0 : return nullptr;
2700 : }
2701 :
2702 46 : CPLString osSubFilename;
2703 23 : if (STARTS_WITH(pszHref, "http"))
2704 0 : osSubFilename = CPLSPrintf("/vsicurl_streaming/%s", pszHref);
2705 : else
2706 : {
2707 23 : if (CPLHasPathTraversal(pszHref))
2708 : {
2709 0 : CPLError(CE_Failure, CPLE_AppDefined,
2710 : "Path traversal detected in %s", pszHref);
2711 0 : CPLDestroyXMLNode(psNode);
2712 0 : return nullptr;
2713 : }
2714 23 : osSubFilename = CPLFormFilenameSafe(
2715 46 : CPLGetPathSafe(osFilename).c_str(), pszHref, nullptr);
2716 23 : osSubFilename = KMLRemoveSlash(osSubFilename);
2717 : }
2718 :
2719 46 : CPLString osOverlayName, osOverlayDescription;
2720 23 : psDocument = CPLGetXMLNode(psNode, "=kml.Document");
2721 23 : if (psDocument)
2722 : {
2723 : const char *pszOverlayName =
2724 23 : CPLGetXMLValue(psDocument, "name", nullptr);
2725 45 : if (pszOverlayName != nullptr &&
2726 22 : strcmp(pszOverlayName,
2727 45 : CPLGetBasenameSafe(pszFilename).c_str()) != 0)
2728 : {
2729 2 : osOverlayName = pszOverlayName;
2730 : }
2731 : const char *pszOverlayDescription =
2732 23 : CPLGetXMLValue(psDocument, "description", nullptr);
2733 23 : if (pszOverlayDescription != nullptr)
2734 : {
2735 1 : osOverlayDescription = pszOverlayDescription;
2736 : }
2737 : }
2738 :
2739 23 : CPLDestroyXMLNode(psNode);
2740 :
2741 : // FIXME
2742 23 : GDALDataset *poDS = Open(osSubFilename, poParent, nRec + 1);
2743 23 : if (poDS != nullptr)
2744 : {
2745 22 : poDS->SetDescription(pszFilename);
2746 :
2747 22 : if (!osOverlayName.empty())
2748 : {
2749 2 : poDS->SetMetadataItem("NAME", osOverlayName);
2750 : }
2751 22 : if (!osOverlayDescription.empty())
2752 : {
2753 1 : poDS->SetMetadataItem("DESCRIPTION", osOverlayDescription);
2754 : }
2755 : }
2756 :
2757 23 : return poDS;
2758 : }
2759 :
2760 39 : CPLAssert(psDocument != nullptr);
2761 39 : CPLAssert(psGroundOverlay != nullptr);
2762 39 : CPLAssert(psRegion != nullptr);
2763 :
2764 39 : std::array<double, 4> adfExtents = {0, 0, 0, 0};
2765 39 : if (!KmlSuperOverlayGetBoundingBox(psGroundOverlay, adfExtents))
2766 : {
2767 1 : CPLDestroyXMLNode(psNode);
2768 1 : return nullptr;
2769 : }
2770 :
2771 38 : const char *pszIcon = CPLGetXMLValue(psGroundOverlay, "Icon.href", nullptr);
2772 38 : if (pszIcon == nullptr)
2773 : {
2774 0 : CPLDestroyXMLNode(psNode);
2775 0 : return nullptr;
2776 : }
2777 76 : auto poDSIcon = KmlSuperOverlayLoadIcon(pszFilename, pszIcon);
2778 38 : if (poDSIcon == nullptr)
2779 : {
2780 0 : CPLDestroyXMLNode(psNode);
2781 0 : return nullptr;
2782 : }
2783 :
2784 : int nFactor;
2785 38 : if (poParent != nullptr)
2786 13 : nFactor = poParent->nFactor / 2;
2787 : else
2788 : {
2789 25 : int nDepth = 0;
2790 25 : if (!KmlSuperOverlayComputeDepth(pszFilename, psDocument, nDepth))
2791 : {
2792 0 : CPLDestroyXMLNode(psNode);
2793 0 : return nullptr;
2794 : }
2795 25 : nFactor = 1 << nDepth;
2796 : }
2797 :
2798 76 : auto poDS = std::make_unique<KmlSuperOverlayReadDataset>();
2799 38 : poDS->osFilename = pszFilename;
2800 38 : poDS->psRoot = psNode;
2801 38 : poDS->psDocument = psDocument;
2802 38 : poDS->poParent = poParent;
2803 38 : poDS->nFactor = nFactor;
2804 38 : poDS->nRasterXSize = nFactor * poDSIcon->GetRasterXSize();
2805 38 : poDS->nRasterYSize = nFactor * poDSIcon->GetRasterYSize();
2806 38 : poDS->m_gt[0] = adfExtents[0];
2807 38 : poDS->m_gt[1] = (adfExtents[2] - adfExtents[0]) / poDS->nRasterXSize;
2808 38 : poDS->m_gt[3] = adfExtents[3];
2809 38 : poDS->m_gt[5] = -(adfExtents[3] - adfExtents[1]) / poDS->nRasterYSize;
2810 38 : poDS->nBands = 4;
2811 190 : for (int i = 0; i < 4; i++)
2812 304 : poDS->SetBand(i + 1, std::make_unique<KmlSuperOverlayRasterBand>(
2813 304 : poDS.get(), i + 1));
2814 38 : poDS->SetDescription(pszFilename);
2815 38 : poDS->SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE");
2816 :
2817 48 : while (poDS->poParent == nullptr && nFactor > 1)
2818 : {
2819 10 : nFactor /= 2;
2820 :
2821 20 : auto poOvrDS = std::make_unique<KmlSuperOverlayReadDataset>();
2822 :
2823 10 : poOvrDS->bIsOvr = true;
2824 : // The life-time of objects is such that poOvrDS is destroyed when
2825 : // poDS is destroyed.
2826 : // coverity[escape]
2827 10 : poOvrDS->poParent = poDS.get();
2828 10 : poOvrDS->nFactor = nFactor;
2829 10 : poOvrDS->nRasterXSize = nFactor * poDSIcon->GetRasterXSize();
2830 10 : poOvrDS->nRasterYSize = nFactor * poDSIcon->GetRasterYSize();
2831 10 : poOvrDS->m_gt[0] = adfExtents[0];
2832 10 : poOvrDS->m_gt[1] =
2833 10 : (adfExtents[2] - adfExtents[0]) / poOvrDS->nRasterXSize;
2834 10 : poOvrDS->m_gt[3] = adfExtents[3];
2835 10 : poOvrDS->m_gt[5] =
2836 10 : -(adfExtents[3] - adfExtents[1]) / poOvrDS->nRasterYSize;
2837 10 : poOvrDS->nBands = 4;
2838 50 : for (int i = 0; i < 4; i++)
2839 80 : poOvrDS->SetBand(i + 1, std::make_unique<KmlSuperOverlayRasterBand>(
2840 80 : poOvrDS.get(), i + 1));
2841 10 : poOvrDS->SetDescription(pszFilename);
2842 10 : poOvrDS->SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE");
2843 :
2844 10 : poDS->m_apoOverviewDS.push_back(std::move(poOvrDS));
2845 : }
2846 38 : poDS->poDSIcon = std::move(poDSIcon);
2847 :
2848 38 : return poDS.release();
2849 : }
2850 :
2851 : /************************************************************************/
2852 : /* KmlSuperOverlayDatasetDelete() */
2853 : /************************************************************************/
2854 :
2855 2 : static CPLErr KmlSuperOverlayDatasetDelete(CPL_UNUSED const char *fileName)
2856 : {
2857 : /* Null implementation, so that people can Delete("MEM:::") */
2858 2 : return CE_None;
2859 : }
2860 :
2861 : /************************************************************************/
2862 : /* GDALRegister_KMLSUPEROVERLAY() */
2863 : /************************************************************************/
2864 :
2865 2024 : void CPL_DLL GDALRegister_KMLSUPEROVERLAY()
2866 :
2867 : {
2868 2024 : if (GDALGetDriverByName("KMLSUPEROVERLAY") != nullptr)
2869 283 : return;
2870 :
2871 1741 : GDALDriver *poDriver = new GDALDriver();
2872 :
2873 1741 : poDriver->SetDescription("KMLSUPEROVERLAY");
2874 1741 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
2875 1741 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "Kml Super Overlay");
2876 1741 : poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES,
2877 : "Byte Int16 UInt16 Int32 UInt32 Float32 Float64 "
2878 1741 : "CInt16 CInt32 CFloat32 CFloat64");
2879 :
2880 1741 : poDriver->SetMetadataItem(GDAL_DMD_EXTENSIONS, "kml kmz");
2881 :
2882 1741 : poDriver->SetMetadataItem(
2883 : GDAL_DMD_CREATIONOPTIONLIST,
2884 : "<CreationOptionList>"
2885 : " <Option name='NAME' type='string' description='Overlay name'/>"
2886 : " <Option name='DESCRIPTION' type='string' description='Overlay "
2887 : "description'/>"
2888 : " <Option name='ALTITUDE' type='float' description='Distance above "
2889 : "the earth surface, in meters, interpreted according to the altitude "
2890 : "mode'/>"
2891 : " <Option name='ALTITUDEMODE' type='string-select' "
2892 : "default='clampToGround' description='Specifies hows the altitude is "
2893 : "interpreted'>"
2894 : " <Value>clampToGround</Value>"
2895 : " <Value>absolute</Value>"
2896 : " <Value>relativeToSeaFloor</Value>"
2897 : " <Value>clampToSeaFloor</Value>"
2898 : " </Option>"
2899 : " <Option name='FORMAT' type='string-select' default='JPEG' "
2900 : "description='Format of the tiles'>"
2901 : " <Value>PNG</Value>"
2902 : " <Value>JPEG</Value>"
2903 : " <Value>AUTO</Value>"
2904 : " </Option>"
2905 : " <Option name='FIX_ANTIMERIDIAN' type='boolean' description='Fix "
2906 : "for images crossing the antimeridian causing errors in Google Earth' "
2907 : "/>"
2908 1741 : "</CreationOptionList>");
2909 :
2910 1741 : poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
2911 :
2912 1741 : poDriver->pfnIdentify = KmlSuperOverlayReadDataset::Identify;
2913 1741 : poDriver->pfnOpen = KmlSuperOverlayReadDataset::Open;
2914 1741 : poDriver->pfnCreateCopy = KmlSuperOverlayCreateCopy;
2915 1741 : poDriver->pfnDelete = KmlSuperOverlayDatasetDelete;
2916 :
2917 1741 : GetGDALDriverManager()->RegisterDriver(poDriver);
2918 : }
|