Inicio > Delphi, Ejemplos > Generar ficheros KML de rutas; Tracks en Google Maps

Generar ficheros KML de rutas; Tracks en Google Maps

miércoles, 2 de marzo de 2011 Dejar un comentario Ir a comentarios

En esta entrada mostraré el código nesario para generar ficheros KML (Keyhole Markup Language) a partir de datos que provienen de un DataSet o de un fichero GPX (GPX eXchage Format).
Los ficheros KML se utilizan para representar datos geográficos en 3 dimensiones (además de la longitud y la Latitud incluyen también la altura) y fue desarrollado para ser utilizado por un programa que a posteriori se convertiría en Google Earth.

En una entrada anterior ya vimos como importar un fichero GPX, con posiciones geográficas a un Dataset para posteriormente poder tratar y manejar estas posiciones (Cargar fichero GPX (XML) y acceder a los datos). Partiendo de lo visto en esa entrada como base, debe ser sencillo generar un fichero KML con los puntos que forma la ruta.

Hay que decir que los ficheros KML permiten muchas más cosas de las vistas en esta entrada, y para ver todas las posibilidades podemos revisar los ejemplos que se muestran aquí (Tutorial KML en Google); En este entrada me voy a centrar en la creación de rutas (lista de puntos) aunque las posibilidades van mucho más allá.

La estructura de nuestro fichero se compone por una cabecera, donde se almacenan datos genéricos de la ruta, y el bloque central donde añadimos los diferentes puntos geográficos que la componen.
Un ejemplo sencillo de fichero KML podría ser este:

<?xml version=»1.0″ encoding=»UTF-8″?>
<Placemark>
<name>Camino de ejemplo</name>
<description>Puntos de ejemplo sobre Collserola.</description>
<Style><LineStyle><color>ff0000FF</color><width>3</width></LineStyle></Style>
<LineString>
<coordinates>
2.12578324,41.4157072,275.5550537109375
2.12559993,41.4156055,275.074462890625
2.12537404,41.4152729,275.074462890625
2.12531452,41.4151532,274.59375
2.12518930,41.4149554,275.074462890625
2.12525032,41.4145536,274.113037109375
2.12553421,41.4141423,275.5550537109375
2.12541938,41.4139779,275.5550537109375
2.12537412,41.4139778,275.5550537109375
2.12492954,41.4139116,274.59375
2.12441666,41.4137039,273.6324462890625
2.12425304,41.4136318,274.113037109375
2.12396864,41.4136072,273.6324462890625
2.12380528,41.4136296,273.15185546875
2.12357947,41.4136973,273.15185546875
2.12343120,41.4137774,273.15185546875
2.12328091,41.4139091,272.671142578125
2.12321687,41.4140272,273.15185546875
2.12320983,41.4141903,273.15185546875
</coordinates>
</LineString>
</Placemark>

Como vemos en la parte superior añadimos datos de la ruta y algunas opciones de visualización. En este caso un nombre y una descripción y porteriormente se definen opciones de estilo para el track (LineStyle); Definimos que usaremos una línea para representar esos puntos con color Rojo y un a anchura de 3.

Este archivo podemos abrirlo directamente con Google Earth o importarlo a «Mis Mapas» desde Google Maps y la visualización ser algo así;

En Google Maps al importar la ruta tendríamos esto:

Y al Abrir el fichero con Google Earth obtendríamos esto:

Podemos ver que como la ruta está definida como <LineString>, esta se pinta como una línea siguiendo los parámetros definidos en el fichero (color rojo y ancho 3).

Para generar el fichero con una ruta, partiremos de que los datos han sido imprtados a un Dataset y que ya disponemos de ellos en ese formato.

Primero generaremos la cabecera del fichero con los datos genéricos de la ruta; Nombre, Descripción, y las características de estilo para la línea:

const
  CAB_XML         = '<!--?xml version="1.0" encoding="UTF-8"?-->';
  CAB_KLM         = '';
  CAB_PLACEMARK   = '';
  CAB_NAME        = '%s';
  STR_STYLE       = '<!--
<LineStyle>
<color>%s</color>' +
                    '<width>%d</width></LineStyle>
-->';
  CAB_LINE        = '';
begin
  // Limpiar
  TSTrack.Clear;
  // Añadir cabeceras
  TSTrack.Add(CAB_XML);
  TSTrack.Add(CAB_KLM);
  TSTrack.Add(CAB_PLACEMARK);
  // Datos de la ruta
  TSTrack.Add(Format(CAB_NAME,[AtrackName]));
  // color
  {$IFDEF VER220}
  // transparencia + color
  Str := 'ff' + RGBToWebColorStr(ATrackColor);
  {$ELSE}
  Str := 'ff' + uProcedures.RGBToWebColorStr(ATrackColor);
  {$ENDIF}
  Str := AnsiReplaceText(Str, '#', '');
  // TrackColor en HEXA
  TSTrack.Add(Format(STR_STYLE,[Str, ATrackWidth]));
  TSTrack.Add(CAB_LINE);

Con este código generamos la cabecera del fichero. En mi caso he usado una directiva condicional, para la función RGBToWebColorStr, que no está definida en versiones antiguas de la VCL.

Lo siguiente es realizar el recorrido por los puntos, añadiendo información de cada uno de ellos.

dsPoints.First;
// recorrido
while not (dsPoints.Eof) do begin
  // Info. de punto
  StrLine :=
     Copy(dsPoints.FieldByName(lonFName).AsString,1,10) + ',';
  StrLine := StrLine +
      Copy(dsPoints.FieldByName(latFName).AsString, 1,10) +  ',';
  StrLine := StrLine +
      dsPoints.FieldByName(eleFName).AsString + ' ';
  // Añadir la linea
  TSTrack.Add(StrLine);
  // Next
  dsPoints.Next;
end;

Sólo queda finalmente completar el fichero añadiendo los TAG’s de las diferentes secciones que hemos ido creando.

Para facilitar el trabajo he encapsulado la conversión en el procedimiento siguiente:

procedure ConvertTrackToKLM(
   ATrackName:string;  // Nombre
   ATrackDesc:string;  //descripción
   dsPoints:TDataSet;   //  Dataset que contiene los datos de los puntos
   {Nombres de los campos en el Dataset que contienen
    Latitud, Longitud, Elevación y Time}
   latFieldName, lonFieldName, eleFieldName, timeFieldName:string;
   var TSTrack:TStrings;   // Estructura de salida con el track
   ATrackColor:TColor=clBlue;  // Color para la linea del track
   ATrackWidth:integer=2   // Anchura de la línea);

Y una llamada de ejemplo a este procedimiento podrías ser la siguiente:

  TS := TStringList.Create();
  try
    // convertir a KML
    ConvertTrackToKLM(
           FormData.cdsTrack.FieldByName('name').AsString,    // trackName
           'Descripción del track',      // descripción
           FormData.cdsPoints,        // Dataset con los puntos
           'lat', 'lon','ele','time',        // Nombre de los campos
           TS);
    // Fichero de salida
    newName := ChangeFileExt(Self.TrackPath, '.KML');
    TS.SaveToFile(newName);
  finally
    FreeAndNil(TS);
  end;

Partiendo de la aplicación que sirvió de ejemplo en la entrada <Google Maps en Delphi>, he generado otro ejemplo que nos permitirá integrar la carga de un fichero GPX (utilizando el XML Mapper de Delphi), la conversión del fichero GPX a formato KML y la visualización de la ruta creada utilizando Google Maps integrado en nuestra aplicación, como prometí en la última entrada sobre el tema.

He probado a generar un video del ejemplo para ver el funcionamiento.

[youtube DLoX_KVSJ7c nolink]

Para las pruebas podeís descargar las rutas de ejemplo de la última entrada o buscar por internet en la infinidad de páginas que hay que las publican

<Rutas de ejemplo (GPX)>

Podéis descargar tanto el ejecutable del proyecto (con alguna ruta de ejemplo) como los fuentes:

<Sources del proyecto>
<Ejecutable del proyecto>

CORRECCIÓN: He añadido un fichero que faltaba al ejecutable de ejemplo.

Un saludo.

5/5 - (1 voto)
  1. Dipi
    sábado, 5 de marzo de 2011 a las 03:26 | #1

    No se puede decir mas que Excelente!!!, muy muy interesante como siempre.

    Saludos

  2. Ariel
    viernes, 11 de marzo de 2011 a las 15:21 | #2

    Excelente, la verdad de mucha utilidad. Ahora mi consulta:

    Quería solicitarte información acerca de como crear un archivo kml que muestre en Google earth el avance (paso a paso) de una aeronave. Yo poseo un archivo con registros del tipo: time, Lat, lon, altitud, pitch, … adquiridos en un vuelo y quisiera poder visualizar el avance de la aeronave, es decir no solo mostrar la ruta sino que tambien me gustaria tener un icono de un avioncito avanzando en el tiempo sobre dicha ruta. Si es posible hacer un merge con datos de otra aeronave y ver los dos o mas vuelos sincronizados mucho mejor.

    Desde ya muchas gracias,
    Saludos cordiales.

  3. jomadi04
    lunes, 14 de marzo de 2011 a las 17:06 | #3

    hola, tu tutorial me está viniendo muy bien, pero me gustaría añadir una cosa más. Me estoy haciendo una aplicación para cuando salgo con la bici ver la ruta que hago y en muchas rutas tengo el track gps y me gustaría cargar las dos: el track q tengo y la que mi gps ha recogido para compararlas. ¿cómo se puede cargar una segunda ruta?

    gracias.
    Un saludo

  4. Neftalí
    lunes, 14 de marzo de 2011 a las 17:19 | #4

    @jomadi04
    Hola jomadi.
    Pues posíblemente estemos duplicando trabajo, porque yo también estoy haciendo una aplicación similar llamada TrackInfo, para visualizar rutas (en mi paso también hechas en bici y descargadas desde un GPX). He hablado algo de ella aquí (http://neftali.clubdelphi.com/?p=1363). Justamente una de las cosas que tengo pendientes es la de añadir rutas a una visualización que ya posee una ruta cargada, para poder comparar 2 o más rutas.
    El sistema al final no tiene ningun secreto, es similar a como se añade 1, pero duplicando el proceso.
    Por ahora no está acabada y espero pronto sacar una primera versión.

    Un saludo.

  5. jomadi04
    martes, 22 de marzo de 2011 a las 18:17 | #5

    pues he investigado un poco (no mucho) sobre lo que te pregunté de pintar una segunda ruta, y no parece complicado, de hecho, cuando pintas una polilínea con la api, no es nada complicado pintar una segunda, sólo tienes que definir un segundo elemento de línea que pinte otro path sobre el mismo mapa (con otras opciones de color y grosor si se desea). Así que para una segunda ruta, el asunto estará en decirle que los datos que saca del archivo no se guarden en las variables que utiliza el otro, para que no lo machaque, sino en otras nuevas, y que lo pinte sobre el mismo mapa.
    Y lo mismo que vale para 2, vale para 20…

    Un saludo.

  6. martes, 22 de marzo de 2011 a las 18:56 | #6

    @jomadi04
    Hola jomadi.
    Gracias por el comentario. Ya me imaginaba que iban por ahí los tiros y aunque no he llegado a probarlo parece lógico que definiendo una segunda polilínea no deba haber problemas; Al igual que si se define un área o una marca.
    Para la aplicación que estoy haciendo todavía no contemplo esa opción, al menos para la primera versión.
    Una pregunta: Ya que estás con este mismo tema, ¿Te importaría si en unos días te paso una versión beta y la pruebas?
    Como estás con lo mismo supongo que te será fácil evaluar cómo va y las opciones que posee; Si son fñaciles de utilizar, si la interfaz es clara y útil,… Acepto sugerencias, críticas y comentarios.
    ¿Qué te parece?

    Un saludo.

  7. jomadi04
    miércoles, 30 de marzo de 2011 a las 16:29 | #7

    hola, pásame la versión de prueba cuando la tengas y te comento cómo la veo. Puedes ver mi email desde la administración de la web?si no te lo paso por mensaje privado en el club delphi.

    Un saludo

  8. Neftalí
    jueves, 31 de marzo de 2011 a las 09:31 | #8

    @jomadi04
    Hola jomadi.
    Envíame un correo a mi dirección de contacto (que por cierto la he añadido en la página de Acerca de…(http://neftali.clubdelphi.com/?page_id=2) -que no estaba-) con tu dirección de Mail.
    Estoy acabando algunos detalles y errores; Espero que en unos días pueda estar listo. Te la envío en cuanto pueda y me das tu apinión.

    Gracias y un saludo.

  9. Ricardo
    miércoles, 8 de octubre de 2014 a las 03:44 | #9

    Excelente, como siempre. Gracias por tu aporte.

  10. Javier Chacón
    domingo, 29 de marzo de 2015 a las 20:22 | #10

    Excelente aporte, gracias.

  1. miércoles, 9 de marzo de 2011 a las 12:24 | #1