MaxRev-Dev / gdal.netcore

GDAL 3.x C#/F# bindings for .NET apps
MIT License
162 stars 36 forks source link

VRT RasterBand support #118

Closed clintsinger closed 10 months ago

clintsinger commented 10 months ago

I'm in the process of porting some of the gdal_translate functionality to convert a raster to RGBA format. To do so I need to use the functions in vrtdataset.h, such as VRTSourcedRasterBand and CopyBandInfo.

It appears these are not available. Is the vrtdataset not included with this build or am I looking in the wrong area?

In the C++ world, I'm using things like the following (among other functions from the same lib)

...
VRTDataset* poVDS = (VRTDataset*)VRTCreate(xSize, ySize);
(VRTSourcedRasterBand*)poVDS->GetRasterBand(i + 1);
poVRTBand->AddComplexSource(...)

I'm looking for the equivalent in C#

MaxRev-Dev commented 10 months ago

Yes, you're using the wrong API. The VRTDataset type is internal to C++ and not exposed to any bindings. You need to use GdalDriver by specifying the "VRT" name. https://gdal.org/drivers/raster/vrt.html#creation-of-vrt-datasets

using var vrt = Gdal.GetDriverByName("VRT");
// do your work
clintsinger commented 10 months ago

Thanks for the reply but I'm not sure how it answers my question. I do start out with what you said, but there is some functionality that is missing that I'm asking about.

I'm trying to create the following. The parts that are commented out are what appears to be missing from the library and I am wondering if they can be added. This is being ported over from a managed C++ implementation that I had done.

private bool CreateRgbaVrt()
{
    _ = this.inputDataset ?? throw new NullReferenceException("inputDataset is null");

    //if (CSLCount(this.inputDataset.GetMetadata("SUBDATASETS")) > 0
    //    && this.inputDataset.GetRasterCount() == 0)
    //{
    //    Console.WriteLine("Files with multiple datasets are not supported");
    //    return false;
    //}

    int xSize = this.inputDataset.RasterXSize;
    int ySize = this.inputDataset.RasterYSize;

    int bandCount = this.inputDataset.RasterCount;
    if (bandCount == 0)
    {
        Console.WriteLine("Input has no bands so cannot be translated");
        return false;
    }

    var panBandList = new int[bandCount];

    for (int i = 0; i < bandCount; i++)
    {
        panBandList[i] = i + 1;
    }

    var vrtDriver = Gdal.GetDriverByName("VRT");
    if (vrtDriver == null)
    {
        throw new GeospatialException("Error creating VRT output driver");
    }

    using var poVDS = vrtDriver.Create("", xSize, ySize, 4, DataType.GDT_Byte, null);

    string pszProjection = this.inputDataset.GetProjectionRef();
    if (pszProjection != null && pszProjection.Length > 0)
    {
        poVDS.SetProjection(pszProjection);
    }

    var adfGeoTransform = new double[6];
    if (MapTools.GetTransform(this.inputDataset, adfGeoTransform) == CPLErr.CE_None)
    {
        poVDS.SetGeoTransform(adfGeoTransform);
    }

    if (this.inputDataset.GetGCPCount() > 0)
    {
        int nGCPs = this.inputDataset.GetGCPCount();
        GCP[] pasGCPs = this.inputDataset.GetGCPs();

        poVDS.SetGCPs(pasGCPs, this.inputDataset.GetGCPProjection());
    }

    var papszMetadata = this.inputDataset.GetMetadata(null);

    poVDS.SetMetadata(papszMetadata, null);

    var pszInterleave = this.inputDataset.GetMetadataItem("INTERLEAVE", "IMAGE_STRUCTURE");
    if (pszInterleave != null)
    {
        poVDS.SetMetadataItem("INTERLEAVE", pszInterleave, "IMAGE_STRUCTURE");
    }

    var papszMD = this.inputDataset.GetMetadata("RPC");
    if (papszMD != null)
    {
        poVDS.SetMetadata(papszMD, "RPC");
    }

    papszMD = this.inputDataset.GetMetadata("GEOLOCATION");
    if (papszMD != null)
    {
        poVDS.SetMetadata(papszMD, "GEOLOCATION");
    }

    int nSrcBandCount = bandCount;

    var poSrcBand = this.inputDataset.GetRasterBand(Math.Abs(panBandList[0]));
    if (panBandList[0] < 0)
    {
        poSrcBand = poSrcBand.GetMaskBand();
    }

    var poColorTable = poSrcBand.GetColorTable();
    if (poColorTable == null)
    {
        throw new GeospatialException(string.Format("Band {0} has no color table", Math.Abs(panBandList[0])));
    }

    // Move band count to 4 for RGBA
    bandCount = RGBA_BAND_COUNT;

    for (int i = 0; i < bandCount; i++)
    {
        // There doesn't appear to be an implementation of VRTSourcedRasterBand
        //VRTSourcedRasterBand poVRTBand;
        int nComponent = 0;

        int nSrcBand;
        if (nSrcBandCount == 2 && i == 3)
        {
            nSrcBand = panBandList[1];
        }
        else
        {
            nSrcBand = panBandList[0];
            nComponent = i + 1;
        }

        poSrcBand = this.inputDataset.GetRasterBand(Math.Abs(nSrcBand));

        poVDS.AddBand(poSrcBand.DataType, null);
        //poVRTBand = (VRTSourcedRasterBand)poVDS.GetRasterBand(i + 1);
        if (nSrcBand < 0)
        {

            //poVRTBand.AddMaskBandSource(poSrcBand);
            continue;
        }

        double dfScale = 1.0, dfOffset = 0.0;

        if (i < RGBA_BAND_COUNT)
        {
            //poVRTBand.AddComplexSource(poSrcBand,
            //                            0, 0,
            //                            xSize, ySize,
            //                            0, 0, xSize, ySize,
            //                            0.0, 1.0,
            //                            VRT_NODATA_UNSET,
            //                            nComponent);
        }
        else
        {
            //poVRTBand.AddSimpleSource(poSrcBand,
            //                           0, 0,
            //                           xSize, ySize,
            //                           0, 0, xSize, ySize);
        }

        if (i < RGBA_BAND_COUNT)
        {
            poVRTBand.SetColorInterpretation((ColorInterp)((int)GdalConst.GCI_RedBand) + i);
        }
        else
        {
            //CopyBandInfo(poSrcBand, poVRTBand, FALSE, TRUE, FALSE);
        }

        if ((this.inputDataset.GetRasterBand(1).GetMaskFlags() & GdalConst.GMF_PER_DATASET) == 0 &&
            (poSrcBand.GetMaskFlags() & (GdalConst.GMF_ALL_VALID | GdalConst.GMF_NODATA)) == 0)
        {
            if (poVRTBand.CreateMaskBand(poSrcBand.GetMaskFlags()) == CPLErr.CE_None)
            {
                //VRTSourcedRasterBand hMaskVRTBand = (VRTSourcedRasterBand)poVRTBand.GetMaskBand();
                //hMaskVRTBand.AddMaskBandSource(poSrcBand,
                //                        0, 0,
                //                        xSize, ySize,
                //                        0, 0, xSize, ySize);
            }
        }
    }

    this.tempRgbaVrtFile = Path.ChangeExtension(Path.GetTempFileName(), "vrt");

    Dataset vrtOutput = vrtDriver.CreateCopy(this.tempRgbaVrtFile, poVDS, 0, null, null, null);
    if (vrtOutput == null)
    {
        return false;
    }

    vrtOutput.FlushCache();

    // Open the RGBA VRT file for the new input     
    this.inputDataset = Gdal.Open(this.tempRgbaVrtFile, Access.GA_ReadOnly);
    if (this.inputDataset == null)
    {
        Console.WriteLine("Unable to open temporary VRT file");
        return false;
    }

    return true;
}
MaxRev-Dev commented 10 months ago

@clintsinger again, they are not missing. My packages have the identical interface as the official GDAL packages. This repo is only a builder and it doesn't extend GDAL functionality.

You can review the SWIG interface in the GDAL repo - here. If you need any additional functionality consider to request it in official GDAL repo.