> **本文回答**：`featuresToEntities(fc)` —— 把后端 FeatureCollection 转成 `EntityBase[]` 的映射规则、回退策略、meta 属性、色值转换。

## 一、基础用法

```js
const ents = mapcadLayer.featuresToEntities(fc);
ents.forEach(e => mapcadLayer.addEntity(e));
```

返回 `EntityBase[]`，类型按 `feature.properties.name` 自动匹配。

## 二、类型映射表

| AcDb 类型 | SDK 实体 | 关键字段 |
|---|---|---|
| `AcDbLine` | `LineEnt` | `points` → startPoint / endPoint |
| `AcDbPolyline / AcDb2dPolyline / AcDb3dPolyline` | `PolylineEnt` | `points` + `bulge` + `isclosed` |
| `AcDbCircle` | `CircleEnt` | `center` + `radius` |
| `AcDbArc` | `ArcEnt` | `center` + `radius` + `startAngle` + `endAngle` |
| `AcDbEllipse` | `EllipseEnt` | `center` + `majorRadius`/`major` + `minorRadius`/`minor`/`ratio` + `rotate`/`rotation` + `startAngle` + `endAngle` |
| `AcDbSpline` | `SplineEnt` | `points`（geometry 顶点）+ `degree` + `isclosed` |
| `AcDbText` | `TextEnt` | `location` + `text` + `height` + `rotate` + `horzMode` |
| `AcDbMText` | `MTextEnt` | `location` + `contents` + `textHeight` + `width` + `rotate` |
| `AcDbAttributeDefinition / AcDbAttribute` | `TextEnt` | 同 AcDbText |
| `AcDbHatch` | `HatchEnt` | geometry(Polygon/MultiPolygon)→`loops` + `patternName`（`;` 前段）+ `patternScale` + `patternAngle` + `origin`/`patternOrigin` |
| `AcDbSolid / AcDbTrace / AcDbFace` | `HatchEnt` (SOLID) | geometry→`loops`，`patternName` 强制 `SOLID` |
| `AcDbBlockReference / AcDbMInsertBlock` | `BlockRefEnt` | `blockname`/`blockName` + `insertionPoint`/`location`/`position` + `scale`/`scaleFactor` + `rotate`/`rotation`（⚠️ 1.0.1 把弧度 ×180/π 后写进了本应是弧度的 `BlockRefEnt.rotation`，带旋转的块引用角度错误，见下方修正） |
| `AcDbPoint` | `PointEnt` | `position / location` |

未识别类型按 `fallbackToGeometry` 兜底（见下）。

块引用旋转修正（`BlockRefEnt.rotation` 是弧度，1.0.1 转换时多乘了 180/π）：

```js
const ents = mapcadLayer.featuresToEntities(fc);
ents.forEach(e => {
  if (e.type === 'INSERT' && e.rotation) { e.rotation *= Math.PI / 180; e.changed(); }  // 还原为原始弧度
});
```

## 三、通用字段映射

| GeoJSON property | SDK 字段 | 备注 |
|---|---|---|
| `color` (int32，可能负) | `ent.color` | 经 `dwgColorToInt` 转 0xRRGGBB；0 / 0xFFFFFF 视为 ByLayer |
| `alpha` | `ent.opacity`（无则 `borderOpacity`；HatchEnt 用 `fillOpacity` + `fillColor`） | 0~255 → 归一化 0~1；`alpha===0` 时 `visible=false`。**不写 `effectOpacity`**；非 Hatch 实体若带 `fillOpacity` 字段会被重置为 0（不继承后端填充） |
| `linetype`（非 BYLAYER/BYBLOCK） | `ent.lineType` | |
| `linetypeScale` / `lineTypeScale` | `ent.ltScale` | |
| `lineWidth` | `ent.lineWidth` | ≥ 0 |
| `layerindex` | `ent.layerId = 'layer_' + n` | 与 `DwgExporter` 一致 |
| `layer`（字符串） | `ent.layerId` | layerindex 缺失时用 |
| `visible === false` | `ent.visible = false` | |
| `xdata`（JSON 对象字符串） | 逐 key → `setCustomProperty(key, val)`（**不带 meta 前缀**） | 非对象/解析失败 → `__md_raw_xdata`（带前缀） |

## 四、meta 属性（追踪用）

每个实体自动附加（默认前缀 `__md_`）：

| key | 内容 |
|---|---|
| `__md_source` | `'mapdata'` |
| `__md_objectid` | 原 DWG 实体 objectid |
| `__md_typename` | 原 AcDb 类型名 |
| `__md_layerindex` | 原图层索引 |

自定义前缀：

```js
mapcadLayer.featuresToEntities(fc, { metaPrefix: '__src_' });
```

## 五、fallbackToGeometry 回退策略

未实现或找不到块定义时：

| geometry.type | 兜底实体 |
|---|---|
| Point / MultiPoint | `PointEnt`（MultiPoint 逐点生成） |
| LineString | `PolylineEnt`（`isclosed` 为真时闭合） |
| MultiLineString | 每条线一个 `PolylineEnt`（非闭合） |
| Polygon / MultiPolygon | `HatchEnt`（`patternName` 默认 `SOLID`，内环作 inner loop） |
| GeometryCollection | 递归展开为多个子实体 |

禁用回退：

```js
mapcadLayer.featuresToEntities(fc, { fallbackToGeometry: false });
// 未识别 feature 被忽略
```

## 六、BlockRefEnt 两条路径

`AcDbBlockReference` 处理：

1. **有块定义**（`mapcadLayer.blocks` 里能找到 `blockname`）→ 真正的 `BlockRefEnt`
2. **无块定义 + 回退** → 从 `feature.geometry` 生成一组展开的基础实体（视觉在，语义丢）
3. **无块定义 + 不回退** → 忽略

保 100% 块信息：

```js
// 先注册块定义（BlockDef 可来自 createBlockDefFromSvg / fromJSON 的 doc.blocks）
mapcadLayer.blocks.add(blockDef);
const ents = mapcadLayer.featuresToEntities(fc);
```

或显式传 blocks：

```js
const ents = mapcadLayer.featuresToEntities(fc, { blocks: mapcadLayer.blocks });
```

## 七、dwgColorToInt 色值转换

后端 `color` 是 int32（负值常见），SDK 用 `0xRRGGBB`。手动转：

```js
import { dwgColorToInt } from 'vjmapext';

dwgColorToInt(-65536);   // 0xFF0000 = 16711680
dwgColorToInt(0);        // -1（ByLayer）
dwgColorToInt(0xFFFFFF); // -1（ByBlock）
```

## 八、脱离 MapCadLayer 独立使用

`featuresToEntities` 是纯函数：

```js
import { featuresToEntities } from 'vjmapext';

const ents = featuresToEntities(fc, {
  blocks: myBlocks,
  fallbackToGeometry: true,
  metaPrefix: '__src_'
});
```

## 九、典型场景

### 把查到的文字全部染红并加动画

```js
const ents = mapcadLayer.featuresToEntities(fc);
ents.forEach(e => {
  e.color = 0xff0000;
  e.setCustomProperty('hl', true);
  mapcadLayer.addEntity(e);
});
```

### 按 custom property 反查 objectid

```js
const ent = mapcadLayer.getEntities().find(e => e.getCustomProperty('__md_objectid')?.value === '9BE_2');
// getCustomProperty 返回 {type, value} 包装对象，比较要取 .value
```

## 十、常见坑

1. **转换后颜色变黑**：`color=0` 被判成 ByLayer，SDK 不写入；原实体设了颜色但没跟上。
2. **缺 blocks 导致 Block 变一堆线**：`BlockRefEnt` 回退到 geometry；先 `mapcadLayer.blocks.add(blockDef)`。
3. **对齐字段缺失**：AcDbText 侧由 `horzMode`/`vertMode`/`alignMode` 推导 `TextEnt.textAlignment`（1-9 锚点值）；AcDbMText 侧由 `attachment`/`attachmentPoint`（1-9 直用）或 `horzMode`/`vertMode` 推导 `MTextEnt.attachmentPoint`；源属性缺这些值时对齐回退默认。
4. **location 字符串解析失败**：后端偶尔返回非标准 `"x,y;z"`；SDK 做了容错，仍异常可手动 `location.split(/[,;]/)`。
5. **metaPrefix 和业务属性冲突**：默认 `__md_`；若业务也有这个前缀要改 `metaPrefix`。

## 相关

- 查询入口 → [mapdata-query](mapdata-query.md)
- 隐藏原图 → [mapdata-hider](mapdata-hider.md)
- 实体公共属性 → [entity/style-props](../entity/style-props.md)
