7

Esiste un modo per interrogare e ottenere dati sulla posizione utilizzando la query geospaziale di mongodb che corrisponde ai seguenti criteri?MongoDB - Intersezione geospaziale di due poligoni

  • Ottenere tutte le posizioni che fanno parte dell'intersezione tra due caselle o in generale due poligoni.

Per esempio di seguito è possibile ottenere nella query di output solo quelle posizioni che si trovano nell'area gialla che in realtà è l'area comune per gli oggetti geometrici viola e rosso [poligoni]?

enter image description here

Il mio studio di documento MongoDB finora

Caso d'uso

db.places.find({ 
    loc: { $geoWithin: { $box: [ [ 0, 0 ], [ 100, 100 ] ] } } 
}) 

Sopra interrogazione fornisce i risultati entro una zona geometrica rettangolare [Cerco posizioni che sono comuni a due di queste singole query]

db.places.find({ 
    loc: { $geoWithin: { $box: [ [ 0, 0 ], [ 100, 100 ] ] } } 
}) 

    db.places.find({ 
    loc: { $geoWithin: { $box: [ [ 50, 50 ], [ 90, 120 ] ] } } 
}) 
+0

Questo aiuto? -http: //docs.mongodb.org/manual/reference/operator/query/geoIntersects/#example. Includi i documenti di esempio nella tua collezione che descrivono le coordinate. – BatScream

+0

@BatScream Il documento di esempio è la seguente: { "_id": "35004", "città": "ACMAR", "loc": [ -86,51557, 33,584,132 mila ], "pop" : 6055, "stato": "AL" } – Viraj

+0

@BatScream Grazie per la risposta. In realtà il link doc mongodb che hai fornito fornisce risultati basati sul poligono. Quello che sto cercando per qualcosa di simile. 1. Prima la query ottiene i risultati all'interno della casella A. 2. Secondo la query ottiene i risultati all'interno della casella B. 3. Terzo la query restituisce i risultati comuni a entrambi i riquadri A e B, il che significa che la query fornisce l'intersezione a due scatole o forse in generale due poligoni: D. C'è un modo per farlo? – Viraj

risposta

5

Così guardando questo con una mente fresca la risposta mi sta fissando in faccia. La cosa fondamentale che hai già detto è che vuoi trovare "l'intersezione" di due query in una singola risposta.

Un altro modo per osservare questo è che si desidera che tutti i punti vincolati dalla prima query siano "input" per la seconda query e così via come richiesto. Questo è essenzialmente ciò che fa un incrocio, ma la logica è in realtà letterale.

Quindi utilizzare lo aggregation framework per concatenare le query corrispondenti. Per un semplice esempio, prendere in considerazione i seguenti documenti:

{ "loc" : { "type" : "Point", "coordinates" : [ 4, 4 ] } } 
{ "loc" : { "type" : "Point", "coordinates" : [ 8, 8 ] } } 
{ "loc" : { "type" : "Point", "coordinates" : [ 12, 12 ] } } 

e il gasdotto di aggregazione incatenato, solo due domande:

db.geotest.aggregate([ 
    { "$match": { 
     "loc": { 
      "$geoWithin": { 
       "$box": [ [0,0], [10,10] ] 
      } 
     } 
    }}, 
    { "$match": { 
     "loc": { 
      "$geoWithin": { 
       "$box": [ [5,5], [20,20] ] 
      } 
     } 
    }} 
]) 

Quindi, se si considera che logicamente, il primo risultato sarà trovare i punti che cadono entro i limiti della casella iniziale o dei primi due elementi. Questi risultati vengono quindi interpretati dalla seconda query e poiché i nuovi limiti di casella iniziano da [5,5] che esclude il primo punto. Il terzo punto era già stato escluso, ma se le restrizioni sulla scatola fossero state invertite, il risultato sarebbe stato lo stesso solo per il documento centrale.

Come funziona in modo del tutto unico per l'operatore $geoWithin interrogazione rispetto a varie altre funzioni di geo:

$geoWithin non richiede un indice geospaziale.Tuttavia, un indice geospaziale migliorerà le prestazioni della query. Entrambi gli indici geospaziali supportati da 2dsphere e 2d supportano $geoWithin.

Quindi i risultati sono buoni e cattivi. In questo modo è possibile eseguire questo tipo di operazione senza un indice, ma è negativo perché una volta che la pipeline di aggregazione ha modificato i risultati della raccolta dopo la prima operazione di query, non è possibile utilizzare altri indici. Pertanto, qualsiasi vantaggio in termini di prestazioni di un indice viene perso in seguito alla fusione dei risultati "impostati" da qualsiasi elemento dopo l'iniziale Polygon/MultiPolygon come supportato.


Per questo motivo mi raccomando ancora che si calcolano i limiti di intersezione "al di fuori" della query rilasciato a MongoDB. Anche se il framework di aggregazione può fare ciò a causa della natura "concatenata" della pipeline e anche se le intersezioni risultanti diventeranno sempre più piccole, la migliore prestazione è una singola query con i limiti corretti che può utilizzare tutti i vantaggi dell'indice.

Ci sono vari metodi per farlo, ma per riferimento qui è un'implementazione utilizzando la libreria JSTS, che è una porta JavaScript della famosa libreria JTS per Java. Ci possono essere altri o altre porte lingua, ma questo ha semplice analisi GeoJSON e costruito in metodi per tali cose come ottenere i limiti di intersezione:

var async = require('async'); 
    util = require('util'), 
    jsts = require('jsts'), 
    mongo = require('mongodb'), 
    MongoClient = mongo.MongoClient; 

var parser = new jsts.io.GeoJSONParser(); 

var polys= [ 
    { 
    type: 'Polygon', 
    coordinates: [[ 
     [ 0, 0 ], [ 0, 10 ], [ 10, 10 ], [ 10, 0 ], [ 0, 0 ] 
    ]] 
    }, 
    { 
    type: 'Polygon', 
    coordinates: [[ 
     [ 5, 5 ], [ 5, 20 ], [ 20, 20 ], [ 20, 5 ], [ 5, 5 ] 
    ]] 
    } 
]; 

var points = [ 
    { type: 'Point', coordinates: [ 4, 4 ] }, 
    { type: 'Point', coordinates: [ 8, 8 ] }, 
    { type: 'Point', coordinates: [ 12, 12 ] } 
]; 

MongoClient.connect('mongodb://localhost/test',function(err,db) { 

    db.collection('geotest',function(err,geo) { 

    if (err) throw err; 

    async.series(
     [ 
     // Insert some data 
     function(callback) { 
      var bulk = geo.initializeOrderedBulkOp(); 
      bulk.find({}).remove(); 
      async.each(points,function(point,callback) { 
      bulk.insert({ "loc": point }); 
      callback(); 
      },function(err) { 
      bulk.execute(callback); 
      }); 
     }, 

     // Run each version of the query 
     function(callback) { 
      async.parallel(
      [ 
       // Aggregation 
       function(callback) { 
       var pipeline = []; 
       polys.forEach(function(poly) { 
        pipeline.push({ 
        "$match": { 
         "loc": { 
         "$geoWithin": { 
          "$geometry": poly 
         } 
         } 
        } 
        }); 
       }); 

       geo.aggregate(pipeline,callback); 
       }, 

       // Using external set resolution 
       function(callback) { 
       var geos = polys.map(function(poly) { 
        return parser.read(poly); 
       }); 

       var bounds = geos[0]; 

       for (var x=1; x<geos.length; x++) { 
        bounds = bounds.intersection(geos[x]); 
       } 

       var coords = parser.write(bounds); 

       geo.find({ 
        "loc": { 
        "$geoWithin": { 
         "$geometry": coords 
        } 
        } 
       }).toArray(callback); 
       } 
      ], 
      callback 
     ); 
     } 
     ], 
     function(err,results) { 
     if (err) throw err; 
     console.log(
      util.inspect(results.slice(-1), false, 12, true)); 
     db.close(); 
     } 
    ); 

    }); 

}); 

Utilizzando la piena GeoJSON "Poligono" rappresentazioni là come questo si traduce in ciò che JTS può capire e lavorare con È probabile che qualsiasi input che potresti ricevere per un'applicazione reale sia in questo formato, piuttosto che applicare comodità come $box.

Così può essere fatto con il framework di aggregazione, o anche con le query parallele che uniscono il "set" dei risultati. Ma mentre il framework di aggregazione può fare meglio di unire all'esterno un insieme di risultati, i risultati migliori verranno sempre dal calcolo dei limiti.

+1

Grande! tuttavia, è necessario un po 'di tempo per elaborare la soluzione ... sembra promettente: D – Viraj

+0

L'utilizzo dell'aggregazione a due fasi sembra il migliore in questo scenario – Viraj

0

Nel caso in cui qualcun altro lo guardi, come nella versione 2.4 di mongo, è possibile utilizzare $geoIntersects per trovare l'intersezione di oggetti GeoJSON, che supporta le intersezioni di due poligoni, tra gli altri tipi.

{ 
    <location field>: { 
    $geoIntersects: { 
     $geometry: { 
      type: "<GeoJSON object type>" , 
      coordinates: [ <coordinates> ] 
     } 
    } 
    } 
} 

C'è una bella scrittura on this blog.

+0

Beh, no. La domanda chiede di inviare "due o più poligoni" e vedere se "quelle" forme "insertsect" e contenere "punti" trovati all'interno della "intersezione di quel risultato". '$ geoInsersects' serve per cercare" usando "un oggetto geometrico e trovare se qualche" oggetto geometrico "nella" collezione "in realtà" interseca un limite "sull'oggetto emesso nella query. Queste sono due cose "completamente" diverse. –