Visor GIS con WPF y el API de Esri

sábado, 08 de septiembre de 2012

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.

Quintanar de la Orden

Etiquetas,

Deja un comentario

Buscar

Search