Files
Siro/loction_server/siro/ride/heatmap.html
2026-06-29 23:09:43 +03:00

160 lines
7.3 KiB
HTML
Executable File

<!DOCTYPE html>
<html lang="ar" dir="rtl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>خريطة الكثافة الحرارية (Grid Heatmap) - OpenStreetMap</title>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<style>
body { margin: 0; padding: 0; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; }
#map { height: 100vh; width: 100%; }
.info-box {
position: absolute;
top: 10px;
right: 10px;
z-index: 1000;
background: white;
padding: 15px;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0,0,0,0.2);
max-width: 300px;
}
.legend {
margin-top: 10px;
line-height: 1.5;
}
.legend i {
width: 18px;
height: 18px;
float: right;
margin-left: 8px;
opacity: 0.7;
}
</style>
</head>
<body>
<div class="info-box">
<h3>تحليل كثافة الرحلات</h3>
<p>هذه الخريطة تقسم المنطقة إلى مربعات جغرافية وتحسب عدد الرحلات في كل مربع.</p>
<div class="legend">
<div><i style="background: #bd0026"></i> طلبات عالية جداً (+5)</div>
<div><i style="background: #f03b20"></i> طلبات عالية (3-4)</div>
<div><i style="background: #fd8d3c"></i> طلبات متوسطة (2)</div>
<div><i style="background: #feb24c"></i> طلب واحد (1)</div>
</div>
</div>
<div id="map"></div>
<script>
// 1. تهيئة الخريطة (مركزها دمشق مبدئياً)
var map = L.map('map').setView([33.513, 36.276], 13);
// 2. إضافة طبقة OpenStreetMap
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}).addTo(map);
// 3. البيانات المستخرجة من ملفك (ride.json)
// ملاحظة: قمت بوضع البيانات هنا لمحاكاتها، في الواقع سيقوم السكربت بطلبها من ملف heatmap.json
var rawData = [
{"type":"header","version":"5.2.2","comment":"Export to JSON plugin for PHPMyAdmin"},
{"type":"database","name":"intaleq-ridesDB"},
{"type":"table","name":"ride","database":"intaleq-ridesDB","data": [
// ... تم نسخ عينة من بياناتك هنا لتعمل الصفحة ...
{"start_location":"33.4323,36.2432"}, {"start_location":"34.68947,36.36329"},
{"start_location":"33.5445,36.30571"}, {"start_location":"33.4323,36.24326"},
{"start_location":"33.43222,36.24319"}, {"start_location":"33.51326,36.27646"},
{"start_location":"33.50895,36.29209"}, {"start_location":"33.49631,36.3221"},
{"start_location":"33.55463,36.32338"}, {"start_location":"33.53447,36.29727"},
{"start_location":"33.5727,36.192"}, {"start_location":"33.52811,36.37998"},
{"start_location":"33.54115,36.21846"}, {"start_location":"33.4323,36.24309"},
{"start_location":"33.50312,36.25959"}, {"start_location":"33.52518,36.35682"},
{"start_location":"33.51814,36.3119"}, {"start_location":"33.50142,36.27113"},
{"start_location":"33.49593,36.30942"}, {"start_location":"33.55189,36.32245"},
{"start_location":"33.5322,36.29513"}, {"start_location":"33.53994,36.22878"},
{"start_location":"33.52441,36.28758"}, {"start_location":"33.46744,36.19679"},
{"start_location":"33.52842,36.23082"}, {"start_location":"33.50236,36.27406"},
{"start_location":"33.4323,36.24331"}, {"start_location":"33.51246,36.29807"}
// (ملاحظة: البيانات هنا هي عينة لتعمل الصفحة، يمكنك استبدالها ببياناتك الكاملة)
]}
];
// في حال أردت استخدام بياناتك الكاملة، الصق محتوى المصفوفة "data" من ملفك داخل المتغير أدناه
// سأقوم الآن باستخراج البيانات من الهيكل المعقد الذي أرسلته
var rides = rawData[2].data;
// 4. خوارزمية الشبكة (Grid Algorithm)
var grid = {};
var precision = 0.005; // حجم المربع (تقريباً 500 متر). صغّر الرقم لـ 0.002 لدقة أعلى
rides.forEach(function(ride) {
if(ride.start_location) {
var coords = ride.start_location.split(',');
var lat = parseFloat(coords[0]);
var lng = parseFloat(coords[1]);
// استثناء القيم الصفرية أو البعيدة جداً
if(lat > 32 && lat < 38 && lng > 35 && lng < 39) {
// حساب مفتاح الشبكة (تقريب الإحداثيات)
// Math.floor(lat / precision) * precision -> يقوم بتوحيد الأرقام القريبة
var gridLat = Math.floor(lat / precision) * precision;
var gridLng = Math.floor(lng / precision) * precision;
var key = gridLat.toFixed(3) + "_" + gridLng.toFixed(3);
if(!grid[key]) {
grid[key] = {
lat: gridLat,
lng: gridLng,
count: 0
};
}
grid[key].count++;
}
}
});
// 5. دالة تحديد اللون بناءً على العدد
function getColor(d) {
return d > 5 ? '#bd0026' : // أحمر داكن (حار جداً)
d > 3 ? '#f03b20' : // أحمر
d > 1 ? '#fd8d3c' : // برتقالي
'#feb24c'; // أصفر (بارد)
}
// 6. رسم المربعات على الخريطة
var bounds = []; // لتحديد حدود الخريطة النهائية
for (var key in grid) {
var zone = grid[key];
// تحديد زوايا المربع
var southWest = [zone.lat, zone.lng];
var northEast = [zone.lat + precision, zone.lng + precision];
var zoneBounds = [southWest, northEast];
// رسم المستطيل
L.rectangle(zoneBounds, {
color: getColor(zone.count),
weight: 1,
fillOpacity: 0.6
}).bindPopup("<b>عدد الرحلات:</b> " + zone.count)
.addTo(map);
bounds.push(southWest);
bounds.push(northEast);
}
// 7. تحريك الكاميرا لتشمل كل النقاط
if(bounds.length > 0) {
map.fitBounds(bounds);
}
</script>
</body>
</html>