@taichina/map-sdk 1.0.0
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.
- package/README.md +169 -0
- package/package.json +51 -0
- package/teee-sdk/index.d.ts +181 -0
- package/teee-sdk/map-sdk.css +2 -0
- package/teee-sdk/teee-map-sdk.es.js +13143 -0
- package/teee-sdk/teee-map-sdk.umd.js +10 -0
package/README.md
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
# teee-map-sdk
|
|
2
|
+
|
|
3
|
+
基于 [maplibre-gl](https://maplibre.org/) 封装的铁路 GIS 业务地图 SDK,面向太昌 GIS 业务流提供开箱即用的地图初始化、图层管理、要素状态控制、加密瓦片及离线包支持。
|
|
4
|
+
|
|
5
|
+
## 安装
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install
|
|
9
|
+
git submodule update --init --recursive
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## 快速开始
|
|
13
|
+
|
|
14
|
+
```typescript
|
|
15
|
+
import { TeeeMap } from "teee-map-sdk";
|
|
16
|
+
|
|
17
|
+
const teeeMap = new TeeeMap("map-container");
|
|
18
|
+
|
|
19
|
+
// CTC 施工推演图
|
|
20
|
+
teeeMap.createCTCMap("ST-001", "https://tiles.example.com/{z}/{x}/{y}", [
|
|
21
|
+
"track",
|
|
22
|
+
"signal",
|
|
23
|
+
"switch",
|
|
24
|
+
]);
|
|
25
|
+
|
|
26
|
+
// 获取底层 maplibre 实例
|
|
27
|
+
const map = teeeMap.getMap();
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## 引入方式
|
|
31
|
+
|
|
32
|
+
```html
|
|
33
|
+
<!-- UMD -->
|
|
34
|
+
<script src="teee-sdk/teee-map-sdk.umd.js"></script>
|
|
35
|
+
<script>
|
|
36
|
+
const map = new TeeeMapSDK.TeeeMap("map");
|
|
37
|
+
</script>
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
// ES Module
|
|
42
|
+
import { TeeeMap } from "teee-map-sdk";
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## API
|
|
46
|
+
|
|
47
|
+
### 构造函数
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
new TeeeMap(container: string | HTMLElement, mbtileDbClient?: any)
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
| 参数 | 说明 |
|
|
54
|
+
|------|------|
|
|
55
|
+
| `container` | DOM 元素 ID 或 HTMLElement |
|
|
56
|
+
| `mbtileDbClient` | 可选。SQLite 客户端(需具备 `prepare` / `get` / `free` 方法),传入后启用 `mbtiles://` 协议 |
|
|
57
|
+
|
|
58
|
+
### 地图创建方法
|
|
59
|
+
|
|
60
|
+
| 方法 | 说明 |
|
|
61
|
+
|------|------|
|
|
62
|
+
| `createCTCMap(stcode, url, layers)` | CTC 施工推演 / 站场平面图 |
|
|
63
|
+
| `createCTCEncryptedMap(stcode, url, layers)` | 加密 CTC 图 |
|
|
64
|
+
| `createGraphMap(stcode, url, layers)` | 普通站场平面图 |
|
|
65
|
+
| `createGraphEncryptedMap(stcode, url, layers)` | 加密平面图 |
|
|
66
|
+
| `createGeomMap(stcode, url, layers)` | 地理图(EPSG:4326 / 4490) |
|
|
67
|
+
| `createGeomEncryptedMap(stcode, url, layers)` | 加密地理图 |
|
|
68
|
+
| `createRasterMap(url)` | 影像 / 栅格底图 |
|
|
69
|
+
| `createEncryptedRasterMap(url)` | 加密栅格图 |
|
|
70
|
+
| `createRSWPMap(url)` | 人员定位图(栅格 + GeoJSON) |
|
|
71
|
+
| `createRAGEMap(url, subUrl, code)` | 信息所综合业务图 |
|
|
72
|
+
| `createMbtilesMap(url, layers)` | MBTiles 离线包 |
|
|
73
|
+
|
|
74
|
+
### 数据源管理
|
|
75
|
+
|
|
76
|
+
| 方法 | 说明 |
|
|
77
|
+
|------|------|
|
|
78
|
+
| `addGeoJsonSource(id, data?)` | 添加 GeoJSON 源 |
|
|
79
|
+
| `addVectorTileSource(id, url)` | 添加矢量瓦片源 |
|
|
80
|
+
| `addRasterTileSource(id, url)` | 添加栅格瓦片源 |
|
|
81
|
+
| `updateGeoJsonSource(id, data)` | 更新 GeoJSON 数据 |
|
|
82
|
+
| `removeSource(id)` | 移除数据源 |
|
|
83
|
+
|
|
84
|
+
### 图层管理
|
|
85
|
+
|
|
86
|
+
| 方法 | 说明 |
|
|
87
|
+
|------|------|
|
|
88
|
+
| `addGeoJsonLayer(layerId, sourceId, type?, paint?, layout?)` | 添加 GeoJSON 图层 |
|
|
89
|
+
| `updateLayerStyle(id, paint)` | 动态修改图层 paint 样式 |
|
|
90
|
+
| `removeLayer(id)` | 移除图层 |
|
|
91
|
+
|
|
92
|
+
### 要素状态管理(CTC 业务)
|
|
93
|
+
|
|
94
|
+
| 方法 | 说明 |
|
|
95
|
+
|------|------|
|
|
96
|
+
| `stashAllFeature()` | 缓存当前视口全部要素(首次 idle 自动执行) |
|
|
97
|
+
| `updateFeatureState(code, state)` | 更新要素状态,如封锁、停电、选中等 |
|
|
98
|
+
| `initCTCFeatureState(feature)` | 为单个要素初始化 CTC 业务状态 |
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
teeeMap.updateFeatureState("DG-102", {
|
|
102
|
+
state: "1", // 股道封锁
|
|
103
|
+
select: true, // 选中
|
|
104
|
+
});
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### 交互事件
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
teeeMap.onFeatureClick("layer-id", (feature, lngLat) => {
|
|
111
|
+
console.log(feature.properties.code);
|
|
112
|
+
});
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### 绘制工具
|
|
116
|
+
|
|
117
|
+
地图 `load` 完成后,`drawTool` 会自动挂载:
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
const draw = teeeMap.drawTool;
|
|
121
|
+
draw.changeMode("polygon"); // 切换绘制模式
|
|
122
|
+
draw.onFinish((geojson) => {...}); // 绑定完成回调
|
|
123
|
+
draw.clear(); // 清空画布
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### 其他
|
|
127
|
+
|
|
128
|
+
| 方法 | 说明 |
|
|
129
|
+
|------|------|
|
|
130
|
+
| `getMap()` | 获取底层 `maplibregl.Map` 实例 |
|
|
131
|
+
| `getMapConfigs()` | 获取当前 `MapForgeConfig` |
|
|
132
|
+
| `destroy()` | 销毁地图实例 |
|
|
133
|
+
|
|
134
|
+
### LayerType
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
type LayerType =
|
|
138
|
+
| "background" | "track" | "track_circuit"
|
|
139
|
+
| "signal" | "switch" | "switch_loc"
|
|
140
|
+
| "bumper" | "insulation" | "label" | "prevention";
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## 构建
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
npm run build # 输出到 teee-sdk/
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## 项目结构
|
|
150
|
+
|
|
151
|
+
```
|
|
152
|
+
├── src/
|
|
153
|
+
│ ├── index.ts # SDK 入口,导出 TeeeMap
|
|
154
|
+
│ ├── teee-map/ # 核心 SDK(git 子模块,含详细设计文档)
|
|
155
|
+
│ └── map-const/ # 地图常量 & 枚举(git 子模块)
|
|
156
|
+
├── teee-sdk/ # 构建产物(.es.js / .umd.js / .css)
|
|
157
|
+
├── SDK/ # 示例 HTML 页面 + SDK 副本
|
|
158
|
+
├── vite.config.ts # Vite library mode 配置
|
|
159
|
+
└── tsconfig.json
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
核心 SDK 采用工厂模式:[MapForge](src/teee-map/map-forge/) 生成配置 → [SourceFactory](src/teee-map/source/) 生成数据源 → [LayerFactory](src/teee-map/layers/) 生成图层 → [ToolFactory](src/teee-map/utils/) 注册交互工具。详见 [src/teee-map/DESIGN.md](src/teee-map/DESIGN.md)。
|
|
163
|
+
|
|
164
|
+
## 依赖
|
|
165
|
+
|
|
166
|
+
- **maplibre-gl** ^5.24.0 — 地图渲染引擎
|
|
167
|
+
- **pako** ^2.1.0 — MBTiles Gzip 解压
|
|
168
|
+
- **@watergis/maplibre-gl-terradraw** ^1.13.1 — 绘制工具
|
|
169
|
+
- **TypeScript** ^6.0.3 / **Vite** ^8.0.10(开发依赖)
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@taichina/map-sdk",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "基于 GIS 的铁路业务地图 SDK。",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./teee-sdk/teee-map-sdk.umd.js",
|
|
7
|
+
"module": "./teee-sdk/teee-map-sdk.es.js",
|
|
8
|
+
"types": "./teee-sdk/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./teee-sdk/index.d.ts",
|
|
12
|
+
"import": "./teee-sdk/teee-map-sdk.es.js",
|
|
13
|
+
"require": "./teee-sdk/teee-map-sdk.umd.js",
|
|
14
|
+
"default": "./teee-sdk/teee-map-sdk.es.js"
|
|
15
|
+
},
|
|
16
|
+
"./teee-sdk/*": "./teee-sdk/*"
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"teee-sdk"
|
|
20
|
+
],
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "vite build && node -e \"require('fs').copyFileSync('src/types/public-api.d.ts', 'teee-sdk/index.d.ts')\"",
|
|
23
|
+
"update-submodule": "git submodule update --remote --merge",
|
|
24
|
+
"prepublishOnly": "npm run build",
|
|
25
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
26
|
+
},
|
|
27
|
+
"keywords": [
|
|
28
|
+
"gis",
|
|
29
|
+
"railway",
|
|
30
|
+
"map"
|
|
31
|
+
],
|
|
32
|
+
"author": "gengar_chhkkk",
|
|
33
|
+
"license": "ISC",
|
|
34
|
+
"publishConfig": {
|
|
35
|
+
"access": "public"
|
|
36
|
+
},
|
|
37
|
+
"peerDependencies": {
|
|
38
|
+
"maplibre-gl": "^5.24.0"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@types/node": "^25.6.0",
|
|
42
|
+
"maplibre-gl": "^5.24.0",
|
|
43
|
+
"typescript": "^6.0.3",
|
|
44
|
+
"vite": "^8.0.10"
|
|
45
|
+
},
|
|
46
|
+
"dependencies": {
|
|
47
|
+
"@turf/bbox": "^7.0.0",
|
|
48
|
+
"@watergis/maplibre-gl-terradraw": "^1.13.1",
|
|
49
|
+
"pako": "^2.1.0"
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import type { Map as MaplibreMap, GeoJSONFeature, LngLat, SourceSpecification } from 'maplibre-gl';
|
|
2
|
+
|
|
3
|
+
/** 地图图层类型 */
|
|
4
|
+
export type LayerType =
|
|
5
|
+
| 'station'
|
|
6
|
+
| 'track'
|
|
7
|
+
| 'signal'
|
|
8
|
+
| 'switch'
|
|
9
|
+
| 'switch_loc'
|
|
10
|
+
| 'insulation'
|
|
11
|
+
| 'station_track'
|
|
12
|
+
| 'platform'
|
|
13
|
+
| 'crossing'
|
|
14
|
+
| 'bridge'
|
|
15
|
+
| 'tunnel'
|
|
16
|
+
| 'culvert'
|
|
17
|
+
| 'mileage'
|
|
18
|
+
| string;
|
|
19
|
+
|
|
20
|
+
/** 地图初始化参数 */
|
|
21
|
+
export interface MapParams {
|
|
22
|
+
id: string;
|
|
23
|
+
source: SourceSpecification | SourceSpecification[];
|
|
24
|
+
layers?: unknown[];
|
|
25
|
+
subSourceId?: string;
|
|
26
|
+
subSource?: SourceSpecification;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** 画笔工具接口 */
|
|
30
|
+
export interface DrawTool {
|
|
31
|
+
changeMode(mode: string): void;
|
|
32
|
+
setOnFinish(cb: (feature: unknown) => void): void;
|
|
33
|
+
setOnChange(cb: (feature: unknown) => void): void;
|
|
34
|
+
setOnDelete(cb: (feature: unknown) => void): void;
|
|
35
|
+
setOnSelect(cb: (id: string | number) => void): void;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* TeeeMap 实例对外暴露的公共接口。
|
|
40
|
+
*/
|
|
41
|
+
export interface TeeeMap {
|
|
42
|
+
/** 底层 maplibre-gl Map 实例 */
|
|
43
|
+
readonly map: MaplibreMap;
|
|
44
|
+
|
|
45
|
+
/** 添加 GeoJSON 数据源 */
|
|
46
|
+
addGeoJsonSource(id: string, data?: GeoJSON.GeoJSON | string): void;
|
|
47
|
+
/** 移除指定数据源 */
|
|
48
|
+
removeSource(id: string): void;
|
|
49
|
+
/** 更新 GeoJSON 数据源内容 */
|
|
50
|
+
updateGeoJsonSource(id: string, data: GeoJSON.GeoJSON | string): void;
|
|
51
|
+
/** 添加矢量瓦片数据源 */
|
|
52
|
+
addVectorTileSource(id: string, url: string): void;
|
|
53
|
+
/** 添加栅格瓦片数据源 */
|
|
54
|
+
addRasterTileSource(id: string, url: string): void;
|
|
55
|
+
/** 添加 GeoJSON 图层 */
|
|
56
|
+
addGeoJsonLayer(
|
|
57
|
+
layerId: string,
|
|
58
|
+
sourceId: string,
|
|
59
|
+
type?: 'fill' | 'line' | 'circle' | 'symbol',
|
|
60
|
+
paint?: Record<string, unknown>,
|
|
61
|
+
layout?: Record<string, unknown>,
|
|
62
|
+
): void;
|
|
63
|
+
/** 动态更新图层 paint 样式 */
|
|
64
|
+
updateLayerStyle(id: string, paint: Record<string, unknown>): void;
|
|
65
|
+
/** 移除指定图层 */
|
|
66
|
+
removeLayer(id: string): void;
|
|
67
|
+
/** 为指定图层绑定要素点击事件 */
|
|
68
|
+
onFeatureClick(
|
|
69
|
+
layerIds: string | string[],
|
|
70
|
+
callback: (feature: GeoJSONFeature, lngLat: LngLat) => void,
|
|
71
|
+
): void;
|
|
72
|
+
|
|
73
|
+
/** 销毁地图实例并释放资源 */
|
|
74
|
+
destroy(): void;
|
|
75
|
+
/** 初始化地图基础状态 */
|
|
76
|
+
initState(): void;
|
|
77
|
+
/** 为指定要素初始化 CTC 业务状态 */
|
|
78
|
+
initCTCFeatureState(f: GeoJSONFeature): void;
|
|
79
|
+
/** 为指定要素初始化 Graph 业务状态 */
|
|
80
|
+
initGraphFeatureState(f: GeoJSONFeature): void;
|
|
81
|
+
/** 为指定要素初始化 GeoJSON 图层业务状态 */
|
|
82
|
+
initGeoJsonFeatureState(f: GeoJSONFeature, layerType: string): void;
|
|
83
|
+
/** 初始化 GeoJSON 业务状态 */
|
|
84
|
+
initGeoJsonState(): void;
|
|
85
|
+
|
|
86
|
+
/** 更新要素业务状态 */
|
|
87
|
+
updateFeatureState(code: string | number, state: Record<string, unknown>): void;
|
|
88
|
+
/** 获取要素业务状态 */
|
|
89
|
+
getFeatureState(code: string | number): Record<string, unknown> | undefined;
|
|
90
|
+
|
|
91
|
+
/** 画笔工具实例(地图 load 后挂载) */
|
|
92
|
+
readonly drawTool?: DrawTool;
|
|
93
|
+
|
|
94
|
+
/** 创建 CTC 施工推演 / 站场平面图 */
|
|
95
|
+
createCTCMap(stcode: string, url: string, layers: LayerType[]): void;
|
|
96
|
+
/** 创建加密 CTC 施工推演 / 站场平面图 */
|
|
97
|
+
createCTCEncryptedMap(stcode: string, url: string, layers: LayerType[]): void;
|
|
98
|
+
/** 创建人员定位图(RSWP) */
|
|
99
|
+
createRSWPMap(url: string): void;
|
|
100
|
+
/** 创建普通站场平面图(Graph) */
|
|
101
|
+
createGraphMap(stcode: string, url: string, layers: LayerType[]): void;
|
|
102
|
+
/** 创建加密站场平面图(Graph) */
|
|
103
|
+
createGraphEncryptedMap(stcode: string, url: string, layers: LayerType[]): void;
|
|
104
|
+
/** 创建地理地图(Geom) */
|
|
105
|
+
createGeomMap(stcode: string, url: string, layers: LayerType[]): void;
|
|
106
|
+
/** 创建加密地理地图(Geom) */
|
|
107
|
+
createGeomEncryptedMap(stcode: string, url: string, layers: LayerType[]): void;
|
|
108
|
+
/** 创建 GeoJSON 站场图 */
|
|
109
|
+
createGeoJsonStationMap(stcode: string, url: string, layers: LayerType[]): Promise<void>;
|
|
110
|
+
/** 创建栅格影像地图 */
|
|
111
|
+
createRasterMap(url: string): void;
|
|
112
|
+
/** 创建加密栅格影像地图 */
|
|
113
|
+
createEncryptedRasterMap(url: string): void;
|
|
114
|
+
/** 创建 RAGE 地图 */
|
|
115
|
+
createRAGEMap(url: string, subUrl: string, code: string): void;
|
|
116
|
+
/** 获取 maplibre-gl Map 实例 */
|
|
117
|
+
getMap(): MaplibreMap;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* 太昌业务地图 SDK 主类。
|
|
122
|
+
* 基于 maplibre-gl 封装,提供一键式地图初始化、图层管理、要素状态控制、加密瓦片及离线包支持。
|
|
123
|
+
*
|
|
124
|
+
* @example
|
|
125
|
+
* ```ts
|
|
126
|
+
* import { TeeeMap } from '@teee/map-sdk';
|
|
127
|
+
* import maplibregl from 'maplibre-gl';
|
|
128
|
+
*
|
|
129
|
+
* const teeeMap = new TeeeMap('map-container');
|
|
130
|
+
* teeeMap.createCTCMap('1234', 'https://tiles.example.com/{z}/{x}/{y}', ['station', 'track', 'signal']);
|
|
131
|
+
* ```
|
|
132
|
+
*/
|
|
133
|
+
export declare class TeeeMap {
|
|
134
|
+
constructor(container: string | HTMLElement);
|
|
135
|
+
|
|
136
|
+
/** 底层 maplibre-gl Map 实例 */
|
|
137
|
+
readonly map: MaplibreMap;
|
|
138
|
+
|
|
139
|
+
addGeoJsonSource(id: string, data?: GeoJSON.GeoJSON | string): void;
|
|
140
|
+
removeSource(id: string): void;
|
|
141
|
+
updateGeoJsonSource(id: string, data: GeoJSON.GeoJSON | string): void;
|
|
142
|
+
addVectorTileSource(id: string, url: string): void;
|
|
143
|
+
addRasterTileSource(id: string, url: string): void;
|
|
144
|
+
addGeoJsonLayer(
|
|
145
|
+
layerId: string,
|
|
146
|
+
sourceId: string,
|
|
147
|
+
type?: 'fill' | 'line' | 'circle' | 'symbol',
|
|
148
|
+
paint?: Record<string, unknown>,
|
|
149
|
+
layout?: Record<string, unknown>,
|
|
150
|
+
): void;
|
|
151
|
+
updateLayerStyle(id: string, paint: Record<string, unknown>): void;
|
|
152
|
+
removeLayer(id: string): void;
|
|
153
|
+
onFeatureClick(
|
|
154
|
+
layerIds: string | string[],
|
|
155
|
+
callback: (feature: GeoJSONFeature, lngLat: LngLat) => void,
|
|
156
|
+
): void;
|
|
157
|
+
|
|
158
|
+
destroy(): void;
|
|
159
|
+
initState(): void;
|
|
160
|
+
initCTCFeatureState(f: GeoJSONFeature): void;
|
|
161
|
+
initGraphFeatureState(f: GeoJSONFeature): void;
|
|
162
|
+
initGeoJsonFeatureState(f: GeoJSONFeature, layerType: string): void;
|
|
163
|
+
initGeoJsonState(): void;
|
|
164
|
+
updateFeatureState(code: string | number, state: Record<string, unknown>): void;
|
|
165
|
+
getFeatureState(code: string | number): Record<string, unknown> | undefined;
|
|
166
|
+
|
|
167
|
+
readonly drawTool?: DrawTool;
|
|
168
|
+
|
|
169
|
+
createCTCMap(stcode: string, url: string, layers: LayerType[]): void;
|
|
170
|
+
createCTCEncryptedMap(stcode: string, url: string, layers: LayerType[]): void;
|
|
171
|
+
createRSWPMap(url: string): void;
|
|
172
|
+
createGraphMap(stcode: string, url: string, layers: LayerType[]): void;
|
|
173
|
+
createGraphEncryptedMap(stcode: string, url: string, layers: LayerType[]): void;
|
|
174
|
+
createGeomMap(stcode: string, url: string, layers: LayerType[]): void;
|
|
175
|
+
createGeomEncryptedMap(stcode: string, url: string, layers: LayerType[]): void;
|
|
176
|
+
createGeoJsonStationMap(stcode: string, url: string, layers: LayerType[]): Promise<void>;
|
|
177
|
+
createRasterMap(url: string): void;
|
|
178
|
+
createEncryptedRasterMap(url: string): void;
|
|
179
|
+
createRAGEMap(url: string, subUrl: string, code: string): void;
|
|
180
|
+
getMap(): MaplibreMap;
|
|
181
|
+
}
|