csydjc.py 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759
  1. # -*- coding: utf-8 -*-
  2. import os
  3. import sys
  4. import json
  5. import log
  6. import appconfig
  7. import arcpy
  8. import utils
  9. from arcpy import env
  10. from db.oracle import Oracle
  11. import uuid
  12. # 全局用地类型定义
  13. LAND_TYPES = {
  14. 'SYJSYD': {
  15. 'name': 'SYJSYD', # 拼音简化名称
  16. 'chinese_name': '所有建设用地',
  17. 'cxtj': 'SYJSYD',
  18. 'xzq_field': 'QSDWDM' # 行政编码字段名称
  19. },
  20. 'YJJBNT': {
  21. 'name': 'YJJBNT',
  22. 'chinese_name': '永久基本农田',
  23. 'xzq_field': 'YJJBNTTBBH' # 行政编码字段名称
  24. },
  25. 'STBHHL': {
  26. 'name': 'STBHHL',
  27. 'chinese_name': '生态保护红线',
  28. 'xzq_field': 'XZQDM' # 行政编码字段名称
  29. },
  30. 'GYYD': {
  31. 'name': 'GYYD',
  32. 'chinese_name': '工业用地',
  33. 'xzq_field': 'QSDWDM' # 行政编码字段名称
  34. },
  35. 'CZKFBJ': {
  36. 'name': 'CZKFBJ',
  37. 'chinese_name': '城镇开发边界',
  38. 'xzq_field': 'XZQDM' # 行政编码字段名称
  39. },
  40. 'CZZYD': {
  41. 'name': 'CZZYD',
  42. 'chinese_name': '城镇住宅用地',
  43. 'xzq_field': 'QSDWDM' # 行政编码字段名称
  44. }
  45. }
  46. class Csydjc:
  47. """
  48. 图层面积计算系统
  49. 功能:
  50. - 计算图层间的相交面积
  51. - 计算图层间的擦除面积
  52. 流程:
  53. 1. 查询行政区域数据
  54. 2. 查询各类用地查询条件
  55. 3. 生成临时GDB文件
  56. 4. 计算图层面积
  57. 5. 输出计算结果
  58. """
  59. def __init__(self, year=None, xzq_id=None):
  60. self.db = Oracle(appconfig.DB_CONN)
  61. self.outputname = "CSYDJC_YJFX"
  62. self.year = year
  63. self.xzq_id = xzq_id
  64. log.info("图层面积计算系统开始")
  65. # 临时 GDB - 按年份和行政区域组织缓存目录
  66. self.root = os.path.dirname(os.path.abspath(__file__))
  67. # 如果提供了年份和行政区域,使用缓存目录
  68. if year and xzq_id:
  69. cache_dir = "cache_{}_{}_{}".format(year, xzq_id, self.outputname)
  70. self.cache_path = os.path.join(self.root, "cache")
  71. self.outGdb = os.path.join(self.cache_path, "{}.gdb".format(cache_dir))
  72. else:
  73. # 兼容旧版本,使用随机目录
  74. random_suffix = uuid.uuid4().hex[:8]
  75. path = os.path.join(self.root, "out", random_suffix)
  76. if not os.path.exists(path):
  77. os.makedirs(path)
  78. self.outGdb = os.path.join(path, "{}.gdb".format(self.outputname))
  79. self.cache_path = path
  80. # 确保缓存目录存在
  81. if not os.path.exists(self.cache_path):
  82. os.makedirs(self.cache_path)
  83. # 只有当GDB不存在时才创建
  84. if not arcpy.Exists(self.outGdb):
  85. arcpy.CreateFileGDB_management(self.cache_path, os.path.basename(self.outGdb))
  86. log.info("创建新的GDB缓存: {}".format(self.outGdb))
  87. else:
  88. log.info("使用现有GDB缓存: {}".format(self.outGdb))
  89. env.overwriteOutput = True
  90. def get_xzq_data(self, xzq_id="1505"):
  91. """
  92. 从v_yzt_zysxcx表查询行政区域数据
  93. 查询条件:id=xzq_id或pid=xzq_id,且type='XZQ'
  94. """
  95. sql = """
  96. SELECT id, name, pid, type
  97. FROM v_yzt_zysxcx
  98. WHERE (id = '{}' OR pid = '{}') AND type = 'XZQ'
  99. ORDER BY id
  100. """.format(xzq_id, xzq_id)
  101. log.info("查询行政区域数据: " + sql)
  102. xzq_data = self.db.query(sql)
  103. log.info("查询到{}条行政区域数据".format(len(xzq_data)))
  104. return xzq_data
  105. def generate_land_conditions_template(self, year):
  106. """
  107. 生成用地查询条件模板字典,不包含具体的行政ID
  108. 返回格式: {land_type: {'SJLX': sjlx, 'CXTJ_TEMPLATE': cxtj_template, 'XZQ_FIELD': xzq_field, 'SSMK': ssmk}}
  109. """
  110. # 使用全局LAND_TYPES变量
  111. land_types = [land_type['chinese_name'] for land_type in LAND_TYPES.values()]
  112. # 构建IN条件,一次性查询所有数据
  113. land_types_str = "','".join(land_types)
  114. sql = """
  115. SELECT name, sjlx, cxtj, ssmk
  116. FROM t_fxpj_ctfx_yz
  117. WHERE name IN ('{}')
  118. """.format(land_types_str)
  119. log.info("查询用地条件模板: {}".format(sql))
  120. try:
  121. results = self.db.query(sql)
  122. conditions_template = {}
  123. if results:
  124. for result in results:
  125. name = result.get('NAME')
  126. sjlx = result.get('SJLX')
  127. cxtj = result.get('CXTJ')
  128. ssmk = result.get('SSMK')
  129. # 确保数据类型正确
  130. sjlx = str(sjlx) if sjlx is not None else ''
  131. cxtj = str(cxtj) if cxtj is not None else ''
  132. name = str(name) if name is not None else ''
  133. ssmk = str(ssmk) if ssmk is not None else ''
  134. # 处理sjlx:如果是DLTB,创建模板
  135. if sjlx == 'DLTB':
  136. sjlx_template = "DLTB_{xzq_id}00_{year}12"
  137. elif sjlx == 'YJJBNTBHTB':
  138. sjlx_template = 'yjjbntbhtb_150500_202212'
  139. else:
  140. sjlx_template = sjlx
  141. # 在所有sjlx前面添加sde.前缀
  142. if sjlx_template:
  143. sjlx_template = "sde." + sjlx_template
  144. # 处理cxtj:根据年份和条件处理查询条件
  145. cxtj_base = cxtj
  146. if cxtj and ';' in cxtj:
  147. if year > 2018:
  148. # 年份大于2018:取;后面的条件
  149. parts = cxtj.split(';')
  150. if len(parts) > 1:
  151. cxtj_base = parts[1].strip()
  152. # 若条件中有=号,则取=号前面的
  153. if '=' in cxtj_base:
  154. cxtj_base = cxtj_base.split('=')[0].strip()
  155. else:
  156. # 年份小于等于2018:取;前面的条件
  157. parts = cxtj.split(';')
  158. if len(parts) > 0:
  159. cxtj_base = parts[0].strip()
  160. # 若条件中有=号,则取=号前面的
  161. if '=' in cxtj_base:
  162. cxtj_base = cxtj_base.split('=')[0].strip()
  163. # 根据用地类型获取对应的行政编码字段名称
  164. xzq_field = 'XZQDM' # 默认字段名称
  165. for land_type_key, land_type_info in LAND_TYPES.items():
  166. if land_type_info['chinese_name'] == name:
  167. xzq_field = land_type_info.get('xzq_field', 'XZQDM')
  168. break
  169. # 构建条件模板对象
  170. condition_template = {
  171. 'SJLX_TEMPLATE': sjlx_template,
  172. 'CXTJ_BASE': cxtj_base,
  173. 'XZQ_FIELD': xzq_field,
  174. 'SSMK': ssmk
  175. }
  176. conditions_template[name] = condition_template
  177. log.info("{}条件模板: sjlx_template={}, cxtj_base={}, xzq_field={}, ssmk={}".format(
  178. name, sjlx_template, cxtj_base, xzq_field, ssmk))
  179. # 检查是否所有需要的用地类型都查询到了
  180. for land_type in land_types:
  181. if land_type not in conditions_template:
  182. log.warning("未找到{}的查询条件模板".format(land_type))
  183. else:
  184. log.error("未查询到任何用地条件数据")
  185. except Exception as e:
  186. log.error("查询用地条件模板失败: {}".format(str(e)))
  187. conditions_template = {}
  188. return conditions_template
  189. def apply_xzq_to_conditions(self, conditions_template, year, xzq_id):
  190. """
  191. 将行政ID应用到条件模板中,生成具体的查询条件
  192. """
  193. conditions = {}
  194. for land_type, template in conditions_template.items():
  195. sjlx_template = template['SJLX_TEMPLATE']
  196. cxtj_base = template['CXTJ_BASE']
  197. xzq_field = template['XZQ_FIELD']
  198. ssmk = template['SSMK']
  199. # 应用年份和行政ID到sjlx模板
  200. sjlx = sjlx_template.format(year=year, xzq_id=xzq_id[0:4])
  201. # 构建完整的cxtj条件
  202. if cxtj_base:
  203. # 如果已有条件,添加AND连接符
  204. cxtj = "{} AND {} LIKE '{}%'".format(cxtj_base, xzq_field, xzq_id)
  205. else:
  206. # 如果没有条件,直接设置行政id条件
  207. cxtj = "{} LIKE '{}%'".format(xzq_field, xzq_id)
  208. # 构建最终条件对象
  209. condition = {
  210. 'SJLX': sjlx,
  211. 'CXTJ': cxtj,
  212. 'SSMK': ssmk
  213. }
  214. conditions[land_type] = condition
  215. return conditions
  216. def get_land_type_name(self, chinese_name):
  217. """
  218. 根据中文名称获取拼音简化名称
  219. """
  220. for key, value in LAND_TYPES.items():
  221. if value['chinese_name'] == chinese_name:
  222. return value['name']
  223. return chinese_name.replace(' ', '_') # 如果找不到,使用原名称并替换空格
  224. def check_cache_exists(self, land_type, xzq_id):
  225. """
  226. 检查指定用地类型的缓存是否存在
  227. """
  228. # 使用拼音简化名称进行文件命名
  229. land_type_name = self.get_land_type_name(land_type)
  230. output_fc = os.path.join(self.outGdb, "{}_{}".format(land_type_name, xzq_id))
  231. exists = arcpy.Exists(output_fc)
  232. if exists:
  233. # 检查要素类是否有数据
  234. try:
  235. count = int(arcpy.GetCount_management(output_fc).getOutput(0))
  236. if count > 0:
  237. log.info("发现{}的缓存数据: {} (共{}个要素)".format(land_type, output_fc, count))
  238. return True, output_fc
  239. else:
  240. log.warning("{}的缓存文件存在但无数据,将重新查询".format(land_type))
  241. return False, None
  242. except Exception as e:
  243. log.warning("检查{}缓存数据时出错: {},将重新查询".format(land_type, str(e)))
  244. return False, None
  245. return False, None
  246. def query_land_data(self, xzq_id, land_conditions):
  247. """
  248. 根据行政区域ID和查询条件从SDE库查询土地数据,支持缓存机制
  249. """
  250. land_data = {}
  251. cache_hits = 0
  252. total_queries = 0
  253. for land_type, condition in land_conditions.items():
  254. if not condition:
  255. continue
  256. total_queries += 1
  257. # 首先检查缓存
  258. cache_exists, cached_fc = self.check_cache_exists(land_type, xzq_id)
  259. if cache_exists:
  260. land_data[land_type] = cached_fc
  261. cache_hits += 1
  262. continue
  263. # 缓存不存在,从数据库查询
  264. sjlx = condition.get('SJLX')
  265. cxtj = condition.get('CXTJ')
  266. if not sjlx:
  267. log.warning("{}缺少sjlx参数".format(land_type))
  268. continue
  269. try:
  270. env.workspace = appconfig.getSDE("SDE")
  271. # 使用拼音简化名称进行文件命名
  272. land_type_name = self.get_land_type_name(land_type)
  273. output_fc = os.path.join(self.outGdb, "{}_{}".format(land_type_name, xzq_id))
  274. # 构建查询条件:使用1=1作为前缀,然后拼接cxtj
  275. if cxtj and cxtj.strip():
  276. where_clause = "1=1 AND {}".format(cxtj.strip())
  277. else:
  278. where_clause = "1=1"
  279. log.info("从数据库查询{}数据...".format(land_type))
  280. # 使用MakeFeatureLayer_management创建临时图层
  281. temp_layer = "temp_layer_{}".format(land_type)
  282. arcpy.MakeFeatureLayer_management(sjlx, temp_layer, where_clause)
  283. # 将临时图层复制到输出要素类
  284. arcpy.CopyFeatures_management(temp_layer, output_fc)
  285. # 删除临时图层
  286. arcpy.Delete_management(temp_layer)
  287. land_data[land_type] = output_fc
  288. log.info("成功查询{}数据到: {}".format(land_type, output_fc))
  289. log.info("使用的查询条件: {}".format(where_clause))
  290. except Exception as e:
  291. log.error("查询{}数据失败: {}".format(land_type, str(e)))
  292. log.error("查询条件: {}".format(where_clause if 'where_clause' in locals() else 'N/A'))
  293. log.error("数据源: {}".format(sjlx))
  294. # 输出缓存统计信息
  295. if total_queries > 0:
  296. cache_rate = (cache_hits / total_queries) * 100
  297. log.info("缓存命中统计: {}/{} ({:.1f}%)".format(cache_hits, total_queries, cache_rate))
  298. if cache_hits > 0:
  299. log.info("使用缓存加速了{}个查询,节省了大量时间".format(cache_hits))
  300. return land_data
  301. def calculate_overlap_area(self, fc1, fc2, operation="intersect"):
  302. """
  303. 计算两个要素类的重叠面积
  304. operation: intersect(相交), erase(擦除)
  305. """
  306. try:
  307. output_name = "temp_{}".format(operation)
  308. output_fc = os.path.join(self.outGdb, output_name)
  309. if operation == "intersect":
  310. arcpy.Intersect_analysis([fc1, fc2], output_fc)
  311. elif operation == "erase":
  312. arcpy.Erase_analysis(fc1, fc2, output_fc)
  313. else:
  314. raise ValueError("不支持的操作类型: {}".format(operation))
  315. # 计算面积
  316. total_area = 0
  317. with arcpy.da.SearchCursor(output_fc, ["SHAPE@AREA"]) as cursor:
  318. for row in cursor:
  319. total_area += row[0]
  320. # 转换为平方公里
  321. area_km2 = total_area / 1000000
  322. # 清理临时文件
  323. arcpy.Delete_management(output_fc)
  324. return area_km2
  325. except Exception as e:
  326. log.error("计算面积失败: {}".format(str(e)))
  327. return 0
  328. def calculate_areas(self, land_data, year):
  329. """
  330. 计算图层间的相交和擦除面积,提供有意义的分析结果
  331. 参数:
  332. land_data: 土地数据
  333. year: 年份
  334. """
  335. results = []
  336. # 检查输入数据是否为空
  337. if not land_data:
  338. log.warning("土地数据为空,返回零值结果")
  339. return self.generate_zero_results(year)
  340. # 获取查询到的图层数据
  341. layers = {}
  342. for land_type, fc_path in land_data.items():
  343. # 使用拼音简化名称生成图层名称,避免中文字符
  344. land_type_name = self.get_land_type_name(land_type)
  345. layer_name = "layer_{}".format(land_type_name)
  346. try:
  347. arcpy.MakeFeatureLayer_management(fc_path, layer_name)
  348. count = int(arcpy.GetCount_management(layer_name).getOutput(0))
  349. if count > 0:
  350. layers[land_type_name] = layer_name
  351. log.info("加载{}图层成功,要素数量: {}".format(land_type, count))
  352. else:
  353. log.info("{}图层无数据".format(land_type))
  354. except Exception as e:
  355. log.error("加载{}图层失败: {}".format(land_type, str(e)))
  356. # 如果没有任何有效图层,返回零值结果
  357. if not layers:
  358. log.warning("没有有效的图层数据,返回零值结果")
  359. return self.generate_zero_results(year)
  360. # 获取分析组合定义
  361. analysis_combinations = self.get_analysis_combinations(year)
  362. # 执行分析
  363. for combo in analysis_combinations:
  364. layer1_name = combo['layer1']
  365. layer2_name = combo['layer2']
  366. operation = combo['operation']
  367. description = combo['description']
  368. # 将中文名称转换为拼音简化名称来查找图层
  369. layer1_key = self.get_land_type_name(layer1_name)
  370. layer2_key = self.get_land_type_name(layer2_name)
  371. if layer1_key in layers and layer2_key in layers:
  372. layer1 = layers[layer1_key]
  373. layer2 = layers[layer2_key]
  374. # 计算面积
  375. area = self.calculate_overlap_area(layer1, layer2, operation)
  376. if operation == 'intersect':
  377. log.info("{}: {:.4f}平方公里".format(description, area))
  378. else: # erase
  379. log.info("{}: {:.4f}平方公里".format(description, area))
  380. result = {
  381. 'analysis_type': description,
  382. 'layer1': layer1_name,
  383. 'layer2': layer2_name,
  384. 'operation': operation,
  385. 'area_km2': area,
  386. 'description': description
  387. }
  388. results.append(result)
  389. # 将数据库相关信息添加到结果中,供后续统一更新
  390. result['zbbh'] = combo['zbbh']
  391. result['jcn'] = combo['jcn']
  392. else:
  393. # 当图层缺失时,生成零值结果而不是跳过
  394. missing_layers = []
  395. if layer1_key not in layers:
  396. missing_layers.append(layer1_name)
  397. if layer2_key not in layers:
  398. missing_layers.append(layer2_name)
  399. log.warning("无法进行{}分析,缺少图层: {},生成零值结果".format(description, ', '.join(missing_layers)))
  400. # 生成零值结果
  401. result = {
  402. 'analysis_type': description,
  403. 'layer1': layer1_name,
  404. 'layer2': layer2_name,
  405. 'operation': operation,
  406. 'area_km2': 0.0,
  407. 'description': description,
  408. 'zbbh': combo['zbbh'],
  409. 'jcn': combo['jcn']
  410. }
  411. results.append(result)
  412. # 记录零值结果
  413. log.info("{}: 0.0000平方公里 (图层缺失)".format(description))
  414. # 清理临时图层
  415. for layer_name in layers.values():
  416. try:
  417. arcpy.Delete_management(layer_name)
  418. except:
  419. pass
  420. return results
  421. def update_zbmx_zbjcz_table(self, zbbh, xzqdm, jcn, area_km2):
  422. """
  423. 查询并更新t_zbmx_zbjcz表
  424. 参数:
  425. zbbh: 指标编号
  426. xzqdm: 行政区代码
  427. jcn: 检查年
  428. area_km2: 计算出的面积(平方公里)
  429. """
  430. try:
  431. # 将面积值保留2位小数
  432. area_rounded = round(area_km2, 2)
  433. # 查询是否存在符合条件的记录
  434. query_sql = """
  435. SELECT COUNT(*)
  436. FROM t_zbmx_zbjcz
  437. WHERE zbbh = '{}'
  438. AND xzqdm = '{}'
  439. AND jcn = '{}'
  440. """.format(zbbh, xzqdm, jcn)
  441. log.info("执行查询SQL: {}".format(query_sql.strip()))
  442. result = self.db.query(query_sql)
  443. count = result[0][0] if result and len(result) > 0 else 0
  444. if count > 0:
  445. # 存在记录,执行更新
  446. update_sql = """
  447. UPDATE t_zbmx_zbjcz
  448. SET jcz = {}
  449. WHERE zbbh = '{}'
  450. AND xzqdm = '{}'
  451. AND jcn = '{}'
  452. """.format(area_rounded, zbbh, xzqdm, jcn)
  453. log.info("执行更新SQL: {}".format(update_sql.strip()))
  454. self.db.execute(update_sql)
  455. log.info("已更新t_zbmx_zbjcz表: zbbh={}, xzqdm={}, jcn={}, jcz={:.2f}平方公里".format(
  456. zbbh, xzqdm, jcn, area_rounded))
  457. else:
  458. # 不存在记录,记录日志
  459. log.info("t_zbmx_zbjcz表中未找到符合条件的记录: zbbh={}, xzqdm={}, jcn={}".format(zbbh, xzqdm, jcn))
  460. except Exception as e:
  461. log.error("更新t_zbmx_zbjcz表失败: {}".format(str(e)))
  462. def output_analysis_results(self, results):
  463. """
  464. 输出格式化的分析结果并更新数据库
  465. """
  466. if not results:
  467. log.info("没有分析结果")
  468. return
  469. log.info("=" * 80)
  470. log.info("图层面积分析结果")
  471. log.info("=" * 80)
  472. total_area = 0
  473. updated_count = 0
  474. for result in results:
  475. area = result['area_km2']
  476. description = result['description']
  477. log.info("{}: {:.4f} 平方公里".format(description, area))
  478. total_area += area
  479. # 更新数据库
  480. if 'zbbh' in result and 'xzq_id' in result and 'jcn' in result:
  481. try:
  482. self.update_zbmx_zbjcz_table(
  483. result['zbbh'],
  484. result['xzq_id'],
  485. result['jcn'],
  486. area
  487. )
  488. updated_count += 1
  489. except Exception as e:
  490. log.error("更新数据库失败 - {}: {}".format(description, str(e)))
  491. log.info("-" * 60)
  492. log.info("总计面积: {:.4f} 平方公里".format(total_area))
  493. log.info("数据库更新记录数: {}".format(updated_count))
  494. log.info("=" * 80)
  495. # 输出详细的JSON格式结果
  496. log.info("详细分析结果:")
  497. print(json.dumps(results, ensure_ascii=False, indent=2))
  498. def get_analysis_combinations(self, year):
  499. """
  500. 获取分析组合定义
  501. 参数:
  502. year: 年份
  503. 返回:
  504. 分析组合列表
  505. """
  506. return [
  507. {
  508. 'layer1': 'SYJSYD',
  509. 'layer2': 'YJJBNT',
  510. 'operation': 'intersect',
  511. 'description': '建设用地侵占永久基本农田面积',
  512. 'zbbh': 'A-150',
  513. 'jcn': year
  514. },
  515. {
  516. 'layer1': 'SYJSYD',
  517. 'layer2': 'STBHHL',
  518. 'operation': 'intersect',
  519. 'description': '建设用地侵占生态保护红线面积',
  520. 'zbbh': 'A-151',
  521. 'jcn': year
  522. },
  523. {
  524. 'layer1': 'GYYD',
  525. 'layer2': 'CZKFBJ',
  526. 'operation': 'erase',
  527. 'description': '城镇开发边界外工业用地面积',
  528. 'zbbh': 'A-152',
  529. 'jcn': year
  530. },
  531. {
  532. 'layer1': 'CZZYD',
  533. 'layer2': 'CZKFBJ',
  534. 'operation': 'erase',
  535. 'description': '城镇开发边界外城镇住宅用地面积',
  536. 'zbbh': 'A-153',
  537. 'jcn': year
  538. }
  539. ]
  540. def generate_zero_results(self, year=None):
  541. """
  542. 生成零值结果,用于没有数据的行政区域
  543. 参数:
  544. year: 年份,用于生成分析组合
  545. """
  546. if year is None:
  547. year = 2023 # 默认年份
  548. # 获取分析组合定义
  549. analysis_combinations = self.get_analysis_combinations(year)
  550. # 生成零值结果
  551. zero_results = []
  552. for combo in analysis_combinations:
  553. result = {
  554. 'analysis_type': combo['description'],
  555. 'layer1': combo['layer1'],
  556. 'layer2': combo['layer2'],
  557. 'operation': combo['operation'],
  558. 'area_km2': 0.0,
  559. 'description': combo['description'],
  560. 'zbbh': combo['zbbh'],
  561. 'jcn': combo['jcn']
  562. }
  563. zero_results.append(result)
  564. log.info("生成{}个零值分析结果".format(len(zero_results)))
  565. return zero_results
  566. def run(self, year, xzq_id="1505"):
  567. """
  568. 执行图层面积计算
  569. 参数:
  570. year: 年份
  571. xzq_id: 行政区域代码,默认为1505
  572. """
  573. try:
  574. log.info("开始图层面积计算,行政区域: {}, 年份: {}".format(xzq_id, year))
  575. # 1. 查询行政区域数据(包括当前行政区域及其子行政区)
  576. xzq_data = self.get_xzq_data(xzq_id)
  577. if not xzq_data:
  578. raise Exception("未查询到行政区域数据")
  579. log.info("查询到{}个行政区域".format(len(xzq_data)))
  580. # 2. 生成用地查询条件模板(一次性生成,避免重复查询)
  581. log.info("生成用地查询条件模板...")
  582. conditions_template = self.generate_land_conditions_template(year)
  583. if not conditions_template:
  584. log.warning("未生成用地查询条件模板,将为所有行政区域生成零值结果")
  585. conditions_template = {}
  586. log.info("成功生成{}种用地类型的条件模板".format(len(conditions_template)))
  587. # 3. 遍历每个行政区域进行计算
  588. all_results = []
  589. for xzq_item in xzq_data:
  590. current_xzq_id = xzq_item.get('ID')
  591. current_xzq_name = xzq_item.get('NAME')
  592. if current_xzq_id == "1505":
  593. continue;
  594. log.info("正在处理行政区域: {} ({})".format(current_xzq_name, current_xzq_id))
  595. # 3.1 将行政ID应用到条件模板中,生成具体的查询条件
  596. land_conditions = self.apply_xzq_to_conditions(conditions_template, year, current_xzq_id)
  597. if not land_conditions:
  598. log.warning("行政区域 {} 未生成用地查询条件,生成零值结果".format(current_xzq_name))
  599. # 为空区域生成零值结果
  600. zero_results = self.generate_zero_results(year)
  601. for result in zero_results:
  602. result['xzq_id'] = current_xzq_id
  603. result['xzq_name'] = current_xzq_name
  604. all_results.extend(zero_results)
  605. continue
  606. log.info("为行政区域 {} 生成了{}种用地类型的查询条件".format(current_xzq_name, len(land_conditions)))
  607. # 3.2 查询当前行政区域的土地数据
  608. land_data = self.query_land_data(current_xzq_id, land_conditions)
  609. # 3.3 计算当前行政区域的面积
  610. log.info("开始计算行政区域 {} 的图层面积...".format(current_xzq_name))
  611. if not land_data or all(not data for data in land_data.values()):
  612. log.warning("行政区域 {} 未查询到土地数据,生成零值结果".format(current_xzq_name))
  613. # 为没有数据的区域生成零值结果
  614. zero_results = self.generate_zero_results(year)
  615. for result in zero_results:
  616. result['xzq_id'] = current_xzq_id
  617. result['xzq_name'] = current_xzq_name
  618. all_results.extend(zero_results)
  619. else:
  620. results = self.calculate_areas(land_data, year)
  621. # 为结果添加行政区域信息
  622. for result in results:
  623. result['xzq_id'] = current_xzq_id
  624. result['xzq_name'] = current_xzq_name
  625. all_results.extend(results)
  626. # 4. 输出所有结果
  627. self.output_analysis_results(all_results)
  628. log.info("所有行政区域图层面积计算完成,共处理{}个行政区域".format(len(xzq_data)))
  629. except Exception as e:
  630. log.error("图层面积计算失败: {}".format(str(e)))
  631. raise e
  632. finally:
  633. # 清理资源
  634. self.db.close()
  635. if __name__ == "__main__":
  636. # 执行图层面积计算
  637. year = 2020
  638. xzq_id = "1505"
  639. # 创建实例时传递年份和行政区域参数以启用缓存
  640. analyzer = Csydjc(year=year, xzq_id=xzq_id)
  641. analyzer.run(year, xzq_id)