1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【初心者向け】ZENRIN Maps APIで震度データを地図に可視化する

Last updated at Posted at 2025-09-01

はじめに

毎年9月1日は「防災の日」。自然災害の多い日本では、日頃からの備えがとても大切です。

本記事では、防災に関する情報をわかりやすく可視化するための地図表示の実装方法を紹介します。
災害時に避難所を素早く確認したり、必要な支援情報を共有したりするための仕組みを、技術の力で支えることができれば——そんな思いを込めて書いています。

小さな取り組みかもしれませんが、1人でも多くの方にとって“役立つ備え”となれば幸いです。

本記事では、ZENRIN Maps APIを使ってCSV形式の震度データを地図上に表示する方法について紹介します。
「地図上に情報を載せる」処理の中でも、CSVファイルの読み込み → 地図表示 → ウィジェット描画 までを順を追って解説します。

初心者向けの記事として、初めてZENRIN Maps APIを扱う方でも動作を確認できるよう、全体コードとステップごとの説明を掲載しています。

この記事でできること

  • 南海トラフ地震を想定した震度データ(CSV形式)を読み込む
  • 読み込んだ震度情報を地図上にアイコン表示する
  • ボタンで「表示/非表示」を切り替える

earthquake_zoomout_sample.png

重畳に使用したデータ

南海トラフ巨大地震対策検討ワーキンググループ

APIキー取得手順

ZENRIN Maps API を利用するには、事前に APIキーの取得が必要です。
現在、ZENRIN Maps API は2か月間の無料トライアルが用意されており、期間中は主要な機能を実際にお試しいただけます。
開発や評価の初期段階でも安心してご利用いただけます。
APIキーの取得方法については、以下の記事で詳しく解説されています。
初めての方は、まずこちらをご覧いただき、APIキーの発行と設定を行ってください。
ZENRIN Maps APIの始め方

公式リファレンス

ファイル構成

project/
├── index.html          メインHTMLサンプルコード
├── css/
   └── zma_earthquake.css   スタイルシート
├── csv/
   └── nankaitorafu_sample.csv  震度データCSV形式

ZENRIN Maps APIで地図上に震度データを表示するサンプルコード

以下はZENRIN Maps APIで地図上に震度データを表示するサンプルコードです。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>地震情報</title>

    <!-- ZENRIN Maps API -->
    <script src="https://test-js.zmaps-api.com/zma_loader.js?key=[APIキー]&auth=referer"></script>
    
    <!-- jQuery -->
    <script src="https://code.jquery.com/jquery-3.7.1.min.js" integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo=" crossorigin="anonymous"></script>

    <!-- CSS -->
    <link rel="stylesheet" href="css/zma_earthquake.css" />
  </head>
  <body>
    <div id="ZMap"></div>

    <div class="select_wrapper">
      <button id="btn-draw" disabled>CSVデータ読み込み中...</button>
    </div>

    <!-- JavaScript本体 -->
    <script src="js/zma_earthquake.js"></script>
  </body>
</html>

body {margin: 0; padding: 0;}
#ZMap {position: absolute; top: 0; bottom: 0; width: 100%;}

.intensity{
  width: 40px;
  height: 40px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #fff;
  border: 2px solid #fff;
  font-size: 125%;
  font-weight: 700;
}
.intensity.i1{
  background: #bcbbbb;
  color: #393939;
}
.intensity.i2{
  background: #4cc4e7;
}
.intensity.i3{
  background: #4c53e7;
}
.intensity.i4{
  background: #d9dc2b;
  color: #393939;
}
.intensity.i5-{
  background: #facd6d;
  color: #393939;
}
.intensity.i5\+{
  background: #f9b627;
  color: #393939;
}
.intensity.i6-{
  background: #f79d76;
}
.intensity.i6\+{
  background: #fc7338;
}
.intensity.i7{
  background: #ff1c1c;
}


select {
  -webkit-appearance: none;
  appearance: none;
}
select::-ms-expand {
  display: none;
}

.select_wrapper {
  position: absolute;
  top: 10px;
  left: 10px;
  width: 160px;
}

#btn-draw {
  padding: 8px 16px;
  background: #007cba;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}
#btn-draw:hover {
  background: #005a87;
}
#btn-draw:disabled {
  background: #ccc;
  cursor: not-allowed;
}
let map;
let intensityList = Object(); // 震度情報を格納する変数
let csvData = []; // CSVデータを格納する変数
let csvLoaded = false; // CSV読み込み完了フラグ
let isDataVisible = false; // データ表示状態フラグ

// CSVデータを解析する関数
function parseCSV(text) {
  const lines = text.split('\n');
  if (lines.length === 0) return [];

  const headers = lines[0].split(',');
  const data = [];

  for (let i = 1; i < lines.length; i++) {
    if (lines[i].trim() === '') continue;
    const values = lines[i].split(',');
    const row = {};
    headers.forEach((header, index) => {
      row[header.trim()] = values[index] ? values[index].trim() : '';
    });
    data.push(row);
  }
  return data;
}

// CSVファイルを読み込む関数
function loadCSVFile() {

  fetch('csv/nankaitorafu_sample.csv')
    .then(response => {
      if (!response.ok) {
        throw new Error(`HTTP error! status: ${response.status}`);
      }
      return response.text();
    })
    .then(text => {
      csvData = parseCSV(text);
      if (csvData.length === 0) {
        throw new Error('CSVデータが空です');
      }
      console.log('CSVデータが読み込まれました:', csvData);
      csvLoaded = true;
      updateButtonState();
    });
}

// ボタンの状態を更新する関数
function updateButtonState() {
  const button = $('#btn-draw');
  if (isDataVisible) {
    button.prop('disabled', false).text('震度データを非表示');
  } else {
    button.prop('disabled', false).text('震度データを表示');
  }
}

// CSVファイルを読み込み
loadCSVFile();

ZMALoader.setOnLoad(function (mapOptions, error) {
  if (error) {
    console.error(error);
    return;
  }

  const lat = 35.681406, lng = 139.767132;
  const mapElement = document.getElementById('ZMap');
  mapOptions.center = new ZDC.LatLng(lat, lng);

  mapOptions.minZoom = 4;
  mapOptions.maxZoom = 11;
  mapOptions.zoom = 8;
  mapOptions.mouseWheelReverseZoom = true;

  map = new ZDC.Map(
    mapElement,
    mapOptions,
    function () {
      map.addControl(new ZDC.ZoomButton('bottom-right', new ZDC.Point(-20, -35)));
      map.addControl(new ZDC.Compass('top-right'));
      map.addControl(new ZDC.ScaleBar('bottom-left'));

      $(document).on('click', '#btn-draw', function () {
        if (csvLoaded && csvData.length > 0) {
          if (isDataVisible) {
            console.log("ボタンクリック - データ非表示");
            hideIntensity();
          } else {
            console.log("ボタンクリック - データ表示開始");
            drawIntensity();
          }
        } else {
          console.warn("CSVデータがまだ読み込まれていません");
        }
      });
    },
    function (error) {
      console.error('マップの初期化エラー:', error);
    }
  );
});

// 地図に震度の数値を表示
const drawIntensity = () => {
  if (!csvData || csvData.length === 0) {
    console.error('CSVデータが利用できません');
    alert('CSVデータが読み込まれていません。しばらく待ってから再試行してください。');
    return;
  }

  let datas = [];

  // CSVデータから震度情報を取得
  csvData.forEach((row, idx) => {
    const lat = parseFloat(row.latitude);
    const lng = parseFloat(row.longitude);

    if (!isNaN(lat) && !isNaN(lng) && row.intensity) {
      datas.push({
        lat: lat,
        lng: lng,
        intensity: row.intensity,
      });
    } else {
      console.warn(`無効なデータをスキップ: row ${idx}`, row);
    }
  });

  if (datas.length === 0) {
    console.error('有効なデータがありません');
    alert('有効な震度データが見つかりません');
    return;
  }

  // 震度の小さい順にソート
  datas.sort((a, b) => {
    return a.intensity.localeCompare(b.intensity, 'ja');
  });

  // 既存のウィジェットを削除
  if (map && map.removeAllWidgets) {
    map.removeAllWidgets();
  }

  console.log(`${datas.length}個の震度データを表示します`);

  for (let row of datas) {
    try {
      var widget = new ZDC.UserWidget(
        new ZDC.LatLng(row.lat, row.lng),
        {
          htmlSource: `<div class="intensity i${row.intensity}">${row.intensity}</div>`,
          offset: new ZDC.Point(-20, -20),
          propagation: true
        }
      );
      map.addWidget(widget);
    } catch (error) {
      console.error('ウィジェット追加エラー:', error, row);
    }
  }

  isDataVisible = true;
  updateButtonState();
};

// 地図から震度データを非表示にする
const hideIntensity = () => {
  if (map && map.removeAllWidgets) {
    map.removeAllWidgets();
    console.log('震度データを非表示にしました');
  }

  isDataVisible = false;
  updateButtonState();
};

コードを実行した結果は、以下になります。
earthquake_zoomin_sample.png

実装ステップ(解説付き)

Step 1:地図の読み込み

<script src="https://test-js.zmaps-api.com/zma_loader.js?key=[APIキー]&auth=referer"></script>
  • 上記のスクリプトでZENRIN Maps APIを読み込みます。
  • APIキー は取得したキーに差し替えてください。

Step 2:CSVの読み込み処理

fetch('csv/nankaitorafu_sample.csv')
  .then(response => response.text())
  .then(text => {
    csvData = parseCSV(text);
    csvLoaded = true;
    updateButtonState();
  });
  • fetch を使ってローカルCSVを取得
  • parseCSV 関数でヘッダー付きの2次元オブジェクトに変換します。

Step 3:地図初期化とコントロールの追加

map = new ZDC.Map(
  document.getElementById('ZMap'),
  mapOptions,
  function () {
      map.addControl(new ZDC.ZoomButton('bottom-right', new ZDC.Point(-20, -35)));
      map.addControl(new ZDC.Compass('top-right'));
      map.addControl(new ZDC.ScaleBar('bottom-left'));
  }
);

  • ZMap に地図を表示し、ズーム・コンパスなどのUIも設置します。

Step 4:ボタンの表示/非表示切替(地図初期化後にハンドラ登録 + 安全チェック)

$(document).on('click', '#btn-draw', function () {
  if (csvLoaded && csvData.length > 0) {
    if (isDataVisible) {
      console.log("ボタンクリック - データ非表示");
      hideIntensity();
    } else {
      console.log("ボタンクリック - データ表示開始");
      drawIntensity();
    }
  } else {
    console.warn("CSVデータがまだ読み込まれていません");
  }
});

地図生成が完了して map が利用可能になってから、#btn-draw のクリックハンドラを登録します。
ハンドラ内では、csvLoadedcsvData.length をチェックし、CSV の読み込みが未完了のときに誤って処理が走らないようガードします。
条件を満たす場合のみ drawIntensity() または hideIntensity() を呼び出します。

ボタン名の制御は以下です。

// ボタンの状態を更新する関数
function updateButtonState() {
  const button = $('#btn-draw');
  if (isDataVisible) {
    button.prop('disabled', false).text('震度データを非表示');
  } else {
    button.prop('disabled', false).text('震度データを表示');
  }
}

Step 5:地図に震度の数値を表示

このステップでは、CSV データから読み込んだ震度情報を地図上に表示する処理を解説します。
地図上の震度アイコンは、drawIntensity() 関数内で次の手順で処理されています。

csvData.forEach((row, idx) => {
  const lat = parseFloat(row.latitude);
  const lng = parseFloat(row.longitude);

  if (!isNaN(lat) && !isNaN(lng) && row.intensity) {
    datas.push({
      'lat': lat,
      'lng': lng,
      'intensity': row.intensity,
    });
  } else {
    console.warn(`無効なデータをスキップ: row ${idx}`, row);
  }
});

latitudelongitude が数値であるかをチェック
intensity が存在する行だけを datas 配列に格納
これにより、無効なデータが地図表示に影響しないよう安全性を確保

Step 6:震度アイコンをウィジェットで表示

new ZDC.UserWidget(
  new ZDC.LatLng(row.lat, row.lng),
  {
    htmlSource: `<div class="intensity i${row.intensity}">${row.intensity}</div>`,
    offset: new ZDC.Point(-20, -20)
  }
)

  • ZDC.UserWidget によって、緯度経度に対応する震度アイコンを地図上に追加
  • htmlSource 内で ${row.intensity} を表示することで、地図上に数値として震度が見える
  • CSS クラス i1~i7 により震度ごとの色分けを行っている

おわりに

防災と聞くと堅苦しく感じるかもしれませんが、技術者としてできることは意外とたくさんあります。

地図の表示、データの整理、検索機能の強化……こうした小さな工夫の積み重ねが、非常時の安心や迅速な行動につながります。

今後も、防災の現場に寄り添えるような技術の活用方法を模索していきたいと思います。

最後までお読みいただきありがとうございました。この記事が、あなたの「もしも」に備える一歩となればうれしいです。

1
1
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
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?