Spaces:
Running
Running
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>Facilities & Trash Bins Heatmap</title> | |
<script src="https://cdn.tailwindcss.com"></script> | |
<script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyBybAUlzNzGZG6-eqpjCnLJWOBfNPsmNaU&libraries=visualization"></script> | |
<style> | |
.custom-radio input:checked + label { | |
background-color: #3b82f6; | |
color: white; | |
} | |
.heatmap-gradient { | |
background: linear-gradient(to right, | |
rgba(0, 0, 255, 0.5), | |
rgba(0, 255, 0, 0.5), | |
rgba(255, 255, 0, 0.5), | |
rgba(255, 165, 0, 0.5), | |
rgba(255, 0, 0, 0.5)); | |
} | |
.map-container { | |
height: 70vh; | |
} | |
@media (max-width: 640px) { | |
.map-container { | |
height: 50vh; | |
} | |
} | |
</style> | |
</head> | |
<body class="bg-gray-100"> | |
<div class="container mx-auto px-4 py-8"> | |
<header class="mb-8 text-center"> | |
<h1 class="text-3xl md:text-4xl font-bold text-blue-800 mb-2">Facilities & Trash Bins Heatmap</h1> | |
<p class="text-gray-600 max-w-2xl mx-auto"> | |
Visualize the density of public facilities and trash bins in your area. | |
The heatmap shows concentrations with warmer colors indicating higher density. | |
</p> | |
</header> | |
<div class="bg-white rounded-xl shadow-lg overflow-hidden mb-8"> | |
<div class="p-4 md:p-6 flex flex-col md:flex-row justify-between items-center gap-4"> | |
<div class="flex-1"> | |
<h2 class="text-xl font-semibold text-gray-800 mb-2">Map Controls</h2> | |
<div class="flex flex-wrap gap-4"> | |
<div class="flex items-center space-x-2"> | |
<div class="custom-radio"> | |
<input type="radio" id="show-all" name="map-type" checked class="hidden"> | |
<label for="show-all" class="px-3 py-1 rounded-full border border-blue-500 text-blue-500 cursor-pointer transition-colors"> | |
Show All | |
</label> | |
</div> | |
<div class="custom-radio"> | |
<input type="radio" id="show-facilities" name="map-type" class="hidden"> | |
<label for="show-facilities" class="px-3 py-1 rounded-full border border-green-500 text-green-500 cursor-pointer transition-colors"> | |
Facilities Only | |
</label> | |
</div> | |
<div class="custom-radio"> | |
<input type="radio" id="show-trash" name="map-type" class="hidden"> | |
<label for="show-trash" class="px-3 py-1 rounded-full border border-red-500 text-red-500 cursor-pointer transition-colors"> | |
Trash Bins Only | |
</label> | |
</div> | |
</div> | |
</div> | |
</div> | |
<div class="flex items-center gap-2"> | |
<div class="h-4 w-4 rounded-full bg-blue-500"></div> | |
<span class="text-sm text-gray-600">Facilities</span> | |
<div class="h-4 w-4 rounded-full bg-red-500 ml-2"></div> | |
<span class="text-sm text-gray-600">Trash Bins</span> | |
</div> | |
</div> | |
<div class="relative"> | |
<div id="map" class="map-container w-full"></div> | |
<div class="absolute bottom-4 left-4 bg-white p-2 rounded-lg shadow-md"> | |
<div class="flex items-center mb-1"> | |
<span class="text-xs mr-2">Low</span> | |
<div class="heatmap-gradient w-32 h-4 rounded"></div> | |
<span class="text-xs ml-2">High</span> | |
</div> | |
<p class="text-xs text-gray-600">Heatmap intensity scale</p> | |
</div> | |
</div> | |
</div> | |
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-8"> | |
<div class="bg-white p-6 rounded-xl shadow-lg"> | |
<h3 class="text-xl font-semibold text-gray-800 mb-4">Facilities Density</h3> | |
<div class="h-64" id="facilities-chart"></div> | |
<p class="text-sm text-gray-600 mt-2"> | |
This chart shows the distribution of public facilities across different areas. | |
</p> | |
</div> | |
<div class="bg-white p-6 rounded-xl shadow-lg"> | |
<h3 class="text-xl font-semibold text-gray-800 mb-4">Trash Bins Density</h3> | |
<div class="h-64" id="trash-chart"></div> | |
<p class="text-sm text-gray-600 mt-2"> | |
This chart shows the concentration of trash bins in different zones. | |
</p> | |
</div> | |
</div> | |
<div class="bg-white rounded-xl shadow-lg overflow-hidden"> | |
<div class="p-6"> | |
<h3 class="text-xl font-semibold text-gray-800 mb-4">Data Summary</h3> | |
<div class="overflow-x-auto"> | |
<table class="min-w-full divide-y divide-gray-200"> | |
<thead class="bg-gray-50"> | |
<tr> | |
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Type</th> | |
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Count</th> | |
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Density</th> | |
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Area Coverage</th> | |
</tr> | |
</thead> | |
<tbody class="bg-white divide-y divide-gray-200"> | |
<tr> | |
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">Public Facilities</td> | |
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">248</td> | |
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">High</td> | |
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">85%</td> | |
</tr> | |
<tr> | |
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">Trash Bins</td> | |
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">176</td> | |
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">Medium</td> | |
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">72%</td> | |
</tr> | |
</tbody> | |
</table> | |
</div> | |
</div> | |
</div> | |
</div> | |
<script> | |
// Sample data - in a real app, this would come from an API | |
const facilitiesData = [ | |
{lat: 11.5664, lng: 104.9182}, // Central Market | |
{lat: 11.5464, lng: 104.9382}, // Russian Market | |
{lat: 11.5564, lng: 104.9282}, // Independence Monument | |
{lat: 11.5764, lng: 104.9182}, // Wat Phnom | |
{lat: 11.5364, lng: 104.9482}, // Chroy Changvar | |
{lat: 11.5664, lng: 104.9082}, // Riverside | |
{lat: 11.5464, lng: 104.9182}, // BKK1 | |
{lat: 11.5564, lng: 104.9382}, // Toul Tom Poung | |
{lat: 11.5864, lng: 104.9282}, // Phsar Thmey | |
{lat: 11.5264, lng: 104.9282}, // Chbar Ampov | |
{lat: 11.5664, lng: 104.9482}, // Boeung Keng Kang | |
{lat: 11.5464, lng: 104.9082}, // Daun Penh | |
{lat: 11.5764, lng: 104.9382}, // Olympic Stadium | |
{lat: 11.5364, lng: 104.9182}, // Koh Pich | |
{lat: 11.5564, lng: 104.9082}, // Sisowath Quay | |
{lat: 11.5664, lng: 104.9282}, // Central Business District | |
{lat: 11.5464, lng: 104.9282}, // Street 51 | |
{lat: 11.5564, lng: 104.9182}, // Royal Palace | |
{lat: 11.5364, lng: 104.9382}, // Tonle Bassac | |
{lat: 11.5764, lng: 104.9082} // Phnom Penh International Airport | |
]; | |
const trashData = [ | |
{lat: 11.5554, lng: 104.9272}, // Near Independence Monument | |
{lat: 11.5554, lng: 104.9272}, // Near Independence Monument | |
{lat: 11.5554, lng: 104.9272}, // Near Independence Monument | |
{lat: 11.5574, lng: 104.9292}, // Near Wat Langka | |
{lat: 11.5544, lng: 104.9252}, // Near Sihanouk Blvd | |
{lat: 11.5584, lng: 104.9262}, // Near Street 51 | |
{lat: 11.5534, lng: 104.9292}, // Near Street 63 | |
{lat: 11.5564, lng: 104.9312}, // Near Street 310 | |
{lat: 11.5594, lng: 104.9252}, // Near Street 154 | |
{lat: 11.5524, lng: 104.9282}, // Near Street 19 | |
{lat: 11.5554, lng: 104.9232}, // Near Riverside | |
{lat: 11.5574, lng: 104.9322}, // Near Russian Market | |
{lat: 11.5544, lng: 104.9302}, // Near Street 135 | |
{lat: 11.5564, lng: 104.9242}, // Near Street 13 | |
{lat: 11.5584, lng: 104.9282}, // Near Street 51 | |
{lat: 11.5534, lng: 104.9272}, // Near Street 21 | |
{lat: 11.5554, lng: 104.9252}, // Near Street 178 | |
{lat: 11.5574, lng: 104.9302} // Near Street 63 | |
]; | |
let map, facilitiesHeatmap, trashHeatmap; | |
let allHeatmapData = [...facilitiesData.map(p => new google.maps.LatLng(p.lat, p.lng)), | |
...trashData.map(p => new google.maps.LatLng(p.lat, p.lng))]; | |
function initMap() { | |
map = new google.maps.Map(document.getElementById("map"), { | |
zoom: 13, | |
center: { lat: 11.5564, lng: 104.9282 }, | |
mapTypeId: "roadmap", | |
styles: [ | |
{ | |
featureType: "poi", | |
elementType: "labels", | |
stylers: [{ visibility: "off" }] | |
} | |
] | |
}); | |
// Create heatmap layers | |
facilitiesHeatmap = new google.maps.visualization.HeatmapLayer({ | |
data: facilitiesData.map(p => new google.maps.LatLng(p.lat, p.lng)), | |
map: map, | |
radius: 30, | |
gradient: [ | |
'rgba(0, 0, 255, 0)', | |
'rgba(0, 0, 255, 1)', | |
'rgba(0, 255, 255, 1)', | |
'rgba(0, 255, 0, 1)', | |
'rgba(255, 255, 0, 1)', | |
'rgba(255, 165, 0, 1)', | |
'rgba(255, 0, 0, 1)' | |
] | |
}); | |
trashHeatmap = new google.maps.visualization.HeatmapLayer({ | |
data: trashData.map(p => new google.maps.LatLng(p.lat, p.lng)), | |
map: map, | |
radius: 25, | |
gradient: [ | |
'rgba(255, 0, 0, 0)', | |
'rgba(255, 0, 0, 0.5)', | |
'rgba(255, 69, 0, 0.8)', | |
'rgba(255, 140, 0, 1)', | |
'rgba(255, 165, 0, 1)' | |
] | |
}); | |
// Set up radio button controls | |
document.getElementById('show-all').addEventListener('change', function() { | |
facilitiesHeatmap.setMap(map); | |
trashHeatmap.setMap(map); | |
}); | |
document.getElementById('show-facilities').addEventListener('change', function() { | |
facilitiesHeatmap.setMap(map); | |
trashHeatmap.setMap(null); | |
}); | |
document.getElementById('show-trash').addEventListener('change', function() { | |
facilitiesHeatmap.setMap(null); | |
trashHeatmap.setMap(map); | |
}); | |
// Simple chart visualization | |
renderSimpleCharts(); | |
} | |
function renderSimpleCharts() { | |
// This is a simplified chart - in a real app you'd use a charting library | |
const facilitiesChart = document.getElementById('facilities-chart'); | |
const trashChart = document.getElementById('trash-chart'); | |
// Create simple bar charts using divs | |
facilitiesChart.innerHTML = ` | |
<div class="h-full flex items-end gap-1"> | |
<div class="flex-1 bg-blue-500 rounded-t" style="height: 70%"></div> | |
<div class="flex-1 bg-blue-500 rounded-t" style="height: 85%"></div> | |
<div class="flex-1 bg-blue-500 rounded-t" style="height: 60%"></div> | |
<div class="flex-1 bg-blue-500 rounded-t" style="height: 45%"></div> | |
<div class="flex-1 bg-blue-500 rounded-t" style="height: 75%"></div> | |
<div class="flex-1 bg-blue-500 rounded-t" style="height: 90%"></div> | |
<div class="flex-1 bg-blue-500 rounded-t" style="height: 65%"></div> | |
</div> | |
`; | |
trashChart.innerHTML = ` | |
<div class="h-full flex items-end gap-1"> | |
<div class="flex-1 bg-red-500 rounded-t" style="height: 50%"></div> | |
<div class="flex-1 bg-red-500 rounded-t" style="height: 75%"></div> | |
<div class="flex-1 bg-red-500 rounded-t" style="height: 40%"></div> | |
<div class="flex-1 bg-red-500 rounded-t" style="height: 85%"></div> | |
<div class="flex-1 bg-red-500 rounded-t" style="height: 60%"></div> | |
<div class="flex-1 bg-red-500 rounded-t" style="height: 45%"></div> | |
<div class="flex-1 bg-red-500 rounded-t" style="height: 70%"></div> | |
</div> | |
`; | |
} | |
// Initialize the map when the window loads | |
window.onload = initMap; | |
</script> | |
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=sombochea/heatmaps-composelink" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
</html> |