| Server IP : 103.233.193.20 / Your IP : 216.73.216.169 Web Server : Apache/2 System : Linux host1.itclever.com 4.18.0-553.16.1.el8_10.x86_64 #1 SMP Thu Aug 8 17:47:08 UTC 2024 x86_64 User : oriscomadm ( 1120) PHP Version : 5.6.40 Disable Function : exec,system,passthru,shell_exec,escapeshellarg,escapeshellcmd,proc_close,proc_open,dl,popen,show_source,posix_kill,posix_mkfifo,posix_getpwuid,posix_setpgid,posix_setsid,posix_setuid,posix_setgid,posix_seteuid,posix_setegid,posix_uname MySQL : ON | cURL : ON | WGET : OFF | Perl : OFF | Python : OFF | Sudo : OFF | Pkexec : OFF Directory : /home/oriscomadm/domains/oriscom.com/private_html/taxi_estimate/ |
Upload File : |
<!DOCTYPE html>
<html lang="th">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title id="pageTitle">คนขับ - เลือกเส้นทาง</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Sarabun:wght@400;600&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/qrcodejs/1.0.0/qrcode.min.js"></script>
<script src="https://api.longdo.com/map/?key=4fc6a833488e90b3df56acc1388c198b"></script>
<style>
/* ========== BASE STYLES ========== */
body {
font-family: 'Sarabun', sans-serif;
background: #B0C4DE;
font-size: 16px;
margin: 0;
padding: 0;
}
.container {
max-width: 600px;
/*padding-top: 16px;*/
/*padding-bottom: 16px;*/
padding-left: 0;
padding-right: 0;
}
/* ========== LANGUAGE SWITCHER (inside card) ========== */
.language-switcher {
position: absolute;
top: 16px;
right: 16px;
font-size: 13px;
color: #6c757d;
font-weight: 400;
z-index: 10;
}
.language-switcher .lang-option {
color: #6c757d;
text-decoration: none;
cursor: pointer;
transition: 0.2s;
/* ทำให้รองรับพื้นหลังวงกลม */
display: inline-flex;
align-items: center;
justify-content: center;
width: 26px;
height: 26px;
border-radius: 50%;
}
.language-switcher .lang-option:hover {
color: #198754;
}
.language-switcher .lang-option.active {
color: #198754;
font-weight: 600;
/* พื้นหลังวงกลม */
background: #d1e7dd;
}
.language-switcher .separator {
margin: 0 6px;
color: #dee2e6;
}
/* ========== CARD STYLES ========== */
.card {
border: none;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05);
background: #ffffff;
/*border-radius: 10px;*/
position: relative;
}
.card-header-title {
margin-top: 12px;
font-size: 20px;
font-weight: bold;
color: #191970;
}
.card-subtitle {
font-size: 14px;
color: #6c757d;
}
/* ========== FORM INPUT STYLES ========== */
.form-label {
font-size: 15px;
margin-bottom: 6px;
font-weight: 600;
color: #2b2b2b;
}
.input-with-icons {
position: relative;
display: flex;
align-items: center;
gap: 0;
}
.input-icon {
position: absolute;
left: 14px;
top: 50%;
transform: translateY(-50%);
z-index: 10;
display: flex;
align-items: center;
justify-content: center;
pointer-events: none;
}
.input-icon i {
font-size: 20px;
}
.input-with-icons .form-control-lg {
height: 50px;
font-size: 16px;
padding: 10px 50px 10px 46px;
border: 1.5px solid #B0C4DE;
background: #F8F8FF;
box-shadow: none;
outline: none;
border-radius: 8px;
flex: 1;
transition: all 0.3s;
}
.input-with-icons .form-control-lg:focus {
border-color: #1E90FF;
background: #ffffff;
}
#origin {
border-left: 4px solid #198754;
}
#destination {
border-left: 4px solid #dc3545;
}
/* ========== CUSTOM INPUT DISPLAY (สำหรับข้อความสีเขียว) ========== */
.custom-input-display {
display: none;
position: absolute;
top: 0;
left: 46px;
right: 50px;
height: 50px;
padding: 13px 0;
pointer-events: none;
z-index: 5;
line-height: 24px;
font-size: 16px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.custom-input-display.active {
display: block;
}
.current-location-text {
color: #198754;
font-weight: 600;
}
.input-has-custom-display {
color: transparent !important;
caret-color: #2b2b2b;
}
/* ========== CLEAR BUTTON ========== */
.clear-btn {
position: absolute;
right: 10px;
top: 50%;
transform: translateY(-50%);
background: #dc3545;
color: white;
border: none;
border-radius: 50%;
width: 26px;
height: 26px;
display: none;
align-items: center;
justify-content: center;
cursor: pointer;
font-size: 16px;
line-height: 1;
padding: 0;
z-index: 10;
transition: all 0.2s;
}
.clear-btn:hover {
background: #bb2d3b;
transform: translateY(-50%) scale(1.1);
}
.clear-btn.show {
display: flex;
}
/* ========== SUGGESTIONS DROPDOWN ========== */
.suggestions {
position: absolute;
top: 100%;
left: 0;
width: 100%;
background: #ffffff;
border: 1px solid #dee2e6;
margin-top: 4px;
overflow: hidden;
z-index: 99;
display: none;
border-radius: 8px;
max-height: 300px;
overflow-y: auto;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
.suggestion-item {
padding: 12px 14px;
font-size: 14px;
cursor: pointer;
display: flex;
align-items: center;
transition: background 0.2s;
}
.suggestion-item:hover {
background: #f1f3f5;
}
.suggestion-item i {
flex-shrink: 0;
margin-right: 8px;
}
.current-location-item {
background: #F0FFF0 !important;
border-bottom: 2px solid #198754;
font-weight: 600;
color: #198754;
}
.current-location-item:hover {
background: #d1e7dd !important;
}
.suggestion-separator {
padding: 8px 14px;
background: #f8f9fa;
font-size: 12px;
font-weight: 600;
color: #6c757d;
text-transform: uppercase;
letter-spacing: 0.5px;
border-top: 1px solid #dee2e6;
border-bottom: 1px solid #dee2e6;
}
/* ========== BUTTONS ========== */
.btn-success {
font-size: 16px;
font-weight: bold;
padding: 12px 0;
border-radius: 10px;
border: none;
background: #008000;
box-shadow: none;
transition: background 0.3s;
}
.btn-success:hover {
background: #006400;
}
.btn-confirm {
width: 100%;
padding: 12px 0;
font-size: 18px;
font-weight: 600;
background: #198754;
border: none;
border-radius: 10px;
color: white;
display: none;
transition: background 0.3s;
}
.btn-confirm:hover {
background: #157347;
}
.btn-confirm:disabled {
background: #6c757d;
cursor: not-allowed;
}
.btn-toggle-qr {
width: 100%;
margin-top: 12px;
font-size: 14px;
padding: 10px;
border-radius: 8px;
}
/* ========== MAP STYLES ========== */
#mapContainer {
display: flex;
align-items: center;
justify-content: center;
}
#map {
width: 100%;
height: 250px;
border-radius: 12px;
border: 2px solid #dee2e6;
cursor: crosshair;
display: flex;
align-items: center;
justify-content: center;
}
.map-instruction {
text-align: center;
padding: 8px;
background: #fff3cd;
border: 1px solid #ffc107;
border-radius: 8px;
margin-top: 10px;
font-size: 14px;
color: #856404;
}
.map-mode-selector {
display: flex;
gap: 8px;
margin-bottom: 12px;
}
.map-mode-btn {
flex: 1;
padding: 10px;
border: 2px solid #dee2e6;
background: #f8f9fa;
border-radius: 8px;
cursor: pointer;
transition: all 0.3s;
display: flex;
align-items: center;
justify-content: center;
gap: 6px;
font-weight: 600;
}
.map-mode-btn.active {
border-color: #0d1b3a;
background: #0d1b3a;
color: white;
}
.map-mode-btn.origin.active {
border-color: #198754;
background: #198754;
}
.map-mode-btn.destination.active {
border-color: #dc3545;
background: #dc3545;
}
/* ========== GPS STATUS ========== */
.gps-status {
display: flex;
align-items: center;
justify-content: center;
gap: 6px;
padding: 8px;
background: #d1e7dd;
border-radius: 8px;
margin-bottom: 12px;
font-size: 14px;
color: #0f5132;
}
/* ========== ROUTE CARDS ========== */
.routes-section {
display: none;
margin-top: 20px;
}
.route-card {
background: #f8f9fa;
border: 2px solid #dee2e6;
border-radius: 12px;
padding: 16px;
margin-bottom: 12px;
cursor: pointer;
transition: all 0.3s;
}
.route-card:hover {
border-color: #0d1b3a;
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
.route-card.selected {
border-color: #198754;
background: #d1e7dd;
}
.route-card-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
}
.route-title {
font-size: 16px;
font-weight: 600;
color: #0d1b3a;
}
.route-badge {
background: #0d1b3a;
color: white;
padding: 4px 12px;
border-radius: 20px;
font-size: 12px;
}
.route-card.selected .route-badge {
background: #198754;
}
.route-info {
display: flex;
gap: 16px;
font-size: 14px;
flex-wrap: wrap;
}
.route-info-item {
display: flex;
align-items: center;
gap: 6px;
}
.route-info-item i {
color: #6c757d;
}
.cost-highlight {
color: #dc3545;
font-weight: 600;
font-size: 16px;
}
/* ========== QR CODE SECTION ========== */
#qrcode-box {
padding: 16px;
border-radius: 10px;
border: 1px solid #dee2e6;
background: #f8f9fa;
display: none;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 220px;
text-align: center;
margin-top: 20px;
}
#qrcode {
margin: 0 auto;
text-align: center;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 180px;
}
/* ========== LOADING & SUCCESS MESSAGES ========== */
.loading-spinner {
display: none;
text-align: center;
padding: 20px;
color: #0d1b3a;
}
.loading-spinner.active {
display: block;
}
.success-message {
display: none;
background: #d1e7dd;
color: #0f5132;
padding: 16px;
border-radius: 10px;
margin-top: 20px;
text-align: center;
}
.success-message.active {
display: block;
}
.success-message i {
font-size: 48px;
margin-bottom: 10px;
}
/* ========== LOCATION DISPLAY ========== */
.location-display {
background: #e7f3ff;
padding: 12px;
border-radius: 8px;
margin-top: 8px;
font-size: 14px;
display: none;
}
.location-display.active {
display: block;
}
.location-display strong {
color: #0d1b3a;
}
/* ========== RESPONSIVE ========== */
@media (max-width: 576px) {
.language-switcher {
top: 12px;
right: 12px;
font-size: 12px;
}
}
</style>
</head>
<body>
<div class="container">
<div class="card p-4">
<!-- ========== LANGUAGE SWITCHER (inside card) ========== -->
<div class="language-switcher mb-3">
<span class="lang-option" id="langTH" onclick="setLanguage('th')">TH</span>
<span class="separator">|</span>
<span class="lang-option" id="langEN" onclick="setLanguage('en')">EN</span>
</div>
<!-- Header -->
<div class="text-center mb-3">
<div class="card-header-title" data-i18n="title">ระบบประมาณการค่าโดยสาร</div>
</div>
<!-- Origin Input -->
<div class="position-relative mb-3">
<div class="input-with-icons">
<div class="input-icon">
<i class="bi bi-record-circle text-success"></i>
</div>
<input
type="text"
id="origin"
class="form-control form-control-lg"
data-i18n-placeholder="origin_placeholder"
placeholder="ตำแหน่งปัจจุบัน/ค้นหาจุดเริ่มต้น..."
autocomplete="off"
onclick="setMapMode('origin')">
<button class="clear-btn" id="clearOrigin" onclick="clearInput('origin')" type="button">
<i class="bi bi-x"></i>
</button>
</div>
<div id="originSuggestions" class="suggestions"></div>
</div>
<!-- Destination Input -->
<div class="position-relative mb-3">
<div class="input-with-icons">
<div class="input-icon">
<i class="bi bi-geo-alt-fill text-danger"></i>
</div>
<input
type="text"
id="destination"
class="form-control form-control-lg"
data-i18n-placeholder="destination_placeholder"
placeholder="ค้นหาจุดหมาย..."
autocomplete="off"
onclick="setMapMode('destination')">
<button class="clear-btn" id="clearDestination" onclick="clearInput('destination')" type="button">
<i class="bi bi-x"></i>
</button>
</div>
<div id="destinationSuggestions" class="suggestions"></div>
</div>
<!-- Search Button -->
<button class="btn btn-success w-100 mb-3" onclick="searchRoute()">
<i class="bi bi-search"></i> <span data-i18n="search_btn">ค้นหาเส้นทาง</span>
</button>
<!-- Map -->
<div id="mapContainer">
<div id="map"></div>
</div>
<!-- Loading Spinner -->
<div class="loading-spinner" id="loadingSpinner">
<div class="spinner-border text-success" role="status"></div>
<p class="mt-2" data-i18n="searching">กำลังค้นหาเส้นทาง...</p>
</div>
<!-- Routes Section -->
<div class="routes-section" id="routesSection">
<h6 class="mb-3"><i class="bi bi-signpost-2"></i> <span data-i18n="select_route">เลือกเส้นทาง</span></h6>
<div class="route-card" id="route1" onclick="selectRoute('main')">
<div class="route-card-header">
<div class="route-title" data-i18n="route1_title">เส้นทางหลัก (วิ่งทางด่วน)</div>
<div class="route-badge" data-i18n="recommended">แนะนำ</div>
</div>
<div class="route-info">
<div class="route-info-item"><i class="bi bi-geo-alt-fill"></i> <span id="dist1">-</span></div>
<div class="route-info-item"><i class="bi bi-clock-fill"></i> <span id="time1">-</span></div>
<div class="route-info-item"><i class="bi bi-cash"></i> <span class="cost-highlight" id="cost1">-</span></div>
</div>
</div>
<div class="route-card" id="route2" onclick="selectRoute('fastest')">
<div class="route-card-header">
<div class="route-title" data-i18n="route2_title">เส้นทางเร็วสุด (ไม่วิ่งทางด่วน)</div>
<div class="route-badge" data-i18n="economical">ประหยัด</div>
</div>
<div class="route-info">
<div class="route-info-item"><i class="bi bi-geo-alt-fill"></i> <span id="dist2">-</span></div>
<div class="route-info-item"><i class="bi bi-clock-fill"></i> <span id="time2">-</span></div>
<div class="route-info-item"><i class="bi bi-cash"></i> <span class="cost-highlight" id="cost2">-</span></div>
</div>
</div>
</div>
<!-- Confirm Button -->
<button class="btn btn-confirm" id="confirmBtn" onclick="confirmRoute()">
<i class="bi bi-check-circle"></i> <span data-i18n="confirm_btn">ยืนยันเส้นทางนี้</span>
</button>
<!-- Success Message -->
<div class="success-message" id="successMessage">
<i class="bi bi-check-circle-fill"></i>
<h5 data-i18n="success_msg">บันทึกเส้นทางไว้อ้างอิงสำเร็จ!</h5>
</div>
<!-- QR Code Toggle & Box -->
<div class="text-center mt-2">
<button class="btn btn-outline-secondary btn-toggle-qr" onclick="toggleQR()">
<i class="bi bi-qr-code"></i> <span data-i18n="toggle_qr">ซ่อน/แสดง QR Code</span>
</button>
</div>
<div id="qrcode-box">
<div class="text-center card-subtitle mb-2" data-i18n="qr_for_passenger">QR Code สำหรับผู้โดยสาร</div>
<div id="qrcode"></div>
<p class="text-secondary small mt-2" data-i18n="scan_qr">ให้ผู้โดยสารสแกน QR เพื่อกรอกข้อมูล</p>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
<script>
// ========== LANGUAGE TRANSLATIONS ==========
const translations = {
th: {
page_title: "คนขับ - เลือกเส้นทาง",
title: "ระบบประมาณการค่าโดยสาร",
origin_placeholder: "ตำแหน่งปัจจุบัน/ค้นหาจุดเริ่มต้น...",
destination_placeholder: "ค้นหาจุดหมาย...",
search_btn: "ค้นหาเส้นทาง",
searching: "กำลังค้นหาเส้นทาง...",
select_route: "เลือกเส้นทาง",
route1_title: "เส้นทางหลัก (วิ่งทางด่วน)",
route2_title: "เส้นทางเร็วสุด (ไม่วิ่งทางด่วน)",
recommended: "แนะนำ",
economical: "ประหยัด",
confirm_btn: "ยืนยันเส้นทางนี้",
success_msg: "บันทึกเส้นทางไว้อ้างอิงสำเร็จ!",
toggle_qr: "ซ่อน/แสดง QR Code",
qr_for_passenger: "QR Code สำหรับผู้โดยสาร",
scan_qr: "ให้ผู้โดยสารสแกน QR เพื่อกรอกข้อมูล",
use_current_location: "ใช้ตำแหน่งปัจจุบัน",
search_results: "ผลการค้นหา",
current_location: "(ตำแหน่งปัจจุบัน)",
km: "กม.",
hr: "ชม.",
min: "นาที",
baht: "บาท",
alert_select_locations: "กรุณาเลือกจุดเริ่มต้นและปลายทาง",
alert_select_route: "กรุณาเลือกเส้นทาง",
alert_gps_error: "ไม่สามารถเข้าถึงตำแหน่งปัจจุบันได้ กรุณาตรวจสอบการอนุญาตใช้งาน GPS",
alert_no_gps: "เบราว์เซอร์ของคุณไม่รองรับ GPS",
getting_location: "กำลังค้นหาตำแหน่ง..."
},
en: {
page_title: "Driver - Route Selection",
title: "Fare Estimation System",
origin_placeholder: "Current location/Search origin...",
destination_placeholder: "Search destination...",
search_btn: "Search Route",
searching: "Searching for routes...",
select_route: "Select Route",
route1_title: "Main Route (with expressway)",
route2_title: "Fastest Route (no expressway)",
recommended: "Recommended",
economical: "Economical",
confirm_btn: "Confirm This Route",
success_msg: "Data Saved Successfully!",
toggle_qr: "Show/Hide QR Code",
qr_for_passenger: "QR Code for Passenger",
scan_qr: "Passenger can scan QR to fill in information",
use_current_location: "Use Current Location",
search_results: "Search Results",
current_location: "(Current Location)",
km: "km",
hr: "hr",
min: "min",
baht: "THB",
alert_select_locations: "Please select origin and destination",
alert_select_route: "Please select a route",
alert_gps_error: "Unable to access current location. Please check GPS permissions",
alert_no_gps: "Your browser does not support GPS",
getting_location: "Getting location..."
}
};
// ========== LANGUAGE MANAGEMENT ==========
let currentLang = localStorage.getItem('language') || 'th';
function setLanguage(lang) {
currentLang = lang;
localStorage.setItem('language', currentLang);
updateLanguage();
updateLanguageSwitcher();
}
function updateLanguageSwitcher() {
document.getElementById('langTH').classList.remove('active');
document.getElementById('langEN').classList.remove('active');
if (currentLang === 'th') {
document.getElementById('langTH').classList.add('active');
} else {
document.getElementById('langEN').classList.add('active');
}
}
function updateLanguage() {
const t = translations[currentLang];
// Update page title
document.getElementById('pageTitle').textContent = t.page_title;
document.documentElement.lang = currentLang;
// Update all elements with data-i18n attribute
document.querySelectorAll('[data-i18n]').forEach(element => {
const key = element.getAttribute('data-i18n');
if (t[key]) {
element.textContent = t[key];
}
});
// Update all placeholders with data-i18n-placeholder attribute
document.querySelectorAll('[data-i18n-placeholder]').forEach(element => {
const key = element.getAttribute('data-i18n-placeholder');
if (t[key]) {
element.placeholder = t[key];
}
});
// 🔥 อัพเดทข้อความ current location ใน input field
if (selectedOrigin && selectedOrigin.isCurrentLocation) {
document.getElementById('origin').value = t.current_location;
} else if (selectedOrigin && !selectedOrigin.isCurrentLocation) {
// 🔥 ถ้าไม่ใช่ current location ให้เรียก reverse geocode ใหม่เพื่อแปลภาษา
reverseGeocode(selectedOrigin.lat, selectedOrigin.lon, 'origin', false);
}
if (selectedDestination && selectedDestination.isCurrentLocation) {
document.getElementById('destination').value = t.current_location;
} else if (selectedDestination && !selectedDestination.isCurrentLocation) {
// 🔥 ถ้าไม่ใช่ current location ให้เรียก reverse geocode ใหม่เพื่อแปลภาษา
reverseGeocode(selectedDestination.lat, selectedDestination.lon, 'destination', false);
}
// 🔥 อัพเดทหน่วยใน route cards ถ้ามีข้อมูลแล้ว
if (routeData.main && routeData.fastest) {
displayRoutes(routeData.main, routeData.fastest);
}
// 🔥 เปลี่ยนภาษาของแผนที่
if (map) {
map.language(currentLang);
}
console.log(`🌐 Language switched to: ${currentLang}`);
}
// Apply language on page load
document.addEventListener('DOMContentLoaded', () => {
updateLanguage();
updateLanguageSwitcher();
});
// ========== ORIGINAL JAVASCRIPT CODE (ทุกฟังก์ชันเดิม) ==========
const LONGDO_API_KEY = '4fc6a833488e90b3df56acc1388c198b';
let sessionId = 'session_' + Date.now() + '_' + Math.random().toString(36).substring(2, 9);
let selectedOrigin = null;
let selectedDestination = null;
let searchTimeout = null;
let map = null;
let selectedRouteType = null;
let routeData = {};
let currentMapMode = 'origin';
let originMarker = null;
let destinationMarker = null;
// ========== MARKER ICONS ==========
const createMarkerIcon = (color, label) => {
return `data:image/svg+xml,${encodeURIComponent(`
<svg width="40" height="50" xmlns="http://www.w3.org/2000/svg">
<path d="M20 2C11.716 2 5 8.716 5 17c0 8.284 15 31 15 31s15-22.716 15-31c0-8.284-6.716-15-15-15z"
fill="${color}" stroke="#fff" stroke-width="2"/>
<circle cx="20" cy="17" r="11" fill="white"/>
<text x="50%" y="35%" text-anchor="middle" dominant-baseline="central"
font-size="14" font-weight="bold" fill="${color}">${label}</text>
</svg>
`)}`;
};
const greenMarker = createMarkerIcon('#22c55e', 'S');
const redMarker = createMarkerIcon('#ef4444', 'E');
// ========== FORMAT LOCATION NAME ==========
function formatLocationName(data) {
const parts = [];
if (data.house_no) parts.push(`เลขที่ ${data.house_no}`);
if (data.moo) parts.push(data.moo);
if (data.place) parts.push(data.place);
if (data.building) parts.push(data.building);
if (data.condominium) parts.push(data.condominium);
if (data.village) parts.push(data.village);
if (data.village_official) parts.push(data.village_official);
if (data.sublane) parts.push(data.sublane);
if (data.alley) parts.push(data.alley);
if (data.road) parts.push(data.road);
if (data.subdistrict) parts.push(data.subdistrict);
if (data.district) parts.push(data.district);
if (data.province) parts.push(data.province);
return parts.length > 0 ? parts.join(' ') : null;
}
// ========== INITIALIZE MAP ==========
function initMap() {
map = new longdo.Map({
placeholder: document.getElementById('map'),
language: currentLang, // 🔥 ใช้ภาษาที่เลือกไว้
zoom: 12
});
map.Event.bind('click', function() {
const loc = map.location(longdo.LocationMode.Pointer);
handleMapClick(loc.lat, loc.lon);
});
getCurrentLocation();
}
// ========== GET CURRENT GPS LOCATION ==========
function getCurrentLocation() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(
(position) => {
const lat = position.coords.latitude;
const lon = position.coords.longitude;
map.location({
lon,
lat
}, true);
map.zoom(15);
reverseGeocode(lat, lon, 'origin', true);
},
(error) => {
console.error('GPS Error:', error);
map.location({
lon: 100.5,
lat: 13.75
}, true);
map.zoom(12);
}, {
enableHighAccuracy: true,
timeout: 10000,
maximumAge: 0
}
);
} else {
map.location({
lon: 100.5,
lat: 13.75
}, true);
}
}
// ========== SET MAP MODE ==========
function setMapMode(mode) {
currentMapMode = mode;
}
// ========== HANDLE MAP CLICK ==========
function handleMapClick(lat, lon) {
reverseGeocode(lat, lon, currentMapMode, false);
}
// ========== REVERSE GEOCODING ==========
async function reverseGeocode(lat, lon, type, isCurrentLocation = false) {
const t = translations[currentLang];
try {
// 🔥 เพิ่ม locale parameter เพื่อให้ที่อยู่เป็นภาษาที่เลือก
const response = await fetch(`https://api.longdo.com/map/services/address?lon=${lon}&lat=${lat}&locale=${currentLang}&key=${LONGDO_API_KEY}`);
const data = await response.json();
let name = formatLocationName(data) || `${lat.toFixed(6)}, ${lon.toFixed(6)}`;
if (isCurrentLocation) {
name = `${t.current_location}`;
}
updateLocation(type, name, lat, lon, isCurrentLocation);
} catch (error) {
console.error('Reverse Geocode Error:', error);
let name = `${lat.toFixed(6)}, ${lon.toFixed(6)}`;
if (isCurrentLocation) {
name = `${t.current_location} ${name}`;
}
updateLocation(type, name, lat, lon, isCurrentLocation);
}
}
// ========== UPDATE LOCATION ==========
function updateLocation(type, name, lat, lon, isCurrentLocation = false) {
const locationData = {
name,
lat,
lon,
isCurrentLocation: isCurrentLocation // 🔥 เก็บสถานะว่าเป็น current location หรือไม่
};
if (type === 'origin') {
selectedOrigin = locationData;
const inputElement = document.getElementById('origin');
inputElement.value = name;
if (isCurrentLocation) {
inputElement.style.color = '#198754';
inputElement.style.fontWeight = '600';
} else {
inputElement.style.color = '';
inputElement.style.fontWeight = '';
}
toggleClearButton('origin', true);
if (originMarker) map.Overlays.remove(originMarker);
originMarker = new longdo.Marker({
lat,
lon
}, {
icon: {
url: greenMarker,
offset: {
x: 20,
y: 50
}
},
title: name
});
map.Overlays.add(originMarker);
} else {
selectedDestination = locationData;
const inputElement = document.getElementById('destination');
inputElement.value = name;
if (isCurrentLocation) {
inputElement.style.color = '#198754';
inputElement.style.fontWeight = '600';
} else {
inputElement.style.color = '';
inputElement.style.fontWeight = '';
}
toggleClearButton('destination', true);
if (destinationMarker) map.Overlays.remove(destinationMarker);
destinationMarker = new longdo.Marker({
lat,
lon
}, {
icon: {
url: redMarker,
offset: {
x: 20,
y: 50
}
},
title: name
});
map.Overlays.add(destinationMarker);
}
map.location({
lat,
lon
}, true);
}
// ========== CLEAR INPUT ==========
function clearInput(type) {
if (type === 'origin') {
const inputElement = document.getElementById('origin');
inputElement.value = '';
inputElement.style.color = '';
inputElement.style.fontWeight = '';
selectedOrigin = null;
document.getElementById('clearOrigin').classList.remove('show');
if (originMarker) {
map.Overlays.remove(originMarker);
originMarker = null;
}
} else {
const inputElement = document.getElementById('destination');
inputElement.value = '';
inputElement.style.color = '';
inputElement.style.fontWeight = '';
selectedDestination = null;
document.getElementById('clearDestination').classList.remove('show');
if (destinationMarker) {
map.Overlays.remove(destinationMarker);
destinationMarker = null;
}
}
if (!selectedOrigin || !selectedDestination) {
map.Route.clear();
document.getElementById('routesSection').style.display = 'none';
document.getElementById('confirmBtn').style.display = 'none';
}
}
// ========== TOGGLE CLEAR BUTTON ==========
function toggleClearButton(inputId, show) {
const clearBtn = document.getElementById('clear' + inputId.charAt(0).toUpperCase() + inputId.slice(1));
if (clearBtn) {
if (show) {
clearBtn.classList.add('show');
} else {
clearBtn.classList.remove('show');
}
}
}
// ========== SEARCH PLACES ==========
async function searchPlace(keyword, element, isOrigin) {
if (keyword.length < 1) {
element.style.display = 'none';
return;
}
try {
// 🔥 เพิ่ม locale parameter เพื่อให้ผลค้นหาเป็นภาษาที่เลือก
const url = `https://search.longdo.com/mapsearch/json/search?keyword=${encodeURIComponent(keyword)}&limit=5&locale=${currentLang}&key=${LONGDO_API_KEY}`;
const response = await fetch(url);
const data = await response.json();
if (data?.data?.length > 0) {
displaySuggestions(data.data, element, isOrigin);
} else {
displaySuggestions([], element, isOrigin);
}
} catch (error) {
console.error('Search Error:', error);
displaySuggestions([], element, isOrigin);
}
}
// ========== DISPLAY SUGGESTIONS ==========
function displaySuggestions(places, element, isOrigin) {
const t = translations[currentLang];
element.innerHTML = '';
// ตัวเลือก "ใช้ตำแหน่งปัจจุบัน"
const currentLocationItem = document.createElement('div');
currentLocationItem.className = 'suggestion-item current-location-item';
currentLocationItem.innerHTML = `
<i class="bi bi-crosshair text-success me-2"></i>
<strong>${t.use_current_location}</strong>
`;
currentLocationItem.onclick = () => {
useCurrentLocation(isOrigin);
element.style.display = 'none';
};
element.appendChild(currentLocationItem);
// แสดงผลการค้นหา
if (places.length > 0) {
const separator = document.createElement('div');
separator.className = 'suggestion-separator';
separator.textContent = t.search_results;
element.appendChild(separator);
places.sort((a, b) => (a.name || "").localeCompare(b.name || "", "th"));
places.forEach(place => {
const item = document.createElement('div');
item.className = 'suggestion-item';
const fullAddress = place.address || '';
item.innerHTML = `
<i class="bi bi-geo-alt text-secondary me-2"></i>
<div>
<div><strong>${place.name}</strong></div>
${fullAddress ? `<div class="text-muted small">${fullAddress}</div>` : ''}
</div>
`;
item.onclick = async () => {
await reverseGeocode(place.lat, place.lon, isOrigin ? 'origin' : 'destination', false);
element.style.display = 'none';
};
element.appendChild(item);
});
}
element.style.display = 'block';
}
// ========== USE CURRENT LOCATION ==========
function useCurrentLocation(isOrigin) {
const t = translations[currentLang];
if (navigator.geolocation) {
const inputId = isOrigin ? 'origin' : 'destination';
document.getElementById(inputId).value = t.getting_location;
navigator.geolocation.getCurrentPosition(
(position) => {
const lat = position.coords.latitude;
const lon = position.coords.longitude;
map.location({
lon,
lat
}, true);
map.zoom(15);
reverseGeocode(lat, lon, isOrigin ? 'origin' : 'destination', true);
},
(error) => {
console.error('GPS Error:', error);
alert(t.alert_gps_error);
document.getElementById(inputId).value = '';
}, {
enableHighAccuracy: true,
timeout: 10000,
maximumAge: 0
}
);
} else {
alert(t.alert_no_gps);
}
}
// ========== EVENT LISTENERS ==========
document.getElementById('origin').addEventListener('input', e => {
toggleClearButton('origin', e.target.value.length > 0);
clearTimeout(searchTimeout);
searchTimeout = setTimeout(() =>
searchPlace(e.target.value, document.getElementById('originSuggestions'), true), 300);
});
document.getElementById('destination').addEventListener('input', e => {
toggleClearButton('destination', e.target.value.length > 0);
clearTimeout(searchTimeout);
searchTimeout = setTimeout(() =>
searchPlace(e.target.value, document.getElementById('destinationSuggestions'), false), 300);
});
document.addEventListener('click', e => {
if (!e.target.closest('.position-relative')) {
document.getElementById('originSuggestions').style.display = 'none';
document.getElementById('destinationSuggestions').style.display = 'none';
}
});
document.getElementById('origin').addEventListener('focus', () => {
const box = document.getElementById('originSuggestions');
if (box.innerHTML === '') {
displaySuggestions([], box, true);
}
box.style.display = 'block';
});
document.getElementById('destination').addEventListener('focus', () => {
const box = document.getElementById('destinationSuggestions');
if (box.innerHTML === '') {
displaySuggestions([], box, false);
}
box.style.display = 'block';
});
// ========== QR CODE ==========
function generateQRCode() {
const qrDiv = document.getElementById('qrcode');
qrDiv.innerHTML = '';
const baseUrl = window.location.href.split('/').slice(0, -1).join('/');
new QRCode(qrDiv, {
text: baseUrl + '/passengerqp.php?session=' + sessionId,
width: 180,
height: 180,
colorDark: '#0d1b3a',
colorLight: '#ffffff',
correctLevel: QRCode.CorrectLevel.H
});
}
function toggleQR() {
const box = document.getElementById('qrcode-box');
box.style.display = box.style.display === 'none' || box.style.display === '' ? 'flex' : 'none';
}
// ========== ROUTE FUNCTIONS ==========
async function searchRoute() {
const t = translations[currentLang];
if (!selectedOrigin || !selectedDestination) {
alert(t.alert_select_locations);
return;
}
document.getElementById('loadingSpinner').classList.add('active');
document.getElementById('routesSection').style.display = 'none';
document.getElementById('confirmBtn').style.display = 'none';
try {
const [route1, route2] = await Promise.all([
fetchRoute('t', '25'),
fetchRoute('d', '17')
]);
routeData = {
main: route1,
fastest: route2
};
displayRoutes(route1, route2);
displayRouteOnMap();
document.getElementById('loadingSpinner').classList.remove('active');
document.getElementById('routesSection').style.display = 'block';
document.getElementById('routesSection').scrollIntoView({
behavior: 'smooth',
block: 'start'
});
} catch (error) {
document.getElementById('loadingSpinner').classList.remove('active');
alert('เกิดข้อผิดพลาดในการค้นหาเส้นทาง');
console.error(error);
}
}
function fetchRoute(mode, type) {
const url = 'https://api.longdo.com/RouteService/json/route/guide';
const params = new URLSearchParams({
flon: selectedOrigin.lon,
flat: selectedOrigin.lat,
tlon: selectedDestination.lon,
tlat: selectedDestination.lat,
mode,
type,
locale: currentLang, // 🔥 ใช้ภาษาที่เลือก แทน hardcode 'th'
key: LONGDO_API_KEY
});
return fetch(`${url}?${params}`).then(r => r.json());
}
function calculateTaxiFare(distanceKm, timeSeconds) {
let fare = 35; // ราคาเริ่มต้น
// คำนวณค่าระยะทาง
if (distanceKm > 1) {
let remainingKm = distanceKm - 1;
// 1-2 กม.: +6.5 บาท
if (remainingKm <= 1) {
fare += 6.5;
} else {
fare += 6.5;
remainingKm -= 1;
// 2-10 กม.: 6.5 บาท/กม.
if (remainingKm <= 8) {
fare += remainingKm * 6.5;
} else {
fare += 8 * 6.5;
remainingKm -= 8;
// 10-20 กม.: 7 บาท/กม.
if (remainingKm <= 10) {
fare += remainingKm * 7;
} else {
fare += 10 * 7;
remainingKm -= 10;
// 20-40 กม.: 8 บาท/กม.
if (remainingKm <= 20) {
fare += remainingKm * 8;
} else {
fare += 20 * 8;
remainingKm -= 20;
// 40-60 กม.: 8.5 บาท/กม.
if (remainingKm <= 20) {
fare += remainingKm * 8.5;
} else {
fare += 20 * 8.5;
remainingKm -= 20;
// 60-80 กม.: 9 บาท/กม.
if (remainingKm <= 20) {
fare += remainingKm * 9;
} else {
fare += 20 * 9;
remainingKm -= 20;
// 80+ กม.: 10.5 บาท/กม.
fare += remainingKm * 10.5;
}
}
}
}
}
}
}
// คำนวณค่ารถติด
// 40 วินาที = 2 บาท → 3 บาท/นาที (60/40 * 2 = 3)
const trafficFarePerMinute = 0;
const timeMinutes = timeSeconds / 60;
const trafficFare = timeMinutes * trafficFarePerMinute;
fare += trafficFare;
return Math.round(fare);
}
function displayRoutes(r1, r2) {
const t = translations[currentLang];
const d1 = r1.data[0];
const d2 = r2.data[0];
const taxiFare1 = calculateTaxiFare(d1.distance / 1000, d1.interval);
const taxiFare2 = calculateTaxiFare(d2.distance / 1000, d2.interval);
document.getElementById('dist1').textContent = `${(d1.distance / 1000).toFixed(2)} ${t.km}`;
document.getElementById('time1').textContent = formatTime(d1.interval);
document.getElementById('cost1').textContent = `${taxiFare1} ${t.baht}`;
document.getElementById('dist2').textContent = `${(d2.distance / 1000).toFixed(2)} ${t.km}`;
document.getElementById('time2').textContent = formatTime(d2.interval);
document.getElementById('cost2').textContent = `${taxiFare2} ${t.baht}`;
}
function formatTime(sec) {
const t = translations[currentLang];
const min = Math.round(sec / 60);
const h = Math.floor(min / 60);
const m = min % 60;
return h > 0 ? `${h} ${t.hr} ${m} ${t.min}` : `${m} ${t.min}`;
}
function displayRouteOnMap() {
map.Route.clear();
map.Route.add(new longdo.Marker({
lon: selectedOrigin.lon,
lat: selectedOrigin.lat
}, {
icon: {
url: greenMarker,
offset: {
x: 20,
y: 50
}
}
}));
map.Route.add(new longdo.Marker({
lon: selectedDestination.lon,
lat: selectedDestination.lat
}, {
icon: {
url: redMarker,
offset: {
x: 20,
y: 50
}
}
}));
// <-- ตั้งค่าสีเส้นก่อน search() (ใช้วิธี map.call แนะนำ)
try {
map.call('Route.line', 'road', {
lineColor: '#1500ffff',
lineWidth: 6,
borderColor: '#000000',
borderWidth: 1
});
} catch (e) {
// หากเรียกไม่ได้ ให้ลองวิธีสำรอง
if (typeof map.Route.line === 'function') {
map.Route.line('road', {
lineColor: '#ff0000',
lineWidth: 2
});
} else {
// fallback: map.Route.option (ถ้ามี)
if (typeof map.Route.option === 'function') {
map.Route.option({
lineColor: '#00ff59ff',
lineWidth: 2
});
}
}
console.warn('Route style applied via fallback methods (if available).', e);
}
map.Route.mode(longdo.RouteMode.Traffic);
map.Route.search();
}
function selectRoute(type) {
selectedRouteType = type;
document.querySelectorAll('.route-card').forEach(c => c.classList.remove('selected'));
document.getElementById(type === 'main' ? 'route1' : 'route2').classList.add('selected');
document.getElementById('confirmBtn').style.display = 'block';
map.Route.clear();
if (type === 'main') {
map.Route.mode(longdo.RouteMode.Traffic);
map.Route.enableRoute(longdo.RouteType.Tollway, true);
} else {
map.Route.mode(longdo.RouteMode.Traffic);
map.Route.enableRoute(longdo.RouteType.Tollway, false);
}
map.Route.add(new longdo.Marker({
lon: selectedOrigin.lon,
lat: selectedOrigin.lat
}, {
icon: {
url: greenMarker,
offset: {
x: 20,
y: 50
}
}
}));
map.Route.add(new longdo.Marker({
lon: selectedDestination.lon,
lat: selectedDestination.lat
}, {
icon: {
url: redMarker,
offset: {
x: 20,
y: 50
}
}
}));
map.Route.search();
setTimeout(() => {
fitMapToRoute();
}, 800);
}
function fitMapToRoute() {
if (!selectedOrigin || !selectedDestination) return;
try {
const bounds = {
minLon: Math.min(selectedOrigin.lon, selectedDestination.lon),
minLat: Math.min(selectedOrigin.lat, selectedDestination.lat),
maxLon: Math.max(selectedOrigin.lon, selectedDestination.lon),
maxLat: Math.max(selectedOrigin.lat, selectedDestination.lat)
};
const lonPadding = (bounds.maxLon - bounds.minLon) * 0.15 || 0.01;
const latPadding = (bounds.maxLat - bounds.minLat) * 0.15 || 0.01;
bounds.minLon -= lonPadding;
bounds.maxLon += lonPadding;
bounds.minLat -= latPadding;
bounds.maxLat += latPadding;
map.bound(bounds, true);
console.log('🗺️ Map fitted to route bounds:', bounds);
} catch (error) {
console.error('Error fitting map to route:', error);
const centerLat = (selectedOrigin.lat + selectedDestination.lat) / 2;
const centerLon = (selectedOrigin.lon + selectedDestination.lon) / 2;
map.location({
lon: centerLon,
lat: centerLat
}, true);
map.zoom(10, true);
}
}
async function confirmRoute() {
const t = translations[currentLang];
if (!selectedRouteType) {
alert(t.alert_select_route);
return;
}
const routeInfo = selectedRouteType === 'main' ? routeData.main.data[0] : routeData.fastest.data[0];
const fare = calculateTaxiFare(routeInfo.distance / 1000, routeInfo.interval);
const dataToSave = {
sessionId: sessionId,
origin: {
name: selectedOrigin.name,
lat: selectedOrigin.lat,
lon: selectedOrigin.lon
},
destination: {
name: selectedDestination.name,
lat: selectedDestination.lat,
lon: selectedDestination.lon
},
routeType: selectedRouteType,
distance: (routeInfo.distance / 1000).toFixed(2),
time: formatTime(routeInfo.interval),
fare: fare,
timestamp: new Date().toISOString()
};
localStorage.setItem('routeData_' + sessionId, JSON.stringify(dataToSave));
console.log('Data saved:', dataToSave);
document.getElementById('confirmBtn').style.display = 'none';
document.getElementById('successMessage').classList.add('active');
document.getElementById('successMessage').scrollIntoView({
behavior: 'smooth',
block: 'center'
});
}
// ========== INITIALIZE ==========
window.addEventListener('load', () => {
initMap();
generateQRCode();
});
</script>
</body>
</html>