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