地理空间 — React Native SDK
版本 12.0.0 中的新增内容。
地理空间数据或“地理数据”指定地球表面上的点和几何对象。 使用地理数据类型,您可以创建查询来检查给定点是否包含在形状中。 例如,您可以查找距离指定点15公里范围内的所有咖啡店。
在版本 realm@12.3.0
中进行了更改:Atlas Device Sync 支持地理空间数据
Realm .js v 12.3.0及更高版本添加了对Atlas Device Sync中地理空间数据的支持。 这允许您订阅同步域中的地理空间查询。 如果您尝试使用旧版本的 SDK订阅地理空间查询,您将收到带有补偿写入的服务器错误。 有关管理 Sync 订阅的更多信息,请参阅地理空间 - React Native SDK。
有关使用 查询地理空间数据的更多信息,请参阅Device Sync Atlas App Services文档中的 地理空间数据 。
地理空间数据类型
SDK 支持使用以下数据类型进行地理空间查询:
GeoPoint
GeoCircle
GeoBox
GeoPolygon
SDK 提供这些地理空间数据类型是为了简化地理空间数据的查询。 您无法直接持久化这些数据类型。
有关如何保存地理空间数据的信息,请参阅本页的保存地理空间数据部分。
要查询地理空间数据,可以将geoWithin
操作符与RQL结合使用。 geoWithin
操作符采用嵌入式对象的coordinates
属性(定义我们正在查询的点)和地理空间形状之一来检查该点是否包含在该形状中。
注意
无论地理数据区域的形状如何,查询地理空间数据的格式都是相同的。
GeoPoint
GeoPoint定义了地球表面的特定位置。 所有地理空间数据类型都使用GeoPoints
来定义其位置。
GeoPoint
可以是以下三种类型之一:
一个对象:
latitude
:一个数值longitude
:一个数值altitude
:可选数值
CanonicalGeoPoint
:满足点的 GeoJSON 规范的接口GeoPosition
:具有经度、纬度和可选海拔高度的数组
GeoPoint 仅用作其他形状的构建基块:GeoCircle、GeoBox 和 GeoPolygon。 这些形状和 GeoPoint 类型用于查询,而不是用于持久性。
要将地理空间数据保存到域,请参阅本页的持久地理空间数据。
GeoCircle
GeoCircle定义了地球表面上的一个圆。 您可以通过提供以下内容来定义GeoCircle
:
代表圆心的
GeoPoint
表示圆的距离(半径)的数字
半径距离使用弧度作为测量单位。 SDK 提供了方法kmToRadians
和miToRadians
将公里或英里转换为弧度。
以下代码显示了创建圆形的两个示例:
import React from 'react'; import {View, Text} from 'react-native'; import {GeoCircle, GeoPoint, kmToRadians} from 'realm'; import {useQuery} from '@realm/react'; function Geocircle(): JSX.Element { // Define a GeoCircle 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, }; // Realm provides `kmToRadians` and `miToRadians` // to convert these measurements. Import the relevant // convenience method for your app's needs. const radiusFromKm = kmToRadians(44.4); // Define a GeoPoint within a GeoCircle const largeCircleCenter: GeoPoint = { longitude: -122.6, latitude: 47.8, }; const largeCircle: GeoCircle = { center: largeCircleCenter, distance: radiusFromKm, }; // Query geospatial data const companiesInSmallCircle = useQuery( Company, collection => collection.filtered('location geoWithin $0', smallCircle), [smallCircle], ); // Query geospatial data const companiesInLargeCircle = useQuery( Company, collection => collection.filtered('location geoWithin $0', largeCircle), [largeCircle], ); return ( <View> <Text>Small circle: {companiesInSmallCircle.length}</Text> <Text>Large circle: {companiesInLargeCircle.length}</Text> </View> ); }
下图显示了创建两个对象的结果:一个小圆圈和一个大圆圈。

对地理空间数据的查询会返回您定义的形状内的 Realm 对象。在上面的示例中,这是我们的查询结果:

GeoBox
GeoBox在地球表面定义了一个矩形。 您可以通过指定左下(西南)角和右上角(东北)角来定义矩形。 GeoBox 的行为与相应的 GeoPolygon 相同。
以下示例将创建2框:
import React from 'react'; import {View, Text} from 'react-native'; import {GeoBox, GeoPoint} from 'realm'; import {useQuery} from '@realm/react'; function Geobox(): JSX.Element { // Define a GeoBox const largeBox: GeoBox = { bottomLeft: [-122.7, 47.3], topRight: [-122.1, 48.1], }; // Define GeoBox corners 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, }; // Query geospatial data const companiesInLargeBox = useQuery( Company, collection => collection.filtered('location geoWithin $0', largeBox), [largeBox], ); // Query geospatial data const companiesInSmallBox = useQuery( Company, collection => collection.filtered('location geoWithin $0', smallBox), [smallBox], ); return ( <View> <Text>Small box: {companiesInSmallBox.length}</Text> <Text>Large box: {companiesInLargeBox.length}</Text> </View> ); }
下图显示了创建两个对象的结果:一个小方框和一个大方框。

对地理空间数据的查询会返回您定义的形状内的 Realm 对象。在上面的示例中,这是我们的查询结果:

GeoPolygon
GeoPolygon定义了地球表面上的多边形。 由于多边形是闭合形状,因此必须提供至少4个点: 3个点用于定义多边形的形状,以及第四个点用于闭合形状。
重要
多边形中的第四个点必须与第一个点相同。
您还可以通过定义一个或多个“孔”来排除多边形内的区域。 孔洞是指其边界完全位于外部多边形内的另一个多边形。 以下示例创建了 3 个多边形:第一个是具有 5 个点的基本多边形,第一个是具有单孔的相同多边形,第三个是具有两个孔的相同多边形:
import React from 'react'; import {View, Text} from 'react-native'; import {GeoPolygon, GeoPoint} from 'realm'; import {useQuery} from '@realm/react'; function Geopolygon(): JSX.Element { // Define a basic GeoPolygon 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], ], }; // Define a GeoPolygon 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 GeoPolygon 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], }; // Query geospatial data const companiesInBasicPolygon = useQuery( Company, collection => collection.filtered('location geoWithin $0', basicPolygon), [basicPolygon], ); // Query geospatial data const companiesInPolygonWithTwoHoles = useQuery( Company, collection => collection.filtered('location geoWithin $0', polygonWithTwoHoles), [polygonWithTwoHoles], ); return ( <View> <Text>Basic polygon: {companiesInBasicPolygon.length}</Text> <Text> Polygon with two holes: {companiesInPolygonWithTwoHoles.length} </Text> </View> ); }
下图显示了创建三个对象的结果:一个多边形、一个带一个孔的多边形和一个带两个孔的多边形。

对地理空间数据的查询会返回您定义的形状内的 Realm 对象。在上面的示例中,这是我们的查询结果:

保留地理空间数据
重要
无法保留地理空间数据类型
目前,您只能保留地理空间数据。 地理空间数据类型无法直接持久化。 例如,您不能声明类型为GeoBox
的属性。
这些类型只能用作地理空间查询的参数。
如果要持久保存地理空间数据,则必须符合 GeoJSON规范 。
创建与 GeoJSON 兼容的类
要创建符合 GeoJSON 规范的类,您需要:
创建嵌入式 Realm 对象。有关嵌入式对象的更多信息,请参阅嵌入式对象 - React Native SDK。
至少添加 GeoJSON 规范要求的两个字段:
类型为
double[]
的字段,映射到 Realm 模式中的“坐标”(区分大小写)属性。类型为
string
的字段,映射到“类型”属性。 此字段的值必须是“Point”。
为了简化地理数据持久性,您可以定义一个实现CanonicalGeoPoint
的模型,该模型已经具有正确的形状。 以下示例显示了一个名为MyGeoPoint
的嵌入式类,用于保存地理空间数据。
然后,您可以在 域 模型中使用自定义MyGeoPoint
类,如以下示例所示。您可以将类的实例添加到 Realm,就像添加任何其他 Realm 模型一样。但是,在此示例中,由于MyGeoPoint
类未扩展Realm.Object
,因此在打开域时必须指定MyGeoPoint.schema
:
import React from 'react'; import Realm, {ObjectSchema, CanonicalGeoPoint, GeoPosition} from 'realm'; import {RealmProvider} from '@realm/react'; // 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[]', }, }; } class Company extends Realm.Object<Company> { _id!: number; location!: MyGeoPoint; static schema: ObjectSchema = { name: 'Company', properties: { _id: 'int', location: 'MyGeoPoint', }, primaryKey: '_id', }; } export const Geospatial = () => { return ( <View> {/* `MyGeoPoint` does not extend `Realm.Object`, so you pass only the `.schema` when opening the realm. */} <RealmProvider schema={[Company, MyGeoPoint.schema]}> <WriteGeospatialObjects /> </RealmProvider> </View> ); };
编写嵌入式类
在RealmProvider
中,通过将嵌入式类与扩展Realm.Object
的类相结合来创建地理空间对象。
import React from 'react'; import {View} from 'react-native'; import {useEffect} from 'react'; import {useRealm, useQuery} from '@realm/react'; function WriteGeospatialObjects(): JSX.Element { const realm = useRealm(); const companies = useQuery(Company); useEffect(() => { if (!companies.length) { // Add geospatial objects to realm. writeNewCompany({_id: 6, location: new MyGeoPoint(-122.35, 47.68)}); writeNewCompany({_id: 9, location: new MyGeoPoint(-121.85, 47.9)}); } }, []); type CompanyProps = { _id: number; location: MyGeoPoint; }; const writeNewCompany = ({_id, location}: CompanyProps) => { // Add geospatial object to realm. realm.write(() => { realm.create(Company, { _id, location, }); }); }; return ( <View> <Geocircle /> <Geobox /> <Geopolygon /> </View> ); }
下图显示了创建这两个公司对象的结果。
