def test_midpoint(self):
     """El punto medio entre dos puntos debe ser correcto."""
     p1 = Point(0, 0)
     p2 = Point(10, 10)
     midpoint = p1.midpoint(p2)
     self.assertAlmostEqual(midpoint.lat, 5)
     self.assertAlmostEqual(midpoint.lon, 5)
Exemple #2
0
    def test_between_location_no_door_num(self):
        """Si no se especifica una altura en una una dirección de tipo
        'between', el resultado final debería tener una posición calculada
        a partir del promedio de la posición de las dos intersecciones."""
        resp_btwn = self.get_response({
            'direccion': 'Mar del Plata entre Diaz y 12 de Octubre',
            'departamento': 'Río Hondo'
        })
        point_btwn = Point.from_json_location(resp_btwn[0]['ubicacion'])

        resp_isct_1 = self.get_response({
            'direccion': 'Mar del Plata esquina Diaz',
            'departamento': 'Río Hondo'
        })
        point_isct_1 = Point.from_json_location(resp_isct_1[0]['ubicacion'])

        resp_isct_2 = self.get_response({
            'direccion': 'Mar del Plata esq. 12 de Octubre',
            'departamento': 'Río Hondo'
        })
        point_isct_2 = Point.from_json_location(resp_isct_2[0]['ubicacion'])

        midpoint = point_isct_1.midpoint(point_isct_2)

        distance = point_btwn.approximate_distance_meters(midpoint)
        self.assertAlmostEqual(distance, 0)
Exemple #3
0
    def test_intersection_location_no_door_num(self):
        """Si no se especifica una altura en una intersección, se debería
        utilizar la posición de la intersección como campo 'ubicacion'."""
        resp = self.get_response({
            'direccion': 'Maipú esquina Mendoza',
            'departamento': 'Rosario'
        })
        loc = resp[0]['ubicacion']

        point_isct = Point.from_json_location(loc)
        point_target = Point(-60.636548, -32.952020)
        error_m = point_isct.approximate_distance_meters(point_target)
        self.assertTrue(error_m < 15, error_m)
Exemple #4
0
    def test_position(self):
        """Cuando sea posible, se debería georreferenciar la dirección
        utilizando los datos de la calle y la altura."""
        resp = self.get_response({
            'direccion': 'Billinghurst nro 650',
            'departamento': 'Comuna 5'
        })

        point_resp = Point.from_json_location(resp[0]['ubicacion'])
        point_target = Point(-58.415413, -34.602319)
        error_m = point_resp.approximate_distance_meters(point_target)

        self.assertTrue(error_m < 15, error_m)
Exemple #5
0
    def test_intersection_position(self):
        """La posición de una intersección debería estar cerca a la posición de
        una altura sobre la primera calle cerca de la esquina."""
        resp_simple = self.get_response({
            'direccion': 'Dr. Adolfo Guemes al 600',
            'departamento': '66028'
        })

        resp_isct = self.get_response({
            'direccion': 'Dr. Adolfo esq. Rivadavia',
            'departamento': '66028'
        })

        point_simple = Point.from_json_location(resp_simple[0]['ubicacion'])
        point_isct = Point.from_json_location(resp_isct[0]['ubicacion'])

        self.assertLess(
            point_simple.approximate_distance_meters(point_isct),
            30  # metros
        )
Exemple #6
0
def run_location_queries(es, params_list, queries):
    """Dada una lista de queries de ubicación, construye las queries apropiadas
    a índices de departamentos y municipios, y las ejecuta utilizando
    Elasticsearch.

    Args:
        es (Elasticsearch): Conexión a Elasticsearch.
        params_list (list): Lista de ParametersParseResult.
        queries (list): Lista de queries de ubicación, generadas a partir de
            'params_list'.

    Returns:
        list: Resultados de ubicaciones (QueryResult).

    """
    # TODO:
    # Por problemas con los datos de origen, se optó por utilizar una
    # implementación simple para la la funcion 'run_location_queries'.
    # Cuando los datos de departamentos cubran todo el departamento nacional,
    # se podría modificar la función para que funcione de la siguiente forma:
    #
    # (Recordar que las provincias y departamentos cubren todo el territorio
    # nacional, pero no los municipios.)
    #
    # Por cada consulta, implementar un patrón similar al de address.py (con
    # iteradores de consultas), donde cada iterador ('búsqueda') realiza los
    # siguientes pasos:
    #
    # 1) Buscar la posición en el índice de departamentos.
    # 2) Si se obtuvo un departamento, buscar la posición nuevamente pero en el
    #    índice de municipios. Si no se obtuvo nada, cancelar la búsqueda.
    # 3) Componer el departamento, la provincia del departamento y el municipio
    #    en un QueryResult para completar la búsqueda.

    all_searches = []

    state_searches = []
    muni_searches = []
    dept_searches = []

    for query in queries:
        es_query = {
            'geo_shape_geoms': [Point.from_json_location(query).to_geojson()],
            'fields': [N.ID, N.NAME, N.SOURCE],
            'size': 1
        }

        # Buscar la posición en provincias, departamentos y municipios

        search = StatesSearch(es_query)
        all_searches.append(search)
        state_searches.append(search)

        search = DepartmentsSearch(es_query)
        all_searches.append(search)
        dept_searches.append(search)

        search = MunicipalitiesSearch(es_query)
        all_searches.append(search)
        muni_searches.append(search)

    # Ejecutar todas las búsquedas preparadas
    ElasticsearchSearch.run_searches(es, all_searches)

    locations = []
    iterator = zip(params_list, queries, state_searches, dept_searches,
                   muni_searches)

    for params, query, state_search, dept_search, muni_search in iterator:
        # Ya que la query de tipo location retorna una o cero entidades,
        # extraer la primera entidad de los resultados, o tomar None si
        # no hay resultados.
        state = state_search.result.hits[0] if state_search.result else None
        dept = dept_search.result.hits[0] if dept_search.result else None
        muni = muni_search.result.hits[0] if muni_search.result else None

        result = _build_location_result(params.received_values(), query, state,
                                        dept, muni)
        locations.append(result)

    return locations
Exemple #7
0
    def _process_intersections(self, intersections, street_1_ids, street_2_ids,
                               street_3_ids, street_1_points):
        """Procesa los resultados de las tres búsquedas realizadas en
        'planner_steps', para generar los 'BetweenEntry' que representan
        resultados potenciales a la búsqueda de direcciones.

        Args:
            intersections (list): Lista de intersecciones encontradas
                (documentos).
            street_1_ids (set): Conjunto de IDs de resultados para la calle 1.
            street_2_ids (set): Conjunto de IDs de resultados para la calle 2.
            street_3_ids (set): Conjunto de IDs de resultados para la calle 3.
            street_1_points (dict): Posición calculada utilizando la altura
                sobre cada resultado de la calle 1.

        Returns:
            list: Lista de 'BetweenEntry', cada una representando un resultado
                potencial.

        """
        between_entries = {}
        street_2_3_ids = street_2_ids | street_3_ids

        # Recorrer cada intersección. Las intersecciones pueden ser entre las
        # calles 1 y 2, o 1 y 3. Es necesario distinguir entre las dos
        # opciones.
        for intersection in intersections:
            id_a = intersection[N.STREET_A][N.ID]
            id_b = intersection[N.STREET_B][N.ID]

            # Comprobar que la intersección es entre las calles 1 y 2, o 1 y 3
            if id_a in street_1_ids and id_b in street_2_3_ids:
                street_1 = intersection[N.STREET_A]
                street_other = intersection[N.STREET_B]
            elif id_a in street_2_3_ids and id_b in street_1_ids:
                street_1 = intersection[N.STREET_B]
                street_other = intersection[N.STREET_A]
            else:
                raise RuntimeError(
                    'Unknown street IDs for intersection {} - {}'.format(id_a,
                                                                         id_b))

            # Si la calle 1 no está en between_entries, construir un nuevo
            # BetweenEntry.
            if street_1[N.ID] in between_entries:
                entry = between_entries[street_1[N.ID]]
            else:
                entry = AddressBtwnQueryPlanner.BetweenEntry(street_1)
                if self._numerical_door_number:
                    entry.street_1_point = street_1_points[street_1[N.ID]]

                between_entries[street_1[N.ID]] = entry

            point = Point.from_geojson_point(intersection[N.GEOM])

            # Tomar nuestro BetweenEntry asociado a la calle 1, y asignarle su
            # calle 2 o 3. Un BetweenEntry está completo cuando tiene las tres
            # calles asignadas (1, 2 y 3).
            if street_other[N.ID] in street_2_ids:
                entry.street_2 = street_other
                entry.street_2_point = point
            elif street_other[N.ID] in street_3_ids:
                entry.street_3 = street_other
                entry.street_3_point = point
            else:
                raise RuntimeError('Unknown street ID: {}'.format(
                    street_other[N.ID]))

        # Luego de iterar, algunos 'BetweenEntry' van a contener las tres
        # calles necesarias, y otros no.
        return between_entries.values()
Exemple #8
0
    def planner_steps(self):
        """Crea un iterador de ElasticsearchSearch, representando los pasos
        requeridos para completar la búsqueda de los datos la dirección.

        Pasos requeridos:
            1) Expandir búsqueda de localidad, si la hay.
            2) Búsqueda de la calle principal (calle 1).
            3) Búsqueda de la calle 2.
            4) Búsqueda de intersecciones entre las calles 1 y 2.

        Explicación: Primero, se buscan las calles 1 y 2, obteniendo todos los
        IDs de las mismas. Luego, se buscan intersecciones de calles que
        hagan referencia a los IDs obtenidos. Aunque es posible buscar
        intersecciones directamente por nombre, no se hace esto ya que sería
        difícil luego interpretar si la calle A de la intersección corresponde
        a la calle 1 y la B a la 2, o vice versa.

        Yields:
            ElasticsearchSearch: Búsqueda que debe ser ejecutada por el
                invocador de 'next()'.

        """
        if self._locality:
            found = yield from self._expand_locality_search()
            if not found:
                return

        # Buscar la primera calle, incluyendo la altura si está presente
        result = yield self._build_street_blocks_search(
            self._address_data.street_names[0],
            add_number=True,
            force_all=True
        )

        street_1_ids, street_1_points = self._read_street_blocks_1_results(
            result)

        if not street_1_ids:
            # Ninguno de los resultados pudo ser utilizado para
            # calcular la ubicación, o no se encontraron resultados.
            # Devolver 0 resultados de intersección.
            return

        result = yield self._build_street_blocks_search(
            self._address_data.street_names[1],
            force_all=True
        )

        # Resultados de la segunda calle
        street_2_ids = {hit[N.STREET][N.ID] for hit in result.hits}
        if not street_2_ids:
            return

        # Buscar intersecciones que tengan nuestras dos calles en cualquier
        # orden ("Calle 1 y Calle 2" o "Calle 2 y Calle 1"). Si tenemos altura,
        # comprobar que las intersecciones no estén a mas de X metros de cada
        # ubicación sobre la calle 1 que calculamos anteriormente.
        self._intersections_result = yield self._build_intersections_search(
            street_1_ids,
            street_2_ids,
            street_1_points.values(),
            constants.ISCT_DOOR_NUM_TOLERANCE_M
        )

        # Iterar sobre los resultados, fijándose si cada intersección tiene la
        # calle 1 del lado A o B. Si la calle 1 está del lado B, invertir la
        # intersección. Se requiere que los datos devueltos al usuario tengan
        # el mismo orden en el que fueron recibidos ("calle X y calle Y" no es
        # lo mismo que "calle Y y calle X").
        intersections = []
        for intersection in self._intersections_result.hits:
            id_a = intersection[N.STREET_A][N.ID]
            id_b = intersection[N.STREET_B][N.ID]

            if id_a in street_1_ids and id_b in street_2_ids:
                street_1 = intersection[N.STREET_A]
                street_2 = intersection[N.STREET_B]
            elif id_a in street_2_ids and id_b in street_1_ids:
                street_1 = intersection[N.STREET_B]
                street_2 = intersection[N.STREET_A]
            else:
                raise RuntimeError(
                    'Unknown street IDs for intersection {} - {}'.format(id_a,
                                                                         id_b))

            if street_1[N.ID] in street_1_points:
                # Como tenemos altura, usamos la posición sobre la calle 1 en
                # lugar de la posición de la intersección.
                point = street_1_points[street_1[N.ID]]
            else:
                point = Point.from_geojson_point(intersection[N.GEOM])

            intersections.append((street_1, street_2, point))

        self._intersection_hits = self._build_intersection_hits(intersections)
 def test_distance_0(self):
     """La distancia entre dos puntos iguales debería ser 0."""
     point = Point(0, 0)
     self.assertAlmostEqual(point.approximate_distance_meters(point), 0)
from service.geometry import Point
from . import GeorefMockTest

POINT_1 = Point(-58.381614, -34.603713)  # Obelisco
POINT_2 = Point(-58.373691, -34.608874)  # Cabildo
POINT_3 = Point(-58.373720, -34.592172)  # Torre Monumental - Retiro

DISTANCES = [(POINT_1, POINT_2, 920), (POINT_1, POINT_3, 1470),
             (POINT_2, POINT_3, 1855)]


class PointTest(GeorefMockTest):
    def test_distance_0(self):
        """La distancia entre dos puntos iguales debería ser 0."""
        point = Point(0, 0)
        self.assertAlmostEqual(point.approximate_distance_meters(point), 0)

    def test_distances(self):
        """La distancia entre dos puntos conocidos debe ser correcta."""
        for p1, p2, distance in DISTANCES:
            calculated = p1.approximate_distance_meters(p2)
            self.assertAlmostEqual(distance, calculated, delta=5)

    def test_midpoint(self):
        """El punto medio entre dos puntos debe ser correcto."""
        p1 = Point(0, 0)
        p2 = Point(10, 10)
        midpoint = p1.midpoint(p2)
        self.assertAlmostEqual(midpoint.lat, 5)
        self.assertAlmostEqual(midpoint.lon, 5)