Novo na versão 12.0.0.
Os dados geoespaciais, ou "geodata", especificam pontos e objetos geométricos na superfície da Terra. Com os tipos de dados geográficos, você pode criar consultas que verificam 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 12,3,0: Dados geoespaciais suportados no Atlas Device Sync
O Realm Node.js SDK v12.3.0 e posterior adiciona suporte para dados geoespaciais no Atlas Device Sync. Isto permite a você assinar queries geoespaciais em um domínio 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 Gerenciar assinaturas de sincronização - SDK do Node.js.
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 Node.js SDK suporta query 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.
GeoPoint pode ser um dos três tipos:
Um objeto com:
latitude: um valor numéricolongitude: um valor numéricoaltitude: um valor de número opcional
CanonicalGeoPoint: uma interface que satisfaz as especificações GeoJSON para um pontoGeoPosition: uma matriz com longitude, latitude e uma altura opcional
Um GeoPoint é utilizado apenas como um bloco de construção das outras formas: GeoCircle, GeoBox e GeoPolygon. Estas formas, e o tipo GeoPoint, são usadas em query, não para persistência.
Para salvar dados geoespaciais no domínio, consulte Dados geoespaciais persistentes.
Círculo geográfico
Um GeoCircle define um círculo na superfície da Terra. Você define um GeoCircle fornecendo:
Um
GeoPointpara o centro do círculoUm número para a distância (raio) do círculo
A distância do raio utiliza radianos como unidade de medida. O SDK fornece os métodos kmToRadians e miToRadians para converter quilômetros ou milhas para radianos.
O seguinte código mostra dois exemplos de criação de um círculo:
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, };

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). Uma GeoBox se comporta da mesma forma que o GeoPolygon correspondente.
O exemplo a seguir cria 2 caixas:
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, };

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. O exemplo abaixo cria 3 polígonos: um é um polígono básico com 5 pontos, um é o mesmo polígono com um único orifício e o terceiro é o mesmo polígono com dois orifícios:
// 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], };

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.
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 realm incorporado. Para obter mais informações sobre objetos incorporados, consulte Objetos incorporados - SDK do Node.js.
No mínimo, adicione os dois campos exigidos pela especificação GeoJSON:
Um campo do tipo
double[]que mapeia para uma propriedade de "coordenadas" (diferencia maiúsculas de minúsculas) no esquema do Realm.Um campo do tipo
stringque mapeia para uma propriedade "type". O valor deste campo deve ser "Ponto".
Para simplificar a persistência de geodados, você pode definir um modelo que implementa CanonicalGeoPoint, que já tem a forma correta. O exemplo seguinte mostra uma classe incorporada denominada MyGeoPoint que é utilizada para persistir dados geoespaciais:
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[]", }, }; }
Use a classe incorporada
Em seguida, você usa a classe personalizada MyGeoPoint no seu modelo de domínio, conforme mostrado no exemplo a seguir:
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", }; }
Você adiciona instâncias de sua classe ao domínio como qualquer outro modelo do Realm. No entanto, neste exemplo, como a classe MyGeoPoint não estende Realm.Object, devemos especificar MyGeoPoint.schema ao abrir o Realm:
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), }); });
A imagem a seguir mostra os resultados da criação desses dois objetos de empresa.

Executar query de dados geoespaciais
Para fazer query de dados geoespaciais, você pode usar o operador geoWithin com RQL. O operador geoWithin pega a propriedade "coordenadas" de um objeto embarcado que define o ponto que estamos consultando e uma das formas geoespaciais para verificar se esse ponto está contido dentro da forma.
Observação
O formato para consultar dados geoespaciais é o mesmo, independentemente da forma da região de dados geoespaciais.
Os exemplos a seguir mostram a consulta a várias formas para retornar uma lista de empresas dentro da forma:
Círculo geográfico
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}`);

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}`);

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}` );
