LCOV - code coverage report
Current view: top level - frmts/kmlsuperoverlay - kmlsuperoverlaydataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1303 1483 87.9 %
Date: 2024-05-04 12:52:34 Functions: 47 47 100.0 %

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

Generated by: LCOV version 1.14