Skip to content
niryuu edited this page May 25, 2026 · 1 revision

怪しい / wrong に見えるロジック カタログ

このファイルの前提:

ここに挙げる項目は仕様の正解が不明、現に動いているものを正と看做す。

長年積み重なった embed コードベースには、なぜそうなっているのか説明しにくい分岐・ハードコード・非対称な条件がある。書き直しの際にこれらを「現状維持」「修正」「再設計」のいずれに振るか判断するための材料として、各項目に 場所・現状・不明点・リスク を記録する。

1. dev stage の hostname 置換が tileserver のみ

  • 場所: geolonia-map.ts:156-158
  • 現状:
    if (atts.stage === 'dev' && transformedUrlObj.hostname === 'tileserver.geolonia.com') {
      transformedUrlObj.hostname = 'tileserver-dev.geolonia.com';
    }
    dev stage のとき tileserver.geolonia.com のみ tileserver-dev.geolonia.com に置換。
  • 不明点:
    • *.tiles.geolonia.com (osm.v3 等) は dev でも置換されない。サーバ側に対応する dev サブドメインがあるのか? 無いなら dev 用の OSM タイルは無い?
    • sprite URL は別ロジック (geolonia-map.ts:171pathParts[1] 置換) で対称性が無い
  • リスク: dev stage で osm.v3.tiles.geolonia.com を使うとプロダクション同じホストに行く → 認証失敗 or 古いデータ
  • 対応案: サーバ側仕様を確認のうえ全 Geolonia ホストで dev 置換を統一するか、現状の特殊扱いを明文化する

2. isGeoloniaTilesHost の whitelist と新ホスト追加

  • 場所: util.ts:38-50
  • 現状:
    return (
      urlObj.hostname === 'tileserver.geolonia.com' ||
      urlObj.hostname.endsWith('.tiles.geolonia.com')
    );
    コメント: // NOTE: geolonia use tileserver.geolonia.com or *.tiles.geolonia.com for tile server. Other domains is NOT allowed.
  • 不明点:
    • 新タイルサーバ追加時の運用 (例: tileserver-v2.geolonia.com, *.maps.geolonia.com 等の予定)
    • cdn.geolonia.com (スタイル) はここに含まれない (transformRequest 対象でないため。スタイル JSON は public、認証不要)
  • リスク: 新ホスト追加時にこの関数を修正し忘れると認証注入されず実 fetch が失敗
  • 対応案: 環境変数 / ビルド時定数 / サーバ側 manifest から動的取得 (大袈裟か検討)

3. iframe 許可特例リスト (Codepen / JSFiddle / CodeSandbox)

  • 場所: util.ts:67-105
  • 現状: 3 サイトに対し referrer prefix を hardcode。新サイト (StackBlitz, JS Bin, etc.) は許可されない
  • 不明点:
    • 過去にどういう経緯でこの 3 サイトが追加されたか (issue 履歴に残っているはず)
    • 将来追加可能性のあるサイト
  • リスク: メンテナンス負担。新サイトユーザに「動かない」と問い合わせ来る
  • 対応案: ホワイトリストを設定ファイル化 or サーバ側で管理してロード時に取得

4. keyring.isGeoloniaStyleCheck の 5-way 分岐

  • 場所: keyring.ts:102-131
  • 現状: 空文字 → URL prefix 2 種 → 一般 URL → .json 終端 → 論理名 の順で判定
  • 不明点:
    • 境界条件: './foo.json' で location.host が geolonia.com を含む場合と含まない場合で結果が変わる
    • 'foo' (拡張子なし、URL でない) は論理名扱い → 任意の文字列が Geolonia 判定 → API key 必須化
    • 'geolonia.com/foo' (スキームなし) も URL 扱いされず論理名扱い
  • リスク:
    • 誤って Geolonia 扱いされた外部スタイルで API key 強制 → ユーザが key なくて使えない
    • 誤って外部扱いされた Geolonia スタイルで認証注入されず 401 / 403
  • 対応案: 仕様を整理して decision table を文書化 (Data-Attributes の "data-style" 参照)、エッジケースのテスト追加

5. getStyleisGeoloniaStyleCheck の独立性

  • 場所: util.ts:217-273 (getStyle) vs keyring.ts:102-131 (isGeoloniaStyleCheck)
  • 現状: 両者で別々のロジックで「style 文字列を解釈」している
    • getStyle: URL に変換 (空 / 絶対 URL / 相対 / .json / 論理名 の 5 分岐)
    • isGeoloniaStyleCheck: Geolonia 判定 (5 分岐、上記)
  • 不明点: 両者の分岐が完全に同期しているか不明 — 例えば「論理名 'foo'」を getStyle は https://cdn.geolonia.com/style/foo/{lang}.json に解決し、isGeoloniaStyleCheck は Geolonia 扱い (true)。一致するが微妙な境界 (例: relative URL かつ host に geolonia.com 含まない) でズレる可能性
  • リスク: ロジック齟齬で UNDEFINED 挙動
  • 対応案: 1 つの decision table から両方を生成 (共通関数 categorizeStyle(style) → 'default' | 'absolute-url' | 'relative-json' | 'logical-name')

6. IntersectionObserver の once-only

  • 場所: render.ts:67-80
  • 現状: 初回 isIntersecting=true で renderSingleMap + unobserve。再度 viewport に入ってもマップ再生成しない
  • 不明点: 意図的か (lazy-load の本来の意味: 一度ロードしたら保持) バグか (一度離れたら map を破棄して再表示で再構築すべき) は仕様 unclear
  • リスク: 長いページで多数の .geolonia がある場合、メモリ使用量が累積
  • 対応案: 現状維持で問題ない場合が多い。明示的に再表示で再構築したいなら observer.observe を再度呼ぶ運用に

7. MutationObserver は target.parentNode を observe

  • 場所: render.ts:37-45
  • 現状: observer.observe(target.parentNode, { childList: true })。target 自身ではなく親要素を監視。removedNodes に target が含まれたら map.remove()
  • 不明点:
    • target の親自身が削除された場合は検知できない (親より上の祖先削除を見ない)
    • target が他の親に moveTo された場合の挙動 (MutationObserver は新親では fire しない)
  • リスク: 親要素ごと消える典型ケース (例: React のコンポーネントツリー unmount) で map が remove されずメモリリーク
  • 対応案: MutationObserver の代わりに target.isConnected を定期的に check するか、React 等で明示的に map.remove() を呼ぶ運用に倒す

8. プラグインの DOMContentLoaded 待ち + 早期 render 時の二重実行リスク

  • 場所: render.ts:47-65
  • 現状:
    • DOMContentLoaded 前: alreadyRenderedMaps にキュー
    • DOMContentLoaded ハンドラで一括実行
    • 各 plugin に同じ atts を渡す (renderSingleMap 時点のスナップショット)
  • 不明点:
    • プラグインが内部で非同期処理を持つ場合、map.on('load') の前後で何が保証されているか
    • 同じ plugin が registerPlugin で複数回登録された場合の意図 (現状は重複実行)
  • リスク: プラグインが marker 等を追加する場合、map.on('load') を待たずに addLayer を呼ぶと失敗
  • 対応案: プラグインに「load 後に呼ばれる」契約を明示。map.on('load') 内で plugin を呼ぶ設計に変える案も

9. SimpleStyleVectorremove() 欠落

  • 場所: simplestyle-vector.ts 全体
  • 現状: addTo 内で map.on('sourcedata', ...) で listener を永続登録するが、remove() メソッドが存在しない
  • 不明点: 単純な実装漏れか、意図的か (map.remove() で十分?)
  • リスク: 同一マップで SimpleStyleVector を再 addTo すると listener 二重登録、メモリリーク
  • 対応案: GeoJSON 版と同様 _eventHandlers 配列で管理し remove() を追加

10. loader spinner 削除の try-catch

  • 場所: geolonia-map.ts:242-247
  • 現状:
    if (atts.loader !== 'off') {
      try {
        container.removeChild(loading);
      } catch {
        // Nothing to do.
      }
    }
  • 不明点: loading は同じ constructor で container.appendChild(loading) したばかり。なぜ削除が失敗しうる?
    • 可能性 1: ユーザコードが loader を消した
    • 可能性 2: super(options) が container.innerHTML を上書きした (まさかとは思うが)
    • 可能性 3: 過去のバグの安全弁が残っている
  • リスク: 現状動いているが、根本原因不明のまま try-catch でスタックが食われる
  • 対応案: 削除失敗の根本原因を調査、不要なら try-catch を外す

11. Number(atts.minZoom) === 0 || Number(atts.minZoom)

  • 場所: util.ts:299-303
  • 現状:
    if (atts.minZoom !== '' && (Number(atts.minZoom) === 0 || Number(atts.minZoom))) {
      options.minZoom = Number(atts.minZoom);
    }
  • 不明点: なぜ === 0 分岐が必要?
    • Number('')0 なので、!== '' ガードで弾く必要
    • Number('0') === 0Number('foo') === NaNNumber('5') === 5
    • Number(atts.minZoom) だけだと 0 が falsy で弾かれてしまう ('0' 文字列が許容されない)
    • つまりこの冗長条件は「'0' を許容しつつ '' と NaN を弾く」意図
  • リスク: なし (現状動作する)
  • 対応案: コメントを追加するか、Number.isFinite(Number(atts.minZoom)) の方が明確

12. sprite パスの pathParts[1] 直接操作

  • 場所: geolonia-map.ts:164-177
  • 現状:
    if (
      (resourceType === 'SpriteJSON' || resourceType === 'SpriteImage') &&
      transformedUrl.match(/^https:\/\/api\.geolonia\.com\/(dev|v1)\/sprites\//)
    ) {
      const pathParts = transformedUrlObj.pathname.split('/');
      pathParts[1] = String(atts.stage);
      transformedUrlObj.pathname = pathParts.join('/');
      // ...
    }
  • 不明点:
    • pathname 構造 /dev/sprites/... または /v1/sprites/... に強く依存
    • pathParts[0] = '' (先頭スラッシュの前)、pathParts[1] = stage、pathParts[2] = 'sprites'、以下続く
    • サーバ側の URL 仕様が変わると壊れる
  • リスク: サーバ側で /v2/sprites/... 構造が変わると embed が動かなくなる
  • 対応案: URL の置換ではなくテンプレート化 (https://api.geolonia.com/${stage}/sprites/${originalPathParts.slice(2).join('/')} 等)

13. getSessionId のグローバルキャッシュ

  • 場所: util.ts:337-356
  • 現状: モジュール変数 sessionId にキャッシュ。1 度生成したら page life で固定
  • 不明点: 複数 GeoloniaMap インスタンス間で session を共有する意図 (アクセス集計の都合?)
  • リスク:
    • Web Worker / 別 iframe で別 module instance になる → 別 session ID
    • sessionStorage / localStorage に書かないので reload で別 ID (これは意図通り?)
  • 対応案: 現状で問題ない。複数マップで同じ session 集計したいなら仕様として明文化

14. getOptionsparams === container 比較

  • 場所: util.ts:282-284
  • 現状:
    if (params === container) {
      params = {};
    }
  • 不明点: paramscontainer が同一の HTMLElement になるケース?
    • new GeoloniaMap(htmlElement) のときに上層で parseAtts(container, { interactive }) を呼んでから getOptions(container, params, atts) に進むが、params には { interactive: true/false } が入っているはず (geolonia-map.ts:97-100)
    • 直接 new GeoloniaMap(htmlElement) で呼ばれて params が HTMLElement になる経路はない
    • もしかすると古い API で getOptions(container, htmlElement, atts) のように呼んでいた名残?
  • リスク: なし (defensive code)
  • 対応案: 呼び出し経路を整理して不要なら削除

15. デフォルト色の重複定義

  • 場所: parse-atts.ts:41 ('#E4402F'), geolonia-marker.ts:40 ('#E4402F')、parse-atts.ts:54 ('#ff0000'), simplestyle.ts:33 ('#ff0000')
  • 現状: 同じ literal が 2 箇所で独立に定義
  • リスク: 一方を変更したら他方が追従しない → デザインが分裂する
  • 対応案: constants.ts のような共通モジュールに移す

16. MAP_PLATFORM_STAGE の build-time 固定

  • 場所: webpack.config.js:7, keyring.ts:73
  • 現状: webpack DefinePlugin で process.env.MAP_PLATFORM_STAGE を JS リテラルに静的置換
  • 不明点: ランタイム可変にする意図がなかった理由 (セキュリティ?最適化?)
  • リスク: 同じバンドルを dev/v1 で使い回せない → 2 種類ビルドする必要
  • 対応案: embed-core 化で lib として複数環境で使うなら、ランタイム設定可能化を検討

17. SimpleStyle heatmap TODO

  • 場所: simplestyle.ts:32
  • 現状: heatmap: false, // TODO: It should support heatmap.
  • 対応案: 実装するか TODO を削除するか判断。実装しないなら option 自体を削除推奨

18. SimpleStyle レイヤ ID 固定 prefix で複数インスタンス衝突

  • 場所: simplestyle.ts:30 (id: 'geolonia-simple-style')
  • 現状: options.id デフォルトが固定 prefix。同じマップに 2 つの SimpleStyle を addTo すると getLayer 等で衝突
  • 回避策: コンストラクタ第 2 引数で { id: 'mydata-1' } を渡す → 別 prefix になる
  • 対応案: ドキュメントに明記、または自動 UUID 付与で衝突回避

19. 3D toggle の全レイヤ走査

  • 場所: geolonia-map.ts:327-356
  • 現状: styledata イベントで style.layers.forEach((layer) => {...}) で全レイヤを走査し metadata チェック
  • 不明点: zoom / pan / setData 等でも styledata が fire するため、頻繁に走査が走る可能性
  • リスク: 大規模スタイル (数百レイヤ) でパフォーマンス影響
  • 対応案: __styleExtensionLoadRequired フラグで 1 度限りに guard されているため現状は深刻でない (geolonia-map.ts:294-297)。setStyle 後の再走査はあるが頻度低い

20. setStyle 内の super.setStyle.call(this, ...)

  • 場所: geolonia-map.ts:395
  • 現状: super.setStyle(style, options) ではなく super.setStyle.call(this, style, options) の形で呼ぶ
  • コメント (geolonia-map.ts:381-383):

    It can't access this because setStyle() will be called with super(). So, we need to run parseAtts() again(?)

  • 不明点: maplibre の constructor 内で setStyle が呼ばれる際、this の bind が壊れるのか? .call(this, ...) で本当に解決されるのか?
  • リスク: maplibre のバージョンアップで内部実装が変わると壊れる
  • 対応案: maplibre の constructor 挙動を追って、本当に必要か検証

21. keyring.parse(container) の container 引数

  • 場所: parse-atts.ts:27
  • 現状: keyring.parse(container) を呼ぶが、keyring.parse(doc: Document = document) のシグネチャに HTMLElement を渡している
  • 不明点: 過去に意味があったか、最初から no-op だったか
  • リスク: 型エラー警告は出ない (ts loose check)、実害なし
  • 対応案: container 引数を削除して keyring.parse() のみに

22. プラグイン atts スナップショット

  • 場所: render.ts:48
  • 現状: parseAtts(target) の結果を atts としてキューに保持、DOMContentLoaded 後の遅延実行でも同じ atts を渡す
  • 不明点: data-* が動的に変更されたケースで何を期待するか
  • 対応案: ドキュメントで「プラグインは初期化時の atts を受け取る」と明文化、動的変更を見たければ target.dataset を直接読む運用

23. GeoloniaControlenabled を見ない

  • 場所: geolonia-map.ts:202-205
  • 現状: parseControlOption(atts.geoloniaControl) の戻り値のうち position のみ参照、enabled を無視
  • 不明点: 「Geolonia ブランディングは消せない」という仕様か、enabled チェック忘れか
  • リスク: data-geolonia-control="off" を指定したユーザの期待を裏切る
  • 対応案: 仕様確認 → 仕様通りなら docs に明記、忘れなら enabled チェック追加

24. parseSimpleVectorusername キャプチャ捨て

  • 場所: geolonia-map.ts:140-145
  • 現状: regex ^geolonia:\/\/tiles\/(?<username>.+)\/(?<customtileId>.+) の username キャプチャは使わず捨てる
  • 不明点: 過去 username で識別する設計だったが、現状 customtileId だけで十分という変遷
  • リスク: なし (使われない情報)
  • 対応案: regex から username を削るか、コメントで意図を残す

25. random デッドコード?

  • 場所: util.ts:405
  • 現状: export const random = (max: number): number => Math.floor(Math.random() * max);
  • 不明点: embed コードベース内で呼び出されていない (util.test.ts のみ?)。外部から import される可能性は低い (embed.ts / embed-core.ts から export されていない)
  • 対応案: 削除候補

26. SimpleStyleVector のレイヤ ID prefix 非対称

  • 場所: simplestyle-vector.ts:170
  • 現状: vt-circle-simple-style-points のみ prefix が vt-circle-、他は vt-geolonia-simple-style-
  • 不明点: なぜ統一されていないか
  • 対応案: 統一すべきだが、外部から layer ID を直接参照する利用があるなら破壊的変更になる

27. EmbedAttributes 型 hole

  • 場所: parse-atts.ts:33-66 (戻り値)、src/types.ts:3-34
  • 現状: simpleVector, stage が戻り値に含まれるが EmbedAttributes 型に定義されていない。[otherKey: string] で型チェックを通している
  • 対応案: EmbedAttributes に正式追加

28. embed-core 経由と embed 経由で pmtiles 二重登録の可能性

  • 場所: embed-core.ts:11-12 (import 時), render.ts:13-14 (renderGeoloniaMap 内)
  • 現状: 両方が同じプロセスでロードされると maplibregl.addProtocol('pmtiles', ...) を 2 回呼ぶ
  • 不明点: maplibre の挙動 (二重登録で error か silent overwrite か)
  • リスク: 同一ページで embed.js と embed-core.js を併用するケース (稀だが React wrapper + iframe 等)
  • 対応案: maplibre 仕様確認のうえ、ガード条件 (if (!maplibregl.getProtocol('pmtiles')) ...) を追加

29. localIdeographFontFamily: 'sans-serif' hardcoded

  • 場所: util.ts:294
  • 現状: maplibre option として固定値で sans-serif を指定
  • 不明点: ユーザがカスタムフォントを使いたい場合は params 経由で上書きする (Object.assign(options, params) が後で動くため) のが想定挙動だが、明示されていない
  • 対応案: docs に明記

30. handleErrorMode 日本語固定文言

  • 場所: util.ts:386-388
  • 現状: 「地図の初期化に失敗しました。管理者にお問い合わせ下さい。開発者の方は開発者ツールでエラー詳細をご確認下さい。」
  • 不明点: i18n の予定 / 必要性
  • 対応案: 多言語化検討 (atts.lang から切替) または英語に統一

合計 30 項目。書き直し時にこのリストを 1 つずつ評価し、「現状維持」「修正」「削除」のいずれかを決定する。

Clone this wiki locally