Coverage Report

Created: 2026-09-26 08:22

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