6 votos

¿Cómo conservar los caracteres no ASCII en la importación de ogr2ogr?

Quiero importar una lista de nombres de lugares internacionales y sus ubicaciones a una base de datos de SQL Server utilizando ogr2ogr.

Los datos de origen son un archivo CSV codificado en UTF-8.

He preparado un archivo de muestra de cinco registros llamado london_points.csv . Contiene los registros de Londres en árabe, checo, inglés ruso y chino. Tiene el siguiente aspecto:

LanguageCode,Name,Longitude,Latitude
"ar","","-0.143555","51.577222"
"cz","Londýn","-0.143555","51.577222"
"en","London","-0.143555","51.577222"
"ru","","-0.143555","51.577222"
"zw","","-0.143555","51.577222"

Para importar el archivo CSV utilizando ogr2ogr he creado un archivo VRT llamado london_points.vrt . Le dice a ogr2ogr que importe los datos y derive una columna de puntos a partir de las columnas de Longitud y Latitud. El aspecto es el siguiente:

<OGRVRTDataSource>
    <OGRVRTLayer name="london_points">
        <SrcDataSource>london_points.csv</SrcDataSource>
        <GeometryType>wkbPoint</GeometryType>
        <LayerSRS>WGS84</LayerSRS>
        <GeometryField encoding="PointFromColumns" x="Longitude" y="Latitude"/>
    </OGRVRTLayer>
</OGRVRTDataSource>

ogr2ogr aparentemente importa con éxito los cinco registros en tempdb:

$ ogr2ogr --DEBUG ON -s_srs EPSG:4326 -lco GEOM_TYPE=geography -overwrite -f MSSQLSpatial "MSSQL:server=.;database=tempdb;trusted_connection=yes" london_points.vrt
OGR: OGROpen(london_points.vrt/014AFF40) succeeded as VRT.
OGR_MSSQLSpatial: EstablishSession(Connection:"server=.;database=tempdb;trusted_connection=yes")
ODBC: SQLDriverConnect(DRIVER=SQL Server;server=.;database=tempdb;trusted_connection=yes)
OGR_MSSQLSpatial: Using column ogr_fid as FID for table london_points.
OGR: OGROpen(MSSQL:server=.;database=tempdb;trusted_connection=yes/0338CFF0) succeeded as MSSQLSpatial.
OGR: OGROpen(london_points.csv/033A41C8) succeeded as CSV.
MSSQLSpatial: DeleteLayer(london_points)
OGR_MSSQLSpatial: Using column ogr_fid as FID for table london_points.
OGR2OGR: 5 features written in layer 'london_points'
ODBC: SQLDisconnect()
VRT: 5 features read on layer 'london_points'.
CSV: 5 features read on layer 'london_points'.

Utilizo una consulta como ésta para inspeccionar los resultados en la base de datos:

SELECT languagecode, name, longitude, latitude
FROM tempdb.dbo.london_points;

El conjunto de resultados tiene el siguiente aspecto:

languagecode name         longitude latitude
------------ ------------ --------- ---------
ar                -0.143555 51.577222
cz           Londýn      -0.143555 51.577222
en           London       -0.143555 51.577222
ru            -0.143555 51.577222
zw                  -0.143555 51.577222

El conjunto de resultados contiene el número correcto de filas. La página web languagecode , longitude y latitude las columnas, que sólo contienen caracteres ASCII, contienen los valores correctos.

Pero todos los caracteres no ASCII en el name ¡la columna está destrozada!

El principal documentación de ogr2ogr no menciona ningún interruptor que controle la codificación de caracteres.

El Documentación del controlador CSV sólo afirma que "Todos los archivos CSV se tratan con codificación UTF-8".

¿Es esto una limitación de ogr2ogr, o me falta algún interruptor mágico en alguna parte?

¿Hay algún problema con la configuración del controlador de GDAL SQL Server?

preguntó Andre Joost: "Tal vez sea un problema en el sitio del controlador de GDAL SQL Server. ¿Ha mirado en msdn.microsoft.com/es-us/library/ms130822.aspx sección AutoTranslate?"

Los dos valores documentados para AutoTranslate son yes y no . No está explicado, pero creo que yes es el valor por defecto.

SQL Server asigna automáticamente los puntos de código para los caracteres extendidos entre los diferentes conjuntos de caracteres ANSI (de un solo byte). Si se desactiva el comportamiento de traducción, los datos de los caracteres podrían quedar desordenados si el cliente y el servidor utilizan páginas de códigos diferentes.

El tipo de datos almacenados es Unicode (nvarchar) en lugar de ANSI (varchar) por lo que no creo que la configuración tenga efecto aquí.

Importaré los datos una vez con la traducción explícitamente desactivada y otra vez con ella explícitamente activada para ver si hay alguna diferencia.

En este ejemplo, la cadena de conexión contiene autotranslate=no :

$ ogr2ogr --DEBUG ON -s_srs EPSG:4326 -lco GEOM_TYPE=geography -overwrite -f MSSQLSpatial "MSSQL:server=.;database=tempdb;trusted_connection=yes;autotranslate=no" london_points.vrt
OGR: OGROpen(london_points.vrt/012AFF40) succeeded as VRT.
OGR_MSSQLSpatial: EstablishSession(Connection:"server=.;database=tempdb;trusted_connection=yes;autotranslate=no")
ODBC: SQLDriverConnect(DRIVER=SQL Server;server=.;database=tempdb;trusted_connection=yes;autotranslate=no)
OGR_MSSQLSpatial: Using column ogr_fid as FID for table london_points.
OGR: OGROpen(MSSQL:server=.;database=tempdb;trusted_connection=yes;autotranslate=no/0335E048) succeeded as MSSQLSpatial.
OGR: OGROpen(london_points.csv/033732D8) succeeded as CSV.
MSSQLSpatial: DeleteLayer(london_points)
OGR_MSSQLSpatial: Using column ogr_fid as FID for table london_points.
OGR2OGR: 5 features written in layer 'london_points'
ODBC: SQLDisconnect()
VRT: 5 features read on layer 'london_points'.
CSV: 5 features read on layer 'london_points'.

SELECT languagecode, name, longitude, latitude
FROM tempdb.dbo.london_points;

languagecode name         longitude latitude
------------ ------------ --------- ---------
ar                -0.143555 51.577222
cz           Londýn      -0.143555 51.577222
en           London       -0.143555 51.577222
ru            -0.143555 51.577222
zw                  -0.143555 51.577222

En este ejemplo, la cadena de conexión contiene autotranslate=yes :

$ ogr2ogr --DEBUG ON -s_srs EPSG:4326 -lco GEOM_TYPE=geography -overwrite -f MSSQLSpatial "MSSQL:server=.;database=tempdb;trusted_connection=yes;autotranslate=yes" london_points.vrt
OGR: OGROpen(london_points.vrt/02DCFF40) succeeded as VRT.
OGR_MSSQLSpatial: EstablishSession(Connection:"server=.;database=tempdb;trusted_connection=yes;autotranslate=yes")
ODBC: SQLDriverConnect(DRIVER=SQL Server;server=.;database=tempdb;trusted_connection=yes;autotranslate=yes)
OGR_MSSQLSpatial: Using column ogr_fid as FID for table london_points.
OGR: OGROpen(MSSQL:server=.;database=tempdb;trusted_connection=yes;autotranslate=yes/0344E048) succeeded as MSSQLSpatial.
OGR: OGROpen(london_points.csv/034632D8) succeeded as CSV.
MSSQLSpatial: DeleteLayer(london_points)
OGR_MSSQLSpatial: Using column ogr_fid as FID for table london_points.
OGR2OGR: 5 features written in layer 'london_points'
ODBC: SQLDisconnect()
VRT: 5 features read on layer 'london_points'.
CSV: 5 features read on layer 'london_points'.

SELECT languagecode, name, longitude, latitude
FROM tempdb.dbo.london_points;

languagecode name         longitude latitude
------------ ------------ --------- ---------
ar                -0.143555 51.577222
cz           Londýn      -0.143555 51.577222
en           London       -0.143555 51.577222
ru            -0.143555 51.577222
zw                  -0.143555 51.577222

Los resultados son idénticos.

¿Qué sentencias SQL envía ogr2ogr al servidor?

He rastreado la importación utilizando Perfilador de SQL Server para capturar las sentencias SQL que ogr2ogr utiliza para insertar datos en el london_points mesa.

El rastreo capturó estas declaraciones de inserción:

INSERTINTO [dbo].[london_points] (ogr_geometry, [languagecode],[name], [longitude], [latitude])
VALUES (geography::STGeomFromText('POINT (-0.143555 51.577222)',4326), 'ar', '', '-0.143555', '51.577222');
go
INSERT INTO [dbo].[london_points] (ogr_geometry, [languagecode], [name], [longitude], [latitude])
VALUES (geography::STGeomFromText('POINT (-0.143555 51.577222)',4326), 'cz', 'Londýn', '-0.143555', '51.577222');
go
INSERT INTO [dbo].[london_points] (ogr_geometry, [languagecode], [name], [longitude], [latitude])
VALUES (geography::STGeomFromText('POINT (-0.143555 51.577222)',4326), 'en', 'London', '-0.143555', '51.577222');
go
INSERT INTO [dbo].[london_points] (ogr_geometry, [languagecode], [name], [longitude], [latitude])
VALUES (geography::STGeomFromText('POINT (-0.143555 51.577222)',4326), 'ru', '', '-0.143555', '51.577222');
go
INSERT INTO [dbo].[london_points] (ogr_geometry, [languagecode], [name], [longitude], [latitude])
VALUES (geography::STGeomFromText('POINT (-0.143555 51.577222)',4326), 'zw', '', '-0.143555', '51.577222');
go

Para mayor claridad, he dividido cada declaración INSERT VALUES en dos líneas.

La inspección del SQL revela dos problemas.

ogr2ogr manipula los valores para el name antes de que lleguen al servidor. Esto demuestra que el problema reside en ogr2ogr.

ogr2ogr pasa los valores como literales varchar ( 'abc' ) en lugar de los literales nvarchar ( N'abc' ). Aunque el texto de la consulta contuviera los caracteres correctos, el servidor sustituiría por un signo de interrogación cualquier carácter que no existiera en la página de códigos por defecto.

Esta consulta muestra la diferencia:

SELECT '' AS varchar_value, N'' AS nvarchar_value
UNION ALL
SELECT 'Londýn', N'Londýn'
UNION ALL
SELECT 'London', N'London'
UNION ALL
SELECT '', N''
UNION ALL
SELECT '', N'';

varchar_value nvarchar_value
------------- --------------
????          
Londýn        Londýn
London        London
??????        
??            

Mi página de códigos por defecto es Windows-1252. Contiene caracteres para el inglés y el checo, por lo que los literales de cadena varchar están bien para estos idiomas. El árabe, el ruso y el chino tradicional sólo son compatibles con Unicode, por lo que todos los caracteres se sustituyen por signos de interrogación.

Ahora creo que se trata de un fallo en el controlador MSSQLSpatial de OGR.

4voto

Hartmut Puntos 431

Tengo el mismo problema, mi entendimiento es que los datos almacenados son correctos, si se consideran datos binarios codificados en utf-8, para convertir estos datos a nvarchar escribí esta función UDF (parcialmente probada) en sql server:

CREATE FUNCTION varbinUTF8_to_nvarchar ( @data VARBINARY(MAX) )
RETURNS NVARCHAR(MAX)
AS
BEGIN
    DECLARE @n INT
    DECLARE @buffer NVARCHAR(MAX)
    SET @buffer = N''
    SET @n = DATALENGTH(@data)

    DECLARE @i INT
    SET @i = 0

    DECLARE @byte INT
    DECLARE @byte2 INT
    DECLARE @byte3 INT
    DECLARE @byte4 INT

    DECLARE @decoded NCHAR

    WHILE @i < @n
        BEGIN
            SET @i = @i + 1
            SET @byte = SUBSTRING(@data, @i, 1)
            IF ( @byte < 0x7f )
                SET @decoded = NCHAR(@byte)
            ELSE
                IF @byte & 0xe0 = 0xc0
                    BEGIN
                        SET @i = @i + 1
                        SET @byte2 = SUBSTRING(@data, @i, 1)
                        SET @decoded = NCHAR(( ( @byte & 0x1F ) * 0x40 )
                                             + ( @byte2 & 0x3f ))       
                    END
                ELSE
                    IF @byte & 0xf0 = 0xe0
                        BEGIN
                            SET @i = @i + 1
                            SET @byte2 = SUBSTRING(@data, @i, 1)
                            SET @i = @i + 1
                            SET @byte3 = SUBSTRING(@data, @i, 1)
                            SET @decoded = NCHAR(( ( @byte & 0xF )
                                                   * 0x1000 ) + ( ( @byte2
                                                          & 0x3f ) * 0x40 )
                                                 + ( (@byte3 & 0x3f) ))
                        END 
                    ELSE
                        IF @byte & 0xf8 = 0xf0
                            BEGIN
                                SET @i = @i + 1
                                SET @byte2 = SUBSTRING(@data, @i, 1)
                                SET @i = @i + 1
                                SET @byte3 = SUBSTRING(@data, @i, 1)
                                SET @i = @i + 1
                                SET @byte4 = SUBSTRING(@data, @i, 1)
                                /*
                                EDIT 2014-11-27 THIS PART WERE WRONG
                                SET @decoded = NCHAR(( ( @byte & 0xF )
                                                       * 0x1000 )
                                                     + ( ( @byte2 & 0x3f )
                                                         * 0x40 )
                                                     + ( ( @byte3 & 0x3f )
                                                         * 0x40 )
                                                     + ( (@byte4 & 0x3f) ))
                                 FOLLOWS PATCH, but sql server does not support unicode chars > 16 bit
                                */
                                SET @decoded = NCHAR(( ( @byte & 0x7 )
                                                       * 0x40000)
                                                     + ( ( @byte2 & 0x3f )
                                                         * 0x1000 )
                                                     + ( ( @byte3 & 0x3f )
                                                         * 0x40 )
                                                     + ( (@byte4 & 0x3f) ))

                            END 
                        ELSE
                            BEGIN
                                return  N'BAD UTF-8 MESSAGE'
                            END 

            SET @buffer = @buffer + @decoded

        END 
    return  @buffer
END

Entonces puedo convertir el campo mal formateado usando algo como

SELECT     
    dbo.varbinUTF8_to_nvarchar(CONVERT(VARBINARY(MAX), name_1) )
FROM
    dbo.che_adm1

En tu caso concreto deberías poder obtener datos nvarchar haciendo :

SELECT languagecode, dbo.varbinUTF8_to_nvarchar(CONVERT(VARBINARY(MAX), name) as name, longitude, latitude
FROM tempdb.dbo.london_points;

Saludos cordiales Andrea

1voto

SpliFF Puntos 214

Es no un problema del controlador ogr csv.

Cuando cargo sus datos en QGIS como texto delimitado, la codificación se conserva. Lo único que QGIS se vuelve loco es que lat y lon no deben estar entre comillas. Pero en la tabla de atributos todas las entradas de nombres se ven bien.

Además, la transformación con ogr2ogr a kml y gml da archivos de salida correctos.

Así que el error debe estar en el lado de la exportación de SQL Server.

Si nadie da con una solución, podría valer la pena preguntar en la lista de correo de gdal-dev, o abrir un ticket de error en http://trac.osgeo.org/gdal/report .

Tal vez este billete: http://trac.osgeo.org/gdal/ticket/2339 no se ha fijado correctamente. O los tipos de variables de su base de datos necesitan una atención especial, como se menciona en el ticket.

La forma en que ogr maneja la codificación unicode está documentada aquí: http://trac.osgeo.org/gdal/wiki/rfc23_ogr_unicode

0voto

Tomáš Zato Puntos 835

Parece un defecto del controlador OGR MSSQLSpatial.

He planteado billete #5239 con el equipo de GDAL para solucionar el defecto.

Actualizaré esta respuesta cuando el ticket esté resuelto.

i-Ciencias.com

I-Ciencias es una comunidad de estudiantes y amantes de la ciencia en la que puedes resolver tus problemas y dudas.
Puedes consultar las preguntas de otros usuarios, hacer tus propias preguntas o resolver las de los demás.

Powered by:

X