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

Generated by: LCOV version 1.14