def test_floor_encoding_and_decoding(self): p2 = Polygon.from_absolute_coordinates([(12, 0), (22, 0), (22, 10), (12, 10)]) r2 = Room(p2) f1 = Floor("Building cool name", "Floor cool name", [self.room1, r2]) f_dump = json.dumps(f1.to_serializable()) f_load = json.loads(f_dump) f2 = Floor.from_serializable(f_load) self.assertEqual(f1, f2)
def test_floor_encoding_and_decoding(self): p2 = Polygon.from_absolute_coordinates([(12,0),(22,0),(22,10),(12,10)]) r2 = Room(p2) f1 = Floor("Building cool name","Floor cool name", [self.room1,r2]) f_dump = json.dumps(f1.to_serializable()) f_load = json.loads(f_dump) f2 = Floor.from_serializable(f_load) self.assertEqual(f1,f2)
def test_floor_equal(self): p2 = Polygon.from_absolute_coordinates([(12,0),(22,0),(22,10),(12,10)]) r2 = Room(p2) floor = Floor("Building 1", "Floor1", [self.room1,r2]) floor2 = Floor("Building 1", "Floor1",[self.room1,r2]) self.assertEqual(floor,floor2) floor3 = Floor("Building 1", "Floor", [self.room1,r2]) self.assertNotEqual(floor, floor3) floor3 = Floor("Building", "Floor1", [self.room1,r2]) self.assertNotEqual(floor, floor3) floor3 = Floor("Building 1", "Floor1", [self.room1]) self.assertNotEqual(floor, floor3)
def test_calculate_scale_amount_and_trasform(self): polygon = Polygon.from_absolute_coordinates( [(0,0),(1024,0),(1024,1024),(2048,1024),(2048,2048),(0,2048)] ) room = Room(polygon) f = Floor("Pippo", "disneyland", [room]) self.assertEqual(f.max_output_size / 2048, f.calculate_scale_amount()) f.normalize() for point in f.rooms[0].polygon.points: self.assertTrue(point.x <= f.max_output_size) self.assertTrue(point.y <= f.max_output_size)
def test_floor_to_serializable(self): p2 = Polygon.from_absolute_coordinates([(12,0),(22,0),(22,10),(12,10)]) r2 = Room(p2) f = Floor("Building cool name","Floor cool name", [self.room1, r2]) self.assertEqual( f.to_serializable() , { "walls" : [], "windows" : [], "b_id" : f.b_id, "f_id" : f.f_id, "rooms" : [self.room1.to_serializable(), r2.to_serializable()] })
def test_associate_text_to_rooms(self): p2 = Polygon.from_absolute_coordinates([(12,0),(22,0),(22,10),(12,10)]) r2 = Room(p2) t1 = Text("Text room 1", Point(5,5)) t2 = Text("Text room 2", Point(15,8)) t3 = Text("Text outside",Point(11,5)) floor = Floor("Building 1", "Floor1",[self.room1, r2]) floor.associate_room_texts([t1,t2,t3]) self.assertEqual( len(self.room1.texts), 1 ) self.assertTrue( len(r2.texts) == 1 ) self.assertTrue( t1 in self.room1.texts ) self.assertTrue( t2 in r2.texts )
def test_floor_to_serializable(self): p2 = Polygon.from_absolute_coordinates([(12, 0), (22, 0), (22, 10), (12, 10)]) r2 = Room(p2) f = Floor("Building cool name", "Floor cool name", [self.room1, r2]) self.assertEqual( f.to_serializable(), { "walls": [], "windows": [], "b_id": f.b_id, "f_id": f.f_id, "rooms": [self.room1.to_serializable(), r2.to_serializable()], }, )
def __init__(self, filename): """ Try reading a dxf file pointed by filename and save the floor in the respective attribute. Arguments: - filename: string rapresents the filename with path of the dxf file. Raise: - FileUpdateException in case of impossibility to identify building, floor or rooms. Initialise a DxfReader, try to read a dxf file and associate the texts found to the respective room. Call the FloorInference class to find a standard floor id, and save the results of the operations in the floor attribute as a Floor object. """ self._filename = filename self._basename = os.path.basename(filename) self.floor = None self._read_dxf(self._filename) self._extract_entities() b_id = self._get_b_id(self._basename) if not b_id: raise FileUpdateException( "It was not possible to identify the building associated to the DXF file" ) f_id = FloorInference.get_identifier(self._basename, self._grabber) if not f_id: raise FileUpdateException( "It was not possible to identify the floor associated to the DXF file" ) self.floor = Floor(b_id, f_id, self._rooms, self._wall_lines, self._window_lines) if self.floor.n_rooms == 0: raise FileUpdateException("The floor read has no rooms: " + self._filename) self.floor.associate_room_texts(self._texts) self.floor.normalize() self.floor.discard_tiny_lines()
def test_associate_text_to_rooms2(self): p2 = Polygon.from_absolute_coordinates([(6,0),(12,0),(12,10),(11,10),(11,4),(6,4)]) r2 = Room(p2) t1_1 = Text("Text room 1",Point(2,2)) t1_2 = Text("Text room 1",Point(8,8)) t2_1 = Text("Text room 2",Point(7,2)) t2_2 = Text("Text room 2",Point(11,8)) t_none = Text("Text none",Point(5,12)) floor = Floor("Building 1", "Floor1",[ self.room1,r2]) floor.associate_room_texts([t1_1,t1_2,t2_1,t2_2,t_none]) self.assertTrue( len(self.room1.texts) == 2 ) self.assertTrue( len(r2.texts) == 2 ) self.assertTrue( t1_1 in self.room1.texts ) self.assertTrue( t1_2 in self.room1.texts ) self.assertTrue( t2_1 in r2.texts ) self.assertTrue( t2_2 in r2.texts ) self.assertTrue( t1_1 in self.room1.texts ) self.assertTrue( t_none not in self.room1.texts )
def __init__(self, filename): """ Try reading a dxf file pointed by filename and save the floor in the respective attribute. Arguments: - filename: string rapresents the filename with path of the dxf file. Raise: - FileUpdateException in case of impossibility to identify building, floor or rooms. Initialise a DxfReader, try to read a dxf file and associate the texts found to the respective room. Call the FloorInference class to find a standard floor id, and save the results of the operations in the floor attribute as a Floor object. """ self._filename = filename; self._basename = os.path.basename(filename) self.floor = None self._read_dxf(self._filename) self._extract_entities() b_id = self._get_b_id(self._basename) if not b_id: raise FileUpdateException("It was not possible to identify the building associated to the DXF file") f_id = FloorInference.get_identifier( self._basename, self._grabber ) if not f_id: raise FileUpdateException("It was not possible to identify the floor associated to the DXF file") self.floor = Floor(b_id, f_id, self._rooms, self._wall_lines, self._window_lines) if self.floor.n_rooms == 0: raise FileUpdateException("The floor read has no rooms: " + self._filename) self.floor.associate_room_texts(self._texts) self.floor.normalize() self.floor.discard_tiny_lines()
class DxfReader(): """ Class to read the dxf file with the dxfgrabber. """ # Todo: extract to external config file valid_poly_layers = ["RM$"] valid_text_layers = ["NLOCALI", "RM$TXT"] valid_wall_layers = ["MURI", "GROS$"] def __init__(self, filename): """ Try reading a dxf file pointed by filename and save the floor in the respective attribute. Arguments: - filename: string rapresents the filename with path of the dxf file. Raise: - FileUpdateException in case of impossibility to identify building, floor or rooms. Initialise a DxfReader, try to read a dxf file and associate the texts found to the respective room. Call the FloorInference class to find a standard floor id, and save the results of the operations in the floor attribute as a Floor object. """ self._filename = filename; self._basename = os.path.basename(filename) self.floor = None self._read_dxf(self._filename) self._extract_entities() b_id = self._get_b_id(self._basename) if not b_id: raise FileUpdateException("It was not possible to identify the building associated to the DXF file") f_id = FloorInference.get_identifier( self._basename, self._grabber ) if not f_id: raise FileUpdateException("It was not possible to identify the floor associated to the DXF file") self.floor = Floor(b_id, f_id, self._rooms, self._wall_lines, self._window_lines) if self.floor.n_rooms == 0: raise FileUpdateException("The floor read has no rooms: " + self._filename) self.floor.associate_room_texts(self._texts) self.floor.normalize() self.floor.discard_tiny_lines() def _extract_entities(self): self._rooms = [] self._texts = [] self._wall_lines = [] self._window_lines = [] for ent in self._grabber.entities: if self._is_valid_room(ent): points = [(p[0], -p[1]) for p in ent.points] polygon = Polygon.from_absolute_coordinates(points) polygon.ensure_is_closed(tollerance = 0.8) polygon.simplify_close_points(tollerance = 0.8) if polygon.is_self_crossing(): Logger.warning("Self crossing room is not valid: "+str(polygon)) continue self._rooms.append( Room( polygon ) ) elif self._is_valid_text(ent): self._texts.append( Text( ent.plain_text().strip(), Point(ent.insert[0], -ent.insert[1]) ) ) elif self._is_valid_wall_line(ent): start = Point(ent.start[0], -ent.start[1]) end = Point(ent.end[0], -ent.end[1]) line = Segment(start, end) self._wall_lines.append( line ) elif self._is_valid_wall_polyline(ent): points = [(p[0], -p[1]) for p in ent.points] polygon = Polygon.from_relative_coordinates((0,0), points) polygon.ensure_is_closed(tollerance = 1) polygon.simplify_close_points(tollerance = 1) self._wall_lines.extend( polygon.as_segment_list() ) elif self._is_valid_window_line(ent): start = Point(ent.start[0], -ent.start[1]) end = Point(ent.end[0], -ent.end[1]) line = Segment(start, end) self._window_lines.append( line ) def _is_valid_room(self, ent): """ Method to validate a room entity. Arguments: - ent: an entity read from the dxf file. Returns: True or False. If ent is a valid polyline in the polylines layer returns True else returns False. """ return type(ent) in [LWPolyline, Polyline] and ent.layer in self.valid_poly_layers def _is_valid_text(self, ent): """ Method to validate a text entity. Arguments: - ent: an entity read from the dxf file. Returns: True or False. If ent is a text in the text layers returns True else return False. """ if type(ent) not in [MText, dxfgrabber.entities.Text]: return False if ent.layer not in self.valid_text_layers: return False txt = ent.plain_text().strip() m1 = re.match("^[a-zA-Z\d]*\d+[a-zA-Z\d]*$", txt) m2 = re.match("^([a-zA-Z]{2,}\s*)+$", txt) if not(m1 or m2): return False return True def _is_valid_wall_line(self, ent): return ent.layer.upper() in self.valid_wall_layers and type(ent) is dxfgrabber.entities.Line def _is_valid_wall_polyline(self, ent): return ent.layer.upper() in self.valid_wall_layers and type(ent) in [LWPolyline, Polyline] def _is_valid_window_line(self, ent): return ent.layer.upper() == "FINESTRE" and type(ent) is dxfgrabber.entities.Line def _get_b_id(self, basename): """ Method to extract the building id from the filename. Arguments: - basename: a string representing the name of the dxf file. Returns: a string. """ b_id = None rm = re.match("(\d+)_(-?[a-zA-Z0-9]+(\.\d+)?).*\.dxf", basename, re.I) if rm: b_id = rm.group(1) else: rm = re.match("(\d+)\.dxf", basename, re.I) if rm: b_id = rm.group(1) return b_id def _read_dxf(self, filename): """ Read the dxf file with the dxf grabber. Arguments: - filename: representing the path and the name of the dxf file. Returns: None. Raise: PermissionError, IsADirectoryError, FileNotFoundError or generic Exception in case of reading failure. Try to read the dxf file with the grabber. """ try: self._grabber = dxfgrabber.readfile(filename) except PermissionError: Logger.error("Permission error: cannot read file " + filename) raise except IsADirectoryError: Logger.error("File is a directory error: cannot read file " + filename) raise except FileNotFoundError: Logger.error("File not found error: cannot read file " + filename) raise except Exception as e: Logger.error("Unknown exception on DXF file reading: " + str(e)) raise
def setUp(self): self.f = Floor("Pippo", "disneyland") self.polygon1 = Polygon.from_absolute_coordinates([(0,0),(5,0),(5,5),(10,5),(10,10),(0,10)]) self.room1 = Room(self.polygon1)
class FloorTest(unittest.TestCase): def setUp(self): self.f = Floor("Pippo", "disneyland") self.polygon1 = Polygon.from_absolute_coordinates([(0,0),(5,0),(5,5),(10,5),(10,10),(0,10)]) self.room1 = Room(self.polygon1) def test_floor_creation(self): self.assertEqual(self.f.b_id, "Pippo") self.assertEqual(self.f.f_id, "disneyland") self.assertEqual(self.f.rooms, []) def test_floor_room_addition(self): self.f.add_room(self.room1) self.assertEqual(self.f.rooms[0], self.room1) p2 = Polygon.from_absolute_coordinates( [(4, 4), (22, 14), (53, 53)] ) r2 = Room(p2) self.f.add_room(r2) self.assertEqual(len(self.f.rooms), 2) self.assertTrue( r2 in self.f.rooms ) def test_associate_text_to_rooms(self): p2 = Polygon.from_absolute_coordinates([(12,0),(22,0),(22,10),(12,10)]) r2 = Room(p2) t1 = Text("Text room 1", Point(5,5)) t2 = Text("Text room 2", Point(15,8)) t3 = Text("Text outside",Point(11,5)) floor = Floor("Building 1", "Floor1",[self.room1, r2]) floor.associate_room_texts([t1,t2,t3]) self.assertEqual( len(self.room1.texts), 1 ) self.assertTrue( len(r2.texts) == 1 ) self.assertTrue( t1 in self.room1.texts ) self.assertTrue( t2 in r2.texts ) def test_associate_text_to_rooms2(self): p2 = Polygon.from_absolute_coordinates([(6,0),(12,0),(12,10),(11,10),(11,4),(6,4)]) r2 = Room(p2) t1_1 = Text("Text room 1",Point(2,2)) t1_2 = Text("Text room 1",Point(8,8)) t2_1 = Text("Text room 2",Point(7,2)) t2_2 = Text("Text room 2",Point(11,8)) t_none = Text("Text none",Point(5,12)) floor = Floor("Building 1", "Floor1",[ self.room1,r2]) floor.associate_room_texts([t1_1,t1_2,t2_1,t2_2,t_none]) self.assertTrue( len(self.room1.texts) == 2 ) self.assertTrue( len(r2.texts) == 2 ) self.assertTrue( t1_1 in self.room1.texts ) self.assertTrue( t1_2 in self.room1.texts ) self.assertTrue( t2_1 in r2.texts ) self.assertTrue( t2_2 in r2.texts ) self.assertTrue( t1_1 in self.room1.texts ) self.assertTrue( t_none not in self.room1.texts ) def test_floor_equal(self): p2 = Polygon.from_absolute_coordinates([(12,0),(22,0),(22,10),(12,10)]) r2 = Room(p2) floor = Floor("Building 1", "Floor1", [self.room1,r2]) floor2 = Floor("Building 1", "Floor1",[self.room1,r2]) self.assertEqual(floor,floor2) floor3 = Floor("Building 1", "Floor", [self.room1,r2]) self.assertNotEqual(floor, floor3) floor3 = Floor("Building", "Floor1", [self.room1,r2]) self.assertNotEqual(floor, floor3) floor3 = Floor("Building 1", "Floor1", [self.room1]) self.assertNotEqual(floor, floor3) def test_floor_trasform(self): self.room1.traslate = MagicMock() self.room1.scale = MagicMock() self.f.add_room(self.room1) self.f.transform() self.room1.traslate.assert_called_once_with(0, 0) self.room1.scale.assert_called_once_with(1) for count in range(1, 10): self.f.add_room(self.room1) self.f.transform() self.assertEqual(self.room1.traslate.call_count, 11) self.assertEqual(self.room1.scale.call_count, 11) def test_floor_normalize(self): self.f.transform = MagicMock() sa = self.f.calculate_scale_amount() self.f.normalize() self.f.transform.assert_called_once_with( scale_amount = sa, traslate_x = -self.f.min_x, traslate_y = -self.f.min_y ) def test_calculate_scale_amount_and_trasform(self): polygon = Polygon.from_absolute_coordinates( [(0,0),(1024,0),(1024,1024),(2048,1024),(2048,2048),(0,2048)] ) room = Room(polygon) f = Floor("Pippo", "disneyland", [room]) self.assertEqual(f.max_output_size / 2048, f.calculate_scale_amount()) f.normalize() for point in f.rooms[0].polygon.points: self.assertTrue(point.x <= f.max_output_size) self.assertTrue(point.y <= f.max_output_size)
class DxfReader(): """ Class to read the dxf file with the dxfgrabber. """ # Todo: extract to external config file valid_poly_layers = ["RM$"] valid_text_layers = ["NLOCALI", "RM$TXT"] valid_wall_layers = ["MURI", "GROS$"] def __init__(self, filename): """ Try reading a dxf file pointed by filename and save the floor in the respective attribute. Arguments: - filename: string rapresents the filename with path of the dxf file. Raise: - FileUpdateException in case of impossibility to identify building, floor or rooms. Initialise a DxfReader, try to read a dxf file and associate the texts found to the respective room. Call the FloorInference class to find a standard floor id, and save the results of the operations in the floor attribute as a Floor object. """ self._filename = filename self._basename = os.path.basename(filename) self.floor = None self._read_dxf(self._filename) self._extract_entities() b_id = self._get_b_id(self._basename) if not b_id: raise FileUpdateException( "It was not possible to identify the building associated to the DXF file" ) f_id = FloorInference.get_identifier(self._basename, self._grabber) if not f_id: raise FileUpdateException( "It was not possible to identify the floor associated to the DXF file" ) self.floor = Floor(b_id, f_id, self._rooms, self._wall_lines, self._window_lines) if self.floor.n_rooms == 0: raise FileUpdateException("The floor read has no rooms: " + self._filename) self.floor.associate_room_texts(self._texts) self.floor.normalize() self.floor.discard_tiny_lines() def _extract_entities(self): self._rooms = [] self._texts = [] self._wall_lines = [] self._window_lines = [] for ent in self._grabber.entities: if self._is_valid_room(ent): points = [(p[0], -p[1]) for p in ent.points] polygon = Polygon.from_absolute_coordinates(points) polygon.ensure_is_closed(tollerance=0.8) polygon.simplify_close_points(tollerance=0.8) if polygon.is_self_crossing(): Logger.warning("Self crossing room is not valid: " + str(polygon)) continue self._rooms.append(Room(polygon)) elif self._is_valid_text(ent): self._texts.append( Text(ent.plain_text().strip(), Point(ent.insert[0], -ent.insert[1]))) elif self._is_valid_wall_line(ent): start = Point(ent.start[0], -ent.start[1]) end = Point(ent.end[0], -ent.end[1]) line = Segment(start, end) self._wall_lines.append(line) elif self._is_valid_wall_polyline(ent): points = [(p[0], -p[1]) for p in ent.points] polygon = Polygon.from_relative_coordinates((0, 0), points) polygon.ensure_is_closed(tollerance=1) polygon.simplify_close_points(tollerance=1) self._wall_lines.extend(polygon.as_segment_list()) elif self._is_valid_window_line(ent): start = Point(ent.start[0], -ent.start[1]) end = Point(ent.end[0], -ent.end[1]) line = Segment(start, end) self._window_lines.append(line) def _is_valid_room(self, ent): """ Method to validate a room entity. Arguments: - ent: an entity read from the dxf file. Returns: True or False. If ent is a valid polyline in the polylines layer returns True else returns False. """ return type(ent) in [LWPolyline, Polyline ] and ent.layer in self.valid_poly_layers def _is_valid_text(self, ent): """ Method to validate a text entity. Arguments: - ent: an entity read from the dxf file. Returns: True or False. If ent is a text in the text layers returns True else return False. """ if type(ent) not in [MText, dxfgrabber.entities.Text]: return False if ent.layer not in self.valid_text_layers: return False txt = ent.plain_text().strip() m1 = re.match("^[a-zA-Z\d]*\d+[a-zA-Z\d]*$", txt) m2 = re.match("^([a-zA-Z]{2,}\s*)+$", txt) if not (m1 or m2): return False return True def _is_valid_wall_line(self, ent): return ent.layer.upper( ) in self.valid_wall_layers and type(ent) is dxfgrabber.entities.Line def _is_valid_wall_polyline(self, ent): return ent.layer.upper() in self.valid_wall_layers and type(ent) in [ LWPolyline, Polyline ] def _is_valid_window_line(self, ent): return ent.layer.upper( ) == "FINESTRE" and type(ent) is dxfgrabber.entities.Line def _get_b_id(self, basename): """ Method to extract the building id from the filename. Arguments: - basename: a string representing the name of the dxf file. Returns: a string. """ b_id = None rm = re.match("(\d+)_(-?[a-zA-Z0-9]+(\.\d+)?).*\.dxf", basename, re.I) if rm: b_id = rm.group(1) else: rm = re.match("(\d+)\.dxf", basename, re.I) if rm: b_id = rm.group(1) return b_id def _read_dxf(self, filename): """ Read the dxf file with the dxf grabber. Arguments: - filename: representing the path and the name of the dxf file. Returns: None. Raise: PermissionError, IsADirectoryError, FileNotFoundError or generic Exception in case of reading failure. Try to read the dxf file with the grabber. """ try: self._grabber = dxfgrabber.readfile(filename) except PermissionError: Logger.error("Permission error: cannot read file " + filename) raise except IsADirectoryError: Logger.error("File is a directory error: cannot read file " + filename) raise except FileNotFoundError: Logger.error("File not found error: cannot read file " + filename) raise except Exception as e: Logger.error("Unknown exception on DXF file reading: " + str(e)) raise