Please help! I have a project using Sequelize, postgres and postgis (for gps). The schema (simplified) is:
export default class Activity extends Model {
static init(sequelize) {
return super.init({
sequelize,
tableName: 'activities',
})
}
static get modelAttributes() {
return {
id: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
primaryKey: true,
},
activity_name: {
type: 'citext',
allowNull: false,
unique: {
args: true,
msg: 'Activity name already in use.',
},
point: {
type: DataTypes.GEOMETRY,
allowNull: true,
},
}
}
}
I don't put the migration file, because this part works well: whene I save the data, it's correctly registered.
await Activity.bulkCreate({
activity_name: 'whatever,
point: { type: 'Point', coordinates: [lat, lng] },
})
The problem comes when I try to get activities within a certain distance:
const result = await User.findAll({
include: [
{
model: Activity,
required: true,
where: {
point: Sequelize.fn(
'ST_DWithin',
Sequelize.fn(
'ST_SetSRID',
Sequelize.fn('ST_MakePoint', 40.119536, 8.495837),
4326
),
parseFloat(distance) // number 10000
),
},
},
],
})
This code throws an error: "function st_dwithin(geometry, integer) does not exist". I struggling with this problem since a while. Any solution? thanks in advance!!!
The problem is two-fold:
ST_DWithin()
requires two geometries and a distance limit. You only supplied a single geometry and the limit:
boolean ST_DWithin(geometry g1, geometry g2, double precision distance_of_srid);
ST_SetSRID()
adds the context of a reference system to the supplied geometry. You selected EPSG:4326, which uses geographical degrees as the base unit, not meters, not miles. There's a warning in ST_DWithin
documentation:
The distance is specified in units defined by the spatial reference system of the geometries.
Which means a distance limit of 180 already returns everything, filters nothing, because on a sphere you're always at most 180 degrees away: 181 degrees East is just 179 degrees West. Change the SRID or convert your units: assuming WGS-84's spherical Earth radius of 6371008.8m
, you can arrive at a single degree of 111195.08m
by dividing the circumference over 360
degrees: 6371008.8*2*pi/360
. A single meter is then 1/111195.08
of a degree which is why you might want to trade this:
parseFloat(distance) * 5000
for this
parseFloat(distance) /111195.08