@maplat/transform 0.4.1 → 0.5.1

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.
@@ -0,0 +1,338 @@
1
+ import type { Feature, Polygon, Position } from "geojson";
2
+ import { booleanPointInPolygon, point, getCoords } from "@turf/turf";
3
+ import { unitCalc, transformArr } from "./geometry.ts";
4
+ import type { Tri } from "./geometry.ts";
5
+ import {
6
+ FORMAT_VERSION,
7
+ isModernCompiled,
8
+ restoreLegacyState,
9
+ restoreModernState
10
+ } from "./compiled-state.ts";
11
+ import type { EdgeSet } from "./edgeutils.ts";
12
+ import type {
13
+ Compiled,
14
+ CompiledLegacy,
15
+ IndexedTinsBD,
16
+ KinksBD,
17
+ LegacyStatePayload,
18
+ ModernStatePayload,
19
+ PointSet,
20
+ StrictMode,
21
+ StrictStatus,
22
+ TinsBD,
23
+ VertexMode,
24
+ VerticesParamsBD,
25
+ WeightBufferBD,
26
+ YaxisMode,
27
+ CentroidBD
28
+ } from "./types.ts";
29
+
30
+ export const format_version = FORMAT_VERSION;
31
+
32
+ /**
33
+ * 座標変換の基本機能を提供するクラス
34
+ *
35
+ * 2つの座標系間の変換を、TINネットワークを使用して実現します。
36
+ * このクラスは基本的な変換機能のみを提供し、
37
+ * 設定ファイルの生成などの追加機能はTinクラスで提供されます。
38
+ */
39
+ export class Transform {
40
+ /**
41
+ * 各種モードの定数定義
42
+ * すべてreadonlyで、型安全性を確保
43
+ */
44
+ static VERTEX_PLAIN = "plain" as const;
45
+ static VERTEX_BIRDEYE = "birdeye" as const;
46
+ static MODE_STRICT = "strict" as const;
47
+ static MODE_AUTO = "auto" as const;
48
+ static MODE_LOOSE = "loose" as const;
49
+ static STATUS_STRICT = "strict" as const;
50
+ static STATUS_ERROR = "strict_error" as const;
51
+ static STATUS_LOOSE = "loose" as const;
52
+ static YAXIS_FOLLOW = "follow" as const;
53
+ static YAXIS_INVERT = "invert" as const;
54
+
55
+ points: PointSet[] = [];
56
+ pointsWeightBuffer?: WeightBufferBD;
57
+ strict_status?: StrictStatus;
58
+ vertices_params?: VerticesParamsBD;
59
+ centroid?: CentroidBD;
60
+ edgeNodes?: PointSet[];
61
+ edges?: EdgeSet[];
62
+ tins?: TinsBD;
63
+ kinks?: KinksBD;
64
+ yaxisMode: YaxisMode = Transform.YAXIS_INVERT;
65
+ strictMode: StrictMode = Transform.MODE_AUTO;
66
+ vertexMode?: VertexMode = Transform.VERTEX_PLAIN;
67
+ bounds?: number[][];
68
+ boundsPolygon?: Feature<Polygon>;
69
+ wh?: number[];
70
+ xy?: number[];
71
+ indexedTins?: IndexedTinsBD;
72
+ stateFull = false;
73
+ stateTriangle?: Tri;
74
+ stateBackward?: boolean;
75
+
76
+ /**
77
+ * Optional properties for MaplatCore extension
78
+ * These properties allow consuming applications to extend Transform instances
79
+ * with additional metadata without requiring Module Augmentation
80
+ */
81
+
82
+ /** Layer priority for rendering order */
83
+ priority?: number;
84
+
85
+ /** Layer importance for display decisions */
86
+ importance?: number;
87
+
88
+ /** Bounds in XY (source) coordinate system */
89
+ xyBounds?: Feature<Polygon>;
90
+
91
+ /** Bounds in Mercator (Web Mercator) coordinate system */
92
+ mercBounds?: Feature<Polygon>;
93
+
94
+ constructor() { }
95
+
96
+ /**
97
+ * コンパイルされた設定を適用します
98
+ *
99
+ * @param compiled - コンパイルされた設定オブジェクト
100
+ * @returns 変換に必要な主要なオブジェクトのセット
101
+ *
102
+ * 以下の処理を行います:
103
+ * 1. バージョンに応じた設定の解釈
104
+ * 2. 各種パラメータの復元
105
+ * 3. TINネットワークの再構築
106
+ * 4. インデックスの作成
107
+ */
108
+ setCompiled(compiled: Compiled | CompiledLegacy): void {
109
+ if (isModernCompiled(compiled)) {
110
+ this.applyModernState(restoreModernState(compiled));
111
+ return;
112
+ }
113
+ this.applyLegacyState(restoreLegacyState(compiled as CompiledLegacy));
114
+ }
115
+
116
+ private applyModernState(state: ModernStatePayload): void {
117
+ this.points = state.points;
118
+ this.pointsWeightBuffer = state.pointsWeightBuffer;
119
+ this.strict_status = state.strictStatus;
120
+ this.vertices_params = state.verticesParams;
121
+ this.centroid = state.centroid;
122
+ this.edges = state.edges;
123
+ this.edgeNodes = state.edgeNodes || [];
124
+ this.tins = state.tins;
125
+ this.addIndexedTin();
126
+ this.kinks = state.kinks;
127
+ this.yaxisMode = state.yaxisMode ?? Transform.YAXIS_INVERT;
128
+ this.vertexMode = state.vertexMode ?? Transform.VERTEX_PLAIN;
129
+ this.strictMode = state.strictMode ?? Transform.MODE_AUTO;
130
+ if (state.bounds) {
131
+ this.bounds = state.bounds;
132
+ this.boundsPolygon = state.boundsPolygon;
133
+ this.xy = state.xy;
134
+ this.wh = state.wh;
135
+ } else {
136
+ this.bounds = undefined;
137
+ this.boundsPolygon = undefined;
138
+ this.xy = state.xy ?? [0, 0];
139
+ if (state.wh) this.wh = state.wh;
140
+ }
141
+ }
142
+
143
+ private applyLegacyState(state: LegacyStatePayload): void {
144
+ this.tins = state.tins;
145
+ this.addIndexedTin();
146
+ this.strict_status = state.strictStatus;
147
+ this.pointsWeightBuffer = state.pointsWeightBuffer;
148
+ this.vertices_params = state.verticesParams;
149
+ this.centroid = state.centroid;
150
+ this.kinks = state.kinks;
151
+ this.points = state.points;
152
+ }
153
+
154
+ /**
155
+ * TINネットワークのインデックスを作成します
156
+ *
157
+ * インデックスは変換処理を高速化するために使用されます。
158
+ * グリッド形式のインデックスを作成し、各グリッドに
159
+ * 含まれる三角形を記録します。
160
+ */
161
+ addIndexedTin() {
162
+ const tins = this.tins!;
163
+ const forw = tins.forw;
164
+ const bakw = tins.bakw;
165
+ const gridNum = Math.ceil(Math.sqrt(forw!.features.length));
166
+ if (gridNum < 3) {
167
+ this.indexedTins = undefined;
168
+ return;
169
+ }
170
+ let forwBound: Position[] = [];
171
+ let bakwBound: Position[] = [];
172
+ const forwEachBound = forw!.features.map((tri: Tri) => {
173
+ let eachBound: Position[] = [];
174
+ getCoords(tri)[0].map((point: Position) => {
175
+ if (forwBound.length === 0)
176
+ forwBound = [Array.from(point), Array.from(point)];
177
+ else {
178
+ if (point[0] < forwBound[0][0]) forwBound[0][0] = point[0];
179
+ if (point[0] > forwBound[1][0]) forwBound[1][0] = point[0];
180
+ if (point[1] < forwBound[0][1]) forwBound[0][1] = point[1];
181
+ if (point[1] > forwBound[1][1]) forwBound[1][1] = point[1];
182
+ }
183
+ if (eachBound.length === 0)
184
+ eachBound = [Array.from(point), Array.from(point)];
185
+ else {
186
+ if (point[0] < eachBound[0][0]) eachBound[0][0] = point[0];
187
+ if (point[0] > eachBound[1][0]) eachBound[1][0] = point[0];
188
+ if (point[1] < eachBound[0][1]) eachBound[0][1] = point[1];
189
+ if (point[1] > eachBound[1][1]) eachBound[1][1] = point[1];
190
+ }
191
+ });
192
+ return eachBound;
193
+ });
194
+ const forwXUnit = (forwBound[1][0] - forwBound[0][0]) / gridNum;
195
+ const forwYUnit = (forwBound[1][1] - forwBound[0][1]) / gridNum;
196
+ const forwGridCache = forwEachBound.reduce(
197
+ (prev: number[][][], bound: Position[], index: number) => {
198
+ const normXMin = unitCalc(bound[0][0], forwBound[0][0], forwXUnit, gridNum);
199
+ const normXMax = unitCalc(bound[1][0], forwBound[0][0], forwXUnit, gridNum);
200
+ const normYMin = unitCalc(bound[0][1], forwBound[0][1], forwYUnit, gridNum);
201
+ const normYMax = unitCalc(bound[1][1], forwBound[0][1], forwYUnit, gridNum);
202
+ for (let cx = normXMin; cx <= normXMax; cx++) {
203
+ if (!prev[cx]) prev[cx] = [];
204
+ for (let cy = normYMin; cy <= normYMax; cy++) {
205
+ if (!prev[cx][cy]) prev[cx][cy] = [];
206
+ prev[cx][cy].push(index);
207
+ }
208
+ }
209
+ return prev;
210
+ },
211
+ []
212
+ );
213
+ const bakwEachBound = bakw!.features.map((tri: Tri) => {
214
+ let eachBound: Position[] = [];
215
+ getCoords(tri)[0].map((point: Position) => {
216
+ if (bakwBound.length === 0)
217
+ bakwBound = [Array.from(point), Array.from(point)];
218
+ else {
219
+ if (point[0] < bakwBound[0][0]) bakwBound[0][0] = point[0];
220
+ if (point[0] > bakwBound[1][0]) bakwBound[1][0] = point[0];
221
+ if (point[1] < bakwBound[0][1]) bakwBound[0][1] = point[1];
222
+ if (point[1] > bakwBound[1][1]) bakwBound[1][1] = point[1];
223
+ }
224
+ if (eachBound.length === 0)
225
+ eachBound = [Array.from(point), Array.from(point)];
226
+ else {
227
+ if (point[0] < eachBound[0][0]) eachBound[0][0] = point[0];
228
+ if (point[0] > eachBound[1][0]) eachBound[1][0] = point[0];
229
+ if (point[1] < eachBound[0][1]) eachBound[0][1] = point[1];
230
+ if (point[1] > eachBound[1][1]) eachBound[1][1] = point[1];
231
+ }
232
+ });
233
+ return eachBound;
234
+ });
235
+ const bakwXUnit = (bakwBound[1][0] - bakwBound[0][0]) / gridNum;
236
+ const bakwYUnit = (bakwBound[1][1] - bakwBound[0][1]) / gridNum;
237
+ const bakwGridCache = bakwEachBound.reduce(
238
+ (prev: number[][][], bound: Position[], index: number) => {
239
+ const normXMin = unitCalc(bound[0][0], bakwBound[0][0], bakwXUnit, gridNum);
240
+ const normXMax = unitCalc(bound[1][0], bakwBound[0][0], bakwXUnit, gridNum);
241
+ const normYMin = unitCalc(bound[0][1], bakwBound[0][1], bakwYUnit, gridNum);
242
+ const normYMax = unitCalc(bound[1][1], bakwBound[0][1], bakwYUnit, gridNum);
243
+ for (let cx = normXMin; cx <= normXMax; cx++) {
244
+ if (!prev[cx]) prev[cx] = [];
245
+ for (let cy = normYMin; cy <= normYMax; cy++) {
246
+ if (!prev[cx][cy]) prev[cx][cy] = [];
247
+ prev[cx][cy].push(index);
248
+ }
249
+ }
250
+ return prev;
251
+ },
252
+ []
253
+ );
254
+ this.indexedTins = {
255
+ forw: {
256
+ gridNum,
257
+ xOrigin: forwBound[0][0],
258
+ yOrigin: forwBound[0][1],
259
+ xUnit: forwXUnit,
260
+ yUnit: forwYUnit,
261
+ gridCache: forwGridCache
262
+ },
263
+ bakw: {
264
+ gridNum,
265
+ xOrigin: bakwBound[0][0],
266
+ yOrigin: bakwBound[0][1],
267
+ xUnit: bakwXUnit,
268
+ yUnit: bakwYUnit,
269
+ gridCache: bakwGridCache
270
+ }
271
+ };
272
+ }
273
+
274
+ /**
275
+ * 座標変換を実行します
276
+ *
277
+ * @param apoint - 変換する座標
278
+ * @param backward - 逆方向の変換かどうか
279
+ * @param ignoreBounds - 境界チェックを無視するかどうか
280
+ * @returns 変換後の座標、または境界外の場合はfalse
281
+ *
282
+ * @throws {Error} 逆方向変換が許可されていない状態での逆変換時
283
+ */
284
+ transform(apoint: number[], backward?: boolean, ignoreBounds?: boolean): number[] | false {
285
+ if (!this.tins)
286
+ throw new Error("setCompiled() must be called before transform()");
287
+ if (backward && this.strict_status == Transform.STATUS_ERROR)
288
+ throw new Error('Backward transform is not allowed if strict_status == "strict_error"');
289
+ if (this.yaxisMode == Transform.YAXIS_FOLLOW && backward) {
290
+ apoint = [apoint[0], -1 * apoint[1]];
291
+ }
292
+ const tpoint = point(apoint);
293
+ if (this.bounds && !backward && !ignoreBounds) {
294
+ if (!booleanPointInPolygon(tpoint, this.boundsPolygon!)) return false;
295
+ }
296
+ const tins = backward ? this.tins!.bakw : this.tins!.forw;
297
+ const indexedTins = backward
298
+ ? this.indexedTins!.bakw
299
+ : this.indexedTins!.forw;
300
+ const verticesParams = backward
301
+ ? this.vertices_params!.bakw
302
+ : this.vertices_params!.forw;
303
+ const centroid = backward ? this.centroid!.bakw : this.centroid!.forw;
304
+ const weightBuffer = backward
305
+ ? this.pointsWeightBuffer!.bakw
306
+ : this.pointsWeightBuffer!.forw;
307
+ let stateTriangle = undefined,
308
+ stateSetFunc = undefined;
309
+ if (this.stateFull) {
310
+ if (this.stateBackward == backward) {
311
+ stateTriangle = this.stateTriangle;
312
+ } else {
313
+ this.stateBackward = backward;
314
+ this.stateTriangle = undefined;
315
+ }
316
+ stateSetFunc = (tri?: Tri) => {
317
+ this.stateTriangle = tri;
318
+ };
319
+ }
320
+ let ret = transformArr(
321
+ tpoint,
322
+ tins!,
323
+ indexedTins,
324
+ verticesParams,
325
+ centroid,
326
+ weightBuffer,
327
+ stateTriangle,
328
+ stateSetFunc
329
+ );
330
+ if (this.bounds && backward && !ignoreBounds) {
331
+ const rpoint = point(ret);
332
+ if (!booleanPointInPolygon(rpoint, this.boundsPolygon!)) return false;
333
+ } else if (this.yaxisMode == Transform.YAXIS_FOLLOW && !backward) {
334
+ ret = [ret[0], -1 * ret[1]];
335
+ }
336
+ return ret;
337
+ }
338
+ }
@@ -1,180 +1,171 @@
1
- import { polygon } from "@turf/turf";
2
- import type { Position } from "geojson";
3
- import { PropertyTriKey, Tri, Tins } from "./geometry.ts";
4
-
5
- /**
6
- * 三角形の頂点の順序を修正する
7
- * 地図外郭の頂点を含む三角形について、頂点の順序を統一する
8
- * @param tins 三角形群
9
- * @returns 頂点順序が修正された三角形群
10
- */
11
- function rotateVerticesTriangle(tins: Tins): Tins {
12
- const features = tins.features;
13
- for (let i = 0; i < features.length; i++) {
14
- const feature = features[i];
15
- if (
16
- `${feature.properties!.a.index}`.substring(0, 1) === "b" &&
17
- `${feature.properties!.b.index}`.substring(0, 1) === "b"
18
- ) {
19
- features[i] = {
20
- geometry: {
21
- type: "Polygon",
22
- coordinates: [
23
- [
24
- feature.geometry!.coordinates[0][2],
25
- feature.geometry!.coordinates[0][0],
26
- feature.geometry!.coordinates[0][1],
27
- feature.geometry!.coordinates[0][2]
28
- ]
29
- ]
30
- },
31
- properties: {
32
- a: {
33
- geom: feature.properties!.c.geom,
34
- index: feature.properties!.c.index
35
- },
36
- b: {
37
- geom: feature.properties!.a.geom,
38
- index: feature.properties!.a.index
39
- },
40
- c: {
41
- geom: feature.properties!.b.geom,
42
- index: feature.properties!.b.index
43
- }
44
- },
45
- type: "Feature"
46
- };
47
- } else if (
48
- `${feature.properties!.c.index}`.substring(0, 1) === "b" &&
49
- `${feature.properties!.a.index}`.substring(0, 1) === "b"
50
- ) {
51
- features[i] = {
52
- geometry: {
53
- type: "Polygon",
54
- coordinates: [
55
- [
56
- feature.geometry!.coordinates[0][1],
57
- feature.geometry!.coordinates[0][2],
58
- feature.geometry!.coordinates[0][0],
59
- feature.geometry!.coordinates[0][1]
60
- ]
61
- ]
62
- },
63
- properties: {
64
- a: {
65
- geom: feature.properties!.b.geom,
66
- index: feature.properties!.b.index
67
- },
68
- b: {
69
- geom: feature.properties!.c.geom,
70
- index: feature.properties!.c.index
71
- },
72
- c: {
73
- geom: feature.properties!.a.geom,
74
- index: feature.properties!.a.index
75
- }
76
- },
77
- type: "Feature"
78
- };
79
- }
80
- }
81
- return tins;
82
- }
83
-
84
- /**
85
- * 三角形の頂点の座標系を反転する
86
- * @param tri 元の三角形
87
- * @returns 座標系が反転された三角形
88
- */
89
- function counterTri(tri: Tri): Tri {
90
- const coordinates = (["a", "b", "c", "a"] as PropertyTriKey[]).map(
91
- key => tri.properties![key].geom
92
- );
93
- const geoms = tri.geometry!.coordinates[0];
94
- const props = tri.properties!;
95
- const properties = {
96
- a: { geom: geoms[0], index: props["a"].index },
97
- b: { geom: geoms[1], index: props["b"].index },
98
- c: { geom: geoms[2], index: props["c"].index }
99
- };
100
- return polygon([coordinates], properties);
101
- }
102
-
103
- /**
104
- * 頂点座標群から三角形を生成する
105
- * @param points 頂点座標群 [座標, インデックス][]
106
- * @returns 生成された三角形
107
- */
108
- function buildTri(points: [Position[], string | number][]): Tri {
109
- const coordinates = [0, 1, 2, 0].map(i => points[i][0][0]);
110
- const properties = {
111
- a: { geom: points[0][0][1], index: points[0][1] },
112
- b: { geom: points[1][0][1], index: points[1][1] },
113
- c: { geom: points[2][0][1], index: points[2][1] }
114
- };
115
- return polygon([coordinates], properties);
116
- }
117
-
118
- /**
119
- * インデックス配列から三角形を生成する
120
- * @param indexes インデックス配列
121
- * @param points 点群座標
122
- * @param edgeNodes エッジノード座標
123
- * @param cent 重心座標
124
- * @param bboxes バウンディングボックス座標
125
- * @param bakw 座標系を反転するかどうか
126
- * @param version バージョン番号(オプション)
127
- * @returns 生成された三角形
128
- */
129
- function indexesToTri(
130
- indexes: (number | string)[],
131
- points: Position[][],
132
- edgeNodes: Position[][],
133
- cent: Position[],
134
- bboxes: Position[][],
135
- bakw = false,
136
- version?: number
137
- ): Tri {
138
- const points_: [Position[], string | number][] = indexes.map(
139
- (index: number | string) => {
140
- if (!version || version < 2.00703) index = normalizeNodeKey(index);
141
- const point_base = isFinite(index as number)
142
- ? points[index as number]
143
- : index === "c"
144
- ? cent
145
- : index === "b0"
146
- ? bboxes[0]
147
- : index === "b1"
148
- ? bboxes[1]
149
- : index === "b2"
150
- ? bboxes[2]
151
- : index === "b3"
152
- ? bboxes[3]
153
- : (function () {
154
- const match = (index as string).match(/e(\d+)/);
155
- if (match) {
156
- const nodeIndex = parseInt(match[1]);
157
- return edgeNodes[nodeIndex];
158
- }
159
- throw "Bad index value for indexesToTri";
160
- })();
161
- return bakw
162
- ? [[point_base![1], point_base![0]], index]
163
- : [[point_base![0], point_base![1]], index];
164
- }
165
- );
166
- return buildTri(points_);
167
- }
168
-
169
- // After 7.0.3 Normalizing node index key
170
- function normalizeNodeKey(index: number | string) {
171
- if (typeof index === "number") return index;
172
- return index.replace(/^(c|e|b)(?:ent|dgeNode|box)(\d+)?$/, "$1$2");
173
- }
174
-
175
- export {
176
- rotateVerticesTriangle,
177
- counterTri,
178
- indexesToTri,
179
- normalizeNodeKey
1
+ import { polygon } from "@turf/turf";
2
+ import type { Position } from "geojson";
3
+ import { PropertyTriKey, Tri, Tins } from "./geometry.ts";
4
+
5
+ /**
6
+ * 三角形の頂点の順序を修正する
7
+ * 地図外郭の頂点を含む三角形について、頂点の順序を統一する
8
+ * @param tins 三角形群
9
+ * @returns 頂点順序が修正された三角形群
10
+ */
11
+ function rotateVerticesTriangle(tins: Tins): Tins {
12
+ const features = tins.features;
13
+ for (let i = 0; i < features.length; i++) {
14
+ const feature = features[i];
15
+ if (
16
+ `${feature.properties!.a.index}`.substring(0, 1) === "b" &&
17
+ `${feature.properties!.b.index}`.substring(0, 1) === "b"
18
+ ) {
19
+ features[i] = {
20
+ geometry: {
21
+ type: "Polygon",
22
+ coordinates: [
23
+ [
24
+ feature.geometry!.coordinates[0][2],
25
+ feature.geometry!.coordinates[0][0],
26
+ feature.geometry!.coordinates[0][1],
27
+ feature.geometry!.coordinates[0][2]
28
+ ]
29
+ ]
30
+ },
31
+ properties: {
32
+ a: {
33
+ geom: feature.properties!.c.geom,
34
+ index: feature.properties!.c.index
35
+ },
36
+ b: {
37
+ geom: feature.properties!.a.geom,
38
+ index: feature.properties!.a.index
39
+ },
40
+ c: {
41
+ geom: feature.properties!.b.geom,
42
+ index: feature.properties!.b.index
43
+ }
44
+ },
45
+ type: "Feature"
46
+ };
47
+ } else if (
48
+ `${feature.properties!.c.index}`.substring(0, 1) === "b" &&
49
+ `${feature.properties!.a.index}`.substring(0, 1) === "b"
50
+ ) {
51
+ features[i] = {
52
+ geometry: {
53
+ type: "Polygon",
54
+ coordinates: [
55
+ [
56
+ feature.geometry!.coordinates[0][1],
57
+ feature.geometry!.coordinates[0][2],
58
+ feature.geometry!.coordinates[0][0],
59
+ feature.geometry!.coordinates[0][1]
60
+ ]
61
+ ]
62
+ },
63
+ properties: {
64
+ a: {
65
+ geom: feature.properties!.b.geom,
66
+ index: feature.properties!.b.index
67
+ },
68
+ b: {
69
+ geom: feature.properties!.c.geom,
70
+ index: feature.properties!.c.index
71
+ },
72
+ c: {
73
+ geom: feature.properties!.a.geom,
74
+ index: feature.properties!.a.index
75
+ }
76
+ },
77
+ type: "Feature"
78
+ };
79
+ }
80
+ }
81
+ return tins;
82
+ }
83
+
84
+ /**
85
+ * 三角形の頂点の座標系を反転する
86
+ * @param tri 元の三角形
87
+ * @returns 座標系が反転された三角形
88
+ */
89
+ function counterTri(tri: Tri): Tri {
90
+ const coordinates = (["a", "b", "c", "a"] as PropertyTriKey[]).map(
91
+ key => tri.properties![key].geom
92
+ );
93
+ const geoms = tri.geometry!.coordinates[0];
94
+ const props = tri.properties!;
95
+ const properties = {
96
+ a: { geom: geoms[0], index: props["a"].index },
97
+ b: { geom: geoms[1], index: props["b"].index },
98
+ c: { geom: geoms[2], index: props["c"].index }
99
+ };
100
+ return polygon([coordinates], properties);
101
+ }
102
+
103
+ /**
104
+ * 頂点座標群から三角形を生成する
105
+ * @param points 頂点座標群 [座標, インデックス][]
106
+ * @returns 生成された三角形
107
+ */
108
+ function buildTri(points: [Position[], string | number][]): Tri {
109
+ const coordinates = [0, 1, 2, 0].map(i => points[i][0][0]);
110
+ const properties = {
111
+ a: { geom: points[0][0][1], index: points[0][1] },
112
+ b: { geom: points[1][0][1], index: points[1][1] },
113
+ c: { geom: points[2][0][1], index: points[2][1] }
114
+ };
115
+ return polygon([coordinates], properties);
116
+ }
117
+
118
+ /**
119
+ * インデックス配列から三角形を生成する
120
+ * @param indexes インデックス配列
121
+ * @param points 点群座標
122
+ * @param edgeNodes エッジノード座標
123
+ * @param cent 重心座標
124
+ * @param bboxes バウンディングボックス座標
125
+ * @param bakw 座標系を反転するかどうか
126
+ * @param version バージョン番号(オプション)
127
+ * @returns 生成された三角形
128
+ */
129
+ function indexesToTri(
130
+ indexes: (number | string)[],
131
+ points: Position[][],
132
+ edgeNodes: Position[][],
133
+ cent: Position[],
134
+ bboxes: Position[][],
135
+ bakw = false,
136
+ version?: number
137
+ ): Tri {
138
+ const points_: [Position[], string | number][] = indexes.map(
139
+ (index: number | string) => {
140
+ if (!version || version < 2.00703) index = normalizeNodeKey(index);
141
+ const point_base = isFinite(index as number)
142
+ ? points[index as number]
143
+ : index === "c"
144
+ ? cent
145
+ : (function () {
146
+ const bMatch = (index as string).match(/^b(\d+)$/);
147
+ if (bMatch) return bboxes[parseInt(bMatch[1])];
148
+ const eMatch = (index as string).match(/^e(\d+)$/);
149
+ if (eMatch) return edgeNodes[parseInt(eMatch[1])];
150
+ throw new Error("Bad index value for indexesToTri");
151
+ })();
152
+ return bakw
153
+ ? [[point_base![1], point_base![0]], index]
154
+ : [[point_base![0], point_base![1]], index];
155
+ }
156
+ );
157
+ return buildTri(points_);
158
+ }
159
+
160
+ // After 7.0.3 Normalizing node index key
161
+ function normalizeNodeKey(index: number | string) {
162
+ if (typeof index === "number") return index;
163
+ return index.replace(/^(c|e|b)(?:ent|dgeNode|box)(\d+)?$/, "$1$2");
164
+ }
165
+
166
+ export {
167
+ rotateVerticesTriangle,
168
+ counterTri,
169
+ indexesToTri,
170
+ normalizeNodeKey
180
171
  };