LCOV - code coverage report
Current view: top level - frmts/kmlsuperoverlay - kmlsuperoverlaydataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1282 1462 87.7 %
Date: 2024-11-21 22:18:42 Functions: 47 47 100.0 %

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

Generated by: LCOV version 1.14