Skip to content

geometry

Native Layer v2 几何与数据库实体模块

对外暴露 numpy 数组、NativeTin、NativeEntity/NativeLayer 等 Python 类型, 内部持有 C++ 对象。

NativeDatabaseHandle

数据库不透明句柄,内部持有 C++ dmDbDatabase 对象

Source code in dimine_python_sdk\lib\native\geometry.py
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
class NativeDatabaseHandle:
    """数据库不透明句柄,内部持有 C++ dmDbDatabase 对象"""

    def __init__(self, cpp_obj: Any):
        self._cpp_obj = cpp_obj

    def __repr__(self) -> str:
        return f"NativeDatabaseHandle(layers={self.layer_count})"

    @classmethod
    def create(cls) -> "NativeDatabaseHandle":
        """创建只读数据库"""
        return cls(Dm.dmDbDatabase())

    @classmethod
    def create_local(cls) -> "NativeDatabaseHandle":
        """创建可编辑本地数据库"""
        return cls(Dm.dmDbDatabase.CreateLocalDB())

    def load(self, file_path: str) -> None:
        """加载 DMF 文件"""
        if not self._cpp_obj.Load(file_path):
            raise NativeDBError(f"加载 DMF 失败: {file_path}")

    def save(self, file_path: str | None = None) -> None:
        """保存 DMF 文件"""
        if file_path is None:
            if not self._cpp_obj.Save():
                raise NativeDBError("保存 DMF 失败")
        else:
            if not self._cpp_obj.Save(file_path):
                raise NativeDBError(f"保存 DMF 失败: {file_path}")

    def set_file_name(self, file_path: str) -> None:
        """设置文件路径"""
        self._cpp_obj.SetFileName(file_path)

    @property
    def layer_count(self) -> int:
        return self._cpp_obj.GetLayersCount()

    def get_layer(self, index: int) -> NativeLayerHandle:
        """通过索引获取图层"""
        if index < 0 or index >= self.layer_count:
            raise IndexError(f"图层索引 {index} 超出范围 [0, {self.layer_count})")
        cpp_layer = self._cpp_obj.GetLayerFromIndex(index)
        if not cpp_layer:
            raise NativeDBError(f"获取图层 {index} 返回空对象")
        return NativeLayerHandle(cpp_layer)

    def insert_layer(self, name: str) -> NativeLayerHandle:
        """插入新图层"""
        cpp_layer = self._cpp_obj.InsertLayer(str(name))
        return NativeLayerHandle(cpp_layer)

    def get_active_layer(self) -> NativeLayerHandle | None:
        """获取当前激活图层"""
        cpp_layer = self._cpp_obj.GetActiveLayer()
        if not cpp_layer:
            return None
        return NativeLayerHandle(cpp_layer)

    def insert_feature(self, name: str) -> NativeFeature:
        """插入新要素"""
        cpp_feature = self._cpp_obj.InsertFeature(str(name))
        return NativeFeature(
            name=cpp_feature.GetFeatureName(),
            properties=self._extract_feature_properties(cpp_feature),
        )

    def _insert_feature_raw(self, name: str) -> Any:
        """插入新要素并返回底层 C++ dmDbFeatureSet 对象"""
        return self._cpp_obj.InsertFeature(str(name))

    def get_feature(self, name: str) -> NativeFeature | None:
        """按名称获取要素"""
        cpp_feature = self._cpp_obj.GetFeatureFromName(str(name))
        if not cpp_feature:
            return None
        return NativeFeature(
            name=cpp_feature.GetFeatureName(),
            properties=self._extract_feature_properties(cpp_feature),
        )

    def set_active_feature(self, name: str) -> None:
        """设置当前激活要素"""
        self._cpp_obj.SetActiveFeature(str(name))

    def features(self) -> list[NativeFeature]:
        """获取所有要素定义(含属性定义清单)"""
        cpp_features = self._cpp_obj.GetFeatureSet()
        if not cpp_features:
            return []
        features= [
            NativeFeature(
                name=f.GetFeatureName(),
                properties=self._extract_feature_properties(f),
            )
            for f in cpp_features
        ]
        return features

    def set_features(self, features: Sequence[NativeFeature]) -> None:
        """注册所有要素及其属性定义。

        以 ``features`` 作为属性定义清单,直接写入各要素的 CDataTable;
        不再依赖实体反向推导,也不调用 ``add_property``。
        """
        for feat in features:
            if not feat.name:
                continue
            cpp_feature = self._insert_feature_raw(feat.name)
            self._register_feature_properties(cpp_feature, feat.properties)

    # 要素属性定义表中的元数据列,非用户自定义属性
    _FEATURE_META_COLUMNS = frozenset({"ent_handle", "实体类型", "实体名称", "XData"})

    def _extract_feature_properties(self, cpp_feature: Any) -> list[NativeProperty]:
        """从要素的属性定义表中提取属性定义清单

        属性定义表结构为:若干标准元数据列(ent_handle、实体类型、实体名称、
        XData)+ 若干用户自定义属性列。每列的列名即属性名,数据类型通过
        ``NativeDataTableHandle.field_definitions()`` 调用 ``Get_Field_Type``
        获取,不再依赖首行记录的值。
        """
        props: list[NativeProperty] = []
        try:
            cpp_table = cpp_feature.GetDataTable()
            if cpp_table is None:
                return props

            table = NativeDataTableHandle(cpp_table)
            for field_def in table.field_definitions():
                name = field_def.name
                if name in self._FEATURE_META_COLUMNS:
                    continue

                type_code = field_def.type if isinstance(field_def.type, int) else NativeFieldType.STRING
                ptype = _FIELD_TYPE_TO_PROPERTY_TYPE.get(type_code, "string")
                props.append(NativeProperty(name=name, type=ptype, value=None))
        except Exception as e:
            raise NativeDBError(f"提取属性定义失败: {e}") from e
        return props

    def _register_feature_properties(
        self,
        cpp_feature: Any,
        properties: Sequence[NativeProperty],
    ) -> None:
        """向要素的属性定义表写入属性定义清单。

        使用 ``NativeDataTableHandle`` 操作 CDataTable:属性名作为列名,
        列类型通过 ``Get_Field_Type`` 编码存放;元数据列仅确保存在,
        不再向首行记录写入类型码。
        """
        cpp_table = cpp_feature.GetDataTable()
        if cpp_table is None:
            raise NativeDBError("要素未关联属性定义表")

        table = NativeDataTableHandle(cpp_table)
        existing = set(table.field_names())

        # 确保元数据列存在
        for col in self._FEATURE_META_COLUMNS:
            if col not in existing:
                table.add_field(col, NativeFieldType.STRING)
                existing.add(col)

        # 添加用户属性列
        for prop in properties:
            if (
                not prop.name
                or prop.name in self._FEATURE_META_COLUMNS
                or prop.name in existing
            ):
                continue
            code = _PROPERTY_TYPE_TO_FIELD_TYPE.get(prop.type or "string", NativeFieldType.STRING)
            table.add_field(prop.name, code)
            existing.add(prop.name)

    def add_property(self, name: str, ptype: str) -> None:
        """添加属性定义"""
        success, message = self._cpp_obj.AddProperty(str(name), str(ptype))
        if not success:
            raise NativeDBError(f"添加属性失败: {message}")

    def close_polyline(self) -> None:
        """闭合多段线"""
        self._cpp_obj.ClosePolyline()

    def set_color(self, rgb: Sequence[int]) -> None:
        """设置当前颜色"""
        r, g, b = ensure_sequence_3(rgb)
        self._cpp_obj.SetColor(int(r), int(g), int(b))

add_property(name, ptype)

添加属性定义

Source code in dimine_python_sdk\lib\native\geometry.py
773
774
775
776
777
def add_property(self, name: str, ptype: str) -> None:
    """添加属性定义"""
    success, message = self._cpp_obj.AddProperty(str(name), str(ptype))
    if not success:
        raise NativeDBError(f"添加属性失败: {message}")

close_polyline()

闭合多段线

Source code in dimine_python_sdk\lib\native\geometry.py
779
780
781
def close_polyline(self) -> None:
    """闭合多段线"""
    self._cpp_obj.ClosePolyline()

create() classmethod

创建只读数据库

Source code in dimine_python_sdk\lib\native\geometry.py
602
603
604
605
@classmethod
def create(cls) -> "NativeDatabaseHandle":
    """创建只读数据库"""
    return cls(Dm.dmDbDatabase())

create_local() classmethod

创建可编辑本地数据库

Source code in dimine_python_sdk\lib\native\geometry.py
607
608
609
610
@classmethod
def create_local(cls) -> "NativeDatabaseHandle":
    """创建可编辑本地数据库"""
    return cls(Dm.dmDbDatabase.CreateLocalDB())

features()

获取所有要素定义(含属性定义清单)

Source code in dimine_python_sdk\lib\native\geometry.py
681
682
683
684
685
686
687
688
689
690
691
692
693
def features(self) -> list[NativeFeature]:
    """获取所有要素定义(含属性定义清单)"""
    cpp_features = self._cpp_obj.GetFeatureSet()
    if not cpp_features:
        return []
    features= [
        NativeFeature(
            name=f.GetFeatureName(),
            properties=self._extract_feature_properties(f),
        )
        for f in cpp_features
    ]
    return features

get_active_layer()

获取当前激活图层

Source code in dimine_python_sdk\lib\native\geometry.py
648
649
650
651
652
653
def get_active_layer(self) -> NativeLayerHandle | None:
    """获取当前激活图层"""
    cpp_layer = self._cpp_obj.GetActiveLayer()
    if not cpp_layer:
        return None
    return NativeLayerHandle(cpp_layer)

get_feature(name)

按名称获取要素

Source code in dimine_python_sdk\lib\native\geometry.py
667
668
669
670
671
672
673
674
675
def get_feature(self, name: str) -> NativeFeature | None:
    """按名称获取要素"""
    cpp_feature = self._cpp_obj.GetFeatureFromName(str(name))
    if not cpp_feature:
        return None
    return NativeFeature(
        name=cpp_feature.GetFeatureName(),
        properties=self._extract_feature_properties(cpp_feature),
    )

get_layer(index)

通过索引获取图层

Source code in dimine_python_sdk\lib\native\geometry.py
634
635
636
637
638
639
640
641
def get_layer(self, index: int) -> NativeLayerHandle:
    """通过索引获取图层"""
    if index < 0 or index >= self.layer_count:
        raise IndexError(f"图层索引 {index} 超出范围 [0, {self.layer_count})")
    cpp_layer = self._cpp_obj.GetLayerFromIndex(index)
    if not cpp_layer:
        raise NativeDBError(f"获取图层 {index} 返回空对象")
    return NativeLayerHandle(cpp_layer)

insert_feature(name)

插入新要素

Source code in dimine_python_sdk\lib\native\geometry.py
655
656
657
658
659
660
661
def insert_feature(self, name: str) -> NativeFeature:
    """插入新要素"""
    cpp_feature = self._cpp_obj.InsertFeature(str(name))
    return NativeFeature(
        name=cpp_feature.GetFeatureName(),
        properties=self._extract_feature_properties(cpp_feature),
    )

insert_layer(name)

插入新图层

Source code in dimine_python_sdk\lib\native\geometry.py
643
644
645
646
def insert_layer(self, name: str) -> NativeLayerHandle:
    """插入新图层"""
    cpp_layer = self._cpp_obj.InsertLayer(str(name))
    return NativeLayerHandle(cpp_layer)

load(file_path)

加载 DMF 文件

Source code in dimine_python_sdk\lib\native\geometry.py
612
613
614
615
def load(self, file_path: str) -> None:
    """加载 DMF 文件"""
    if not self._cpp_obj.Load(file_path):
        raise NativeDBError(f"加载 DMF 失败: {file_path}")

save(file_path=None)

保存 DMF 文件

Source code in dimine_python_sdk\lib\native\geometry.py
617
618
619
620
621
622
623
624
def save(self, file_path: str | None = None) -> None:
    """保存 DMF 文件"""
    if file_path is None:
        if not self._cpp_obj.Save():
            raise NativeDBError("保存 DMF 失败")
    else:
        if not self._cpp_obj.Save(file_path):
            raise NativeDBError(f"保存 DMF 失败: {file_path}")

set_active_feature(name)

设置当前激活要素

Source code in dimine_python_sdk\lib\native\geometry.py
677
678
679
def set_active_feature(self, name: str) -> None:
    """设置当前激活要素"""
    self._cpp_obj.SetActiveFeature(str(name))

set_color(rgb)

设置当前颜色

Source code in dimine_python_sdk\lib\native\geometry.py
783
784
785
786
def set_color(self, rgb: Sequence[int]) -> None:
    """设置当前颜色"""
    r, g, b = ensure_sequence_3(rgb)
    self._cpp_obj.SetColor(int(r), int(g), int(b))

set_features(features)

注册所有要素及其属性定义。

features 作为属性定义清单,直接写入各要素的 CDataTable; 不再依赖实体反向推导,也不调用 add_property

Source code in dimine_python_sdk\lib\native\geometry.py
695
696
697
698
699
700
701
702
703
704
705
def set_features(self, features: Sequence[NativeFeature]) -> None:
    """注册所有要素及其属性定义。

    以 ``features`` 作为属性定义清单,直接写入各要素的 CDataTable;
    不再依赖实体反向推导,也不调用 ``add_property``。
    """
    for feat in features:
        if not feat.name:
            continue
        cpp_feature = self._insert_feature_raw(feat.name)
        self._register_feature_properties(cpp_feature, feat.properties)

set_file_name(file_path)

设置文件路径

Source code in dimine_python_sdk\lib\native\geometry.py
626
627
628
def set_file_name(self, file_path: str) -> None:
    """设置文件路径"""
    self._cpp_obj.SetFileName(file_path)

NativeEntityHandle

实体不透明句柄,内部持有 C++ dmDbEntity 对象。

警告:OpenNextEntity() 返回的实体对象通常是图层查询游标的临时引用。 当需要遍历图层中多个实体时,应使用 NativeLayerHandle.iter_entities(), 该函数会在进入下一次 OpenNextEntity() 之前把数据完整拷贝到 Python 对象。

Source code in dimine_python_sdk\lib\native\geometry.py
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
class NativeEntityHandle:
    """实体不透明句柄,内部持有 C++ dmDbEntity 对象。

    警告:OpenNextEntity() 返回的实体对象通常是图层查询游标的临时引用。
    当需要遍历图层中多个实体时,应使用 NativeLayerHandle.iter_entities(),
    该函数会在进入下一次 OpenNextEntity() 之前把数据完整拷贝到 Python 对象。
    """

    # 要素属性定义表中的元数据列,非用户自定义属性
    _ATT_META_COLUMNS = frozenset({"ent_handle", "实体类型", "实体名称", "XData"})

    def __init__(self, cpp_obj: Any):
        if cpp_obj is None:
            raise Exception("cpp obj is None.")
        self._cpp_obj = cpp_obj


    def __repr__(self) -> str:
        return f"NativeEntityHandle(type_code={self.type_code}, name={self.name!r})"

    @property
    def type_code(self) -> int:
        return self._cpp_obj.GetType()

    @property
    def type_name(self) -> str:
        return _type_code_to_name(self.type_code)

    @property
    def name(self) -> str:
        try:
            return self._cpp_obj.GetEntityName() or ""
        except Exception as exc:
            raise NativeGeometryError(f"读取实体名称失败: {exc}") from exc

    @name.setter
    def name(self, value: str) -> None:
        self._cpp_obj.SetEntityName(str(value))

    @property
    def color(self) -> list[int]:
        try:
            color = self._cpp_obj.GetColor()
            r, g, b = int(color[0]), int(color[1]), int(color[2])
            return [r, g, b]
        except Exception as exc:
            raise NativeGeometryError(f"读取实体颜色失败: {exc}") from exc

    @color.setter
    def color(self, rgb: Sequence[int]) -> None:
        r, g, b = ensure_sequence_3(rgb)
        self._cpp_obj.SetColor(int(r), int(g), int(b))

    def att_record(self) -> tuple[str, list[NativeProperty]]:
        """读取实体属性记录

        返回 (所属要素名称, 属性列表)。
        属性列的数据类型通过 ``NativeDataTableHandle.field_definitions()``
        调用 ``Get_Field_Type`` 获取,不再从首行记录推断;实体属性记录
        (record)中存放的是对应字段的真实属性值。record 仅提供
        asDouble/asString 两种读取接口,字符串类用 asString,其余数值型
        统一用 asDouble。
        """
        props: list[NativeProperty] = []
        try:
            record = self._cpp_obj.GetAttRecord()
        except Exception as exc:
            raise NativeDBError(f"获取属性记录失败: {exc}") from exc
        if record is None:
            return "", props

        try:
            feature = self._cpp_obj.GetFeatureSet()
            if feature is None:
                return "", props
            feature_name = feature.GetFeatureName() or ""

            dt = feature.GetDataTable()
            if dt is None:
                return feature_name, props
            table = NativeDataTableHandle(dt)

            for field_def in table.field_definitions():
                name = field_def.name
                if name in self._ATT_META_COLUMNS:
                    continue

                type_code = field_def.type if isinstance(field_def.type, int) else 7
                ptype = _FIELD_TYPE_TO_PROPERTY_TYPE.get(type_code, "double")
                value: Any
                if ptype == "string":
                    value = record.asString(name)
                elif ptype == "date":
                    value = datetime.strptime(record.asString(name), "%Y-%m-%d %H:%M")
                else:
                    # byte/short/int/long/float/double 等数值型统一用 asDouble
                    value = record.asDouble(name)

                props.append(NativeProperty(name=name, value=value, type=ptype))
            return feature_name, props
        except Exception as exc:
            raise NativeDBError(f"读取实体属性记录失败: {exc}") from exc

    # 数值型属性类型集合,写入属性记录时统一按 double 处理
    _NUMERIC_PROPERTY_TYPES = {"byte", "short", "int", "long", "float", "double"}

    def set_properties(self, properties: list[NativeProperty]) -> None:
        """将 NativeProperty 列表写入 C++ 实体属性记录。

        与 ``att_record`` 互为逆过程:先获取 ``self._cpp_obj.GetAttRecord()``,
        再对记录中的每个字段调用 ``Set_Value`` 写入真实属性值。
        单条属性写入失败不会中断整体写入。
        """
        if not properties:
            return

        try:
            record = self._cpp_obj.GetAttRecord()
        except Exception as exc:
            raise NativeDBError(f"获取属性记录失败: {exc}") from exc
        if record is None:
            return

        for prop in properties:
            if prop.value is None or prop.name == "text":
                continue
            try:
                if (prop.type or "string") in self._NUMERIC_PROPERTY_TYPES:
                    record.Set_Value(prop.name, float(prop.value))
                elif prop.type == "date":
                    if isinstance(prop.value, datetime):
                        record.Set_Value(prop.name, prop.value.strftime("%Y-%m-%d 0:0"))
                    else:
                        record.Set_Value(prop.name, str(prop.value))
                else:
                    record.Set_Value(prop.name, str(prop.value))
            except Exception:
                pass

    @staticmethod
    def _polydata_points(polydata: Any) -> "np.ndarray":
        """提取 polydata 中的顶点坐标"""
        import numpy as np

        n = polydata.GetNumberOfPoints()
        if n == 0:
            return np.zeros((0, 3), dtype=float)
        pts = []
        for i in range(n):
            pt = polydata.GetPoint(i)
            pts.append([float(pt.x), float(pt.y), float(pt.z)])
        return np.array(pts, dtype=float)

    def geometry(self) -> "np.ndarray | NativeTin":
        """读取实体几何数据"""
        import numpy as np

        tcode = self.type_code
        if tcode == _type_name_to_code("text"):
            try:
                polydata = self._cpp_obj.ToPolyData()
                pts = self._polydata_points(polydata)
                if len(pts) > 0:
                    return pts[0]
            except Exception:
                pass
            return ensure_array_3d([0, 0, 0])

        if tcode == _type_name_to_code("shell"):
            try:
                shell = self._cpp_obj.ConvertToShell()
                data = json.loads(shell.ParseToJson())
                points = np.array(data["points"], dtype=float).reshape(-1, 3)
                faces = np.array(data["indexs"], dtype=int).reshape(-1, 3)
                return NativeTin(points=points, faces=faces)
            except Exception:
                pass

        try:
            polydata = self._cpp_obj.ToPolyData()
            pts = self._polydata_points(polydata)
            if tcode == _type_name_to_code("point") and pts.shape[0] > 0:
                return pts[0]
            return pts
        except Exception as exc:
            raise NativeGeometryError(f"读取实体几何失败: {exc}") from exc

    def to_model(self) -> NativeEntity:
        """转换为 NativeEntity 数据模型"""
        feature_name, props = self.att_record()
        if self.type_name == "text":
            # 文本内容优先从属性记录读取;缺失时回退到 GetText()
            if not any(p.name == "text" for p in props):
                try:
                    text = self._cpp_obj.GetText() or ""
                except Exception:
                    text = ""
                if text:
                    props = list(props)
                    props.append(NativeProperty(name="text", value=text, type="string"))
        return NativeEntity(
            entity_type=self.type_name,
            name=self.name,
            geometry=self.geometry(),
            color=self.color,
            properties=props,
            feature_name=feature_name,
        )

att_record()

读取实体属性记录

返回 (所属要素名称, 属性列表)。 属性列的数据类型通过 NativeDataTableHandle.field_definitions() 调用 Get_Field_Type 获取,不再从首行记录推断;实体属性记录 (record)中存放的是对应字段的真实属性值。record 仅提供 asDouble/asString 两种读取接口,字符串类用 asString,其余数值型 统一用 asDouble。

Source code in dimine_python_sdk\lib\native\geometry.py
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
def att_record(self) -> tuple[str, list[NativeProperty]]:
    """读取实体属性记录

    返回 (所属要素名称, 属性列表)。
    属性列的数据类型通过 ``NativeDataTableHandle.field_definitions()``
    调用 ``Get_Field_Type`` 获取,不再从首行记录推断;实体属性记录
    (record)中存放的是对应字段的真实属性值。record 仅提供
    asDouble/asString 两种读取接口,字符串类用 asString,其余数值型
    统一用 asDouble。
    """
    props: list[NativeProperty] = []
    try:
        record = self._cpp_obj.GetAttRecord()
    except Exception as exc:
        raise NativeDBError(f"获取属性记录失败: {exc}") from exc
    if record is None:
        return "", props

    try:
        feature = self._cpp_obj.GetFeatureSet()
        if feature is None:
            return "", props
        feature_name = feature.GetFeatureName() or ""

        dt = feature.GetDataTable()
        if dt is None:
            return feature_name, props
        table = NativeDataTableHandle(dt)

        for field_def in table.field_definitions():
            name = field_def.name
            if name in self._ATT_META_COLUMNS:
                continue

            type_code = field_def.type if isinstance(field_def.type, int) else 7
            ptype = _FIELD_TYPE_TO_PROPERTY_TYPE.get(type_code, "double")
            value: Any
            if ptype == "string":
                value = record.asString(name)
            elif ptype == "date":
                value = datetime.strptime(record.asString(name), "%Y-%m-%d %H:%M")
            else:
                # byte/short/int/long/float/double 等数值型统一用 asDouble
                value = record.asDouble(name)

            props.append(NativeProperty(name=name, value=value, type=ptype))
        return feature_name, props
    except Exception as exc:
        raise NativeDBError(f"读取实体属性记录失败: {exc}") from exc

geometry()

读取实体几何数据

Source code in dimine_python_sdk\lib\native\geometry.py
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
def geometry(self) -> "np.ndarray | NativeTin":
    """读取实体几何数据"""
    import numpy as np

    tcode = self.type_code
    if tcode == _type_name_to_code("text"):
        try:
            polydata = self._cpp_obj.ToPolyData()
            pts = self._polydata_points(polydata)
            if len(pts) > 0:
                return pts[0]
        except Exception:
            pass
        return ensure_array_3d([0, 0, 0])

    if tcode == _type_name_to_code("shell"):
        try:
            shell = self._cpp_obj.ConvertToShell()
            data = json.loads(shell.ParseToJson())
            points = np.array(data["points"], dtype=float).reshape(-1, 3)
            faces = np.array(data["indexs"], dtype=int).reshape(-1, 3)
            return NativeTin(points=points, faces=faces)
        except Exception:
            pass

    try:
        polydata = self._cpp_obj.ToPolyData()
        pts = self._polydata_points(polydata)
        if tcode == _type_name_to_code("point") and pts.shape[0] > 0:
            return pts[0]
        return pts
    except Exception as exc:
        raise NativeGeometryError(f"读取实体几何失败: {exc}") from exc

set_properties(properties)

将 NativeProperty 列表写入 C++ 实体属性记录。

att_record 互为逆过程:先获取 self._cpp_obj.GetAttRecord(), 再对记录中的每个字段调用 Set_Value 写入真实属性值。 单条属性写入失败不会中断整体写入。

Source code in dimine_python_sdk\lib\native\geometry.py
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
def set_properties(self, properties: list[NativeProperty]) -> None:
    """将 NativeProperty 列表写入 C++ 实体属性记录。

    与 ``att_record`` 互为逆过程:先获取 ``self._cpp_obj.GetAttRecord()``,
    再对记录中的每个字段调用 ``Set_Value`` 写入真实属性值。
    单条属性写入失败不会中断整体写入。
    """
    if not properties:
        return

    try:
        record = self._cpp_obj.GetAttRecord()
    except Exception as exc:
        raise NativeDBError(f"获取属性记录失败: {exc}") from exc
    if record is None:
        return

    for prop in properties:
        if prop.value is None or prop.name == "text":
            continue
        try:
            if (prop.type or "string") in self._NUMERIC_PROPERTY_TYPES:
                record.Set_Value(prop.name, float(prop.value))
            elif prop.type == "date":
                if isinstance(prop.value, datetime):
                    record.Set_Value(prop.name, prop.value.strftime("%Y-%m-%d 0:0"))
                else:
                    record.Set_Value(prop.name, str(prop.value))
            else:
                record.Set_Value(prop.name, str(prop.value))
        except Exception:
            pass

to_model()

转换为 NativeEntity 数据模型

Source code in dimine_python_sdk\lib\native\geometry.py
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
def to_model(self) -> NativeEntity:
    """转换为 NativeEntity 数据模型"""
    feature_name, props = self.att_record()
    if self.type_name == "text":
        # 文本内容优先从属性记录读取;缺失时回退到 GetText()
        if not any(p.name == "text" for p in props):
            try:
                text = self._cpp_obj.GetText() or ""
            except Exception:
                text = ""
            if text:
                props = list(props)
                props.append(NativeProperty(name="text", value=text, type="string"))
    return NativeEntity(
        entity_type=self.type_name,
        name=self.name,
        geometry=self.geometry(),
        color=self.color,
        properties=props,
        feature_name=feature_name,
    )

NativeLayerHandle

图层不透明句柄,内部持有 C++ dmDbLayer 对象

Source code in dimine_python_sdk\lib\native\geometry.py
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
class NativeLayerHandle:
    """图层不透明句柄,内部持有 C++ dmDbLayer 对象"""

    def __init__(self, cpp_obj: Any):
        self._cpp_obj = cpp_obj

    def __repr__(self) -> str:
        return f"NativeLayerHandle(name={self.name!r}, entities={self.entity_count})"

    @property
    def name(self) -> str:
        raw = self._cpp_obj.GetLayerName()
        return raw if isinstance(raw, str) else ""

    @property
    def entity_count(self) -> int:
        return self._cpp_obj.GetEntitiesCount()

    def iter_entities(self) -> Iterator[NativeEntity]:
        """迭代图层中所有实体,返回 NativeEntity

        OpenNextEntity() 返回的实体对象通常是图层查询游标的临时引用,
        一旦进入下一次 OpenNextEntity(),之前的引用即可能失效。
        因此这里一次性把每个实体的数据拷贝到 Python 对象,并立即释放
        底层 C++ 引用后再读取下一个实体。
        """
        self._cpp_obj.StartQueryEntity()
        results: list[NativeEntity] = []
        raw = self._cpp_obj.OpenNextEntity()
        while raw:
            results.append(NativeEntityHandle(raw).to_model())
            raw = self._cpp_obj.OpenNextEntity()
        return iter(results)

    def _insert_point_raw(self, position: "np.ndarray") -> NativeEntityHandle:
        """插入点,返回实体句柄"""
        cpp_entity = self._cpp_obj.InsertPoint(point_to_native(position))
        return NativeEntityHandle(cpp_entity)

    def _insert_line_raw(
        self, start: "np.ndarray", end: "np.ndarray"
    ) -> NativeEntityHandle:
        """插入直线,返回实体句柄"""
        cpp_entity = self._cpp_obj.InsertLine(
            point_to_native(start), point_to_native(end)
        )
        return NativeEntityHandle(cpp_entity)

    def _insert_polyline_raw(self, points: "np.ndarray") -> NativeEntityHandle:
        """插入多段线,返回实体句柄"""
        cpp_entity = self._cpp_obj.InsertPolyline(points_to_native(points))
        return NativeEntityHandle(cpp_entity)


    def _insert_shell_raw(self, tin: NativeTin) -> NativeEntityHandle:
        """插入 Shell,返回实体句柄"""
        polydata = tin_to_polydata(tin)
        cpp_entity = self._cpp_obj.InsertShell(polydata)
        return NativeEntityHandle(cpp_entity)

    def _insert_text_raw(
        self, position: "np.ndarray", text: str
    ) -> NativeEntityHandle:
        """插入文本,返回实体句柄"""
        cpp_entity = self._cpp_obj.InsertText(point_to_native(position), str(text))
        return NativeEntityHandle(cpp_entity)


    def insert_native_entity(self, entity: NativeEntity) -> NativeEntityHandle:
        """根据 NativeEntity 数据模型插入实体,并返回实体句柄

        插入几何后会通过返回的 ``NativeEntityHandle`` 设置实体名称、颜色,
        并以 ``att_record`` 的反向操作将属性值写入 C++ 属性记录。
        """
        etype = entity.entity_type
        if etype == "point":
            handle = self._insert_point_raw(ensure_array_3d(entity.geometry))
        elif etype == "line":
            pts = ensure_points(entity.geometry)
            if len(pts) != 2:
                raise NativeGeometryError("line 实体需要 2 个顶点")
            handle = self._insert_line_raw(pts[0], pts[1])
        elif etype in ("polyline", "polygon"):
            handle = self._insert_polyline_raw(ensure_points(entity.geometry))
        elif etype == "shell":
            tin = entity.geometry
            if not isinstance(tin, NativeTin):
                raise NativeGeometryError("shell 实体 geometry 必须为 NativeTin")
            handle = self._insert_shell_raw(tin)
        elif etype == "text":
            pos = ensure_array_3d(entity.geometry)
            text = ""
            for prop in entity.properties:
                if prop.name == "text":
                    text = str(prop.value or "")
                    break
            handle = self._insert_text_raw(pos, text)
        else:
            raise NativeGeometryError(f"不支持的实体类型: {etype}")

        if entity.name:
            handle.name = entity.name
        if entity.color:
            handle.color = entity.color
        handle.set_properties(entity.properties)
        return handle

    def insert_entities(
        self, entities: Sequence[NativeEntity]
    ) -> list[NativeEntityHandle]:
        """批量插入 NativeEntity,返回实体句柄列表"""
        return [self.insert_native_entity(e) for e in entities]

    def to_model(self) -> NativeLayer:
        """转换为 NativeLayer 数据模型"""
        return NativeLayer(name=self.name, entities=list(self.iter_entities()))

insert_entities(entities)

批量插入 NativeEntity,返回实体句柄列表

Source code in dimine_python_sdk\lib\native\geometry.py
578
579
580
581
582
def insert_entities(
    self, entities: Sequence[NativeEntity]
) -> list[NativeEntityHandle]:
    """批量插入 NativeEntity,返回实体句柄列表"""
    return [self.insert_native_entity(e) for e in entities]

insert_native_entity(entity)

根据 NativeEntity 数据模型插入实体,并返回实体句柄

插入几何后会通过返回的 NativeEntityHandle 设置实体名称、颜色, 并以 att_record 的反向操作将属性值写入 C++ 属性记录。

Source code in dimine_python_sdk\lib\native\geometry.py
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
def insert_native_entity(self, entity: NativeEntity) -> NativeEntityHandle:
    """根据 NativeEntity 数据模型插入实体,并返回实体句柄

    插入几何后会通过返回的 ``NativeEntityHandle`` 设置实体名称、颜色,
    并以 ``att_record`` 的反向操作将属性值写入 C++ 属性记录。
    """
    etype = entity.entity_type
    if etype == "point":
        handle = self._insert_point_raw(ensure_array_3d(entity.geometry))
    elif etype == "line":
        pts = ensure_points(entity.geometry)
        if len(pts) != 2:
            raise NativeGeometryError("line 实体需要 2 个顶点")
        handle = self._insert_line_raw(pts[0], pts[1])
    elif etype in ("polyline", "polygon"):
        handle = self._insert_polyline_raw(ensure_points(entity.geometry))
    elif etype == "shell":
        tin = entity.geometry
        if not isinstance(tin, NativeTin):
            raise NativeGeometryError("shell 实体 geometry 必须为 NativeTin")
        handle = self._insert_shell_raw(tin)
    elif etype == "text":
        pos = ensure_array_3d(entity.geometry)
        text = ""
        for prop in entity.properties:
            if prop.name == "text":
                text = str(prop.value or "")
                break
        handle = self._insert_text_raw(pos, text)
    else:
        raise NativeGeometryError(f"不支持的实体类型: {etype}")

    if entity.name:
        handle.name = entity.name
    if entity.color:
        handle.color = entity.color
    handle.set_properties(entity.properties)
    return handle

iter_entities()

迭代图层中所有实体,返回 NativeEntity

OpenNextEntity() 返回的实体对象通常是图层查询游标的临时引用, 一旦进入下一次 OpenNextEntity(),之前的引用即可能失效。 因此这里一次性把每个实体的数据拷贝到 Python 对象,并立即释放 底层 C++ 引用后再读取下一个实体。

Source code in dimine_python_sdk\lib\native\geometry.py
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
def iter_entities(self) -> Iterator[NativeEntity]:
    """迭代图层中所有实体,返回 NativeEntity

    OpenNextEntity() 返回的实体对象通常是图层查询游标的临时引用,
    一旦进入下一次 OpenNextEntity(),之前的引用即可能失效。
    因此这里一次性把每个实体的数据拷贝到 Python 对象,并立即释放
    底层 C++ 引用后再读取下一个实体。
    """
    self._cpp_obj.StartQueryEntity()
    results: list[NativeEntity] = []
    raw = self._cpp_obj.OpenNextEntity()
    while raw:
        results.append(NativeEntityHandle(raw).to_model())
        raw = self._cpp_obj.OpenNextEntity()
    return iter(results)

to_model()

转换为 NativeLayer 数据模型

Source code in dimine_python_sdk\lib\native\geometry.py
584
585
586
def to_model(self) -> NativeLayer:
    """转换为 NativeLayer 数据模型"""
    return NativeLayer(name=self.name, entities=list(self.iter_entities()))

create_database()

创建 C++ dmDbDatabase 对象

Source code in dimine_python_sdk\lib\native\geometry.py
793
794
795
def create_database():
    """创建 C++ dmDbDatabase 对象"""
    return Dm.dmDbDatabase()

create_layer()

创建 C++ dmDbLayer 对象

Source code in dimine_python_sdk\lib\native\geometry.py
803
804
805
def create_layer():
    """创建 C++ dmDbLayer 对象"""
    return Dm.dmDbLayer()

create_line(start, end, *, name='', feature_name='', color=None, properties=None)

创建线实体数据模型

Source code in dimine_python_sdk\lib\native\geometry.py
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
def create_line(
    start: "np.ndarray",
    end: "np.ndarray",
    *,
    name: str = "",
    feature_name: str = "",
    color: list[int] | None = None,
    properties: list[NativeProperty] | None = None,
) -> NativeEntity:
    """创建线实体数据模型"""
    import numpy as np

    s = ensure_array_3d(start)
    e = ensure_array_3d(end)
    return NativeEntity(
        entity_type="line",
        name=name,
        geometry=np.array([s, e], dtype=float),
        color=color or [0, 0, 0],
        feature_name=feature_name,
        properties=properties or [],
    )

create_local_database()

创建可编辑本地 C++ dmDbDatabase 对象

Source code in dimine_python_sdk\lib\native\geometry.py
798
799
800
def create_local_database():
    """创建可编辑本地 C++ dmDbDatabase 对象"""
    return Dm.dmDbDatabase.CreateLocalDB()

create_point(position, *, name='', feature_name='', color=None, properties=None)

创建点实体数据模型

Source code in dimine_python_sdk\lib\native\geometry.py
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
def create_point(
    position: "np.ndarray",
    *,
    name: str = "",
    feature_name: str = "",
    color: list[int] | None = None,
    properties: list[NativeProperty] | None = None,
) -> NativeEntity:
    """创建点实体数据模型"""
    pos = ensure_array_3d(position)
    return NativeEntity(
        entity_type="point",
        name=name,
        geometry=pos,
        color=color or [0, 0, 0],
        feature_name=feature_name,
        properties=properties or [],
    )

create_polyline(points, *, name='', feature_name='', color=None, properties=None)

创建多段线实体数据模型

Source code in dimine_python_sdk\lib\native\geometry.py
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
def create_polyline(
    points: "np.ndarray",
    *,
    name: str = "",
    feature_name: str = "",
    color: list[int] | None = None,
    properties: list[NativeProperty] | None = None,
) -> NativeEntity:
    """创建多段线实体数据模型"""
    return NativeEntity(
        entity_type="polyline",
        name=name,
        geometry=ensure_points(points),
        color=color or [0, 0, 0],
        feature_name=feature_name,
        properties=properties or [],
    )

create_shell(tin, *, name='', feature_name='', color=None, properties=None)

创建 Shell 实体数据模型

Source code in dimine_python_sdk\lib\native\geometry.py
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
def create_shell(
    tin: NativeTin,
    *,
    name: str = "",
    feature_name: str = "",
    color: list[int] | None = None,
    properties: list[NativeProperty] | None = None,
) -> NativeEntity:
    """创建 Shell 实体数据模型"""
    return NativeEntity(
        entity_type="shell",
        name=name,
        geometry=tin,
        color=color or [0, 0, 0],
        feature_name=feature_name,
        properties=properties or [],
    )

create_text(position, text, *, name='', feature_name='', color=None, properties=None)

创建文本实体数据模型(作为 NativeEntity)

Source code in dimine_python_sdk\lib\native\geometry.py
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
def create_text(
    position: "np.ndarray",
    text: str,
    *,
    name: str = "",
    feature_name: str = "",
    color: list[int] | None = None,
    properties: list[NativeProperty] | None = None,
) -> NativeEntity:
    """创建文本实体数据模型(作为 NativeEntity)"""
    props: list[NativeProperty] = [NativeProperty(name="text", value=text, type="string")]
    if properties:
        props.extend(properties)
    return NativeEntity(
        entity_type="text",
        name=name,
        geometry=ensure_array_3d(position),
        color=color or [0, 0, 0],
        feature_name=feature_name,
        properties=props,
    )

point_from_native(cpp_point)

将 C++ dmDPoint 转换为 (3,) numpy 数组

Source code in dimine_python_sdk\lib\native\geometry.py
82
83
84
85
86
def point_from_native(cpp_point: Any) -> "np.ndarray":
    """将 C++ dmDPoint 转换为 (3,) numpy 数组"""
    import numpy as np

    return np.array([float(cpp_point.x), float(cpp_point.y), float(cpp_point.z)], dtype=float)

point_to_native(point)

将 (3,) numpy 数组转换为单个 C++ dmDPoint

Source code in dimine_python_sdk\lib\native\geometry.py
76
77
78
79
def point_to_native(point: "np.ndarray") -> Any:
    """将 (3,) numpy 数组转换为单个 C++ dmDPoint"""
    arr = ensure_array_3d(point)
    return Dm.dmDPoint(float(arr[0]), float(arr[1]), float(arr[2]))

points_from_native(cpp_points)

将 C++ dmDPoint 列表转换为 (N, 3) numpy 数组

Source code in dimine_python_sdk\lib\native\geometry.py
89
90
91
92
93
94
95
def points_from_native(cpp_points: list[Any]) -> "np.ndarray":
    """将 C++ dmDPoint 列表转换为 (N, 3) numpy 数组"""
    import numpy as np

    return np.array(
        [[float(p.x), float(p.y), float(p.z)] for p in cpp_points], dtype=float
    )

points_to_native(points)

将 (N, 3) numpy 数组转换为 C++ dmDPoint 列表

Source code in dimine_python_sdk\lib\native\geometry.py
70
71
72
73
def points_to_native(points: "np.ndarray") -> list[Any]:
    """将 (N, 3) numpy 数组转换为 C++ dmDPoint 列表"""
    arr = ensure_points(points)
    return [Dm.dmDPoint(float(p[0]), float(p[1]), float(p[2])) for p in arr]

tin_from_polydata(polydata)

C++ dmPolyData -> NativeTin

Source code in dimine_python_sdk\lib\native\geometry.py
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
def tin_from_polydata(polydata: Any) -> NativeTin:
    """C++ dmPolyData -> NativeTin"""
    import numpy as np

    n_points = polydata.GetNumberOfPoints()
    n_polys = polydata.GetNumberOfPolys()

    points = np.zeros((n_points, 3), dtype=float)
    for i in range(n_points):
        pt = polydata.GetPoint(i)
        points[i] = [float(pt.x), float(pt.y), float(pt.z)]

    faces = np.zeros((n_polys, 3), dtype=int)
    for i in range(n_polys):
        cell = polydata.GetPolyCell(i)
        faces[i] = [int(cell[0]), int(cell[1]), int(cell[2])]

    return NativeTin(points=points, faces=faces)

tin_to_polydata(tin)

NativeTin -> C++ dmPolyData

Source code in dimine_python_sdk\lib\native\geometry.py
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
def tin_to_polydata(tin: NativeTin) -> Any:
    """NativeTin -> C++ dmPolyData"""
    import numpy as np

    points = np.asarray(tin.points, dtype=float)
    faces = np.asarray(tin.faces, dtype=int)
    if points.ndim != 2 or points.shape[1] != 3:
        raise ValueError(f"tin.points shape 必须为 (N, 3),当前: {points.shape}")
    if faces.ndim != 2 or faces.shape[1] != 3:
        raise ValueError(f"tin.faces shape 必须为 (M, 3),当前: {faces.shape}")

    dm_points = Dm.dmPoints()
    for p in points:
        dm_points.InsertNextPoint(Dm.dmDPoint(float(p[0]), float(p[1]), float(p[2])))

    polydata = Dm.dmPolyData()
    polydata.SetDmPoints(dm_points)
    for face in faces:
        polydata.InsertNextPolyCell([int(face[0]), int(face[1]), int(face[2])])
    return polydata