แสดงเส้นทางบนแผนที่ด้วย Open Source Routing Machine (OSRM)
บทความนี้แสดงวิธีติดตั้งและใช้งาน Open Source Routing Machine (OSRM) 1 ระบบโอเพ่นซอร์สสำหรับค้นหาเส้นทางบนแผนที่ โดยใช้ข้อมูลจาก OpenStreetMap (OSM) 2
เนื้อหาเน้นไปที่การตั้งค่า ระบบ OSRM ฝั่งเซิร์ฟเวอร์ (Backend) เพื่อให้สามารถคำนวณระยะทาง เวลาเดินทาง หรือเส้นทางที่เหมาะสมระหว่างพิกัดต่างๆ ได้ผ่าน REST API
โดยจะแบ่งเนื้อหาออกเป็น 4 ส่วนหลัก:
- ดาวน์โหลดข้อมูลแผนที่ของประเทศไทยจาก OSM
- เตรียมข้อมูลให้เหมาะกับอัลกอริทึมของ OSRM (CH 3 หรือ MLD 4 5)
- สร้างและเปิดใช้งานระบบ OSRM
- ทดสอบการคำนวณระยะทางและระยะเวลา
- ทดสอบการคำนวณเส้นทาง
1. ดาวน์โหลดข้อมูลแผนที่ของประเทศไทยจาก OpenStreetMap
ก่อนใช้งาน OSRM จำเป็นต้องมีข้อมูลแผนที่ที่ประกอบด้วยโครงสร้างถนน บทความนี้แนะนำการดาวน์โหลดข้อมูล OSM ของประเทศไทยจาก 2 แหล่ง:
1.1 ดาวน์โหลดจาก slice.openstreetmap.us 6
- เปิดเว็บไซต์ แล้วกรอก GeoJSON ของประเทศไทยลงในช่อง
Paste bbox or GeoJSON:
- คลิก
Load
เพื่อโหลดขอบเขตข้อมูล (รูปที่ 1) - คลิก
Generate Slice
รอจนระบบประมวลผลเสร็จ - คลิก
Download
เพื่อดาวน์โหลดไฟล์ข้อมูล - เปลี่ยนชื่อไฟล์เป็น
thailand-latest.osm.pbf
ℹ️ หมายเหตุ: เว็บไซต์นี้เหมาะสำหรับผู้ที่ต้องการเลือกพื้นที่แผนที่เฉพาะ เช่น รายจังหวัด หรือเฉพาะจุดที่สนใจ และต้องการข้อมูลที่อัปเดตเร็วภายในไม่กี่นาที สามารถดาวน์โหลดตัวอย่างไฟล์ GeoJSON ของประเทศไทยได้ที่ลิงก์นี้
1.2 ดาวน์โหลดจาก Geofabrik 7
Geofabrik แบ่งข้อมูล OSM ตามประเทศไว้เรียบร้อย สามารถเลือกดาวน์โหลดไฟล์ thailand-latest.osm.pbf
ได้ทันที โดยไฟล์อัปเดตทุกวัน
ข้อมูลในเว็บไซต์นี้จะมีการปรีบปรุงวันละ 1 ครั้ง โดยจะมีการระบุวันและเวลาที่ข้อมูล OSM ของไฟล์ thailand-latest.osm.pbf
ถูกสร้างขึ้น 8
ตัวอย่างคำสั่ง:
VERSION=latest
wget http://download.geofabrik.de/asia/thailand-${VERSION}.osm.pbf
ℹ️ หมายเหตุ: ไฟล์
-latest
จะถูกอัปเดตทุกวัน แต่หากต้องการความสามารถในการติดตามเวอร์ชันย้อนหลัง สามารถเลือกดาวน์โหลดตามวันที่ได้ เช่นthailand-240401.osm.pbf
2. เตรียมข้อมูล OSM ให้กับระบบ OSRM ใช้งาน
ข้อมูลที่ดาวน์โหลดมา ต้องผ่านขั้นตอนการเตรียมให้เหมาะกับอัลกอริทึมของ OSRM โดยแบ่งออกเป็น:
2.1 แยกข้อมูลด้วย osrm-extract
ตัวอย่างคำสั่งสำหรับแยกข้อมูลด้วยโปรไฟล์รถยนต์:
docker run -t -v "${PWD}:/data" ghcr.io/project-osrm/osrm-backend \
osrm-extract -p /opt/car.lua /data/thailand-${VERSION}.osm.pbf \
|| echo "osrm-extract failed"
💡 หมายเหตุ: ไฟล์
car.lua
เป็นไฟล์ที่กำหนดวิธีการคำนวณเส้นทางเดินทาง เช่น ตั้งค่าให้หลีกเลี่ยงทางด่วน หรือกำหนดความเร็วสูงสุดตามประเภทของถนน เช่น ถนนหลวง ถนนในเมือง หรือถนนลูกรัง คำสั่งที่ใช้ด้านบนจะอ้างอิงไฟล์car.lua
แบบค่าเริ่มต้น (default) สามารถดูรายละเอียดของไฟล์นี้เพิ่มเติมได้ที่ลิงก์นี้
2.2 เตรียมข้อมูลตามอัลกอริทึมที่เลือกใช้
OSRM รองรับ 2 อัลกอริทึม:
- Contraction Hierarchies (CH)
- Multi-Level Dijkstra (MLD)
เลือกใช้เพียงแบบใดแบบหนึ่งเท่านั้น:
CH:
docker run -t -v "${PWD}:/data" ghcr.io/project-osrm/osrm-backend \
osrm-contract /data/thailand-${VERSION}.osrm \
|| echo "osrm-contract failed"
MLD:
docker run -t -v "${PWD}:/data" ghcr.io/project-osrm/osrm-backend \
osrm-partition /data/thailand-${VERSION}.osrm \
|| echo "osrm-partition failed"
docker run -t -v "${PWD}:/data" ghcr.io/project-osrm/osrm-backend \
osrm-customize /data/thailand-${VERSION}.osrm \
|| echo "osrm-customize failed"
3. เปิดใช้งาน OSRM ผ่าน REST API
เปิดใช้เซิร์ฟเวอร์ OSRM ตามอัลกอริทึมที่เตรียมไว้ในขั้นตอนที่ 2:
CH:
docker run -t -i -p 5000:5000 -v "${PWD}:/data" \
ghcr.io/project-osrm/osrm-backend \
osrm-routed --algorithm ch /data/thailand-${VERSION}.osrm
MLD:
docker run -t -i -p 5000:5000 -v "${PWD}:/data" \
ghcr.io/project-osrm/osrm-backend \
osrm-routed --algorithm mld /data/thailand-${VERSION}.osrm
ℹ️ หมายเหตุ: ค่าเริ่มต้นของ
osrm-routed
คือจะเปิด REST API บนเครื่อง localhost ที่พอร์ต 5000 ทำให้สามารถเข้าถึงการคำนวณเส้นทางได้ผ่าน URLhttp://localhost:5000
.
4. การทดสอบการคำนวณระยะทางระหว่างสองจุดบนแผนที่ด้วย OSRM ผ่าน REST API
เพื่อคำนวณระยะทางและระยะเวลา เช่น จากจุฬาลงกรณ์มหาวิทยาลัยไปมหาวิทยาลัยเทคโนโลยีพระจอมเกล้าธนบุรี สามารถใช้ API /table
เพื่อสำหรับการหาค่าระยะทางที่เร็วที่สุดด้วยพาธ (path) /table/v1/driving
9 และพารามิเตอร์จุดเริ่มต้นและจุดหมายปลายทาง พร้อมกับพารามิเตอร์ annotations=duration,distance
ข้อควรระวัง
- พิกัดจะต้องระบุค่าลองจิจูดก่อน และตามด้วยลัตติจูด
- พิกัดแรกคือจุดเริ่มต้นและพิกัดถัดไปคือจุดหมายปลายทาง
ตัวอย่างคำสั่ง cURL เพื่อคำนวณระยะทางระหว่างจุฬาลงกรณ์มหาวิทยาลัยและมหาวิทยาลัยเทคโนโลยีพระจอมเกล้าธนบุรี:
CHULA=100.5261394,13.7389135
KMUTT=100.4964428,13.6512522
curl --location "http://localhost:5000/table/v1/driving/${CHULA};${KMUTT}?annotations=duration%2Cdistance"
ผลลัพธ์ที่ได้จะประกอบด้วยระยะทางและระยะเวลาไป-กลับ โดยค่าผลลัพธ์ตัวอย่างแสดงถึง:
- ระยะทางจากจุฬาลงกรณ์มหาวิทยาลัยไปมหาวิทยาลัยเทคโนโลยีพระจอมเกล้าธนบุรี คือ 13,349.6 เมตร
- ระยะเวลาที่ใช้จากจุฬาลงกรณ์มหาวิทยาลัยไปมหาวิทยาลัยเทคโนโลยีพระจอมเกล้าธนบุรี คือ 845.9 วินาที
และ
- ระยะทางจากมหาวิทยาลัยเทคโนโลยีพระจอมเกล้าธนบุรีกลับไปจุฬาลงกรณ์มหาวิทยาลัย คือ 13,542.4 เมตร
- ระยะเวลาที่ใช้จากมหาวิทยาลัยเทคโนโลยีพระจอมเกล้าธนบุรีกลับไปจุฬาลงกรณ์มหาวิทยาลัย คือ 863.3 วินาที
ผลลัพธ์ JSON:
{
"code": "Ok",
"distances": [
[
0,
13349.6
],
[
13542.4,
0
]
],
"durations": [
[
0,
845.9
],
[
863.3,
0
]
],
"destinations": [ ...
],
"sources": [ ...
]
}
5. การทดสอบการคำนวณเส้นทางระหว่างสองจุดบนแผนที่ด้วย OSRM ผ่าน REST API
การคำนวณหาเส้นทางที่เร็วที่สุดสามารถใช้ API /route
หรือ /route/v1/driving/
10 ตามด้วยพารามิเตอร์ของจุดเริ่มต้นและตามด้วยจุดหมายปลายทาง
ตัวอย่างคำสั่ง cURL เพื่อคำนวณหาเส้นทางระหว่างจุฬาลงกรณ์มหาวิทยาลัยและมหาวิทยาลัยเทคโนโลยีพระจอมเกล้าธนบุรี:
curl --location "http://localhost:5000/route/v1/driving/${CHULA};${KMUTT}"
ผลลัพธ์ที่ได้จะประกอบด้วยระยะทาง, ระยะเวลา และเส้นทางจากจุดเริ่มต้นไปยังจุดหมายปลายทางโดยเส้นทางแสดงผลลัพธ์ในข้อมูลของ geometry
ในรูปแบบ polyline
:
- เส้นทางจากจุฬาลงกรณ์มหาวิทยาลัยไปมหาวิทยาลัยเทคโนโลยีพระจอมเกล้าธนบุรี คือ
ghzrA}~pdRuBbWdTbCzOzTf_@nB`Yk@nv@sMzt@dI~Qe@hTuIda@sc@tHgA|OdQfpBrwA`TsXmQz]vEjRbv@rStr@p^bM|A?dGYmE
ผลลัพธ์ JSON:
{
"code": "Ok",
"routes": [
{
"geometry": "ghzrA}~pdRuBbWdTbCzOzTf_@nB`Yk@nv@sMzt@dI~Qe@hTuIda@sc@tHgA|OdQfpBrwA`TsXmQz]vEjRbv@rStr@p^bM|A?dGYmE",
"legs": [
{
"steps": [],
"summary": "",
"weight": 845.9,
"duration": 845.9,
"distance": 13349.6
}
],
"weight_name": "routability",
"weight": 845.9,
"duration": 845.9,
"distance": 13349.6
}
],
"waypoints": [ ...
]
}
สามารถนำ polyline
ไปวาดลงบนแผนที่ได้หลากหลายวิธี ในที่นี้จะแสดง 2 วิธี:
- วาดเส้นทางที่เว็บไซต์ valhalla.github.io/demos/polyline หรือ developers.google.com/maps/documentation/routes/polylinedecoder
- เขียนโปรแกรม Python และใช้
folium
library เพื่อวาดเส้นทางลงใน Python Notebook
5.1.1 วาดเส้นทางบนเว็บไซต์ valhalla.github.io/demos/polyline
เปิดเว็บไซต์แล้วกรอกผลลัพธ์ polyline
ที่ได้
ในตัวอย่างข้างต้น คือ ghzrA}~pdRuBbWdTbCzOzTf_@nB`Yk@nv@sMzt@dI~Qe@hTuIda@sc@tHgA|OdQfpBrwA`TsXmQz]vEjRbv@rStr@p^bM|A?dGYmE
ลงในช่อง Encoded Line
(กรอบสีแดง) ระบบจะแสดงผลเส้นทาง (เส้นสีม่วง) ตามตัวอย่างดังรูปที่ 2
⚠️ หมายเหตุ: พบว่าบางครั้ง
polyline
ที่แสดงผลด้วยเว็บไซต์ข้างต้น11 ไม่ถูกต้อง
5.1.2 วาดเส้นทางบนเว็บไซต์ developers.google.com/maps/documentation/routes/polylinedecoder
เปิดเว็บไซต์แล้วกรอกผลลัพธ์ polyline
ที่ได้
ในตัวอย่างข้างต้น คือ ghzrA}~pdRuBbWdTbCzOzTf_@nB`Yk@nv@sMzt@dI~Qe@hTuIda@sc@tHgA|OdQfpBrwA`TsXmQz]vEjRbv@rStr@p^bM|A?dGYmE
ลงในช่อง Encoded ployline
และกดปุ่ม Decode
ระบบจะแสดงผลเส้นทาง (เส้นน้ำเงิน) ตามตัวอย่างดังรูปที่ 3
5.2 วาดเส้นทางบนแผนที่ด้วยโปรแกรม Python 12
ด้านล่างคือตัวอย่างโปรแกรม Python ที่แสดงการเส้นทาง:
- เรียกข้อมูลเส้นทางจากเซิร์ฟเวอร์ OSRM (ในข้อที่ 3) ด้วย REST API ที่
http://localhost:5000/route/v1/driving
- แสดงเส้นทางบนแผนที่ด้วยไลบรารี
folium
โดยใช้ข้อมูลpolyline
ที่ได้จาก OSRM
import requests
import folium
from folium.plugins import PolyLineFromEncoded
# request route from a local osrm server
CHULA=[100.5261394,13.7389135]
KMUTT=[100.4964428,13.6512522]
# make the request to the OSRM server
osrm_request_url = f"http://localhost:5000/route/v1/driving/{CHULA[0]},{CHULA[1]};{KMUTT[0]},{KMUTT[1]}"
response = requests.get(osrm_request_url)
data = response.json()
# extract the polyline from the response
encoded_polyline = data['routes'][0]['geometry']
# create a folium map centered at the midpoint of CHULA and KMUTT
m = folium.Map(location=[(CHULA[1]+KMUTT[1])/2.,(CHULA[0]+KMUTT[0])/2.], zoom_start=13)
# draw an encoded polyline on a map
PolyLineFromEncoded(encoded=encoded_polyline).add_to(m)
m