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