155 lines
7.0 KiB
PHP
Executable File
155 lines
7.0 KiB
PHP
Executable File
<?php
|
|
// ضبط الهيدر لإرجاع JSON بترميز UTF-8
|
|
header('Content-Type: application/json; charset=utf-8');
|
|
|
|
// --- إعدادات الاتصال بقاعدة البيانات ---
|
|
$servername = "localhost";
|
|
$username = "routeuser"; // <== عدّل
|
|
$password = "VETA9mX4tSZzm6AGouIM"; // <== عدّل
|
|
$dbname = "routedb"; // <== عدّل
|
|
|
|
// --- استقبال الإحداثيات من الطلب ---
|
|
$input_lat = isset($_GET['lat']) ? (float)$_GET['lat'] : null;
|
|
$input_lon = isset($_GET['lon']) ? (float)$_GET['lon'] : null;
|
|
|
|
// التحقق من وجود الإحداثيات
|
|
if ($input_lat === null || $input_lon === null) {
|
|
echo json_encode(['status' => 'error', 'message' => 'Missing lat or lon parameters']);
|
|
exit;
|
|
}
|
|
|
|
// --- الاتصال بقاعدة البيانات ---
|
|
$conn = new mysqli($servername, $username, $password, $dbname);
|
|
$conn->set_charset("utf8mb4");
|
|
if ($conn->connect_error) {
|
|
echo json_encode(['status' => 'error', 'message' => 'Database connection failed: ' . $conn->connect_error]);
|
|
exit;
|
|
}
|
|
|
|
// --- دالة لتحليل other_tags (نفس الدالة من السكربت السابق) ---
|
|
function parseHstoreValue($hstoreString, $keyToFind) {
|
|
if (empty($hstoreString) || empty($keyToFind)) return null;
|
|
if (preg_match('/"' . preg_quote($keyToFind, '/') . '"\s*=>\s*"([^"]+)"/', $hstoreString, $matches)) {
|
|
$value = $matches[1];
|
|
$decodedValue = urldecode($value);
|
|
$decodedValue = urldecode($decodedValue);
|
|
$cleanedValue = iconv('UTF-8', 'UTF-8//IGNORE', $decodedValue);
|
|
return ($cleanedValue === false || trim($cleanedValue) === '') ? null : $cleanedValue;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
|
|
// --- الاستعلام الرئيسي: البحث عن أقرب نقطة باستخدام الفهرس المكاني ---
|
|
// نختار الأعمدة الأساسية + أعمدة المناطق المحسوبة مسبقاً + other_tags
|
|
$sql = "
|
|
SELECT
|
|
p.name,
|
|
p.neighbourhood_name,
|
|
p.city_name,
|
|
p.other_tags,
|
|
ST_Distance_Sphere(
|
|
p.geom,
|
|
ST_PointFromText(CONCAT('POINT(', ?, ' ', ?, ')'), 4326)
|
|
) AS distance_meters
|
|
FROM
|
|
osm_points_with_area p
|
|
WHERE
|
|
-- استخدام MBRContains للفلترة الأولية السريعة (باستخدام الفهرس المكاني)
|
|
MBRContains(
|
|
ST_Buffer(ST_PointFromText(CONCAT('POINT(', ?, ' ', ?, ')'), 4326), 0.01), -- مربع بحث ~ 1 كم
|
|
p.geom
|
|
)
|
|
-- الترتيب الدقيق حسب المسافة الأقرب (يستخدم الفهرس المكاني بكفاءة)
|
|
ORDER BY
|
|
p.geom <-> ST_PointFromText(CONCAT('POINT(', ?, ' ', ?, ')'), 4326)
|
|
LIMIT 1"; // نريد أقرب نقطة فقط
|
|
|
|
$stmt = $conn->prepare($sql);
|
|
if ($stmt === false) {
|
|
echo json_encode(['status' => 'error', 'message' => 'Failed to prepare statement: ' . $conn->error]);
|
|
$conn->close();
|
|
exit;
|
|
}
|
|
|
|
// ربط المتغيرات (6 متغيرات: lon, lat مرتين للمربع ومرة للمسافة)
|
|
$stmt->bind_param("dddddd", $input_lon, $input_lat, $input_lon, $input_lat, $input_lon, $input_lat);
|
|
|
|
$stmt->execute();
|
|
$result = $stmt->get_result();
|
|
|
|
// --- تنسيق وإرجاع النتيجة ---
|
|
if ($result->num_rows > 0) {
|
|
$row = $result->fetch_assoc();
|
|
|
|
// استخراج التفاصيل الإضافية من other_tags
|
|
$name_ar = parseHstoreValue($row['other_tags'], 'name:ar');
|
|
$addr_street = parseHstoreValue($row['other_tags'], 'addr:street');
|
|
$amenity = parseHstoreValue($row['other_tags'], 'amenity');
|
|
$shop = parseHstoreValue($row['other_tags'], 'shop');
|
|
|
|
// بناء اسم وصفي (الأولوية للعربي إن وجد)
|
|
$primaryName = $name_ar ?? $row['name'] ?? $addr_street ?? null; // الاسم الأساسي للنقطة
|
|
$displayName = $primaryName ?? 'موقع قريب'; // اسم افتراضي إذا لم يوجد اسم
|
|
|
|
// إضافة اسم الحي والمدينة (المحسوبة مسبقاً)
|
|
$addressParts = array_filter([
|
|
$row['neighbourhood_name'],
|
|
$row['city_name']
|
|
]);
|
|
if (!empty($addressParts)) {
|
|
// تجنب تكرار اسم المدينة إذا كان هو نفسه اسم النقطة
|
|
if ($primaryName !== $row['city_name']) {
|
|
$displayName .= '، ' . implode('، ', $addressParts);
|
|
} elseif ($row['neighbourhood_name'] && $primaryName !== $row['neighbourhood_name']) {
|
|
$displayName .= '، ' . $row['neighbourhood_name'];
|
|
}
|
|
}
|
|
|
|
// إرجاع النتيجة كـ JSON
|
|
echo json_encode([
|
|
'status' => 'ok',
|
|
'display_name' => $displayName, // الاسم المنسق للعرض
|
|
'name' => $row['name'], // الاسم الأصلي (إن وجد)
|
|
'name_ar' => $name_ar, // الاسم العربي (إن وجد)
|
|
'street' => $addr_street, // اسم الشارع (إن وجد)
|
|
'neighbourhood' => $row['neighbourhood_name'], // اسم الحي (المحسوب مسبقاً)
|
|
'city' => $row['city_name'], // اسم المدينة (المحسوب مسبقاً)
|
|
'amenity' => $amenity, // نوع الخدمة (إن وجد)
|
|
'shop' => $shop, // نوع المحل (إن وجد)
|
|
'distance_meters' => round($row['distance_meters'], 1) // المسافة لأقرب POI
|
|
], JSON_UNESCAPED_UNICODE); // مهم لعرض العربية بشكل صحيح
|
|
|
|
} else {
|
|
// لم يتم العثور على نقطة قريبة، ابحث عن أقرب مدينة/حي كحل بديل
|
|
$areaSqlFallback = "
|
|
SELECT name, other_tags, place_type
|
|
FROM osm_areas
|
|
ORDER BY ST_Distance_Sphere(geom, ST_PointFromText(CONCAT('POINT(', ?, ' ', ?, ')'), 4326)) ASC
|
|
LIMIT 1";
|
|
$stmtFallback = $conn->prepare($areaSqlFallback);
|
|
if ($stmtFallback) {
|
|
$stmtFallback->bind_param("dd", $input_lon, $input_lat);
|
|
$stmtFallback->execute();
|
|
$fallbackResult = $stmtFallback->get_result()->fetch_assoc();
|
|
$stmtFallback->close();
|
|
|
|
if ($fallbackResult) {
|
|
$fallbackNameAr = parseHstoreValue($fallbackResult['other_tags'], 'name:ar');
|
|
$fallbackDisplayName = $fallbackNameAr ?? $fallbackResult['name'] ?? 'منطقة غير معروفة';
|
|
echo json_encode([
|
|
'status' => 'ok',
|
|
'display_name' => $fallbackDisplayName,
|
|
($fallbackResult['place_type'] === 'city' || $fallbackResult['place_type'] === 'town' || $fallbackResult['place_type'] === 'village') ? 'city' : 'neighbourhood' => $fallbackDisplayName
|
|
], JSON_UNESCAPED_UNICODE);
|
|
} else {
|
|
echo json_encode(['status' => 'not_found', 'message' => 'No nearby places or areas found']);
|
|
}
|
|
} else {
|
|
echo json_encode(['status' => 'error', 'message' => 'Fallback query failed: ' . $conn->error]);
|
|
}
|
|
}
|
|
|
|
$stmt->close();
|
|
$conn->close();
|
|
?>
|