Dados geoespaciais - Kotlin SDK
Nesta página
Novidades na versão 1.11.0.
Os dados geoespaciais, ou "geodados", especificam pontos e objetos geométricos na superfície da Terra.
O Kotlin SDK versão 1.11.0 e posterior adiciona APIs geoespaciais experimentais que suportam queries com dados geoespaciais. Estas queries podem verificar se um determinado ponto está contido dentro de uma forma. Por exemplo, você pode encontrar todos os cafés em um raio de 15 km de um ponto especificado.
Alterado na versão 1,13,0: Dados geoespaciais suportados no Atlas Device Sync
O Kotlin SDK versão 1.13.0 e posterior adiciona suporte para dados geoespaciais no Atlas Realm Mobile Sync. Isto permite a você assinar query geoespaciais em um Realm sincronizado. Se você tentar se inscrever em uma query geoespacial com uma versão mais antiga do SDK, receberá um erro de servidor com uma gravação compensatória.
Para obter mais informações sobre como gerenciar suas assinaturas de sincronização, consulte managed assinaturas de sincronização - Kotlin SDK.
Para mais informações sobre query de dados geoespaciais com o Device Sync, consulte Dados geoespaciais na documentação do App Services.
Tipos de dados geoespaciais
O Kotlin SDK suporta queries geoespaciais utilizando os seguintes tipos de dados:
GeoPoint
Círculo geográfico
GeoBox
GeoPolygon
O SDK fornece estes tipos de dados geoespaciais para simplificar a query de dados geoespaciais. Você não pode persistir esses tipos de dados diretamente.
Para obter informações sobre como persistir dados geoespaciais, consulte a seção Persistir dados geoespaciais nesta página.
GeoPoint
Um GeoPoint define um local específico na superfície da Terra. Todos os tipos de dados geoespaciais utilizam GeoPoints
para definir sua localização.
Círculo geográfico
Um GeoCircle define um círculo na superfície da Terra. Você define um GeoCircle
fornecendo um GeoPoint
para o centro do círculo e um objeto de distância para especificar o raio do círculo.
Observação
Você pode definir o raio em quilômetros, milhas, graus ou radianos.
O exemplo a seguir cria 2 círculos:
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
Uma GeoBox define um retângulo na superfície da Terra. Você define o retângulo especificando o canto inferior esquerdo (sudoeste) e o canto superior direito (nordeste).
O exemplo a seguir cria 2 caixas:
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
Um GeoPolygon define um polígono na superfície da Terra.
Como um polígono é uma forma fechada, você deve fornecer um mínimo de 4 pontos: 3 pontos para definir a forma do polígono e um quarto para fechar a forma.
Importante
O quarto ponto em um polígono deve ser igual ao primeiro ponto.
Você também pode excluir áreas dentro de um polígono definindo um ou mais "furos". Um orifício é outro polígono cujos limites se encaixam completamente dentro do polígono externo. Os orifícios também podem ser aninhados uns dentro dos outros. Um local é considerado dentro do polígono se estiver incluído em um número ímpar de anéis.
O exemplo seguinte cria 3 polígonos:
Um polígono básico com 5 pontos
O mesmo polígono com um único orifício
O mesmo polígono com dois orifícios
// 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)
Persistir dados geoespaciais
Importante
Não é possível persistir tipos de dados geoespaciais
Atualmente, você só pode persistir dados geoespaciais. Os tipos de dados geoespaciais não podem ser persistidos diretamente. Por exemplo, você não pode declarar uma propriedade do tipo GeoBox
.
Estes tipos podem ser utilizados somente como argumentos para queries geoespaciais.
Se você deseja persistir os dados geoespaciais, eles devem estar em conformidade com a especificação GeoJSON.
Para fazer isso com o Kotlin SDK, você pode criar uma classe incorporada compatível com GeoJSON que pode ser usada em seu modelo de dados.
Criar uma classe compatível com GeoJSON
Para criar uma classe que esteja em conformidade com a especificação GeoJSON, você:
Crie um objeto de domínio incorporado (uma classe que herda de EmbeddedRealmObject).
No mínimo, adicione os dois campos exigidos pela especificação GeoJSON:
Um campo de propriedade de tipo
String
que mapeia para uma propriedadetype
com o valor de"Point"
:var type: String = "Point"
Um campo do tipo
RealmList<Double>
que mapeia para uma propriedadecoordinates
no esquema do Realm contendo um par latitude/longitude:var coordinates: RealmList<Double> = realmListOf()
O exemplo seguinte mostra uma classe embarcada denominada CustomGeoPoint
que é utilizada para persistir dados geoespaciais:
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 } }
Use a classe incorporada
Utilize a classe customGeoPoint
em seu modelo de Realm, como mostrado no seguinte exemplo:
class Company : RealmObject { var _id: ObjectId = ObjectId() var location: CustomGeoPoint? = null }
Você pode então adicionar instâncias da sua classe ao Realm:
realm.writeBlocking { copyToRealm( Company().apply { location = CustomGeoPoint(47.68, -122.35) } ) copyToRealm( Company().apply { location = CustomGeoPoint(47.9, -121.85) } ) }
A seguinte imagem mostra os resultados da criação destes dois objetos Company
:
Executar query de dados geoespaciais
Para query dos dados geoespaciais, você pode usar o operador GEOWITHIN
com RealmQL. Este método pega a propriedade coordinates
de um objeto embarcado e verifica se este ponto está contido dentro da forma geoespacial desse objeto.
O formato para consultar dados geoespaciais é o mesmo, independentemente da forma da região de dados geoespaciais.
Importante
Você não pode utilizar query parametrizadas com dados geoespaciais.
Os exemplos a seguir mostram a consulta de várias formas para retornar uma lista de empresas dentro da forma.
Círculo geográfico
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