Searching OSM nodes in Spatialite
Third step of my SoTM10 pet project: finding the POIs.
I put together a query to find all nodes with a given tag inside a bounding box, and also a query to find all the tag values for a given tag name inside a bounding box.
The result is this simple POI search engine:
# # poisearch - simple geographical POI search engine # # Copyright (C) 2010 Enrico Zini <enrico@enricozini.org> # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # from pysqlite2 import dbapi2 as sqlite class PoiDB(object): def __init__(self): self.db = sqlite.connect("pois.db") self.db.enable_load_extension(True) self.db.execute("SELECT load_extension('libspatialite.so')") self.oldsearch = [] self.bbox = None def set_bbox(self, xmin, xmax, ymin, ymax): '''Set bbox for searches''' self.bbox = (xmin, xmax, ymin, ymax) def tagid(self, name, val): '''Get the database ID for a tag''' c = self.db.cursor() c.execute("SELECT id FROM tag WHERE name=? AND value=?", (name, val)) res = None for row in c: res = row[0] return res def tagnames(self): '''Get all tag names''' c = self.db.cursor() c.execute("SELECT DISTINCT name FROM tag ORDER BY name") for row in c: yield row[0] def tagvalues(self, name, use_bbox=False): ''' Get all tag values for a given tag name, optionally in the current bounding box ''' c = self.db.cursor() if self.bbox is None or not use_bbox: c.execute("SELECT DISTINCT value FROM tag WHERE name=? ORDER BY value", (name,)) else: c.execute("SELECT DISTINCT tag.value FROM poi, poitag, tag" " WHERE poi.rowid IN (SELECT pkid FROM idx_poi_geom WHERE (" " xmin >= ? AND xmax <= ? AND ymin >= ? AND ymax <= ?) )" " AND poitag.tag = tag.id AND poitag.poi = poi.id" " AND tag.name=?", self.bbox + (name,)) for row in c: yield row[0] def search(self, name, val): '''Get all name:val tags in the current bounding box''' # First resolve the tagid tagid = self.tagid(name, val) if tagid is None: return c = self.db.cursor() c.execute("SELECT poi.name, poi.data, X(poi.geom), Y(poi.geom) FROM poi, poitag" " WHERE poi.rowid IN (SELECT pkid FROM idx_poi_geom WHERE (" " xmin >= ? AND xmax <= ? AND ymin >= ? AND ymax <= ?) )" " AND poitag.tag = ? AND poitag.poi = poi.id", self.bbox + (tagid,)) self.oldsearch = [] for row in c: self.oldsearch.append(row) yield row[0], simplejson.loads(row[1]), row[2], row[3] def count(self, name, val): '''Count all name:val tags in the current bounding box''' # First resolve the tagid tagid = self.tagid(name, val) if tagid is None: return c = self.db.cursor() c.execute("SELECT COUNT(*) FROM poi, poitag" " WHERE poi.rowid IN (SELECT pkid FROM idx_poi_geom WHERE (" " xmin >= ? AND xmax <= ? AND ymin >= ? AND ymax <= ?) )" " AND poitag.tag = ? AND poitag.poi = poi.id", self.bbox + (tagid,)) for row in c: return row[0] def replay(self): for row in self.oldsearch: yield row[0], simplejson.loads(row[1]), row[2], row[3]
Problem 3 solved: now on to the next step, building a user interface for it.