Skip to content

Commit

Permalink
Add docs for MapDefinitionPool class and serialize_map function
Browse files Browse the repository at this point in the history
  • Loading branch information
ll7 committed Mar 6, 2024
1 parent 2147c49 commit 5ef97a4
Showing 1 changed file with 67 additions and 1 deletion.
68 changes: 67 additions & 1 deletion robot_sf/nav/map_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -274,68 +274,134 @@ def find_route(self, spawn_id: int, goal_id: int) -> Union[GlobalRoute, None]:

@dataclass
class MapDefinitionPool:
"""
A class to represent a pool of map definitions.
Attributes
----------
maps_folder : str
The directory where the map files are located.
map_defs : List[MapDefinition]
The list of map definitions.
Methods
-------
__post_init__():
Validates and initializes the map_defs attribute.
choose_random_map():
Returns a random map definition from the pool.
"""

maps_folder: str = os.path.join(os.path.dirname(os.path.dirname(__file__)), "maps")
map_defs: List[MapDefinition] = field(default_factory=list)

def __post_init__(self):
"""
Validates and initializes the map_defs attribute.
If map_defs is empty, it loads the map definitions from the files in the
maps_folder directory.
Raises a ValueError if the maps_folder directory does not exist or if
map_defs is still empty after loading.
"""

# If map_defs is empty, load the map definitions from the files
if not self.map_defs:
# Check if the maps_folder directory exists
if not os.path.exists(self.maps_folder) or not os.path.isdir(self.maps_folder):
raise ValueError(f"Map directory '{self.maps_folder}' does not exist!")

# Function to load a JSON file
def load_json(path: str) -> dict:
with open(path, 'r', encoding='utf-8') as file:
return json.load(file)

# Get the list of map files
map_files = [os.path.join(self.maps_folder, f) for f in os.listdir(self.maps_folder)]

# Load the map definitions from the files
self.map_defs = [serialize_map(load_json(f)) for f in map_files]

# If map_defs is still empty, raise an error
if not self.map_defs:
raise ValueError('Map pool is empty! Please specify some maps!')

def choose_random_map(self) -> MapDefinition:
"""
Returns a random map definition from the pool.
Returns
-------
MapDefinition
A random map definition.
"""

return random.choice(self.map_defs)


def serialize_map(map_structure: dict) -> MapDefinition:
"""
Converts a map structure dictionary into a MapDefinition object.
Parameters
----------
map_structure : dict
The map structure dictionary.
Returns
-------
MapDefinition
The MapDefinition object.
"""

# Extract the x and y margins and calculate the width and height
(min_x, max_x), (min_y, max_y) = map_structure['x_margin'], map_structure['y_margin']
width, height = max_x - min_x, max_y - min_y

# Function to normalize a position
def norm_pos(pos: Vec2D) -> Vec2D:
return (pos[0] - min_x, pos[1] - min_y)

# Normalize the obstacles
obstacles = [Obstacle([norm_pos(p) for p in vertices])
for vertices in map_structure['obstacles']]

# Function to normalize a zone
def norm_zone(rect: Rect) -> Rect:
return (norm_pos(rect[0]), norm_pos(rect[1]), norm_pos(rect[2]))

# Normalize the zones
robot_goal_zones = [norm_zone(z) for z in map_structure['robot_goal_zones']]
robot_spawn_zones = [norm_zone(z) for z in map_structure['robot_spawn_zones']]
ped_goal_zones = [norm_zone(z) for z in map_structure['ped_goal_zones']]
ped_spawn_zones = [norm_zone(z) for z in map_structure['ped_spawn_zones']]
ped_crowded_zones = [norm_zone(z) for z in map_structure['ped_crowded_zones']]

# Normalize the routes
robot_routes = [GlobalRoute(o['spawn_id'], o['goal_id'], [norm_pos(p) for p in o['waypoints']],
robot_spawn_zones[o['spawn_id']], robot_goal_zones[o['goal_id']])
for o in map_structure['robot_routes']]
ped_routes = [GlobalRoute(o['spawn_id'], o['goal_id'], [norm_pos(p) for p in o['waypoints']],
ped_spawn_zones[o['spawn_id']], ped_goal_zones[o['goal_id']])
for o in map_structure['ped_routes']]

# Function to reverse a route
def reverse_route(route: GlobalRoute) -> GlobalRoute:
return GlobalRoute(
route.goal_id, route.spawn_id, list(reversed(route.waypoints)),
route.goal_zone, route.spawn_zone)

# info: this works because spawn and goal zones are the same
# Reverse the robot routes and add them to the list of routes
rev_robot_routes = [reverse_route(r) for r in robot_routes]
robot_routes = robot_routes + rev_robot_routes

# Define the map bounds
map_bounds = [
(0, width, 0, 0), # bottom
(0, width, height, height), # top
(0, 0, 0, height), # left
(width, width, 0, height)] # right

# Return the MapDefinition object
return MapDefinition(
width, height, obstacles, robot_spawn_zones,
ped_spawn_zones, robot_goal_zones, map_bounds, robot_routes,
Expand Down

0 comments on commit 5ef97a4

Please sign in to comment.