@uoa-css-lab/duckscatter 1.3.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.
Files changed (83) hide show
  1. package/.github/dependabot.yml +42 -0
  2. package/.github/workflows/ci.yaml +111 -0
  3. package/.github/workflows/release.yml +55 -0
  4. package/.prettierrc +11 -0
  5. package/LICENSE +22 -0
  6. package/README.md +250 -0
  7. package/dist/data/data-layer.d.ts +169 -0
  8. package/dist/data/data-layer.js +402 -0
  9. package/dist/data/index.d.ts +2 -0
  10. package/dist/data/index.js +2 -0
  11. package/dist/data/repository.d.ts +48 -0
  12. package/dist/data/repository.js +109 -0
  13. package/dist/diagnostics.d.ts +27 -0
  14. package/dist/diagnostics.js +71 -0
  15. package/dist/errors.d.ts +22 -0
  16. package/dist/errors.js +58 -0
  17. package/dist/event-emitter.d.ts +62 -0
  18. package/dist/event-emitter.js +82 -0
  19. package/dist/index.d.ts +12 -0
  20. package/dist/index.js +13 -0
  21. package/dist/renderer/gpu-layer.d.ts +204 -0
  22. package/dist/renderer/gpu-layer.js +611 -0
  23. package/dist/renderer/index.d.ts +3 -0
  24. package/dist/renderer/index.js +3 -0
  25. package/dist/renderer/shaders.d.ts +13 -0
  26. package/dist/renderer/shaders.js +216 -0
  27. package/dist/renderer/webgpu-context.d.ts +20 -0
  28. package/dist/renderer/webgpu-context.js +88 -0
  29. package/dist/scatter-plot.d.ts +210 -0
  30. package/dist/scatter-plot.js +450 -0
  31. package/dist/types.d.ts +171 -0
  32. package/dist/types.js +1 -0
  33. package/dist/ui/index.d.ts +1 -0
  34. package/dist/ui/index.js +1 -0
  35. package/dist/ui/label-layer.d.ts +176 -0
  36. package/dist/ui/label-layer.js +488 -0
  37. package/docs/image.png +0 -0
  38. package/eslint.config.js +72 -0
  39. package/examples/next/README.md +36 -0
  40. package/examples/next/app/components/ColorExpressionInput.tsx +41 -0
  41. package/examples/next/app/components/ControlPanel.tsx +30 -0
  42. package/examples/next/app/components/HoverControlPanel.tsx +69 -0
  43. package/examples/next/app/components/HoverInfoDisplay.tsx +40 -0
  44. package/examples/next/app/components/LabelFilterInput.tsx +46 -0
  45. package/examples/next/app/components/LabelList.tsx +106 -0
  46. package/examples/next/app/components/PointAlphaSlider.tsx +21 -0
  47. package/examples/next/app/components/PointLimitSlider.tsx +23 -0
  48. package/examples/next/app/components/PointList.tsx +105 -0
  49. package/examples/next/app/components/PointSizeScaleSlider.tsx +22 -0
  50. package/examples/next/app/components/ScatterPlotCanvas.tsx +150 -0
  51. package/examples/next/app/components/SearchBox.tsx +46 -0
  52. package/examples/next/app/components/Slider.tsx +76 -0
  53. package/examples/next/app/components/StatsDisplay.tsx +15 -0
  54. package/examples/next/app/components/TimeFilterSlider.tsx +169 -0
  55. package/examples/next/app/context/ScatterPlotContext.tsx +402 -0
  56. package/examples/next/app/favicon.ico +0 -0
  57. package/examples/next/app/globals.css +23 -0
  58. package/examples/next/app/layout.tsx +35 -0
  59. package/examples/next/app/page.tsx +15 -0
  60. package/examples/next/eslint.config.mjs +18 -0
  61. package/examples/next/next.config.ts +7 -0
  62. package/examples/next/package-lock.json +6572 -0
  63. package/examples/next/package.json +27 -0
  64. package/examples/next/postcss.config.mjs +7 -0
  65. package/examples/next/scripts/generate_labels.py +167 -0
  66. package/examples/next/tsconfig.json +34 -0
  67. package/package.json +43 -0
  68. package/src/data/data-layer.ts +515 -0
  69. package/src/data/index.ts +2 -0
  70. package/src/data/repository.ts +146 -0
  71. package/src/diagnostics.ts +108 -0
  72. package/src/errors.ts +69 -0
  73. package/src/event-emitter.ts +88 -0
  74. package/src/index.ts +40 -0
  75. package/src/renderer/gpu-layer.ts +757 -0
  76. package/src/renderer/index.ts +3 -0
  77. package/src/renderer/shaders.ts +219 -0
  78. package/src/renderer/webgpu-context.ts +98 -0
  79. package/src/scatter-plot.ts +533 -0
  80. package/src/types.ts +218 -0
  81. package/src/ui/index.ts +1 -0
  82. package/src/ui/label-layer.ts +648 -0
  83. package/tsconfig.json +19 -0
@@ -0,0 +1,42 @@
1
+ # To get started with Dependabot version updates, you'll need to specify which
2
+ # package ecosystems to update and where the package manifests are located.
3
+ # Please see the documentation for all configuration options:
4
+ # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
5
+
6
+ version: 2
7
+ updates:
8
+ - package-ecosystem: "github-actions"
9
+ directory: "/"
10
+ schedule:
11
+ interval: "monthly"
12
+ open-pull-requests-limit: 50
13
+ labels:
14
+ - "dependencies"
15
+ groups:
16
+ dependencies:
17
+ patterns:
18
+ - "*"
19
+
20
+ - package-ecosystem: "npm"
21
+ directory: "/"
22
+ schedule:
23
+ interval: "monthly"
24
+ open-pull-requests-limit: 50
25
+ labels:
26
+ - "dependencies"
27
+ groups:
28
+ dependencies:
29
+ patterns:
30
+ - "*"
31
+
32
+ - package-ecosystem: "npm"
33
+ directory: "/examples/next"
34
+ schedule:
35
+ interval: "monthly"
36
+ open-pull-requests-limit: 50
37
+ labels:
38
+ - "dependencies"
39
+ groups:
40
+ dependencies:
41
+ patterns:
42
+ - "*"
@@ -0,0 +1,111 @@
1
+ on:
2
+ push:
3
+ branches:
4
+ - development
5
+ tags:
6
+ - v*
7
+ pull_request:
8
+ paths-ignore:
9
+ - '**.md'
10
+
11
+ jobs:
12
+ build:
13
+ runs-on: ubuntu-latest
14
+ steps:
15
+ - uses: actions/checkout@v6
16
+ - uses: actions/setup-node@v6
17
+ with:
18
+ node-version: 20
19
+ - uses: actions/cache@v5
20
+ with:
21
+ path: node_modules
22
+ key: ${{ hashFiles('package-lock.json') }}
23
+ - run: npm install
24
+ - name: build
25
+ run: npm run build
26
+ - uses: actions/cache/save@v5
27
+ with:
28
+ path: dist
29
+ key: dist-${{ github.sha }}
30
+
31
+ lint:
32
+ needs:
33
+ - build
34
+ runs-on: ubuntu-latest
35
+ steps:
36
+ - uses: actions/checkout@v6
37
+ - uses: actions/setup-node@v6
38
+ with:
39
+ node-version: 20
40
+ - uses: actions/cache@v5
41
+ with:
42
+ path: "**/node_modules"
43
+ key: ${{ hashFiles('package-lock.json', 'examples/next/package-lock.json') }}
44
+ - run: npm install
45
+ - run: npm install --prefix examples/next
46
+ - uses: actions/cache/restore@v5
47
+ with:
48
+ path: dist
49
+ key: dist-${{ github.sha }}
50
+ - name: lint
51
+ run: npm run lint:all
52
+
53
+ test:
54
+ needs:
55
+ - build
56
+ runs-on: ubuntu-latest
57
+ steps:
58
+ - uses: actions/checkout@v6
59
+ - uses: actions/setup-node@v6
60
+ with:
61
+ node-version: 20
62
+ - uses: actions/cache@v5
63
+ with:
64
+ path: "**/node_modules"
65
+ key: ${{ hashFiles('package-lock.json') }}
66
+ - run: npm install
67
+ - uses: actions/cache/restore@v5
68
+ with:
69
+ path: dist
70
+ key: dist-${{ github.sha }}
71
+ - name: test
72
+ run: npm run test
73
+
74
+ publish:
75
+ runs-on: ubuntu-latest
76
+ needs:
77
+ - build
78
+ - lint
79
+ - test
80
+ if: startsWith(github.ref, 'refs/tags/')
81
+ permissions:
82
+ packages: write
83
+ contents: read
84
+ strategy:
85
+ matrix:
86
+ registry-url:
87
+ - https://npm.pkg.github.com
88
+ - https://registry.npmjs.org
89
+ steps:
90
+ - uses: actions/checkout@v6
91
+ - uses: actions/setup-node@v6
92
+ if: ${{ matrix.registry-url == 'https://npm.pkg.github.com' }}
93
+ with:
94
+ node-version: 20
95
+ registry-url: ${{ matrix.registry-url }}
96
+ env:
97
+ NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
98
+ - uses: actions/setup-node@v6
99
+ if: ${{ matrix.registry-url == 'https://registry.npmjs.org' }}
100
+ with:
101
+ node-version: 20
102
+ registry-url: ${{ matrix.registry-url }}
103
+ env:
104
+ NODE_AUTH_TOKEN: ${{ secrets.NPMJS_TOKEN }}
105
+ - uses: actions/cache/restore@v5
106
+ with:
107
+ path: dist
108
+ key: dist-${{ github.sha }}
109
+ - run: npm publish --registry ${{ matrix.registry-url }} --access public
110
+ env:
111
+ NODE_AUTH_TOKEN: ${{ matrix.registry-url == 'https://npm.pkg.github.com' && secrets.GITHUB_TOKEN || secrets.NPMJS_TOKEN }}
@@ -0,0 +1,55 @@
1
+ on:
2
+ workflow_dispatch:
3
+ inputs:
4
+ version:
5
+ description: version
6
+ required: true
7
+ type: choice
8
+ options:
9
+ - patch
10
+ - minor
11
+ - major
12
+ - premajor
13
+ - prerelease
14
+ preid:
15
+ description: preid
16
+ type: choice
17
+ options:
18
+ - ""
19
+ - rc
20
+ - alpha
21
+
22
+ jobs:
23
+ create-tag:
24
+ runs-on: ubuntu-latest
25
+ permissions:
26
+ contents: write
27
+ actions: write
28
+ outputs:
29
+ version: ${{ steps.version.outputs.version }}
30
+ steps:
31
+ - name: Generate a token
32
+ id: generate-token
33
+ uses: actions/create-github-app-token@v2
34
+ with:
35
+ app-id: ${{ secrets.RELEASE_APP_ID }}
36
+ private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }}
37
+ - uses: actions/checkout@v6
38
+ with:
39
+ token: ${{ steps.generate-token.outputs.token }}
40
+ - uses: actions/setup-node@v6
41
+ with:
42
+ node-version: 20
43
+
44
+ - run: |
45
+ git config user.name "github-actions[bot]"
46
+ git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
47
+
48
+ - if: ${{ github.event.inputs.preid == '' }}
49
+ run: npm version ${{ github.event.inputs.version }}
50
+
51
+ - if: ${{ github.event.inputs.preid != '' }}
52
+ run: npm version ${{ github.event.inputs.version }} --preid ${{ github.event.inputs.preid }}
53
+
54
+ - name: git push
55
+ run: git push origin --tags ${{ github.ref }}:${{ github.ref }}
package/.prettierrc ADDED
@@ -0,0 +1,11 @@
1
+ {
2
+ "semi": true,
3
+ "trailingComma": "es5",
4
+ "singleQuote": true,
5
+ "printWidth": 100,
6
+ "tabWidth": 2,
7
+ "useTabs": false,
8
+ "endOfLine": "lf",
9
+ "arrowParens": "always",
10
+ "bracketSpacing": true
11
+ }
package/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) Meta Platforms, Inc. and affiliates.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
package/README.md ADDED
@@ -0,0 +1,250 @@
1
+ # duckscatter
2
+
3
+ [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/UoA-CSS-Lab/duckscatter/blob/main/LICENSE)
4
+ [![npm version](https://img.shields.io/npm/v/@uoa-css-lab/duckscatter.svg?style=flat)](https://www.npmjs.com/package/@uoa-css-lab/duckscatter)
5
+ [![CI](https://github.com/UoA-CSS-Lab/duckscatter/actions/workflows/ci.yaml/badge.svg)](https://github.com/UoA-CSS-Lab/duckscatter/actions/workflows/ci.yaml)
6
+ [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/UoA-CSS-Lab/duckscatter)
7
+
8
+ > duckscatterは大規模な散布図を描画するための、TypeScriptライブラリです。
9
+
10
+ 従来の散布図描画ライブラリでは、全文検索などの複雑なフィルタリングに時間がかかりました。duckscatterは、DuckDB-WASMによるSQL内での高速なデータ処理と、WebGPUによる大規模並列レンダリングを組み合わせることで、数十万点規模のデータでもスムーズな操作を実現します。
11
+
12
+ * **WebGPUによるレンダリング:** GPUの能力を活用し、ブラウザ上で大規模な散布図を高速に描画します。CanvasやSVGでは扱えきれないような大量のデータポイントであっても、スムーズな操作を実現します。
13
+
14
+ * **DuckDB-WASMによる高速なデータ分析:** DuckDB-WASMを内蔵しており、標準的なSQLを実行できます。SQLを使って動的に描画データをフィルタリングしたり、点の色やサイズを計算したりすることが可能です。
15
+
16
+ * **ラベル表示:** データポイントにテキストラベルを表示できます。クラスタリングの結果を可視化する際に、各クラスタの中心や代表点にラベルを表示したり、特定のデータポイントに注釈を付けたりするのに最適です。
17
+
18
+ ## 画面イメージ
19
+
20
+ <img src="./docs/image.png" alt="screen image" width="600">
21
+
22
+ ## データについて
23
+
24
+ ### Parquetファイル
25
+
26
+ duckscatterは、Parquet形式のデータファイルを読み込みます。以下のカラムが必要です:
27
+
28
+ | カラム | 型 | 説明 |
29
+ |--------|------|------|
30
+ | `x` | double | X座標 |
31
+ | `y` | double | Y座標 |
32
+ | IDカラム | 任意 | ポイントを識別するための一意キー(カラム名は`idColumn`オプションで指定) |
33
+
34
+ その他のカラムはSQLで参照でき、色やサイズの計算に利用できます。
35
+
36
+ ### GeoJSONファイル(ラベル用)
37
+
38
+ ラベルを表示するには、GeoJSON形式のファイルを指定します:
39
+
40
+ ```json
41
+ {
42
+ "type": "FeatureCollection",
43
+ "features": [
44
+ {
45
+ "type": "Feature",
46
+ "geometry": {
47
+ "type": "Point",
48
+ "coordinates": [x, y]
49
+ },
50
+ "properties": {
51
+ "cluster_label": "ラベルテキスト"
52
+ }
53
+ }
54
+ ]
55
+ }
56
+ ```
57
+
58
+ ## API
59
+
60
+ ```typescript
61
+ const plot = new ScatterPlot({
62
+ canvas: HTMLCanvasElement, // 描画先のcanvas要素
63
+ dataUrl: string, // ParquetファイルのURL
64
+ data: {
65
+ idColumn: string, // IDカラム名(必須)
66
+ visiblePointLimit?: number, // 描画最大ポイント数(デフォルト: 100,000)
67
+ sizeSql?: string, // サイズ計算SQL式(デフォルト: "3")
68
+ colorSql?: string, // 色計算SQL式(ARGB 32bit整数、デフォルト: "0x4D4D4DCC")
69
+ whereConditions?: WhereCondition[], // フィルタ条件(CPU側)
70
+ gpuFilterColumns?: string[], // GPUフィルタリング用カラム名(最大4つ)
71
+ gpuWhereConditions?: GpuWhereCondition[], // GPUフィルター条件
72
+ },
73
+ gpu?: {
74
+ backgroundColor?: ColorRGBA, // 背景色
75
+ pointAlpha?: number, // グローバル透明度(0.0-1.0、デフォルト: 1.0)
76
+ pointSizeScale?: number, // グローバルサイズスケール(デフォルト: 1.0)
77
+ },
78
+ labels?: {
79
+ url?: string, // GeoJSONファイルのURL
80
+ fontSize?: number, // フォントサイズ(デフォルト: 12)
81
+ filterLambda?: LabelFilterLambda, // ラベル表示フィルタ
82
+ onClick?: (label: Label) => void, // クリックコールバック
83
+ hoverOutlineOptions?: HoverOutlineOptions, // ホバーアウトライン設定
84
+ },
85
+ interaction?: {
86
+ onPointHover?: PointHoverCallback, // ポイントホバーコールバック
87
+ onLabelHover?: LabelHoverCallback, // ラベルホバーコールバック
88
+ },
89
+ });
90
+
91
+ await plot.initialize();
92
+ ```
93
+
94
+ 主要メソッド:
95
+
96
+ * `render()`: 描画
97
+ * `resize(width, height)`: キャンバスリサイズ
98
+ * `setZoom(zoom)` / `getZoom()` / `zoomIn()` / `zoomOut()`: ズーム操作
99
+ * `zoomToPoint(newZoom, screenX, screenY)`: 指定座標を中心にズーム
100
+ * `setPan(x, y)` / `getPan()` / `pan(dx, dy)`: パン操作
101
+ * `resetView()`: ビューリセット
102
+ * `update(options)`: オプション更新
103
+ * `runQuery(sql)`: カスタムSQLクエリ実行
104
+ * `getLabels()`: ラベル全件取得
105
+ * `destroy()`: リソース解放
106
+
107
+ **ポイント表示属性制御:**
108
+
109
+ * `setPointAlpha(alpha)`: グローバル透明度を設定(0.0-1.0)
110
+ * `getPointAlpha()`: 現在のグローバル透明度を取得
111
+ * `setPointSizeScale(scale)`: グローバルサイズスケールを設定
112
+ * `getPointSizeScale()`: 現在のグローバルサイズスケールを取得
113
+
114
+ **ホバー制御API:**
115
+
116
+ 外部コンポーネントからプログラム的にホバー状態を制御できます。
117
+
118
+ * `setPointHover(pointId)`: ポイントをホバー状態に(`Promise<boolean>`)
119
+ * `clearPointHover()`: ポイントホバー解除
120
+ * `getHoveredPoint()`: ホバー中のポイント取得
121
+ * `setLabelHover(identifier)`: ラベルをホバー状態に(`boolean`を返す)
122
+ * `clearLabelHover()`: ラベルホバー解除
123
+ * `getHoveredLabel()`: ホバー中のラベル取得
124
+ * `clearAllHover()`: 全ホバー解除
125
+
126
+ ```typescript
127
+ // 使用例
128
+ await plot.setPointHover(12345); // IDでポイントをホバー
129
+ plot.setLabelHover({ text: 'Cluster A' }); // テキストでラベルをホバー
130
+ plot.setLabelHover({ cluster: 5 }); // クラスタ番号でラベルをホバー
131
+ plot.clearAllHover(); // 全ホバー解除
132
+ ```
133
+
134
+ ## GPUフィルタリング
135
+
136
+ GPUフィルタリングを使用すると、数値カラムの範囲フィルタをGPU側で高速に実行できます。データの再フェッチなしにリアルタイムでフィルタリングが可能です。
137
+
138
+ ```typescript
139
+ const plot = new ScatterPlot({
140
+ // ...
141
+ data: {
142
+ idColumn: 'word',
143
+ // GPUフィルタリング用のカラムを指定(最大4つ)
144
+ gpuFilterColumns: ['frequency', 'length'],
145
+ // フィルター条件を指定
146
+ gpuWhereConditions: [
147
+ { column: 'frequency', min: 100, max: 10000 },
148
+ { column: 'length', min: 3 },
149
+ ],
150
+ },
151
+ });
152
+
153
+ // 実行時にフィルター条件を更新
154
+ await plot.update({
155
+ data: {
156
+ gpuWhereConditions: [
157
+ { column: 'frequency', min: 500 },
158
+ ],
159
+ },
160
+ });
161
+ ```
162
+
163
+ **CPUフィルタ(`whereConditions`)との違い:**
164
+
165
+ | | CPUフィルタ | GPUフィルタ |
166
+ |---|---|---|
167
+ | **対応演算子** | 数値比較、文字列検索、生SQL | 範囲のみ(min/max) |
168
+ | **更新速度** | SQLクエリ再実行が必要 | 即座に反映 |
169
+ | **用途** | 複雑な条件、全文検索 | スライダーなどリアルタイム操作 |
170
+
171
+ ## 型定義
172
+
173
+ 主な型定義:
174
+
175
+ ```typescript
176
+ // GPUフィルター条件
177
+ interface GpuWhereCondition {
178
+ column: string; // gpuFilterColumnsで指定したカラム名
179
+ min?: number; // 最小値(省略時: -Infinity)
180
+ max?: number; // 最大値(省略時: +Infinity)
181
+ }
182
+
183
+ // ホバーアウトラインオプション
184
+ interface HoverOutlineOptions {
185
+ enabled?: boolean; // 有効化(デフォルト: true)
186
+ color?: string; // 線色(デフォルト: 白)
187
+ width?: number; // 線幅(ピクセル、デフォルト: 2)
188
+ minimumHoverSize?: number; // 最小ホバーサイズ
189
+ outlinedPointAddition?: number;
190
+ }
191
+
192
+ // WHERE条件フィルター
193
+ type WhereCondition = NumericFilter | StringFilter | RawSqlFilter;
194
+
195
+ interface NumericFilter {
196
+ type: 'numeric';
197
+ column: string;
198
+ operator: '>=' | '>' | '<=' | '<';
199
+ value: number;
200
+ }
201
+
202
+ interface StringFilter {
203
+ type: 'string';
204
+ column: string;
205
+ operator: 'contains' | 'equals' | 'startsWith' | 'endsWith';
206
+ value: string;
207
+ }
208
+
209
+ interface RawSqlFilter {
210
+ type: 'raw';
211
+ sql: string;
212
+ }
213
+ ```
214
+
215
+ ## Examples
216
+
217
+ ### 実行方法
218
+
219
+ ```bash
220
+ # ライブラリのビルド(ルートディレクトリで)
221
+ npm install
222
+ npm run build
223
+
224
+ # サンプルアプリの実行
225
+ cd examples/next
226
+ npm install
227
+ npm run dev
228
+ ```
229
+
230
+ ブラウザで http://localhost:3000 を開きます。
231
+
232
+ ### サンプルデータ
233
+
234
+ サンプルでは、GloVe 6B単語ベクトルをUMAPで2次元に投影したデータを使用しています(約40万単語)。
235
+
236
+ * データセット: https://huggingface.co/datasets/mt0rm0/glove.6B.50d.umap.2d
237
+
238
+ ### ラベル生成
239
+
240
+ ラベルはDBSCANクラスタリングとOpenAI APIを使って生成できます:
241
+
242
+ ```bash
243
+ cd examples/next
244
+ export OPENAI_API_KEY="your-api-key"
245
+ python scripts/generate_labels.py
246
+ ```
247
+
248
+ ## ライセンス
249
+
250
+ このライブラリは [MIT License](./LICENSE) の下でライセンスされています。
@@ -0,0 +1,169 @@
1
+ import type { ParquetData } from './repository.js';
2
+ import type { WhereCondition, ScatterPlotError, PointId } from '../types.js';
3
+ import type { AllPointsData } from '../renderer/gpu-layer.js';
4
+ /**
5
+ * DataLayerの設定オプション
6
+ */
7
+ export interface DataLayerOptions {
8
+ /** ポイントサイズを計算するSQL式 */
9
+ sizeSql?: string;
10
+ /** ポイント色を計算するSQL式(ARGB形式) */
11
+ colorSql?: string;
12
+ /** データフィルタリング用のWHERE条件 */
13
+ whereConditions?: WhereCondition[];
14
+ /** GPUでフィルタリングするカラム名(最大4つ) */
15
+ gpuFilterColumns?: string[];
16
+ /** ポイントを識別するためのカラム名 */
17
+ idColumn: string;
18
+ /** エラーをScatterPlotに通知するためのコールバック */
19
+ onError?: (error: ScatterPlotError) => void;
20
+ /** データ変更時に呼び出されるコールバック */
21
+ onDataChanged?: () => void;
22
+ }
23
+ /**
24
+ * データ取得とクエリ管理を担当するレイヤー
25
+ * 責務:
26
+ * - ParquetReaderを介したParquetデータの読み込みと管理
27
+ * - 全データをGPU用フォーマットに変換
28
+ * - データ変更の検知と通知
29
+ */
30
+ export declare class DataLayer {
31
+ /** Parquetデータへのアクセスを提供するリポジトリ */
32
+ private repository;
33
+ /** ポイントサイズのSQL式 */
34
+ private sizeSql;
35
+ /** ポイント色のSQL式(ARGB形式: a=0.3, r=0.3, g=0.3, b=0.8) */
36
+ private colorSql;
37
+ /** フィルタリング用のWHERE条件 */
38
+ private whereConditions;
39
+ /** GPUでフィルタリングするカラム名 */
40
+ private gpuFilterColumns;
41
+ /** エラー通知用コールバック */
42
+ private onError?;
43
+ /** データ変更通知用コールバック */
44
+ private onDataChanged?;
45
+ /** 全ポイントデータのキャッシュ(ポイント検索用) */
46
+ private allPointsCache;
47
+ /** ポイント識別用のカラム名 */
48
+ private idColumn;
49
+ /**
50
+ * DataLayerインスタンスを作成する
51
+ * @param options 設定オプション
52
+ */
53
+ constructor(options: DataLayerOptions);
54
+ /**
55
+ * データレイヤーを初期化し、データを読み込む
56
+ * @param dataUrl Parquetファイルのurl
57
+ * @returns 処理済みの全データ
58
+ */
59
+ initialize(dataUrl: string): Promise<AllPointsData>;
60
+ /**
61
+ * GeoJSONラベルデータをDuckDBテーブルに読み込む
62
+ * @param geojson GeoJSON FeatureCollectionオブジェクト
63
+ */
64
+ loadLabelData(geojson: any): Promise<void>;
65
+ /**
66
+ * 単一の条件からWHERE句文字列を構築する
67
+ * @param condition WHERE条件
68
+ * @returns SQL WHERE句の文字列
69
+ */
70
+ private buildWhereClauseString;
71
+ /**
72
+ * 全データを読み込んでGPU用フォーマットに変換する
73
+ * @returns 処理済みの全データ
74
+ */
75
+ loadAllPoints(): Promise<AllPointsData>;
76
+ /**
77
+ * GPUフィルターカラムデータを読み込む
78
+ * @returns フィルターカラムデータとカラム数、カラムが指定されていない場合はnull
79
+ */
80
+ loadGpuFilterColumns(): Promise<{
81
+ data: Float32Array;
82
+ columnCount: number;
83
+ columnMapping: Map<string, number>;
84
+ } | null>;
85
+ /**
86
+ * カスタムSQLクエリを実行する
87
+ * @param query SQLクエリ
88
+ * @returns クエリ結果のParquetData
89
+ */
90
+ executeQuery(query: string | {
91
+ toString: () => string;
92
+ }): Promise<ParquetData | undefined>;
93
+ /**
94
+ * カラム形式のデータをGPU用インスタンスデータフォーマットに変換する
95
+ * フォーマット: ポイントごとに [x (f32), y (f32), color (u32), size (f32)]
96
+ * @param data ParquetData形式のデータ
97
+ * @returns 処理済みデータ
98
+ */
99
+ private processDataToGpuFormat;
100
+ /**
101
+ * 設定オプションを更新する
102
+ * @param options 更新する設定オプション
103
+ * @returns GPUフィルターカラムが変更された場合はtrue
104
+ */
105
+ updateOptions(options: Partial<DataLayerOptions>): {
106
+ gpuFilterColumnsChanged: boolean;
107
+ };
108
+ /**
109
+ * 行データからポイントの色を取得する
110
+ * @param row 行データ
111
+ * @param columns カラム名の配列
112
+ * @returns RGBAカラーオブジェクト
113
+ */
114
+ getPointColor(row: any[], columns: string[]): {
115
+ r: number;
116
+ g: number;
117
+ b: number;
118
+ a: number;
119
+ };
120
+ /**
121
+ * 行データからポイントのサイズを取得する
122
+ * @param row 行データ
123
+ * @param columns カラム名の配列
124
+ * @returns ポイントサイズ
125
+ */
126
+ getPointSize(row: any[], columns: string[]): number;
127
+ /**
128
+ * 画面座標に最も近いポイントを検索する
129
+ * @param screenX マウスのスクリーンX座標
130
+ * @param screenY マウスのスクリーンY座標
131
+ * @param canvasWidth キャンバスの幅(ピクセル)
132
+ * @param canvasHeight キャンバスの高さ(ピクセル)
133
+ * @param zoom 現在のズームレベル
134
+ * @param panX 現在のパンX
135
+ * @param panY 現在のパンY
136
+ * @param aspectRatio キャンバスのアスペクト比
137
+ * @param thresholdPixels ヒットと見なす最大距離(ピクセル、デフォルト: 10)
138
+ * @returns 見つかった場合はポイントデータ、そうでない場合はnull
139
+ */
140
+ findNearestPoint(screenX: number, screenY: number, canvasWidth: number, canvasHeight: number, zoom: number, panX: number, panY: number, aspectRatio: number, thresholdPixels?: number): Promise<{
141
+ row: any[];
142
+ columns: string[];
143
+ } | null>;
144
+ /**
145
+ * データレイヤーが初期化されているかチェックする
146
+ * @returns 初期化されていればtrue
147
+ */
148
+ isInitialized(): boolean;
149
+ /**
150
+ * IDでポイントを検索する
151
+ * @param pointId 検索するポイントのidColumn値
152
+ * @returns 見つかった場合はポイントデータ、そうでない場合はnull
153
+ */
154
+ findPointById(pointId: PointId): Promise<{
155
+ row: any[];
156
+ columns: string[];
157
+ } | null>;
158
+ /**
159
+ * ParquetDataから指定行のデータを配列として構築する
160
+ * @param data ParquetData
161
+ * @param rowIndex 行インデックス
162
+ * @returns 行データの配列
163
+ */
164
+ private buildRowFromData;
165
+ /**
166
+ * リソースをクリーンアップする
167
+ */
168
+ destroy(): Promise<void>;
169
+ }