# วิธีจัดทำข้อมูล — CityMETER Transit

เอกสารนี้อธิบายว่าข้อมูลบนหน้า [Transit](https://montri-th.github.io/transit/) มาจากไหน นับอะไร ตัดสินสถานะอย่างไร และอะไรที่ข้อมูลไม่ได้พิสูจน์ ทุกตัวเลขบนหน้าจอสร้างจาก `scripts/build-data.py` ซึ่งอ่านเฉพาะไฟล์ส่งออกของชีต ไม่แก้ชีตต้นทาง

ป้ายหลักฐานตาม Landometer MBB v0.5.3 §3.7: **Observed fact** (ใช้ได้ใน scope ของแหล่ง) · **Inference** (ข้อสรุปของผู้จัดทำ) · **Hypothesis** · **Owner-stated** · **Project evidence**

## 1. แหล่งข้อมูลและรุ่น

| ชั้น | แหล่ง | รุ่น / วันที่ตรวจ |
|---|---|---|
| Research master | Google Sheet “CityMETER Transit Thailand — Research Master 2026-10-30” (32 แท็บ, 292,007 แถว) — ภายในองค์กร ไม่เผยแพร่ลิงก์ | ส่งออกครบ 32 แท็บ 31 ต.ค. 2569; 31/32 แท็บจำนวนแถว×คอลัมน์ตรงกับ Drive (`00_README` ต่าง 13 แถวว่าง) |
| ทะเบียนชุดข้อมูล | Google Sheet “CityMETER Dataset Registry — Product, Evidence, Usage & SEO” — ภายในองค์กร | `R01_DATASETS` research.TH.transit ตรวจ 31 ต.ค. 2569; ทะเบียนยังไม่มี Statement ของ transit (`R06_REVIEW` STATEMENT_NOT_OBSERVED) |
| แหล่งต้นทางรายระเบียน | 34 แหล่งสาธารณะจาก `07_SOURCES` เช่น รายชื่อสถานีของ BTS/EBM/NBM, BEM, SRTET, กรมการขนส่งทางราง, GTFS ของ สนข. (CC BY 4.0), หน้าโครงการ รฟม./รฟท., สถิติกรุงเทพมหานคร | ดู `data/transit.json` → `meta.sources` (ชื่อ ผู้เผยแพร่ ลิงก์ สิทธิ์ ช่วงเวลา) |
| OpenStreetMap | Overpass API (ODbL 1.0, © OpenStreetMap contributors) — ไฟล์ `osm-rail-stations-2026-10-31.json` ที่เจ้าของอนุมัติให้ดาวน์โหลด (3,840,695 ไบต์, sha256 `1cdb65b8…3de9fd67`, `timestamp_osm_base` 2026-10-31T13:12:06Z) | ดู §8; ผลที่สกัดแล้วอยู่ใน `sources/osm/osm-derived-2026-10-31.json` และ `meta.osm` |
| กรอบจังหวัด | OpenGISData-Thailand provinces (CC BY 4.0, ผู้เผยแพร่อ้าง DOPA) | commit `fafcea17…`, sha256 `310c3adf…`; ดู `data/provinces.geojson` metadata |

ไฟล์ส่งออกทั้งชุดถูกตรวจ sha256 รายไฟล์ก่อนใช้ (60 ไฟล์ ZIP sha256 `b0e186db…7019`) ลิงก์ Google Drive/Docs ทุกแบบถูกตัดออกจากผลลัพธ์ และ build จะหยุดทันทีถ้าพบลิงก์ภายในหรือ path ของเครื่องในไฟล์ใดก็ตาม (publication guard ใน `scripts/build-data.py` และ `tests/data.test.js`)

## 2. หน่วยนับ (grain) — ไม่รวมสถานีข้ามสาย

- **สถานีที่เปิดให้บริการ** = หนึ่งรายการสถานี–สายใน `17_RAIL_CATALOG` (`station_catalog_id`) — 195 รายการใน 10 สาย ไม่ใช่ 195 อาคารหรือชานชาลา (Observed fact)
- **สถานีที่กำลังก่อสร้าง** = หนึ่งสมาชิกสถานี–โครงการใน `23_CONSTRUCTION_STATIONS` (`construction_station_id`) — 106 สถานี/ที่หยุดรถใหม่ใน 7 โครงการ
- **ยังยืนยันไม่ได้** = 4 รายการในโครงการเดียวกันที่เป็นสถานีเดิมหรือชุมทางอ้างอิง (`construction_scope_role` existing_station_project_member / junction_reference_boarding_unverified) แสดงเมื่อเลือก “ทั้งหมด” เท่านั้น และไม่ถูกตีความว่าปิด
- ทุกความสัมพันธ์ใน `31_INTERCHANGES` มี `physical_entity_merge = FALSE` จึง **ไม่รวมระเบียน** แม้ชื่อหรือพิกัดเดียวกัน (เช่น สยาม CEN ของสายสุขุมวิทและสายสีลมเป็นสองระเบียน)
- **จุดเปลี่ยนสาย (complex)** = กลุ่มระเบียนที่เชื่อมกันด้วยความสัมพันธ์ `current_network` (ผังเครือข่ายของผู้ให้บริการ) แบบ connected components ได้ 18 จุด: 17 จุดเชื่อม 2 สาย และบางซื่อ–กรุงเทพอภิวัฒน์เชื่อม 3 สาย นี่คือการจัดกลุ่มเพื่อแสดงผล ไม่ใช่การยืนยันว่าเป็นอาคารเดียวหรือเดินถึงกันได้ (Inference ระดับการนำเสนอ)
- “ระยะไปถึง” (reach) ของจุดเปลี่ยนสาย = จำนวนระเบียนสถานีที่เปิดให้บริการบนสายที่มาบรรจบกันที่จุดนั้น ใช้ตัดสินลำดับเมื่อจำนวนสายเท่ากัน ไม่ใช่เวลาเดินทางหรือจำนวนผู้โดยสาร
- ความสัมพันธ์ `within_line_branch` (ท่าพระ, เมืองทองธานี) คือการเปลี่ยนขบวนภายในสาย ไม่นับเป็นจุดเปลี่ยนสาย; `future_project` 12 รายการแสดงเป็น “อนาคต ยังไม่เปิด” ในแผงสถานี และไม่ทำให้สถานีใดเป็นจุดเปลี่ยนสายวันนี้
- การเชื่อมกับ BRT สองแห่ง (ช่องนนทรี–สาทร, ตลาดพลู–ราชพฤกษ์) แสดงในแผงสถานี แต่ไม่นับเป็นจุดเปลี่ยนสายระบบราง

## 3. สถานะ — ไม่ยกระดับ

| บนหน้าจอ | กติกา | หลักฐาน |
|---|---|---|
| เปิดให้บริการ | `service_status` = operator_roster_listed (187) หรือ government_8station_extent_and_gtfs_scheduled (ARL 8) | รายชื่อสถานีปัจจุบันของผู้ให้บริการ/ภาครัฐ ตรวจ 30 ต.ค. 2569 — เป็นรายการที่เผยแพร่ ไม่ใช่สถานะการเดินรถแบบสด (`live_verified = false` ทุก 1,114 หลักฐานบริการ) |
| กำลังก่อสร้าง | สมาชิกโครงการที่ `construction_scope_role` = new_station_or_halt_in_construction_project | ขั้นงานระดับโครงการและหลักฐานรายสถานีแยกกัน (`station_construction_stage`); เปอร์เซ็นต์ระดับโครงการไม่ใช่ความคืบหน้าของสถานี |
| ยังยืนยันไม่ได้ | สถานีเดิม/ชุมทางอ้างอิงในโครงการ | ไม่สรุปว่าปิดหรือเปิด |

วันเปิดและวันครบสัญญาใน `source_target_dates` เป็น **เป้าหมายตามแหล่ง** ไม่ใช่วันเปิดจริง และแสดงพร้อมข้อความนั้นเสมอ

## 4. พิกัดและจังหวัด

- สถานีที่เปิดให้บริการ: ใช้พิกัดของตัวตนอ้างอิงใน `09_ENTITIES` (`canonical_reference_id`) เมื่อ `coordinate_status` = selected_source_position (194 รายการ) — เท่ากับพิกัดในบัญชีสถานีทุกกรณีที่บัญชีมีพิกัด (ระยะต่างสูงสุด 0 ม.) และเติม 68 รายการที่บัญชีไม่มีพิกัด (สายสีน้ำเงิน ม่วง แดง)
- ARL A1 สุวรรณภูมิ: `09_ENTITIES` พักพิกัดไว้ (coordinate_conflict_held) เพราะ DRT กับ GTFS ไม่ตรงกัน หน้าเว็บใช้พิกัดจากบัญชี DRT พร้อมป้าย “พิกัดโดยประมาณ” (`coordStatus` = conflict_held); PK01 ศูนย์ราชการนนทบุรีใช้พิกัดที่เลือกแล้ว โดยระบุว่า GTFS ยังไม่ตรง
- สถานีก่อสร้าง: ใช้พิกัดอ้างอิงของ `23_CONSTRUCTION_STATIONS` (110/110) พร้อมบทบาทและช่วงตีความ (เช่น 20 ม. จากแบบประกวดราคา, 100/500 ม. จากแผนที่โครงการ) — ไม่ใช่ทางเข้าหรือหมุดสำรวจ
- พิกัดทุกจุดเป็น “จุดอ้างอิงสถานี” ไม่ใช่ทางเข้า ชานชาลา หรือขอบเขตอาคาร
- จังหวัด: ใช้ `reference_province` ของแหล่ง (274 รายการ; อ้างอิงขอบเขตอำเภอ GISTDA ไม่ใช่การกำหนดเขตตามกฎหมาย) เมื่อไม่มีจึงใช้ point-in-polygon กับกรอบจังหวัด (31 รายการ) — บันทึกใน `provinceBasis`

## 5. แนวเส้นทาง

- สายที่เปิดให้บริการ: แนวเส้น (shapes) ของ route ใน GTFS ของ สนข. (`03_ROUTES` → `26_ROUTE_GEOMETRY` / `27_GEOMETRY_PARTS`, encoded polyline precision 8 ถอดหลังต่อชิ้นตาม `part_index`; ความยาวที่ถอดได้ตรงกับ `length_of_source_shape_m` ภายใน 1.5% และ build จะหยุดถ้าไม่ตรง)
- หนึ่ง route มีหลาย shape (ทิศทางและรูปแบบเที่ยว) จึงรวมแบบ **coverage union**: เรียงจากยาวไปสั้น เก็บเฉพาะช่วงที่ยังไม่มี shape ที่เก็บแล้วอยู่ภายใน 30 ม. (ไม่สร้างเส้นเชื่อมใหม่) แล้วลดจุดแบบ Douglas–Peucker 3 ม.
- ความยาวแนวเส้นต่อสาย (รวม 269.2 กม.) คือความยาวของแนวใน GTFS หลังรวม ไม่ใช่ระยะทางเดินรถที่สำรวจ
- สายที่กำลังก่อสร้าง: ใช้แนวโครงการอ้างอิงเฉพาะที่ research master มี (`project_alignment_reference_not_service_route`): รถไฟความเร็วสูงระยะที่ 1 (แนววางแผน MOT 2018, 6 ชิ้น), เด่นชัย–เชียงราย–เชียงของ (OSM reference, 299 ชิ้น), บ้านไผ่–นครพนม (OSM, 1 ชิ้น) — ชิ้นส่วนแยกกันไม่ถูกต่อเป็นเส้นเดียว
- สายสีส้ม (ตะวันตก 3 ทาง, ตะวันออก 16 ทาง) และสายสีม่วงใต้ (10 ทาง) ไม่มีแนวในชีต (CPUD สายสีส้มถูกพักเพราะขอบเขตขัดกัน) จึงใช้ทาง `railway=construction` ของ OpenStreetMap (§8) — สถานีทุกแห่งของสามช่วงนี้อยู่ห่างแนวไม่เกิน 15 ม. OSM วาดแยกรายราง (สองเส้นขนานกัน) จึงไม่คำนวณความยาวสายจากแนวนี้ (`lengthKm` = null) และบอกที่มาในแผงสายว่า “แนวจาก OpenStreetMap ไม่ใช่แบบก่อสร้างทางการ”
- บนแผนที่ สายที่กำลังก่อสร้างเป็น **เส้นประ** ทั้งแบบ 2D และ 3D (ไม่ได้อาศัยสีหรือความโปร่งใสอย่างเดียว)

## 6. ผู้โดยสาร — แยกชุด ไม่เทียบข้ามชุด

| ชุด | ตัวชี้วัดบนหน้าจอ | วิธีคำนวณ | ข้อจำกัด |
|---|---|---|---|
| ARL รายสถานี รายวัน (กรมการขนส่งทางราง, 2022-01-01 ถึง 2025-02-28) | ผู้โดยสาร arrival / departure เฉลี่ยต่อวัน ปี 2567 | ค่าเฉลี่ยของค่ารายวันที่ `value_status` = measured และ `aggregate_status` = same_series_only ในปี 2024 (ตัด 32 แถวที่ขัดกันของวันที่ 8 ส.ค. 2567 ออก และแหล่งไม่มีวันที่ 7 ส.ค. จึงใช้ 364 วัน ซึ่งแสดงกำกับไว้) | arrival/departure เป็นสองชุด ห้ามบวกเป็นจำนวนคน หน่วยต้นทางไม่ระบุ |
| BTS รายสถานี รายเดือน ปี 2564 (สถิติกรุงเทพมหานคร) | ผู้โดยสารขาออก รวมทั้งปี (เที่ยวคน) | ผลรวม 12 ยอดรายเดือนของสถานีเดียวกัน (59 สถานีที่ research master เชื่อมไว้); สถานีที่ขาดบางเดือนแสดงจำนวนเดือนกำกับ | ปี 2564 อยู่ในช่วงมาตรการโควิด-19; สยาม 12 ค่าแยกตามสายไม่ได้ จึงแสดงเป็นค่ารวมของทั้งสถานีในทั้งสองระเบียน (`shared`) และนับครั้งเดียวในกราฟ (`seriesKey`) |

กราฟ “ผู้โดยสาร” จัดกลุ่มตาม metric + หน่วย + งวด + วิธีรวม จึงไม่มีแท่งของสองชุดในกราฟเดียว สถิติที่ไม่ได้รายงานไม่แสดงเป็น 0

## 7. สีของสาย — ข้อยกเว้นจาก LDS ที่เจ้าของอนุมัติแล้ว (31 ต.ค. 2569)

ชื่อสายในไทยระบุสีอยู่แล้ว (สายสีน้ำเงิน สายสีม่วง …) การวาดสายด้วยสีที่ขัดกับชื่อทำให้อ่านผิด จึงให้ **สีของสายเป็นสีอัตลักษณ์ของบุคคลที่สาม** ไม่ใช่ categorical palette ของ LDS และมีแหล่งกำกับรายสาย (Owner-stated: “เห็นด้วย” 31 ต.ค. 2569):

1. ลำดับแรก: แท็ก `colour` ของ route relation ใน OpenStreetMap (§8) — `lines[].colorBasis` = `osm_route_colour` พร้อมรหัส relation
2. ระหว่างยังไม่มีข้อมูล OSM: ใช้ token ของ LDS categorical series (fill หรือ ink) ที่ใกล้ตระกูลสีในชื่อสาย (`colorBasis` = `lds-series-fallback`, `colorToken` เช่น `series-8-ink` สำหรับสายสีน้ำเงิน) ซึ่งเปลี่ยนตามธีมตามกติกา LDS — เป็นสีแทน ไม่ใช่สีทางการ สายสีทองกับสายสีเหลืองใช้ token เดียวกัน (อยู่ห่างกันและไม่บรรจบกัน) สายสีส้มกับสายสีแดงอ่อนก็เช่นกัน และสายสีม่วงไม่มีตระกูลสีที่ตรงใน series ชุดนี้ (ใช้โทน slate)

ผลรอบนี้: 9 สายได้สีจาก OSM route relation (BTS สุขุมวิท #65B724, สีลม #02817D, สีทอง #A58704, MRT สีน้ำเงิน #1E398D, สีม่วง #893B90, สีชมพู #E76589, สีเหลือง #FFD400, สายสีแดงเข้ม #B5191E, สายสีแดงอ่อน #FC5353) สายสีม่วงใต้ใช้สีของสายสีม่วง (`osm_route_colour_of_line_family`) สายสีชมพูมี relation สองสี (#E76589 สายหลัก 26 ทาง, #C4007B สาขาเมืองทองธานี 6 ทาง) จึงใช้สีของสายหลักและรายงานไว้ ARL สายสีส้ม และโครงการรถไฟทางไกลยังไม่มี relation ที่มีสี จึงใช้ token สำรองของ LDS ต่อไป

ข้อยกเว้นนี้ใช้กับสีอัตลักษณ์ของสายเท่านั้น สเกลข้อมูลทุกตัว (count, activity) ยังใช้ LUT 41 ค่าของ LDS ตรงตามต้นฉบับในทั้งสองธีม และชั้นขนส่งอื่นใช้ token บทบาทแผนที่ (ไม่ใช้ categorical palette)

## 8. OpenStreetMap — อาคารสถานี ระดับทาง และสีสาย

สถานะรอบนี้ดูที่ `data/transit.json` → `meta.osm` (null = ยังไม่มีไฟล์ OSM ในรอบ build นี้)

**การนำผลเข้า build (รอบ 31 ต.ค. 2569):** ไฟล์ดิบอยู่ในเครื่องของเจ้าของและคัดลอกเข้าสภาพแวดล้อม build ไม่ได้ จึงรันกติกาเดียวกับ `scripts/osm_enrich.py` ในเบราว์เซอร์ที่มีไฟล์ด้วย `scripts/osm-browser-extract.js` แล้วส่งเฉพาะผลลัพธ์ (`sources/osm/osm-derived-2026-10-31.json`, 42,258 ไบต์) ตรวจครบสามชั้น: sha256 ของไฟล์ดิบที่เบราว์เซอร์คำนวณตรงกับไฟล์ที่ดาวน์โหลด, sha256 ของโค้ดฟังก์ชันที่รันตรงกับไฟล์ใน repo และ sha256 ของผลลัพธ์ตรงกันทั้งสองฝั่ง `build-data.py --osm-derived` ใช้ผลนี้ผ่าน `apply_derived()` ซึ่งตรวจว่าแต่ละช่วงแนวเส้นยังเป็นรูปทรงเดิม (จำนวนจุดและความยาว) และระดับสถานีที่คำนวณซ้ำตรงกับที่ใช้จับคู่อาคาร กับไฟล์ OSM สังเคราะห์ ทางนี้ให้ผลเหมือนการอ่านไฟล์ดิบตรง ๆ ทุกไบต์

**คำค้น Overpass** (กรอบ 13.50–14.10°N, 100.30–100.85°E; บันทึกในไฟล์นี้เพื่อให้ทำซ้ำได้):

```
[out:json][timeout:240][bbox:13.50,100.30,14.10,100.85];
( node["railway"~"^(station|halt|stop)$"]; node["public_transport"="station"]; node["construction:railway"="station"]; )->.s;
( relation["route"~"^(subway|light_rail|monorail)$"]; relation["route"="train"]["name"~"สีแดง|Red Line|Airport|แอร์พอร์ต",i]; )->.routes;
( way["railway"~"^(subway|light_rail|monorail|construction)$"]; way["railway"="rail"][!"service"]; way(r.routes)["railway"]; )->.r;
( way["building"~"^(train_station|transportation)$"]; way["railway"="station"]; way["public_transport"="station"];
  relation["railway"="station"]; relation["public_transport"="station"]["type"="multipolygon"]; way(around.s:60)["building"]; )->.b;
(.s; .r; .b;); out body geom qt;
.routes out body qt;
```

ไฟล์ดิบไม่ถูกเผยแพร่ (เก็บ sha256 และเวลา `timestamp_osm_base`) ส่วนผลที่สกัดและข้อมูลที่มาจาก OSM ใน `data/` เผยแพร่ภายใต้ ODbL 1.0 พร้อมการอ้างอิง © OpenStreetMap contributors `scripts/osm_enrich.py` ใช้ไฟล์นี้:

- **ระดับทาง:** แบ่งแนวเส้นแต่ละช่วงตามแท็กของทาง OSM ที่ใกล้ที่สุดของ **สายเดียวกัน** (สมาชิก route relation ภายใน 40 ม.; ถ้าสายไม่มี relation ใช้ทางรถไฟใดก็ได้ภายใน 30 ม.): `tunnel=yes` → ใต้ดิน, `bridge=yes|viaduct` หรือ `layer>0` → ยกระดับ, อื่น ๆ → ระดับพื้น ช่วงสั้นกว่า 100 ม. รวมกับช่วงข้างเคียง ช่วงที่หาทาง OSM ไม่พบเก็บเป็น null (ไม่ทราบ) ไม่ใส่ค่าตั้งต้น
- **ระดับของสถานี:** ระดับของสายตนเองที่ตำแหน่งสถานี
- **การวาดสถานีที่มีอาคาร:** สถานีที่จับคู่อาคารได้ (132 ระเบียนใน 125 อาคาร) วาดเป็นอาคารนั้นอย่างเดียวและวางหมุดที่อาคาร ไม่วาดแผ่นสถานีซ้ำ (เจ้าของแจ้ง 1 พ.ย. 2569 ว่าแผ่นสถานีที่จุดอ้างอิงกับอาคาร OSM อ่านเป็นสองสถานี) จุดอ้างอิงในข้อมูลไม่เปลี่ยน แผงสถานีบอกระยะระหว่างจุดอ้างอิงกับอาคารเมื่อห่างตั้งแต่ 20 ม. (ค่ากลาง 12 ม.; 19 ระเบียนห่างเกิน 50 ม. เช่น เจริญนคร 150 ม., บางเขน 280 ม.)
- **อาคารสถานี:** จับคู่สถานี OSM กับระเบียนด้วยรหัสสถานี (`ref` เช่น N8, BL13) หรือชื่อไทย/อังกฤษ ภายใน 400 ม. แล้วเลือกอาคารที่ครอบจุดนั้น หรือ `building=train_station` ภายใน 80 ม. บันทึกวิธีจับคู่ ระยะ รหัส way/relation และลิงก์ OSM ต่ออาคาร ความสูงจาก `height` หรือ `building:levels × 3.5 ม.`; ถ้าไม่มี ใช้ค่าตั้งต้นเพื่อแสดงผล (`height_basis` = display_default) และบอกในแผงสถานี
- **สีสาย:** แท็ก `colour` ของ route relation ที่ชื่อ/รหัสตรงกับตาราง `ROUTE_KEYS` (ตรวจด้วยคนได้ในโค้ด) ถ้าหลาย relation ให้สีต่างกันจะใช้สีที่พบบ่อยที่สุดและรายงานใน `build-report.json`
- **แนวก่อสร้างในเมือง:** ทาง `railway=construction` ที่ชื่อมีสีส้ม/สีม่วง กำหนดให้โครงการที่สถานีอยู่ใกล้ที่สุด (≤ 2.5 กม.) พร้อมป้าย “แนวจาก OpenStreetMap ไม่ใช่แบบก่อสร้างทางการ”

อาคาร ความสูง และระดับทางจาก OSM เป็นบริบทเชิงภาพที่ชุมชนบันทึก ไม่ใช่ผลรังวัดหรือแบบก่อสร้าง

## 9. ชั้นขนส่งอื่น (ปิดไว้เป็นค่าเริ่มต้น เปิดจากปุ่ม “ขนส่งอื่น”)

เจ้าของแจ้ง 31 ต.ค. 2569 ว่ารถเมล์ วินมอเตอร์ไซค์ บขส. และโหมดอื่นหายไป: ในรุ่น 0.1 เส้นรถเมล์และเรือถูกวาดใต้ชั้นถนนจึงมองไม่เห็นแม้เปิดชั้นแล้ว (แก้แล้ว: วาดเหนือถนน ใต้สายราง) และยังไม่มีชั้นของโหมดอื่น รุ่นนี้เพิ่มทุกโหมดใน research master ที่มีพิกัด ระบบรางยังเป็นชั้นหลัก แต่ละโหมดใช้ **รูปทรง** ต่างกัน (ไม่ใช่ categorical palette) และเจ้าของกำหนดเพิ่มว่าเมื่อเปิดแล้วต้องเห็นได้ตลอดทุกหน้าจอ: ไม่มีระดับซูมขั้นต่ำ (สัญลักษณ์เล็กลงเมื่อซูมออก ป้ายรถเมล์เป็นเนื้อจุดละเอียด) สัญลักษณ์ไม่รอฟอนต์ของชื่อ และปุ่มของทุกชั้นอยู่ในแถว “ขนส่งอื่น” ใต้แถบเครื่องมือเสมอทั้งจอกว้างและมือถือ (กดเปิด/ปิด มี ✓ เมื่อเปิด กดคำว่า “ขนส่งอื่น” เพื่อดูจำนวนและที่มา) สายรถเมล์กับป้ายรถเมล์แยกเป็นสองชั้น

- **รถเมล์** (`data/bus.json`, โหลดเมื่อเปิด): route ของรถเมล์ใน GTFS ที่อยู่ในปฏิทินวันที่ 30 ต.ค. 2569 และอยู่ในกรอบกรุงเทพฯ และปริมณฑล (1,327 route; ใช้ shape ที่ยาวที่สุดของแต่ละ route) รวมแบบ coverage union ภายใน 25 ม. แล้วนับว่ามีกี่ route ผ่านแต่ละช่วงถนน แบ่ง 5 ระดับ (1, 2–3, 4–7, 8–15, 16+) แสดงด้วย **ความหนาของเส้น** สีเดียว (ไม่ใช่สีข้อมูล) — เป็นจำนวนระเบียนเส้นทาง ไม่ใช่ความถี่ จำนวนรถ หรือผู้โดยสาร ป้ายรถเมล์ 15,502 จุดเป็น provider stop reference ที่อยู่ในปฏิทิน (ไม่ใช่จำนวนป้ายจริง) พร้อม BRT 1 สายและ 12 สถานีที่มีพิกัด
- **เรือ** (`data/boat.json`): route ของเรือด่วน เรือคลอง และเรือข้ามฟากใน GTFS ภายในกรุงเทพฯ และปริมณฑล กับท่าเรือที่อยู่ในปฏิทินหรือรายชื่อผู้ให้บริการ — แนวเส้นของแหล่ง ไม่ใช่ร่องน้ำที่สำรวจ
- **วินมอเตอร์ไซค์รับจ้าง** (`data/layer-moto.json`, ▲): 2,438 จุดของ grain `motorcycle_taxi_stand` (ข้อมูลเปิด กทม. ชั้น DISTRICT66 แก้ไขล่าสุด ก.ย.–ธ.ค. 2566 — ไม่ใช่วันสำรวจ) ไม่รวมรายการจากเอกสารปี 2562 (780) และแผนที่ กทม. ที่ไม่ระบุวันที่ (5,341) ซึ่ง research master แยกเป็นคนละ grain ไม่ใส่ข้อมูลส่วนบุคคลของผู้ดูแลวิน ขอบเขตคือ กทม. เท่านั้น สถานีนอก กทม. จึงขึ้นว่า “นอกขอบเขตข้อมูล” ไม่ใช่ 0 จุด **สัญญาอนุญาตของแหล่งไม่ระบุ** (research master บันทึกว่าการเข้าถึงสาธารณะไม่ใช่การอนุญาตเผยแพร่ซ้ำ) — เจ้าของควรยืนยันก่อนใช้ภายนอก
- **รถโดยสารระหว่างเมือง** (`data/layer-intercity.json`, ◆/◇): สถานีขนส่งผู้โดยสาร 126 แห่ง (กระทรวงคมนาคม พ.ค. 2569) และจุดขึ้นรถของ บขส. 31 จุด (สถานีเดินรถ 8, จุดจอด/จำหน่ายตั๋ว 23 — ตรงกับ 31 รายการที่ research master ระบุว่าเป็นจุดเข้าถึงได้) ไม่รวมที่ทำการ ด่านตรวจ จุดพัสดุ ไม่ได้ตัดรายการซ้ำข้ามสองแหล่ง
- **รถไฟทางไกล** (`data/layer-srt.json`, ■): ที่หยุดรถ 176 จุดที่มีเที่ยวตามตาราง GTFS วันที่ 30 ต.ค. 2569 (ไม่ใช่ทะเบียนสถานีทั้งหมดของ รฟท.) ไม่วาดแนวราง เพราะ shape ของรถไฟทางไกลใน GTFS หยาบมาก (เช่น เชียงใหม่–กรุงเทพมีราว 25 จุด) ใช้แนวรางของแผนที่ฐานแทน
- **ท่าอากาศยาน** (`data/layer-air.json`, เครื่องบิน): 32 แห่งตาม eAIP ของ กพท. (มีผล 29 ต.ค. 2569) จุดคือจุดอ้างอิงสนามบิน (ARP) ไม่ใช่อาคารผู้โดยสาร
- จำนวนและที่มาของทุกชั้นอยู่ใน `meta.contextLayers` ของ `data/transit.json` (เมนูแสดงก่อนโหลดไฟล์)

**ขนส่งอื่นรอบสถานี** (แผงสถานี): นับจุดของแต่ละชั้นในรัศมี 500 ม. จากจุดอ้างอิงสถานี และบอกจุดที่ใกล้ที่สุด เป็นระยะเส้นตรง ไม่ใช่ระยะเดิน ชั้นที่ไม่ครอบคลุมสถานี (นอกกรอบ หรือวินนอก กทม.) แสดงว่า “นอกขอบเขตข้อมูล” — ไม่มีข้อมูลไม่แสดงเป็นศูนย์ ส่วนในขอบเขตแล้วไม่พบคือศูนย์ที่วัดได้ของแหล่งนั้น ท่าอากาศยานแสดงเฉพาะระยะใกล้สุดเพราะจุด ARP ไม่ใช่ทางเข้าอาคาร

## 10. สิ่งที่ข้อมูลนี้ไม่ได้พิสูจน์

- การเดินรถจริง ความถี่ เวลาเดินทาง ความแออัด หรือความต่อเนื่องของบริการ
- การเดินเชื่อมจริงระหว่างสถานี เวลาเดิน ระยะเดิน ทางลาด/ลิฟต์ (ยกเว้นที่แหล่งระบุ) หรือการต่อในพื้นที่ชำระเงิน
- มูลค่าทำเล ราคาที่ดิน จำนวนผู้อยู่อาศัย หรือผลทางธุรกิจรอบสถานี — ความใกล้สถานีเป็นปัจจัยหนึ่งเท่านั้น
- วันเปิดของโครงการ (เป็นเป้าหมายตามแหล่ง) และความคืบหน้ารายสถานี (มีเพียง 1 สถานี)
- ความครบถ้วนทั่วประเทศ: ชั้นหลักครอบคลุมรถไฟฟ้า/รถไฟชานเมืองที่เปิดในกรุงเทพฯ และปริมณฑล กับโครงการรางที่มีรายชื่อสถานี รถไฟทางไกลแสดงเป็นชั้นเสริมเฉพาะที่หยุดรถที่มีเที่ยวตาม GTFS ยังไม่รวมทะเบียนสถานี รฟท. ที่ research master จัดเป็นรายการเก่าที่ยังยืนยันบริการปัจจุบันไม่ได้
- การให้บริการของวิน สถานีขนส่ง จุด บขส. ท่าเรือ และท่าอากาศยานในวันนี้ — ทุกชั้นเป็นรายการตามแหล่งในช่วงเวลาของแหล่ง

## 11. ทำซ้ำ

```sh
python3 -I scripts/build-data.py --export <โฟลเดอร์ไฟล์ส่งออก> [--osm <osm-rail-stations-2026-10-31.json> | --osm-derived sources/osm/osm-derived-2026-10-31.json]
npm test
```

ผลลัพธ์: `data/transit.json`, `data/rail-lines.geojson`, `data/station-buildings.geojson`, `data/bus.json`, `data/boat.json`, `data/layer-{moto,intercity,srt,air}.json` และ `data/build-report.json` (จำนวน การพัก และ sha256 ของแต่ละไฟล์) build ใช้ Python มาตรฐานเท่านั้นและให้ผลเหมือนเดิมเมื่ออินพุตเดิม
