こぼれネット

MATSim 連載準備用

現在の状況(11/30) 一通りの稼働を確認

(Step 1) 広袴のOSMファイルをダウンロード
G:\home\ebata\hirohakama\hiro_db\small-hirohakama.osm

(Step 2) OSM → network.xmlへの変更
G:\home\ebata\hirohakama\hiro_db
$ python osm_to_matsim_network.py
network.xmlが生成

(Step.3)
ebata@DESKTOP-1QS7OI7:~/matsim-example-project$ java -jar matsim-example-project-0.0.1-SNAPSHOT.jar

[config-test1.xml]

<?xml version="1.0" ?>
<!DOCTYPE config SYSTEM "http://www.matsim.org/files/dtd/config_v2.dtd">
<config>

	<module name="global">
		<param name="randomSeed" value="4711" />
		<param name="coordinateSystem" value="EPSG:6685" />
	</module>

	<module name="network">
		<param name="inputNetworkFile" value="network-test1.xml" />
	</module>

	<module name="plans">
		<param name="inputPlansFile" value="plans-test1.xml" />
	</module>

	<module name="controller">
		<param name="outputDirectory" value="./output" />
		<param name="lastIteration" value="0" />

		<!-- 追記:毎イテレーション events を出す -->
		<param name="writeEventsInterval" value="1" />

	</module>


	<module name="scoring">
		<parameterset type="activityParams" >
			<param name="activityType"            value="h" />
			<param name="typicalDuration" value="12:00:00" />
		</parameterset>
		<parameterset type="activityParams" >
			<param name="activityType"            value="w" />
			<param name="typicalDuration" value="08:00:00" />
		</parameterset>
	</module>

	<module name="routing">
	  <!-- 既に書いてある他の routing パラメータがあればそのまま残す -->
	  
	  <!-- ネットワーク整合性チェックを無効化 -->
	  <param name="networkRouteConsistencyCheck" value="disable" />
	</module>	
	
</config>

[network-test1.xml]

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE network SYSTEM "http://www.matsim.org/files/dtd/network_v1.dtd">

<network name="equil test network">
  <nodes>
    <node id="279332805" x="768875.871" y="1096994.329"/>
    <node id="279332812" x="768964.532" y="1097238.641"/>
    <node id="279332877" x="768972.629" y="1097302.450"/>
(中略)
    <node id="12893847140" x="768966.269" y="1097305.846"/>
    <node id="13206970768" x="768642.219" y="1096803.771"/>
    <node id="13206970769" x="768656.013" y="1096826.212"/>
  </nodes>

  <links capperiod="01:00:00">
    <link id="1239817547_0_bwd" from="12891801850" to="279332880" length="2.25" freespeed="13.89" capacity="900.00" permlanes="1" />
    <link id="1239817547_0_fwd" from="279332880" to="12891801850" length="2.25" freespeed="13.89" capacity="900.00" permlanes="1" />
    <link id="1239817547_1_bwd" from="1768138693" to="12891801850" length="62.84" freespeed="13.89" capacity="900.00" permlanes="1" />
(中略)
    <link id="761529596_5_bwd" from="12891801828" to="12891801827" length="1.72" freespeed="10.00" capacity="1000.00" permlanes="1" />
    <link id="761529596_5_fwd" from="12891801827" to="12891801828" length="1.72" freespeed="10.00" capacity="1000.00" permlanes="1" />
  </links>
</network>

[plans-test1.xml]

<?xml version="1.0" ?>
<!DOCTYPE plans SYSTEM "http://www.matsim.org/files/dtd/plans_v4.dtd">
<plans xml:lang="de-CH">
<person id="1">
  <plan>
    <act type="h" link="153188623_0_fwd" end_time="06:00" />
    <leg mode="car">
      <route> </route>
    </leg>

    <act type="w" link="165222823_3_fwd" dur="00:00:10" />
    <leg mode="car">
      <route> </route>
    </leg>
    <act type="w" link="165257680_2_fwd" dur="00:00:05" />

    <leg mode="car">
      <!-- ここは書かない(MATSim に任せる) -->
    </leg>

    <act type="h" link="257057591_26_fwd" />
  </plan>
</person>
</plans>

(Step.4)

ebata@DESKTOP-1QS7OI7:~/matsim-example-project/scenarios/equil2$ python matsim_events_to_geo_csv.py
CSV exported: output/agents_positions_geo.csv

(Step.5)

ここからは、QGISで動画表示をするところは、前回の方式とおなじ

======
現在の状況(11/24)

WSL(Ubuntu)起動→
ebata@DESKTOP-1QS7OI7:~/matsim-example-project$ java -jar matsim-example-project-0.0.1-SNAPSHOT.jar

 

これが通ると

ebata@DESKTOP-1QS7OI7:~/matsim-example-project/scenarios/equil2/output$ pwd
/home/ebata/matsim-example-project/scenarios/equil2/output が書換えられるので注意

必要なファイルは3つ

<?xml version="1.0" ?>
<!DOCTYPE config SYSTEM "http://www.matsim.org/files/dtd/config_v2.dtd">
<config>

	<module name="global">
		<param name="randomSeed" value="4711" />
		<param name="coordinateSystem" value="EPSG:6685" />
	</module>

	<module name="network">
		<param name="inputNetworkFile" value="network.xml" />
	</module>

	<module name="plans">
		<param name="inputPlansFile" value="plans100.xml" />
	</module>

	<module name="controller">
		<param name="outputDirectory" value="./output" />
		<param name="lastIteration" value="10" />

		<!-- 追記:毎イテレーション events を出す -->
		<param name="writeEventsInterval" value="1" />

	</module>

	<module name="scoring">
		<param name="lateArrival" value="-18" />
		<param name="performing" value="+6" />
		<parameterset type="activityParams" >
			<param name="activityType"            value="h" />
			<param name="typicalDuration" value="12:00:00" />
		</parameterset>
		<parameterset type="activityParams" >
			<param name="activityType"            value="w" />
			<param name="typicalDuration" value="08:00:00" />
			<param name="openingTime"     value="07:00:00" />
			<param name="closingTime"     value="18:00:00" />
		</parameterset>
	</module>
	
	<module name="replanning">
		<parameterset type="strategysettings" >
			<param name="strategyName" value="BestScore" />
			<param name="weight" value="0.9" />
		</parameterset>
		<parameterset type="strategysettings" >
			<param name="strategyName" value="ReRoute" />
			<param name="weight" value="0.1" />
		</parameterset>
	</module>
</config>

上記のconfig.xmlの概要は以下の通り。

config.xml の概要

本設定ファイルは、MATSim シミュレーションを実行するための基本パラメータを定義したものであり、全体として「ネットワーク」「プラン」「コントローラ」「スコアリング」「リプランニング(経路再探索)」の各モジュール設定から構成されている。

1. global モジュール

2. network モジュール

3. plans モジュール

4. controller モジュール

5. scoring モジュール

6. replanning モジュール


総括

本 config.xml は、標準的な MATSim 実行構成を踏襲しながら、日本の座標系(EPSG:6685)を用いたネットワーク・プランデータを読み込み、10 イテレーションのシミュレーションを行う設定となっている。スコアリングおよび再計画戦略も基本設定が施されており、小規模テストから実用的な解析まで広く利用可能な構成である。

[network.xml]

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE network SYSTEM "http://www.matsim.org/files/dtd/network_v1.dtd">

<network name="equil test network">
  <nodes>
	<node id="1"  x="774558.033" y="1108851.844"/>
	<node id="2"  x="779558.033" y="1108851.844"/>
	<node id="3"  x="793693.033" y="1114776.844"/>
	<node id="4"  x="792060.033" y="1113182.844"/>
	<node id="5"  x="790729.033" y="1112066.844"/>
	<node id="6"  x="789860.033" y="1110562.844"/>
	<node id="7"  x="789558.033" y="1108851.844"/>
	<node id="8"  x="789860.033" y="1107140.844"/>
	<node id="9"  x="790729.033" y="1105636.844"/>
	<node id="10" x="792060.033" y="1104520.844"/>
	<node id="11" x="793693.033" y="1102926.844"/>
	<node id="12" x="794558.033" y="1108851.844"/>
	<node id="13" x="799558.033" y="1108851.844"/>
	<node id="14" x="799558.033" y="1098851.844"/>
	<node id="15" x="774558.033" y="1098851.844"/>
  </nodes>

   <links capperiod="01:00:00">
      <link id="1" from="1" to="2" length="10000.00" capacity="36000" freespeed="27.78" permlanes="1"  />
      <link id="2" from="2" to="3" length="10000.00" capacity="3600" freespeed="27.78" permlanes="1"  />
      <link id="3" from="2" to="4" length="10000.00" capacity="3600" freespeed="27.78" permlanes="1"  />
      <link id="4" from="2" to="5" length="10000.00" capacity="3600" freespeed="27.78" permlanes="1"  />
      <link id="5" from="2" to="6" length="10000.00" capacity="3600" freespeed="27.78" permlanes="1"  />
      <link id="6" from="2" to="7" length="10000.00" capacity="3600" freespeed="27.78" permlanes="1"  />
      <link id="7" from="2" to="8" length="10000.00" capacity="3600" freespeed="27.78" permlanes="1"  />
      <link id="8" from="2" to="9" length="10000.00" capacity="3600" freespeed="27.78" permlanes="1"  />
      <link id="9" from="2" to="10" length="10000.00" capacity="3600" freespeed="27.78" permlanes="1"  />
      <link id="10" from="2" to="11" length="10000.00" capacity="3600" freespeed="27.78" permlanes="1"  />
      <link id="11" from="3" to="12" length="5000.00" capacity="1000" freespeed="27.78" permlanes="1"  />
      <link id="12" from="4" to="12" length="5000.00" capacity="1000" freespeed="27.78" permlanes="1"  />
      <link id="13" from="5" to="12" length="5000.00" capacity="1000" freespeed="27.78" permlanes="1"  />
      <link id="14" from="6" to="12" length="5000.00" capacity="1000" freespeed="27.78" permlanes="1"  />
      <link id="15" from="7" to="12" length="5000.00" capacity="1000" freespeed="27.78" permlanes="1"  />
      <link id="16" from="8" to="12" length="5000.00" capacity="1000" freespeed="27.78" permlanes="1"  />
      <link id="17" from="9" to="12" length="5000.00" capacity="1000" freespeed="27.78" permlanes="1"  />
      <link id="18" from="10" to="12" length="5000.00" capacity="1000" freespeed="27.78" permlanes="1"  />
      <link id="19" from="11" to="12" length="5000.00" capacity="1000" freespeed="27.78" permlanes="1"  />
      <link id="20" from="12" to="13" length="10000.00" capacity="36000" freespeed="27.78" permlanes="1"  />
      <link id="21" from="13" to="14" length="10000.00" capacity="36000" freespeed="27.78" permlanes="1"  />
      <link id="22" from="14" to="15" length="35000.00" capacity="36000" freespeed="27.78" permlanes="1"  />
      <link id="23" from="15" to="1" length="10000.00" capacity="36000" freespeed="27.78" permlanes="1"  />
   </links>
</network>

network.xml の概要

本 network.xml は、MATSim の「equil(equilibrium)」テストで用いられる代表的な小規模ネットワークの構造を記述したものであり、ノード 15 点とリンク 23 本から構成される。全体として、中央ノード「2」を中心とした放射状構造と、外周を囲うリング構造をあわせ持つネットワークである。


1. ノード構成(nodes)

■ 中央に位置するノード

■ 外周ノード

■ 放射状ノード


2. リンク構成(links)

総リンク数は 23 本。時間帯は capperiod="01:00:00"(1時間あたり容量)。

(1) ハブ(ノード 2)から放射状に伸びるリンク(リンクID 2〜10)

(2) 放射状ノードからノード 12 への接続(リンクID 11〜19)

(3) 外周ループ(リンクID 1, 20〜23)


3. ネットワーク構造の特徴


総括

この network.xml は、MATSim の代表的な「equil」ネットワークをベースとしたもので、中心に交通が集中するハブ構造と、大容量リング道路、放射状道路からなるシンプルかつ典型的なテスト用ネットワークである。渋滞発生や再経路探索のような動態変化を観察するのに適した構成となっている。

[plans100.xml]

<?xml version="1.0" ?>
<!DOCTYPE plans SYSTEM "http://www.matsim.org/files/dtd/plans_v4.dtd">
<plans xml:lang="de-CH">
<person id="1">
	<plan>
		<act type="h" x="-25000" y="0" link="1" end_time="06:00" />
		<leg mode="car">
			<route>2 7 12</route>
		</leg>
		<act type="w" x="10000" y="0" link="20" dur="00:10" />
		<leg mode="car">
			<route> </route>
		</leg>
		<act type="w" x="10000" y="0" link="20" dur="03:30" />
		<leg mode="car">
			<route>13 14 15 1</route>
		</leg>
		<act type="h" x="-25000" y="0" link="1" />
	</plan>
</person>

</plans>

この概要は以下の通り。

plans100.xml の概要

この plans100.xml は、MATSim に投入する **エージェントの活動計画(1日の行動スケジュール)**を定義したファイルである。
現状では person は1名のみ(id="1")であり、その人物が 自宅(h)→職場(w)→職場(w)→自宅(h) と移動しながら活動する1日の行動列が記述されている。


1. person と plan の構成


2. 行動列の中身(act / leg)

(1) 自宅活動 h

<act type="h" ... link="1" end_time="06:00" />

(2) 車移動(自宅→職場)

<leg mode="car">
<route>2 7 12</route>
</leg>

(3) 職場活動 w(短い滞在)

<act type="w" ... link="20" dur="00:10" />

(4) 車移動(職場→職場)

<leg mode="car">
<route> </route>
</leg>

(5) 職場活動 w(長い滞在)

<act type="w" ... link="20" dur="03:30" />

(6) 車移動(職場→自宅)

<leg mode="car">
<route>13 14 15 1</route>
</leg>

(7) 自宅活動 h(帰宅後)

<act type="h" ... link="1" />

3. この plans.xml の特徴と用途


総括

plans100.xml は、エージェント1名の1日行動(06:00 出発の通勤、職場での短時間/長時間滞在、外周経路で帰宅)を記述したテスト用 plans である。経路指定を明示する部分と MATSim に任せる部分が共存しており、ルート選択やリプランニングの動作確認に適した構成となっている。

さて、3つのファイルで

ebata@DESKTOP-1QS7OI7:~/matsim-example-project/scenarios/equil2/output$ ls
ITERS                      modestats.png              output_households.xml.gz  pkm_modestats.csv
logfile.log                modestats_stackedbar.png   output_legs.csv.gz        pkm_modestats.png
logfileWarningsErrors.log  modules.dot                output_links.csv.gz       scorestats.csv
modeChoiceCoverage10x.png  output_activities.csv.gz   output_network.xml.gz     scorestats.png
modeChoiceCoverage10x.txt  output_allVehicles.xml.gz  output_persons.csv.gz     stopwatch.csv
modeChoiceCoverage1x.png   output_config.xml          output_plans.xml.gz       stopwatch.png
modeChoiceCoverage1x.txt   output_config_reduced.xml  output_trips.csv.gz       tmp
modeChoiceCoverage5x.png   output_counts.xml.gz       output_vehicles.xml.gz    traveldistancestats.csv
modeChoiceCoverage5x.txt   output_events.xml.gz       ph_modestats.csv          traveldistancestatslegs.png
modestats.csv              output_facilities.xml.gz   ph_modestats.png          traveldistancestatstrips.png
ebata@DESKTOP-1QS7OI7:~/matsim-example-project/scenarios/equil2/output$

ができている。

ここにcsvファイル作成用のPythonプログラム(matsim_events_to_csv.py)を書き込む。

[matsim_events_to_csv.py]

import gzip
import xml.etree.ElementTree as ET
import csv
import math

EVENTS_FILE = "output_events.xml.gz"
NETWORK_FILE = "output_network.xml.gz"
OUT_CSV = "agents_positions.csv"
STEP_SEC = 1.0  # 1秒刻み


def load_network(network_file):
    nodes = {}
    links = {}

    with gzip.open(network_file, 'rb') as f:
        tree = ET.parse(f)
        root = tree.getroot()

        for node in root.find("nodes"):
            nid = node.attrib["id"]
            x = float(node.attrib["x"])
            y = float(node.attrib["y"])
            nodes[nid] = (x, y)

        for link in root.find("links"):
            lid = link.attrib["id"]
            fnode = link.attrib["from"]
            tnode = link.attrib["to"]
            length = float(link.attrib.get("length", "0"))
            freespeed = float(link.attrib.get("freespeed", "0"))
            links[lid] = (fnode, tnode, length, freespeed)

    return nodes, links


def interpolate_points(t0, t1, x0, y0, x1, y1, step=1.0):
    dt = t1 - t0
    if dt <= 0:
        return [(t0, x0, y0), (t1, x1, y1)]

    n_steps = int(math.floor(dt / step))
    pts = []

    for i in range(n_steps + 1):
        t = t0 + i * step
        if t > t1:
            break
        r = (t - t0) / dt
        x = x0 + r * (x1 - x0)
        y = y0 + r * (y1 - y0)
        pts.append((t, x, y))

    if not pts or pts[-1][0] < t1:
        pts.append((t1, x1, y1))

    return pts


def process_events(events_file, nodes, links, out_csv):
    # vehicle -> person 対応
    vehicle_to_person = {}

    # vehicleごとの entered 状態
    # in_progress[vehicle] = (link_id, enter_time)
    in_progress = {}

    with gzip.open(events_file, 'rb') as f, open(out_csv, "w", newline="", encoding="utf-8") as csvfile:
        writer = csv.writer(csvfile)
        writer.writerow(["time", "person", "x", "y", "link", "event_type"])

        for ev, elem in ET.iterparse(f, events=("end",)):
            if elem.tag != "event":
                continue

            a = elem.attrib
            etype = a.get("type", "")
            time = float(a.get("time", "0"))

            # 1) vehicle -> person 対応を作る
            if etype == "PersonEntersVehicle":
                person = a.get("person")
                vehicle = a.get("vehicle")
                if person and vehicle:
                    vehicle_to_person[vehicle] = person
                elem.clear()
                continue

            # 2) entered/left link は vehicle 単位で処理
            if etype in ("entered link", "left link"):
                vehicle = a.get("vehicle")
                link_id = a.get("link")
                if vehicle is None or link_id is None:
                    elem.clear()
                    continue
                if link_id not in links:
                    elem.clear()
                    continue
                if vehicle not in vehicle_to_person:
                    # person がまだ紐付いていない車両は無視
                    elem.clear()
                    continue

                fnode, tnode, length, freespeed = links[link_id]
                if fnode not in nodes or tnode not in nodes:
                    elem.clear()
                    continue

                if etype == "entered link":
                    in_progress[vehicle] = (link_id, time)

                elif etype == "left link":
                    if vehicle not in in_progress:
                        elem.clear()
                        continue

                    entered_link_id, t_enter = in_progress[vehicle]
                    if entered_link_id != link_id:
                        # 不整合対策:状態を更新してスキップ
                        in_progress[vehicle] = (link_id, time)
                        elem.clear()
                        continue

                    t_left = time
                    actual_dt = t_left - t_enter

                    expected_dt = length / freespeed if freespeed > 0 and length > 0 else None
                    if actual_dt <= 0:
                        if expected_dt is not None and expected_dt > 0:
                            actual_dt = expected_dt
                            t_left = t_enter + actual_dt
                        else:
                            actual_dt = 0

                    x0, y0 = nodes[fnode]
                    x1, y1 = nodes[tnode]

                    pts = interpolate_points(t_enter, t_left, x0, y0, x1, y1, step=STEP_SEC)

                    person = vehicle_to_person[vehicle]
                    for t, x, y in pts:
                        writer.writerow([t, person, x, y, link_id, "move"])

                    in_progress.pop(vehicle, None)

            elem.clear()


def main():
    nodes, links = load_network(NETWORK_FILE)
    process_events(EVENTS_FILE, nodes, links, OUT_CSV)
    print(f"CSV exported: {OUT_CSV}")


if __name__ == "__main__":
    main()

$ebata@DESKTOP-1QS7OI7:~/matsim-example-project/scenarios/equil2/output$ python matsim_events_to_csv.py
CSV exported: agents_positions.csv

このagents_positions.csvが、エージェントの軌跡となるが、面倒なので、時間と座標を出すプログラムを作成

[matsim_events_to_geo_csv.py]

import gzip
import xml.etree.ElementTree as ET
import csv
import math
from datetime import datetime, timedelta, timezone

try:
    from pyproj import Transformer
except ImportError as e:
    raise SystemExit(
        "pyproj が必要です。pip install pyproj を実行してください。"
    )

EVENTS_FILE = "output_events.xml.gz"
NETWORK_FILE = "output_network.xml.gz"
OUT_CSV = "agents_positions_geo.csv"
STEP_SEC = 1.0  # 1秒刻み

# シミュレーション開始日時(必要に応じて変更)
# 例: 日本時間で 2025-11-24 00:00:00 開始
SIM_START = datetime(2025, 11, 24, 0, 0, 0, tzinfo=timezone(timedelta(hours=9)))

# 座標変換: EPSG:6685 -> EPSG:4326 (lon, lat)
TRANSFORMER = Transformer.from_crs("EPSG:6685", "EPSG:4326", always_xy=True)


def load_network(network_file):
    nodes = {}
    links = {}

    with gzip.open(network_file, 'rb') as f:
        tree = ET.parse(f)
        root = tree.getroot()

        for node in root.find("nodes"):
            nid = node.attrib["id"]
            x = float(node.attrib["x"])
            y = float(node.attrib["y"])
            nodes[nid] = (x, y)

        for link in root.find("links"):
            lid = link.attrib["id"]
            fnode = link.attrib["from"]
            tnode = link.attrib["to"]
            length = float(link.attrib.get("length", "0"))
            freespeed = float(link.attrib.get("freespeed", "0"))
            links[lid] = (fnode, tnode, length, freespeed)

    return nodes, links


def interpolate_points(t0, t1, x0, y0, x1, y1, step=1.0):
    dt = t1 - t0
    if dt <= 0:
        return [(t0, x0, y0), (t1, x1, y1)]

    n_steps = int(math.floor(dt / step))
    pts = []

    for i in range(n_steps + 1):
        t = t0 + i * step
        if t > t1:
            break
        r = (t - t0) / dt
        x = x0 + r * (x1 - x0)
        y = y0 + r * (y1 - y0)
        pts.append((t, x, y))

    if not pts or pts[-1][0] < t1:
        pts.append((t1, x1, y1))

    return pts

def sec_to_datetime_str(sec):
    dt = SIM_START + timedelta(seconds=sec)
    # QGISで扱いやすい形式 → 2025-11-24 06:00:01
    return dt.strftime('%Y-%m-%d %H:%M:%S')

def xy_to_lonlat(x, y):
    lon, lat = TRANSFORMER.transform(x, y)
    return lon, lat


def process_events(events_file, nodes, links, out_csv):
    vehicle_to_person = {}
    in_progress = {}

    with gzip.open(events_file, 'rb') as f, open(out_csv, "w", newline="", encoding="utf-8") as csvfile:
        writer = csv.writer(csvfile)
        writer.writerow(["datetime", "time_sec", "person", "lat", "lon", "link", "event_type"])

        for ev, elem in ET.iterparse(f, events=("end",)):
            if elem.tag != "event":
                continue

            a = elem.attrib
            etype = a.get("type", "")
            time_sec = float(a.get("time", "0"))

            # vehicle -> person 対応付け
            if etype == "PersonEntersVehicle":
                person = a.get("person")
                vehicle = a.get("vehicle")
                if person and vehicle:
                    vehicle_to_person[vehicle] = person
                elem.clear()
                continue

            if etype in ("entered link", "left link"):
                vehicle = a.get("vehicle")
                link_id = a.get("link")
                if vehicle is None or link_id is None:
                    elem.clear()
                    continue
                if link_id not in links:
                    elem.clear()
                    continue
                if vehicle not in vehicle_to_person:
                    elem.clear()
                    continue

                fnode, tnode, length, freespeed = links[link_id]
                if fnode not in nodes or tnode not in nodes:
                    elem.clear()
                    continue

                if etype == "entered link":
                    in_progress[vehicle] = (link_id, time_sec)

                elif etype == "left link":
                    if vehicle not in in_progress:
                        elem.clear()
                        continue

                    entered_link_id, t_enter = in_progress[vehicle]
                    if entered_link_id != link_id:
                        in_progress[vehicle] = (link_id, time_sec)
                        elem.clear()
                        continue

                    t_left = time_sec
                    actual_dt = t_left - t_enter

                    expected_dt = length / freespeed if freespeed > 0 and length > 0 else None
                    if actual_dt <= 0:
                        if expected_dt is not None and expected_dt > 0:
                            actual_dt = expected_dt
                            t_left = t_enter + actual_dt
                        else:
                            actual_dt = 0

                    x0, y0 = nodes[fnode]
                    x1, y1 = nodes[tnode]

                    pts = interpolate_points(t_enter, t_left, x0, y0, x1, y1, step=STEP_SEC)

                    person = vehicle_to_person[vehicle]
                    for t, x, y in pts:
                        lon, lat = xy_to_lonlat(x, y)
                        dt_str = sec_to_datetime_str(t)
                        writer.writerow([dt_str, t, person, lat, lon, link_id, "move"])

                    in_progress.pop(vehicle, None)

            elem.clear()


def main():
    nodes, links = load_network(NETWORK_FILE)
    process_events(EVENTS_FILE, nodes, links, OUT_CSV)
    print(f"CSV exported: {OUT_CSV}")


if __name__ == "__main__":
    main()

QGISで動画表示する
レイヤ→属性テーブルを開く


これでdtというエントリーができる。
これで「プロパティ」→「時系列」で、

を投入する
これで
「プロジェクト」→「プロパティ」→「時系列」→「レイヤから計算」→「適用」

これもセットしないと出てこない

時系列コントロールを表示するには4つ設定しないと出てこない。
(1)そのレイヤ→"属性テーブル"によるdtのセット
(2)そのレイヤ→"プロパティ"によるセット
(3)メニューの"プロジェクト"→"プロパティ"→"時系列"によるセット
(4)メニューの"ビュー"→"パネル"→"時系列コントローラ"のチェック

こんな感じの動画がでます。

 

======

まず失敗から。

MATSim Docker image(https://github.com/maptic/matsim-docker)で動かせない。3日間くらいの時間を費やした結果、断念

-----

(1)WSLのUbuntuを起動

(2)matsim-example-project(https://github.com/matsim-org/matsim-example-project)にあるコンテンツをダウンロード

cd ~
git clone https://github.com/matsim-org/matsim-example-project.git

(3)ビルド

cd ~/matsim-example-project$

./mvnw clean package

(3-1) どの java が呼ばれているか確認

which -a java
readlink -f "$(which java)"
echo "$JAVA_HOME"
which -a で複数出たら、一番上が今使われている java です。

JAVA_HOME が 17 を指していれば、PATH 先頭に $JAVA_HOME/bin が来て 17 が優先されます。

~/.bashrc や ~/.profile に JAVA_HOME=/usr/lib/jvm/java-17... の行が残っていると、それが勝ちます。

(3-2) 一時的に 21 へ切り替え(すぐ試せます)

export JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64
export PATH="$JAVA_HOME/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
hash -r # シェルのコマンドキャッシュをクリア
java -version
→ ここで openjdk version "21..." になれば OK。

(3-3) 恒久設定(ログイン時に毎回 21 になるように)

~/.bashrc または ~/.profile の中の 古い JAVA_HOME/PATH 行をコメントアウトし、下を追記:

# Java 21 を既定にする
export JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64
export PATH="$JAVA_HOME/bin:$PATH"

保存後、反映:

exec $SHELL -l
java -version

(4)MATSim起動

cd ~/matsim-example-project$

java -jar matsim-example-project-0.0.1-SNAPSHOT.jar

(4)テスト起動

「Choose」を押す → リポジトリ内の
scenarios/equil/config.xml を選択。

ちなみに、Output Directoryのは、自動的に、
/home/ebata/matsim-example-project/scenarios/equil/output
が入っていた。

「Start MATSim」のボタンを押下

2~3分後に、こんな感じになった

コンソール画面も、こんな感じになって、入力できない状況

ただ、この段階で、データはできているので、コンソールをCtrl-Cで落しても良い。

で、ここから図示したかったのですが、すったもんだしたあげく失敗したので、via-appを使うようにしました。

で、WSLのUbuntuで、以下からダンロードします。

https://simunto.com/via/download

で、

$CPU=$(dpkg --print-architecture)
$wget https://www.simunto.com/data/via/release/debian/simunto-via-app_25.1.0_${CPU}.deb
$sudo apt install ./simunto-via-app_25.1.0_${CPU}.deb

を実施すると、

$via-app

で、viaが起動します。

ebata@DESKTOP-1QS7OI7:~/matsim-example-project$ export DISPLAY=:0
ebata@DESKTOP-1QS7OI7:~/matsim-example-project$ export LIBGL_ALWAYS_INDIRECT=1

ebata@DESKTOP-1QS7OI7:~/via-app

も必要かもしれない。

"output_network.xml.gz"を選択

同じ様に、

"output_events.xml.gz"

を選択する。

上図のように、"Load Data"ボタンを押して下さい。

上図のようにタイムラインが表示され、08:00から数分間のみ、自動車が表示されます。右側のインジケータを若干右に移動させると、移動の様子(といっても表われて消えているだけの様子)が見えます。

Windows版については
https://kobore.net/matsim/MATSim-begining.html
を参考にして下さい。
----
ちなみに、Ubuntuで作った
java -jar matsim-example-project-0.0.1-SNAPSHOT.jar
は、Windowsでも動きました。

======

もっとも簡単なMATSimの起動方法

~/matsim-example-project/simple-scenario というディレクトリを作り、さらに出力用にoutput/simple1 というディレクトリも掘っておく。

simple-scenario
├── config.xml
├── config.xml~
├── network.xml
├── network.xml~
├── output
└── simple1

[config.xml]
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE config SYSTEM "http://www.matsim.org/files/dtd/config_v2.dtd">
<config>
  <module name="controller">
    <param name="firstIteration" value="0"/>
    <param name="lastIteration"  value="0"/>
    <param name="overwriteFiles" value="overwriteExistingFiles"/>
    <param name="outputDirectory" value="output/simple1"/>
  </module>

  <module name="network">
    <param name="inputNetworkFile" value="network.xml"/>
  </module>

  <module name="plans">
    <param name="inputPlansFile" value="plans.xml"/>
  </module>

  <module name="qsim">
    <param name="startTime" value="00:00:00"/>
    <param name="endTime"   value="24:00:00"/>
  </module>

  <!-- 旧 strategy → replanning に修正済み -->
  <module name="replanning">
    <param name="maxAgentPlanMemorySize" value="1"/>
  </module>

  <!-- scoring に h / w を定義 -->
  <module name="scoring">
    <parameterset type="activityParams">
      <param name="activityType" value="h"/>
      <param name="typicalDuration" value="12:00:00"/>
    </parameterset>
    <parameterset type="activityParams">
      <param name="activityType" value="w"/>
      <param name="typicalDuration" value="08:00:00"/>
      <param name="openingTime" value="07:00:00"/>
      <param name="closingTime" value="18:00:00"/>
    </parameterset>
  </module>
</config>

この `config.xml` は、MATSim(Multi-Agent Transport Simulation)の実行設定を記述したファイルです。内容をセクションごとに整理すると次のようになります。

---

### 1. `<module name="controller">`

シミュレーション全体の制御に関する設定。

* `firstIteration=0`
最初のイテレーション番号を 0 に設定。
* `lastIteration=0`
最後のイテレーションも 0 にしているので、学習や繰り返しは行わず、初期の状態のみを実行する(ワンショットのシミュレーション)。
* `overwriteFiles=overwriteExistingFiles`
出力ディレクトリに既存のファイルがあっても上書きする設定。
* `outputDirectory=output/simple1`
シミュレーション結果の出力先フォルダ。

---

### 2. `<module name="network">`

利用する交通ネットワークデータの指定。

* `inputNetworkFile=network.xml`
道路やリンク構造が記載されたネットワーク定義ファイル。

---

### 3. `<module name="plans">`

エージェント(個人)の行動計画データの指定。

* `inputPlansFile=plans.xml`
各エージェントの出発地・目的地・活動スケジュールを記載したプランファイル。

---

### 4. `<module name="qsim">`

シミュレーション実行時間の設定。

* `startTime=00:00:00`
シミュレーション開始時刻を午前 0 時に設定。
* `endTime=24:00:00`
シミュレーション終了時刻を 24 時に設定(丸一日分を対象にする)。

---

### 5. `<module name="replanning">`

プラン修正(replanning)の設定。
※旧名称 `strategy` が `replanning` に変更されたもの。

* `maxAgentPlanMemorySize=1`
各エージェントが保持できるプランの数を 1 に制限(つまり再計画はせず、最初のプランをそのまま利用)。

---

### 6. `<module name="scoring">`

エージェントが行動を評価するためのスコアリング設定。ここでは活動(activity)のパラメータを定義している。

* `activityType=h`
「h」(home、自宅滞在)活動を 12 時間の典型的持続時間で設定。
* `activityType=w`
「w」(work、仕事)活動を 8 時間の典型的持続時間で設定。
また、開店時間を 7:00、閉店時間を 18:00 とすることで、仕事に従事できる時間帯の制約を表現。

---

### 総合説明

この設定ファイルは、**1日分の単発シミュレーション**を対象とし、ネットワークとプランデータを与え、再計画を行わずに実行するシンプルなケースを記述している。活動は「自宅(h)」と「仕事(w)」の2種類のみで、典型的な1日の行動(12時間の在宅と8時間の勤務)を再現する構成になっている。

---

?? 要するに、この `config.xml` は \*\*「network.xml」と「plans.xml」を入力として、エージェントが自宅と仕事の往復を行う1日シミュレーションを出力/simple1 に結果保存する」\*\*ための設定ファイルです。

[config.xml]

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE network SYSTEM "http://www.matsim.org/files/dtd/network_v2.dtd">
<network name="tiny-net">
  <nodes>
    <node id="1" x="0.0"    y="0.0"/>
    <node id="2" x="1000.0" y="0.0"/>
  </nodes>

  <links>
    <!-- 1km、自由速度15m/s ≒ 54km/h、容量 1000veh/h、1車線 -->
    <link id="1_2" from="1" to="2" length="1000.0" freespeed="15.0"
          capacity="1000.0" permlanes="1" modes="car"/>
    <link id="2_1" from="2" to="1" length="1000.0" freespeed="15.0"
          capacity="1000.0" permlanes="1" modes="car"/>
  </links>
</network>

この `network.xml` は、MATSim で利用する交通ネットワークを記述したファイルで、最小限の「2ノード・2リンク」からなる非常にシンプルなネットワークを定義しています。内容を整理すると以下の通りです。

---

### 1. `<network name="tiny-net">`

ネットワーク全体の名前として `"tiny-net"` を定義。小規模なテスト用ネットワークであることを示す。

---

### 2. `<nodes>`

ノード(交差点や地点)を定義。ここでは2つのノードがある。

* `<node id="1" x="0.0" y="0.0"/>`
座標 (0,0) にあるノード。IDは `1`。
* `<node id="2" x="1000.0" y="0.0"/>`
座標 (1000,0) にあるノード。IDは `2`。
→ ノード間は直線で 1000m(=1km)離れている。

---

### 3. `<links>`

ノード間を結ぶリンク(道路)を定義。2本のリンクが双方向に設定されている。

* `<link id="1_2" from="1" to="2" ... />`
ノード1からノード2へ向かうリンク。

* 長さ:`1000.0` m (1km)
* 自由速度:`15.0` m/s ≒ 54 km/h
* 容量:`1000.0` 台/時 (1時間あたりの通過可能車両数)
* 車線数:`1`
* 利用可能モード:`car` のみ
* `<link id="2_1" from="2" to="1" ... />`
ノード2からノード1へ戻るリンク。条件は上記と同じ。

---

### まとめ

このファイルは、**1kmの直線道路を2ノードで表現し、車が双方向に走行できるようにした最小限の道路ネットワーク**を定義している。速度や容量も現実的な数値(片側1車線、時速54km、1000台/時)で設定されており、テストや学習用のシミュレーションに適したネットワーク構成になっている。

?? 要するに「**ノード1とノード2を結ぶ片側1車線の道路(往復2車線)だけのシンプルネットワーク**」です。

[plans.xml]

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plans SYSTEM "http://www.matsim.org/files/dtd/plans_v4.dtd">
<plans>
  <person id="1">
    <plan selected="yes">
      <act type="h" link="1_2" x="0.0" y="0.0" end_time="08:00:00"/>
      <leg mode="car"/>
      <act type="w" link="2_1" x="1000.0" y="0.0" end_time="17:00:00"/>
      <leg mode="car"/>
      <act type="h" link="1_2" x="0.0" y="0.0"/>
    </plan>
  </person>
</plans>

この `plans.xml` は、MATSim におけるエージェント(人物)の行動計画を記述したファイルです。内容を整理すると以下のようになります。

---

### 1. `<plans>` ルート要素

計画ファイル全体を示すルート。内部に個々の人物(`<person>`)とその行動計画(`<plan>`)を記述。

---

### 2. `<person id="1">`

ID が `1` の人物を定義。ここでは 1 人だけ。

---

### 3. `<plan selected="yes">`

この人物の計画(行動スケジュール)を記述。

* `selected="yes"` → このプランが実行対象として選択されている。

---

### 4. 行動と移動の流れ

計画は **行動(`<act>`)と移動(`<leg>`)が交互に記述**される。ここでは1日の典型的な「自宅→仕事→自宅」往復を表現している。

#### (1) 自宅での行動

```xml
<act type="h" link="1_2" x="0.0" y="0.0" end_time="08:00:00"/>
```

* `type="h"`:自宅(home)活動。
* `link="1_2"`:ノード1→2のリンク上に自宅があると仮定。
* 座標 `(0.0, 0.0)`(ノード1の位置に対応)。
* `end_time="08:00:00"` → 午前8時まで自宅に滞在し、その後移動を開始。

#### (2) 通勤移動

```xml
<leg mode="car"/>
```

* `mode="car"` → 自動車で移動する。
* 出発地点は自宅(link="1\_2")、目的地は次の `<act>` で指定。

#### (3) 仕事での行動

```xml
<act type="w" link="2_1" x="1000.0" y="0.0" end_time="17:00:00"/>
```

* `type="w"`:仕事(work)活動。
* `link="2_1"`:ノード2→1のリンク上に職場があると仮定。
* 座標 `(1000.0, 0.0)`(ノード2の位置に対応)。
* `end_time="17:00:00"` → 午後5時まで仕事をして、その後帰宅移動。

#### (4) 帰宅移動

```xml
<leg mode="car"/>
```

* 仕事先から自動車で自宅へ戻る。

#### (5) 自宅での行動(帰宅後)

```xml
<act type="h" link="1_2" x="0.0" y="0.0"/>
```

* 再び自宅(link="1\_2")で活動。
* 終了時刻は未設定なので、そのまま夜間~翌日まで滞在。

---

### まとめ

この `plans.xml` は、**1人の人物(id=1)が「午前8時に自宅を出発 → 車で職場へ通勤 → 午後5時まで勤務 → 車で帰宅 → 以降は自宅で滞在」する一日の行動計画**を記述している。

ネットワーク(`network.xml`)と組み合わせると、ノード1(自宅)とノード2(職場)の間を往復する最小限の通勤シナリオが再現できる構成になっている。

---

 

 

 

 

 

kno

モバイルバージョンを終了