public static double[] GetClippedGeoTransform(string gdalDataset, PixelLims subsetCoords)
        {
            if (!System.IO.File.Exists(gdalDataset))
            {
                throw new ArgumentException("File does not exist!");
            }
            Dataset ds = Gdal.Open(gdalDataset, Access.GA_ReadOnly);

            if (ds == null)
            {
                throw new ArgumentException("Can't open " + gdalDataset);
            }
            double[] inGT = new double[6];
            ds.GetGeoTransform(inGT);
            var topLeftLongIn = inGT[0];
            var topLeftLatIn  = inGT[3];
            var resX          = inGT[1];
            var resY          = inGT[5];

            if (inGT[2] != 0.0 || inGT[4] != 0)
            {
                throw new InvalidOperationException("Only datasets with zero skew parameters (i.e. those aligned north-south) are supported");
            }
            var topLeftLongOut = topLeftLongIn + subsetCoords.WestPixelCoord * resX;
            var topLeftLatOut  = topLeftLatIn + subsetCoords.NorthPixelCoord * resY;
            var clippedGT      = new double[] { topLeftLongOut, resX, 0.0, topLeftLatOut, 0.0, resY };

            return(clippedGT);
        }
        public static bool WritePartTiff(string Filename, float[] Data, PixelLims WriteShape)
        {
            var ds         = Gdal.Open(Filename, Access.GA_Update);
            var band       = ds.GetRasterBand(1);
            var fileShapeX = ds.RasterXSize;
            var fileShapeY = ds.RasterYSize;
            var writeSizeX = WriteShape.EastPixelCoord - WriteShape.WestPixelCoord;
            var writeSizeY = WriteShape.SouthPixelCoord - WriteShape.NorthPixelCoord;

            if (WriteShape.SouthPixelCoord > fileShapeY || WriteShape.EastPixelCoord > fileShapeX ||
                writeSizeX * writeSizeY != Data.Length)
            {
                return(false);
            }
            int dataBlockSizeX, dataBlockSizeY;

            band.GetBlockSize(out dataBlockSizeX, out dataBlockSizeY);
            if (dataBlockSizeX != writeSizeX ||
                dataBlockSizeY != writeSizeY ||
                WriteShape.WestPixelCoord % dataBlockSizeX != 0 ||
                WriteShape.NorthPixelCoord % dataBlockSizeY != 0)
            {
                // only for testing, we will allow non-whole block writing eventually
                return(false);
            }
            band.WriteRaster(
                (int)WriteShape.WestPixelCoord, (int)WriteShape.NorthPixelCoord,
                (int)writeSizeX, (int)writeSizeY,
                Data,
                (int)writeSizeX, (int)writeSizeY,
                0, 0);
            band.FlushCache();
            ds.FlushCache();
            return(true);
        }
        public double[] GetSubsetGeoTransform(PixelLims TileCoords)
        {
            string firstFilename = m_FileDates.First().Value;
            var    res           = GDAL_Operations.GetClippedGeoTransform(firstFilename, TileCoords);

            return(res);
        }
        private static Dataset CreateTiff(string Filename, double[] GeoTransform, string Projection, PixelLims shape, bool CreateCompressed, float?NDV, int?BlockSize)
        {
            var creationOpts = new List <string>();

            if (CreateCompressed)
            {
                creationOpts = new List <string>()
                {
                    "COMPRESS=DEFLATE", "ZLEVEL=9", "TILED=YES", "SPARSE_OK=FALSE", "BIGTIFF=YES", "NUM_THREADS=ALL_CPUS"
                };
            }
            if (BlockSize.HasValue)
            {
                creationOpts.Add("BLOCKXSIZE=" + BlockSize.Value.ToString());
                creationOpts.Add("BLOCKYSIZE=" + BlockSize.Value.ToString());
            }
            var drv    = Gdal.GetDriverByName("GTiff");
            var shapeX = shape.EastPixelCoord - shape.WestPixelCoord;
            var shapeY = shape.SouthPixelCoord - shape.NorthPixelCoord;

            var ds = drv.Create(Filename, (int)shapeX, (int)shapeY, 1, DataType.GDT_Float32, creationOpts.ToArray());

            ds.SetGeoTransform(GeoTransform);
            ds.SetProjection(Projection);
            var band = ds.GetRasterBand(1);

            if (NDV.HasValue)
            {
                band.SetNoDataValue(NDV.Value);
            }
            return(ds);
        }
        public static bool WriteWholeTiff(string Filename, float[] Data, double[] GeoTransform, string Projection, PixelLims shape, bool CreateCompressed, float?NDV)
        {
            // we will create a tiff with tiles (blocks) but let the size of them be specified automatically
            var ds           = CreateTiff(Filename, GeoTransform, Projection, shape, CreateCompressed, NDV, null);
            var creationOpts = new string[0];

            if (CreateCompressed)
            {
                creationOpts = new string[] { "COMPRESS=DEFLATE", "ZLEVEL=9", "TILED=YES", "SPARSE_OK=FALSE", "BIGTIFF=YES" };
            }
            var drv    = Gdal.GetDriverByName("GTiff");
            var shapeX = shape.EastPixelCoord - shape.WestPixelCoord;
            var shapeY = shape.SouthPixelCoord - shape.NorthPixelCoord;

            if (shapeX * shapeY != Data.Length)
            {
                return(false);
            }
            var band = ds.GetRasterBand(1);

            band.WriteRaster(0, 0, (int)shapeX, (int)shapeY, Data, (int)shapeX, (int)shapeY, 0, 0);
            band.FlushCache();
            ds.FlushCache();
            return(true);
        }
        /// <summary>
        /// Runs TS model for all cells of all tiles required to cover the given bounding box at the given tile size.
        /// Any tile wholly in the sea will be skipped (no output files generated)
        /// </summary>
        /// <param name="WestDegrees"></param>
        /// <param name="EastDegrees"></param>
        /// <param name="NorthDegrees"></param>
        /// <param name="SouthDegrees"></param>
        /// <param name="TileSize"></param>
        public void RunAllTiles()
        {
            var globalGT  = _maxReader.GeoTransform;
            var lims      = _cfg.spatialLimits;
            var pxOverall = GDAL_Operations.CalculatePixelCoordsOfBlock(globalGT,
                                                                        lims.WestLimitDegrees, lims.EastLimitDegrees,
                                                                        lims.NorthLimitDegrees, lims.SouthLimitDegrees
                                                                        );
            var TileSize  = _cfg.modelRunConfig.MaxTileSizePx;
            var latsToRun = _maxReader.GetSubsetLatitudeCoords((int)pxOverall.NorthPixelCoord, (int)(pxOverall.SouthPixelCoord - pxOverall.NorthPixelCoord));
            var lonsToRun = _maxReader.GetSubsetLongitudeCoords((int)pxOverall.WestPixelCoord, (int)(pxOverall.EastPixelCoord - pxOverall.WestPixelCoord));
            var nTilesX   = (int)Math.Ceiling((double)lonsToRun.Length / TileSize);
            var nTilesY   = (int)Math.Ceiling((double)latsToRun.Length / TileSize);

            var    nTilesTotal = nTilesX * nTilesY;
            string tileDir     = lims.WestLimitDegrees.ToString() + "W-"
                                 + lims.EastLimitDegrees.ToString() + "E-"
                                 + lims.NorthLimitDegrees.ToString() + "N-"
                                 + lims.SouthLimitDegrees.ToString() + "S-"
                                 + TileSize.ToString() + "px_tiles";

            Console.WriteLine("Initiating run of  " + nTilesTotal.ToString() + " tiles");
            var runDir = nTilesTotal == 1 ?
                         _cfg.dataPathConfig.OutputFolder :
                         System.IO.Path.Combine(_cfg.dataPathConfig.OutputFolder, tileDir);

            System.IO.Directory.CreateDirectory(runDir);
            for (int tileRow = 0; tileRow < nTilesY; tileRow++)
            {
                int yOff          = (int)pxOverall.NorthPixelCoord + tileRow * TileSize;
                int yEnd          = yOff + TileSize;
                int thisTileYSize = TileSize;

                if (yEnd > pxOverall.SouthPixelCoord)
                {
                    thisTileYSize = (int)pxOverall.SouthPixelCoord - yOff;
                }
                for (int tileCol = 0; tileCol < nTilesX; tileCol++)
                {
                    var    tileNum = tileRow * nTilesX + tileCol + 1;
                    string tilenameLocPart;
                    if (nTilesTotal > 1)
                    {
                        tilenameLocPart = ".r" + tileRow.ToString("D3") + "_c" + tileCol.ToString("D3") + ".tif";
                    }
                    else
                    {
                        tilenameLocPart = ".tif";
                    }
                    if (System.IO.Directory.EnumerateFiles(runDir, "*" + tilenameLocPart).Count() != 0)
                    {
                        System.Console.WriteLine("Tile " + tileNum.ToString() + " appears to be already done, skipping");
                        continue;
                    }
                    int xOff          = (int)pxOverall.WestPixelCoord + tileCol * TileSize;
                    int xEnd          = xOff + TileSize;
                    int thisTileXSize = TileSize;
                    if (xEnd > pxOverall.EastPixelCoord)
                    {
                        thisTileXSize = (int)pxOverall.EastPixelCoord - xOff;
                    }

                    var tileCoords = new PixelLims(xOff, xOff + thisTileXSize, yOff, yOff + thisTileYSize);
                    var cellRes    = RunTile(xOff, yOff, thisTileXSize, thisTileYSize);
                    if (cellRes.Length == 0)
                    {
                        // whole tile was in masked / sea area. Do not bother to write output.
                        Console.WriteLine("Tile " + tileNum.ToString() + " was wholly in sea - skipped");
                        continue;
                    }
                    Console.WriteLine("Tile computation completed, writing output");
                    var tileRes = TransposeCellData(
                        cellRes,
                        thisTileXSize, thisTileYSize);
                    var tileGT   = _maxReader.GetSubsetGeoTransform(tileCoords);
                    var tileProj = _maxReader.Projection;
                    // write each tile to a separate tiff file - we can mosaic them later.
                    var tileLims = new PixelLims(0, thisTileXSize, 0, thisTileYSize);
                    for (int t = 0; t < tileRes.Length; t++)
                    {
                        var    tData = tileRes[t];
                        string fn    = _cfg.modelRunConfig.OutputFileTag + "." +
                                       _outputDates[t].Date.ToString("yyyy.MM") + tilenameLocPart;
                        string outFile = System.IO.Path.Combine(runDir, fn);
                        GDAL_Operations.WriteWholeTiff(outFile, tData, tileGT, tileProj, tileLims, true, _maxReader.NoDataValue);
                    }
                    Console.WriteLine("Tile " + tileNum.ToString() + " finished - wrote " + tileRes.Length.ToString() + " files");
                }
            }
        }