The first part of this blog series is providing some background information and lays the basis for working with the ESRI File Geodatabase data format for geospatial data.
The first part of this blog series is providing some background information and lays the basis for working with the ESRI File Geodatabase data format for geospatial data.
Working with geospatial data can be tricky without proper tools and guidelines. Years ago we have been working on a project with the task to analyze and handle raster and vector datasets. Back then the only supported vector-format was ESRI Shapefile, so the .NET library DotSpatial looked quite promising and was working well until now. However this package was not updated in the last few years, also new requirements like .NET Core, potentially Linux support and File Geodatabase support required some rethinking.
We are focussing now on the File Geodatabase support here. File Geodatabase is data format developed by ESRI and more or less replaces the older Personal Geodatabase format, based on Microsoft Access. It is a container format for storing both vector as well as raster data. Typically this format is represented as a folder with the .gdb suffix, but it can also be compressed in a zip-archive.
This tutorial series is split into 3 separate posts and gives you some insights into how to handle File Geodatabase in your C# solutions.
Note: As test data for the following examples, we are using the Urban Atlas 2012 for Austria, which you also find in the sample code of this blog post. Any sceenshots or output we are showing here will visualize the output of this particular file geodatabase, but the results should look very similar for any other dataset as well.
GDAL/OGR is the de-facto standard open source libary for reading and writing geospatial data formats. The Geospatial Data Abstraction Library (GDAL) is split into 3 main components:
GDAL is written in C, so we need a wrapper to use it in our .NET application. Searching for NuGet packages results in two potential solutions:
We start with a new .NET Core Console App and install the Gdal.Core NuGet Packages (select the runtime based on your target OS):
dotnet add package MaxRev.Gdal.Core
dotnet add package MaxRev.Gdal.WindowsRuntime.Minimal
Next step is to add the using statement and run the ConfigureAll method:
namespace FileGeodatabaseSample
{
using MaxRev.Gdal.Core;
public class Program
{
public static void Main(string[] args)
{
GdalBase.ConfigureAll();
// GDAL is ready.
}
}
}
In the following chapters we are going to perform a few common tasks when handling file geodatabases, starting from simply opening and examining its content over more in-depth analysis regarding individual layers, attributes and geometric properties to a number of specific and applied tasks.
To open a file geodatabase we have to select the appropriate OGR driver. There are two options:
Since we are only reading and analyzing data in this post we will go with the simpler, built-in option and leave the latter option for a potential future blog post.
After we selected the right driver, we can simply open the GDB. Note: There is no need to extract the zipped geodatabase. The driver works with both the zipped or unzipped version.
After that we can iterate over the contained layers and print their names in the console.
private static void Open(string dataSetPath)
{
var fileGdbDriver = Ogr.GetDriverByName("OpenFileGDB");
var dataSource = fileGdbDriver.Open(dataSetPath, 0);
var layerCount = dataSource.GetLayerCount();
for (var i = 0; i < layerCount; i++)
{
var layer = dataSource.GetLayerByIndex(i);
Console.WriteLine($"Layer: {layer.GetName()}");
}
}
In our case the ouput will look like this:
Layer: UA2012_AT_Merge
Now that we opened the file geodatabase and successfully identified the contained layers, we can have a closer look at the layer. In the following example we will:
private static void Details(string dataSetPath)
{
var fileGdbDriver = Ogr.GetDriverByName("OpenFileGDB");
var dataSource = fileGdbDriver.Open(dataSetPath, 0);
var layer = dataSource.GetLayerByIndex(0);
var shapeType = layer.GetGeomType().ToString("G").Substring(3);
Console.WriteLine($"Shape Type: {shapeType}");
var spatialReference = layer.GetSpatialRef();
var projectionName = spatialReference.GetName();
Console.WriteLine($"Projection: {projectionName}");
var extent = new Envelope();
layer.GetExtent(extent, 0);
var dataSetExtent = new
{
XMin = extent.MinX,
XMax = extent.MaxX,
YMin = extent.MinY,
YMax = extent.MaxY,
};
Console.WriteLine($"Extent: {JsonSerializer.Serialize(dataSetExtent, new JsonSerializerOptions { WriteIndented = true })}");
var featureCount = (int)layer.GetFeatureCount(0);
Console.WriteLine($"Feature Count: {featureCount}");
var columns = new List<dynamic>();
var layerDefinition = layer.GetLayerDefn();
for (var j = 0; j < layerDefinition.GetFieldCount(); j++)
{
var field = layerDefinition.GetFieldDefn(j);
columns.Add(new
{
Name = field.GetName(),
DataType = field.GetFieldTypeName(field.GetFieldType()),
});
}
Console.WriteLine($"Columns: {JsonSerializer.Serialize(columns, new JsonSerializerOptions { WriteIndented = true })}");
}
Output:
Shape Type: MultiPolygon
Projection: ETRS89 / LAEA Europe
Extent: {
"XMin": 4382010.8362,
"XMax": 4854633.2937,
"YMin": 2595132.8378,
"YMax": 2879685.3701
}
Feature Count: 304122
Columns: [
{
"Name": "COUNTRY",
"DataType": "String"
},
{
"Name": "CITIES",
"DataType": "String"
},
{
"Name": "FUA_OR_CIT",
"DataType": "String"
},
{
"Name": "CODE2012",
"DataType": "String"
},
{
"Name": "ITEM2012",
"DataType": "String"
},
{
"Name": "PROD_DATE",
"DataType": "String"
},
{
"Name": "IDENT",
"DataType": "String"
},
{
"Name": "Shape_Leng",
"DataType": "Real"
},
{
"Name": "Shape_Length",
"DataType": "Real"
},
{
"Name": "Shape_Area",
"DataType": "Real"
}
]
Since we are now able to open a File Geodatabase, iterate over its layers and extract some metadata, it is time to look at the actual data in the layer. This will be done in part 2 of our series.