diff --git a/index.html b/index.html
new file mode 100644
index 0000000..7527a0c
--- /dev/null
+++ b/index.html
@@ -0,0 +1,35 @@
+
+
+
+
+
+ Ride Comparing
+
+
+
+
+
+
+ Ride Comparing
+
+
+
+
+
+
+
+
+
Uber:
+
Lyft:
+
Bolt:
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/script.js b/script.js
new file mode 100644
index 0000000..feb25fe
--- /dev/null
+++ b/script.js
@@ -0,0 +1,309 @@
+let map;
+let userMarker;
+let userLocation;
+let dropoffMarker;
+let routesLayerGroup;
+let currentRequestController = null;
+let currentActiveRoute = null;
+
+function initMap() {
+ map = L.map('map').setView([52.3676, 4.9041], 13);
+
+ L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
+ maxZoom: 19,
+ }).addTo(map);
+
+ // Request user location
+ if (navigator.geolocation) {
+ navigator.geolocation.getCurrentPosition(
+ (position) => {
+ userLocation = [position.coords.latitude, position.coords.longitude];
+ map.setView(userLocation, 15);
+ userMarker = L.marker(userLocation).addTo(map).bindPopup('You are here').openPopup();
+ },
+ (error) => {
+ console.error('Error getting location: ', error);
+ }
+ );
+ }
+
+ map.on('click', async (e) => {
+ const latlng = e.latlng;
+ const address = await getAddress(latlng.lat, latlng.lng);
+ if (address) {
+ document.getElementById("end").value = address;
+ resetMap();
+ addDropoffMarker({ lat: latlng.lat, lon: latlng.lng });
+ }
+ });
+
+ map.touchZoom.enable();
+ map.scrollWheelZoom.enable();
+ map.doubleClickZoom.enable();
+}
+
+async function fetchStartSuggestions() {
+ const startLocation = document.getElementById("start").value;
+ const startSuggestionsDiv = document.getElementById("start-suggestions");
+
+ if (startLocation.length < 3) {
+ startSuggestionsDiv.style.display = 'none';
+ return;
+ }
+
+ if (currentRequestController) {
+ currentRequestController.abort();
+ }
+
+ currentRequestController = new AbortController();
+
+ try {
+ const response = await fetch(`https://nominatim.openstreetmap.org/search?q=${encodeURIComponent(startLocation)}&format=json&addressdetails=1&limit=5`, {
+ signal: currentRequestController.signal
+ });
+ const data = await response.json();
+
+ if (currentRequestController.signal.aborted) return;
+
+ startSuggestionsDiv.innerHTML = '';
+ data.forEach(feature => {
+ const div = document.createElement('div');
+ div.className = 'suggestion-item';
+ div.textContent = feature.display_name;
+ div.onclick = () => selectStartSuggestion(feature);
+ startSuggestionsDiv.appendChild(div);
+ });
+
+ startSuggestionsDiv.style.display = data.length ? 'block' : 'none';
+ } catch (error) {
+ if (error.name !== 'AbortError') {
+ console.error('Fetch error:', error);
+ }
+ } finally {
+ currentRequestController = null;
+ }
+}
+
+function selectStartSuggestion(feature) {
+ document.getElementById("start").value = feature.display_name;
+ document.getElementById("start-suggestions").style.display = 'none';
+ resetMap();
+ userLocation = [feature.lat, feature.lon];
+ if (userMarker) {
+ map.removeLayer(userMarker);
+ }
+ userMarker = L.marker(userLocation).addTo(map).bindPopup('You are here').openPopup();
+ map.setView(userLocation, 15);
+}
+
+async function fetchSuggestions() {
+ const endLocation = document.getElementById("end").value;
+ const suggestionsDiv = document.getElementById("suggestions");
+
+ if (endLocation.length < 3) {
+ suggestionsDiv.style.display = 'none';
+ return;
+ }
+
+ if (currentRequestController) {
+ currentRequestController.abort();
+ }
+
+ currentRequestController = new AbortController();
+
+ try {
+ const response = await fetch(`https://nominatim.openstreetmap.org/search?q=${encodeURIComponent(endLocation)}&format=json&addressdetails=1&limit=5`, {
+ signal: currentRequestController.signal
+ });
+ const data = await response.json();
+
+ if (currentRequestController.signal.aborted) return;
+
+ suggestionsDiv.innerHTML = '';
+ data.forEach(feature => {
+ const div = document.createElement('div');
+ div.className = 'suggestion-item';
+ div.textContent = feature.display_name;
+ div.onclick = () => selectEndSuggestion(feature);
+ suggestionsDiv.appendChild(div);
+ });
+
+ suggestionsDiv.style.display = data.length ? 'block' : 'none';
+ } catch (error) {
+ if (error.name !== 'AbortError') {
+ console.error('Fetch error:', error);
+ }
+ } finally {
+ currentRequestController = null;
+ }
+}
+
+function selectEndSuggestion(feature) {
+ document.getElementById("end").value = feature.display_name;
+ document.getElementById("suggestions").style.display = 'none';
+ resetMap();
+ addDropoffMarker({ lat: feature.lat, lon: feature.lon });
+}
+
+async function getAddress(lat, lon) {
+ try {
+ const response = await fetch(`https://nominatim.openstreetmap.org/reverse?lat=${lat}&lon=${lon}&format=json`);
+ const data = await response.json();
+ return data.display_name || null;
+ } catch (error) {
+ console.error('Fetch error:', error);
+ return null;
+ }
+}
+
+function resetMap() {
+ if (dropoffMarker) {
+ map.removeLayer(dropoffMarker);
+ }
+ if (routesLayerGroup) {
+ map.removeLayer(routesLayerGroup);
+ }
+ document.getElementById("uber-price").textContent = "";
+ document.getElementById("lyft-price").textContent = "";
+ document.getElementById("bolt-price").textContent = "";
+ currentActiveRoute = null;
+}
+
+function addDropoffMarker(location) {
+ dropoffMarker = L.marker([location.lat, location.lon]).addTo(map);
+ fetchRoutes(location);
+}
+
+async function fetchRoutes(location) {
+ const url = `https://router.project-osrm.org/route/v1/driving/${userLocation[1]},${userLocation[0]};${location.lon},${location.lat}?geometries=geojson&overview=full&alternatives=true`;
+
+ try {
+ const response = await fetch(url);
+ const data = await response.json();
+ if (data.routes.length) {
+ displayRoutes(data.routes);
+ const route = data.routes[0];
+ const distanceMeters = route.distance;
+ const distanceKilometers = (distanceMeters / 1000).toFixed(2);
+ const duration = route.duration;
+ const hours = Math.floor(duration / 3600);
+ const minutes = Math.floor((duration % 3600) / 60);
+ const seconds = Math.round(duration % 60);
+
+ const distanceDisplay = distanceMeters > 1000
+ ? `${distanceKilometers} km (${distanceMeters} m)`
+ : `${distanceMeters} m`;
+
+ const durationDisplay = hours > 0
+ ? `${hours} hr ${minutes} min ${seconds} sec`
+ : `${minutes} min ${seconds} sec`;
+
+ dropoffMarker.bindPopup(`Distance: ${distanceDisplay}
Duration: ${durationDisplay}`).openPopup();
+ fetchRideEstimates(location);
+ }
+ } catch (error) {
+ console.error('Error fetching routes:', error);
+ }
+}
+
+function displayRoutes(routes) {
+ routesLayerGroup = L.layerGroup().addTo(map);
+
+ routes.forEach((route, index) => {
+ const coordinates = route.geometry.coordinates.map(coord => [coord[1], coord[0]]);
+ const polylineOptions = {
+ color: 'blue',
+ opacity: index === 0 ? 0.7 : 0.3,
+ weight: 5
+ };
+
+ const routePolyline = L.polyline(coordinates, polylineOptions).addTo(routesLayerGroup);
+
+ routePolyline.on('click', () => {
+ if (currentActiveRoute) {
+ currentActiveRoute.setStyle({ opacity: 0.3 });
+ }
+ routePolyline.setStyle({ opacity: 0.7 });
+ currentActiveRoute = routePolyline;
+ });
+
+ const duration = route.duration;
+ const hours = Math.floor(duration / 3600);
+ const minutes = Math.floor((duration % 3600) / 60);
+ const seconds = Math.round(duration % 60);
+ const distanceMeters = route.distance;
+ const distanceKilometers = (distanceMeters / 1000).toFixed(2);
+
+ const distanceDisplay = distanceMeters > 1000
+ ? `${distanceKilometers} km (${distanceMeters} m)`
+ : `${distanceMeters} m`;
+
+ const durationDisplay = hours > 0
+ ? `${hours} hr ${minutes} min ${seconds} sec`
+ : `${minutes} min ${seconds} sec`;
+
+ routePolyline.bindPopup(`Route ${index + 1}: ${durationDisplay}
Distance: ${distanceDisplay}`);
+
+ if (index === 0) {
+ currentActiveRoute = routePolyline;
+ }
+ });
+}
+
+async function fetchRideEstimates(location) {
+ const dropoffLat = dropoffMarker.getLatLng().lat;
+ const dropoffLng = dropoffMarker.getLatLng().lng;
+
+ const uberApiKey = 'YOUR_UBER_API_KEY';
+ const lyftApiKey = 'YOUR_LYFT_API_KEY';
+ const boltApiKey = 'YOUR_BOLT_API_KEY';
+
+ const uberUrl = `https://api.uber.com/v1.2/estimates/price?start_latitude=${userLocation[0]}&start_longitude=${userLocation[1]}&end_latitude=${dropoffLat}&end_longitude=${dropoffLng}`;
+ const lyftUrl = `https://api.lyft.com/v1/cost?start_lat=${userLocation[0]}&start_lng=${userLocation[1]}&end_lat=${dropoffLat}&end_lng=${dropoffLng}`;
+ const boltUrl = `https://api.bolt.eu/v1/estimates/price?start_latitude=${userLocation[0]}&start_longitude=${userLocation[1]}&end_latitude=${dropoffLat}&end_longitude=${dropoffLng}`;
+
+ try {
+ const [uberResponse, lyftResponse, boltResponse] = await Promise.all([
+ fetch(uberUrl, { headers: { Authorization: `Bearer ${uberApiKey}` } }),
+ fetch(lyftUrl, { headers: { Authorization: `Bearer ${lyftApiKey}` } }),
+ fetch(boltUrl, { headers: { Authorization: `Bearer ${boltApiKey}` } })
+ ]);
+
+ const [uberData, lyftData, boltData] = await Promise.all([uberResponse.json(), lyftResponse.json(), boltResponse.json()]);
+
+ const uberEstimate = uberData.prices[0] ? `${uberData.prices[0].estimated_cost_cents / 100} USD` : "Not available";
+ const lyftEstimate = lyftData.cost_estimates[0] ? `${lyftData.cost_estimates[0].estimated_cost_cents / 100} USD` : "Not available";
+ const boltEstimate = boltData.estimates[0] ? `${boltData.estimates[0].cost} USD` : "Not available";
+
+ document.getElementById("uber-price").textContent = uberEstimate;
+ document.getElementById("lyft-price").textContent = lyftEstimate;
+ document.getElementById("bolt-price").textContent = boltEstimate;
+ } catch (error) {
+ console.error('Error fetching ride estimates:', error);
+ document.getElementById("uber-price").textContent = "Error";
+ document.getElementById("lyft-price").textContent = "Error";
+ document.getElementById("bolt-price").textContent = "Error";
+ }
+}
+
+async function fetchRides() {
+ const endLocation = document.getElementById("end").value;
+
+ if (!endLocation) {
+ return;
+ }
+
+ const endCoordinates = await getCoordinates(endLocation);
+ if (endCoordinates && endCoordinates.lat && endCoordinates.lon) {
+ resetMap();
+ addDropoffMarker(endCoordinates);
+ }
+}
+
+async function getCoordinates(location) {
+ const response = await fetch(`https://nominatim.openstreetmap.org/search?q=${encodeURIComponent(location)}&format=json&limit=1`);
+ const data = await response.json();
+ return data[0] ? { lat: parseFloat(data[0].lat), lon: parseFloat(data[0].lon), address: data[0].display_name } : null;
+}
+
+window.onload = initMap;
diff --git a/style.css b/style.css
new file mode 100644
index 0000000..00a33b2
--- /dev/null
+++ b/style.css
@@ -0,0 +1,103 @@
+body {
+ font-family: Arial, sans-serif;
+ margin: 0;
+ padding: 20px;
+ background-color: #f4f4f4;
+}
+
+h1 {
+ text-align: center;
+ color: #333;
+ margin-bottom: 20px;
+}
+
+#map {
+ height: 400px;
+ width: 100%;
+ margin-bottom: 20px;
+ border-radius: 8px;
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
+}
+
+.filter {
+ margin-bottom: 20px;
+ padding: 15px;
+ background: #fff;
+ border-radius: 8px;
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
+ display: flex;
+ flex-direction: column;
+}
+
+input[type="text"] {
+ width: 100%;
+ padding: 10px;
+ border: 1px solid #ddd;
+ border-radius: 5px;
+ margin-bottom: 10px;
+}
+
+button {
+ padding: 10px;
+ background-color: #28a745;
+ color: white;
+ border: none;
+ border-radius: 5px;
+ cursor: pointer;
+ transition: background-color 0.3s;
+}
+
+button:hover {
+ background-color: #218838;
+}
+
+.suggestions {
+ border: 1px solid #ddd;
+ background-color: white;
+ position: absolute;
+ z-index: 1000;
+ width: calc(100% - 20px);
+ max-height: 200px;
+ overflow-y: auto;
+ border-radius: 5px;
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
+}
+
+.suggestion-item {
+ padding: 10px;
+ cursor: pointer;
+}
+
+.suggestion-item:hover {
+ background-color: #f0f0f0;
+}
+
+.price-estimates {
+ margin-top: 10px;
+ font-weight: bold;
+ display: flex;
+ justify-content: space-between;
+ padding: 10px;
+ background: #f8f9fa;
+ border-radius: 5px;
+ box-shadow: 0 1px 5px rgba(0, 0, 0, 0.1);
+}
+
+@media (max-width: 600px) {
+ .filter {
+ flex-direction: column;
+ }
+
+ input[type="text"] {
+ margin-bottom: 10px;
+ }
+
+ .price-estimates {
+ flex-direction: column;
+ align-items: flex-start;
+ }
+
+ .price-estimates div {
+ margin-bottom: 5px;
+ }
+}
\ No newline at end of file