Общее·количество·просмотров·страницы

Java Dev Notes - разработка на Java (а также на JavaScript/Python/Flex и др), факты, события из АйТи

вторник, 30 марта 2010 г.

Обратное геокодирование на Google Maps, получение региона

Понадобилось мне как-то получить регион для множества гео-объектов. У меня есть координаты точек, и более ничего. А хочется для каждого гео-объекта иметь следующее: Страну-Регион-Субрегион. После этого я смогу, например, делать кластеризацию по регионам.

Пример данных вида Страна-Регион-Субрегион: Россия-Ленинградская область-Всеволожский район. Или: Россия-город Санкт-Петербург-Фрунзенский район.

Получение адреса (хотя бы приблизительного) объекта по его географическим координатам называется обратным геокодированием. В разделе Геокодирование можно почитать о том, что это такое, и как этим пользоваться в Google Maps.

Обратное геокодирование в Google Maps выполняется следующим образом: передается запрос вида

http://maps.google.com/maps/geo?q=60.6462623161,29.7290039063&output=xml&oe=utf8

Он возвращает информацию об адресе объекта с координатами (60.6462623161,29.7290039063), формат возвращаемых данных XML (т.е. KML). Можно еще получать данные в CSV, JSON. Информация об адресе содержится в xml-контейнерах Placemark. Этих контейнеров обычно бывает несколько в XML-документе. Каждый из них содержит информацию о ближайшей к объекту точке и ее адресе. В Placemark есть контейнер AddressDetails с атрибутом Accuracy. Если Accuracy=1, то в данном плейсмарке определена только страна, если Accuracy=2, то определена страна и регион, а если Accuracy=3, то дополнительно определен и субрегион. На большие значения Accuracy я не заглядывал, т.к. значения 3 мне хватало для моих целей за глаза и за уши.

Итак, у нас есть KML-документ, теперь мы его будем парсить. Парсер написан на Python.
Сначала нам потребуются две дополнительные функции: для извлечения текста из XML-ноды, а также для получения первого дочернего элемента. Вот эти функции:

def getFirstChildNode(parent,childNodeName):
choices = [e for e in parent.childNodes if e.nodeType == e.ELEMENT_NODE and e.nodeName == childNodeName]
for c in choices:
return c
return None
 
def getNodeText(node):
result = None
for c in node.childNodes:
if c.nodeType == Node.TEXT_NODE:
result = c.data
return result


Теперь представим всю функцию под названием getRegionFromKml:

def getRegionFromKml(kml):
kmldoc = minidom.parseString(kml)
 
result = {}
 
result['ok'] = False
 
result['CountryNameCode'] = None
result['CountryName'] = None
result['AdministrativeAreaName'] = None
result['SubAdministrativeAreaName'] = None
 
nodelist_status = kmldoc.getElementsByTagName("Status")
if len(nodelist_status)==0:
return result
node_status = nodelist_status[0]
node_code = getFirstChildNode(node_status,"code")
if not node_code: return result
code = getNodeText(node_code)
if not code == '200':
return result
 
nodelist_Placemark = kmldoc.getElementsByTagName("Placemark")
 
for node_Placemark in nodelist_Placemark:
if result['ok']:
break;
node_AddressDetails = getFirstChildNode(node_Placemark,"AddressDetails")
if not node_AddressDetails: continue
attribute_Accuracy = node_AddressDetails.getAttribute("Accuracy")
if not attribute_Accuracy: continue
accuracy = int(attribute_Accuracy)
if accuracy < 2: continue
node_Country = getFirstChildNode(node_AddressDetails,"Country")
if not node_Country: continue
 
node_CountryNameCode = getFirstChildNode(node_Country,"CountryNameCode")
if not node_CountryNameCode: continue
result['CountryNameCode'] = getNodeText(node_CountryNameCode)
#logging.info("country code: %s" %(result['CountryNameCode']) )
 
node_CountryName = getFirstChildNode(node_Country,"CountryName")
if not node_CountryName: continue
result['CountryName'] = getNodeText(node_CountryName)
 
#logging.info("CountryName: '%s'" % (CountryName))
node_AdministrativeArea = getFirstChildNode(node_Country,"AdministrativeArea")
if not node_AdministrativeArea: continue
 
node_AdministrativeAreaName = getFirstChildNode(node_AdministrativeArea,"AdministrativeAreaName")
if not node_AdministrativeAreaName: continue
result['AdministrativeAreaName'] = getNodeText(node_AdministrativeAreaName)
 
node_SubAdministrativeArea = getFirstChildNode(node_AdministrativeArea,"SubAdministrativeArea")
if node_SubAdministrativeArea:
node_SubAdministrativeAreaName = getFirstChildNode(node_SubAdministrativeArea,"SubAdministrativeAreaName")
if node_SubAdministrativeAreaName:
result['SubAdministrativeAreaName'] = getNodeText(node_SubAdministrativeAreaName)
 
node_Locality = getFirstChildNode(node_AdministrativeArea,"Locality")
if node_Locality:
node_LocalityName = getFirstChildNode(node_Locality,"LocalityName")
if node_LocalityName:
result['SubAdministrativeAreaName'] = getNodeText(node_LocalityName)
 
result['ok'] = True
 
return result
 


Для парсинга XML мы пользуемся пакетом minidom, так что в код нужно добавить пару импортов:

from xml.dom import minidom 
from xml.dom.minidom import Node


А вот как может выглядеть функция, которая а вход принимает широту и долготу, а на выход выдает объект, содержащий Страну/Регион/Субрегион данных координат:

def getRegionByLatLon(lat,lon):
result = {}
result['ok'] = False
 
try:
url = "http://maps.google.com/maps/geo?q=%10.8f,%10.8f&output=xml&oe=utf8" % (lat,lon)
urlResult = urlfetch.fetch(url)
 
if urlResult.status_code == 200:
kml = urlResult.content
result = getRegionFromKml(kml)
except:
logging.info('error occured when adding station: %s' % (sys.exc_info()[0]))
exceptionType, exceptionValue, exceptionTraceback = sys.exc_info()
logging.error('exceptionType: %s' % exceptionType)
logging.error('exceptionValue: %s' % exceptionValue)
logging.error('exceptionTraceback: %s' % exceptionTraceback)
 
return result


Подробнее о парсинге XML на Питоне можно прочитать по следующим ссылкам:

xml.dom.minidom — Lightweight DOM implementation

Dive in Python - Chapter 5. XML Processing

Python & XML - Chapter 1

Комментариев нет:

Отправить комментарий

Постоянные читатели