Skip to content

Prajuab Riabroy's Blog

Survey Application – GNSS – UAV – Programing – Python – Dart&Flutter – Rust

Menu
  • Home
  • About Us
  • Survey Application
    • Surveyor Pocket Tools
    • Traverse Pro
    • XSection Plot
    • Geoid Height Pro
    • Spot Fire
    • โปรแกรมงานสำรวจสำหรับเครื่องคิดเลข Casio fx-5800P
    • โปรแกรมงานสำรวจสำหรับเครื่องคิดเลข Casio fx-9860G II SD
  • Download
  • Privacy Policy
Menu

เมื่อวิศวกรสำรวจคำนวณเรื่องน้ำ – วิเคราะห์ระบบระบายน้ำบริเวณ RESA

Posted on 13/07/2026

เริ่มต้นจากคำถามง่ายๆ

เริ่มจากคำถามว่า “ถ้าฝนตกหนักแบบฝน 50 ปี น้ำพื้นที่ RESA จะระบายทันไหม?”

คำถามนี้ฟังดูเหมือนเป็นงานของวิศวกรโยธาหรือวิศวกรชลประทาน แต่ถ้าคิดดูดีๆ — คนที่มีพื้นผิวออกแบบ (Design Surface) อยู่ในมือ คนที่รู้ว่าน้ำไหลไปทางไหน และคนทำอาชีพที่วัดพื้นที่และระดับมาตลอด — นั่นคือวิศวกรสำรวจ

ผมเรียน Hydrology มาในมหาวิทยาลัย เนื่องจากรู้สึกว่ายากและคิดว่าคงไม่มีโอกาสได้ใช้ เกรดที่ออกมาจึงน่าสงสาร จำได้ว่าได้เกรด C ไปแบบอย่างถูลู่ถูกัง เมื่อสิบกว่าปีที่แล้วได้ใช้ตอนคำนวณขนาดท่อที่จะฝังไว้ใต้ถนนตัดใหม่ โดยคำนวณจากปริมาณน้ำฝนและทราบพื้นที่ที่รองรับน้ำฝนบริเวณที่จะวางท่อนั้นๆ วันนี้จะหยิบมันขึ้นมาทวนความจำอีกครั้ง


North RESA คืออะไร

RESA ย่อมาจาก Runway End Safety Area คือพื้นที่นิรภัยต่อเนื่องจากปลายทางวิ่ง ออกแบบมาเพื่อรองรับกรณีที่อากาศยานวิ่งออกนอกทางวิ่ง (Runway Excursion)

North RESA อยู่ด้านเหนือของสนามบินอู่ตะเภามีขนาด 0.5 ตารางกิโลเมตร พื้นผิวเป็น Grass Turf ลาดเอียง ระบายน้ำออกสองข้างซ้าย-ขวาแบ่งครึ่งผ่าน Trapezoidal Ditch ทั้งสองเส้น ก่อนไหลต่อไปยัง Retention Pond


ตรวจสอบพื้นผิว DEM

ผมมี Top surface จาก Civil 3D ที่ผู้ออกแบบได้ออกแบบไว้บริเวณ RESA สามารส่งออกเป็นรูปแบบ LandXML เพื่อไปโปรแกรมอื่นได้ สามารถใช้ Global Mapper อ่านได้สบายๆ แต่รูปแบบไฟล์ LandXML ผมพยายามนำเข้า QGIS ผ่านทางปลั๊กอิน ไม่สำเร็จมี errors ผมลองปรึกษาคุณคล็อดเอไอ เขาบอกว่าเขาสามารถเขียนสคริปต์ให้โดยแปลงจาก LandXML ไปยัง Geotiff แถมสามารถตัดหรือครอปตามขอบเขตไฟล์ DXF ได้ ผมอุทานเลยเลยมันจะเก่งอะไรขนาดนั้น ใจยังไม่เชื่อ แต่พอเขาทำสคริปต์มาให้ ลองรันรอบแรกมันได้ผลเลย ตอนนี้คิดไม่ออกเลยว่าเอไออีกห้าปี มันจะเก่งขนาดไหน หรือว่ายุคซูปเปอร์แอพใกล้จะถึงแล้ว

#!/usr/bin/env python3
"""
landxml_to_geotiff.py
Convert LandXML TIN surface to GeoTIFF raster (elevation)

Usage:
  python landxml_to_geotiff.py input.xml output.tif [resolution] [epsg] [crop_area.dxf]

Arguments:
  input.xml    : LandXML file containing TIN surface
  output.tif   : Output GeoTIFF file
  resolution   : Grid resolution in metres (default: 0.10)
  epsg         : CRS EPSG code (default: 32647 = UTM Zone 47N Thailand)

Example:
  python landxml_to_geotiff.py NorthRESA.xml NorthRESA_DEM.tif 0.10 32647

Requirements:
  pip install lxml numpy scipy rasterio

Author: based on workflow by Prajuab Riabroy (priabroy.name)
"""

import sys
import numpy as np
from lxml import etree
from scipy.interpolate import LinearNDInterpolator
import rasterio
from rasterio.transform import from_bounds
from rasterio.crs import CRS

# ─── Parse arguments ────────────────────────────────────────────
def usage():
    print(__doc__)
    sys.exit(1)

if len(sys.argv) < 3:
    usage()

input_xml  = sys.argv[1]
output_tif = sys.argv[2]
resolution = float(sys.argv[3]) if len(sys.argv) > 3 else 0.10
epsg       = int(sys.argv[4])   if len(sys.argv) > 4 else 32647

print(f"Input  : {input_xml}")
print(f"Output : {output_tif}")
print(f"Resolution: {resolution} m")
print(f"CRS    : EPSG:{epsg}")

# ─── Parse LandXML ──────────────────────────────────────────────
print("\nParsing LandXML...")
tree = etree.parse(input_xml)
root = tree.getroot()

# Handle namespace — LandXML may or may not have one
ns = root.nsmap.get(None, '')
if ns:
    ns_prefix = f'{{{ns}}}'
else:
    ns_prefix = ''

def tag(name):
    return f'{ns_prefix}{name}'

# Find first Surface
surface = root.find(f'.//{tag("Surface")}')
if surface is None:
    print("ERROR: No <Surface> element found in LandXML file.")
    sys.exit(1)

surface_name = surface.get('name', 'Unknown')
print(f"Surface: {surface_name}")

# ─── Extract Points ──────────────────────────────────────────────
print("Reading points...")
pnts_elem = surface.find(f'.//{tag("Pnts")}')
if pnts_elem is None:
    print("ERROR: No <Pnts> element found.")
    sys.exit(1)

points = {}
for p in pnts_elem.findall(tag('P')):
    pid = int(p.get('id'))
    coords = p.text.strip().split()
    # LandXML point order: N E Z (Northing, Easting, Elevation)
    n, e, z = float(coords[0]), float(coords[1]), float(coords[2])
    points[pid] = (e, n, z)  # store as (X=Easting, Y=Northing, Z)

print(f"  Points: {len(points):,}")

# ─── Extract Faces (Triangles) ──────────────────────────────────
print("Reading faces...")
faces_elem = surface.find(f'.//{tag("Faces")}')
if faces_elem is None:
    print("ERROR: No <Faces> element found.")
    sys.exit(1)

faces = []
for f in faces_elem.findall(tag('F')):
    ids = f.text.strip().split()
    faces.append((int(ids[0]), int(ids[1]), int(ids[2])))

print(f"  Faces : {len(faces):,}")

# ─── Build point arrays ──────────────────────────────────────────
print("\nBuilding interpolator...")
xs = np.array([points[pid][0] for pid in points])
ys = np.array([points[pid][1] for pid in points])
zs = np.array([points[pid][2] for pid in points])

x_min, x_max = xs.min(), xs.max()
y_min, y_max = ys.min(), ys.max()

print(f"  X range: {x_min:.2f} → {x_max:.2f}  ({x_max-x_min:.1f} m)")
print(f"  Y range: {y_min:.2f} → {y_max:.2f}  ({y_max-y_min:.1f} m)")
print(f"  Z range: {zs.min():.3f} → {zs.max():.3f} m")

# ─── Create output grid ──────────────────────────────────────────
cols = int(np.ceil((x_max - x_min) / resolution)) + 1
rows = int(np.ceil((y_max - y_min) / resolution)) + 1
print(f"\nOutput grid: {cols} × {rows} cells at {resolution}m resolution")
print(f"  Estimated size: {cols * rows / 1e6:.1f} million cells")

# Grid coordinates (centre of each cell)
xi = np.linspace(x_min, x_max, cols)
yi = np.linspace(y_max, y_min, rows)  # top-to-bottom (raster convention)
xi_grid, yi_grid = np.meshgrid(xi, yi)

# ─── Interpolate ─────────────────────────────────────────────────
print("\nInterpolating (LinearNDInterpolator)...")
print("  This may take a moment for large surfaces...")

interp = LinearNDInterpolator(list(zip(xs, ys)), zs)
zi = interp(xi_grid, yi_grid)

# Replace NaN (outside TIN boundary) with nodata
nodata = -9999.0
zi[np.isnan(zi)] = nodata

valid_count = np.sum(zi != nodata)
print(f"  Valid cells: {valid_count:,} / {cols*rows:,} ({100*valid_count/(cols*rows):.1f}%)")

# ─── Write GeoTIFF ───────────────────────────────────────────────
print(f"\nWriting GeoTIFF: {output_tif}")
transform = from_bounds(x_min, y_min, x_max, y_max, cols, rows)
crs = CRS.from_epsg(epsg)

with rasterio.open(
    output_tif, 'w',
    driver   = 'GTiff',
    height   = rows,
    width    = cols,
    count    = 1,
    dtype    = np.float32,
    crs      = crs,
    transform= transform,
    nodata   = nodata,
    compress = 'lzw',
) as dst:
    dst.write(zi.astype(np.float32), 1)

print(f"\n✓ Done! Output: {output_tif}")
print(f"  Grid size   : {cols} × {rows}")
print(f"  Resolution  : {resolution} m")
print(f"  CRS         : EPSG:{epsg}")
print(f"  Z min/max   : {zs.min():.3f} / {zs.max():.3f} m")
print(f"\nReady to open in QGIS or use with dem_viewer.html")

เมื่อได้ DEM แบบ Geotiff มาแล้วผมขอคุณคล็อดเอไอ ช่วยร่าง 3D Viewer มาให้เพื่อแสดงผลในเวบ เขาร่างรอบเดียวใช้งานได้ผ่านสบาย(อีกแล้ว) ผู้อ่านสามารถดู DEM ได้ผ่านทาง 3D Viewer ด้านล่าง ซูม หมุน เลื่อนได้



ขั้นตอนที่ 1 — น้ำฝนรอบ 50 ปี (IDF Curve สถานีระยอง)

พื้นที่ขนาด 0.5 ตารางกิโลเมตรมี ผมเลือกใช้ Duration 1 ชั่วโมง ซึ่งให้ Intensity = 130 มม./ชม. จาก IDF Curve สถานีระยอง รอบปีที่ 50

พารามิเตอร์ค่าหมายเหตุ
C (Grass Turf ลาดเอียง)0.50ไม่ใช่ 0.92 แบบลานคอนกรีต
i (50yr, 1hr)130 มม./ชม.IDF Curve ระยอง
A (พื้นที่ North RESA)47.7 เฮกตาร์0.477 กม²
tc1 ชั่วโมง

คำนวณ Peak Flow Rate:

$$Q = \frac{C \times i \times A}{360} = \frac{0.50 \times 130 \times 47.7}{360} = 8.61 \text{ ม}^3/\text{วินาที}$$

ปริมาตรน้ำท่าทั้งหมดตลอด 1 ชั่วโมง:

$$V_{runoff} = Q \times t_c \times 3600 = 8.61 \times 1 \times 3600 = 30,966 \text{ ม}^3$$

แบ่งซ้าย-ขวา 50/50:

รวมต่อด้าน
Q8.61 ม³/วินาที4.30 ม³/วินาที
V_runoff30,996 ม³15,498 ม³

ขั้นตอนที่ 2 — ตรวจสอบ Trapezoidal Ditch ด้วย Manning’s Equation

ระบบระบายน้ำ North RESA มี Trapezoidal Ditch สองเส้น (ซ้าย-ขวา) รับน้ำจาก RESA แล้วระบายต่อไปยัง Retention Pond

ข้อมูลหน้าตัด Trapezoidal Ditch:

ด้านล่างกว้าง (b)    = 2.0 ม.
ความลึก (d)        = 2.0 ม.
ด้านบนกว้าง         = 8.0 ม.
Side slope (z)    = 1.5:1
พื้นผิว             = Concrete lined (n = 0.013)
Slope (S)        = 1.5% = 0.015

คำนวณตามลำดับ:

พื้นที่หน้าตัด: $$A = (b + z \times d) \times d = (2 + 1.5 \times 2) \times 2 = 10 \text{ ม}^2$$

Wetted Perimeter: $$P = b + 2d\sqrt{1+z^2} = 2 + 2(2)\sqrt{1+1.5^2} = 9.211 \text{ ม.}$$

Hydraulic Radius: $$R = \frac{A}{P} = \frac{10}{9.211} = 1.086 \text{ ม.}$$

Manning’s Equation: $$Q_{ditch} = \frac{1}{n} \times A \times R^{2/3} \times S^{1/2} = \frac{1}{0.013} \times 10 \times 1.056 \times 0.1225 = 99.5 \text{ ม}^3/\text{วินาที}$$

เปรียบเทียบ:

ค่า
Q ที่ต้องการ (ต่อด้าน)4.30 ม³/วินาที
Q ที่รับได้ (Ditch)99.5 ม³/วินาที
Safety Ratio23.1 เท่า ✓

Ditch รับได้มากกว่าที่ต้องการถึง 23.1 เท่า — ผ่านเกณฑ์ Hydraulic Capacity อย่างสบาย


ข้อควรระวัง — Velocity สูงเกินไป

แม้ว่าขนาด Ditch จะใหญ่พอ แต่ด้วย Slope 1.5% ทำให้ Flow Velocity สูงถึง:

$$V = \frac{Q}{A} = \frac{99.5}{10} = 9.95 \text{ ม./วินาที}$$

ค่านี้เกินมาตรฐานสูงสุดสำหรับ Concrete lined ที่แนะนำไว้ที่ 6–8 ม./วินาที ซึ่งอาจทำให้เกิดการกัดเซาะที่จุดต่อและปลายทาง ควรพิจารณาติดตั้ง Energy Dissipator หรือ Stilling Basin ที่ปลาย Ditch


สรุป

หัวข้อผล
แอ่งน้ำบนพื้นผิว RESAต้องตรวจสอบจาก QGIS Analysis
Q ฝนรอบ 50 ปี (ต่อด้าน)4.30 ม³/วินาที
Trapezoidal Ditch Capacity99.5 ม³/วินาที ✓ ผ่าน
Velocity9.95 ม./วินาที ⚠ สูง — ต้องตรวจสอบ

Trapezoidal Ditch มีขนาดเพียงพอรับน้ำจาก RESA ในสภาวะฝน 50 ปีได้อย่างสบาย แต่ความเร็วน้ำที่สูงเป็นสิ่งที่ผู้ออกแบบได้แก้ไขมาแล้วโดยออกแบบ Energy Dissipator สำหรับรางน้ำที่มีความชันมากกว่า 1.5%


บทส่งท้าย

วิศวกรสำรวจมีพื้นผิว DEM อยู่ในมือ มีข้อมูลเพียงพอที่จะตอบคำถามสำคัญได้ว่า “ฝน 50 ปี รางระบายน้ำรับได้ — แต่ต้องพิจารณาความเร็วของน้ำไหลด้วย“

เราตอบคำถามได้แล้วว่า Trapezoidal Ditch มีขนาดเพียงพอรับน้ำฝนรอบ 50 ปีจาก North RESA หรือไม่ แต่นั่นเป็นเพียงการตรวจสอบ ระบบระบายน้ำ เท่านั้น ยังมีคำถามที่สองที่ยังไม่ได้ตอบ — บนพื้นผิว RESA เองนั้น น้ำไหลออกไปยัง Ditch ได้จริงหรือไม่ หรือมีจุดใดที่น้ำขังอยู่โดยไม่มีทางออก?

ในการก่อสร้างสนามบิน แม้แต่แอ่งน้ำเล็กน้อยบนพื้นผิวก็อาจเป็นปัญหาต่อการปฏิบัติงานและความปลอดภัย ใน QGIS มีอัลกอริทึม Fill Sinks (Wang & Liu) เพื่อสแกนพื้นผิวออกแบบทุก Cell แล้วคำนวณความลึกและปริมาตรของแอ่งน้ำทุกจุด ก่อนนำมาเปรียบเทียบกับเกณฑ์มาตรฐานเพื่อตอบคำถามนี้อย่างสมบูรณ์ได้ สำหรับ North RESA ไม่มีปัญหาเรื่องนี้ แต่ทางขับและทางวิ่งที่ออกแบบมาแล้ว จะนำ Surface มาคำนวณด้วยอัลกอริทึม Fill Sinks เพื่อให้มั่นใจว่าฝนตกแล้วน้ำจะไม่ขัง

ฉบับนี้คงพอแค่นี้ก่อน หอมปากหอมคอ ผมเพิ่งจะเข้าใจว่าทำไมหลักสูตรวศบ.สำรวจ ที่ผมเคยเรียนทำไมต้องมี hydrology อยู่ เพราะมันความเกี่ยวพันเกี่ยวเนื่องกันอย่างนี้นี่เองครับ


Related

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

October 2026
M T W T F S S
 1234
567891011
12131415161718
19202122232425
262728293031  
« Sep    

Blog Stats

There was an issue retrieving stats. Please try again later.

Recent Posts

  • เร็วแรงทะลุนรกกับ POCUT (POint Cloud UTils)
  • อัพเดทแอพ “SurveyStar LDP Thailand” เพื่อรับกับยกร่างมาตรฐาน “TH-LDP”
  • ออกแอพใหม่ SurveyStar Traverse Pro สืบสานงานคำนวณวงรอบ ตอนที่ 2
  • ออกแอพใหม่ SurveyStar Traverse Pro สืบสานงานคำนวณวงรอบ ตอนที่ 1
  • โปรแกรมอรรถประโยชน์ Pocut (POint Cloud Utils)
  • โปรแกรมแปลงไฟล์แคดข้ามระบบพิกัด (Drawing Transformer)
  • มาตรฐานระบบพิกัดแผนที่แบบแอลดีพีของประเทศไทยสำหรับงานวิศวกรรมและการก่อสร้าง (Thailand Low Distortion Map Coordinate System for Engineering and Construction)
  • ประสบการณ์ใช้งาน Kimi K3 Code เขมือบแบบสยอง
  • สร้างแพลตฟอร์มดู Point Cloud บนเว็บด้วย Potree + CesiumJS — เรื่องเล่าจากสนามบินอู่ตะเภา
  • เมื่อวิศวกรสำรวจคำนวณเรื่องน้ำ – วิเคราะห์ระบบระบายน้ำบริเวณ RESA
  • แนะนำหนังสือ “จีออเดซี” ที่ยอดเยี่ยมของดร.พุทธิพล
  • ESPrtk ทำให้ ESP32 ใช้ไม่ได้? วิธีกู้คืน
  • กรณีศึกษา – คืนชีพ SDL30 ปี 2000 ด้วย ESP32 และแอพทำเอง SDL30 Collector
  • DIY RTK Base Station (survey-grade) รุ่น V3.21 ด้วย Raspberry Pi 2B + UM980 เมื่อคุณปู่กลับมาเป็นฮีโร่ ตอนที่ 4
  • รีวิวกูปรี (CoPre2) โปรแกรมประมวลผลไลดาร์จากค่าย CHCNav
  • DIY RTK Base Station (survey-grade) รุ่น V3.2 ด้วย Orange Pi 3 Zero + UM980 ตอนที่ 3
  • DIY RTK Base Station (survey-grade) รุ่น V3 ด้วย Orange Pi 3 Zero + UM980 ตอนที่ 2
  • เส้นทางและไทม์ไลน์ของใบรับรองความรู้ความชำนาญในการประกอบอาชีพวิศวกรรม(สำรวจ)ระดับวิศวกรวิชาชีพ
  • มัดแหนมรวมสามโปรแกรมไปใช้บนสโตร์ Snap ฝั่งลีนุกซ์
  • ปัดฝุ่น Traverse Pro เพิ่มรายละเอียดรายการคำนวณใหม่แบบแนวนอน (Landscape) และธีมมืด
  • DIY RTK Base Station (Survey-Grade) ด้วยชิป UM980 และบอร์ด ESP32 ตอนที่ 1
  • สร้าง NTRIP Caster ด้วย BNC บนคลาวด์แบบ DIY สำหรับ RTK Base Station ใช้ CHCNav P5
  • จับ AI มาไตรกรีฬา สร้างตัวติดตั้ง Snap สำหรับ Surveyor Pocket Tools กับประเด็นแฝงเร้นความเป็นส่วนตัวของผู้ใช้
  • ทดสอบรีด QGIS แบบโหดสัสด้วยชุดข้อมูล Open Building V3 โพลีกอน 74 ล้านหลัง
  • เมื่อ AI เป็นพิษชักจูงผู้ใช้ด้วยข้อมูลผิดๆ

Subscribe to Blog via Email

Enter your email address to subscribe to this blog and receive notifications of new posts by email.

Join 122 other subscribers

Archives

  • October 2026
  • September 2026
  • August 2026
  • July 2026
  • May 2026
  • April 2026
  • March 2026
  • January 2026
  • December 2025
  • November 2025
  • September 2025
  • August 2025
  • July 2025
  • June 2025
  • May 2025
  • January 2025
  • December 2024
  • November 2024
  • October 2024
  • September 2024
  • July 2024
  • May 2024
  • April 2024
  • January 2024
  • November 2023
  • October 2023
  • September 2023
  • July 2023
  • June 2023
  • May 2023
  • March 2023
  • February 2023
  • January 2023
  • November 2022
  • October 2022
  • September 2022
  • July 2022
  • May 2022
  • December 2021
  • October 2021
  • September 2021
  • August 2021
  • May 2021
  • February 2021
  • January 2021
  • December 2020
  • November 2020
  • October 2020
  • September 2020
  • July 2020
  • June 2020
  • May 2020
  • April 2020
  • March 2020
  • February 2020
  • January 2020
  • December 2019
  • November 2019
  • September 2019
  • August 2019
  • July 2019
  • May 2019
  • April 2019
  • March 2019
  • February 2019
  • January 2019
  • November 2018
  • October 2018
  • September 2018
  • August 2018
  • July 2018
  • June 2018
  • May 2018
  • April 2018
  • March 2018
  • January 2018
  • November 2017
  • July 2017
  • June 2017
  • May 2017
  • April 2017
  • March 2017
  • February 2017
  • January 2017
  • December 2016
  • November 2016
  • October 2016
  • August 2016
  • November 2015
  • March 2015
  • August 2014
  • March 2014
  • January 2014
  • December 2013
  • February 2012
  • January 2012
  • November 2011
  • October 2011
  • August 2011
  • April 2011
  • February 2011
  • December 2010
  • November 2010
  • October 2010
  • September 2010
  • June 2010
  • April 2010
  • March 2010
  • January 2010
  • December 2009
  • November 2009
  • October 2009
  • September 2009
  • August 2009

Recent Comments

  • priabroy on ออกแอพใหม่ SurveyStar Traverse Pro สืบสานงานคำนวณวงรอบ ตอนที่ 1
  • Konexana Yomleuangsa on ออกแอพใหม่ SurveyStar Traverse Pro สืบสานงานคำนวณวงรอบ ตอนที่ 1
  • เฟือง เถา on DIY RTK Base Station (survey-grade) รุ่น V3.21 ด้วย Raspberry Pi 2B + UM980 เมื่อคุณปู่กลับมาเป็นฮีโร่ ตอนที่ 4
  • priabroy on DIY RTK Base Station (survey-grade) รุ่น V3.21 ด้วย Raspberry Pi 2B + UM980 เมื่อคุณปู่กลับมาเป็นฮีโร่ ตอนที่ 4
  • ตรันฟองนาม on DIY RTK Base Station (survey-grade) รุ่น V3.21 ด้วย Raspberry Pi 2B + UM980 เมื่อคุณปู่กลับมาเป็นฮีโร่ ตอนที่ 4
©2026 Prajuab Riabroy's Blog | Design: Newspaperly WordPress Theme