• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

OSGeo / gdal / 15171299595

21 May 2025 07:51PM UTC coverage: 70.949% (+0.03%) from 70.921%
15171299595

Pull #12392

github

web-flow
Merge 405a716f5 into ce2393a45
Pull Request #12392: GDALOverviews: Limit external file size in GDALRegenerateOverviewsMultiBand

174 of 189 new or added lines in 2 files covered. (92.06%)

22859 existing lines in 88 files now uncovered.

568261 of 800943 relevant lines covered (70.95%)

235911.46 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

87.99
/frmts/kmlsuperoverlay/kmlsuperoverlaydataset.cpp
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,
75✔
39
                          int rxsize, int rysize, CPL_UNUSED int ix,
40
                          CPL_UNUSED int iy, int rx, int ry, int dxsize,
41
                          int dysize, int bands, GDALDataset *poSrcDs,
42
                          GDALDriver *poOutputTileDriver, bool isJpegDriver)
43
{
44
    GDALRasterBand *alphaBand = nullptr;
75✔
45

46
    std::vector<GByte> abyScanline(dxsize);
150✔
47
    std::vector<bool> hadnoData(dxsize);
150✔
48

49
    if (isJpegDriver && bands == 4)
75✔
50
        bands = 3;
41✔
51

52
    auto poTmpDataset = std::unique_ptr<GDALDataset>(
53
        MEMDataset::Create("", dxsize, dysize, bands, GDT_Byte, nullptr));
150✔
54

55
    if (!isJpegDriver)  // Jpeg dataset only has one or three bands
75✔
56
    {
57
        if (bands < 4)  // add transparency to files with one band or three
12✔
58
                        // bands
59
        {
60
            poTmpDataset->AddBand(GDT_Byte);
7✔
61
            alphaBand =
62
                poTmpDataset->GetRasterBand(poTmpDataset->GetRasterCount());
7✔
63
        }
64
    }
65

66
    const int rowOffset = rysize / dysize;
75✔
67
    const int loopCount = rysize / rowOffset;
75✔
68
    for (int row = 0; row < loopCount; row++)
7,045✔
69
    {
70
        if (!isJpegDriver)
6,970✔
71
        {
72
            for (int i = 0; i < dxsize; i++)
643,640✔
73
            {
74
                hadnoData[i] = false;
641,920✔
75
            }
76
        }
77

78
        for (int band = 1; band <= bands; band++)
22,860✔
79
        {
80
            GDALRasterBand *poBand = poSrcDs->GetRasterBand(band);
15,890✔
81
            int hasNoData = 0;
15,890✔
82
            const double noDataValue = poBand->GetNoDataValue(&hasNoData);
15,890✔
83

84
            int yOffset = ry + row * rowOffset;
15,890✔
85
            CPLErr errTest = poBand->RasterIO(
15,890✔
86
                GF_Read, rx, yOffset, rxsize, rowOffset, abyScanline.data(),
15,890✔
87
                dxsize, 1, GDT_Byte, 0, 0, nullptr);
88

89
            const bool bReadFailed = (errTest == CE_Failure);
15,890✔
90
            if (bReadFailed)
15,890✔
91
            {
92
                hasNoData = 1;
×
93
            }
94

95
            // fill the true or false for hadnoData array if the source data has
96
            // nodata value
97
            if (!isJpegDriver)
15,890✔
98
            {
99
                if (hasNoData == 1)
5,480✔
100
                {
101
                    for (int j = 0; j < dxsize; j++)
×
102
                    {
103
                        double v = abyScanline[j];
×
104
                        if (v == noDataValue || bReadFailed)
×
105
                        {
106
                            hadnoData[j] = true;
×
107
                        }
108
                    }
109
                }
110
            }
111

112
            if (!bReadFailed)
15,890✔
113
            {
114
                GDALRasterBand *poBandtmp = poTmpDataset->GetRasterBand(band);
15,890✔
115
                CPL_IGNORE_RET_VAL(poBandtmp->RasterIO(
15,890✔
116
                    GF_Write, 0, row, dxsize, 1, abyScanline.data(), dxsize, 1,
15,890✔
117
                    GDT_Byte, 0, 0, nullptr));
118
            }
119
        }
120

121
        // fill the values for alpha band
122
        if (!isJpegDriver)
6,970✔
123
        {
124
            if (alphaBand)
1,720✔
125
            {
126
                for (int i = 0; i < dxsize; i++)
561,400✔
127
                {
128
                    if (hadnoData[i])
560,000✔
129
                    {
130
                        abyScanline[i] = 0;
×
131
                    }
132
                    else
133
                    {
134
                        abyScanline[i] = 255;
560,000✔
135
                    }
136
                }
137

138
                CPL_IGNORE_RET_VAL(alphaBand->RasterIO(
1,400✔
139
                    GF_Write, 0, row, dxsize, 1, abyScanline.data(), dxsize, 1,
1,400✔
140
                    GDT_Byte, 0, 0, nullptr));
141
            }
142
        }
143
    }
144

145
    CPLConfigOptionSetter oSetter("GDAL_OPEN_AFTER_COPY", "NO", false);
75✔
146
    /* to prevent CreateCopy() from calling QuietDelete() */
147
    const char *const apszOptions[] = {"@QUIET_DELETE_ON_CREATE_COPY=NO",
75✔
148
                                       nullptr};
149
    std::unique_ptr<GDALDataset>(
75✔
150
        poOutputTileDriver->CreateCopy(filename.c_str(), poTmpDataset.get(),
151
                                       FALSE, apszOptions, nullptr, nullptr));
152
}
75✔
153

154
/************************************************************************/
155
/*                          GenerateRootKml()                           */
156
/************************************************************************/
157

158
static int GenerateRootKml(const char *filename, const char *kmlfilename,
19✔
159
                           double north, double south, double east, double west,
160
                           int tilesize, const char *pszOverlayName,
161
                           const char *pszOverlayDescription)
162
{
163
    VSILFILE *fp = VSIFOpenL(filename, "wb");
19✔
164
    if (fp == nullptr)
19✔
165
    {
166
        CPLError(CE_Failure, CPLE_AppDefined, "Cannot create %s", filename);
×
167
        return FALSE;
×
168
    }
169
    int minlodpixels = tilesize / 2;
19✔
170

171
    const std::string osOverlayName = pszOverlayName
172
                                          ? std::string(pszOverlayName)
173
                                          : CPLGetBasenameSafe(kmlfilename);
19✔
174

175
    // If we have not written any features yet, output the layer's schema.
176
    VSIFPrintfL(fp, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
19✔
177
    VSIFPrintfL(fp, "<kml xmlns=\"http://www.opengis.net/kml/2.2\">\n");
19✔
178
    VSIFPrintfL(fp, "\t<Document>\n");
19✔
179
    char *pszEncoded = CPLEscapeString(osOverlayName.c_str(), -1, CPLES_XML);
19✔
180
    VSIFPrintfL(fp, "\t\t<name>%s</name>\n", pszEncoded);
19✔
181
    CPLFree(pszEncoded);
19✔
182
    if (pszOverlayDescription == nullptr)
19✔
183
    {
184
        VSIFPrintfL(fp, "\t\t<description></description>\n");
18✔
185
    }
186
    else
187
    {
188
        pszEncoded = CPLEscapeString(pszOverlayDescription, -1, CPLES_XML);
1✔
189
        VSIFPrintfL(fp, "\t\t<description>%s</description>\n", pszEncoded);
1✔
190
        CPLFree(pszEncoded);
1✔
191
    }
192
    VSIFPrintfL(fp, "\t\t<styleUrl>#hideChildrenStyle</styleUrl>\n");
19✔
193
    VSIFPrintfL(fp, "\t\t<Style id=\"hideChildrenStyle\">\n");
19✔
194
    VSIFPrintfL(fp, "\t\t\t<ListStyle id=\"hideChildren\">\n");
19✔
195
    VSIFPrintfL(fp, "\t\t\t\t<listItemType>checkHideChildren</listItemType>\n");
19✔
196
    VSIFPrintfL(fp, "\t\t\t</ListStyle>\n");
19✔
197
    VSIFPrintfL(fp, "\t\t</Style>\n");
19✔
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
    VSIFPrintfL(fp, "\t\t<NetworkLink>\n");
19✔
207
    VSIFPrintfL(fp, "\t\t\t<open>1</open>\n");
19✔
208
    VSIFPrintfL(fp, "\t\t\t<Region>\n");
19✔
209
    VSIFPrintfL(fp, "\t\t\t\t<LatLonAltBox>\n");
19✔
210
    VSIFPrintfL(fp, "\t\t\t\t\t<north>%f</north>\n", north);
19✔
211
    VSIFPrintfL(fp, "\t\t\t\t\t<south>%f</south>\n", south);
19✔
212
    VSIFPrintfL(fp, "\t\t\t\t\t<east>%f</east>\n", east);
19✔
213
    VSIFPrintfL(fp, "\t\t\t\t\t<west>%f</west>\n", west);
19✔
214
    VSIFPrintfL(fp, "\t\t\t\t</LatLonAltBox>\n");
19✔
215
    VSIFPrintfL(fp, "\t\t\t\t<Lod>\n");
19✔
216
    VSIFPrintfL(fp, "\t\t\t\t\t<minLodPixels>%d</minLodPixels>\n",
19✔
217
                minlodpixels);
218
    VSIFPrintfL(fp, "\t\t\t\t\t<maxLodPixels>-1</maxLodPixels>\n");
19✔
219
    VSIFPrintfL(fp, "\t\t\t\t</Lod>\n");
19✔
220
    VSIFPrintfL(fp, "\t\t\t</Region>\n");
19✔
221
    VSIFPrintfL(fp, "\t\t\t<Link>\n");
19✔
222
    VSIFPrintfL(fp, "\t\t\t\t<href>0/0/0.kml</href>\n");
19✔
223
    VSIFPrintfL(fp, "\t\t\t\t<viewRefreshMode>onRegion</viewRefreshMode>\n");
19✔
224
    VSIFPrintfL(fp, "\t\t\t</Link>\n");
19✔
225
    VSIFPrintfL(fp, "\t\t</NetworkLink>\n");
19✔
226
    VSIFPrintfL(fp, "\t</Document>\n");
19✔
227
    VSIFPrintfL(fp, "</kml>\n");
19✔
228

229
    VSIFCloseL(fp);
19✔
230
    return TRUE;
19✔
231
}
232

233
/************************************************************************/
234
/*                          GenerateChildKml()                          */
235
/************************************************************************/
236

237
static int GenerateChildKml(
75✔
238
    const std::string &filename, int zoom, int ix, int iy, double zoomxpixel,
239
    double zoomypixel, int dxsize, int dysize, double south, double west,
240
    int xsize, int ysize, int maxzoom, OGRCoordinateTransformation *poTransform,
241
    const std::string &fileExt, bool fixAntiMeridian, const char *pszAltitude,
242
    const char *pszAltitudeMode,
243
    const std::vector<std::pair<std::pair<int, int>, bool>> &childTiles)
244
{
245
    double tnorth = south + zoomypixel * ((iy + 1) * dysize);
75✔
246
    double tsouth = south + zoomypixel * (iy * dysize);
75✔
247
    double teast = west + zoomxpixel * ((ix + 1) * dxsize);
75✔
248
    double twest = west + zoomxpixel * ix * dxsize;
75✔
249

250
    double upperleftT = twest;
75✔
251
    double lowerleftT = twest;
75✔
252

253
    double rightbottomT = tsouth;
75✔
254
    double leftbottomT = tsouth;
75✔
255

256
    double lefttopT = tnorth;
75✔
257
    double righttopT = tnorth;
75✔
258

259
    double lowerrightT = teast;
75✔
260
    double upperrightT = teast;
75✔
261

262
    if (poTransform)
75✔
263
    {
264
        poTransform->Transform(1, &twest, &tsouth);
10✔
265
        poTransform->Transform(1, &teast, &tnorth);
10✔
266

267
        poTransform->Transform(1, &upperleftT, &lefttopT);
10✔
268
        poTransform->Transform(1, &upperrightT, &righttopT);
10✔
269
        poTransform->Transform(1, &lowerrightT, &rightbottomT);
10✔
270
        poTransform->Transform(1, &lowerleftT, &leftbottomT);
10✔
271
    }
272

273
    if (fixAntiMeridian && teast < twest)
75✔
274
    {
275
        teast += 360;
3✔
276
        lowerrightT += 360;
3✔
277
        upperrightT += 360;
3✔
278
    }
279

280
    std::vector<int> xchildren;
150✔
281
    std::vector<int> ychildern;
150✔
282

283
    int minLodPixels = 128;
75✔
284
    if (zoom == 0)
75✔
285
    {
286
        minLodPixels = 1;
19✔
287
    }
288

289
    int maxLodPix = -1;
75✔
290
    if (zoom < maxzoom)
75✔
291
    {
292
        double zareasize = pow(2.0, (maxzoom - zoom - 1)) * dxsize;
16✔
293
        double zareasize1 = pow(2.0, (maxzoom - zoom - 1)) * dysize;
16✔
294
        xchildren.push_back(ix * 2);
16✔
295
        int tmp = ix * 2 + 1;
16✔
296
        int tmp1 = static_cast<int>(ceil(xsize / zareasize));
16✔
297
        if (tmp < tmp1)
16✔
298
        {
299
            xchildren.push_back(ix * 2 + 1);
16✔
300
        }
301
        ychildern.push_back(iy * 2);
16✔
302
        tmp = iy * 2 + 1;
16✔
303
        tmp1 = static_cast<int>(ceil(ysize / zareasize1));
16✔
304
        if (tmp < tmp1)
16✔
305
        {
306
            ychildern.push_back(iy * 2 + 1);
16✔
307
        }
308
        maxLodPix = 2048;
16✔
309

310
        bool hasChildKML = false;
16✔
311
        for (const auto &kv : childTiles)
60✔
312
        {
313
            if (kv.second)
49✔
314
            {
315
                hasChildKML = true;
5✔
316
                break;
5✔
317
            }
318
        }
319
        if (!hasChildKML)
16✔
320
        {
321
            // no child KML files, so don't expire this one at any zoom.
322
            maxLodPix = -1;
11✔
323
        }
324
    }
325

326
    VSILFILE *fp = VSIFOpenL(filename.c_str(), "wb");
75✔
327
    if (fp == nullptr)
75✔
328
    {
329
        CPLError(CE_Failure, CPLE_AppDefined, "Cannot create %s",
×
330
                 filename.c_str());
331
        return FALSE;
×
332
    }
333

334
    VSIFPrintfL(fp, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
75✔
335
    VSIFPrintfL(fp, "<kml xmlns=\"http://www.opengis.net/kml/2.2\" "
75✔
336
                    "xmlns:gx=\"http://www.google.com/kml/ext/2.2\">\n");
337
    VSIFPrintfL(fp, "\t<Document>\n");
75✔
338
    VSIFPrintfL(fp, "\t\t<name>%d/%d/%d.kml</name>\n", zoom, ix, iy);
75✔
339
    VSIFPrintfL(fp, "\t\t<styleUrl>#hideChildrenStyle</styleUrl>\n");
75✔
340
    VSIFPrintfL(fp, "\t\t<Style id=\"hideChildrenStyle\">\n");
75✔
341
    VSIFPrintfL(fp, "\t\t\t<ListStyle id=\"hideChildren\">\n");
75✔
342
    VSIFPrintfL(fp, "\t\t\t\t<listItemType>checkHideChildren</listItemType>\n");
75✔
343
    VSIFPrintfL(fp, "\t\t\t</ListStyle>\n");
75✔
344
    VSIFPrintfL(fp, "\t\t</Style>\n");
75✔
345
    VSIFPrintfL(fp, "\t\t<Region>\n");
75✔
346
    VSIFPrintfL(fp, "\t\t\t<LatLonAltBox>\n");
75✔
347
    VSIFPrintfL(fp, "\t\t\t\t<north>%f</north>\n", tnorth);
75✔
348
    VSIFPrintfL(fp, "\t\t\t\t<south>%f</south>\n", tsouth);
75✔
349
    VSIFPrintfL(fp, "\t\t\t\t<east>%f</east>\n", teast);
75✔
350
    VSIFPrintfL(fp, "\t\t\t\t<west>%f</west>\n", twest);
75✔
351
    VSIFPrintfL(fp, "\t\t\t</LatLonAltBox>\n");
75✔
352
    VSIFPrintfL(fp, "\t\t\t<Lod>\n");
75✔
353
    VSIFPrintfL(fp, "\t\t\t\t<minLodPixels>%d</minLodPixels>\n", minLodPixels);
75✔
354
    VSIFPrintfL(fp, "\t\t\t\t<maxLodPixels>%d</maxLodPixels>\n", maxLodPix);
75✔
355
    VSIFPrintfL(fp, "\t\t\t</Lod>\n");
75✔
356
    VSIFPrintfL(fp, "\t\t</Region>\n");
75✔
357
    VSIFPrintfL(fp, "\t\t<GroundOverlay>\n");
75✔
358
    VSIFPrintfL(fp, "\t\t\t<drawOrder>%d</drawOrder>\n", zoom);
75✔
359
    VSIFPrintfL(fp, "\t\t\t<Icon>\n");
75✔
360
    VSIFPrintfL(fp, "\t\t\t\t<href>%d%s</href>\n", iy, fileExt.c_str());
75✔
361
    VSIFPrintfL(fp, "\t\t\t</Icon>\n");
75✔
362

363
    if (pszAltitude != nullptr)
75✔
364
    {
365
        VSIFPrintfL(fp, "\t\t\t<altitude>%s</altitude>\n", pszAltitude);
5✔
366
    }
367
    if (pszAltitudeMode != nullptr &&
75✔
368
        (strcmp(pszAltitudeMode, "clampToGround") == 0 ||
5✔
369
         strcmp(pszAltitudeMode, "absolute") == 0))
5✔
370
    {
371
        VSIFPrintfL(fp, "\t\t\t<altitudeMode>%s</altitudeMode>\n",
5✔
372
                    pszAltitudeMode);
373
    }
374
    else if (pszAltitudeMode != nullptr &&
70✔
375
             (strcmp(pszAltitudeMode, "relativeToSeaFloor") == 0 ||
×
376
              strcmp(pszAltitudeMode, "clampToSeaFloor") == 0))
×
377
    {
378
        VSIFPrintfL(fp, "\t\t\t<gx:altitudeMode>%s</gx:altitudeMode>\n",
×
379
                    pszAltitudeMode);
380
    }
381

382
    /* When possible, use <LatLonBox>. I've noticed otherwise that */
383
    /* if using <gx:LatLonQuad> with extents of the size of a country or */
384
    /* continent, the overlay is really bad placed in GoogleEarth */
385
    if (lowerleftT == upperleftT && lowerrightT == upperrightT &&
75✔
386
        leftbottomT == rightbottomT && righttopT == lefttopT)
70✔
387
    {
388
        VSIFPrintfL(fp, "\t\t\t<LatLonBox>\n");
70✔
389
        VSIFPrintfL(fp, "\t\t\t\t<north>%f</north>\n", tnorth);
70✔
390
        VSIFPrintfL(fp, "\t\t\t\t<south>%f</south>\n", tsouth);
70✔
391
        VSIFPrintfL(fp, "\t\t\t\t<east>%f</east>\n", teast);
70✔
392
        VSIFPrintfL(fp, "\t\t\t\t<west>%f</west>\n", twest);
70✔
393
        VSIFPrintfL(fp, "\t\t\t</LatLonBox>\n");
70✔
394
    }
395
    else
396
    {
397
        VSIFPrintfL(fp, "\t\t\t<gx:LatLonQuad>\n");
5✔
398
        VSIFPrintfL(fp, "\t\t\t\t<coordinates>\n");
5✔
399
        VSIFPrintfL(fp, "\t\t\t\t\t%f,%f,0\n", lowerleftT, leftbottomT);
5✔
400
        VSIFPrintfL(fp, "\t\t\t\t\t%f,%f,0\n", lowerrightT, rightbottomT);
5✔
401
        VSIFPrintfL(fp, "\t\t\t\t\t%f,%f,0\n", upperrightT, righttopT);
5✔
402
        VSIFPrintfL(fp, "\t\t\t\t\t%f,%f,0\n", upperleftT, lefttopT);
5✔
403
        VSIFPrintfL(fp, "\t\t\t\t</coordinates>\n");
5✔
404
        VSIFPrintfL(fp, "\t\t\t</gx:LatLonQuad>\n");
5✔
405
    }
406
    VSIFPrintfL(fp, "\t\t</GroundOverlay>\n");
75✔
407

408
    for (const auto &kv : childTiles)
131✔
409
    {
410
        int cx = kv.first.first;
56✔
411
        int cy = kv.first.second;
56✔
412

413
        double cnorth = south + zoomypixel / 2 * ((cy + 1) * dysize);
56✔
414
        double csouth = south + zoomypixel / 2 * (cy * dysize);
56✔
415
        double ceast = west + zoomxpixel / 2 * ((cx + 1) * dxsize);
56✔
416
        double cwest = west + zoomxpixel / 2 * cx * dxsize;
56✔
417

418
        if (poTransform)
56✔
419
        {
420
            poTransform->Transform(1, &cwest, &csouth);
8✔
421
            poTransform->Transform(1, &ceast, &cnorth);
8✔
422
        }
423

424
        if (fixAntiMeridian && ceast < cwest)
56✔
425
        {
426
            ceast += 360;
2✔
427
        }
428

429
        VSIFPrintfL(fp, "\t\t<NetworkLink>\n");
56✔
430
        VSIFPrintfL(fp, "\t\t\t<name>%d/%d/%d%s</name>\n", zoom + 1, cx, cy,
56✔
431
                    fileExt.c_str());
432
        VSIFPrintfL(fp, "\t\t\t<Region>\n");
56✔
433
        VSIFPrintfL(fp, "\t\t\t\t<Lod>\n");
56✔
434
        VSIFPrintfL(fp, "\t\t\t\t\t<minLodPixels>128</minLodPixels>\n");
56✔
435
        VSIFPrintfL(fp, "\t\t\t\t\t<maxLodPixels>-1</maxLodPixels>\n");
56✔
436
        VSIFPrintfL(fp, "\t\t\t\t</Lod>\n");
56✔
437
        VSIFPrintfL(fp, "\t\t\t\t<LatLonAltBox>\n");
56✔
438
        VSIFPrintfL(fp, "\t\t\t\t\t<north>%f</north>\n", cnorth);
56✔
439
        VSIFPrintfL(fp, "\t\t\t\t\t<south>%f</south>\n", csouth);
56✔
440
        VSIFPrintfL(fp, "\t\t\t\t\t<east>%f</east>\n", ceast);
56✔
441
        VSIFPrintfL(fp, "\t\t\t\t\t<west>%f</west>\n", cwest);
56✔
442
        VSIFPrintfL(fp, "\t\t\t\t</LatLonAltBox>\n");
56✔
443
        VSIFPrintfL(fp, "\t\t\t</Region>\n");
56✔
444
        VSIFPrintfL(fp, "\t\t\t<Link>\n");
56✔
445
        VSIFPrintfL(fp, "\t\t\t\t<href>../../%d/%d/%d.kml</href>\n", zoom + 1,
56✔
446
                    cx, cy);
447
        VSIFPrintfL(fp,
56✔
448
                    "\t\t\t\t<viewRefreshMode>onRegion</viewRefreshMode>\n");
449
        VSIFPrintfL(fp, "\t\t\t\t<viewFormat/>\n");
56✔
450
        VSIFPrintfL(fp, "\t\t\t</Link>\n");
56✔
451
        VSIFPrintfL(fp, "\t\t</NetworkLink>\n");
56✔
452
    }
453

454
    VSIFPrintfL(fp, "\t</Document>\n");
75✔
455
    VSIFPrintfL(fp, "</kml>\n");
75✔
456
    VSIFCloseL(fp);
75✔
457

458
    return TRUE;
75✔
459
}
460

461
/************************************************************************/
462
/*                         DetectTransparency()                         */
463
/************************************************************************/
464
int KmlSuperOverlayReadDataset::DetectTransparency(int rxsize, int rysize,
85✔
465
                                                   int rx, int ry, int dxsize,
466
                                                   int dysize,
467
                                                   GDALDataset *poSrcDs)
468
{
469
    int bands = poSrcDs->GetRasterCount();
85✔
470
    int rowOffset = rysize / dysize;
85✔
471
    int loopCount = rysize / rowOffset;
85✔
472
    int hasNoData = 0;
85✔
473
    std::vector<GByte> abyScanline(dxsize);
85✔
474

475
    int flags = 0;
85✔
476
    for (int band = 1; band <= bands; band++)
425✔
477
    {
478
        GDALRasterBand *poBand = poSrcDs->GetRasterBand(band);
340✔
479
        int noDataValue = static_cast<int>(poBand->GetNoDataValue(&hasNoData));
340✔
480

481
        if (band < 4 && hasNoData)
340✔
482
        {
483
            for (int row = 0; row < loopCount; row++)
×
484
            {
485
                int yOffset = ry + row * rowOffset;
×
486
                CPL_IGNORE_RET_VAL(poBand->RasterIO(
×
487
                    GF_Read, rx, yOffset, rxsize, rowOffset, abyScanline.data(),
×
488
                    dxsize, 1, GDT_Byte, 0, 0, nullptr));
489
                for (int i = 0; i < dxsize; i++)
×
490
                {
491
                    if (abyScanline[i] == noDataValue)
×
492
                    {
493
                        flags |= KMLSO_ContainsTransparentPixels;
×
494
                    }
495
                    else
496
                    {
497
                        flags |= KMLSO_ContainsOpaquePixels;
×
498
                    }
499
                }
500
                // shortcut - if there are both types of pixels, flags is as
501
                // full as it is going to get.
502
                // so no point continuing, skip to the next band
503
                if ((flags & KMLSO_ContainsTransparentPixels) &&
×
504
                    (flags & KMLSO_ContainsOpaquePixels))
×
505
                {
506
                    break;
×
507
                }
508
            }
×
509
        }
510
        else if (band == 4)
340✔
511
        {
512
            for (int row = 0; row < loopCount; row++)
5,525✔
513
            {
514
                int yOffset = ry + row * rowOffset;
5,440✔
515
                CPL_IGNORE_RET_VAL(poBand->RasterIO(
5,440✔
516
                    GF_Read, rx, yOffset, rxsize, rowOffset, abyScanline.data(),
5,440✔
517
                    dxsize, 1, GDT_Byte, 0, 0, nullptr));
518
                for (int i = 0; i < dxsize; i++)
1,398,080✔
519
                {
520
                    if (abyScanline[i] == 255)
1,392,640✔
521
                    {
522
                        flags |= KMLSO_ContainsOpaquePixels;
696,320✔
523
                    }
524
                    else if (abyScanline[i] == 0)
696,320✔
525
                    {
526
                        flags |= KMLSO_ContainsTransparentPixels;
696,320✔
527
                    }
528
                    else
529
                    {
530
                        flags |= KMLSO_ContainsPartiallyTransparentPixels;
×
531
                    }
532
                }
533
            }
534
        }
535
    }
536
    return flags;
170✔
537
}
538

539
/************************************************************************/
540
/*                           CreateCopy()                               */
541
/************************************************************************/
542

543
class KmlSuperOverlayDummyDataset final : public GDALDataset
544
{
545
  public:
546
    KmlSuperOverlayDummyDataset() = default;
1✔
547
};
548

549
static GDALDataset *
550
KmlSuperOverlayCreateCopy(const char *pszFilename, GDALDataset *poSrcDS,
24✔
551
                          CPL_UNUSED int bStrict, char **papszOptions,
552
                          GDALProgressFunc pfnProgress, void *pProgressData)
553
{
554
    bool isKmz = false;
24✔
555

556
    if (pfnProgress == nullptr)
24✔
557
        pfnProgress = GDALDummyProgress;
×
558

559
    int bands = poSrcDS->GetRasterCount();
24✔
560
    if (bands != 1 && bands != 3 && bands != 4)
24✔
561
        return nullptr;
3✔
562

563
    // correct the file and get the directory
564
    char *output_dir = nullptr;
21✔
565
    std::string osFilename;
42✔
566
    if (pszFilename == nullptr)
21✔
567
    {
568
        output_dir = CPLGetCurrentDir();
×
569
        osFilename = CPLFormFilenameSafe(output_dir, "doc", "kml");
×
570
    }
571
    else
572
    {
573
        osFilename = pszFilename;
21✔
574
        const std::string osExtension = CPLGetExtensionSafe(pszFilename);
21✔
575
        const char *extension = osExtension.c_str();
21✔
576
        if (!EQUAL(extension, "kml") && !EQUAL(extension, "kmz"))
21✔
577
        {
578
            CPLError(CE_Failure, CPLE_None,
×
579
                     "File extension should be kml or kmz.");
580
            return nullptr;
×
581
        }
582
        if (EQUAL(extension, "kmz"))
21✔
583
        {
584
            isKmz = true;
17✔
585
        }
586

587
        output_dir = CPLStrdup(CPLGetPathSafe(pszFilename).c_str());
21✔
588
        if (strcmp(output_dir, "") == 0)
21✔
589
        {
590
            CPLFree(output_dir);
×
591
            output_dir = CPLGetCurrentDir();
×
592
        }
593
    }
594
    pszFilename = osFilename.c_str();
21✔
595

596
    CPLString outDir = output_dir ? output_dir : "";
42✔
597
    CPLFree(output_dir);
21✔
598
    output_dir = nullptr;
21✔
599

600
    VSILFILE *zipHandle = nullptr;
21✔
601
    if (isKmz)
21✔
602
    {
603
        outDir = "/vsizip/";
17✔
604
        outDir += pszFilename;
17✔
605
        zipHandle = VSIFOpenL(outDir, "wb");
17✔
606
        if (zipHandle == nullptr)
17✔
607
        {
608
            CPLError(CE_Failure, CPLE_AppDefined, "Cannot create %s",
2✔
609
                     pszFilename);
610
            return nullptr;
2✔
611
        }
612
    }
613

614
    GDALDriver *poOutputTileDriver = nullptr;
19✔
615
    GDALDriver *poJpegOutputTileDriver = nullptr;
19✔
616
    GDALDriver *poPngOutputTileDriver = nullptr;
19✔
617
    bool isAutoDriver = false;
19✔
618
    bool isJpegDriver = false;
19✔
619

620
    const char *pszFormat =
621
        CSLFetchNameValueDef(papszOptions, "FORMAT", "JPEG");
19✔
622
    if (EQUAL(pszFormat, "AUTO"))
19✔
623
    {
624
        isAutoDriver = true;
1✔
625
        poJpegOutputTileDriver =
626
            GetGDALDriverManager()->GetDriverByName("JPEG");
1✔
627
        poPngOutputTileDriver = GetGDALDriverManager()->GetDriverByName("PNG");
1✔
628
    }
629
    else
630
    {
631
        poOutputTileDriver = GetGDALDriverManager()->GetDriverByName(pszFormat);
18✔
632
        if (EQUAL(pszFormat, "JPEG"))
18✔
633
        {
634
            isJpegDriver = true;
15✔
635
        }
636
    }
637

638
    if ((!isAutoDriver && poOutputTileDriver == nullptr) ||
19✔
639
        (isAutoDriver && (poJpegOutputTileDriver == nullptr ||
1✔
640
                          poPngOutputTileDriver == nullptr)))
641
    {
UNCOV
642
        CPLError(CE_Failure, CPLE_None, "Image export driver was not found..");
×
UNCOV
643
        if (zipHandle != nullptr)
×
644
        {
645
            VSIFCloseL(zipHandle);
×
646
            VSIUnlink(pszFilename);
×
647
        }
648
        return nullptr;
×
649
    }
650

651
    int xsize = poSrcDS->GetRasterXSize();
19✔
652
    int ysize = poSrcDS->GetRasterYSize();
19✔
653

654
    double north = 0.0;
19✔
655
    double south = 0.0;
19✔
656
    double east = 0.0;
19✔
657
    double west = 0.0;
19✔
658

659
    double adfGeoTransform[6];
660

661
    if (poSrcDS->GetGeoTransform(adfGeoTransform) == CE_None)
19✔
662
    {
663
        north = adfGeoTransform[3];
19✔
664
        south = adfGeoTransform[3] + adfGeoTransform[5] * ysize;
19✔
665
        east = adfGeoTransform[0] + adfGeoTransform[1] * xsize;
19✔
666
        west = adfGeoTransform[0];
19✔
667
    }
668

669
    std::unique_ptr<OGRCoordinateTransformation> poTransform;
19✔
670
    const auto poSrcSRS = poSrcDS->GetSpatialRef();
19✔
671
    if (poSrcSRS && poSrcSRS->IsProjected())
19✔
672
    {
673
        OGRSpatialReference poLatLong;
4✔
674
        poLatLong.SetWellKnownGeogCS("WGS84");
2✔
675
        poLatLong.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
2✔
676

677
        poTransform.reset(
2✔
678
            OGRCreateCoordinateTransformation(poSrcSRS, &poLatLong));
679
        if (poTransform != nullptr)
2✔
680
        {
681
            poTransform->Transform(1, &west, &south);
2✔
682
            poTransform->Transform(1, &east, &north);
2✔
683
        }
684
    }
685

686
    const bool fixAntiMeridian =
687
        CPLFetchBool(papszOptions, "FIX_ANTIMERIDIAN", false);
19✔
688
    if (fixAntiMeridian && east < west)
19✔
689
    {
690
        east += 360;
1✔
691
    }
692

693
    // Zoom levels of the pyramid.
694
    int maxzoom = 0;
19✔
695
    int tilexsize;
696
    int tileysize;
697
    // Let the longer side determine the max zoom level and x/y tilesizes.
698
    if (xsize >= ysize)
19✔
699
    {
700
        double dtilexsize = xsize;
19✔
701
        while (dtilexsize > 400)  // calculate x tile size
25✔
702
        {
703
            dtilexsize = dtilexsize / 2;
6✔
704
            maxzoom++;
6✔
705
        }
706
        tilexsize = static_cast<int>(dtilexsize);
19✔
707
        tileysize = static_cast<int>(dtilexsize * ysize / xsize);
19✔
708
    }
709
    else
710
    {
UNCOV
711
        double dtileysize = ysize;
×
UNCOV
712
        while (dtileysize > 400)  // calculate y tile size
×
713
        {
714
            dtileysize = dtileysize / 2;
×
715
            maxzoom++;
×
716
        }
717

718
        tileysize = static_cast<int>(dtileysize);
×
UNCOV
719
        tilexsize = static_cast<int>(dtileysize * xsize / ysize);
×
720
    }
721

722
    std::vector<double> zoomxpixels;
38✔
723
    std::vector<double> zoomypixels;
38✔
724
    for (int zoom = 0; zoom < maxzoom + 1; zoom++)
44✔
725
    {
726
        zoomxpixels.push_back(adfGeoTransform[1] * pow(2.0, (maxzoom - zoom)));
25✔
727
        // zoomypixels.push_back(abs(adfGeoTransform[5]) * pow(2.0, (maxzoom -
728
        // zoom)));
729
        zoomypixels.push_back(fabs(adfGeoTransform[5]) *
25✔
730
                              pow(2.0, (maxzoom - zoom)));
25✔
731
    }
732

733
    std::string tmpFileName;
38✔
734
    std::vector<std::string> fileVector;
38✔
735
    int nRet;
736

737
    const char *pszOverlayName = CSLFetchNameValue(papszOptions, "NAME");
19✔
738
    const char *pszOverlayDescription =
739
        CSLFetchNameValue(papszOptions, "DESCRIPTION");
19✔
740

741
    if (isKmz)
19✔
742
    {
743
        tmpFileName = CPLFormFilenameSafe(outDir, "doc.kml", nullptr);
15✔
744
        nRet = GenerateRootKml(tmpFileName.c_str(), pszFilename, north, south,
15✔
745
                               east, west, static_cast<int>(tilexsize),
746
                               pszOverlayName, pszOverlayDescription);
747
        fileVector.push_back(tmpFileName);
15✔
748
    }
749
    else
750
    {
751
        nRet = GenerateRootKml(pszFilename, pszFilename, north, south, east,
4✔
752
                               west, static_cast<int>(tilexsize),
753
                               pszOverlayName, pszOverlayDescription);
754
    }
755

756
    if (nRet == FALSE)
19✔
757
    {
UNCOV
758
        if (zipHandle != nullptr)
×
759
        {
UNCOV
760
            VSIFCloseL(zipHandle);
×
761
            VSIUnlink(pszFilename);
×
762
        }
763
        return nullptr;
×
764
    }
765

766
    const char *pszAltitude = CSLFetchNameValue(papszOptions, "ALTITUDE");
19✔
767
    const char *pszAltitudeMode =
768
        CSLFetchNameValue(papszOptions, "ALTITUDEMODE");
19✔
769
    if (pszAltitudeMode != nullptr)
19✔
770
    {
771
        if (strcmp(pszAltitudeMode, "clampToGround") == 0)
1✔
772
        {
UNCOV
773
            pszAltitudeMode = nullptr;
×
UNCOV
774
            pszAltitude = nullptr;
×
775
        }
776
        else if (strcmp(pszAltitudeMode, "absolute") == 0)
1✔
777
        {
778
            if (pszAltitude == nullptr)
1✔
779
            {
UNCOV
780
                CPLError(CE_Warning, CPLE_AppDefined,
×
781
                         "Using ALTITUDE=0 as default value");
UNCOV
782
                pszAltitude = "0";
×
783
            }
784
        }
785
        else if (strcmp(pszAltitudeMode, "relativeToSeaFloor") == 0)
×
786
        {
787
            /* nothing to do */
788
        }
UNCOV
789
        else if (strcmp(pszAltitudeMode, "clampToSeaFloor") == 0)
×
790
        {
UNCOV
791
            pszAltitude = nullptr;
×
792
        }
793
        else
794
        {
UNCOV
795
            CPLError(CE_Warning, CPLE_AppDefined,
×
796
                     "Ignoring unhandled value of ALTITUDEMODE");
UNCOV
797
            pszAltitudeMode = nullptr;
×
798
            pszAltitude = nullptr;
×
799
        }
800
    }
801

802
    int zoom;
803
    int nTotalTiles = 0;
19✔
804
    int nTileCount = 0;
19✔
805

806
    for (zoom = maxzoom; zoom >= 0; --zoom)
44✔
807
    {
808
        const int rmaxxsize = tilexsize * (1 << (maxzoom - zoom));
25✔
809
        const int rmaxysize = tileysize * (1 << (maxzoom - zoom));
25✔
810

811
        const int xloop = std::max(1, static_cast<int>(xsize / rmaxxsize));
25✔
812
        const int yloop = std::max(1, static_cast<int>(ysize / rmaxysize));
25✔
813
        nTotalTiles += xloop * yloop;
25✔
814
    }
815

816
    // {(x, y): [((childx, childy), hasChildKML), ...], ...}
817
    std::map<std::pair<int, int>,
818
             std::vector<std::pair<std::pair<int, int>, bool>>>
819
        childTiles;
38✔
820
    std::map<std::pair<int, int>,
821
             std::vector<std::pair<std::pair<int, int>, bool>>>
822
        currentTiles;
38✔
823
    std::pair<int, int> childXYKey;
19✔
824
    std::pair<int, int> parentXYKey;
19✔
825
    for (zoom = maxzoom; zoom >= 0; --zoom)
44✔
826
    {
827
        const int rmaxxsize = tilexsize * (1 << (maxzoom - zoom));
25✔
828
        const int rmaxysize = tileysize * (1 << (maxzoom - zoom));
25✔
829

830
        const int xloop = std::max(1, static_cast<int>(xsize / rmaxxsize));
25✔
831
        const int yloop = std::max(1, static_cast<int>(ysize / rmaxysize));
25✔
832

833
        std::stringstream zoomStr;
50✔
834
        zoomStr << zoom;
25✔
835

836
        std::string zoomDir = outDir;
50✔
837
        zoomDir += "/" + zoomStr.str();
25✔
838
        VSIMkdir(zoomDir.c_str(), 0775);
25✔
839

840
        for (int ix = 0; ix < xloop; ix++)
64✔
841
        {
842
            int rxsize = static_cast<int>(rmaxxsize);
39✔
843
            int rx = static_cast<int>(ix * rmaxxsize);
39✔
844
            int dxsize = static_cast<int>(rxsize / rmaxxsize * tilexsize);
39✔
845

846
            std::stringstream ixStr;
78✔
847
            ixStr << ix;
39✔
848

849
            zoomDir = outDir;
39✔
850
            zoomDir += "/" + zoomStr.str();
39✔
851
            zoomDir += "/" + ixStr.str();
39✔
852
            VSIMkdir(zoomDir.c_str(), 0775);
39✔
853

854
            for (int iy = 0; iy < yloop; iy++)
154✔
855
            {
856
                int rysize = static_cast<int>(rmaxysize);
115✔
857
                int ry = static_cast<int>(ysize - (iy * rmaxysize)) - rysize;
115✔
858
                int dysize = static_cast<int>(rysize / rmaxysize * tileysize);
115✔
859

860
                std::stringstream iyStr;
115✔
861
                iyStr << iy;
115✔
862

863
                if (isAutoDriver)
115✔
864
                {
865
                    int flags = KmlSuperOverlayReadDataset::DetectTransparency(
85✔
866
                        rxsize, rysize, rx, ry, dxsize, dysize, poSrcDS);
867
                    if (flags & (KmlSuperOverlayReadDataset::
85✔
868
                                     KMLSO_ContainsPartiallyTransparentPixels |
869
                                 KmlSuperOverlayReadDataset::
870
                                     KMLSO_ContainsTransparentPixels))
871
                    {
872
                        if (!(flags &
45✔
873
                              (KmlSuperOverlayReadDataset::
874
                                   KMLSO_ContainsPartiallyTransparentPixels |
875
                               KmlSuperOverlayReadDataset::
876
                                   KMLSO_ContainsOpaquePixels)))
877
                        {
878
                            // don't bother creating empty tiles
879
                            continue;
40✔
880
                        }
881
                        poOutputTileDriver = poPngOutputTileDriver;
5✔
882
                        isJpegDriver = false;
5✔
883
                    }
884
                    else
885
                    {
886
                        poOutputTileDriver = poJpegOutputTileDriver;
40✔
887
                        isJpegDriver = true;
40✔
888
                    }
889
                }
890

891
                std::string fileExt = ".jpg";
150✔
892
                if (isJpegDriver == false)
75✔
893
                {
894
                    fileExt = ".png";
12✔
895
                }
896
                std::string filename = zoomDir + "/" + iyStr.str() + fileExt;
225✔
897
                if (isKmz)
75✔
898
                {
899
                    fileVector.push_back(filename);
19✔
900
                }
901

902
                GenerateTiles(filename, zoom, rxsize, rysize, ix, iy, rx, ry,
75✔
903
                              dxsize, dysize, bands, poSrcDS,
904
                              poOutputTileDriver, isJpegDriver);
905
                std::string childKmlfile = zoomDir + "/" + iyStr.str() + ".kml";
225✔
906
                if (isKmz)
75✔
907
                {
908
                    fileVector.push_back(childKmlfile);
19✔
909
                }
910

911
                double tmpSouth =
75✔
912
                    adfGeoTransform[3] + adfGeoTransform[5] * ysize;
75✔
913
                double zoomxpix = zoomxpixels[zoom];
75✔
914
                double zoomypix = zoomypixels[zoom];
75✔
915
                if (zoomxpix == 0)
75✔
916
                {
UNCOV
917
                    zoomxpix = 1;
×
918
                }
919

920
                if (zoomypix == 0)
75✔
921
                {
UNCOV
922
                    zoomypix = 1;
×
923
                }
924

925
                childXYKey = std::make_pair(ix, iy);
75✔
926
                parentXYKey = std::make_pair(ix / 2, iy / 2);
75✔
927

928
                // only create child KML if there are child tiles
929
                bool hasChildKML = !childTiles[childXYKey].empty();
75✔
930
                if (!currentTiles.count(parentXYKey))
75✔
931
                {
932
                    currentTiles[parentXYKey] =
35✔
933
                        std::vector<std::pair<std::pair<int, int>, bool>>();
70✔
934
                }
935
                currentTiles[parentXYKey].push_back(
150✔
936
                    std::make_pair(std::make_pair(ix, iy), hasChildKML));
75✔
937
                GenerateChildKml(childKmlfile, zoom, ix, iy, zoomxpix, zoomypix,
75✔
938
                                 dxsize, dysize, tmpSouth, adfGeoTransform[0],
939
                                 xsize, ysize, maxzoom, poTransform.get(),
940
                                 fileExt, fixAntiMeridian, pszAltitude,
941
                                 pszAltitudeMode, childTiles[childXYKey]);
75✔
942

943
                nTileCount++;
75✔
944
                pfnProgress(1.0 * nTileCount / nTotalTiles, "", pProgressData);
75✔
945
            }
946
        }
947
        childTiles = currentTiles;
25✔
948
        currentTiles.clear();
25✔
949
    }
950

951
    if (zipHandle != nullptr)
19✔
952
    {
953
        VSIFCloseL(zipHandle);
15✔
954
    }
955

956
    GDALOpenInfo oOpenInfo(pszFilename, GA_ReadOnly);
38✔
957
    auto poDS = std::unique_ptr<GDALDataset>(
958
        KmlSuperOverlayReadDataset::Open(&oOpenInfo));
38✔
959
    if (!poDS)
19✔
960
        poDS = std::make_unique<KmlSuperOverlayDummyDataset>();
1✔
961
    return poDS.release();
19✔
962
}
963

964
/************************************************************************/
965
/*                            KMLRemoveSlash()                          */
966
/************************************************************************/
967

968
/* replace "a/b/../c" pattern by "a/c" */
969
static std::string KMLRemoveSlash(const char *pszPathIn)
127✔
970
{
971
    std::string osRet(pszPathIn);
254✔
972

973
    while (true)
974
    {
975
        size_t nSlashDotDot = osRet.find("/../");
135✔
976
        if (nSlashDotDot == std::string::npos || nSlashDotDot == 0)
135✔
977
            break;
978
        size_t nPos = nSlashDotDot - 1;
8✔
979
        while (nPos > 0 && osRet[nPos] != '/')
16✔
980
            --nPos;
8✔
981
        if (nPos == 0)
8✔
UNCOV
982
            break;
×
983
        osRet = osRet.substr(0, nPos + 1) +
16✔
984
                osRet.substr(nSlashDotDot + strlen("/../"));
24✔
985
    }
8✔
986
    return osRet;
127✔
987
}
988

989
/************************************************************************/
990
/*                      KmlSuperOverlayReadDataset()                    */
991
/************************************************************************/
992

993
KmlSuperOverlayReadDataset::KmlSuperOverlayReadDataset()
48✔
994
{
995
    m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
48✔
996
    m_oSRS.importFromWkt(SRS_WKT_WGS84_LAT_LONG);
48✔
997
    adfGeoTransform[0] = 0.0;
48✔
998
    adfGeoTransform[1] = 1.0;
48✔
999
    adfGeoTransform[2] = 0.0;
48✔
1000
    adfGeoTransform[3] = 0.0;
48✔
1001
    adfGeoTransform[4] = 0.0;
48✔
1002
    adfGeoTransform[5] = 1.0;
48✔
1003
}
48✔
1004

1005
/************************************************************************/
1006
/*                     ~KmlSuperOverlayReadDataset()                    */
1007
/************************************************************************/
1008

1009
KmlSuperOverlayReadDataset::~KmlSuperOverlayReadDataset()
96✔
1010

1011
{
1012
    if (psRoot != nullptr)
48✔
1013
        CPLDestroyXMLNode(psRoot);
38✔
1014
    KmlSuperOverlayReadDataset::CloseDependentDatasets();
48✔
1015
}
96✔
1016

1017
/************************************************************************/
1018
/*                         CloseDependentDatasets()                     */
1019
/************************************************************************/
1020

1021
int KmlSuperOverlayReadDataset::CloseDependentDatasets()
48✔
1022
{
1023
    int bRet = FALSE;
48✔
1024
    if (poDSIcon)
48✔
1025
    {
1026
        CPLString l_osFilename(poDSIcon->GetDescription());
38✔
1027
        poDSIcon.reset();
38✔
1028
        VSIUnlink(l_osFilename);
38✔
1029
        bRet = TRUE;
38✔
1030
    }
1031

1032
    LinkedDataset *psCur = psFirstLink;
48✔
1033
    psFirstLink = nullptr;
48✔
1034
    psLastLink = nullptr;
48✔
1035

1036
    while (psCur != nullptr)
61✔
1037
    {
1038
        LinkedDataset *psNext = psCur->psNext;
13✔
1039
        if (psCur->poDS != nullptr)
13✔
1040
        {
1041
            if (psCur->poDS->nRefCount == 1)
13✔
1042
                bRet = TRUE;
13✔
1043
            GDALClose(psCur->poDS);
13✔
1044
        }
1045
        delete psCur;
13✔
1046
        psCur = psNext;
13✔
1047
    }
1048

1049
    if (!m_apoOverviewDS.empty())
48✔
1050
    {
1051
        bRet = TRUE;
8✔
1052
        m_apoOverviewDS.clear();
8✔
1053
    }
1054

1055
    return bRet;
48✔
1056
}
1057

1058
/************************************************************************/
1059
/*                          GetSpatialRef()                             */
1060
/************************************************************************/
1061

1062
const OGRSpatialReference *KmlSuperOverlayReadDataset::GetSpatialRef() const
1✔
1063

1064
{
1065
    return &m_oSRS;
1✔
1066
}
1067

1068
/************************************************************************/
1069
/*                          GetGeoTransform()                           */
1070
/************************************************************************/
1071

1072
CPLErr KmlSuperOverlayReadDataset::GetGeoTransform(double *padfGeoTransform)
1✔
1073
{
1074
    memcpy(padfGeoTransform, adfGeoTransform.data(), 6 * sizeof(double));
1✔
1075
    return CE_None;
1✔
1076
}
1077

1078
/************************************************************************/
1079
/*                        KmlSuperOverlayRasterBand()                   */
1080
/************************************************************************/
1081

1082
KmlSuperOverlayRasterBand::KmlSuperOverlayRasterBand(
192✔
1083
    KmlSuperOverlayReadDataset *poDSIn, int /* nBand*/)
192✔
1084
{
1085
    nRasterXSize = poDSIn->nRasterXSize;
192✔
1086
    nRasterYSize = poDSIn->nRasterYSize;
192✔
1087
    eDataType = GDT_Byte;
192✔
1088
    nBlockXSize = 256;
192✔
1089
    nBlockYSize = 256;
192✔
1090
}
192✔
1091

1092
/************************************************************************/
1093
/*                               IReadBlock()                           */
1094
/************************************************************************/
1095

1096
CPLErr KmlSuperOverlayRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff,
8✔
1097
                                             void *pData)
1098
{
1099
    int nXOff = nBlockXOff * nBlockXSize;
8✔
1100
    int nYOff = nBlockYOff * nBlockYSize;
8✔
1101
    int nXSize = nBlockXSize;
8✔
1102
    int nYSize = nBlockYSize;
8✔
1103
    if (nXOff + nXSize > nRasterXSize)
8✔
1104
        nXSize = nRasterXSize - nXOff;
4✔
1105
    if (nYOff + nYSize > nRasterYSize)
8✔
1106
        nYSize = nRasterYSize - nYOff;
8✔
1107

1108
    GDALRasterIOExtraArg sExtraArg;
1109
    INIT_RASTERIO_EXTRA_ARG(sExtraArg);
8✔
1110

1111
    return IRasterIO(GF_Read, nXOff, nYOff, nXSize, nYSize, pData, nXSize,
16✔
1112
                     nYSize, eDataType, 1, nBlockXSize, &sExtraArg);
16✔
1113
}
1114

1115
/************************************************************************/
1116
/*                          GetColorInterpretation()                    */
1117
/************************************************************************/
1118

1119
GDALColorInterp KmlSuperOverlayRasterBand::GetColorInterpretation()
15✔
1120
{
1121
    return static_cast<GDALColorInterp>(GCI_RedBand + nBand - 1);
15✔
1122
}
1123

1124
/************************************************************************/
1125
/*                            IRasterIO()                               */
1126
/************************************************************************/
1127

1128
CPLErr KmlSuperOverlayRasterBand::IRasterIO(
50✔
1129
    GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
1130
    void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
1131
    GSpacing nPixelSpace, GSpacing nLineSpace, GDALRasterIOExtraArg *psExtraArg)
1132
{
1133
    KmlSuperOverlayReadDataset *poGDS =
1134
        cpl::down_cast<KmlSuperOverlayReadDataset *>(poDS);
50✔
1135

1136
    return poGDS->IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData,
50✔
1137
                            nBufXSize, nBufYSize, eBufType, 1, &nBand,
1138
                            nPixelSpace, nLineSpace, 0, psExtraArg);
50✔
1139
}
1140

1141
/************************************************************************/
1142
/*                          GetOverviewCount()                          */
1143
/************************************************************************/
1144

1145
int KmlSuperOverlayRasterBand::GetOverviewCount()
31✔
1146
{
1147
    KmlSuperOverlayReadDataset *poGDS =
1148
        cpl::down_cast<KmlSuperOverlayReadDataset *>(poDS);
31✔
1149

1150
    return static_cast<int>(poGDS->m_apoOverviewDS.size());
31✔
1151
}
1152

1153
/************************************************************************/
1154
/*                           GetOverview()                              */
1155
/************************************************************************/
1156

1157
GDALRasterBand *KmlSuperOverlayRasterBand::GetOverview(int iOvr)
37✔
1158
{
1159
    KmlSuperOverlayReadDataset *poGDS =
1160
        cpl::down_cast<KmlSuperOverlayReadDataset *>(poDS);
37✔
1161

1162
    if (iOvr < 0 || iOvr >= static_cast<int>(poGDS->m_apoOverviewDS.size()))
37✔
UNCOV
1163
        return nullptr;
×
1164

1165
    return poGDS->m_apoOverviewDS[iOvr]->GetRasterBand(nBand);
37✔
1166
}
1167

1168
/************************************************************************/
1169
/*                     KmlSuperOverlayGetBoundingBox()                  */
1170
/************************************************************************/
1171

1172
static bool KmlSuperOverlayGetBoundingBox(const CPLXMLNode *psNode,
118✔
1173
                                          std::array<double, 4> &adfExtents)
1174
{
1175
    const CPLXMLNode *psBox = CPLGetXMLNode(psNode, "LatLonBox");
118✔
1176
    if (!psBox)
118✔
1177
        psBox = CPLGetXMLNode(psNode, "LatLonAltBox");
77✔
1178
    if (psBox)
118✔
1179
    {
1180
        const char *pszNorth = CPLGetXMLValue(psBox, "north", nullptr);
116✔
1181
        const char *pszSouth = CPLGetXMLValue(psBox, "south", nullptr);
116✔
1182
        const char *pszEast = CPLGetXMLValue(psBox, "east", nullptr);
116✔
1183
        const char *pszWest = CPLGetXMLValue(psBox, "west", nullptr);
116✔
1184
        if (pszNorth && pszSouth && pszEast && pszWest)
116✔
1185
        {
1186
            adfExtents[0] = CPLAtof(pszWest);
116✔
1187
            adfExtents[1] = CPLAtof(pszSouth);
116✔
1188
            adfExtents[2] = CPLAtof(pszEast);
116✔
1189
            adfExtents[3] = CPLAtof(pszNorth);
116✔
1190

1191
            return true;
116✔
1192
        }
1193
    }
1194
    else
1195
    {
1196
        const CPLXMLNode *psLatLonQuad = CPLGetXMLNode(psNode, "gx:LatLonQuad");
2✔
1197
        if (psLatLonQuad)
2✔
1198
        {
1199
            const CPLStringList aosTuples(CSLTokenizeString2(
1200
                CPLGetXMLValue(psLatLonQuad, "coordinates", ""), " \t\n\r", 0));
2✔
1201
            if (aosTuples.size() == 4)
2✔
1202
            {
1203
                const CPLStringList aosLL(
1204
                    CSLTokenizeString2(aosTuples[0], ",", 0));
2✔
1205
                const CPLStringList aosLR(
1206
                    CSLTokenizeString2(aosTuples[1], ",", 0));
2✔
1207
                const CPLStringList aosUR(
1208
                    CSLTokenizeString2(aosTuples[2], ",", 0));
2✔
1209
                const CPLStringList aosUL(
1210
                    CSLTokenizeString2(aosTuples[3], ",", 0));
2✔
1211
                if (aosLL.size() >= 2 && aosLR.size() >= 2 &&
4✔
1212
                    aosUR.size() >= 2 && aosUL.size() >= 2 &&
2✔
1213
                    strcmp(aosLL[0], aosUL[0]) == 0 &&
2✔
1214
                    strcmp(aosLL[1], aosLR[1]) == 0 &&
1✔
1215
                    strcmp(aosLR[0], aosUR[0]) == 0 &&
5✔
1216
                    strcmp(aosUR[1], aosUL[1]) == 0)
1✔
1217
                {
1218
                    adfExtents[0] = CPLAtof(aosLL[0]);
1✔
1219
                    adfExtents[1] = CPLAtof(aosLL[1]);
1✔
1220
                    adfExtents[2] = CPLAtof(aosUR[0]);
1✔
1221
                    adfExtents[3] = CPLAtof(aosUR[1]);
1✔
1222
                    return true;
1✔
1223
                }
1224
            }
1225
        }
1226
    }
1227

1228
    return false;
1✔
1229
}
1230

1231
/************************************************************************/
1232
/*                            IRasterIO()                               */
1233
/************************************************************************/
1234

1235
class SubImageDesc
1236
{
1237
  public:
1238
    GDALDataset *poDS = nullptr;
1239
    std::array<double, 4> adfExtents = {0, 0, 0, 0};
1240
};
1241

1242
CPLErr KmlSuperOverlayReadDataset::IRasterIO(
91✔
1243
    GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
1244
    void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
1245
    int nBandCount, BANDMAP_TYPE panBandMap, GSpacing nPixelSpace,
1246
    GSpacing nLineSpace, GSpacing nBandSpace, GDALRasterIOExtraArg *psExtraArg)
1247
{
1248
    if (eRWFlag == GF_Write)
91✔
UNCOV
1249
        return CE_Failure;
×
1250

1251
    if (bIsOvr)
91✔
1252
    {
1253
        GDALRasterIOExtraArg sExtraArgs;
1254
        GDALCopyRasterIOExtraArg(&sExtraArgs, psExtraArg);
1✔
1255
        const int nOvrFactor = poParent->nFactor / nFactor;
1✔
1256
        if (sExtraArgs.bFloatingPointWindowValidity)
1✔
1257
        {
UNCOV
1258
            sExtraArgs.dfXOff *= nOvrFactor;
×
UNCOV
1259
            sExtraArgs.dfYOff *= nOvrFactor;
×
UNCOV
1260
            sExtraArgs.dfXSize *= nOvrFactor;
×
1261
            sExtraArgs.dfYSize *= nOvrFactor;
×
1262
        }
1263
        return poParent->IRasterIO(
1✔
1264
            eRWFlag, nXOff * nOvrFactor, nYOff * nOvrFactor,
1265
            nXSize * nOvrFactor, nYSize * nOvrFactor, pData, nBufXSize,
1266
            nBufYSize, eBufType, nBandCount, panBandMap, nPixelSpace,
1267
            nLineSpace, nBandSpace, &sExtraArgs);
1✔
1268
    }
1269

1270
    double dfXOff = 1.0 * nXOff / nFactor;
90✔
1271
    double dfYOff = 1.0 * nYOff / nFactor;
90✔
1272
    double dfXSize = 1.0 * nXSize / nFactor;
90✔
1273
    double dfYSize = 1.0 * nYSize / nFactor;
90✔
1274

1275
    int nIconCount = poDSIcon->GetRasterCount();
90✔
1276

1277
    if (nBufXSize > dfXSize || nBufYSize > dfYSize)
90✔
1278
    {
1279
        const double dfRequestXMin =
1280
            adfGeoTransform[0] + nXOff * adfGeoTransform[1];
28✔
1281
        const double dfRequestXMax =
1282
            adfGeoTransform[0] + (nXOff + nXSize) * adfGeoTransform[1];
28✔
1283
        const double dfRequestYMin =
1284
            adfGeoTransform[3] + (nYOff + nYSize) * adfGeoTransform[5];
28✔
1285
        const double dfRequestYMax =
1286
            adfGeoTransform[3] + nYOff * adfGeoTransform[5];
28✔
1287

1288
        const CPLXMLNode *psIter = psDocument->psChild;
28✔
1289
        std::vector<SubImageDesc> aoImages;
28✔
1290
        const double dfXRes = adfGeoTransform[1] * nFactor;
28✔
1291
        const double dfYRes = -adfGeoTransform[5] * nFactor;
28✔
1292
        double dfNewXRes = dfXRes;
28✔
1293
        double dfNewYRes = dfYRes;
28✔
1294

1295
        while (psIter != nullptr)
242✔
1296
        {
1297
            const CPLXMLNode *psRegion = nullptr;
214✔
1298
            const CPLXMLNode *psLink = nullptr;
214✔
1299
            std::array<double, 4> adfExtents = {0, 0, 0, 0};
214✔
1300
            const char *pszHref = nullptr;
214✔
1301
            if (psIter->eType == CXT_Element &&
642✔
1302
                strcmp(psIter->pszValue, "NetworkLink") == 0 &&
214✔
1303
                (psRegion = CPLGetXMLNode(psIter, "Region")) != nullptr &&
74✔
1304
                (psLink = CPLGetXMLNode(psIter, "Link")) != nullptr &&
74✔
1305
                KmlSuperOverlayGetBoundingBox(psRegion, adfExtents) &&
502✔
1306
                (pszHref = CPLGetXMLValue(psLink, "href", nullptr)) != nullptr)
74✔
1307
            {
1308
                if (dfRequestXMin < adfExtents[2] &&
74✔
1309
                    dfRequestXMax > adfExtents[0] &&
74✔
1310
                    dfRequestYMin < adfExtents[3] &&
206✔
1311
                    dfRequestYMax > adfExtents[1])
58✔
1312
                {
1313
                    CPLString osSubFilename;
78✔
1314
                    if (STARTS_WITH(pszHref, "http"))
39✔
1315
                        osSubFilename =
UNCOV
1316
                            CPLSPrintf("/vsicurl_streaming/%s", pszHref);
×
1317
                    else
1318
                    {
1319
                        const char *pszBaseFilename = osFilename.c_str();
39✔
1320
                        if (EQUAL(CPLGetExtensionSafe(pszBaseFilename).c_str(),
39✔
1321
                                  "kmz") &&
39✔
UNCOV
1322
                            !STARTS_WITH(pszBaseFilename, "/vsizip/"))
×
1323
                        {
UNCOV
1324
                            osSubFilename = "/vsizip/";
×
1325
                            osSubFilename += CPLGetPathSafe(pszBaseFilename);
×
UNCOV
1326
                            osSubFilename += "/";
×
1327
                            osSubFilename += pszHref;
×
1328
                        }
1329
                        else
1330
                        {
1331
                            osSubFilename = CPLFormFilenameSafe(
39✔
1332
                                CPLGetPathSafe(pszBaseFilename).c_str(),
78✔
1333
                                pszHref, nullptr);
39✔
1334
                        }
1335
                        osSubFilename = KMLRemoveSlash(osSubFilename);
39✔
1336
                    }
1337

1338
                    KmlSuperOverlayReadDataset *poSubImageDS = nullptr;
39✔
1339
                    if (EQUAL(CPLGetExtensionSafe(osSubFilename).c_str(),
39✔
1340
                              "kml"))
1341
                    {
1342
                        KmlSuperOverlayReadDataset *poRoot =
39✔
1343
                            poParent ? poParent : this;
39✔
1344
                        LinkedDataset *psLinkDS =
1345
                            poRoot->oMapChildren[osSubFilename];
39✔
1346
                        if (psLinkDS == nullptr)
39✔
1347
                        {
1348
                            if (poRoot->oMapChildren.size() == 64)
13✔
1349
                            {
UNCOV
1350
                                psLinkDS = poRoot->psLastLink;
×
UNCOV
1351
                                CPLAssert(psLinkDS);
×
UNCOV
1352
                                poRoot->oMapChildren.erase(
×
1353
                                    psLinkDS->osSubFilename);
×
1354
                                GDALClose(psLinkDS->poDS);
×
1355
                                if (psLinkDS->psPrev != nullptr)
×
1356
                                {
1357
                                    poRoot->psLastLink = psLinkDS->psPrev;
×
1358
                                    psLinkDS->psPrev->psNext = nullptr;
×
1359
                                }
1360
                                else
1361
                                {
UNCOV
1362
                                    CPLAssert(psLinkDS == poRoot->psFirstLink);
×
UNCOV
1363
                                    poRoot->psFirstLink = nullptr;
×
UNCOV
1364
                                    poRoot->psLastLink = nullptr;
×
1365
                                }
1366
                            }
1367
                            else
1368
                                psLinkDS = new LinkedDataset();
13✔
1369

1370
                            poRoot->oMapChildren[osSubFilename] = psLinkDS;
13✔
1371
                            poSubImageDS =
1372
                                cpl::down_cast<KmlSuperOverlayReadDataset *>(
13✔
1373
                                    KmlSuperOverlayReadDataset::Open(
1374
                                        osSubFilename, poRoot));
1375
                            if (poSubImageDS)
13✔
1376
                                poSubImageDS->MarkAsShared();
13✔
1377
                            else
UNCOV
1378
                                CPLDebug("KMLSuperOverlay", "Cannot open %s",
×
1379
                                         osSubFilename.c_str());
1380
                            psLinkDS->osSubFilename = osSubFilename;
13✔
1381
                            psLinkDS->poDS = poSubImageDS;
13✔
1382
                            psLinkDS->psPrev = nullptr;
13✔
1383
                            psLinkDS->psNext = poRoot->psFirstLink;
13✔
1384
                            if (poRoot->psFirstLink != nullptr)
13✔
1385
                            {
1386
                                CPLAssert(poRoot->psFirstLink->psPrev ==
8✔
1387
                                          nullptr);
1388
                                poRoot->psFirstLink->psPrev = psLinkDS;
8✔
1389
                            }
1390
                            else
1391
                                poRoot->psLastLink = psLinkDS;
5✔
1392
                            poRoot->psFirstLink = psLinkDS;
13✔
1393
                        }
1394
                        else
1395
                        {
1396
                            poSubImageDS = psLinkDS->poDS;
26✔
1397
                            if (psLinkDS != poRoot->psFirstLink)
26✔
1398
                            {
1399
                                if (psLinkDS == poRoot->psLastLink)
26✔
1400
                                {
1401
                                    poRoot->psLastLink = psLinkDS->psPrev;
25✔
1402
                                    CPLAssert(poRoot->psLastLink != nullptr);
25✔
1403
                                    poRoot->psLastLink->psNext = nullptr;
25✔
1404
                                }
1405
                                else
1406
                                    psLinkDS->psNext->psPrev = psLinkDS->psPrev;
1✔
1407
                                CPLAssert(psLinkDS->psPrev != nullptr);
26✔
1408
                                psLinkDS->psPrev->psNext = psLinkDS->psNext;
26✔
1409
                                psLinkDS->psPrev = nullptr;
26✔
1410
                                poRoot->psFirstLink->psPrev = psLinkDS;
26✔
1411
                                psLinkDS->psNext = poRoot->psFirstLink;
26✔
1412
                                poRoot->psFirstLink = psLinkDS;
26✔
1413
                            }
1414
                        }
1415
                    }
1416
                    if (poSubImageDS)
39✔
1417
                    {
1418
                        int nSubImageXSize = poSubImageDS->GetRasterXSize();
39✔
1419
                        int nSubImageYSize = poSubImageDS->GetRasterYSize();
39✔
1420
                        adfExtents[0] = poSubImageDS->adfGeoTransform[0];
39✔
1421
                        adfExtents[1] =
78✔
1422
                            poSubImageDS->adfGeoTransform[3] +
39✔
1423
                            nSubImageYSize * poSubImageDS->adfGeoTransform[5];
39✔
1424
                        adfExtents[2] =
78✔
1425
                            poSubImageDS->adfGeoTransform[0] +
39✔
1426
                            nSubImageXSize * poSubImageDS->adfGeoTransform[1];
39✔
1427
                        adfExtents[3] = poSubImageDS->adfGeoTransform[3];
39✔
1428

1429
                        double dfSubXRes =
1430
                            (adfExtents[2] - adfExtents[0]) / nSubImageXSize;
39✔
1431
                        double dfSubYRes =
1432
                            (adfExtents[3] - adfExtents[1]) / nSubImageYSize;
39✔
1433

1434
                        if (dfSubXRes < dfNewXRes)
39✔
1435
                            dfNewXRes = dfSubXRes;
19✔
1436
                        if (dfSubYRes < dfNewYRes)
39✔
1437
                            dfNewYRes = dfSubYRes;
19✔
1438

1439
                        SubImageDesc oImageDesc;
39✔
1440
                        oImageDesc.poDS = poSubImageDS;
39✔
1441
                        poSubImageDS->Reference();
39✔
1442
                        oImageDesc.adfExtents = adfExtents;
39✔
1443
                        aoImages.push_back(oImageDesc);
39✔
1444
                    }
1445
                    CPL_IGNORE_RET_VAL(osSubFilename);
39✔
1446
                }
1447
            }
1448
            psIter = psIter->psNext;
214✔
1449
        }
1450

1451
        if (dfNewXRes < dfXRes || dfNewYRes < dfYRes)
28✔
1452
        {
1453
            const double dfXFactor = dfXRes / dfNewXRes;
19✔
1454
            const double dfYFactor = dfYRes / dfNewYRes;
19✔
1455
            auto poVRTDS = std::make_unique<VRTDataset>(
UNCOV
1456
                static_cast<int>(nRasterXSize * dfXFactor + 0.5),
×
1457
                static_cast<int>(nRasterYSize * dfYFactor + 0.5));
19✔
1458

1459
            for (int iBandIdx = 0; iBandIdx < 4; iBandIdx++)
95✔
1460
            {
1461
                poVRTDS->AddBand(GDT_Byte, nullptr);
76✔
1462

1463
                auto poVRTBand = static_cast<VRTSourcedRasterBand *>(
1464
                    poVRTDS->GetRasterBand(iBandIdx + 1));
76✔
1465
                const int nBand = iBandIdx + 1;
76✔
1466
                if (nBand <= nIconCount || (nIconCount == 1 && nBand != 4))
76✔
1467
                {
1468
                    poVRTBand->AddSimpleSource(
74✔
1469
                        poDSIcon->GetRasterBand(nBand <= nIconCount ? nBand
1470
                                                                    : 1),
1471
                        0, 0, nRasterXSize, nRasterYSize, 0, 0,
74✔
1472
                        poVRTDS->GetRasterXSize(), poVRTDS->GetRasterYSize(),
74✔
1473
                        nullptr, VRT_NODATA_UNSET);
1474
                }
1475
                else
1476
                {
1477
                    poVRTBand->AddComplexSource(
2✔
1478
                        poDSIcon->GetRasterBand(1), 0, 0, nRasterXSize,
2✔
1479
                        nRasterYSize, 0, 0, poVRTDS->GetRasterXSize(),
2✔
1480
                        poVRTDS->GetRasterYSize(), VRT_NODATA_UNSET, 0, 255);
2✔
1481
                }
1482
            }
1483

1484
            for (const auto &oImage : aoImages)
58✔
1485
            {
1486
                const int nDstXOff = static_cast<int>(
1487
                    (oImage.adfExtents[0] - adfGeoTransform[0]) / dfNewXRes +
39✔
1488
                    0.5);
39✔
1489
                const int nDstYOff = static_cast<int>(
1490
                    (adfGeoTransform[3] - oImage.adfExtents[3]) / dfNewYRes +
39✔
1491
                    0.5);
39✔
1492
                const int nDstXSize = static_cast<int>(
1493
                    (oImage.adfExtents[2] - oImage.adfExtents[0]) / dfNewXRes +
39✔
1494
                    0.5);
39✔
1495
                const int nDstYSize = static_cast<int>(
1496
                    (oImage.adfExtents[3] - oImage.adfExtents[1]) / dfNewYRes +
39✔
1497
                    0.5);
39✔
1498

1499
                const int nSrcBandCount = oImage.poDS->GetRasterCount();
39✔
1500
                for (int iBandIdx = 0; iBandIdx < 4; iBandIdx++)
195✔
1501
                {
1502
                    const int nBand = iBandIdx + 1;
156✔
1503
                    auto poVRTBand = static_cast<VRTSourcedRasterBand *>(
1504
                        poVRTDS->GetRasterBand(iBandIdx + 1));
156✔
1505

1506
                    if (nBand <= nSrcBandCount ||
156✔
UNCOV
1507
                        (nSrcBandCount == 1 && nBand != 4))
×
1508
                    {
1509
                        poVRTBand->AddSimpleSource(
156✔
1510
                            oImage.poDS->GetRasterBand(
156✔
1511
                                nBand <= nSrcBandCount ? nBand : 1),
1512
                            0, 0, oImage.poDS->GetRasterXSize(),
156✔
1513
                            oImage.poDS->GetRasterYSize(), nDstXOff, nDstYOff,
156✔
1514
                            nDstXSize, nDstYSize, nullptr, VRT_NODATA_UNSET);
1515
                    }
1516
                    else
1517
                    {
UNCOV
1518
                        poVRTBand->AddComplexSource(
×
UNCOV
1519
                            oImage.poDS->GetRasterBand(1), 0, 0,
×
UNCOV
1520
                            oImage.poDS->GetRasterXSize(),
×
1521
                            oImage.poDS->GetRasterYSize(), nDstXOff, nDstYOff,
×
1522
                            nDstXSize, nDstYSize, VRT_NODATA_UNSET, 0, 255);
1523
                    }
1524
                }
1525
            }
1526

1527
            int nReqXOff = static_cast<int>(dfXOff * dfXFactor + 0.5);
19✔
1528
            int nReqYOff = static_cast<int>(dfYOff * dfYFactor + 0.5);
19✔
1529
            int nReqXSize = static_cast<int>(dfXSize * dfXFactor + 0.5);
19✔
1530
            int nReqYSize = static_cast<int>(dfYSize * dfYFactor + 0.5);
19✔
1531
            if (nReqXOff + nReqXSize > poVRTDS->GetRasterXSize())
19✔
UNCOV
1532
                nReqXSize = poVRTDS->GetRasterXSize() - nReqXOff;
×
1533
            if (nReqYOff + nReqYSize > poVRTDS->GetRasterYSize())
19✔
UNCOV
1534
                nReqYSize = poVRTDS->GetRasterYSize() - nReqYOff;
×
1535

1536
            GDALRasterIOExtraArg sExtraArgs;
1537
            INIT_RASTERIO_EXTRA_ARG(sExtraArgs);
19✔
1538
            // cppcheck-suppress redundantAssignment
1539
            sExtraArgs.eResampleAlg = psExtraArg->eResampleAlg;
19✔
1540
            CPLErr eErr = poVRTDS->RasterIO(
19✔
1541
                eRWFlag, nReqXOff, nReqYOff, nReqXSize, nReqYSize, pData,
1542
                nBufXSize, nBufYSize, eBufType, nBandCount, panBandMap,
1543
                nPixelSpace, nLineSpace, nBandSpace, &sExtraArgs);
1544

1545
            for (auto &oImage : aoImages)
58✔
1546
            {
1547
                oImage.poDS->Dereference();
39✔
1548
            }
1549

1550
            return eErr;
19✔
1551
        }
1552
    }
1553

1554
    GDALProgressFunc pfnProgressGlobal = psExtraArg->pfnProgress;
71✔
1555
    void *pProgressDataGlobal = psExtraArg->pProgressData;
71✔
1556
    CPLErr eErr = CE_None;
71✔
1557

1558
    for (int iBandIdx = 0; iBandIdx < nBandCount && eErr == CE_None; iBandIdx++)
148✔
1559
    {
1560
        int nBand = panBandMap[iBandIdx];
77✔
1561

1562
        if ((nIconCount > 1 || nBand == 4) && nBand > nIconCount)
77✔
1563
        {
UNCOV
1564
            GByte nVal = (nBand == 4) ? 255 : 0;
×
UNCOV
1565
            for (int j = 0; j < nBufYSize; j++)
×
1566
            {
1567
                GDALCopyWords(&nVal, GDT_Byte, 0,
×
1568
                              static_cast<GByte *>(pData) + j * nLineSpace +
×
UNCOV
1569
                                  iBandIdx * nBandSpace,
×
1570
                              eBufType, static_cast<int>(nPixelSpace),
1571
                              nBufXSize);
1572
            }
UNCOV
1573
            continue;
×
1574
        }
1575

1576
        int nIconBand = (nIconCount == 1) ? 1 : nBand;
77✔
1577

1578
        int nReqXOff = static_cast<int>(dfXOff + 0.5);
77✔
1579
        int nReqYOff = static_cast<int>(dfYOff + 0.5);
77✔
1580
        int nReqXSize = static_cast<int>(dfXSize + 0.5);
77✔
1581
        int nReqYSize = static_cast<int>(dfYSize + 0.5);
77✔
1582
        if (nReqXOff + nReqXSize > poDSIcon->GetRasterXSize())
77✔
UNCOV
1583
            nReqXSize = poDSIcon->GetRasterXSize() - nReqXOff;
×
1584
        if (nReqYOff + nReqYSize > poDSIcon->GetRasterYSize())
77✔
UNCOV
1585
            nReqYSize = poDSIcon->GetRasterYSize() - nReqYOff;
×
1586

1587
        GDALRasterIOExtraArg sExtraArgs;
1588
        INIT_RASTERIO_EXTRA_ARG(sExtraArgs);
77✔
1589
        // cppcheck-suppress redundantAssignment
1590
        sExtraArgs.eResampleAlg = psExtraArg->eResampleAlg;
77✔
1591
        sExtraArgs.pfnProgress = GDALScaledProgress;
77✔
1592
        sExtraArgs.pProgressData = GDALCreateScaledProgress(
154✔
1593
            1.0 * iBandIdx / nBandCount, 1.0 * (iBandIdx + 1) / nBandCount,
77✔
1594
            pfnProgressGlobal, pProgressDataGlobal);
1595

1596
        eErr = poDSIcon->GetRasterBand(nIconBand)->RasterIO(
77✔
1597
            eRWFlag, nReqXOff, nReqYOff, nReqXSize, nReqYSize,
1598
            static_cast<GByte *>(pData) + nBandSpace * iBandIdx, nBufXSize,
77✔
1599
            nBufYSize, eBufType, nPixelSpace, nLineSpace, &sExtraArgs);
1600

1601
        GDALDestroyScaledProgress(sExtraArgs.pProgressData);
77✔
1602
    }
1603

1604
    psExtraArg->pfnProgress = pfnProgressGlobal;
71✔
1605
    psExtraArg->pProgressData = pProgressDataGlobal;
71✔
1606

1607
    return eErr;
71✔
1608
}
1609

1610
/************************************************************************/
1611
/*                    KmlSuperOverlayFindRegionStart()                  */
1612
/************************************************************************/
1613

1614
static int KmlSuperOverlayFindRegionStartInternal(CPLXMLNode *psNode,
1,462✔
1615
                                                  CPLXMLNode **ppsRegion,
1616
                                                  CPLXMLNode **ppsDocument,
1617
                                                  CPLXMLNode **ppsGroundOverlay,
1618
                                                  CPLXMLNode **ppsLink)
1619
{
1620
    CPLXMLNode *psRegion = nullptr;
1,462✔
1621
    CPLXMLNode *psLink = nullptr;
1,462✔
1622
    CPLXMLNode *psGroundOverlay = nullptr;
1,462✔
1623
    if (strcmp(psNode->pszValue, "NetworkLink") == 0 &&
2,947✔
1624
        (psRegion = CPLGetXMLNode(psNode, "Region")) != nullptr &&
1,485✔
1625
        (psLink = CPLGetXMLNode(psNode, "Link")) != nullptr)
23✔
1626
    {
1627
        *ppsRegion = psRegion;
23✔
1628
        *ppsLink = psLink;
23✔
1629
        return TRUE;
23✔
1630
    }
1631
    if ((strcmp(psNode->pszValue, "Document") == 0 ||
4,211✔
1632
         strcmp(psNode->pszValue, "Folder") == 0) &&
1,333✔
1633
        (psRegion = CPLGetXMLNode(psNode, "Region")) != nullptr &&
2,821✔
1634
        (psGroundOverlay = CPLGetXMLNode(psNode, "GroundOverlay")) != nullptr)
49✔
1635
    {
1636
        *ppsDocument = psNode;
49✔
1637
        *ppsRegion = psRegion;
49✔
1638
        *ppsGroundOverlay = psGroundOverlay;
49✔
1639
        return TRUE;
49✔
1640
    }
1641

1642
    CPLXMLNode *psIter = psNode->psChild;
1,390✔
1643
    while (psIter != nullptr)
4,094✔
1644
    {
1645
        if (psIter->eType == CXT_Element)
2,799✔
1646
        {
1647
            if (KmlSuperOverlayFindRegionStartInternal(
1,246✔
1648
                    psIter, ppsRegion, ppsDocument, ppsGroundOverlay, ppsLink))
1,246✔
1649
                return TRUE;
95✔
1650
        }
1651

1652
        psIter = psIter->psNext;
2,704✔
1653
    }
1654

1655
    return FALSE;
1,295✔
1656
}
1657

1658
static int KmlSuperOverlayFindRegionStart(CPLXMLNode *psNode,
108✔
1659
                                          CPLXMLNode **ppsRegion,
1660
                                          CPLXMLNode **ppsDocument,
1661
                                          CPLXMLNode **ppsGroundOverlay,
1662
                                          CPLXMLNode **ppsLink)
1663
{
1664
    CPLXMLNode *psIter = psNode;
108✔
1665
    while (psIter != nullptr)
252✔
1666
    {
1667
        if (psIter->eType == CXT_Element)
216✔
1668
        {
1669
            if (KmlSuperOverlayFindRegionStartInternal(
216✔
1670
                    psIter, ppsRegion, ppsDocument, ppsGroundOverlay, ppsLink))
216✔
1671
                return TRUE;
72✔
1672
        }
1673

1674
        psIter = psIter->psNext;
144✔
1675
    }
1676

1677
    return FALSE;
36✔
1678
}
1679

1680
/************************************************************************/
1681
/*                             Identify()                               */
1682
/************************************************************************/
1683

1684
int KmlSuperOverlayReadDataset::Identify(GDALOpenInfo *poOpenInfo)
57,888✔
1685

1686
{
1687
    const char *pszExt = poOpenInfo->osExtension.c_str();
57,888✔
1688
    if (EQUAL(pszExt, "kmz"))
57,888✔
1689
        return -1;
143✔
1690
    if (poOpenInfo->nHeaderBytes == 0)
57,745✔
1691
        return FALSE;
53,119✔
1692
    if (
4,626✔
1693
#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
1694
        !EQUAL(pszExt, "kml") ||
4,626✔
1695
#endif
1696
        strstr(reinterpret_cast<const char *>(poOpenInfo->pabyHeader),
111✔
1697
               "<kml") == nullptr)
1698
        return FALSE;
4,515✔
1699

1700
    for (int i = 0; i < 2; i++)
245✔
1701
    {
1702
        // Leave below variable here as the TryToIngest() at end might
1703
        // invalidate it
1704
        const char *pszText =
178✔
1705
            reinterpret_cast<const char *>(poOpenInfo->pabyHeader);
1706
        if (strstr(pszText, "<NetworkLink>") != nullptr &&
178✔
1707
            strstr(pszText, "<Region>") != nullptr &&
28✔
1708
            strstr(pszText, "<Link>") != nullptr)
28✔
1709
            return TRUE;
18✔
1710

1711
        if (strstr(pszText, "<Document>") != nullptr &&
160✔
1712
            strstr(pszText, "<Region>") != nullptr &&
22✔
1713
            strstr(pszText, "<GroundOverlay>") != nullptr)
18✔
1714
            return TRUE;
18✔
1715

1716
        if (strstr(pszText, "<GroundOverlay>") != nullptr &&
142✔
1717
            strstr(pszText, "<Icon>") != nullptr &&
8✔
1718
            strstr(pszText, "<href>") != nullptr &&
8✔
1719
            (strstr(pszText, "<LatLonBox>") != nullptr ||
8✔
1720
             strstr(pszText, "<gx:LatLonQuad>") != nullptr))
2✔
1721
            return TRUE;
8✔
1722

1723
        if (i == 0 && !poOpenInfo->TryToIngest(1024 * 10))
134✔
UNCOV
1724
            return FALSE;
×
1725
    }
1726

1727
    return -1;
67✔
1728
}
1729

1730
/************************************************************************/
1731
/*                                Open()                                */
1732
/************************************************************************/
1733

1734
GDALDataset *KmlSuperOverlayReadDataset::Open(GDALOpenInfo *poOpenInfo)
111✔
1735

1736
{
1737
    if (Identify(poOpenInfo) == FALSE)
111✔
UNCOV
1738
        return nullptr;
×
1739

1740
    return Open(poOpenInfo->pszFilename);
111✔
1741
}
1742

1743
/************************************************************************/
1744
/*                         KmlSuperOverlayLoadIcon()                    */
1745
/************************************************************************/
1746

1747
#define BUFFER_SIZE 20000000
1748

1749
static std::unique_ptr<GDALDataset>
1750
KmlSuperOverlayLoadIcon(const char *pszBaseFilename, const char *pszIcon)
38✔
1751
{
1752
    const std::string osExt = CPLGetExtensionSafe(pszIcon);
76✔
1753
    const char *pszExt = osExt.c_str();
38✔
1754
    if (!EQUAL(pszExt, "png") && !EQUAL(pszExt, "jpg") &&
38✔
UNCOV
1755
        !EQUAL(pszExt, "jpeg"))
×
1756
    {
UNCOV
1757
        return nullptr;
×
1758
    }
1759

1760
    CPLString osSubFilename;
76✔
1761
    if (STARTS_WITH(pszIcon, "http"))
38✔
UNCOV
1762
        osSubFilename = CPLSPrintf("/vsicurl_streaming/%s", pszIcon);
×
1763
    else
1764
    {
1765
        osSubFilename = CPLFormFilenameSafe(
38✔
1766
            CPLGetPathSafe(pszBaseFilename).c_str(), pszIcon, nullptr);
76✔
1767
        osSubFilename = KMLRemoveSlash(osSubFilename);
38✔
1768
    }
1769

1770
    VSILFILE *fp = VSIFOpenL(osSubFilename, "rb");
38✔
1771
    if (fp == nullptr)
38✔
1772
    {
UNCOV
1773
        return nullptr;
×
1774
    }
1775
    GByte *pabyBuffer = static_cast<GByte *>(VSIMalloc(BUFFER_SIZE));
38✔
1776
    if (pabyBuffer == nullptr)
38✔
1777
    {
UNCOV
1778
        VSIFCloseL(fp);
×
UNCOV
1779
        return nullptr;
×
1780
    }
1781
    const size_t nRead = VSIFReadL(pabyBuffer, 1, BUFFER_SIZE, fp);
38✔
1782
    VSIFCloseL(fp);
38✔
1783
    if (nRead == BUFFER_SIZE)
38✔
1784
    {
UNCOV
1785
        CPLFree(pabyBuffer);
×
UNCOV
1786
        return nullptr;
×
1787
    }
1788

1789
    osSubFilename = VSIMemGenerateHiddenFilename("kmlsuperoverlay");
38✔
1790
    VSIFCloseL(VSIFileFromMemBuffer(osSubFilename, pabyBuffer, nRead, TRUE));
38✔
1791

1792
    auto poDSIcon = std::unique_ptr<GDALDataset>(GDALDataset::Open(
1793
        osSubFilename, GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR));
76✔
1794
    if (!poDSIcon)
38✔
1795
    {
UNCOV
1796
        VSIUnlink(osSubFilename);
×
UNCOV
1797
        return nullptr;
×
1798
    }
1799

1800
    return poDSIcon;
38✔
1801
}
1802

1803
/************************************************************************/
1804
/*                    KmlSuperOverlayComputeDepth()                     */
1805
/************************************************************************/
1806

1807
static bool KmlSuperOverlayComputeDepth(const std::string &osFilename,
35✔
1808
                                        CPLXMLNode *psDocument, int &nLevel)
1809
{
1810
    CPLXMLNode *psIter = psDocument->psChild;
35✔
1811
    while (psIter != nullptr)
210✔
1812
    {
1813
        const char *pszHref = nullptr;
185✔
1814
        if (psIter->eType == CXT_Element &&
555✔
1815
            strcmp(psIter->pszValue, "NetworkLink") == 0 &&
185✔
1816
            CPLGetXMLNode(psIter, "Region") != nullptr &&
380✔
1817
            (pszHref = CPLGetXMLValue(psIter, "Link.href", nullptr)) != nullptr)
10✔
1818
        {
1819
            if (EQUAL(CPLGetExtensionSafe(pszHref).c_str(), "kml"))
10✔
1820
            {
1821
                CPLString osSubFilename;
10✔
1822
                if (STARTS_WITH(pszHref, "http"))
10✔
1823
                    osSubFilename =
UNCOV
1824
                        CPLSPrintf("/vsicurl_streaming/%s", pszHref);
×
1825
                else
1826
                {
1827
                    osSubFilename = CPLFormFilenameSafe(
10✔
1828
                        CPLGetPathSafe(osFilename.c_str()).c_str(), pszHref,
20✔
1829
                        nullptr);
10✔
1830
                    osSubFilename = KMLRemoveSlash(osSubFilename);
10✔
1831
                }
1832

1833
                VSILFILE *fp = VSIFOpenL(osSubFilename, "rb");
10✔
1834
                if (fp != nullptr)
10✔
1835
                {
1836
                    char *pszBuffer = static_cast<char *>(
1837
                        VSI_MALLOC_VERBOSE(BUFFER_SIZE + 1));
10✔
1838
                    if (pszBuffer == nullptr)
10✔
1839
                    {
UNCOV
1840
                        VSIFCloseL(fp);
×
UNCOV
1841
                        return false;
×
1842
                    }
1843
                    const size_t nRead =
1844
                        VSIFReadL(pszBuffer, 1, BUFFER_SIZE, fp);
10✔
1845
                    pszBuffer[nRead] = '\0';
10✔
1846
                    VSIFCloseL(fp);
10✔
1847
                    if (nRead == BUFFER_SIZE)
10✔
1848
                    {
UNCOV
1849
                        CPLFree(pszBuffer);
×
1850
                    }
1851
                    else
1852
                    {
1853
                        CPLXMLNode *psNode = CPLParseXMLString(pszBuffer);
10✔
1854
                        CPLFree(pszBuffer);
10✔
1855
                        if (psNode != nullptr)
10✔
1856
                        {
1857
                            CPLXMLNode *psRegion = nullptr;
10✔
1858
                            CPLXMLNode *psNewDocument = nullptr;
10✔
1859
                            CPLXMLNode *psGroundOverlay = nullptr;
10✔
1860
                            CPLXMLNode *psLink = nullptr;
10✔
1861
                            if (KmlSuperOverlayFindRegionStart(
10✔
1862
                                    psNode, &psRegion, &psNewDocument,
1863
                                    &psGroundOverlay, &psLink) &&
10✔
1864
                                psNewDocument != nullptr && nLevel < 20)
10✔
1865
                            {
1866
                                nLevel++;
10✔
1867
                                if (!KmlSuperOverlayComputeDepth(
10✔
1868
                                        osSubFilename, psNewDocument, nLevel))
1869
                                {
UNCOV
1870
                                    CPLDestroyXMLNode(psNode);
×
UNCOV
1871
                                    return false;
×
1872
                                }
1873
                            }
1874
                            CPLDestroyXMLNode(psNode);
10✔
1875
                            break;
10✔
1876
                        }
1877
                    }
1878
                }
1879
            }
1880
        }
1881
        psIter = psIter->psNext;
175✔
1882
    }
1883
    return true;
35✔
1884
}
1885

1886
/************************************************************************/
1887
/*                    KmlSingleDocRasterDataset                         */
1888
/************************************************************************/
1889

1890
class KmlSingleDocRasterRasterBand;
1891

1892
struct KmlSingleDocRasterTilesDesc
1893
{
1894
    int nMaxJ_i;    /* i index at which a tile with max j is realized */
1895
    int nMaxJ_j;    /* j index at which a tile with max j is realized */
1896
    int nMaxI_i;    /* i index at which a tile with max i is realized */
1897
    int nMaxI_j;    /* j index at which a tile with max i is realized */
1898
    char szExtJ[4]; /* extension of tile at which max j is realized */
1899
    char szExtI[4]; /* extension of tile at which max i is realized */
1900
};
1901

1902
class KmlSingleDocRasterDataset final : public GDALDataset
1903
{
1904
    friend class KmlSingleDocRasterRasterBand;
1905
    OGRSpatialReference m_oSRS{};
1906
    CPLString osDirname{};
1907
    CPLString osNominalExt{};
1908
    std::unique_ptr<GDALDataset> poCurTileDS{};
1909
    std::array<double, 4> adfGlobalExtents = {0, 0, 0, 0};
1910
    std::array<double, 6> adfGeoTransform = {0, 0, 0, 0, 0, 0};
1911
    std::vector<std::unique_ptr<KmlSingleDocRasterDataset>> m_apoOverviews{};
1912
    std::vector<KmlSingleDocRasterTilesDesc> aosDescs{};
1913
    int nLevel = 0;
1914
    int nTileSize = 0;
1915
    bool bHasBuiltOverviews = false;
1916
    bool bLockOtherBands = false;
1917

1918
  protected:
1919
    virtual int CloseDependentDatasets() override;
1920

1921
  public:
1922
    KmlSingleDocRasterDataset();
1923
    virtual ~KmlSingleDocRasterDataset();
1924

1925
    virtual CPLErr GetGeoTransform(double *padfGeoTransform) override
1✔
1926
    {
1927
        memcpy(padfGeoTransform, adfGeoTransform.data(), 6 * sizeof(double));
1✔
1928
        return CE_None;
1✔
1929
    }
1930

1931
    const OGRSpatialReference *GetSpatialRef() const override
1✔
1932
    {
1933
        return &m_oSRS;
1✔
1934
    }
1935

1936
    void BuildOverviews();
1937

1938
    static GDALDataset *Open(const char *pszFilename,
1939
                             const CPLString &osFilename, CPLXMLNode *psNode);
1940
};
1941

1942
/************************************************************************/
1943
/*                    KmlSingleDocRasterRasterBand                      */
1944
/************************************************************************/
1945

1946
class KmlSingleDocRasterRasterBand final : public GDALRasterBand
1947
{
1948
  public:
1949
    KmlSingleDocRasterRasterBand(KmlSingleDocRasterDataset *poDS, int nBand);
1950

1951
    virtual CPLErr IReadBlock(int, int, void *) override;
1952
    virtual GDALColorInterp GetColorInterpretation() override;
1953

1954
    virtual int GetOverviewCount() override;
1955
    virtual GDALRasterBand *GetOverview(int) override;
1956
};
1957

1958
/************************************************************************/
1959
/*                        KmlSingleDocRasterDataset()                   */
1960
/************************************************************************/
1961

1962
KmlSingleDocRasterDataset::KmlSingleDocRasterDataset()
2✔
1963
{
1964
    m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
2✔
1965
    m_oSRS.importFromWkt(SRS_WKT_WGS84_LAT_LONG);
2✔
1966
}
2✔
1967

1968
/************************************************************************/
1969
/*                       ~KmlSingleDocRasterDataset()                   */
1970
/************************************************************************/
1971

1972
KmlSingleDocRasterDataset::~KmlSingleDocRasterDataset()
4✔
1973
{
1974
    KmlSingleDocRasterDataset::CloseDependentDatasets();
2✔
1975
}
4✔
1976

1977
/************************************************************************/
1978
/*                         CloseDependentDatasets()                     */
1979
/************************************************************************/
1980

1981
int KmlSingleDocRasterDataset::CloseDependentDatasets()
2✔
1982
{
1983
    int bRet = FALSE;
2✔
1984

1985
    if (poCurTileDS)
2✔
1986
    {
1987
        bRet = TRUE;
2✔
1988
        poCurTileDS.reset();
2✔
1989
    }
1990
    if (!m_apoOverviews.empty())
2✔
1991
    {
1992
        bRet = TRUE;
1✔
1993
        m_apoOverviews.clear();
1✔
1994
    }
1995

1996
    return bRet;
2✔
1997
}
1998

1999
/************************************************************************/
2000
/*                     KmlSingleDocGetDimensions()                      */
2001
/************************************************************************/
2002

2003
static bool KmlSingleDocGetDimensions(const CPLString &osDirname,
2✔
2004
                                      const KmlSingleDocRasterTilesDesc &oDesc,
2005
                                      int nLevel, int nTileSize, int &nXSize,
2006
                                      int &nYSize, int &nBands, int &bHasCT)
2007
{
2008
    std::string osImageFilename = CPLFormFilenameSafe(
2009
        osDirname,
2010
        CPLSPrintf("kml_image_L%d_%d_%d", nLevel, oDesc.nMaxJ_j, oDesc.nMaxJ_i),
2✔
2011
        oDesc.szExtJ);
4✔
2012
    auto poImageDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
2013
        osImageFilename.c_str(), GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR));
4✔
2014
    if (!poImageDS)
2✔
2015
    {
UNCOV
2016
        return false;
×
2017
    }
2018
    int nRightXSize;
2019
    int nBottomYSize = poImageDS->GetRasterYSize();
2✔
2020
    nBands = poImageDS->GetRasterCount();
2✔
2021
    bHasCT = (nBands == 1 &&
3✔
2022
              poImageDS->GetRasterBand(1)->GetColorTable() != nullptr);
1✔
2023
    if (oDesc.nMaxJ_j == oDesc.nMaxI_j && oDesc.nMaxJ_i == oDesc.nMaxI_i)
2✔
2024
    {
2025
        nRightXSize = poImageDS->GetRasterXSize();
2✔
2026
    }
2027
    else
2028
    {
2029
        osImageFilename =
UNCOV
2030
            CPLFormFilenameSafe(osDirname,
×
2031
                                CPLSPrintf("kml_image_L%d_%d_%d", nLevel,
UNCOV
2032
                                           oDesc.nMaxI_j, oDesc.nMaxI_i),
×
2033
                                oDesc.szExtI);
×
UNCOV
2034
        poImageDS.reset(GDALDataset::Open(
×
2035
            osImageFilename.c_str(), GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR));
2036
        if (!poImageDS)
×
2037
        {
UNCOV
2038
            return false;
×
2039
        }
UNCOV
2040
        nRightXSize = poImageDS->GetRasterXSize();
×
2041
    }
2042

2043
    nXSize = nRightXSize + oDesc.nMaxI_i * nTileSize;
2✔
2044
    nYSize = nBottomYSize + oDesc.nMaxJ_j * nTileSize;
2✔
2045
    return (nXSize > 0 && nYSize > 0);
2✔
2046
}
2047

2048
/************************************************************************/
2049
/*                           BuildOverviews()                           */
2050
/************************************************************************/
2051

2052
void KmlSingleDocRasterDataset::BuildOverviews()
2✔
2053
{
2054
    if (bHasBuiltOverviews)
2✔
2055
        return;
1✔
2056
    bHasBuiltOverviews = TRUE;
1✔
2057

2058
    for (int k = 2; k <= static_cast<int>(aosDescs.size()); k++)
2✔
2059
    {
2060
        const KmlSingleDocRasterTilesDesc &oDesc =
2061
            aosDescs[aosDescs.size() - k];
1✔
2062
        int nXSize = 0;
1✔
2063
        int nYSize = 0;
1✔
2064
        int nTileBands = 0;
1✔
2065
        int bHasCT = FALSE;
1✔
2066
        if (!KmlSingleDocGetDimensions(
1✔
2067
                osDirname, oDesc, static_cast<int>(aosDescs.size()) - k + 1,
1✔
2068
                nTileSize, nXSize, nYSize, nTileBands, bHasCT))
2069
        {
UNCOV
2070
            break;
×
2071
        }
2072

2073
        auto poOvrDS = std::make_unique<KmlSingleDocRasterDataset>();
2✔
2074
        poOvrDS->nRasterXSize = nXSize;
1✔
2075
        poOvrDS->nRasterYSize = nYSize;
1✔
2076
        poOvrDS->nLevel = static_cast<int>(aosDescs.size()) - k + 1;
1✔
2077
        poOvrDS->nTileSize = nTileSize;
1✔
2078
        poOvrDS->osDirname = osDirname;
1✔
2079
        poOvrDS->osNominalExt = oDesc.szExtI;
1✔
2080
        poOvrDS->adfGeoTransform[0] = adfGlobalExtents[0];
1✔
2081
        poOvrDS->adfGeoTransform[1] =
1✔
2082
            (adfGlobalExtents[2] - adfGlobalExtents[0]) / poOvrDS->nRasterXSize;
1✔
2083
        poOvrDS->adfGeoTransform[2] = 0.0;
1✔
2084
        poOvrDS->adfGeoTransform[3] = adfGlobalExtents[3];
1✔
2085
        poOvrDS->adfGeoTransform[4] = 0.0;
1✔
2086
        poOvrDS->adfGeoTransform[5] =
1✔
2087
            -(adfGlobalExtents[3] - adfGlobalExtents[1]) /
1✔
2088
            poOvrDS->nRasterXSize;
1✔
2089
        for (int iBand = 1; iBand <= nBands; iBand++)
5✔
2090
            poOvrDS->SetBand(iBand,
8✔
2091
                             std::make_unique<KmlSingleDocRasterRasterBand>(
4✔
2092
                                 poOvrDS.get(), iBand));
8✔
2093
        poOvrDS->SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE");
1✔
2094

2095
        m_apoOverviews.push_back(std::move(poOvrDS));
1✔
2096
    }
2097
}
2098

2099
/************************************************************************/
2100
/*                      KmlSingleDocRasterRasterBand()                  */
2101
/************************************************************************/
2102

2103
KmlSingleDocRasterRasterBand::KmlSingleDocRasterRasterBand(
8✔
2104
    KmlSingleDocRasterDataset *poDSIn, int nBandIn)
8✔
2105
{
2106
    poDS = poDSIn;
8✔
2107
    nBand = nBandIn;
8✔
2108
    nBlockXSize = poDSIn->nTileSize;
8✔
2109
    nBlockYSize = poDSIn->nTileSize;
8✔
2110
    eDataType = GDT_Byte;
8✔
2111
}
8✔
2112

2113
/************************************************************************/
2114
/*                               IReadBlock()                           */
2115
/************************************************************************/
2116

2117
CPLErr KmlSingleDocRasterRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff,
12✔
2118
                                                void *pImage)
2119
{
2120
    KmlSingleDocRasterDataset *poGDS =
2121
        cpl::down_cast<KmlSingleDocRasterDataset *>(poDS);
12✔
2122
    const std::string osImageFilename =
2123
        CPLFormFilenameSafe(poGDS->osDirname,
2124
                            CPLSPrintf("kml_image_L%d_%d_%d", poGDS->nLevel,
2125
                                       nBlockYOff, nBlockXOff),
2126
                            poGDS->osNominalExt);
24✔
2127
    if (poGDS->poCurTileDS == nullptr ||
22✔
2128
        strcmp(CPLGetFilename(poGDS->poCurTileDS->GetDescription()),
10✔
2129
               CPLGetFilename(osImageFilename.c_str())) != 0)
2130
    {
2131
        CPLErrorHandlerPusher oErrorHandler(CPLQuietErrorHandler);
6✔
2132
        poGDS->poCurTileDS.reset(
3✔
2133
            GDALDataset::Open(osImageFilename.c_str(), GDAL_OF_RASTER));
2134
    }
2135
    GDALDataset *poImageDS = poGDS->poCurTileDS.get();
12✔
2136
    if (poImageDS == nullptr)
12✔
2137
    {
UNCOV
2138
        memset(pImage, 0, static_cast<size_t>(nBlockXSize) * nBlockYSize);
×
UNCOV
2139
        return CE_None;
×
2140
    }
2141
    int nXSize = poImageDS->GetRasterXSize();
12✔
2142
    int nYSize = poImageDS->GetRasterYSize();
12✔
2143

2144
    int nReqXSize = nBlockXSize;
12✔
2145
    if (nBlockXOff * nBlockXSize + nReqXSize > nRasterXSize)
12✔
2146
        nReqXSize = nRasterXSize - nBlockXOff * nBlockXSize;
8✔
2147
    int nReqYSize = nBlockYSize;
12✔
2148
    if (nBlockYOff * nBlockYSize + nReqYSize > nRasterYSize)
12✔
2149
        nReqYSize = nRasterYSize - nBlockYOff * nBlockYSize;
12✔
2150

2151
    if (nXSize != nReqXSize || nYSize != nReqYSize)
12✔
2152
    {
UNCOV
2153
        CPLDebug("KMLSUPEROVERLAY", "Tile %s, dimensions %dx%d, expected %dx%d",
×
2154
                 osImageFilename.c_str(), nXSize, nYSize, nReqXSize, nReqYSize);
UNCOV
2155
        return CE_Failure;
×
2156
    }
2157

2158
    CPLErr eErr = CE_Failure;
12✔
2159
    if (poImageDS->GetRasterCount() == 1)
12✔
2160
    {
2161
        GDALColorTable *poColorTable =
2162
            poImageDS->GetRasterBand(1)->GetColorTable();
4✔
2163
        if (nBand == 4 && poColorTable == nullptr)
4✔
2164
        {
2165
            /* Add fake alpha band */
UNCOV
2166
            memset(pImage, 255, static_cast<size_t>(nBlockXSize) * nBlockYSize);
×
UNCOV
2167
            eErr = CE_None;
×
2168
        }
2169
        else
2170
        {
2171
            eErr = poImageDS->GetRasterBand(1)->RasterIO(
4✔
2172
                GF_Read, 0, 0, nXSize, nYSize, pImage, nXSize, nYSize, GDT_Byte,
2173
                1, nBlockXSize, nullptr);
4✔
2174

2175
            /* Expand color table */
2176
            if (eErr == CE_None && poColorTable != nullptr)
4✔
2177
            {
2178
                GByte *pabyImage = static_cast<GByte *>(pImage);
4✔
2179
                int j, i;
2180
                for (j = 0; j < nReqYSize; j++)
2,276✔
2181
                {
2182
                    for (i = 0; i < nReqXSize; i++)
147,680✔
2183
                    {
2184
                        GByte nVal = pabyImage[j * nBlockXSize + i];
145,408✔
2185
                        const GDALColorEntry *poEntry =
2186
                            poColorTable->GetColorEntry(nVal);
145,408✔
2187
                        if (poEntry != nullptr)
145,408✔
2188
                        {
2189
                            if (nBand == 1)
145,408✔
2190
                                pabyImage[j * nBlockXSize + i] =
36,352✔
2191
                                    static_cast<GByte>(poEntry->c1);
36,352✔
2192
                            else if (nBand == 2)
109,056✔
2193
                                pabyImage[j * nBlockXSize + i] =
36,352✔
2194
                                    static_cast<GByte>(poEntry->c2);
36,352✔
2195
                            else if (nBand == 3)
72,704✔
2196
                                pabyImage[j * nBlockXSize + i] =
36,352✔
2197
                                    static_cast<GByte>(poEntry->c3);
36,352✔
2198
                            else
2199
                                pabyImage[j * nBlockXSize + i] =
36,352✔
2200
                                    static_cast<GByte>(poEntry->c4);
36,352✔
2201
                        }
2202
                    }
2203
                }
2204
            }
2205
        }
2206
    }
2207
    else if (nBand <= poImageDS->GetRasterCount())
8✔
2208
    {
2209
        eErr = poImageDS->GetRasterBand(nBand)->RasterIO(
16✔
2210
            GF_Read, 0, 0, nXSize, nYSize, pImage, nXSize, nYSize, GDT_Byte, 1,
2211
            nBlockXSize, nullptr);
8✔
2212
    }
UNCOV
2213
    else if (nBand == 4 && poImageDS->GetRasterCount() == 3)
×
2214
    {
2215
        /* Add fake alpha band */
2216
        memset(pImage, 255, static_cast<size_t>(nBlockXSize) * nBlockYSize);
×
UNCOV
2217
        eErr = CE_None;
×
2218
    }
2219

2220
    /* Cache other bands */
2221
    if (!poGDS->bLockOtherBands)
12✔
2222
    {
2223
        poGDS->bLockOtherBands = TRUE;
3✔
2224
        for (int iBand = 1; iBand <= poGDS->nBands; iBand++)
15✔
2225
        {
2226
            if (iBand != nBand)
12✔
2227
            {
2228
                KmlSingleDocRasterRasterBand *poOtherBand =
2229
                    static_cast<KmlSingleDocRasterRasterBand *>(
2230
                        poGDS->GetRasterBand(iBand));
9✔
2231
                GDALRasterBlock *poBlock =
2232
                    poOtherBand->GetLockedBlockRef(nBlockXOff, nBlockYOff);
9✔
2233
                if (poBlock == nullptr)
9✔
UNCOV
2234
                    continue;
×
2235
                poBlock->DropLock();
9✔
2236
            }
2237
        }
2238
        poGDS->bLockOtherBands = FALSE;
3✔
2239
    }
2240

2241
    return eErr;
12✔
2242
}
2243

2244
/************************************************************************/
2245
/*                          GetColorInterpretation()                    */
2246
/************************************************************************/
2247

2248
GDALColorInterp KmlSingleDocRasterRasterBand::GetColorInterpretation()
4✔
2249
{
2250
    return static_cast<GDALColorInterp>(GCI_RedBand + nBand - 1);
4✔
2251
}
2252

2253
/************************************************************************/
2254
/*                          GetOverviewCount()                          */
2255
/************************************************************************/
2256

2257
int KmlSingleDocRasterRasterBand::GetOverviewCount()
1✔
2258
{
2259
    KmlSingleDocRasterDataset *poGDS =
2260
        cpl::down_cast<KmlSingleDocRasterDataset *>(poDS);
1✔
2261
    poGDS->BuildOverviews();
1✔
2262

2263
    return static_cast<int>(poGDS->m_apoOverviews.size());
1✔
2264
}
2265

2266
/************************************************************************/
2267
/*                           GetOverview()                              */
2268
/************************************************************************/
2269

2270
GDALRasterBand *KmlSingleDocRasterRasterBand::GetOverview(int iOvr)
1✔
2271
{
2272
    KmlSingleDocRasterDataset *poGDS =
2273
        cpl::down_cast<KmlSingleDocRasterDataset *>(poDS);
1✔
2274
    poGDS->BuildOverviews();
1✔
2275

2276
    if (iOvr < 0 || iOvr >= static_cast<int>(poGDS->m_apoOverviews.size()))
1✔
UNCOV
2277
        return nullptr;
×
2278

2279
    return poGDS->m_apoOverviews[iOvr]->GetRasterBand(nBand);
1✔
2280
}
2281

2282
/************************************************************************/
2283
/*                       KmlSingleDocCollectTiles()                     */
2284
/************************************************************************/
2285

2286
static void
2287
KmlSingleDocCollectTiles(CPLXMLNode *psNode,
60✔
2288
                         std::vector<KmlSingleDocRasterTilesDesc> &aosDescs,
2289
                         CPLString &osURLBase)
2290
{
2291
    if (strcmp(psNode->pszValue, "href") == 0)
60✔
2292
    {
2293
        int level, j, i;
2294
        char szExt[4];
2295
        const char *pszHref = CPLGetXMLValue(psNode, "", "");
3✔
2296
        if (STARTS_WITH(pszHref, "http"))
3✔
2297
        {
UNCOV
2298
            osURLBase = CPLGetPathSafe(pszHref);
×
2299
        }
2300
        if (sscanf(CPLGetFilename(pszHref), "kml_image_L%d_%d_%d.%3s", &level,
3✔
2301
                   &j, &i, szExt) == 4)
3✔
2302
        {
2303
            if (level > static_cast<int>(aosDescs.size()))
3✔
2304
            {
2305
                KmlSingleDocRasterTilesDesc sDesc;
2306
                while (level > static_cast<int>(aosDescs.size()) + 1)
2✔
2307
                {
UNCOV
2308
                    sDesc.nMaxJ_i = -1;
×
UNCOV
2309
                    sDesc.nMaxJ_j = -1;
×
UNCOV
2310
                    sDesc.nMaxI_i = -1;
×
2311
                    sDesc.nMaxI_j = -1;
×
2312
                    strcpy(sDesc.szExtI, "");
×
2313
                    strcpy(sDesc.szExtJ, "");
×
2314
                    aosDescs.push_back(sDesc);
×
2315
                }
2316

2317
                sDesc.nMaxJ_j = j;
2✔
2318
                sDesc.nMaxJ_i = i;
2✔
2319
                strcpy(sDesc.szExtJ, szExt);
2✔
2320
                sDesc.nMaxI_j = j;
2✔
2321
                sDesc.nMaxI_i = i;
2✔
2322
                strcpy(sDesc.szExtI, szExt);
2✔
2323
                aosDescs.push_back(sDesc);
2✔
2324
            }
2325
            else
2326
            {
2327
                /* 2010_USACE_JALBTCX_Louisiana_Mississippi_Lidar.kmz has not a
2328
                 * lower-right tile */
2329
                /* so the right most tile and the bottom most tile might be
2330
                 * different */
2331
                if ((j > aosDescs[level - 1].nMaxJ_j) ||
2✔
2332
                    (j == aosDescs[level - 1].nMaxJ_j &&
1✔
2333
                     i > aosDescs[level - 1].nMaxJ_i))
1✔
2334
                {
2335
                    aosDescs[level - 1].nMaxJ_j = j;
1✔
2336
                    aosDescs[level - 1].nMaxJ_i = i;
1✔
2337
                    strcpy(aosDescs[level - 1].szExtJ, szExt);
1✔
2338
                }
2339
                if (i > aosDescs[level - 1].nMaxI_i ||
1✔
UNCOV
2340
                    (i == aosDescs[level - 1].nMaxI_i &&
×
UNCOV
2341
                     j > aosDescs[level - 1].nMaxI_j))
×
2342
                {
2343
                    aosDescs[level - 1].nMaxI_j = j;
1✔
2344
                    aosDescs[level - 1].nMaxI_i = i;
1✔
2345
                    strcpy(aosDescs[level - 1].szExtI, szExt);
1✔
2346
                }
2347
            }
2348
        }
2349
    }
2350
    else
2351
    {
2352
        CPLXMLNode *psIter = psNode->psChild;
57✔
2353
        while (psIter != nullptr)
152✔
2354
        {
2355
            if (psIter->eType == CXT_Element)
95✔
2356
                KmlSingleDocCollectTiles(psIter, aosDescs, osURLBase);
59✔
2357
            psIter = psIter->psNext;
95✔
2358
        }
2359
    }
2360
}
60✔
2361

2362
/************************************************************************/
2363
/*                                Open()                                */
2364
/************************************************************************/
2365

2366
/* Read raster with a structure like
2367
 * http://opentopo.sdsc.edu/files/Haiti/NGA_Haiti_LiDAR2.kmz */
2368
/* i.e. made of a doc.kml that list all tiles at all overview levels */
2369
/* The tile name pattern is "kml_image_L{level}_{j}_{i}.{png|jpg}" */
2370
GDALDataset *KmlSingleDocRasterDataset::Open(const char *pszFilename,
99✔
2371
                                             const CPLString &osFilename,
2372
                                             CPLXMLNode *psRoot)
2373
{
2374
    CPLXMLNode *psRootFolder = CPLGetXMLNode(psRoot, "=kml.Document.Folder");
99✔
2375
    if (psRootFolder == nullptr)
99✔
2376
        return nullptr;
65✔
2377
    const char *pszRootFolderName = CPLGetXMLValue(psRootFolder, "name", "");
34✔
2378
    if (strcmp(pszRootFolderName, "kml_image_L1_0_0") != 0)
34✔
2379
        return nullptr;
33✔
2380

2381
    std::array<double, 4> adfGlobalExtents = {0, 0, 0, 0};
1✔
2382
    CPLXMLNode *psRegion = CPLGetXMLNode(psRootFolder, "Region");
1✔
2383
    if (psRegion == nullptr)
1✔
UNCOV
2384
        return nullptr;
×
2385
    if (!KmlSuperOverlayGetBoundingBox(psRegion, adfGlobalExtents))
1✔
UNCOV
2386
        return nullptr;
×
2387

2388
    std::vector<KmlSingleDocRasterTilesDesc> aosDescs;
2✔
2389
    CPLString osDirname = CPLGetPathSafe(osFilename);
2✔
2390
    KmlSingleDocCollectTiles(psRootFolder, aosDescs, osDirname);
1✔
2391
    if (aosDescs.empty())
1✔
UNCOV
2392
        return nullptr;
×
2393
    for (const auto &oDesc : aosDescs)
3✔
2394
    {
2395
        if (oDesc.nMaxJ_i < 0)
2✔
UNCOV
2396
            return nullptr;
×
2397
    }
2398

2399
    const std::string osImageFilename =
2400
        CPLFormFilenameSafe(osDirname,
2401
                            CPLSPrintf("kml_image_L%d_%d_%d",
2402
                                       static_cast<int>(aosDescs.size()), 0, 0),
1✔
2403
                            aosDescs.back().szExtI);
2✔
2404
    auto poImageDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
2405
        osImageFilename.c_str(), GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR));
2✔
2406
    if (poImageDS == nullptr)
1✔
2407
    {
UNCOV
2408
        return nullptr;
×
2409
    }
2410
    int nTileSize = poImageDS->GetRasterXSize();
1✔
2411
    if (nTileSize != poImageDS->GetRasterYSize())
1✔
2412
    {
2413
        nTileSize = 1024;
1✔
2414
    }
2415

2416
    const KmlSingleDocRasterTilesDesc &oDesc = aosDescs.back();
1✔
2417
    int nXSize = 0;
1✔
2418
    int nYSize = 0;
1✔
2419
    int nBands = 0;
1✔
2420
    int bHasCT = FALSE;
1✔
2421
    if (!KmlSingleDocGetDimensions(osDirname, oDesc,
1✔
2422
                                   static_cast<int>(aosDescs.size()), nTileSize,
1✔
2423
                                   nXSize, nYSize, nBands, bHasCT))
2424
    {
UNCOV
2425
        return nullptr;
×
2426
    }
2427

2428
    auto poDS = std::make_unique<KmlSingleDocRasterDataset>();
2✔
2429
    poDS->nRasterXSize = nXSize;
1✔
2430
    poDS->nRasterYSize = nYSize;
1✔
2431
    poDS->nLevel = static_cast<int>(aosDescs.size());
1✔
2432
    poDS->nTileSize = nTileSize;
1✔
2433
    poDS->osDirname = std::move(osDirname);
1✔
2434
    poDS->osNominalExt = oDesc.szExtI;
1✔
2435
    poDS->adfGlobalExtents = adfGlobalExtents;
1✔
2436
    poDS->adfGeoTransform[0] = adfGlobalExtents[0];
1✔
2437
    poDS->adfGeoTransform[1] =
1✔
2438
        (adfGlobalExtents[2] - adfGlobalExtents[0]) / poDS->nRasterXSize;
1✔
2439
    poDS->adfGeoTransform[2] = 0.0;
1✔
2440
    poDS->adfGeoTransform[3] = adfGlobalExtents[3];
1✔
2441
    poDS->adfGeoTransform[4] = 0.0;
1✔
2442
    poDS->adfGeoTransform[5] =
1✔
2443
        -(adfGlobalExtents[3] - adfGlobalExtents[1]) / poDS->nRasterYSize;
1✔
2444
    if (nBands == 1 && bHasCT)
1✔
2445
        nBands = 4;
1✔
2446
    for (int iBand = 1; iBand <= nBands; iBand++)
5✔
2447
        poDS->SetBand(iBand, std::make_unique<KmlSingleDocRasterRasterBand>(
8✔
2448
                                 poDS.get(), iBand));
8✔
2449
    poDS->SetDescription(pszFilename);
1✔
2450
    poDS->SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE");
1✔
2451
    poDS->aosDescs = std::move(aosDescs);
1✔
2452

2453
    return poDS.release();
1✔
2454
}
2455

2456
/************************************************************************/
2457
/*                   KmlSingleOverlayRasterDataset                      */
2458
/************************************************************************/
2459

2460
class KmlSingleOverlayRasterDataset final : public VRTDataset
2461
{
2462
  public:
2463
    KmlSingleOverlayRasterDataset(int nXSize, int nYSize)
4✔
2464
        : VRTDataset(nXSize, nYSize)
4✔
2465
    {
2466
    }
4✔
2467

2468
    static GDALDataset *Open(const char *pszFilename,
2469
                             const CPLString &osFilename, CPLXMLNode *psRoot);
2470
};
2471

2472
/************************************************************************/
2473
/*                                Open()                                */
2474
/************************************************************************/
2475

2476
/* Read raster with a structure like https://trac.osgeo.org/gdal/ticket/6712 */
2477
/* i.e. made of a doc.kml that has a single GroundOverlay */
2478
GDALDataset *KmlSingleOverlayRasterDataset::Open(const char *pszFilename,
36✔
2479
                                                 const CPLString &osFilename,
2480
                                                 CPLXMLNode *psRoot)
2481
{
2482
    CPLXMLNode *psGO = CPLGetXMLNode(psRoot, "=kml.GroundOverlay");
36✔
2483
    if (psGO == nullptr)
36✔
2484
    {
2485
        // Otherwise look for kml.Document.Folder.GroundOverlay if there's
2486
        // a single occurrence of Folder and GroundOverlay
2487
        auto psDoc = CPLGetXMLNode(psRoot, "=kml.Document");
34✔
2488
        if (psDoc == nullptr)
34✔
2489
        {
UNCOV
2490
            return nullptr;
×
2491
        }
2492
        CPLXMLNode *psFolder = nullptr;
34✔
2493
        for (auto psIter = psDoc->psChild; psIter; psIter = psIter->psNext)
116✔
2494
        {
2495
            if (psIter->eType == CXT_Element &&
98✔
2496
                strcmp(psIter->pszValue, "Folder") == 0)
66✔
2497
            {
2498
                if (psFolder == nullptr)
49✔
2499
                    psFolder = psIter;
33✔
2500
                else
2501
                    return nullptr;
16✔
2502
            }
2503
        }
2504

2505
        // folder is not mandatory -- some kml have a structure
2506
        // kml.Document.GroundOverlay
2507
        CPLXMLNode *psParent = psFolder != nullptr ? psFolder : psDoc;
18✔
2508
        for (auto psIter = psParent->psChild; psIter; psIter = psIter->psNext)
36✔
2509
        {
2510
            if (psIter->eType == CXT_Element &&
18✔
2511
                strcmp(psIter->pszValue, "GroundOverlay") == 0)
18✔
2512
            {
2513
                if (psGO == nullptr)
2✔
2514
                    psGO = psIter;
2✔
2515
                else
UNCOV
2516
                    return nullptr;
×
2517
            }
2518
        }
2519
        if (psGO == nullptr)
18✔
2520
        {
2521
            return nullptr;
16✔
2522
        }
2523
    }
2524

2525
    const char *pszHref = CPLGetXMLValue(psGO, "Icon.href", nullptr);
4✔
2526
    if (pszHref == nullptr)
4✔
UNCOV
2527
        return nullptr;
×
2528
    std::array<double, 4> adfExtents = {0, 0, 0, 0};
4✔
2529
    if (!KmlSuperOverlayGetBoundingBox(psGO, adfExtents))
4✔
2530
        return nullptr;
×
2531
    const std::string osImageFilename = CPLFormFilenameSafe(
2532
        CPLGetPathSafe(osFilename).c_str(), pszHref, nullptr);
8✔
2533
    GDALDataset *poImageDS = GDALDataset::FromHandle(
4✔
2534
        GDALOpenShared(osImageFilename.c_str(), GA_ReadOnly));
2535
    if (poImageDS == nullptr)
4✔
UNCOV
2536
        return nullptr;
×
2537

2538
    auto poDS = std::make_unique<KmlSingleOverlayRasterDataset>(
2539
        poImageDS->GetRasterXSize(), poImageDS->GetRasterYSize());
8✔
2540
    for (int i = 1; i <= poImageDS->GetRasterCount(); ++i)
12✔
2541
    {
2542
        poDS->AddBand(GDT_Byte, nullptr);
8✔
2543

2544
        auto poImageBand = poImageDS->GetRasterBand(i);
8✔
2545
        auto poVRTBand =
2546
            static_cast<VRTSourcedRasterBand *>(poDS->GetRasterBand(i));
8✔
2547
        poVRTBand->AddSimpleSource(
32✔
2548
            poImageBand, 0, 0, poImageDS->GetRasterXSize(),
8✔
2549
            poImageDS->GetRasterYSize(), 0, 0, poImageDS->GetRasterXSize(),
8✔
2550
            poImageDS->GetRasterYSize(), nullptr, VRT_NODATA_UNSET);
8✔
2551

2552
        poVRTBand->SetColorInterpretation(
8✔
2553
            poImageBand->GetColorInterpretation());
8✔
2554

2555
        const auto poCT = poImageBand->GetColorTable();
8✔
2556
        if (poCT)
8✔
2557
            poVRTBand->SetColorTable(poCT);
2✔
2558
    }
2559
    poImageDS->Dereference();
4✔
2560
    double adfGeoTransform[6] = {
4✔
2561
        adfExtents[0],
4✔
2562
        (adfExtents[2] - adfExtents[0]) / poImageDS->GetRasterXSize(),
4✔
2563
        0,
2564
        adfExtents[3],
4✔
2565
        0,
2566
        -(adfExtents[3] - adfExtents[1]) / poImageDS->GetRasterYSize()};
8✔
2567
    poDS->SetGeoTransform(adfGeoTransform);
4✔
2568
    poDS->SetProjection(SRS_WKT_WGS84_LAT_LONG);
4✔
2569
    poDS->SetWritable(false);
4✔
2570
    poDS->SetDescription(pszFilename);
4✔
2571

2572
    return poDS.release();
4✔
2573
}
2574

2575
/************************************************************************/
2576
/*                                Open()                                */
2577
/************************************************************************/
2578

2579
GDALDataset *
2580
KmlSuperOverlayReadDataset::Open(const char *pszFilename,
147✔
2581
                                 KmlSuperOverlayReadDataset *poParent, int nRec)
2582

2583
{
2584
    if (nRec == 2)
147✔
UNCOV
2585
        return nullptr;
×
2586
    CPLString osFilename(pszFilename);
294✔
2587
    if (EQUAL(CPLGetExtensionSafe(pszFilename).c_str(), "kmz"))
147✔
2588
    {
2589
        if (!STARTS_WITH(pszFilename, "/vsizip/"))
65✔
2590
            osFilename = CPLSPrintf("/vsizip/%s", pszFilename);
65✔
2591
        char **papszFiles = VSIReadDir(osFilename);
65✔
2592
        if (papszFiles == nullptr)
65✔
2593
            return nullptr;
48✔
2594
        char **papszIter = papszFiles;
17✔
2595
        for (; *papszIter != nullptr; papszIter++)
17✔
2596
        {
2597
            if (EQUAL(CPLGetExtensionSafe(*papszIter).c_str(), "kml"))
17✔
2598
            {
2599
                osFilename =
2600
                    CPLFormFilenameSafe(osFilename, *papszIter, nullptr);
17✔
2601
                osFilename = KMLRemoveSlash(osFilename);
17✔
2602
                break;
17✔
2603
            }
2604
        }
2605
        CSLDestroy(papszFiles);
17✔
2606
    }
2607
    VSILFILE *fp = VSIFOpenL(osFilename, "rb");
99✔
2608
    if (fp == nullptr)
99✔
UNCOV
2609
        return nullptr;
×
2610
    char *pszBuffer = static_cast<char *>(VSI_MALLOC_VERBOSE(BUFFER_SIZE + 1));
99✔
2611
    if (pszBuffer == nullptr)
99✔
2612
    {
UNCOV
2613
        VSIFCloseL(fp);
×
UNCOV
2614
        return nullptr;
×
2615
    }
2616
    const size_t nRead = VSIFReadL(pszBuffer, 1, BUFFER_SIZE, fp);
99✔
2617
    pszBuffer[nRead] = '\0';
99✔
2618
    VSIFCloseL(fp);
99✔
2619
    if (nRead == BUFFER_SIZE)
99✔
2620
    {
UNCOV
2621
        CPLFree(pszBuffer);
×
UNCOV
2622
        return nullptr;
×
2623
    }
2624

2625
    CPLXMLNode *psNode = CPLParseXMLString(pszBuffer);
99✔
2626
    CPLFree(pszBuffer);
99✔
2627
    if (psNode == nullptr)
99✔
UNCOV
2628
        return nullptr;
×
2629

2630
    GDALDataset *psSingleDocDS =
2631
        KmlSingleDocRasterDataset::Open(pszFilename, osFilename, psNode);
99✔
2632
    if (psSingleDocDS != nullptr)
99✔
2633
    {
2634
        CPLDestroyXMLNode(psNode);
1✔
2635
        return psSingleDocDS;
1✔
2636
    }
2637

2638
    CPLXMLNode *psRegion = nullptr;
98✔
2639
    CPLXMLNode *psDocument = nullptr;
98✔
2640
    CPLXMLNode *psGroundOverlay = nullptr;
98✔
2641
    CPLXMLNode *psLink = nullptr;
98✔
2642
    if (!KmlSuperOverlayFindRegionStart(psNode, &psRegion, &psDocument,
98✔
2643
                                        &psGroundOverlay, &psLink))
2644
    {
2645
        // If we didn't find a super overlay, this still could be a valid kml
2646
        // containing a single overlay. Test for that now. (Note that we need to
2647
        // test first for super overlay in order to avoid false positive matches
2648
        // of super overlay datasets to single overlay datasets)
2649
        GDALDataset *psSingleOverlayDS = KmlSingleOverlayRasterDataset::Open(
36✔
2650
            pszFilename, osFilename, psNode);
2651
        CPLDestroyXMLNode(psNode);
36✔
2652
        return psSingleOverlayDS;
36✔
2653
    }
2654

2655
    if (psLink != nullptr)
62✔
2656
    {
2657
        const char *pszHref = CPLGetXMLValue(psLink, "href", nullptr);
23✔
2658
        if (pszHref == nullptr ||
46✔
2659
            !EQUAL(CPLGetExtensionSafe(pszHref).c_str(), "kml"))
46✔
2660
        {
UNCOV
2661
            CPLDestroyXMLNode(psNode);
×
UNCOV
2662
            return nullptr;
×
2663
        }
2664

2665
        CPLString osSubFilename;
46✔
2666
        if (STARTS_WITH(pszHref, "http"))
23✔
UNCOV
2667
            osSubFilename = CPLSPrintf("/vsicurl_streaming/%s", pszHref);
×
2668
        else
2669
        {
2670
            osSubFilename = CPLFormFilenameSafe(
23✔
2671
                CPLGetPathSafe(osFilename).c_str(), pszHref, nullptr);
46✔
2672
            osSubFilename = KMLRemoveSlash(osSubFilename);
23✔
2673
        }
2674

2675
        CPLString osOverlayName, osOverlayDescription;
46✔
2676
        psDocument = CPLGetXMLNode(psNode, "=kml.Document");
23✔
2677
        if (psDocument)
23✔
2678
        {
2679
            const char *pszOverlayName =
2680
                CPLGetXMLValue(psDocument, "name", nullptr);
23✔
2681
            if (pszOverlayName != nullptr &&
45✔
2682
                strcmp(pszOverlayName,
22✔
2683
                       CPLGetBasenameSafe(pszFilename).c_str()) != 0)
45✔
2684
            {
2685
                osOverlayName = pszOverlayName;
2✔
2686
            }
2687
            const char *pszOverlayDescription =
2688
                CPLGetXMLValue(psDocument, "description", nullptr);
23✔
2689
            if (pszOverlayDescription != nullptr)
23✔
2690
            {
2691
                osOverlayDescription = pszOverlayDescription;
1✔
2692
            }
2693
        }
2694

2695
        CPLDestroyXMLNode(psNode);
23✔
2696

2697
        // FIXME
2698
        GDALDataset *poDS = Open(osSubFilename, poParent, nRec + 1);
23✔
2699
        if (poDS != nullptr)
23✔
2700
        {
2701
            poDS->SetDescription(pszFilename);
22✔
2702

2703
            if (!osOverlayName.empty())
22✔
2704
            {
2705
                poDS->SetMetadataItem("NAME", osOverlayName);
2✔
2706
            }
2707
            if (!osOverlayDescription.empty())
22✔
2708
            {
2709
                poDS->SetMetadataItem("DESCRIPTION", osOverlayDescription);
1✔
2710
            }
2711
        }
2712

2713
        return poDS;
23✔
2714
    }
2715

2716
    CPLAssert(psDocument != nullptr);
39✔
2717
    CPLAssert(psGroundOverlay != nullptr);
39✔
2718
    CPLAssert(psRegion != nullptr);
39✔
2719

2720
    std::array<double, 4> adfExtents = {0, 0, 0, 0};
39✔
2721
    if (!KmlSuperOverlayGetBoundingBox(psGroundOverlay, adfExtents))
39✔
2722
    {
2723
        CPLDestroyXMLNode(psNode);
1✔
2724
        return nullptr;
1✔
2725
    }
2726

2727
    const char *pszIcon = CPLGetXMLValue(psGroundOverlay, "Icon.href", nullptr);
38✔
2728
    if (pszIcon == nullptr)
38✔
2729
    {
UNCOV
2730
        CPLDestroyXMLNode(psNode);
×
UNCOV
2731
        return nullptr;
×
2732
    }
2733
    auto poDSIcon = KmlSuperOverlayLoadIcon(pszFilename, pszIcon);
76✔
2734
    if (poDSIcon == nullptr)
38✔
2735
    {
UNCOV
2736
        CPLDestroyXMLNode(psNode);
×
UNCOV
2737
        return nullptr;
×
2738
    }
2739

2740
    int nFactor;
2741
    if (poParent != nullptr)
38✔
2742
        nFactor = poParent->nFactor / 2;
13✔
2743
    else
2744
    {
2745
        int nDepth = 0;
25✔
2746
        if (!KmlSuperOverlayComputeDepth(pszFilename, psDocument, nDepth))
25✔
2747
        {
UNCOV
2748
            CPLDestroyXMLNode(psNode);
×
UNCOV
2749
            return nullptr;
×
2750
        }
2751
        nFactor = 1 << nDepth;
25✔
2752
    }
2753

2754
    auto poDS = std::make_unique<KmlSuperOverlayReadDataset>();
76✔
2755
    poDS->osFilename = pszFilename;
38✔
2756
    poDS->psRoot = psNode;
38✔
2757
    poDS->psDocument = psDocument;
38✔
2758
    poDS->poParent = poParent;
38✔
2759
    poDS->nFactor = nFactor;
38✔
2760
    poDS->nRasterXSize = nFactor * poDSIcon->GetRasterXSize();
38✔
2761
    poDS->nRasterYSize = nFactor * poDSIcon->GetRasterYSize();
38✔
2762
    poDS->adfGeoTransform[0] = adfExtents[0];
38✔
2763
    poDS->adfGeoTransform[1] =
38✔
2764
        (adfExtents[2] - adfExtents[0]) / poDS->nRasterXSize;
38✔
2765
    poDS->adfGeoTransform[3] = adfExtents[3];
38✔
2766
    poDS->adfGeoTransform[5] =
38✔
2767
        -(adfExtents[3] - adfExtents[1]) / poDS->nRasterYSize;
38✔
2768
    poDS->nBands = 4;
38✔
2769
    for (int i = 0; i < 4; i++)
190✔
2770
        poDS->SetBand(i + 1, std::make_unique<KmlSuperOverlayRasterBand>(
304✔
2771
                                 poDS.get(), i + 1));
304✔
2772
    poDS->SetDescription(pszFilename);
38✔
2773
    poDS->SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE");
38✔
2774

2775
    while (poDS->poParent == nullptr && nFactor > 1)
48✔
2776
    {
2777
        nFactor /= 2;
10✔
2778

2779
        auto poOvrDS = std::make_unique<KmlSuperOverlayReadDataset>();
20✔
2780

2781
        poOvrDS->bIsOvr = true;
10✔
2782
        // The life-time of objects is such that poOvrDS is destroyed when
2783
        // poDS is destroyed.
2784
        // coverity[escape]
2785
        poOvrDS->poParent = poDS.get();
10✔
2786
        poOvrDS->nFactor = nFactor;
10✔
2787
        poOvrDS->nRasterXSize = nFactor * poDSIcon->GetRasterXSize();
10✔
2788
        poOvrDS->nRasterYSize = nFactor * poDSIcon->GetRasterYSize();
10✔
2789
        poOvrDS->adfGeoTransform[0] = adfExtents[0];
10✔
2790
        poOvrDS->adfGeoTransform[1] =
10✔
2791
            (adfExtents[2] - adfExtents[0]) / poOvrDS->nRasterXSize;
10✔
2792
        poOvrDS->adfGeoTransform[3] = adfExtents[3];
10✔
2793
        poOvrDS->adfGeoTransform[5] =
10✔
2794
            -(adfExtents[3] - adfExtents[1]) / poOvrDS->nRasterYSize;
10✔
2795
        poOvrDS->nBands = 4;
10✔
2796
        for (int i = 0; i < 4; i++)
50✔
2797
            poOvrDS->SetBand(i + 1, std::make_unique<KmlSuperOverlayRasterBand>(
80✔
2798
                                        poOvrDS.get(), i + 1));
80✔
2799
        poOvrDS->SetDescription(pszFilename);
10✔
2800
        poOvrDS->SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE");
10✔
2801

2802
        poDS->m_apoOverviewDS.push_back(std::move(poOvrDS));
10✔
2803
    }
2804
    poDS->poDSIcon = std::move(poDSIcon);
38✔
2805

2806
    return poDS.release();
38✔
2807
}
2808

2809
/************************************************************************/
2810
/*                    KmlSuperOverlayDatasetDelete()                    */
2811
/************************************************************************/
2812

2813
static CPLErr KmlSuperOverlayDatasetDelete(CPL_UNUSED const char *fileName)
2✔
2814
{
2815
    /* Null implementation, so that people can Delete("MEM:::") */
2816
    return CE_None;
2✔
2817
}
2818

2819
/************************************************************************/
2820
/*                    GDALRegister_KMLSUPEROVERLAY()                    */
2821
/************************************************************************/
2822

2823
void CPL_DLL GDALRegister_KMLSUPEROVERLAY()
1,898✔
2824

2825
{
2826
    if (GDALGetDriverByName("KMLSUPEROVERLAY") != nullptr)
1,898✔
2827
        return;
282✔
2828

2829
    GDALDriver *poDriver = new GDALDriver();
1,616✔
2830

2831
    poDriver->SetDescription("KMLSUPEROVERLAY");
1,616✔
2832
    poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
1,616✔
2833
    poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "Kml Super Overlay");
1,616✔
2834
    poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES,
1,616✔
2835
                              "Byte Int16 UInt16 Int32 UInt32 Float32 Float64 "
2836
                              "CInt16 CInt32 CFloat32 CFloat64");
1,616✔
2837

2838
    poDriver->SetMetadataItem(GDAL_DMD_EXTENSIONS, "kml kmz");
1,616✔
2839

2840
    poDriver->SetMetadataItem(
1,616✔
2841
        GDAL_DMD_CREATIONOPTIONLIST,
2842
        "<CreationOptionList>"
2843
        "   <Option name='NAME' type='string' description='Overlay name'/>"
2844
        "   <Option name='DESCRIPTION' type='string' description='Overlay "
2845
        "description'/>"
2846
        "   <Option name='ALTITUDE' type='float' description='Distance above "
2847
        "the earth surface, in meters, interpreted according to the altitude "
2848
        "mode'/>"
2849
        "   <Option name='ALTITUDEMODE' type='string-select' "
2850
        "default='clampToGround' description='Specifies hows the altitude is "
2851
        "interpreted'>"
2852
        "       <Value>clampToGround</Value>"
2853
        "       <Value>absolute</Value>"
2854
        "       <Value>relativeToSeaFloor</Value>"
2855
        "       <Value>clampToSeaFloor</Value>"
2856
        "   </Option>"
2857
        "   <Option name='FORMAT' type='string-select' default='JPEG' "
2858
        "description='Format of the tiles'>"
2859
        "       <Value>PNG</Value>"
2860
        "       <Value>JPEG</Value>"
2861
        "       <Value>AUTO</Value>"
2862
        "   </Option>"
2863
        "   <Option name='FIX_ANTIMERIDIAN' type='boolean' description='Fix "
2864
        "for images crossing the antimeridian causing errors in Google Earth' "
2865
        "/>"
2866
        "</CreationOptionList>");
1,616✔
2867

2868
    poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
1,616✔
2869

2870
    poDriver->pfnIdentify = KmlSuperOverlayReadDataset::Identify;
1,616✔
2871
    poDriver->pfnOpen = KmlSuperOverlayReadDataset::Open;
1,616✔
2872
    poDriver->pfnCreateCopy = KmlSuperOverlayCreateCopy;
1,616✔
2873
    poDriver->pfnDelete = KmlSuperOverlayDatasetDelete;
1,616✔
2874

2875
    GetGDALDriverManager()->RegisterDriver(poDriver);
1,616✔
2876
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc