Observação: Esse post leva em consideração que o leitor possua conhecimento de desenvolvimento de aplicações de mapeamento na plataforma ESRI e que já possua um conhecimento mínimo da ArcGIS API For Microsoft Silverlight/WPF. No final do post vou indicar alguns links com maiores informações.
Introdução
O objetivo da ArcGIS API For Microsoft Silverlight/WPF é integrar serviços de mapa publicados no ArcGIS Server, dados espaciais armazenados no SQL Server 2008 (ESRI MapIt) ou até mesmo serviços do Bing Maps com aplicações desenvolvidas em Silverlight ou WPF.
O ArcGIS Server é um software servidor desenvolvido pela ESRI que permite a criação e a distribuição de dados espaciais por meio de serviços web. Uma característica interessante do ArcGIS Server é que este possui uma API que disponibiliza os serviços de mapas por meio de uma interface REST, ou seja, podemos utilizar os recursos e operações de cada serviço por meio de URLs.
A idéia então é criar um método de extensão na classe ArcGISDynamicMapServiceLayer que recupera essas informações usando a ArcGIS Server REST API e deserialização de dados no formato JSON.
Instalação e Requisitos Mínimos
Primeiramente temos que fazer o download da versão 2.0 Beta da ArcGIS Server API For Microsoft Silverlight/WPF, que somente funciona com o Silverlight 4 e consequentemente com o Visual Studio 2010 e Expression Blend 4. Para utilizar essa API você deve possuir um login na ESRI e então fazer o download do instalador neste link.
Por questões práticas irei pular as instruções de instalação e os requisitos mínimos para utilizar a API. Para mais informações veja esse vídeo.
Hands-On
A classe ArcGISDynamicMapServiceLayer possui uma propriedade que é um array de layers refresentados pela classe LayerInfo, que representa informações relativas a um layer. Infelizmente existem poucas informações disponíveis (ID, Name, DefaultVisibility e SubLayerIds). A ArcGIS REST API disponibiliza além dessas informações diversas outras, tais como: escala de visualização (minScale, maxScale), atributos, tipo de geometria e muitos outros.
Observação: As APIs do ArcGIS Server para Flex e JavaScript possuem essas informações, mas a API do Silverlight/WPF não. É bem possível que o que irei demonstrar aqui possa surgir em versões futuras da API.
Um exemplo desse recurso em formato JSON pode ser encontrado no exemplo da ESRI do link abaixo.
http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Specialty/ESRI_StateCityHighway_USA/MapServer/1?f=json&pretty=true
Vejam uma parte desse recurso abaixo.
{ "id" : 1, "name" : "states", "type" : "Feature Layer", "geometryType" : "esriGeometryPolygon", "description" : "This service provides ... ", "definitionExpression" : "", "copyrightText" : "(c) ESRI and its data partners", "minScale" : 0, "maxScale" : 0, "extent" : { "xmin" : -178.217598362366, "ymin" : 18.9247817993165, "xmax" : -66.9692710360024, "ymax" : 71.4062353532711, "spatialReference" : { "wkid" : 4326 } }, "displayField" : "STATE_NAME", "fields" : [ {"name" : "OBJECTID", "type" : "esriFieldTypeOID", "alias" : "OBJECTID"}, ... {"name" : "Shape_Area", "type" : "esriFieldTypeDouble", "alias" : "Shape_Area"} ], "parentLayer" : null, "subLayers" : [] }
Silverlight Class Library
Vamos agora criar o nosso projeto no Visual Studio 2010. No Startup Page clique em New Project, escolha o template Silverlight Class Library e dê o nome ArcGISServerAPIExtension.
Escolha a versão 4 do Silverlight como target da class library.
Helper Classes
Vamos agora criar algumas classes que irão representar as informações que iremos recuperar do serviço dinâmico de mapa.
Para isso primeiramente devemos fazer referência ao assembly System.Runtime.Serialization e System.ServiceModel.Web. O primeiro possibilita a criação ou implementação de contratos usando os atributos DataContractAttribute e DataMemberAttribute, já o segundo possui a classe DataContractJsonSerializer que realiza serialização em objetos no formato JSON.
Observação: A classe DataContractJsonSerializer encontra-se no assembly System.ServiceModel.Web mas no namespace System.Runtime.Serialization.Json. Não me perguntem o porquê.
Vamos fazer a referência também do assembly ESRI.ArcGIS.Client que contém a classe ArcGISDynamicMapServiceLayer.
A classe FieldDetails irá representar algumas informações referentes a um dos atributos de um layer.
[DataContract] public class FieldDetails { [DataMember(Name="name")] public string Name { get; set; } [DataMember(Name = "type")] public string Type { get; set; } [DataMember(Name = "alias")] public string Alias { get; set; } }
O parâmetro Name do DataMember mapeia a propriedade à informação correspondente disponibilizada no serviço de mapa. Perceba que os nomes mapeados encontram-se no array fields do exemplo do recurso REST mostrado anteriormente.
A classe LayerDetails irá representar algumas informações de um layer e seu conjunto de atributos em forma de um array
[DataContract] public class LayerDetails { [DataMember(Name = "id")] public int ID { get; set; } [DataMember(Name = "name")] public string Name { get; set; } [DataMember(Name = "description")] public string Description { get; set; } [DataMember(Name = "geometryType")] public string GeometryType { get; set; } [DataMember(Name = "minScale")] public double MinScale { get; set; } [DataMember(Name = "maxScale")] public double MaxScale { get; set; } [DataMember(Name = "fields")] public FieldDetails[] Fields { get; set; } }
Observação: Por praticidade não irei mapear todas as informações do layer. Se necessário, realize o mapeamento na classe acima.
Uma outra classe chamada GetLayerDetailsResult também será criada com o objetivo de retornar as informações para um método callback, que será explicado posteriormente.
public class GetLayerDetailsResult { public LayerDetails Result { get; set; } public Exception Error { get; set; } public object UserState { get; set; } }
Extension Method
Vamos então criar uma nova classe em nosso projeto com um método chamado GetLayerDetails que será o método de extensão da classe ArcGISDynamicMapServiceLayer.
public static void GetLayerDetails(this ArcGISDynamicMapServiceLayer dynamicLayer, int layerId, object userState, Action<GetLayerDetailsResult> resultCallback) { var webClient = new WebClient(); webClient.DownloadStringCompleted += (s, a) =>; { // verifica erro if (a.Error != null) { // executa método callback (retorna null, se erro) resultCallback( new GetLayerDetailsResult { Error = a.Error, Result = null, UserState = userState } ); return; } // transforma string em uma stream na memória var bytes = Encoding.Unicode.GetBytes(a.Result); var memStream = new MemoryStream(bytes); // deserializa a stream com base no contrato definido na classe LayerDetails var dataContractJson = new DataContractJsonSerializer(typeof(LayerDetails)); var layerDetails = (LayerDetails)dataContractJson.ReadObject(memStream); // executa método callback resultCallback( new GetLayerDetailsResult { Error = null, Result = layerDetails, UserState = userState } ); }; // realiza download da string conforme URL padrão // [url do serviço]/[id do layer]?f=json } webClient.DownloadStringAsync(new Uri(dynamicLayer.Url + @"/" + layerId + "?f=json")); }
Os comentários são auto-explicativos, mas vou explicar algumas partes do código.
O método possui os seguintes parâmetros:
- layerId – o ID do layer cujas informações serão recuperadas.
- userState – um objeto que representa o estado do usuário.
- resultCallback – método que será chamado quando o método GetLayerDetails terminar o processamento.
Com a string, transformamos a mesma em um objeto MemoryStream e utilizamos a classe DataContractJsonSerializer para deserializar a string com conteúdo JSON em um objeto da classe LayerDetails.
O método resultCallback é executado e entrega para o cliente da classe informações sobre o processamento por meio da classe GetLayerDetailsResult. Com essa classe podemos verificar se ocorreu um erro (propriedade Error) ou recuperar as informações do layer (propriedade LayerDetails), que é o resultado do processamento do método.
A implementação desse método surgiu com a necessidade em um projeto de recuperar as escalas máxima e mínima de visualização de um layer para habilitar ou desabilitar o mesmo em uma legenda de acordo com o zoom atual do mapa. O único plágio foi copiar o nome do método da API do Flex, que você encontra aqui.
Em uma segunda parte desse post, explicarei como utilizar essa classe.
Links diversos:
ArcGIS Server
http://bit.ly/pLj3T
ArcGIS Server REST API
http://bit.ly/8KLgfe
ESRI Products
http://bit.ly/cyduyV
ArcGIS API for Microsoft Silverlight/WPF
http://bit.ly/cgUZ5o
DataContractAttribute Class
http://bit.ly/9286VZ
DataContractJsonSerializer Class
http://bit.ly/bobuuI
WebClient Class
http://bit.ly/aDEK9N
Parabéns pelo artigo.
ResponderExcluirVc poderia listar as vantagens de se trabalhar com o Silverlight em relação ao Flex no ArcGis API?
Olá, eu não disse que a ArcGIS API do Silverlight era melhor do que a do Flex. Aliás, a API do Flex possui mais funcionalidades, pois já existe a mais tempo.
ResponderExcluirNa minha opinião, a principal vantagem de trabalhar com o Silverlight é não ter que aprender uma nova linguagem (ActionScript).
Outra vantagem é poder trabalhar com a melhor ferramenta de desenvolvimento que existe (Visual Studio) e integrar sua aplicação com o restante da plataforma .NET.
O Silverlight (não a ArcGIS API For Silverlight) vem evoluindo muito rapidamente e é bem provável que possua a maioria das funcionalidades do Flex nas próximas releases em um futuro bem próximo.
Excelente artigo !
ResponderExcluir