SilverStripe ORM中每个mysql的位置Y周围的半径X内的位置

时间:2016-08-15 09:16:53

标签: mysql orm geolocation silverstripe

我正在SilverStripe 3.4.0中的每个mysql的位置Y周围的半径X内过滤位置。

到目前为止,我已经实现了一个原始查询来获取圈子中的ID,而不是使用这些ID来过滤每个SilverStripe ORM,因为我必须根据多个条件进行过滤,而geofilter只是其中之一。

另请参阅Google“商店定位器”示例: https://developers.google.com/maps/articles/phpsqlsearch_v3

$searchDistance = '...';
$searchLat = '...';
$searchLng = '...';

$geolimitedIDs = DB::query('SELECT id, (6371 * acos(cos(radians('.$searchLat.')) * cos(radians(Latitude)) * cos( radians(Longitude) - radians('.$searchLng.')) + sin(radians('.$searchLat.')) * sin(radians(Latitude))))
    AS distance
    FROM "DataObject"
    HAVING distance < ' . $searchDistance . '
    ORDER BY distance')->column();

if($geolimitedIDs) {
    $DataObjects = $DataObjects->filter(array(
        'ID' => $geolimitedIDs
    ));
}

在_config中我创建了DataObject-Tabel MyISAM

DataObject:
  create_table_options:
    MySQLDatabase:
      'ENGINE=MyISAM'

这可以提供所需的结果,但需要额外的查询。 是否可以将地理过滤器直接添加到ORM中的查询中?

2 个答案:

答案 0 :(得分:3)

是的,可以使用ORM来实现这一目标。您可以从DataQuery中提取DataList,更改它(添加条款等),然后使用它更新DataList

类似的东西:

$dataList = MyObject::get();

$dataQuery = $dataList->dataQuery();

$dataQuery->where(...);
$dataQuery->having(...);

$dataList->setDataQuery($dataQuery);

添加和别名选择有点棘手,因为您需要针对SQLQuery修改DataQuery,但它也应该是可行的,尽管只需添加哈希公式作为排序工作

$dataList->sort(...)

答案 1 :(得分:1)

我不确定“直接查询ORM中的查询”是什么意思,但是我的意思是“没有调用网络服务”,那么我可以提供以下解决方案......

注意我建议使用javascript直接在商店定位器中使用google地图执行此操作,以便通过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 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;
}

上面的函数使用邮政编码从公共可用数据之后的lon / lat对(此位仅限英国)。

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."')"
                );
            }
    }
}

找到上述数据文件here