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

Generated by: LCOV version 1.14