Работа с MapWindow GIS. Новая версия 4.9.2. Обзор обновлений

Всем доброго времени суток. Давненько я не писал ничего про MapWindow GIS в Delphi. Может оно и к лучшему, так как летом вышла очередная новая версия данного ActiveX-компонента, в которой наконец-то появилась поддержка Google-карт.
Мы уже с Вами рассматривали один проект, который позволяет в своих программах добавлять интерактивные карты Google. Эта библиотека называется GMLib и я уже о ней рассказывал на этом блоге. Но многим нравиться именно MapWindows GIS, так как по мне, она более функциональная и универсальная, да и разобрали в данном компоненте мы уже прилично вопросов.
Так вот, здесь я хотел бы поговорить о новой версии, которая вышла в августе 2014 года – MapWinGis 4.9.2. Вам лишь необходимо скачать последнюю версию с официального сайта (ссылка) для своей операционной системы.
Затем на всякий случай удалите старую версию компонента и установите новую, как это сделать я рассказывал в данной статье. Сейчас поговорим о самых интересных и больших обновлениях в этой версии, а в следующей статье уже будем рассматривать, как можно работать с этим компонентом.
Вообще, карты Google теперь в проекте Вашем могут выглядеть следующим образом:
Горячие клавиши по умолчанию
Теперь на карте есть свои встроенные горячие клавиши, которыми можно с легкостью управлять своим проектом, например:
- «+» — увеличение на карте
- «-» — уменьшение на карте
- «*» — увеличение на карте до ближайшей плитки
- «M» — измерение расстояния
- «Колесо мыши» — увеличение или уменьшение масштаба, в независимости от того, какой тип курсора у Вас установлен
- «Z» — увеличение
- «Shift+Left» – увеличение до предыдущего слоя
- «Shift+Right» – увеличение до следующего слоя
Горячие клавиши будут работать только в том случае, если на вашей карте установлен фокус. А стрелки влево и вправо будут работать, только при выполнение некоторого кода, об этом будет рассмотрено в следующих статьях, так как здесь в основном рассматриваем новые возможности.
Измерения расстояния и площади
Теперь Вы можете очень легко и просто измерять площадь и расстояние на своих картах. Поэтому, чтобы Вы могли начать измерение расстояния, необходимо в свойствах компонента CursorMode выбрать cmMeasure, либо же использовать горячую клавишу «M», но в данном случае карта должна находиться в фокусе.
Чтобы измерить площадь необходимо включить режим измерения площади, это делается следующим образом:
Measuring.MeasuringType:=MeasureArea;
Вот так будет выглядеть измерение расстояния:
А вот так измерение площади:
Данный режим также поддерживает различные горячие клавиши:
- Щелчок левой кнопки мыши – добавление новой точки
- Щелчок правой кнопки мыши – стирание последней добавленной точки
- Двойной щелчок мыши – разделение измерения пути
- Ctrl+щелчок мыши – закрывает полигон для измерения
- Shift+щелчок мыши – привязывает линию к ближайшей вершине на слое карты
Кроме этого, теперь можете создавать слои без создания нужных объектов. В версии 4.9.2 еще много различных вкусностей: он поддерживает и гибридные карты Яндекс, Yahoo и других картографических систем, о которых мы будем говорить в следующих статьях.
Обновление предыдущей версии MapWindowGIS
Если Вы не знаете, как можно установить новую версию ActiveX-компонента, то посетите данную статью. Если же она у Вас установлена, то можно просто ее обновить, например, при помощи регистрации библиотеки с помощью команды regsvr32 (Пуск-Выполнить), либо же просто импортируйте новую версию компонента в Delphi.
На этом все, хотели бы Вы дальше читать статьи по MapWindowGIS в Delphi?
Похожие записи
10. События формы. Лабораторные Delphi, C++ (6)

События перемещения мыши на форме в Delphi и C++Builder
Как добавить "резиновый" SplashScreen в XE7

Как известно, в XE7 упростили возможность добавления заставки (SplashScreen) для нашего приложения. В этой заметке я расскажу как добавлять «статичную» и «резиновую» заставки. Данная заметка
Немного про замыкания

В этом легко убедиться:
program closuretest;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils;
procedure Test;
var
i: Integer;
m: string;
proc, proc2: TProc;
function GetClosure1:TProc;
begin
Result := proc;
end;
function GetClosure2:TProc;
begin
Result := proc2;
end;
var
intf1, intf2: IInterface;
begin
i := 0;
proc := procedure()
begin
inc(i,2);
Writeln(i);
end;
proc2 := procedure()
begin
inc(i,3);
Writeln(i);
end;
proc();
proc2();
GetClosure1.QueryInterface(IInterface, intf1);
GetClosure2.QueryInterface(IInterface, intf2);
Writeln(GetClosure1 = GetClosure2); //False
Writeln(intf1 = intf2); // TRUE, что и требовалось доказать
Read(m);
end;
begin
try
Test;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
Android 4.4 и запись на внешнюю карту памяти…

До Android 4.4 (API 19), нам приходилось читать файлы (vold.fstab, mounts), чтобы понять, установлена ли внешняя карта памяти и какой до неё путь. Теперь у
Переход с Delphi 2010 на Delphi XE7

Чуть больше года назад я писал об успешном переходе с Delphi 7 на Delphi 2010 приложений в нашей компании (Вот и мы перешли на Юникод).
В этой заметке кратенько опишу об особенностях перехода с Delphi 2010 на Delphi XE7.
Требования остались прежними – надо чтобы работало и там, и там. Впоследствии, возможно, с отказом от использования Delphi 2010. (От Delphi 7 мы уже отказались… ну за исключением совсем старых программ, которые не развиваются.)
Алгоритм тоже не претерпел сильных изменений – сначала обновление сторонних компонентов, затем обновление компонент и библиотек, которые уже не поддерживаются первоначальными разработчиками, затем обновление своих исходников.
Черновой переход – компиляция и запуск программ со случайным “прокликиванием” интерфейсов и без прогонки тестов – у меня занял всего несколько часов (менее одного рабочего дня). Тяжелее скучнее всего было делать замены такого плана:
с DecimalSeparator на
{$if CompilerVersion >= 24}FormatSettings.{$ifend}DecimalSeparator
потому что надо было:
- найти номер версии Delphi, где это произошло в RTL (в данном случае это XE3 – произошёл отказ аж от 20 штук глобальных переменных в пользу всего одной);
- таких подобных мест очень много – кроме групповой замены я себе ещё помечал, что не плохо бы пересмотреть код.
Удивила так называемая нормализация типов TPoint, TRect, TSize (в XE2). В одном месте даже написал такой хелпер (хелпер оказался лишним, всё решилось проще, путём переименования локальной переменной).
Ну и некоторые другие редкие мелочи, типа UNIT_PTR или LRESULT.
Переход пока черновой, ради спортивного интереса. Скорость полной сборки приложений можно сказать, что осталась прежней – я проводил по несколько измерений после разных сценариев использования IDE – разница в среднем на 0.4 сек дольше, чем в Delphi 2010 (редко бывало и наоборот). Размер исполняемого exe-файла, получаемого на выходе – в XE7 в среднем на 2.5 мегабайта больше (в Release-сборке, проверял на семи проектах с размером экзешников от 6 до 19 мегабайт).
При этом, стоит отметить как плюс в сторону IDE – Find Declaration (он же Ctrl+Click) стал стабильно добираться до цели, в Delphi 2010 же он через какое-то время просто отваливается.
Сейчас в XE7 пока достаёт только то, что при открытии проекта IDE сразу загружает все модули,объявленные в dpr-файле. И из-за того, что компоненты в IDE я пока не устанавливал, Delphi выдаёт десятки окон вида:
Приходится зажимать Escape и ждать… ну секунд 10, терпимо.
Пока я доволен.
P.S.: не спешно (ну очень не спешно) готовлю обновление для BaseForms и dnSplitter. Ваши комментарии могут сыграть роль волшебного пендаля :с).
Про OCIBreak и принудительное прерывание обращения к БД

Я как-то уже не раз писал о том, что мы не используем стандартные компоненты доступа к БД. Почти всё самописное. И работаем мы с Oracle.
Недавно я, наконец-таки, сделал “фишку”, без которой вполне можно жить, но с ней приятнее.
Представьте, что у вас есть запрос к БД, который выполняется длительное время. Ну, например, пользователь указал слишком мягкие критерии для фильтрации данных. Или индекса в БД нет. Или запрос изначально “кривой”. Или всё вместе взятое… Для прерывания выполнения текущего обращения к серверу в OCI есть стандартная функция – OCIBreak.
У нас я реализовывал так: в отдельном потоке запускается запрос к серверу. Если запрос выполняется длительное время, то появляется модальное окошко с кнопкой [Прервать]:
По завершению запроса – окошко скрывается. Если пользователь успеет нажать кнопку – вызывается OCIBreak, и запрос корректно прерывается.
Таймаут до появления такого окошка установлен в две секунды – большинство запросов отрабатывают гораздо быстрее. Но если вдруг пользователь видит это окно, то на интуитивном уровне он может догадаться, что надо как-то ограничивать параметры своего запроса.
Однако иногда бывает так, что OCIBreak не прерывает запрос. Точнее он его прерывает, но приходится долго ждать. Это встречается у нас всё реже, но встречается, обычно в старых запросах, когда клиент говорит серверу – мол сделай то-то, а я подожду. И пока сервер не закончит транзакцию – приложение как бы “висит”. А если пользователь испугался и нажал [Перервать] – начинается откат транзакции. И пользователь снова ждёт, пока сервер не отпустит транзакцию. А приложение – продолжает “висеть”. И, по хорошему, дождаться бы. Но это раздражает, и есть “продвинутые” пользователи, которые тупо прекращают выполнение программы через диспетчер задач.
Вот для таких, довольно редких случаев, я реализовал дополнительную “фишку” – принудительное прерывание. Работает так: если в течении 5 секунд OCIBreak не отпустил обращение к БД, то кнопка [Перервать] превращается в [Принудительно] и её снова можно нажать.
Что же происходит в этом случае? (Сначала я пробовал убить поток, выполняющий обращение к серверу, но это, конечно же, ничем хорошим не кончилось.)
При нажатии на кнопку [Принудительно] я делаю две вещи:
- запускаю отдельным потоком вторую сессию к БД и выполняю: alter system kill session »:sid, :serial» immediate;
- разрываю текущее TCP-соединение на стороне приложения.
Первый пункт нужен, чтобы сервер понял о наших намерениях – мы не собираемся больше ждать. Второй пункт для меня был не тривиальным. Не вдаваясь в подробности поиска решения, привожу код модуля, который у меня получился:
unit MyMIBUtils;
interface
uses
Windows;
type
ULONG = Integer;
PVOID = Pointer;
const
ANY_SIZE = 1;
AF_INET = 2;
type
PMIB_TCPROW = ^MIB_TCPROW;
_MIB_TCPROW_W2K = packed record
dwState: DWORD;
dwLocalAddr: DWORD;
dwLocalPort: DWORD;
dwRemoteAddr: DWORD;
dwRemotePort: DWORD;
end;
MIB_TCPROW = _MIB_TCPROW_W2K;
TMibTcpRow = MIB_TCPROW;
PMibTcpRow = PMIB_TCPROW;
const
MIB_TCP_STATE_CLOSED = 1;
MIB_TCP_STATE_LISTEN = 2;
MIB_TCP_STATE_SYN_SENT = 3;
MIB_TCP_STATE_SYN_RCVD = 4;
MIB_TCP_STATE_ESTAB = 5;
MIB_TCP_STATE_FIN_WAIT1 = 6;
MIB_TCP_STATE_FIN_WAIT2 = 7;
MIB_TCP_STATE_CLOSE_WAIT = 8;
MIB_TCP_STATE_CLOSING = 9;
MIB_TCP_STATE_LAST_ACK = 10;
MIB_TCP_STATE_TIME_WAIT = 11;
MIB_TCP_STATE_DELETE_TCB = 12;
type
TCP_TABLE_CLASS = Integer;
const
TCP_TABLE_BASIC_LISTENER = 0;
TCP_TABLE_BASIC_CONNECTIONS = 1;
TCP_TABLE_BASIC_ALL = 2;
TCP_TABLE_OWNER_PID_LISTENER = 3;
TCP_TABLE_OWNER_PID_CONNECTIONS = 4;
TCP_TABLE_OWNER_PID_ALL = 5;
TCP_TABLE_OWNER_MODULE_LISTENER = 6;
TCP_TABLE_OWNER_MODULE_CONNECTIONS = 7;
TCP_TABLE_OWNER_MODULE_ALL = 8;
type
PMIB_TCPROW_OWNER_PID = ^MIB_TCPROW_OWNER_PID;
MIB_TCPROW_OWNER_PID = packed record
dwState: DWORD;
dwLocalAddr: DWORD;
dwLocalPort: DWORD;
dwRemoteAddr: DWORD;
dwRemotePort: DWORD;
dwOwningPid: DWORD;
end;
TMibTcpRowOwnerPid = MIB_TCPROW_OWNER_PID;
PMibTcpRowOwnerPid = PMIB_TCPROW_OWNER_PID;
PMIB_TCPTABLE_OWNER_PID = ^MIB_TCPTABLE_OWNER_PID;
MIB_TCPTABLE_OWNER_PID = packed record
dwNumEntries: DWord;
Table: array [0..ANY_SIZE - 1] of MIB_TCPROW_OWNER_PID ;
end;
TMibTcpTableOwnerPid = MIB_TCPTABLE_OWNER_PID;
PMibTcpTableOwnerPid = PMIB_TCPTABLE_OWNER_PID;
function SetTcpEntry(const pTcpRow: MIB_TCPROW): DWORD; stdcall;
function GetExtendedTcpTable(pTcpTable: PVOID; var dwSize: DWORD; bOrder: BOOL;
ulAf: ULONG; TableClass: TCP_TABLE_CLASS; Reserved: ULONG): DWORD; stdcall;
function KillProcessAllTCPConnections(AProcessId: DWORD): DWORD;
implementation
const
iphlpapilib = 'iphlpapi.dll';
function SetTcpEntry; external iphlpapilib name 'SetTcpEntry';
function GetExtendedTcpTable; external iphlpapilib name 'GetExtendedTcpTable';
function KillProcessAllTCPConnections(AProcessId: DWORD): DWORD;
var
TCPTable: PMibTcpTableOwnerPid;
Size: DWORD;
Res: DWORD;
I: DWORD;
TCPRow: TMibTcpRow;
begin
Result := 0;
TcpTable := nil;
Size := 0;
Res := GetExtendedTcpTable(TCPTable, Size, False, AF_INET, TCP_TABLE_OWNER_PID_CONNECTIONS, 0);
if Res <> ERROR_INSUFFICIENT_BUFFER then
Exit;
GetMem(TCPTable, Size);
try
Res := GetExtendedTcpTable(TCPTable, Size, False, AF_INET, TCP_TABLE_OWNER_PID_CONNECTIONS, 0);
if Res <> NO_ERROR then
Exit;
for I := 0 to TCPTable^.dwNumEntries - 1 do
if TCPTable^.Table[I].dwOwningPID = AProcessId then
with TCPTable^.Table[I] do
begin
TCPRow.dwState := MIB_TCP_STATE_DELETE_TCB;
TCPRow.dwLocalAddr := dwLocalAddr;
TCPRow.dwLocalPort := dwLocalPort;
TCPRow.dwRemoteAddr := dwRemoteAddr;
TCPRow.dwRemotePort := dwRemotePort;
Res := SetTCPEntry(TCPRow);
if Res = NO_ERROR then
Inc(Result);
end;
finally
FreeMem(TCPTable);
end;
end;
end.
Этот код работает на Windows XP with SP2 и выше.
Соответственно я вызываю:
KillProcessAllTCPConnections(GetCurrentProcessId);
и все текущие TCP-соединения моего процесса прерываются (а у меня оно всего одно). Получить информацию о TCP-соединении, которое используется именно текущим OCI-обращением к серверу мне не удалось, да я особо и не пытался. Если вдруг понадобится – это можно сделать, просмотрев активные соединения непосредственно до и после коннекта к БД.
P.S.: Пару слов про NonBlocking-mode, который есть в OCI. В современном мире многопоточных операционных систем его не рекомендуется использовать вовсе.
Delphi Event bus

- Объявление слушателя — аннотация
- Синхронность отправки в шину — по умолчанию отправка синхронна.
- Асинхронность — в планах на будующее
- Фильтрация событий — статическая, т.е. у обработчиков в листенера можно задать декларативно фильтры и их значения. При посылки события задаются значения предопределенных фильтров для конкретного события
- Иерархия событий — да. Событие это объект. Есть базовый класс всех событий. Обработчик может ждать события определенного класса и всех его наследников
- Строгость ссылочности листенера — строгая. Обязательная дерегистрация. Регистрируются и дерегистрируются сразу все обработчики в листенере
- Приоритет обработчиков — отсутствует. Но дополнительно реализован механизм хуков, похожий на виндовый механизм.
- Реализованы хуки — возможность игнорировать фильтры и выстраивать цепочки обработки. Вызов хуков строго обратен порядку регистрации.







