android

Obtener drivers USB Samsung sin instalar Kies

Los que desarrollamos para Android en sistemas operativos como GNU/Linux o MacOs X no solemos tener problemas para acceder a los teléfonos a través de USB, bien para instalar aplicaciones o acceder a los logs a través de ADB. El problema lo tenemos cuando queremos hacer esto mismo en sistemas Windows y nos encontramos que al conectar el móvil al PC nos aparece un mensaje de dispositivo desconocido o que no encuentra los drivers.

En mi caso, hace poco he me hecho con un Samsung Galaxy SI II con el que estoy haciendo unas pruebas. Y lo primero que hice fue buscar los drivers para poder conectarme vía ADB. Mi sorpresa fue encontrar un montón de referencias a un paquete instalador de drivers para dispositivos Samsung que no me funcionaba. Otras referencias me indicaban que instalara Kies, un software de Samsung para acceder al dispositivos, sincronizar y demás herramientas que suelen ofrecer las empresas fabricantes de teléfonos móviles. Por supuesto, no estaba dispuesto a instalar todo una suite para instalar unos drivers USB, por lo que pensé que quizás seria posible obtener los driver del propio paquete de instalación de Kies.

Por lo tanto, lo primero que hice fue buscar Kies en Google y acceder a la pagina oficial de Kies donde descargué el software para Windows

http://org.downloadcenter.samsung.com/downloadfile/ContentsFile.aspx?CDSite=UNI_ES&CDCttType=SW&ModelType=N&ModelName=FM_UPDATE&idyn=N&VPath=SW/201105/20110512115717796/Kies_2.0.2.11071_128_2.exe

Por otro lado me descargué también el UniversalExtractor, el cual es un software capaz de desempaquetar instaladores, y ya que al fin y al cabo un instalador no es mas que un contenedor de binarios con instrucciones de donde copiarlos, no tiene mayor misterio acceder a su interior (la otra opción es instalar Kies coger el driver y desinstalarlo después).

http://legroom.net/scripts/download.php?file=uniextract161

Une vez instalado el UniversalExtractor, accedemos a nuestro directorio de descargas y extraemos el instalador de Kies seleccionando la opción “Instalshield /b Interconector

Lo que hará que se nos cree una carpeta Kies_XXXXXX\ uni_xuemcfc\

Dentro de esta carpeta tendremos otro instalador MSI “Samsung Kies.msi“. Que volveremos a extraer de manera parecida pero esta vez con la opción “MSI Instalador Administrativo“.

Al desempaquetar el MSI obtendremos el contenido del instalador de Kies y si indagáis encontrareis que el instalador de los Drivers USB esta en Samsung Kies\program files\Samsung\Kies\USB Driver con el nombre de SAMSUNG_USB_Driver_for_Mobile_Phones.exe

Ejecutáis el instalador y pronto veréis como Windows va reconociendo vuestros dispositivos móviles.

El nombre del instalador de drivers es el mismo que el que en foros anteriores había encontrado e instalado pero que no me funcionaban. Esto se debía a que eran versiones anteriores del instalador de drivers que no contenían los drivers compatibles con el Samsung Galaxy S II. Por tanto en vez de alojar el instalador en un repositorio y enlazarlo en foros, he creído mas interesante mostrar como obtener la ultima versión del instalador de drivers a partir del instalador de Kies.

Espero que os haya sido de utilidad.

XML, JSON… Protocol Buffers

El intercambio de información en los sistema distribuidos o en cualquier tipo de servicio, es un tema clave, para lo cual, a lo largo de los años han surgido un montón de soluciones y tecnologías. Hasta ahora la formula mas utilizada era el formato XML, en muchos caso envueltos de una capa extra de gestión, como son el caso de SOAP o WCF impulsados por Microsoft. En general XML sigue siendo claro dominador a la hora de proveer información estructurada. Los feeds de nuestra Web, Sitemaps o muchos proyectos de OpenData proporcionan información en este formato.

A pesar de ello, XML para muchos es un formato engorroso, su interpretación o extracción de la información, a pesar de que existen muchas librerías que lo facilitan, suele traer de cabeza a muchos desarrolladores y cada vez, tiene mas presencia la tecnología JSON a la hora de acceder a servidores de datos, APIs, etc.. la facilidad para generar y recuperar la información que ofrece JSON esta haciendo que sea estándar de facto en prácticamente todos los servicios que permiten interactuar con servicios terceros. Empresas como Facebook o WOT, apuestan por JSON para el acceso a la información, sobre todo cuando esta se genera de forma dinámica. Aun así en la mayoría de los casos sigue estando opcional el acceso s los datos en XML.

En este articulo trato de presentar Protocol Buffers, una solución que pretende ser mas optima, en lo que respecta a trafico de datos, y mas opaca, si lo que interesa es que la información no viaje de forma totalmente visible. Personalmente, creo que lo mas interesante de este sistema es que reduce considerablemente el volumen de trafico generado en cada petición de información, lo cual es muy interesante si lo que queremos es desarrollar un servicio explotado por aplicaciones móviles, donde podemos conseguir aplicaciones mas fluidas, si conseguimos que el tiempo de conexión sea el menor posible.

En mi caso, Protocol Buffers me parece una solución interesante, ya que suelo usar Google App Engine, como web service generador de datos, y para terminales móviles suelo trabajar con Android, para el cual existen librerias. Además hay que tener en cuenta que muchas de las aplicaciones de serie en Android como el Market usan Protocol Buffers internamente (a pesar de que oficialmente la SDK de Android no ofrece acceso a dichas librerías).

En próximos post explicare como manejar Protocol Buffers en python.

Archivos DEX (Dalvik Executable): La Tabla de Tipos

Seguimos desenmarañando la estructura de los archivos Dex y ahora nos toca la tabla de tipos, es decir, los diferentes tipos de datos que utiliza nuestra aplicación a analizar. En este caso al igual que hicimos con la tabla de strings, obtenemos el offset y numero de elementos en la tabla de tipos de la cabecera. En el caso que estamos analizando la tabla de tipos comienza en 0x000001AC y tiene 28 (0x0000001C) elementos. Recorriendo de 4 en 4 bytes obtenemos las posiciones en la tabla de strings que identifican los tipos a usar: 2,7,8,9,10..

  1. elemento 2 de la lista de STRINGS: I
  2. elemento 7 de la lista de STRINGS: Landroid/app/Activity;
  3. elemento 8 de la lista de STRINGS: Landroid/os/Bundle;
  4. elemento 9 de la lista de STRINGS: Landroid/text/Editable;
  5. elemento 10 de la lista de STRINGS: Landroid/view/View$OnClickListener;
  6. elemento 11 de la lista de STRINGS: Landroid/view/View;
  7. elemento 12 de la lista de STRINGS: Landroid/widget/Button;
  8. elemento 13 de la lista de STRINGS: Landroid/widget/EditText;
  9. elemento 14 de la lista de STRINGS: Landroid/widget/TextView;
  10. elemento 15 de la lista de STRINGS: Lcom/marakana/NDKDemo$1;
  11. elemento 16 de la lista de STRINGS: Lcom/marakana/NDKDemo;
  12. elemento 17 de la lista de STRINGS: Lcom/marakana/NativeLib;
  13. elemento 18 de la lista de STRINGS: Lcom/marakana/R$attr;
  14. elemento 19 de la lista de STRINGS: Lcom/marakana/R$drawable;
  15. elemento 20 de la lista de STRINGS: Lcom/marakana/R$id;
  16. elemento 21 de la lista de STRINGS: Lcom/marakana/R$layout;
  17. elemento 22 de la lista de STRINGS: Lcom/marakana/R$string;
  18. elemento 23 de la lista de STRINGS: Lcom/marakana/R;
  19. elemento 24 de la lista de STRINGS: Ldalvik/annotation/EnclosingClass;
  20. elemento 25 de la lista de STRINGS: Ldalvik/annotation/EnclosingMethod;
  21. elemento 26 de la lista de STRINGS: Ldalvik/annotation/InnerClass;
  22. elemento 27 de la lista de STRINGS: Ldalvik/annotation/MemberClasses;
  23. elemento 28 de la lista de STRINGS: Ljava/lang/CharSequence;
  24. elemento 29 de la lista de STRINGS: Ljava/lang/Integer;
  25. elemento 30 de la lista de STRINGS: Ljava/lang/Object;
  26. elemento 31 de la lista de STRINGS: Ljava/lang/String;
  27. elemento 32 de la lista de STRINGS: Ljava/lang/System;
  28. elemento 37 de la lista de STRINGS: V

Estos strings usan una sintaxis concreta y pueden traducirse a una forma mas común de definir los tipos mediante la siguiente tabla.

Syntax Meaning
V void
Z boolean
B byte
S short
C char
I int
J long
F float
D double
Lnombre/completo/Clase; la clase nombre.completo.Clase
[descriptor Array de elementos descriptor, es posible crear arrays de arrays, con limite de profundidad 255
Ejemplo: [I ~ array de ints (int[])

Por tanto, la tabla, nos quedaria de esta manera:

  1. int
  2. android.app.Activity
  3. android.os.Bundle
  4. android.text.Editable
  5. android.view.View.OnClickListener
  6. android.view.View
  7. android.widget.Button
  8. android.widget.EditText
  9. android.widget.TextView
  10. com.marakana.NDKDemo.1
  11. com.marakana.NDKDemo
  12. com.marakana.NativeLib
  13. com.marakana.R.attr
  14. com.marakana.R.drawable
  15. com.marakana.R.id
  16. com.marakana.R.layout
  17. com.marakana.R.string
  18. com.marakana.R
  19. dalvik.annotation.EnclosingClass
  20. dalvik.annotation.EnclosingMethod
  21. dalvik.annotation.InnerClass
  22. dalvik.annotation.MemberClasses
  23. java.lang.CharSequence
  24. java.lang.Integer
  25. java.lang.Object
  26. java.lang.String
  27. java.lang.System
  28. void

Otro recurso mas, que sera utilizado junto a la tabla de strings para desensamblar el resto de estructuras de nuestra aplicación.

Entrevista en NickDutNik

Como anuncié a bombo y platillo semanas antes y publiqué (no lo suficiente) en las redes sociales, el día 2 de Febrero (2011) aparecía en el programa NickDutNik de ETB producido por Factoria Crossmedia. En la entrevista presentaba mi aplicación de Bizkaibus para Android y además explicaba las razones y motivaciones que me llevaron a desarrollar esta aplicación. Pinchando en la imagen podéis ver el video de la entrevista.

Archivos DEX (Dalvik Executable): La Tabla de Strings

La tabla strings, es una estructura que contiene las posiciones de los recursos string que utiliza nuestra aplicación. En esta lista de strings tendremos desde nombres de clases, métodos, variables o constantes que nos indicaran el tipo de los datos. Es una lista ordenada ya que desde otras estructuras se hará referencia a los strings en función de su posición en la lista. Es decir, en el proceso de compilación, se extraen los strings de todas las estructuras y código, se eliminan duplicados y se meten en una lista ordenada, por lo que a partir de entonces, ahí donde había un string ahora hay un entero que hace referencia a la posición del string en la lista. Con esto se consigue reducir el tamaño de la aplicación evitando la redundancia de recursos.

Para entenderlo mejor, a partir de este momento vamos a desensamblar, paso a paso, un archivo .dex de ejemplo que hemos obtenido a partir de este ejemplo http://marakana.com/forums/android/examples/49.html que es pequeño y usa la NDK (de cara a ser un ejemplo relativamente completo).

Analizando la cabecera como explicamos en el artículo anterior obtenemos el offset o desplazamiento desde el inicio del fichero donde empieza la tabla de posiciones de los strings, y el numero de strings. Por tanto obtenemos que la tabla de strings empieza en la posición 0×00000070 y que tiene 79 (0x0000004F) elementos. Por tanto deberemos recorrer el archivo desde la posición 0×00000070 en bloques de 32bist, 79 veces para obtener la posición de los 79 strings.

En el ejemplo de la imagen superior recorremos los tres primeros strings. La estructura de cada string es, un byte que indica la longitud del string y a continuación el string, por tanto, el primer string esta en la posición 0x0000084E, su longitud es de 0×08 caracteres y su valor es “<clinit>”. El segundo string esta en la posición 0×00000858, es de longitud 0×06 y valor “<init>”, el tercero esta en la posición 0×00000860 es de longitud 0×01 y valor “I”. Seguiríamos así con los 79 strings, con lo que en el ejemplo de la imagen obtendríamos la siguiente lista.

  1. <clinit>
  2. <init>
  3. I
  4. III
  5. IL
  6. L
  7. LI
  8. Landroid/app/Activity;
  9. Landroid/os/Bundle;
  10. Landroid/text/Editable;
  11. Landroid/view/View$OnClickListener;
  12. Landroid/view/View;
  13. Landroid/widget/Button;
  14. Landroid/widget/EditText;
  15. Landroid/widget/TextView;
  16. Lcom/marakana/NDKDemo$1;
  17. Lcom/marakana/NDKDemo;
  18. Lcom/marakana/NativeLib;
  19. Lcom/marakana/R$attr;
  20. Lcom/marakana/R$drawable;
  21. Lcom/marakana/R$id;
  22. Lcom/marakana/R$layout;
  23. Lcom/marakana/R$string;
  24. Lcom/marakana/R;
  25. Ldalvik/annotation/EnclosingClass;
  26. Ldalvik/annotation/EnclosingMethod;
  27. Ldalvik/annotation/InnerClass;
  28. Ldalvik/annotation/MemberClasses;
  29. Ljava/lang/CharSequence;
  30. Ljava/lang/Integer;
  31. Ljava/lang/Object;
  32. Ljava/lang/String;
  33. Ljava/lang/System;
  34. NDKDemo.java
  35. NativeLib.java
  36. R.java
  37. TextView01
  38. V
  39. VI
  40. VL
  41. accessFlags
  42. add
  43. app_name
  44. attr
  45. buttonCalc
  46. drawable
  47. findViewById
  48. getText
  49. hello
  50. helloText
  51. icon
  52. id
  53. layout
  54. loadLibrary
  55. main
  56. name
  57. nativeLib
  58. ndk_demo
  59. onClick
  60. onCreate
  61. outText
  62. parseInt
  63. res
  64. result
  65. savedInstanceState
  66. setContentView
  67. setOnClickListener
  68. setText
  69. string
  70. textOut
  71. this
  72. this$0
  73. toString
  74. v
  75. v1
  76. v2
  77. value
  78. value1
  79. value2

A partir de ahora las siguientes estructuras que vayamos desensamblando harán referencia a esta lista de strings.

Archivos DEX (Dalvik Executable): La Cabecera

La cabecera es como cabe de esperar, una de las partes mas importantes de la estructura el archivo .dex. Contiene el magic que nos permite identificar el formato de archivo, además de diferentes chechsums y hashes para comprobar la integridad del propio archivo. Pero lo que es mas importante, en la cabecera se encuentra el “mapa” para entender y descomponer el formato .dex en sus diferentes partes, ya que contiene una lista con los offsets y longitud de las diferentes parte de el archivo que comentábamos en el artículo anterior.

  • Tabla con las posiciones de los Strings
  • Tabla con las posiciones de los Tipos
  • Tabla con las posiciones de las estructuras/Prototipos de los métodos
  • Tabla con las posiciones de las Propiedades de las clases o Campos de los métodos
  • Tabla con las posiciones de los Métodos
  • Tabla con las posiciones de las Clases
  • Datos



Name Format Description
magic ubyte[8] Este valor es el que identifica el tipo de fichero.
checksum uint Checksum (adler32) calculado en base a todo el fichero, menos magic y checksum.
signature ubyte[20] Firma HashSHA-1 de todo el fichero menos magic, checksum y la propia firma.
file_size uint Tamaño de todo el fichero incluida la cabecera (en bytes).
header_size uint Tamañode la cabecera (en bytes). Actualmente siempre toma el valor de 0×70 (112)
endian_tag uint Nos indica que tipo de formato endian usa el fichero. Importante de cara a interpretar los valores de los datos del archivo, pero defecto es litle-endian. [aclaración]
link_size uint Indicael tamaño de la sección de enlace (link section) o 0, si elfichero es enlazado de forma dinámica.
link_off uint Desplazamientoal comienzo de la sección de enlace desde el inicio del ficheroo 0 si link_size == 0.
map_off uint Desplazamientoal map_list en caso que éste exista o 0 en caso contrario. Elmap_list es una lista con todo el contenido del fichero. Estaestructura de datos puede contener datos redundantes, pero laintención de la misma es el poder recorrer el contenido delfichero de una forma más cómoda. Esta lista está ordenada.
string_ids_size uint Númerode elementos en la lista de strings.
string_ids_off uint Desplazamientoa la lista de strings o 0 en caso que dicha lista este vacía,circunstancia que raramente se va a dar.
type_ids_size uint Númerode elementos en la lista de tipos (type).
type_ids_off uint Desplazamientoa la lista de tipos o 0 en caso que dicha lista este vacía. Casoque raramente también se dará.
proto_ids_size uint Númerode elementos en la lista de prototipos.
proto_ids_off uint Desplazamientoa la lista de prototipos. 0 en caso que dicha lista este vacía.De nuevo, situación que raramente se dará.
field_ids_size uint Númerode elementos en la lista de campos.
field_ids_off uint Desplazamientoa la lista de campos o 0 en caso que dicha lista esté vacía.
method_ids_size uint Númerode elementos en la lista de métodos.
method_ids_off uint Desplazamientoa la lista de métodos. 0 si la lista está vacía.
class_defs_size uint Númerode elementos en la lista de clases.
class_defs_off uint Desplazamientoa la lista de clases. 0 en caso dicha lista este vacía,situación poco probable.
data_size uint Tamañoen bytes de la sección de datos. Debe ser un número parmúltiplo del tamaño de un uint (sizeof(uint)).
data_off uint Desplazamientoa la sección de datos.


ENDIAN_CONSTANT y REVERSE_ENDIAN_CONSTANT

En caso de que el valor se el campo endian_tag sea igual a REVERSE_ENDIAN_CONSTANT (0×78563412), consideraremos que el archivo esta en formato litle-endian, lo cual es el estandar y lo mas común. A pesar de ello, es posible usar otras configuraciones, tipo big-endian, en tal caso el campo endian_tag tomara el valor ENDIAN_CONSTANT (0×12345678).

Fuente: http://www.netmite.com/android/mydroid/dalvik/docs/dex-format.html

Archivos DEX (Dalvik Executable): Introducción

En los entornos Java estándar, el código fuente de Java es compilado en bytecode de Java, y almacenado en archivos .class. Los archivos .class son leídos por la máquina virtual Java en tiempo de ejecución. Cada clase en tu código Java se traducirá en un archivo .class. Esto significa que si tienes, por ejemplo, un archivo .java (código fuente) que contiene una clase pública, una clase interna estática, y tres clases anónimas, el proceso de compilación (javac) nos generará 5 archivos .class.

En la plataforma Android, el código fuente de Java también se compila en archivos .class. Pero después de generar los archivos .class, mediante la herramienta dx son convertidos a un único archivo dex (Dalvik Executable). Mientras que un archivo .class contiene una sola clase, un archivo .dex contiene múltiples clases. Es el archivo .dex el que se ejecuta en la máquina virtual Dalvik.

El archivo .dex ha sido optimizado para el mínimo consumo de memoria y el diseño esta condicionado por la reutilización de datos. El siguiente diagrama compara el formato de los archivos .class utilizado por la JVM con el formato de .dex utilizado por la máquina virtual Dalvik.

.jar vs .dex

A grandes rasgos las estructura de un Archivo .dex consta de las siguientes partes:

  • Cabecera
  • Tabla con las posiciones de los Strings
  • Tabla con las posiciones de los Tipos
  • Tabla con las posiciones de las estructuras/Prototipos de los métodos
  • Tabla con las posiciones de las Propiedades de las clases o Campos de los métodos
  • Tabla con las posiciones de los Métodos
  • Tabla con las posiciones de las Clases
  • Datos

Salvo la tabla de Strings (que es a la que hacen referencia todas las tablas ya que es en esta parte donde se almacenan todos los nombre de clases, métodos, funciones, variable y tipos de datos), el resto sigue un orden jerárquico inverso, es decir, si quisiéramos desensamblar el archivos dex, tras obtener el listado de Strings, obtendríamos el listado de Clases, sus métodos, sus propiedades y campos de los métodos, la estructura de dichos métodos – que relaciona métodos y campos – y por ultimo los tipos, que nos indicaría los tipos de los campos de los métodos y los tipos que devuelven los métodos. Es decir, es una estructura relacional, que tiene como objetivo el reaprovechamiento máximo de la información, evitando redundancias y así conseguir un formato optimo para terminales móviles.

Como he indicado, son tablas donde se indica la posición donde esta la información que compone dicha tabla, normalmente mediante un Offset y opcionalmente una longitud. Estos datos junto con el código maquina están en la sección de datos.

En próximos artículos iremos desgranando con mas detalle, cada parte de la estructura del formato Dalvik Executable.

Fuente 1:http://davidehringer.com/software/android/The_Dalvik_Virtual_Machine.pdf
Fuente 2:http://www.scribd.com/doc/17194679/DalvikVMInternals#

Disponible la aplicación de Bizkaibus para Android desde el Market

Por fin ya esta disponible en Android Market la nueva aplicación para consultar información sobre líneas y horarios de Bizkaibus en nuestros terminales Android. Han sido varios meses en los que he ido sacando ratitos para ir montando las diferentes partes de la infraestructura, como el webservice OpenBizkaibus API, necesarias para poder completar una aplicación estable con un acabado relativamente profesional. Si sois usuarios de los servicios de Bizkaibus y tenéis un teléfono móvil Android con conexión a Internet, esta aplicación os será de gran utilidad. Para poder descargarla solo tenéis que pinchar aquí o escanear el código QR que tenéis a la derecha.




Review

La aplicación para android de Bizkaibus es una interfaz para poder consultar la información sobre horarios, líneas y tiempos de llegada por parada, de los autobuses del servicio Bizkaibus. Por tanto es una aplicación que consulta información en tiempo real a través de la conexión a Internet del terminal móvil. Por tanto al iniciar la aplicación, esta intentara conectarse a Internet para obtener información actualizada sobre cobertura, líneas, etc… Y en caso de que no pueda realizarse dicha conexión nos mostrara un dialogo avisándonos y no nos permitirá seguir adelante ya que es indispensable estar conectado para el uso de la aplicación.

Una vez inicializada la aplicación se nos mostrara la interfaz principal, donde podremos realizar los diferentes tipos de consultas: buscar líneas indicando origen y destino, buscar líneas que pasen por una determinada población o seleccionar directamente la línea de una lista. Actualmente la opción de búsquedas por línea a partir de origen y destino no esta disponible ya que es necesario ampliar la funcionalidad de BizkaibusOpen API, es decir, el webservice, en el que se apoya esta aplicación. Por tanto si pulsamos dicha opción de búsqueda se nos mostrara un dialogo indicándonos que aun no esta disponible.

Las opciones que si están ya implementadas en este versión de la aplicación son la búsqueda por Municipio, que nos mostrara las líneas que pasa por el municipio seleccionado, y la selección directa de una línea que queramos consultar.

Por tanto, una vez seleccionada línea, se nos mostrara la información relativa a horarios y demás incidencias. El usuario puede considerar que esta información es suficiente o de lo contrario puede desear saber cuando va a pasar un autobús por una parada determinada.

Actualmente la flota de autobuses de Bizkaibus esta equipada con GPSs que actualizan la posición del autobús en tiempo real. Por tanto es posible conocer el tiempo de llegada aproximado en función de dicha posición. Por tanto la aplicación es capaz de mostrar las paradas en las que para un autobús que cubre una línea determinada, tanto es su sentido de ida como en el de vuelta y mostrarnos sus tiempos de llegada.

Una vez seleccionado el sentido de la ruta, se mostraran todas las paradas por las que pasara la línea seleccionada. Existe la posibilidad de filtrar por Municipio de cara a facilitar la búsqueda de la parada en la lista.

Una vez elegida la parada se nos mostrara los tiempos de llegada para todas las líneas que pasan por dicha parada, al igual que aparecen en el panel informativo que hay en algunas marquesinas de Bizkaibus. En caso de desconocer donde se sitúa dicha parada o querer comprobar si la parada seleccionada se corresponde con la parada que queremos consultar, es posible mostrar la situación de dicha parada en el mapa.

Debido a que el proceso de búsqueda puede ser relativamente largo, existe la posibilidad de añadir paradas a nuestra lista de paradas favoritas. Con lo que, al acceder al la pantalla principal de la aplicación, mostrando el menú, podemos acceder a nuestra lista de paradas favoritas, para acceder a su información directamente.

Creando aplicación de Bizkaibus para Android (Parte 3)

Cada vez tengo menos tiempo para mis cosas, así que tengo menos tiempo aun para dedicarme a terminal mis proyectos personales. Aun así, poco a poco voy haciendo sprines (así lo llaman los amantes de las técnicas de programación tipo “scrum“) y voy finalizando partes importantes del core de mi aplicación. En los últimos días he ido completando la lista de llamadas SOAP al servicio de Bizkaibus y me he encontrado con que algunos datos geográficos se daban en unas magnitudes que me resultaban bastante extrañas.

<br />
<Consulta><br />
  <Registro OBJECTID="121792" PROVINCIA="48" MUNICIPIO="020" PARADA="154" COORDENADA_X="504145.0000000000" COORDENADA_Y="4790044.0000000000" IR_CLINEA="A3930" IR_NMRORU="001" IR_SENTDO="I" /><br />
  <Registro OBJECTID="122054" PROVINCIA="48" MUNICIPIO="036" PARADA="027" COORDENADA_X="512951.0000000000" COORDENADA_Y="4786654.0000000000" IR_CLINEA="A3930" IR_NMRORU="001" IR_SENTDO="I" /><br />
  <Registro OBJECTID="122055" PROVINCIA="48" MUNICIPIO="036" PARADA="028" COORDENADA_X="512767.0000000000" COORDENADA_Y="4786614.0000000000" IR_CLINEA="A3930" IR_NMRORU="001" IR_SENTDO="I" /><br />
  <Registro OBJECTID="122065" PROVINCIA="48" MUNICIPIO="036" PARADA="038" COORDENADA_X="513233.0000000000" COORDENADA_Y="4786603.0000000000" IR_CLINEA="A3930" IR_NMRORU="001" IR_SENTDO="I" /><br />
  <Registro OBJECTID="122079" PROVINCIA="48" MUNICIPIO="036" PARADA="053" COORDENADA_X="512755.0000000000" COORDENADA_Y="4786887.0000000000" IR_CLINEA="A3930" IR_NMRORU="001" IR_SENTDO="I" /><br />
</Consulta><br />

Tras investigar un rato, me he enterado de que existen bastante mas formas de expresar una posición geográfica de las que creía. En este caso, el sistema de coordenadas que usa la aplicación de Bizkaibus se llama ED50 y es una notación que era bastante utilizada en Europa hasta hace pocos años. Actualmente es mas popular la notación WGS84 que es la usada por Google maps y la mayoria de GPS actuales. Ademas, para complicar mas la cosa existen diferentes modo de expresar una coordenada: UTM (expresado como x, y y huso) o Geográficas (expresado como Latitud y Longitud) en segundos o en grados/minutos/segundos. Este segundo tiene la ventaja no depende de valores relativos como era el caso de UTM, en el cual debíamos indicar la zona en la que nos encontramos (en Bizkaia, que es el área que nos atañe, estamos en el huso 30 del hemisferio Norte).

El problema que tenía entonces era como convertir esos valores UTM/ED50 en valores de longitud y latitud en formato WGS84 compatibles con la gran mayoría de aplicaciones y APIs de geolocalización actuales.

Así que tras mucho buscar, y analizar formulas geométricas, conseguí esta aproximación hecha en python que nos permite trasformar coordenadas UTM a coordenadas Geográficas. Al principio de la función he aplicado unas correcciones ya que aun no he conseguido implementar la parte de conversión de ED50 a WGS84 (espero corregirlo pronto).

<br />
def utmToLatLng(zone, easting, northing, northernHemisphere=True):<br />
    easting = easting - 105<br />
    northing = northing - 210<br />
    if not northernHemisphere:<br />
        northing = 10000000 - northing</p>
<p>    a = 6378137<br />
    e = 0.081819191<br />
    e1sq = 0.006739497<br />
    k0 = 0.9996</p>
<p>    arc = northing / k0<br />
    mu = arc / (a * (1 - math.pow(e, 2) / 4.0 - 3 * math.pow(e, 4) / 64.0 - 5 * math.pow(e, 6) / 256.0))</p>
<p>    ei = (1 - math.pow((1 - e * e), (1 / 2.0))) / (1 + math.pow((1 - e * e), (1 / 2.0)))</p>
<p>    ca = 3 * ei / 2 - 27 * math.pow(ei, 3) / 32.0</p>
<p>    cb = 21 * math.pow(ei, 2) / 16 - 55 * math.pow(ei, 4) / 32<br />
    cc = 151 * math.pow(ei, 3) / 96<br />
    cd = 1097 * math.pow(ei, 4) / 512<br />
    phi1 = mu + ca * math.sin(2 * mu) + cb * math.sin(4 * mu) + cc * math.sin(6 * mu) + cd * math.sin(8 * mu)</p>
<p>    n0 = a / math.pow((1 - math.pow((e * math.sin(phi1)), 2)), (1 / 2.0))</p>
<p>    r0 = a * (1 - e * e) / math.pow((1 - math.pow((e * math.sin(phi1)), 2)), (3 / 2.0))<br />
    fact1 = n0 * math.tan(phi1) / r0</p>
<p>    _a1 = 500000 - easting<br />
    dd0 = _a1 / (n0 * k0)<br />
    fact2 = dd0 * dd0 / 2</p>
<p>    t0 = math.pow(math.tan(phi1), 2)<br />
    Q0 = e1sq * math.pow(math.cos(phi1), 2)<br />
    fact3 = (5 + 3 * t0 + 10 * Q0 - 4 * Q0 * Q0 - 9 * e1sq) * math.pow(dd0, 4) / 24</p>
<p>    fact4 = (61 + 90 * t0 + 298 * Q0 + 45 * t0 * t0 - 252 * e1sq - 3 * Q0 * Q0) * math.pow(dd0, 6) / 720</p>
<p>    lof1 = _a1 / (n0 * k0)<br />
    lof2 = (1 + 2 * t0 + Q0) * math.pow(dd0, 3) / 6.0<br />
    lof3 = (5 - 2 * Q0 + 28 * t0 - 3 * math.pow(Q0, 2) + 8 * e1sq + 24 * math.pow(t0, 2)) * math.pow(dd0, 5) / 120<br />
    _a2 = (lof1 - lof2 + lof3) / math.cos(phi1)<br />
    _a3 = _a2 * 180 / math.pi</p>
<p>    latitude = 180 * (phi1 - fact1 * (fact2 + fact3 + fact4)) / math.pi</p>
<p>    if not northernHemisphere:<br />
        latitude = -latitude</p>
<p>    longitude = ((zone > 0) and (6 * zone - 183.0) or 3.0) - _a3</p>
<p>    return (latitude, longitude)<br />

Creando aplicación de Bizkaibus para Android (Parte 2)

En este segundo post voy a explicar la parte mas importante de la aplicación: como obtener los tiempos de llegada de los autobuses a cada parada.

Como en toda aplicación web, lo normal es la mayoría de aplicaciones de diseñen en forma de Web Services a los que hacer peticiones HTTP para consultar cierta información. Una de los protocolos que durante bastante tiempo fue muy popular es SOAP (Simple Object Access Protocol), que básicamente es una petición http utilizando el metodo POST en la cual el cuerpo de la petición es una estructura xml con un formato determinado que nos permitirá enviar cierta información que será analizada por el Servicio Web que nos responderá con una estructura XML con la información solicitada.

Utilizando un sniffer de red tipo Wireshark es posible capturar una petición y respuesta de este tipo. La petición de información sobre la parada 48036029 tendrá la siguiente estructura:

<br />
POST http://apli.bizkaia.net/APPS/DANOK/TQWS/TQ.ASMX HTTP/1.1<br />
Host: apli.bizkaia.net<br />
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; es-ES; rv:1.9.2.10) Gecko/20100914 Firefox/3.6.10<br />
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8<br />
Accept-Language: es-es,eu;q=0.8,es;q=0.7,en-us;q=0.5,en;q=0.3,de-de;q=0.2<br />
Accept-Encoding: gzip,deflate<br />
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7<br />
Keep-Alive: 115<br />
Proxy-Connection: keep-alive<br />
Cookie: idioma=CA; RealApli=1056775084.20480.0000<br />
Referer: http://apli.bizkaia.net/apps/danok/tq/Resize.swf?horaEntrada=4885&#038;idiomaEntrada=es<br />
Content-type: text/xml; charset=utf-8<br />
SOAPAction: "http://tempuri.org/LANTIK_TQWS/TQ/GetPasoParada"<br />
Content-length: 393</p>
<p><?xml version="1.0" encoding="utf-8"?><br />
<SOAP-ENV:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"><br />
	<SOAP-ENV:Body><br />
		<GetPasoParada xmlns="http://tempuri.org/LANTIK_TQWS/TQ"><br />
			<strLinea>*</strLinea><br />
			<strParada>48036029</strParada><br />
		</GetPasoParada><br />
	</SOAP-ENV:Body><br />
</SOAP-ENV:Envelope><br />

Como vemos es una petición POST donde destacan la cabecera “SOAPAction” y el cuerpo del mensaje que se trata de una estructura XML donde en la etiqueta <strParada> esta completada con el ID de la parada que queremos consultar.

Si la petición esta correctamente formateada, obtendremos una respuesta con la siguiente información:

<br />
HTTP/1.0 200 OK<br />
Date: Mon, 27 Sep 2010 09:16:37 GMT<br />
Server: Microsoft-IIS/6.0<br />
X-Powered-By: ASP.NET<br />
X-AspNet-Version: 1.1.4322<br />
Cache-Control: private, max-age=0<br />
Content-Type: text/xml; charset=utf-8<br />
Content-Length: 5812<br />
X-Cache: MISS from escorpimc03.pandasoftware.local<br />
Proxy-Connection: keep-alive</p>
<p><?xml version="1.0" encoding="utf-8"?><br />
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><br />
	<soap:Body><br />
		<GetPasoParadaResponse xmlns="http://tempuri.org/LANTIK_TQWS/TQ"><br />
			<GetPasoParadaResult><br />
				&lt;GetPasoParadaResult&gt;&lt;PasoParada&gt;&lt;cabecera&gt;False&lt;/cabecera&gt;&lt;e1&gt;&lt;minutos&gt;47&lt;/minutos&gt;&lt;metros&gt;17145&lt;/metros&gt;&lt;tipo&gt;SIN COMUNICACION&lt;/tipo&gt;&lt;/e1&gt;&lt;e2&gt;&lt;minutos&gt;107&lt;/minutos&gt;&lt;metros&gt;39990&lt;/metros&gt;&lt;tipo&gt;NORMAL&lt;/tipo&gt;&lt;/e2&gt;&lt;linea&gt;A2610&lt;/linea&gt;&lt;parada&gt;48036029&lt;/parada&gt;&lt;ruta&gt;UPV/EHU&lt;/ruta&gt;&lt;/PasoParada&gt;&lt;PasoParada&gt;&lt;cabecera&gt;False&lt;/cabecera&gt;&lt;e1&gt;&lt;minutos&gt;62&lt;/minutos&gt;&lt;metros&gt;16803&lt;/metros&gt;&lt;tipo&gt;NORMAL&lt;/tipo&gt;&lt;/e1&gt;&lt;e2&gt;&lt;minutos&gt;122&lt;/minutos&gt;&lt;metros&gt;37277&lt;/metros&gt;&lt;tipo&gt;NORMAL&lt;/tipo&gt;&lt;/e2&gt;&lt;linea&gt;A3511&lt;/linea&gt;&lt;parada&gt;48036029&lt;/parada&gt;&lt;ruta&gt;BILBAO&lt;/ruta&gt;&lt;/PasoParada&gt;&lt;PasoParada&gt;&lt;cabecera&gt;False&lt;/cabecera&gt;&lt;e1&gt;&lt;minutos&gt;7&lt;/minutos&gt;&lt;metros&gt;754&lt;/metros&gt;&lt;tipo&gt;NORMAL&lt;/tipo&gt;&lt;/e1&gt;&lt;e2&gt;&lt;minutos&gt;27&lt;/minutos&gt;&lt;metros&gt;5529&lt;/metros&gt;&lt;tipo&gt;NORMAL&lt;/tipo&gt;&lt;/e2&gt;&lt;linea&gt;A3621&lt;/linea&gt;&lt;parada&gt;48036029&lt;/parada&gt;&lt;ruta&gt;BILBAO&lt;/ruta&gt;&lt;/PasoParada&gt;&lt;PasoParada&gt;&lt;cabecera&gt;False&lt;/cabecera&gt;&lt;e1&gt;&lt;minutos&gt;11&lt;/minutos&gt;&lt;metros&gt;1979&lt;/metros&gt;&lt;tipo&gt;NORMAL&lt;/tipo&gt;&lt;/e1&gt;&lt;e2&gt;&lt;minutos&gt;71&lt;/minutos&gt;&lt;metros&gt;21043&lt;/metros&gt;&lt;tipo&gt;NORMAL&lt;/tipo&gt;&lt;/e2&gt;&lt;linea&gt;A3631&lt;/linea&gt;&lt;parada&gt;48036029&lt;/parada&gt;&lt;ruta&gt;BILBAO&lt;/ruta&gt;&lt;/PasoParada&gt;&lt;PasoParada&gt;&lt;cabecera&gt;False&lt;/cabecera&gt;&lt;e1&gt;&lt;minutos&gt;12&lt;/minutos&gt;&lt;metros&gt;3728&lt;/metros&gt;&lt;tipo&gt;NORMAL&lt;/tipo&gt;&lt;/e1&gt;&lt;e2&gt;&lt;minutos&gt;56&lt;/minutos&gt;&lt;metros&gt;14889&lt;/metros&gt;&lt;tipo&gt;NORMAL&lt;/tipo&gt;&lt;/e2&gt;&lt;linea&gt;A3641&lt;/linea&gt;&lt;parada&gt;48036029&lt;/parada&gt;&lt;ruta&gt;ARRIGORRIAGA&lt;/ruta&gt;&lt;/PasoParada&gt;&lt;PasoParada&gt;&lt;cabecera&gt;False&lt;/cabecera&gt;&lt;e1&gt;&lt;minutos&gt;2&lt;/minutos&gt;&lt;metros&gt;481&lt;/metros&gt;&lt;tipo&gt;NORMAL&lt;/tipo&gt;&lt;/e1&gt;&lt;e2&gt;&lt;minutos&gt;64&lt;/minutos&gt;&lt;metros&gt;23008&lt;/metros&gt;&lt;tipo&gt;NORMAL&lt;/tipo&gt;&lt;/e2&gt;&lt;linea&gt;A3911&lt;/linea&gt;&lt;parada&gt;48036029&lt;/parada&gt;&lt;ruta&gt;BILBAO&lt;/ruta&gt;&lt;/PasoParada&gt;&lt;PasoParada&gt;&lt;cabecera&gt;False&lt;/cabecera&gt;&lt;e1&gt;&lt;minutos&gt;27&lt;/minutos&gt;&lt;metros&gt;12575&lt;/metros&gt;&lt;tipo&gt;NORMAL&lt;/tipo&gt;&lt;/e1&gt;&lt;e2&gt;&lt;minutos&gt;146&lt;/minutos&gt;&lt;metros&gt;38793&lt;/metros&gt;&lt;tipo&gt;NORMAL&lt;/tipo&gt;&lt;/e2&gt;&lt;linea&gt;A3912&lt;/linea&gt;&lt;parada&gt;48036029&lt;/parada&gt;&lt;ruta&gt;BILBAO&lt;/ruta&gt;&lt;/PasoParada&gt;&lt;PasoParada&gt;&lt;cabecera&gt;False&lt;/cabecera&gt;&lt;e1&gt;&lt;minutos&gt;16&lt;/minutos&gt;&lt;metros&gt;7537&lt;/metros&gt;&lt;tipo&gt;NORMAL&lt;/tipo&gt;&lt;/e1&gt;&lt;e2&gt;&lt;minutos&gt;77&lt;/minutos&gt;&lt;metros&gt;31963&lt;/metros&gt;&lt;tipo&gt;NORMAL&lt;/tipo&gt;&lt;/e2&gt;&lt;linea&gt;A3917&lt;/linea&gt;&lt;parada&gt;48036029&lt;/parada&gt;&lt;ruta&gt;BILBAO&lt;/ruta&gt;&lt;/PasoParada&gt;&lt;PasoParada&gt;&lt;cabecera&gt;False&lt;/cabecera&gt;&lt;e1&gt;&lt;minutos&gt;53&lt;/minutos&gt;&lt;metros&gt;14084&lt;/metros&gt;&lt;tipo&gt;NORMAL&lt;/tipo&gt;&lt;/e1&gt;&lt;e2&gt;&lt;minutos&gt;&lt;/minutos&gt;&lt;metros&gt;-1&lt;/metros&gt;&lt;tipo&gt;INV&lt;/tipo&gt;&lt;/e2&gt;&lt;linea&gt;A3919&lt;/linea&gt;&lt;parada&gt;48036029&lt;/parada&gt;&lt;ruta&gt;OROZKO (Ibarra)&lt;/ruta&gt;&lt;/PasoParada&gt;&lt;PasoParada&gt;&lt;cabecera&gt;False&lt;/cabecera&gt;&lt;e1&gt;&lt;minutos&gt;113&lt;/minutos&gt;&lt;metros&gt;36355&lt;/metros&gt;&lt;tipo&gt;NORMAL&lt;/tipo&gt;&lt;/e1&gt;&lt;e2&gt;&lt;minutos&gt;&lt;/minutos&gt;&lt;metros&gt;-1&lt;/metros&gt;&lt;tipo&gt;INV&lt;/tipo&gt;&lt;/e2&gt;&lt;linea&gt;A3919&lt;/linea&gt;&lt;parada&gt;48036029&lt;/parada&gt;&lt;ruta&gt;OROZKO Uribiarte&lt;/ruta&gt;&lt;/PasoParada&gt;&lt;PasoParada&gt;&lt;cabecera&gt;False&lt;/cabecera&gt;&lt;e1&gt;&lt;minutos&gt;38&lt;/minutos&gt;&lt;metros&gt;20112&lt;/metros&gt;&lt;tipo&gt;NORMAL&lt;/tipo&gt;&lt;/e1&gt;&lt;e2&gt;&lt;minutos&gt;157&lt;/minutos&gt;&lt;metros&gt;85232&lt;/metros&gt;&lt;tipo&gt;NORMAL&lt;/tipo&gt;&lt;/e2&gt;&lt;linea&gt;A3925&lt;/linea&gt;&lt;parada&gt;48036029&lt;/parada&gt;&lt;ruta&gt;BILBAO&lt;/ruta&gt;&lt;/PasoParada&gt;&lt;PasoParada&gt;&lt;cabecera&gt;False&lt;/cabecera&gt;&lt;e1&gt;&lt;minutos&gt;282&lt;/minutos&gt;&lt;metros&gt;138213&lt;/metros&gt;&lt;tipo&gt;NORMAL&lt;/tipo&gt;&lt;/e1&gt;&lt;e2&gt;&lt;minutos&gt;&lt;/minutos&gt;&lt;metros&gt;-1&lt;/metros&gt;&lt;tipo&gt;INV&lt;/tipo&gt;&lt;/e2&gt;&lt;linea&gt;A3925&lt;/linea&gt;&lt;parada&gt;48036029&lt;/parada&gt;&lt;ruta&gt;BILBAO&lt;/ruta&gt;&lt;/PasoParada&gt;&lt;PasoParada&gt;&lt;cabecera&gt;False&lt;/cabecera&gt;&lt;e1&gt;&lt;minutos&gt;96&lt;/minutos&gt;&lt;metros&gt;33686&lt;/metros&gt;&lt;tipo&gt;NORMAL&lt;/tipo&gt;&lt;/e1&gt;&lt;e2&gt;&lt;minutos&gt;216&lt;/minutos&gt;&lt;metros&gt;80815&lt;/metros&gt;&lt;tipo&gt;NORMAL&lt;/tipo&gt;&lt;/e2&gt;&lt;linea&gt;A3925&lt;/linea&gt;&lt;parada&gt;48036029&lt;/parada&gt;&lt;ruta&gt;BILBAO&lt;/ruta&gt;&lt;/PasoParada&gt;&lt;/GetPasoParadaResult&gt;<br />
			</GetPasoParadaResult><br />
		</GetPasoParadaResponse><br />
	</soap:Body><br />
</soap:Envelope><br />

La información que nos interesa esta dentro de la etiqueta <GetPasoParadaResult> y comos e puede ver los caracteres no alfanuméricos están codificados en HTML. Decodificando esta parte obtenemos una estructura xml, esta vez con la información que nos interesa.

<br />
<GetPasoParadaResult><br />
	<PasoParada><br />
		<cabecera>False</cabecera><br />
		<e1><br />
			<minutos>47</minutos><br />
			<metros>17145</metros><br />
			<tipo>SIN COMUNICACION</tipo><br />
		</e1><br />
		<e2><br />
			<minutos>107</minutos><br />
			<metros>39990</metros><br />
			<tipo>NORMAL</tipo><br />
		</e2></p>
<linea>A2610</linea>
<parada>48036029</parada>
		<ruta>UPV/EHU</ruta><br />
	</PasoParada><br />
	<PasoParada><br />
		<cabecera>False</cabecera><br />
		<e1><br />
			<minutos>62</minutos><br />
			<metros>16803</metros><br />
			<tipo>NORMAL</tipo><br />
		</e1><br />
		<e2><br />
			<minutos>122</minutos><br />
			<metros>37277</metros><br />
			<tipo>NORMAL</tipo><br />
		</e2></p>
<linea>A3511</linea>
<parada>48036029</parada>
		<ruta>BILBAO</ruta><br />
	</PasoParada><br />
	...<br />
	...<br />
</GetPasoParadaResult><br />

Comos e puede ver la estructura resultante dispone de diferentes estructuras <pasoParada> que tiene dos elementos <e1> y <e2> que son las dos estimaciones de tiempo que se dan por línea (tanto en minutos como tiempo). En la etiqueta <linea> tenemos información de la línea que sigue dicho autobús y en la etiqueta <ruta> el destino final.

Una vez conocido el sistema de consulta de tiempos toca la labor de chinos de consultar cada parada en la interfaz grafica para obtener su código y así tener una lista que relacione: nombre de la parada, población, dirección, código de la parada, latitud, longitud.

<br />
Bengoetxe, Galdakao,Galdakao,48036002,43.23198189686291,-2.857748866081238<br />
Bengoetxe, Galdakao,Bilbao,48036015,43.23243136604213,-2.859540581703186<br />
Aperribai, Galdakao,Bilbao,48036016,43.240374698213465,-2.8738126158714294<br />
Aperribai, Galdakao,Galdakao,48036001,43.24008942056928,-2.873823344707489<br />
Urbi, Galdakao, Galdakao,48036052,43.2392355336108,-2.8744053840637207<br />
Kukullaga, Etxebarri, Bilbao,48029002,43.24677550835569,-2.888430655002594<br />
Kukullaga, Etxebarri, Etxebarri,48029001,43.246728618419084,-2.8887444734573364<br />
...<br />
...<br />

1 2  Scroll to top