first commit
This commit is contained in:
637
dxf_processor_fixed.py
Normal file
637
dxf_processor_fixed.py
Normal file
@@ -0,0 +1,637 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
수정된 DXF 파일 처리 모듈 - 속성 추출 문제 해결
|
||||
ezdxf 라이브러리를 사용하여 DXF 파일에서 모든 속성을 정확히 추출
|
||||
"""
|
||||
|
||||
import os
|
||||
import json
|
||||
import logging
|
||||
from typing import Dict, List, Optional, Tuple, Any
|
||||
from dataclasses import dataclass, asdict, field
|
||||
|
||||
try:
|
||||
import ezdxf
|
||||
from ezdxf.document import Drawing
|
||||
from ezdxf.entities import Insert, Attrib, AttDef, Text, MText
|
||||
from ezdxf.layouts import BlockLayout, Modelspace
|
||||
from ezdxf import bbox, disassemble
|
||||
EZDXF_AVAILABLE = True
|
||||
except ImportError:
|
||||
EZDXF_AVAILABLE = False
|
||||
logging.warning("ezdxf 라이브러리가 설치되지 않았습니다. DXF 기능이 비활성화됩니다.")
|
||||
|
||||
from config import Config
|
||||
|
||||
|
||||
@dataclass
|
||||
class BoundingBox:
|
||||
"""바운딩 박스 정보를 담는 데이터 클래스"""
|
||||
min_x: float
|
||||
min_y: float
|
||||
max_x: float
|
||||
max_y: float
|
||||
|
||||
@property
|
||||
def width(self) -> float:
|
||||
return self.max_x - self.min_x
|
||||
|
||||
@property
|
||||
def height(self) -> float:
|
||||
return self.max_y - self.min_y
|
||||
|
||||
@property
|
||||
def center(self) -> Tuple[float, float]:
|
||||
return ((self.min_x + self.max_x) / 2, (self.min_y + self.max_y) / 2)
|
||||
|
||||
def merge(self, other: 'BoundingBox') -> 'BoundingBox':
|
||||
"""다른 바운딩 박스와 병합하여 가장 큰 외곽 박스 반환"""
|
||||
return BoundingBox(
|
||||
min_x=min(self.min_x, other.min_x),
|
||||
min_y=min(self.min_y, other.min_y),
|
||||
max_x=max(self.max_x, other.max_x),
|
||||
max_y=max(self.max_y, other.max_y)
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class AttributeInfo:
|
||||
"""속성 정보를 담는 데이터 클래스"""
|
||||
tag: str
|
||||
text: str
|
||||
position: Tuple[float, float, float]
|
||||
height: float
|
||||
rotation: float
|
||||
layer: str
|
||||
prompt: Optional[str] = None
|
||||
style: Optional[str] = None
|
||||
invisible: bool = False
|
||||
const: bool = False
|
||||
bounding_box: Optional[BoundingBox] = None
|
||||
entity_handle: Optional[str] = None
|
||||
|
||||
# 좌표 정보
|
||||
insert_x: float = 0.0
|
||||
insert_y: float = 0.0
|
||||
insert_z: float = 0.0
|
||||
|
||||
|
||||
@dataclass
|
||||
class BlockInfo:
|
||||
"""블록 정보를 담는 데이터 클래스"""
|
||||
name: str
|
||||
position: Tuple[float, float, float]
|
||||
scale: Tuple[float, float, float]
|
||||
rotation: float
|
||||
layer: str
|
||||
attributes: List[AttributeInfo]
|
||||
bounding_box: Optional[BoundingBox] = None
|
||||
|
||||
|
||||
@dataclass
|
||||
class TitleBlockInfo:
|
||||
"""도곽 정보를 담는 데이터 클래스"""
|
||||
drawing_name: Optional[str] = None
|
||||
drawing_number: Optional[str] = None
|
||||
construction_field: Optional[str] = None
|
||||
construction_stage: Optional[str] = None
|
||||
scale: Optional[str] = None
|
||||
project_name: Optional[str] = None
|
||||
designer: Optional[str] = None
|
||||
date: Optional[str] = None
|
||||
revision: Optional[str] = None
|
||||
location: Optional[str] = None
|
||||
bounding_box: Optional[BoundingBox] = None
|
||||
block_name: Optional[str] = None
|
||||
|
||||
# 모든 attributes 정보 저장
|
||||
all_attributes: List[AttributeInfo] = field(default_factory=list)
|
||||
attributes_count: int = 0
|
||||
|
||||
def __post_init__(self):
|
||||
"""초기화 후 처리"""
|
||||
self.attributes_count = len(self.all_attributes)
|
||||
|
||||
|
||||
class FixedDXFProcessor:
|
||||
"""수정된 DXF 파일 처리기 - 속성 추출 문제 해결"""
|
||||
|
||||
def __init__(self):
|
||||
self.logger = logging.getLogger(self.__class__.__name__)
|
||||
|
||||
# 도곽 식별을 위한 키워드 (한국어/영어)
|
||||
self.title_block_keywords = {
|
||||
'도면명': ['도면명', '도면', 'drawing', 'title', 'name', 'dwg'],
|
||||
'도면번호': ['도면번호', '번호', 'number', 'no', 'dwg_no'],
|
||||
'건설분야': ['건설분야', '분야', 'field', 'construction', 'civil'],
|
||||
'건설단계': ['건설단계', '단계', 'stage', 'phase', 'step'],
|
||||
'축척': ['축척', 'scale', 'ratio'],
|
||||
'설계자': ['설계자', '설계', 'designer', 'design', 'engineer'],
|
||||
'프로젝트': ['프로젝트', '사업', 'project', 'work'],
|
||||
'날짜': ['날짜', '일자', 'date', 'time'],
|
||||
'리비전': ['리비전', '개정', 'revision', 'rev'],
|
||||
'위치': ['위치', '장소', 'location', 'place', 'site']
|
||||
}
|
||||
|
||||
if not EZDXF_AVAILABLE:
|
||||
self.logger.warning("ezdxf 라이브러리가 설치되지 않았습니다.")
|
||||
|
||||
def validate_dxf_file(self, file_path: str) -> bool:
|
||||
"""DXF 파일 유효성 검사"""
|
||||
if not EZDXF_AVAILABLE:
|
||||
self.logger.error("ezdxf 라이브러리가 설치되지 않음")
|
||||
return False
|
||||
|
||||
if not os.path.exists(file_path):
|
||||
self.logger.error(f"DXF 파일이 존재하지 않음: {file_path}")
|
||||
return False
|
||||
|
||||
if not file_path.lower().endswith('.dxf'):
|
||||
self.logger.error(f"DXF 파일이 아님: {file_path}")
|
||||
return False
|
||||
|
||||
try:
|
||||
# 파일 열기 시도
|
||||
doc = ezdxf.readfile(file_path)
|
||||
self.logger.info(f"DXF 파일 유효성 검사 통과: {file_path}")
|
||||
return True
|
||||
except Exception as e:
|
||||
self.logger.error(f"DXF 파일 유효성 검사 실패: {e}")
|
||||
return False
|
||||
|
||||
def load_dxf_document(self, file_path: str) -> Optional[Drawing]:
|
||||
"""DXF 문서 로드"""
|
||||
try:
|
||||
doc = ezdxf.readfile(file_path)
|
||||
self.logger.info(f"DXF 문서 로드 성공: {file_path}")
|
||||
return doc
|
||||
except Exception as e:
|
||||
self.logger.error(f"DXF 문서 로드 실패: {e}")
|
||||
return None
|
||||
|
||||
def extract_all_insert_attributes(self, doc: Drawing) -> List[BlockInfo]:
|
||||
"""모든 INSERT 엔티티에서 속성 추출 - 수정된 로직"""
|
||||
block_refs = []
|
||||
|
||||
try:
|
||||
# 모델스페이스에서 INSERT 엔티티 검색
|
||||
msp = doc.modelspace()
|
||||
inserts = msp.query('INSERT')
|
||||
|
||||
self.logger.info(f"모델스페이스에서 {len(inserts)}개의 INSERT 엔티티 발견")
|
||||
|
||||
for insert in inserts:
|
||||
block_info = self._process_insert_entity(insert, doc)
|
||||
if block_info:
|
||||
block_refs.append(block_info)
|
||||
|
||||
# 페이퍼스페이스에서도 검색
|
||||
for layout in doc.layouts:
|
||||
if layout.is_any_paperspace:
|
||||
inserts = layout.query('INSERT')
|
||||
self.logger.info(f"페이퍼스페이스 '{layout.name}'에서 {len(inserts)}개의 INSERT 엔티티 발견")
|
||||
|
||||
for insert in inserts:
|
||||
block_info = self._process_insert_entity(insert, doc)
|
||||
if block_info:
|
||||
block_refs.append(block_info)
|
||||
|
||||
self.logger.info(f"총 {len(block_refs)}개의 블록 참조 처리 완료")
|
||||
return block_refs
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f"INSERT 속성 추출 중 오류: {e}")
|
||||
return []
|
||||
|
||||
def _process_insert_entity(self, insert: Insert, doc: Drawing) -> Optional[BlockInfo]:
|
||||
"""개별 INSERT 엔티티 처리 - 향상된 속성 추출"""
|
||||
try:
|
||||
attributes = []
|
||||
|
||||
# 방법 1: INSERT에 연결된 ATTRIB 엔티티들 추출
|
||||
self.logger.debug(f"INSERT '{insert.dxf.name}'의 연결된 속성 추출 중...")
|
||||
|
||||
# insert.attribs는 리스트를 반환
|
||||
insert_attribs = insert.attribs
|
||||
self.logger.debug(f"INSERT.attribs: {len(insert_attribs)}개 발견")
|
||||
|
||||
for attrib in insert_attribs:
|
||||
attr_info = self._extract_attrib_info(attrib)
|
||||
if attr_info:
|
||||
attributes.append(attr_info)
|
||||
self.logger.debug(f"ATTRIB 추출: tag='{attr_info.tag}', text='{attr_info.text}'")
|
||||
|
||||
# 방법 2: 블록 정의에서 ATTDEF 정보 추출 (search_const=True와 유사한 효과)
|
||||
try:
|
||||
block_layout = doc.blocks.get(insert.dxf.name)
|
||||
if block_layout:
|
||||
# 블록 정의에서 ATTDEF 엔티티 검색
|
||||
attdefs = block_layout.query('ATTDEF')
|
||||
self.logger.debug(f"블록 정의에서 {len(attdefs)}개의 ATTDEF 발견")
|
||||
|
||||
for attdef in attdefs:
|
||||
# ATTDEF에서 기본값이나 상수 값 추출
|
||||
if hasattr(attdef.dxf, 'text') and attdef.dxf.text.strip():
|
||||
attr_info = self._extract_attdef_info(attdef, insert)
|
||||
if attr_info:
|
||||
# 중복 체크 (같은 tag가 이미 있으면 건너뛰기)
|
||||
existing_tags = [attr.tag for attr in attributes]
|
||||
if attr_info.tag not in existing_tags:
|
||||
attributes.append(attr_info)
|
||||
self.logger.debug(f"ATTDEF 추출: tag='{attr_info.tag}', text='{attr_info.text}'")
|
||||
|
||||
# 방법 3: 블록 내부의 TEXT/MTEXT 엔티티도 추출
|
||||
text_entities = block_layout.query('TEXT MTEXT')
|
||||
self.logger.debug(f"블록 정의에서 {len(text_entities)}개의 TEXT/MTEXT 발견")
|
||||
|
||||
for text_entity in text_entities:
|
||||
attr_info = self._extract_text_as_attribute(text_entity, insert)
|
||||
if attr_info:
|
||||
attributes.append(attr_info)
|
||||
self.logger.debug(f"TEXT 추출: text='{attr_info.text}'")
|
||||
|
||||
except Exception as e:
|
||||
self.logger.warning(f"블록 정의 처리 중 오류: {e}")
|
||||
|
||||
# 방법 4: get_attrib_text 메서드를 사용한 확인
|
||||
try:
|
||||
# 일반적인 속성 태그들로 시도
|
||||
common_tags = ['TITLE', 'NAME', 'NUMBER', 'DATE', 'SCALE', 'DESIGNER',
|
||||
'PROJECT', 'DRAWING', 'DWG_NO', 'REV', 'REVISION']
|
||||
|
||||
for tag in common_tags:
|
||||
try:
|
||||
# search_const=True로 ATTDEF도 검색
|
||||
text_value = insert.get_attrib_text(tag, search_const=True)
|
||||
if text_value and text_value.strip():
|
||||
# 이미 있는 태그인지 확인
|
||||
existing_tags = [attr.tag for attr in attributes]
|
||||
if tag not in existing_tags:
|
||||
attr_info = AttributeInfo(
|
||||
tag=tag,
|
||||
text=text_value.strip(),
|
||||
position=insert.dxf.insert,
|
||||
height=12.0, # 기본값
|
||||
rotation=insert.dxf.rotation,
|
||||
layer=insert.dxf.layer,
|
||||
insert_x=insert.dxf.insert[0],
|
||||
insert_y=insert.dxf.insert[1],
|
||||
insert_z=insert.dxf.insert[2] if len(insert.dxf.insert) > 2 else 0.0
|
||||
)
|
||||
attributes.append(attr_info)
|
||||
self.logger.debug(f"get_attrib_text 추출: tag='{tag}', text='{text_value.strip()}'")
|
||||
except:
|
||||
continue
|
||||
|
||||
except Exception as e:
|
||||
self.logger.warning(f"get_attrib_text 처리 중 오류: {e}")
|
||||
|
||||
# BlockInfo 생성
|
||||
if attributes or True: # 속성이 없어도 블록 정보는 수집
|
||||
block_info = BlockInfo(
|
||||
name=insert.dxf.name,
|
||||
position=insert.dxf.insert,
|
||||
scale=(insert.dxf.xscale, insert.dxf.yscale, insert.dxf.zscale),
|
||||
rotation=insert.dxf.rotation,
|
||||
layer=insert.dxf.layer,
|
||||
attributes=attributes
|
||||
)
|
||||
|
||||
self.logger.info(f"INSERT '{insert.dxf.name}' 처리 완료: {len(attributes)}개 속성")
|
||||
return block_info
|
||||
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f"INSERT 엔티티 처리 중 오류: {e}")
|
||||
return None
|
||||
|
||||
def _extract_attrib_info(self, attrib: Attrib) -> Optional[AttributeInfo]:
|
||||
"""ATTRIB 엔티티에서 속성 정보 추출"""
|
||||
try:
|
||||
# 텍스트가 비어있으면 건너뛰기
|
||||
text_value = attrib.dxf.text.strip()
|
||||
if not text_value:
|
||||
return None
|
||||
|
||||
attr_info = AttributeInfo(
|
||||
tag=attrib.dxf.tag,
|
||||
text=text_value,
|
||||
position=attrib.dxf.insert,
|
||||
height=getattr(attrib.dxf, 'height', 12.0),
|
||||
rotation=getattr(attrib.dxf, 'rotation', 0.0),
|
||||
layer=getattr(attrib.dxf, 'layer', '0'),
|
||||
style=getattr(attrib.dxf, 'style', None),
|
||||
invisible=getattr(attrib, 'is_invisible', False),
|
||||
const=getattr(attrib, 'is_const', False),
|
||||
entity_handle=attrib.dxf.handle,
|
||||
insert_x=attrib.dxf.insert[0],
|
||||
insert_y=attrib.dxf.insert[1],
|
||||
insert_z=attrib.dxf.insert[2] if len(attrib.dxf.insert) > 2 else 0.0
|
||||
)
|
||||
|
||||
return attr_info
|
||||
|
||||
except Exception as e:
|
||||
self.logger.warning(f"ATTRIB 정보 추출 중 오류: {e}")
|
||||
return None
|
||||
|
||||
def _extract_attdef_info(self, attdef: AttDef, insert: Insert) -> Optional[AttributeInfo]:
|
||||
"""ATTDEF 엔티티에서 속성 정보 추출"""
|
||||
try:
|
||||
# 텍스트가 비어있으면 건너뛰기
|
||||
text_value = attdef.dxf.text.strip()
|
||||
if not text_value:
|
||||
return None
|
||||
|
||||
# INSERT의 위치를 기준으로 실제 위치 계산
|
||||
actual_position = (
|
||||
insert.dxf.insert[0] + attdef.dxf.insert[0] * insert.dxf.xscale,
|
||||
insert.dxf.insert[1] + attdef.dxf.insert[1] * insert.dxf.yscale,
|
||||
insert.dxf.insert[2] + (attdef.dxf.insert[2] if len(attdef.dxf.insert) > 2 else 0.0)
|
||||
)
|
||||
|
||||
attr_info = AttributeInfo(
|
||||
tag=attdef.dxf.tag,
|
||||
text=text_value,
|
||||
position=actual_position,
|
||||
height=getattr(attdef.dxf, 'height', 12.0),
|
||||
rotation=getattr(attdef.dxf, 'rotation', 0.0) + insert.dxf.rotation,
|
||||
layer=getattr(attdef.dxf, 'layer', insert.dxf.layer),
|
||||
prompt=getattr(attdef.dxf, 'prompt', None),
|
||||
style=getattr(attdef.dxf, 'style', None),
|
||||
invisible=getattr(attdef, 'is_invisible', False),
|
||||
const=getattr(attdef, 'is_const', False),
|
||||
entity_handle=attdef.dxf.handle,
|
||||
insert_x=actual_position[0],
|
||||
insert_y=actual_position[1],
|
||||
insert_z=actual_position[2]
|
||||
)
|
||||
|
||||
return attr_info
|
||||
|
||||
except Exception as e:
|
||||
self.logger.warning(f"ATTDEF 정보 추출 중 오류: {e}")
|
||||
return None
|
||||
|
||||
def _extract_text_as_attribute(self, text_entity, insert: Insert) -> Optional[AttributeInfo]:
|
||||
"""TEXT/MTEXT 엔티티를 속성으로 추출"""
|
||||
try:
|
||||
# 텍스트 내용 추출
|
||||
if hasattr(text_entity, 'text'):
|
||||
text_value = text_entity.text.strip()
|
||||
elif hasattr(text_entity.dxf, 'text'):
|
||||
text_value = text_entity.dxf.text.strip()
|
||||
else:
|
||||
return None
|
||||
|
||||
if not text_value:
|
||||
return None
|
||||
|
||||
# INSERT의 위치를 기준으로 실제 위치 계산
|
||||
text_pos = text_entity.dxf.insert
|
||||
actual_position = (
|
||||
insert.dxf.insert[0] + text_pos[0] * insert.dxf.xscale,
|
||||
insert.dxf.insert[1] + text_pos[1] * insert.dxf.yscale,
|
||||
insert.dxf.insert[2] + (text_pos[2] if len(text_pos) > 2 else 0.0)
|
||||
)
|
||||
|
||||
# 태그는 텍스트 내용의 첫 단어나 전체 내용으로 설정
|
||||
tag = text_value.split()[0] if ' ' in text_value else text_value[:20]
|
||||
|
||||
attr_info = AttributeInfo(
|
||||
tag=f"TEXT_{tag}",
|
||||
text=text_value,
|
||||
position=actual_position,
|
||||
height=getattr(text_entity.dxf, 'height', 12.0),
|
||||
rotation=getattr(text_entity.dxf, 'rotation', 0.0) + insert.dxf.rotation,
|
||||
layer=getattr(text_entity.dxf, 'layer', insert.dxf.layer),
|
||||
style=getattr(text_entity.dxf, 'style', None),
|
||||
entity_handle=text_entity.dxf.handle,
|
||||
insert_x=actual_position[0],
|
||||
insert_y=actual_position[1],
|
||||
insert_z=actual_position[2]
|
||||
)
|
||||
|
||||
return attr_info
|
||||
|
||||
except Exception as e:
|
||||
self.logger.warning(f"TEXT 엔티티 추출 중 오류: {e}")
|
||||
return None
|
||||
|
||||
def identify_title_block(self, block_refs: List[BlockInfo]) -> Optional[TitleBlockInfo]:
|
||||
"""도곽 블록 식별 및 정보 추출"""
|
||||
if not block_refs:
|
||||
return None
|
||||
|
||||
title_block_candidates = []
|
||||
|
||||
# 각 블록 참조에 대해 도곽 가능성 점수 계산
|
||||
for block_ref in block_refs:
|
||||
score = self._calculate_title_block_score(block_ref)
|
||||
if score > 0:
|
||||
title_block_candidates.append((block_ref, score))
|
||||
|
||||
if not title_block_candidates:
|
||||
self.logger.warning("도곽 블록을 찾을 수 없습니다.")
|
||||
return None
|
||||
|
||||
# 가장 높은 점수의 블록을 도곽으로 선택
|
||||
title_block_candidates.sort(key=lambda x: x[1], reverse=True)
|
||||
best_candidate = title_block_candidates[0][0]
|
||||
|
||||
self.logger.info(f"도곽 블록 식별: '{best_candidate.name}' (점수: {title_block_candidates[0][1]})")
|
||||
|
||||
# TitleBlockInfo 생성
|
||||
title_block = TitleBlockInfo(
|
||||
block_name=best_candidate.name,
|
||||
all_attributes=best_candidate.attributes
|
||||
)
|
||||
|
||||
# 속성들을 분류하여 해당 필드에 할당
|
||||
for attr in best_candidate.attributes:
|
||||
self._assign_attribute_to_field(title_block, attr)
|
||||
|
||||
title_block.attributes_count = len(best_candidate.attributes)
|
||||
|
||||
return title_block
|
||||
|
||||
def _calculate_title_block_score(self, block_ref: BlockInfo) -> int:
|
||||
"""블록이 도곽일 가능성 점수 계산"""
|
||||
score = 0
|
||||
|
||||
# 블록 이름에 도곽 관련 키워드가 있는지 확인
|
||||
name_lower = block_ref.name.lower()
|
||||
title_keywords = ['title', 'titleblock', 'title_block', '도곽', '타이틀', 'border', 'frame']
|
||||
|
||||
for keyword in title_keywords:
|
||||
if keyword in name_lower:
|
||||
score += 10
|
||||
break
|
||||
|
||||
# 속성 개수 (도곽은 보통 많은 속성을 가짐)
|
||||
if len(block_ref.attributes) >= 5:
|
||||
score += 5
|
||||
elif len(block_ref.attributes) >= 3:
|
||||
score += 3
|
||||
elif len(block_ref.attributes) >= 1:
|
||||
score += 1
|
||||
|
||||
# 도곽 관련 속성이 있는지 확인
|
||||
for attr in block_ref.attributes:
|
||||
for field_name, keywords in self.title_block_keywords.items():
|
||||
if self._contains_any_keyword(attr.tag.lower(), keywords) or \
|
||||
self._contains_any_keyword(attr.text.lower(), keywords):
|
||||
score += 2
|
||||
|
||||
return score
|
||||
|
||||
def _contains_any_keyword(self, text: str, keywords: List[str]) -> bool:
|
||||
"""텍스트에 키워드 중 하나라도 포함되어 있는지 확인"""
|
||||
text_lower = text.lower()
|
||||
return any(keyword.lower() in text_lower for keyword in keywords)
|
||||
|
||||
def _assign_attribute_to_field(self, title_block: TitleBlockInfo, attr: AttributeInfo):
|
||||
"""속성을 도곽 정보의 해당 필드에 할당"""
|
||||
attr_text = attr.text.strip()
|
||||
|
||||
if not attr_text:
|
||||
return
|
||||
|
||||
# 태그나 텍스트에서 키워드 매칭
|
||||
attr_tag_lower = attr.tag.lower()
|
||||
attr_text_lower = attr_text.lower()
|
||||
|
||||
# 각 필드별로 키워드 매칭
|
||||
for field_name, keywords in self.title_block_keywords.items():
|
||||
if self._contains_any_keyword(attr_tag_lower, keywords) or \
|
||||
self._contains_any_keyword(attr_text_lower, keywords):
|
||||
|
||||
if field_name == '도면명' and not title_block.drawing_name:
|
||||
title_block.drawing_name = attr_text
|
||||
elif field_name == '도면번호' and not title_block.drawing_number:
|
||||
title_block.drawing_number = attr_text
|
||||
elif field_name == '건설분야' and not title_block.construction_field:
|
||||
title_block.construction_field = attr_text
|
||||
elif field_name == '건설단계' and not title_block.construction_stage:
|
||||
title_block.construction_stage = attr_text
|
||||
elif field_name == '축척' and not title_block.scale:
|
||||
title_block.scale = attr_text
|
||||
elif field_name == '설계자' and not title_block.designer:
|
||||
title_block.designer = attr_text
|
||||
elif field_name == '프로젝트' and not title_block.project_name:
|
||||
title_block.project_name = attr_text
|
||||
elif field_name == '날짜' and not title_block.date:
|
||||
title_block.date = attr_text
|
||||
elif field_name == '리비전' and not title_block.revision:
|
||||
title_block.revision = attr_text
|
||||
elif field_name == '위치' and not title_block.location:
|
||||
title_block.location = attr_text
|
||||
break
|
||||
|
||||
def process_dxf_file_comprehensive(self, file_path: str) -> Dict[str, Any]:
|
||||
"""DXF 파일 종합적인 처리 - 수정된 버전"""
|
||||
result = {
|
||||
'success': False,
|
||||
'error': None,
|
||||
'file_path': file_path,
|
||||
'title_block': None,
|
||||
'block_references': [],
|
||||
'summary': {}
|
||||
}
|
||||
|
||||
try:
|
||||
# 파일 유효성 검사
|
||||
if not self.validate_dxf_file(file_path):
|
||||
result['error'] = "유효하지 않은 DXF 파일입니다."
|
||||
return result
|
||||
|
||||
# DXF 문서 로드
|
||||
doc = self.load_dxf_document(file_path)
|
||||
if not doc:
|
||||
result['error'] = "DXF 문서를 로드할 수 없습니다."
|
||||
return result
|
||||
|
||||
# 모든 INSERT 엔티티에서 속성 추출
|
||||
self.logger.info("INSERT 엔티티 속성 추출 중...")
|
||||
block_references = self.extract_all_insert_attributes(doc)
|
||||
|
||||
# 도곽 정보 추출
|
||||
self.logger.info("도곽 정보 추출 중...")
|
||||
title_block = self.identify_title_block(block_references)
|
||||
|
||||
# 요약 정보 생성
|
||||
total_attributes = sum(len(br.attributes) for br in block_references)
|
||||
non_empty_attributes = sum(len([attr for attr in br.attributes if attr.text.strip()])
|
||||
for br in block_references)
|
||||
|
||||
summary = {
|
||||
'total_blocks': len(block_references),
|
||||
'title_block_found': title_block is not None,
|
||||
'title_block_name': title_block.block_name if title_block else None,
|
||||
'attributes_count': title_block.attributes_count if title_block else 0,
|
||||
'total_attributes': total_attributes,
|
||||
'non_empty_attributes': non_empty_attributes
|
||||
}
|
||||
|
||||
# 결과 설정
|
||||
result['title_block'] = asdict(title_block) if title_block else None
|
||||
result['block_references'] = [asdict(br) for br in block_references]
|
||||
result['summary'] = summary
|
||||
result['success'] = True
|
||||
|
||||
self.logger.info(f"DXF 파일 처리 완료: {file_path}")
|
||||
self.logger.info(f"처리 요약: 블록 {len(block_references)}개, 속성 {total_attributes}개 (비어있지 않은 속성: {non_empty_attributes}개)")
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f"DXF 파일 처리 중 오류: {e}")
|
||||
result['error'] = str(e)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
# 기존 클래스명과의 호환성을 위한 별칭
|
||||
DXFProcessor = FixedDXFProcessor
|
||||
|
||||
|
||||
def main():
|
||||
"""테스트용 메인 함수"""
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
|
||||
if not EZDXF_AVAILABLE:
|
||||
print("ezdxf 라이브러리가 설치되지 않았습니다.")
|
||||
return
|
||||
|
||||
processor = FixedDXFProcessor()
|
||||
|
||||
# 업로드 폴더에서 DXF 파일 찾기
|
||||
upload_dir = "uploads"
|
||||
if os.path.exists(upload_dir):
|
||||
for file in os.listdir(upload_dir):
|
||||
if file.lower().endswith('.dxf'):
|
||||
test_file = os.path.join(upload_dir, file)
|
||||
print(f"\n테스트 파일: {test_file}")
|
||||
|
||||
result = processor.process_dxf_file_comprehensive(test_file)
|
||||
|
||||
if result['success']:
|
||||
print("✅ DXF 파일 처리 성공!")
|
||||
summary = result['summary']
|
||||
print(f" 블록 수: {summary['total_blocks']}")
|
||||
print(f" 도곽 발견: {summary['title_block_found']}")
|
||||
print(f" 도곽 속성 수: {summary['attributes_count']}")
|
||||
print(f" 전체 속성 수: {summary['total_attributes']}")
|
||||
print(f" 비어있지 않은 속성 수: {summary['non_empty_attributes']}")
|
||||
|
||||
if summary['title_block_name']:
|
||||
print(f" 도곽 블록명: {summary['title_block_name']}")
|
||||
else:
|
||||
print(f"❌ 처리 실패: {result['error']}")
|
||||
|
||||
break
|
||||
else:
|
||||
print("uploads 폴더를 찾을 수 없습니다.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user