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.