KML documents structure geographic information using an XML schema, fundamentally relying on longitude, latitude, and altitude parameters. Translating these structures into ESRI Shapefiles requires initializing the GDAL/OGR library and adjusting runtime environment variables to manage file paths and character encoding correctly.
The implementation below exports KML placemark data to a point-based shapefile. It registers the OGR drivers, applies UTF-8 configuration, defines the attribute schema, and iterates through the input collection to construct and append geometries.
public static string ExportKmlPlacemarksToShapefile(IEnumerable<Placemark> points, string outputPath)
{
Ogr.RegisterAll();
Gdal.SetConfigOption("GDAL_FILENAME_IS_UTF8", "YES");
Gdal.SetConfigOption("SHAPE_ENCODING", "");
var shapeDriver = Ogr.GetDriverByName("ESRI Shapefile");
if (shapeDriver == null) return "Driver initialization failed.";
using (var dataSource = shapeDriver.CreateDataSource(outputPath, null))
{
if (dataSource == null) return "Data source creation failed.";
var targetLayer = dataSource.CreateLayer(
Path.GetFileNameWithoutExtension(outputPath),
null,
wkbGeometryType.wkbPoint,
null
);
if (targetLayer == null) return "Layer creation failed.";
var nameField = new FieldDefn("label", FieldType.OFTString) { Width = 64 };
var altField = new FieldDefn("elevation", FieldType.OFTReal);
var latField = new FieldDefn("y_coordinate", FieldType.OFTReal);
var lonField = new FieldDefn("x_coordinate", FieldType.OFTReal);
targetLayer.CreateField(nameField, true);
targetLayer.CreateField(altField, true);
targetLayer.CreateField(latField, true);
targetLayer.CreateField(lonField, true);
using (var pointGeom = new Geometry(wkbGeometryType.wkbPoint))
using (var featureTemplate = new Feature(targetLayer.GetLayerDefn()))
{
foreach (var place in points)
{
pointGeom.SetPoint(0, place.Longitude, place.Latitude, place.Altitude);
featureTemplate.SetField("label", place.Name);
featureTemplate.SetField("elevation", place.Altitude);
featureTemplate.SetField("y_coordinate", place.Latitude);
featureTemplate.SetField("x_coordinate", place.Longitude);
featureTemplate.SetGeometry(pointGeom);
if (targetLayer.CreateFeature(featureTemplate) != 0)
return $"Failed to write feature: {place.Name}";
}
}
return "Export completed successfully.";
}
}
Translating Shapefiles to MapInfo TAB format frequently triggers precision degradation, where high-decimal coordinates are silently truncated. This behavior originates from implicit coordinate system mismatches between the drivers. The Shapefile format often lacks an embedded projection, while the MapInfo driver may enforce a default transformation that clips floating-point values. Resolving the issue requires explicitly inheriting the source spatial reference during destination layer creation.
The following routine extracts spatial metadata from the input dataset, applies it to the target TAB layer, and performs a structural copy of each feature definition and geometry.
public static string ConvertShapefileToTab(string sourcePath, string destinationPath)
{
Ogr.RegisterAll();
using (var sourceDs = Ogr.Open(sourcePath, 0))
{
if (sourceDs == null) return "Failed to open source dataset.";
var tabDriver = Ogr.GetDriverByName("MapInfo File");
if (tabDriver == null) return "MapInfo driver unavailable.";
using (var destDs = tabDriver.CreateDataSource(destinationPath, null))
{
if (destDs == null) return "Failed to initialize TAB dataset.";
for (int i = 0; i < sourceDs.GetLayerCount(); i++)
{
using (var sourceLayer = sourceDs.GetLayerByIndex(i))
{
var sourceSrs = sourceLayer.GetSpatialRef();
var layerGeomType = sourceLayer.GetLayerDefn().GetGeomType();
var layerName = sourceLayer.GetName();
using (var destLayer = destDs.CreateLayer(layerName, sourceSrs, layerGeomType, null))
{
if (destLayer == null) return $"Layer {layerName} creation failed.";
var sourceDefn = sourceLayer.GetLayerDefn();
for (int f = 0; f < sourceDefn.GetFieldCount(); f++)
{
destLayer.CreateField(sourceDefn.GetFieldDefn(f), true);
}
sourceLayer.ResetReading();
Feature sourceFeature;
while ((sourceFeature = sourceLayer.GetNextFeature()) != null)
{
using (sourceFeature)
{
var destFeature = sourceFeature.Clone();
if (destLayer.CreateFeature(destFeature) != 0)
{
return "Feature insertion error.";
}
destFeature.Dispose();
}
}
destLayer.SyncToDisk();
}
}
}
return "Format translation successful.";
}
}
}
Passing the spatial reference object directly into CreateLayer ensures the destination MapInfo TAB file adopts the exact projection parameters of the original Shapefile. This explicit assignment prevents automatic coordinate scaling and preserevs geometric accuracy throughout the transformation pipeline.