ThinkGeo.com    |     Documentation    |     Premium Support

Pdf and Lambert Conformal Conic

Hello,

We need your help with the following issue.
We have a pdf map file which is having this geo information
"
Coordinate Reference System (CRS)
Name EPSG:7899 - GDA2020 / Vicgrid
Units meters
Method Lambert Conformal Conic
Celestial body Earth
Reference Static (relies on a datum which is plate-fixed)
"
When trying to open such file ThinkGeo returns an exception error
"
System.ApplicationException
HResult=0x80131600
Message=No translation for Lambert_Conformal_Conic to PROJ.4 format is known.
Source=ThinkGeo.Dependency.Gdal
StackTrace:
at OSGeo.GDAL.Gdal.Open(String utf8_path, Access eAccess)
at ThinkGeo.Core.GdalRasterSource.OpenCore()
at ThinkGeo.Core.RasterSource.Open()
at ThinkGeo.Core.Layer.Open()
"

Here is the code call:
"
GdalRasterLayer gdalLayer = new GdalRasterLayer(imagePathFileName);
myOverlay.Layers.Add(gdalLayer);
gdalLayer.Open();
"
The used version of ThinkGeo is 13.2.0

Please advice us how to solve such an issue.

Thank you very much for your help and support.

Br,
Anwar Sagar

Hi Anwar, any chance you can send us the pdf? You can either update it here or email to support@thinkgeo.com.

Thanks,
Ben

Hi Anwar,

We are in the middle of upgrading our GDAL engine, after it’s finished you can use the GDAL within ThinkGeo to convert a geoPdf to a geotiff, and then display it on the map. We tested with your pdf and it works correctly. We tested directly displaying your geoPdf though but having some issues, still working on it. The new GDAL engine is supposed to be ready by early next month.

Thanks,
Ben

Hi Ben,

Any new update on this matter?

Br,
Anwar

Hi Anwar,

The latest Gdal v3.9.1 is now available in ThinkGeo v14.2.

I found the gdal feature layer successfully gets the bounding box / reads the features, but it has some issues reading the native styles and render it properly, we’re still working on it. Here we created a layer for you which convert part of the GeoPdf to an png and display it, have a try and see if it works for you.

GeoImage bitmap2 = new GeoImage(1024, 512);
var layer2 = new GeoPdfRasterLayer("101018 MAC Moorefields road S T1 THP A3 Landscape.pdf");
layer2.Open();
RectangleShape extent = MapUtil.GetDrawingExtent(layer2.GetBoundingBox(), bitmap2.Width, bitmap2.Height);

GeoCanvas canvas2 = GeoCanvas.CreateDefaultGeoCanvas();
canvas2.BeginDrawing(bitmap2, extent, GeographyUnit.Meter);
layer2.Draw(canvas2, new Collection<SimpleCandidate>());
canvas2.EndDrawing();
bitmap2.Save(Path.Combine($"{_resultFolder}", "GeoPdfTest2_GeoPdfRasterLayer.png"));


class GeoPdfRasterSource : GdalRasterSource
{
    private Dataset _dataset;
    private string _geoPdfPath;
    private RectangleShape? _boundingBox;

    public GeoPdfRasterSource(string geoPdfPath)
    {
        _geoPdfPath = geoPdfPath;
    }

    protected override void OpenCore()
    {
        // Register GDAL drivers
        OSGeo.GDAL.Gdal.AllRegister();

        // Open the GeoPDF
        _dataset = OSGeo.GDAL.Gdal.Open(_geoPdfPath, Access.GA_ReadOnly);
        if (_dataset == null)
        {
            throw new Exception("Unable to open GeoPDF file.");
        }
    }

    protected override RectangleShape GetBoundingBoxCore()
    {
        if (_boundingBox != null) return _boundingBox;
        GdalFeatureSource gdalFeatureSource = new GdalFeatureSource(_geoPdfPath);
        gdalFeatureSource.Open();
        _boundingBox = gdalFeatureSource.GetBoundingBox();
        gdalFeatureSource.Close();

        return _boundingBox;
    }
    protected override GeoImage GetImageCore(RectangleShape worldExtent, int canvasWidth, int canvasHeight)
    {
        // Get the bounding box of the dataset
        double[] geoTransform = new double[6];
        _dataset.GetGeoTransform(geoTransform);

        var extent = worldExtent;

        // Get the pixel extent corresponding to the requested geographic extent
        var (xOff, yOff, xSize, ySize) = GetPixelExtent(extent, geoTransform, _dataset.RasterXSize, _dataset.RasterYSize);

        // Create a memory driver to write PNG to memory
        Driver memDriver = OSGeo.GDAL.Gdal.GetDriverByName("MEM");
        Dataset memDataset = memDriver.Create("", xSize, ySize, _dataset.RasterCount, _dataset.GetRasterBand(1).DataType, null);

        // Read raster data into memory dataset
        for (int i = 1; i <= _dataset.RasterCount; i++)
        {
            Band srcBand = _dataset.GetRasterBand(i);
            Band memBand = memDataset.GetRasterBand(i);

            // Create a buffer to hold the raster data for this band
            int bufferSize = xSize * ySize;
            byte[] buffer = new byte[bufferSize];

            // Read the raster data from the source band into the buffer
            srcBand.ReadRaster(xOff, yOff, xSize, ySize, buffer, xSize, ySize, 0, 0);

            // Write the buffer to the memory band
            memBand.WriteRaster(0, 0, xSize, ySize, buffer, xSize, ySize, 0, 0);
        }

        // Convert the memory dataset to PNG
        byte[] pngImage = ConvertDatasetToPNG(memDataset);
        
        var geoImage = new GeoImage(pngImage);
        var newGeoImage = geoImage.Scale(canvasWidth, canvasHeight);
        return newGeoImage;
    }

    private (int xOff, int yOff, int xSize, int ySize) GetPixelExtent(RectangleShape extent, double[] geoTransform, int rasterWidth, int rasterHeight)
    {
        // Convert geographic coordinates to pixel coordinates
        int xOff = (int)((extent.MinX - geoTransform[0]) / geoTransform[1]);
        int yOff = (int)((geoTransform[3] - extent.MaxY) / Math.Abs(geoTransform[5]));
        int xSize = (int)((extent.MaxX - extent.MinX) / geoTransform[1]);
        int ySize = (int)((extent.MaxY - extent.MinY) / Math.Abs(geoTransform[5]));

        // Clamp xOff, yOff to be within valid raster bounds
        xOff = Math.Max(0, xOff);
        yOff = Math.Max(0, yOff);

        // Adjust xSize and ySize if the requested size goes out of bounds
        if (xOff + xSize > rasterWidth)
        {
            xSize = rasterWidth - xOff;
        }
        if (yOff + ySize > rasterHeight)
        {
            ySize = rasterHeight - yOff;
        }

        return (xOff, yOff, xSize, ySize);
    }


    // Converts a GDAL dataset to a PNG image in byte array format
    private byte[] ConvertDatasetToPNG(Dataset dataset)
    {
        // Create a PNG driver
        Driver pngDriver = OSGeo.GDAL.Gdal.GetDriverByName("PNG");
        if (pngDriver == null)
        {
            throw new Exception("PNG driver not available.");
        }

        // Create a temporary file to store the PNG
        string tempPngPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName() + ".png");

        // Create the PNG file using the dataset
        Dataset pngDataset = pngDriver.CreateCopy(tempPngPath, dataset, 0, null, null, null);

        // Properly dispose of the created PNG dataset
        pngDataset.FlushCache();
        pngDataset.Dispose();  // This is crucial to release the file lock

        // Read the PNG file into a byte array
        byte[] pngImage = File.ReadAllBytes(tempPngPath);

        // Clean up the temporary file
        File.Delete(tempPngPath);

        return pngImage;
    }
}


class GeoPdfRasterLayer : GdalRasterLayer
{
    public GeoPdfRasterLayer(string geoPdfPath)
    {
        this.ImageSource = new GeoPdfRasterSource(geoPdfPath);
    }
}

Thanks,
Ben