Refactor: typing cleanup, 경로 하드코딩 제거, GPS 데이터 Lon/Lat 분리, 불필요 모듈 삭제
This commit is contained in:
6
.gitignore
vendored
6
.gitignore
vendored
@@ -188,5 +188,11 @@ poetry.toml
|
|||||||
# LSP config files
|
# LSP config files
|
||||||
pyrightconfig.json
|
pyrightconfig.json
|
||||||
|
|
||||||
|
# Sample Images
|
||||||
|
/Images
|
||||||
|
|
||||||
|
*복사본.xlsx
|
||||||
|
.~lock*
|
||||||
|
|
||||||
# End of https://www.toptal.com/developers/gitignore/api/python,linux
|
# End of https://www.toptal.com/developers/gitignore/api/python,linux
|
||||||
|
|
||||||
|
|||||||
BIN
EPoleDB.xlsx
BIN
EPoleDB.xlsx
Binary file not shown.
@@ -1,5 +1,5 @@
|
|||||||
import os
|
import os
|
||||||
import sys
|
#import sys
|
||||||
|
|
||||||
import UtilPack as util
|
import UtilPack as util
|
||||||
import PoleXLS as myxl
|
import PoleXLS as myxl
|
||||||
@@ -7,38 +7,8 @@ import ExtEXIFData as exEXIF
|
|||||||
import VWorldAPIs as vwapi
|
import VWorldAPIs as vwapi
|
||||||
import JusoMngr
|
import JusoMngr
|
||||||
|
|
||||||
mCurPath = "" # 스크립트가 실행되는 경로
|
|
||||||
mXlsPath = "" # 엑셀 파일이 있는 경로, 틀리면 안됨.
|
|
||||||
|
|
||||||
# "/Volumes/ExSSD/Working/Images" # 이미지가 들어있는 폴더, 이 아래에 A,B,Pole 등이 있어야 한다.
|
def GetPoleInfos(xls, nRow:int):
|
||||||
mImgPath = ""
|
|
||||||
|
|
||||||
|
|
||||||
def WorkingFolderCheck(path, xlsPath):
|
|
||||||
if False == os.path.exists(path):
|
|
||||||
print(f"Error: Working Folder is not exist. {path}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
pathList = ["A", "B", "Pole"]
|
|
||||||
|
|
||||||
for folder in pathList:
|
|
||||||
fullPath = os.path.join(path, folder)
|
|
||||||
if False == os.path.isdir(fullPath):
|
|
||||||
print(f"Error: {folder} Folder is not exist. {fullPath}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
mImgPath = path
|
|
||||||
mCurPath = os.getcwd()
|
|
||||||
|
|
||||||
if False == os.path.exists(xlsPath):
|
|
||||||
mXlsPath = os.getcwd()
|
|
||||||
else:
|
|
||||||
mXlsPath = xlsPath
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def GetPoleInfos(xls, nRow):
|
|
||||||
if None == xls or False == isinstance(xls, myxl.PoleXLS):
|
if None == xls or False == isinstance(xls, myxl.PoleXLS):
|
||||||
return []
|
return []
|
||||||
|
|
||||||
@@ -51,7 +21,8 @@ def CollectImageData(xls, sheetTrg, listImg, pathImgDir):
|
|||||||
if None == xls or False == isinstance(xls, myxl.PoleXLS) or True == util.IsEmptyStr(sheetTrg):
|
if None == xls or False == isinstance(xls, myxl.PoleXLS) or True == util.IsEmptyStr(sheetTrg):
|
||||||
return -1
|
return -1
|
||||||
|
|
||||||
pathcsv = os.path.join(mCurPath, "JusoDB.csv")
|
pathCurrent = os.getcwd()
|
||||||
|
pathcsv = os.path.join(pathCurrent, "JusoDB.csv")
|
||||||
jusomgr = JusoMngr.JusoDB(pathcsv)
|
jusomgr = JusoMngr.JusoDB(pathcsv)
|
||||||
|
|
||||||
nIdx = 1
|
nIdx = 1
|
||||||
@@ -86,18 +57,20 @@ def CollectImageData(xls, sheetTrg, listImg, pathImgDir):
|
|||||||
xls.SetCellValueStr(sheetTrg, 3, nIdx, all_date)
|
xls.SetCellValueStr(sheetTrg, 3, nIdx, all_date)
|
||||||
xls.SetCellValueStr(sheetTrg, 4, nIdx, level)
|
xls.SetCellValueStr(sheetTrg, 4, nIdx, level)
|
||||||
xls.SetCellValueStr(sheetTrg, 5, nIdx, pathRel)
|
xls.SetCellValueStr(sheetTrg, 5, nIdx, pathRel)
|
||||||
xls.SetCellValueStr(sheetTrg, 6, nIdx, juso[3])
|
xls.SetCellValueStr(sheetTrg, 6, nIdx, juso[1])
|
||||||
xls.SetCellValueStr(sheetTrg, 7, nIdx, juso[2])
|
xls.SetCellValueStr(sheetTrg, 7, nIdx, juso[0])
|
||||||
xls.SetCellValueStr(sheetTrg, 8, nIdx, f"{lat},{lon}")
|
xls.SetCellValueStr(sheetTrg, 8, nIdx, juso[3])
|
||||||
xls.SetCellValueStr(sheetTrg, 9, nIdx, juso[1])
|
xls.SetCellValueStr(sheetTrg, 9, nIdx, juso[2])
|
||||||
xls.SetCellValueStr(sheetTrg, 10, nIdx, juso[0])
|
xls.SetCellValueStr(sheetTrg, 10, nIdx, f"{lon}")
|
||||||
|
xls.SetCellValueStr(sheetTrg, 11, nIdx, f"{lat}")
|
||||||
|
|
||||||
# VWorld 좌표로 변환하여 저장
|
# VWorld 좌표로 변환하여 저장
|
||||||
lat_Vworld, lon_Vworld = 0.0, 0.0
|
lat_Vworld, lon_Vworld = 0.0, 0.0
|
||||||
if lat != 0.0 and lon != 0.0 :
|
if lat != 0.0 and lon != 0.0 :
|
||||||
lat_Vworld, lon_Vworld = vwapi.Transform4326to3857(lat, lon)
|
lat_Vworld, lon_Vworld = vwapi.Transform4326to3857(lat, lon)
|
||||||
|
|
||||||
xls.SetCellValueStr(sheetTrg, 11, nIdx, f"{lat_Vworld},{lon_Vworld}")
|
xls.SetCellValueStr(sheetTrg, 12, nIdx, f"{lon_Vworld}")
|
||||||
|
xls.SetCellValueStr(sheetTrg, 13, nIdx, f"{lat_Vworld}")
|
||||||
|
|
||||||
nIdx += 1
|
nIdx += 1
|
||||||
|
|
||||||
@@ -105,20 +78,23 @@ def CollectImageData(xls, sheetTrg, listImg, pathImgDir):
|
|||||||
|
|
||||||
# DB 시트에 있는 파일을 골라내서 PoleInfo 시트에 넣는다.
|
# DB 시트에 있는 파일을 골라내서 PoleInfo 시트에 넣는다.
|
||||||
# PoleInfo : 번호, GPS 좌표, 구역, 주소, 사진
|
# PoleInfo : 번호, GPS 좌표, 구역, 주소, 사진
|
||||||
def CollectPoleInfoData(xls, TrgSheetName, DBSheetName, pathImgDir):
|
def CollectPoleInfoData(xls, TrgSheetName:str, DBSheetName:str, pathImgDir:str)-> int:
|
||||||
if None == xls:
|
if None == xls:
|
||||||
return -1
|
return -1
|
||||||
|
|
||||||
nLastDBRow = xls.FindLastRow( DBSheetName )
|
nLastDBRow:int = xls.FindLastRow( DBSheetName )
|
||||||
if 0 >= nLastDBRow:
|
if 0 >= nLastDBRow:
|
||||||
print(f"{DBSheetName} Sheet is Empty.")
|
print(f"{DBSheetName} Sheet is Empty.")
|
||||||
return -1
|
return -1
|
||||||
|
|
||||||
# 헤더가 있는 시트는 헤더가 1, 그래서 2부터 시작
|
# 헤더가 있는 시트는 헤더의 인덱스가 1, 그래서 2부터 시작
|
||||||
nTrgRow = xls.FindLastRow(TrgSheetName) + 1
|
nTrgRow = xls.FindLastRow(TrgSheetName) + 1
|
||||||
for nDBRow in range(1, nLastDBRow + 1):
|
for nDBRow in range(1, nLastDBRow + 1):
|
||||||
listValues = xls.GetAllRowValue(DBSheetName, nDBRow, 8)
|
listValues = xls.GetAllRowValue(DBSheetName, nDBRow, 11)
|
||||||
# 0: Index, 1: Sheetname, 2:Date, 3:Level, 4:Rel Path, 5:Area, 6:Juso, 7:GPS(Lat,Lon)
|
|
||||||
|
# 0: Index, 1: Sheetname, 2: Date, 3: Level, 4: Rel Path,
|
||||||
|
# 5: Area, 6: Juso, 7: Area_Road, 8: Juso_Road,
|
||||||
|
# 9: GPS(Lon), 10: GPS(Lat), 11: VWorld(Lon), 12: VWorld(Lat)
|
||||||
if None == listValues:
|
if None == listValues:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@@ -128,13 +104,14 @@ def CollectPoleInfoData(xls, TrgSheetName, DBSheetName, pathImgDir):
|
|||||||
if listValues[3] != "Pole" :
|
if listValues[3] != "Pole" :
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if listValues[7] == "" or listValues[7] == "0.0,0.0":
|
# GPS 좌표가 없으면 패스
|
||||||
|
if listValues[9] == "" or listValues[10] == "":
|
||||||
continue
|
continue
|
||||||
|
|
||||||
xls.SetCellValueStr(TrgSheetName, 1, nTrgRow, str(nTrgRow -1))
|
xls.SetCellValueStr(TrgSheetName, 1, nTrgRow, str(nTrgRow -1))
|
||||||
xls.SetCellValueStr(TrgSheetName, 2, nTrgRow, listValues[7])
|
xls.SetCellValueStr(TrgSheetName, 2, nTrgRow, listValues[5])
|
||||||
xls.SetCellValueStr(TrgSheetName, 3, nTrgRow, listValues[5])
|
xls.SetCellValueStr(TrgSheetName, 3, nTrgRow, listValues[6])
|
||||||
xls.SetCellValueStr(TrgSheetName, 4, nTrgRow, listValues[6])
|
xls.SetCellValueStr(TrgSheetName, 4, nTrgRow, listValues[7])
|
||||||
|
|
||||||
imgPath = os.path.join(pathImgDir, listValues[4])
|
imgPath = os.path.join(pathImgDir, listValues[4])
|
||||||
imgPath = exEXIF.ProcessImg(imgPath, 0.1, 80, True)
|
imgPath = exEXIF.ProcessImg(imgPath, 0.1, 80, True)
|
||||||
@@ -142,6 +119,9 @@ def CollectPoleInfoData(xls, TrgSheetName, DBSheetName, pathImgDir):
|
|||||||
|
|
||||||
xls.SetCellValueStr(TrgSheetName, 6, nTrgRow, listValues[0])
|
xls.SetCellValueStr(TrgSheetName, 6, nTrgRow, listValues[0])
|
||||||
|
|
||||||
|
xls.SetCellValueStr(TrgSheetName, 7, nTrgRow, listValues[9])
|
||||||
|
xls.SetCellValueStr(TrgSheetName, 8, nTrgRow, listValues[10])
|
||||||
|
|
||||||
# 목표 시트에 넣었으니 시트 이름을 DB 시트에 적어 넣는다.
|
# 목표 시트에 넣었으니 시트 이름을 DB 시트에 적어 넣는다.
|
||||||
xls.SetCellValueStr(DBSheetName, 2, nDBRow, TrgSheetName)
|
xls.SetCellValueStr(DBSheetName, 2, nDBRow, TrgSheetName)
|
||||||
|
|
||||||
@@ -163,9 +143,11 @@ def CollectPoleData( xls, TrgSheetName, InfoSheetName, DBSheetName, pathImgDir )
|
|||||||
# 헤더가 있는 시트는 헤더가 1, 그래서 2부터 시작
|
# 헤더가 있는 시트는 헤더가 1, 그래서 2부터 시작
|
||||||
nTrgRow = xls.FindLastRow(TrgSheetName) + 1
|
nTrgRow = xls.FindLastRow(TrgSheetName) + 1
|
||||||
for nDBRow in range(1, nLastDBRow + 1):
|
for nDBRow in range(1, nLastDBRow + 1):
|
||||||
listValues = xls.GetAllRowValue(DBSheetName, nDBRow, 8)
|
listValues = xls.GetAllRowValue(DBSheetName, nDBRow, 13)
|
||||||
|
|
||||||
# 0: Index, 1: Sheetname, 2:Date, 3:Level, 4:Rel Path, 5:Area, 6:Juso, 7:GPS(Lat,Lon)
|
# 0: Index, 1: Sheetname, 2: Date, 3: Level, 4: Rel Path,
|
||||||
|
# 5: Area, 6: Juso, 7: Area_Road, 8: Juso_Road,
|
||||||
|
# 9: GPS(Lon), 10: GPS(Lat), 11: VWorld(Lon), 12: VWorld(Lat)
|
||||||
if None == listValues:
|
if None == listValues:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@@ -175,7 +157,8 @@ def CollectPoleData( xls, TrgSheetName, InfoSheetName, DBSheetName, pathImgDir )
|
|||||||
if listValues[3] != "A" and listValues[3] != "B" :
|
if listValues[3] != "A" and listValues[3] != "B" :
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if listValues[7] == "" or listValues[7] == "0.0,0.0":
|
# GPS 좌표가 없으면 패스
|
||||||
|
if listValues[9] == "" or listValues[10] == "":
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# 날짜만 골라낸다.
|
# 날짜만 골라낸다.
|
||||||
@@ -188,12 +171,13 @@ def CollectPoleData( xls, TrgSheetName, InfoSheetName, DBSheetName, pathImgDir )
|
|||||||
xls.SetCellValueStr(TrgSheetName, 2, nTrgRow, strDate)
|
xls.SetCellValueStr(TrgSheetName, 2, nTrgRow, strDate)
|
||||||
|
|
||||||
# 가장 가까운 점봇대를 찾는다. 인덱스를 얻어서...
|
# 가장 가까운 점봇대를 찾는다. 인덱스를 얻어서...
|
||||||
strLat, strLon = listValues[7] .split(',')
|
strLon = listValues[9]
|
||||||
|
strLat = listValues[10]
|
||||||
nPoleIdx, nDistance = xls.FindCloestPole(float(strLat), float(strLon))
|
nPoleIdx, nDistance = xls.FindCloestPole(float(strLat), float(strLon))
|
||||||
|
|
||||||
# 배열은 0부터, Pole 인덱스는 1부터 시작하고, 셀 번호는 헤더를 포함해서 2 부터 시작한다.
|
# 배열은 0부터, Pole 인덱스는 1부터 시작하고, 셀 번호는 헤더를 포함해서 2 부터 시작한다.
|
||||||
# 셀 번호를 넣어서 해당하는 정보를 가져온다.
|
# 셀 번호를 넣어서 해당하는 정보를 가져온다.
|
||||||
# 0:번호, 1:GPS 좌표, 2:구역, 3:주소, 4:사진
|
# 0:번호, 1:구역, 2:주소, 3: 도로명, 4:사진, 5: ImgIndex, 6:GPS(Lon), 7:GPS(Lat)
|
||||||
#print( f"{nDBRow} : {nTrgRow} : {nPoleIdx}")
|
#print( f"{nDBRow} : {nTrgRow} : {nPoleIdx}")
|
||||||
retList = xls.GetAllRowValue(InfoSheetName, nPoleIdx, 5)
|
retList = xls.GetAllRowValue(InfoSheetName, nPoleIdx, 5)
|
||||||
|
|
||||||
@@ -220,13 +204,13 @@ def CollectPoleData( xls, TrgSheetName, InfoSheetName, DBSheetName, pathImgDir )
|
|||||||
return nTrgRow
|
return nTrgRow
|
||||||
|
|
||||||
|
|
||||||
def GetJPGFileLIst(pathImageDir):
|
def GetJPGFileLIst(pathImageDir:str) -> list[str]:
|
||||||
trgDirList = util.GetSubDirectories(pathImageDir)
|
trgDirList:list[str] = util.GetSubDirectories(pathImageDir)
|
||||||
|
|
||||||
#print(f"DBG : GetJPGFileLIst : {pathImageDir}, {trgDirList}")
|
#print(f"DBG : GetJPGFileLIst : {pathImageDir}, {trgDirList}")
|
||||||
#input("Press Enter to continue...")
|
#input("Press Enter to continue...")
|
||||||
|
|
||||||
retImgList = []
|
retImgList:list[str] = []
|
||||||
for dirName in trgDirList:
|
for dirName in trgDirList:
|
||||||
if dirName.lower() == "thumb":
|
if dirName.lower() == "thumb":
|
||||||
continue
|
continue
|
||||||
@@ -243,9 +227,9 @@ def GetJPGFileLIst(pathImageDir):
|
|||||||
return retImgList
|
return retImgList
|
||||||
|
|
||||||
|
|
||||||
def Process(PathImg, PathXLS):
|
def Process(PathImg:str, PathXLS:str):
|
||||||
# 이미지 파일 목록 작성
|
# 이미지 파일 목록 작성
|
||||||
trgImgList = GetJPGFileLIst(PathImg)
|
trgImgList:list[str] = GetJPGFileLIst(PathImg)
|
||||||
|
|
||||||
# 엑셀을 연다.
|
# 엑셀을 연다.
|
||||||
XLSPath = os.path.join(PathXLS, "EPoleDB.xlsx")
|
XLSPath = os.path.join(PathXLS, "EPoleDB.xlsx")
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import cv2
|
import cv2
|
||||||
import pytesseract
|
import pytesseract
|
||||||
from pytesseract import Output
|
from pytesseract import Output
|
||||||
|
import UtilPack as util
|
||||||
|
|
||||||
img_path = '/Volumes/ExSSD/Working/용공추 사진/2,3월 데이터/Pole/20250307_153821.jpg'
|
img_path = util.NormalizePath('/Volumes/ExSSD/Working/용공추 사진/2,3월 데이터/Pole/20250307_153821.jpg')
|
||||||
|
|
||||||
# 이미지 경로
|
# 이미지 경로
|
||||||
img = cv2.imread(img_path)
|
img = cv2.imread(img_path)
|
||||||
|
|||||||
@@ -143,7 +143,8 @@ class MyApp(QMainWindow):
|
|||||||
# Show GPS Mapped map in Right Layout
|
# Show GPS Mapped map in Right Layout
|
||||||
self.webview_map = QWebEngineView()
|
self.webview_map = QWebEngineView()
|
||||||
self.webview_map.setFixedWidth(450)
|
self.webview_map.setFixedWidth(450)
|
||||||
self.webview_map.setUrl(QUrl("file:///Users/minarinari/Workspace/Python/UtilityPole_Info/sample_Event.html"))
|
pathHtml = os.path.join(os.path.dirname(os.path.abspath(__file__)), "sample_Event.html")
|
||||||
|
self.webview_map.setUrl(QUrl.fromLocalFile(pathHtml))
|
||||||
|
|
||||||
# 레이아웃 설정
|
# 레이아웃 설정
|
||||||
layout = QHBoxLayout()
|
layout = QHBoxLayout()
|
||||||
@@ -165,7 +166,7 @@ class MyApp(QMainWindow):
|
|||||||
self.btn_ParseImages.setEnabled(False)
|
self.btn_ParseImages.setEnabled(False)
|
||||||
pathImage= self.edit_ImgPath.text()
|
pathImage= self.edit_ImgPath.text()
|
||||||
|
|
||||||
mgrConv.Process(pathImage,"/home/gerd/Workspace/Python/UtilityPole_Info" )
|
mgrConv.Process(pathImage,"D:\\Workspace\\Python\\UtilityPole_Info" )
|
||||||
|
|
||||||
self.btn_ParseImages.setEnabled(True)
|
self.btn_ParseImages.setEnabled(True)
|
||||||
print("Parse END!")
|
print("Parse END!")
|
||||||
|
|||||||
37
PoleXLS.py
37
PoleXLS.py
@@ -23,7 +23,8 @@ class PoleXLS(DBXLStorage):
|
|||||||
|
|
||||||
self.getSheet(sheetName, True)
|
self.getSheet(sheetName, True)
|
||||||
|
|
||||||
title, recp = util.GetSheetInfoValue(item)
|
text = str(item) if item is not None else ""
|
||||||
|
title, recp = util.GetSheetInfoValue(text)
|
||||||
self.SetCellValueStr(sheetName, colIdx, 1, title)
|
self.SetCellValueStr(sheetName, colIdx, 1, title)
|
||||||
listRecp.append(recp)
|
listRecp.append(recp)
|
||||||
|
|
||||||
@@ -43,17 +44,16 @@ class PoleXLS(DBXLStorage):
|
|||||||
# 헤더 다음 줄부터 읽는다.
|
# 헤더 다음 줄부터 읽는다.
|
||||||
listDistance = []
|
listDistance = []
|
||||||
for row in sheet.iter_rows(min_row=2, values_only=True):
|
for row in sheet.iter_rows(min_row=2, values_only=True):
|
||||||
temp = row[1]
|
strLon = row[6]
|
||||||
|
strLat = row[7]
|
||||||
|
|
||||||
if "None" in temp:
|
if "None" in strLon or "None" in strLat:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
dist = 0
|
dist = 0
|
||||||
if temp != "":
|
if strLon != "" and strLat != "":
|
||||||
parts = temp.split(',')
|
trgLat = float(strLat)
|
||||||
|
trgLon = float(strLon)
|
||||||
trgLat = float(parts[0])
|
|
||||||
trgLon = float(parts[1])
|
|
||||||
dist = util.GetDistanceGPS(srcLat, srcLon, trgLat, trgLon)
|
dist = util.GetDistanceGPS(srcLat, srcLon, trgLat, trgLon)
|
||||||
|
|
||||||
listDistance.append(dist)
|
listDistance.append(dist)
|
||||||
@@ -67,7 +67,7 @@ class PoleXLS(DBXLStorage):
|
|||||||
return retIdx, round( min_value, 0 )
|
return retIdx, round( min_value, 0 )
|
||||||
|
|
||||||
|
|
||||||
def GetPoleInfoAll(self, nIdx):
|
def GetPoleInfoAll(self, nIdx:int):
|
||||||
sheet = self.getSheet("PoleInfo")
|
sheet = self.getSheet("PoleInfo")
|
||||||
if sheet is None:
|
if sheet is None:
|
||||||
return None
|
return None
|
||||||
@@ -83,7 +83,7 @@ class PoleXLS(DBXLStorage):
|
|||||||
return retList
|
return retList
|
||||||
|
|
||||||
|
|
||||||
def GetPoleInfo(self, nIdx, colNum):
|
def GetPoleInfo(self, nIdx:int, colNum:int):
|
||||||
sheet = self.getSheet("PoleInfo")
|
sheet = self.getSheet("PoleInfo")
|
||||||
if sheet is None:
|
if sheet is None:
|
||||||
return None
|
return None
|
||||||
@@ -94,7 +94,7 @@ class PoleXLS(DBXLStorage):
|
|||||||
return sheet.cell(row=nIdx,column=colNum).value
|
return sheet.cell(row=nIdx,column=colNum).value
|
||||||
|
|
||||||
|
|
||||||
def AddImgInfo(self, TrgSheetName, EXIF_Date, EXIF_GPS, Path):
|
def AddImgInfo(self, TrgSheetName:str, EXIF_Date:str, EXIF_GPS:str, Path:str):
|
||||||
sheet = self.getSheet("ImgInfo")
|
sheet = self.getSheet("ImgInfo")
|
||||||
if sheet is None:
|
if sheet is None:
|
||||||
return -1
|
return -1
|
||||||
@@ -114,7 +114,7 @@ class PoleXLS(DBXLStorage):
|
|||||||
return TrgRow
|
return TrgRow
|
||||||
|
|
||||||
|
|
||||||
def FindRowCompValue(self, sheetNameTrg, nTrgCol, valueSrc, bCmpFile = False, pathBase = ""):
|
def FindRowCompValue(self, sheetNameTrg:str, nTrgCol:int, valueSrc:str, bCmpFile:bool = False, pathBase:str = ""):
|
||||||
sheet = self.getSheet(sheetNameTrg)
|
sheet = self.getSheet(sheetNameTrg)
|
||||||
if sheet is None:
|
if sheet is None:
|
||||||
return -1
|
return -1
|
||||||
@@ -129,12 +129,16 @@ class PoleXLS(DBXLStorage):
|
|||||||
for row in sheet.iter_rows(min_row=2, values_only=True):
|
for row in sheet.iter_rows(min_row=2, values_only=True):
|
||||||
valueTrg = row[nTrgCol]
|
valueTrg = row[nTrgCol]
|
||||||
|
|
||||||
|
if valueTrg is None or valueTrg == "":
|
||||||
|
continue;
|
||||||
|
|
||||||
if True == bCmpFile:
|
if True == bCmpFile:
|
||||||
# 상대경로로 저장했지만 혹시 절대경로 일 수도 있으니...
|
# 상대경로로 저장했지만 혹시 절대경로 일 수도 있으니...
|
||||||
if False == os.path.isabs(valueTrg):
|
if False == os.path.isabs(valueTrg):
|
||||||
valueTrg = os.path.join(pathBase, valueTrg)
|
valueTrg = os.path.join(pathBase, valueTrg)
|
||||||
|
|
||||||
if True == os.path.samefile(value, valueTrg):
|
|
||||||
|
if True == os.path.samefile(os.path.normpath(value), os.path.normpath(valueTrg)):
|
||||||
retIdx = trgIdx + 1
|
retIdx = trgIdx + 1
|
||||||
break;
|
break;
|
||||||
else:
|
else:
|
||||||
@@ -147,16 +151,19 @@ class PoleXLS(DBXLStorage):
|
|||||||
return retIdx
|
return retIdx
|
||||||
|
|
||||||
|
|
||||||
def FindInsertedImgPath(self, pathBase, pathImg):
|
def FindInsertedImgPath(self, pathBase: str, pathImg: str):
|
||||||
retIdx = self.FindRowCompValue("ImgInfo", 4, pathImg, True, pathBase )
|
retIdx = self.FindRowCompValue("ImgInfo", 4, pathImg, True, pathBase )
|
||||||
return retIdx
|
return retIdx
|
||||||
|
|
||||||
#
|
#
|
||||||
def GetAllRowValue(self, SheetName, nRow, nColCount):
|
def GetAllRowValue(self, SheetName:str, nRow:int, nColCount:int):
|
||||||
sheet = self.getSheet(SheetName)
|
sheet = self.getSheet(SheetName)
|
||||||
if sheet is None:
|
if sheet is None:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
if 0 >= nRow:
|
||||||
|
return None
|
||||||
|
|
||||||
if 0 >= nColCount:
|
if 0 >= nColCount:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|||||||
16
StoreXLS.py
16
StoreXLS.py
@@ -116,16 +116,16 @@ class DBXLStorage(ABC):
|
|||||||
|
|
||||||
# 시트를 가져온다.
|
# 시트를 가져온다.
|
||||||
# 엑셀 파일이 안 열려 있으면 None
|
# 엑셀 파일이 안 열려 있으면 None
|
||||||
def getSheet(self, sheetName, bNew = False):
|
def getSheet(self, sheetName:str, bNew:bool = False):
|
||||||
retSheet = None
|
|
||||||
|
|
||||||
if None == sheetName or "" == sheetName:
|
|
||||||
return None
|
|
||||||
|
|
||||||
if self.m_wb is None:
|
if self.m_wb is None:
|
||||||
util.DbgOut("XLS not opened", True)
|
util.DbgOut("XLS not opened", True)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
if util.IsEmptyStr(sheetName):
|
||||||
|
return None
|
||||||
|
|
||||||
|
retSheet = None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
retSheet = self.m_wb[sheetName]
|
retSheet = self.m_wb[sheetName]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
@@ -138,7 +138,7 @@ class DBXLStorage(ABC):
|
|||||||
|
|
||||||
# 데이터베이스용 엑셀 파일의 경로를 얻어온다.
|
# 데이터베이스용 엑셀 파일의 경로를 얻어온다.
|
||||||
# 폴더만 입력되었으면 파일이름을 삽입하고, 완전한 경로라면 패스
|
# 폴더만 입력되었으면 파일이름을 삽입하고, 완전한 경로라면 패스
|
||||||
def GetXLSPath(self, pathSrc):
|
def GetXLSPath(self, pathSrc:str)-> str:
|
||||||
retPath = self.path
|
retPath = self.path
|
||||||
|
|
||||||
if os.path.isdir(pathSrc):
|
if os.path.isdir(pathSrc):
|
||||||
@@ -146,7 +146,7 @@ class DBXLStorage(ABC):
|
|||||||
|
|
||||||
return retPath
|
return retPath
|
||||||
|
|
||||||
def FindLastRow(self, sheetName):
|
def FindLastRow(self, sheetName:str)-> int:
|
||||||
sheet = self.getSheet(sheetName)
|
sheet = self.getSheet(sheetName)
|
||||||
if sheet is None:
|
if sheet is None:
|
||||||
return -1
|
return -1
|
||||||
|
|||||||
4
UI.py
4
UI.py
@@ -1,6 +1,7 @@
|
|||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import UtilPack as util
|
import UtilPack as util
|
||||||
|
from pathlib import Path
|
||||||
import VWorldAPIs as vw
|
import VWorldAPIs as vw
|
||||||
import ExtEXIFData as exif
|
import ExtEXIFData as exif
|
||||||
|
|
||||||
@@ -141,7 +142,8 @@ class MyApp(QMainWindow):
|
|||||||
# Show GPS Mapped map in Right Layout
|
# Show GPS Mapped map in Right Layout
|
||||||
self.webview_map = QWebEngineView()
|
self.webview_map = QWebEngineView()
|
||||||
self.webview_map.setFixedWidth(450)
|
self.webview_map.setFixedWidth(450)
|
||||||
self.webview_map.setUrl(QUrl("file:///Users/minarinari/Workspace/Python/UtilityPole_Info/sample_Event.html"))
|
html_path = Path(__file__).resolve().parent / "sample_Event.html"
|
||||||
|
self.webview_map.setUrl(QUrl.fromLocalFile(str(html_path)))
|
||||||
|
|
||||||
# 레이아웃 설정
|
# 레이아웃 설정
|
||||||
layout = QHBoxLayout()
|
layout = QHBoxLayout()
|
||||||
|
|||||||
66
UtilPack.py
66
UtilPack.py
@@ -2,22 +2,25 @@ import os
|
|||||||
import re
|
import re
|
||||||
import math
|
import math
|
||||||
import time
|
import time
|
||||||
import rarfile
|
#import rarfile
|
||||||
import zipfile
|
import zipfile
|
||||||
import shutil
|
#import shutil
|
||||||
import difflib
|
#import difflib
|
||||||
import subprocess
|
import subprocess
|
||||||
import UtilPack as util
|
from pathlib import Path
|
||||||
|
|
||||||
m_dbgLevel = 0
|
m_dbgLevel = 0
|
||||||
listDbgStr = []
|
listDbgStr:list[str] = []
|
||||||
|
|
||||||
#
|
#
|
||||||
def IsEmptyStr(string):
|
def IsEmptyStr(string:str) -> bool:
|
||||||
|
if type(string) is not str:
|
||||||
|
return True
|
||||||
|
|
||||||
return 0 == len(string.strip())
|
return 0 == len(string.strip())
|
||||||
|
|
||||||
#
|
#
|
||||||
def GetCurrentTime():
|
def GetCurrentTime() -> str:
|
||||||
# 현재 시간을 구하고 구조체로 변환
|
# 현재 시간을 구하고 구조체로 변환
|
||||||
current_time_struct = time.localtime()
|
current_time_struct = time.localtime()
|
||||||
|
|
||||||
@@ -34,7 +37,7 @@ def GetCurrentTime():
|
|||||||
return strRet
|
return strRet
|
||||||
|
|
||||||
#for debug
|
#for debug
|
||||||
def DbgOut(strInput, bPrint = False):
|
def DbgOut(strInput:str, bPrint:bool = False):
|
||||||
strMsg = (f"{GetCurrentTime()} : {strInput}")
|
strMsg = (f"{GetCurrentTime()} : {strInput}")
|
||||||
listDbgStr.append(strMsg)
|
listDbgStr.append(strMsg)
|
||||||
|
|
||||||
@@ -46,7 +49,7 @@ def printDbgMessages():
|
|||||||
print(line)
|
print(line)
|
||||||
|
|
||||||
# 입력된 패스에서 부모 폴더를 찾는다. 0 은 자기자신 1은 부모, 숫자대로 위로.
|
# 입력된 패스에서 부모 폴더를 찾는다. 0 은 자기자신 1은 부모, 숫자대로 위로.
|
||||||
def GetParentDirName(FullPath, nUp):
|
def GetParentDirName(FullPath:str, nUp:int) -> str:
|
||||||
parts = FullPath.split(os.sep)
|
parts = FullPath.split(os.sep)
|
||||||
|
|
||||||
nTrgIdx = 0
|
nTrgIdx = 0
|
||||||
@@ -60,7 +63,7 @@ def GetParentDirName(FullPath, nUp):
|
|||||||
return parts[nTrgIdx]
|
return parts[nTrgIdx]
|
||||||
|
|
||||||
# os 모듈을 사용하여 자식 폴더를 반환
|
# os 모듈을 사용하여 자식 폴더를 반환
|
||||||
def GetSubDirectories(folder_path):
|
def GetSubDirectories(folder_path:str)-> list[str]:
|
||||||
subdirectories = [
|
subdirectories = [
|
||||||
d for d in os.listdir(folder_path)
|
d for d in os.listdir(folder_path)
|
||||||
if os.path.isdir(os.path.join(folder_path, d))]
|
if os.path.isdir(os.path.join(folder_path, d))]
|
||||||
@@ -69,8 +72,8 @@ def GetSubDirectories(folder_path):
|
|||||||
|
|
||||||
# 입력된 경로의 자식 폴더를 찾아 반환한다.
|
# 입력된 경로의 자식 폴더를 찾아 반환한다.
|
||||||
# 반환하는 리스트는 리커시브 - 손자, 증손자 폴더까지 전부 포함한다
|
# 반환하는 리스트는 리커시브 - 손자, 증손자 폴더까지 전부 포함한다
|
||||||
def GetAllSubDirectories(root_dir):
|
def GetAllSubDirectories(root_dir:str)-> list[str]:
|
||||||
subdirectories = []
|
subdirectories:list[str] = []
|
||||||
|
|
||||||
# root_dir에서 하위 디렉토리 및 파일 목록을 얻음
|
# root_dir에서 하위 디렉토리 및 파일 목록을 얻음
|
||||||
for dirpath, dirnames, filenames in os.walk(root_dir):
|
for dirpath, dirnames, filenames in os.walk(root_dir):
|
||||||
@@ -83,8 +86,8 @@ def GetAllSubDirectories(root_dir):
|
|||||||
|
|
||||||
return subdirectories
|
return subdirectories
|
||||||
|
|
||||||
def ListFileExtRcr(pathTrg, strExt):
|
def ListFileExtRcr(pathTrg:str, strExt:str) -> list[str]:
|
||||||
listRet= []
|
listRet:list[str] = []
|
||||||
|
|
||||||
# pathTrg의 하위 디렉토리 및 파일 목록을 얻음
|
# pathTrg의 하위 디렉토리 및 파일 목록을 얻음
|
||||||
for dirpath, dirnames, filenames in os.walk(pathTrg):
|
for dirpath, dirnames, filenames in os.walk(pathTrg):
|
||||||
@@ -97,7 +100,7 @@ def ListFileExtRcr(pathTrg, strExt):
|
|||||||
|
|
||||||
# 입력된 경로가 자식 폴더를 가지고 있는지 판단한다.- 최종 폴더인지 여부
|
# 입력된 경로가 자식 폴더를 가지고 있는지 판단한다.- 최종 폴더인지 여부
|
||||||
# 자식이 없으면 True, 자식이 있으면 False
|
# 자식이 없으면 True, 자식이 있으면 False
|
||||||
def IsFinalFolder(path):
|
def IsFinalFolder(path:str) -> bool:
|
||||||
bRet = True
|
bRet = True
|
||||||
|
|
||||||
contents = os.listdir(path)
|
contents = os.listdir(path)
|
||||||
@@ -109,12 +112,12 @@ def IsFinalFolder(path):
|
|||||||
return bRet;
|
return bRet;
|
||||||
|
|
||||||
# 어떤 경로 안에서 특정 확장자의 파일을 뽑아내어 그 리스트를 반환한다.
|
# 어떤 경로 안에서 특정 확장자의 파일을 뽑아내어 그 리스트를 반환한다.
|
||||||
def FindFileFromExt(path, ext):
|
def FindFileFromExt(path:str, ext:str) -> list[str]:
|
||||||
bDot = False
|
bDot = False
|
||||||
if 0 <= ext.find('.'):
|
if 0 <= ext.find('.'):
|
||||||
bDot = True
|
bDot = True
|
||||||
|
|
||||||
listRet = []
|
listRet:list[str] = []
|
||||||
if False == os.path.exists(path):
|
if False == os.path.exists(path):
|
||||||
return listRet
|
return listRet
|
||||||
|
|
||||||
@@ -131,7 +134,7 @@ def FindFileFromExt(path, ext):
|
|||||||
|
|
||||||
|
|
||||||
# 파일 이름에서 확장자를 뽑아낸다. True : '.' 을 포함한다.
|
# 파일 이름에서 확장자를 뽑아낸다. True : '.' 을 포함한다.
|
||||||
def GetExtStr(file_path, bDot = True):
|
def GetExtStr(file_path:str, bDot:bool = True) -> str:
|
||||||
retStr = ""
|
retStr = ""
|
||||||
# 파일 경로에서 마지막 점을 찾아 확장자를 추출
|
# 파일 경로에서 마지막 점을 찾아 확장자를 추출
|
||||||
last_dot_index = file_path.rfind('.')
|
last_dot_index = file_path.rfind('.')
|
||||||
@@ -147,7 +150,7 @@ def GetExtStr(file_path, bDot = True):
|
|||||||
|
|
||||||
|
|
||||||
# 문자열에 포함된 단어를 지운다.
|
# 문자열에 포함된 단어를 지운다.
|
||||||
def RmvSubString(mainString, subString):
|
def RmvSubString(mainString:str, subString:str) -> str:
|
||||||
# 문자열에서 부분 문자열의 인덱스를 찾습니다.
|
# 문자열에서 부분 문자열의 인덱스를 찾습니다.
|
||||||
strIdx = mainString.find(subString)
|
strIdx = mainString.find(subString)
|
||||||
if strIdx == -1: # 부분 문자열이 존재하지 않으면 그대로 반환합니다.
|
if strIdx == -1: # 부분 문자열이 존재하지 않으면 그대로 반환합니다.
|
||||||
@@ -158,12 +161,12 @@ def RmvSubString(mainString, subString):
|
|||||||
# 부분 문자열을 제거하고 새로운 문자열을 반환합니다.
|
# 부분 문자열을 제거하고 새로운 문자열을 반환합니다.
|
||||||
return mainString[:strIdx] + mainString[endIdx:]
|
return mainString[:strIdx] + mainString[endIdx:]
|
||||||
|
|
||||||
def ExtractZIP(zip_file, extract_to):
|
def ExtractZIP(zip_file:str, extract_to:str):
|
||||||
with zipfile.ZipFile(zip_file, 'r') as zf:
|
with zipfile.ZipFile(zip_file, 'r') as zf:
|
||||||
zf.extractall(extract_to)
|
zf.extractall(extract_to)
|
||||||
|
|
||||||
#
|
#
|
||||||
def CreateZIP(output_zip, *files):
|
def CreateZIP(output_zip:str, *files):
|
||||||
with zipfile.ZipFile(output_zip, 'w') as zf:
|
with zipfile.ZipFile(output_zip, 'w') as zf:
|
||||||
for file in files:
|
for file in files:
|
||||||
pathTemp = os.path.join('root', os.path.basename(file))
|
pathTemp = os.path.join('root', os.path.basename(file))
|
||||||
@@ -176,7 +179,7 @@ def CreateZIP(output_zip, *files):
|
|||||||
return bRet
|
return bRet
|
||||||
|
|
||||||
# 파일 리스트에 들어있는 파일만 골라서 압축을 합니다. 상대경로를 제거하는게 기본값.
|
# 파일 리스트에 들어있는 파일만 골라서 압축을 합니다. 상대경로를 제거하는게 기본값.
|
||||||
def CreateZIPShell(zipName, *files, bRmvRPath = True):
|
def CreateZIPShell(zipName:str, *files, bRmvRPath:bool = True)-> bool:
|
||||||
command = "zip "
|
command = "zip "
|
||||||
|
|
||||||
if True == bRmvRPath:
|
if True == bRmvRPath:
|
||||||
@@ -206,7 +209,7 @@ def CreateZIPShell(zipName, *files, bRmvRPath = True):
|
|||||||
return bRet
|
return bRet
|
||||||
|
|
||||||
# 특정 확장자만 쉘을 이용해서 압축한다
|
# 특정 확장자만 쉘을 이용해서 압축한다
|
||||||
def CreateZIPShExt(zipName, TrgExt):
|
def CreateZIPShExt(zipName:str, TrgExt:str):
|
||||||
command = f"zip -j {zipName} *.{TrgExt}"
|
command = f"zip -j {zipName} *.{TrgExt}"
|
||||||
|
|
||||||
result = subprocess.run(command, shell=True, capture_output=True, text=True)
|
result = subprocess.run(command, shell=True, capture_output=True, text=True)
|
||||||
@@ -218,7 +221,7 @@ def CreateZIPShExt(zipName, TrgExt):
|
|||||||
return bRet
|
return bRet
|
||||||
|
|
||||||
# JSON 을 트리 구조로 출력한다.
|
# JSON 을 트리 구조로 출력한다.
|
||||||
def PrintJSONTree(data, indent=0):
|
def PrintJSONTree(data, indent:int=0):
|
||||||
if isinstance(data, dict):
|
if isinstance(data, dict):
|
||||||
for key, value in data.items():
|
for key, value in data.items():
|
||||||
print(' ' * indent + str(key))
|
print(' ' * indent + str(key))
|
||||||
@@ -230,7 +233,7 @@ def PrintJSONTree(data, indent=0):
|
|||||||
print(' ' * indent + str(data))
|
print(' ' * indent + str(data))
|
||||||
|
|
||||||
# 세로 크기를 가로 비율대로 줄여서 반환 (가로를 기준으로 세로 길이)
|
# 세로 크기를 가로 비율대로 줄여서 반환 (가로를 기준으로 세로 길이)
|
||||||
def GetRSzHeight(nWidth, nHeight, nTrgW):
|
def GetRSzHeight(nWidth:int, nHeight:int, nTrgW:int) -> int:
|
||||||
if nWidth <= 0 or nTrgW <= 0:
|
if nWidth <= 0 or nTrgW <= 0:
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
@@ -240,7 +243,7 @@ def GetRSzHeight(nWidth, nHeight, nTrgW):
|
|||||||
return nTrgH
|
return nTrgH
|
||||||
|
|
||||||
# 가로 크기를 세로 비율대로 줄여서 반환 (세로를 기준으로 가로 길이)
|
# 가로 크기를 세로 비율대로 줄여서 반환 (세로를 기준으로 가로 길이)
|
||||||
def GetRSzWidth(nWidth, nHeight, nTrgH):
|
def GetRSzWidth(nWidth:int, nHeight:int, nTrgH:int) -> int:
|
||||||
if nHeight <= 0 or nTrgH <= 0:
|
if nHeight <= 0 or nTrgH <= 0:
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
@@ -252,15 +255,15 @@ def GetRSzWidth(nWidth, nHeight, nTrgH):
|
|||||||
# 엑셀 열 너비는 약 7.5픽셀 단위
|
# 엑셀 열 너비는 약 7.5픽셀 단위
|
||||||
# 엑셀 행 높이는 약 25픽셀 단위
|
# 엑셀 행 높이는 약 25픽셀 단위
|
||||||
# 이대로는 사이즈가 안 맞아서 적당히 곱해줌
|
# 이대로는 사이즈가 안 맞아서 적당히 곱해줌
|
||||||
def GetCellSize(nImgW, nImgH, DPI = 96):
|
def GetCellSize(nImgW:int, nImgH:int, DPI:int = 96) -> tuple[float, float]:
|
||||||
column_width = (nImgW / DPI) * 7.5 * 2
|
column_width = (nImgW / DPI) * 7.5 * 2
|
||||||
row_height = (nImgH / DPI) * 25 * 3
|
row_height = (nImgH / DPI) * 25 * 3
|
||||||
|
|
||||||
return column_width, row_height
|
return column_width, row_height
|
||||||
|
|
||||||
def GetSheetInfoValue(text):
|
def GetSheetInfoValue(text:str) -> tuple[str, list[str]]:
|
||||||
if text is None:
|
if IsEmptyStr(text):
|
||||||
return "", ""
|
return "", []
|
||||||
|
|
||||||
pattern = r'<(.*?)>'
|
pattern = r'<(.*?)>'
|
||||||
matches = re.findall(pattern, text)
|
matches = re.findall(pattern, text)
|
||||||
@@ -269,7 +272,7 @@ def GetSheetInfoValue(text):
|
|||||||
return cleaned, matches
|
return cleaned, matches
|
||||||
|
|
||||||
# 두 GPS 좌표 간 거리 구하기 (미터)
|
# 두 GPS 좌표 간 거리 구하기 (미터)
|
||||||
def GetDistanceGPS(lat1, lon1, lat2, lon2):
|
def GetDistanceGPS(lat1:float, lon1:float, lat2:float, lon2:float) -> float:
|
||||||
# 지구의 반지름 (단위: 미터)
|
# 지구의 반지름 (단위: 미터)
|
||||||
R = 6371008.8
|
R = 6371008.8
|
||||||
|
|
||||||
@@ -295,4 +298,5 @@ def GetDistanceGPS(lat1, lon1, lat2, lon2):
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
402
UtilPack2.py
402
UtilPack2.py
@@ -1,402 +0,0 @@
|
|||||||
import os
|
|
||||||
import re
|
|
||||||
import time
|
|
||||||
import uuid
|
|
||||||
import rarfile
|
|
||||||
import zipfile
|
|
||||||
import shutil
|
|
||||||
import difflib
|
|
||||||
import subprocess
|
|
||||||
import hashlib
|
|
||||||
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
m_dbgLevel = 0
|
|
||||||
listDbgStr: list[str] = []
|
|
||||||
|
|
||||||
#for debug
|
|
||||||
def DbgOut(strInput:str, bPrint:bool = False):
|
|
||||||
strMsg = (f"{GetCurrentTime()} : {strInput}")
|
|
||||||
listDbgStr.append(strMsg)
|
|
||||||
|
|
||||||
if True == bPrint:
|
|
||||||
print(strMsg)
|
|
||||||
|
|
||||||
#
|
|
||||||
def printDbgMessages():
|
|
||||||
for line in listDbgStr:
|
|
||||||
print(line)
|
|
||||||
|
|
||||||
#
|
|
||||||
def SaveDbgMessages(Path: str):
|
|
||||||
try:
|
|
||||||
with open(Path, 'w') as file:
|
|
||||||
for line in listDbgStr:
|
|
||||||
file.write(line + "\n")
|
|
||||||
except IOError:
|
|
||||||
DbgOut(f"Error: Could not write to the file at {Path}.", True)
|
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
def GetCurrentTime() -> str:
|
|
||||||
# 현재 시간을 구하고 구조체로 변환
|
|
||||||
current_time_struct = time.localtime()
|
|
||||||
|
|
||||||
# 구조체에서 연, 월, 일, 시간, 분, 초를 추출
|
|
||||||
year = current_time_struct.tm_year
|
|
||||||
month = current_time_struct.tm_mon
|
|
||||||
day = current_time_struct.tm_mday
|
|
||||||
hour = current_time_struct.tm_hour
|
|
||||||
minute = current_time_struct.tm_min
|
|
||||||
second = current_time_struct.tm_sec
|
|
||||||
|
|
||||||
strRet = (f"{year}/{month}/{day}_{hour}:{minute}:{second}")
|
|
||||||
|
|
||||||
return strRet
|
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
def IsEmptyStr(string: str) -> bool:
|
|
||||||
temp = f"{string}"
|
|
||||||
return 0 == len(temp.strip())
|
|
||||||
|
|
||||||
# 입력된 경로의 자식 폴더를 찾아 반환한다.
|
|
||||||
# 반환하는 리스트는 리커시브 - 손자, 증손자 폴더까지 전부 포함한다
|
|
||||||
def ListSubDirectories(root_dir:str)-> list[str]:
|
|
||||||
subdirectories: list[str] = []
|
|
||||||
|
|
||||||
# root_dir에서 하위 디렉토리 및 파일 목록을 얻음
|
|
||||||
for dirpath, dirnames, filenames in os.walk(root_dir):
|
|
||||||
# 하위 디렉토리 목록을 반복하며 하위 디렉토리만 추출
|
|
||||||
for dirname in dirnames:
|
|
||||||
path = os.path.join(dirpath, dirname)
|
|
||||||
|
|
||||||
if True == IsFinalFolder(path):
|
|
||||||
subdirectories.append(path)
|
|
||||||
|
|
||||||
return subdirectories
|
|
||||||
|
|
||||||
# 자식 폴더를 구해온다. 직계 자식만
|
|
||||||
def ListChildDirectories(pathDir:str, bVisibleOnly: bool = True) -> list[str]:
|
|
||||||
listRet:list[str] = []
|
|
||||||
listTemp = os.listdir(pathDir)
|
|
||||||
for name in listTemp:
|
|
||||||
pathChild = os.path.join(pathDir, name)
|
|
||||||
if os.path.isdir(pathChild):
|
|
||||||
if True == bVisibleOnly and name.startswith('.'):
|
|
||||||
continue
|
|
||||||
|
|
||||||
listRet.append(name)
|
|
||||||
|
|
||||||
return listRet
|
|
||||||
|
|
||||||
# PathDir 에 지정된 폴더의 파일목록만 구해온다. 자식 폴더에 있는건 무시.
|
|
||||||
def ListContainFiles(pathDir:str, bVisibleOnly:bool = True)-> list[str]:
|
|
||||||
listRet:list[str] = []
|
|
||||||
listTemp = os.listdir(pathDir)
|
|
||||||
for name in listTemp:
|
|
||||||
pathChild = os.path.join(pathDir, name)
|
|
||||||
if os.path.isfile(pathChild):
|
|
||||||
if True == bVisibleOnly and name.startswith('.'):
|
|
||||||
continue
|
|
||||||
|
|
||||||
listRet.append(name)
|
|
||||||
|
|
||||||
return listRet
|
|
||||||
|
|
||||||
# 리스트에 담긴 확장자와 같은 확장자를 가진 파일을 찾아서 리스트로 반환
|
|
||||||
def ListFileExtRcr(pathTrg: str, listExt: list[str]) -> list[str]:
|
|
||||||
listRet:list[str] = []
|
|
||||||
|
|
||||||
# pathTrg의 하위 디렉토리 및 파일 목록을 얻음
|
|
||||||
for dirpath, dirnames, filenames in os.walk(pathTrg):
|
|
||||||
for file in filenames:
|
|
||||||
extTmp = GetExtStr(file)
|
|
||||||
if extTmp.lower() in listExt and not file.startswith('.'):
|
|
||||||
listRet.append(os.path.join(dirpath, file))
|
|
||||||
|
|
||||||
return listRet
|
|
||||||
|
|
||||||
# 입력된 경로에서 부모 폴더를 찾는다. 0 은 자기자신 1은 부모, 숫자대로 위로.
|
|
||||||
def GetParentDirName(FullPath : str, nUp : int)->str:
|
|
||||||
parts = FullPath.split(os.sep)
|
|
||||||
|
|
||||||
nTrgIdx = 0
|
|
||||||
if nUp < len(parts):
|
|
||||||
nTrgIdx = len(parts) -nUp -1
|
|
||||||
elif nUp < 0:
|
|
||||||
nTrgIdx = len(parts) - 1
|
|
||||||
else:
|
|
||||||
nTrgIdx = 0
|
|
||||||
|
|
||||||
return parts[nTrgIdx]
|
|
||||||
|
|
||||||
# 입력된 경로가 자식 폴더를 가지고 있는지 판단한다.- 최종 폴더인지 여부
|
|
||||||
# 자식이 없으면 True, 자식이 있으면 False
|
|
||||||
def IsFinalFolder(path : str) -> bool:
|
|
||||||
bRet = True
|
|
||||||
|
|
||||||
contents = os.listdir(path)
|
|
||||||
for item in contents:
|
|
||||||
if True == os.path.isdir(item):
|
|
||||||
bRet = False
|
|
||||||
break
|
|
||||||
|
|
||||||
return bRet;
|
|
||||||
|
|
||||||
|
|
||||||
# 어떤 경로 안에서 특정 확장자의 파일을 뽑아내어 그 리스트를 반환한다.
|
|
||||||
def FindFileFromExt(path: str, ext: str)-> list[str]:
|
|
||||||
bDot = False
|
|
||||||
if 0 <= ext.find('.'):
|
|
||||||
bDot = True
|
|
||||||
|
|
||||||
listRet:list[str] = []
|
|
||||||
if False == os.path.exists(path):
|
|
||||||
return listRet
|
|
||||||
|
|
||||||
contents = os.listdir(path)
|
|
||||||
for item in contents:
|
|
||||||
if True == os.path.isdir(item):
|
|
||||||
continue
|
|
||||||
|
|
||||||
extItem = GetExtStr(item, bDot)
|
|
||||||
if extItem.lower() == ext.lower():
|
|
||||||
listRet.append(item)
|
|
||||||
|
|
||||||
return listRet
|
|
||||||
|
|
||||||
# 파일 이름에서 확장자를 뽑아낸다. True : '.' 을 포함한다.
|
|
||||||
def GetExtStr(file_path: str, bDot: bool = True)-> str:
|
|
||||||
retStr = ""
|
|
||||||
# 파일 경로에서 마지막 점을 찾아 확장자를 추출
|
|
||||||
last_dot_index = file_path.rfind('.')
|
|
||||||
if last_dot_index == -1:
|
|
||||||
retStr = "" # 점이 없는 경우 확장자가 없음
|
|
||||||
else:
|
|
||||||
if True == bDot:
|
|
||||||
retStr = file_path[last_dot_index:]
|
|
||||||
else:
|
|
||||||
retStr = file_path[last_dot_index+1:]
|
|
||||||
|
|
||||||
return retStr
|
|
||||||
|
|
||||||
# 문자열에 포함된 단어를 지운다.
|
|
||||||
def RmvSubString(mainString: str, subString: str)-> str:
|
|
||||||
# 문자열에서 부분 문자열의 인덱스를 찾습니다.
|
|
||||||
strIdx = mainString.find(subString)
|
|
||||||
if strIdx == -1: # 부분 문자열이 존재하지 않으면 그대로 반환합니다.
|
|
||||||
return mainString
|
|
||||||
|
|
||||||
endIdx = strIdx + len(subString)
|
|
||||||
|
|
||||||
# 부분 문자열을 제거하고 새로운 문자열을 반환합니다.
|
|
||||||
return mainString[:strIdx] + mainString[endIdx:]
|
|
||||||
|
|
||||||
#
|
|
||||||
def ExtractZIP(zip_file: str, extract_to: str):
|
|
||||||
with zipfile.ZipFile(zip_file, 'r') as zf:
|
|
||||||
zf.extractall(extract_to)
|
|
||||||
|
|
||||||
#
|
|
||||||
def CreateZIP(output_zip: str, files: list[str]) -> bool:
|
|
||||||
with zipfile.ZipFile(output_zip, 'w') as zf:
|
|
||||||
for file in files:
|
|
||||||
pathTemp = os.path.join('root', os.path.basename(file))
|
|
||||||
zf.write(file, pathTemp)
|
|
||||||
|
|
||||||
bRet = False
|
|
||||||
if os.path.exists(output_zip):
|
|
||||||
bRet = True
|
|
||||||
|
|
||||||
return bRet
|
|
||||||
|
|
||||||
# 파일 리스트에 들어있는 파일만 골라서 압축을 합니다. 상대경로를 제거하는게 기본값.
|
|
||||||
def CreateZIPShell(zipName: str, files: list[str], bRmvRPath: bool = True) -> bool:
|
|
||||||
command = "zip "
|
|
||||||
|
|
||||||
if True == bRmvRPath:
|
|
||||||
command += "-j "
|
|
||||||
|
|
||||||
command += f"\"{zipName}\" "
|
|
||||||
|
|
||||||
# 이중 리스트인 이유를 모르겠다.
|
|
||||||
for file in files:
|
|
||||||
command += f"\"{file}\" "
|
|
||||||
|
|
||||||
result = subprocess.run(command, shell=True, capture_output=True, text=True)
|
|
||||||
|
|
||||||
bRet = False
|
|
||||||
if 0 == result.returncode:
|
|
||||||
bRet = True
|
|
||||||
|
|
||||||
return bRet
|
|
||||||
|
|
||||||
# 특정 확장자만 쉘을 이용해서 압축한다
|
|
||||||
def CreateZIPShExt(zipName: str, TrgExt: str)-> bool:
|
|
||||||
command = f"zip -j {zipName} *.{TrgExt}"
|
|
||||||
|
|
||||||
result = subprocess.run(command, shell=True, capture_output=True, text=True)
|
|
||||||
|
|
||||||
bRet = False
|
|
||||||
if 0 == result.returncode:
|
|
||||||
bRet = True
|
|
||||||
|
|
||||||
return bRet
|
|
||||||
|
|
||||||
# 압축 파일 내의 모든 파일 및 디렉토리 목록 가져오기
|
|
||||||
def GetZipContentList(path: str) -> list[str]:
|
|
||||||
if True == IsEmptyStr(path) or not os.path.isfile(path):
|
|
||||||
return []
|
|
||||||
|
|
||||||
listRet = []
|
|
||||||
with zipfile.ZipFile( path , 'r') as zip_file:
|
|
||||||
listRet = zip_file.namelist()
|
|
||||||
|
|
||||||
return listRet
|
|
||||||
|
|
||||||
#
|
|
||||||
def GetZippedFileByte(pathZip: str, FileName: str) -> bytes:
|
|
||||||
retBytes:bytes = bytes()
|
|
||||||
if True == os.path.isfile(pathZip):
|
|
||||||
with zipfile.ZipFile( pathZip , 'r') as zip_file:
|
|
||||||
# 압축 파일 내의 특정 파일을 읽기
|
|
||||||
with zip_file.open(FileName) as file:
|
|
||||||
retBytes = file.read()
|
|
||||||
|
|
||||||
return retBytes
|
|
||||||
|
|
||||||
# JSON 을 트리 구조로 출력한다.
|
|
||||||
def PrintJSONTree(data, indent: int=0 ) -> None:
|
|
||||||
if isinstance(data, dict):
|
|
||||||
for key, value in data.items():
|
|
||||||
print(' ' * indent + str(key))
|
|
||||||
PrintJSONTree(value, indent + 1)
|
|
||||||
elif isinstance(data, list):
|
|
||||||
for item in data:
|
|
||||||
PrintJSONTree(item, indent)
|
|
||||||
else:
|
|
||||||
print(' ' * indent + str(data))
|
|
||||||
|
|
||||||
|
|
||||||
# 랜덤 UUID 생성
|
|
||||||
def UUIDGenRandom():
|
|
||||||
random_uuid = uuid.uuid4()
|
|
||||||
return random_uuid
|
|
||||||
|
|
||||||
# 이름을 기반으로 UUID 생성
|
|
||||||
def UUIDGenName(SeedName:str):
|
|
||||||
namespace_uuid = uuid.uuid5(uuid.NAMESPACE_DNS, SeedName)
|
|
||||||
return namespace_uuid
|
|
||||||
|
|
||||||
#
|
|
||||||
def GetTextInBrakets(text:str)-> list[str]:
|
|
||||||
return re.findall(r'\[(.*?)\]', text)
|
|
||||||
|
|
||||||
#파일의 해시를 계산하는 함수.
|
|
||||||
#Args:
|
|
||||||
# strFilePath (str): 파일 경로
|
|
||||||
# method (str): 사용할 해시 알고리즘 ('md5', 'sha1', 'sha256' 등)
|
|
||||||
#Returns:
|
|
||||||
# str: 계산된 해시값 (16진수 문자열)
|
|
||||||
def CalculateFileHash(strFilePath: str, method: str="sha256")-> str:
|
|
||||||
funcHash = getattr(hashlib, method)()
|
|
||||||
|
|
||||||
with open(strFilePath, "rb") as f:
|
|
||||||
while True:
|
|
||||||
chunk = f.read(4096)
|
|
||||||
if not chunk:
|
|
||||||
break
|
|
||||||
|
|
||||||
funcHash.update(chunk)
|
|
||||||
|
|
||||||
return funcHash.hexdigest()
|
|
||||||
|
|
||||||
#파일의 해시를 비교하여 무결성 검증.
|
|
||||||
#Args:
|
|
||||||
# file_path (str): 파일 경로
|
|
||||||
# expected_hash (str): 기대하는 해시값
|
|
||||||
# method (str): 사용할 해시 알고리즘
|
|
||||||
#Returns:
|
|
||||||
# bool: 파일이 정상인지 여부
|
|
||||||
"""
|
|
||||||
# 사용 예시
|
|
||||||
if __name__ == "__main__":
|
|
||||||
file_to_check = "example.txt"
|
|
||||||
known_good_hash = "5d41402abc4b2a76b9719d911017c592" # 예시 (MD5)
|
|
||||||
|
|
||||||
is_valid = verify_file(file_to_check, known_good_hash, method='md5')
|
|
||||||
if is_valid:
|
|
||||||
print("파일이 정상입니다!")
|
|
||||||
else:
|
|
||||||
print("파일이 손상되었거나 다릅니다!")
|
|
||||||
"""
|
|
||||||
def VerifyIsValidFile(strPath: str, strCompHash: str, strMethod: str="sha256")->bool:
|
|
||||||
Hash_Calcd = CalculateFileHash(strPath, strMethod)
|
|
||||||
|
|
||||||
return strCompHash.lower() == Hash_Calcd.lower()
|
|
||||||
|
|
||||||
|
|
||||||
# 세로 크기를 가로 비율대로 줄여서 반환 (가로를 기준으로 세로 길이)
|
|
||||||
def GetRSzHeight(nWidth, nHeight, nTrgW):
|
|
||||||
if nWidth <= 0 or nTrgW <= 0:
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
fRatio = nTrgW / nWidth
|
|
||||||
nTrgH = round(nHeight * fRatio)
|
|
||||||
|
|
||||||
return nTrgH
|
|
||||||
|
|
||||||
# 가로 크기를 세로 비율대로 줄여서 반환 (세로를 기준으로 가로 길이)
|
|
||||||
def GetRSzWidth(nWidth, nHeight, nTrgH):
|
|
||||||
if nHeight <= 0 or nTrgH <= 0:
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
fRatio = nTrgH / nHeight
|
|
||||||
nTrgW = round(nWidth * fRatio)
|
|
||||||
|
|
||||||
return nTrgW
|
|
||||||
|
|
||||||
# 엑셀 열 너비는 약 7.5픽셀 단위
|
|
||||||
# 엑셀 행 높이는 약 25픽셀 단위
|
|
||||||
# 이대로는 사이즈가 안 맞아서 적당히 곱해줌
|
|
||||||
def GetCellSize(nImgW, nImgH, DPI = 96):
|
|
||||||
column_width = (nImgW / DPI) * 7.5 * 2
|
|
||||||
row_height = (nImgH / DPI) * 25 * 3
|
|
||||||
|
|
||||||
return column_width, row_height
|
|
||||||
|
|
||||||
def GetSheetInfoValue(text):
|
|
||||||
if text is None:
|
|
||||||
return "", ""
|
|
||||||
|
|
||||||
pattern = r'<(.*?)>'
|
|
||||||
matches = re.findall(pattern, text)
|
|
||||||
cleaned = re.sub(pattern, '', text)
|
|
||||||
|
|
||||||
return cleaned, matches
|
|
||||||
|
|
||||||
# 두 GPS 좌표 간 거리 구하기 (미터)
|
|
||||||
def GetDistanceGPS(lat1, lon1, lat2, lon2):
|
|
||||||
# 지구의 반지름 (단위: 미터)
|
|
||||||
R = 6371008.8
|
|
||||||
|
|
||||||
# 위도와 경도를 라디안으로 변환
|
|
||||||
lat1 = math.radians(lat1)
|
|
||||||
lon1 = math.radians(lon1)
|
|
||||||
lat2 = math.radians(lat2)
|
|
||||||
lon2 = math.radians(lon2)
|
|
||||||
|
|
||||||
# 차이 계산
|
|
||||||
dlat = lat2 - lat1
|
|
||||||
dlon = lon2 - lon1
|
|
||||||
|
|
||||||
# Haversine 공식 적용
|
|
||||||
a = math.sin(dlat / 2)**2 + math.cos(lat1) * math.cos(lat2) * math.sin(dlon / 2)**2
|
|
||||||
c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a))
|
|
||||||
|
|
||||||
# 거리 계산
|
|
||||||
distance = R * c
|
|
||||||
|
|
||||||
return distance
|
|
||||||
|
|
||||||
@@ -35,10 +35,8 @@ pure_eval==0.2.3
|
|||||||
Pygments==2.18.0
|
Pygments==2.18.0
|
||||||
pyproj==3.7.2
|
pyproj==3.7.2
|
||||||
PyQt5==5.15.11
|
PyQt5==5.15.11
|
||||||
PyQt5-Qt5==5.15.17
|
|
||||||
PyQt5_sip==12.17.1
|
PyQt5_sip==12.17.1
|
||||||
PyQtWebEngine==5.15.7
|
PyQtWebEngine==5.15.7
|
||||||
PyQtWebEngine-Qt5==5.15.17
|
|
||||||
pytesseract==0.3.13
|
pytesseract==0.3.13
|
||||||
python-dateutil==2.9.0.post0
|
python-dateutil==2.9.0.post0
|
||||||
rarfile==4.2
|
rarfile==4.2
|
||||||
|
|||||||
6
test.py
6
test.py
@@ -5,9 +5,9 @@ import PoleXLS as myxl
|
|||||||
import VWorldAPIs as vwapi
|
import VWorldAPIs as vwapi
|
||||||
|
|
||||||
|
|
||||||
ImgDirPath = "/Volumes/ExSSD/Working/Images"
|
ImgDirPath = util.NormalizePath("/Volumes/ExSSD/Working/Images")
|
||||||
ImgSortedPath = "/Volumes/ExSSD/Working/Sorted"
|
ImgSortedPath = util.NormalizePath("/Volumes/ExSSD/Working/Sorted")
|
||||||
XlsDirPath = "/Users/minarinari/Workspace/Python/UtilityPole_Info"
|
XlsDirPath = util.NormalizePath("/Users/minarinari/Workspace/Python/UtilityPole_Info")
|
||||||
|
|
||||||
indexFile = ".index.maps"
|
indexFile = ".index.maps"
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user