feat: create interactive hotel map and search interface for Paris hotels under 300€
This commit is contained in:
parent
ee946f020b
commit
a2f6e2e378
5
.dockerignore
Normal file
5
.dockerignore
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
Dockerfile
|
||||||
|
.dockerignore
|
||||||
|
.git
|
||||||
|
.gitignore
|
||||||
|
README.md
|
||||||
18
Dockerfile
Normal file
18
Dockerfile
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# Use an official Nginx image as the base image
|
||||||
|
FROM nginx:alpine
|
||||||
|
|
||||||
|
# Set the working directory in the container
|
||||||
|
WORKDIR /usr/share/nginx/html
|
||||||
|
|
||||||
|
# Remove default Nginx welcome page
|
||||||
|
RUN rm -rf ./*
|
||||||
|
|
||||||
|
# Copy all the static assets from your project to the Nginx public directory
|
||||||
|
# This includes index.html, app.js, style.css, and any images or data files
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Expose port 80 (the default Nginx port)
|
||||||
|
EXPOSE 80
|
||||||
|
|
||||||
|
# Command to run Nginx when the container starts
|
||||||
|
CMD ["nginx", "-g", "daemon off;"]
|
||||||
543
app.js
Normal file
543
app.js
Normal file
@ -0,0 +1,543 @@
|
|||||||
|
// Hotel data
|
||||||
|
const hotelData = [
|
||||||
|
{
|
||||||
|
"name": "Hotel Britannique",
|
||||||
|
"address": "20 Av. Victoria, 75001 Paris",
|
||||||
|
"rating": 4.6,
|
||||||
|
"price_range": "€200-320",
|
||||||
|
"arrondissement": "1st",
|
||||||
|
"coordinates": [48.8567, 2.3471],
|
||||||
|
"description": "Classic 19th-century hotel near Châtelet with theatrical red decor and excellent Metro access",
|
||||||
|
"website": "https://hotel-britannique.fr",
|
||||||
|
"amenities": ["Free WiFi", "Minibar", "Air Conditioning", "Elevator"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Hôtel Signature Saint Germain des Prés",
|
||||||
|
"address": "5 Rue Chomel, 75007 Paris",
|
||||||
|
"rating": 4.8,
|
||||||
|
"price_range": "€220-290",
|
||||||
|
"arrondissement": "7th",
|
||||||
|
"coordinates": [48.8485, 2.3244],
|
||||||
|
"description": "Chic boutique property in Saint-Germain with elegant, colorful rooms and vintage-chic lobby",
|
||||||
|
"website": "http://www.signature-saintgermain.com",
|
||||||
|
"amenities": ["Free WiFi", "Air Conditioning", "Buffet Breakfast", "24-hour Reception"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Paris France Hotel",
|
||||||
|
"address": "72 R. de Turbigo, 75003 Paris",
|
||||||
|
"rating": 4.7,
|
||||||
|
"price_range": "€180-280",
|
||||||
|
"arrondissement": "3rd",
|
||||||
|
"coordinates": [48.8644, 2.3614],
|
||||||
|
"description": "Traditional hotel with a cafe/bar and lounge in the heart of the Marais district",
|
||||||
|
"website": "https://www.paris-france-hotel.com",
|
||||||
|
"amenities": ["Free WiFi", "Café/Bar", "Lounge", "Optional Breakfast"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Hôtel Lenox Montparnasse",
|
||||||
|
"address": "15 Rue Delambre, 75014 Paris",
|
||||||
|
"rating": 4.5,
|
||||||
|
"price_range": "€170-260",
|
||||||
|
"arrondissement": "14th",
|
||||||
|
"coordinates": [48.8427, 2.3269],
|
||||||
|
"description": "Classic rooms & suites with warmly decorated honesty bar near Montparnasse",
|
||||||
|
"website": "http://www.paris-hotel-lenox.com",
|
||||||
|
"amenities": ["Free WiFi", "Honesty Bar", "Classic Decor", "Cozy Atmosphere"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Hôtel Caron de Beaumarchais",
|
||||||
|
"address": "12 Rue Vieille du Temple, 75004 Paris",
|
||||||
|
"rating": 4.6,
|
||||||
|
"price_range": "€200-280",
|
||||||
|
"arrondissement": "4th",
|
||||||
|
"coordinates": [48.8572, 2.3598],
|
||||||
|
"description": "18th-century lodging with antiques-filled rooms and private balconies in Le Marais",
|
||||||
|
"website": "http://www.carondebeaumarchais.com",
|
||||||
|
"amenities": ["Free WiFi", "Private Balconies", "Antique Furnishings", "18th Century Charm"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Hôtel Bourg Tibourg",
|
||||||
|
"address": "19 Rue du Bourg Tibourg, 75004 Paris",
|
||||||
|
"rating": 4.7,
|
||||||
|
"price_range": "€250-300",
|
||||||
|
"arrondissement": "4th",
|
||||||
|
"coordinates": [48.8547, 2.3581],
|
||||||
|
"description": "Lavish boutique hotel with opulent rooms and sophisticated dramatic atmosphere",
|
||||||
|
"website": "https://www.bourgtibourg.com",
|
||||||
|
"amenities": ["Free WiFi", "Opulent Decor", "Sophisticated Atmosphere", "Dramatic Design"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Hôtel de l'Abbaye Saint-Germain",
|
||||||
|
"address": "10 Rue Cassette, 75006 Paris",
|
||||||
|
"rating": 4.6,
|
||||||
|
"price_range": "€280-300",
|
||||||
|
"arrondissement": "6th",
|
||||||
|
"coordinates": [48.8495, 2.3309],
|
||||||
|
"description": "Refined hotel with plush lounges and garden, plus free WiFi & breakfast",
|
||||||
|
"website": "https://www.hotelabbayeparis.com",
|
||||||
|
"amenities": ["Free WiFi", "Garden", "Plush Lounges", "Breakfast Included"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Hotel Quartier Latin",
|
||||||
|
"address": "9 Rue des Écoles, 75005 Paris",
|
||||||
|
"rating": 4.3,
|
||||||
|
"price_range": "€160-250",
|
||||||
|
"arrondissement": "5th",
|
||||||
|
"coordinates": [48.8490, 2.3468],
|
||||||
|
"description": "Traditional hotel with simple rooms, minibars and breakfast buffet option",
|
||||||
|
"website": "https://hotelquartierlatin.com",
|
||||||
|
"amenities": ["Free WiFi", "Minibar", "Breakfast Buffet", "Traditional Decor"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "CitizenM Paris Gare de Lyon",
|
||||||
|
"address": "8 Rue Van Gogh, 75012 Paris",
|
||||||
|
"rating": 4.4,
|
||||||
|
"price_range": "€150-220",
|
||||||
|
"arrondissement": "12th",
|
||||||
|
"coordinates": [48.8448, 2.3732],
|
||||||
|
"description": "Trendy budget hotel with compact rooms, ambient lighting and 24-hour cafe/bar",
|
||||||
|
"website": "https://www.citizenm.com/hotels/europe/paris/gare-de-lyon-hotel",
|
||||||
|
"amenities": ["24-hour Café/Bar", "Ambient Lighting", "Compact Design", "Modern Amenities"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Hôtel Migny Opéra-Montmartre",
|
||||||
|
"address": "13 Rue Victor Massé, 75009 Paris",
|
||||||
|
"rating": 4.1,
|
||||||
|
"price_range": "€140-200",
|
||||||
|
"arrondissement": "9th",
|
||||||
|
"coordinates": [48.8810, 2.3375],
|
||||||
|
"description": "Colorful rooms with satellite TV, casual breakfast area and lounge near Opéra",
|
||||||
|
"website": "https://www.hotelmigny.com",
|
||||||
|
"amenities": ["Free WiFi", "Satellite TV", "Breakfast Area", "LGBTQ+ Friendly"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Hôtel Saint-André des Arts",
|
||||||
|
"address": "66 Rue Saint-André des Arts, 75006 Paris",
|
||||||
|
"rating": 4.8,
|
||||||
|
"price_range": "€180-280",
|
||||||
|
"arrondissement": "6th",
|
||||||
|
"coordinates": [48.8540, 2.3401],
|
||||||
|
"description": "Boutique hotel in Saint-Germain with unique rooms and bar",
|
||||||
|
"website": "https://www.saintandredesarts.com",
|
||||||
|
"amenities": ["Bar", "Unique Rooms", "Saint-Germain Location", "Boutique Style"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Grand Hôtel des Gobelins",
|
||||||
|
"address": "57 Bd Saint-Marcel, 75013 Paris",
|
||||||
|
"rating": 4.1,
|
||||||
|
"price_range": "€130-180",
|
||||||
|
"arrondissement": "13th",
|
||||||
|
"coordinates": [48.8375, 2.3556],
|
||||||
|
"description": "Casual hotel with warmly furnished rooms and lobby bar",
|
||||||
|
"website": "http://www.grandhotelgobelins.com",
|
||||||
|
"amenities": ["Free WiFi", "Lobby Bar", "Warm Furnishings", "Relaxed Atmosphere"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Hotel Marignan",
|
||||||
|
"address": "13 Rue du Sommerard, 75005 Paris",
|
||||||
|
"rating": 4.1,
|
||||||
|
"price_range": "€120-180",
|
||||||
|
"arrondissement": "5th",
|
||||||
|
"coordinates": [48.8504, 2.3445],
|
||||||
|
"description": "Laid-back hotel with simple rooms, guest kitchen and library, plus free breakfast",
|
||||||
|
"website": "http://www.hotel-marignan.com",
|
||||||
|
"amenities": ["Free Breakfast", "Free WiFi", "Guest Kitchen", "Library"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Hôtel La Comtesse Tour Eiffel",
|
||||||
|
"address": "29 Av. de Tourville, 75007 Paris",
|
||||||
|
"rating": 4.6,
|
||||||
|
"price_range": "€250-300",
|
||||||
|
"arrondissement": "7th",
|
||||||
|
"coordinates": [48.8548, 2.3078],
|
||||||
|
"description": "Traditional hotel with classic rooms and Eiffel Tower views",
|
||||||
|
"website": "http://www.comtesse-hotel.com",
|
||||||
|
"amenities": ["Free WiFi", "Eiffel Tower Views", "Bar", "Traditional Decor"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Boutique Hôtel Mareuil Paris",
|
||||||
|
"address": "51 Rue de Malte, 75011 Paris",
|
||||||
|
"rating": 4.7,
|
||||||
|
"price_range": "€200-280",
|
||||||
|
"arrondissement": "11th",
|
||||||
|
"coordinates": [48.8640, 2.3677],
|
||||||
|
"description": "Hip property with modern rooms & suites, plus bar, hammam & exercise room",
|
||||||
|
"website": "https://www.hotelmareuil.com/fr/",
|
||||||
|
"amenities": ["Bar", "Hammam", "Exercise Room", "Modern Design"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Generator Paris",
|
||||||
|
"address": "9-11 Pl. du Colonel Fabien, 75010 Paris",
|
||||||
|
"rating": 4.1,
|
||||||
|
"price_range": "€90-150",
|
||||||
|
"arrondissement": "10th",
|
||||||
|
"coordinates": [48.8772, 2.3711],
|
||||||
|
"description": "Trendy hostel with stylish rooms & streamlined dorms, rooftop terrace & vibrant eatery",
|
||||||
|
"website": "https://staygenerator.com/hostels/paris?lang=fr-FR",
|
||||||
|
"amenities": ["Rooftop Terrace", "Restaurant", "Bar", "Budget-Friendly"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Hôtel Henriette",
|
||||||
|
"address": "9 rue des Gobelins, 75013 Paris",
|
||||||
|
"rating": 4.5,
|
||||||
|
"price_range": "€130-180",
|
||||||
|
"arrondissement": "13th",
|
||||||
|
"coordinates": [48.8363, 2.3518],
|
||||||
|
"description": "Stylish boutique hotel with subtly decorated rooms and courtyard breakfast area",
|
||||||
|
"website": "https://www.hotelhenriette.com",
|
||||||
|
"amenities": ["Free WiFi", "Courtyard", "Breakfast", "Design Interiors"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Hotel Paradis",
|
||||||
|
"address": "41 Rue Des Petities Ecuries, 75010 Paris",
|
||||||
|
"rating": 4.3,
|
||||||
|
"price_range": "€120-180",
|
||||||
|
"arrondissement": "10th",
|
||||||
|
"coordinates": [48.8718, 2.3502],
|
||||||
|
"description": "Tastefully decorated rooms in a top-notch location near trendy hotspots",
|
||||||
|
"website": "https://hotelparadisparis.com",
|
||||||
|
"amenities": ["Free WiFi", "Designer Decor", "Trendy Area", "Modern Amenities"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "COQ Hotel Paris",
|
||||||
|
"address": "15 Rue Edouard Manet, 75013 Paris",
|
||||||
|
"rating": 4.4,
|
||||||
|
"price_range": "€140-190",
|
||||||
|
"arrondissement": "13th",
|
||||||
|
"coordinates": [48.8261, 2.3542],
|
||||||
|
"description": "Stylish design hotel with cozy bar, extensive breakfast and warm atmosphere",
|
||||||
|
"website": "https://coq-hotel-paris.com",
|
||||||
|
"amenities": ["Bar", "Breakfast", "Design Interiors", "Cozy Atmosphere"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Hotel Diana",
|
||||||
|
"address": "73 Rue Saint-Jacques, 75005 Paris",
|
||||||
|
"rating": 4.2,
|
||||||
|
"price_range": "€150-200",
|
||||||
|
"arrondissement": "5th",
|
||||||
|
"coordinates": [48.8485, 2.3456],
|
||||||
|
"description": "Simple but comfortable 2-star hotel with bright rooms and modern bathrooms",
|
||||||
|
"website": "http://www.hoteldiana.fr",
|
||||||
|
"amenities": ["Free WiFi", "Modern Bathrooms", "Central Location", "Budget-Friendly"]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
// Global variables
|
||||||
|
let map;
|
||||||
|
let markers = [];
|
||||||
|
let filteredHotels = hotelData;
|
||||||
|
let selectedHotel = null;
|
||||||
|
|
||||||
|
// Initialize the application
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
initializeApp();
|
||||||
|
});
|
||||||
|
|
||||||
|
function initializeApp() {
|
||||||
|
initializeMap();
|
||||||
|
populateArrondissementFilter();
|
||||||
|
bindEventListeners();
|
||||||
|
applyFilters();
|
||||||
|
updateHotelCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize Leaflet map
|
||||||
|
function initializeMap() {
|
||||||
|
// Initialize map centered on Paris
|
||||||
|
map = L.map('map').setView([48.8566, 2.3522], 12);
|
||||||
|
|
||||||
|
// Add OpenStreetMap tiles
|
||||||
|
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||||
|
attribution: '© OpenStreetMap contributors',
|
||||||
|
maxZoom: 18
|
||||||
|
}).addTo(map);
|
||||||
|
|
||||||
|
// Add hotels to map
|
||||||
|
addHotelsToMap(hotelData);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add hotel markers to map
|
||||||
|
function addHotelsToMap(hotels) {
|
||||||
|
// Clear existing markers
|
||||||
|
markers.forEach(marker => map.removeLayer(marker));
|
||||||
|
markers = [];
|
||||||
|
|
||||||
|
hotels.forEach(hotel => {
|
||||||
|
const marker = L.marker([hotel.coordinates[0], hotel.coordinates[1]], {
|
||||||
|
icon: L.divIcon({
|
||||||
|
className: 'custom-marker',
|
||||||
|
html: `<div class="marker-pin" style="background-color: #21808D; width: 24px; height: 24px; border-radius: 50%; border: 3px solid white; box-shadow: 0 2px 4px rgba(0,0,0,0.3);"></div>`,
|
||||||
|
iconSize: [24, 24],
|
||||||
|
iconAnchor: [12, 12]
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create popup content
|
||||||
|
const popupContent = `
|
||||||
|
<div class="popup-content">
|
||||||
|
<h3 class="popup-hotel-name">${hotel.name}</h3>
|
||||||
|
<div class="popup-rating">
|
||||||
|
<span class="stars">${generateStars(hotel.rating)}</span>
|
||||||
|
<span>${hotel.rating}</span>
|
||||||
|
</div>
|
||||||
|
<div class="popup-price">${hotel.price_range}</div>
|
||||||
|
<div class="popup-arrondissement">${hotel.arrondissement} Arrondissement</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
marker.bindPopup(popupContent);
|
||||||
|
marker.addTo(map);
|
||||||
|
markers.push(marker);
|
||||||
|
|
||||||
|
// Store hotel data with marker for easy access
|
||||||
|
marker.hotelData = hotel;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Populate arrondissement dropdown
|
||||||
|
function populateArrondissementFilter() {
|
||||||
|
const select = document.getElementById('arrondissement');
|
||||||
|
const arrondissements = [...new Set(hotelData.map(hotel => hotel.arrondissement))].sort();
|
||||||
|
|
||||||
|
arrondissements.forEach(arr => {
|
||||||
|
const option = document.createElement('option');
|
||||||
|
option.value = arr;
|
||||||
|
option.textContent = `${arr} Arrondissement`;
|
||||||
|
select.appendChild(option);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bind event listeners
|
||||||
|
function bindEventListeners() {
|
||||||
|
// View toggle buttons
|
||||||
|
document.getElementById('map-view-btn').addEventListener('click', () => switchView('map'));
|
||||||
|
document.getElementById('list-view-btn').addEventListener('click', () => switchView('list'));
|
||||||
|
|
||||||
|
// Search input
|
||||||
|
document.getElementById('search').addEventListener('input', applyFilters);
|
||||||
|
|
||||||
|
// Price filter buttons
|
||||||
|
document.querySelectorAll('.price-filter').forEach(button => {
|
||||||
|
button.addEventListener('click', function() {
|
||||||
|
document.querySelectorAll('.price-filter').forEach(btn => btn.classList.remove('active'));
|
||||||
|
this.classList.add('active');
|
||||||
|
applyFilters();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Arrondissement filter
|
||||||
|
document.getElementById('arrondissement').addEventListener('change', applyFilters);
|
||||||
|
|
||||||
|
// Rating slider
|
||||||
|
const ratingSlider = document.getElementById('rating');
|
||||||
|
const ratingValue = document.getElementById('rating-value');
|
||||||
|
ratingSlider.addEventListener('input', function() {
|
||||||
|
ratingValue.textContent = this.value;
|
||||||
|
applyFilters();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Reset filters button
|
||||||
|
document.getElementById('reset-filters').addEventListener('click', resetFilters);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Switch between map and list view
|
||||||
|
function switchView(view) {
|
||||||
|
const mapViewBtn = document.getElementById('map-view-btn');
|
||||||
|
const listViewBtn = document.getElementById('list-view-btn');
|
||||||
|
const mapView = document.getElementById('map-view');
|
||||||
|
const listView = document.getElementById('list-view');
|
||||||
|
|
||||||
|
if (view === 'map') {
|
||||||
|
mapViewBtn.classList.add('active');
|
||||||
|
mapViewBtn.classList.remove('btn--secondary');
|
||||||
|
mapViewBtn.classList.add('btn--primary');
|
||||||
|
listViewBtn.classList.remove('active');
|
||||||
|
listViewBtn.classList.remove('btn--primary');
|
||||||
|
listViewBtn.classList.add('btn--secondary');
|
||||||
|
mapView.classList.add('active');
|
||||||
|
listView.classList.remove('active');
|
||||||
|
|
||||||
|
// Refresh map after view switch
|
||||||
|
setTimeout(() => map.invalidateSize(), 100);
|
||||||
|
} else {
|
||||||
|
listViewBtn.classList.add('active');
|
||||||
|
listViewBtn.classList.remove('btn--secondary');
|
||||||
|
listViewBtn.classList.add('btn--primary');
|
||||||
|
mapViewBtn.classList.remove('active');
|
||||||
|
mapViewBtn.classList.remove('btn--primary');
|
||||||
|
mapViewBtn.classList.add('btn--secondary');
|
||||||
|
listView.classList.add('active');
|
||||||
|
mapView.classList.remove('active');
|
||||||
|
|
||||||
|
renderHotelList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply all filters
|
||||||
|
function applyFilters() {
|
||||||
|
const searchTerm = document.getElementById('search').value.toLowerCase();
|
||||||
|
const selectedArrondissement = document.getElementById('arrondissement').value;
|
||||||
|
const minRating = parseFloat(document.getElementById('rating').value);
|
||||||
|
const activePriceFilter = document.querySelector('.price-filter.active');
|
||||||
|
const minPrice = parseInt(activePriceFilter.dataset.min);
|
||||||
|
const maxPrice = parseInt(activePriceFilter.dataset.max);
|
||||||
|
|
||||||
|
filteredHotels = hotelData.filter(hotel => {
|
||||||
|
// Search filter
|
||||||
|
if (searchTerm && !hotel.name.toLowerCase().includes(searchTerm)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Arrondissement filter
|
||||||
|
if (selectedArrondissement !== 'all' && hotel.arrondissement !== selectedArrondissement) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rating filter
|
||||||
|
if (hotel.rating < minRating) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Price filter
|
||||||
|
const hotelMinPrice = parseInt(hotel.price_range.match(/€(\d+)/)[1]);
|
||||||
|
const hotelMaxPrice = parseInt(hotel.price_range.match(/€\d+-(\d+)/)[1]);
|
||||||
|
|
||||||
|
// Check if hotel price range overlaps with selected price range
|
||||||
|
if (hotelMaxPrice < minPrice || hotelMinPrice > maxPrice) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update map markers
|
||||||
|
addHotelsToMap(filteredHotels);
|
||||||
|
|
||||||
|
// Update list view if active
|
||||||
|
if (document.getElementById('list-view').classList.contains('active')) {
|
||||||
|
renderHotelList();
|
||||||
|
}
|
||||||
|
|
||||||
|
updateHotelCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update hotel count display
|
||||||
|
function updateHotelCount() {
|
||||||
|
document.getElementById('hotel-count').textContent = filteredHotels.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset all filters
|
||||||
|
function resetFilters() {
|
||||||
|
document.getElementById('search').value = '';
|
||||||
|
document.getElementById('arrondissement').value = 'all';
|
||||||
|
document.getElementById('rating').value = '4.0';
|
||||||
|
document.getElementById('rating-value').textContent = '4.0';
|
||||||
|
|
||||||
|
// Reset price filter to "Under €150" to focus on budget options
|
||||||
|
document.querySelectorAll('.price-filter').forEach(btn => btn.classList.remove('active'));
|
||||||
|
document.querySelector('.price-filter[data-max="150"]').classList.add('active');
|
||||||
|
|
||||||
|
applyFilters();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render hotel list
|
||||||
|
function renderHotelList() {
|
||||||
|
const listContainer = document.getElementById('hotels-list');
|
||||||
|
|
||||||
|
if (filteredHotels.length === 0) {
|
||||||
|
listContainer.innerHTML = `
|
||||||
|
<div class="empty-state">
|
||||||
|
<h3>No hotels found</h3>
|
||||||
|
<p>Try adjusting your filters to see more results.</p>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
listContainer.innerHTML = filteredHotels.map(hotel => `
|
||||||
|
<div class="hotel-card" data-hotel-name="${hotel.name}" onclick="centerMapOnHotel('${hotel.name}')">
|
||||||
|
<div class="hotel-header">
|
||||||
|
<h3 class="hotel-name">${hotel.name}</h3>
|
||||||
|
<div class="hotel-price">${hotel.price_range}</div>
|
||||||
|
</div>
|
||||||
|
<div class="hotel-rating">
|
||||||
|
<span class="stars">${generateStars(hotel.rating)}</span>
|
||||||
|
<span class="rating-value">${hotel.rating}</span>
|
||||||
|
</div>
|
||||||
|
<div class="hotel-address">${hotel.address}</div>
|
||||||
|
<div class="hotel-description">${hotel.description}</div>
|
||||||
|
<div class="hotel-amenities">
|
||||||
|
${hotel.amenities.map(amenity => `<span class="amenity-tag">${amenity}</span>`).join('')}
|
||||||
|
</div>
|
||||||
|
<div class="hotel-actions">
|
||||||
|
<div class="arrondissement-badge">${hotel.arrondissement} Arrondissement</div>
|
||||||
|
<a href="${hotel.website}" target="_blank" class="visit-website">
|
||||||
|
Visit Website <i class="fas fa-external-link-alt"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`).join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Center map on selected hotel
|
||||||
|
function centerMapOnHotel(hotelName) {
|
||||||
|
const hotel = hotelData.find(h => h.name === hotelName);
|
||||||
|
if (hotel) {
|
||||||
|
// Switch to map view
|
||||||
|
switchView('map');
|
||||||
|
|
||||||
|
// Center map on hotel
|
||||||
|
map.setView([hotel.coordinates[0], hotel.coordinates[1]], 15);
|
||||||
|
|
||||||
|
// Find and open the marker popup
|
||||||
|
const marker = markers.find(m => m.hotelData && m.hotelData.name === hotelName);
|
||||||
|
if (marker) {
|
||||||
|
marker.openPopup();
|
||||||
|
|
||||||
|
// Highlight the marker temporarily
|
||||||
|
const markerElement = marker.getElement();
|
||||||
|
if (markerElement) {
|
||||||
|
const pin = markerElement.querySelector('.marker-pin');
|
||||||
|
if (pin) {
|
||||||
|
pin.style.backgroundColor = '#A84B2F'; // Warning color
|
||||||
|
setTimeout(() => {
|
||||||
|
pin.style.backgroundColor = '#21808D'; // Back to primary
|
||||||
|
}, 2000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate star rating HTML
|
||||||
|
function generateStars(rating) {
|
||||||
|
const fullStars = Math.floor(rating);
|
||||||
|
const hasHalfStar = rating % 1 >= 0.5;
|
||||||
|
let stars = '';
|
||||||
|
|
||||||
|
for (let i = 0; i < fullStars; i++) {
|
||||||
|
stars += '<i class="fas fa-star"></i>';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasHalfStar) {
|
||||||
|
stars += '<i class="fas fa-star-half-alt"></i>';
|
||||||
|
}
|
||||||
|
|
||||||
|
const emptyStars = 5 - fullStars - (hasHalfStar ? 1 : 0);
|
||||||
|
for (let i = 0; i < emptyStars; i++) {
|
||||||
|
stars += '<i class="far fa-star"></i>';
|
||||||
|
}
|
||||||
|
|
||||||
|
return stars;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize with focus on budget hotels (under €150)
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
// Small delay to ensure everything is loaded
|
||||||
|
setTimeout(() => {
|
||||||
|
// Set default filter to show budget hotels under €150
|
||||||
|
document.querySelectorAll('.price-filter').forEach(btn => btn.classList.remove('active'));
|
||||||
|
document.querySelector('.price-filter[data-max="150"]').classList.add('active');
|
||||||
|
applyFilters();
|
||||||
|
}, 500);
|
||||||
|
});
|
||||||
126
index.html
Normal file
126
index.html
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Paris Hotels Under 300€ Per Night</title>
|
||||||
|
<link rel="stylesheet" href="style.css">
|
||||||
|
<!-- Leaflet CSS -->
|
||||||
|
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"
|
||||||
|
integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY="
|
||||||
|
crossorigin=""/>
|
||||||
|
<!-- Font Awesome for icons -->
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header class="app-header">
|
||||||
|
<div class="container flex items-center justify-between">
|
||||||
|
<h1>Paris Hotels Under 300€ Per Night</h1>
|
||||||
|
<div class="view-toggle">
|
||||||
|
<button id="map-view-btn" class="btn btn--primary active" aria-label="Switch to map view">
|
||||||
|
<i class="fas fa-map-marked-alt"></i> Map View
|
||||||
|
</button>
|
||||||
|
<button id="list-view-btn" class="btn btn--secondary" aria-label="Switch to list view">
|
||||||
|
<i class="fas fa-list"></i> List View
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main class="container">
|
||||||
|
<div class="app-layout">
|
||||||
|
<!-- Sidebar with filters -->
|
||||||
|
<aside class="sidebar">
|
||||||
|
<div class="filter-section">
|
||||||
|
<h2>Filter Hotels</h2>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="search" class="form-label">Search by name</label>
|
||||||
|
<input type="text" id="search" class="form-control" placeholder="Hotel name...">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">Price Range</label>
|
||||||
|
<div class="price-filters">
|
||||||
|
<button class="price-filter active" data-min="0" data-max="150">Under €150</button>
|
||||||
|
<button class="price-filter" data-min="150" data-max="200">€150-200</button>
|
||||||
|
<button class="price-filter" data-min="200" data-max="250">€200-250</button>
|
||||||
|
<button class="price-filter" data-min="250" data-max="300">€250-300</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="arrondissement" class="form-label">Arrondissement</label>
|
||||||
|
<select id="arrondissement" class="form-control">
|
||||||
|
<option value="all">All Arrondissements</option>
|
||||||
|
<!-- Will be populated by JavaScript -->
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="rating" class="form-label">Minimum Rating: <span id="rating-value">4.0</span></label>
|
||||||
|
<input type="range" id="rating" class="form-control" min="4.0" max="5.0" step="0.1" value="4.0">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="filter-stats">
|
||||||
|
<p>Showing <span id="hotel-count">0</span> hotels</p>
|
||||||
|
<button id="reset-filters" class="btn btn--outline btn--sm">Reset Filters</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="chart-section">
|
||||||
|
<h3>Hotel Distribution</h3>
|
||||||
|
<div class="chart-container">
|
||||||
|
<img src="https://pplx-res.cloudinary.com/image/upload/v1749992538/pplx_code_interpreter/210c0c6e_x63wmb.jpg"
|
||||||
|
alt="Distribution of Paris Hotels Under 300€ by Arrondissement" class="chart">
|
||||||
|
</div>
|
||||||
|
<h3>Price Range Distribution</h3>
|
||||||
|
<div class="chart-container">
|
||||||
|
<img src="https://pplx-res.cloudinary.com/image/upload/v1749992575/pplx_code_interpreter/50b2ec80_uupyt8.jpg"
|
||||||
|
alt="Price Range Distribution of Paris Hotels Under 300€" class="chart">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
<!-- Main content area -->
|
||||||
|
<div class="content">
|
||||||
|
<!-- Map view -->
|
||||||
|
<div id="map-view" class="view active">
|
||||||
|
<div id="map"></div>
|
||||||
|
<div class="map-legend">
|
||||||
|
<h4>Map Legend</h4>
|
||||||
|
<div class="legend-item">
|
||||||
|
<div class="marker blue-marker"></div>
|
||||||
|
<span>Hotel</span>
|
||||||
|
</div>
|
||||||
|
<div class="legend-item">
|
||||||
|
<div class="marker highlighted-marker"></div>
|
||||||
|
<span>Selected Hotel</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- List view -->
|
||||||
|
<div id="list-view" class="view">
|
||||||
|
<div id="hotels-list">
|
||||||
|
<!-- Will be populated by JavaScript -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
<div class="container">
|
||||||
|
<p>© 2025 Paris Hotels Explorer</p>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
<!-- Leaflet JS -->
|
||||||
|
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"
|
||||||
|
integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo="
|
||||||
|
crossorigin=""></script>
|
||||||
|
<!-- Application JS -->
|
||||||
|
<script src="app.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
BIN
paris_hotels_chart.png
Normal file
BIN
paris_hotels_chart.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 166 KiB |
150
paris_hotels_map.html
Normal file
150
paris_hotels_map.html
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Paris Hotels Under 300€</title>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css" />
|
||||||
|
<style>
|
||||||
|
#map { height: 600px; width: 100%; }
|
||||||
|
body { font-family: Arial, sans-serif; margin: 0; padding: 20px; }
|
||||||
|
h1, h2 { color: #333; }
|
||||||
|
.hotel-list { margin-top: 20px; }
|
||||||
|
.hotel-item { margin-bottom: 15px; padding: 10px; border: 1px solid #ddd; border-radius: 5px; }
|
||||||
|
.hotel-name { font-weight: bold; font-size: 18px; }
|
||||||
|
.hotel-rating { color: #f39c12; }
|
||||||
|
.hotel-price { color: #27ae60; }
|
||||||
|
.hotel-address { color: #7f8c8d; }
|
||||||
|
.hotel-description { margin-top: 8px; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Paris Hotels Under 300€ Per Night</h1>
|
||||||
|
<div id="map"></div>
|
||||||
|
<script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"></script>
|
||||||
|
<script>
|
||||||
|
// Map initialization
|
||||||
|
var map = L.map('map').setView([48.8566, 2.3522], 12);
|
||||||
|
|
||||||
|
// Add OpenStreetMap tile layer
|
||||||
|
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||||
|
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
||||||
|
}).addTo(map);
|
||||||
|
|
||||||
|
// Hotel data
|
||||||
|
var hotels = [{"name": "Hotel Britannique", "coords": [48.8567, 2.3471], "price": "\u20ac200-320", "rating": 4.6, "arrondissement": "1st"}, {"name": "H\u00f4tel Signature Saint Germain des Pr\u00e9s", "coords": [48.8485, 2.3244], "price": "\u20ac220-290", "rating": 4.8, "arrondissement": "7th"}, {"name": "Paris France Hotel", "coords": [48.8644, 2.3614], "price": "\u20ac180-280", "rating": 4.7, "arrondissement": "3rd"}, {"name": "H\u00f4tel Lenox Montparnasse", "coords": [48.8427, 2.3269], "price": "\u20ac170-260", "rating": 4.5, "arrondissement": "14th"}, {"name": "H\u00f4tel Caron de Beaumarchais", "coords": [48.8572, 2.3598], "price": "\u20ac200-280", "rating": 4.6, "arrondissement": "4th"}, {"name": "H\u00f4tel Bourg Tibourg", "coords": [48.8547, 2.3581], "price": "\u20ac250-300", "rating": 4.7, "arrondissement": "4th"}, {"name": "H\u00f4tel de l'Abbaye Saint-Germain", "coords": [48.8495, 2.3309], "price": "\u20ac280-300", "rating": 4.6, "arrondissement": "6th"}, {"name": "Hotel Quartier Latin", "coords": [48.849, 2.3468], "price": "\u20ac160-250", "rating": 4.3, "arrondissement": "5th"}, {"name": "CitizenM Paris Gare de Lyon", "coords": [48.8448, 2.3732], "price": "\u20ac150-220", "rating": 4.4, "arrondissement": "12th"}, {"name": "H\u00f4tel Migny Op\u00e9ra-Montmartre", "coords": [48.881, 2.3375], "price": "\u20ac140-200", "rating": 4.1, "arrondissement": "9th"}, {"name": "H\u00f4tel Saint-Andr\u00e9 des Arts", "coords": [48.854, 2.3401], "price": "\u20ac180-280", "rating": 4.8, "arrondissement": "6th"}, {"name": "Grand H\u00f4tel des Gobelins", "coords": [48.8375, 2.3556], "price": "\u20ac130-180", "rating": 4.1, "arrondissement": "13th"}, {"name": "Hotel Marignan", "coords": [48.8504, 2.3445], "price": "\u20ac120-180", "rating": 4.1, "arrondissement": "5th"}, {"name": "H\u00f4tel La Comtesse Tour Eiffel", "coords": [48.8548, 2.3078], "price": "\u20ac250-300", "rating": 4.6, "arrondissement": "7th"}, {"name": "Boutique H\u00f4tel Mareuil Paris", "coords": [48.864, 2.3677], "price": "\u20ac200-280", "rating": 4.7, "arrondissement": "11th"}, {"name": "Generator Paris", "coords": [48.8772, 2.3711], "price": "\u20ac90-150", "rating": 4.1, "arrondissement": "10th"}, {"name": "H\u00f4tel Henriette", "coords": [48.8363, 2.3518], "price": "\u20ac130-180", "rating": 4.5, "arrondissement": "13th"}, {"name": "Hotel Paradis", "coords": [48.8718, 2.3502], "price": "\u20ac120-180", "rating": 4.3, "arrondissement": "10th"}, {"name": "COQ Hotel Paris", "coords": [48.8261, 2.3542], "price": "\u20ac140-190", "rating": 4.4, "arrondissement": "13th"}, {"name": "Hotel Diana", "coords": [48.8485, 2.3456], "price": "\u20ac150-200", "rating": 4.2, "arrondissement": "5th"}];
|
||||||
|
|
||||||
|
// Add markers for each hotel
|
||||||
|
hotels.forEach(function(hotel) {
|
||||||
|
var marker = L.marker(hotel.coords).addTo(map);
|
||||||
|
marker.bindPopup(
|
||||||
|
"<strong>" + hotel.name + "</strong><br>" +
|
||||||
|
"Rating: " + hotel.rating + "/5.0<br>" +
|
||||||
|
"Price: " + hotel.price + "<br>" +
|
||||||
|
"Arrondissement: " + hotel.arrondissement
|
||||||
|
);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<h2>Top 5 Hotels Under 300€</h2>
|
||||||
|
<div class="hotel-list">
|
||||||
|
|
||||||
|
<div class="hotel-item">
|
||||||
|
<div class="hotel-name">Hôtel Signature Saint Germain des Prés</div>
|
||||||
|
<div class="hotel-rating">Rating: 4.8/5.0</div>
|
||||||
|
<div class="hotel-price">Price Range: €220-290</div>
|
||||||
|
<div class="hotel-address">Address: 5 Rue Chomel, 75007 Paris (Arr. 7th)</div>
|
||||||
|
<div class="hotel-description">Chic boutique property in Saint-Germain with elegant, colorful rooms and vintage-chic lobby</div>
|
||||||
|
<div><a href="http://www.signature-saintgermain.com" target="_blank">Visit Website</a></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="hotel-item">
|
||||||
|
<div class="hotel-name">Hôtel Saint-André des Arts</div>
|
||||||
|
<div class="hotel-rating">Rating: 4.8/5.0</div>
|
||||||
|
<div class="hotel-price">Price Range: €180-280</div>
|
||||||
|
<div class="hotel-address">Address: 66 Rue Saint-André des Arts, 75006 Paris (Arr. 6th)</div>
|
||||||
|
<div class="hotel-description">Boutique hotel in Saint-Germain with unique rooms and bar</div>
|
||||||
|
<div><a href="https://www.saintandredesarts.com" target="_blank">Visit Website</a></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="hotel-item">
|
||||||
|
<div class="hotel-name">Paris France Hotel</div>
|
||||||
|
<div class="hotel-rating">Rating: 4.7/5.0</div>
|
||||||
|
<div class="hotel-price">Price Range: €180-280</div>
|
||||||
|
<div class="hotel-address">Address: 72 R. de Turbigo, 75003 Paris (Arr. 3rd)</div>
|
||||||
|
<div class="hotel-description">Traditional hotel with a cafe/bar and lounge in the heart of the Marais district</div>
|
||||||
|
<div><a href="https://www.paris-france-hotel.com" target="_blank">Visit Website</a></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="hotel-item">
|
||||||
|
<div class="hotel-name">Hôtel Bourg Tibourg</div>
|
||||||
|
<div class="hotel-rating">Rating: 4.7/5.0</div>
|
||||||
|
<div class="hotel-price">Price Range: €250-300</div>
|
||||||
|
<div class="hotel-address">Address: 19 Rue du Bourg Tibourg, 75004 Paris (Arr. 4th)</div>
|
||||||
|
<div class="hotel-description">Lavish boutique hotel with opulent rooms and sophisticated dramatic atmosphere</div>
|
||||||
|
<div><a href="https://www.bourgtibourg.com" target="_blank">Visit Website</a></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="hotel-item">
|
||||||
|
<div class="hotel-name">Boutique Hôtel Mareuil Paris</div>
|
||||||
|
<div class="hotel-rating">Rating: 4.7/5.0</div>
|
||||||
|
<div class="hotel-price">Price Range: €200-280</div>
|
||||||
|
<div class="hotel-address">Address: 51 Rue de Malte, 75011 Paris (Arr. 11th)</div>
|
||||||
|
<div class="hotel-description">Hip property with modern rooms & suites, plus bar, hammam & exercise room</div>
|
||||||
|
<div><a href="https://www.hotelmareuil.com/fr/" target="_blank">Visit Website</a></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2>Budget-Friendly Options (Under 200€)</h2>
|
||||||
|
<div class="hotel-list">
|
||||||
|
|
||||||
|
<div class="hotel-item">
|
||||||
|
<div class="hotel-name">Generator Paris</div>
|
||||||
|
<div class="hotel-rating">Rating: 4.1/5.0</div>
|
||||||
|
<div class="hotel-price">Price Range: €90-150</div>
|
||||||
|
<div class="hotel-address">Address: 9-11 Pl. du Colonel Fabien, 75010 Paris (Arr. 10th)</div>
|
||||||
|
<div class="hotel-description">Trendy hostel with stylish rooms & streamlined dorms, rooftop terrace & vibrant eatery</div>
|
||||||
|
<div><a href="https://staygenerator.com/hostels/paris?lang=fr-FR" target="_blank">Visit Website</a></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="hotel-item">
|
||||||
|
<div class="hotel-name">Hotel Marignan</div>
|
||||||
|
<div class="hotel-rating">Rating: 4.1/5.0</div>
|
||||||
|
<div class="hotel-price">Price Range: €120-180</div>
|
||||||
|
<div class="hotel-address">Address: 13 Rue du Sommerard, 75005 Paris (Arr. 5th)</div>
|
||||||
|
<div class="hotel-description">Laid-back hotel with simple rooms, guest kitchen and library, plus free breakfast</div>
|
||||||
|
<div><a href="http://www.hotel-marignan.com" target="_blank">Visit Website</a></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="hotel-item">
|
||||||
|
<div class="hotel-name">Hotel Paradis</div>
|
||||||
|
<div class="hotel-rating">Rating: 4.3/5.0</div>
|
||||||
|
<div class="hotel-price">Price Range: €120-180</div>
|
||||||
|
<div class="hotel-address">Address: 41 Rue Des Petities Ecuries, 75010 Paris (Arr. 10th)</div>
|
||||||
|
<div class="hotel-description">Tastefully decorated rooms in a top-notch location near trendy hotspots</div>
|
||||||
|
<div><a href="https://hotelparadisparis.com" target="_blank">Visit Website</a></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="hotel-item">
|
||||||
|
<div class="hotel-name">Grand Hôtel des Gobelins</div>
|
||||||
|
<div class="hotel-rating">Rating: 4.1/5.0</div>
|
||||||
|
<div class="hotel-price">Price Range: €130-180</div>
|
||||||
|
<div class="hotel-address">Address: 57 Bd Saint-Marcel, 75013 Paris (Arr. 13th)</div>
|
||||||
|
<div class="hotel-description">Casual hotel with warmly furnished rooms and lobby bar</div>
|
||||||
|
<div><a href="http://www.grandhotelgobelins.com" target="_blank">Visit Website</a></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="hotel-item">
|
||||||
|
<div class="hotel-name">Hôtel Henriette</div>
|
||||||
|
<div class="hotel-rating">Rating: 4.5/5.0</div>
|
||||||
|
<div class="hotel-price">Price Range: €130-180</div>
|
||||||
|
<div class="hotel-address">Address: 9 rue des Gobelins, 75013 Paris (Arr. 13th)</div>
|
||||||
|
<div class="hotel-description">Stylish boutique hotel with subtly decorated rooms and courtyard breakfast area</div>
|
||||||
|
<div><a href="https://www.hotelhenriette.com" target="_blank">Visit Website</a></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
209
paris_hotels_map_data.json
Normal file
209
paris_hotels_map_data.json
Normal file
@ -0,0 +1,209 @@
|
|||||||
|
{
|
||||||
|
"center": [
|
||||||
|
48.8566,
|
||||||
|
2.3522
|
||||||
|
],
|
||||||
|
"zoom": 12,
|
||||||
|
"markers": [
|
||||||
|
{
|
||||||
|
"name": "Hotel Britannique",
|
||||||
|
"coords": [
|
||||||
|
48.8567,
|
||||||
|
2.3471
|
||||||
|
],
|
||||||
|
"price": "€200-320",
|
||||||
|
"rating": 4.6,
|
||||||
|
"arrondissement": "1st"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Hôtel Signature Saint Germain des Prés",
|
||||||
|
"coords": [
|
||||||
|
48.8485,
|
||||||
|
2.3244
|
||||||
|
],
|
||||||
|
"price": "€220-290",
|
||||||
|
"rating": 4.8,
|
||||||
|
"arrondissement": "7th"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Paris France Hotel",
|
||||||
|
"coords": [
|
||||||
|
48.8644,
|
||||||
|
2.3614
|
||||||
|
],
|
||||||
|
"price": "€180-280",
|
||||||
|
"rating": 4.7,
|
||||||
|
"arrondissement": "3rd"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Hôtel Lenox Montparnasse",
|
||||||
|
"coords": [
|
||||||
|
48.8427,
|
||||||
|
2.3269
|
||||||
|
],
|
||||||
|
"price": "€170-260",
|
||||||
|
"rating": 4.5,
|
||||||
|
"arrondissement": "14th"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Hôtel Caron de Beaumarchais",
|
||||||
|
"coords": [
|
||||||
|
48.8572,
|
||||||
|
2.3598
|
||||||
|
],
|
||||||
|
"price": "€200-280",
|
||||||
|
"rating": 4.6,
|
||||||
|
"arrondissement": "4th"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Hôtel Bourg Tibourg",
|
||||||
|
"coords": [
|
||||||
|
48.8547,
|
||||||
|
2.3581
|
||||||
|
],
|
||||||
|
"price": "€250-300",
|
||||||
|
"rating": 4.7,
|
||||||
|
"arrondissement": "4th"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Hôtel de l'Abbaye Saint-Germain",
|
||||||
|
"coords": [
|
||||||
|
48.8495,
|
||||||
|
2.3309
|
||||||
|
],
|
||||||
|
"price": "€280-300",
|
||||||
|
"rating": 4.6,
|
||||||
|
"arrondissement": "6th"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Hotel Quartier Latin",
|
||||||
|
"coords": [
|
||||||
|
48.849,
|
||||||
|
2.3468
|
||||||
|
],
|
||||||
|
"price": "€160-250",
|
||||||
|
"rating": 4.3,
|
||||||
|
"arrondissement": "5th"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "CitizenM Paris Gare de Lyon",
|
||||||
|
"coords": [
|
||||||
|
48.8448,
|
||||||
|
2.3732
|
||||||
|
],
|
||||||
|
"price": "€150-220",
|
||||||
|
"rating": 4.4,
|
||||||
|
"arrondissement": "12th"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Hôtel Migny Opéra-Montmartre",
|
||||||
|
"coords": [
|
||||||
|
48.881,
|
||||||
|
2.3375
|
||||||
|
],
|
||||||
|
"price": "€140-200",
|
||||||
|
"rating": 4.1,
|
||||||
|
"arrondissement": "9th"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Hôtel Saint-André des Arts",
|
||||||
|
"coords": [
|
||||||
|
48.854,
|
||||||
|
2.3401
|
||||||
|
],
|
||||||
|
"price": "€180-280",
|
||||||
|
"rating": 4.8,
|
||||||
|
"arrondissement": "6th"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Grand Hôtel des Gobelins",
|
||||||
|
"coords": [
|
||||||
|
48.8375,
|
||||||
|
2.3556
|
||||||
|
],
|
||||||
|
"price": "€130-180",
|
||||||
|
"rating": 4.1,
|
||||||
|
"arrondissement": "13th"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Hotel Marignan",
|
||||||
|
"coords": [
|
||||||
|
48.8504,
|
||||||
|
2.3445
|
||||||
|
],
|
||||||
|
"price": "€120-180",
|
||||||
|
"rating": 4.1,
|
||||||
|
"arrondissement": "5th"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Hôtel La Comtesse Tour Eiffel",
|
||||||
|
"coords": [
|
||||||
|
48.8548,
|
||||||
|
2.3078
|
||||||
|
],
|
||||||
|
"price": "€250-300",
|
||||||
|
"rating": 4.6,
|
||||||
|
"arrondissement": "7th"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Boutique Hôtel Mareuil Paris",
|
||||||
|
"coords": [
|
||||||
|
48.864,
|
||||||
|
2.3677
|
||||||
|
],
|
||||||
|
"price": "€200-280",
|
||||||
|
"rating": 4.7,
|
||||||
|
"arrondissement": "11th"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Generator Paris",
|
||||||
|
"coords": [
|
||||||
|
48.8772,
|
||||||
|
2.3711
|
||||||
|
],
|
||||||
|
"price": "€90-150",
|
||||||
|
"rating": 4.1,
|
||||||
|
"arrondissement": "10th"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Hôtel Henriette",
|
||||||
|
"coords": [
|
||||||
|
48.8363,
|
||||||
|
2.3518
|
||||||
|
],
|
||||||
|
"price": "€130-180",
|
||||||
|
"rating": 4.5,
|
||||||
|
"arrondissement": "13th"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Hotel Paradis",
|
||||||
|
"coords": [
|
||||||
|
48.8718,
|
||||||
|
2.3502
|
||||||
|
],
|
||||||
|
"price": "€120-180",
|
||||||
|
"rating": 4.3,
|
||||||
|
"arrondissement": "10th"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "COQ Hotel Paris",
|
||||||
|
"coords": [
|
||||||
|
48.8261,
|
||||||
|
2.3542
|
||||||
|
],
|
||||||
|
"price": "€140-190",
|
||||||
|
"rating": 4.4,
|
||||||
|
"arrondissement": "13th"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Hotel Diana",
|
||||||
|
"coords": [
|
||||||
|
48.8485,
|
||||||
|
2.3456
|
||||||
|
],
|
||||||
|
"price": "€150-200",
|
||||||
|
"rating": 4.2,
|
||||||
|
"arrondissement": "5th"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
BIN
paris_hotels_price_range.png
Normal file
BIN
paris_hotels_price_range.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 165 KiB |
21
paris_hotels_under_300.csv
Normal file
21
paris_hotels_under_300.csv
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
name,address,rating,price_range,arrondissement,latitude,longitude,description,website,amenities
|
||||||
|
Hotel Britannique,"20 Av. Victoria, 75001 Paris",4.6,€200-320,1st,48.8567,2.3471,Classic 19th-century hotel near Châtelet with theatrical red decor and excellent Metro access,https://hotel-britannique.fr,"Free WiFi, Minibar, Air Conditioning, Elevator"
|
||||||
|
Hôtel Signature Saint Germain des Prés,"5 Rue Chomel, 75007 Paris",4.8,€220-290,7th,48.8485,2.3244,"Chic boutique property in Saint-Germain with elegant, colorful rooms and vintage-chic lobby",http://www.signature-saintgermain.com,"Free WiFi, Air Conditioning, Buffet Breakfast, 24-hour Reception"
|
||||||
|
Paris France Hotel,"72 R. de Turbigo, 75003 Paris",4.7,€180-280,3rd,48.8644,2.3614,Traditional hotel with a cafe/bar and lounge in the heart of the Marais district,https://www.paris-france-hotel.com,"Free WiFi, Café/Bar, Lounge, Optional Breakfast"
|
||||||
|
Hôtel Lenox Montparnasse,"15 Rue Delambre, 75014 Paris",4.5,€170-260,14th,48.8427,2.3269,Classic rooms & suites with warmly decorated honesty bar near Montparnasse,http://www.paris-hotel-lenox.com,"Free WiFi, Honesty Bar, Classic Decor, Cozy Atmosphere"
|
||||||
|
Hôtel Caron de Beaumarchais,"12 Rue Vieille du Temple, 75004 Paris",4.6,€200-280,4th,48.8572,2.3598,18th-century lodging with antiques-filled rooms and private balconies in Le Marais,http://www.carondebeaumarchais.com,"Free WiFi, Private Balconies, Antique Furnishings, 18th Century Charm"
|
||||||
|
Hôtel Bourg Tibourg,"19 Rue du Bourg Tibourg, 75004 Paris",4.7,€250-300,4th,48.8547,2.3581,Lavish boutique hotel with opulent rooms and sophisticated dramatic atmosphere,https://www.bourgtibourg.com,"Free WiFi, Opulent Decor, Sophisticated Atmosphere, Dramatic Design"
|
||||||
|
Hôtel de l'Abbaye Saint-Germain,"10 Rue Cassette, 75006 Paris",4.6,€280-300,6th,48.8495,2.3309,"Refined hotel with plush lounges and garden, plus free WiFi & breakfast",https://www.hotelabbayeparis.com,"Free WiFi, Garden, Plush Lounges, Breakfast Included"
|
||||||
|
Hotel Quartier Latin,"9 Rue des Écoles, 75005 Paris",4.3,€160-250,5th,48.849,2.3468,"Traditional hotel with simple rooms, minibars and breakfast buffet option",https://hotelquartierlatin.com,"Free WiFi, Minibar, Breakfast Buffet, Traditional Decor"
|
||||||
|
CitizenM Paris Gare de Lyon,"8 Rue Van Gogh, 75012 Paris",4.4,€150-220,12th,48.8448,2.3732,"Trendy budget hotel with compact rooms, ambient lighting and 24-hour cafe/bar",https://www.citizenm.com/hotels/europe/paris/gare-de-lyon-hotel,"24-hour Café/Bar, Ambient Lighting, Compact Design, Modern Amenities"
|
||||||
|
Hôtel Migny Opéra-Montmartre,"13 Rue Victor Massé, 75009 Paris",4.1,€140-200,9th,48.881,2.3375,"Colorful rooms with satellite TV, casual breakfast area and lounge near Opéra",https://www.hotelmigny.com,"Free WiFi, Satellite TV, Breakfast Area, LGBTQ+ Friendly"
|
||||||
|
Hôtel Saint-André des Arts,"66 Rue Saint-André des Arts, 75006 Paris",4.8,€180-280,6th,48.854,2.3401,Boutique hotel in Saint-Germain with unique rooms and bar,https://www.saintandredesarts.com,"Bar, Unique Rooms, Saint-Germain Location, Boutique Style"
|
||||||
|
Grand Hôtel des Gobelins,"57 Bd Saint-Marcel, 75013 Paris",4.1,€130-180,13th,48.8375,2.3556,Casual hotel with warmly furnished rooms and lobby bar,http://www.grandhotelgobelins.com,"Free WiFi, Lobby Bar, Warm Furnishings, Relaxed Atmosphere"
|
||||||
|
Hotel Marignan,"13 Rue du Sommerard, 75005 Paris",4.1,€120-180,5th,48.8504,2.3445,"Laid-back hotel with simple rooms, guest kitchen and library, plus free breakfast",http://www.hotel-marignan.com,"Free Breakfast, Free WiFi, Guest Kitchen, Library"
|
||||||
|
Hôtel La Comtesse Tour Eiffel,"29 Av. de Tourville, 75007 Paris",4.6,€250-300,7th,48.8548,2.3078,Traditional hotel with classic rooms and Eiffel Tower views,http://www.comtesse-hotel.com,"Free WiFi, Eiffel Tower Views, Bar, Traditional Decor"
|
||||||
|
Boutique Hôtel Mareuil Paris,"51 Rue de Malte, 75011 Paris",4.7,€200-280,11th,48.864,2.3677,"Hip property with modern rooms & suites, plus bar, hammam & exercise room",https://www.hotelmareuil.com/fr/,"Bar, Hammam, Exercise Room, Modern Design"
|
||||||
|
Generator Paris,"9-11 Pl. du Colonel Fabien, 75010 Paris",4.1,€90-150,10th,48.8772,2.3711,"Trendy hostel with stylish rooms & streamlined dorms, rooftop terrace & vibrant eatery",https://staygenerator.com/hostels/paris?lang=fr-FR,"Rooftop Terrace, Restaurant, Bar, Budget-Friendly"
|
||||||
|
Hôtel Henriette,"9 rue des Gobelins, 75013 Paris",4.5,€130-180,13th,48.8363,2.3518,Stylish boutique hotel with subtly decorated rooms and courtyard breakfast area,https://www.hotelhenriette.com,"Free WiFi, Courtyard, Breakfast, Design Interiors"
|
||||||
|
Hotel Paradis,"41 Rue Des Petities Ecuries, 75010 Paris",4.3,€120-180,10th,48.8718,2.3502,Tastefully decorated rooms in a top-notch location near trendy hotspots,https://hotelparadisparis.com,"Free WiFi, Designer Decor, Trendy Area, Modern Amenities"
|
||||||
|
COQ Hotel Paris,"15 Rue Edouard Manet, 75013 Paris",4.4,€140-190,13th,48.8261,2.3542,"Stylish design hotel with cozy bar, extensive breakfast and warm atmosphere",https://coq-hotel-paris.com,"Bar, Breakfast, Design Interiors, Cozy Atmosphere"
|
||||||
|
Hotel Diana,"73 Rue Saint-Jacques, 75005 Paris",4.2,€150-200,5th,48.8485,2.3456,Simple but comfortable 2-star hotel with bright rooms and modern bathrooms,http://www.hoteldiana.fr,"Free WiFi, Modern Bathrooms, Central Location, Budget-Friendly"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user