Fundamentos de Programación  >  Clases  >  Ejercicios  >  ejercicio 1

Ejercicio 1

FAQ: Preguntas frecuentes

Objetivos

Enunciado

Se trata de calcular rutas sobre una superficio esférica:
Puede descargar AgenciaViaje.jar para ver cómo funciona.

Para ejecutar, necesitará un mapa de la Tierra:

mapa ortográfico, centrado en España

El problema consiste en calcular rutas geodésicas (mínina distancia) entre dos puntos sobre la superficie de una esfera.

Clases

Tenemos objetos de varias clases, algunas de las cuales se las damos y otras tiene que programarlas el alumno:

El alumno debe presentar las siguientes clases:

  1. class Punto3D (documentación)
  2. class ProyeccionOrtografica (documentación)
  3. class AgenciaViajes (documentación)

1. class Punto3D

Implemente la clase Punto3D con los campos privados: proporcionando los constructores y demás métodos mencionados en la documentación.

Para calcular las coordenadas a partir de la longitud y latitud, use trigonometría:

Como radio de la tierra, use R = 1.

Para interpretar las coordenadas de un punto, por ejemplo "40 26 N 3 42 W", debe

  1. fragmentar la cadena: String[] parte= cadena.split(" ");
  2. pasar los dígitos a números, usando Integer.parseInt()
  3. un par de IFs para difeenciar N, S, E, W.

Más precisamente:

    private double extraeLongitud(String coordenadas) {
        String[] part = coordenadas.split(" ");
        if (part.length != 6)
            throw new IllegalArgumentException("notacion: grados minutos N|S grados minutos E|W");

        // angulo horizontal
        double longitud = Math.toRadians(Integer.parseInt(part[3]) + 
                Integer.parseInt(part[4]) / 60.0);
        if (part[5].equalsIgnoreCase("W"))
            longitud = -longitud;
        return longitud;
    }

    private double extraeLatitud(String coordenadas) {
        String[] part = coordenadas.split(" ");
        if (part.length != 6)
            throw new IllegalArgumentException("notacion: grados minutos N|S grados minutos E|W");

        // angulo vertical
        double latitud = Math.toRadians(Integer.parseInt(part[0]) + 
                Integer.parseInt(part[1]) / 60.0);
        if (part[2].equalsIgnoreCase("S"))
            latitud = -latitud;
        return latitud;
    }

2. class ProyeccionOrtografica

Vamos a utilizar proyecciones ortográficas. Esta forma de proyectar la superficie de la Tierra viene explicada en numerosos textos, por ejemplo en la wikipedia.

Para pasar de un punto en 3D a un punto en el plano de proyección, necesitamos conocer la posición del plano de proyección respecto de la esfera.

Siendo

entonces, un punto 3D de longitud λ y latitud φ, se proyecta en el punto 2D determinado por la siguientes ecuaciones:

Para este ejercicio, considere R = 1.

Un detalle importante es saber cuándo un punto está en el lado visible o en el otro lado. La siguiente fórmula da valores positivos si el punto está de este lado, y negativos si está del otro lado

Para el ejercicio, si el punto está del otro lado, getProyeccion(Punto3D punto) debe devolver NULL.

En Internet puede encontrar numerosas imágenes de la Tierra. Las fotografías desde un punto suficientemente lejano (desde cerca del infinito), son proyecciones ortográfinas son prácticamente proyecciones ortográficas. A veces las imágenes indican el punto central de la proyección; en otras ocasiones, tendrá que determinarlo usted mismo.

Por ejemplo, la imagen del prncipio de esta página web tiene como centro de proyección el punto situado en "55 0 N 20 0 E".

3. class AgenciaViajes

Esta clase tiene como objeto reprsentar en pantalla la ruta más corta entre 2 puntos 3D

public static void main(String[] args)

Sirve para arrancar el programa. Internamente debe

  1. construir un objeto de clase AgenciaViajes
  2. construr un objeto de clase Ventana pasándole la agencia como parámetro

Necesita disponer de un constructor de AgenciaViajes, que será PRIVATE. Internamente, este constructor se limita a crear un objeto de clase MapaOrtografico y guardarlo en un campo privado de AgenciaViajes.

public void viaje(String ciudadOrigen, String ciudadDestino)

Este método pinta la ruta en pantalla usando la clase Ventana que se preparó en el constructor.

Lo primero que necesitamos es conocer las coordenadas 3D de los puntos A (origen) y B (destino). Para ello usaremos el método getCoordenadas(String) de la clase Ciudades. Esta clase tiene registradas una serie de ciudades del planeta. Puede añadir otras ciudades. Es fácil encontrar en Internet la coordenadas de las principales ciudades del Mundo.

  1. obtenga las coordenadas de la ciudad
  2. cree un Punto3D usando esas coordenadas

Necesitará un objeto de clase ProyeccionOrtografica. Construya uno. No necesita guardarlo en un campo, basta que sea una variable local.

Usando este objeto, es fácil calcular las coordenadas de los puntos A y B. Calcúlelas y marque el punto en la Ventana, algo así

mapa.punto(proyeccion.getProyeccion(A), Color.RED);

Para trazar la ruta necesitamos navegar por la superficie de la Tierra a lo largo del corte de la superficie de la esfera y un plano que pase por 3 puntos: A, B y en centro C de la Tierra.

El centro C de la esfera tiene como coordenadas 3D los valores (0, 0, 0). Construya el punto C y el plano 3D que pasa por los 3 puntos.

Para identificar una serie de puntos de la ruta, se sugiere el siguiente algoritmo

  1. determinamos un número de pasos, por ejemplo 20 (si pone muchos pasos, los puntos de la ruta estarán muy cerca; si pone pocos pasos, estarán más separados)
  2. conocido el número de pasos, determinamos el ángulo horizontal de un paso que llamaremos delta (vea indicaciones de cómo hacerlo más abajo)
  3. hacemos un bucle para dar el número de pasos determinado; en cada pasada por el bucle determinamos un punto M para marcar en la ventana
    1. determinamos la longitud del meridiano del punto M: simplemente es la longitud del punto A más tantas veces delta como pasos hemos dado
    2. determianmos el plano 3D que pasa por los puntos
      • punto C: centro de la Tierra
      • punto M
      • punto M', que es un punto con la misma longitud que M y una latitud igual a la se M menos PI/2
      lo que tenemos es un plano a lo largo del meridiano de M
    3. determinamos el vector intersección entre los dos planos anteriores
      Vector3D interseccion = new Vector3D(plano1.getVectorNormal(), plano2.getVectorNormal());
      el punto de la superficie de la Tierra será un punto de la intersección de la esfera de radio 1 y una línea a lo largo del vector intersección anterior;
      para ser precisos, se corta a la superficie de la esfera en dos puntos (a uno y otro lado de la Tierra):
        double t = 1 / Math.sqrt(interseccion.getVx() * interseccion.getVx() +
                interseccion.getVy() * interseccion.getVy() +
                interseccion.getVz() * interseccion.getVz());
        // puntos de interseccion de la linea y la esfera
        Punto3D s1 = new Punto3D(
                interseccion.getVx() * t,
                interseccion.getVy() * t,
                interseccion.getVz() * t);
        Punto3D s2 = new Punto3D(
                -interseccion.getVx() * t,
                -interseccion.getVy() * t,
                -interseccion.getVz() * t);
        
    4. determinamos cual de esos 2 puntos queremos pintar; para ello calcula la distancia del último punto pintado a s1 y s2: nos quedaremos con el más cercano (el otro está en el otro lado de la Tierra)
    5. marque el mapa y refresque la ventana:
        mapa.punto(proyeccion.getProyeccion(s), Color.BLUE);
        ultimo = s;
        mapa.pinta();
        

private double seleccionaDelta(double longitudA, double longitudB)

Se recomienda hacer un método privado para calcular los incrementos de longitud entre pasos de ruta.

Normalmente debe devolver la diferencia de longitud entre A y B, dividida por el número de pasos. Dando un resultado positivo si viajamos hacia el Este, y negativo si viajamos hacia el Oeste.

Tenga en cuanta que debe elegir la ruta más corta, lo que a veces supone cruzar al meridiano internacional de cambio de fecha (opuesto al meridiano 0 de Greenwich). Para ello, tenga en cuenta que si la diferencia de longitud entre A y B es, en valor absoluto, mayor que PI, es que está haciendo una ruta demasiado larga y debe viajar en dirección opuesta.

Para que ponga a punto este pequeño método, la siguiente tabla facilita algunos puntos de prueba:

ABdelta
SantiagoAmsterdam0.066
AmsterdamSantiago-0.066
SantiagoAuckland-0.100
AucklandSantiago0.100

Se han usado ciudades de la clase Ciudades y PASOS = 20.