Skip to content

Commit d5ed3d8

Browse files
kochizufanclaude
andcommitted
feat: MapTransform 処理2〜4 実装 (0.5.1)
- MapTransform クラス: submap 選択付き座標変換(処理2) - MapTransform クラス: ビューポート変換 viewpoint2Mercs / mercs2Viewpoint(処理3) - 地図間ビューポート同期(処理4): viewpoint2Mercs + mercs2Viewpoint の組み合わせ - デモ: submaps.html(処理2)、mapsync.html(処理3・4) - README(英語・日本語)に MapTransform 処理2〜4 の使用方法を追記 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2 parents 51310bd + 4196a61 commit d5ed3d8

42 files changed

Lines changed: 56202 additions & 923 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

18th_mapcontest_ea.json

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.

18th_mapcontest_gp.json

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.

README.ja.md

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ English README is [here](./README.md).
1515
- **位相保存:** 変換時の同相性(トポロジー)を維持
1616
- **複数の座標系サポート:** 通常の直交座標系、Y軸反転座標系、鳥瞰図のような歪んだ座標系など、様々な座標系間の変換に対応
1717
- **状態管理:** 変換状態の保存と復元をサポート
18+
- **サブマップ選択(処理2):** 同一画像上に複数のTIN領域(`sub_maps`)が存在する地図で、領域判定・優先度・重要度に基づいて適用するTINを自動選択し座標変換する
19+
- **ビューポート変換(処理3):** ピクセル座標系上の表示ビューポート(中心位置・ズームレベル・回転角)を地図座標系(EPSG:3857)上のビューポートに相互変換する
20+
- **地図間ビューポート同期(処理4):** ある絵地図の表示ビューポートを、共通の地図座標系(EPSG:3857)を中継して別の絵地図の表示ビューポートに直接変換する
1821

1922
## 動作要件
2023

@@ -67,6 +70,90 @@ const restored = transform.transform(transformed, true);
6770

6871
エラーが発生した場合は、変換定義データの修正が必要です。変換定義の修正は[@maplat/tin](https://github.com/code4history/MaplatTin/)を使用したエディタツールで行ってください。
6972

73+
## MapTransform の使用方法
74+
75+
### 処理2 — サブマップ選択つき座標変換
76+
77+
1枚の地図画像上に複数のTIN定義(`sub_maps`)が重なって存在する場合、`MapTransform` を使うと適切なTINを自動選択して座標変換できます。
78+
79+
```javascript
80+
import { MapTransform } from '@maplat/transform';
81+
82+
const mt = new MapTransform();
83+
mt.setMapData({
84+
compiled: mainCompiledData, // メインTINのコンパイル済みデータ
85+
sub_maps: [
86+
{ compiled: sub0Data, priority: 1, importance: 1 },
87+
{ compiled: sub1Data, priority: 2, importance: 2 },
88+
],
89+
});
90+
91+
// 順方向: ピクセルXY → EPSG:3857([レイヤーインデックス, Merc座標] または false を返す)
92+
const result = mt.xy2MercWithLayer([320, 240]);
93+
if (result) {
94+
const [layerIndex, merc] = result;
95+
console.log('レイヤー:', layerIndex, 'Merc座標:', merc);
96+
}
97+
98+
// 逆方向: EPSG:3857 → ピクセルXY(重要度順に最大2レイヤー返す)
99+
const results = mt.merc2XyWithLayer([15000000, 4000000]);
100+
results.forEach((r, i) => {
101+
if (r) console.log(`結果${i}: レイヤー${r[0]}, XY座標`, r[1]);
102+
});
103+
```
104+
105+
### 処理3 — ビューポート変換
106+
107+
ピクセル地図の表示ビューポート(中心位置・ズームレベル・回転角)を、EPSG:3857空間上の5点(中心+東西南北)として相互変換します。
108+
109+
```javascript
110+
import { MapTransform } from '@maplat/transform';
111+
112+
const mt = new MapTransform();
113+
mt.setMapData({ compiled: compiledData });
114+
115+
const canvasSize = [800, 600]; // キャンバスサイズ [幅, 高さ]
116+
117+
// ピクセルビューポート → EPSG:3857 5点
118+
const viewpoint = {
119+
center: [15000000, 4000000], // ピクセル空間の中心に対応するEPSG:3857相当座標
120+
zoom: 14,
121+
rotation: 0,
122+
};
123+
const mercs = mt.viewpoint2Mercs(viewpoint, canvasSize);
124+
// mercs: [[中心], [北], [東], [南], [西]] (EPSG:3857)
125+
126+
// EPSG:3857 5点 → ピクセルビューポート
127+
const vp = mt.mercs2Viewpoint(mercs, canvasSize);
128+
console.log(vp.center, vp.zoom, vp.rotation);
129+
```
130+
131+
### 処理4 — 地図間ビューポート同期
132+
133+
ある絵地図の表示ビューポートをEPSG:3857空間を中継して別の絵地図のビューポートへ直接変換します。
134+
135+
```javascript
136+
import { MapTransform } from '@maplat/transform';
137+
138+
const mtA = new MapTransform();
139+
mtA.setMapData({ compiled: compiledDataA });
140+
141+
const mtB = new MapTransform();
142+
mtB.setMapData({ compiled: compiledDataB });
143+
144+
const canvasSize = [800, 600];
145+
146+
// 地図Aのビューポート(ピクセル空間A)
147+
const vpA = { center: [15000000, 4000000], zoom: 14, rotation: 0 };
148+
149+
// ピクセル空間A → EPSG:3857 5点(処理3の順変換)
150+
const mercs = mtA.viewpoint2Mercs(vpA, canvasSize);
151+
152+
// EPSG:3857 5点 → 地図Bのビューポート(処理3の逆変換)
153+
const vpB = mtB.mercs2Viewpoint(mercs, canvasSize);
154+
console.log('地図Bのビューポート:', vpB);
155+
```
156+
70157
## API リファレンス
71158

72159
### Transform クラス
@@ -119,13 +206,88 @@ Maplatで生成されたコンパイル済み変換定義をインポートし
119206
- `Transform.YAXIS_FOLLOW`: Y軸方向に従う
120207
- `Transform.YAXIS_INVERT`: Y軸方向を反転
121208

209+
### MapTransform クラス
210+
211+
サブマップ選択、ビューポート変換、地図間ビューポート同期(処理2〜4)を担うクラスです。
212+
213+
#### コンストラクタ
214+
215+
```javascript
216+
const mt = new MapTransform();
217+
```
218+
219+
#### メソッド
220+
221+
##### `setMapData(mapData: MapData): void`
222+
223+
メインTINとオプションのサブマップTINをロードします。
224+
225+
- **パラメータ:**
226+
- `mapData`: `{ compiled, maxZoom?, sub_maps? }` — メインのコンパイル済みTINデータ、オプションの明示的なmaxZoom、オプションのサブマップ定義配列
227+
228+
##### `xy2Merc(xy: number[]): number[] | false`
229+
230+
メインTINを使ってピクセル座標をEPSG:3857に変換します。
231+
232+
- **パラメータ:** `xy` — ピクセル座標 `[x, y]`
233+
- **戻り値:** EPSG:3857座標、または範囲外の場合は `false`
234+
235+
##### `merc2Xy(merc: number[]): number[] | false`
236+
237+
メインTINを使ってEPSG:3857座標をピクセル座標に逆変換します。
238+
239+
- **パラメータ:** `merc` — EPSG:3857座標 `[x, y]`
240+
- **戻り値:** ピクセル座標、または範囲外の場合は `false`
241+
242+
##### `xy2MercWithLayer(xy: number[]): [number, number[]] | false`
243+
244+
優先度と領域に基づいてサブマップから適切なTINを自動選択し、ピクセル座標をEPSG:3857に変換します(処理2)。
245+
246+
- **パラメータ:** `xy` — ピクセル座標 `[x, y]`
247+
- **戻り値:** `[レイヤーインデックス, Merc座標]`、または範囲外の場合は `false`
248+
249+
##### `merc2XyWithLayer(merc: number[]): ([number, number[]] | undefined)[]`
250+
251+
該当する全TINレイヤーでEPSG:3857座標をピクセル座標に逆変換し、重要度順に最大2件を返します(処理2)。
252+
> 3件以上返したい場合は、実装内の `.slice(0, 2)` / `.filter(i < 2)` の上限値を変更してください。
253+
254+
- **パラメータ:** `merc` — EPSG:3857座標 `[x, y]`
255+
- **戻り値:** 最大2要素の配列。各要素は `[レイヤーインデックス, XY座標]` または `undefined`
256+
257+
##### `viewpoint2Mercs(viewpoint: Viewpoint, size: [number, number]): number[][]`
258+
259+
ピクセル空間のビューポートをEPSG:3857の5点に変換します(処理3)。
260+
261+
- **パラメータ:**
262+
- `viewpoint`: `{ center, zoom, rotation }` — ピクセル空間のビューポート(centerは `xy2SysCoord` 変換後のEPSG:3857相当値)
263+
- `size`: キャンバスサイズ `[幅, 高さ]`
264+
- **戻り値:** EPSG:3857の5点配列 `[中心, 北, 東, 南, 西]`
265+
- **例外:** 中心点がTIN範囲外の場合にエラー
266+
267+
##### `mercs2Viewpoint(mercs: number[][], size: [number, number]): Viewpoint`
268+
269+
EPSG:3857の5点からピクセル空間のビューポートに逆変換します(処理3の逆変換)。
270+
271+
- **パラメータ:**
272+
- `mercs`: EPSG:3857の5点配列(`viewpoint2Mercs` の戻り値と同形式)
273+
- `size`: キャンバスサイズ `[幅, 高さ]`
274+
- **戻り値:** ピクセル空間の `Viewpoint``{ center, zoom, rotation }`
275+
- **例外:** 中心点が逆変換できない場合にエラー
276+
277+
#### アクセサ
278+
279+
- `maxxy: number``2^maxZoom × 256`。ピクセル座標とEPSG:3857座標の変換スケール係数
280+
122281
### エクスポートされる型
123282

124283
- `PointSet`, `BiDirectionKey`, `WeightBufferBD`, `VertexMode`, `StrictMode`, `StrictStatus`, `YaxisMode`
125284
- `CentroidBD`, `TinsBD`, `KinksBD`, `VerticesParamsBD`, `IndexedTinsBD`
126285
- `Compiled`, `CompiledLegacy`
127286
- `Tins`, `Tri`, `PropertyTriKey`
128287
- `Edge`, `EdgeSet`, `EdgeSetLegacy`
288+
- `Viewpoint``{ center: number[], zoom: number, rotation: number }`
289+
- `MapData``{ compiled: Compiled, maxZoom?: number, sub_maps?: SubMapData[] }`
290+
- `SubMapData``{ compiled: Compiled, priority: number, importance: number, bounds?: number[][] }`
129291

130292
### エクスポートされるユーティリティ関数
131293

README.md

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ This is part of the [Maplat](https://github.com/code4history/Maplat/) project.
1515
- **Topology Preservation:** Maintains homeomorphic properties during transformation
1616
- **Multiple Coordinate System Support:** Handles transformations between various coordinate systems including standard orthogonal coordinates, Y-axis inverted coordinates, and distorted coordinates like bird's-eye views
1717
- **State Management:** Save and restore transformation states
18+
- **Sub-map Selection (Processing 2):** For maps with multiple overlapping TIN regions (`sub_maps`), automatically determines which TIN to apply based on region boundaries, priority, and importance
19+
- **Viewport Transformation (Processing 3):** Converts a display viewport (center, zoom, rotation) in pixel coordinate space to a viewport in the map coordinate space (EPSG:3857)
20+
- **Cross-map Viewport Sync (Processing 4):** Converts a display viewport from one pixel map directly to the corresponding viewport of another pixel map, via the shared map coordinate space
1821

1922
## Requirements
2023

@@ -67,6 +70,90 @@ The library may throw errors in the following cases:
6770

6871
If errors occur, the transformation definition data needs to be modified. Please use editor tools that incorporate [@maplat/tin](https://github.com/code4history/MaplatTin/) to modify transformation definitions.
6972

73+
## MapTransform Usage
74+
75+
### Processing 2 — Sub-map selection and coordinate transformation
76+
77+
When a single map image contains multiple overlapping TIN definitions (`sub_maps`), use `MapTransform` to automatically select the correct TIN and transform coordinates.
78+
79+
```javascript
80+
import { MapTransform } from '@maplat/transform';
81+
82+
const mt = new MapTransform();
83+
mt.setMapData({
84+
compiled: mainCompiledData, // Main TIN compiled data
85+
sub_maps: [
86+
{ compiled: sub0Data, priority: 1, importance: 1 },
87+
{ compiled: sub1Data, priority: 2, importance: 2 },
88+
],
89+
});
90+
91+
// Forward: pixel XY → EPSG:3857 (returns [layerIndex, mercCoord] or false)
92+
const result = mt.xy2MercWithLayer([320, 240]);
93+
if (result) {
94+
const [layerIndex, merc] = result;
95+
console.log('layer:', layerIndex, 'merc:', merc);
96+
}
97+
98+
// Reverse: EPSG:3857 → pixel XY (returns up to 2 layers, each [layerIndex, xyCoord] or undefined)
99+
const results = mt.merc2XyWithLayer([15000000, 4000000]);
100+
results.forEach((r, i) => {
101+
if (r) console.log(`result ${i}: layer ${r[0]}, xy`, r[1]);
102+
});
103+
```
104+
105+
### Processing 3 — Viewport transformation
106+
107+
Convert a pixel-map viewport (center position, zoom level, rotation angle) to and from a geographic viewport in EPSG:3857. The viewport is represented as five Mercator points (center + four cardinal offsets).
108+
109+
```javascript
110+
import { MapTransform } from '@maplat/transform';
111+
112+
const mt = new MapTransform();
113+
mt.setMapData({ compiled: compiledData });
114+
115+
const canvasSize = [800, 600]; // [width, height] in pixels
116+
117+
// Pixel viewport → EPSG:3857 five points
118+
const viewpoint = {
119+
center: [15000000, 4000000], // EPSG:3857 center of the pixel-space viewport
120+
zoom: 14,
121+
rotation: 0,
122+
};
123+
const mercs = mt.viewpoint2Mercs(viewpoint, canvasSize);
124+
// mercs: [[cx,cy], [north], [east], [south], [west]] (EPSG:3857)
125+
126+
// EPSG:3857 five points → pixel viewport
127+
const vp = mt.mercs2Viewpoint(mercs, canvasSize);
128+
console.log(vp.center, vp.zoom, vp.rotation);
129+
```
130+
131+
### Processing 4 — Cross-map viewport synchronization
132+
133+
Convert the display viewport of one pixel map to the corresponding viewport of another pixel map, using the shared EPSG:3857 space as an intermediary.
134+
135+
```javascript
136+
import { MapTransform } from '@maplat/transform';
137+
138+
const mtA = new MapTransform();
139+
mtA.setMapData({ compiled: compiledDataA });
140+
141+
const mtB = new MapTransform();
142+
mtB.setMapData({ compiled: compiledDataB });
143+
144+
const canvasSize = [800, 600];
145+
146+
// Map A viewport (pixel space A)
147+
const vpA = { center: [15000000, 4000000], zoom: 14, rotation: 0 };
148+
149+
// Pixel space A → EPSG:3857 five points (Processing 3 forward)
150+
const mercs = mtA.viewpoint2Mercs(vpA, canvasSize);
151+
152+
// EPSG:3857 five points → Map B viewport (Processing 3 reverse)
153+
const vpB = mtB.mercs2Viewpoint(mercs, canvasSize);
154+
console.log('Map B viewport:', vpB);
155+
```
156+
70157
## API Reference
71158

72159
### Transform Class
@@ -119,13 +206,88 @@ Perform coordinate transformation.
119206
- `Transform.YAXIS_FOLLOW`: Follow Y-axis direction
120207
- `Transform.YAXIS_INVERT`: Invert Y-axis direction
121208

209+
### MapTransform Class
210+
211+
The class for sub-map selection, viewport transformation, and cross-map viewport synchronization (Processings 2–4).
212+
213+
#### Constructor
214+
215+
```javascript
216+
const mt = new MapTransform();
217+
```
218+
219+
#### Methods
220+
221+
##### `setMapData(mapData: MapData): void`
222+
223+
Load a main TIN and optional sub-map TINs.
224+
225+
- **Parameters:**
226+
- `mapData`: `{ compiled, maxZoom?, sub_maps? }` — main compiled TIN data, optional explicit maxZoom, and optional array of sub-map definitions
227+
228+
##### `xy2Merc(xy: number[]): number[] | false`
229+
230+
Transform a pixel coordinate to EPSG:3857 using the main TIN.
231+
232+
- **Parameters:** `xy` — pixel coordinate `[x, y]`
233+
- **Returns:** EPSG:3857 coordinate, or `false` if out of bounds
234+
235+
##### `merc2Xy(merc: number[]): number[] | false`
236+
237+
Transform an EPSG:3857 coordinate to pixel coordinate using the main TIN (reverse).
238+
239+
- **Parameters:** `merc` — EPSG:3857 coordinate `[x, y]`
240+
- **Returns:** Pixel coordinate, or `false` if out of bounds
241+
242+
##### `xy2MercWithLayer(xy: number[]): [number, number[]] | false`
243+
244+
Transform a pixel coordinate to EPSG:3857, automatically selecting the appropriate TIN from sub-maps based on priority and region (Processing 2).
245+
246+
- **Parameters:** `xy` — pixel coordinate `[x, y]`
247+
- **Returns:** `[layerIndex, mercCoord]` or `false` if out of bounds
248+
249+
##### `merc2XyWithLayer(merc: number[]): ([number, number[]] | undefined)[]`
250+
251+
Transform an EPSG:3857 coordinate to pixel coordinate across all applicable TIN layers, returning up to 2 results ordered by importance (Processing 2).
252+
> To return more than 2 layers, increase the limit in the `.slice(0, 2)` / `.filter(i < 2)` lines inside the implementation.
253+
254+
- **Parameters:** `merc` — EPSG:3857 coordinate `[x, y]`
255+
- **Returns:** Array of up to 2 elements; each is `[layerIndex, xyCoord]` or `undefined`
256+
257+
##### `viewpoint2Mercs(viewpoint: Viewpoint, size: [number, number]): number[][]`
258+
259+
Convert a pixel-space viewport to five EPSG:3857 points (Processing 3).
260+
261+
- **Parameters:**
262+
- `viewpoint`: `{ center, zoom, rotation }` — viewport in pixel space (center as EPSG:3857 equivalent via `xy2SysCoord`)
263+
- `size`: Canvas size `[width, height]`
264+
- **Returns:** Array of 5 EPSG:3857 points `[center, north, east, south, west]`
265+
- **Throws:** Error if the center point is outside the TIN bounds
266+
267+
##### `mercs2Viewpoint(mercs: number[][], size: [number, number]): Viewpoint`
268+
269+
Convert five EPSG:3857 points back to a pixel-space viewport (Processing 3 reverse).
270+
271+
- **Parameters:**
272+
- `mercs`: Array of 5 EPSG:3857 points (as returned by `viewpoint2Mercs`)
273+
- `size`: Canvas size `[width, height]`
274+
- **Returns:** `Viewpoint``{ center, zoom, rotation }` in pixel space
275+
- **Throws:** Error if the center point cannot be reverse-transformed
276+
277+
#### Accessors
278+
279+
- `maxxy: number``2^maxZoom × 256`; the pixel-to-EPSG:3857 scale factor
280+
122281
### Exported Types
123282

124283
- `PointSet`, `BiDirectionKey`, `WeightBufferBD`, `VertexMode`, `StrictMode`, `StrictStatus`, `YaxisMode`
125284
- `CentroidBD`, `TinsBD`, `KinksBD`, `VerticesParamsBD`, `IndexedTinsBD`
126285
- `Compiled`, `CompiledLegacy`
127286
- `Tins`, `Tri`, `PropertyTriKey`
128287
- `Edge`, `EdgeSet`, `EdgeSetLegacy`
288+
- `Viewpoint``{ center: number[], zoom: number, rotation: number }`
289+
- `MapData``{ compiled: Compiled, maxZoom?: number, sub_maps?: SubMapData[] }`
290+
- `SubMapData``{ compiled: Compiled, priority: number, importance: number, bounds?: number[][] }`
129291

130292
### Exported Utility Functions
131293

0 commit comments

Comments
 (0)