While building a food truck service using which a user can browse and search for food options in a given area, I had to work with geographic latitude/longitude information. I was using MongoDB as my data store which supports geospatial queries. A sample document in my foodtruck collection looks like this:
> db.foodtrucks.find().pretty().limit(1);
{
"_id" : ObjectId("53d07bf1e3e11dacc2105697"),
"locationid" : 559777,
"Applicant" : "Mang Hang Catering",
"FacilityType" : "Truck",
"cnn" : 131000,
"LocationDescription" : "02ND ST: JESSIE ST to MISSION ST (69 - 99)",
"Address" : "85 02ND ST",
"blocklot" : 3708019,
"block" : 3708,
"lot" : 19,
"permit" : "14MFF-0109",
"Status" : "REQUESTED",
"FoodItems" : "COLD TRUCK. Deli: bbq chicken skewer: Chinese spring roll: Chinese fried rice/noodle: fried chicken leg/wing: bbq chicken sandwich: chicken cheese burger: burrito: lumpia. Snack: sunflower seeds: muffins: chips: snickers: kit-kat: 10 types of chocolate. Drinks: Coke: 7-Up: Dr. Pepper: Pepsi: Redbull: Vitamin Water: Rockstar: Coconut Juice: Water. Hot drinks: coffee: tea.",
"X" : 6012696.635,
"Y" : 2115129.488,
"Latitude" : 37.7884570288289,
"Longitude" : -122.399884160566,
"Schedule" : "http://bsm.sfdpw.org/PermitsTracker/reports/report.aspx?title=schedule&report=rptSchedule¶ms=permit=14MFF-0109&ExportPDF=1&Filename=14MFF-0109_schedule.pdf",
"NOISent" : "",
"Approved" : "",
"Received" : "Jul 10 2014 2:56PM",
"PriorPermit" : 1,
"ExpirationDate" : "03/15/2015 12:00:00 AM",
"Location" : "(37.7884570426015, -122.399884160556)",
}
Before I could run geospatial queries on my collection, I needed to make sure the latitude/longitude elements were stored in the document's field in a certain format - either as a map in the format "pos:{Longitude: -122.399884, Latitude: 37.7884570}" or an array of two elements in the format "pos:[-122.399884, 37.7884570]". For my implementation I went with the map option. Here are the steps I took to accomplish this:
i. Adding coordinate entry into all the documents of the collection
Next step was to add an entry with the coordinate information into each of the documents in the collection. MongoDB supports scripts for the mongo shell to manipulate data in the database. I wrote a small javascript module to do this for me. Here it is:
XDB = db.foodtrucks;
var iter = XDB.find()
do {
var doc = iter.next();
var newdoc = {};
newdoc.pos = [];
var newpair = {Longitude: doc.Longitude,Latitude: doc.Latitude} ;
newdoc.pos.push(newpair);
XDB.update({_id: doc._id},{$set:{pos: newpair}});
} while ( iter.hasNext() );
var iter = XDB.find()
do {
var doc = iter.next();
var newdoc = {};
newdoc.pos = [];
var newpair = {Longitude: doc.Longitude,Latitude: doc.Latitude} ;
newdoc.pos.push(newpair);
XDB.update({_id: doc._id},{$set:{pos: newpair}});
} while ( iter.hasNext() );
To execute this run this command in your shell: mongo localhost:27017/testdb <name of your javascript file>
> db.foodtrucks.find().pretty().limit(1);
{
"_id" : ObjectId("53d07bf1e3e11dacc2105697"),
"locationid" : 559777,
"Applicant" : "Mang Hang Catering",
"FacilityType" : "Truck",
"cnn" : 131000,
"LocationDescription" : "02ND ST: JESSIE ST to MISSION ST (69 - 99)",
"Address" : "85 02ND ST",
"blocklot" : 3708019,
"block" : 3708,
"lot" : 19,
"permit" : "14MFF-0109",
"Status" : "REQUESTED",
"FoodItems" : "COLD TRUCK. Deli: bbq chicken skewer: Chinese spring roll: Chinese fried rice/noodle: fried chicken leg/wing: bbq chicken sandwich: chicken cheese burger: burrito: lumpia. Snack: sunflower seeds: muffins: chips: snickers: kit-kat: 10 types of chocolate. Drinks: Coke: 7-Up: Dr. Pepper: Pepsi: Redbull: Vitamin Water: Rockstar: Coconut Juice: Water. Hot drinks: coffee: tea.",
"X" : 6012696.635,
"Y" : 2115129.488,
"Latitude" : 37.7884570288289,
"Longitude" : -122.399884160566,
"Schedule" : "http://bsm.sfdpw.org/PermitsTracker/reports/report.aspx?title=schedule&report=rptSchedule¶ms=permit=14MFF-0109&ExportPDF=1&Filename=14MFF-0109_schedule.pdf",
"NOISent" : "",
"Approved" : "",
"Received" : "Jul 10 2014 2:56PM",
"PriorPermit" : 1,
"ExpirationDate" : "03/15/2015 12:00:00 AM",
"Location" : "(37.7884570426015, -122.399884160556)",
"pos" : {
"Longitude" : -122.399884160566,
"Latitude" : 37.7884570288289
}
}
{
"_id" : ObjectId("53d07bf1e3e11dacc2105697"),
"locationid" : 559777,
"Applicant" : "Mang Hang Catering",
"FacilityType" : "Truck",
"cnn" : 131000,
"LocationDescription" : "02ND ST: JESSIE ST to MISSION ST (69 - 99)",
"Address" : "85 02ND ST",
"blocklot" : 3708019,
"block" : 3708,
"lot" : 19,
"permit" : "14MFF-0109",
"Status" : "REQUESTED",
"FoodItems" : "COLD TRUCK. Deli: bbq chicken skewer: Chinese spring roll: Chinese fried rice/noodle: fried chicken leg/wing: bbq chicken sandwich: chicken cheese burger: burrito: lumpia. Snack: sunflower seeds: muffins: chips: snickers: kit-kat: 10 types of chocolate. Drinks: Coke: 7-Up: Dr. Pepper: Pepsi: Redbull: Vitamin Water: Rockstar: Coconut Juice: Water. Hot drinks: coffee: tea.",
"X" : 6012696.635,
"Y" : 2115129.488,
"Latitude" : 37.7884570288289,
"Longitude" : -122.399884160566,
"Schedule" : "http://bsm.sfdpw.org/PermitsTracker/reports/report.aspx?title=schedule&report=rptSchedule¶ms=permit=14MFF-0109&ExportPDF=1&Filename=14MFF-0109_schedule.pdf",
"NOISent" : "",
"Approved" : "",
"Received" : "Jul 10 2014 2:56PM",
"PriorPermit" : 1,
"ExpirationDate" : "03/15/2015 12:00:00 AM",
"Location" : "(37.7884570426015, -122.399884160556)",
"pos" : {
"Longitude" : -122.399884160566,
"Latitude" : 37.7884570288289
}
}
The "pos" entry has now been added to the document in the collection (in red).
ii. Executing the query
Before writing any queries I had to create a geospatial index on the "pos" field using the command below:
> db.foodtrucks.ensureIndex({pos:"2d"})
Now we can start writing queries on this collection. For ex- if we want to find the closest foodtrucks to the position which is passed in, we would write something as follows:
> db.foodtrucks.find({pos :{$near:[-122,37]}}).pretty().limit(1)
{
"_id" : ObjectId("53d07bf1e3e11dacc210579f"),
"locationid" : 528583,
"Applicant" : "Golden Catering",
"FacilityType" : "Truck",
"cnn" : 5374000,
"LocationDescription" : "EXECUTIVE PARK BLVD: CRESCENT WAY to HARNEY WAY (300 - 399)",
"Address" : "301 EXECUTIVE PARK BLVD",
"blocklot" : 4991240,
"block" : 4991,
"lot" : 240,
"permit" : "14MFF-0072",
"Status" : "APPROVED",
"FoodItems" : "Cold Truck: Pre-packaged Sandwiches: Various Beverages: Salads: Snacks",
"X" : 6014982.64302,
"Y" : 2087104.0558,
"Latitude" : 37.7116322811949,
"Longitude" : -122.390016333636,
"Schedule" : "http://bsm.sfdpw.org/PermitsTracker/reports/report.aspx?title=schedule&report=rptSchedule¶ms=permit=14MFF-0072&ExportPDF=1&Filename=14MFF-0072_schedule.pdf",
"NOISent" : "",
"Approved" : "03/21/2014 01:52:04 PM",
"Received" : "Mar 21 2014 1:42PM",
"PriorPermit" : 1,
"ExpirationDate" : "03/15/2015 12:00:00 AM",
"Location" : "(37.711632295042, -122.390016333627)",
"pos" : {
"Longitude" : -122.390016333636,
"Latitude" : 37.7116322811949
}
}
If we want more choices, we just change the value in the limit to the number of values we want returned. MongoDB finds the places closest to the position provided and returns them in a sorted order by distance.
> db.foodtrucks.find({pos :{$near:[-122,37]}}).pretty().limit(1)
{
"_id" : ObjectId("53d07bf1e3e11dacc210579f"),
"locationid" : 528583,
"Applicant" : "Golden Catering",
"FacilityType" : "Truck",
"cnn" : 5374000,
"LocationDescription" : "EXECUTIVE PARK BLVD: CRESCENT WAY to HARNEY WAY (300 - 399)",
"Address" : "301 EXECUTIVE PARK BLVD",
"blocklot" : 4991240,
"block" : 4991,
"lot" : 240,
"permit" : "14MFF-0072",
"Status" : "APPROVED",
"FoodItems" : "Cold Truck: Pre-packaged Sandwiches: Various Beverages: Salads: Snacks",
"X" : 6014982.64302,
"Y" : 2087104.0558,
"Latitude" : 37.7116322811949,
"Longitude" : -122.390016333636,
"Schedule" : "http://bsm.sfdpw.org/PermitsTracker/reports/report.aspx?title=schedule&report=rptSchedule¶ms=permit=14MFF-0072&ExportPDF=1&Filename=14MFF-0072_schedule.pdf",
"NOISent" : "",
"Approved" : "03/21/2014 01:52:04 PM",
"Received" : "Mar 21 2014 1:42PM",
"PriorPermit" : 1,
"ExpirationDate" : "03/15/2015 12:00:00 AM",
"Location" : "(37.711632295042, -122.390016333627)",
"pos" : {
"Longitude" : -122.390016333636,
"Latitude" : 37.7116322811949
}
}
If we want more choices, we just change the value in the limit to the number of values we want returned. MongoDB finds the places closest to the position provided and returns them in a sorted order by distance.
Comments
Post a Comment