地理空间数据 - Kotlin SDK
在此页面上
1.11.0 版本中的新增功能。
地理空间数据指定地球表面上的点和几何对象。
Kotlin SDK 版本 1.11.0 及更高版本添加了实验性地理空间 API,支持使用地理空间数据进行查询。 这些查询可以检查给定点是否包含在形状中。例如,您可以查找指定点 15 公里范围内的所有咖啡店。
在版本 1.13.0 中进行了更改: Atlas Device Sync 支持的地理空间数据
Kotlin SDK 版本 1.13.0 及更高版本增加了对 Atlas Device Sync 中地理空间数据的支持。 这允许您订阅同步 Realm 中的地理空间查询。 如果您尝试使用旧版本的 SDK 订阅地理空间查询,您将收到带有补偿写入的服务器错误。
有关托管同步订阅的更多信息,请参阅托管同步订阅 - Kotlin SDK。
有关使用Device Sync查询地理空间数据的更多信息,请参阅App Services文档中的地理空间数据。
地理空间数据类型
Kotlin SDK 支持使用以下数据类型进行地理空间查询:
GeoPoint
GeoCircle
GeoBox
GeoPolygon
SDK 提供这些地理空间数据类型是为了简化地理空间数据的查询。 您无法直接持久化这些数据类型。
有关如何保存地理空间数据的信息,请参阅本页的保存地理空间数据部分。
GeoPoint
GeoPoint定义了地球表面的特定位置。 所有地理空间数据类型都使用 GeoPoints
来定义其位置。
GeoCircle
GeoCircle定义了地球表面上的一个圆。 您可以通过为圆的中心提供GeoPoint
和指定圆的半径的Distance对象来定义GeoCircle
。
注意
您可以以公里、英里、度或弧度为单位定义半径。
以下示例创建了 2 个圆形:
val circle1 = GeoCircle.create( center = GeoPoint.create(47.8, -122.6), radius = Distance.fromKilometers(44.4) ) val circle2 = GeoCircle.create( center = GeoPoint.create(47.3, -121.9), radius = Distance.fromDegrees(0.25) )
GeoBox
GeoBox在地球表面定义了一个矩形。 您可以通过指定左下(西南)角和右上角(东北)角来定义矩形。
以下示例将创建2框:
val box1 = GeoBox.create( bottomLeft = GeoPoint.create(47.3, -122.7), topRight = GeoPoint.create(48.1, -122.1) ) val box2 = GeoBox.create( bottomLeft = GeoPoint.create(47.5, -122.4), topRight = GeoPoint.create(47.9, -121.8) )
GeoPolygon
GeoPolygon定义了地球表面上的多边形。
由于多边形是闭合形状,因此必须至少提供 4 个点:3 个点用于定义多边形的形状,第 4 个点用于闭合该形状。
重要
多边形中的第四个点必须与第一个点相同。
您还可以通过定义一个或多个“孔”来排除多边形内的区域。 孔洞是指其边界完全位于外部多边形内的另一个多边形。 孔也可以相互嵌套。 如果一个位置包含在奇数个环中,则该位置被视为位于多边形内部。
以下示例创建了 3 个多边形:
具有 5 个点的基本多边形
具有单孔的相同多边形
有两个孔的同一多边形
// Create a basic polygon val basicPolygon = GeoPolygon.create( listOf( GeoPoint.create(48.0, -122.8), GeoPoint.create(48.2, -121.8), GeoPoint.create(47.6, -121.6), GeoPoint.create(47.0, -122.0), GeoPoint.create(47.2, -122.6), GeoPoint.create(48.0, -122.8) ) ) // Create a polygon with a single hole val outerRing = listOf( GeoPoint.create(48.0, -122.8), GeoPoint.create(48.2, -121.8), GeoPoint.create(47.6, -121.6), GeoPoint.create(47.0, -122.0), GeoPoint.create(47.2, -122.6), GeoPoint.create(48.0, -122.8) ) val hole1 = listOf( GeoPoint.create(47.8, -122.6), GeoPoint.create(47.7, -122.2), GeoPoint.create(47.4, -122.6), GeoPoint.create(47.6, -122.5), GeoPoint.create(47.8, -122.6) ) val polygonWithOneHole = GeoPolygon.create(outerRing, hole1) // Add a second hole to the polygon val hole2 = listOf( GeoPoint.create(47.55, -122.05), GeoPoint.create(47.5, -121.9), GeoPoint.create(47.3, -122.1), GeoPoint.create(47.55, -122.05) ) val polygonWithTwoHoles = GeoPolygon.create(outerRing, hole1, hole2)
保留地理空间数据
重要
无法保留地理空间数据类型
目前,您只能保留地理空间数据。 地理空间数据类型无法直接持久化。 例如,您不能声明类型为GeoBox
的属性。
这些类型只能用作地理空间查询的参数。
如果要持久保存地理空间数据,则必须符合 GeoJSON规范。
要使用 Kotlin SDK 执行此操作,您可以创建一个与 GeoJSON 兼容的嵌入式类,然后在 Realm 数据模型中使用该类。
创建与 GeoJSON 兼容的类
要创建符合 GeoJSON 规范的类,您需要:
创建嵌入式 Realm 对象(从EmbeddedRealmObject继承的类)。
至少添加 GeoJSON 规范要求的两个字段:
String
类型的属性字段,它映射到一个值为"Point"
的type
属性:var type: String = "Point"
RealmList<Double>
类型为 的字段,映射到 域coordinates
模式中的属性,其中包含纬度/经度对:var coordinates: RealmList<Double> = realmListOf()
以下示例显示了一个名为CustomGeoPoint
的嵌入式类,用于保存地理空间数据:
class CustomGeoPoint : EmbeddedRealmObject { constructor(latitude: Double, longitude: Double) { coordinates.apply { add(longitude) add(latitude) } } // Empty constructor required by Realm constructor() : this(0.0, 0.0) // Name and type required by Realm var coordinates: RealmList<Double> = realmListOf() // Name, type, and value required by Realm private var type: String = "Point" var latitude: Double get() = coordinates[1] set(value) { coordinates[1] = value } var longitude: Double get() = coordinates[0] set(value) { coordinates[0] = value } }
使用嵌入式类
在 域 模型中使用customGeoPoint
类,如以下示例所示:
class Company : RealmObject { var _id: ObjectId = ObjectId() var location: CustomGeoPoint? = null }
然后,您可以将类的实例添加到 Realm 中:
realm.writeBlocking { copyToRealm( Company().apply { location = CustomGeoPoint(47.68, -122.35) } ) copyToRealm( Company().apply { location = CustomGeoPoint(47.9, -121.85) } ) }
下图显示了创建这两个Company
对象的结果:
查询地理空间数据
要查询地理空间数据,可以将GEOWITHIN
操作符与RQL结合使用。 此方法采用嵌入式对象的coordinates
属性,并检查该点是否包含在该对象的地理空间形状内。
无论地理数据区域的形状如何,查询地理空间数据的格式都是相同的。
重要
不能对地理空间数据使用参数化查询。
以下示例展示了如何查询各种形状以返回形状内的公司列表。
GeoCircle
val companiesInLargeCircle = realm.query<Company>("location GEOWITHIN $circle1").find() println("Companies in large circle: ${companiesInLargeCircle.size}") val companiesInSmallCircle = realm.query<Company>("location GEOWITHIN $circle2").find() println("Companies in small circle: ${companiesInSmallCircle.size}")
Companies in large circle: 1 Companies in small circle: 0
GeoBox
val companiesInLargeBox = realm.query<Company>("location GEOWITHIN $box1").find() println("Companies in large box: ${companiesInLargeBox.size}") val companiesInSmallBox = realm.query<Company>("location GEOWITHIN $box2").find() println("Companies in small box: ${companiesInSmallBox.size}")
Companies in large box: 1 Companies in small box: 2
GeoPolygon
val companiesInBasicPolygon = realm.query<Company>("location GEOWITHIN $basicPolygon").find() println("Companies in basic polygon: ${companiesInBasicPolygon.size}") val companiesInPolygonWithHoles = realm.query<Company>("location GEOWITHIN $polygonWithTwoHoles").find() println("Companies in polygon with holes: ${companiesInPolygonWithHoles.size}")
Companies in basic polygon: 2 Companies in polygon with holes: 1