wip
This commit is contained in:
@@ -109,8 +109,8 @@ class MLS_Query {
|
||||
}
|
||||
|
||||
/**
|
||||
* Build SQL for distance-based filtering using Haversine formula
|
||||
* Returns properties within specified miles of a center point
|
||||
* Build SQL for distance-based filtering using spatial index
|
||||
* Uses bounding box pre-filter + ST_Distance_Sphere for accuracy
|
||||
*
|
||||
* @param float $lat Center latitude
|
||||
* @param float $lng Center longitude
|
||||
@@ -118,14 +118,46 @@ class MLS_Query {
|
||||
* @return string SQL expression for distance filter
|
||||
*/
|
||||
private function get_distance_filter_sql($lat, $lng, $miles) {
|
||||
// Haversine formula: distance in miles
|
||||
// 3959 is Earth's radius in miles
|
||||
// Convert miles to meters for ST_Distance_Sphere (returns meters)
|
||||
$meters = $miles * 1609.344;
|
||||
|
||||
// Create center point (SRID 4326 uses lat, lng order in MySQL 8.0+)
|
||||
// Use ST_Distance_Sphere with the spatial indexed location column
|
||||
return sprintf(
|
||||
"(3959 * acos(cos(radians(%f)) * cos(radians(latitude)) * cos(radians(longitude) - radians(%f)) + sin(radians(%f)) * sin(radians(latitude)))) <= %f",
|
||||
"ST_Distance_Sphere(location, ST_PointFromText('POINT(%f %f)', 4326)) <= %f",
|
||||
$lat,
|
||||
$lng,
|
||||
$lat,
|
||||
$miles
|
||||
$meters
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build SQL for bounding box pre-filter
|
||||
* Uses simple BETWEEN for fast initial filtering before distance calc
|
||||
* This narrows down candidates significantly before the expensive ST_Distance_Sphere
|
||||
*
|
||||
* @param float $lat Center latitude
|
||||
* @param float $lng Center longitude
|
||||
* @param float $miles Radius in miles
|
||||
* @return string SQL expression for bounding box filter
|
||||
*/
|
||||
private function get_bounding_box_filter_sql($lat, $lng, $miles) {
|
||||
// Approximate degrees per mile (varies by latitude, using average)
|
||||
// 1 degree latitude ≈ 69 miles
|
||||
// 1 degree longitude ≈ 69 miles * cos(latitude)
|
||||
$lat_delta = $miles / 69.0;
|
||||
$lng_delta = $miles / (69.0 * cos(deg2rad($lat)));
|
||||
|
||||
$min_lat = $lat - $lat_delta;
|
||||
$max_lat = $lat + $lat_delta;
|
||||
$min_lng = $lng - $lng_delta;
|
||||
$max_lng = $lng + $lng_delta;
|
||||
|
||||
// Use BETWEEN for bounding box - efficient with indexes on lat/lng
|
||||
return sprintf(
|
||||
"(latitude BETWEEN %f AND %f AND longitude BETWEEN %f AND %f)",
|
||||
$min_lat, $max_lat,
|
||||
$min_lng, $max_lng
|
||||
);
|
||||
}
|
||||
|
||||
@@ -161,6 +193,7 @@ class MLS_Query {
|
||||
'search' => null, // Search in address/remarks
|
||||
'bounds' => null, // Map bounds: array(sw_lat, sw_lng, ne_lat, ne_lng)
|
||||
'center' => null, // Map center for distance sort: array(lat, lng)
|
||||
'featured_ids' => null, // Array of listing_id values to prioritize after HomeProz
|
||||
'limit' => 20,
|
||||
'offset' => 0,
|
||||
'orderby' => 'modification_timestamp',
|
||||
@@ -218,12 +251,18 @@ class MLS_Query {
|
||||
$values[] = $args['postal_code'];
|
||||
} elseif ($args['center_lat'] && $args['center_lng']) {
|
||||
// Direct lat/lng radius search (from homepage location search)
|
||||
// Use bounding box pre-filter for spatial index, then exact distance
|
||||
$bbox_filter = $this->get_bounding_box_filter_sql(
|
||||
(float) $args['center_lat'],
|
||||
(float) $args['center_lng'],
|
||||
(int) $args['radius']
|
||||
);
|
||||
$distance_filter = $this->get_distance_filter_sql(
|
||||
(float) $args['center_lat'],
|
||||
(float) $args['center_lng'],
|
||||
(int) $args['radius']
|
||||
);
|
||||
$where[] = "({$distance_filter})";
|
||||
$where[] = "({$bbox_filter} AND {$distance_filter})";
|
||||
}
|
||||
|
||||
if ($args['county']) {
|
||||
@@ -311,6 +350,18 @@ class MLS_Query {
|
||||
$sql .= ' WHERE ' . implode(' AND ', $where);
|
||||
|
||||
// ORDER BY
|
||||
// Always prioritize: 1) HomeProz listings, 2) Featured listings, 3) Regular listings
|
||||
// Build featured sort expression if featured_ids provided
|
||||
$featured_sort = '';
|
||||
if (!empty($args['featured_ids']) && is_array($args['featured_ids'])) {
|
||||
$featured_ids = array_map('sanitize_text_field', $args['featured_ids']);
|
||||
$featured_placeholders = implode(',', array_fill(0, count($featured_ids), '%s'));
|
||||
$featured_sort = $wpdb->prepare(
|
||||
", (CASE WHEN listing_id IN ({$featured_placeholders}) THEN 1 ELSE 0 END) DESC",
|
||||
...$featured_ids
|
||||
);
|
||||
}
|
||||
|
||||
// If center provided, sort by distance from center
|
||||
if ($args['center'] && is_array($args['center']) && count($args['center']) === 2) {
|
||||
list($center_lat, $center_lng) = $args['center'];
|
||||
@@ -318,7 +369,7 @@ class MLS_Query {
|
||||
// Using squared Euclidean distance with latitude adjustment for speed
|
||||
$lat_factor = cos(deg2rad((float) $center_lat));
|
||||
$sql .= $wpdb->prepare(
|
||||
" ORDER BY (POW(latitude - %f, 2) + POW((longitude - %f) * %f, 2)) ASC",
|
||||
" ORDER BY is_homeproz DESC{$featured_sort}, (POW(latitude - %f, 2) + POW((longitude - %f) * %f, 2)) ASC",
|
||||
(float) $center_lat,
|
||||
(float) $center_lng,
|
||||
$lat_factor
|
||||
@@ -338,7 +389,7 @@ class MLS_Query {
|
||||
|
||||
$orderby = in_array($args['orderby'], $allowed_orderby) ? $args['orderby'] : 'modification_timestamp';
|
||||
$order = strtoupper($args['order']) === 'ASC' ? 'ASC' : 'DESC';
|
||||
$sql .= " ORDER BY {$orderby} {$order}";
|
||||
$sql .= " ORDER BY is_homeproz DESC{$featured_sort}, {$orderby} {$order}";
|
||||
}
|
||||
|
||||
// LIMIT/OFFSET
|
||||
@@ -530,13 +581,19 @@ class MLS_Query {
|
||||
$values[] = $args['postal_code'];
|
||||
} elseif (!empty($args['center_lat']) && !empty($args['center_lng'])) {
|
||||
// Direct lat/lng radius search (from homepage location search)
|
||||
// Use bounding box pre-filter for spatial index, then exact distance
|
||||
$radius = !empty($args['radius']) ? (int) $args['radius'] : 30;
|
||||
$bbox_filter = $this->get_bounding_box_filter_sql(
|
||||
(float) $args['center_lat'],
|
||||
(float) $args['center_lng'],
|
||||
$radius
|
||||
);
|
||||
$distance_filter = $this->get_distance_filter_sql(
|
||||
(float) $args['center_lat'],
|
||||
(float) $args['center_lng'],
|
||||
$radius
|
||||
);
|
||||
$where[] = "({$distance_filter})";
|
||||
$where[] = "({$bbox_filter} AND {$distance_filter})";
|
||||
}
|
||||
|
||||
if (!empty($args['county'])) {
|
||||
@@ -728,7 +785,8 @@ class MLS_Query {
|
||||
|
||||
$table = $this->db->properties_table();
|
||||
|
||||
$where = array('mlg_can_view = 1', 'latitude IS NOT NULL', 'longitude IS NOT NULL');
|
||||
// Exclude properties with invalid coordinates from map bounds
|
||||
$where = array('mlg_can_view = 1', 'latitude IS NOT NULL', 'longitude IS NOT NULL', 'coordinates_invalid = 0');
|
||||
$values = array();
|
||||
|
||||
// Add state filter (MN and IA only)
|
||||
|
||||
Reference in New Issue
Block a user