Пример #1
0
    def _closest_neighbour_in_grid4_directions(
            current_city_idx: int,
            city_positions: IntVector2DArray) -> List[int]:
        """
        Finds the closest city in each direction of the current city
        Parameters
        ----------
        current_city_idx: int
            Index of current city
        city_positions: IntVector2DArray
            Vector containing the coordinates of all cities

        Returns
        -------
        Returns indices of closest neighbour in every direction NESW
        """

        city_distances = []
        closest_neighbour: List[int] = [None for i in range(4)]

        # compute distance to all other cities
        for city_idx in range(len(city_positions)):
            city_distances.append(
                Vec2dOperations.get_manhattan_distance(
                    city_positions[current_city_idx],
                    city_positions[city_idx]))
        sorted_neighbours = np.argsort(city_distances)

        for neighbour in sorted_neighbours[1:]:  # do not include city itself
            direction_to_neighbour = direction_to_point(
                city_positions[current_city_idx], city_positions[neighbour])
            if closest_neighbour[direction_to_neighbour] is None:
                closest_neighbour[direction_to_neighbour] = neighbour

            # early return once all 4 directions have a closest neighbour
            if None not in closest_neighbour:
                return closest_neighbour

        return closest_neighbour
Пример #2
0
    def _generate_city_connection_points(
        city_positions: IntVector2DArray,
        city_radius: int,
        vector_field: IntVector2DArray,
        rails_between_cities: int,
        rails_in_city: int = 2,
        np_random: RandomState = None
    ) -> (List[List[List[IntVector2D]]], List[List[List[IntVector2D]]], List[
            np.ndarray], List[Grid4TransitionsEnum]):
        """
        Generate the city connection points. Internal connection points are used to generate the parallel paths
        within the city.
        External connection points are used to connect different cities together

        Parameters
        ----------
        city_positions: IntVector2DArray
            Vector that contains all the positions of the cities
        city_radius: int
            Radius of each city. Cities are squares with edge length 2 * city_radius + 1
        vector_field: IntVector2DArray
            Vectorfield of the size of the environment. It is used to generate preferred orienations for each cell.
            Each cell contains the prefered orientation of cells. If no prefered orientation is present it is set to -1
        rails_between_cities: int
            Number of rails that connect out from the city
        rails_in_city: int
            Number of rails within the city

        Returns
        -------
        inner_connection_points: List of List of length number of cities
            Contains all the inner connection points for each boarder of each city.
            [North_Points, East_Poinst, South_Points, West_Points]
        outer_connection_points: List of List of length number of cities
            Contains all the outer connection points for each boarder of the city.
            [North_Points, East_Poinst, South_Points, West_Points]
        city_orientations: List of length number of cities
            Contains all the orientations of cities. This is then used to orient agents according to the rails
        city_cells: List
            List containing the coordinates of all the cells that belong to a city. This is used by other algorithms
            to avoid drawing inter-city-rails through cities.
        """
        inner_connection_points: List[List[List[IntVector2D]]] = []
        outer_connection_points: List[List[List[IntVector2D]]] = []
        city_orientations: List[Grid4TransitionsEnum] = []
        city_cells: IntVector2DArray = []

        for city_position in city_positions:

            # Chose the directions where close cities are situated
            neighb_dist = []
            for neighbour_city in city_positions:
                neighb_dist.append(
                    Vec2dOperations.get_manhattan_distance(
                        city_position, neighbour_city))
            closest_neighb_idx = argsort(neighb_dist)

            # Store the directions to these neighbours and orient city to face closest neighbour
            connection_sides_idx = []
            idx = 1
            if grid_mode:
                current_closest_direction = np_random.randint(4)
            else:
                current_closest_direction = direction_to_point(
                    city_position, city_positions[closest_neighb_idx[idx]])
            connection_sides_idx.append(current_closest_direction)
            connection_sides_idx.append((current_closest_direction + 2) % 4)
            city_orientations.append(current_closest_direction)
            city_cells.extend(
                _get_cells_in_city(city_position, city_radius,
                                   city_orientations[-1], vector_field))
            # set the number of tracks within a city, at least 2 tracks per city
            connections_per_direction = np.zeros(4, dtype=int)
            nr_of_connection_points = np_random.randint(2, rails_in_city + 1)
            for idx in connection_sides_idx:
                connections_per_direction[idx] = nr_of_connection_points
            connection_points_coordinates_inner: List[List[IntVector2D]] = [
                [] for i in range(4)
            ]
            connection_points_coordinates_outer: List[List[IntVector2D]] = [
                [] for i in range(4)
            ]
            number_of_out_rails = np_random.randint(
                1,
                min(rails_between_cities, nr_of_connection_points) + 1)
            start_idx = int(
                (nr_of_connection_points - number_of_out_rails) / 2)
            for direction in range(4):
                connection_slots = np.arange(
                    nr_of_connection_points) - start_idx
                # Offset the rails away from the center of the city
                offset_distances = np.arange(nr_of_connection_points) - int(
                    nr_of_connection_points / 2)
                # The clipping helps ofsetting one side more than the other to avoid switches at same locations
                # The magic number plus one is added such that all points have at least one offset
                inner_point_offset = np.abs(offset_distances) + np.clip(
                    offset_distances, 0, 1) + 1
                for connection_idx in range(
                        connections_per_direction[direction]):
                    if direction == 0:
                        tmp_coordinates = (city_position[0] - city_radius +
                                           inner_point_offset[connection_idx],
                                           city_position[1] +
                                           connection_slots[connection_idx])
                        out_tmp_coordinates = (
                            city_position[0] - city_radius, city_position[1] +
                            connection_slots[connection_idx])
                    if direction == 1:
                        tmp_coordinates = (city_position[0] +
                                           connection_slots[connection_idx],
                                           city_position[1] + city_radius -
                                           inner_point_offset[connection_idx])
                        out_tmp_coordinates = (
                            city_position[0] +
                            connection_slots[connection_idx],
                            city_position[1] + city_radius)
                    if direction == 2:
                        tmp_coordinates = (city_position[0] + city_radius -
                                           inner_point_offset[connection_idx],
                                           city_position[1] +
                                           connection_slots[connection_idx])
                        out_tmp_coordinates = (
                            city_position[0] + city_radius, city_position[1] +
                            connection_slots[connection_idx])
                    if direction == 3:
                        tmp_coordinates = (city_position[0] +
                                           connection_slots[connection_idx],
                                           city_position[1] - city_radius +
                                           inner_point_offset[connection_idx])
                        out_tmp_coordinates = (
                            city_position[0] +
                            connection_slots[connection_idx],
                            city_position[1] - city_radius)
                    connection_points_coordinates_inner[direction].append(
                        tmp_coordinates)
                    if connection_idx in range(start_idx, start_idx +
                                               number_of_out_rails):
                        connection_points_coordinates_outer[direction].append(
                            out_tmp_coordinates)

            inner_connection_points.append(connection_points_coordinates_inner)
            outer_connection_points.append(connection_points_coordinates_outer)
        return inner_connection_points, outer_connection_points, city_orientations, city_cells
Пример #3
0
    def _connect_cities(
            city_positions: IntVector2DArray,
            connection_points: List[List[List[IntVector2D]]],
            city_cells: IntVector2DArray, rail_trans: RailEnvTransitions,
            grid_map: RailEnvTransitions) -> List[IntVector2DArray]:
        """
        Connects cities together through rails. Each city connects from its outgoing connection points to the closest
        cities. This guarantees that all connection points are used.

        Parameters
        ----------
        city_positions: IntVector2DArray
            All coordinates of the cities
        connection_points: List[List[List[IntVector2D]]]
            List of coordinates of all outer connection points
        city_cells: IntVector2DArray
            Coordinates of all the cells contained in any city. This is used to avoid drawing rails through existing
            cities.
        rail_trans: RailEnvTransitions
            Railway transition objects
        grid_map: RailEnvTransitions
            The grid map containing the rails. Used to draw new rails

        Returns
        -------
        Returns a list of all the cells (Coordinates) that belong to a rail path. This can be used to access railway
        cells later.
        """
        all_paths: List[IntVector2DArray] = []
        connect_cities = []
        connected_points = []

        grid4_directions = [
            Grid4TransitionsEnum.NORTH, Grid4TransitionsEnum.EAST,
            Grid4TransitionsEnum.SOUTH, Grid4TransitionsEnum.WEST
        ]

        for current_city_idx in np.arange(len(city_positions)):
            closest_neighbours = _closest_neighbour_in_grid4_directions(
                current_city_idx, city_positions)
            for out_direction in grid4_directions:

                neighbour_idx = get_closest_neighbour_for_direction(
                    closest_neighbours, out_direction)
                if set((current_city_idx, neighbour_idx)) in connect_cities:
                    continue
                for city_out_connection_point in connection_points[
                        current_city_idx][out_direction]:
                    city_out_connection_dir = out_direction
                    min_connection_dist = np.inf
                    for direction in grid4_directions:
                        current_points = connection_points[neighbour_idx][
                            direction]
                        for tmp_in_connection_point in current_points:
                            tmp_dist = Vec2dOperations.get_manhattan_distance(
                                city_out_connection_point,
                                tmp_in_connection_point)
                            if tmp_dist < min_connection_dist:
                                min_connection_dist = tmp_dist
                                neighbour_connection_point = tmp_in_connection_point
                                neighbour_connection_dir = direction
                    if set((*city_out_connection_point,
                            *neighbour_connection_point)) in connected_points:
                        continue
                    lines = _align_start_end(city_out_connection_point, neighbour_connection_point,\
                         city_out_connection_dir, neighbour_connection_dir, grid_map, rail_trans, city_cells)
                    if len(city_positions) == 2:
                        new_line = connect_points(lines, grid_map, rail_trans)
                    else:
                        new_line = connect_rail_in_grid_map(
                            grid_map,
                            city_out_connection_point,
                            neighbour_connection_point,
                            rail_trans,
                            flip_start_node_trans=False,
                            flip_end_node_trans=False,
                            respect_transition_validity=False,
                            avoid_rail=True,
                            forbidden_cells=city_cells)
                    all_paths.extend(new_line)
                    connect_cities.append(
                        set((current_city_idx, neighbour_idx)))
                    connected_points.append(
                        set((*city_out_connection_point,
                             *neighbour_connection_point)))

        return all_paths
Пример #4
0
def test_vec2d_manhattan_distance():
    node_a = (3, -7)
    node_0 = (0, 0)
    assert Vec2d.get_manhattan_distance(node_a, node_0) == 3 + 7