уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.

Если вы использовали когда-нибудь Google Earth, то могли обратить внимание на то, что инструменты для рисования своих объектов, даже в Pro-версии достаточно ограничены. Можно нарисовать линию, полигон, кривую, поставить метку, но, что касается других фигур, например тех же кругов и окружностей, то здесь Google Earth нам ничем помочь не может — надо по точкам самому вырисовывать необходимый элемент. Однако, используя возможности KML нарисовать круг в Google Earth, как оказалось, не так уж и сложно. Именно этим мы сегодня и займемся — научимся рисовать круг заданного размера в Google Earth, используя KML в Delphi.

Вводная часть

Что касается того, как нарисовать круг в Delphi, в принципе, то тут, думаю ни у кого, кроме новичков вопросов нет. Есть Canvas.Ellipse, можно нарисовать (если уж сильно приспичит) по-пиксельно, воспользовавшись формулами поворота относительно начала координат. При этом, работаем мы в декартовой системе координат, которая известна всем ещё со школы.

Что же касается Google Earth и KML, то здесь мы имеем дело с системой WGS-84 — всемирной системой геодезических параметров Земли 1984 года, в число которых входит система геоцентрических координат. И задача рисования круга или окружности становится не такой уж и тривиальной.

Надо сказать, что этой задачей я занимался примерно год назад, когда только начинал разбираться с тем, как преобразовывать географические координаты из одной системы координат в другую, например, из WGS-84 в ПЗ-90,  из WGS-84 в обычную декартову систему координат с заданной точкой «привязки» и так далее. Перерыл тогда много всяких интересных форумов по этому вопросу и точно помню, что ответ в виде исходника на Pascal был найден на форуме http://geodesist.ru/, но вот точную ссылку, к сожалению, потерял.

Поэтому говорю как есть: исходный код, в котором рассчитываются географические координаты при повороте точки — не мой. Он взят с форума «Геодезист«. Буду признателен, если кто-нибудь в комментариях кинет ссылочку на тему форума, где этот код подробно рассматривался.

Круг и окружность в KML

В одной из статей, посвященных KML в Delphi, мы учились рисовать различные элементы на карте. В числи прочих, мы рисовали так называемый полигон (Polygon), который состоит из одной или нескольких замкнутых линий (LinearRing). Именно этот элемент KML и и будем использовать в качестве базового для того, чтобы нарисовать круг или окружность в KML.

Ну, а чтобы сделать из круга окружность и наоборот, мы воспользуемся возможностями стиля PolyStyle — если наш полигон будет использовать заливку, то на карте будет изображен круг, если нет — окружность. Всё просто :)

Что нам необходимо, чтобы нарисовать окружность? Во-первых, нам необходимы координаты центра. Во-вторых, нам необходим радиус окружности (R). Далее, нам необходимо «поставить» на карте точку, которая будет находится на расстоянии R от центра и поворачивать эту точку на угол от 0 до 360 градусов, сохраняя каждый раз координаты точки в списке координат, который мы и передадим нашему полигону в качестве внешней границы. При этом, направление поворота роли не сыграет (в случае рисования окружности).

Теперь реализуем всё, что сказано выше в виде кода Delphi.

Класс TKmlCircle можно представить следующим образом:

type
  TKmlCircle = class(TKmlPolygon)
  private
    FRadius: single;
    FCenter: TKmlCoordinate;
    procedure CalcOutBound(AStart, AEnd, AIncrement: single; ABound: TKmlLinearRing);
  public
    constructor Create; override;
    destructor Destroy; override;
    procedure Calculate; overload;
    property Radius: single read FRadius write FRadius;
    property Center: TKmlCoordinate read FCenter write FCenter;
  end;

Основной метод в этом классе — CalcOutBound, который рассчитывает координаты всех точек заданной границы полигона. Выглядит он следующим образом (код расчета координат не мой):

uses Math;
 
const
  сa = 6378137; // большая полуось эллипсоида в WGS-84
  сb = 298.257223563; // 1/cb = сжатие эллипсоида в WGS-84
 
procedure TKmlCircle.CalcOutBound(AStart, AEnd, AIncrement: single; ABound: TKmlLinearRing);
var
  lat_center, lon_center, d_rad: double;
  i: double;
  radial, lat_rad, lon_rad_tmp, lon_rad: double;
  Limit: double;
  AStop: boolean;
begin
  AStop := false;
  ABound.Coordinates.Clear;
 
  lat_center := DegToRad(FCenter.Latitude);
  lon_center := DegToRad(FCenter.Longtitude);
  d_rad := FRadius / сa;
 
  Limit := AEnd;
  i := AStart;
  while i <= Limit do begin if AStop then break; radial := DegToRad(i); lat_rad := ArcSin(Sin(lat_center) * Cos(d_rad) + Cos(lat_center) * Sin(d_rad) * Cos(radial)); lon_rad_tmp := ArcTan2(Sin(radial) * Sin(d_rad) * Cos(lat_center), Cos(d_rad) - Sin(lat_center) * Sin(lat_rad)); lon_rad := FMod((lon_center + lon_rad_tmp + PI), (2 * PI)) - PI; //добавляем координаты точки в список ABound.AddCoord(RadToDeg(lon_rad), RadToDeg(lat_rad), 0); if ((i + AIncrement) > Limit) and ((AIncrement - Limit) > AIncrement) then
    begin
      AIncrement := Limit;
      AStop := true;
    end
    else
      i := i + AIncrement;
  end;
  //замыкаем линию
  ABound.CloseLine;
end;

В публичном методе Calculate этот метод вызывается следующим образом:

procedure TKmlCircle.Calculate(AIncrement: single);
begin
  CalcOutBound(0, 360, 1, OuterBoundaryIs);
end;

Чтобы продемонстрировать как работает класс TKmlCircle, напишем вот такой код в демонстрационном приложении из этой статьи:

procedure TForm10.Button2Click(Sender: TObject);
var
  Style: TKmlStyle;
begin
  ADocument.Placemark.Add(TKmlPlacemark.Create(TKmlCircle));
  with ADocument.Placemark.Last do
  begin
    Name := 'Наведи мышь - получишь окружность';
    Snippet := 'Круг и окружность в KML';
    Description := 'При наведении мыши на круг он превратится в окружность';
    Visibility := True;
    TKmlCircle(Geometry).Radius := 500;
    TKmlCircle(Geometry).Center.Latitude := 37.826988;
    TKmlCircle(Geometry).Center.Longtitude := -122.365662;
    TKmlCircle(Geometry).Calculate;
    //карта стилей будет называться StyleMap
    StyleUrl := '#StyleMap';
  end;
 
  //стиль в состоянии normal
  Style := TKmlStyle.Create;
  ADocument.Styles.Add(Style);
  Style.ID := 'NormalStyle';
  Style.AddStyle(TKmlLineStyle);
  //Задаем стиль линии
  TKmlLineStyle(Style.SubStyle.Last).Color := 'ff00ff00';
  TKmlLineStyle(Style.SubStyle.Last).OuterColor := '7fff00ff';
  TKmlLineStyle(Style.SubStyle.Last).ColorMode := cmNormal;
  TKmlLineStyle(Style.SubStyle.Last).Width := 2;
  //задаем стиль полигона
  Style.AddStyle(TKmlPolyStyle);
  TKmlPolyStyle(Style.SubStyle.Last).Color := '7f0000ff';
  TKmlPolyStyle(Style.SubStyle.Last).ColorMode := cmNormal;
  //заливаем полигон - получим круг
  TKmlPolyStyle(Style.SubStyle.Last).Fill := True;
  TKmlPolyStyle(Style.SubStyle.Last).Outline := True;
 
  //стиль в состоянии highlight
  Style := TKmlStyle.Create;
  ADocument.Styles.Add(Style);
  Style.ID := 'HiglightStyle';
  //стиль линии
  Style.AddStyle(TKmlLineStyle);
  TKmlLineStyle(Style.SubStyle.Last).Color := '7f0000ff';
  TKmlLineStyle(Style.SubStyle.Last).OuterColor := '7fff00ff';
  TKmlLineStyle(Style.SubStyle.Last).ColorMode := cmNormal;
  TKmlLineStyle(Style.SubStyle.Last).Width := 4;
  //стиль полигона
  Style.AddStyle(TKmlPolyStyle);
  TKmlPolyStyle(Style.SubStyle.Last).Color := '7f0000ff';
  TKmlPolyStyle(Style.SubStyle.Last).ColorMode := cmNormal;
  //убираем заливку - получаем окружность
  TKmlPolyStyle(Style.SubStyle.Last).Fill := False;
  TKmlPolyStyle(Style.SubStyle.Last).Outline := True;
  //добавляем карту стилей в документ
  ADocument.StyleMaps.Add(TKmlStyleMap.Create('StyleMap', '#NormalStyle', '#HiglightStyle'));
end;

Результатом выполнения этого кода будет вот такая карта в Google Earth:
1. Круг

Круг в KML

2. Наводим мышь на круг — получаем окружность:

Окружность в KML

Чтобы убедиться, что наш(а) круг (окружность) имеет действительно заданный нами размер (в примере радиус равен 500 метрам) измерим диаметр окружности в Google Earth:

Диаметр окружности

Небольшое отклонение в размере связано с погрешностью измерения — измерял при большом масштабе, поэтому мог на несколько сантиметров промахнуться мышкой.

Итог

Как вы могли заметить, в классе TKmlCircle метод расчёта координат границы вынесен отдельно в private, а метод Calculate определен с директивой overload.

Используя два этих метода, вы можете создавать любые другие фигуры на карте Google Earth, используя KML в Delphi, например, октагон:

Октагон

Да хоть квадрат, задавая приращение AIncrement в методе CalcOutBound равным 90 градусов. А, немного дополнив публичный метод Calculate можно рисовать сектора. В общем — поле действий достаточно широкой :)

Модуль с классом TKmlCircle можно скачать тут:

Исходники — Google API

 

Книжная полка

Описание Подробно рассматривается библиотека FM, позволяющая создавать полнофункциональное программное обеспечение для операционных систем Windows и OS X, а также для смартфонов и планшетных компьютеров, работающих под управлением Android и iOS
купить книгу delphi на ЛитРес
Описание: Рассмотрены практические вопросы по разработке клиент-серверных приложений в среде Delphi 7 и Delphi 2005 с использованием СУБД MS SQL Server 2000, InterBase и Firebird. Приведена информация о теории построения реляционных баз данных и языке SQL. Освещены вопросы эксплуатации и администрирования СУБД.
купить книгу delphi на ЛитРес
Описание: Рассмотрены малоосвещенные вопросы программирования в Delphi. Описаны методы интеграции VCL и API. Показаны внутренние механизмы VCL и приведены примеры вмешательства в эти механизмы. Рассмотрено использование сокетов в Delphi: различные режимы их работы, особенности для протоколов TCP и UDP и др.
купить книгу delphi на ЛитРес
0 0 голоса
Рейтинг статьи
уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.
Подписаться
Уведомить о
0 Комментарий
Межтекстовые Отзывы
Посмотреть все комментарии