Die Daten von Geonames können in einem wunderbaren CSV-Format länderweise heruntergeladen werden. Der Import der Daten in mongo ist wirklich einfach. Zuerst benötigen wir eine Header-Datei, in der die Spalten definiert sind (siehe dazu das entsprechende readme).
cat header.txt
id name asciiname alternatenames latitude longitude featureclass featurecode countrycode cc2 admin1 admin2 admin3 admin4 population elevation dem timezone modification
Die Felder müssen natürlich mit Tabulatoren getrennt sein, wie in der von mir heruntergeladenen AT.txt Datei mit den Geonames Einträgen für Österreich. Anschließend den Header und die Daten zusammen kopieren und mit mongoimport
in die Datenbank einspielen.
cat header.txt AT.txt >> AT_header.txt
mongoimport --db geonames --collection snowhow -type tsv --file AT_header.txt --headerline
mongo geonames
db.snowhow.find({ "name": "Sattelspitze" })
{ "_id" : ObjectId("502d20427529fd3eebcaf58d"), "id" : 6939577, "name" : "Sattelspitze", "asciiname" : "Sattelspitze", "alternatenames" : "", "latitude" : 47.33778, "longitude" : 9.77, "featureclass" : "T", "featurecode" : "PK", "countrycode" : "AT", "cc2" : "", "admin1" : 8, "admin2" : "", "admin3" : "", "admin4" : "", "population" : 0, "elevation" : 1562, "dem" : 1541, "timezone" : "Europe/Vienna", "modification" : "2009-03-19" }
Extrem cool. Aber leider ist so kein 2D-Index auf die Felder longitude
und latitude
möglich. Ein bisschen Googlen bestätigte die Vermutung, dass mongoimport
keine Möglichkeit bietet, Geopositionen so zu erkennen. Als schnellste Möglichkeit für kleine Datensätze exportierte ich die Collection im JSON Format und korrigierte die Positionen mit sed
. Ein mögliches Suchen-Ersetzen Kommando mit sed dazu lautet wie folgt:
sed -e 's/"latitude" : ([0-9.]+), "longitude" : ([0-9.]+),/loc: { "lon" : 2 , "lat" : 1 },/'
< at.json > at_loc.json
Jetzt wird die collection gelöscht und die neue, konvertierte JSON-Datei in die Datenbank eingespielt:
mongo geonames
db.snowhow.remove()
exit
mongoimport --db geonames --collection snowhow --file at_loc.json
Abschließend noch den Geospatial-Index erzeugen, dann können räumliche Abfragen durchgeführt werden:
db.snowhow.ensureIndex( { loc: "2d" } );
db.snowhow.find( { loc : { $nearSphere : [9.77, 47.33778] } }, {name: true, loc: true}).limit(5)
{ "_id" : ObjectId("502d20427529fd3eebcaf58d"), "name" : "Sattelspitze", "loc" : { "lon" : 9.77, "lat" : 47.33778 } }
{ "_id" : ObjectId("502d20417529fd3eebcacf19"), "name" : "Alpkopf", "loc" : { "lon" : 9.76667, "lat" : 47.33306 } }
{ "_id" : ObjectId("502d20427529fd3eebcaf578"), "name" : "Salzbödenkopf", "loc" : { "lon" : 9.79361, "lat" : 47.33056 } }
{ "_id" : ObjectId("502d20417529fd3eebcac667"), "name" : "Ebnit", "loc" : { "lon" : 9.75, "lat" : 47.35 } }
{ "_id" : ObjectId("502d20427529fd3eebcaf55a"), "name" : "Hörnle", "loc" : { "lon" : 9.74056, "lat" : 47.31944 } }
Update
Zugegeben, wesentlich elaganter ist der hier (Update 2017: Link zu laulima.com funktioniert leider nicht mehr) vorgeschlagene Weg: Nach dem importieren der Daten wird die Struktur direkt in mongo umgebaut:
db.snowhow.find().forEach(function(data) {
db.snowhow.update({_id:data._id},{$set:{loc:{lng:parseFloat(data.longitude),lat:parseFloat(data.latitude)}}});
})
Update 2012 / 2013
Und hier die Variante um nach dem Import mit mongoimport
GeoJSON Punkte anzulegen:
db.geonames.find().forEach(function(data) {
db.geonames.update( { _id: data._id }, {
$set: { loc: { type: "Point",
coordinates: [parseFloat(data.longitude), parseFloat(data.latitude)] } }
})
})