(495) 925-0049, ITShop интернет-магазин 229-0436, Учебный Центр 925-0049
  Главная страница Карта сайта Контакты
Поиск
Вход
Регистрация
Рассылки сайта
 
 
 
 
 

Google Maps API. Геокодирование

Источник: delphikingdom
Максим Мазитов

Автор: Максим Мазитов, Королевство Delphi

В предыдущей статье я рассматривал API статических карт Google. Рядом с этой темой, буквально "впритык", располагается геокодирование. Что это за зверь? Цитирую:

"Geocoding is the process of converting addresses (like "1600 Amphitheatre Parkway, Mountain View, CA") into geographic coordinates (like latitude 37.423021 and longitude -122.083739), which you can use to place markers or position the map"

А если нормальным языком, то геокодирование - процесс превращения строкового почтового (не путать с электронной почтой :) ) адреса в координаты. А также, обратный процесс.

Описание API лежит на гугле, к сожалению, пока не переведенное, хотя в адресе присутствует код языка.

Итак, приступим.

Прямое геокодирование

Идем по этой ссылке. Результатом оказывается XML документ. Смотрим на него внимательно. Ветки документа содержат информацию о найденных объектах и их координатах. Дело за малым - сформировать URL, закинуть запрос, считать ответ и разобрать XML. "За чем же дело встало?" (жизненная цитата)

Строка запроса состоит из трех параметров:

  • q - собственно текст запроса. Разделитель слов запроса знак "+".
  • output - тип возвращаемых данных. Возможные значения xml, kml, csv, или json. Описание сморим на сайте указанной ссылке. CSV не рассматриваем, т.к. в этом случае выдается мало информации. KML используется в Google Earth. Json внутренний формат Google Map. XML нам наиболее близок и сердцу дорог, так что берем его.
  • key - ключ карт. Как себя ведет не ясно, смотрим предыдущую статью. Пусть живет как есть.
  • gl - код страны. Необходимо устанавливать в соответствии с доменами первого уровня страны проживания. В нашем случае - "ru". Для Украины, например, "ua".

Скачиваем пример и смотрим в код:

// получение списка точек по запросу
function TfmMain.DoGeocodingRequest(SearchString:String):TObjectList;
var
  Point:TMapPoint;
  FileOnNet:String;
  Stream:TMemoryStream;
  Utf8Content:UTF8String;
  Node:IXMLNode;
  PlacemarkNode, PointNode, AddressNode:IXMLNode;
  i:Integer;
  sCoordinates:String;
  StringList:TStringList;
begin
  // создаем хранилище результатов
  Result:=TObjectList.Create(True);
  // создаем поток
  Stream:=TMemoryStream.Create;
  StringList:=TStringList.Create;
  try
    // формируем url для запроса
    FileOnNet:='http://maps.google.com/maps/geo?q=%s&output=xml&key=abcdefg&gl=ru';
    FileOnNet:=Format(FileOnNet,[SearchString]);
    // получение потока с данными ответа
    if GetInetFile(FileOnNet,Stream) = True then begin
      // кому надо-расскоментировать и смотреть содержимое при отладке
      //Stream.SaveToFile('c:\geo.xml');

      StreamToUtf8Stream(Stream);
      // заполняем XMLDocument
      XMLDocument.LoadFromStream(Stream);
      // формируем содержимое списка точек
      Node:=XMLDocument.DocumentElement;
      Node:=Node.ChildNodes.FindNode('Response');
      if (Node<>nil) and (Node.ChildNodes.Count>0) then
        for i:=0 to Node.ChildNodes.Count-1 do
          if Node.ChildNodes[i].NodeName='Placemark' then begin
            // находим узел точки
            PlacemarkNode:=Node.ChildNodes[i];
            // получаем узел адреса
            AddressNode:=PlacemarkNode.ChildNodes.FindNode('address');
            //получаем узел координат
            PointNode:=PlacemarkNode.ChildNodes.FindNode('Point');
            PointNode:=PointNode.ChildNodes.FindNode('coordinates');
            if (AddressNode<>nil) and (PointNode<>nil) then begin
              Point:=TMapPoint.Create;
              // получаем адрес
              Point.Address:=AddressNode.Text;
              // получаем координаты
              sCoordinates:=PointNode.Text;
              // разбираем координаты
              ExtractStrings([','],[],PChar(sCoordinates),StringList);
              if StringList.Count>1 then begin
                // Формируем точку
                Point.Lon:=StrToFloatDef(StringList[0],-1);
                Point.Lat:=StrToFloatDef(StringList[1],-1);
                // добавляем точку в список 
                if (Point.Lat<>-1) and (Point.Lon<>-1) then
                  Result.Add(Point);
                StringList.Clear;
              end else
                Point.Free;
            end;
          end;
    end;
  finally
    StringList.Free;
    Stream.Free;
  end;
end;

Для хранения результатов обработки XML я создал класс.

  TMapPoint=class
  public
    Lat:Double;
    Lon:Double;
    Address:String;
  end;

И складировал результаты в TObjectList. Если уж совсем заморачиваться, то "по уму" нужно написать хранилище для результатов, но почему то мне было лень.

Надеюсь, я достаточно подробно прокомментировал код, и нет необходимости его расписывать. В результате, мы имеем список точек с координатами и подробным адресом. Если кому-то понадобится точное разложение по городам, улицам и домам, то не составляет большого труда разобрать подчиненные ветки документа, детализация достаточно подробная.

Со списком точек я поступил следующим образом - вывел в ListView и отобразил на карте. Если кто-нибудь ковырял самостоятельно описание статических карт на сайте гугла, приведенное в предыдущей статье, то там есть возможность вывода нескольких точек на одной карте. Я добавил следующую функцию:

// получение карты со списком точек
function GetMap(Points:TObjectList):TOleGraphic;overload;
var
  FileOnNet: String;
  Stream:TMemoryStream;
  Markers:String;
  i:Integer;
  Point:TMapPoint;
begin
  // проверяем наличие точек
  if Points.Count<1 then begin
    Result:=nil;
    Exit;
  end;
  Markers:='';
  // формируем список маркеров
  for i:=0 to Points.Count-1 do
    if (Points[i] is TMapPoint) then begin
      Point:=TMapPoint(Points[i]);
      Markers:=Markers+Format('%.6f,%.6f/',[Point.Lat,Point.Lon]);
    end;
  // создаем поток
  Stream:=TMemoryStream.Create;
  try
    // формируем url для запроса
    FileOnNet:='http://maps.google.com/staticmap?size=640x640'
      +'&markers=%s'
      +'&maptype=mobile&key=MAPS_API_KEY';
    FileOnNet:=Format(FileOnNet,[Markers]);
    // получение потока с данными ответа
    if GetInetFile(FileOnNet,Stream) = True then begin
      // создаем графический объект
      Stream.Position:=0;
      Result:=TOleGraphic.Create;
      Result.LoadFromStream(Stream);
    end else
      Result:=nil;
  finally
    Stream.Free;
  end;
end;

Разница с уже имевшейся функцией GetMap во входных параметрах - списке точек для отображения. Вот что получилось:

Почему-то я всегда думал, что улиц Ленина в России больше. И знаю пару неотмеченных городов на карте в которых она есть. Кстати, это отличный тест гугла на наличие карт городов - есть улица/проспект Ленина, Октября, Революции и ещё немного названий - есть и подробная карта. И следствие - Сибирь у гугла не отобразилась, значит городов там нет!

Кстати, а есть ли жизнь за МКАДом? :)

Танцы с бубнами вокруг кодировок

Гугл принимает и отдает данные в формате UTF-8. Этот факт упомянут в описании. К сожалению, не всё так просто. При посылке запроса с указанием языка в URL возвращаемые данные не принимаются в TXMLDocument, русские символы ему, видите ли, не нравятся. Если перевести URL в UTF-8, тогда документ принимается. Но в большинстве случаев часть адресных строк содержат английские названия. Так Дворцовая площадь становится Palace Embankment. Не вполне допустимая ситуация для использования софта пользователями, не знакомыми с языком потенциального противника. При этом, посылая аналогичный запрос из любого браузера, данные приходят по-русски. Приходит мысль, что собачко порылось в HTTP заголовках. Т.к. гугль знает всё (чё, правда? :), решение найдено в добавлении заголовка "Accept-Language: ru" в вызов функции InternetOpenURL. Тогда гугль начинает присылать данные по-русски, НО! в виндовой кодировке. Тут опять больше всех надо становится TXMLDocument-у. В XML указано UTF-8, а реально 1251. Ну, наше дело маленькое, пользуем функцию AnsiToUtf8, и будет нам счастье. Результаты танцев смотрим в функциях GetInetFile и StreamToUtf8Stream.

Обратное геокодирование

Допустим, что у нас есть мобильный объект и нам известны его координаты. Необходимо получить ближайший адрес к точке, по возможности конечно. Адрес "Баренцево море", например, несет слабую смысловую нагрузку, поэтому не для любой точки можно провести процедуру обратного геокодирования с получением почтового адреса.

Обратное геокодирование возможно построением двух типов запросов. В запросе можно указывать координаты в параметре "q" и запрос не отличается от вышеописанного. Но эта функция не документирована. Честный же способ заключается в использовании параметра ll - "latitude, longitude". Пробуем следующую ссылку. Мы видим данные адреса по координатам.

Получение данных выглядит следующим образом:

// запрос обратного геокодирования
procedure TfmMain.DoReverseGeocodingRequest;
var
  FileOnNet: String;
  Stream:TMemoryStream;
begin
  // создаем поток
  Stream:=TMemoryStream.Create;
  try
    // формируем url для запроса
    FileOnNet:='http://maps.google.com/maps/geo?ll=%.6f,%.6f&output=xml&key=abcdefg&gl=ru';
    FileOnNet:=Format(FileOnNet,[Latitude,Longitude]);
    // получение потока с данными ответа
    if GetInetFile(FileOnNet,Stream) = True then begin
      StreamToUtf8Stream(Stream);
      // заполняем XMLDocument
      XMLDocument.LoadFromStream(Stream);

      // отображаем на дерево
      FillGeocodingTree;
    end;
  finally
    Stream.Free;
  end;
end;

Отобразим полученное дерево на TreeView. Чтобы не перегружать статью, я не буду приводить заполнение дерева, можно посмотреть исходники.

Структура XML документа содержит узлы "Placemark". Узлы содержат информацию о точке по слоям карты. Самый первый узел "Placemark" содержит наиболее подробную информацию об адресе точки (верхний слой). Прикрутим получение адреса к примеру получения картинки карты из предыдущей статьи.

// обработка нажатия кнопки "Обновить"
procedure TfmMain.btRefershClick(Sender: TObject);
var
  OleGraphic: TOleGraphic;
begin
  // получаем изображение
  OleGraphic:=GetMap(edLatitude.Value,edLongitude.Value,tbScale.Position);
  if OleGraphic<>nil then begin
    // передаем изображение на Image
    Image.Picture.Assign(OleGraphic);
    OleGraphic.Free;
  end;
  // получение адреса
  DoReverseGeocodingRequest(edLatitude.Value,edLongitude.Value);
end;

В результате в строке состояния окна появится адрес точки, отображаемой на карте, на первой вкладке:

Ссылки по теме

Файлы для загрузки


 Распечатать »
 Правила публикации »
  Написать редактору 
 Рекомендовать » Дата публикации: 18.05.2009 
 

Магазин программного обеспечения   WWW.ITSHOP.RU
Enterprise Connectors (1 Year term)
Delphi Professional Named User
ABViewer Professional пользовательская
VMware Horizon 7 Standard : 10 Pack (CCU)
Microsoft Office 365 Бизнес. Подписка на 1 рабочее место на 1 год
 
Другие предложения...
 
Курсы обучения   WWW.ITSHOP.RU
 
Другие предложения...
 
Магазин сертификационных экзаменов   WWW.ITSHOP.RU
 
Другие предложения...
 
3D Принтеры | 3D Печать   WWW.ITSHOP.RU
 
Другие предложения...
 
Новости по теме
 
Рассылки Subscribe.ru
Информационные технологии: CASE, RAD, ERP, OLAP
Новости ITShop.ru - ПО, книги, документация, курсы обучения
Программирование на Microsoft Access
CASE-технологии
СУБД Oracle "с нуля"
Новости мира 3D-ускорителей
Adobe Photoshop: алхимия дизайна
 
Статьи по теме
 
Новинки каталога Download
 
Исходники
 
Документация
 
 



    
rambler's top100 Rambler's Top100