Rambler's Top100
"Knowledge itself is power"
F.Bacon
Поиск | Карта сайта | Помощь | О проекте | ТТХ  
 Сокровищница
  
 

Фильтр по датам

 
 К н и г и
 
Книжная полка
 
 
Библиотека
 
  
  
 


Поиск
 
Поиск по КС
Поиск в статьях
Яndex© + Google©
Поиск книг

 
  
Тематический каталог
Все манускрипты

 
  
Карта VCL
ОШИБКИ
Сообщения системы

 
Форумы
 
Круглый стол
Новые вопросы

 
  
Базарная площадь
Городская площадь

 
   
С Л С

 
Летопись
 
Королевские Хроники
Рыцарский Зал
Глас народа!

 
  
ТТХ
Конкурсы
Королевская клюква

 
Разделы
 
Hello, World!
Лицей

Квинтана

 
  
Сокровищница
Подземелье Магов
Подводные камни
Свитки

 
  
Школа ОБЕРОНА

 
  
Арсенальная башня
Фолианты
Полигон

 
  
Книга Песка
Дальние земли

 
  
АРХИВЫ

 
 

Сейчас на сайте присутствуют:
 
  
 
Во Флориде и в Королевстве сейчас  11:33[Войти] | [Зарегистрироваться]

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

Максим Мазитов
дата публикации 12-05-2009 10:49

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

В предыдущей статье я рассматривал 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;

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

That's all, folks! Пользуйтесь :)

К статье прилагается пример




Смотрите также материалы по темам:
[Сетевые службы и протоколы] [HTTP/HTTPS] [Взаимодействие с ГИС]

 Обсуждение материала [ 08-10-2010 05:18 ] 18 сообщений
  
Время на сайте: GMT минус 5 часов

Если вы заметили орфографическую ошибку на этой странице, просто выделите ошибку мышью и нажмите Ctrl+Enter.
Функция может не работать в некоторых версиях броузеров.

Web hosting for this web site provided by DotNetPark (ASP.NET, SharePoint, MS SQL hosting)  
Software for IIS, Hyper-V, MS SQL. Tools for Windows server administrators. Server migration utilities  

 
© При использовании любых материалов «Королевства Delphi» необходимо указывать источник информации. Перепечатка авторских статей возможна только при согласии всех авторов и администрации сайта.
Все используемые на сайте торговые марки являются собственностью их производителей.

Яндекс цитирования