# Data and method

ข้อมูลและวิธีของ CityMETER · Restaurants รุ่น 0.8.0 (3 พฤศจิกายน 2569; ร้านชุดเดียวกับรุ่น 0.1 ดึง 22 ต.ค. 2569 — ชีต 1 ฉบับล่าสุดตรงกับฉบับเดิมทุกไบต์ — ทะเบียนร้านเครือฉบับอ่าน 3 พ.ย. 2569 §8.2, ราคา 6 ช่วงและขนาดเครือจากแท็บจัดกลุ่มของทะเบียน §8.5, ตรวจ spatial join กับชุดข้อมูลกลุ่ม Land §8.3; รุ่นเอกสารก่อนหน้า 0.4.0 วันที่ 28 ต.ค. 2569) ป้ายหลักฐานตาม Landometer Master Brand Brief v0.5.3 §3.7: **Observed fact** · **Inference** · **Hypothesis** · **Owner-stated** · **Project evidence** ตัวเลขทุกตัวในเอกสารนี้มาจากไฟล์ใน repository และอ้าง field path ได้

## 1. แหล่งข้อมูลและวันที่

| ชั้น | แหล่ง | วันที่ | ไฟล์ในเดโม |
|---|---|---|---|
| ร้าน (แถวข้อมูล) | CityMETER Restaurants public API — overlay MVT ระดับ z10 ต่อแขวง/ตำบล (`/api/v2/citymeter-v3/restaurant/overlayMvt/{z}/{x}/{y}?inTileType=SUBDISTRICT&inTileId={id}`) ผ่านชีตวิจัย “CityMETER Restaurants Thailand — Master” (ชีต 1) แท็บ `02_STORES` และแถวที่ย้ายไปชีต 2 “CityMETER Chain Restaurants Thailand — Research Master” แท็บ `16_MASTER_OBSERVATIONS` (§2) | ร้านดึงและตรวจกับ API 22 ต.ค. 2569 · อ่านชีตทั้งสอง 28 ต.ค. 2569 | `data/restaurants.json` |
| ทะเบียนร้านเครือ (ชุดแยก) | ชีต 2 แท็บ `02_STORES` (สาขา), `01_BRANDS`, `03_COMPANIES`, `08_COVERAGE` — หลักฐานรายสาขาจากเว็บ/แอปของแบรนด์ บริษัท สถานที่ และแหล่งรอง | ตรวจ 25–28 ต.ค. 2569 (`meta.checkedFrom/checkedTo`) | `data/chains.json` |
| จังหวัด 77 จังหวัด (ทะเบียน + มุมมองทั้งประเทศตั้งแต่ 0.6.0) | CityMETER production `/api/v2/citymeter-v3/restaurant/info/geom/{id}/PROVINCE` (อ่านอย่างเดียว; ใบรับ 70 ใบใน `data/boundary-receipts.json` → `provinceReceipts`) | 28 ต.ค. 2569 | ใช้จัดจังหวัดของสาขาตอน build; `data/provinces.geojson` 77 กรอบ = 7 จังหวัดจากกรอบแขวง/ตำบล + 70 จังหวัดจากรูปจังหวัดของ production ลดทอน 300 ม. เพื่อแสดงผล (`scripts/build-provinces.py`) |
| ทะเบียน Land | ชีตวิจัย Shopping Centers & Retail Venues (`01_VENUES`, `13_BUILDING_LINKS`), Hotel (`01_VENUES`, `13_BUILDING_LINKS`, `15_BUILDING_MASS`, `11_STORE_LINKS`), Office (`01_OFFICES`), Industrial Estates (`01_ESTATES`, `03_GEOMETRY_VERSIONS`) ตาม registry R11/R12 | อ่าน 28 ต.ค. 2569 | `data/land.json` (เฉพาะ host ที่มีจุดในอาคาร; ตำแหน่งแสดงผลของจุดที่รวบเข้าอาคารใน `snaps`) |
| อาคารของ host ใน Land | รูปอาคาร OpenStreetMap จาก tile OpenFreeMap z14 ชั้น `building` (planet 20261025_080012_pt; 938 tiles ครอบจุด host ดึงในเบราว์เซอร์จากชุด tile เดียวกับที่แผนที่วาด) — หลังที่มีจุดของ host หรือใกล้ไม่เกิน 10 ม.; รูปทรงที่ทะเบียนตรวจแล้ว (Shopping Center release, Hotel `15_BUILDING_MASS`) ใช้เลือกหลังและใส่ส่วนอาคาร LOD2 เท่านั้น ไม่ใช้ footprint V4 (เจ้าของ 28 ต.ค. 18:27) | 28 ต.ค. 2569 | `data/land-buildings.geojson` |
| เครือร้าน (chain candidate) | ชีตวิจัยแท็บ `01_BRANDS` (จับคู่จากชื่อร้าน) + manifest โลโก้ | 22 ต.ค. 2569 | `data/restaurants.json → brands`, `data/brand-logos.json`, `assets/brand-logos/` |
| สถานที่ (venue candidate) | ชีตวิจัยแท็บ `07_VENUES` (ชื่อร้านอ้างถึงสถานที่) + manifest โลโก้ | 22 ต.ค. 2569 | `data/restaurants.json → venues`, `data/venue-logos.json`, `assets/venue-logos/` |
| ขอบเขตแขวง/ตำบล เขต/อำเภอ จังหวัด | CityMETER production public API (`/restaurant/info/geom/{uuid}/SUBDISTRICT`, `/base/info/lv1/{province uuid}`) | 22 ต.ค. 2569 | `data/areas.geojson`, `data/districts.geojson`, `data/provinces.geojson`, `data/boundary-receipts.json` |
| ชื่ออังกฤษของเขต/อำเภอ และของแขวง/ตำบลที่ production ไม่มีชื่ออังกฤษ | OpenGISData-Thailand `districts.geojson` / `subdistricts.geojson` @ `fafcea1` (CC BY 4.0, อ้าง DOPA) — ใช้อ่านชื่อในหน้าภาษาอังกฤษ ไม่ใช้เป็นขอบเขต | ตาม commit | `data/districts.geojson → metadata.englishNames`, `data/areas.geojson → metadata.englishNames`, `metadata.englishFromOgd` |
| อาคารของสถานที่ | ชุด host buildings ของ CityMETER · Luxury Brands รุ่น 0.5 (footprint จาก OpenStreetMap ODbL 1.0, ส่วนอาคารจาก CityMETER) จับคู่ด้วยชื่อสถานที่ตรงตัวหลัง normalize; ส่วนอาคาร LOD2 ของ Shopping Center release แทนเมื่อละเอียดกว่า จับด้วย uuid ของ CityMETER (§8.4) | footprint ดึง 21 ต.ค. 2569 | `data/venue-buildings.geojson` |
| แผนที่ฐานและมวลอาคารเมือง | OpenFreeMap (OpenMapTiles schema, © OpenStreetMap contributors) | โหลดตอนเปิดหน้า | ไม่มีไฟล์ใน repository |

**Observed fact — ตรวจย้อนกับ API (22 ต.ค. 2569):** ดึง overlay ทั้งชุดใหม่จาก API สาธารณะ (399 tile requests, ทุกคำขอ HTTP 200) แล้วเทียบกับ `data/restaurants.json` ที่สร้างจากชีต: คีย์ตรงกัน 70,214/70,214 แถว และไม่มีความต่างเลยในชื่อ, primary type, ราคาเริ่ม/สิ้นสุด, ระดับราคา, คะแนน, จำนวนรีวิว, รหัสแขวง/ตำบล และพิกัดแสดงผล (ต่างสูงสุด 0°) ผลอยู่ใน handoff/VERIFICATION.md เดโมจึงถือชีตเป็นแหล่งบันทึก และ API สาธารณะเป็นแหล่งต้นทาง

**ข้อจำกัดต้นทาง (Observed fact จาก `meta.sourceLimitation`):** API สาธารณะไม่เปิดเผยผู้ให้ข้อมูลต้นทาง วันที่เก็บข้อมูล source UUID ที่อยู่ เว็บไซต์ หรือพิกัดต้นฉบับ ห้ามอนุมานว่ามาจาก Google เพียงเพราะชื่อ field คะแนน/รีวิว

## 2. หน่วยข้อมูล

หนึ่งแถว = หนึ่ง **observation ของร้านใน overlay สาธารณะ** (บนหน้าจอเรียก “ร้าน”) ไม่ใช่หนึ่งกิจการที่ยืนยันแล้ว

- **ที่มาของแถว (`meta.rowSources`):** ชีต 1 แท็บ `02_STORES` 68,980 แถว + 1,234 แถวที่เจ้าของย้ายไปชีต 2 แท็บ `16_MASTER_OBSERVATIONS` เมื่อ 25 ต.ค. 2569 (ร้านในพอร์ตเครือ ใช้ field `master_*`) — `snapshot_observation_id` เดียวกัน นับครั้งเดียว ไม่หล่นและไม่ซ้ำ แถวที่ย้ายมีป้าย `register` (1 = รอยืนยันเครือ/สาขา 1,179 แถว, 2 = มีรายการสาขาที่ตรงกันในทะเบียน 55 แถว) และ `registerOutlets` (รหัสในทะเบียนที่อาจตรงกัน 846 แถว) — ป้ายนี้ไม่เปลี่ยนการนับใด
- **70,214 แถว** (`meta.rows`) ใน 7 จังหวัด: กรุงเทพมหานคร 44,086 · นนทบุรี 11,532 · สมุทรปราการ 11,505 · ปทุมธานี 1,999 · สมุทรสาคร 883 · ฉะเชิงเทรา 189 · นครปฐม 20 (`meta.rowsByProvince`) — Observed fact ของขอบเขตชุดข้อมูล ไม่ใช่สำมะโนร้านอาหารของประเทศ และไม่ใช่หลักฐานว่าจังหวัดอื่นไม่มีร้าน
- การคัดแถว (ทำโดยงานวิจัยต้นทาง): overlay ต่อแขวงส่งจุดที่อยู่นอก tile ที่ขอมาด้วย (`clip_geom=false`) จึงเก็บเฉพาะ observation ที่พิกัดตกใน tile ของตัวเอง (owning tile) ตัด 24,214 มุมมองซ้ำของการขนส่งข้อมูล โดยไม่รวมร้านด้วยชื่อหรือพิกัด สี่คู่ observation ที่แยกไม่ออกคงไว้เป็น 8 แถว
- `cols.key` = 12 หลักแรกของ `snapshot_observation_id` (+ `-NN` เมื่อ ordinal ไม่ใช่ 01) — คีย์เฉพาะ snapshot นี้ ไม่ใช่ id ของฐานข้อมูลต้นทางและไม่ใช่คีย์สำหรับอัปเดตข้ามรอบ
- สถานะ: ทุกแถวผ่านตัวกรอง operational ของต้นทาง (`meta.operatingStatus.source_filtered_operational_not_revalidated = 70214`) เดโมไม่ได้ตรวจซ้ำว่ายังเปิดอยู่

## 3. พิกัด

พิกัดคือ **ตำแหน่งแสดงผล** ที่ API สาธารณะส่งให้เบราว์เซอร์ (MVT z10, extent 4096) ไม่ใช่พิกัดต้นฉบับหรือทางเข้าร้าน (`meta.coordinate.role = mvt_display_position_z10`)

- เก็บเป็นจำนวนเต็มบนกริดโลก `n = 2^10 × 4096 = 4,194,304` ช่อง (`cols.gx`, `cols.gy`) ช่องละประมาณ **9.55 ม.** (Web Mercator)
- ถอดค่าแบบเดียวกับตัวถอดของงานวิจัย: `lon = gx/n·360 − 180`, `lat = atan(sinh(π(1 − 2·gy/n)))` (`js/model.js#gridToLngLat`) — ตรงกับ latitude/longitude ในชีตภายใน 5×10⁻⁷ องศา (ตรวจตอน build)
- แต่ละแถวอยู่ใน tile z10 ที่มีพิกัดแสดงผลของตัวเอง (`meta.tileRule`, ตรวจตอน build)
- Inference จาก source audit: backend ใช้ `ST_AsMVTGeom` กับเรขาคณิต EPSG:4326 ในกรอบ EPSG:4326 แต่เบราว์เซอร์ตีความเป็น Web Mercator — ตำแหน่งแสดงผลกับตำแหน่งต้นทางต่างกันได้ราว 5 ม. (มัธยฐาน) ก่อนรวมความคลาดเคลื่อนจากการปัดกริด พิกัดจริงต้องขอจาก source export
- ร้านที่อยู่ช่องกริดเดียวกันแสดงแยกเป็นหมุดเล็กรอบจุดเดิม (เส้นนำชี้กลับจุดเดิมเมื่อชี้/เลือก) โดยไม่ย้ายพิกัดในข้อมูล

## 4. การเป็นสมาชิกของพื้นที่

แถวทุกแถวมี **รหัสแขวง/ตำบลของต้นทาง** (`cols.area` → `areas[]`) ซึ่ง API ใช้นับร้าน (`ST_Contains` บนพิกัดต้นทาง) เดโมใช้รหัสนี้ตรง ๆ ไม่ทำ point-in-polygon ใหม่บนพิกัดแสดงผล ไม่เดาด้วยชื่อ — ตัวเลขรายพื้นที่จึงตรงกับ aggregate ของ production

- เขต/อำเภอ และจังหวัดของแถว มาจากแขวง/ตำบลนั้น (`data/areas.geojson → properties.district / province`)
- ตรวจแล้ว: ทุกแถวมีแขวง/ตำบลในชุดขอบเขต (`decodeDataset → dropped = 0`) และนับต่อจังหวัดตรงกับ `meta.rowsByProvince` (`tests/data.test.js`)
- Municipality/locale ของ production เป็นค่าประมาณจากการปันส่วนตามพื้นที่ (source audit) เดโมนี้ไม่ใช้และไม่บวกข้ามเรขาคณิตที่ซ้อนกัน

## 5. ขอบเขตพื้นที่

- **แขวง/ตำบล 581 กรอบ** ของ 7 จังหวัด (ทุกตำบลในจังหวัดเหล่านั้น) จาก geometry endpoint ของ production — เก็บใบรับ (URL, ขนาด, SHA-256, เวลา) ครบ 581 ใบใน `data/boundary-receipts.json` และผูกชุดใบรับด้วย `metadata.receiptsSha256`
- ทำให้เบาสำหรับเว็บด้วย mapshaper `-simplify interval=16 keep-shapes -clean` (คง topology) เขต/อำเภอ 90 กรอบ dissolve จากแขวง/ตำบลที่ทำให้เบาแล้ว จังหวัด 7 กรอบ dissolve แล้วทำให้เบาที่ 120 ม. สำหรับมุมมองประเทศ (`scripts/build-boundaries.py`)
- **พื้นที่ (ตร.กม.)** มาจาก `areaSqm` ของ production — ไม่คำนวณใหม่จากเส้นที่ทำให้เบา
- กรอบเป็น navigation และตัวหารของความหนาแน่น ไม่ใช่การยืนยันเขตปกครองทางกฎหมาย
- **ชื่ออังกฤษ** production ให้ชื่ออังกฤษของจังหวัดและแขวง/ตำบล 394 จาก 581 แห่ง แต่ไม่มีของเขต/อำเภอเลย `scripts/add-english-names.py` เติมจาก OpenGISData-Thailand @ `fafcea1` (ตรวจ SHA-256 ของไฟล์ก่อนใช้): เขต/อำเภอตามรหัส 90 จาก 90 (เหมือนเดโมอื่นของ CityMETER) และแขวง/ตำบลที่ production ไม่มีชื่อ เฉพาะเมื่อรหัสและชื่อไทยตรงกัน 159 แห่ง (หลักเดียวกับ Fitness) ชื่ออังกฤษของ production คงไว้เสมอ ยังไม่มีชื่ออังกฤษ 28 แขวง/ตำบลในกรุงเทพฯ และสมุทรปราการ หน้าอังกฤษจึงแสดงชื่อไทยของพื้นที่เหล่านี้ ชื่อเป็นตัวช่วยอ่าน ไม่ใช่ชื่อทางการที่ยืนยันแล้ว

## 6. ตัวชี้วัด

| ตัวชี้วัด | สูตร | หมายเหตุ |
|---|---|---|
| ร้าน | จำนวนแถวในตัวกรอง | นับ observation ไม่ใช่กิจการที่ยืนยัน |
| ร้าน/ตร.กม. (แขวง/ตำบล, เขต/อำเภอ) | ร้านในพื้นที่ ÷ `areaSqm`/10⁶ | LDS family `density.area`; ตัวหาร = พื้นที่ production |
| ร้าน/ตร.กม. ของหลายพื้นที่ | Σร้าน ÷ Σตร.กม. | ไม่เฉลี่ยอัตราส่วนรายพื้นที่ |
| ร้าน/ตร.กม. พื้นที่ที่มีข้อมูล | ร้านในตัวกรอง ÷ ผลรวมตร.กม. ของแขวง/ตำบลที่มีร้านอย่างน้อย 1 แถวในชุดข้อมูลเต็ม (ภายในจังหวัด/เขตที่กรอง) | 308 กรอบ 3,899.1 ตร.กม. เมื่อไม่กรอง |
| ร้านต่อจังหวัด | จำนวนแถว | LDS family `count` — เป็นยอดรวม ไม่ใช่ความหนาแน่น เพราะนอก กทม. นนทบุรี สมุทรปราการ แหล่งข้อมูลครอบคลุมเพียงบางตำบล |
| รีวิว | ผลรวม `rating_count` ของแถวที่ระบุ | ตัวชี้ความนิยมตามแหล่งข้อมูล ไม่ใช่ยอดขายหรือจำนวนลูกค้า |
| คะแนนเฉลี่ย | ค่าเฉลี่ยเลขคณิตของแถวที่มีคะแนน | แถวไม่มีคะแนน (16,511 แถว) ไม่ถูกนับเป็น 0 |
| ระดับราคา (ตั้งแต่ 0.8.0) | 6 ช่วงตาม `rules.price` ของทะเบียน คำนวณจากราคาเริ่ม/สูงสุดที่ระบุ (`js/model.js#priceTier`): เริ่ม ≥ 1,000 และไม่ระบุราคาสูงสุด = P6; อื่น ๆ ใช้ราคาสูงสุด (ไม่มีจึงใช้ราคาเริ่ม) เทียบ 100 / 200 / 300 / 500 / 1,000 บาท | 50,215 แถวไม่ระบุราคา (ไม่ใช่ราคา 0); ตรงกับ `price_band_observed` ทุกแถวที่จัดกลุ่ม 68,980 แถว; ถังของ API (`cols.price`, `COALESCE(end, start)`) เลิกใช้เพราะนับร้าน “เริ่ม 1,000” 886 แถวเป็น 500–1,000 (§8.5) |
| ขนาดเครือ (ตั้งแต่ 0.8.0) | ช่วงจำนวนแถวต่อเครือตาม `chain_size_band` ของทะเบียน: 1 · 2–9 · 10–49 · 50–199 · 200+ | นับแถว ไม่ใช่คำตัดสิน; “1 สาขาที่พบ” ไม่ได้แปลว่าร้านอิสระ; เครือเดียวกันใช้ช่วงที่ใหญ่ที่สุดที่แหล่งให้ (§8.5) |
| ประเภทร้าน | จัด `primary_type` ของต้นทาง 142 ค่าเป็น 10 กลุ่ม (§7) | primary type ไม่ใช่ชุดประเภททั้งหมดของสถานที่ |

## 7. กลุ่มประเภทร้าน

กลุ่มเป็นการจัดเพื่ออ่านง่าย (Inference ของผู้จัดทำ) ไม่เปลี่ยนค่าประเภทเดิม ซึ่งยังแสดงในรายละเอียดร้านและตาราง (`scripts/build-data.py#TYPE_GROUP`)

| กลุ่ม | ประเภทต้นทางที่รวม (ตัวอย่าง) | แถว |
|---|---|---:|
| ร้านอาหารทั่วไป | Restaurant, Food, Diner, Food Court, Meal Takeaway, Vegetarian/Vegan Restaurant | 50,886 |
| อาหารไทย | Thai Restaurant | 4,547 |
| ฟาสต์ฟู้ด | Fast Food, Hamburger, Pizza, Sandwich Shop | 4,504 |
| คาเฟ่และของหวาน | Coffee Shop, Cafe, Dessert, Bakery, Ice Cream, Breakfast/Brunch | 2,668 |
| ญี่ปุ่น | Japanese, Sushi, Ramen | 2,260 |
| จีนและเอเชีย | Chinese, Korean, Asian, Vietnamese, Indian, Indonesian | 2,002 |
| ปิ้งย่าง บุฟเฟต์ ซีฟู้ด | Steak House, Barbecue, Buffet, Seafood, Bar And Grill | 1,994 |
| อื่น ๆ | ประเภทที่ไม่อยู่ในกลุ่มข้างต้น เช่น Bar, Hotel, Store, Gas Station, Market | 779 |
| ตะวันตกและนานาชาติ | Italian, American, French, Mexican, Mediterranean, Fine Dining | 435 |
| ไม่ระบุประเภท | ไม่มี primary type | 139 |

Observed fact จาก source audit: ชุด Restaurants ของ production ไม่มีเงื่อนไขประเภทร้าน — รวมสถานที่ operational ทั้งหมดในตารางต้นทาง จึงมี 130 แถวที่ primary type ระบุชัดว่าไม่ใช่อาหาร (เช่น ร้านเสริมสวย ซักรีด ธนาคาร) ซึ่งเดโมคงไว้ในกลุ่ม “อื่น ๆ” และไม่ลบ หมวด Café ของ production ใช้ type tag ทั้งหมด (`%cafe%`) จึงสร้างซ้ำจาก primary type ไม่ได้ กลุ่ม “คาเฟ่และของหวาน” ของเดโมไม่ใช่ตัวเลข Café ของ production

## 8. เครือร้านและสถานที่ (enrichment)

- **เครือร้าน** 169 รายการ (`brands`) จับคู่จากชื่อร้านที่ระบุเครือชัด (`01_BRANDS`); `national` = รหัสเครือในทะเบียนร้านเครือที่ชีตระบุ (`01_BRANDS.national_brand_ids`) ใช้เชื่อมไปทะเบียนด้วย id เท่านั้น สถานะลิงก์ทุกแถวคือ “จับคู่จากชื่อ ต้องตรวจ” (`meta.brandRule`) — ไม่ได้ยืนยันว่าเป็นสาขาจริงของเครือ บนหน้าจอใช้คำว่า “ร้านอื่นที่ชื่อตรงกับ…” ไม่ใช้ “สาขา”
- 3 เครืออยู่ระหว่างยืนยันตัวตน (identity hold) จึงไม่มีโลโก้
- **สถานที่** 1,100 รายการ (`venues`) ได้จากชื่อร้านทั้งหมด — ไม่ได้ยืนยันว่าร้านตั้งอยู่ภายใน (`meta.venueRule`) สถานที่จึงไม่เป็นระดับของ breadcrumb ใต้แขวง/ตำบล จับคู่ 3 ขั้น (เจ้าของตัดสิน 23 ต.ค. 2569; `meta.venueSuffix`, แถว `venueMatch`):
  1. ลิงก์ในชีตวิจัย (`07_VENUES`) — 1,016 สถานที่
  2. แถวที่ชีตไม่ได้ผูก: ชื่อหรือชื่อเรียกอื่นของสถานที่ในชีตอยู่ **ท้าย** ชื่อร้าน (ตามด้วยคำบอกชั้น โซน สาขา หรืออาคารผู้โดยสารได้) ชื่อเรียกต้องไม่ซ้ำหลายสถานที่ ไม่ใช่ชื่อเครือทั่วไป (เช่น ปตท. โลตัส Big C) และยาว ≥ 6 ตัวอักษร — 3 แถว เช่น “Shabu Shabu Nangnai (Panya Market Branch)” → Panya Market (รุ่น 0.2.1 มี 11 แถว; ชีตฉบับ 28 ต.ค. ผูก 8 แถวเองแล้ว เช่น “KFC Gateway Bangsue” → Gateway Bangsue และผูกเพิ่มอีก 19 แถว)
  3. คำลงท้ายแบบชื่อสถานที่ (ตลาด… อาคาร… …Tower …Avenue …Market ฯลฯ) ที่ซ้ำกันตั้งแต่ 2 ชื่อ และทุกจุดอยู่ในรัศมี 200 ม. จากจุดกลางของกลุ่ม (ตัดจุดไกลจนคงที่) ตั้งเป็น “สถานที่จากชื่อร้าน” id `RX-…` — 84 สถานที่ 252 แถว เช่น Charn At The Avenue (14 ร้าน) ภาษาไทยที่เว้นวรรคต่างกัน (“ตลาดพระราม 5” / “ตลาดพระราม5”) รวมเป็นที่เดียว
  - คำบอกตำแหน่งข้างเคียงก่อนชื่อสถานที่ (หน้า ข้าง ตรงข้าม ใกล้ หลัง ติด เยื้อง near opposite behind next to …) แปลว่าอยู่ใกล้ ไม่ใช่อยู่ใน จึงไม่จับคู่ — 41 แถว เช่น “ข้าวต้มลุงเล็ก หน้าโลตัสศรีนครินทร์”
- ไม่มีการอนุมานเครือหรือสถานที่จากระยะใกล้ รัศมี 200 ม. ของขั้นที่ 3 ใช้กันชื่อเดียวกันที่อยู่คนละที่ไม่ให้รวมเป็นสถานที่เดียว ไม่ได้ใช้ผูกร้านที่ชื่อไม่ได้อ้างถึง
- **อาคารของสถานที่** 26 สถานที่ (61 features) จับคู่ชื่อสถานที่ตรงตัวกับชุด host buildings ของ Luxury Brands และเอ็ม บี เค เซ็นเตอร์ด้วย uuid (§8.4) — ในพื้นที่ที่โฟกัสแสดงอาคารของทุกสถานที่ที่ชื่อร้านในโฟกัสอ้างถึง และกดเปิดสถานที่ได้ ความสูงเป็นค่าแสดงผลตาม CityMETER/OSM ไม่ใช่ผลรังวัด

## 8.1 จุดของร้าน × อาคารบนแผนที่ (spatial join ตอนแสดงผล)

เจ้าของตัดสิน 23 ต.ค. 2569 (`js/building-join.js`): จุดแสดงผลของร้าน (กริด 9.55 ม.) จับกับขอบอาคารที่แผนที่วาด — อาคาร OpenStreetMap ใน tile ของ OpenFreeMap ที่ส่วนโฟกัสใช้ (ขอบเดิมก่อนทำมุมโค้ง) และอาคารของสถานที่ข้างบน จุดที่ตกในหลายขอบ (ส่วนอาคารซ้อนในขอบใหญ่) ไปที่ขอบเล็กสุดในบรรดารูปที่ตั้งบนพื้น (ตั้งแต่ 0.8.4: ยอดตึกที่ OpenStreetMap วาดเป็นรูปลอยเหนือตัวตึกไม่ถูกเลือกเป็นอาคาร และเดินตามอาคารที่มันวางอยู่)

- คำที่ใช้: “จุดตกในอาคาร” ไม่ใช้ “ตั้งอยู่ใน” — ความละเอียดของจุดราว 9.55 ม. จุดอาจตกในอาคารข้างเคียงหรือถนนได้ และไม่บอกชั้นหรือห้อง
- อาคารของสถานที่บนจอ: ข้อมูลอาคารของสถานที่ถ้ามี มิฉะนั้นอาคาร OSM ที่มีจุดของร้านซึ่งชื่ออ้างถึงสถานที่ตั้งแต่ max(1, ⌈20 %⌉) ของจุดที่จับได้ — ใช้สีข้อมูลเดียวกับอาคารของสถานที่ (`--map-active-layer`)
- อาคารของร้านหรือสาขาที่เปิดดู (0.8.4–0.8.5): อาคาร OSM ที่จุดของร้านตกทั้งหลัง — เมื่อ OpenStreetMap วาดอาคารเป็นหลายส่วน (ขอบอาคารที่ tile ซ่อนไว้ `hide_3d` ครอบส่วนต่าง ๆ) ทุกส่วนที่จุดกึ่งกลางอยู่ในขอบเดียวกัน ไม่เช่นนั้นตัวอาคารพร้อมส่วนที่ซ้อนอยู่บนอาคาร — ขึ้นสีข้อมูลเดียวกัน (การนับ “จุดตกในอาคาร” ยังนับตามรูปที่จุดตก ไม่เปลี่ยน) มี x-ray และกล้องสองจุดเหมือนสถานที่ — ร้านในอาคาร Land หรือในสถานที่ที่มีข้อมูลอาคารใช้ชั้นของตัวเอง
- กดอาคารในพื้นที่ที่โฟกัส: อาคารของสถานที่ → สถานที่ · มีจุดร้านเดียว → ร้าน · หลายจุด → รายการ “ร้านที่จุดตกในอาคารนี้” · ไม่มีจุด → เลือกแขวง/ตำบลตรงนั้นตามปกติ
- ไม่เปลี่ยนแถวหรือจำนวนของสถานที่: ร้านที่จุดตกในอาคารเดียวกันแต่ชื่อไม่ได้อ้างถึงสถานที่ แสดงแยกเป็น “ร้านอื่นที่จุดตกในอาคารเดียวกัน”
- ทำในเบราว์เซอร์เฉพาะอาคารที่วาดในส่วนโฟกัส (tile z14 ไม่เกิน 9 แผ่น) — ไม่มีตารางผลลัพธ์ถาวร และผลขึ้นกับ OSM ณ เวลาที่ tile ถูกสร้าง

## 8.2 ทะเบียนร้านเครือทั่วประเทศ (ชุดแยก รุ่น 0.3)

แหล่ง: ชีต 2 “CityMETER Chain Restaurants Thailand — Research Master” ตรวจ 25–28 ต.ค. 2569 และอ่านกลับเติมพิกัดและโลโก้ 29 ต.ค. 2569 (ไฟล์ฉบับ 30 ต.ค.; `data/chains.json → meta.checkedFrom/checkedTo/readBackOn`) สร้างด้วย `scripts/build-chains.py` (ตั้งแต่ 0.7.1 รับโฟลเดอร์โลโก้ของทะเบียนด้วย `--logo-files`)

- **หน่วยนับ (Owner-stated จาก README ของทะเบียน):** หนึ่ง `outlet_id` ต่อสาขา/จุดขายที่มีหลักฐานผ่านเกณฑ์ รวมครัวเดลิเวอรีและจุดขายรถเข็นที่แยกสถานะแล้ว — ไม่ใช่จำนวนร้านที่เปิดอยู่จริงจากการสำรวจภาคสนาม ทะเบียนเองระบุว่ายังไม่รับรองว่าครบทุกสาขาปัจจุบันทั่วประเทศ (`meta.coverageStatus`)
- **จำนวน (Observed fact, ฉบับอ่าน 3 พ.ย. 2569, รุ่น 0.8.0):** 27,527 สาขาในทะเบียน (ตรงกับ README ของทะเบียน; 0.7.1 ฉบับ 30 ต.ค.: 27,120; 0.6–0.7.0: 26,819) · 907 เครือที่มีสาขา (จาก 1,249 รหัสแบรนด์/รูปแบบ/พอร์ตอ้างอิง; 0.7.1: 821 จาก 1,149) · 397 ระเบียนบริษัท/กลุ่มและบทบาท (0.7.1: 336) · หลักฐาน: แหล่งของแบรนด์ บริษัท หรือสถานที่ 20,375 · แหล่งรอง รอยืนยันจากแบรนด์ 4,734 · จุดขายรถเข็น 2,418 · สถานะตามแหล่ง: ระบุสาขา 21,992 · รอยืนยัน 5,387 · ระบุปิดชั่วคราว 104 · ระบุปิด/ไม่ใช้งาน 44 (ยังไม่ยืนยัน) · วันตรวจ 25–31 ต.ค. 2569 · เติมพิกัดและโลโก้ 30 ต.ค. 2569
- **ประชากรเดียวตั้งแต่ 0.6.0 (Owner-stated 28 ต.ค. 2569 22:36 “จำนวนร้านต้อง 7x,xxx + 26,xxx แสดงนับรวมกัน”):** สาขาในทะเบียน (outlet_id; 27,120 ตั้งแต่ 0.7.1 → ประชากร 96,100) เป็นแถวของประชากรเดียวกับร้านในชุดข้อมูล (observation ของ overlay) — สัญลักษณ์หมุด — นับรวมในทุกตัวเลขร้าน ความหนาแน่น (สาขาที่จุดตกในกรอบ) กรอบจังหวัด รายการ และตาราง; แถวที่ย้าย 1,234 แถวนับครั้งเดียวฝั่งทะเบียน; คะแนน รีวิว ราคามีเฉพาะฝั่งชุดข้อมูล (ไม่เติม tile ที่ไม่มีค่าแสดง —); สาขาที่ไม่มีพิกัดอยู่ในยอดรวมและรายชื่อจังหวัด (“ไม่ทราบจังหวัด”) ไม่อยู่ในความหนาแน่น · กติกา 0.3 “ไม่บวกกับ snapshot” ยกเลิกเฉพาะการนับและการแสดงผล
- **คำเดียวตั้งแต่ 0.7.0 (Owner-stated 29 ต.ค. 2569 23:39 “เลิกแยกร้านทะเบียนร้านเครือ … search / filter / คำนวณ / aggregate รวมกันทั้งสองตาราง”):** หน้าจอเรียกทุกแถวว่า “ร้าน” (สาขาเครือบอกหน่วยในบรรทัดย่อยว่า “สาขาเครือ”) ไม่มีบรรทัด “ร้าน = N + M” ไม่มีทางเข้า “ทะเบียน” ในหน้าหลัก ความต่างของสองชีตบอกในบรรทัดแหล่งข้อมูลท้ายหน้าเท่านั้น · **เครือหนึ่งคีย์ข้ามสองชีต** (`js/chains.js`): คีย์ = รหัสเครือในทะเบียนเมื่อ candidate จากชื่อร้านในชีต 1 เชื่อมถึง (`national_brand_ids[0]`) ไม่เช่นนั้นรหัส candidate; ตัวกรอง “เครือร้าน” จึงมีครบ 792 เครือ นับร้านในชุดข้อมูล + สาขาเครือเป็นตัวเลขเดียว และหน้าเครือคือภาพรวมภายใต้ตัวกรองนั้น (มุมมอง “เครือในทะเบียน” เดิมยกเลิก ลิงก์ `?chain=` เปิดเป็น `?brand=`) · **ประเภทของสาขาเครือเป็นค่าอนุมาน** (`js/model.js#deriveChainGroups`, Inference): กลุ่มประเภทส่วนใหญ่ของร้านในชีต 1 ที่ candidate เชื่อมถึงเครือนั้น (อย่างน้อย 3 ร้านและเกินครึ่ง; ร้านที่ไม่ระบุประเภทไม่นับ) ไม่เช่นนั้นกลุ่มที่รหัส `format` ของทะเบียนบอกตรง ๆ เป็นคำเต็ม (รหัสที่มีคำว่า restaurant ไม่บอกอะไร เว้นพิซซ่า/เบอร์เกอร์/แซนด์วิช) ไม่เช่นนั้น “ไม่ระบุประเภท” — ทะเบียนฉบับ 28 ต.ค.: 190 เครือได้ประเภท (จากร้าน 83 · จาก format 107) 15,800 จาก 27,120 สาขา (ฉบับ 28 ต.ค.: 188 เครือ 15,751 จาก 26,819); หน้าสาขาพิมพ์ที่มาพร้อมคำว่า (อนุมาน); ราคา คะแนน รีวิว ไม่อนุมาน · **ชื่อขึ้นด้วยเครือ** (`js/model.js#entryParts`): โลโก้ · เครือ · สาขา ทุกรายการ ตาราง ป้ายบนแผนที่ และหัวหน้า (ร้านที่ไม่มี candidate ใช้ชื่อตัวเอง) · **ร้านที่ไม่มีพิกัดบอกตรง ๆ** (“ยังไม่มีพิกัด N ร้าน — อยู่ในตัวเลข ไม่อยู่บนแผนที่”) ไม่ใช้จุดของสถานที่หรือจุดกลางแทน — ฉบับ 28 ต.ค. 625 จาก 756 เครือไม่มีสาขาที่มีพิกัดเลย (เช่น TP TEA 12/12) ผู้ใช้จึงเห็นแผนที่ว่างจนกว่าทะเบียนจะเติมพิกัด (เจ้าของแจ้ง 29 ต.ค. ว่าเติมแล้ว รอไฟล์; N22)
- **พิกัด:** ใช้พิกัดที่ทะเบียนเลือก (locator ของแบรนด์/บริษัท หรือแหล่งอื่น) — 22,547 สาขามีจุด (81.9%; ฉบับ 30 ต.ค.: 22,182; ฉบับ 28 ต.ค.: 16,728) อีก 4,980 สาขาไม่มีจุดจึงไม่วาด (ไม่ใช้จุดกลางของสถานที่หรือแผนที่แทน) · ทะเบียนเก็บ “พิกัดประมาณ” แยกอีก 938 แถว (ฉบับ 30 ต.ค.: 936) (`latitude_approx`, จุดอ้างอิงสถานที่) — นับไว้ใน `meta.stats.approxOnly` และไม่วาด · เจ้าของหยุดเติมพิกัดชุด CPF 1,166 แถวตามคำสั่ง (README ของทะเบียน)
- **จังหวัด (คำนวณ):** จุดของสาขาตกในรูปจังหวัดของ production 77 จังหวัด (22,174 สาขา) → ถ้าไม่มีจุด ใช้ข้อความจังหวัดในทะเบียนเทียบชื่อ 77 จังหวัด (2,733) → ไม่ทราบ 2,213 สาขา แสดงแยกเป็น “ไม่ระบุจังหวัด” ไม่เดา · จุด 8 จุดอยู่นอกรูปจังหวัดทุกจังหวัด (ทะเล/ชายแดน) ใช้ข้อความจังหวัดแทน
- **แขวง/ตำบล (คำนวณ):** จุดของสาขาตกในกรอบแขวง/ตำบลของ 7 จังหวัดที่ครอบคลุม (11,353 สาขา; ฉบับ 28 ต.ค.: 7,630) — นับในความหนาแน่นของกรอบนั้นตั้งแต่ 0.6.0
- **ลิงก์กับ snapshot ด้วย id เท่านั้น:** เครือใน snapshot → รหัสเครือในทะเบียนที่ชีตระบุ (`national`, 133 จาก 170 รหัสมีสาขาในทะเบียน) · แถวที่ย้ายไปทะเบียน → รหัสที่ทะเบียนระบุว่าอาจตรงกัน (565 จาก 988 รหัสเป็น outlet_id ที่มีในทะเบียน ที่เหลือเป็นรหัสแหล่งรอง `SEC-…` ซึ่งยังไม่ใช่สาขา) — ชื่อใช้ค้นหาเท่านั้น
- **โลโก้ (0.7.1):** ไฟล์จากโฟลเดอร์โลโก้ของทะเบียนที่เจ้าของดาวน์โหลดให้ (577 ไฟล์) ส่งขึ้นเว็บเฉพาะไฟล์ที่ SHA-256 ตรงกับ `01_BRANDS.logo_sha256` ของเครือที่มีสาขา — 408 ไฟล์ใหม่ + 155 ไฟล์เดิม = 563 ไฟล์ เครือในทะเบียนที่มีโลโก้ 541 (จาก 553 โปรไฟล์ที่มีไฟล์; 12 โปรไฟล์ไม่มีสาขา) ไฟล์ที่ทะเบียนไม่ได้ชี้ถึง 27 ไฟล์ไม่ส่งขึ้น · `20_CHAIN_LOGOS` ให้หน้าแหล่ง URL ต้นฉบับ บทบาท และพื้นรองที่แนะนำ (`recommended_surface` → พื้นรองของ registry) · ไบต์ไม่เปลี่ยน ชื่อไฟล์ถูกทำให้ปลอดภัยเท่านั้น · SVG ที่มี script/event/foreignObject ถูกปฏิเสธ (0 ไฟล์) · การเก็บไฟล์ไม่ใช่สิทธิ์ในเครื่องหมายการค้า
- **ค้นต่อ (0.7.0; 0.8.1):** ทุกหน้ามีแถว “ค้นต่อ” ไป Google พร้อมคำถามตามบริบทของหน้า (รีวิว/เมนู/เวลาเปิดของร้าน สาขาและแฟรนไชส์ของเครือ ร้านดังและค่าเช่าของย่าน ร้านในสถานที่) Street View และ Google Maps (`js/outbound.js`) — เป็นทางออกไปค้นภายนอก ไม่ใช่หลักฐานและไม่เปลี่ยนตัวเลขใด · ตั้งแต่ 0.8.1 คำถามเปิดหน้าค้น Google ปกติ (ไม่ใช่ AI Mode `udm=50` ซึ่ง Google ถามยืนยันก่อนทุกครั้งที่มาจากลิงก์) คำตอบ AI Overview ขึ้นบนสุดเมื่อ Google มี และแท็บ AI Mode ในหน้านั้นถามต่อได้โดยไม่ถามยืนยัน · Street View ของร้าน สาขา สถานที่ และอาคาร ยืนบนถนนที่ชื่อร้านหรือที่อยู่ในทะเบียนระบุ (เมื่ออยู่ใกล้กว่าถนนที่ใกล้ที่สุดไม่เกิน 30 ม. เช่น ร้านหัวมุมตะนาว–ข้าวสาร) ไม่เช่นนั้นถนนที่ใกล้ที่สุดจากข้อมูลถนนของแผนที่ (OpenFreeMap `transportation` ไม่รวมทางด่วน ทางขึ้นลง อุโมงค์ ทางเดิน ราง ระยะไม่เกิน 150 ม.) แล้วหันกล้องเข้าหาจุด — จุดเป็นตำแหน่งแสดงผลหรือจุดของผู้เผยแพร่ จึงหันเข้าหาสถานที่ ไม่ได้รับรองว่าเห็นประตูร้าน ร้านในห้างเห็นตัวอาคาร
- **ลิงก์แหล่ง:** เฉพาะ https สาธารณะ ไม่มีลิงก์ชีตหรือ Drive ภายใน (`tests/register.test.js`)

## 8.3 จุดของร้านและสาขา × อาคารในทะเบียน Land (รุ่น 0.3; อาคาร OpenStreetMap อย่างเดียวตั้งแต่ 0.4)

ผลตรวจทั้งหมดและตัวเลขอยู่ใน [docs/LAND_JOIN_CHECK.md](docs/LAND_JOIN_CHECK.md) · สคริปต์ `scripts/build-land.py`

- **host:** สถานที่ในทะเบียนศูนย์การค้าและสถานที่ค้าปลีก โรงแรม และอาคารสำนักงานที่จุดอยู่ในแขวง/ตำบลของ 7 จังหวัด (5,906 แห่ง) — `data/land.json → hosts` เผยแพร่เฉพาะ 1,036 แห่งที่มีจุดร้านหรือสาขาในอาคาร
- **อาคารของ host:** รูปอาคาร OpenStreetMap หลังเดียวกับที่แผนที่วาด — หลังที่มีจุดของ host อยู่ข้างใน (หลายหลังซ้อนกัน: รูปทรงของทะเบียนตัดสินว่าหลังไหนคืออาคารเดียวกัน IoU ≥ 0.3; ไม่เช่นนั้นศูนย์การค้าใช้หลังใหญ่สุด โรงแรม/สำนักงานหลังเล็กสุด) แล้วจึงหลังที่ใกล้ที่สุดไม่เกิน 10 ม. แล้วจึงหลังที่ซ้อนทับรูปทรงของทะเบียนมากที่สุด; รูปอาคารเดียวที่หลายทะเบียนอ้างถึงเป็นอาคารเดียว (host รวมกัน); ไม่ใช้ footprint V4
- **ความสัมพันธ์:** ในอาคาร = ในขอบ หรือห่างขอบไม่เกิน 10 ม. (อยู่ในความคลาดของจุดเอง; จุดนอกขอบได้ตำแหน่งแสดงผลลึกเข้าไป ≥ 1.5 ม. ใน `snaps.rows` / `snaps.chains` ซึ่งแอปใช้วาดหมุด ส่วนพิกัดต้นทางยังอยู่ในไฟล์ข้อมูลและแผง) · รอบอาคาร 10–100 ม. (รายงานเท่านั้น) — วัดบนเมตริก equirectangular ที่ 13.75° N; จุดในอาคารซ้อนหลายหลังสัมพันธ์กับทุกหลัง (หลังเล็กสุดเป็นหลัก); `data/land.json → snapshot / chains` = [key, building, 0, d]
- **ในแผง:** อาคารในทะเบียน Land เทียบประเภทร้านในอาคารกับรอบอาคาร (10–100 ม. คำนวณในเบราว์เซอร์จากร้านตามตัวกรอง) เป็นจำนวนและร้อยละของแต่ละคอลัมน์ (เมื่อมีอย่างน้อย 30 ร้าน) — ตอบคำถามของ registry links ที่ให้แยกร้านภายในออกจากรอบ ๆ; ทุกหน้าจบด้วยบรรทัดแหล่งข้อมูลบรรทัดเดียว (ชุดข้อมูล · แหล่ง · วันที่ · ขอบเขต + ความหมายของหน่วย 1 ประโยค)
- **คำ:** “ในอาคาร” (= จุดอยู่ในขอบอาคาร) / “รอบอาคาร” — ไม่ใช่ “ตั้งอยู่ใน” ไม่บอกชั้นหรือการเป็นผู้เช่า และไม่เปลี่ยนจำนวนร้าน สถานที่ หรือสาขาใด
- **นิคมอุตสาหกรรม:** ไม่เทียบและไม่เผยแพร่ polygon จนกว่าขอบเขต วันที่ และสิทธิ์จะผ่านการตรวจรับ (registry link `DL-2669875b0346b95e63be`)

## 8.4 รูปทรงอาคารที่ละเอียดกว่า (LOD > 1) — เจ้าของสั่ง 28 ต.ค. 2569

- **อาคาร Land:** เมื่อรูปทรงของทะเบียนมีส่วนอาคาร (ฐาน–ยอดต่อส่วน) และเป็นอาคารเดียวกับรูป OpenStreetMap (IoU ≥ 0.5, ส่วนอาคารอยู่ในขอบ ≥ 90%) วาดส่วนอาคารในขอบ OpenStreetMap แทนก้อน LOD1 และวาดขอบเป็นเส้น — 15 อาคาร 64 ส่วน · ตั้งแต่ 0.8.4 อาคารที่ OpenStreetMap เองแยกเป็นชั้นซ้อน (รูปที่อยู่ในขอบ ≥ 90% และเริ่มหรือสูงกว่า เช่น ยอดตึก Q House Lumpini 140–148 ม. บนตัวตึก 0–140 ม.) วาดรูปเหล่านั้นเป็นส่วนอาคารจากฐานของแต่ละส่วน (`parts_source = osm_parts`) อีก 32 อาคาร 136 ส่วน และเมื่อจุดตกในหลายรูปซ้อนกัน เลือกรูปที่ตั้งบนพื้นก่อนแล้วจึงเล็กที่สุด ทั้งตอนสร้าง Land และตอนจับคู่บนหน้าเว็บ ยอดตึกจึงไม่ถูกนับเป็นอาคารเอง (`land-buildings.geojson`: `geometry_role = land_host_building` เป็นเส้นขอบ/ก้อน LOD1, `land_host_building_part` เป็นส่วนอาคาร, `properties.lod`, `source = openfreemap_osm`)
- **อาคารของสถานที่ (venue):** สถานที่ในชีตที่บันทึก uuid ของ CityMETER (`07_VENUES.citymeter_shopping_center_id`) ซึ่งตรงกับ `citymeter_venue_id` ของทะเบียนศูนย์การค้า ได้ส่วนอาคารของ Shopping Center release แทน footprint ที่จับจากชื่อเมื่อส่วนอาคารมากกว่า — รุ่นนี้ 1 แห่ง: เอ็ม บี เค เซ็นเตอร์ (2 ส่วน + เส้นขอบ; `venue-buildings.geojson → metadata.lodVenues`) สถานที่อื่นที่มีส่วนอาคารอยู่แล้ว (สยามพารากอน เซ็นทรัลเวิลด์ เอ็มโพเรียม เซ็นทรัล เอ็มบาสซี เซ็นทรัล ปิ่นเกล้า) ได้ชุดเดียวกันจากงานวิจัยเดิม
- ความสูงทุกส่วนเป็นค่าตามแหล่ง (CityMETER parts / OSM) ไม่ใช่ผลรังวัด

## 8.5 ราคา 6 ช่วงและขนาดเครือ (รุ่น 0.8.0 — แท็บ `10_CLASSIFICATION` ของทะเบียน จัดกลุ่ม 2 พ.ย. 2569)

- **ที่มา (Observed fact):** ทะเบียนฉบับอ่าน 3 พ.ย. 2569 มีแท็บ `10_CLASSIFICATION` หนึ่งแถวต่อร้านในชีต 1 (68,980) และต่อสาขาในทะเบียน (26,361; ชุด CPF 1,166 สาขาไม่อยู่ในแท็บตามคำสั่งหยุดเดิมของเจ้าของ) จัด 6 มุม: หมวด/กลุ่ม/กลุ่มย่อยอาหาร (6/19/31) รูปแบบร้าน (10) ราคา (6) ขนาดเครือ (5) โอกาส (8) และป้ายพิเศษ พร้อม codebook ใน `06_DATA_DICTIONARY` ทุกแถว `review_status = unreviewed`
- **ประตูตรวจ (Owner-stated ในทะเบียน `rules.QA`):** ผลอัตโนมัติขึ้นเว็บได้เมื่อสุ่มตรวจกลุ่มย่อยละ 50 แถว ถูกอย่างน้อย 48 (seed `six-axes-20261102-v1`, เลือก SHA-256(seed|classification_id) ต่ำสุด) — 31 กลุ่มย่อย × 50 = 1,550 ตัวอย่าง เจ้าของเลือก 3 พ.ย. 2569 ให้ขึ้นส่วนที่ไม่ต้องรอการตรวจก่อน: **ขนาดเครือ** (เป็นการนับแถว) และ **ราคา 6 ช่วง** (เป็นค่าที่แหล่งระบุ) ส่วนหมวดอาหาร รูปแบบ โอกาส ป้ายพิเศษ ราคาอนุมาน และมุมคู่แข่ง (กลุ่มย่อยเดียวกันในแขวง/ตำบลเดียวกัน แสดงกลุ่มเมื่อ ≥ 5 ร้าน สัดส่วนเมื่อ ≥ 30) ยังอยู่ในทะเบียน (`data/segments.json` `meta.held`) · หมวดอาหารขึ้นเว็บใน 0.9.2 ตามคำตัดสินของเจ้าของ (§8.7)
- **ราคา 6 ช่วง:** P1 ไม่เกิน 100 · P2 101–200 · P3 201–300 · P4 301–500 · P5 501–1,000 · P6 เริ่ม 1,000 บาทขึ้นไป — หน้าเว็บคำนวณจาก `p0`/`p1` เอง (`priceTier`) ไม่ต้องมีคอลัมน์ใหม่ build ตรวจว่าตรงกับ `price_band_observed` ทุกแถว (`meta.priceCheck = {agree: 68980}`) ถังเดิมของ API แยกเป็นช่วงใหม่ได้ครบ (≤100 = P1; 100–300 = P2+P3; 300–500 = P4; 500–1,000 + >1,000 = P5+P6) ลิงก์รุ่น 0.1–0.7 จึงเปิดช่วงเดียวกันได้ (`LEGACY_PRICE`) จำนวนใน snapshot 70,214 แถว: P1 9,056 · P2 6,090 · P3 2,844 · P4 937 · P5 165 · P6 907 · ไม่ระบุ 50,215 (ร้านในประชากร 68,980 แถว: 9,048 · 5,942 · 2,658 · 832 · 155 · 886 · 49,459) สาขาเครือไม่มีราคา
- **สีราคา:** 6 ค่าจาก LUT 41 ค่าของ family `price` ที่ดัชนี round(i × 40 / 5) = 0, 8, 16, 24, 32, 40 (`lutPicks`) สีเดียวกันทั้งสองธีม — 6 ไม่อยู่ในชุด 3/5/7/9 ของ LDS DATAVIZ-02 จึงใช้กติกาเจ้าของ “color scale always go for 41 colors” (30 ต.ค.) ซึ่งเป็นวิธีเดียวกับที่ ramp ใช้เมื่อมีไม่กี่ช่วง (รายงานเป็นความต่างระหว่างกติกา)
- **ขนาดเครือ:** `chain_size_band` S1–S5 = 1 สาขาที่พบ · 2–9 · 10–49 · 50–199 · 200+ พร้อมวิธีนับ `chain_link_status`: c = นับแถวในทะเบียนที่มีรหัสแบรนด์เดียวกัน · n = นับร้านในชีต 1 ที่ชื่อเข้าเครือเดียวกัน (candidate ไม่ใช่เครือที่ยืนยัน) · s = พบแถวเดียว ยังไม่รู้ว่ามีสาขาอื่น — ไม่ได้แปลว่าร้านอิสระ ตามแหล่ง: ร้านชีต 1 S1:s 66,962 · S1:n 26 · S2:n 349 · S3:n 600 · S4:n 715 · S5:n 328; สาขา S1:s 431 · S2:c 1,033 · S3:c 2,889 · S4:c 5,569 · S5:c 16,439 · ไม่จัดขนาด 1,166 (ทะเบียนจุดขายหลายแบรนด์ของ CPF ไม่ใช่เครือเดียว)
- **เครือเดียว ขนาดเดียว (Inference/กติกาแสดงผล):** แหล่งนับสองชีตแยกกัน เครือเดียวในดัชนีเครือของหน้าเว็บ (คีย์เดียวข้ามสองชีต ตั้งแต่ 0.7.0) จึงอาจได้สองช่วง เช่น ร้านชื่อเดียวกัน 120 ร้านใน 7 จังหวัด กับสาขาในทะเบียน 400 สาขา หน้าเว็บให้ทุกแถวของเครือใช้ช่วงที่ใหญ่ที่สุดที่แหล่งให้แถวใดแถวหนึ่งของเครือนั้น ไม่บวกข้ามชีต (สองชีตอาจนับร้านเดียวกันซ้ำ) — ยก 726 แถว (ร้านชีต 1 725 + สาขา 1) ไม่ลดแถวใด ช่วงเดิมของแถวเก็บใน `sizeOwn` หลังกติกา: S1 67,411 · S2 1,162 · S3 3,398 · S4 6,338 · S5 17,032 · ไม่จัดขนาด 1,166
- **การจัดแนวข้อมูล:** `data/segments.json` เก็บอักขระหนึ่งตัวต่อแถวตามลำดับ `restaurants.json` `cols.key` และ `chains.json` `cols.id` พร้อม SHA-256 ของทั้งสองชุด id; build หยุดถ้าแถวที่เก็บไว้ไม่มีผลจัดกลุ่ม หรือช่วงในทะเบียนไม่ตรงกับการนับแถวต่อรหัสแบรนด์ หน้าเว็บไม่ติดค่าเมื่อชุด id ไม่ตรง (ไม่เดาช่วง) แหล่งภายใน (xlsx SHA-256 `0022662c…d4d8ce`) ไม่อยู่ใน repository และไม่มีลิงก์หรือรหัสชีตในไฟล์ข้อมูล

## 8.6 ข้อค้นพบแรก 3 มุม และบรรทัดลักษณะพื้นที่ (รุ่น 0.9.0 — เจ้าของเลือก 3 พ.ย. 2569)

- **มุม = ตัวกรองสำเร็จรูป** (`js/lenses.js`): “เครือระดับชาติ” = ขนาดเครือ 200+ สาขา (ค่าเริ่มต้นเมื่อเปิดลิงก์เปล่า) · “1,000+ บาท” = ราคาเริ่ม 1,000 บาทขึ้นไป · “แน่นที่สุด” = ไม่กรอง ทุกส่วนของหน้า (สีพื้นที่ อันดับ รายชื่อเครือ ข้อค้นพบ) อ่านแถวชุดเดียวกัน · มุมจะ “เปิดอยู่” เฉพาะเมื่อตัวกรองประเภท/ราคา/ขนาดตรงกับมุมนั้นพอดีและไม่มีคำค้น จังหวัด หรือเครือ
- **สัดส่วนเครือระดับชาติ** = แถวเครือ 200+ สาขา ÷ แถวทั้งหมดที่มีกรอบในแขวงสามแห่งที่เครือ 200+ แน่นที่สุด (ประชากรทั้งหมด ไม่กรอง) เทียบสัดส่วนเดียวกันของทุกแถวที่มีกรอบ ฉบับ 3 พ.ย.: 187/695 = 26.9% เทียบ 7,605/80,629 = 9.4% · เป็นการนับแถวตามช่วงขนาดที่ทะเบียนจัด (เครือเดียวใช้ช่วงที่ใหญ่ที่สุด §8.5) ไม่ใช่ส่วนแบ่งยอดขาย
- **มุม 1,000+ บาท** ใช้ข้อค้นพบแบบนับ (886 แถว < 1,000): สามแขวงที่มีร้านมากที่สุดและสัดส่วนจากร้านที่ระบุราคาเริ่ม 1,000 บาทขึ้นไป — 215/886 = 24.3%
- **บรรทัดลักษณะพื้นที่** (`js/profile-model.js#areaCharacter`): อ่านจากประชากรทั้งหมดของพื้นที่ ไม่ขึ้นกับตัวกรอง — กลุ่มประเภทที่มีแถวมากที่สุด (ไม่นับ “อื่น ๆ” และ “ไม่ระบุประเภท”) พร้อมสัดส่วนต่อแถวทั้งหมด · ช่วงราคาที่ร้านระบุบ่อยที่สุด พร้อม “N จาก M ร้านที่ระบุราคา” (สาขาเครือไม่มีราคา; ราว 7 ใน 10 ของร้านไม่ระบุราคา) · สัดส่วนเครือ 200+ สาขาเทียบทุกพื้นที่
- **ไม่แสดง caveat บนหน้าเว็บ** (เจ้าของ 3 พ.ย. 11:04): ข้อจำกัดทั้งหมดของหัวข้อนี้และ §12 อยู่ในเอกสารนี้ หน้าเว็บเขียนข้อความให้แคบพอดีกับหลักฐาน ค่าที่อนุมาน (เครือหรือสถานที่ที่จับจากชื่อ ขนาดเครือที่นับจากชื่อหรือรับจากเครือเดียวกัน ประเภทของสาขาเครือ) มีป้าย “อนุมาน” คำเดียว และทุกหน้ายังจบด้วยบรรทัดแหล่งข้อมูล
- **ไม่ได้ทำตารางขนาดเครือ × ราคาต่อพื้นที่**: สาขาเครือไม่มีราคา และร้านในชุดข้อมูล 49,459 จาก 68,980 แถวไม่ระบุราคา ตารางจะอ่านผิดว่าเครือใหญ่ “ไม่มีราคา”

## 8.7 หมวดอาหาร 6/19/31 (รุ่น 0.9.2 — ขึ้นเว็บก่อนผลสุ่มตรวจ ตามคำตัดสินของเจ้าของ 3 พ.ย. 2569 16:59)

- **คำตัดสิน (Owner-stated):** เจ้าของถาม “พวกกลุ่มร้านอาหารที่แบ่งไว้ ตอนนี้จะดูใน web demo ต้องทำยังไง หาไม่เจอเลย” และเลือก “เปิดให้ทุกคนเห็นตอนนี้” — ขึ้นเป็นตัวกรองและแถบในแผงพื้นที่พร้อมป้าย “อนุมาน” ข้ามกติกา `rules.QA` ของทะเบียน คำตัดสินนี้แทนกติกา 0.8.0 (“ไม่แสดงแม้มีป้ายอนุมาน”) เฉพาะหมวดอาหาร รูปแบบ โอกาส ป้ายพิเศษ ราคาอนุมาน และมุมคู่แข่งยังรอผลตรวจ
- **ที่มา (Observed fact: ค่าที่ทะเบียนจัด ไม่ใช่ข้อเท็จจริงของร้าน):** `food_category_id` · `food_group_id` · `food_subgroup_primary_id` ของแท็บ `10_CLASSIFICATION` (จัด 2 พ.ย. 2569 ทุกแถว `unreviewed`) กับ codebook ใน `06_DATA_DICTIONARY` (ชื่อและแม่ของแต่ละรหัส) — ทุกค่าเป็นผลจัดกลุ่มอัตโนมัติจากชื่อและประเภทร้าน (Inference ของทะเบียน) หน้าเว็บจึงติด “อนุมาน” ทุกที่ที่แสดง
- **การเก็บ:** `data/segments.json` เก็บอักขระหนึ่งตัวต่อแถวสำหรับกลุ่มและหนึ่งตัวสำหรับกลุ่มย่อย (ลำดับใน codebook บน `meta.food.alphabet`; `.` = ไม่มี) หมวดได้จากแม่ของกลุ่ม build หยุดถ้าหมวดหรือกลุ่มของแถวไม่ตรงกับ codebook กลุ่ม/กลุ่มย่อยรองยังไม่ขึ้นเว็บ
- **ตัวเลข (Observed fact ของ build 3 พ.ย.):** จัดได้ 68,887 จาก 96,507 ร้าน (ร้านในชุดข้อมูล 46,626 · สาขาเครือ 22,261) ไม่จัดหมวด 27,620 แสดงแยกเป็น “ไม่จัดหมวด N ร้าน” ไม่นับเป็นศูนย์ของหมวดใด — ไทยจานเดียว 21,786 · เครื่องดื่ม/ของหวาน 17,872 · ไทยนั่งทาน 12,669 · ตะวันตก/ฟาสต์ฟู้ด 8,080 · เอเชีย 4,561 · กินเป็นกลุ่ม 3,919
- **กลุ่มย่อย “ไม่ระบุชนิด”:** ก๋วยเตี๋ยว/เส้น · ไม่ระบุชนิด (FSG13), ส้มตำ/ยำ/อีสาน · ไม่ระบุชนิด (FSG22) และ ญี่ปุ่น · อื่น ๆ (FSG28) อ่านคู่กับกลุ่มเสมอ ไม่เป็นตัวเลือกหรือชิปเมนูของตัวเอง (กลุ่มครอบคลุมแล้ว)
- **ที่แสดง:** ตัวกรอง “หมวดอาหาร” (`?food=` รหัส FC/FG/FSG) · ชิป “ดูตามหมวดอาหาร” ในภาพรวม (นับจากขอบเขตเดิมโดยไม่ใช้ตัวกรองหมวด) · ส่วน “หมวดอาหาร” ในแผงแขวง เขต สถานที่ และอาคาร (แถบหมวดและกลุ่ม ชิปเมนูที่มี ≥ 3 ร้าน) · บรรทัด หมวด › กลุ่ม › เมนู ในแผงร้านและสาขา ทุกแถบ ชิป และลิงก์กรองได้ แถบใช้สีกลางสีเดียวเหมือนขนาดเครือ ไม่ใช้สีหรือสัญลักษณ์บนแผนที่ (สัญลักษณ์บนแผนที่ยังเป็นประเภทร้าน 10 กลุ่ม)
- **ข้อจำกัด (อยู่ในเอกสารนี้ ไม่อยู่บนหน้าเว็บ):** ยังไม่มีผลสุ่มตรวจ ความแม่นต่อกลุ่มย่อยจึงยังไม่ทราบ · “ประเภทร้าน” 10 กลุ่ม (จากประเภทในแหล่งข้อมูล) กับ “หมวดอาหาร” (จากทะเบียน) เป็นคนละระบบ กลุ่มที่ชื่อคล้ายกัน เช่น ญี่ปุ่น จึงนับได้ไม่เท่ากัน · หมวดของสาขาเครือมาจากการจัดของทะเบียน ไม่ใช่เมนูที่สาขาขายจริง

## 9. โลโก้

- โลโก้เครือ 563 ไฟล์ (155 จาก manifest 22 ต.ค. + 408 จากโฟลเดอร์ของทะเบียนร้านเครือ อ่านกลับ 29 ต.ค.; ตั้งแต่ 0.7.1) และโลโก้สถานที่ 157 ไฟล์ คัดลอกไบต์เดิมจากโฟลเดอร์ Web ที่ตรวจแล้ว ตรวจ SHA-256 กับ manifest และชีตทุกไฟล์ (`tests/logos.test.js`) · รวม 34.3 MB โหลดเฉพาะเมื่อแสดง (lazy)
- ค้นด้วย id ของเครือ/สถานที่เท่านั้น ไม่ fuzzy ตามชื่อ โลโก้ช่วยจำตัวตน ไม่ได้ยืนยันความเป็นสาขาหรือที่ตั้ง โลโก้แบบใช้ร่วม (เครือ ห้างในเครือ หน่วยงานแม่ ฯลฯ) มีคำบอกบทบาทกำกับ
- พื้นรองโลโก้มาจาก registry (หินอ่อนสำหรับพื้นโปร่ง เข้มสำหรับโลโก้สีอ่อน ขาวสำหรับไฟล์ทึบ) ไม่ย้อมสี artwork
- การรวบรวมไฟล์ไม่ใช่การได้รับอนุญาตใช้เครื่องหมายการค้า

## 10. สถานะค่า (EVID-05) และการแสดงผล

| สถานะ | ความหมายในเดโม | บนแผนที่ |
|---|---|---|
| measured | แขวง/ตำบลที่มีร้านตามตัวกรอง | สีระดับของ `density.area` (5 ระดับเท่าช่วงจากค่าสูงสุด) |
| measured_zero | แขวง/ตำบลที่มีร้านในชุดข้อมูลเต็ม แต่ไม่มีตามตัวกรองนี้ | สีระดับต่ำสุด + เส้นขอบศูนย์ 2 px |
| no_data | แขวง/ตำบลที่ไม่มีร้านหรือสาขาในประชากรเลย (106 จาก 581 กรอบตั้งแต่ 0.6.0; 273 บนร้านอย่างเดียว) — แหล่งข้อมูลไม่ครอบคลุม ไม่ได้แปลว่าไม่มีร้านอาหาร | ลายเส้นทแยง 135° บนพื้น no-data + เส้นขอบ emphasis |
| (ยกเลิก 0.6.0) out_of_scope | จังหวัดนอกชุดข้อมูลร้าน (70 จังหวัด) วาดและนับสาขาในทะเบียนแล้ว | กรอบจังหวัดจาก production; ไม่มีกรอบแขวง/ตำบล จึงไม่มีความหนาแน่น (ตัวกรองจังหวัดแสดงจำนวนสาขาและ —) |

สีข้อมูลทั้งหมดเป็นค่า HEX เดียวกันทั้งสองธีมตาม LDS 0.9.7 (`js/scales.js`, `assets/theme.css`) ดูการเลือกสเกลใน [docs/AREA_METHOD.md](docs/AREA_METHOD.md) และ [docs/CHART_SELECTION.md](docs/CHART_SELECTION.md)

## 11. การสร้างไฟล์ข้อมูล

```sh
# ขอบเขต (อินพุต: geometry responses + .meta.json receipts ที่ดึงจาก API สาธารณะ)
python3 -I scripts/build-boundaries.py --api <api-dir> --geom <geom-dir> --provinces 10,11,12,13,24,73,74 --out . --mapshaper <path-to-mapshaper>
# ชื่ออังกฤษที่ production ไม่มี (อินพุต: districts.geojson + subdistricts.geojson ของ OpenGISData-Thailand ที่ commit fafcea1)
python3 -I scripts/add-english-names.py --ogd <ogd-dir>
# แปลง xlsx ของชีตวิจัยเป็น JSON ทีละแท็บ (อินพุตภายใน)
python3 -I scripts/xlsx-dump.py <sheet.xlsx> <dump-dir>
# ร้าน เครือ สถานที่ โลโก้ อาคาร (ชีต 1 + แถวที่ย้ายไปชีต 2 + manifest + โฟลเดอร์โลโก้ + host buildings + ส่วนอาคาร LOD2 จาก Shopping Center release)
python3 -I scripts/build-data.py --sheet <sheet1-dump> --chain-sheet <sheet2-dump> --logo-manifest <dir> --brand-files <dir> --venue-files <dir> --host-buildings <venue-buildings.geojson> --lod-buildings <shoppingcenter/data/venue-buildings.geojson> --shop-registry <shopping-dump> --areas data/areas.geojson --out .
# ทะเบียนร้านเครือ (ชีต 2 + รูปจังหวัด 77 จังหวัดของ production + ทะเบียนโลโก้จากขั้นก่อน)
python3 -I scripts/build-chains.py --chain-dump <sheet2-dump> --provinces <province-geom-dir> --areas data/areas.geojson --brand-logos data/brand-logos.json --out .
# จุด × อาคาร Land (ทะเบียน Land + รูปอาคาร OpenStreetMap จาก tile OpenFreeMap + Shopping Center release)
python3 -I scripts/build-land.py … --out . --report docs/land-join-check.json   # อาร์กิวเมนต์ครบใน docs/LAND_JOIN_CHECK.md §8
# ขนาดเครือและการตรวจราคา 6 ช่วง (แท็บ 10_CLASSIFICATION + 06_DATA_DICTIONARY + 02_STORES ของทะเบียน) — §8.5
python3 -I scripts/build-segments.py --chain-dump <sheet2-dump> --restaurants data/restaurants.json --chains data/chains.json --workbook-sha256 <sha256 ของ xlsx> --read-on <YYYY-MM-DD> --out data/segments.json
```

ลำดับ: build-data → build-chains (อ่าน brand-logos.json ของขั้นแรก) → build-land (อ่าน restaurants.json และ chains.json) → build-segments (อ่าน restaurants.json และ chains.json ของรุ่นเดียวกัน)

ต้องใช้ Python 3.11+ พร้อม `openpyxl` และ `Pillow` และ Node (mapshaper 0.6) อินพุตเป็นไฟล์ภายในจึงไม่อยู่ใน repository สคริปต์ตรวจตอน build: id ไม่ซ้ำ, รหัสพื้นที่มีอยู่, พิกัดย้อนกลับได้ภายใน 5×10⁻⁷°, แถวอยู่ใน tile ของตัวเอง, SHA-256 ของโลโก้ตรง manifest และชีต

## 12. ข้อจำกัดที่ทราบ

1. ไม่ใช่สำมะโนร้านอาหาร: ครอบคลุม 7 จังหวัด และนอก กทม./นนทบุรี/สมุทรปราการ ครอบคลุมเพียงบางตำบล
2. ไม่มีวันที่เก็บข้อมูลต้นทางและผู้ให้ข้อมูลต้นทาง วันที่ในเดโมคือวันที่ดึงและตรวจ (22 ต.ค. 2569)
3. พิกัดเป็นตำแหน่งแสดงผล (กริด 9.55 ม.) ไม่ใช่ทางเข้าร้าน
4. คะแนน/รีวิวเป็นตัวชี้ความนิยมตามแหล่งข้อมูล ไม่ใช่ยอดขาย footfall หรือคุณภาพอาหาร
5. ประเภทร้านมาจาก primary type เดียว และรวมสถานที่ที่ไม่ใช่อาหาร 130 แถว
6. เครือร้าน/สถานที่เป็น candidate จากชื่อ ไม่ใช่ความสัมพันธ์ที่ยืนยัน · การจับจุดกับอาคารเป็นผลแสดงผลที่ความละเอียดราว 9.55 ม. ไม่ใช่ที่อยู่หรือชั้น
7. ความหนาแน่นใช้ตัวหารเป็นพื้นที่ทั้งตำบล รวมพื้นที่ที่อยู่อาศัยไม่ได้ (เช่น นา สวน แหล่งน้ำ)
8. โลโก้บางไฟล์ใหญ่ (KFC 1.2 MB, The Circle Ratchaphruek 1.1 MB) — คงไบต์เดิมตาม manifest; การทำไฟล์เว็บที่เล็กลงพร้อมบันทึก derivation อยู่ใน backlog
9. ทะเบียนร้านเครือยังไม่รับรองว่าครบทุกสาขาปัจจุบัน; 4,980 สาขาไม่มีพิกัด (610 จาก 907 เครือไม่มีเลย รวม 3,773 สาขา; ฉบับ 30 ต.ค.: 4,938 / 576 จาก 821 เครือ; ฉบับ 28 ต.ค.: 10,091 / 625 เครือ) และ 2,236 สาขาไม่ทราบจังหวัด (ฉบับ 30 ต.ค.: 2,213) (อยู่ในยอดรวมและรายการ ไม่อยู่ในความหนาแน่น กรอบ หรือแผนที่); สถานะปิดเป็นสิ่งที่แหล่งระบุ ไม่ได้ตรวจภาคสนาม; ตั้งแต่ 0.6.0 นับรวมกับร้านในชุดข้อมูลเป็นประชากรเดียวตามคำสั่งเจ้าของ และตั้งแต่ 0.7.0 เรียกรวมว่า “ร้าน” โดยบอกความต่างในบรรทัดแหล่งข้อมูลเท่านั้น — ผู้อ่านตัวเลขรวมต้องรู้ว่าสองฝั่งมาจากคนละแหล่งและคนละความครบ
12. ประเภทของสาขาเครือเป็นค่าอนุมานจากร้านชื่อเดียวกันในชุดข้อมูลหรือรหัส format ของทะเบียน (§8.2) ไม่ใช่ประเภทที่แหล่งระบุต่อสาขา; 11,320 สาขายังไม่ระบุประเภท (15,800 จาก 27,120 ได้ประเภท จาก 190 เครือ: 83 จากร้านชื่อเดียวกัน 107 จาก format)
10. การจับจุดกับอาคาร Land ขึ้นกับความแม่นของจุดและสิ่งที่ OpenStreetMap วาดไว้; host 3,476 แห่งไม่มีรูปอาคารที่จุด (ตลาด ตลาดนัด ปั๊ม ร้านในเครือ ฯลฯ) จึงไม่มีผลจับ ไม่ได้แปลว่าไม่มีร้าน
11. นิคมอุตสาหกรรมยังไม่เทียบจนกว่าขอบเขตจะผ่านการตรวจรับ
13. ไฟล์ข้อมูลใหญ่ขึ้น: chains.json 8.0 MB (gzip ≈ 1.5 MB) และ land ≈ 1.2 MB โหลดหลังภาพแรก ถ้าโหลดไม่ได้ ข้อมูลร้านยังใช้ได้ครบ · โลโก้ 563 ไฟล์ 34.3 MB โหลดเมื่อแสดงเท่านั้น
