Docs 菜单
Docs 主页
/ /
数据类型

地理空间 — Node.js SDK

版本 12.0.0 中的新增内容

地理空间数据或“地理数据”指定地球表面上的点和几何对象。 使用地理数据类型,您可以创建查询来检查给定点是否包含在形状中。 例如,您可以查找距离指定点15公里范围内的所有咖啡店。

在版本 12.3.0 中进行了更改: Atlas Device Sync 支持的地理空间数据

Realm Node.js SDK v12.3.0 及更高版本增加了对 Atlas Device Sync 中地理空间数据的支持。 这允许您订阅同步 Realm 中的地理空间查询。 如果您尝试使用旧版本的 SDK 订阅地理空间查询,您将收到带有补偿写入的服务器错误。 有关托管同步订阅的更多信息,请参阅托管同步订阅 - Node.js SDK。

有关使用Device Sync查询地理空间数据的更多信息,请参阅App Services文档中的地理空间数据

Node.js SDK 支持使用以下数据类型进行地理空间查询:

  • GeoPoint

  • GeoCircle

  • GeoBox

  • GeoPolygon

SDK 提供这些地理空间数据类型是为了简化地理空间数据的查询。 您无法直接持久化这些数据类型。

有关如何保存地理空间数据的信息,请参阅本页的保存地理空间数据部分。

GeoPoint定义了地球表面的特定位置。 所有地理空间数据类型都使用 GeoPoints来定义其位置。

GeoPoint 可以是以下三种类型之一:

  • 一个对象:

    • latitude:一个数值

    • longitude:一个数值

    • altitude:可选数值

  • CanonicalGeoPoint:满足点的 GeoJSON 规范的接口

  • GeoPosition:具有经度、纬度和可选海拔高度的数组

GeoPoint 仅用作其他形状的构建基块:GeoCircle、GeoBox 和 GeoPolygon。 这些形状和 GeoPoint 类型用于查询,而不是用于持久性。

要将地理空间数据保存到 Realm,请参阅持久地理空间数据。

GeoCircle定义了地球表面上的一个圆。 您可以通过提供以下内容来定义GeoCircle

  • 代表圆心的GeoPoint

  • 表示圆的距离(半径)的数字

半径距离使用弧度作为测量单位。 SDK 提供了方法kmToRadiansmiToRadians将公里或英里转换为弧度。

以下代码显示了创建圆形的两个示例:

const smallCircle = {
center: [-121.9, 47.3],
// The GeoCircle radius is measured in radians.
// This radian distance corresponds with 0.25 degrees.
distance: 0.004363323,
};
const largeCircleCenter = {
longitude: -122.6,
latitude: 47.8,
};
// Realm provides `kmToRadians` and `miToRadians`
// to convert these measurements. Import the relevant
// convenience method for your app's needs.
const radiusFromKm = kmToRadians(44.4);
const largeCircle = {
center: largeCircleCenter,
distance: radiusFromKm,
};
const smallCircle: GeoCircle = {
center: [-121.9, 47.3],
// The GeoCircle radius is measured in radians.
// This radian distance corresponds with 0.25 degrees.
distance: 0.004363323,
};
const largeCircleCenter: GeoPoint = {
longitude: -122.6,
latitude: 47.8,
};
// Realm provides `kmToRadians` and `miToRadians`
// to convert these measurements. Import the relevant
// convenience method for your app's needs.
const radiusFromKm = kmToRadians(44.4);
const largeCircle: GeoCircle = {
center: largeCircleCenter,
distance: radiusFromKm,
};
两个 GeoCircle
点击放大

GeoBox在地球表面定义了一个矩形。 您可以通过指定左下(西南)角和右上角(东北)角来定义矩形。 GeoBox 的行为与相应的GeoPolygon 相同。

以下示例将创建2框:

const largeBox = {
bottomLeft: [-122.7, 47.3],
topRight: [-122.1, 48.1],
};
const smallBoxBottomLeft = {
longitude: -122.4,
latitude: 47.5,
};
const smallBoxTopRight = {
longitude: -121.8,
latitude: 47.9,
};
const smallBox = {
bottomLeft: smallBoxBottomLeft,
topRight: smallBoxTopRight,
};
const largeBox: GeoBox = {
bottomLeft: [-122.7, 47.3],
topRight: [-122.1, 48.1],
};
const smallBoxBottomLeft: GeoPoint = {
longitude: -122.4,
latitude: 47.5,
};
const smallBoxTopRight: GeoPoint = {
longitude: -121.8,
latitude: 47.9,
};
const smallBox: GeoBox = {
bottomLeft: smallBoxBottomLeft,
topRight: smallBoxTopRight,
};
2 GeoBox
点击放大

GeoPolygon定义了地球表面上的多边形。 由于多边形是闭合形状,因此必须提供至少4个点: 3个点用于定义多边形的形状,以及第四个点用于闭合形状。

重要

多边形中的第四个点必须与第一个点相同。

您还可以通过定义一个或多个“孔”来排除多边形内的区域。 孔洞是指其边界完全位于外部多边形内的另一个多边形。 以下示例创建了 3 个多边形:第一个是具有 5 个点的基本多边形,第一个是具有单孔的相同多边形,第三个是具有两个孔的相同多边形:

// Create a basic polygon
const basicPolygon = {
outerRing: [
[-122.8, 48.0],
[-121.8, 48.2],
[-121.6, 47.6],
[-122.0, 47.0],
[-122.6, 47.2],
[-122.8, 48.0],
],
};
// Create a polygon with one hole
const outerRing = [
[-122.8, 48.0],
[-121.8, 48.2],
[-121.6, 47.6],
[-122.0, 47.0],
[-122.6, 47.2],
[-122.8, 48.0],
];
const hole = [
[-122.6, 47.8],
[-122.2, 47.7],
[-122.6, 47.4],
[-122.5, 47.6],
[-122.6, 47.8],
];
const polygonWithOneHole = {
outerRing: outerRing,
holes: [hole],
};
// Add a second hole to the polygon
const hole2 = [
{
longitude: -122.05,
latitude: 47.55,
},
{
longitude: -121.9,
latitude: 47.55,
},
{
longitude: -122.1,
latitude: 47.3,
},
{
longitude: -122.05,
latitude: 47.55,
},
];
const polygonWithTwoHoles = {
outerRing: outerRing,
holes: [hole, hole2],
};
// Create a basic polygon
const basicPolygon: GeoPolygon = {
outerRing: [
[-122.8, 48.0],
[-121.8, 48.2],
[-121.6, 47.6],
[-122.0, 47.0],
[-122.6, 47.2],
[-122.8, 48.0],
],
};
// Create a polygon with one hole
const outerRing: GeoPoint[] = [
[-122.8, 48.0],
[-121.8, 48.2],
[-121.6, 47.6],
[-122.0, 47.0],
[-122.6, 47.2],
[-122.8, 48.0],
];
const hole: GeoPoint[] = [
[-122.6, 47.8],
[-122.2, 47.7],
[-122.6, 47.4],
[-122.5, 47.6],
[-122.6, 47.8],
];
const polygonWithOneHole: GeoPolygon = {
outerRing: outerRing,
holes: [hole],
};
// Add a second hole to the polygon
const hole2: GeoPoint[] = [
{
longitude: -122.05,
latitude: 47.55,
},
{
longitude: -121.9,
latitude: 47.55,
},
{
longitude: -122.1,
latitude: 47.3,
},
{
longitude: -122.05,
latitude: 47.55,
},
];
const polygonWithTwoHoles: GeoPolygon = {
outerRing: outerRing,
holes: [hole, hole2],
};
3 GeoPolygons
点击放大

重要

无法保留地理空间数据类型

目前,您只能保留地理空间数据。 地理空间数据类型无法直接持久化。 例如,您不能声明类型为GeoBox的属性。

这些类型只能用作地理空间查询的参数。

如果要持久保存地理空间数据,则必须符合GeoJSON规范

要创建符合 GeoJSON 规范的类,您需要:

  1. 创建一个嵌入式 Realm 对象。有关嵌入式对象的更多信息,请参阅嵌入式对象 - Node.js SDK。

  2. 至少添加 GeoJSON 规范要求的两个字段:

    • 类型为double[]的字段,映射到 Realm 模式中的“坐标”(区分大小写)属性。

    • 类型为string的字段,映射到“类型”属性。 此字段的值必须是“Point”。

为了简化地理数据持久性,您可以定义一个实现CanonicalGeoPoint的模型,该模型已经具有正确的形状。 以下示例显示了一个名为MyGeoPoint的嵌入式类,用于保存地理空间数据:

class MyGeoPoint {
type = "Point";
constructor(long, lat) {
this.coordinates = [long, lat];
}
static schema = {
name: "MyGeoPoint",
embedded: true,
properties: {
type: "string",
coordinates: "double[]",
},
};
}
// Implement `CanonicalGeoPoint`
// for convenience when persisting geodata.
class MyGeoPoint implements CanonicalGeoPoint {
coordinates!: GeoPosition;
type = "Point" as const;
constructor(long: number, lat: number) {
this.coordinates = [long, lat];
}
static schema: ObjectSchema = {
name: "MyGeoPoint",
embedded: true,
properties: {
type: "string",
coordinates: "double[]",
},
};
}

然后,您可以在域模型中使用自定义MyGeoPoint类,如以下示例所示:

class Company extends Realm.Object {
static schema = {
name: "Company",
properties: {
_id: "int",
location: "MyGeoPoint",
},
primaryKey: "_id",
};
}
class Company extends Realm.Object<Company> {
_id!: number;
location!: MyGeoPoint;
static schema: ObjectSchema = {
name: "Company",
properties: {
_id: "int",
location: "MyGeoPoint",
},
primaryKey: "_id",
};
}

您可以将类的实例添加到 Realm,就像添加任何其他 Realm 模型一样。但是,在此示例中,由于MyGeoPoint类未扩展Realm.Object ,因此在打开域时必须指定MyGeoPoint.schema

const realm = await Realm.open({
// `MyGeoPoint` does not extend `Realm.Object`, so you pass
// only the `.schema` when opening the realm.
schema: [Company, MyGeoPoint.schema],
});
// Add geospatial object to realm.
realm.write(() => {
realm.create(Company, {
_id: 6,
location: new MyGeoPoint(-122.35, 47.68),
});
realm.create(Company, {
_id: 9,
location: new MyGeoPoint(-121.85, 47.9),
});
});
const realm = await Realm.open({
// `MyGeoPoint` does not extend `Realm.Object`, so you pass
// only the `.schema` when opening the realm.
schema: [Company, MyGeoPoint.schema],
});
// Add geospatial object to realm.
realm.write(() => {
realm.create(Company, {
_id: 6,
location: new MyGeoPoint(-122.35, 47.68),
});
realm.create(Company, {
_id: 9,
location: new MyGeoPoint(-121.85, 47.9),
});
});

下图显示了创建这两个公司对象的结果。

2 个 GeoPoint
点击放大

要查询地理空间数据,可以将geoWithin操作符与RQL结合使用。 geoWithin操作符获取嵌入式对象的“坐标”属性(定义我们正在查询的点)和地理空间形状之一,以检查该点是否包含在该形状中。

注意

无论地理数据区域的形状如何,查询地理空间数据的格式都是相同的。

以下示例展示了如何查询各种形状以返回形状内的公司列表:

GeoCircle

const companiesInSmallCircle = realm
.objects(Company)
.filtered("location geoWithin $0", smallCircle);
console.debug(`Companies in smallCircle: ${companiesInSmallCircle.length}`);
const companiesInLargeCircle = realm
.objects(Company)
.filtered("location geoWithin $0", largeCircle);
console.debug(`Companies in largeCircle: ${companiesInLargeCircle.length}`);
const companiesInSmallCircle = realm
.objects(Company)
.filtered("location geoWithin $0", smallCircle);
console.debug(`Companies in smallCircle: ${companiesInSmallCircle.length}`);
const companiesInLargeCircle = realm
.objects(Company)
.filtered("location geoWithin $0", largeCircle);
console.debug(`Companies in largeCircle: ${companiesInLargeCircle.length}`);
查询 GeoCircle 示例。
点击放大

GeoBox

const companiesInLargeBox = realm
.objects(Company)
.filtered("location geoWithin $0", largeBox);
console.debug(`Companies in large box: ${companiesInLargeBox.length}`);
const companiesInSmallBox = realm
.objects(Company)
.filtered("location geoWithin $0", smallBox);
console.debug(`Companies in small box: ${companiesInSmallBox.length}`);
const companiesInLargeBox = realm
.objects(Company)
.filtered("location geoWithin $0", largeBox);
console.debug(`Companies in large box: ${companiesInLargeBox.length}`);
const companiesInSmallBox = realm
.objects(Company)
.filtered("location geoWithin $0", smallBox);
console.debug(`Companies in small box: ${companiesInSmallBox.length}`);
查询 GeoBox 示例。

GeoPolygon

const companiesInBasicPolygon = realm
.objects(Company)
.filtered("location geoWithin $0", basicPolygon);
console.debug(
`Companies in basic polygon: ${companiesInBasicPolygon.length}`
);
const companiesInPolygonWithTwoHoles = realm
.objects(Company)
.filtered("location geoWithin $0", polygonWithTwoHoles);
console.debug(
`Companies in polygon with two holes: ${companiesInPolygonWithTwoHoles.length}`
);
const companiesInBasicPolygon = realm
.objects(Company)
.filtered("location geoWithin $0", basicPolygon);
console.debug(
`Companies in basic polygon: ${companiesInBasicPolygon.length}`
);
const companiesInPolygonWithTwoHoles = realm
.objects(Company)
.filtered("location geoWithin $0", polygonWithTwoHoles);
console.debug(
`Companies in polygon with two holes: ${companiesInPolygonWithTwoHoles.length}`
);
查询 GeoPolygon 示例。
点击放大

后退

嵌入式对象

在此页面上