Sequelize 地理空间查询:找到距某个位置最近的“n”个点

Posted

技术标签:

【中文标题】Sequelize 地理空间查询:找到距某个位置最近的“n”个点【英文标题】:Sequelize geospatial query: find "n" closest points to a location 【发布时间】:2017-10-16 05:06:01 【问题描述】:

使用Sequelize和地理空间查询,如果我想找到离某个位置最近的“n”个点,Sequelize 查询应该如何?

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

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

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

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

假设我们有一个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

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

谢谢!

【问题讨论】:

我不确定,但看起来你的属性周围有多余的方括号 这个有什么消息吗? 【参考方案1】:

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

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

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

    const location = sequelize.literal(`ST_GeomFromText('POINT($lng $lat)', 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:确保将“用户”替换为您拥有几何数据类型的模型。

更新:如果您仍然无法使用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($lng $lat)')`);
    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))))");

【讨论】:

这个查询给了我以下错误:Error: Order must be type of array or instance of a valid sequelize method. 我试图将order 更改为['distance'] 而不是'distance',现在我得到:SequelizeDatabaseError: column User.distance does not exist 您是否在属性中包含字符串“距离”?不带引号的距离也应该起作用:...order: distance... 我已经为属性添加了距离,但是当我进行 where 查询时,它会显示距离 Unknown column 'location' in 'field list' 只是要提一下,当使用POINT时,经度应该是第一个参数 什么是maxDistance 请你举个maxDistance的例子【参考方案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
)

位置定义为:

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

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

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

【讨论】:

半径的单位是什么?公里还是英里?【参考方案3】:

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
);

【讨论】:

我如何在距离列上应用有或在哪里? @AbhiBurk 你可以在 findAll sequelize.org/master/manual/…的对象中添加 where 键 Haversine 公式部分非常完美!

以上是关于Sequelize 地理空间查询:找到距某个位置最近的“n”个点的主要内容,如果未能解决你的问题,请参考以下文章

Apache点燃地理空间查询示例

arcgis定义查询like公式

地理编码 - 获取位置

Mongoose 地理空间查询查找所有包含给定点的用户球体

查询地理位置附近的东西?

MongoDB 对地理空间查询的计数不准确