Sequelize geospatial query:find" n"最近的位置

时间:2017-05-16 22:59:26

标签: mysql postgresql sequelize.js geospatial postgis

使用Sequelize和地理空间查询,如果我想找到" n"最近点到某个位置,Sequelize查询应该怎么做?

假设我的模型看起来像这样:

sequelize.define('Point', {geo: DataTypes.GEOMETRY('POINT')});

现在让我们说我们通过以下方式在数据库中输入100个随机点:

db.Point.create({geo: {type: 'Point', coordinates: [randomLat, randomLng]}});

想象一下,我们有一个latlng变量来定义一个位置,我们希望找到最近的10个点。当我运行此查询时出现错误:

const location = sequelize.literal(`ST_GeomFromText('POINT(${lat} ${lng})', 4326)`);

db.Point.findAll({
  attributes: [['distance', sequelize.fn('ST_Distance', sequelize.col('Point'), location)]],
  order: 'distance',
  limit: 10
});

// -> TypeError: s.replace is not a function

知道问题是什么/如何解决?

THX!

3 个答案:

答案 0 :(得分:4)

当您用括号括起 sequelize.fn 时,您还必须包含一个字符串作为别名:

[sequelize.fn('ST_Distance_Sphere', sequelize.literal('geolocation'), location), 'ALIASNAME']

另外,请尝试将ST_Distance更改为ST_Distance_Sphere。所以:

    const location = sequelize.literal(`ST_GeomFromText('POINT(${lat} ${lng})', 4326)`);

    User.findAll({
      attributes: [[sequelize.fn('ST_Distance_Sphere', sequelize.literal('geolocation'), location),'distance']],
      order: 'distance',
      limit: 10,
      logging: console.log
    })
    .then(function(instance){
      console.log(instance);
    })

这实际上对我有用。 obs:请确保将“User”替换为具有几何数据类型的模型。

更新:如果您仍然无法使用order: 'distance'订购,也许您应该在var中声明它并使用order: distance不带引号,如下所示:

    var lat = parseFloat(json.lat);
    var lng = parseFloat(json.lng);
    var attributes = Object.keys(User.attributes);

    var location = sequelize.literal(`ST_GeomFromText('POINT(${lat} ${lng})')`);
    var distance = sequelize.fn('ST_Distance_Sphere', sequelize.literal('geolocation'), location);
    attributes.push([distance,'distance']);

    var query = {
      attributes: attributes,
      order: distance,
      include: {model: Address, as: 'address'},
      where: sequelize.where(distance, {$lte: maxDistance}),
      logging: console.log
    }

距离准确度更新:

sarikaya 提到的解决方案似乎更准确。以下是使用postgres的方法:

var distance = sequelize.literal("6371 * acos(cos(radians("+lat+")) * cos(radians(ST_X(location))) * cos(radians("+lng+") - radians(ST_Y(location))) + sin(radians("+lat+")) * sin(radians(ST_X(location))))");

答案 1 :(得分:4)

MySQL可以提供函数ST_Distance_Sphere不存在的错误。在这种情况下,您可以使用此替代解决方案:

我将点数信息分别作为纬度和经度小数。假设你应该有一个看起来像这样的模型:

sequelize.define('Point', {latitude: DataTypes.DECIMAL(11,2)},
                          {longitude: DataTypes.DECIMAL(11,2)});

想象一下,我们有一个latlng变量来定义一个位置,我们希望找到最近的10个点:

db.Point.findAll({
  attributes: [[sequelize.fn('POW',sequelize.fn('ABS',sequelize.literal("latitude-"+lat)),2),'x1'],
               [sequelize.fn('POW',sequelize.fn('ABS',sequelize.literal("longitude-"+lng)),2),'x2']], 
  order: sequelize.fn('SQRT', sequelize.literal('x1+x2')),
  limit: 10
});

<强>更新

使用Haversine Formula,距离更准确:

db.Point.findAll({
  attributes: [[sequelize.literal("6371 * acos(cos(radians("+lat+")) * cos(radians(latitude)) * cos(radians("+lng+") - radians(longitude)) + sin(radians("+lat+")) * sin(radians(latitude)))"),'distance']],
  order: sequelize.col('distance'),
  limit: 10
});

答案 2 :(得分:2)

建立 @ Edudjr的答案,这就是我在项目中使用它所做的工作:

const location = sequelize.literal(`ST_GeomFromText('POINT(${ startLongitude } ${  startLatitude })')`)
const distance = sequelize.fn('ST_Distance_Sphere', sequelize.col('location'), location)

const inRadius = await Position.findAll({
    order: distance,
    where: sequelize.where(distance, { $lte: radius }),
    logging: console.log
})

其中Position定义为:

sequelize.define('Position', {
    location: DataTypes.GEOMETRY('POINT')
})
  

请注意,Point需要格式为(经度)的坐标   纬度)

https://gis.stackexchange.com/questions/209008/incorrect-arguments-to-st-distance-sphere-in-special-cases

https://dba.stackexchange.com/questions/33410/whats-the-difference-between-pointx-y-and-geomfromtextpointx-y