2016-04-15 28 views
6

che sto cercando di sviluppare una visualizzazione basata su mappe che include una "mappa di calore" di sottopopolazioni, sulla base di una collezione MongoDB che contiene documenti come questo:MongoDB: documenti a grappolo per area geografica e per punti max?

{ 
    "PlaceName" : "Boston", 
    "Location" : { 
     "type" : "Point", 
     "coordinates" : [ 42.358056, -71.063611 ] 
    }, 
    "Subpopulations": { 
     "Age": { 
       "0_4" : 37122, 
       "6_11" : 33167, 
       "12_17" : 35464, 
       "18_24" : 130885, 
       "25_34" : 127058, 
       "34_44" : 79092, 
       "45_54" : 72076, 
       "55_64" : 59766, 
       "65_74" : 33997, 
       "75_84" : 20219, 
       "85_" : 9057 
     } 
    } 
} 

ci sono centinaia di migliaia di singole posizioni in il database. Fanno non si sovrappongono - cioè non ci sarebbero due voci individuali per "New York City" e "Manhattan".

L'obiettivo è utilizzare Leaflet.js e alcuni plugin per rendere varie visualizzazioni di questi dati. Il volantino è abbastanza buono per i dati di clustering sul lato client - quindi se l'ho passato a migliaia di posizioni con valori di densità, potrebbe rendere una mappa di calore dell'area pertinente semplicemente sgranocchiando tutti i singoli valori.

Il problema è, ad esempio, eseguire lo zoom indietro sulla mappa per mostrare il mondo intero. Sarebbe orribilmente inefficiente, se non impossibile, inviare tutti quei dati al client e fare in modo che le informazioni vengano elaborate abbastanza velocemente da rendere la visualizzazione più omogenea.

Quindi quello che devo fare è raggruppare automaticamente il lato server dati, che spero possa essere fatto in una query MongoDB. Ho letto che geohashing potrebbe essere un buon punto di partenza per determinare quali punti appartengono a quali cluster, ma sono sicuro che qualcuno ha già fatto questa cosa esatta prima e potrebbe avere una visione migliore di quella. Idealmente mi piacerebbe mandare una query per il mio script node.js che assomiglia a questo:

http://myserver.com/popdata?top=42.48&left=-80.57&bottom=37.42&right=-62.55&stat=Age&value=6_11 

che sarebbe determinare come granulare il raggruppamento deve essere in base a quanti singoli punti sono all'interno di tale area geografica specificata , dato un numero massimo di punti dati da restituire, o qualcosa di simile; e sarebbe restituire i dati in questo modo:

[ 
    { "clusterlocation": [ 42.304, -72.622 ], "total_age_6_11": 59042 }, 
    { "clusterlocation": [ 36.255, -64.124 ], "total_age_6_11": 7941 }, 
    { "clusterlocation": [ 40.425, -70.693 ], "total_age_6_11": 90257 }, 
    { "clusterlocation": [ 39.773, -67.992 ], "total_age_6_11": 102752 }, 
    ... 
] 

... dove "clusterlocation" è qualcosa di simile alla media di tutte le posizioni di documenti in cluster, e "total_age_6_11" è la somma dei valori tali documenti per "Subpopulations.Age.6_11".

È qualcosa che posso fare puramente in una query Mongo? Esiste un modo "provato e testato" per farlo bene?

+1

Questo sarebbe difficile su un solo dati grezzi senza un concetto preallocato di "clustering" sia con "attrbutes" aggiuntivi, sia semplicemente pre-aggregando ad altri dati di raccolta granulari al "livello di zoom". Il problema di base che vedo per la gestione di una singola query è che mentre si potrebbe usare '$ geoNear' per determinare la prossimità a un punto centrale (diciamo il centro della selezione dell'area), questo" sarebbe "per dare la distanza da quel punto a" cluster " "on, tuttavia, non tiene conto della vicinanza dei punti a se stessi. Quindi in pratica dovresti "iterare" i dati dei punti per trovare il più vicino a ciascuno. –

+1

TLDR di sopra è, * "senza assegnazione di cluster precalcolata, questo non è molto performante" *. –

risposta

4

Anche se si esegue questa query in fase di esecuzione, sarà inefficiente e non veloce da considerarsi una buona interfaccia utente. Vorrei suggerire gruppi pregenerati di dimensioni specifiche e tenerli memorizzati nella tua raccolta corrente insieme ai documenti originali. Ecco come:

  • Ogni documento verrà memorizzato un campo aggiuntivo (consente di chiamare geolevel), che denotano quanto piccolo o entità è grande. I vostri documenti di base avranno geolevel = 1:

    { 
        "PlaceName" : "Boston", 
        "Location" : { 
         "type" : "Point", 
         "coordinates" : [ 42.358056, -71.063611 ] 
        }, 
        "Subpopulations": { 
         "Age": { 
           "0_4" : 37122, 
           "6_11" : 33167, 
           "12_17" : 35464, 
           "18_24" : 130885, 
           "25_34" : 127058, 
           "34_44" : 79092, 
           "45_54" : 72076, 
           "55_64" : 59766, 
           "65_74" : 33997, 
           "75_84" : 20219, 
           "85_" : 9057 
         } 
        }, 
        "geolevel":1 // added geolevel 
    } 
    
    • È possibile eseguire l'elaborazione sul database di controllare la validità generare documenti simili per i cluster, e per più livelli. ad es. geolevel: 2 sarà un cluster di poche città nel raggio di 250 km, geolevel: 3 sarà il cluster di geolevel: 2 cluster.

    • È inoltre possibile memorizzare un campo come memberids per memorizzare ID di bambini in per ciascun cluster.Questo potrebbe essere necessario per evitare che un'entità entri in due cluster adiacenti, può essere assegnata a uno qualsiasi dei cluster adiacenti e la visualizzazione funzionerà comunque bene. A geolevel: 2 grappolo doc sarà simile:

      { 
          "PlaceName" : "cluster_sdfs34535", // The id can be generated from hash like sha of a list of all children ids. 
          "Location" : { // center of the cluster 
           "type" : "Point", 
           "coordinates" : [ 42.358056, -71.063611 ] 
          }, 
          "Subpopulations": { // total population of the cluster 
           "Age": { 
             "0_4" : 371220, 
             "6_11" : 331670, 
             "12_17" : 354640, 
             "18_24" : 1308850, 
             "25_34" : 1270580, 
             "34_44" : 790920, 
             "45_54" : 720760, 
             "55_64" : 597660, 
             "65_74" : 339970, 
             "75_84" : 202190, 
             "85_" : 90570 
           } 
          }, 
          "geolevel":2 , 
          "childs":[4,5,6,7] // ids of child documents 
      } 
      
    • Ora la vostra visualizzazione applicazione ha bisogno di fare una mappatura delle zoomlevel a geolevel, e sulla base che si seleziona i documenti. Per la visualizzazione a livello di città, è possibile interrogare per geolevel: 1 documenti, e come si zoom indietro per stato, paese, ecc è possibile aumentare la geolevel a 2,3 ...
+0

Mi piace molto l'approccio di costruire una gerarchia in questo modo. Sembra un compito abbastanza semplice costruire un meccanismo per generare questi documenti. Molto apprezzato. – DanM