DhakaMetroWeb / index.html
shukdevdatta123's picture
Update index.html
824f8af verified
<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];
// Convert sheet to JSON to filter blank rows
var jsonData = XLSX.utils.sheet_to_json(worksheet, { header: 1, blankrows: false, defval: '' });
// Filter out blank rows (rows where all cells are empty, null, or undefined)
var filteredData = jsonData.filter(row => row.some(filledCell));
// Heuristic to find the header row by ignoring rows with fewer filled cells than the next row
var headerRowIndex = filteredData.findIndex((row, index) =>
row.filter(filledCell).length >= filteredData[index + 1]?.filter(filledCell).length
);
// Fallback
if (headerRowIndex === -1 || headerRowIndex > 25) {
headerRowIndex = 0;
}
// Convert filtered JSON back to CSV
var csv = XLSX.utils.aoa_to_sheet(filteredData.slice(headerRowIndex)); // Create a new sheet from filtered array of arrays
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/[email protected]/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/[email protected]/dist/leaflet.js"></script>
<script src="data.js"></script>
<script>
// Populate origin dropdown
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);
});
// Handle origin selection
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);
});
}
});
// Update fare display
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);
}
});
}
}
// Clear destinations
document.getElementById('clear-destinations').addEventListener('click', () => {
document.querySelectorAll('#destination-buttons button.selected').forEach(btn => {
btn.classList.remove('selected');
});
selectedDestinations = [];
updateFareDisplay(originSelect.value);
});
// Map setup
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>