Sigo con el ejemplo de la entrada anterior, convertir un fichero KML en un fichero GPX. Para convertir un tipo de fichero en otro tipo de fichero necesito conocer la estructura de los datos en ambos tipos de ficheros. Mas o menos ambas están explicadas en la entrada anterior.
Esta vez lo voy a hacer por programación, no mediante el camino, un tanto barroco, de preparar el fichero de texto para colocar los distintos campos de datos a mi conveniencia y mediante formulas excel, separar, concatenar, para volver a editar un fichero de texto y ¡por fin! obtener el resultado final.
Antes de empezar la a trabajar es conveniente conocer algunas particularidades del código ASCII en ficheros de texto, que es el código que se utiliza para representar los distintos caracteres. El código ASCII se puede dividir en dos partes, los llamados caracteres de control y los caracteres imprimibles. De los caracteres de control, desde mi punto de vista, solo tienen interés en un fichero de texto, la tabulación (ASCII 9), el salto de línea (LF, ASCII 10) y el retorno de carro (CR, ASCII 13). El resto de los caracteres de control , algunos, probablemente se sigan utilizando en los teclados y otros ya no tengan ninguna utilidad, se utilizaban para máquinas hoy en día en prácticamente desuso, como los teletipos.
Dependiendo de donde venga un fichero de texto puede que el fin de línea lo haga con un CR+LF o solamente con un LF. El doble fin de línea, desde mi punto de vista, viene de las ya olvidadas máquinas de escibir, anteriores a cualquier ordenador y a otro tipo de máquinas como los telex o teletipos. Para los que conocimos las máquinas de escribir tiene sentido, era lo que se hacía para pasar a la siguiente línea.
Algunas funciones de VBasic para el manejo de textos:
Algunas funciones de VBasic para el manejo de textos:
- Dos textos se concatenan con un &. Es para textos el equivalente al + para los números.
- Asc(Carac). Devuelve el número ASCII del carácter.
- Right(Cadena,n). Devuelve los últimos n caracteres de la derecha de una cadena alfanumérica.
- Left(Cadena,n). Devuelve los primeros n caracteres de la izquierda de una cadena alfanumérica.
- Lin = Mid(Lin, Pos1 + 6,n). Devuelve, a partir de el segundo parámetro (Pos1+6), n caracteres. Este tercer parámetro es opcional.
- InStr(LCase(Lin), "<when>"). Devuelve la posición en la que se encuentra una cadena dentro de otra.
- LCase(Cad). Devuelve la cadena en minúsculas.
- UCase(cad). Devuelve la cadena en mayúsculas.
- Len(Cad). Longitud o número de caracteres de una cadena alfanumérica.
- Split(Cad,Car). Divide y pasa la cadena alfanumérica a una matriz. El carácter "car" es el separador.
- Chr(n). Devuelve el carácter correspondiente al número n (en decimal).
- Replace("ABCD", "D", "X"). Cambia un conjunto de caracteres por otros. En este caso cambia D por X.
- Format(9, "0.00"). Da formato a un texto, en este caso presenta 9 como 9,00. Da muchas posibilidades.
- Algunas de las funciones anteriores tienen otros parámetros adicionales.
En principio la conversión es relativamente sencilla, incluso es mucho mas sencilla que hacerla sin programación. Sin programación, lo reconozco es una cosa muy compleja.
Proceso de lectura:
- Cierro, el fichero #n con close #n. En este caso n=1.
- Leo, en la primera hoja del libro excel, el nombre y el directorio de trabajo.
- Abro el fichero origen con Open DirT & Nom For Input As #1
- Leo, secuencialmente, hasta el final y carácter a carácter, el fichero de texto origen de los datos.
- En este caso no considero el carácter de ascii 13. No lo trato.
- Concateno el carácter leído con los caracteres leídos con anterioridad.
- Busco el/los delimitadores, o etiquetas que me marcan los datos. Para la fecha y hora de un punto busco las etiquetas <when> y </when> con Pos1 = InStr(LCase(Lin), "<when>") y Pos2 = InStr(LCase(Lin), "</when>"), trabajando en minúsculas. Extraigo los datos con Lin = Left(Lin, Pos2 - 1) y Lin = Mid(Lin, Pos1 + 6).
- Separo los datos. Una vez encontrados, mantengo fecha y hora pero separo las coordenadas y la altura con Coor = Split(Lin, ",").
- Concateno los datos válidos con las etiquetas correspondientes a los ficheros GPX.
- Un punto está definido por unas coordenadas, su altura y la fecha y hora en que fue tomado, aunque en este fichero kml aparece primero la fecha y hora. Al encontrar una linea de datos pongo a cero (Lin=""). Al encontrar todos los datos de un punto los escribo en el fichero de salida y en la hoja excel Aux e incremento línea para la siguiente vez que escriba en la hoja excel. Hay otras maneras, quizás mas correctas, de hacerlo. Cosas que he heredado de mi mismo. Empiezas a hacerlo de una manera y continuas haciendolo así, sin plantearmelo.
- Al encontrar un carácter 10 (LF) borro Lin (Lin=""). En otros casos, lo lo mejor, sería conveniente convertirlo en un cáracter nulo.
Libro excel:
- Tiene dos hojas, en la primera ("Config"), sitúo el directorio de trabajo y el nombre del fichero de texto a leer.
- El fichero de salida hereda tanto directorio de trabajo como nombre del fichero de entrada.
- Con Alt+F11 se puede entrar a los módulos VBasic. Localizo la macro LeeTexto2 y la ejecuto con F5.
- La macro LeeTexto2 convierte el fichero KML a fichero GPX.
Escritura en fichero de texto de salida:
- Cierro, el fichero #n con close #n.
- Abro el fichero de salida. Nombre, incluido directorio, y tipo de E/S. En este caso Open DirT & NomRut For Output As #2. A partir de este momento podemos escribir en el fichero de salida con Print #2, Texto
- Escribo la cabecera propia de los ficheros GPX.
- Incorporo los datos encontrados durante el proceso de lectura del fichero origen (#1)
- Escribo la cola propia de los ficheros GPX.
- Cierro fichero.
Código de LeeTexto2:
Sub LeeTexto2()
Dim Carac, Lin, Pos1, Pos2, DirT, Nom, N, Longitud, Coor, LinT, NomRut, H
NomRut = "prueba"
Nom = Sheets(1).Range("b2")
DirT = Sheets("Config").Range("a2")
Set H = Sheets("aux")
H.UsedRange.Clear
Close #1
Close #2
N = 1
NomRut = Left(Nom, Len(Nom) - 4) & "xx.GPX"
'******************** Fichero gpx de salida ********************************
Open DirT & NomRut For Output As #2
'*************************************************Cabecera fichero GPX ***************
Print #2, "<?xml version='1.0' encoding='UTF-8' standalone='no' ?>"
Print #2, "<gpx xmlns='http://www.topografix.com/GPX/1/1' creator='MapSource 6.12.4' version='1.1' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xsi:schemaLocation='http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd'>"
Print #2, "<metadata>"
Print #2, "<link href='http://www.garmin.com'>"
Print #2, "<text>Convertido por programa</text>"
Print #2, " </link>"
Print #2, "</metadata>"
Print #2, "<trk>"
Print #2, "<name>" & NomRut & "</name>"
Print #2, "<extensions>"
Print #2, "<gpxx:TrackExtension xmlns:gpxx='http://www.garmin.com/xmlschemas/GpxExtensions/v3' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xsi:schemaLocation='http://www.garmin.com/xmlschemas/GpxExtensions/v3 http://www.garmin.com/xmlschemas/GpxExtensions/v3/GpxExtensionsv3.xsd'>"
Print #2, "<gpxx:DisplayColor>Red</gpxx:DisplayColor>"
Print #2, "</gpxx:TrackExtension>"
Print #2, "</extensions>"
Print #2, "<trkseg>"
'*************************************************Cabecera fichero GPX ***************
Open DirT & Nom For Input As #1
Do While Not EOF(1)
Carac = Input(1, #1)
Lin = Lin & Carac
Pos1 = InStr(LCase(Lin), "<when>")
Pos2 = InStr(LCase(Lin), "</when>")
If Pos1 > 0 And Pos2 > 0 Then
Lin = Left(Lin, Pos2 - 1)
Lin = Mid(Lin, Pos1 + 6)
LinT = Lin
Lin = ""
End If
Pos1 = InStr(LCase(Lin), "<coordinates>")
Pos2 = InStr(LCase(Lin), "</coordinates>")
Longitud = Len("<coordinates>")
If Pos1 > 0 And Pos2 > 0 Then
Lin = Left(Lin, Pos2 - 1)
Lin = Mid(Lin, Pos1 + Longitud)
Coor = Split(Lin, ",")
'Sheets("Aux").Range("b" & N).Value = Coor(0)
'Sheets("Aux").Range("c" & N).Value = Coor(1)
'Sheets("Aux").Range("d" & N).Value = Coor(2)
'Sheets("Aux").Range("b" & N & ":d" & N) = Split(Lin, ",")
Lin = ""
Print #2, "<trkpt lat='" & Coor(1) & "' lon='" & Coor(0) & "'>"
Print #2, "<ele>" & Coor(2) & "</ele>"
Print #2, "<time>" & LinT & "</time>"
Print #2, "</trkpt>"
H.Range("a" & N).Value = "<trkpt lat='" & Coor(1) & "' lon='" & Coor(0) & "'>" & _
"<ele>" & Coor(2) & "</ele>" & "<time>" & LinT & "</time>"
N = N + 1
End If
If Asc(Carac) = 10 Then
'H.Range("a" & N).Value = Lin
Lin = ""
'N = N + 1
End If
Loop
Close #1
'*************************************************Cierre ruta fichero GPX ***************
Print #2, "</trkseg>"
Print #2, "</trk>"
Print #2, "</gpx>"
'*************************************************Cierre fichero salida***************
Close #2
End Sub