0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

『AI補助金・助成金ファインダー』政府AI補助金データ(7制度・採択率推移)をWordPressで可視化してみた

0
Last updated at Posted at 2026-04-20

ai-subsidy-finder.jpg

この記事でできること

  • D3.js v7を使って補助金の補助額比較・採択率推移・政府予算推移を描画する手順を理解できる
  • WordPressにD3.jsを組み込む際の3つの必須対応(CDNタグ禁止・&&禁止・wp:htmlラッパー)が分かる
  • 異なる出典のデータを単一チャートに統合するときの設計上の注意点を学べる
  • 実際に動作するツールはAI補助金・助成金ファインダー(AI Japan Index)で確認できる

環境・前提

  • ブラウザ: Chrome 123+ / Firefox 125+ / Safari 17+
  • D3.js: v7(CDNからの動的ロード)
  • WordPress: 6.5以降(SWELLテーマ使用環境を想定)
  • JavaScript: ES2020(Vanilla)
  • 追加のビルドツールなし(webpack/Vite等不要)

完成形

政府AI関連予算の推移(2018〜2024年度)、IT導入補助金の採択率推移(2020〜2024年度)、AI補助金7制度の最大補助額比較の3チャートをインタラクティブに表示できる状態になる。動作確認はAI補助金・助成金ファインダーで行える。


手順

Step 1: WordPressでD3.jsを動的ロードする(CDNタグは使えない)

WordPressのコンテンツ内で <script src="CDN"> を使ってはいけない。WordPress/SWELLはコンテンツ内の外部スクリプトタグを無視するため、D3.jsが読み込まれずに描画が一切されない。

NG例(WordPress環境では動かない):

<script src="https://cdn.jsdelivr.net/npm/d3@7/dist/d3.min.js"></script>
<script>
  // d3 is undefined → エラー
  const svg = d3.select("#chart");
</script>

OK例(動的ロードで解決):

function loadD3(callback) {
  if (typeof d3 !== "undefined") {
    // すでにロード済みなら即コールバック
    callback();
    return;
  }
  const script = document.createElement("script");
  script.src = "https://cdn.jsdelivr.net/npm/d3@7/dist/d3.min.js";
  script.onload = callback;
  script.onerror = function() {
    document.getElementById("subsidy-error").style.display = "block";
  };
  document.head.appendChild(script);
}

document.addEventListener("DOMContentLoaded", function() {
  loadD3(initCharts);
});

このコードの意味: typeof d3 でロード済みかを確認し、未ロードなら createElement("script") で動的に追加する。onload で描画初期化関数を呼び出す。

Step 2: && 演算子を三項演算子に置き換える

WordPressのコンテンツフィルターは &&&#038;&#038; にHTMLエンティティ変換する。これはJSの構文エラーになりスクリプト全体が停止する。

NG例(WordPressで壊れる):

const label = isMobile && showLabel ? data.name : "...";
const margin = { left: isMobile && hasYAxis ? 200 : 20 };

OK例(三項演算子またはネストifで代替):

const label = isMobile ? (showLabel ? data.name : "...") : data.name;
const leftMargin = isMobile ? 20 : 200;
const margin = { left: leftMargin };

つまずきポイント: この変換はWordPressの管理画面から投稿を保存するタイミングで発生する。ローカルHTMLでは問題なく動作するのに、WordPress投稿後だけ動かない——という現象はこれが原因。

Step 3: データ構造を設計する(出典定義を統一する)

補助金データは複数の出典から収集する。同一チャートに混入させる場合、データの「定義・スコープ」が一致しているかを確認する。

// 政府AI予算推移データ(内閣府AI戦略会議定義に統一)
// ※SIP/BRIDGE等一部施策分は含まない(内閣府AI戦略会議第5回資料に準拠)
const budgetTrend = [
  { year: 2018, budget: 575.5 },
  { year: 2019, budget: 770.4 },
  { year: 2020, budget: 1178.4 },
  { year: 2021, budget: 1168.3 },
  { year: 2022, budget: 1024.9 },
  { year: 2023, budget: 1126.7 },
  { year: 2024, budget: 1138.4 }
];
// lastVerified: "2026-03-30"
// sourceUrl: "https://www8.cao.go.jp/cstp/ai/ai_senryaku/5kai/shisaku.pdf"

// 補助額比較データ(各制度公式要領に基づく最大補助額)
const subsidyData = [
  { name: "省力化投資補助金(一般型)", maxAmount: 10000, rate: "1/2〜2/3" },
  { name: "新事業進出補助金",          maxAmount: 9000,  rate: "1/2〜2/3" },
  { name: "ものづくり補助金",          maxAmount: 3500,  rate: "1/2〜2/3" },
  { name: "省力化投資補助金(カタログ型)", maxAmount: 1500, rate: "1/2" },
  { name: "事業承継・M&A補助金",       maxAmount: 800,   rate: "1/2〜2/3" },
  { name: "デジタル化・AI導入補助金",  maxAmount: 450,   rate: "1/2〜4/5" },
  { name: "小規模事業者持続化補助金",  maxAmount: 200,   rate: "2/3〜3/4" }
];

ポイント: 経産省の「AI・半導体関連予算1兆2,390億円」(2026年度)は定義が内閣府とは異なる広い範囲を含むため、上記の時系列に混入させない。別の参考値として注記付きで表示する。

Step 4: 横棒チャートを描画する(対数スケール対応)

補助額の最大値(1億円)と最小値(200万円)の比率が50倍になるため、線形スケールでは小さいバーが潰れる。対数スケールを使う。

function drawSubsidyChart(data) {
  const isMobile = window.innerWidth <= 767;
  const margin = {
    top: 20, right: 80,
    bottom: 50,
    left: isMobile ? 15 : 220
  };
  const width = document.getElementById("subsidy-chart").clientWidth;
  const height = 320;
  const innerWidth = width - margin.left - margin.right;
  const innerHeight = height - margin.top - margin.bottom;

  const svg = d3.select("#subsidy-chart")
    .append("svg")
    .attr("width", width)
    .attr("height", height);

  const g = svg.append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

  // 対数スケール(最小値を少し下げてバーが0から始まるように見せる)
  const xScale = d3.scaleLog()
    .domain([100, 12000])  // 100万円〜12,000万円
    .range([0, innerWidth]);

  const yScale = d3.scaleBand()
    .domain(data.map(d => d.name))
    .range([0, innerHeight])
    .padding(0.25);

  // バーを描画
  g.selectAll(".bar")
    .data(data)
    .enter()
    .append("rect")
    .attr("class", "bar")
    .attr("x", 0)
    .attr("y", d => yScale(d.name))
    .attr("width", d => xScale(d.maxAmount))
    .attr("height", yScale.bandwidth());

  // PCのみY軸ラベルを表示
  if (!isMobile) {
    g.selectAll(".y-label")
      .data(data)
      .enter()
      .append("text")
      .attr("class", "y-label")
      .attr("x", -8)
      .attr("y", d => yScale(d.name) + yScale.bandwidth() / 2)
      .attr("text-anchor", "end")
      .attr("dominant-baseline", "middle")
      .text(d => d.name);
  }
}

Step 5: スマホ対応のタッチイベント(下部固定パネル)

スマホではY軸ラベルを非表示にし、バーをタップしたときに画面下部固定パネルで詳細を表示する。

// 下部パネルのHTML(ツールのHTMLに事前配置)
// <div id="mobile-panel" style="display:none; position:fixed; bottom:0; left:0;
//   width:100%; background:#1a1a2e; border-top:2px solid #f26419;
//   padding:12px 16px; z-index:10000; max-height:40vh; overflow-y:auto;">
//   <button id="panel-close">×</button>
//   <div id="panel-content"></div>
// </div>

function showMobilePanel(name, amount, rate) {
  document.getElementById("panel-content").innerHTML =
    "<strong>" + name + "</strong><br>" +
    "最大補助額: " + amount + "<br>" +
    "補助率: " + rate;
  document.getElementById("mobile-panel").style.display = "block";
}

// バーへのタッチイベント登録
bars.on("touchstart", function(event, d) {
  event.preventDefault();
  event.stopPropagation();  // ← 省略するとパネルが開いて即閉じる
  showMobilePanel(d.name, d.maxAmount + "万円", d.rate);
});

// パネル外タップで閉じる
document.addEventListener("touchstart", function(event) {
  const panel = document.getElementById("mobile-panel");
  if (panel.style.display === "block") {
    panel.style.display = "none";
  }
});

document.getElementById("panel-close").addEventListener("click", function() {
  document.getElementById("mobile-panel").style.display = "none";
});

つまずきポイント: e.stopPropagation() を省略すると「パネルが表示された直後に閉じる」現象が起きる。バーのタッチイベントが document まで伝播し、「パネル外タップ」と誤判定されるためだ。必ずセットで書く。

Step 6: wp:htmlラッパーで全体をまとめる(DOCTYPE禁止)

WordPressへの貼り付けは <!-- wp:html --> ブロックで全体を囲む。<!DOCTYPE html><html> タグから始まるフルHTML構造は使わない(wpautopフィルターがscript内に <p> タグを挿入してJSを破壊する)。

<!-- wp:html -->
<div class="aji-subsidy-finder-wrap">
  <style>
    .aji-subsidy-finder-wrap { /* スコープ付きCSS */ }
    .aji-subsidy-finder-wrap .bar { fill: #f26419; }
    /* 全セレクタを .aji-subsidy-finder-wrap で囲む。グローバルCSS禁止 */
  </style>

  <div id="subsidy-chart"></div>
  <div id="mobile-panel" style="display:none; ...">...</div>

  <script>
    (function() {
      // D3動的ロード + 全チャート描画コード
    })();
  </script>
</div>
<!-- /wp:html -->

つまずきポイント: body {}h1 {} 等のグローバルCSSセレクタを使うとSWELLテーマ全体のスタイルが壊れる。全セレクタを .aji-subsidy-finder-wrap で囲む必要がある。


つまずきポイントまとめ

  • D3が動かない(WordPress): <script src="CDN"> はWordPressに無視される → 動的ロードに変更
  • && の後でスクリプトが止まる: WordPressが &&&#038;&#038; に変換 → 三項演算子に置換
  • スマホでラベルが重なる: 日本語ラベルはスマホ幅に入らない → if (!isMobile) で非表示にしてタッチパネル表示に切り替え
  • バーがずれる: scaleBandのドメインとバー配置のアクセサが不一致 → 同じキーを使う
  • タッチパネルが即閉じる: e.stopPropagation() が抜けている → タッチイベントに必ず追加
  • スタイルが全ページに影響する: グローバルCSSを使っている → .aji-wrap 等のスコープセレクタで囲む

まとめ

  • D3.js v7のSVG描画方式でWordPressのChart.js競合問題を回避できた
  • && 禁止・CDNタグ禁止・wp:htmlラッパーの3点がWordPressでのD3実装必須事項
  • 200倍の差があるデータ比較には対数スケールが有効
  • scaleBandはドメインとアクセサキーを一致させる
  • e.stopPropagation() なしのタッチイベントはパネルが即閉じる

実装の動作確認はAI補助金・助成金ファインダー(AI Japan Index)で行える。類似のデータビジュアライゼーションツールはAI Japan Indexのツール一覧にまとまっている。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?