Uso de GPS com Mapa no Windows Phone 8.1

Renato Haddad

Dn690107.060DE5057573180CEC6D227C6D3E2207(pt-br,MSDN.10).png

Maio, 2014

O objetivo deste artigo é mostrar o uso da API de GPS no Windows Phone 8.1, assim como mostrar a localização no controle de mapa, tudo é claro com a linguagem Visual C# 5. Usar GPS se tornou corriqueiro em diversas aplicações, pois praticamente todo dispositivo (celular e tablet) vem com esta funcionalidade, e integrar à aplicação é muito mais fácil do que você imagina. O código que mostrarei neste artigo pode ser usado nas versões 7.5, 8 e 8.1 do Windows Phone.

Se você quiser integrar a API do Bing, que é nativa, diretamente com a sua aplicação, leia este artigo http://msdn.microsoft.com/pt-br/library/hh972467.aspx e outro que acho muito importante para integrar uma base de dados SQL Server com o recurso de mapas é o http://msdn.microsoft.com/pt-br/library/jj900151.aspx . Com isto você poderá integrar dados armazenados em uma base de dados ou requisitar dados de um serviço e mostrar no mapa os devidos pontos. Quando falo sobre dados, refiro-me à latitude e longitude, isto é o que um mapa necessita. Então, vamos ao projeto em si. Neste projeto usarei o Visual Studio 2013 Ultimate com o Update 2 instalado.

Os pré-requisitos para este artigo são conhecimento básicos de C# e o Visual Studio .NET 2013 Update 2.

Projeto Windows Phone

Já que faremos passo a passo, abra o Visual Studio 2013, selecione a opção File / New / Project ou CTRL + SHIFT + N ou na janela inicial, clique em New Project. Conforme a figura 1, selecione Visual C# / Store Apps / Windows Phone Apps / Blank App (Windows Phone Silverlight). Em Name digite GPS_Mapa_MSDN e no Location você pode gravar onde desejar.

Dn690107.C93D3B8CA9FF0C90C1F58CFACCBEECE6(pt-br,MSDN.10).png

Figura 1 – Novo projeto

Clique no botão OK e o VS solicita o Target do OS do Windows Phone, conforme a figura 2. Selecione Windows Phone 8.1, assim o projeto já é criado com o que há de mais novo.

Dn690107.BCB896E30F89AA5473EA894F121BFD59(pt-br,MSDN.10).png

Figura 2 – Projeto no WPhone 8.1

Clique no botão OK e deixe que o Visual Studio crie o projeto. A página MainPage.xaml é a inicial, que já aparece aberta. Todas as referências do XAML estão mostradas nas primeiras linhas, assim como a estrutura inicial sugerida pelo template. Como teremos um mapa nesta página e é necessário espaço suficiente para exibi-lo, a primeira coisa a fazer é comentar este bloco de códigos a seguir, que já vem com o template.

<!--TitlePanel contains the name of the application and page title--><!--
<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
    <TextBlock Text="MY APPLICATION" Style="{StaticResource PhoneTextNormalStyle}" Margin="12,0"/>
    <TextBlock Text="page name" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
</StackPanel>-->

Pergunta: Renatão, posso deixar estas linhas para colocar o título para que o usuário veja onde está? Sim, pode mas eu prefiro uma tela mais limpa quando exibo mapas, senão o usuário irá desistir de ter que navegar no mapa num tamanho muito pequeno de tela, isto desanima qualquer um.

A seguir, como iremos lidar com GPS, vamos criar a seguinte estrutura de controles para mostrar a latitude e longitude. Só que o próximo controle disponível chamado ContentPanel é um Grid, então, basta troca-lo para um StackPanel e acrescentar uma orientação Vertical. Dentro deste, adicione um outro StackPanel Vertical com dois TextBlocks, conforme códigos a seguir. Veja que defini as propriedades Style e Foreground conforme os recursos (StaticResource) do Windows Phone. Claro que você pode alterar para qualquer outro que achar mais adequado.

<StackPanel x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0" 
        Orientation="Vertical">
    <StackPanel Orientation="Vertical">
        <TextBlock x:Name="StatusTextBlock" 
                   Text="serviço localização desligado" 
                   Style="{StaticResource PhoneTextTitle2Style}" 
                   Foreground="{StaticResource PhoneAccentBrush}" />
        <TextBlock x:Name="DadosTextBlock" 
                   Text="latitude/longitude"
                   Style="{StaticResource PhoneTextTitle2Style}" 
                   Foreground="{StaticResource PhoneAccentBrush}" /> 
    </StackPanel>
    
</StackPanel>

E o mapa? O mapa é um controle existente na Toolbox, então, localize o controle Map na toolbox e arraste-o logo após o TextBlock chamado DadosTextBlock. Será adicionada a seguinte linha no XAML, e aproveite e já configure as propriedades x:Name (nome do controle), ZoomLevel (Zoom default para exibir o mapa) e Height (altura).

<maps:Map x:Name="mapa" ZoomLevel="10" Height="602"/>

Por que foi adicionado o <maps:Map />? Esta notação faz referência ao namespace que é inserido automaticamente na lista no início da página, para que o controle possa ser inicalizado e usado, sendo:

xmlns:maps="clr-namespace:Microsoft.Phone.Maps.Controls;assembly=Microsoft.Phone.Maps"

A seguir, precisamos criar a barra de menu da aplicação, o qual conterá 3 botões e 4 opções de menus. Insira o bloco de códigos XAML a seguir logo abaixo do fechamento da tag do bloco shell:SystemTray.IsVisible="True">. Veja que para os 3 ícones estou usando imagens (png) que você pode baixar da internet ou criar as próprias imagens. Observe que o ApplicationBar está configurado para mostrar tudo (IsVisible=True) e que contém menus ativos (IsMenuEnabled=True).

<phone:PhoneApplicationPage.ApplicationBar>
    <shell:ApplicationBar IsVisible="True" IsMenuEnabled="True">
        <shell:ApplicationBarIconButton 
            IconUri="/Assets/low.png" 
            Text="Baixa"
            Click="Baixa_Click"/>

        <shell:ApplicationBarIconButton 
            IconUri="/Assets/high.png" 
            Text="Alta"
            Click="Alta_Click"/>

        <shell:ApplicationBarIconButton 
            IconUri="/Assets/stop.png" 
            Text="Parar"
            Click="Parar_Click"/>

        <shell:ApplicationBar.MenuItems>
            <shell:ApplicationBarMenuItem Text="Rua" Click="Rua_Click"/>
            <shell:ApplicationBarMenuItem Text="Aerio" Click="Aerio_Click"/>
            <shell:ApplicationBarMenuItem Text="Híbrido" Click="Hibrido_Click"/>
            <shell:ApplicationBarMenuItem Text="Terra" Click="Terra_Click"/>
        </shell:ApplicationBar.MenuItems>
    </shell:ApplicationBar>
</phone:PhoneApplicationPage.ApplicationBar>

Note na figura 3 como está a tela com o layout definido até o momento.

Dn690107.7180FB2F99DFDFE01157F3B0346FB608(pt-br,MSDN.10).png

Figura 3 – Tela da página com o mapa e menus

Cabe ressaltar que cada opção do ApplicationBar, seja um ícone ou menu, contém o evento Click do objeto, ou seja, para cada uma destas opções você deverá assinar o evento. Pronto, salve o projeto e pressione F7 para exibir o código C#. A primeira coisa a fazer é referenciar os dois namespaces a seguir, os quais nos dão acesso à manipulação de serviços de localização e ao controle mapa.

using System.Device.Location;
using Microsoft.Phone.Maps.Controls;

A seguir, logo abaixo da declaração da classe, digite as duas linhas para referenciar a classe GeoCoordinateWatcher na variável watcher, o qual nos dá acesso a todo o serviço de localização, e crie a variável texto do tipo string.

private void Baixa_Click(object sender, EventArgs e)
{
    texto = "força mínima";
    IniciarLocalizacao(GeoPositionAccuracy.Default);
}

private void Alta_Click(object sender, EventArgs e)
{
    texto = "força total";
    IniciarLocalizacao(GeoPositionAccuracy.High);
}

Como este evento IniciarLocalizacao ainda não existe, crie-o. Para isto, clique no nome do evento a ser criado e dê um CTRL + Ponto (.) + ENTER. O bloco de código a seguir recebe um argumento que é o geoPositionAccuracy, mostra o texto “iniciando força mínima” ou “iniciando força total” no TextBlock StatusTextBlock. Depois, inicializa o objeto watcher de acordo com o parâmetro passado (Default ou High), define a distância mínima de 20 metros conforme a posição é alterada (evento PositionChanged que veremos depois). Duas coisas importantes que você não pode deixar de setar são os eventos de StatusChanged e PositionChanged, os quais pertencem ao objeto watcher. O primeiro controle o status do GPS e o segundo é o que irá ficar alerta a qualquer alteração de posição. Sendo assim, já insira o handler para ambos. A dica na digitação é que quando digitar o += já pressione o TAB (2 vezes) logo em seguida, assim o respectivo evento é criado automaticamente. Ao final, o evento Start é disparado.

private void IniciarLocalizacao(GeoPositionAccuracy geoPositionAccuracy)
{
    StatusTextBlock.Text = "iniciando " + texto;
    watcher = new GeoCoordinateWatcher(geoPositionAccuracy);
    watcher.MovementThreshold = 20;

    watcher.StatusChanged += watcher_StatusChanged;
    watcher.PositionChanged += watcher_PositionChanged;
    watcher.Start();
}

Alteração do Status do GPS

O status do GPS pode ser: desabilitado, inicializando, sem dados ou pronto. Para cada opção é importante avisar o usuário para que o mesmo saiba o que está acontecendo. O código do evento StatusChanged recebe como argumento o objeto sender e os eventArgs do mesmo, conforme o código a seguir. Sendo assim, vamos criar um delegate assíncrono para disparar o evento a ser criado chamado TrocarStatusChanged, contendo um swtich para cada estado do GPS. Desta forma, como será um delegate assíncrono, ele fica o tempo todo em alerta verificando o GPS, e quando o status mudar, ele dispara o evento para exibir o respectivo texto ao usuário. Quando você criar o evento TrocarStatusChanged veja que ele é um void.

void watcher_StatusChanged(object sender, GeoPositionStatusChangedEventArgs e)
{
    Deployment.Current.Dispatcher.BeginInvoke(() => TrocarStatusChanged(e));
}


void TrocarStatusChanged(GeoPositionStatusChangedEventArgs e)
{
    switch (e.Status)
    {
        case GeoPositionStatus.Disabled:
            StatusTextBlock.Text = "localização não suportada neste dispositivo";
            break;
        case GeoPositionStatus.Initializing:
            StatusTextBlock.Text = "inicializando o serviço " + texto;
            break;
        case GeoPositionStatus.NoData:
            StatusTextBlock.Text = "dados indisponíveis " + texto;
            break;
        case GeoPositionStatus.Ready:
            StatusTextBlock.Text = "recebendo dados " + texto;
            break;
        default:
            break;
    }  
}

Alteração da Posição do GPS

Quando se utiliza os serviços do GPS, presume-se que este irá adquirir novas coordenadas a cada momento, portanto é preciso ficar atento a cada alteração. Para isto, crie o evento a seguir o qual é um delegate assíncrono que irá chamar o evento TrocarPositionChanged para capturar a nova latitude e longitude, informar ao usuário e mostrar no mapa.

Aqui cabe uma observação importante quanto ao uso do mapa da Nokia. Enquanto estamos no ambiente de desenvolvimento, não é preciso ter um appID e um Token, o seu mapa funcionará sem problemas. Mas, no momento que for distribuir a aplicação na lojinha, aí sim, você precisa entrar no site da MS Store, registrar a sua aplicação, pedir para gerar um Token e este gerado é o que você deverá adicionar nas linhas comentadas que deixei de propósito no código a seguir.

void watcher_PositionChanged(object sender, GeoPositionChangedEventArgs<GeoCoordinate> e)
{
    Deployment.Current.Dispatcher.BeginInvoke(() => TrocarPositionChanged(e));
}


void TrocarPositionChanged(GeoPositionChangedEventArgs<GeoCoordinate> e)
{
    DadosTextBlock.Text = string.Format("lat: {0:0.000} / long: {1:0.000}",
                            e.Position.Location.Latitude, e.Position.Location.Longitude);

    //MapsSettings.ApplicationContext.ApplicationId = "appID";
    //MapsSettings.ApplicationContext.AuthenticationToken = "AuthenticationToken";

    mapa.Center = new GeoCoordinate(e.Position.Location.Latitude,
                                    e.Position.Location.Longitude);
}

Agora que já temos os delegates para ficar observando as alterações de Status e posição, digite o código para o botão Parar, conforme a seguir.

private void Parar_Click(object sender, EventArgs e)
{
    if (watcher != null)
        watcher.Stop();

    StatusTextBlock.Text = "sem serviço de localização";
    DadosTextBlock.Text = string.Empty;
}

Configuração do Manifesto

Como esta é uma aplicação que usará o serviço de localização, é preciso configurar o manifesto. Portanto, abra o arquivo WMAppManifest.xml, e na guia Capabilities, marque o checkbox ID_CAP_LOCATION, conforme a figura 4. Em seguida, salve a aplicação.

Dn690107.A02381D548D93F66EFB453DA1B9AEE37(pt-br,MSDN.10).png

Figura 4 – Configuração do manifesto

Tipos de Visões do Mapa

A maioria dos mapas utiliza recurso de mostrar as imagens de diferentes visões, e como o nosso menu já está preparado para tal funcionalidade, resta-nos implementar o código a seguir. No geral, esta funcionalidade é tão simples que basta configurar o Enum da propriedade CartographicMode.

private void Rua_Click(object sender, EventArgs e)
{
    mapa.CartographicMode = MapCartographicMode.Road;
}

private void Aerio_Click(object sender, EventArgs e)
{
    mapa.CartographicMode = MapCartographicMode.Aerial;
}

private void Hibrido_Click(object sender, EventArgs e)
{
    mapa.CartographicMode = MapCartographicMode.Hybrid;
}

private void Terra_Click(object sender, EventArgs e)
{
    mapa.CartographicMode = MapCartographicMode.Terrain;
}

Lidar como problemas de memória em qualquer aplicação não é fácil, e deixar coisas pendentes não é uma boa prática. Como atribuimos o handler para dois eventos do watcher no IniciarLocalizacao, sobrescreva o evento OnNavigateFrom com o código a seguir para desmarcar estes dois handlers invocados anteriormente. Isto é uma boa prática e fundamental para não dar problemas de memory leak ou conflitos desnecessários. O OnNavigateFrom ocorre quando a aplicação é suspensa ou fechada.


protected override void OnNavigatedFrom(NavigationEventArgs e)
{
    watcher.StatusChanged -= watcher_StatusChanged;
    watcher.PositionChanged -= watcher_PositionChanged;
            
    base.OnNavigatedFrom(e);
}

Pronto, todos os códigos estão prontos. Compile a aplicação e certifique-se que está compilado 100% com sucesso. Pressione F5 para executar a aplicação no emulador do Windows Phone 8.1. Se você estiver utilizando um proxy e não mostrar o mapa, é preciso configurar a rede para que a máquina virtual enxergue a sua rede, afinal o emulador é uma máquina virtual.

Assim que a aplicação abrir no emulador, clique no primeiro ou segundo ícones para ativar a localização. Uma excelente opção é clicar no icone Tools (>>) para exibir as ferramentas adicionais. Na guia Location, digite o nome da cidade e marque um ponto com o mouse para que o mapa no celular seja mostrado, assim como a latitude e longitude. Na figura 5 digitei a cidade Seattle e marquei um determinado ponto.

Dn690107.B60140CB6D7B685AB61E18F8CB8A96B5(pt-br,MSDN.10).png

Figura 5 – Mapa de Seattle

Agora, alterei para a cidade de Florianopolis e marquei um ponto. Na aplicação, selecione o menu com vista aérea, conforme a figura 6. Nos ícones do emulador, há um ícone com um indicador, o qual você deve utiliza-lo para dar o zoom no mapa do emulador.

Dn690107.FE56E88C5F4920AF12E74CDB06F79CCE(pt-br,MSDN.10).png

Figura 6 – Vista aérea

Já que programamos os códigos, fique à vontade para selecionar os demais tipos de visões e identificar qual é a mais adequada para você, conforme a figura 7.

Dn690107.9E92E56CE8CACAB5AB6414BDCD15DF52(pt-br,MSDN.10).png

Figura 7 – Visões Híbrida e Terra do mapa

Você pode colocar breakpoints nos códigos para identificar exatamente o que se passa quando ocorre uma alteração na mudança de posição ou alteração do status do GPS.

Conclusão

O uso de mapas itegrados à aplicação tem se tornado comum em diversas áreas. Imagine aplicações de rastreamento de encomendas ou ainda entregas de mercadorias, ou seja, o horizonte que podemos ter nesta área é muito amplo. E, do ponto de vista de implementação de códigos visto neste artigo facilita muito o desenvolvedor que deseja adicionar mapas para enriquecer a aplicação.

Agradeço a oportunidade de poder compartilhar o conhecimento com todos. Qualquer dúvida e preparação de times de desenvolvimento, por favor me contate.

Sobre o Autor

Renato Haddad (rehaddad@msn.comwww.renatohaddad.com ) é MVP, MCPD e MCTS, palestrante em eventos da Microsoft em diversos países, ministra treinamentos focados em produtividade com o VS.NET 2012/2013, ASP.NET 4/5, ASP.NET MVC, Entity Framework, Reporting Services, Windows Phone e Windows 8. Visite o blog http://weblogs.asp.net/renatohaddad.

Mostrar: