ago 312010
 

Buenas,

Vimos en un mensaje anterior cómo funcionaban las clases creadas para manejar la estructura JSON devuelta por el API de Google Maps. Ahora veremos cómo usarlas en una aplicación.

Para el ejemplo crearemos una aplicación nueva, pondremos un TEdit (eDireccion) para introducir la dirección a buscar, un TButton (bSearch) para realizar el proceso, un TMemo (mJson) para visualizar los datos recibidos – estructura JSON -, y un TValueListEditor (vlData) para visualizar el contenido del JSON de una forma más amigable.

A nivel de código, vamos a definir una variable de tipo TGeoCode en la parte privada del objeto TForm y, también en la misma sección, definiremos los manejadores de los eventos de la variable declarada que queramos manejar, en este caso los de «After»

  private
    GC: TGeoCode;

    procedure AfterGetData(Sender: TObject; AllData: string);
    procedure AfterGetValues(Sender: TObject; AllValues: TStrings);

Otra cosa que haremos es, o bien sobreescribir constructor y destructor para crear y liberar el objeto GC o bien usar los eventos OnCreate y OnDestroy del formulario. Personalmente tengo la costumbre de sobreescribir los métodos, así que vamos a ello.

Primero la declaración (en la parte pública, claro está)

  public
    constructor Create(aOwner: TComponent); override;
    destructor Destroy; override;

Y ahora la implementación (aunque obvia para muchos)

constructor TForm1.Create(aOwner: TComponent);
begin
  inherited;

  // creación del objeto
  GC := TGeoCode.Create;
  // asignación de los eventos
  GC.AfterGetData := AfterGetData;
  GC.AfterGetValues := AfterGetValues;

  eDireccion.Text := '';
  mJson.Lines.Text := '';
end;

destructor TForm1.Destroy;
begin
  if Assigned(GC) then FreeAndNil(GC);

  inherited;
end;

Lo que haremos en los eventos no es más que rellenar los componentes que hemos puesto en pantalla. En el AfterGetData rellenaremos el memo con la estructura JSON devuelta por el API de Google Maps y en el AfterGetValues rellenaremos el TValueListEditor.

AfterGetData tiene el parámetro AllData que contiene la estructura JSON, así que será una simple asignación.

AfterGetValues tiene el parámetro AllValues de tipo TStrings que contiene una lista formateada con los diferentes valores del estilo propiedad=valor, vamos, lo que necesita cualquier TValueListEditor para ser rellenado, así que también será una sencilla asignación.

procedure TForm1.AfterGetData(Sender: TObject; AllData: string);
begin
  mJson.Lines.Text := AllData;
end;

procedure TForm1.AfterGetValues(Sender: TObject; AllValues: TStrings);
begin
  vlData.Strings.Text := AllValues.Text;
end;

Y para terminar sólo nos queda ver el código del botón de búsqueda que, por otra parte, será algo muy sencillo dado que la clase se encarga de todo

procedure TForm1.bSearchClick(Sender: TObject);
begin
  tsPlaces.Tabs.Clear;
  GC.Clear;
  GC.Direccion := eDireccion.Text;
  GC.Execute;
end;

Con esto ya tenemos el programa terminado y, el usuario podrá ver de forma clara los resultados devueltos por el API de Google Maps.

Puedes descargarte la implementación de lo que llevamos explicado aquí.

Continuará…

Nos leemos

ago 312010
 

Buenas,

Vista la estructura JSON en un mensaje anterior, ahora toca realizar el desglose de la misma en Delphi.

Por defecto, Delphi no trae nada para leer/interpretar una estructura JSON, así que tenemos dos opciones, o nos montamos nosotros algo, o miramos lo que ya hay hecho. Como no soy de los que les gusta reinventar la rueda, con una sencilla consulta a la página oficial de JSON vemos que hay varias librerías disponibles que nos realizan la labor. He escogido la JSON Delphi Library debido a que sólo es un .pas (no hay que instalar nada), trae ejemplos de su funcionamiento y su uso no es complicado.

Antes de ver el código de lectura del JSON, vamos a ver dónde lo vamos a almacenar. Para ello he diseñado un par de clases. La primera, TGeoCode, es la clase que controlará todo el resultado JSON:

  TAfterGetData = procedure (Sender: TObject; AllData: string) of object;
  TAfterGetValues = procedure (Sender: TObject; AllValues: TStrings) of object;

  TGeoCode = class
  private
    FDireccion: string;
    FGeoStatus: string;
    FGeoList: TObjectList;
    FBeforeProcess: TNotifyEvent;
    FAfterProcess: TNotifyEvent;
    FAfterGetData: TAfterGetData;
    FAfterGetValues: TAfterGetValues;
    FData: TStrings;

    function GetCount: Integer;
  protected
    procedure Parse;
    procedure ShowValues;
  public
    constructor Create(Dir: string = ''); virtual;
    destructor Destroy; override;

    // realiza la carga del JSON
    procedure Execute;
    // inicializa la estructura
    procedure Clear;

    // dirección que queremos GeoLocalizar
    property Direccion: string read FDireccion write FDireccion;
    // cantidad de GeoLocalizaciones devueltas por el API de Google Maps
    property Count: Integer read GetCount;
    // JSON devuelto por el API de Google Maps
    property Data: TStrings read FData write FData;
    // *********** propiedades devueltas por la geolocalización ***************
    // estado de la consulta
    property GeoStatus: string read FGeoStatus write FGeoStatus;
    // lista de objetos de la clase TPlace, es decir, cada una de las GeoLocalizaciones
    //    devueltar en el JSON
    property GeoList: TObjectList read FGeoList write FGeoList;
    // eventos
    property BeforeProcess: TNotifyEvent read FBeforeProcess write FBeforeProcess;
    property AfterProcess: TNotifyEvent read FAfterProcess write FAfterProcess;
    property AfterGetData: TAfterGetData read FAfterGetData write FAfterGetData;
    property AfterGetValues: TAfterGetValues read FAfterGetValues write FAfterGetValues;
  end;

Como vemos, también le he definido 4 eventos, para así poder, en el caso que se quiera, alguna pantalla de espera, cambiar el ratón para decir que se está trabajando, leer la info extraída justo después de que se haga,….. Vamos, para hacer lo que uno buenamente quiera.

La segunda clase es TPlace que contendrá cada una de las GeoLocalizaciones devueltas en la estructura JSON.

  TGeoGeometry = record
    Lat: Real;
    Lon: Real;
  end;

  TAddrComp = record
    LongName: string;
    ShortName: string;
    AddrCompType: TStrings;
  end;

  TGeoAddrComp = array of TAddrComp;

  TPlace = class
  private
    FGeoAddrComp: TGeoAddrComp;
    FGeoFormatedAddr: string;
    FGeoType: TStrings;
    FGeoGeometry: TGeoGeometry;
  public
    constructor Create; virtual;
    destructor Destroy; override;

    property GeoType: TStrings read FGeoType write FGeoType;
    property GeoFormatedAddr: string read FGeoFormatedAddr write FGeoFormatedAddr;
    property GeoAddrComp: TGeoAddrComp read FGeoAddrComp write FGeoAddrComp;
    property GeoGeometry: TGeoGeometry read FGeoGeometry write FGeoGeometry;
  end;

Ahora ya sólo queda ver cómo se cargaría la estructura JSON en objetos de nuestra clase, o lo que es lo mismo, ver el método Execute de la clase TGeoCode.

Lo primero que tenemos que hacer es crear un objeto de la clase TlkJSONobject el cual se encargará de leer la estructura JSON y montar su estructura interna. Luego sólo tendremos que recorrer esta estructura para ir almacenándola en nuestros objetos.

procedure TGeoCode.Execute;
var
  IdHTTP: TIdHTTP;
  Str: string;
  Stream: TStringStream;
begin
  if FDireccion = '' then Exit;

  if Assigned(FBeforeProcess) then FBeforeProcess(Self);

  IdHTTP := TIdHTTP.Create(nil);
  Stream := TStringStream.Create('');
  try
    // sustituimos blancos por signo +
    Str := AnsiReplaceStr(FDireccion, CHAR_SPACE, CHAR_PLUS);
    // generamos url con parámetros
    Str := STR_WEB + Str + STR_SENSOR + STR_REGION;
    // hacemos petición a la API de Google
    IdHTTP.Get(Str, Stream);
    // pasamos de UTF8 a string
    FData.Text := UTF8ToString(Stream.DataString);
    if Assigned(FAfterGetData) then FAfterGetData(Self, FData.Text);
    // realizamos PARSE
    Parse;
    // mostramos valores en la lista
    ShowValues;
  finally
    if Assigned(Stream) then FreeAndNil(Stream);
    if Assigned(IdHTTP) then FreeAndNil(IdHTTP);
  end;

  if Assigned(FAfterProcess) then FAfterProcess(Self);
end;

En el próximo capítulo veremos cómo funcionan nuestras clases en una aplicación para mostrar los datos recibidos en, por ejemplo, un TValueListEditor.

Puedes descargarte la unit GeoCode desde aquí.

continuará……..

Nos leemos

ago 312010
 

Buenas,

Vimos en un mensaje anterior qué era y cómo obtener las coordenadas geográficas de una determinada dirección mediante la API de GeoCodificación de Google Maps. También vimos que la API podía devolver el resultado en dos formatos diferentes, JSON y XML. Me he decidido a explicar el JSON dado que es lo recomendado por Google. Para quien no sepa qué es el JSON, les recomiendo una visita a su web o en la wiki.

La estructura JSON

Cuando realizamos una búsqueda, ésta puede contener una dirección exacta de lo que buscamos (ej: Plaza de España, 5, Barcelona, España) o bien una parte de la misma (Ej: Plaza de España, 5). Como bien nos podemos imaginar, de «Plaza de España» hay muchas, por lo que la API de Google Maps nos devolverá todas las coincidencias que encuentre hasta un máximo de 10 (esta cantidad no la he visto reflejada en la documentación de Google Maps pero por las pruebas que he realizado parece que es así, no obstante, ésto no quiere decir que no salga dicha cantidad en la documentación).

Así pues, la estructura JSON devuelta por el API tendrá una parte común a todos los resultados (el estado de la consulta) y un array que contendrá todos los resultados.

Ésta sería una estructura base para 1 resultado de la llamada a la API:

{
  "status": "OK",
  "results": [ {
    "types": [ "street_address" ],
    "formatted_address": "Plaça d'Espanya, 5, 08014 Barcelona, Spain",
    "address_components": [ {
      "long_name": "5",
      "short_name": "5",
      "types": [ "street_number" ]
    }, {
      "long_name": "Plaça d'Espanya",
      "short_name": "Plaça d'Espanya",
      "types": [ "route" ]
    }, {
      "long_name": "Barcelona",
      "short_name": "Barcelona",
      "types": [ "locality", "political" ]
    }, {
      "long_name": "Barcelona",
      "short_name": "B",
      "types": [ "administrative_area_level_2", "political" ]
    }, {
      "long_name": "Catalonia",
      "short_name": "CT",
      "types": [ "administrative_area_level_1", "political" ]
    }, {
      "long_name": "Spain",
      "short_name": "ES",
      "types": [ "country", "political" ]
    }, {
      "long_name": "08014",
      "short_name": "08014",
      "types": [ "postal_code" ]
    } ],
    "geometry": {
      "location": {
        "lat": 41.3749536,
        "lng": 2.1485485
      },
      "location_type": "RANGE_INTERPOLATED",
      "viewport": {
        "southwest": {
          "lat": 41.3718069,
          "lng": 2.1454125
        },
        "northeast": {
          "lat": 41.3781022,
          "lng": 2.1517078
        }
      },
      "bounds": {
        "southwest": {
          "lat": 41.3749536,
          "lng": 2.1485485
        },
        "northeast": {
          "lat": 41.3749555,
          "lng": 2.1485718
        }
      }
    }
  } ]
}

Veamos cada una de las partes de este resultado:

  • status: estado de la consulta. Hay varios estados, ver la API para consultarlos. A nosotros nos interesa el estado «OK» devuelto cuando la consulta se realiza correctamente y sin errores.
  • results: array con los resultados devueltos por el API. Si no hay resultados a mostrar, devolverá un array vacío.
    • types: array con uno o más tags indicando el tipo de resultado. Ver en el API los diferentes tipos posibles.
    • formatted_address: dirección en formato «legible» devuelta por el API de Google Maps
    • address_components: array que contiene de forma desglosada el contenido de formatted_address. Éste suele tener la siguiente información:
      • types: array con el tipo de componente
      • long_name: todo el texto descriptivo del componente
      • short_name: abreviatura del componente si existiera.
    • geometry: lista con datos de localización
      • location: lista que contiene las coordenadas geográficas
        • lat: latitud
        • lng: longitud
      • location_type: información extra sobre la localización. Ver en el API los diferentes valores.
      • viewport: lista que contiene la ventana recomendada para la visualización del resultado.
        • southwest: lista con las coordenadas suroeste de la ventana.
          • lat: latitud
          • lng: longitud
        • northeast: lista con las coordenada noreste de la ventana.
          • lat: latitud
          • lng: longitud
      • bounds: (opcional) lista con la ventana que contiene la totalidad del resultado. Destacar que no tiene porque coincidir con el viewport.
        • southwest: lista con las coordenadas suroeste de la ventana.
          • lat: latitud
          • lng: longitud
        • northeast: lista con las coordenada noreste de la ventana.
          • lat: latitud
          • lng: longitud
    • partial_match: indica la exactitud del resultado devuelto. Aunque la documentación del API de Google Maps no lo marca como opcional, en las pruebas que he realizado no siempre ha devuelto este parámetro.

Continuará…

Nos leemos