Adadev

Converter JSON para shapefile usando Gdal em C#

27 de Maio de 2017

Neste artigo eu explico como converter um arquivo no formato JSON para shapefile usando a biblioteca Gdal em C#. Segue um exemplo de arquivo JSON (você também pode baixar o arquivo completo):

{
	"features": [{
		"geometry": {
			"coordinates": [-37.2726631195437,
			-9.91254751352393],
			"type": "Point"
		},
		"properties": {
			"identifier": "12",
			"serial_number": "29213220",
			"name": "POINT-10",
			"length": 3.0
		},
		"type": "Feature"
	},
	{
		"geometry": {
			"coordinates": [-37.2726722686872,
			-9.91252509027536],
			"type": "Point"
		},
		"properties": {
			"identifier": "11",
			"serial_number": "29213220",
			"name": "POINT-10",
			"length": 4.0,
		},
		"type": "Feature"
	}
}

Vamos para o passo a passo:

Importar o Gdal no Visual Studio

Primeiramente, importamos o Gdal para o projeto: no Visual Studio, clique com o botão direito no projeto, vá em "Manage NuGet packages", digite "Gdal" no campo de busca, selecione o Gdal (1.11.1 é a última versão disponível até o momento em que este artigo foi escrito) e instale. Selecione e instale também o Gdal.Native (sem ele o projeto compila mas não executa corretamente). A classe "GdalConfiguration.cs" será criada automaticamente. Ela será usada antes de iniciar o método para conversão.

Para zipar os arquivos gerados do shapefile, será necessária também a biblioteca "DotNetZip". Adicione-a também pelo "Manage NuGet packages". Caso não queira essa funcionalidade, não é necessário instalar.

Para importá-lo em sua classe use:

using OSGeo.OGR;
using OSGeo.OSR;

Configurar o Gdal

Antes de usar as classes do Gdal, precisamos configurá-lo. Para isso, basta chamar os métodos ConfigureGdal e ConfigureOgr da classe GdalConfiguration.cs. Você pode chamá-los, por exemplo, no construtor de sua classe.

public GdalUtilities() {
    GdalConfiguration.ConfigureGdal();
    GdalConfiguration.ConfigureOgr();
}

Abrir o JSON com o Gdal

Código para abrir o arquivo JSON com o Gdal e pegar sua camada:

Driver jsonFileDriver = Ogr.GetDriverByName("GeoJSON");
DataSource jsonFile = Ogr.Open(jsonFilePath, 0);
if(jsonFile == null) {
    return false;
}
Layer jsonLayer = jsonFile.GetLayerByIndex(0);

Criar o arquivo shapefile

Código para criar o arquivo shapefile com o Gdal. Caso o arquivo já exista ele será removido.

string filesPathName = shapeFilePath.Substring(0, shapeFilePath.Length - 4);
removeShapeFileIfExists(filesPathName);

Driver esriShapeFileDriver = Ogr.GetDriverByName("ESRI Shapefile");
DataSource shapeFile = esriShapeFileDriver.CreateDataSource(shapeFilePath, new string[] { });
Layer shplayer = shapeFile.CreateLayer(jsonLayer.GetName(), jsonLayer.GetSpatialRef(), jsonLayer.GetGeomType(), new string[] { });

Copiar os dados e propriedades do JSON para o shapefile

Código para criar copiar as propriedades e valores de cada elemento do arquivo JSON para o novo shapefile criado:

// create fields (properties) in new layer
Feature jsonFeature = jsonLayer.GetNextFeature();
for(int i = 0; i < jsonFeature.GetFieldCount(); i++) {
	FieldDefn fieldDefn = new FieldDefn(getValidFieldName(jsonFeature.GetFieldDefnRef(i)), jsonFeature.GetFieldDefnRef(i).GetFieldType());
	shplayer.CreateField(fieldDefn, 1);
}

while(jsonFeature != null) {
	Geometry geometry = jsonFeature.GetGeometryRef();
	Feature shpFeature = createGeometryFromGeometry(geometry, shplayer, jsonLayer.GetSpatialRef());

	// copy values for each field
	for(int i = 0; i < jsonFeature.GetFieldCount(); i++) {
		if(FieldType.OFTInteger == jsonFeature.GetFieldDefnRef(i).GetFieldType()) {
			shpFeature.SetField(getValidFieldName(jsonFeature.GetFieldDefnRef(i)), jsonFeature.GetFieldAsInteger(i));
		} else if(FieldType.OFTReal == jsonFeature.GetFieldDefnRef(i).GetFieldType()) {
			shpFeature.SetField(getValidFieldName(jsonFeature.GetFieldDefnRef(i)), jsonFeature.GetFieldAsDouble(i));
		} else {
			shpFeature.SetField(getValidFieldName(jsonFeature.GetFieldDefnRef(i)), jsonFeature.GetFieldAsString(i));
		}
	}
	shplayer.SetFeature(shpFeature);

	jsonFeature = jsonLayer.GetNextFeature();
}

Fechar o shapefile e salvar como zip

Código para fechar o shapefile criado e setado e zipar todos arquivos gerados.

shapeFile.Dispose();

// if you want to generate zip of generated files
string zipName = filesPathName + ".zip";
CompressToZipFile(new List<string>() { 
	shapeFilePath, filesPathName + ".dbf", filesPathName + ".prj", filesPathName + ".shx" }, zipName);

Converter JSON para shapefile - código completo

new GdalUtilities().convertJsonToShapeFile(@"C:\development\example-data.json", @"C:\development\shapefile-out.shp");
using OSGeo.OGR;
using OSGeo.OSR;
using System.Collections.Generic;
using Ionic.Zip;
using System.IO;

namespace Adadev.GdalModule {

    public class GdalUtilities {

        public GdalUtilities() {
            GdalConfiguration.ConfigureGdal();
            GdalConfiguration.ConfigureOgr();
        }

        public bool convertJsonToShapeFile(string jsonFilePath, string shapeFilePath) {

            Driver jsonFileDriver = Ogr.GetDriverByName("GeoJSON");
            DataSource jsonFile = Ogr.Open(jsonFilePath, 0);
            if(jsonFile == null) {
                return false;
            }

            string filesPathName = shapeFilePath.Substring(0, shapeFilePath.Length - 4);
            removeShapeFileIfExists(filesPathName);

            Layer jsonLayer = jsonFile.GetLayerByIndex(0);

            Driver esriShapeFileDriver = Ogr.GetDriverByName("ESRI Shapefile");
            DataSource shapeFile = Ogr.Open(shapeFilePath, 0);

            shapeFile = esriShapeFileDriver.CreateDataSource(shapeFilePath, new string[] { });
            Layer shplayer = shapeFile.CreateLayer(jsonLayer.GetName(), jsonLayer.GetSpatialRef(), jsonLayer.GetGeomType(), new string[] { });

            // create fields (properties) in new layer
            Feature jsonFeature = jsonLayer.GetNextFeature();
            for(int i = 0; i < jsonFeature.GetFieldCount(); i++) {
                FieldDefn fieldDefn = new FieldDefn(getValidFieldName(jsonFeature.GetFieldDefnRef(i)), jsonFeature.GetFieldDefnRef(i).GetFieldType());
                shplayer.CreateField(fieldDefn, 1);
            }

            while(jsonFeature != null) {
                Geometry geometry = jsonFeature.GetGeometryRef();
                Feature shpFeature = createGeometryFromGeometry(geometry, shplayer, jsonLayer.GetSpatialRef());

                // copy values for each field
                for(int i = 0; i < jsonFeature.GetFieldCount(); i++) {
                    if(FieldType.OFTInteger == jsonFeature.GetFieldDefnRef(i).GetFieldType()) {
                        shpFeature.SetField(getValidFieldName(jsonFeature.GetFieldDefnRef(i)), jsonFeature.GetFieldAsInteger(i));
                    } else if(FieldType.OFTReal == jsonFeature.GetFieldDefnRef(i).GetFieldType()) {
                        shpFeature.SetField(getValidFieldName(jsonFeature.GetFieldDefnRef(i)), jsonFeature.GetFieldAsDouble(i));
                    } else {
                        shpFeature.SetField(getValidFieldName(jsonFeature.GetFieldDefnRef(i)), jsonFeature.GetFieldAsString(i));
                    }
                }
                shplayer.SetFeature(shpFeature);

                jsonFeature = jsonLayer.GetNextFeature();
            }
            shapeFile.Dispose();

            // if you want to generate zip of generated files
            string zipName = filesPathName + ".zip";
            CompressToZipFile(new List<string>() { shapeFilePath, filesPathName + ".dbf", filesPathName + ".prj", filesPathName + ".shx" }, zipName);

            return true;
        }

        private void removeShapeFileIfExists(string filesPathName) {
            removeFileIfExists(filesPathName + ".shp");
            removeFileIfExists(filesPathName + ".shx");
            removeFileIfExists(filesPathName + ".prj");
            removeFileIfExists(filesPathName + ".zip");
        }

        public static bool removeFileIfExists(string filePath) {
            if(File.Exists(filePath)) {
                File.Delete(filePath);
                return true;
            }
            return false;
        }

        // the field names in shapefile have limit of 10 characteres
        private string getValidFieldName(FieldDefn fieldDefn) {
            string fieldName = fieldDefn.GetName();
            return fieldName.Length > 10 ? fieldName.Substring(0, 10) : fieldName;
        }

        private Feature createGeometryFromGeometry(Geometry geometry, Layer layer, SpatialReference reference) {
            Feature feature = new Feature(layer.GetLayerDefn());

            string wktgeometry = "";
            geometry.ExportToWkt(out wktgeometry);
            Geometry newGeometry = Geometry.CreateFromWkt(wktgeometry);
            newGeometry.AssignSpatialReference(reference);

            feature.SetGeometry(newGeometry);
            layer.CreateFeature(feature);

            return feature;
        }

        public static void CompressToZipFile(List<string> files, string zipPath) {
            using(ZipFile zip = new ZipFile()) {
                foreach(string file in files) {
                    zip.AddFile(file, "");
                }
                zip.Save(zipPath);
            }
        }
    }
}

Você pode baixar o código completo e arquivos de exemplo no nosso repositório no Github.

Conteúdo relacionado:

Ler e Escrever Imagens com o Gdal em C#

Algoritmo Balanço de Branco com Gdal em C#

Veja mais em:

Exemplos de código

Gdal

[voltar ao início]