Quantcast
Channel: Amin Espinoza
Viewing all articles
Browse latest Browse all 116

Medir la distancia entre dos coordenadas geográficas usando Bing Maps y Silverlight

$
0
0

Hacía mucho que no escribía un post relacionado a la geografía y para retomar la tradición en este post voy a explicar como poder obtener la distancia entre dos puntos utilizando el API de Bing Maps.

Lo primero que debes hacer en tu proyecto de Silverlight es agregar tu control de mapa (recuerda tu infaltable llave para la API de Bing Maps).

<bingMaps:Map x:Name="mapa" CredentialsProvider="Ajtc9UQoW_fIE-ijLj_wmEnUvIhpmQ6ygbhyfszjvLKsL0yL3hxIEK0oe9x499" Center="22.75791,-101.5966" ZoomLevel="5.900" ScaleVisibility="Collapsed" CopyrightVisibility="Collapsed" NavigationVisibility="Collapsed" Margin="0" />

Si no sabes muy bien como colocar este control de mapa, te recomiendo visitar la primera parte de este post.

Ya que tienes tu control de mapa, crea un nuevo control de usuario con la siguiente interfaz.

InterfazControl

El XAML es el siguiente.

<UserControl
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="Medir_distancia_entre_puntos.MenuMedir" Width="250" Height="300" HorizontalAlignment="Left" VerticalAlignment="Top">
 
    <Grid x:Name="LayoutRoot">
        <Rectangle Fill="#FF1BA1E2" Margin="0"/>
        <TextBlock Height="20" Margin="8,8,8,0" TextWrapping="Wrap" Text="Selecciona tus dos puntos" VerticalAlignment="Top" Foreground="White" FontSize="14.667"/>
        <TextBlock Height="20" Margin="8,36,8,0" TextWrapping="Wrap" Text="Punto No. 1" VerticalAlignment="Top" Foreground="White" FontSize="14.667"/>
        <TextBox x:Name="txtPuntoUno" Height="24" Margin="8,60,8,0" TextWrapping="Wrap" VerticalAlignment="Top"/>
        <TextBlock Height="20" Margin="8,101,8,0" TextWrapping="Wrap" Text="Punto No. 2" VerticalAlignment="Top" Foreground="White" FontSize="14.667"/>
        <TextBox x:Name="txtPuntoDos" Margin="8,125,8,0" TextWrapping="Wrap" Height="24" VerticalAlignment="Top"/>
        <Button x:Name="btnPuntoUno" Content="Crear punto 1" HorizontalAlignment="Left" Height="28" Margin="8,0,0,109" VerticalAlignment="Bottom" Width="110" Click="btnPuntoUno_Click"/>
        <Button x:Name="btnPuntoDos" Content="Crear punto 2" HorizontalAlignment="Right" Height="28" Margin="0,0,8,109" VerticalAlignment="Bottom" Width="110" Click="btnPuntoDos_Click"/>
        <Button x:Name="btnObtenerDistancia" Content="Obtener distancia" Height="34" Margin="8,0,8,59" VerticalAlignment="Bottom" Click="btnObtenerDistancia_Click"/>
        <TextBlock Height="20" Margin="8,0,8,35" TextWrapping="Wrap" Text="Distancia obtenida:" VerticalAlignment="Bottom" Foreground="White" FontSize="14.667"/>
        <TextBox x:Name="txtDistancia" Margin="8,0,8,8" TextWrapping="Wrap" Height="24" VerticalAlignment="Bottom"/>
    </Grid>
</UserControl>

En el code behind, primero declara las siguientes variables, objetos y propiedades.

public Map mapaMaster { get; set; }
Location ubicacion;
bool banderaUno;
bool banderaDos;
double latitudUno = 0, latitudDos = 0;
double longitudUno = 0, longitudDos = 0;

Los dos primeros botones cuentan con un manejador de eventos, asigna un método para cada uno de ellos.

private void btnPuntoUno_Click(object sender, System.Windows.RoutedEventArgs e)
{
CapturarCoordenadasUno();
}
 
private void btnPuntoDos_Click(object sender, System.Windows.RoutedEventArgs e)
{
CapturarCoordenadasDos();
}

Los métodos (que son sumamente parecidos) solo te permitirán un par de cosas. Ajustar las banderas para determinar que tipo de captura realizarás y la segunda es cambiar el contenido de tus botones para que no haya confusión al final.

private void CapturarCoordenadasUno()
{
if (banderaUno)
{
btnPuntoUno.Content = "Crear punto 1";
}
else
{
btnPuntoUno.Content = "Dejar selección";
if (banderaDos)
{
btnPuntoDos.Content = "Crear punto 2";
banderaDos = false;
}
}
banderaUno = !banderaUno;
}

El segundo método es prácticamente idéntico.

private void CapturarCoordenadasDos()
{
if (banderaDos)
{
btnPuntoDos.Content = "Crear punto 2";
 
}
else
{
btnPuntoDos.Content = "Dejar selección";
if (banderaUno)
{
btnPuntoUno.Content = "Crear punto 1";
banderaUno = false;
}
}
banderaDos = !banderaDos;
}

Ya que tienes control sobre ambas banderas, entonces crea el método que se ejecute al darle clic a tu mapa. Este método debe ser público para que puedas acceder a él desde tu MainPage.

public void mapa_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
Point puntoUbicado = e.GetPosition(mapaMaster);
 
if (mapaMaster.TryViewportPointToLocation(puntoUbicado, out ubicacion))
{
if (banderaUno)
{
txtPuntoUno.Text = Math.Round(ubicacion.Latitude, 5).ToString() + " " + Math.Round(ubicacion.Longitude, 5);
latitudUno = ubicacion.Latitude;
longitudUno = ubicacion.Longitude;
}
if (banderaDos)
{
txtPuntoDos.Text = Math.Round(ubicacion.Latitude, 5).ToString() + " " + Math.Round(ubicacion.Longitude, 5);
latitudDos = ubicacion.Latitude;
longitudDos = ubicacion.Longitude;
}
}
}

Con este método simplemente lo que harás es capturar las coordenadas en latitud y longitud del mapa y las colocarás en una de las dos cajas de texto dependiendo de la bandera que esté “activada” en ese momento. La idea aquí es que ambas cajas de texto tengan su par de coordenadas al momento de poder ejecutar las siguientes operaciones.

El tercer botón tiene un manejador de eventos que llamará a un método adicional.

private void btnObtenerDistancia_Click(object sender, System.Windows.RoutedEventArgs e)
{
ObtenerDistancia();
}

Aquí es donde viene lo bueno e interesante.

private void ObtenerDistancia()
{
double LatA = latitudUno * Math.PI / 180;
double LonA = longitudUno * Math.PI / 180;
 
double LatB = latitudDos * Math.PI / 180;
double LonB = longitudDos * Math.PI / 180;
 
double distancia = Math.Round(6371 * Math.Acos(Math.Cos(LatA) * Math.Cos(LatB) * Math.Cos(LonB - LonA) + Math.Sin(LatA) * Math.Sin(LatB)), 3);
 
txtDistancia.Text = (distancia &lt; 1) ? (distancia * 1000).ToString() + " mts" : txtDistancia.Text = distancia.ToString() + " km";
}

En este último método, lo que debes hacer es convertir los valores de la latitud y longitud (que están en grados decimales) a radianes. Eso lo puedes lograr multiplicando el valor de los dos valores de latitud y longitud por Pi y después multiplicarlos por 180. Los resultados de estas cuatro operaciones son los valores que vas a ingresar en la fórmula. La descripción de las funciones trigonométricas de la fórmula son.

Acos = Arcoseno

Cos = Coseno

Sin = Seno

Todas estas funciones se encuentran disponibles dentro de la clase Math.

Por último utilizando una sentencia IF con un operador ternario. Si la distancia entre ambos puntos es menor a un kilómetro, entonces la puedes expresar en metros, de lo contrario usarás la medida preestablecida.

NOTA: Si te preguntaste de donde sale el número constante de 6371, simplemente es el diámetro de la tierra medido en kilómetros, si quieres usar millas, el valor es de 3959. Ya a partir de cualquiera de estos dos valores puedes fragmentar en la unidad de medida que desees.

Puedes descargar el código aquí.


Viewing all articles
Browse latest Browse all 116

Trending Articles