${props.title ?? ''}

${props.location ? `

${props.location}

` : ''} ${price ? `

${price}

` : ''} ${props.category ? `${props.category}` : ''} View Details →
`) .addTo(this.map); }); this.map.on('mouseenter', 'clusters', () => { this.map.getCanvas().style.cursor = 'pointer'; }); this.map.on('mouseleave', 'clusters', () => { this.map.getCanvas().style.cursor = ''; }); this.map.on('mouseenter', 'project-dots', () => { this.map.getCanvas().style.cursor = 'pointer'; }); this.map.on('mouseleave', 'project-dots', () => { this.map.getCanvas().style.cursor = ''; }); } if (features.length > 0) { const bounds = features.reduce( (box, feature) => box.extend(feature.geometry.coordinates), new mapboxgl.LngLatBounds(features[0].geometry.coordinates, features[0].geometry.coordinates) ); this.map.fitBounds(bounds, { padding: 50, maxZoom: 12 }); } }, toggleMapStyle(style) { this.mapStyle = style; this.map.setStyle('mapbox://styles/mapbox/' + style + '-v12'); }, toggle3D() { this.show3D = !this.show3D; if (this.show3D) { this.map.easeTo({ pitch: 45, duration: 1000 }); this.enable3DBuildings(); } else { this.map.easeTo({ pitch: 0, duration: 1000 }); if (this.map.getLayer('3d-buildings')) { this.map.removeLayer('3d-buildings'); } } }, async toggleLandmarks(type) { const propertyMap = { 'hospitals': 'showHospitals', 'schools': 'showSchools', 'shopping': 'showShopping', 'restaurants': 'showRestaurants' }; const property = propertyMap[type]; if (property) { this[property] = !this[property]; } const show = this[property]; const layerId = type + '-layer'; const sourceId = type + '-source'; if (show) { await this.loadLandmarks(type); } else { if (this.map.getLayer(layerId)) { this.map.removeLayer(layerId); } if (this.map.getSource(sourceId)) { this.map.removeSource(sourceId); } } }, async loadLandmarks(type) { const bounds = this.map.getBounds(); const bbox = [ bounds.getWest(), bounds.getSouth(), bounds.getEast(), bounds.getNorth() ].join(','); // Using Overpass API for OpenStreetMap POIs const queries = { hospitals: `[out:json][bbox:${bbox}];node["amenity"="hospital"];out;`, schools: `[out:json][bbox:${bbox}];node["amenity"~"^(school|university|college)$"];out;`, shopping: `[out:json][bbox:${bbox}];node["shop"];out;`, restaurants: `[out:json][bbox:${bbox}];node["amenity"~"^(restaurant|cafe|fast_food)$"];out;` }; try { const response = await fetch('https://overpass-api.de/api/interpreter', { method: 'POST', body: queries[type] }); const data = await response.json(); const features = data.elements.map(el => ({ type: 'Feature', geometry: { type: 'Point', coordinates: [el.lon, el.lat] }, properties: { name: el.tags?.name || type.charAt(0).toUpperCase() + type.slice(1), type: type } })); const sourceId = type + '-source'; const layerId = type + '-layer'; if (this.map.getSource(sourceId)) { this.map.getSource(sourceId).setData({ type: 'FeatureCollection', features: features }); } else { this.map.addSource(sourceId, { type: 'geojson', data: { type: 'FeatureCollection', features: features } }); const colors = { hospitals: '#ef4444', schools: '#3b82f6', shopping: '#10b981', restaurants: '#f59e0b' }; this.map.addLayer({ id: layerId, type: 'circle', source: sourceId, paint: { 'circle-radius': 6, 'circle-color': colors[type], 'circle-stroke-width': 2, 'circle-stroke-color': '#fff' } }); } } catch (error) { console.error('Failed to load landmarks:', error); } } }">