この記事のポイント
国土交通省のProject PLATEAUでは、これまでCityGML 2.0 によるデータ整備が進められてきましたが、昨年度末にCityGML 3.0 対応版の標準仕様案が公開され、3.0 への対応がいよいよ本格化してきました。
CityGML 3.0 ではVersioningなどの新規地物の追加や新しい空間概念が追加されており、データ表現がより豊かなものになっています。
特に新規地物のDynamizerは注目の地物で、センサーデータや時系列データを持ったCityGMLがオープンデータとして公開されれば、それを利用した新たなサービスの創出や公的機関が持っている情報の有効活用が期待できるようになります。(例えば、道路ごとのリアルタイム交通量や気象センサー情報を3D都市モデルに関連付け、その情報を自動配送ロボットの経路探索に利用する、など)
この記事ではこのDynamizerにスポットをあて、どのようなデータを表現する地物なのかを具体的に説明していきます。
要点サマリ
CityGML 3.0 のDynamizerは、センサーデータや時系列データを扱う注目の新規地物ですが、現状では標準仕様案に則ったデータもビューアも存在しないため、実際にどう動くのかイメージしにくいという問題があります。
そこで本記事では、実際に動作するIoTシステムを構築し、それをCityGML 3.0 に置き換えた場合にどのように対応するのかを見ていきます。本記事を読むことで、実際のシステムにおけるDynamizerの役割と他の構成要素との関係が具体的にイメージできるようになります。
用語解説
- Project PLATEAU:国土交通省が主導する、日本全国の3D都市モデルの整備・オープンデータ化プロジェクト。2027年度までに約500都市を整備し、将来的には全国整備を目指している。
- CityGML:都市空間を表現する3次元GISの国際標準規格。XMLベースのフォーマットで、建築年や階数といった属性を持った建築物などの地物を3Dで表現する。Project PLATEAUではCityGML 2.0 に準拠したデータを整備してきたが、今後は3.0に準拠したデータの整備を目指している。
- IoT:Internet of Things(モノのインターネット)の略称。あらゆるモノをインターネットにつなぐことで、データの収集や遠隔操作、新たなサービスを実現する。
- Raspberry Pi:Linux系OSが搭載された小型のコンピュータ。今回使用したのはRaspberry Pi 3 Model B RS版という古い型であり、最新の製品にはRaspberry Pi PicoというOSが搭載されていないもの(=マイコン)もある。
- ThingSpeak:データを収集・可視化・分析するためのIoT向けプラットフォーム。MathWorks社により提供されており、同じく同社が開発した数値解析ソフトウェアのMATLABによる分析機能が利用できる。
- QGIS:オープンソースのGISソフトウェア。無料だが、有料のGISソフトウェアに近い機能を持つ。
- I2C:Inter-Integrated Circuit。マイコンとセンサーやメモリなどの周辺機器を接続するために使用される通信規格。Raspberry Piから利用できる同様の規格としてほかにSPI(Serial Peripheral Interface)がある。
システムの全体像について
ここから記事の大半を使って、Dynamizerを説明するための例として用いるIoTシステムの説明を行います。
このシステムはこの記事のために構築したもので、機材があれば誰でも手元で再現することができます。システムの全体像について触れた後、個々の構成要素について説明し、実行した様子を見てから、本題であるDynamizerの説明を行います。
まず最初に、システムの全体像について解説します。
このシステムは、遠隔地にある温度センサーが計測した温度を、手元のビューアの地図で確認できるというものです。システムは3つの構成要素から成り、それぞれが互いに通信して連係動作します。
解説
システムは次の要素で構成されます。
- デバイス
温度センサーとRaspberry Piから構成されます。温度センサーで温度を計測し、その値をRaspberry Piという小型のコンピュータが読み取り、サーバーへと送信します。
- サーバー
デバイスから送られてきたデータを保存します。また、クライアントから要求があれば蓄積したデータを返します。今回はThingSpeakというインターネット上のサービスをサーバーとして利用します。
- クライアント
サーバーから温度データを取得し、ビューア上の地図に表示します。今回はPC上にQGISというGISソフトを動作させておき、ビューアとして利用します。

デバイスについて
デバイスでは、温度センサーで温度を読み取り、観測値をサーバーに送信します。
解説
デバイスは、大きくわけて
- Raspberry
- Pi電子回路
の2つの部分から構成されます。
電子回路は、温度センサーを動かすための回路になります。
下図の通り、今回はブレッドボード上に組みました。

ブレッドボードに載っているのはモジュール基板(温度センサーであるADT7410を含む必要な部品が実装されたもの)のみです。
この基板を、Raspberry Pi上で動作するPythonプログラムと通信するためにGPIO(General Purpose Input/Output(汎用入出力))とジャンパー線で接続します。温度センサーとRaspberry Piとの間の通信はI2Cによるシリアル通信で行うのですが、利用に先立って予めRaspberry Pi側の設定を有効にしておく必要があります。Raspberry Piのターミナルから下記コマンドを実行し、アドレス(48)が表示されていれば有効化されています。
i2cdetect -y 1
もし有効になっていなければ、メニュー→Preferences→Raspberry Pi Configuration→Interfacesタブの順に設定を辿り、I2CをEnableにします。
Raspberry Piは小型のコンピュータで、今回のシステムではPythonで書かれたプログラムで温度センサーを監視し、温度データを取得してサーバーに送信する役割をします。
Pythonプログラムを実行するにあたり、Raspberry Piにrequestsという外部のライブラリがインストールされている必要があります。今回使用したRaspberry Piには既にインストールされていたのですが、もしインストールされていなければ下記コマンドでrequestsをインストールすることができます。
pip install requestsRaspberry Pi上で動作させるPythonプログラムは下記の通りです。
Pythonの開発環境であるThonnyを起動し、エディタに下記のプログラムを貼り付け、緑色の実行ボタンを押せばプログラムが実行されます。
# -*- coding: utf-8 -*-
import smbus
from time import sleep
import requests
# 温度センサADT7410から温度を読み込む
def read_sensor():
w = bus.read_word_data(address_sensor, register_sensor)
d = (w & 0xff00) >> 8 | (w & 0xff) << 8
d = d >> 3
if d & 0x1000 == 0: # 温度が正数 or 0
tmp = d / 16
else: # 温度が負数
# 絶対値を取得してからマイナスをかける
tmp = ((~d & 0x1fff) + 1) / 16 * -1
return tmp
# 送信先
url_base = 'https://api.thingspeak.com/update?api_key=****************&field1=135.5153054843&field2=34.6726912496&field3=bldg_f9df836f-53b2-4903-8443-0928f3f8a4ce&field4='
bus = smbus.SMBus(1) # I2C通信の初期化
address_sensor = 0x48 # デバイス(ADT7410)のアドレス
register_sensor = 0x00 # データが格納されているレジスタのアドレス
try:
while True:
# 温度を取得する
t = read_sensor()
print("temperature:", t)
# GETリクエストを送信
url_full = url_base + str(t)
res = requests.get(url_full)
# 応答の表示
print("status code:", res.status_code) # ステータスコード
print("text:", res.text) # 取得したコンテンツ
print("--")
# 待機(秒)
sleep(30)
except KeyboardInterrupt:
pass
Pythonプログラムは、30秒ごとに温度センサーから温度を読み取り、応答内容と共にターミナルに値を表示します。
温度の読み取りはread_sensor関数で行っています。分解能の設定をデフォルトの13bitとしており、温度の読み取りは0.0625℃刻みとなります。読み取った温度データは変数url_baseに持たせたURLに埋め込まれ、HTTP GETによりサーバーへ送信されます。このURLは、後述するThingSpeakのチャンネルページにあるAPIキータブの「チャンネルフィードを作成する」を参照して記述しました。
温度の読み取りと表示・送信のループの間隔を30秒としていますが、これは温度データを受け取るThingSpeakのデータ送信・更新間隔との兼ね合いから、若干余裕を持たせた時間間隔で設定しています。
サーバーについて
サーバーでは、温度センサーで検出した温度のデータを収集・保存します。
解説
デバイスとクライアントとの間に入るサーバーとして、今回はThingSpeakを利用します。
ThingSpeakはインターネット上のサービスで、データを収集・可視化・解析するために用いられます。データはHTTP GETでやり取りすることができるので、HTTP GETによる通信ができるならデバイスやクライアントの実装はどのようなソフトウェアでも良いことになります。
ThingSpeakを選択した理由としては、Dynamizerに関する文献の中で取り上げられており、今後リリースされるであろうビューアが対応する可能性が高いと思われたためです。
ThingSpeakにアカウントを作成したら、チャンネルを作成します。
今回はgen_valuesという名前でチャンネルを作成しました。
フィールドの定義としては下図の通りです。緯度・経度(x, y)、建物ID(id)、温度(value)の4つを定義しています。

デバイスおよびクライアントからPythonで送信するHTTP GETのURLについては、APIキータブで確認できます。ページの左下に記載があります。

サーバーはインターネット上で常に起動した状態のため、ここまでの設定が終われば特にやるべきことはありません。
クライアントについて
クライアントでは、サーバーに保存された温度データを地図上に表示します。
解説
クライアントは、PC上に起動したビューアです。
今回はビューアとしてGISソフトであるQGIS(QGIS 3.28.1-Firenze)を使用しました。
QGISでは地図を表示する一方、Pythonプログラムを動作させておき、このプログラムがサーバーと通信して温度データを取得します。また、併せてデバイスの位置(緯度・経度)を取得し、地図上の当該位置にフィーチャをプロットしてラベルとして温度を表示します。
QGISで表示のための準備作業を行います。
予め、下記の内容でデバイスの位置を表すtargetレイヤを用意します。

このtargetレイヤは、レコードを持たない空の入れ物として用意しておきます。targetレイヤのシンボルを☆に設定し、ラベルの表示を有効としておきます。Pythonプログラムが動作すると、プログラムはサーバーから取得した温度を属性valueにセットし、緯度経度の場所にシンボル☆と共に温度をラベル表示します。
属性idは建物IDです。今回は使用しませんが、位置の特定に緯度・経度のほか建物IDによる検索が利用できるようにするために保持します。
背景表示として、XYZTilesのOpenStreetMapを「レイヤをプロジェクトに追加」しています。

サーバーと通信するPythonプログラムは、QGIS上から実行します。
QGISの「プラグイン」-「Pythonコンソール」を押下し、画面下部の「エディタの表示」でPythonエディタを表示します。


このエディタに次のPythonプログラムを貼り付け、実行します。
import urllib.request
import json
import qgis.core
# 自動更新処理
def auto_reload_layer():
# ThingSpeakより最新のデータを取得する
url = "https://api.thingspeak.com/channels/*******/feeds/last.json?api_key=****************&results=1"
res = urllib.request.urlopen(url)
data = json.loads(res.read().decode())
# 各フィールド値を型変換して持つ
x = float(data['field1'])
y = float(data['field2'])
id = data['field3']
value = float(data['field4'])
# 更新対象のレイヤーを取得
lyr = QgsProject.instance().mapLayersByName("target")[0]
lyr.startEditing()
# 古いデータを削除する
for ftr in lyr.getFeatures():
lyr.deleteFeature(ftr.id())
# 新しいデータを追加する
pnt = QgsFeature()
pnt.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(x, y)))
pnt.setAttributes([id, value])
lyr.addFeature(pnt)
lyr.commitChanges()
# データを再読み込みして再描画
lyr.reload()
lyr.triggerRepaint()
# タイマーで自動更新を定期的に実行
timer = QTimer()
timer.timeout.connect(auto_reload_layer)
timer.start(30000)
プログラムの前半は、サーバーとの通信です。
変数urlには、ThingSpeakに送信するHTTP GETのURLをセットしています。このURLは、デバイスと同様にAPIキータブを参照して書いたものです。プログラムの後半は、現在のtargetレイヤのレコードを削除し、取得した値でレコードを追加しています。
ここまでの処理をループで回すことで、30秒おきにThingSpeakにHTTP GETで温度データをリクエストし、受信した内容でtargetレイヤの内容を差し替えるようになります。
以上で、システムの構成要素の説明は終わりです。
システムの実行
ここまで説明してきたシステムを実際に動作させ、実行の様子を見ていきます。
解説
今回用意したシステムは、デバイス・サーバー・クライアントの各構成要素がそれぞれ通信を行い連携して動作することで全体として機能を実現します。
ここまで説明してきた通りの設定を行った上で、デバイス上およびクライアントPCのQGIS上でPythonプログラムを実行します。システム稼働中の様子を、データの流れる順番に沿って見ていきます。
- デバイス
デバイスでは、温度センサーADT7410で計測した温度を、Raspberry PiがI2Cを通してPythonプログラムで読み取ります。
下図では、Thonnyのコンソールに計測した温度(temperatureの左)が表示されています。この温度の値が妥当であることは、室内の温度計が指している温度と比較することで確認しています。

- サーバー
Raspberry Pi上で動作させているPythonプログラムは、HTTP GETでサーバーであるThingSpeakに温度データを送ります。ThingSpeakでは、温度データを受け取り記録します。ThingSpeakのチャンネルのページで、現在まで受信した温度データを確認することができます。
下図の「フィールド4チャート」を見ると、デバイスから送られてきた温度データが時系列に表示されていることがわかります。
※サーバーの設定時、チャンネル設定タブで温度データvalueをフィールド4として設定したため、このように表示されています。

- クライアント
クライアントPC上で動作させているビューアのQGISは、自身のPythonプログラムからHTTP GETを発行し、サーバーであるThingSpeakに対し最新の温度データを要求します。
サーバーから返された温度データはQGIS上のtargetレイヤの属性valueに書き込まれ、デバイス位置のシンボル☆と共に表示されます。30秒間隔のためゆっくりではありますが、ThingSpeak上で確認できる最新の温度の値がビューアに表示されることを確認しました。

システムについての説明は以上です。
Dynamizerが表現する範囲と記述例
ここまで説明してきたシステムを使い、Dynamizerが何を表現するのかを説明します。
解説
ここからが本題になります。
この記事のテーマは、Dynamizerについてわかりやすくお伝えしよう、というものでした。ここまで説明してきたIoTシステムは、あくまで説明に使うための例です。
記事の大半を使ってIoTシステムの説明をした理由は、冒頭で触れた通り、現在のDynamizerが標準仕様案しか存在せず、実際のデータが存在しなければビューアも存在しないために、この章だけ説明してもピンと来ないと思われたからです。
今回のシステムは各構成要素が連携することでひとつの機能を実現するものですが、Dynamizerも同様にDynamizerだけで完結しているわけでなくそれが使われる周辺を含めて見ないとよくわからないと思います。Dynamizerはセンサーデータへのアクセス方法を定義する地物なので、実際に動くシステムと比較・置き換えることでイメージしやすくなると考えました。
Dynamizerの記述について見ていきます。
Dynamizerは、センサーデータの読み込み先とアクセス方法、取得したデータの書き込み先を定義します。今回のシステムにおけるクライアントの動作に必要な情報がDynamizerとして定義されることになります。今回のシステムのビューアが、targetレイヤやPythonプログラムではなく、CityGML 3.0 を元に動作するものだったとしたら、CityGMLには下記のようにDynamizerを書くことになります。
<bldg:Building gml:id="bldg_f9df836f-53b2-4903-8443-0928f3f8a4ce">
...
<genericAttribute>
<gen:DoubleAttribute>
<gen:name>temperature</gen:name>
<gen:value>0.0</gen:value>
</gen:DoubleAttribute>
</genericAttribute>
...
<dynamizer>
<dyn:Dynamizer gml:id="dyn_f9df836f-53b2-4903-8443-0928f3f8a4ce">
<dyn:attributeRef>
//bldg:Building[@gml:id='bldg_f9df836f-53b2-4903-8443-0928f3f8a4ce']/genericAttribute/gen:DoubleAttribute[name='temperature']/gen:value
</dyn:attributeRef>
<dyn:sensorConnection>
<dyn:SensorConnection>
<dyn:connectionType codeSpace="../../codelists/SensorConnection_connectionType.xml">4</dyn:connectionType>
<dyn:observationProperty>温度</dyn:observationProperty>
<dyn:uom>℃</dyn:uom>
<dyn:sensorID>*******</dyn:sensorID>
<dyn:baseURL>https://api.thingspeak.com</dyn:baseURL>
<dyn:authType codeSpace="../../codelists/SensorConnection_authType.xml">1</dyn:authType>
<dyn:linkToObservation>%baseURL%/channels/%sensorID%/feeds/last.json?api_key=**************** &results=1</dyn:linkToObservation>
<dyn:sensorLocation xlink:href="#bldg_f9df836f-53b2-4903-8443-0928f3f8a4ce"></dyn:sensorLocation>
</dyn:SensorConnection>
</dyn:sensorConnection>
</dyn:Dynamizer>
</dynamizer>
...
</bldg:Building>
温度データの取得先は、dyn:sensorConnectionタグ以下に記載されています。
また、取得した温度データの書き込み先は、dyn:attributeRefタグが示す先、つまりgenericAttribute/gen:DoubleAttribute/gen:valueになります。
引き続きビューアがCityGMLを元に動作すると仮定して説明しますが、システムは次のように動作します。
- 温度センサーが計測した温度は、デバイスからサーバーに送信・記録されます。
- クライアントPC上のビューアは、dyn:sensorConnectionタグ以下の情報を元に、サーバーに温度データを要求します。
- 返ってきた温度データは、dyn:attributeRefタグが示す先のタグのテキスト値として書き込まれます。
- ユーザーは、ビューアを通して温度センサーが計測した温度を確認することができます。
今回例として用意したシステムについてご理解頂いていれば、上記の内容でDynamizerとその周辺の動きがイメージできるのではないでしょうか。
FAQ
Q1:CityGML 3.0 のDynamizerの動作を確認する方法はないのでしょうか?
A:2026年7月現在、Dynamizerを含むCityGML 3.0 のビューアは確認できていません。
CityGMLを直接表示することができるソフトウェアとしては、FZK Viewer、FMEが挙げられますが、現状ではDynamizerの表示には対応していない模様です。Dynamizerはセンサー情報を取得するためにサーバーと通信しますが、多様な通信方式への対応や、どのように取得した値を表現するかといったことを考えると、実装の難易度が高いように思われます。ただし、動作確認できないのはCityGML 3.0 を直接読み込む場合の話であって、ビューアが開発されるか既存のシステムで利用できるようコンバータが開発されれば、動作確認は可能となります。
Q2:Dynamizerで時系列データを扱うにはどうしたらよいでしょうか?
A:時系列データへの参照を記述することになります。時系列データには3種類あります。
- TabulatedFileTimeseries
カンマ区切り値(CSV)やMicrosoft Excel(XLSX)などのデータ。外部ファイルとして用意する。 - StandardFileTimeseries
時系列データを表現するための形式(OGC TimeseriesML、OGC Observations and Measurements)に則ったデータ。外部ファイルとして用意する。 - GenericTimeseries
時刻と値のセットをインラインにて記述する。
ビューア側の実装として、時系列データのどの時点の値なのかを表示あるいは時間を指定するためのインターフェースが求められます。
Q3:サーバーとして利用できる技術にはThingSpeak以外に何があるのでしょうか?
A:Dynamizerに関する文献には、52°North、FROST Server、Weather Underground、The Things Network といった名前が挙げられています。ただ、文献が書かれてから時間が経っていることや、日本で実際に使われている製品や規格とは限らないことから、今後Dynamizerに対応したビューアやコンバータが登場したとして、実際にこれらがサポートされるのかどうかは不明です。
Q4:今回のユースケースではどのような場面での活用が考えられますか?
A:以下のような活用が可能です。
- 設備の遠隔監視・予防保全
工場やプラント内の機器に取り付けたセンサー(温度・振動など)の値を3Dモデル上の該当設備にDynamizerで関連付けることで、センサーが異常値を示した際に「どのフロアの、どの設備の、どの部分で」問題が発生しているか地図上で直感的に把握することができます。 - 施設内の稼働状況の可視化
オフィスビルや商業施設で、空調・照明等のセンサーの値を空間データと連動させ、フロアごとの稼働状況をリアルタイムに表示する用途に応用できます。
Q5:その他Dynamizerはどのような場面での活用が考えられますか?
A:以下のような活用が可能です。
- 交通・モビリティ
道路地物に交通量・渋滞状況・信号の切替タイミングを紐づけ、時間帯別の交通シミュレーションや渋滞可視化に利用します。 - 防災・インフラ監視
河川や下水道の地物に水位・浸水センサーの値を紐づけ、災害時の状況把握や避難計画のシミュレーションに活用します。 - エネルギー・環境
建物にスマートメーターの電力・ガス使用量や、大気センサーの環境データ(騒音・粉塵・CO2濃度など)を紐づけ、都市単位のエネルギー消費や環境負荷の可視化に利用します。 - 群衆・人流分析
駅やイベント会場の地物に人流センサーのデータを紐づけ、混雑状況の時系列変化を三次元空間上で表現します。
まとめ
この記事では、実際に動作するシステムを例として示すことで、Dynamizerがどのようなものなのかその周辺も含めてイメージできるよう説明しました。
CityGML 3.0 のデータの整備は国の取り組みとしてはまだ始まったばかりで、このデータが世の中にどのような変化をもたらすことになるのか注目を集めていることと思います。
この記事が少しでも皆様のお役に立てたなら幸いです。
当社ではこれまでProject PLATEAUのデータ整備に関わらせて頂き、特にCityGMLへの変換についてノウハウを蓄えてまいりました。CityGMLの整備につきまして何かお困りのことがございましたら、お気軽にお問い合わせください。
お問い合わせ
本記事に関するお問い合わせは、下記よりご連絡ください。
関連ページ
ラスタタイルの作成方法とは?GDALの基本手順とGUIツール化の進め方を整理 – アサミ情報システム株式会社|GIS/3D/CityGML
PLATEAUのCityGMLから屋根の3D表面積をFMEで集計し、建物属性に付与する方法 – アサミ情報システム株式会社|GIS/3D/CityGML