на главную

о компании

проекты

партнёры

контакты

прайс-лист

карта сайта

     

 

ГИС ИнГео API

на предыдущую страницу

 

Вспомогательные классы расширения

Создание расширения

Ничего не делающее расширение

Создание проекта

Получение идентификатора расширения

Реализация расширения

Регистрация расширения в ГИС

Анализ текста программы

Добавление команд в меню и на панель инструментов

Анализ текста программы

Создание редактора. Контекстное меню карты

Анализ текста программы

Извещения ГИС. Управление окнами

Реализация расширения в виде динамически подгружаемой библиотеки

Отладка расширения

Спецификация

Описание интерфейсов и вспомогательных классов

Описание типов и констант

Регистрация расширений в реестре

 

 


Вспомогательные классы расширения

 

Выше был рассмотрен случай, когда мы работаем с предоставленным нам ГИС интерфейсом, но с другой стороны мы создаём интерфейсы расширения, которые тоже должны быть построены по стандартам OLE, то есть должны возвращать результат успешной/неуспешной работы через HRESULT и плюс, уже по стандартам расширения ГИС ИнГео, если произошла ошибка, то сохранять информацию об ошибке через IM2ErrorInfo, чтобы тот кто вызвал наш метод мог проинформировать пользователя, о том что за ошибка произошла. Для облегчения создания интерфейсов расширения были написаны вспомогательные классы расширения. Эти классы берут на себя работу по передаче информации об ошибках плюс делают часть необходимых действий. Рассмотрим это на примере интерфейса IM2AddOn.

 

Сам интерфейс:
IM2AddOn = interface(IUnknown)
['{C8F844A1-1878-11D0-9DF1-444553540000}']
function i_GetAttrs(out AnAttrs: TM2AddOnAttrs): HResult; stdcall;
function i_Initialize(const AManager: IM2AddOnManager): HResult; stdcall;
...
end;

Вспомогательный класс:
TM2CustomAddon = class
private
...
public
constructor
Create(const AClsID: TCLSID; const AName: TM2Name;
const AHint: TM2Hint; AnOptions: TM2AddOnOptions);
destructor Destroy; override;
...
procedure GetAttrs(out AnAttrs: TM2AddOnAttrs); virtual;
procedure Initialize(const AManager: IM2AddonManager); virtual;
...
property AddonManager: TM2AddonManager read FAddonManager;
property GISDatabase: TM2GISDatabase read FGISDatabase;
property MapProject: TM2MapProject read FMapProject;
property MapObjects: TM2MapObjects read FMapObjects;
property MapView: TM2MapView read FMapView;
property GeometryLib: TM2GeometryLib read FGeometryLib;
property Preferences: TM2Preferences read FPreferences;
end;

 

Чтобы реализовать интерфейс IM2AddOn Вам надо породить свой класс от вспомогательного класса TM2CustomAddon. Класс TM2CustomAddon внутренне создает объект класса TM2AddonFactory (напоминаю, что расширение является сервером OLE), который используется клиентом сервера (то есть ГИС ИнГео) для получения ссылки на объект, реализующий интерфейс IM2AddOn. Этот интерфейс реализован классом TM2AddonProxy. Реализация заключается всего лишь в том, что он вызывает виртуальные методы TM2CustomAddon, но этот вызов он заключает в блок, который отлавливает исключительные ситуации. Например, метод TM2AddonProxy.GetAttrs вызывает метод TM2CustomAddon.GetAttrs и если внутри того порождается исключительная ситуацию, то он через IM2ErrorInfo сохраняет информацию об ошибке для ГИС и возвращает код ошибки, иначе - код успешного завершения S_OK. Остальные методы работают аналогично. Методы TM2CustomAddon реализуют логику по умолчанию. Можно переписать эти методы в том классе, который был порожден от TM2CustomAddon, если надо реализовать свою логику. При инициализации расширения TM2CustomAddon.Initialize запрашивает у IM2AddOnManager следующие интерфейсы: IM2GISDatabase, IM2MapProject, IM2MapObjects, IM2MapView, IM2GeometryLib, IM2Preferences, доступ к которым можно осуществлять соответственно через свойства: GISDatabase, MapProject, MapObjects, MapView, GeometryLib, Preferences. При деинициализации TM2CustomAddon.Uninitialize освобождает эти интерфейсы.

Резюмируя, можно сказать, что работу с интерфейсами, полученными от ГИС надо осуществлять через объекты-помощники. Свои же интерфейсы (интерфейсы расширения) надо реализовывать при помощи вспомогательных классов расширения.

 

Создание расширения

 

Рассмотрим процесс создания расширения на примере. Этот пример написан на Delphi 3. Расширение будет сначала очень простым и малофункциональным, но постепенно мы будем его усложнять, реализовывая различные функции.

 

Ничего не делающее расширение

 

Создадим расширение (в виде LocalServer’а), которое будет пока только загружаться ГИС’ом, и больше ничего не будет делать. Для этого необходимо проделать следующие шаги:

  1. Создать проект в Delphi.

  2. Получить идентификатор расширения.

  3. Создать объект, реализующий интерфейс IM2AddOn.

  4. Зарегистрировать расширение в ГИС.

Создание проекта

 

В Delphi в меню выберите команду File|New Application. Delphi создаст новый проект, состоящий из одной формы. Дадим этой форме имя MainForm и сохраним её в файле Main.pas. Сам же проект сохраним в файле FirstApp.dpr. Далее в меню выберите команду File|New... Появится диалог New Items, в котором укажите, что хотите создать новый модуль (Unit) и нажмите OK. Сохраните его под именем Addon.pas.

 

Получение идентификатора расширения

 

Так как расширение является сервером OLE, то значит оно должно иметь уникальный идентификатор (GUID). Этот идентификатор может быть получен либо с помощью программы GUIDGen.exe (входит в состав Win32 SDK, а также в Microsoft Visual C++), либо в редакторе Delphi нажмем комбинацию клавиш Ctrl+Shift+G. Полученный GUID вставим в файл Addon.pas:


const
CLSID_First: TGUID = '{6D067FB2-3C7F-11D0-9DF1-444553540000}';

 

Реализация расширения

 

Ниже приведены исходные тексты программы.
Файл FirstApp.dpr.
program FirstApp;
uses
Forms,
Main in 'Main.pas' {MainForm},
Addon in 'Addon.pas';
{$R *.RES}
begin
Application.Initialize;
Application.CreateForm(TMainForm, MainForm);
Application.Run;
end.

Файл Addon.pas.
unit Addon;
interface
uses

Windows, ActiveX, M2Addon, M2AddonD, M2AddonImp;
const
CLSID_First: TGUID = '{6D067FB2-3C7F-11D0-9DF1-444553540000}';
type
TFirstAddon = class(TM2CustomAddon)
public
constructor Create;
end;
var
gAddon: TFirstAddon = nil;
implementation
{ TFirstAddon }
constructor TFirstAddon.Create;
begin
inherited Create(CLSID_First, 'FirstAddon', 'Пример расширения для ГИС', 0);
end;
initialization
gAddon := TFirstAddon.Create;
finalization
gAddon.Free;
end.

 

Регистрация расширения в ГИС

 

Теперь надо указать ГИС ИнГео, чтобы она подключала наше расширение к себе. Для этого нам надо зарегистрировать созданное расширение. Регистрация состоит из двух частей. Во-первых, регистрируем нашу программу как сервер OLE, добавляя в реестр (Registry) следующие строки:
HKEY_CLASSES_ROOT\CLSID\{6D067FC1-3C7F-11D0-9DF1-444553540000}
= Пример расширения для ГИС
HKEY_CLASSES_ROOT\CLSID\{6D067FC1-3C7F-11D0-9DF1-444553540000}\
LocalServer32 = C:\SRC\SDK\DOCSAMPL\FirstApp.exe
Во-вторых, информируем ГИС о нашем расширении, добавляя в реестр строку:
HKEY_LOCAL_MACHINE\SOFTWARE\Integro\Ingeo\AddOns\
{6D067FC1-3C7F-11D0-9DF1-444553540000} = Пример расширения для ГИС

 

Регистрацию можно сделать как вручную, используя программу RegEdit.exe (входит в состав Windows), либо из ГИС ИнГео (это предпочтительно). Запустите ГИС, откройте базу данных. Выберите команду меню Файл|Модули расширения. Появится диалог со списком зарегистрированных расширений ГИС. Нажмите на кнопку Добавить. В появившемся диалоге выбора файла выберите Вашу программу.

 

Анализ текста программы

 

Порождаем класс объекта, реализующего интерфейс IM2AddOn от вспомогательного класса TM2CustomAddon. В конструкторе базовому классу передаем свои параметры:
inherited Create(CLSID_First, 'FirstAddon',
'Пример расширения для ГИС', 0);

а именно: идентификатор нашего сервера, название расширения, описание для чего предназначено расширение и какие извещения необходимо присылать расширению от ГИС. Пока извещения нас не интересуют, поэтому передаем 0.

Больше наше расширение ничего не делает. Логику по умолчанию за нас реализует класс TM2CustomAddon.

Единственное что мы делаем - это создаем объект расширения в initialization, а в finalization его удаляем.

 

Добавление команд в меню и на панель инструментов

 

Теперь добавим команды в меню и на панель инструментов ГИС. Для этого необходимо проделать следующие шаги:

  1. Нарисовать для кнопки команды битовую карту (bitmap, растровое изображение) и подключить её к программе.

  2. Создать массив типа TM2MacroAttrs для команд.

  3. При создании расширения загрузить из ресурсов битовые карты для кнопок.

  4. Перегрузить метод GetMacroList.

  5. Перегрузить метод GetBitmap.

  6. Перегрузить метод ExecuteMacro.

  7. При уничтожении расширения выгрузить битовые карты.

Файл Addon.pas.
unit Addon;
interface
uses
Windows, ActiveX, M2Addon, M2AddonD, M2AddonImp;
const
CLSID_First: TGUID = '{6D067FB2-3C7F-11D0-9DF1-444553540000}';
type
TFirstAddon = class(TM2CustomAddon)
public
constructor Create;
destructor Destroy; override;
function GetMacroList: IM2MacroAttrsList; override;
function GetBitmap(const aStm: IStream; aBitmap: Longint): Boolean; override;
procedure ExecuteMacro(aCmd: Longint; const aParams: TM2String); override;
procedure DistanceCalc;
end;
var
gAddon: TFirstAddon = nil;
implementation
{$R Cmds.res}
uses
SysUtils, AUtils;
const
//Команда начать измерение расстояния
cmDistanceCalc = 1;
kMacros: array [0..0] of TM2MacroAttrs = (
( Command: cmDistanceCalc;
Name: 'DistanceCalc';
Hint: 'Измерить расстояние';
MenuPath: 'Пример\Измерить &расстояние';
MenuShortCut: 0;
ToolbarName: 'Пример';
ToolbarGroup: 'Пример';
Bitmap: 0)
);
{ TFirstAddon }
constructor TFirstAddon.Create;
var
i: Integer;
begin
inherited Create(CLSID_First, 'FirstAddon', 'Пример расширения для ГИС', 0);
for i := Low(kMacros) to High(kMacros) do
if kMacros[i].ToolbarName <> '' then
kMacros[i].Bitmap := LoadBitmap(hInstance, PChar(AnsiUpperCase(kMacros[i].Name)));
end;
destructor TFirstAddon.Destroy;
var
i: Integer;
begin
for i := Low(kMacros) to High(kMacros) do
if kMacros[i].Bitmap <> 0 then
DeleteObject(kMacros[i].Bitmap);
inherited Destroy;
end;
function TFirstAddon.GetMacroList: IM2MacroAttrsList;
var
aList: TM2MacroAttrsList;
i: Integer;
begin
aList.OleObject := inherited GetMacroList;
for i := Low(kMacros) to High(kMacros) do
aList.Add(kMacros[i]);
Result := aList.OleObject;
end;
function TFirstAddon.GetBitmap(const aStm: IStream; aBitmap: Longint): Boolean;
begin
Result := aBitmap <> 0;
if Result then
SaveBitmapToIStream(aBitmap, aStm);
end;
procedure TFirstAddon.ExecuteMacro(aCmd: Longint; const aParams: TM2String);
begin
case aCmd of
cmDistanceCalc:
DistanceCalc;
end;
end;
procedure TFirstAddon.DistanceCalc;
begin
MessageBeep(-1);
end;
initialization
gAddon := TFirstAddon.Create;
finalization
gAddon.Free;
end.

 

Анализ текста программы.

 

Создали файл ресурсов Cmds.res. В этом файле нарисовали битовую карту (bitmap) для нашей команды размером 16x16 в 16 цветах и сохранили её под именем команды “DISTANCECALC”. Далее добавили в текст программы строку {$R Cmds.res}, которая информирует компилятор о том, что надо этот файлов ресурсов подключить к программе.

Потом определили константу kDistanceCalc = 1 для нашей команды. Создали константный массив kMacros из элементов типа TM2MacroAttrs, где описали команду. Зачем использовали массив, если у нас только одна команда? Потому что, если потом понадобятся ещё команды, то мы просто добавим их в этот массив. Элемент TM2MacroAttrs описывает куда необходимо встроить команду. В данном случае поле MenuPath определяет, что команда добавляется в меню “Пример” как пункт “Измерить расстояние”. Заметьте, что меню и пункт отделяются друг от друга обратной косой чертой. ToolbarName указывает, что кнопка будет находиться на панели инструментов с именем “Пример”, а ToolbarGroup - что в группе “Пример”. Эти имена могут быть любыми. Если MenuPath содержит пустую строку, то команда не добавляется в меню. Аналогично и для ToolbarName.

В конструкторе расширения мы загружаем битовую карту для кнопки команды, а в деструкторе освобождаем её.

В перегруженном методе GetMacroList мы первым делом вызываем метод базового класса. Этот метод просто возвращает пустой список атрибутов команд. Далее мы добавляем в этот список нашу команду и возвращаем список. Здесь необходимо отметить, что под Windows NT нельзя использовать хэндлы (handles) GDI ресурсов, переданные из одной программы в другую, следовательно, передача хэндла на битовую карту команды будет приводить к ошибке. Ошибка не будет возникать под Windows 95 либо если расширение создано в виде DLL - библиотеки. Раньше ГИС это не учитывала и использовала переданный хэндл. Теперь это исправлено, путём добавления нового метода GetBitmap в интерфейс IM2AddOn. ГИС вызывает его, передавая в aBitmap значение, которое ей было передано в aMacro.Bitmap и интерфейс aStm типа IStream для записи данных битовой карты. IStream - это стандартный интерфейс OLE, предназначенный для работы с потоками данных. В этот поток надо записать информацию о битовой карте в формате: 4-х байтовое целое число равное размеру битовой карты, потом - содержимое самой битовой карты так, как если бы она записывалась в файл формата bmp. В нашем примере мы вызываем функцию SaveBitmapToIStream, которая сохраняет битовую карту в поток в описанном выше формате. Эта функция реализована в файле AUtils.pas.

Теперь перегружаем метод ExecuteMacro. Этот метод вызывает тогда, когда пользователь выбирает нашу команду в меню или на панели инструментов. Определяем какая команда должна быть выполнена. Если kDistanceCalc, то вызываем метод DistanceCalc. Этот метод пока только издаёт звуковой сигнал.

Теперь скомпилируем программу и запустим ГИС. В ГИС появится меню расширения, а также панель с кнопкой. При нажатии на кнопку в динамике компьютера будет издаваться звуковой сигнал.

 

Создание редактора. Контекстное меню карты

 

Добавим в проект новый модуль Editor.pas.


Файл Editor.pas.
unit Editor;
interface
uses
Windows, M2Addon, M2AddonD, Addon;
type
TDistanceCalculator = class(TM2CustomEditor)
private
FAddon: TFirstAddon;
FCurPt: TM2Point;
FPoints: PM2Point;
FPtCount: Integer;
FPtAlloc: Integer;
FOrto: Boolean;
procedure DrawAllDistance;
procedure DrawLastDistance;
function AdjustPoint(aPt: TPoint; aShift: Boolean): TM2Point;
procedure RecalcDistance;
public
constructor Create(anAddon: TFirstAddon);
destructor Destroy; override;
function GetEditorOptions: TM2EditorOptions; override;
procedure HideDragging; override;
procedure ShowDragging(aMouse: TPoint); override;
procedure MouseMove(aShift: TM2ShiftState; aMouse: TPoint); override;
procedure MouseDown(aButton: TM2MouseButton; aShift: TM2ShiftState; aMouse: TPoint); override;
function GetContextMacroList(aMouse: TPoint): IM2ContextMacroAttrsList; override;
procedure ExecuteMacro(aCmd: Longint; const aParams: TM2String); override;
end;
var
gDistanceCalculator: TDistanceCalculator = nil;
implementation
uses
Controls, SysUtils, Graphics;
const
cmCancelLast = 1;
cmCancel = 2;
cmOrto = 3;
cmDrawHandlers = 4;
kContextMacros: array [0..4] of TM2ContextMacroAttrs = (
( Command: 0;
Name: '';
Hint: '';
Path: '-';
ShortCut: 0;
State: 0; ),
( Command: cmCancelLast;
Name: 'CancelLast';
Hint: '';
Path: 'Отменить последнюю точку';
ShortCut: 0;
State: 0; ),
( Command: cmCancel;
Name: 'Cancel';
Hint: '';
Path: 'Завершить измерение';
ShortCut: 0;
State: 0; ),
( Command: cmOrto;
Name: 'Orto';
Hint: '';
Path: 'Под прямым углом';
ShortCut: 0;
State: 0; ),
( Command: cmDrawHandlers;
Name: 'DrawHandlers';
Hint: '';
Path: 'Нарисовать в узлах метки';
ShortCut: 0;
State: 0; )
);
{ TDistanceCalculator }
type
PM2PointArray = ^TM2PointArray;
TM2PointArray = array [0..1024] of TM2Point;
const
kPen: TM2Pen = (
Style: kpsSolid;
WidthInMM: 0;
ForZoomScale: 1/10000;
Color: clWhite;
);
constructor TDistanceCalculator.Create(anAddon: TFirstAddon);
begin
inherited Create;
FAddon := anAddon;
end;
destructor TDistanceCalculator.Destroy;
begin
gDistanceCalculator := nil;
FreeMem(FPoints);
inherited Destroy;
end;
procedure TDistanceCalculator.DrawAllDistance;
begin
if FPtCount > 1 then
FAddon.MapView.DrawPolyline(R2_XORPEN, kPen, FPoints, FPtCount);
DrawLastDistance;
end;
procedure TDistanceCalculator.DrawLastDistance;
var
aPolyline: array [0..1] of TM2Point;
begin
if FPtCount > 0 then begin
aPolyline[0] := PM2PointArray(FPoints)^[FPtCount-1];
aPolyline[1] := FCurPt;
FAddon.MapView.DrawPolyline(R2_XORPEN, kPen, @aPolyline, 2);
end;
end;
function TDistanceCalculator.AdjustPoint(aPt: TPoint; aShift: Boolean): TM2Point;
var
p1, p2: TM2Point;
k1, k2, b1, b2: double;
begin
Result := FAddon.MapView.PointFromDevice(aPt);
if FPtCount > 0 then begin
p2 := PM2PointArray(FPoints)^[FPtCount-1];
if aShift then begin
if Abs(Result.X-p2.X) > Abs(Result.Y-p2.Y) then
Result.Y := p2.Y
else
Result.X := p2.X;
end
else if FOrto and (FPtCount > 1) then begin
p1 := PM2PointArray(FPoints)^[FPtCount-2];
if p1.X = p2.X then
Result.Y := p2.Y
else if p1.Y = p2.Y then
Result.X := p2.X
else begin
k1 := (p2.Y-p1.Y)/(p2.X-p1.X);
b1 := Result.Y-k1*Result.X;
k2 := -1.0/k1;
b2 := p2.Y-k2*p2.X;
Result.X := (b1-b2)/(k2-k1);
Result.Y := k1*Result.X+b1;
end;
end;
end;
end;
procedure TDistanceCalculator.RecalcDistance;
var
aSum, aLastSum: double;
src: PM2Point;
aPrevPt: TM2Point;
i: Integer;
begin
aLastSum := 0;
aSum := 0;
if FPtCount > 0 then begin
src := FPoints;
aPrevPt := src^;
Inc(src);
for i := 1 to FPtCount-1 do begin
aSum := aSum+Sqrt(Sqr(src^.X-aPrevPt.X)+Sqr(src^.Y-aPrevPt.Y));
aPrevPt := src^;
Inc(src);
end;
aLastSum := Sqrt(Sqr(FCurPt.X-aPrevPt.X)+Sqr(FCurPt.Y-aPrevPt.Y));
aSum := aSum+aLastSum;
end;
FAddon.MapView.SetStatusText(0, Format('%.2f/%.2f', [aLastSum, aSum]));
end;
function TDistanceCalculator.GetEditorOptions: TM2EditorOptions;
begin
Result := eopProcessPhase or eopMouseMove or eopMouseDown or
eopContextMenu or eopDragging;
end;
procedure TDistanceCalculator.HideDragging;
begin
DrawAllDistance;
end;
procedure TDistanceCalculator.ShowDragging(aMouse: TPoint);
begin
DrawAllDistance;
end;
procedure TDistanceCalculator.MouseMove(aShift: TM2ShiftState; aMouse: TPoint);
var
aNewPt: TM2Point;
begin
aNewPt := AdjustPoint(aMouse, (kssShift and aShift) <> 0);
DrawLastDistance;
FCurPt := aNewPt;
DrawLastDistance;
RecalcDistance;
FAddon.MapView.SetCursor(crCross); //Устанавливаем курсор
FAddon.MapView.StopHandler; //Запрещаем другим получать это извещение
end;
procedure TDistanceCalculator.MouseDown(aButton: TM2MouseButton; aShift: TM2ShiftState; aMouse: TPoint);
var
aNewPt: TM2Point;
begin
if aButton = kmbLeft then begin
FAddon.MapView.StopHandler;
if (kssDouble and aShift) <> 0 then begin
ExecuteMacro(cmCancel, '');
exit;
end;
aNewPt := AdjustPoint(aMouse, (kssShift and aShift) <> 0);
if FPtCount = FPtAlloc then begin
ReallocMem(FPoints, (FPtAlloc+8)*sizeof(TM2Point));
Inc(FPtAlloc, 8);
end;
DrawLastDistance;
FCurPt := aNewPt;
DrawLastDistance;
PM2PointArray(FPoints)^[FPtCount] := aNewPt;
Inc(FPtCount);
FCurPt := AdjustPoint(aMouse, (kssShift and aShift) <> 0);
DrawLastDistance;
RecalcDistance;
end;
end;
function TDistanceCalculator.GetContextMacroList(aMouse: TPoint): IM2ContextMacroAttrsList;
var
aList: TM2ContextMacroAttrsList;
aState: TM2MenuState;
begin
aList.OleObject := inherited GetContextMacroList(aMouse);
if FPtCount > 0 then
aState := 0
else
aState := mnsDisabled;
kContextMacros[cmCancelLast].State := aState;
kContextMacros[cmDrawHandlers].State := aState;
if FOrto then
kContextMacros[cmOrto].State := mnsChecked
else
kContextMacros[cmOrto].State := 0;
aList.Add(kContextMacros[cmCancelLast]);
aList.Add(kContextMacros[cmCancel]);
aList.Add(kContextMacros[0]);
aList.Add(kContextMacros[cmOrto]);
aList.Add(kContextMacros[cmDrawHandlers]);
FAddon.MapView.StopHandler;
Result := aList.OleObject;
end;
procedure TDistanceCalculator.ExecuteMacro(aCmd: Longint; const aParams: TM2String);
var
src: PM2Point;
i: Integer;
begin
case aCmd of
cmCancel: begin
DrawAllDistance;
//В следующей строчке редактор будет удален!!!
//Поэтому нельзя обращаться к данным редактора
FAddon.MapView.RemoveEditor(IEditor);
end;
cmCancelLast:
if FPtCount > 0 then begin
DrawAllDistance;
Dec(FPtCount);
DrawAllDistance;
end;
cmOrto:
FOrto := not FOrto;
cmDrawHandlers:
if FPtCount > 0 then begin
DrawAllDistance;
src := FPoints;
for i := 0 to FPtCount-1 do begin
FAddon.MapView.DrawHandler(src^, khsNormal);
Inc(src);
end;
DrawAllDistance;
end;
end;
end;
end.

Также в файлеAddon.pasизменим метод:
procedure TFirstAddon.DistanceCalc;
begin
if not Assigned(gDistanceCalculator) then begin
gDistanceCalculator := TDistanceCalculator.Create(Self);
try
MapView.AddEditor(gDistanceCalculator.IEditor);
finally
gDistanceCalculator.IEditor._Release;
end;
end;
end
;

 

Анализ текста программы .

 

Метод TFirstAddon.DistanceCalc, который вызывается из TFirstAddon.ExecuteMacro, первым делом проверяет не присвоено ли значение глобальной переменной gDistanceCalculator. Если присвоено, то это значит что наш вычислитель уже работает. В этом случае ничего не делает. Иначе - создаёт наш редактор (TDistanceCalculator) и внедряет его в карту. Так как наш редактор порождён от вспомогательного класса TM2CustomEditor, то в методе AddEditor передаётся свойство редактора IEditor. При получении редактора карта увеличивает счетчик ссылок у редактора, а так как нам не надо держать ссылку на редактор, то мы вызываем _Release у редактора. После этой строчки только карта держит редактор, поэтому если она сделает Release редактору, то он уничтожиться. Карта делает Release редактору в двух случаях: когда у неё вызывается метод RemoveEditor с этим редактором и когда ГИС завершает свою работу.

Наш редактор (TDistanceCalculator) порождён от вспомогательного класса TM2CustomEditor. TM2CustomEditor внутренне реализует интерфейс IM2Editor через объект класса IM2EditorImp. К этому интерфейсу имеется доступ через свойство IEditor. IM2EditorImpпросто диспатчеризирует вызовы в вызовы виртуальных методов класса TM2CustomEditorплюс берёт на себя работу с ошибками. TM2CustomEditorв своих методах реализует логику по умолчанию. Следовательно, все необходимые методы, которые необходимо кустомизировать, надо перегружать в своём классе, порождённом от TM2CustomEditor.

Первым делом перегружаем метод GetEditorOptions. Этот метод должен сообщить какие события его интересуют. Только если редактор говорит, что какое-то событие его интересует, то соответствующий метод будет вызываться. Нас интересует обычная фаза (eopProcessPhase), перемещение мышки (eopMouseMove), нажатие клавиши мышки (eopMouseDown), контекстное меню (eopContextMenu) и инверсное рисование (eopDragging).

Реализуем свой внутренний метод DrawAllDistance, в котором рисуем инверсно путь, который определил пользователь. Этот метод мы будем вызывать тогда, когда надо показать/спрятать инверсность.

В методах HideDragging и ShowDragging просто вызываем DrawAllDistance.

В методе MouseMove обновляем отображение инверсности и запоминаем текущую позицию курсора.

В методе MouseDown добавляем новую точку в путь. Если пользователь дважды нажал клавишу мышки, то редактор вызывает свой метод ExecuteMacro с параметром cmCancel, в результате чего редактор будет удален из карты.

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

В деструкторе класса присваиваем nil глобальной переменной gDistanceCalculator. Это делается для того, чтобы расширение знало, что в данный момент наш вычислитель не работает (смотри метод TFirstAddon.DistanceCalc) .

 

Извещения ГИС. Управление окнами
 

В процессе работы ГИС посылает извещения расширениям через их метод Notify. Посылаются только те извещения на которые расширение подписалось.

Реализуем следующую задачу: при выборе объекта на карте расширение будет показывать из каких команд состоит контур объекта.

Первым делом добавим форму (не автосоздаваемую!), куда будем выводить информацию об объекте.


Файл Notify.pas.
unit Notify;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, M2Addon, DllForm;
type
TNotifyForm = class(TM2AddonForm)
Memo: TMemo;
procedure FormDestroy(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
private
{ Private declarations }
public
procedure Add(const s: string);
procedure AddFmt(const aFmt: string; const anArgs: array of const);
procedure ShowMapObject(anID: TM2ID);
end;
procedure ShowNotifyForm;
var
NotifyForm: TNotifyForm = nil;
implementation
{$R *.DFM}
uses
Addon;
procedure ShowNotifyForm;
begin
if not Assigned(NotifyForm) then
NotifyForm := TNotifyForm.Create(nil);
NotifyForm.Show;
end;
procedure TNotifyForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Action := caFree;
end;
procedure TNotifyForm.FormDestroy(Sender: TObject);
begin
NotifyForm := nil;
end;
procedure TNotifyForm.Add(const s: string);
begin
Memo.Lines.Add(s);
end;
procedure TNotifyForm.AddFmt(const aFmt: string; const anArgs: array of const);
begin
Memo.Lines.Add(Format(aFmt, anArgs));
end;
procedure TNotifyForm.ShowMapObject(anID: TM2ID);
var
aShapeList: TM2ShapeList;
aContour: TM2Contour;
aContinue: Boolean;
aCmd: TM2ContourCommand;
i: Integer;
begin
aShapeList.OleObject := gAddon.MapObjects.GetObjectShapeList(anID);
AddFmt('Количество фигур = %d', [aShapeList.Count]);
for i := 0 to aShapeList.Count-1 do begin
aContour.OleObject := aShapeList.Contours(i);
Add(#9'Стиль = '+aShapeList.StyleIDs(i));
Add(#9'Контур = (');
aContinue := aContour.GetFirstCommand(aCmd);
while aContinue do begin
case aCmd.CommandType of
ccNone:
Add(#9#9'None');
ccMoveTo:
AddFmt(#9#9'MoveTo: (x:%.2f; y:%.2f)', [aCmd.MoveTo.Point.x, aCmd.MoveTo.Point.y]);
ccLineTo:
AddFmt(#9#9'LineTo: (x:%.2f; y:%.2f)', [aCmd.LineTo.Point.x, aCmd.LineTo.Point.y]);
ccArcTo:
AddFmt(#9#9'ArcTo: (x:%.2f; y:%.2f) C:%.2f', [aCmd.ArcTo.Point.x, aCmd.ArcTo.Point.y,
aCmd.ArcTo.Convexity]);
ccCloseByLine:
Add(#9#9'CloseByLine');
ccCloseByArc:
AddFmt(#9#9'CloseByArc: C:%.2f', [aCmd.CloseByArc.Convexity]);
end;
aContinue := aContour.GetNextCommand(aCmd);
end;
Add(#9')');
end;
end;
end.

 

В массив команд kMacros добавим команду cmShowObjectInfo. В методе ExecuteMacro для этой команды вызываем процедуру ShowNotifyForm.

Потом подпишемся на извещение о выделении объекта. Для этого в конструкторе расширения будем вызывать базовый конструктор следующим образом:


inherited Create(CLSID_First, 'FirstAddon', 'Пример расширения для ГИС', advMapObjectSelected);

Далее перегрузим метод Notify:
procedure TFirstAddon.Notify(anAdvise: TM2Notify; const aParams: IM2ParamsList);
var
aList: TM2ParamsList;
begin
aList.OleObject := aParams;
case anAdvise of
advMapObjectSelected:
if Assigned(NotifyForm) then begin
NotifyForm.Add('Пометили объект с ID = '+aList.Items(kndMapObject)+
'( индекс фигуры = '+aList.Items(kndShape)+
') в слое '+aList.Items(kndLayer));
NotifyForm.ShowMapObject(aList.Items(kndMapObject));
end;
end;
end
;

 

Параметр anAdvise является кодом ALIGN="JUSTIFY" извещения. advMapObjectSelected говорит о том, что пришло извещение о выделении объекта. Параметр aParams имеет тип IM2ParamsList. Данный тип можно рассматривать как ассоциативный массив строк, то есть массив состоит из строк, а доступ к ним осуществляется по ключевым значениям. С каждым извещением приходит массив, содержащий определённые элементы. Например, с advMapObjectSelected приходят следующие элементы: kndMapObject - идентификатор объекта, который был выделен; kndShape - порядковый номер фигуры, которая выделена (-1 обозначает, что выделен весь объект); kndLayer - идентификатор слоя в котором находится объект.

Метод Notify вызывает метод TNotifyForm.ShowMapObject, который показывает информацию по объекту. Этот метод перебирает фигуры объекта, а также в каждой фигуре перебирает команды контура.

Заметьте, что окно расширения появляется поверх окна ГИС. Не являясь модальным оно всегда остаётся поверх ГИС. Как это достигается? Для этого надо, во-первых, породить форму от TM2AddonForm или от TDllForm (описаны в файле DllForm.pas), то есть строку TNotifyForm = class(TForm) вручную исправить на TNotifyForm = class(TM2AddonForm). Во-вторых, форма должна создаваться после вызова метода Initialize расширения. В этом методе TM2CustomAddon вызывает функцию InitDllForms с параметром равным хэндлу окна ГИС: InitDllForms(aManager.GetMainWindow). После вызова этой функции все формы, порождённые от TdllForm, будут находиться всегда поверх главного окна ГИС, потому что TDllForm делает владельцем (owner) наших окон окно ГИС.

Класс TM2AddonForm порожден от TDllForm. Единственная функциональность, которую он добавляет, заключается в том, что TM2CustomAddonавтоматически будет уничтожать эти формы, когда у него будет вызываться метод Uninitialize. Расширение при выходе должно обязательно уничтожить все свои формы!

 

Реализация расширения в виде динамически подгружаемой библиотеки

 

Теперь создадим это расширение в виде InprocServer, то есть в виде динамически подгружаемой библиотеки. Для этого создадим новый проект. В меню Delphi выберем команду File|New... В появившемся диалоге укажем, что хотим создать DLL и нажмём кнопку OK. Создастся проект для DLL. К данному проекту подключим файлы из предыдущего проекта за исключением главной формы (MainForm), которая в LocalServer играла роль заглушки. Сохраним проект под именем First.

Файл FirstInp.dpr.
library First;
uses
SysUtils,
Classes,
Addon in 'Addon.pas',
Editor in 'Editor.pas',
Notify in 'Notify.pas' {NotifyForm};
begin
end.

 

Расширение в виде InprocServer создано (всю работу на себя берёт TM2CustomAddon). Осталось лишь его зарегистрироватьв ГИС. Регистрацию делаем аналогично как и для LocalServer.

 

Отладка расширения

 

Расширения, созданные в виде InprocServer, работают быстрее, чем созданные в виде LocalServer, потому что не делается маршалинг (определенные действия со стороны OLE для вызова метода интерфейса и передачи ему параметров). Поэтому расширения интенсивно взаимодействующие с ГИС должны создаваться как InprocServer. Однако тут возникает проблема с отладкой: как трассировать динамическую библиотеку? Если инструмент разработки позволяет отлаживать динамические библиотеки, то можно воспользоваться этой возможностью. Иначе - решением является отладка расширения в виде LocalServer. В этом случае можно сначала запускать расширение из IDE компилятора (то есть под отладчиком). Значит можно будет ставить точки прерывания, инспектировать переменные программы и т.д. ГИС надо запускать после запуска расширения, тогда она не будет запускать расширение, а начнёт взаимодействовать с уже запущенной (под отладчиком). После того как расширение будет отлажено можно будет произвести окончательную сборку расширения в виде InprocServer. Это достигается тем, что оба вида расширения отличаются только файлом проекта. Естественно, надо будет также придерживаться некоторых правил. Например, не делать автосоздаваемых форм.

 

Спецификация

Описание интерфейсов и вспомогательных классов

 

TM2AddOnManager
Менеджер расширений предоставляет информацию, необходимую для работы расширения.
function GetMainWindow: HWnd;
Возвращает хэндл на главное окно программы ГИС.
function GetAppDesktop: THandle;
Возвращает хэндл на фоновое окно программы ГИС, которое создаёт Delphi.
TM2GISDatabase
Данный интерфейс обеспечивает доступ к данным ГИС.
function DatabaseName: TM2String;
Возвращает путь или IDAPI-алиас на каталог, где находятся семантические таблицы ГИС.
function GetUniqueID: TM2ID;
Создаёт и возвращает новый уникальный идентификатор.
function GetAreaList: IM2IDsList;
Возвращает список территорий, находящихся в текущей базе данных.
procedure GetAreaAttrs(const AnID: TM2ID; out AnAttrs: TM2AreaAttrs);
Возвращает атрибуты территории.
function GetMapList(const AnAreaID: TM2ID): IM2IDsList;
Возвращает список карт территории.
procedure GetMapAttrs(const AnID: TM2ID; out AnAttrs: TM2MapAttrs);
Возвращает атрибуты карты.
function GetLayerList(const AMapID: TM2ID): IM2IDsList;
Возвращает список слоев карты.
procedure GetLayerAttrs(const AnID: TM2ID; out AnAttrs: TM2LayerAttrs);
Возвращает атрибуты слоя.
function GetStyleList(const ALayerID: TM2ID): IM2IDsList;
Возвращает список стилей слоя.
function GetStyle(const AnID: TM2ID): IM2Style;
Возвращает стиль.
function AddMap(const anAttrs: TM2MapAttrs): TM2ID;
Добавляет карту в территорию.
function AddLayer(const anAttrs: TM2LayerAttrs): TM2ID;
Добавляет слой в карту.
function AddStyle(const aLayerID: TM2ID; const anAttrs: TM2StyleAttrs): TM2ID;
Добавляет стиль отображения в слой.
function AddTable(const aLayerID: TM2ID; const anAttrs: TM2CreateTableAttrs): TM2ID;
Добавляет семантическую таблицу в слой.
function DatabaseID: Longint;
Возвращает идентификатор базы данных с которой работает пользователь.
function CurrentUser: TM2ID;
Возвращает идентификатор пользователя, вошедшего в систему.
function GetUserList: IM2IDsList;
Возвращает список идентификаторов пользователей.
procedure GetUserAttrs(const anUserID: TM2ID; out anAttrs: TM2UserAttrs);
Возвращает атрибуты пользователя.
function GetTableList(const aLayerID: TM2ID): IM2IDsList;
Возвращает список идентификаторов семантических таблиц, принадлежащих слою.
procedure GetTableAttrs(const aTableID: TM2ID; out anAttrs: TM2TableAttrs);
Возвращает атрибуты семантической таблицы.
function GetAddonData(const aKey: TM2String; const aStm: IStream): Boolean;
Считывает данные из базы данных. aKey идентифицирует данные. Данные считываются в aStm. Функция возвращает True, если данные для aKeyбыли считаны.
procedure SetAddonData(const aKey: TM2String; const aStm: IStream);
Сохраняет данные в базе данных. aKey идентифицирует данные. Данные записываются из aStm.
TM2MapProject
Интерфейс обеспечивает доступ к информации о проекте с которым работает пользователь.
function ID: TM2ID;
Возвращает идентификатор проекта.
procedure GetAttrs(out AnAttrs: TM2ProjectAttrs);
Возвращает атрибуты проекта.
procedure SetActiveMap(const AMapID: TM2ID);
Устанавливает активную карту.
procedure SetActiveLayer(const ALayerID: TM2ID);
Устанавливает активный слой.
function ActiveMap: TM2ID;
Возвращает идентификатор активной карты.
function ActiveLayer: TM2ID;
Возвращает идентификатор активного слоя.
procedure AddMap(const aMapID: TM2ID);
Включает в проект карту, ранее добавленную в территорию.
function GetMapList: IM2IDsList;
Возвращает список карт, включённых в проект.
function GetLayerList(const aMapID: TM2ID): IM2IDsList;
Возвращает список слоёв карты.
TM2MapObjects
Интерфейс обеспечивает работу с объектами карты.
function CreateContour: IM2Contour;
Создаёт и возвращает пустой контур.
function CreateShapeList: IM2ShapeList;
Создаёт и возвращает пустой список фигур.
function AddObject(const aLayerID: TM2ID; const aShapeList: IM2ShapeList): TM2ID;
Добавляет объект, описываемый списком фигур aShapeList, в слой aLayerID.
function GetObjectLayerID(const anObjectID: TM2ID): TM2ID;
Возвращает идентификатор слоя, которому принадлежит объект.
function GetObjectShapeList(const anObjectID: TM2ID): IM2ShapeList;
Возвращает список фигур, из которых состоит объект.
procedure DeleteObject(const AnObjectID: TM2ID);
Удаляет объект.
procedure ModifyObject(const AnObjectID: TM2ID; const AShapeList: IM2ShapeList);
"2">
Заменяет список фигур у объекта.
function FindObjectAtPos(const ALayer: TM2ID; AnOnlyVisible: BOOL; const APoint: TM2Point; out AShapeIndex: Longint): TM2ID;

Возвращает идентификатор объекта в который попадает точка aPoint. Если aLayer равен kNoID , то поиск производится по всем слоям, иначе - только в данном. Если anOnlyVisible равен True , то при поиске учитывается видимость слоёв, иначе - не учитывается. В aShapeIndex возвращается порядковый номер фигуры, в которую попала точка. Если точка не попадает ни в какой объект, то метод возвращает значение kNoID. Объект ищется не только на попадание, но и на достаточно близкое расположение точки к объекту. При этом учитывается текущий масштаб карты.
function FindIntersectedObjects(const ALayer: TM2ID; AnOnlyVisible: BOOL; const ARect: TM2Rect; AnOnlyEnclosed: BOOL): IM2IDsList;

Возвращает список объектов, попавших в прямоугольник aRect. Если aLayer равен kNoID, то поиск производится по всем слоям, иначе - только в данном. Если anOnlyVisible равен True, то при поиске учитывается видимость слоёв, иначе - не учитывается. Если anOnlyEnclosed равен True, то объекты считаются попавшими в прямоугольник, только если они полностью лежат внутри него, иначе - если они хотя бы попадают какой-либо своей частью в этот прямоугольник.
procedure ObjectDataChanged(const AnObjectID: TM2ID);

Если расширение меняет напрямую данные по объекту в семантической таблице, и отображение объекта на карте зависит от этих данных, то необходимо вызывать данный метод с идентификатором объекта, чтобы ГИС пересчитала границы объекта.
function GetCenter(const APt1: TM2Point; const APt2: TM2Point; const APt: TM2Point; out ADelta: Double): TM2Point;

Возвращает центр дуги, заданной тремя точками (начальная, конечная и лежащая на дуге). aDelta возвращает величину дуги в радианах.
function GetCenter2(const APt1: TM2Point; const APt2: TM2Point; AConvexity: Double; out ADelta: Double): TM2Point;

Возвращает центр дуги, заданной двумя точками (начальная и конечная) и кривизной дуги. Кривизна дуги (convexity в терминах AutoCAD)- это тангенс половинного угла между касательной к дуге, проходящей через начальную точку, и отрезком, соединяющим начальную и конечную точки дуги. aDelta возвращает величину дуги в радианах.

function GetConvexity(const APt1: TM2Point; const APt2: TM2Point; const APt: TM2Point): Double;

Возвращает кривизну дуги, заданной тремя точками (начальная, конечная и лежащая на дуге).
procedure BeginUpdate;

Указывает на необходимость кэширования изменений объектов карты. После вызова этого метода изменения не будут сразу производиться, а будут накапливаться. В момент вызова EndUpdateнакопленные изменения будут обработаны и объекты карты изменятся. Можно вызывать подряд несколько BeginUpdate. Количество EndUpdate должно соответствовать количеству BeginUpdate. Для чего нужно кэширование? Дело в том, что ГИС может оптимизировать внесение сразу нескольких изменений, поэтому единовременное внесение пятидесяти изменений будет производится быстрее чем пятьдесят раз по одному изменению. Допустим расширение делает импорт пространственных объектов в ГИС. В этом случае лучше добавление объектов разбить на порции по двести объектов, которые будут добавляться между BeginUpdate и EndUpdate.
procedure EndUpdate;

Указывает на необходимость завершения кэширования изменений объектов карты.
function GetObjectsEnum(const aLayerID: TM2ID): IM2IDsEnum;
Возвращает перечислитель объектов слоя.
function GetObjectBounds(const anObjectID: TM2ID): TM2Rect;
Возвращает нормализованные границы объекта, то есть X1 <= X2, Y1 <= Y2.
procedure GetObjectData(const anObjectID: TM2ID; aFieldData: Boolean; out aData: TM2ObjectData);
Возвращает данные из семантических таблиц по объекту. anObjectIDсодержит идентификатор объекта. Если aFieldData равно False, то справочные поля будут возвращать не свое значение, а отображаемое значение справочника. Данные возвращаются в aData.
procedure SetObjectData(const anObjectID: TM2ID; aFieldData: Boolean; const aData: TM2ObjectData);
Устанавливает данные в семантических таблицах объекта. anObjectIDсодержит идентификатор объекта. aFieldData должно быть равно True. В aData передаются данные по объекту.
procedure Undo(aCount: Longint);
Производит откатку на aCount шагов назад.
procedure AddTopoLinks(const anID: TM2ID; const aList: IM2IDsList);
Топологически связывает объект, заданный anID, с объектами заданными списком aList. Связываются только объекты имеющие общие границы. После выполнения в списке aList остаются идентификаторы фактически связанных объектов.
procedure DeleteTopoLinks(const anID: TM2ID; const aList: IM2IDsList);
Разрывает топологическую связь объекта anID с объектами заданными в aList. Если aList равен nil, то разрываются все связи объекта anID.
function GetTopoLinks(const anID: TM2ID): IM2IDsList;
Возвращает список идентификаторов объектов с которыми объект anID имеет топологическую связь.
function GetTopoLinksInfo(const anID: TM2ID; const aList: IM2IDsList): IM2TopoLinkList;
Возвращает список, содержащий информацию о топологической связи объекта anID с объектами заданными в списке aList. Если aList равен nil, то возвращается информация по всем объектам, имеющим топологическую связь с anID.
function FindTopoLinksInfo(const aContour: IM2Contour; aDistance: double; const aForLayer: TM2ID; const aRelLayers: IM2IDsList): IM2TopoLinkList;
Производит пространственный анализ на предмет создания топологических связей. aContour задаёт контур по отношению к которому производится поиск соседей. aDistance задает расстояние, в пределах которого (!!!)
TM2MapView
Окно карты через которое отображаются пространственные объекты.
function IsSelectedObject(const AnObjectID: TM2ID): Boolean;
Возвращает True, если объект помечен.
procedure SelectAloneObject(const AnObjectID: TM2ID);
Помечает объект. Остальные размечаются.
procedure SelectObject(const AnObjectID: TM2ID);
Помечает объект.
procedure ToggleObjectSelection(const AnObjectID: TM2ID);
Инвертирует признак помеченности для объекта.
procedure UnselectObject(const AnObjectID: TM2ID);
Размечает объект.
procedure UnselectAllObjects;
Размечает все объекты.
procedure FitObjects;
Делает все помеченные объекты активной карты видимыми на карте.
function GetSelectedObjectList: IM2IDsList;
Возвращает список помеченных объектов активной карты.
procedure SetScale(AScale: Double);
Устанавливает масштаб aScale.
function Scale: Double;
Возвращает масштаб aScale.
function RegisterCursor(ACursor: HCursor): Longint;
Регистрирует курсор.
procedure SetCursor(ARegCursor: Longint);

Устанавливает вид курсора карты. Этот метод должен вызываться из метода MouseMove редактора расширения. После его вызова надо вызывать метод StopHandler, чтобы никто не переопределил установленный курсор. Пример,
procedure TSampleEditor.MouseMove(AShift: TM2ShiftState; AMouse: TPoint);
begin
if FCursorSetting then begin
gAddOn.MapView.SetCursor(gAddOn.SpotCursor);
gAddOn.MapView.StopHandler;
end;
end;


function GetCursor: Longint;
Возвращает вид курсора карты.
procedure SetCaptureEditor(const AnEditor: IM2Editor);
Данный редактор захватывает управление мышью (не вызывать без особой надобности!).
procedure SetTimer(const AnEditor: IM2Editor; AnInterval: Longint);
Устанавливает таймер для редактора. У редактора будет вызываться метод Timer. Если AnIntervalравен нулю, то у редактора удаляется таймер.
function GetPhase: TM2ProcessPhase;
Возвращает какая фаза обработки события является текущей.
procedure AddEditor(const AnEditor: IM2Editor);
Добавляет редактор в карту.
procedure RemoveEditor(const AnEditor: IM2Editor);
Удаляет редактор из карты.
procedure Notify(AWhat: TM2EditorNotification);
Не надо вызывать этот метод.
procedure Invalidate;
Указывает что надо перерисовать всю карту.
procedure InvalidateArea(const ARect: TM2Rect);
Указывает что надо перерисовать часть карты, заданную прямоугольником.
procedure ScrollBy(dx, dy: Longint);
Скролирование карты. На сколько скролировать задаётся в экранных координатах.
function ZoomRect: TM2Rect;
Возвращает прямоугольник видимой части карты в окне.
procedure SetZoomRect(const ARect: TM2Rect);
Устанавливает прямоугольник видимой части карты в окне.
procedure ZoomIn;
Увеличивает масштаб карты в два раза.
procedure ZoomOut;
Уменьшает масштаб карты в два раза.
procedure ZoomToExtent;
Вписывает всю территорию в границы экрана.
procedure StopHandler;

Останавливает обработку события. Редакторы, которые ещё не обрабатывали событие, теперь не получат возможности его обработать. Надо отметить, что в обязательной фазе все редакторы обязательно получают возможность обработать событие.
procedure SetStatusText(ASection: Longint; const AText: TM2String);
Выводит текст aTextв статусную строку главного окна ГИС. ASectionдолжно иметь значение 0.
function PointToDevice(const APoint: TM2Point): TPoint;
Переводит точку из координат карты в координаты окна ГИС.
function PointFromDevice(APoint: TPoint): TM2Point;
Переводит точку из координат окна ГИС в координаты карты.
function SizeToDevice(ASize: Double): Longint;
Переводит длину из координат карты в координаты окна ГИС.
function SizeFromDevice(ASize: Longint): Double;
Переводит длину из координат окна ГИС в координаты карты.
function RectToDevice(const aRect: TM2Rect): TRect;
Переводит прямоугольник из координат карты в координаты окна ГИС.
function RectFromDevice(const aRect: TRect): TM2Rect;
Переводит прямоугольник из координат окна ГИС в координаты карты.
procedure DrawPolyline(ADrawMode: Longint; const APen: TM2Pen; APoints: PM2Point; ACount: Longint);
Рисует полилинию в окне карты.
procedure DrawPolygon(ADrawMode: Longint; APolyFillMode: Longint; const APen: TM2Pen; const ABrush: TM2Brush; APoints: PM2Point; ACount: Longint);
Рисует полигон в окне карты.
procedure DrawArc(ADrawMode: Longint; const APen: TM2Pen; const APoint1: TM2Point; const APoint2: TM2Point; AConvexity: Double);
Рисует дугу в окне карты.
procedure DrawContour(ADrawMode: Longint; APolyFillMode: Longint; const APen: TM2Pen; const ABrush: TM2Brush; const AContour: IM2Contour);
Рисует контур в окне карты.
procedure DrawHandler(const APoint: TM2Point; AState: TM2HandlerState);
Рисует специальный квадратик в окне карты.
procedure DrawText(ADrawMode: Longint; const AFont: TM2Font; const APoint1: TM2Point; const APoint2: TM2Point; AText: PChar);
Рисует текст в окне карты.
function GetTextSize(const AFont: TM2Font; AText: PChar): TM2Point;
Возвращает размерность текста.
procedure GetDirections(out xAngle, yAngle: double);
Возвращает направления осей координат. Направление задаётся в градусах.

Оси между собой всегда образуют угол 90°.
function GetMapImage(const aCenter: TM2Point; aScale: double; xAngle: double; yAngle: double; aDeviceSize: TPoint; aBkColor: TM2Color; aResolution: TPoint): IDataObject;
Возвращает IDataObject через который можно получить изображение карты (пока поддерживается только формат CF_BITMAP). aCenter указывает на центр получаемого изображения на карте. aScale задаёт масштаб. xAngle и yAngle задают направления осей координат. aDeviceSize определяет размеры получаемого изображения на устройстве вывода. aBkColorзадаёт цвет фона для получаемого растра. Если aResolutionсодержит ненулевые значения, то эти значения используются вместо разрешающего значения устройства вывода (LOGPIXELS).
IM2ErrorInfo
Интерфейс для передачи информации о произошедшей ошибке между ГИС и расширениями.
function i_SetInfo(const ASource, AMessage: TM2String): HResult;
Устанавливает информацию об ошибке. ASource - где произошла ошибка, aMessage - строка содержащая информацию по ошибке.
function i_GetInfo(out ASource, AMessage: TM2String): HResult;
Получает информацию об ошибке. ASource - где произошла ошибка, aMessage - строка содержащая информацию по ошибке.
TM2GeometryLib
Интерфейс, реализующий ряд алгоритмов по обработке пространственных данных.
function CalcContourSquare(const aSrc: IM2Contour): double;
Подсчитывает площадь контура.
function CalcContourPerimeter(const aSrc: IM2Contour): double;
Подсчитывает периметр контура.
function CalcContoursRelation(const aSrc, aTest: IM2Contour): TM2ContourRelation;
Определяет пространственное положение контура aTestпо отношению к контуру aSrc.
procedure ContourOperation(const aDst, aSrc: IM2Contour; anOp: TM2ContourOperation);
Производит операцию anOpнад контурами aDstи aSrc. Результат возвращается в aDst.
procedure ContourOperationList(const aDst: IM2Contour; aSrcList: PIM2Contour; aCount: Longint; anOp: TM2ContourOperation; const aCallback: IM2Callback);
Производит операцию anOpнад контуром aDstи массивом контуров aSrcList. Количество контуров задается в aCount. Результат возвращается в aDst.
procedure BuildBufferZone(const aDst: IM2Contour; aDistance: double);

Строит буферную зону для контура aDstна расстояние aDistance. Результат возвращается в aDst. TM2Preferences

Этот интерфейс даёт возможность сохранять настройки работы расширения.

Параметр aRoot, имеющий тип TM2PrefRoot, определяет область действия настройки. Если он равен kprGlobal - для всех пользователей, работающих с любым проектом; kprUser - для текущего пользователя, работающего с любым проектом; kprProject - для любого пользователя, работающего с текущим проектом; kprUserProject - для текущего пользователя, работающего с текущим проектом.

В параметр anObjectIDпередавайте константу kNoID. Хотя если Вы хотите ассоциировать Вашу настройку с каким-либо объектом ГИС, то можете передавать идентификатор этого объекта.
Параметр aKey задаёт имя настройки. Чтобы оно не конфликтовало с настройками других расширений, а также самой ГИС, имя должно строится по следующим правилам: < "Times New Roman" SIZE="2">Название фирмы (краткое)> <\> < Название расширения > <\> < Название настройки>. Например: “MasterSoftware\Построение рельефа\Видимость формы”.
procedure SetString(aRoot: TM2PrefRoot; const anObjectID: TM2ID; const aKey: TM2PrefKeyName; const aValue: TM2PrefKeyValue);
Записывает строку aValue.
function GetString(aRoot: TM2PrefRoot; const anObjectID: TM2ID; const aKey: TM2PrefKeyName; const aDefValue: TM2PrefKeyValue): TM2PrefKeyValue;
Считывает строку. Если строки не было, то возвращается значение задаваемое в aDefValue.
function DeleteKey(aRoot: TM2PrefRoot; const anObjectID: TM2ID; const aKey: TM2PrefKeyName): Boolean;
Удаляет строку. Если строка не была найдена, то возвращает значение False.
TM2ShapeList
Список фигур содержит набор фигур, описывающих объект карты. В свою очередь фигура состоит из контура, описывающего пространственную геометрию фигуры, и стиля отображения, описывающего атрибуты рисования контура. Фигуры в списке нумеруются с нуля.
procedure Clear;
Удаляет все фигуры.
procedure Add(const aContour: IM2Contour; const aStyleID: TM2ID);
Добавляет в конец списка новую фигуру, описываемую заданными контуром и стилем.
procedure Insert(aBefore: Longint; const aContour: IM2Contour; const aStyleID: TM2ID);
Добавляет в позицию aBefore новую фигуру, описываемую заданными контуром и стилем.
procedure Delete(anIndex: Longint);
Удаляет фигуру в позиции anIndex.
function Count: Longint;
Возвращает количество фигур, из которых состоит список.
function Contours(anIndex: Longint): IM2Contour;
Возвращает контур фигуры, находящейся в позиции anIndex.
function StyleIDs(anIndex: Longint): TM2ID;
Возвращает идентификатор стиля фигуры, находящейся в позиции anIndex.
procedure SetStyleID(anIndex: Longint; const aStyleID: TM2ID);
Меняет идентификатор стиля фигуры, находящейся в позиции anIndex.
TM2Contour
Контур описывает пространственную геометрию фигуры объекта. Геометрия задаётся списком команд, поэтому контур - это последовательность команд построения контура. Команды в списке нумеруются с нуля. Контур содержит набор методов для манипуляции этими командами.
procedure Clear;
Удаляет все команды контура.
function GetFirstCommand(out aCommand: TM2ContourCommand): Boolean;
Возвращает в aCommandпервую команду. Если контур не имеет команд, то метод возвращает False. Метод также инициализирует итератор команд контура.
function GetNextCommand(out aCommand: TM2ContourCommand): Boolean;
Возвращает в aCommandследующую команду. Итератор команд контура должен уже быть инициализирован методом GetFirstCommand. Если команд больше нет, то возвращается значение False.
procedure AddCommand(const aCommand: TM2ContourCommand);
Добавляет команду в конец списка.
procedure InsertCommand(aBefore: Longint; const aCommand: TM2ContourCommand);
Добавляет команду в позицию aBefore.
procedure SetCommand(anIndex: Longint; const aCommand: TM2ContourCommand);
Команда, находящаяся в позиции anIndex, заменяется на данную.
procedure DeleteCommand(anIndex: Longint);
Команда в позиции anIndex удаляется.
procedure GetCommand(AnIndex: Longint; out ACommand: TM2ContourCommand);
Получение команды, находящей в позиции anIndex.
function Count: Longint;
Возвращает количество команд контура.
function Clone: IM2Contour;
Возвращает свою копию.
TM2Style
Стиль отображения описывает как объекты должны создаваться, а также содержит список методов отображения объектов для их рисования на карте. Методы отображения нумеруются в списке с нуля.
function ID: TM2ID;
Возвращает идентификатор стиля отображения.
function GetLayerList: IM2IDsList;
Возвращает список слоёв которым он принадлежит.
procedure GetAttrs(out anAttrs: TM2StyleAttrs);
Возвращает атрибуты стиля.
function PainterCount: Longint;
Возвращает количество методов отображения.
procedure GetPainterAttrs(anIndex: Longint; out anAttrs: TM2PainterAttrs);
Возвращает атрибуты метода отображения, находящегося в позиции anIndex списка методов отображения.
procedure AddPainter(const anAttrs: TM2PainterAttrs);
Добаляет в конец списка новый метод отображения.
procedure InsertPainter(anIndex: Longint; const anAttrs: TM2PainterAttrs);
Добаляет новый метод отображения в позицию anIndex списка. Если anIndex меньше нуля, то - в конец.
procedure SetPainter(anIndex: Longint; const anAttrs: TM2PainterAttrs);
Меняет атрибуты метода отображения, находящегося в позиции anIndex.
procedure DeletePainter(anIndex: Longint);
Удаляет метод отображения, находящийся в позиции anIndex.
TM2IDsEnum
Перечислитель идентификаторов.
function Next(out anID: TM2ID): Boolean;
Возвращает в anID следующий идентификатор. Если идентификаторов больше нет, то возвращает False.
function Skip(aCount: Longint): Boolean;
Пропускает aCount идентификаторов. Если в процессе пропуска идентификаторы исчерпались, то возвращается False.
procedure Reset;
Сброс перечислителя на начало списка.
TM2IDsList
Список идентификаторов. Идентификаторы в списке нумеруются с нуля.
function Items(i: Longint): TM2ID;
Возвращает идентификатор, находящийся в позиции i.
function Count: Longint;
Возвращает количество идентификаторов.
procedure Add(const anItem: TM2ID);
Добавляет идентификатор в конец списка.
procedure Insert(i: Longint; const anItem: TM2ID);
Добавляет идентификатор в позицию i.
procedure Change(i: Longint; const anItem: TM2ID);
Изменяет значение идентификатора в позиции i.
procedure Delete(i: Longint);
Удаляет идентификатор, находящийся в позиции i.
procedure Clear;
Удаляет все идентификаторы из списка.
TM2MacroAttrsList
Список команд для добавления их в меню и на панель команд.
procedure GetItem(i: Longint; out AnItem: TM2MacroAttrs);
Возвращает описание команды, находящейся в позиции i.
function Count: Longint;
Возвращает количество команд.
procedure Clear;
Удаляет все команды.
procedure Add(const anItem: TM2MacroAttrs);
Добавляет команду в список.
procedure Insert(i: Longint; const anItem: TM2MacroAttrs);
Добавляет команду в список в позицию, определяемую значением i.
TM2ContextMacroAttrsList
Список команд для добавления в контекстное меню. Меню вызывается при нажатии правой клавиши мыши на карте.
procedure GetItem(i: Longint; out AnItem: TM2ContextMacroAttrs);
Возвращает описание команды, находящейся в позиции i.
function Count: Longint;
Возвращает количество команд.
procedure Clear;
Удаляет все команды.
procedure Add(const anItem: TM2ContextMacroAttrs);
Добавляет команду в список.
procedure Insert(i: Longint; const anItem: TM2ContextMacroAttrs);
Добавляет команду в список в позицию, определяемую значением i.
TM2ParamsList
Ассоциативный список строк. Каждая строка ассоциирована с каким-либо ключём.
function Items(AKey: Longint): TM2String;
Получить строку по ключу aKey.
function Contains(aKey: Longint): Boolean;
Возвращает True, если ключ aKey содержится в списке.
procedure Clear;
Очищает список.
procedure Add(aKey: Longint; const anItem: TM2String);
Добавить в список строку aValue, ассоциированную с ключом aKey.
TM2CustomAddOn
Интерфейс, через который ГИС работает с расширением. Данный интерфейс реализует расширение.
procedure GetAttrs(out AnAttrs: TM2AddOnAttrs);
Возвращает атрибуты расширения.
procedure Initialize(const AManager: IM2AddOnManager);
Инициализация расширения.
procedure Uninitialize;
Деинициализация расширения.
function GetMacroList: IM2MacroAttrsList;
Возвращает список команд, которые необходимо встроить в меню ГИС.
procedure ExecuteMacro(ACommand: Longint; const AParams: TM2String);
Вызывается, когда пользователь выбрал в меню или на панеле инструментов команду расширения.
procedure Notify(AnAdvise: TM2Notify; const AParams: IM2ParamsList);
Через этот метод расширению передаются извещения о происходящих событиях в ГИС.
function GetBitmap(const aStm: IStream; aBitmap: Longint): Boolean;
Возвращает в потоке aStm битмап, для значения aBitmap. Если битмап записан в поток, то функция возвращает True, иначе – False.
TCustomM2Editor
Редакторы предназначены для встраивания в карту. Карта диспетчеризует события карты и действия пользователя всем редакторам встроенным в неё. Таким образом при помощи редактора можно добавлять свою логику в карту.
function GetEditorOptions: TM2EditorOptions;
Карта, в момент встраивания редактора в неё, вызывает этот метод для того, чтобы определить какие события и фазы интересуют этот редактор. Следовательно, будут вызываться только методы, которые соответствуют интересующим событиям. Методы вызываются для каждой заказанной фазы обработки события. Редактор должен вернуть число, полученное “побитовым или” констант eop*. Пример: Result := eopProcessPhase or eopMouseDown or eopMouseMove or eopContextMenu or eopDragging.
procedure HideDragging;
Если редактор рисует что-либо инверсно на карте, то в этом методе он должен убрать нарисованную им инверсность с карты. Карта вызывает этот метод у редакторов перед некими своими действиями, корреляция которых с нарисованными инверсными областями может привести к мусору на экране монитора. После этих действий карта вызывает у редакторов метод ShowDragging, чтобы они снова нарисовали что-либо инверсно, если это конечно им надо.
procedure ShowDragging(AMouse: TPoint);
Редактор может снова нарисовать инверсные области на карте. Подробности смотри в описании метода HideDragging.
procedure MouseDown(AButton: TM2MouseButton; AShift: TM2ShiftState; AMouse: TPoint);
Вызывается при нажатии клавиши мыши на карте. AButton содержит информацию о том какие клавиши мыши нажаты, aShift - состояние клавиш-модификаторов, aMouse - оконные координаты курсора мыши.
procedure MouseMove(AShift: TM2ShiftState; AMouse: TPoint);
Вызывается при перемещении курсоры мыши над картой.
procedure MouseUp(AButton: TM2MouseButton; AShift: TM2ShiftState; AMouse: TPoint);
Вызывается при отпускании клавиши мыши.
procedure KeyDown(Key: Word; AShift: TM2ShiftState);
Вызывается при нажатии клавиши клавиатуры.
procedure KeyUp(Key: Word; AShift: TM2ShiftState);
Вызывается при отпускании клавиши клавиатуры.
procedure Timer;
Если редактор заказал у карты таймер, то периодически будет вызываться этот метод.
procedure Notification(AWhat: TM2EditorNotification);
Извещения от карты.
function GetContextMacroList(AMouse: TPoint): IM2ContextMacroAttrsList;
Вызывается перед показом контекстного меню карты. Редактор должен вернуть список команд, которые необходимо встроить в контекстное меню. Если нет необходимости встраивать команды, то надо вернуть пустой список.
procedure ExecuteMacro(ACommand: Longint; const AParams: TM2String);
Вызывается, если пользователь выбрал команду, которую редактор добавил в контекстное меню. ACommand содержит идентификатор этой команды.
procedure Cancel;
Этот метод пока никогда не вызывается.
procedure Paint;
Вызывается при перерисовке карты.

 

Описание типов и констант

Регистрация расширений в реестре

 

Во-первых, расширение должно быть зарегистрировано как сервер OLE.
Для LocalServer
HKEY_CLASSES_ROOT\CLSID\<CLSID сервера> = <Название сервера>
HKEY_CLASSES_ROOT\CLSID\< CLSID сервера>\LocalServer32 = <Путь до сервера>

Для InprocServer
HKEY_CLASSES_ROOT\CLSID\< CLSID сервера> = <Название сервера>
HKEY_CLASSES_ROOT\CLSID\< CLSID сервера>\InprocServer32 = <Путь до сервера>

Пример
HKEY_CLASSES_ROOT\CLSID\{6D067FC1-3C7F-11D0-9DF1-444553540000} = Пример расширения
HKEY_CLASSES_ROOT\CLSID\{6D067FC1-3C7F-11D0-9DF1-444553540000}\InprocServer32 = C:\Src\DocSampl\First.dll

Во-вторых, необходимо информировать ГИС о расширениях, которые необходимо подгружать.
Для системных расширений (подгружаются при работе с любыми БД ГИС)
HKEY_LOCAL_MACHINE\SOFTWARE\Integro\Ingeo\AddOns\<CLSID сервера> = <Название расширения>
Active = 1|0

Для прикладных расширений (подгружаются только при работе с определённой БД ГИС)
HKEY_LOCAL_MACHINE\SOFTWARE\Integro\Ingeo\Databases\<GUID БД>\AddOns\<CLSID сервера> = <Название расширения>
Active = 1|0

Пример
HKEY_LOCAL_MACHINE\SOFTWARE\Integro\Ingeo\AddOns\{6D067FC1-3C7F-11D0-9DF1-444553540000} = Пример расширения
Active = 1

Если значение (value) Active равно 1, то расширение будет подгружаться, если оно равно 0, то не будет.
Настройка какие расширения надо подключать производится в ГИС через команду меню Файл|Модули расширения либо при в диалоге “Выбор базы данных ГИС”. Для этого расширения со своей стороны должны реализовать регистрацию и разрегистрацию по определённому стандарту.

 

Для LocalServer

  • если программа запускается с ключом /regserver, то она должна зарегистрировать себя как сервер OLE. Далее проверяет не передан ли ей ключ в виде /id:<строка>. Если этого ключа нет, то регистрирует себя как системное расширение, то есть под ключом реестра HKEY_LOCAL_MACHINE\Software\Integro\Ingeo\AddOns. Иначе - оно прикладное, и должно регистрировать себя под ключом реестра HKEY_LOCAL_MACHINE\Software\Albea\GIS\AppliedAddOns\<строка> (созданный ключ ГИС потом переместит в ветвь соответствующей БД). Потом проверяет наличие ключа вида /active:<признак активности>. Если этого ключа нет или <признак активности> равен 1, то программа добавляет значение Active в свою регистрацию со значением 1, иначе - 0. Пример, sample.exe /regserver /id:temp /active:1.

  • если программа запускается с ключом /unregserver, то она должна разрегистировать себя как сервер OLE.

InprocServer сервер должен экспортировать следующие функции

  • function DllRegisterServer: HResult; stdcall. Расширение должно зарегистрировать себя как сервер OLE.

  • function DllUnregisterServer: HResult; stdcall. Расширение должно разрегистрировать себя как сервер OLE.

  • function M2DllRegisterAddOn(anID: PChar; anActive: BOOL): HResult; stdcall. Расширение должно зарегистрироваться в ГИС. Если anID равен нулевому указателю или является пустой строкой, то регистрируется как системное расширение, иначе - как прикладное (принципы регистрации смотри выше). anActive является признаком активности расширения.

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


 

 

 

 карта сайта   к началу страницы