Home mongoDB MongoDB 2.4 ed errore “Can’t extract geo keys from object, malformed geometry?”

MongoDB 2.4 ed errore “Can’t extract geo keys from object, malformed geometry?”

10 min di lettura
3
0
155
Cattura77

In questi giorni ho riscontrato un problema con mongoDB versione 2.4 ed i nuovi oggetti geometrici ovvero con l’indice 2dsphere e l’oggetto geometrico “LineString“.

Avevo parlato di mongoDB 2.4 e nuove funzionalità sugli oggetti geometrici in questo post.

Descrizione del problema

In locale prendo le coordinate gps e le invio poi ad un rest webservice node.js che fa la insert in mongoDB delle coordinate sotto forma di linestring (spezzata), strutturate secondo le specifiche mongoDB 2.4 per gli oggetti geometrici ovvero

1
2
3
{ loc : { type : "LineString" ,
          coordinates : [ [12.0991736,42.4245143],[12.0991226,42.42456500000001] ]
} }

Il problema è che a volte la insert va a buon fine altre volte ritorna il seguente errore:

Can’t extract geo keys from object, malformed geometry?

Sembrerebbe che mongoDB non riconosca i valori inseriti come coordinate corrette o comunque non riconosca l’oggetto “LineString”.

Ho cercato un pochino sul web ma non ho trovato nulla, probabilmente è troppo “giovane” la funzionalità spaziale con il nuovo indice 2dsphere per avere sufficiente letteratura.

Il sorgente mongoDB

Sono quindi arrivato al sorgente di mongoDB per cercare di capire qualcosa.

https://github.com/mongodb/mongo/blob/master/src/mongo/db/geo/s2index.cpp

alla riga 281 (del sorgente git) c’è la stringa dell’errore che mi esce quando faccio la “insert”.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// We only support GeoJSON polygons.  Why?:
// 1. we don't automagically do WGS84/flat -> WGS84, and
// 2. the old polygon format must die.
if (GeoParser::isGeoJSONPolygon(obj)) {
	S2Polygon polygon;
	GeoParser::parseGeoJSONPolygon(obj, &polygon);
	keysFromRegion(&coverer, polygon, &cells);
} else if (GeoParser::parseLineString(obj, &line)) {
	keysFromRegion(&coverer, line, &cells);
} else if (GeoParser::parsePoint(obj, &point)) {
	S2CellId parent(point.id().parent(_params.finestIndexedLevel));
	cells.push_back(parent.toString());
} else {
	uasserted(16572, "<span style="color: #ff0000;">Can't extract geo keys from object, malformed geometry?</span>:"
					 + obj.toString());
}

Il metodo è il seguente:

1
GeoParser::parseLineString(obj, &amp;line)

che si trova in

https://github.com/mongodb/mongo/blob/master/src/mongo/db/geo/geoparser.cpp

1
2
3
4
5
bool GeoParser::parseLineString(const BSONObj &amp;obj, S2Polyline *out) {
	if (!isGeoJSONLineString(obj)) { return false; }
	parseGeoJSONLineString(obj, out);
	return true;
}

che a sua volta richiama “isGeoJSONLineString

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
bool GeoParser::isGeoJSONLineString(const BSONObj&amp; obj) {
	BSONElement type = obj.getFieldDotted(GEOJSON_TYPE);
	if (type.eoo() || (String != type.type())) { return false; }
	if (GEOJSON_TYPE_LINESTRING != type.String()) { return false; }
 
	if (!crsIsOK(obj)) {
		warning() &lt;&lt; "Invalid CRS: " &lt;&lt; obj.toString() &lt;&lt; endl;
		return false;
	}
 
	BSONElement coordElt = obj.getFieldDotted(GEOJSON_COORDINATES);
	if (coordElt.eoo() || (Array != coordElt.type())) { return false; }
 
	const vector&amp; coordinateArray = coordElt.Array();
	if (coordinateArray.size() &lt; 2) { return false; }
	if (!isArrayOfCoordinates(coordinateArray)) { return false; }
	vector vertices;
	parsePoints(obj.getFieldDotted(GEOJSON_COORDINATES).Array(), &amp;vertices);
	return S2Polyline::IsValid(vertices);
}

Analizzando questo metodo si nota subito come ci siano moltissimi controlli sulla correttezza della “LineString” ossia dell’insieme di punti che compongono la spezzata.

Questo un esempio di json reale che ritorna l’errore

1
2
3
{ loc : { type : "LineString" ,
          coordinates : [ [12.0991736,42.4245143],[12.0991226,42.42456500000001],[12.0991525,42.42452249999999],[12.0991435,42.4244968],[12.0991489,42.4245177],[12.0991439,42.4245229],[12.0991472,42.4245192],[12.0991503,42.42452180000001],[12.0991484,42.4245192],[12.0991351,42.4244986],[12.099134,42.4244971],[12.0991409,42.4245173],[12.0991409,42.4245173],[12.0991423,42.424527],[12.0991347,42.4244803],[12.0991462,42.42451519999999],[12.0991441,42.4245188],[12.0991435,42.4245191],[12.0991442,42.42451519999999],[12.0991452,42.424515],[12.0991433,42.4245182],[12.0991429,42.4245156],[12.0991463,42.424517],[12.0991086,42.4245464] ]
} }

I punti sono, o almeno sembrano, essere corretti. Sono espressi in coordinate standard WGS84 lette direttamente dal GPS e correttamente strutturati secondo lo standard mongoDB 2.4.

Dove sta quindi l’errore?

Dopo molto “spippolamento” e confrontandomi con i ragazzi della community G+ “Italian Developers” sono arrivato a capire che per mongoDB quell’insieme di coordinate non è una “LineString” corretta perché ad un certo punto dell’array ci sono due coppie di coordinate uguali.

Queste le due coppie incriminate:

1
[12.0991409,42.4245173],[12.0991409,42.4245173]

Le coordinate, ovvio, me le ha date il GPS per cui il problema potrebbe essere molto più frequente del previsto qualora si tracciassero coordinate stando fermi.

La soluzione

Per risolvere basta verificare che una qualsiasi coppia n-1 sia diversa dalla coppia n, nel caso siano uguali non inserire nell’array la seconda coppia n ( o togliere la n-1 già inserita ).

Ho verificato che il problema si verifichi solo per le coppie contigue, se ci sono coppie uguali non contigue il problema non si presenta.

Il controllo è fatto dal seguente metodo (generico per ogni polilinea):

1
S2Polyline::isValid(vertices)

Una polyline in coordinate sferiche non ammette archi di lunghezza zero e orientamento 180°, che si traduce in due punti consecutivi sovrapposti.

Questo il codice :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
bool S2Polyline::IsValid(vector const&amp; v) {
  // All vertices must be unit length.
  int n = v.size();
  for (int i = 0; i &lt; n; ++i) {
    if (!S2::IsUnitLength(v[i])) {
      S2LOG(INFO) &lt;&lt; "Vertex " &lt;&lt; i &lt;&lt; " is not unit length";
      return false;
    }
  }
 
  // Adjacent vertices must not be identical or antipodal.
  for (int i = 1; i &lt; n; ++i) {
    if (v[i-1] == v[i] || v[i-1] == -v[i]) {
      S2LOG(INFO) &lt;&lt; "Vertices " &lt;&lt; (i - 1) &lt;&lt; " and " &lt;&lt; i
                &lt;&lt; " are identical or antipodal";
      return false;
    }
  }
  return true;
}

Come si vede è espressamente commentato che “Vertici adiacenti non devono essere identici o agli antipodi

Conclusioni

Va benissimo l’errore perché effettivamente da specifiche la linestring non era corretta, però perché non dettagliare meglio il messaggio?

C’è una bella differenza tra :

“Can’t extract geo keys from object, malformed geometry?”

e

“Adjacent vertices must not be identical or antipodal”

Magari mi sarei risparmiato tutto questo tempo per cercare di risolvere il problema.

Ho pensato di farci un post perché potrebbe capitare anche ad altri sviluppatori non appena la nuova release di mongoDB 2.4 diverrà ufficiale sulle varie piattafome cloud.

Che te ne pare dell’articolo? Ti era mai capitata una cosa simile? Pensi che ti potrebbe essere di aiuto?

Ti è piaciuto il mio articolo?

Ricevi tutte le novità dell'etrusco direttamente nella tua casella di posta

Non preoccuparti, non faccio spam!

è Solution Architect e Full Stack Developer specializzato nell’analisi, progettazione e realizzazione di sistemi complessi in ambito Transportation. Dal 2007 è anche blogger con il nickname "etrusco" che ha associato alla sua brand identity; cura personalmente i contenuti di molti altri blog, website e forum. Ha inoltre realizzato molte webapp e siti di ecommerce. La passione per internet, per i nuovi media e per la tecnologia in generale gli permettono di stare sempre al passo con i nuovi linguaggi e le più innovative metodologie di programmazione. Adora sua moglie, le sue due figlie, la fotografia analogica e questo blog.

Carica più articoli correlati
Carica più per Alessandro De Marchi
Carica più in mongoDB

3 Commenti

  1. stefano

    30 giugno 2014 a 02:47

    omioddio!

    sono praticamente settimane che non riuscito a capire da dove cacchio uscira fuori quel problema!! su alcune banalissime linestring che avevo gia importato e sulle quali stavo cercando di applicare un indiche che ovviamente dava sempre QUEL MALEDETTO MESAGGIO INCOMPRENSIBILE!!

    GRAZIE GRAZIE GRAZIE!!
    se capito a Viterbo di devo come minimo ofrire una Birra!!
    Stefano

  2. Alessandro De Marchi

    30 giugno 2014 a 10:46

    Felice di aver aiutato qualcuno …
    ciao

    PS: ti aspetto a Viterbo!

  3. stefano

    30 giugno 2014 a 12:12

    ciao!

    a quanto pare c’è qualche cos’altro che da errore durante la creazione degli indici spaziali, leggi qui:
    https://groups.google.com/forum/?hl=it&fromgroups#!topic/mongodb-it/L7mTFtOAbXc

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *

Guarda anche

L’ inbound marketing spiegato a mio zio (imprenditore)

L’ Inbound marketing spiegato a mio zio (imprenditore) Se sei un imprenditore e le m…