Esempio n. 1
0
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
Esempio n. 2
0
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