'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(); ?>