class OSMHandler(QXmlDefaultHandler): """Class processing XML data from OSM service and holding places. Class is procesing data from xapi.openstreetmap.org and from nominatim.openstreetmap.org. """ def __init__(self, types, location, parent=None): """Initialize Places. @param types: list of allowed types of places @type types: list @param location: location from main aplication, needed for fall back @type location: Location @param parent: parent object @type parent: OSM """ QXmlDefaultHandler.__init__(self) self._location = location self._parent = parent self._place = "" self._places = Places() self._valid_types = types self._error_str = "" self._reverse_geo = False self._current_text = "" self._querystring = "" self._osm_id = 0 def startElement(self, namespaceURI, localName, name, attrs): """Called on every start element of XML file. On elements from xapi creates Place object and set attributes. On elements from nominatim sets display_name(address) and if place is valid, add it to the Places. """ # Node from xapi - create Place if name == "node": self._place = Place(str(attrs.value("id"))) self._place.set_lat(str(attrs.value("lat"))) self._place.set_lon(str(attrs.value("lon"))) # Tag from xapi - assign name and type elif name == "tag": if attrs.value("k") == "name": self._place.set_name(unicode(attrs.value("v"), "utf-8")) elif attrs.value("k") == "amenity": if str(attrs.value("v")) in self._valid_types: self._place.set_amenity(str(attrs.value("v"))) elif attrs.value("k") == "website": self._place.set_website(unicode(attrs.value("v"), "utf-8")) elif attrs.value("k") == "cuisine": self._place.set_cuisine(unicode(attrs.value("v"), "utf-8")) elif attrs.value("k") == "opening_hours": self._place.set_opening_hours(unicode(attrs.value("v"), "utf-8")) # Place from nominatim - set display_name elif name == "reversegeocode": self._reverse_geo = True self._querystring = attrs.value("querystring") elif name == "result": if self._reverse_geo: self._osm_id = str(attrs.value("osm_id")) self._current_text = "" return True def endElement(self, namespaceURI, localName, name): """Called on every end element of XML file.""" # When we are at the end of node from xapi add place to places. if name == "node" and self._place.is_valid(): self._places.add(self._place) # End of data from nominatim elif name == "result": if self._reverse_geo: self._places.set_display_name(self._osm_id, self._current_text) # Send places to dataengine self._parent.send_place(self._osm_id) elif name == "reversegeocode": self._reverse_geo = False self._osm_id = 0 elif name == "error": if self._reverse_geo: self._error_str = self._current_text return False return True def characters(self, string): """Remember XML values.""" self._current_text += unicode(string, "utf-8") return True def fatalError(self, exception): """Called in case of error. If nominatim can't find the id, try fall back mode. """ if self._error_str == "OSM ID Not Found": try: (rest, osm_id) = str(self._querystring).rsplit("=", 1) self._places.set_display_name(osm_id, "%s, %s, %s" % \ (self._places.search(osm_id).get_name(), self._location.getCity(), self._location.getCountry()), True) self._parent.send_place(osm_id) except Exception: pass return True raise Exception(self._error_str) def errorString(self): """Returns error string. @return: error string @rtype:str """ return self._error_str def get_places(self): """Returns places. @return: places @rtype: Places """ return self._places def clear_places(self): """Resets places.""" self._places = Places()