SilverStripe ORM中每个mysql在位置Y周围半径X内的位置
我正在根据SilverStripe 3.4.0中的mysql过滤位置Y周围半径X内的位置 到目前为止,我已经实现了一个原始查询来获取圆圈中的ID,然后使用这些ID来过滤每个SilverStripe ORM,因为我必须根据多个条件进行过滤,而geofilter只是其中之一 另请参见谷歌的“商店定位器”示例:SilverStripe ORM中每个mysql在位置Y周围半径X内的位置,mysql,orm,geolocation,silverstripe,Mysql,Orm,Geolocation,Silverstripe,我正在根据SilverStripe 3.4.0中的mysql过滤位置Y周围半径X内的位置 到目前为止,我已经实现了一个原始查询来获取圆圈中的ID,然后使用这些ID来过滤每个SilverStripe ORM,因为我必须根据多个条件进行过滤,而geofilter只是其中之一 另请参见谷歌的“商店定位器”示例: 这将提供所需的结果,但需要额外的查询是否可以将地理过滤器直接添加到ORM内的查询?我不确定“直接添加到ORM内的查询”是什么意思,但可以理解为“不调用Web服务”,然后我可以提供以下解决方案
这将提供所需的结果,但需要额外的查询是否可以将地理过滤器直接添加到ORM内的查询?我不确定“直接添加到ORM内的查询”是什么意思,但可以理解为“不调用Web服务”,然后我可以提供以下解决方案 注意:我建议使用javascript在商店定位器中直接与google maps一起执行此操作,以便通过javascript同步完成计算
function FilterMemberByPostCodeDistance($params, $query){
$query->where('Member.PostCode IS NOT NULL')
->innerJoin('PostCodeToLocation',"SUBSTRING_INDEX(SUBSTRING_INDEX(Member.PostCode,' ', 1),' ',-1) = PostCodeToLocation.OutCode");
$latitude = (float)$postCodeToLocation->Latitude;
$longitude = (float)$postCodeToLocation->Longitude;
$fTemp = floatval($params['Distance']) / 111.045;
$fMagicSquareMinLatitude = $latitude - $fTemp;
$fMagicSquareMaxLatitude = $latitude + $fTemp;
$fTemp = 50.0 / (111.045 * cos(deg2rad($latitude)));
$fMagicSquareMinLongitude = $longitude - $fTemp;
$fMagicSquareMaxLongitude = $longitude + $fTemp;
$query->where(
//Magic Square - this is a simple square to filter out most out of distance values before the magic circle
//this is done because the circle calculation is much more expensive that the square
'PostCodeToLocation.Latitude BETWEEN '.$fMagicSquareMinLatitude.' AND '.$fMagicSquareMaxLatitude.'
AND PostCodeToLocation.Longitude BETWEEN '.$fMagicSquareMinLongitude.' AND '.$fMagicSquareMaxLongitude
//Magic Circle (https://en.wikipedia.org/wiki/Haversine_formula)
//This is what does the complicated maths to determine if the postcode is in the cirectle or not
//not as we are using out codes only, this is a "good estimate" but not 100% accurate
//.' AND acos(sin(RADIANS('.$latitude.'))
// * sin(RADIANS(PostCodeToLocation.Latitude))
// + cos(RADIANS('.$latitude.'))
// * cos(RADIANS(PostCodeToLocation.Latitude))
// * cos(RADIANS(PostCodeToLocation.Longitude)
// - (RADIANS('.$longitude.'))))
// * 6371 <= '.($params['Distance'] * 1.60934) //Kilometers
//REFACTOR of above to process more upfront within PHP
.' AND acos(sin('.deg2rad($latitude).')
* sin(RADIANS(PostCodeToLocation.Latitude)) + '.cos(deg2rad($latitude))
.' * cos(RADIANS(PostCodeToLocation.Latitude))
* cos(RADIANS(PostCodeToLocation.Longitude) - '.deg2rad($longitude).'))
* 6371 <= '.($params['Distance'] * 1.60934) //Kilometers
);
return $query;
}
可以找到上面的数据文件。是的,可以使用ORM来实现这一点。您可以将
DataQuery
从DataList
中拉出,对其进行更改(添加子句等),然后使用它更新DataList
比如:
$dataList = MyObject::get();
$dataQuery = $dataList->dataQuery();
$dataQuery->where(...);
$dataQuery->having(...);
$dataList->setDataQuery($dataQuery);
添加select并为其添加别名有点棘手,因为您需要针对DataQuery
修改SQLQuery
,但这也应该是可能的,尽管只需添加haversin公式即可进行排序
$dataList->sort(...)
直接在ORM中添加一个过滤器到底是什么意思?@Shadow-SilverStripe进行延迟加载。这意味着我们可以修改查询$DataObjects。我的意思是$DataObjects->where(“…”),因为您的查询只有having子句,所以我将使用dataobject类的
having()
方法。但是,您可以轻松地将filter()
方法中的任何内容包含在查询的where子句中。将此条件添加到原始查询可能更容易,而不是试图找出如何以SilverStripe的方式重写原始查询。我没有说你会删除一个查询,但它会以最理想的方式完成:直接在mysql上,因为这是数据的来源,而不需要在php上做额外的步骤。只需补充说明:你的查询和下面的所有答案(此时)计算“精确”距离(可能不尽可能精确,但仍然如此)针对每一行。虽然当有1000行时,这并不需要花太多时间处理单个行,但处理查询将需要很长时间。执行边界框搜索(位置在适合您距离的区域的NE、SE、SW、NW坐标内)并过滤此信息要快得多(小得多)如果需要精确的距离,用PHP设置。thx@Barry但说实话,我喜欢我拥有的比你建议的更多。为了获得位置-Y的lat/lng,我根据谷歌进行地理编码,但为了找到圆圈中的位置,我只需根据MySQL查询DB。但我想在一次查询中做到这一点。正如我所说的“如果你的意思是…”因为我不是很确定。我认为你现在的情况是最好的,因为你不能把HAVING子句放到ORM中。很抱歉,我有时会误解这个问题:)你每天都会学到新东西……是的@Barry,很酷,'我会尝试一下,然后再报告。如果我失败了,我会在IRC上唠叨你:)
class PostCodeToLocation extends DataObject{
static $db = array(
'OutCode' => 'Varchar(5)',
'Latitude' => 'Float',
'Longitude' => 'Float'
);
public static $indexes = array(
'OutCode' => true
);
public function PopulatePostCodeToLocationTable() {
DB::query('TRUNCATE TABLE PostCodeToLocation');
$arrPostCodetoLocations = file(BASE_PATH .'/mysite/.../postcode_outcode_to_latlong.csv');
if(!empty($arrPostCodetoLocations))
foreach ($arrPostCodetoLocations as $strPostCodetoLocation) {
list ($strOutCode,$strLatitude,$strLongitude) = explode(',',$strPostCodetoLocation);
DB::query("INSERT INTO PostCodeToLocation (OutCode, Latitude, Longitude )
VALUES ('".$strOutCode."','".$strLatitude."','".$strLongitude."')"
);
}
}
}
$dataList = MyObject::get();
$dataQuery = $dataList->dataQuery();
$dataQuery->where(...);
$dataQuery->having(...);
$dataList->setDataQuery($dataQuery);
$dataList->sort(...)