|
<script type="text/javascript"> |
|
var gk_isXlsx = false; |
|
var gk_xlsxFileLookup = {}; |
|
var gk_fileData = {}; |
|
function filledCell(cell) { |
|
return cell !== '' && cell != null; |
|
} |
|
function loadFileData(filename) { |
|
if (gk_isXlsx && gk_xlsxFileLookup[filename]) { |
|
try { |
|
var workbook = XLSX.read(gk_fileData[filename], { type: 'base64' }); |
|
var firstSheetName = workbook.SheetNames[0]; |
|
var worksheet = workbook.Sheets[firstSheetName]; |
|
|
|
|
|
var jsonData = XLSX.utils.sheet_to_json(worksheet, { header: 1, blankrows: false, defval: '' }); |
|
|
|
var filteredData = jsonData.filter(row => row.some(filledCell)); |
|
|
|
|
|
var headerRowIndex = filteredData.findIndex((row, index) => |
|
row.filter(filledCell).length >= filteredData[index + 1]?.filter(filledCell).length |
|
); |
|
|
|
if (headerRowIndex === -1 || headerRowIndex > 25) { |
|
headerRowIndex = 0; |
|
} |
|
|
|
|
|
var csv = XLSX.utils.aoa_to_sheet(filteredData.slice(headerRowIndex)); |
|
csv = XLSX.utils.sheet_to_csv(csv, { header: 1 }); |
|
return csv; |
|
} catch (e) { |
|
console.error(e); |
|
return ""; |
|
} |
|
} |
|
return gk_fileData[filename] || ""; |
|
} |
|
</script><!DOCTYPE html> |
|
<html lang="en"> |
|
<head> |
|
<meta charset="UTF-8"> |
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
<title>Dhaka Metro Rail Fare Checker</title> |
|
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" /> |
|
<style> |
|
@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap'); |
|
|
|
* { |
|
margin: 0; |
|
padding: 0; |
|
box-sizing: border-box; |
|
} |
|
|
|
body { |
|
font-family: 'Roboto', sans-serif; |
|
background-color: #f4f4f4; |
|
color: #333; |
|
overflow-x: hidden; |
|
} |
|
|
|
header { |
|
background: linear-gradient(135deg, #003366, #005588); |
|
color: white; |
|
padding: 2rem; |
|
text-align: center; |
|
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2); |
|
animation: slideInDown 1s ease-out; |
|
} |
|
|
|
header h1 { |
|
font-size: 2.5rem; |
|
display: flex; |
|
align-items: center; |
|
justify-content: center; |
|
gap: 10px; |
|
} |
|
|
|
aside#sidebar { |
|
position: fixed; |
|
top: 0; |
|
left: 0; |
|
width: 280px; |
|
height: 100%; |
|
background: #D8C4B6; |
|
padding: 20px; |
|
overflow-y: auto; |
|
box-shadow: 2px 0 10px rgba(0, 0, 0, 0.1); |
|
transform: translateX(-100%); |
|
animation: slideInLeft 0.8s ease-out forwards; |
|
} |
|
|
|
aside h2 { |
|
color: #003366; |
|
margin-bottom: 15px; |
|
} |
|
|
|
aside p { |
|
font-size: 0.95rem; |
|
line-height: 1.6; |
|
} |
|
|
|
aside a { |
|
color: #009688; |
|
text-decoration: none; |
|
} |
|
|
|
main { |
|
margin-left: 300px; |
|
padding: 30px; |
|
animation: fadeIn 1s ease-in; |
|
} |
|
|
|
#fare-checker, #map-section { |
|
background: white; |
|
padding: 25px; |
|
border-radius: 12px; |
|
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.1); |
|
margin-bottom: 30px; |
|
transition: transform 0.3s ease; |
|
} |
|
|
|
#fare-checker:hover, #map-section:hover { |
|
transform: translateY(-5px); |
|
} |
|
|
|
h2 { |
|
color: #003366; |
|
margin-bottom: 20px; |
|
} |
|
|
|
label { |
|
font-weight: bold; |
|
margin-bottom: 10px; |
|
display: block; |
|
} |
|
|
|
select { |
|
width: 100%; |
|
padding: 12px; |
|
margin-bottom: 20px; |
|
border: 2px solid #009688; |
|
border-radius: 6px; |
|
font-size: 1rem; |
|
transition: border-color 0.3s ease; |
|
} |
|
|
|
select:focus { |
|
border-color: #e91e63; |
|
outline: none; |
|
} |
|
|
|
#destination-buttons { |
|
display: flex; |
|
flex-wrap: wrap; |
|
gap: 12px; |
|
margin-bottom: 20px; |
|
} |
|
|
|
#destination-buttons button { |
|
background: #009688; |
|
color: white; |
|
border: none; |
|
padding: 10px 20px; |
|
border-radius: 25px; |
|
cursor: pointer; |
|
transition: all 0.3s ease; |
|
} |
|
|
|
#destination-buttons button:hover { |
|
background: #00796b; |
|
transform: scale(1.05); |
|
} |
|
|
|
#destination-buttons button.selected { |
|
background: #e91e63; |
|
transform: scale(1.05); |
|
} |
|
|
|
#clear-destinations { |
|
background: #e91e63; |
|
color: white; |
|
border: none; |
|
padding: 12px 25px; |
|
border-radius: 25px; |
|
cursor: pointer; |
|
transition: all 0.3s ease; |
|
} |
|
|
|
#clear-destinations:hover { |
|
background: #c2185b; |
|
transform: scale(1.05); |
|
} |
|
|
|
#fare-display .fare-item { |
|
background: #f0f8ff; |
|
padding: 15px; |
|
margin-bottom: 15px; |
|
border-radius: 8px; |
|
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05); |
|
animation: fadeInUp 0.5s ease-out; |
|
} |
|
|
|
#map { |
|
height: 500px; |
|
border-radius: 8px; |
|
margin-top: 20px; |
|
} |
|
|
|
#map-controls { |
|
display: flex; |
|
justify-content: center; |
|
gap: 10px; |
|
margin-top: 15px; |
|
flex-wrap: wrap; |
|
} |
|
|
|
#map-controls button { |
|
background: #003366; |
|
color: white; |
|
border: none; |
|
padding: 10px 20px; |
|
border-radius: 25px; |
|
cursor: pointer; |
|
transition: all 0.3s ease; |
|
} |
|
|
|
#map-controls button:hover { |
|
background: #005588; |
|
transform: scale(1.05); |
|
} |
|
|
|
@keyframes slideInDown { |
|
from { transform: translateY(-100%); } |
|
to { transform: translateY(0); } |
|
} |
|
|
|
@keyframes slideInLeft { |
|
from { transform: translateX(-100%); } |
|
to { transform: translateX(0); } |
|
} |
|
|
|
@keyframes fadeIn { |
|
from { opacity: 0; } |
|
to { opacity: 1; } |
|
} |
|
|
|
@keyframes fadeInUp { |
|
from { opacity: 0; transform: translateY(20px); } |
|
to { opacity: 1; transform: translateY(0); } |
|
} |
|
|
|
@media (max-width: 768px) { |
|
aside#sidebar { |
|
position: relative; |
|
width: 100%; |
|
height: auto; |
|
transform: none; |
|
animation: none; |
|
} |
|
|
|
main { |
|
margin-left: 0; |
|
padding: 15px; |
|
} |
|
|
|
#map { |
|
height: 300px; |
|
} |
|
|
|
header h1 { |
|
font-size: 1.8rem; |
|
} |
|
} |
|
</style> |
|
</head> |
|
<body> |
|
<header> |
|
<h1>Dhaka Metro Rail Fare Checker 🚇</h1> |
|
</header> |
|
<aside id="sidebar"> |
|
<h2>Instructions</h2> |
|
<p> |
|
<strong>Welcome to the Dhaka Metro Rail Fare Checker! 🚇</strong><br> |
|
<em>How to use:</em><br> |
|
1. <strong>Select your Location station</strong>: Choose your starting station from the "Select your Location" dropdown.<br> |
|
2. <strong>Select your destination(s)</strong>: Click the buttons for your desired destinations. Select multiple for different routes!<br> |
|
3. <strong>Fare Calculation</strong>: See fares below for your selected routes.<br> |
|
4. <strong>Clear Destinations</strong>: Hit "Clear All Destinations" to reset.<br> |
|
<hr> |
|
<strong>Interactive Map</strong>: Visualize routes and animate stations below.<br> |
|
- <strong>Dropdowns</strong>: Pick source and destination.<br> |
|
- <strong>Animate Route</strong>: Watch your route come to life.<br> |
|
- <strong>Animate All</strong>: See all stations animated.<br> |
|
- <strong>Stop</strong>: Pause animations anytime.<br> |
|
Enjoy your journey! 🚉<br> |
|
Need help? <a href="https://wa.me/+8801719296601">Contact Support</a>. |
|
</p> |
|
</aside> |
|
<main> |
|
<section id="fare-checker"> |
|
<h2>Check Your Fare</h2> |
|
<label for="origin">Select your Location:</label> |
|
<select id="origin"> |
|
<option value="">Select Journey from</option> |
|
</select> |
|
<div id="destination-buttons"></div> |
|
<button id="clear-destinations">Clear All Destinations</button> |
|
<div id="fare-display"></div> |
|
</section> |
|
<section id="map-section"> |
|
<h2>Interactive Map</h2> |
|
<div id="map"></div> |
|
<div id="map-controls"> |
|
<select id="map-source"> |
|
<option value="">Select Source</option> |
|
</select> |
|
<select id="map-destination"> |
|
<option value="">Select Destination</option> |
|
</select> |
|
<button onclick="startRouteAnimation()">Animate Route</button> |
|
<button onclick="animateAllLocations()">Animate All Locations</button> |
|
<button onclick="stopAnimation()">Stop Animation</button> |
|
</div> |
|
</section> |
|
</main> |
|
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script> |
|
<script src="data.js"></script> |
|
<script> |
|
|
|
const originSelect = document.getElementById('origin'); |
|
const origins = [...new Set(fareData.map(item => item.Origin))]; |
|
origins.forEach(origin => { |
|
const option = document.createElement('option'); |
|
option.value = option.textContent = origin; |
|
originSelect.appendChild(option); |
|
}); |
|
|
|
|
|
let selectedDestinations = []; |
|
originSelect.addEventListener('change', () => { |
|
const origin = originSelect.value; |
|
const destButtons = document.getElementById('destination-buttons'); |
|
destButtons.innerHTML = ''; |
|
selectedDestinations = []; |
|
document.getElementById('fare-display').innerHTML = ''; |
|
if (origin) { |
|
const destinations = fareData.filter(item => item.Origin === origin).map(item => item.Destination); |
|
destinations.forEach(dest => { |
|
const btn = document.createElement('button'); |
|
btn.textContent = `Select ${dest}`; |
|
btn.dataset.dest = dest; |
|
btn.addEventListener('click', () => { |
|
btn.classList.toggle('selected'); |
|
if (btn.classList.contains('selected')) { |
|
selectedDestinations.push(dest); |
|
} else { |
|
selectedDestinations = selectedDestinations.filter(d => d !== dest); |
|
} |
|
updateFareDisplay(origin); |
|
}); |
|
destButtons.appendChild(btn); |
|
}); |
|
} |
|
}); |
|
|
|
|
|
function updateFareDisplay(origin) { |
|
const fareDisplay = document.getElementById('fare-display'); |
|
fareDisplay.innerHTML = ''; |
|
if (origin && selectedDestinations.length) { |
|
selectedDestinations.forEach(dest => { |
|
const fare = fareData.find(item => item.Origin === origin && item.Destination === dest); |
|
if (fare) { |
|
const div = document.createElement('div'); |
|
div.className = 'fare-item'; |
|
div.innerHTML = ` |
|
<h4>🚇 <strong>${origin}</strong> to <strong>${dest}</strong> Fare</h4> |
|
<p>💵 Fare: <strong>${fare['Fare (৳)']}৳</strong></p> |
|
<p>✨ Enjoy your journey!</p> |
|
`; |
|
fareDisplay.appendChild(div); |
|
} |
|
}); |
|
} |
|
} |
|
|
|
|
|
document.getElementById('clear-destinations').addEventListener('click', () => { |
|
document.querySelectorAll('#destination-buttons button.selected').forEach(btn => { |
|
btn.classList.remove('selected'); |
|
}); |
|
selectedDestinations = []; |
|
updateFareDisplay(originSelect.value); |
|
}); |
|
|
|
|
|
const coordinates = { |
|
"Uttara North": [23.869066, 90.367445], |
|
"Uttara Center": [23.860118, 90.365106], |
|
"Uttara South": [23.845934, 90.363175], |
|
"Pallabi": [23.82619516961383, 90.36481554252525], |
|
"Mirpur 11": [23.819438208310213, 90.36528532902963], |
|
"Mirpur 10": [23.808582994847285, 90.36821595330717], |
|
"Kazipara": [23.800017952100532, 90.37178261495391], |
|
"Shewrapara": [23.79070140857881, 90.37564622631841], |
|
"Agargaon": [23.778385546736345, 90.3800557456356], |
|
"Bijoy Sarani": [23.766638127271825, 90.38307537134754], |
|
"Farmgate": [23.75923604938459, 90.38694218434738], |
|
"Kawran Bazar": [23.751392319539104, 90.39275707447003], |
|
"Shahbagh": [23.740324209546923, 90.39600784811131], |
|
"Dhaka University": [23.732091083122114, 90.39659408796354], |
|
"Bangladesh Secretariat": [23.73004754106779, 90.40764881366906], |
|
"Motijheel": [23.72816566933198, 90.41923497972823], |
|
"Kamalapur": [23.732367758919807, 90.42547378971085] |
|
}; |
|
|
|
const map = L.map('map').setView([23.8103, 90.4125], 12); |
|
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { |
|
maxZoom: 19, |
|
attribution: '© OpenStreetMap contributors' |
|
}).addTo(map); |
|
|
|
const markers = {}; |
|
for (const [name, coord] of Object.entries(coordinates)) { |
|
markers[name] = L.marker(coord).addTo(map).bindPopup(`<b>${name}</b>`); |
|
} |
|
|
|
const mapSource = document.getElementById('map-source'); |
|
const mapDest = document.getElementById('map-destination'); |
|
Object.keys(coordinates).forEach(loc => { |
|
const opt = document.createElement('option'); |
|
opt.value = opt.textContent = loc; |
|
mapSource.appendChild(opt.cloneNode(true)); |
|
mapDest.appendChild(opt); |
|
}); |
|
|
|
let currentIndex = 0; |
|
const markerArray = Object.values(markers); |
|
let animationTimeout; |
|
|
|
function getIntermediateNodes(source, dest) { |
|
const locations = Object.keys(coordinates); |
|
const sIdx = locations.indexOf(source); |
|
const dIdx = locations.indexOf(dest); |
|
return sIdx < dIdx ? locations.slice(sIdx, dIdx + 1) : locations.slice(dIdx, sIdx + 1).reverse(); |
|
} |
|
|
|
function startRouteAnimation() { |
|
const source = mapSource.value; |
|
const dest = mapDest.value; |
|
if (!source || !dest) return alert('Select source and destination.'); |
|
const route = getIntermediateNodes(source, dest); |
|
let idx = 0; |
|
function animate() { |
|
if (idx >= route.length) return; |
|
const marker = markers[route[idx]]; |
|
map.flyTo(marker.getLatLng(), 14, { duration: 2 }); |
|
marker.openPopup(); |
|
idx++; |
|
animationTimeout = setTimeout(animate, 3000); |
|
} |
|
animate(); |
|
} |
|
|
|
function animateAllLocations() { |
|
if (currentIndex > 0) markerArray[currentIndex - 1].closePopup(); |
|
if (currentIndex < markerArray.length) { |
|
const marker = markerArray[currentIndex]; |
|
map.flyTo(marker.getLatLng(), 14, { duration: 2 }); |
|
marker.openPopup(); |
|
currentIndex++; |
|
animationTimeout = setTimeout(animateAllLocations, 3000); |
|
} else { |
|
currentIndex = 0; |
|
} |
|
} |
|
|
|
function stopAnimation() { |
|
clearTimeout(animationTimeout); |
|
currentIndex = 0; |
|
markerArray.forEach(m => m.closePopup()); |
|
map.setView([23.8103, 90.4125], 12); |
|
mapSource.value = mapDest.value = ''; |
|
} |
|
</script> |
|
</body> |
|
</html> |