splatone 0.0.16 → 0.0.18

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 CHANGED
@@ -10,9 +10,15 @@ SNSのジオタグ付きポストをキーワードに基づいて収集する
10
10
 
11
11
  - Bulky: クロールした全てのジオタグを小さな点で描画する
12
12
  - Marker Cluster: 密集しているジオタグをクラスタリングしてまとめて表示する
13
+ - Majority Hex: HexGridの各セルをセル内で最頻出するカテゴリの色で彩色
14
+ - Heat: ヒートマップ
13
15
 
14
16
  ## Change Log
15
17
 
18
+ ### v0.0.17 → v0.0.18
19
+
20
+ * **[可視化モジュール]** ```--vis-heat```追加
21
+
16
22
  ### v0.0.13 → v0.0.14 → v0.0.15 → v0.0.16 → v0.0.17
17
23
  * **[可視化モジュール]** ```--vis-majority-hex```追加
18
24
  * 結果の色固定機能追加 (キーワード指定方法を参照の事)
@@ -37,71 +43,6 @@ $ npx -y -p splatone@latest crawler --help
37
43
  [app] [plugin] loaded: flickr@1.0.0
38
44
  使い方: crawler.js [options]
39
45
 
40
- Basic Options
41
- -p, --plugin 実行するプラグイン[文字列] [必須] [選択してください: "flickr"]
42
- -k, --keywords 検索キーワード(|区切り) [文字列] [デフォルト:
43
- "nature,tree,flower|building,house|water,sea,river,pond"]
44
- -f, --filed 大きなデータをファイルとして送受信する
45
- [真偽] [デフォルト: true]
46
- -c, --chopped 大きなデータを細分化して送受信する
47
- [非推奨] [真偽] [デフォルト: false]
48
-
49
- Debug
50
- --debug-verbose デバッグ情報出力 [真偽] [デフォルト: false]
51
-
52
- For flickr Plugin
53
- --p-flickr-APIKEY Flickr ServiceのAPI KEY [文字列]
54
- --p-flickr-Extras カンマ区切り/保持する写真のメタデータ(デフォルト値は
55
- 記載の有無に関わらず保持)
56
- [文字列] [デフォルト: "date_upload,date_taken,owner_name,geo,url_s,tags"]
57
- --p-flickr-DateMode 利用時間軸(update=Flickr投稿日時/taken=写真撮影日時)
58
- [選択してください: "upload", "taken"] [デフォルト: "upload"]
59
- --p-flickr-Haste 時間軸分割並列処理 [真偽] [デフォルト: true]
60
- --p-flickr-DateMax クローリング期間(最大) UNIX TIMEもしくはYYYY-MM-DD
61
- [文字列] [デフォルト: 1763107393]
62
- --p-flickr-DateMin クローリング期間(最小) UNIX TIMEもしくはYYYY-MM-DD
63
- [文字列] [デフォルト: 1072882800]
64
-
65
- Visualization (最低一つの指定が必須です)
66
- --vis-bulky 全データをCircleMarkerとして地図上に表示
67
- [真偽] [デフォルト: false]
68
- --vis-majority-hex HexGrid内で最も出現頻度が高いカテゴリの色で彩色。Hex
69
- apartiteモードで6分割パイチャート表示。透明度は全体
70
- で正規化。 [真偽] [デフォルト: false]
71
- --vis-marker-cluster マーカークラスターとして地図上に表示
72
- [真偽] [デフォルト: false]
73
-
74
- For bulky Visualizer
75
- --v-bulky-Radius Point Markerの半径 [数値] [デフォルト: 5]
76
- --v-bulky-Stroke Point Markerの線の有無 [真偽] [デフォルト: true]
77
- --v-bulky-Weight Point Markerの線の太さ [数値] [デフォルト: 1]
78
- --v-bulky-Opacity Point Markerの線の透明度 [数値] [デフォルト: 1]
79
- --v-bulky-Filling Point Markerの塗りの有無 [真偽] [デフォルト: true]
80
- --v-bulky-FillOpacity Point Markerの塗りの透明度 [数値] [デフォルト: 0.5]
81
-
82
- For majority-hex Visualizer
83
- --v-majority-hex-Hexapartite 中のカテゴリの頻度に応じて六角形を分割色彩
84
- [真偽] [デフォルト: false]
85
- --v-majority-hex-HexOpacity 六角形の線の透明度 [数値] [デフォルト: 1]
86
- --v-majority-hex-HexWeight 六角形の線の太さ [数値] [デフォルト: 1]
87
- --v-majority-hex-MaxOpacity 正規化後の最大塗り透明度
88
- [数値] [デフォルト: 0.9]
89
- --v-majority-hex-MinOpacity 正規化後の最小塗り透明度
90
- [数値] [デフォルト: 0.5]
91
-
92
- For marker-cluster Visualizer
93
- --v-marker-cluster-MaxClusterRadius クラスタを構成する範囲(半径)
94
- [数値] [デフォルト: 80]
95
-
96
- オプション:
97
- --help ヘルプを表示 [真偽]
98
- --version バージョンを表示 [真偽]
99
-
100
- cold_@bimota-due MINGW64 /c/GitHub/Splatone (61-可視化メソッドmajorityhex)
101
- $ npx -y -p crawler@latest --help
102
- [app] [plugin] loaded: flickr@1.0.0
103
- 使い方: crawler.js [options]
104
-
105
46
  Basic Options
106
47
  -p, --plugin 実行するプラグイン[文字列] [必須] [選択してください: "flickr"]
107
48
  -k, --keywords 検索キーワード(|区切り) [文字列] [デフォルト:
@@ -123,13 +64,15 @@ For flickr Plugin
123
64
  [選択してください: "upload", "taken"] [デフォルト: "upload"]
124
65
  --p-flickr-Haste 時間軸分割並列処理 [真偽] [デフォルト: true]
125
66
  --p-flickr-DateMax クローリング期間(最大) UNIX TIMEもしくはYYYY-MM-DD
126
- [文字列] [デフォルト: 1763107399]
67
+ [文字列] [デフォルト: 1763224757]
127
68
  --p-flickr-DateMin クローリング期間(最小) UNIX TIMEもしくはYYYY-MM-DD
128
69
  [文字列] [デフォルト: 1072882800]
129
70
 
130
71
  Visualization (最低一つの指定が必須です)
131
72
  --vis-bulky 全データをCircleMarkerとして地図上に表示
132
73
  [真偽] [デフォルト: false]
74
+ --vis-heat カテゴリ毎に異なるレイヤのヒートマップで可視化(色=
75
+ カテゴリ色、透明度=頻度) [真偽] [デフォルト: false]
133
76
  --vis-majority-hex HexGrid内で最も出現頻度が高いカテゴリの色で彩色。Hex
134
77
  apartiteモードで6分割パイチャート表示。透明度は全体
135
78
  で正規化。 [真偽] [デフォルト: false]
@@ -144,6 +87,13 @@ For bulky Visualizer
144
87
  --v-bulky-Filling Point Markerの塗りの有無 [真偽] [デフォルト: true]
145
88
  --v-bulky-FillOpacity Point Markerの塗りの透明度 [数値] [デフォルト: 0.5]
146
89
 
90
+ For heat Visualizer
91
+ --v-heat-Radius ヒートマップブラーの半径 [数値] [デフォルト: 0.0005]
92
+ --v-heat-MinOpacity ヒートマップの最小透明度 [数値] [デフォルト: 0]
93
+ --v-heat-MaxOpacity ヒートマップの最大透明度 [数値] [デフォルト: 1]
94
+ --v-heat-MaxValue ヒートマップ強度の最大値
95
+ (未指定時はデータから自動推定) [数値]
96
+
147
97
  For majority-hex Visualizer
148
98
  --v-majority-hex-Hexapartite 中のカテゴリの頻度に応じて六角形を分割色彩
149
99
  [真偽] [デフォルト: false]
@@ -162,7 +112,6 @@ For marker-cluster Visualizer
162
112
  --help ヘルプを表示 [真偽]
163
113
  --version バージョンを表示 [真偽]
164
114
  ```
165
-
166
115
  ## 最小コマンド例
167
116
 
168
117
  1. *plugin*を一つ、*visualizer*を一つ以上指定し、複数のキーワードでクロールを開始します。
@@ -176,7 +125,7 @@ For marker-cluster Visualizer
176
125
  ![](assets/screenshot_venice_simple.png?raw=true)
177
126
 
178
127
  ```bash
179
- $ npx -y -- splatone@latest crawler -p flickr -k "canal,river|street,alley|bridge" --vis-bulky --p-flickr-APIKEY="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
128
+ $ npx -y -p splatone@latest crawler -p flickr -k "canal,river|street,alley|bridge" --vis-bulky --p-flickr-APIKEY="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
180
129
  ```
181
130
 
182
131
  # 詳細説明
@@ -221,7 +170,7 @@ APIキーは以下の3種類の方法で与える事ができます
221
170
  #### コマンド例
222
171
  * クエリは海と山のキーワード検索。上記スクリーンショットは日本のデータ
223
172
  ```shell
224
- $ npx -y -p splatone@latest crawler -p flickr -k "sea,ocean|mountain,mount" --vis-bulky--p-flickr-APIKEY="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
173
+ $ npx -y -p splatone@latest crawler -p flickr -k "sea,ocean|mountain,mount" --vis-bulky--p-flickr-APIKEY="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
225
174
  ```
226
175
 
227
176
  #### コマンドライン引数
@@ -244,13 +193,33 @@ $ npx -y -p splatone@latest crawler -p flickr -k "sea,ocean|mountain,mount" --v
244
193
  ```shell
245
194
  $ npx -y -p splatone@latest crawler -p flickr -k "水域=canal,channel,waterway,river,stream,watercourse,sea,ocean,gulf,bay,strait,lagoon,offshore|橋梁=bridge,overpass,flyover,aqueduct,trestle|通路=street,road,thoroughfare,roadway,avenue,boulevard,lane,alley,roadway,carriageway,highway,motorway|ランドマーク=church,sanctuary,chapel,cathedral,basilica,minster,abbey" --vis-marker-cluster --vis-bulky --p-flickr-APIKEY="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
246
195
  ```
247
-
248
196
  #### コマンドライン引数
249
197
 
250
198
  | オプション | 説明 | 型 | デフォルト |
251
199
  | :---------------------------------------- | :--------------------------- | :--- | :--------- |
252
200
  | ```--v-marker-cluster-MaxClusterRadius``` | クラスタを構成する範囲(半径) | 数値 | 80 |
253
201
 
202
+ ### Heat: ヒートマップ
203
+
204
+ ![](assets/screenshot_venice_heat.png?raw=true)
205
+
206
+ #### コマンド例
207
+
208
+ * クエリは水域・緑地・交通・ランドマークを色分けしたもの。上記スクリーンショットはフロリダ半島全体
209
+
210
+ ```shell
211
+ $ npx -y -p splatone@latest crawler -p flickr -k "canal,river|street,alley|bridge" --vis-heat --p-flickr-APIKEY="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
212
+ ```
213
+
214
+ #### コマンドライン引数
215
+
216
+ | オプション | 説明 | 型 | デフォルト |
217
+ | :------------------------ | :----------------------------------------------------- | :--- | :--------- |
218
+ | ```--v-heat-Radius``` | ヒートマップブラーの半径 | 数値 | 0.0005 |
219
+ | ```--v-heat-MinOpacity``` | ヒートマップの最小透明度 | 数値 | 0 |
220
+ | ```--v-heat-MaxOpacity``` | ヒートマップの最大透明度 | 数値 | 1 |
221
+ | ```--v-heat-MaxValue``` | ヒートマップ強度の最大値(未指定時はデータから自動推定) | 数値 | |
222
+
254
223
  ### Majority Hex: Hexグリッド内の出現頻度に応じた彩色
255
224
 
256
225
  ![](assets/screenshot_florida_hex_majorityr.png?raw=true)
@@ -259,7 +228,7 @@ $ npx -y -p splatone@latest crawler -p flickr -k "水域=canal,channel,waterway,
259
228
  * クエリは水域・緑地・交通・ランドマークを色分けしたもの。上記スクリーンショットはフロリダ半島全体
260
229
  *
261
230
  ```shell
262
- $ npx -y -p splatone@latest crawler -p flickr -k "水域=canal,channel,waterway,river,stream,watercourse,sea,ocean,gulf,bay,strait,lagoon,offshore|緑地=forest,woods,turf,lawn,jungle,trees,rainforest,grove,savanna,steppe|交通=bridge,overpass,flyover,aqueduct,trestle,street,road,thoroughfare,roadway,avenue,boulevard,lane,alley,roadway,carriageway,highway,motorway|ランドマーク=church,chapel,cathedral,basilica,minster,temple,shrine,neon,theater,statue,museum,sculpture,zoo,aquarium,observatory" --vis-majority-hex --v-majority-hex-Hexapartite --p-flickr-APIKEY="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
231
+ $ npx -y -p splatone@latest crawler -p flickr -k "水域=canal,channel,waterway,river,stream,watercourse,sea,ocean,gulf,bay,strait,lagoon,offshore|緑地=forest,woods,turf,lawn,jungle,trees,rainforest,grove,savanna,steppe|交通=bridge,overpass,flyover,aqueduct,trestle,street,road,thoroughfare,roadway,avenue,boulevard,lane,alley,roadway,carriageway,highway,motorway|ランドマーク=church,chapel,cathedral,basilica,minster,temple,shrine,neon,theater,statue,museum,sculpture,zoo,aquarium,observatory" --vis-majority-hex --v-majority-hex-Hexapartite --p-flickr-APIKEY="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
263
232
  ```
264
233
 
265
234
  #### コマンドライン引数
@@ -321,17 +290,17 @@ seaだけでは集められるポストが限定されるので、同様の意
321
290
  - 使い方(6色のカラーパレットを2セット作りたい):
322
291
 
323
292
  ```bash
324
- npx -y -psplatone@latest colors <count> <sets>
293
+ npx -y -p splatone@latest color <count> <sets>
325
294
  # 例: 6色を3セット生成(ターミナルに色付きで表示)
326
- npx -y -p splatone@latest colors 6 3
327
- ```
295
+ npx -y -p splatone@latest color 6 3
296
+ ```
328
297
 
329
298
  - オプション:
330
299
 
331
300
  - `--no-ansi` : ANSI カラーシーケンスを出力せず、プレーンなカンマ区切りの HEX を出力します(パイプやログ向け)。
332
301
 
333
302
  ```bash
334
- npx -y -p splatone@latest colors --no-ansi 6 3
303
+ npx -y -p splatone@latest color --no-ansi 6 3
335
304
  ```
336
305
 
337
306
  ## ダウンロード
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "splatone",
3
- "version": "0.0.16",
3
+ "version": "0.0.18",
4
4
  "description": "Multi-layer Composite Heatmap",
5
5
  "homepage": "https://github.com/YokoyamaLab/Splatone#readme",
6
6
  "bugs": {
@@ -0,0 +1,82 @@
1
+ import path from 'node:path';
2
+ import { fileURLToPath } from 'node:url';
3
+ import { VisualizerBase } from '../../lib/VisualizerBase.js';
4
+ import { featureCollection } from "@turf/turf";
5
+
6
+ export default class HeatVisualizer extends VisualizerBase {
7
+ static name = 'Heat Visualizer';
8
+ static version = '0.0.0';
9
+ static description = "カテゴリ毎に異なるレイヤのヒートマップで可視化(色=カテゴリ色、透明度=頻度)";
10
+
11
+ constructor() {
12
+ super();
13
+ this.id = path.basename(path.dirname(fileURLToPath(import.meta.url)));
14
+ }
15
+
16
+ async yargv(yargv) {
17
+ return yargv.option(this.argKey('Radius'), {
18
+ group: 'For ' + this.id + ' Visualizer',
19
+ type: 'number',
20
+ description: 'ヒートマップブラーの半径',
21
+ default: 0.0005
22
+ }).option(this.argKey('MinOpacity'), {
23
+ group: 'For ' + this.id + ' Visualizer',
24
+ type: 'number',
25
+ description: 'ヒートマップの最小透明度',
26
+ default: 0
27
+ }).option(this.argKey('MaxOpacity'), {
28
+ group: 'For ' + this.id + ' Visualizer',
29
+ type: 'number',
30
+ description: 'ヒートマップの最大透明度',
31
+ default: 1
32
+ }).option(this.argKey('MaxValue'), {
33
+ group: 'For ' + this.id + ' Visualizer',
34
+ type: 'number',
35
+ description: 'ヒートマップ強度の最大値 (未指定時はデータから自動推定)'
36
+ });
37
+ }
38
+
39
+ getFutureCollection(result, target, visOptions) {
40
+ // result: { hexId: { category: { items, ids, final, crawled, total }, ... }, ... }
41
+ // target: { hex, triangles, categories, splatonePalette }
42
+ // Build category-based heatmap layers using individual data points (not hex centroids)
43
+
44
+ const categories = {};
45
+
46
+ // Iterate through all hexes and categories, collecting individual point features
47
+ for (const hexId in result) {
48
+ const hexData = result[hexId];
49
+ for (const cat in hexData) {
50
+ if (!categories[cat]) {
51
+ categories[cat] = [];
52
+ }
53
+
54
+ // Get the actual items (point features) for this category in this hex
55
+ const items = hexData[cat].items;
56
+ if (!items || !items.features || items.features.length === 0) continue;
57
+
58
+ // Add each individual point feature to the category collection
59
+ for (const feature of items.features) {
60
+ const density = hexData[cat]?.total ?? items.features.length ?? 1;
61
+ // Clone the feature and add category property
62
+ const pointFeature = {
63
+ type: 'Feature',
64
+ geometry: feature.geometry,
65
+ properties: {
66
+ ...feature.properties,
67
+ category: cat,
68
+ hexId: hexId,
69
+ hexDensity: density
70
+ }
71
+ };
72
+ categories[cat].push(pointFeature);
73
+ }
74
+ }
75
+ }
76
+
77
+ // Return FeatureCollections per category
78
+ return Object.fromEntries(
79
+ Object.entries(categories).map(([k, v]) => [k, featureCollection(v)])
80
+ );
81
+ }
82
+ }
@@ -0,0 +1,132 @@
1
+ // Heat visualizer dependencies
2
+ const dependencies = [
3
+ { type: 'script', src: 'https://cdn.jsdelivr.net/npm/heatmap.js@2.0.5/build/heatmap.min.js' },
4
+ { type: 'script', src: 'https://raw.githack.com/pa7/heatmap.js/develop/plugins/leaflet-heatmap/leaflet-heatmap.js' }
5
+ ];
6
+
7
+ // Load external dependencies dynamically
8
+ async function loadDependencies() {
9
+ for (const dep of dependencies) {
10
+ if (dep.type === 'script') {
11
+ // Check if already loaded
12
+ const existing = document.querySelector(`script[src="${dep.src}"]`);
13
+ if (existing) continue;
14
+
15
+ await new Promise((resolve, reject) => {
16
+ const script = document.createElement('script');
17
+ script.src = dep.src;
18
+ script.onload = resolve;
19
+ script.onerror = reject;
20
+ document.head.appendChild(script);
21
+ });
22
+ } else if (dep.type === 'link') {
23
+ // Check if already loaded
24
+ const existing = document.querySelector(`link[href="${dep.src}"]`);
25
+ if (existing) continue;
26
+
27
+ const link = document.createElement('link');
28
+ link.rel = 'stylesheet';
29
+ link.href = dep.src;
30
+ document.head.appendChild(link);
31
+ }
32
+ }
33
+ }
34
+
35
+ let booted = false;
36
+ export default async function main(map, geojson, options = {}) {
37
+ console.log("[VIS OPTIONS]", options.visOptions);
38
+ if (booted) return;
39
+ booted = true;
40
+
41
+ // Load dependencies first
42
+ await loadDependencies();
43
+
44
+ const layers = {};
45
+ const visOpts = options.visOptions || {};
46
+
47
+ // Extract category colors from palette (if available)
48
+ const palette = options.palette || {};
49
+
50
+ for (const cat in geojson) {
51
+ const features = geojson[cat].features || [];
52
+ if (features.length === 0) continue;
53
+
54
+ // Convert features to heatmap data format: { lat, lng, value }
55
+ const heatmapData = features.map(f => {
56
+ const coords = f.geometry.coordinates; // [lon, lat]
57
+ const rawValue = Number(f.properties?.weight ?? f.properties?.count ?? 1);
58
+ const value = Number.isFinite(rawValue) && rawValue > 0 ? rawValue : 1;
59
+ return {
60
+ lat: coords[1],
61
+ lng: coords[0],
62
+ value
63
+ };
64
+ });
65
+
66
+ // Estimate density-based max using originating hex totals as hints
67
+ const densityHints = features
68
+ .map(f => Number(f.properties?.hexDensity))
69
+ .filter(v => Number.isFinite(v) && v > 0);
70
+
71
+ const autoMax = densityHints.length > 0
72
+ ? Math.max(...densityHints)
73
+ : Math.max(features.length, 1);
74
+
75
+ const configuredMax = Number(visOpts.MaxValue);
76
+ const maxValue = Number.isFinite(configuredMax) && configuredMax > 0
77
+ ? configuredMax
78
+ : autoMax;
79
+
80
+ // Get category color from palette (fallback to blue)
81
+ const categoryColor = palette[cat]?.color || '#3388ff';
82
+
83
+ // Create gradient using category color
84
+ // Convert hex to RGB for gradient stops
85
+ const hexToRgb = (hex) => {
86
+ const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
87
+ return result ? {
88
+ r: parseInt(result[1], 16),
89
+ g: parseInt(result[2], 16),
90
+ b: parseInt(result[3], 16)
91
+ } : { r: 51, g: 136, b: 255 };
92
+ };
93
+
94
+ const rgb = hexToRgb(categoryColor);
95
+ const gradient = {
96
+ 0.0: `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.0)`,
97
+ 0.2: `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.1)`,
98
+ 0.4: `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.4)`,
99
+ 0.6: `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.7)`,
100
+ 0.8: `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.9)`,
101
+ 1.0: `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 1.0)`
102
+ };
103
+
104
+ // Create heatmap layer configuration
105
+ const cfg = {
106
+ radius: visOpts.Radius || 25,
107
+ maxOpacity: visOpts.MaxOpacity || 0.8,
108
+ minOpacity: visOpts.MinOpacity || 0.1,
109
+ scaleRadius: true,
110
+ useLocalExtrema: false,
111
+ latField: 'lat',
112
+ lngField: 'lng',
113
+ valueField: 'value',
114
+ gradient: gradient
115
+ };
116
+
117
+ // Create HeatmapOverlay instance
118
+ const heatmapLayer = new HeatmapOverlay(cfg);
119
+
120
+ // Set data
121
+ heatmapLayer.setData({
122
+ max: maxValue,
123
+ data: heatmapData
124
+ });
125
+
126
+ // Add to map and store reference
127
+ heatmapLayer.addTo(map);
128
+ layers[cat] = heatmapLayer;
129
+ }
130
+
131
+ return layers;
132
+ }