Continuando con el hilo del último post, y aprovechando que el marcado en
Silverlight y WPF es el mismo, generaremos una aplicación de
escritorio, que acceda a servicios WMS como los del PNOA, e
implementaremos una funcionalidad de búsqueda mediante el uso de
Bing Services.
Ficha técnica:
Una vez instalado el API, lo primero, como con el API para
Silverlight, es referenciar el ArcGIS.Client, ArcGIS.Toolkit y
ArcGIS.Toolkit.DataSources. Nota: Al agregar las
referencias, es importante agregar únicamente las que son
compatibles con el Framework que estamos utilizando, en este post
yo estoy utilizando el 4.0, pero aparecen disponibles las del
3.5.
Continuamos trayéndonos el marcado XAML del ejemplo anterior, al
que vamos a hacerle algún cambio que otro cambio:
<Grid Grid.Row="1">
<esri:Map x:Name="MyMap" WrapAround="True">
<esri:Map.Extent>
<esri:Envelope XMin="-21.37979367" XMax="5.07891717" YMin="27.14108121" YMax="44.03141073">
<esri:Envelope.SpatialReference>
<esri:SpatialReference WKID="4326"/>
</esri:Envelope.SpatialReference>
</esri:Envelope>
</esri:Map.Extent>
</esri:Map>
<esri:ScaleLine x:Name="Scale" Map="{Binding ElementName=MyMap}" HorizontalAlignment="Left" VerticalAlignment="Bottom" Visibility="Visible" />
<esri:Legend x:Name="Legend" Map="{Binding ElementName=MyMap}" HorizontalAlignment="Right" VerticalAlignment="Center"/>
<esri:Navigation x:Name="Navigation" Map="{Binding ElementName=MyMap}"/>
<esri:MapProgressBar x:Name="PBar" Map="{Binding ElementName=MyMap}" HorizontalAlignment="Center" VerticalAlignment="Center" Width="200" Height="30"/>
</Grid>
Después vamos con el code behind
private void MainWindowLoaded(object sender, RoutedEventArgs e)
{
const String wmsStr = @"http://www.idee.es/wms/PNOA/PNOA";
var wms = new WmsLayer
{
Url = wmsStr,
SkipGetCapabilities = false,
Layers = new string[] { "PNOA" },
Version = "1.3.0"
//ProxyUrl = proxyStr
};
MyMap.Layers.Add(wms);
MyMap.Extent = MyMap.Layers.GetFullExtent();
}
He comentado la parte del proxy porque aquí no la vamos a
necesitar. Tampoco vamos a añadir ningún tipo de fichero kml, ya
que lo que vamos a hacer es realizar una búsqueda de un lugar
mediante una búsqueda de texto. Para ello, vamos a agregar un
TextBox y un botón para trabajar esta funcionalidad.
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBox x:Name="TxtSearch" Grid.Column="0"/>
<Button x:Name="BtnSearch" Grid.Column="1" Click="BtnSearchClick">
<AccessText Text="_Buscar"/>
</Button>
</Grid>
Al evento Click del botón, ya le he añadido un método, el cual
va a iniciar la petición de coordenadas al servicio Bing. Cosas
fundamentales a tener en cuenta:
- La petición de datos al servicio, en este ejemplo es en
castellano, pero podría ser en inglés
- El ámbito de trabajo siempre va a ser España
- Muchas ciudades de España tienen el mismo nombre en América,
(Córdoba, Toledo, Madrid, Málaga...)
Considerando esto podemos comprobar si el literal que ha pasado
el usuario contiene la palabra España o Spain, y si no lo tiene
concatenarlo, y enviárselo al servicio de Bing. Una vez realizada
la petición recibimos un xml, al que debemos comprobar que las
coordenadas que trae se encuentran dentro de nuestros límites.
Hacemos esto porque si pones New York, España, nunca va a
devolverte una ubicación, pero si pones Córdoba, España, seguro que
no va a devolverte la Córdoba Argentina. Si las coordenadas que
recibimos están fuera del ámbito, le mostraríamos directamente al
usuario un mensaje diciéndole que está fuera de los límites del
mapa.
private const Double XMin = -21.37979367;
private const Double XMax = 5.07891717;
private const Double YMin = 27.14108121;
private const Double YMax = 44.03141073;
private const string UrlBingLocation =
"http://dev.virtualearth.net/REST/v1/Locations?query={0}&includeNeighborhood=true&maxResults=1&output=xml&key={1}";
private void BtnSearchClick(object sender, RoutedEventArgs e)
{
var txt = TxtSearch.Text;
if (!String.IsNullOrEmpty(txt))
{
if (!txt.ToUpper().Contains("ESPAÑA") && !txt.ToUpper().Contains("SPAIN"))
{
txt += ",%20Spain";
}
txt = txt.Replace(" ", "%20");
var env = GetCoordinates(ref txt);
if (env != null)
{
var center = env.GetCenter();
if (IsInSpain(center.X, XMin, XMax) && IsInSpain(center.Y, YMin, YMax))
{
var gl = new GraphicsLayer {ID = txt};
gl.Graphics.Add(new Graphic { Geometry = new MapPoint(center.X, center.Y)});
MyMap.Layers.Add(gl);
MyMap.Extent = env;
}
else
{
MessageBox.Show("La ciudad seleccionada no se encuentra en España, o no está escrita correctamente");
}
}
}
}
private Envelope GetCoordinates(ref string txtSearched)
{
var env = new Envelope();
var key = "KEY_BING_MAPS";
var url = String.Format(UrlBingLocation, txtSearched, key);
try
{
var xmlTxtReader = new XmlTextReader(url);
var ds = new DataSet("Location");
ds.ReadXml(xmlTxtReader);
var dt = ds.Tables["BoundingBox"];
var sLat = Convert.ToDouble((dt.Rows[0]["SouthLatitude"]).ToString().Replace(".", ","));
var wLon = Convert.ToDouble((dt.Rows[0]["WestLongitude"]).ToString().Replace(".", ","));
var nLat = Convert.ToDouble((dt.Rows[0]["NorthLatitude"]).ToString().Replace(".", ","));
var eLon = Convert.ToDouble((dt.Rows[0]["EastLongitude"]).ToString().Replace(".", ","));
var dtN = ds.Tables["Location"];
txtSearched = dtN.Rows[0]["Name"].ToString();
env = new Envelope(wLon, sLat, eLon, nLat);
return env;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
return null;
}
}
private bool IsInSpain(double value, double min, double max)
{
return (min <= value && max >= value);
}
Es importante reseñar, que estamos haciendo trabajos de remplazo
de texto, "." por ",", para salvar la diferencia cultural de los
números, y espacios por "%20" para montar la url sin espacios.
Finalmente agregamos una nueva capa gráfica al mapa, con el
nombre que nos devuelve el servicio, para mostrar lo que realmente
ha localizado.
