dimine-python-sdk 0.2.0__py3-none-any.whl → 0.2.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (229) hide show
  1. dimine_python_sdk/__init__.py +1 -1
  2. dimine_python_sdk/cli/commands/file.py +2 -2
  3. dimine_python_sdk/cli/commands/geometry.py +144 -20
  4. dimine_python_sdk/conn/conn_client.py +24 -11
  5. dimine_python_sdk/docs/examples/import_csv_to_drill_db.md +60 -8
  6. dimine_python_sdk/docs/examples/lib_file_usage.md +5 -3
  7. dimine_python_sdk/docs/examples/reserve_estimation_pipeline.md +1031 -0
  8. dimine_python_sdk/docs/examples/summary.md +1 -0
  9. dimine_python_sdk/docs/guides/SUMMARY.md +23 -14
  10. dimine_python_sdk/docs/guides/conn/SUMMARY.md +6 -0
  11. dimine_python_sdk/docs/guides/{data_get.md → conn/data_get.md} +4 -56
  12. dimine_python_sdk/docs/guides/{io.md → conn/io.md} +6 -24
  13. dimine_python_sdk/docs/guides/lib/SUMMARY.md +6 -0
  14. dimine_python_sdk/docs/guides/lib/exploitation/SUMMARY.md +5 -0
  15. dimine_python_sdk/docs/guides/{contour_modeling.md → lib/exploitation/contour_modeling.md} +112 -5
  16. dimine_python_sdk/docs/guides/lib/exploitation/implicit_modeling.md +151 -0
  17. dimine_python_sdk/docs/guides/lib/io/SUMMARY.md +10 -0
  18. dimine_python_sdk/docs/guides/lib/io/dmb_file.md +172 -0
  19. dimine_python_sdk/docs/guides/lib/io/dmd_file.md +196 -0
  20. dimine_python_sdk/docs/guides/lib/io/dmf_file.md +271 -0
  21. dimine_python_sdk/docs/guides/lib/io/dmg_file.md +192 -0
  22. dimine_python_sdk/docs/guides/lib/io/dmt_file.md +168 -0
  23. dimine_python_sdk/docs/guides/{file_conversion.md → lib/io/file_conversion.md} +3 -3
  24. dimine_python_sdk/docs/guides/lib/prospecting/SUMMARY.md +4 -0
  25. dimine_python_sdk/docs/guides/{db.md → lib/prospecting/drill_db.md} +218 -111
  26. dimine_python_sdk/docs/guides/{reserve_estimation.md → lib/prospecting/reserve_estimation.md} +42 -82
  27. dimine_python_sdk/docs/guides/lib/visualization/SUMMARY.md +3 -0
  28. dimine_python_sdk/docs/guides/models/SUMMARY.md +3 -0
  29. dimine_python_sdk/docs/reference/SUMMARY.md +2 -1
  30. dimine_python_sdk/docs/reference/lib/io/dmb_file.md +3 -0
  31. dimine_python_sdk/docs/reference/lib/io/dmd_file.md +3 -0
  32. dimine_python_sdk/docs/reference/lib/prospecting/api.md +3 -3
  33. dimine_python_sdk/lib/__init__.py +8 -0
  34. dimine_python_sdk/lib/exploitation/__init__.py +6 -0
  35. dimine_python_sdk/lib/exploitation/contour_modeling.py +190 -0
  36. dimine_python_sdk/lib/exploitation/models.py +23 -5
  37. dimine_python_sdk/lib/io/__init__.py +24 -0
  38. dimine_python_sdk/lib/io/dmb_file.py +322 -0
  39. dimine_python_sdk/lib/io/dmd_file.py +641 -0
  40. dimine_python_sdk/lib/io/dmf_file.py +40 -21
  41. dimine_python_sdk/lib/io/dmg_file.py +120 -43
  42. dimine_python_sdk/lib/io/file_converter.py +7 -3
  43. dimine_python_sdk/lib/io/models.py +11 -11
  44. dimine_python_sdk/lib/native/__init__.py +166 -157
  45. dimine_python_sdk/lib/native/_core.py +101 -10
  46. dimine_python_sdk/lib/native/data_table.py +2 -0
  47. dimine_python_sdk/lib/native/exploitation.py +62 -0
  48. dimine_python_sdk/lib/native/geometry.py +148 -26
  49. dimine_python_sdk/lib/native/io.py +3 -1
  50. dimine_python_sdk/lib/native/models.py +141 -7
  51. dimine_python_sdk/lib/native/prospecting.py +853 -464
  52. dimine_python_sdk/lib/prospecting/__init__.py +4 -22
  53. dimine_python_sdk/lib/prospecting/block_model.py +133 -200
  54. dimine_python_sdk/lib/prospecting/compositing.py +51 -9
  55. dimine_python_sdk/lib/prospecting/drill_db.py +65 -790
  56. dimine_python_sdk/lib/prospecting/ellipsoid.py +43 -51
  57. dimine_python_sdk/lib/prospecting/high_grade.py +43 -19
  58. dimine_python_sdk/lib/prospecting/models.py +47 -12
  59. dimine_python_sdk/logger.py +1 -1
  60. dimine_python_sdk/models/types.py +1 -1
  61. dimine_python_sdk/site/404.html +834 -22
  62. dimine_python_sdk/site/examples/cli_usage.html +834 -22
  63. dimine_python_sdk/site/examples/contour_modeling_visualization.html +834 -22
  64. dimine_python_sdk/site/examples/import_csv_to_drill_db.html +1013 -129
  65. dimine_python_sdk/site/examples/lib_file_usage.html +839 -25
  66. dimine_python_sdk/site/examples/reserve_estimation_pipeline.html +3561 -0
  67. dimine_python_sdk/site/examples/summary.html +835 -22
  68. dimine_python_sdk/site/guides/SUMMARY.html +999 -37
  69. dimine_python_sdk/site/guides/cli.html +844 -23
  70. dimine_python_sdk/site/guides/conn/SUMMARY.html +1915 -0
  71. dimine_python_sdk/site/guides/{data_edit.html → conn/data_edit.html} +895 -72
  72. dimine_python_sdk/site/guides/{data_get.html → conn/data_get.html} +886 -151
  73. dimine_python_sdk/site/guides/{features.html → conn/features.html} +917 -94
  74. dimine_python_sdk/site/guides/{io.html → conn/io.html} +1002 -214
  75. dimine_python_sdk/site/guides/lib/SUMMARY.html +1915 -0
  76. dimine_python_sdk/site/guides/lib/exploitation/SUMMARY.html +1916 -0
  77. dimine_python_sdk/site/guides/{contour_modeling.html → lib/exploitation/contour_modeling.html} +1400 -311
  78. dimine_python_sdk/site/guides/lib/exploitation/implicit_modeling.html +2362 -0
  79. dimine_python_sdk/site/guides/{laneway.html → lib/exploitation/laneway.html} +886 -61
  80. dimine_python_sdk/site/guides/lib/io/SUMMARY.html +1922 -0
  81. dimine_python_sdk/site/guides/lib/io/dmb_file.html +2394 -0
  82. dimine_python_sdk/site/guides/lib/io/dmd_file.html +2475 -0
  83. dimine_python_sdk/site/guides/lib/io/dmf_file.html +2667 -0
  84. dimine_python_sdk/site/guides/lib/io/dmg_file.html +2473 -0
  85. dimine_python_sdk/site/guides/lib/io/dmt_file.html +2501 -0
  86. dimine_python_sdk/site/guides/{file_conversion.html → lib/io/file_conversion.html} +1092 -267
  87. dimine_python_sdk/site/guides/lib/prospecting/SUMMARY.html +1915 -0
  88. dimine_python_sdk/site/guides/{db.html → lib/prospecting/drill_db.html} +1295 -207
  89. dimine_python_sdk/site/guides/{reserve_estimation.html → lib/prospecting/reserve_estimation.html} +1075 -307
  90. dimine_python_sdk/site/guides/lib/visualization/SUMMARY.html +1914 -0
  91. dimine_python_sdk/site/guides/{visualization.html → lib/visualization/visualization.html} +880 -55
  92. dimine_python_sdk/site/guides/models/SUMMARY.html +1912 -0
  93. dimine_python_sdk/site/guides/{data_types.html → models/data_types.html} +1050 -227
  94. dimine_python_sdk/site/index.html +834 -22
  95. dimine_python_sdk/site/objects.inv +0 -0
  96. dimine_python_sdk/site/quickstart.html +834 -22
  97. dimine_python_sdk/site/reference/SUMMARY.html +837 -24
  98. dimine_python_sdk/site/reference/cli/commands/camera.html +834 -22
  99. dimine_python_sdk/site/reference/cli/commands/convert.html +834 -22
  100. dimine_python_sdk/site/reference/cli/commands/datatable.html +834 -22
  101. dimine_python_sdk/site/reference/cli/commands/entity.html +834 -22
  102. dimine_python_sdk/site/reference/cli/commands/feature.html +834 -22
  103. dimine_python_sdk/site/reference/cli/commands/file.html +836 -24
  104. dimine_python_sdk/site/reference/cli/commands/geometry.html +1267 -414
  105. dimine_python_sdk/site/reference/cli/commands/laneway.html +834 -22
  106. dimine_python_sdk/site/reference/cli/commands/layer.html +834 -22
  107. dimine_python_sdk/site/reference/cli/commands/text.html +834 -22
  108. dimine_python_sdk/site/reference/conn/base_conn_client.html +834 -22
  109. dimine_python_sdk/site/reference/conn/conn_client.html +1156 -292
  110. dimine_python_sdk/site/reference/lib/bridge.html +4091 -1136
  111. dimine_python_sdk/site/reference/lib/exploitation/api.html +1618 -344
  112. dimine_python_sdk/site/reference/lib/exploitation/contour_modeling.html +1788 -513
  113. dimine_python_sdk/site/reference/lib/exploitation/implicit_modeling.html +834 -22
  114. dimine_python_sdk/site/reference/lib/exploitation/lane_way.html +834 -22
  115. dimine_python_sdk/site/reference/lib/exploitation/models.html +982 -51
  116. dimine_python_sdk/site/reference/lib/io/dmb_file.html +3682 -0
  117. dimine_python_sdk/site/reference/lib/io/dmd_file.html +4226 -0
  118. dimine_python_sdk/site/reference/lib/io/dmf_file.html +1038 -202
  119. dimine_python_sdk/site/reference/lib/io/dmg_file.html +1408 -134
  120. dimine_python_sdk/site/reference/lib/io/dmt_file.html +834 -22
  121. dimine_python_sdk/site/reference/lib/io/file_converter.html +941 -129
  122. dimine_python_sdk/site/reference/lib/io/models.html +859 -47
  123. dimine_python_sdk/site/reference/lib/native/algorithm.html +834 -22
  124. dimine_python_sdk/site/reference/lib/native/data_table.html +868 -48
  125. dimine_python_sdk/site/reference/lib/native/exploitation.html +1047 -49
  126. dimine_python_sdk/site/reference/lib/native/geometry.html +1723 -647
  127. dimine_python_sdk/site/reference/lib/native/io.html +838 -26
  128. dimine_python_sdk/site/reference/lib/native/models.html +1957 -212
  129. dimine_python_sdk/site/reference/lib/native/prospecting.html +2814 -452
  130. dimine_python_sdk/site/reference/lib/prospecting/api.html +2839 -5836
  131. dimine_python_sdk/site/reference/lib/prospecting/block_model.html +1337 -1434
  132. dimine_python_sdk/site/reference/lib/prospecting/compositing.html +1073 -160
  133. dimine_python_sdk/site/reference/lib/prospecting/drill_db.html +930 -3361
  134. dimine_python_sdk/site/reference/lib/prospecting/ellipsoid.html +959 -141
  135. dimine_python_sdk/site/reference/lib/prospecting/high_grade.html +960 -82
  136. dimine_python_sdk/site/reference/lib/prospecting/models.html +1328 -294
  137. dimine_python_sdk/site/reference/lib/visualization/exceptions.html +834 -22
  138. dimine_python_sdk/site/reference/lib/visualization/models.html +834 -22
  139. dimine_python_sdk/site/reference/lib/visualization/tin_renderer.html +834 -22
  140. dimine_python_sdk/site/reference/logger.html +834 -22
  141. dimine_python_sdk/site/reference/models/conn.html +834 -22
  142. dimine_python_sdk/site/reference/models/errors.html +834 -22
  143. dimine_python_sdk/site/reference/models/types.html +835 -23
  144. dimine_python_sdk/site/search/search_index.json +1 -1
  145. dimine_python_sdk/site/sitemap.xml +141 -81
  146. dimine_python_sdk/site/sitemap.xml.gz +0 -0
  147. dimine_python_sdk/site/summary.html +834 -22
  148. {dimine_python_sdk-0.2.0.dist-info → dimine_python_sdk-0.2.2.dist-info}/METADATA +3 -1
  149. {dimine_python_sdk-0.2.0.dist-info → dimine_python_sdk-0.2.2.dist-info}/RECORD +179 -182
  150. {dimine_python_sdk-0.2.0.dist-info → dimine_python_sdk-0.2.2.dist-info}/WHEEL +1 -1
  151. outline/build_dmd_from_scratch.py +426 -0
  152. outline/contour_extract.py +450 -0
  153. outline/contour_match.py +1095 -0
  154. outline/contour_pipeline.py +586 -0
  155. outline/contour_validate.py +645 -0
  156. outline/dmd_to_dmg.py +164 -0
  157. outline/drill_to_reserves.py +825 -0
  158. outline/ellipsoid_params.py +6 -6
  159. outline/implicit_modeling.py +365 -0
  160. outline/read_dmb.py +200 -0
  161. outline/read_dwg.py +353 -0
  162. outline/test_dmd_file.py +322 -0
  163. outline/test_dmg_to_segments.py +317 -0
  164. outline/test_extend_table.py +193 -0
  165. tests/conn_example.py +3 -3
  166. tests/test_csv_import_flow.py +6 -6
  167. tests/test_dmg_file.py +5 -5
  168. tests/test_high_grade_samples.py +4 -4
  169. tests/test_lib_refactor.py +35 -44
  170. tests/test_native_unit.py +0 -9
  171. tests/test_prospecting_api.py +110 -138
  172. tests/test_sample_compositing.py +11 -11
  173. dimine_python_sdk/docs/reference/lib/io/file_conversion.md +0 -15
  174. dimine_python_sdk/site/reference/lib/io/file_conversion.html +0 -3279
  175. outline/create_empty_block_model.py +0 -136
  176. outline/debug_compare.py +0 -100
  177. outline/debug_file.py +0 -75
  178. outline/debug_from_runtime.py +0 -102
  179. outline/debug_implicit.py +0 -111
  180. outline/debug_pipeline.py +0 -72
  181. outline/debug_simple.py +0 -232
  182. outline/debug_synthetic.py +0 -101
  183. outline/debug_verify.py +0 -76
  184. outline/distance_power_estimation.py +0 -454
  185. outline/dmg_test.py +0 -33
  186. outline/generate_drill_db.py +0 -77
  187. outline/high_grade_processor.py +0 -23
  188. outline/model_upload.py +0 -43
  189. outline/read_dmf_example.py +0 -202
  190. outline/sample_compositing.py +0 -23
  191. outline/test_contour_two_circles.py +0 -193
  192. outline/test_convert_dmd_to_dmg.py +0 -65
  193. outline/test_csv_to_drill_db.py +0 -184
  194. outline/test_dmf_conn.py +0 -55
  195. outline/test_dmf_create.py +0 -100
  196. outline/test_dmf_file_save.py +0 -219
  197. outline/test_dmf_modify.py +0 -85
  198. outline/test_dmf_read.py +0 -81
  199. outline/test_dmgfile.py +0 -108
  200. outline/test_drill_to_implicit_modeling.py +0 -218
  201. outline/test_full_pipeline.py +0 -406
  202. outline/test_geometry_update.py +0 -277
  203. outline/test_high_grade_processor.py +0 -535
  204. outline/test_laneway_connectivity.py +0 -188
  205. outline/test_multi_parallel_contour_modeling.py +0 -139
  206. outline/test_multilayer_ellipse.py +0 -169
  207. outline/test_native_min.py +0 -44
  208. outline/test_native_read.py +0 -100
  209. outline/test_pipeline_real.py +0 -54
  210. outline/test_read_dmd.py +0 -81
  211. outline/test_real_high_grade.py +0 -255
  212. outline/test_sample_compositing.py +0 -784
  213. outline/test_save_dmd.py +0 -84
  214. outline/test_save_synthetic.py +0 -86
  215. outline/test_shaft_generation.py +0 -174
  216. outline/test_viz_1_basic.py +0 -85
  217. outline/test_viz_2_combined.py +0 -83
  218. outline/test_viz_3_overlay.py +0 -92
  219. outline/test_viz_4_custom_camera.py +0 -96
  220. outline/test_viz_5_interactive.py +0 -85
  221. outline/test_viz_6_errors.py +0 -95
  222. outline/tt.py +0 -17
  223. /dimine_python_sdk/docs/guides/{data_edit.md → conn/data_edit.md} +0 -0
  224. /dimine_python_sdk/docs/guides/{features.md → conn/features.md} +0 -0
  225. /dimine_python_sdk/docs/guides/{laneway.md → lib/exploitation/laneway.md} +0 -0
  226. /dimine_python_sdk/docs/guides/{visualization.md → lib/visualization/visualization.md} +0 -0
  227. /dimine_python_sdk/docs/guides/{data_types.md → models/data_types.md} +0 -0
  228. {dimine_python_sdk-0.2.0.dist-info → dimine_python_sdk-0.2.2.dist-info}/entry_points.txt +0 -0
  229. {dimine_python_sdk-0.2.0.dist-info → dimine_python_sdk-0.2.2.dist-info}/top_level.txt +0 -0
@@ -3,7 +3,7 @@ from pathlib import Path
3
3
  from .conn import ConnClient
4
4
 
5
5
  DOCS_PATH = Path(__file__).parent / "docs"
6
- __VERSION__ = "0.2.0" # 由 build.bat 中的 generate_version.py 自动更新
6
+ __VERSION__ = "0.2.2" # 由 build.bat 中的 generate_version.py 自动更新
7
7
 
8
8
 
9
9
  __all__ = [
@@ -195,13 +195,13 @@ def register_commands(subparsers, format_parent) -> None:
195
195
  p.add_argument("--file-id", default=None, help="文件ID,多个文件打开时必填")
196
196
 
197
197
  p = subparsers.add_parser("file-open", parents=[format_parent], help="打开文件到 Dimine 场景")
198
- p.add_argument("--path", required=True, help="要打开的 .dmf/.dwg/.dxf 等文件路径")
198
+ p.add_argument("--path", "--file", required=True, help="要打开的 .dmf/.dwg/.dxf 等文件路径")
199
199
 
200
200
  p = subparsers.add_parser("file-close", parents=[format_parent], help="关闭 Dimine 中的文件")
201
201
  p.add_argument("--file-id", required=True, help="要关闭的文件ID")
202
202
 
203
203
  p = subparsers.add_parser("file-create", parents=[format_parent], help="在 Dimine 中创建新的空文件")
204
- p.add_argument("--path", required=True, help="新文件的路径")
204
+ p.add_argument("--path", "--file", required=True, help="新文件的路径")
205
205
 
206
206
  p = subparsers.add_parser("file-visibility", parents=[format_parent], help="设置文件显示/隐藏状态")
207
207
  p.add_argument("--file-id", required=True, help="文件ID")
@@ -202,6 +202,112 @@ def _build_single_geo_from_flat(file_id: str, layer_id: str, feature: str,
202
202
  return [obj]
203
203
 
204
204
 
205
+ def _build_partial_geo_from_flat(entity_id: str, geo_type: str | None = None,
206
+ file_id: str | None = None, layer_id: str | None = None,
207
+ feature: str | None = None, point: str | None = None,
208
+ points: str | None = None, faces: str | None = None,
209
+ color: str | None = None) -> list:
210
+ """从扁平参数构建部分几何体对象(仅用于 update)。
211
+
212
+ 与 _build_single_geo_from_flat 不同:
213
+ - 仅 entity_id 是必需的
214
+ - 未提供的字段设为 None/空,不会被发送到服务端
215
+ - geo_type 仅在提供几何数据时需要
216
+ """
217
+ from dimine_python_sdk.models.types import Line, Point, Shell
218
+
219
+ color_val = parse_color(color) if color else None
220
+
221
+ # 构建公共参数,只包含非 None 字段
222
+ common_kwargs = {"id": entity_id}
223
+ if file_id is not None:
224
+ common_kwargs["file"] = file_id
225
+ if layer_id is not None:
226
+ common_kwargs["layer"] = layer_id
227
+ if feature is not None:
228
+ common_kwargs["feature"] = feature
229
+ if color_val is not None:
230
+ common_kwargs["color"] = color_val
231
+
232
+ if geo_type == "point":
233
+ kwargs = dict(common_kwargs)
234
+ if point:
235
+ kwargs["geometry"] = parse_point3d(point)
236
+ obj = Point(**kwargs)
237
+ elif geo_type == "line":
238
+ kwargs = dict(common_kwargs)
239
+ if points:
240
+ kwargs["geometry"] = parse_points2d(points)
241
+ obj = Line(**kwargs)
242
+ elif geo_type == "shell":
243
+ kwargs = dict(common_kwargs)
244
+ if points:
245
+ pts = parse_points2d(points)
246
+ face_list = parse_points2d(faces) if faces else []
247
+ kwargs["geometry"] = {"points": pts, "faces": face_list}
248
+ obj = Shell(**kwargs)
249
+ elif geo_type is None:
250
+ # 无几何类型的纯属性更新(如只改 color)
251
+ obj = Shell(**common_kwargs)
252
+ else:
253
+ raise ValueError(f"无效的几何体类型 '{geo_type}',可选: point/line/shell")
254
+
255
+ obj.related = True
256
+ return [obj]
257
+
258
+
259
+ def _build_partial_geometry_objects(geometries: list) -> list:
260
+ """将 JSON 字典列表转换为部分几何体对象(仅用于 update)。
261
+
262
+ 与 _build_geometry_objects 不同:
263
+ - 只设置字典中存在的字段,缺失字段不填默认值
264
+ - 每个字典必须包含 'id' 字段
265
+ """
266
+ from dimine_python_sdk.models.types import Line, Point, Shell
267
+
268
+ result = []
269
+ for g in geometries:
270
+ entity_id = g.get("id")
271
+ if not entity_id:
272
+ raise ValueError("update 模式下每个几何体必须提供 'id' 字段")
273
+
274
+ geo_type = g.get("type", "shell")
275
+ file_id = g.get("file")
276
+ layer_id = g.get("layer")
277
+ feature_name = g.get("feature")
278
+ color = g.get("color")
279
+ geometry = g.get("geometry", {})
280
+
281
+ kwargs = {
282
+ "id": entity_id,
283
+ "file": file_id,
284
+ "layer": layer_id,
285
+ "feature": feature_name,
286
+ "color": color,
287
+ }
288
+
289
+ if geo_type == "point":
290
+ if "geometry" in g and geometry:
291
+ pt = geometry.get("point", [0, 0, 0])
292
+ kwargs["geometry"] = pt
293
+ obj = Point(**kwargs)
294
+ elif geo_type == "line":
295
+ if "geometry" in g and geometry:
296
+ pts = geometry.get("points", [])
297
+ kwargs["geometry"] = pts
298
+ obj = Line(**kwargs)
299
+ else:
300
+ if "geometry" in g and geometry:
301
+ pts = geometry.get("points", [])
302
+ faces = geometry.get("faces", [])
303
+ kwargs["geometry"] = {"points": pts, "faces": faces}
304
+ obj = Shell(**kwargs)
305
+
306
+ obj.related = True
307
+ result.append(obj)
308
+ return result
309
+
310
+
205
311
  def cmd_geometry_get(fmt: str, file_id: str, layer_id: str | None,
206
312
  entity_types: str | None = None, features: str | None = None,
207
313
  with_geometry: bool = False) -> None:
@@ -332,8 +438,10 @@ def cmd_geometry_update(fmt: str, geometries: str | None = None,
332
438
  """更新现有几何体对象(需 Dimine 运行)。
333
439
 
334
440
  两种模式:
335
- 1. 扁平参数(单几何体): --id --type --point/--points [--file --layer --feature --color]
441
+ 1. 扁平参数(单几何体): --id [--type --point/--points] [--file --layer --feature --color]
442
+ 不传的字段保持服务端原值不变。
336
443
  2. JSON/文件/stdin(批量): --geometries JSON/@file.json/-
444
+ 字典中只包含需要更新的字段。
337
445
  """
338
446
  err = require_remote_sdk()
339
447
  if err:
@@ -344,21 +452,21 @@ def cmd_geometry_update(fmt: str, geometries: str | None = None,
344
452
  raw_geos = _read_geometries_input(geometries)
345
453
  except ValueError as e:
346
454
  output_error(str(e), fmt)
347
- elif entity_id and geo_type and file_id and layer_id and feature:
455
+ elif entity_id:
348
456
  try:
349
- raw_geos = _build_single_geo_from_flat(
457
+ raw_geos = _build_partial_geo_from_flat(
458
+ entity_id=entity_id, geo_type=geo_type,
350
459
  file_id=file_id, layer_id=layer_id, feature=feature,
351
- geo_type=geo_type, point=point, points=points, faces=faces, color=color,
352
- entity_id=entity_id,
460
+ point=point, points=points, faces=faces, color=color,
353
461
  )
354
462
  except ValueError as e:
355
463
  output_error(str(e), fmt)
356
464
  else:
357
- output_error("请提供 --geometries(批量)或 --id、--file、--layer、--feature、--type(单个更新)", fmt)
465
+ output_error("请提供 --geometries(批量)或 --id(单个更新)", fmt)
358
466
 
359
467
  if raw_geos and isinstance(raw_geos[0], dict):
360
468
  try:
361
- geo_objects = _build_geometry_objects(raw_geos)
469
+ geo_objects = _build_partial_geometry_objects(raw_geos)
362
470
  except Exception as e:
363
471
  output_error(f"构建几何体对象失败: {e}", fmt)
364
472
  else:
@@ -391,7 +499,10 @@ def cmd_geometry_upsert(fmt: str, geometries: str | None = None,
391
499
 
392
500
  两种模式:
393
501
  1. 扁平参数(单几何体): --file --layer --feature --type [--id] --point/--points [--color]
502
+ - 有 --id: 部分更新,不传的字段保持不变
503
+ - 无 --id: 新建几何体
394
504
  2. JSON/文件/stdin(批量): --geometries JSON/@file.json/-
505
+ - 字典中含 "id" 则部分更新,不含则新建
395
506
  """
396
507
  err = require_remote_sdk()
397
508
  if err:
@@ -402,26 +513,39 @@ def cmd_geometry_upsert(fmt: str, geometries: str | None = None,
402
513
  raw_geos = _read_geometries_input(geometries)
403
514
  except ValueError as e:
404
515
  output_error(str(e), fmt)
516
+ # 按是否有 id 分离:有 id 的部分更新,无 id 的新建
517
+ update_dicts = [g for g in raw_geos if g.get("id")]
518
+ create_dicts = [g for g in raw_geos if not g.get("id")]
519
+ geo_objects = []
520
+ if update_dicts:
521
+ try:
522
+ geo_objects.extend(_build_partial_geometry_objects(update_dicts))
523
+ except Exception as e:
524
+ output_error(f"构建更新几何体对象失败: {e}", fmt)
525
+ if create_dicts:
526
+ try:
527
+ geo_objects.extend(_build_geometry_objects(create_dicts))
528
+ except Exception as e:
529
+ output_error(f"构建新建几何体对象失败: {e}", fmt)
405
530
  elif geo_type and file_id and layer_id and feature:
406
531
  try:
407
- raw_geos = _build_single_geo_from_flat(
408
- file_id=file_id, layer_id=layer_id, feature=feature,
409
- geo_type=geo_type, point=point, points=points, faces=faces, color=color,
410
- entity_id=entity_id,
411
- )
532
+ if entity_id:
533
+ raw_geos = _build_partial_geo_from_flat(
534
+ entity_id=entity_id, geo_type=geo_type,
535
+ file_id=file_id, layer_id=layer_id, feature=feature,
536
+ point=point, points=points, faces=faces, color=color,
537
+ )
538
+ else:
539
+ raw_geos = _build_single_geo_from_flat(
540
+ file_id=file_id, layer_id=layer_id, feature=feature,
541
+ geo_type=geo_type, point=point, points=points, faces=faces, color=color,
542
+ )
412
543
  except ValueError as e:
413
544
  output_error(str(e), fmt)
545
+ geo_objects = raw_geos
414
546
  else:
415
547
  output_error("请提供 --geometries(批量)或 --file、--layer、--feature、--type(单个)", fmt)
416
548
 
417
- if raw_geos and isinstance(raw_geos[0], dict):
418
- try:
419
- geo_objects = _build_geometry_objects(raw_geos)
420
- except Exception as e:
421
- output_error(f"构建几何体对象失败: {e}", fmt)
422
- else:
423
- geo_objects = raw_geos
424
-
425
549
  async def _run():
426
550
  client = await get_client()
427
551
  result = await client.update_or_create_geometry(geo_objects)
@@ -163,7 +163,7 @@ class ConnClient(BaseConnClient):
163
163
  "layer": geom.layer,
164
164
  "feature": geom.feature or "0",
165
165
  "entity_type": geom.type,
166
- "color": geom.color,
166
+ "color": geom.color if geom.color is not None else [0, 0, 0],
167
167
  "properties": [
168
168
  {"name": prop.name, "value": prop.value}
169
169
  for prop in geom.properties
@@ -350,18 +350,31 @@ class ConnClient(BaseConnClient):
350
350
  # 格式化模型数据
351
351
  formatted_geometries = []
352
352
  for geom in geometries:
353
- formatted_geom = {
354
- "id": geom.id,
355
- "file": geom.file,
356
- "layer": geom.layer,
357
- "feature": geom.feature,
358
- "entity_type": geom.type,
359
- "geometry": geom.geometry.tolist() if geom.type!="shell" else {"indexs": geom.geometry.faces.tolist(), "points": geom.geometry.points.tolist()},
360
- "color": geom.color,
361
- }
353
+ formatted_geom: Dict[str, Any] = {"id": geom.id}
354
+ # 仅发送显式设置的非 None/空字段
355
+ if geom.file is not None:
356
+ formatted_geom["file"] = geom.file
357
+ if geom.layer is not None:
358
+ formatted_geom["layer"] = geom.layer
359
+ if geom.feature is not None:
360
+ formatted_geom["feature"] = geom.feature
361
+ if geom.type is not None:
362
+ formatted_geom["entity_type"] = geom.type
363
+ if geom.color is not None:
364
+ formatted_geom["color"] = geom.color
365
+ # geometry:仅当非空时发送
366
+ if geom.type == "shell" and geom.geometry is not None:
367
+ geo = geom.geometry
368
+ if geo.points is not None and len(geo.points) > 0:
369
+ formatted_geom.setdefault("geometry", {})["points"] = geo.points.tolist()
370
+ if geo.faces is not None and len(geo.faces) > 0:
371
+ formatted_geom.setdefault("geometry", {})["indexs"] = geo.faces.tolist()
372
+ elif geom.type in ("point", "line") and geom.geometry is not None and len(geom.geometry) > 0:
373
+ formatted_geom["geometry"] = geom.geometry.tolist()
362
374
 
363
375
  # 处理属性信息
364
- formatted_geom["properties"] = [
376
+ if geom.properties:
377
+ formatted_geom["properties"] = [
365
378
  {"name": prop.name, "value": prop.value}
366
379
  for prop in geom.properties
367
380
  ]
@@ -15,7 +15,7 @@ Dimine 需已启动并开启 WebSocket 服务(默认 `ws://localhost:8000`)
15
15
  ```python
16
16
  import asyncio
17
17
  import pandas as pd
18
- from dimine_python_sdk.lib.prospecting import DrillDBManager
18
+ from dimine_python_sdk.lib.io import DmdFile
19
19
  from dimine_python_sdk.conn import open_client
20
20
 
21
21
 
@@ -23,9 +23,9 @@ async def import_and_open():
23
23
  # ------------------------------------------------------------------
24
24
  # 1. 从 CSV 导入并映射为中文标准列名
25
25
  # ------------------------------------------------------------------
26
- db = DrillDBManager()
26
+ dmd = DmdFile()
27
27
 
28
- db.collar = pd.read_csv("raw_collar.csv").rename(columns={
28
+ dmd.collar = pd.read_csv("raw_collar.csv").rename(columns={
29
29
  "HoleID": "工程号",
30
30
  "X": "横坐标",
31
31
  "Y": "纵坐标",
@@ -34,14 +34,14 @@ async def import_and_open():
34
34
  "Line": "勘探线",
35
35
  })
36
36
 
37
- db.survey = pd.read_csv("raw_survey.csv").rename(columns={
37
+ dmd.survey = pd.read_csv("raw_survey.csv").rename(columns={
38
38
  "HoleID": "工程号",
39
39
  "Depth": "测斜深度",
40
40
  "Azimuth": "方位角",
41
41
  "Dip": "倾角",
42
42
  })
43
43
 
44
- db.lithology = pd.read_csv("raw_lithology.csv").rename(columns={
44
+ dmd.lithology = pd.read_csv("raw_lithology.csv").rename(columns={
45
45
  "HoleID": "工程号",
46
46
  "From": "起始",
47
47
  "To": "结束",
@@ -49,7 +49,7 @@ async def import_and_open():
49
49
  })
50
50
 
51
51
  # 样品表可选;除标准列外可保留元素品位等非标准列
52
- db.sample = pd.read_csv("raw_sample.csv").rename(columns={
52
+ dmd.sample = pd.read_csv("raw_sample.csv").rename(columns={
53
53
  "HoleID": "工程号",
54
54
  "SampleID": "样本编号",
55
55
  "From": "起始",
@@ -62,7 +62,7 @@ async def import_and_open():
62
62
  # 2. 保存为 .dmd 钻孔数据库
63
63
  # ------------------------------------------------------------------
64
64
  dmd_path = "C:/Projects/Mine/钻孔数据.dmd"
65
- db.save(dmd_path)
65
+ dmd.save(dmd_path)
66
66
  print(f"已保存: {dmd_path}")
67
67
 
68
68
  # ------------------------------------------------------------------
@@ -77,6 +77,57 @@ if __name__ == "__main__":
77
77
  asyncio.run(import_and_open())
78
78
  ```
79
79
 
80
+ ## 导入扩展表
81
+
82
+ 除四大核心表外,还可以将分层表、地质编录表等自定义数据作为扩展表导入:
83
+
84
+ ```python
85
+ import pandas as pd
86
+ from dimine_python_sdk.lib.io import DmdFile
87
+ from dimine_python_sdk.lib.native.models import NativeExtendTable
88
+
89
+ dmd = DmdFile()
90
+
91
+ # 设置核心表...
92
+ dmd.collar = pd.read_csv("raw_collar.csv").rename(columns={...})
93
+ dmd.survey = pd.read_csv("raw_survey.csv").rename(columns={...})
94
+ dmd.lithology = pd.read_csv("raw_lithology.csv").rename(columns={...})
95
+
96
+ # 方式一:使用 NativeExtendTable(推荐)
97
+ ext_df = pd.read_csv("raw_stratigraphy.csv").rename(columns={
98
+ "HoleID": "工程号",
99
+ "From": "起始",
100
+ "To": "结束",
101
+ "LayerName": "分层名称",
102
+ })
103
+
104
+ dmd.extend_tables = [
105
+ NativeExtendTable(
106
+ df=ext_df,
107
+ dmt_filename="分层表.dmt",
108
+ hole_id_column="工程号",
109
+ from_column="起始",
110
+ to_column="结束",
111
+ display_title="分层表",
112
+ ),
113
+ ]
114
+
115
+ # 方式二:使用 dict(向后兼容)
116
+ dmd.extend_tables = [
117
+ {
118
+ "df": ext_df,
119
+ "dmt_filename": "分层表.dmt",
120
+ "hole_id_column": "工程号",
121
+ "from_column": "起始",
122
+ "to_column": "结束",
123
+ "display_title": "分层表",
124
+ },
125
+ ]
126
+
127
+ # 保存(扩展表自动写入 _分层表.dmt 并在 .dmd 中记录引用)
128
+ dmd.save("C:/Projects/Mine/钻孔数据.dmd")
129
+ ```
130
+
80
131
  ## 列名映射速查
81
132
 
82
133
  | 外部 CSV 列名 | 中文标准列名 | 对应表 |
@@ -94,6 +145,7 @@ if __name__ == "__main__":
94
145
  | `Rock` | `岩性编号` | 岩性表 |
95
146
 
96
147
  - 列名映射使用 `pandas.DataFrame.rename(columns=...)` 完成
97
- - DataFrame 列名必须包含[标准列名映射规范](../guides/db.md#标准列名映射规范)中对应表的必选标准列
148
+ - DataFrame 列名必须包含[标准列名映射规范](../guides/lib/prospecting/drill_db.md#13-standard-column-mapping)中对应表的必选标准列
98
149
  - 元素品位等非标准列(如 `CU`、`AU`)无需映射,直接保留即可 — 它们会写入 `.dmt` 子表但不出现于 `.dmd` 引用
99
150
  - `.dmd` 文件与 `{stem}_collar.dmt` / `{stem}_survey.dmt` / `{stem}_lithology.dmt` 等子表文件保存在同一目录下
151
+ - 扩展表的 `.dmt` 文件名由 `dmt_filename` 参数指定(如 `分层表.dmt`),保存时写入同一目录
@@ -73,14 +73,15 @@ with DmfFile("input.dmf") as dmf:
73
73
  # 新增图层
74
74
  layer = dmf.add_layer("计算结果")
75
75
 
76
- # 插入点
77
- layer.insert_point(np.array([100.0, 200.0, 300.0]), name="测点-1")
76
+ # 插入点(可指定颜色 color=[R, G, B])
77
+ layer.insert_point(np.array([100.0, 200.0, 300.0]), name="测点-1", color=[255, 0, 0])
78
78
 
79
79
  # 插入直线
80
80
  layer.insert_line(
81
81
  np.array([0.0, 0.0, 0.0]),
82
82
  np.array([10.0, 0.0, 0.0]),
83
83
  name="中线",
84
+ color=[0, 255, 0],
84
85
  )
85
86
 
86
87
  # 插入多段线
@@ -92,6 +93,7 @@ with DmfFile("input.dmf") as dmf:
92
93
  [0, 10, 0],
93
94
  ]),
94
95
  name="轮廓线",
96
+ color=[0, 0, 255],
95
97
  )
96
98
 
97
99
  # 保存为新文件
@@ -120,7 +122,7 @@ faces = np.array([
120
122
 
121
123
  with DmfFile() as dmf:
122
124
  layer = dmf.add_layer("三角网")
123
- layer.insert_shell(points, faces, name="坡面")
125
+ layer.insert_shell(points, faces, name="坡面", color=[128, 128, 128])
124
126
  dmf.save("shell_model.dmf")
125
127
  ```
126
128