地理空间 - Flutter SDK
版本 1.6.0 中的新增内容。
地理空间数据或“地理数据”指定地球表面上的点和几何对象。 使用地理数据类型,您可以创建查询来检查给定点是否包含在形状中。 例如,您可以查找距离指定点15公里范围内的所有咖啡店。
Flutter SDK 1.6.1及更高版本增加了对 Atlas Device Sync 中地理空间数据的支持。 这允许您订阅同步数据库中的地理空间查询。 如果您尝试使用旧版本的 SDK 订阅地理空间查询,您将收到带有补偿写入的服务器错误。 有关管理同步订阅的更多信息,请参阅管理同步订阅 - Flutter SDK。
有关使用Device Sync查询地理空间数据的更多信息,请参阅App Services文档中的地理空间数据。
地理空间数据类型
Flutter SDK 支持使用以下数据类型进行地理空间查询:
GeoPoint
GeoCircle
GeoBox
SDK 提供这些地理空间数据类型是为了简化地理空间数据的查询。 您无法直接持久化这些数据类型。
有关如何保存地理空间数据的信息,请参阅本页的保存地理空间数据部分。
GeoPoint
GeoPoint 定义地球表面的特定位置。所有地理空间数据类型都使用GeoPoints
来定义其位置。
GeoPoint
是具有两个必需属性的对象:
lat
:双精度值lon
:双精度值
GeoPoint 仅用作其他形状的构建基块:GeoCircle 和 GeoBox。 这些形状和 GeoPoint 类型用于查询,而不是用于持久性。
要将地理空间数据保存到数据库,请参阅持久地理空间数据。
GeoCircle
GeoCircle 在地球表面定义一个圆。您可以通过提供以下内容来定义GeoCircle
:
代表圆心的
GeoPoint
GeoDistance
表示圆的距离(半径)
半径距离使用弧度作为测量单位,在 SDK 中实现为双精度值。 SDK 提供了从其他度量单位创建GeoDistance
的便捷方法:
以下代码显示了创建圆形的两个示例:
final smallCircle = GeoCircle(GeoPoint(lon: -121.9, lat: 47.3), 0.25.degrees); final largeCircleCenter = GeoPoint(lon: -122.6, lat: 47.8); // The SDK provides convenience methods to convert measurements to radians. final radiusFromKm = GeoDistance.fromKilometers(44.4); final largeCircle = GeoCircle(largeCircleCenter, radiusFromKm);
GeoBox
GeoBox 在地球表面定义一个矩形。您可以通过指定左下(西南)角和右上角(东北)角来定义矩形。
以下示例将创建2框:
final largeBox = GeoBox( GeoPoint(lon: -122.7, lat: 47.3), GeoPoint(lon: -122.1, lat: 48.1)); final smallBoxSouthWest = GeoPoint(lon: -122.4, lat: 47.5); final smallBoxNorthEast = GeoPoint(lon: -121.8, lat: 47.9); final smallBox = GeoBox(smallBoxSouthWest, smallBoxNorthEast);
保留地理空间数据
重要
无法保留地理空间数据类型
目前,您只能保留地理空间数据。 地理空间数据类型无法直接持久化。 例如,您不能声明类型为GeoBox
的属性。
这些类型只能用作地理空间查询的参数。
如果要持久保存地理空间数据,则必须符合 GeoJSON 规范 。
创建与 GeoJSON 兼容的类
要创建符合 GeoJSON 规范的类,您需要:
创建嵌入式对象。 有关嵌入式对象的更多信息,请参阅嵌入式对象。
至少添加 GeoJSON 规范要求的两个字段:
类型为
double[]
的字段,映射到模式中的“坐标”(区分大小写)属性。类型为
string
的字段,映射到“类型”属性。 此字段的值必须是“Point”。
以下示例显示了一个名为MyGeoPoint
的嵌入式类,用于保存地理空间数据:
// To store geospatial data, create an embedded object with this structure. // Name it whatever is most convenient for your application. (ObjectType.embeddedObject)class _MyGeoPoint { // These two properties are required to persist geo data. final String type = 'Point'; final List<double> coordinates = const []; // You can optionally implement convenience methods to simplify // creating and working with geospatial data. double get lon => coordinates[0]; set lon(double value) => coordinates[0] = value; double get lat => coordinates[1]; set lat(double value) => coordinates[1] = value; GeoPoint toGeoPoint() => GeoPoint(lon: lon, lat: lat); }
使用嵌入式类
然后,您可以在数据模型中使用自定义MyGeoPoint
类,如以下示例所示:
// Use the GeoJSON-compatible class as a property in your model. ()class _Company { () late ObjectId id; _MyGeoPoint? location; }
就像添加任何其他模型一样,将类的实例添加到数据库中。
final realm = Realm(Configuration.local([MyGeoPoint.schema, Company.schema])); realm.write(() { realm.addAll([ Company( firstCompanyID, location: MyGeoPoint(coordinates: [-122.35, 47.68]), ), Company( secondCompanyID, location: MyGeoPoint(coordinates: [-121.85, 47.9]), ) ]); });
下图显示了创建这两个公司对象的结果。
查询地理空间数据
要查询地理空间数据,可以将geoWithin
操作符与RQL结合使用。 geoWithin
操作符获取嵌入式对象的“坐标”属性(定义我们正在查询的点)和地理空间形状之一,以检查该点是否包含在该形状中。
注意
无论地理数据区域的形状如何,查询地理空间数据的格式都是相同的。
以下示例展示了如何查询各种形状以返回形状内的公司列表:
GeoCircle
final companiesInSmallCircle = realm.query<Company>("location geoWithin \$0", [smallCircle]); final companiesInLargeCircle = realm.query<Company>("location geoWithin \$0", [largeCircle]);
GeoBox
final companiesInLargeBox = realm.query<Company>("location geoWithin \$0", [largeBox]); final companiesInSmallBox = realm.query<Company>("location geoWithin \$0", [smallBox]);