Если вы использовали когда-нибудь 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. Круг
2. Наводим мышь на круг — получаем окружность:
Чтобы убедиться, что наш(а) круг (окружность) имеет действительно заданный нами размер (в примере радиус равен 500 метрам) измерим диаметр окружности в Google Earth:
Небольшое отклонение в размере связано с погрешностью измерения — измерял при большом масштабе, поэтому мог на несколько сантиметров промахнуться мышкой.
Итог
Как вы могли заметить, в классе TKmlCircle метод расчёта координат границы вынесен отдельно в private, а метод Calculate определен с директивой overload.
Используя два этих метода, вы можете создавать любые другие фигуры на карте Google Earth, используя KML в Delphi, например, октагон:
Да хоть квадрат, задавая приращение AIncrement в методе CalcOutBound равным 90 градусов. А, немного дополнив публичный метод Calculate можно рисовать сектора. В общем — поле действий достаточно широкой :)
Модуль с классом TKmlCircle можно скачать тут:
Следующая статья: KML Reference в Delphi по-русски #9: Как нарисовать прямую линию, зная длину, начальные координаты и направление?
Книжная полка
Описание Подробно рассматривается библиотека 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 и др.
|