Coverage Report

Created: 2025-06-09 07:43

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