Usando iOS MapKit e CoreLocation com Delphi Prism e MonoTouch

Na minha opinião Maps (Mapas) é um dos aplicativos mais úteis no iPhone, sempre o utilizamos para nos guiar no trânsito, localizar algum restaurante, supermercado, posto de gasolina ou algo do gênero perto de onde estamos. Neste post vou mostrar como usar o MapKit e CoreLocation framework parte do iOS SDK, ambos permitem utilizar as funcionalidades relacionadas a mapas em sua aplicação.

O MapKit fornece uma apresentação visual de informações geográficas com dados e imagens do Google Maps usando o controle MKMapView, o mesmo usado no aplicativo Maps do iPhone. O MapKit está representado no MonoTouch através do namespace MonoTouch.MapKit.

CoreLocation funciona em vários dispositivos iOS usando uma variedade de tecnologias para determinar a posição e com uma bússola embutida no dispositivo consegue fornecer a posição em que o aparelho está apontando. Usando essas informações você pode determinar onde os usuários estão e em que direção o seu dispositivo está apontando, através das alterações no monitoramento você tem como determinar a velocidade e distância em que os usuários estão se locomovendo. O CoreLocation framework está representando no MonoTouch através do namespace MonoTouch.CoreLocation onde trabalha com dois tipos de informação: Location e Heading.

Vou assumir que você já assistiu minha apresentação introdutoria sobre iPhone (em inglês) na qual eu explico como criar um simples Web browser para iPhone usando Mono IDE e Interface Builder, pois neste post vou direto a implementação.

Para usar o MKMapView você precisa apenas arrastar e soltar o controle no seu Window através do Interface Builder, além claro do controle Segmented Control que irá permitir a troca do tipo de visão do Mapa (Standard, Hybrid, Satellite).

Com base no endereço informado a aplicação irá adicionar um PIN no mapa referente ao endereço exato, para adicionar um marcador PIN ao mapa será necessário criar e adicionar MKAnnotations ao MKMapView. MKAnnotation é uma classe abstrata e tem que ser implementada na sua aplicação como mostro no código abaixo.

	  MyAnnotation nested in AppDelegate = public class(MKAnnotation)
	  private
	    _coordinate: CLLocationCoordinate2D;
	    _title: System.String;
	    _subtitle: System.String;
	  public
	    property Coordinate : CLLocationCoordinate2D read _coordinate write _coordinate;override;
	    property Title: System.String read _title; override;
	    property Subtitle: System.String read _subtitle; override;
     	    // The custom constructor is required to pass the values to this class,
	    // because in the MKAnnotation base the properties are read-only
	    constructor (l: CLLocationCoordinate2D; t: System.String; s: System.String);

	  end;

Agora tenho que implementar o Search Bar delegate responsável por gerenciar os eventos gerados, o exemplo abaixo não é uma implementação completa do UISearchDelegate, mas implementa o necessário para gerenciar as mudanças de comportamente na hora de efetuar uma busca (search).

O botão de busca (Search) obtem a latitude/longitude utilizando o web service do Google e utilizando a classe MyAnnotation irá adicionar um PIN ao mapa. Lembre-se que você precisa obter uma chave para utilizar a API do Google Maps através do link http://code.google.com/apis/maps/signup.html para usar esta aplicação, associe o seu Google Maps Key a variável GoogleMapsKey.

	  MySearchBarDelegate nested in AppDelegate = public class(UISearchBarDelegate)
	  private
	    app: AppDelegate;
	    lastResult: MyAnnotation;
	    GoogleMapsKey:String := String.Empty; // Define you google maps key here.
            // Keep a reference to the application so we can access the UIControls.
	   // This needs to be an internal class to access those (private) controls.
	  public
	    constructor (a: AppDelegate);
		// Method for the searchbar to call when the user wants to search,
		// where we create and call the Geocoder class. Note that we are
		// calling it synchronously which is undesirable in a "real"
		// application.
	    method SearchButtonClicked(searchBar: UISearchBar); override;
	  end;

method AppDelegate.MySearchBarDelegate.SearchButtonClicked(searchBar: UISearchBar);
begin

  var g := new Geocoder(GoogleMapsKey);
  var location: CLLocationCoordinate2D;
  searchBar.ResignFirstResponder;
  UIApplication.SharedApplication.NetworkActivityIndicatorVisible := true;

  // synchronous webservice call
  if g.Locate(searchBar.Text, out location) then begin
    if lastResult <> nil then
    begin
      // if there is already a pin on the map, remove it
      app.Map.RemoveAnnotation(lastResult)
    end;

    app.Map.SetCenterCoordinate(location, true);
    var pin := new MyAnnotation(location, searchBar.Text, location.Latitude.ToString + ',' + location.Longitude.ToString);
    app.Map.AddAnnotationObject(pin);
    lastResult := pin;
  end
  else
  begin
    // display a message that the webservice call didn't work
    using alert := new UIAlertView('Not found', 'No match found for ' + searchBar.Text, nil, 'OK', nil) do
    begin
      alert.Show()
    end
  end;
  UIApplication.SharedApplication.NetworkActivityIndicatorVisible := false;
end;

Para finalizar abaixo temos a implementação que busca a latitude/longitude usando o web services do Google, temos apenas que passar o endereço e o Google irá retornar a informação.

interface

uses
  System,
  System.Xml,
  System.IO,
  System.Net,
  MonoTouch.CoreLocation;

// Documentation for the service http://code.google.com/apis/maps/documentation/geocoding/
type
  Geocoder = public class
  public
    constructor (key: System.String);
    // Sign up for a Google Maps key http://code.google.com/apis/maps/signup.html
  private
    var _GoogleMapsKey: System.String;
    var xmlString: System.String := '';
  public
    method Locate(query: System.String; out return: CLLocationCoordinate2D): System.Boolean;
  private
    // Retrieve a Url via WebClient
    class method GetUrl(url: System.String): System.String;
  end;

implementation

constructor Geocoder(key: System.String);
begin
  _GoogleMapsKey := key;
end;

method Geocoder.Locate(query: System.String; out return: CLLocationCoordinate2D): System.Boolean;
var
  url: System.String := 'http://maps.google.com/maps/geo?q={0}&output=xml&key=' + _GoogleMapsKey;
  coords: XmlNode := nil;
  xd: XmlDocument;
  xnm: XmlNamespaceManager;
  gl: System.String;
  coordinateArray: array of System.String;
begin

  url := String.Format(url, query);
  return := new CLLocationCoordinate2D(0, 0);

  try
    xmlString := GetUrl(url);
    xd := new XmlDocument();
    xd.LoadXml(xmlString);
    xnm := new XmlNamespaceManager(xd.NameTable);
    coords := xd.GetElementsByTagName('coordinates')[0]
  except 

  end;

  if coords <> nil then begin
     coordinateArray := coords.InnerText.Split(',');

     if coordinateArray.Length >= 2 then begin
        gl := Convert.ToDouble(coordinateArray[1].ToString()).ToString + ',' + Convert.ToDouble(coordinateArray[0].ToString());

      return := new CLLocationCoordinate2D(Convert.ToDouble(coordinateArray[1].ToString()),
                                            Convert.ToDouble(coordinateArray[0].ToString()));
      exit true
     end
  end;

  exit false
end;

class method Geocoder.GetUrl(url: System.String): System.String;
var
  return: System.String := System.String.Empty;
  client: System.Net.WebClient := new WebClient();
begin

  using strm: Stream := client.OpenRead(url) do
  begin
    var sr: StreamReader := new StreamReader(strm);
    return := sr.ReadToEnd()
  end;

  exit return;
end;

end.

Quando executamos a aplicação podemos visualizar o mapa de 3 formas diferentes conforme as figura abaixo, o PIN (em vermelho) está na posição onde está localizado nosso escritório aqui em Scotts Valley.

O código fonte está disponível para download no CodeCentral.

1 responder

Trackbacks & Pingbacks

  1. […] This post was mentioned on Twitter by Andreano Lanusse. Andreano Lanusse said: Blog post: Usando iOS MapKit e CoreLocation com Delphi Prism e MonoTouch http://bit.ly/gH6eb3 #ios #iphone […]

Deixe uma resposta

Want to join the discussion?
Feel free to contribute!

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *


Esse site utiliza o Akismet para reduzir spam. Aprenda como seus dados de comentários são processados.