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

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

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


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

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

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

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

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

 
   
С Л С

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

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

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

Квинтана

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

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

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

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

 
  
АРХИВЫ

 
 

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

Интерактивные карты Google

Максим Мазитов
дата публикации 16-08-2009 04:08

Интерактивные карты Google

В предыдущих опусах API статических карт Google и Google Maps API. Геокодирование я рассматривал варианты картографии, ограниченные во взаимодействии с пользователем.

В этой статье я попытаюсь расписать построение приложения на Delphi 7, работающего с интерактивными гуглокартами. Используемая здесь информация находится на стыке HTML, JavaScript, ActiveX и Delphi. Я заранее не претендую на полноту и высокую точность описываемого предмета. В данной области танцев с бубнами хватит на всех шаманов Сибири. Возможно, кому-нибудь статья, а может быть, и коды, помогут обойти те проблемы, с которыми я столкнулся. Но и я решил не все.

Краткая история вопроса

Вам требуется включить картографию в приложение. И какой бюджет у вашего проекта? 0? Тогда первый вариант — берем растровую карту, привязываем по трем точкам, немного нетривиальных математических вычислений и вы в состоянии выводить требуемые точки по координатам. Отметаем сразу. Второй вариант — качаем набор компонентов, например NGis (описан на этом сайте). Качаем MapInfo, GpsEdit (есть его исходники!). Идем на www.freemaps.ru. Убиваем вагон времени на конвертацию и сведение слоев. Результат работает, но для коммерческого продукта выглядит не слишком презентабельно. И сразу же возникает вопрос актуальности карт.

У вас есть $500 на карты? Ну что же, есть вариант — (контору называть не буду) ActiveX компонент, HASP ключ, без которого компонент не работает и отдельные деньги за карту каждого региона. Цена экземпляра софта возрастает на стоимость ключа и карт. Есть вариант взлома, как же без него, но если вы собираетесь работать серьезно и распространять легальный софт, то вариант взлома не проходит.

Вы готовы выложить $5000 — отлично! Именно столько стоили все слои Питера в формате MapInfo пару-тройку лет назад. У вас есть права на карты, вы их легко распространяете (закодировав конечно, кто захочет делиться пятью тысячами уёв?). Внимание, вопрос: а существует ли актуальная карта Петербурга, не говоря уже о Москве? Сколько будут стоить обновления? Богатые конторы, конечно, могут себе позволить, хотя и они сейчас экономят. Но для старта проекта без солидной финансовой поддержки этот вариант не проходит.

В нашем случае мы выбрали вариант за $500 и реализовали его. Когда в нете появилась разрозненная информация по возможности встраивания карт с источников web-картографии, естественно, мы за неё ухватились.

Приступим

Чего мы хотим добиться: в приложении Delphi вывести интерактивную карту Google со всеми возможностями пользовательского интерфейса (масштабирование, центрирование, перетаскивание) и отобразить на карте произвольный набор точек.

Кратко схема взаимодействия выглядит так: мы встраиваем TWebBrowser в приложение, открываем HTML страницу, на ней расположен Java-applet Google Maps, которым мы управляем через вызовы JavaScript.

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

И еще: вся инфа по аплету карты почерпнута из http://code.google.com/intl/ru/apis/maps/documentation/index.html.

Немного HTML и Java Script

Качаем пример, распаковываем и смотрим каталог HTML.

Небольшое замечание — приведенные примеры работают только с Internet Explorer 7 и выше. При попытке работать в связке Delpi7-IE6 возникли проблемы, которые от недостатка времени решать даже не пытались.

Список файлов каталога HTML:

map-article-1-simple-map.htmlСамой простой вариант страницы с картой Google.
map-article-2-map-with-controls.htmlКарта с добавленными элементами управления.
map-article-3-map-with-marker.htmlКарта с примером добавления маркеров на карту.
47.pngКартинка для пользовательского маркера.
tlabel.2.05.jsРеализация TLabel (см. ниже)
map-final.htmlРеализация для использования в программе.

Все страницы подробно прокомментированы.

Откроем в текстовом редакторе страницу "map-article-3-map-with-marker.html".

В теге Body в событии загрузки страницы вызывается функция initialize, которая производит действия по созданию и настройке карты. Читаем комментарии.

Производимые действия:

  1. Создание экземпляра класса GMap2.
  2. Центрирование на требуемой координате.
  3. Создание элементов управления картой.
  4. Экспериментальный вывод точек на карте.

Ещё в теге скрипта имеются функции добавления маркеров:

function addMarker(latitude, longitude, hint)добавление пустого маркера с точкой и подсказкой
function addMarkerIcon(latitude, longitude, hint, iconPath)добавление маркера с подсказкой и пользовательской иконкой

Теперь о маркерах — в зависимости от сложности задачи, приведенных маркеров может вполне оказаться достаточно. Но меня не устроили ограничения маркеров Google — если нужно вывести несколько произвольных текстовых надписей в разных местах, то простыми вызовами встроенных функций не обойдешься.

Пришлось посерфить в нете и в результате, я нашел устраивающий меня код на JScript, размещающий прямоугольник с текстом в указанной географической точке. Смотрим файл "tlabel.2.05.js".

В нем реализовывается класс TLabel и добавляется функционал к классу GMap2, в котором добавляются и удаляются экземпляры класса TLabel. В меру своих скромных возможностей, я добавил метод по удалению TLabel по идентификатору. Но вот профессионального пояснения по коду скрипта дать не могу, хоть и понимаю, как он работает (сам такое написать не смогу).

Для добавления на страницу нового маркера пишем следующий код в HTML на JavaScript (см. файл "map-final.html"):

// создание маркера
function createTLabel(id, point, html, astyle) {
  // создаем экземпляр
  var label = new TLabel();
  // идентификатор
  label.id = id;
  // координаты
  label.anchorLatLng = point;
  // расположение относительно координат
  label.anchorPoint = 'center';
  // содержимое
  label.content = '<div class="'+astyle+'"><nobr><b>'+html+'</b></nobr></div>';
  // прозрачность
  label.percentOpacity = 70;
  // добавляем на карту
  map.addTLabel(label);
}

Маркер использует стили, описанные в начале файла "map-final.html". Стили завязаны на строковые названия цветов. Если возникнет надобность в новом цвете достаточно в тексте страницы описать стиль с именем формата ".<имя цвета>label".

Вызов функции добавления маркера на JavaScript:

createTLabel("Id0", new GLatLng(59.944265, 30.319948), "<nobr>Текст маркера</nobr>", "bluelabel");

Первый параметр — уникальный идентификатор, указывая который возможно удалить маркер с карты. Далее идут координаты в десятичных долях градуса, HTML текст маркера и его стиль.

Вроде бы ничего сложного.

Подключаем к Delphi

Основной вопрос — как управлять Java аплетом Google. Берем TWebBrowser и смотрим его интерфейсы. Мы можем заставить IE выполнить код JavaScript из внешнего приложения следующим образом (на этом сайте он тоже описан):

try
  if WebBrowser.Document<>nil then
      IHTMLDocument2(WebBrowser.Document).parentWindow.execScript(Script,'JavaScript');
  except
    on E:Exception do begin
      MessageBox(Handle,PChar(E.Message),'Ошибка исполнения', MB_OK or MB_ICONERROR);
    end;
  end;

, где переменная Script содержит текст скрипта. И этого достаточно.

Дальше остается только кодирование и чтение доков гугла. Смотрим исходные коды.

Все исходники, работающие с картографией, расположены в каталоге "MapsFrames".

  • BaseMapFrames.pas содержит класс точки на карте (описания полей в коде), вспомогательные типы и базовый класс фрейма карты.
  • GoogleMapFrames.pas содержит реализацию базового класса фрейма карты для Google.

Я использовал фреймы (TFrame) вместо компонентов по причине более легкой переносимости кода с компьютера на компьютер — их не надо регистрировать, а написать один раз 5 строк кода по встраиванию фрейма в форму ненапрягающая задача.

Класс точки TMapPoint является простым контейнером свойств точки карты — координаты, текст и т.п.

Тип TPointsChangingType описывает действия, которые производились с точками, для передачи форме в соответствующем событии.

Класс TBaseMapFrame является контейнером точек, позволяющим добавлять, изменять и удалять точки на карте, и содержит абстрактные методы по работе с картой для перекрытия их (методов) в реализациях под конкретную картографию. Это различные варианты масштабирования, а также показ и скрытие точек. При создании класса я применил Doc-View схему (не до конца, правда), поэтому фрейм имеет событие PointsChangingEvent, уведомляющее об изменениях в коллекции точек. По большому счету, это просто обертка над TObjectList с небольшим количеством абстрактных методов.

Теперь о TGoogleMapFrame. Перекрываем методы масштабирования:

// приближение карты
procedure TGoogleMapFrame.ZoomIn;
begin
  ExecScript('map.zoomIn();');
end;

// отдаление карты
procedure TGoogleMapFrame.ZoomOut;
begin
  ExecScript('map.zoomOut();');
end;

Центрирование:

// центрирование карты
procedure TGoogleMapFrame.SetCenter(Latitude:Double;Longitude:Double;Scale:Integer=-1);
var
  Script:String;
begin
  // если при вызове указан масштаб
  if Scale>=0 then begin
    Script:='var point = new GLatLng('+FloatToStr(Latitude)+','+FloatToStr(Longitude)+'); '+
      'map.setCenter(point, '+IntToStr(Scale)+')';
  end else begin
  // если не указан, то масштаб сохраняется
    Script:='var point = new GLatLng('+FloatToStr(Latitude)+','+FloatToStr(Longitude)+'); '+
      'map.setCenter(point)';
  end;
  ExecScript(Script);
end;

Добавление точки:

// формирование скрипта на добавление точки
function TGoogleMapFrame.PointToAddString(MapPoint:TMapPoint):String;
var
  ColorStr:String;
begin
  ColorStr:=ColorToString(MapPoint.Color)+'label';
  if (Copy(ColorStr,1,2)='cl') then
    Delete(ColorStr,1,2);
  Result:='createTLabel("%s", new GLatLng(%.8f, %.8f), "%s", "%s");';
  Result:=Format(Result,[MapPoint.Id,MapPoint.Latitude,MapPoint.Longitude,MapPoint.Caption,ColorStr]);
end;

// нарисовать точку с заданным индексом
procedure TGoogleMapFrame.ShowPoint(Index:Integer);
var
  Script:String;
begin
  // предварительно убить
  HidePoint(Index);
  // получаем скрипт
  Script:=PointToAddString(Points[Index]);
  // выполним
  ExecScript(Script);
end;

// спрятать точку с заданным индексом
procedure TGoogleMapFrame.HidePoint(Index:Integer);
var
  Script:String;
begin
  if Index>=PointsCount then
    Exit;
  // формируем скрипт
  Script:='map.removeTLabelById("'+Points[Index].Id+'");';
  ExecScript(Script);
end;

Функция PointToAddString возвращает скрипт по созданию точки. ShowPoint размещает точку из коллекции на карте. Вызов ShowPoint производится из методов базового класса. HidePoint убирает точку с карты.

Вызовы любых скриптов приводят к исключительной ситуации, если страница находится в процессе загрузки. Для обхода этих грабель я ввел событие загрузки страницы:

// отлавливаем событие загрузки карты
procedure TGoogleMapFrame.WebBrowserStatusTextChange(Sender: TObject;
  const Text: WideString);
begin
  if not FInitialized and (WebBrowser.ReadyState=READYSTATE_COMPLETE) then begin
    FInitialized:=True;
    if Assigned(FOnMapLoaded) then
      FOnMapLoaded(Self);
  end;
end;

При первом получении статуса браузера READYSTATE_COMPLETE вызывается событие OnMapLoaded. До срабатывания этого события работать со скриптами нельзя!

В классе TBaseMapFrame предусмотрены методы поддержки длительных изменений BeginUpate и EndUpdate. Смысл я думаю понятен. При построении больших маршрутов очень даже полезно.

В примерах лежат 2 приложения.

Приложение GoogleInteractiveMap демонстрирует большинство функционала фреймов. Для корректной работы необходимо в приложении отрыть файл "HTML\map-final.html". Только в нем есть все требуемые функции JavaScript. После загрузки страницы карты становятся доступными все функции программы. Маршруты подгружаются из каталога "Routes". Маршруты я создавал через Google Earth, т.к. он выдает в маршрутах некие осмысленные данные (но осознанно не строит маршрутов по России). В файле "RouteFromHelsinkiToRussia.txt" дорога из Хельсинки до Российской границы, более 1100 точек. На моей не самой медленной машинке с четырьмя ядрами загрузка занимает 1 минуту ровно. В файле "SmallRoute.txt" первые 10 точек маршрута.

Приложение RoutePlayer реализует работу с одной точкой и демонстрирует работу события OnMapLoaded. После загрузки карты начинается перемещение по точкам маршрута с периодом 1 секунда. Путь к файлу маршрута зашит в исходном тексте.


Рисунок 2

Видите синий прямоугольник со звездочкой в центре карты?

Все коды обильно закомментированы.

Как я говорил в начале, все проблемы обойти не удалось. Осталось у меня два вопроса, если кто-то подскажет мне решение, моя благодарность не будет знать границ в определенных объемах :):

  1. При достаточно большом маршруте, закрывая GoogleInteractiveMap, я получаю сообщение от IE о том, что скрипт тормозит работу и предложение прекратить выполнять скрипты. Я пытаюсь убивать все точки перед закрытием. Но, то ли я напутал что-то в событиях, то ли в силу природной тупости проблема не решается.
  2. Очень хотелось бы получать координаты под курсором. Насколько я понимаю, функционал у класса GMap2 для этого есть. Но как получить содержимое переменной JavaScript в Delphi я не догнал. Я понимаю, что путь лежит через DOM модель IE. Но как?

На этом, пожалуй, цикл гуглокартографии можно считать закрытым. Ну и отлично.

В эту обертку можно завернуть картографию Яндекс (лучше покрытие России), Yahoo, Microsoft (возможно). И особенно рекомендую посмотреть http://www.openstreetmap.org/, потому что OpenSource.

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




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

 Обсуждение материала [ 22-04-2010 01:38 ] 12 сообщений
  
Время на сайте: 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» необходимо указывать источник информации. Перепечатка авторских статей возможна только при согласии всех авторов и администрации сайта.
Все используемые на сайте торговые марки являются собственностью их производителей.

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