gdbtopostgislist.py 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481
  1. """
  2. ***************************************************************************
  3. gdbtopostgislist.py
  4. ---------------------
  5. Date : November 2012
  6. Copyright : (C) 2012 by Victor Olaya
  7. Email : volayaf at gmail dot com
  8. ***************************************************************************
  9. * *
  10. * This program is free software; you can redistribute it and/or modify *
  11. * it under the terms of the GNU General Public License as published by *
  12. * the Free Software Foundation; either version 2 of the License, or *
  13. * (at your option) any later version. *
  14. * *
  15. ***************************************************************************
  16. """
  17. __author__ = 'wanger'
  18. __date__ = 'November 2024'
  19. __copyright__ = '(C) 2024, wanger'
  20. import os
  21. import subprocess
  22. from PyQt5.QtCore import QCoreApplication
  23. from PyQt5.QtGui import QIcon
  24. from PyQt5.QtWidgets import QMessageBox
  25. from osgeo import ogr
  26. from qgis._core import QgsProcessingParameterDefinition, QgsProcessingAlgorithm, QgsProcessingParameterFolderDestination
  27. from qgis.core import (QgsProcessing,
  28. QgsProcessingParameterFeatureSource,
  29. QgsProcessingParameterString,
  30. QgsProcessingParameterDateTime,
  31. QgsProcessingParameterEnum,
  32. QgsProcessingParameterCrs,
  33. QgsProcessingParameterField,
  34. QgsProcessingParameterExtent,
  35. QgsProcessingParameterBoolean,
  36. QgsProcessingParameterProviderConnection,
  37. QgsProcessingParameterDatabaseSchema,
  38. QgsProcessingParameterDatabaseTable,
  39. QgsProviderRegistry,
  40. QgsProcessingException,
  41. QgsCoordinateReferenceSystem,
  42. QgsProviderConnectionException,
  43. QgsDataSourceUri)
  44. from processing.algs.gdal.GdalAlgorithm import GdalAlgorithm
  45. from processing.algs.gdal.GdalUtils import GdalUtils
  46. from processing.tools.PrintUtils import printStr
  47. from processing.tools.PostgreSQL.PostgreSQL import PostgreSQL
  48. from processing.tools.system import isWindows
  49. from processing.tools.PrintUtils import getLastPrint
  50. from processing.tools.FileListPrintUtils import getFileListPrint
  51. pluginPath = os.path.normpath(os.path.join(
  52. os.path.split(os.path.dirname(__file__))[0], os.pardir))
  53. class GdbToPostGisList(GdalAlgorithm):
  54. UID = 'UID'
  55. DATABASE = 'DATABASE'
  56. INPUTGDB = 'INPUTGDB'
  57. SHAPE_ENCODING = 'SHAPE_ENCODING'
  58. GTYPE = 'GTYPE'
  59. GEOMTYPE = ['', 'NONE', 'GEOMETRY', 'POINT', 'LINESTRING', 'POLYGON', 'GEOMETRYCOLLECTION', 'MULTIPOINT',
  60. 'MULTIPOLYGON', 'MULTILINESTRING', 'CIRCULARSTRING', 'COMPOUNDCURVE', 'CURVEPOLYGON', 'MULTICURVE',
  61. 'MULTISURFACE']
  62. S_SRS = 'S_SRS'
  63. T_SRS = 'T_SRS'
  64. A_SRS = 'A_SRS'
  65. HOST = 'HOST'
  66. PORT = 'PORT'
  67. USER = 'USER'
  68. DBNAME = 'DBNAME'
  69. PASSWORD = 'PASSWORD'
  70. SCHEMA = 'SCHEMA'
  71. TABLE = 'TABLE'
  72. TABLE_ALIAS = 'TABLE_ALIAS'
  73. PK = 'PK'
  74. PRIMARY_KEY = 'PRIMARY_KEY'
  75. GEOCOLUMN = 'GEOCOLUMN'
  76. DIM = 'DIM'
  77. DIMLIST = ['2', '3', '4']
  78. SIMPLIFY = 'SIMPLIFY'
  79. SEGMENTIZE = 'SEGMENTIZE'
  80. SPAT = 'SPAT'
  81. CLIP = 'CLIP'
  82. WHERE = 'WHERE'
  83. GT = 'GT'
  84. OVERWRITE = 'OVERWRITE'
  85. APPEND = 'APPEND'
  86. ADDFIELDS = 'ADDFIELDS'
  87. LAUNDER = 'LAUNDER'
  88. INDEX = 'INDEX'
  89. SKIPFAILURES = 'SKIPFAILURES'
  90. PRECISION = 'PRECISION'
  91. PROMOTETOMULTI = 'PROMOTETOMULTI'
  92. OPTIONS = 'OPTIONS'
  93. # TODO 三亚项目新追加入库属性
  94. VECTOR_YEAR = 'VECTOR_YEAR'
  95. VECTOR_XZQH = 'VECTOR_XZQH'
  96. VECTOR_XMLX = 'VECTOR_XMLX'
  97. VECTOR_SJLY = 'VECTOR_SJLY'
  98. VECTOR_YWLX = 'VECTOR_YWLX'
  99. SOURCE_TYPE = 'SOURCE_TYPE'
  100. XZQH_FIELD = 'XZQH_FIELD'
  101. VECTOR_GLBM = 'VECTOR_GLBM'
  102. VECTOR_ZYML = 'VECTOR_ZYML'
  103. Metadata_storage = 'Metadata_storage'
  104. XLK = 'XLK'
  105. XLKXZX = ['选择一', '选择二', '选择三', '选择四', '选择五']
  106. selectedValue = "selectedValue"
  107. def __init__(self):
  108. super().__init__()
  109. def initAlgorithm(self, config=None):
  110. self.addParameter(QgsProcessingParameterString(self.UID,
  111. self.tr('唯一数据标识前缀,用于更新和删除关联使用'), "",
  112. optional=True))
  113. folderParameter = QgsProcessingParameterFolderDestination(self.INPUTGDB, self.tr('GDB数据文件夹'))
  114. print(folderParameter)
  115. self.addParameter(folderParameter)
  116. db_param = QgsProcessingParameterProviderConnection(
  117. self.DATABASE,
  118. self.tr('数据库'), provider='postgres',
  119. defaultValue=self.pgcoon["host"])
  120. self.addParameter(db_param)
  121. schema_param = QgsProcessingParameterDatabaseSchema(
  122. self.SCHEMA,
  123. self.tr('模式'), defaultValue=self.pgcoon["schema"], connectionParameterName=self.DATABASE,
  124. optional=False)
  125. self.addParameter(schema_param)
  126. # table_param = QgsProcessingParameterString(
  127. # self.TABLE,
  128. # self.tr('表名'), defaultValue="", optional=True)
  129. # self.addParameter(table_param)
  130. # tablealias_param = QgsProcessingParameterString(
  131. # self.TABLE_ALIAS,
  132. # self.tr('别名'), defaultValue="", optional=True)
  133. # self.addParameter(tablealias_param)
  134. # 行政区划字段选择
  135. # self.addParameter(QgsProcessingParameterField(self.XZQH_FIELD,
  136. # self.tr('行政区划字段')))
  137. # 数据来源
  138. self.addParameter(QgsProcessingParameterString(self.VECTOR_SJLY,
  139. self.tr('数据来源'), "",
  140. optional=False))
  141. self.addParameter(
  142. QgsProcessingParameterDateTime(self.VECTOR_YEAR, '数据时效', type=QgsProcessingParameterDateTime.Type.Date,
  143. defaultValue=None))
  144. pgconn = PostgreSQL(schema='base')
  145. rows = pgconn.getVectorYwlx()
  146. self.ywlxs = []
  147. for row in rows:
  148. self.ywlxs.append(row[0])
  149. self.addParameter(QgsProcessingParameterEnum(name=self.VECTOR_YWLX,
  150. description=self.tr('业务类型'), options=self.ywlxs))
  151. rows = pgconn.getDeptList()
  152. self.depts = []
  153. for row in rows:
  154. self.depts.append(row[0])
  155. self.addParameter(QgsProcessingParameterEnum(name=self.VECTOR_GLBM,
  156. description=self.tr('管理部门'), options=self.depts))
  157. rows = pgconn.getVectorZyml()
  158. self.zymls = []
  159. for row in rows:
  160. self.zymls.append(row[1])
  161. self.addParameter(QgsProcessingParameterEnum(name=self.VECTOR_ZYML,
  162. description=self.tr('资源目录'), options=self.zymls))
  163. crs = QgsCoordinateReferenceSystem("EPSG:4525")
  164. crs_parameter = QgsProcessingParameterCrs(self.T_SRS,
  165. self.tr('指定入库坐标系'),
  166. defaultValue=crs,
  167. optional=False)
  168. self.addParameter(crs_parameter)
  169. string = QgsProcessingParameterString(self.SOURCE_TYPE,
  170. self.tr('数据源类型'), "vector",
  171. optional=False)
  172. self.addParameter(string)
  173. self.addParameter(QgsProcessingParameterString(self.GEOCOLUMN,
  174. self.tr('Geometry column name'), defaultValue='geom',
  175. optional=True))
  176. self.addParameter(QgsProcessingParameterString(self.PK,
  177. self.tr('Primary key (new field)'), defaultValue='id',
  178. optional=True))
  179. metadata = QgsProcessingParameterBoolean(self.Metadata_storage,
  180. self.tr('元数据入库'), defaultValue=True)
  181. self.addParameter(metadata)
  182. self.addParameter(QgsProcessingParameterBoolean(self.OVERWRITE,
  183. self.tr('Overwrite existing table'), defaultValue=True))
  184. self.addParameter(QgsProcessingParameterBoolean(self.APPEND,
  185. self.tr('Append to existing table'), defaultValue=False))
  186. self.addParameter(QgsProcessingParameterBoolean(self.ADDFIELDS,
  187. self.tr('Append and add new fields to existing table'),
  188. defaultValue=False))
  189. self.addParameter(QgsProcessingParameterBoolean(self.LAUNDER,
  190. self.tr('Do not launder columns/table names'),
  191. defaultValue=False))
  192. self.addParameter(QgsProcessingParameterBoolean(self.INDEX,
  193. self.tr('Do not create spatial index'), defaultValue=False))
  194. self.addParameter(QgsProcessingParameterBoolean(self.SKIPFAILURES,
  195. self.tr(
  196. 'Continue after a failure, skipping the failed feature'),
  197. defaultValue=False))
  198. self.addParameter(QgsProcessingParameterBoolean(self.PROMOTETOMULTI,
  199. self.tr('Promote to Multipart'),
  200. defaultValue=True))
  201. self.addParameter(QgsProcessingParameterBoolean(self.PRECISION,
  202. self.tr('Keep width and precision of input attributes'),
  203. defaultValue=False))
  204. self.addParameter(QgsProcessingParameterString(self.OPTIONS,
  205. self.tr('Additional creation options'), defaultValue='',
  206. optional=True))
  207. pgconn.close()
  208. def name(self):
  209. return 'gdbtopostgis'
  210. def icon(self):
  211. return QIcon(os.path.join(pluginPath, 'images', 'dbms', 'importvector.png'))
  212. def displayName(self):
  213. return self.tr('GDB数据入库')
  214. def shortDescription(self):
  215. return self.tr('Exports a vector layer to an existing PostgreSQL database connection')
  216. def tags(self):
  217. t = self.tr('import,into,postgis,database,vector').split(',')
  218. t.extend(super().tags())
  219. return t
  220. def group(self):
  221. return self.tr('数据入库工具')
  222. def groupId(self):
  223. return 'vector2postgis'
  224. def setSelectedValue(self, v):
  225. printStr(v)
  226. self.selectedValue = v
  227. def getSelectedValue(self):
  228. return self.selectedValue
  229. def gdbimport(self, parameters, context, feedback, executing=True):
  230. print("############进入自身组件的processAlgorithm################")
  231. gdb_folder = self.parameterAsString(parameters, self.INPUTGDB, context)
  232. uid = self.parameterAsString(parameters, self.UID, context)
  233. driver = ogr.GetDriverByName('OpenFileGDB')
  234. if not driver:
  235. return {
  236. "状态": "OpenFileGDB 驱动不可用"
  237. }
  238. gdb = driver.Open(gdb_folder, 0) # 0 表示只读模式
  239. if not gdb:
  240. return {
  241. "状态": f"无法打开 {gdb_folder} 文件夹"
  242. }
  243. layer_count = gdb.GetLayerCount()
  244. if layer_count > 0:
  245. # 数据库连接参数
  246. db_params = self.pgcoon
  247. # 将每个图层导入到 PostGIS
  248. connection_name = self.parameterAsConnectionName(parameters, self.DATABASE, context)
  249. if not connection_name:
  250. raise QgsProcessingException(
  251. self.tr('No connection specified'))
  252. try:
  253. md = QgsProviderRegistry.instance().providerMetadata('postgres')
  254. conn = md.createConnection(connection_name)
  255. except QgsProviderConnectionException:
  256. raise QgsProcessingException(
  257. self.tr('Could not retrieve connection details for {}').format(connection_name))
  258. uri = conn.uri()
  259. connection_parts = QgsDataSourceUri(uri).connectionInfo(executing).split(' ')
  260. schema = self.parameterAsSchema(parameters, self.SCHEMA, context)
  261. connection_parts.append('active_schema={}'.format(schema or 'public'))
  262. pg_conn_string = 'PG:"{}"'.format(' '.join(connection_parts))
  263. # pg_conn_string = f'PG:"host={db_params["host"]} port={db_params["port"]} dbname={db_params["dbname"]} user={db_params["user"]} password={db_params["password"]} active_schema={db_params["schema"]}"'
  264. layernames = []
  265. for i in range(layer_count):
  266. layer = gdb.GetLayerByIndex(i)
  267. # 获取图层的几何类型
  268. geom_type = self.getLayerGeometryType(layer.GetGeomType())
  269. print(geom_type)
  270. layer_name = layer.GetName()
  271. t_layer_name = f'{uid}_{layer_name}'
  272. print(f"图层 {i}: {layer.GetName()}")
  273. command = f'ogr2ogr.exe -f "PostgreSQL" {pg_conn_string} "{gdb_folder}" {layer_name} -overwrite -nln {t_layer_name}'
  274. if geom_type:
  275. geocolumn = self.parameterAsString(parameters, self.GEOCOLUMN, context)
  276. tsrs = self.parameterAsCrs(parameters, self.T_SRS, context)
  277. command = f'{command} -nlt {geom_type} -lco GEOMETRY_NAME={geocolumn} -t_srs {GdalUtils.gdal_crs_string(tsrs)}'
  278. print(command)
  279. # 使用 subprocess.Popen 在后台运行命令
  280. process = subprocess.Popen(command, shell=True, creationflags=subprocess.CREATE_NO_WINDOW)
  281. # 等待命令执行完毕
  282. process.wait()
  283. # subprocess.run(command, check=True)
  284. print(f"图层 {layer_name} 导入成功")
  285. layernames.append(t_layer_name)
  286. # 所属行政区划
  287. ssxzqh = getLastPrint()
  288. print("ssxzqh====" + ssxzqh)
  289. # 获取附件列表
  290. fileliststr = getFileListPrint()
  291. pgconn = PostgreSQL(schema='base')
  292. ogrLayer = t_layer_name
  293. pgconn.metadataStorage(parameters, ssxzqh, fileliststr, self.ywlxs[parameters.get("VECTOR_YWLX")],
  294. self.depts[parameters.get("VECTOR_GLBM")], ogrLayer,
  295. self.zymls[parameters.get("VECTOR_ZYML")])
  296. pgconn.close()
  297. return {
  298. "状态": f"GDB数据导入成功,共导入{len(layernames)}个图层,分别为:{','.join(layernames)}。"
  299. }
  300. else:
  301. return {
  302. "状态": f"{gdb_folder} 文件夹不包含任何图层"
  303. }
  304. def getConsoleCommands(self, parameters, context, feedback, executing=True):
  305. # print(parameters)
  306. # print(self.ywlxs)
  307. # connection_name = self.parameterAsConnectionName(parameters, self.DATABASE, context)
  308. # if not connection_name:
  309. # raise QgsProcessingException(
  310. # self.tr('No connection specified'))
  311. #
  312. # # resolve connection details to uri
  313. # try:
  314. # md = QgsProviderRegistry.instance().providerMetadata('postgres')
  315. # conn = md.createConnection(connection_name)
  316. # except QgsProviderConnectionException:
  317. # raise QgsProcessingException(
  318. # self.tr('Could not retrieve connection details for {}').format(connection_name))
  319. #
  320. # uri = conn.uri()
  321. # gdb_folder = self.parameterAsString(parameters, self.INPUTGDB, context)
  322. # ogrLayer, layername = self.getOgrCompatibleSource(self.INPUTGDB, parameters, context, feedback, executing)
  323. # shapeEncoding = self.parameterAsString(parameters, self.SHAPE_ENCODING, context)
  324. # ssrs = self.parameterAsCrs(parameters, self.S_SRS, context)
  325. # tsrs = self.parameterAsCrs(parameters, self.T_SRS, context)
  326. # asrs = self.parameterAsCrs(parameters, self.A_SRS, context)
  327. # table = self.parameterAsDatabaseTableName(parameters, self.TABLE, context)
  328. # schema = self.parameterAsSchema(parameters, self.SCHEMA, context)
  329. # pk = self.parameterAsString(parameters, self.PK, context)
  330. # pkstring = "-lco FID=" + pk
  331. # primary_key = self.parameterAsString(parameters, self.PRIMARY_KEY, context)
  332. # geocolumn = self.parameterAsString(parameters, self.GEOCOLUMN, context)
  333. # geocolumnstring = "-lco GEOMETRY_NAME=" + geocolumn
  334. # dim = self.DIMLIST[self.parameterAsEnum(parameters, self.DIM, context)]
  335. # dimstring = "-lco DIM=" + dim
  336. # simplify = self.parameterAsString(parameters, self.SIMPLIFY, context)
  337. # segmentize = self.parameterAsString(parameters, self.SEGMENTIZE, context)
  338. # spat = self.parameterAsExtent(parameters, self.SPAT, context)
  339. # clip = self.parameterAsBoolean(parameters, self.CLIP, context)
  340. # where = self.parameterAsString(parameters, self.WHERE, context)
  341. # wherestring = '-where "' + where + '"'
  342. # gt = self.parameterAsString(parameters, self.GT, context)
  343. # overwrite = self.parameterAsBoolean(parameters, self.OVERWRITE, context)
  344. # append = self.parameterAsBoolean(parameters, self.APPEND, context)
  345. # addfields = self.parameterAsBoolean(parameters, self.ADDFIELDS, context)
  346. # launder = self.parameterAsBoolean(parameters, self.LAUNDER, context)
  347. # launderstring = "-lco LAUNDER=NO"
  348. # index = self.parameterAsBoolean(parameters, self.INDEX, context)
  349. # indexstring = "-lco SPATIAL_INDEX=OFF"
  350. # skipfailures = self.parameterAsBoolean(parameters, self.SKIPFAILURES, context)
  351. # promotetomulti = self.parameterAsBoolean(parameters, self.PROMOTETOMULTI, context)
  352. # precision = self.parameterAsBoolean(parameters, self.PRECISION, context)
  353. # options = self.parameterAsString(parameters, self.OPTIONS, context)
  354. # table_alias = self.parameterAsString(parameters, self.TABLE_ALIAS, context)
  355. #
  356. # arguments = [
  357. # '-progress',
  358. # '--config PG_USE_COPY YES'
  359. # ]
  360. # if shapeEncoding:
  361. # arguments.append('--config')
  362. # arguments.append('SHAPE_ENCODING')
  363. # arguments.append(shapeEncoding)
  364. # arguments.append('-f')
  365. # arguments.append('PostgreSQL')
  366. #
  367. # connection_parts = QgsDataSourceUri(uri).connectionInfo(executing).split(' ')
  368. # connection_parts.append('active_schema={}'.format(schema or 'public'))
  369. # arguments.append('PG:{}'.format(' '.join(connection_parts)))
  370. # # 检查数据库中该模式下是否存在相同的表
  371. # pgconn = PostgreSQL(schema='base')
  372. # resbool = pgconn.checkTableName(schemaname=schema,
  373. # tablename=layername.lower() if len(table) == 0 else table.lower())
  374. # pgconn.close()
  375. # if resbool == False:
  376. # QMessageBox.warning(None, "提示",
  377. # self.tr('数据库中已存在相同表名,请输入或修改表名!'))
  378. # arguments.append(dimstring)
  379. # arguments.append(ogrLayer)
  380. # arguments.append(layername)
  381. # if index:
  382. # arguments.append(indexstring)
  383. # if launder:
  384. # arguments.append(launderstring)
  385. # if append:
  386. # arguments.append('-append')
  387. # if addfields:
  388. # arguments.append('-addfields')
  389. # if overwrite:
  390. # arguments.append('-overwrite')
  391. # if len(self.GEOMTYPE[self.parameterAsEnum(parameters, self.GTYPE, context)]) > 0:
  392. # arguments.append('-nlt')
  393. # arguments.append(self.GEOMTYPE[self.parameterAsEnum(parameters, self.GTYPE, context)])
  394. # if len(geocolumn) > 0:
  395. # arguments.append(geocolumnstring)
  396. # if pk:
  397. # arguments.append(pkstring)
  398. # elif primary_key:
  399. # arguments.append("-lco FID=" + primary_key)
  400. # if len(table) == 0:
  401. # # table = layername.lower()
  402. # table = layername
  403. # if schema:
  404. # table = f'{schema}.{table}'
  405. # # if len(table_alias) > 0:
  406. # # arguments.append("-lco layer_name=" + table_alias)
  407. # arguments.append('-nln')
  408. # arguments.append(table)
  409. # if ssrs.isValid():
  410. # arguments.append('-s_srs')
  411. # arguments.append(GdalUtils.gdal_crs_string(ssrs))
  412. # if tsrs.isValid():
  413. # arguments.append('-t_srs')
  414. # arguments.append(GdalUtils.gdal_crs_string(tsrs))
  415. # if asrs.isValid():
  416. # arguments.append('-a_srs')
  417. # arguments.append(GdalUtils.gdal_crs_string(asrs))
  418. # if not spat.isNull():
  419. # arguments.append('-spat')
  420. # arguments.append(spat.xMinimum())
  421. # arguments.append(spat.yMinimum())
  422. # arguments.append(spat.xMaximum())
  423. # arguments.append(spat.yMaximum())
  424. # if clip:
  425. # arguments.append('-clipsrc spat_extent')
  426. # if skipfailures:
  427. # arguments.append('-skipfailures')
  428. # if where:
  429. # arguments.append(wherestring)
  430. # if len(simplify) > 0:
  431. # arguments.append('-simplify')
  432. # arguments.append(simplify)
  433. # if len(segmentize) > 0:
  434. # arguments.append('-segmentize')
  435. # arguments.append(segmentize)
  436. # if len(gt) > 0:
  437. # arguments.append('-gt')
  438. # arguments.append(gt)
  439. # if promotetomulti:
  440. # arguments.append('-nlt PROMOTE_TO_MULTI')
  441. # if precision is False:
  442. # arguments.append('-lco PRECISION=NO')
  443. # if len(options) > 0:
  444. # arguments.append(options)
  445. if isWindows():
  446. return ['cmd.exe', '/C ', 'ogr2ogr.exe']
  447. else:
  448. return ['ogr2ogr']
  449. def getTableName(self, table):
  450. sp = table.split(".")
  451. res = f'{sp[0]}."{sp[1]}"'
  452. print(res)
  453. return res
  454. def commandName(self):
  455. return "ogr2ogr"