В прошлой статье, касающейся работы с kml в Delphi были обозначены пути работы — созданы несколько базовых классов для работы с географической информацией и дано описание основных свойств этих классов. Сегодня перейдем к конкретным примерам работы с kml в Delphi, а именно — научимся создавать метки на карте в Google Earth.
Контейнеры в KML
В иерархии элементов kml выделяется группа элементов-контейнеров (Container), которые могут хранить в себе несколько элементов (Feature) и, по сути, служат для создания некой иерархии элементов внутри kml-документа.
Вот часть схемы иерархии элементов kml, относящаяся к контейнерам:
Таким образом, в kml-файле можно создать как документ (Document), так и папки (Folder) для хранения различных элементов, включая метки, линии, полигоны и так далее. По описанию свойств Document и Folder идентичны — оба элемента наследуют свойства Feature и могут содержать в себе коллекцию «фич» (Feature), однако назначение этих элементов несколько различны.
Документ (Document) используется в kml-файле можно описывать в файле один раз. Этот элемент, по-мимо всего прочего, предназначен для хранения общих стилей. Например, в Document можно задать то, какие иконки будут применяться ко всем меткам, или цвет линий и так далее.
Папка же (Folder) используется исключительно для создания иерархии элементов внутри документа. Визуально Document и Folder выглядят в Google Earth следующим образом:
Таким образом, наши метки могут содержаться в одном из видов контейнеров — в документе или папке. Сегодня мы рассмотрим самый простой пример создания метки, которая будет находиться в документе (Document).
Создаем методы сохранения элементов в XML-документа
Для дальнейшей работы нам потребуется дописать методы классов, созданных в прошлой статье. Для начала, создадим метод создания XML-узла (IXMLNode) у базового класса TKmlObject. Этот методы мы будем использовать во всех наследниках класса, поэтому необходимо учесть, что новый узел может быть как корневым для XML-документа, так и дочерним для другого узла в документа. TKmlObject будет теперь таким:
type TKmlObject = class private //поля public function CreateNode(ANodeName: string; AParentNode: IXMLNode; ADocument: IXMLDocument = nil): IXMLNode; virtual; //свойства объекта end;
Функция будет работать следующим образом:
- Если определен параметр ADocument, то считаем, что новый узел — корневой для документа
- Если определен параметр AParentNode, то считаем, что новый узел — родительский для AParentNode.
Функция CreateNode будет такой:
function TKmlObject.CreateNode(ANodeName: string; AParentNode: IXMLNode; ADocument: IXMLDocument): IXMLNode; begin if (not Assigned(AParentNode)) and (not Assigned(ADocument)) then raise Exception.Create('AParentNode или ADocument должны быть определены'); if Assigned(ADocument) then begin Result := ADocument.CreateElement(ANodeName, cKmlNamespace); ADocument.DocumentElement.ChildNodes.Add(Result); end else Result := AParentNode.AddChild(ANodeName); if not Id.IsEmpty then Result.Attributes['id'] := Id; if not TargetId.IsEmpty then Result.Attributes['targetId'] := TargetId; end;
Следующий класс, который наследуется от TKmlObject и, имеющий собственные свойства — это TKmlPoint (точка). Для точки можно задать координаты, а также её положение относительно уровня земной поверхности. Так как у этого класса есть конкретное имя в kml-документе (Point) и этот узел обязательно должен быть дочерним для других элементов, то у этого класса мы создадим метод Save:
TKmlPoint = class(TKmlGeometry) private //поля класса public function Save(AParentNode: IXMLNode): IXMLNode; //свойства класса end;
Реализация метода будет следующей:
function TKmlPoint.Save(AParentNode: IXMLNode): IXMLNode; begin Result := inherited CreateNode('Point', AParentNode, nil); Result.AddChild('extrude').Text := BoolStrs[Extrude]; Result.AddChild('altitudeMode').Text := AltitudeModeStr[AltitudeMode]; Result.AddChild('coordinates').Text:=Coordinates.ToString end;
Здесь BoolStrs и AltitudeModeStr — это константы следующего вида:
const BoolStrs: array [boolean] of string = ('0', '1'); AltitudeModeStr: array [TAltitudeModeEnum] of string = ('clampToGround', 'relativeToGround', 'absolute');
TAltitudeModeEnum — тип данных, определенный в первой части.
Координаты точки у нас задаются в виде объекта типа TKmlCoordinate. У TKmlCoordinate был определен метод ToString следующего содержания:
function TKmlCoordinate.ToString: string; var FS: TFormatSettings; SB: TStringBuilder; begin FS.DecimalSeparator := '.'; SB := TStringBuilder.Create; try SB.Append(FloatToStr(Longtitude, FS)); SB.Append(','); SB.Append(FloatToStr(Latitude, FS)); SB.Append(','); SB.Append(FloatToStr(Altitude, FS)); Result := SB.ToString(True) finally FreeAndNil(SB); end; end;
Этот метод формирует строку вида «долгота,широта,высота» в соответствии с KML Reference.
Так как элемент Feature может выступать родителем как для элемента «Документ», так и для элемента «Папка», а сам элемент Feature, как и Object — виртуальный и не может создаваться напрямую в KML, то у класса TKmlFeature метод CreateNode был переопределен следующим образом:
TKmlFeature = class(TKmlObject) private //поля класса public function CreateNode(AFeatureName: string; AParentNode: IXMLNode; ADocument: IXMLDocument = nil): IXMLNode; override; //свойства класса end; function TKmlFeature.CreateNode(AFeatureName: string; AParentNode: IXMLNode; ADocument: IXMLDocument): IXMLNode; var ANode: IXMLNode; begin ANode := inherited CreateNode(AFeatureName, AParentNode, ADocument); if not Name.IsEmpty then ANode.AddChild('name').Text := Name; ANode.AddChild('visibility').Text := BoolStrs[Visibility]; ANode.AddChild('open').Text := BoolStrs[Open]; if not Snippet.IsEmpty then ANode.AddChild('snippet').Text := Snippet; if not Description.IsEmpty then ANode.AddChild('description').Text := Description; if not Link.IsEmpty then ANode.AddChild('Link').Text := Link; Result := ANode; end;
Элемент Placemark может явно создаваться в KML и быть дочерним для папки или документа, поэтому у него создадим метод Save, аналогичный методу у Point:
TKmlPlacemark = class(TKmlFeature) private //поля класса public procedure Save(AParentNode: IXMLNode); //свойства класса end; procedure TKmlPlacemark.Save(AParentNode: IXMLNode); var ANode: IXMLNode; begin //сохраняем свойства Feature ANode := inherited CreateNode('Placemark', AParentNode, nil); //добавляем координаты Point.Save(ANode); end;
Теперь, добавив все необходимые методы, можно приступать уже непосредственно к формированию KML-файла, содержащего наши метки.
Создаем документ (Document) для хранения меток
Для начала, научимся создавать kml-документ и сохранять его на диск. Наш новый класс (назовем его TKmlDocument) будет наследником ранее созданного TKmlFeature и выглядеть следующим образом:
TKmlDocument = class(TKmlFeature) private FXmlDoc: IXMLDocument; FPlacemark: TObjectList; public constructor Create; destructor Destroy;override; function ToString: string;override; procedure SaveToFile(const AFileName: string); end;
Функция ToString создает строку, содержащую xml:
const cKmlNamespace = 'http://www.opengis.net/kml/2.2'; cAtomNamespace = 'http://www.w3.org/2005/Atom'; function TKmlDocument.ToString: string; var kml, DocNode: IXMLNode; I: Integer; begin FXmlDoc := TXMLDocument.Create(nil); FXmlDoc.Active := True; kml := FXmlDoc.AddChild('kml'); kml.DeclareNamespace('', cKmlNamespace); kml.DeclareNamespace('atom', cAtomNamespace); DocNode:=CreateNode('Document', nil, FXmlDoc); for I := 0 to Pred(FPlacemark.Count) do FPlacemark[i].Save(DocNode); Result := FXmlDoc.Xml.Text; end;
Второй метод — SaveToFile сохраняет наш kml-документ в файл на диске:
procedure TKmlDocument.SaveToFile(const AFileName: string); var S: TStringStream; begin S:=TStringStream.Create(ToString,TEncoding.UTF8); try S.SaveToFile(AFileName); finally FreeAndNil(S); end; end;
Попробуем воспользоваться нашим классом, создать пустой kml-документ и посмотрим, что из этого получиться в Google Earth. Создадим новый проект в Delphi, на главную форму бросим кнопку TButton и напишем такой обработчик для события OnClick:
procedure TForm10.Button1Click(Sender: TObject); var D: TKmlDocument; begin D:=TKmlDocument.Create; try D.Name:='Мой первый документ'; D.Visibility:=True; D.Open:=False; D.Link:='http://webdelphi.ru'; D.Snippet:='Краткое описание'; D.Description:='Описание подробное'; D.SaveToFile('Doc.kml'); finally FreeAndNil(D) end; end;
После запуска приложения и клика по кнопке, рядом с exe-файлом будет создан файл Doc.kml. Если его открыть в Google Earth можно увидеть следующее:
Как можно видеть на рисунке — название документа представляет собой гиперссылку. Если нажать мышкой по названию документа, то по центру карты откроется описание документа, которое мы задавали в свойстве Description:
При этом, никто нам не запрещает добавить в описание документа и HTML-разметку и получить вот такое описание документа:
О том, как добавлять в описание HTML и JavaScript мы ещё поговорим в следующих статьях блога, а пока того, что уже сделано вполне достаточно, чтобы перейти к цели статьи — создавать на карте метки.
Добавляем метку на карту Google Earth
В принципе, у нас уже всё готово для того, чтобы добавить на карту любое количество меток. Все метки будут находится в корне нашего документа. Например, можно написать вот такой код:
procedure TForm10.Button1Click(Sender: TObject); var D: TKmlDocument; P:TKmlPlacemark; begin D:=TKmlDocument.Create; try D.Name:='Мой первый документ'; D.Visibility:=True; D.Open:=True; //добавляем новую метку D.Placemark.Add(TKmlPlacemark.Create); P:=D.Placemark.Last; P.Visibility:=True; P.Id:='placemark_id'; P.Name:='Моя метка'; P.Description:='Описание метки'; P.Point.Extrude:=True; P.Point.AltitudeMode:=amAbsolute; P.Point.Coordinates.Latitude:=59.831795; P.Point.Coordinates.Longtitude:=29.814903; P.Point.Coordinates.Altitude:=5; //сохраняем файл на диск D.SaveToFile('Doc.kml'); finally FreeAndNil(D) end; end;
выполнить этот код и получить в Google Earth вот такую картинку:
Нашей метке назначена иконка по умолчанию («кнопка»). Однако, сегодня мы и не ставили перед собой задачи как-то разукрасить нашу карту. Об этом мы поговорим позднее, когда разберемся со всеми возможными географическими примитивами в Google Earth. А пока, нам осталось оформить весь код в виде небольшой демки.
Демонстрационное приложение
Внешний вид демонстрационного приложения будет таким:
Можно добавить в список любое количество меток, которые необходимо добавить на карту в Google Earth, метки можно «выдавливать» над уровнем земли, давать меткам короткие и подробные описания, а также скрывать метки на карте.
Например, ниже в Google Earth была задана точка расположенная на высоте 10 м. от уровня земной поверхности:
В архиве вы найдете исходник демонстрационного приложения и модуль kml.pas, содержащий классы, разработанные в этой и прошлой статье про KML Reference.
Следующая статья: «KML Reference в Delphi по-русски #3: Учимся рисовать линии и полигоны»
Книжная полка
Описание Подробно рассматривается библиотека FM, позволяющая создавать полнофункциональное программное обеспечение для операционных систем Windows и OS X, а также для смартфонов и планшетных компьютеров, работающих под управлением Android и iOS
|
||
Описание: Рассмотрены практические вопросы по разработке клиент-серверных приложений в среде Delphi 7 и Delphi 2005 с использованием СУБД MS SQL Server 2000, InterBase и Firebird. Приведена информация о теории построения реляционных баз данных и языке SQL. Освещены вопросы эксплуатации и администрирования СУБД.
|
||
Название: О чем не пишут в книгах по Delphi
Описание: Рассмотрены малоосвещенные вопросы программирования в Delphi. Описаны методы интеграции VCL и API. Показаны внутренние механизмы VCL и приведены примеры вмешательства в эти механизмы. Рассмотрено использование сокетов в Delphi: различные режимы их работы, особенности для протоколов TCP и UDP и др.
|