Skip to content

Floor Dataclass

Module for Floor class.

Floor dataclass

Representation of a floor in a building.

Parameters:

Name Type Description Default
floor_number int

The number of the floor within the building.

required
rooms list[Room]

The list of rooms on the floor.

required
Source code in src/amr_hub_abm/spatial/floor.py
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
@dataclass
class Floor:
    """
    Representation of a floor in a building.

    Parameters
    ----------
    floor_number : int
        The number of the floor within the building.
    rooms : list[Room]
        The list of rooms on the floor.

    """

    floor_number: int
    rooms: list[Room]
    pseudo_rooms: list[Room] = field(init=False, default_factory=list)

    def __post_init__(self) -> None:
        """
        Post-initialization to validate floor attributes.

        Raises
        ------
        InvalidRoomError
            If there are duplicate room IDs on the floor.

        """
        room_ids = [room.room_id for room in self.rooms]
        if len(room_ids) != len(set(room_ids)):
            msg = f"Duplicate room IDs found on floor {self.floor_number}."
            raise InvalidRoomError(msg)

    @property
    def room_ids(self) -> list[int]:
        """Get a list of room IDs on the floor."""
        return sorted([room.room_id for room in self.rooms])

    @property
    def room_names(self) -> list[str]:
        """Get a list of room names on the floor."""
        id_name_map = {room.room_id: room.name for room in self.rooms}
        return [id_name_map[room_id] for room_id in self.room_ids]

    @property
    def edge_set(self) -> set[tuple[int, int]]:
        """Get a list of all wall edges on the floor."""
        edges = set()
        for room in self.rooms:
            for door in room.doors:
                edges.add(door.connecting_rooms)
                edges.add((door.connecting_rooms[1], door.connecting_rooms[0]))
        return edges

    @property
    def adjacency_matrix(self) -> np.ndarray:
        """Get the adjacency matrix representing room connections on the floor."""
        n = len(self.rooms)
        room_id_to_index = {
            room_id: index for index, room_id in enumerate(self.room_ids)
        }
        adjacency_matrix = np.zeros((n, n), dtype=int)

        for edge in self.edge_set:
            room1, room2 = edge
            index1 = room_id_to_index[room1]
            index2 = room_id_to_index[room2]
            adjacency_matrix[index1, index2] = 1

        return adjacency_matrix

    def plot(
        self,
        ax: Axes,
        agents: list[Agent] | None = None,
        *,
        trajectory: bool = False,
        **kwargs: dict,
    ) -> None:
        """
        Plot the floor layout including rooms and doors.

        Parameters
        ----------
        ax : Axes
            The matplotlib Axes object to plot on.
        agents : list[Agent] | None, optional
            A list of agents to plot on the floor. Defaults to None.
        trajectory : bool, optional
            Whether to plot the trajectories of the agents. Defaults to False.

        """
        if agents is None:
            agents = []

        for room in self.rooms:
            plot_room(room, ax, **kwargs)
            plot_agents_in_room(room, ax, agents, trajectory=trajectory)

    def add_pseudo_rooms(self) -> None:
        """Add pseudo-rooms to the floor."""
        for existing_room in self.rooms:
            if not existing_room.walls:
                pseudo_room = Floor.create_spatial_room_from_pseudo_room(existing_room)
                self.pseudo_rooms.append(pseudo_room)

    @staticmethod
    def create_spatial_room_from_pseudo_room(room: Room) -> Room:
        """
        Create a spatial room from a pseudo-room based on area.

        Parameters
        ----------
        room : Room
            A pseudo-room with defined area and doors but no walls.

        Returns
        -------
        Room
            A spatial room with walls and doors positioned based on the area of the
            pseudo-room.


        Raises
        ------
        InvalidRoomError
            If the pseudo-room does not have a valid positive area.

        Notes
        -----
        This method generates a rectangular room layout based on the area of the
        pseudo-room. The doors are positioned along one side of the rectangle. This
        function is not complete. Currently it only creates single simple
        rectangular rooms. Due to possibly complex topology, rectangular rooms may
        not always be a valid representation. Currently, no topology connections
        are considered. This function will be improved in future versions.

        """
        if not room.area or room.area <= 0:
            msg = "Pseudo-room must have a valid positive area."
            raise InvalidRoomError(msg)
        length = max(room.area**0.5, 2 * len(room.doors) + 2)
        width = room.area / length

        pseudo_doors = room.doors.copy()
        for count, door in enumerate(pseudo_doors):
            object.__setattr__(door, "start", (2 * count + 1, width))
            object.__setattr__(door, "end", (2 * count + 2, width))

        pseudo_walls = [
            Wall((0, width), (0, 0)),
            Wall((0, 0), (length, 0)),
            Wall((length, 0), (length, width)),
        ]

        doorside_walls = [
            Wall((2 * count, width), (2 * count + 1, width))
            for count in range(len(pseudo_doors))
        ]
        doorside_walls.append(Wall((len(pseudo_doors) * 2, width), (length, width)))
        pseudo_walls.extend(doorside_walls)

        return Room(
            room_id=room.room_id,
            name=room.name,
            building=room.building,
            floor=room.floor,
            walls=pseudo_walls,
            doors=pseudo_doors,
            contents=room.contents,
            rng_generator=np.random.default_rng(),
        )

    def find_room_by_location(self, location: tuple[float, float]) -> Room | None:
        """
        Find the room that contains the given location.

        Parameters
        ----------
        location : tuple[float, float]
            The (x, y) coordinates of the location to search for.

        Returns
        -------
        Room | None
            The room that contains the location, or None if no such room exists.

        """
        for room in self.rooms:
            if room.walls and room.contains_point(location):
                return room
        return None

adjacency_matrix property

Get the adjacency matrix representing room connections on the floor.

edge_set property

Get a list of all wall edges on the floor.

room_ids property

Get a list of room IDs on the floor.

room_names property

Get a list of room names on the floor.

__post_init__()

Post-initialization to validate floor attributes.

Raises:

Type Description
InvalidRoomError

If there are duplicate room IDs on the floor.

Source code in src/amr_hub_abm/spatial/floor.py
40
41
42
43
44
45
46
47
48
49
50
51
52
53
def __post_init__(self) -> None:
    """
    Post-initialization to validate floor attributes.

    Raises
    ------
    InvalidRoomError
        If there are duplicate room IDs on the floor.

    """
    room_ids = [room.room_id for room in self.rooms]
    if len(room_ids) != len(set(room_ids)):
        msg = f"Duplicate room IDs found on floor {self.floor_number}."
        raise InvalidRoomError(msg)

add_pseudo_rooms()

Add pseudo-rooms to the floor.

Source code in src/amr_hub_abm/spatial/floor.py
121
122
123
124
125
126
def add_pseudo_rooms(self) -> None:
    """Add pseudo-rooms to the floor."""
    for existing_room in self.rooms:
        if not existing_room.walls:
            pseudo_room = Floor.create_spatial_room_from_pseudo_room(existing_room)
            self.pseudo_rooms.append(pseudo_room)

create_spatial_room_from_pseudo_room(room) staticmethod

Create a spatial room from a pseudo-room based on area.

Parameters:

Name Type Description Default
room Room

A pseudo-room with defined area and doors but no walls.

required

Returns:

Type Description
Room

A spatial room with walls and doors positioned based on the area of the pseudo-room.

Raises:

Type Description
InvalidRoomError

If the pseudo-room does not have a valid positive area.

Notes

This method generates a rectangular room layout based on the area of the pseudo-room. The doors are positioned along one side of the rectangle. This function is not complete. Currently it only creates single simple rectangular rooms. Due to possibly complex topology, rectangular rooms may not always be a valid representation. Currently, no topology connections are considered. This function will be improved in future versions.

Source code in src/amr_hub_abm/spatial/floor.py
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
@staticmethod
def create_spatial_room_from_pseudo_room(room: Room) -> Room:
    """
    Create a spatial room from a pseudo-room based on area.

    Parameters
    ----------
    room : Room
        A pseudo-room with defined area and doors but no walls.

    Returns
    -------
    Room
        A spatial room with walls and doors positioned based on the area of the
        pseudo-room.


    Raises
    ------
    InvalidRoomError
        If the pseudo-room does not have a valid positive area.

    Notes
    -----
    This method generates a rectangular room layout based on the area of the
    pseudo-room. The doors are positioned along one side of the rectangle. This
    function is not complete. Currently it only creates single simple
    rectangular rooms. Due to possibly complex topology, rectangular rooms may
    not always be a valid representation. Currently, no topology connections
    are considered. This function will be improved in future versions.

    """
    if not room.area or room.area <= 0:
        msg = "Pseudo-room must have a valid positive area."
        raise InvalidRoomError(msg)
    length = max(room.area**0.5, 2 * len(room.doors) + 2)
    width = room.area / length

    pseudo_doors = room.doors.copy()
    for count, door in enumerate(pseudo_doors):
        object.__setattr__(door, "start", (2 * count + 1, width))
        object.__setattr__(door, "end", (2 * count + 2, width))

    pseudo_walls = [
        Wall((0, width), (0, 0)),
        Wall((0, 0), (length, 0)),
        Wall((length, 0), (length, width)),
    ]

    doorside_walls = [
        Wall((2 * count, width), (2 * count + 1, width))
        for count in range(len(pseudo_doors))
    ]
    doorside_walls.append(Wall((len(pseudo_doors) * 2, width), (length, width)))
    pseudo_walls.extend(doorside_walls)

    return Room(
        room_id=room.room_id,
        name=room.name,
        building=room.building,
        floor=room.floor,
        walls=pseudo_walls,
        doors=pseudo_doors,
        contents=room.contents,
        rng_generator=np.random.default_rng(),
    )

find_room_by_location(location)

Find the room that contains the given location.

Parameters:

Name Type Description Default
location tuple[float, float]

The (x, y) coordinates of the location to search for.

required

Returns:

Type Description
Room | None

The room that contains the location, or None if no such room exists.

Source code in src/amr_hub_abm/spatial/floor.py
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
def find_room_by_location(self, location: tuple[float, float]) -> Room | None:
    """
    Find the room that contains the given location.

    Parameters
    ----------
    location : tuple[float, float]
        The (x, y) coordinates of the location to search for.

    Returns
    -------
    Room | None
        The room that contains the location, or None if no such room exists.

    """
    for room in self.rooms:
        if room.walls and room.contains_point(location):
            return room
    return None

plot(ax, agents=None, *, trajectory=False, **kwargs)

Plot the floor layout including rooms and doors.

Parameters:

Name Type Description Default
ax Axes

The matplotlib Axes object to plot on.

required
agents list[Agent] | None

A list of agents to plot on the floor. Defaults to None.

None
trajectory bool

Whether to plot the trajectories of the agents. Defaults to False.

False
Source code in src/amr_hub_abm/spatial/floor.py
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def plot(
    self,
    ax: Axes,
    agents: list[Agent] | None = None,
    *,
    trajectory: bool = False,
    **kwargs: dict,
) -> None:
    """
    Plot the floor layout including rooms and doors.

    Parameters
    ----------
    ax : Axes
        The matplotlib Axes object to plot on.
    agents : list[Agent] | None, optional
        A list of agents to plot on the floor. Defaults to None.
    trajectory : bool, optional
        Whether to plot the trajectories of the agents. Defaults to False.

    """
    if agents is None:
        agents = []

    for room in self.rooms:
        plot_room(room, ax, **kwargs)
        plot_agents_in_room(room, ax, agents, trajectory=trajectory)