Доброго времени суток дорогие читатели блога, а особенно любителям MapWindow GIS. В данной статье я хотел бы рассмотреть возможность выделения объектов на слое, а также получение информации о данном объекте. Мы уже рассматривали тему в MapWindow GIS 4.7, как можно выделять мышью объекты, но в отличие от версии MapWindow GIS 4.8 там есть свои нюансы и мне пришлось немного поковыряться, чтобы понять принцип выделения в новой версии объектов.

В принципе ничего сложного нет, как оказалось, а выделение объектов в новой версии MapWindow GIS смотрится очень эффективно, а тем более мы добавим получение информации о данных объектах.

Для начала создайте какой-нибудь проект MapWindow GIS, чтобы можно было в него загружать Shape-файлы. Я возьму свой готовый проект, и буду демонстрировать работы именно на нем. Итак, при загрузке Shape-файлов и дальнейшем отображении их в TMap, необходимо активировать свойства TMapSendMouseMove. Я активирую именно данное свойства, так как я буду выделять объекты, проходя по ней мыши, то есть буду выводить мышью по TMap и каждый объект будет выделять, а затем можно будет нажать левой кнопкой мыши по выделенному объекту и получить информацию о нем. Информацию будем выводить с помощью новой формы, а можно и в виде простого сообщения.

Итак, проект у Вас готов, активируем свойства TMapSendMouseMove, его лучшего всего делать после загрузки Shape-файла, но это, как я уже говорил выше. У меня это выглядит следующим образом:

procedure TForm1.Action1Execute(Sender: TObject);
var
 h,i:integer;
 shp:IShapefile;
 TN:TTreeNode;
 s,s1:Pchar;
 str:string;
begin
   if OpenDialog1.Execute then
    begin
     FName:=OpenDialog1.FileName;
     ShapeLayer[count].shp:=CoShapefile.Create;
     ShapeLayer[count].sname:=FName;
     ShapeLayer[count].shp.Open(ShapeLayer[count].sname,nil);
     ShapeLayer[count].nshp:=0;
     ShapeLayer[count].handle:=Map1.AddLayer(ShapeLayer[count].shp,True);
     StatusBar1.Panels[0].Text:='Количество слоев: '+IntToStr(Map1.NumLayers);
     Map1.SendMouseMove:=True;
     Map1.SendMouseDown:=True;
     TN:=TreeView1.Items.Add(nil,ExtractFileName(ShapeLayer[count].sname));
     for i:=0 to ShapeLayer[count].shp.NumShapes-1 do
      begin
       TreeView1.Items.AddChild(TN,ShapeLayer[count].shp.CellValue[1,i]);
      end;
     Map1.ZoomToMaxExtents;
     TreeView1.Selected:=TN;
     inc(count);
    end;
end;

Из данного кода можно увидеть, что у меня сначала происходит открытие Shape-файлов, а также последующего его отображение в компоненте TMap, а затем я уже активирую данные свойства. Да еще лучше всего включить свойство ShowRedrawTime, чтобы смотреть за какой промежуток времени у нас перерисовывается данные на слое.

Map1.ShowRedrawTime:=True;

Все, у нас почти, что все готово для того, чтобы начать выделять с помощью мыши, когда мы перемещаемся по слою. Для этих целей нам необходимо выделить компонент TMap и открыть вкладку События в инспекторе объектов и найти там событие OnMouseMove. Далее на данное событие нам необходимо написать следующий код:

procedure TForm1.Map1MouseMove(ASender: TObject; Button, Shift: Smallint;
  x, y: Integer);
var
 projx,projy:double;
 ShID:OleVariant;
 ext:Extents;
 i,j,count:integer;
 array_of_word:array of integer;
begin
  count:=TreeView1.Selected.Index;
  Map1.PixelToProj(x,y,projx,projy);
  ext:=CoExtents.Create;
  ext.SetBounds(projx,projy,0.0,projx,projy,0.0);
  ShapeLayer[count].shp.SelectNone;
  ShapeLayer[count].shp.SelectionTransparency:=127;
  ShapeLayer[count].shp.SelectionColor:=clRed;
  if ShapeLayer[count].shp.SelectShapes(ext,0.0,INTERSECTION,ShID)=True then
   begin
    j:=VarArrayHighBound(ShID, VarArrayDimCount(ShID));
    if j=1 then
     begin
      for i:=1 to j do
       begin
        ShapeLayer[count].shp.ShapeSelected[ShID[i]]:=True;
        ShpID:=Integer(ShID[i]);
       end;
     end;
    if j=0 then
     begin
      for i:=0 to j do
       begin
        ShapeLayer[count].shp.ShapeSelected[ShID[i]]:=True;
        ShpID:=Integer(ShID[i]);
       end;
     end;
   end;
  Map1.Redraw;
end;

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

Сперва мы снимаем выделения на нашем слое, если они имеются, если их нет, все равно ничего страшного, это мы делаем с помощью свойства SelectNone. Далее мы устанавливаем прозрачность выделения, это делается с помощью свойства SelectionTransparency, я установил значение 127, то есть максимальное, соответственно у меня прозрачности не будет никакой. Ну и на последок мы выставляем цвет нашего выделения, это делается с помощью свойства SelectionColor, как видно из кода программы, то он у меня установлен в красный.

Далее идет строчка кода:

if ShapeLayer[count].shp.SelectShapes(ext,0.0,INTERSECTION,ShID)=True then

С помощью данного кода мы узнаем – попадаем ли мы мышью в какой-нибудь объект на слое, если попали, то записываем данные о данном объекте в массив ShID. С помощью кода:

J:=VarArrayHighBound(ShID, VarArrayDimCount(ShID));

Мы определяем границы нашего массива (верхнюю границу). Тут я заметил, что при разных границах массива необходимо начинать цикл выделения по-разному, поэтому и сделал два условия: когда j=1 и когда j=0. В зависимости от того, чему равняется j, мы начинаем цикл либо с 1, либо с 0. Ну и с помощью ShapeSelected мы выделяем область, нам необходимо данное свойство установить в True. В конце обязательно нам необходимо перерисовать TMap, это делается с помощью процедуры Redraw и если у Вас будет включено свойство ShowRedrawTime, то Вы увидите, за сколько времени у Вас перерисовались данные. Вот что у меня получилось на самом деле:

То есть когда подводим мышью к какой-нибудь области слоя, то он выделяется (выделяется тот, на котором установлен указатель мыши). Вот и все, теперь нам необходимо получить данные о выделенном нашем слое.

Для этих целей, я создал новую форму и разместил на ней компонент TListView, в нем я буду отображать список полей, а также данные поля (для конкретного объекта слоя). Для получения списка полей, я использую компонент TDBF, Вы же можете использовать тот, который Вам будет удобен. Далее переходим к нашему компоненту TMap. Нам теперь необходимо задействовать событие OnMouseDown. Поэтому выделяем компонент TMap и во вкладке события инспектора объектов находим данное событие и пишем следующий код:

procedure TForm1.Map1MouseDown(ASender: TObject; Button, Shift: Smallint;
  x, y: Integer);
var
 xx,yy:double;
begin
 if Action12.Checked=True then
  begin
   if Button=SmallInt(mbright) then
    begin
     count_s_info:=TreeView1.Selected.Index;
     InfoShape.ShowModal;
    end;
   end;
end;

Тут все очень просто, мы просто определяем, чтобы было нажатие мыши и если оно произошло, то мы вызываем нашу форму, на которой будем отображать данные об объекте слоя. Давайте вернемся не надолго к событию OnMouseMove и вспомним нашу переменную ShID. Так вот, давайте ее объявим в спецификаторе доступа publiс типа OleVariant. Это мы делаем, чтобы она была доступна в другим модулях, нам не надо объявлять больше никаких переменных для того, чтобы получить идентификатор (номер) объекта на нашем слое.

Далее на событие OnShow нашей формы, на которой мы будем отображать данные объекта я написал следующий код:

procedure TInfoShape.FormShow(Sender: TObject);
var
 i:integer;
 DB:TDBF;
 TableName,sID:WideString;
begin
   Caption:='Данные слоя '+IntToStr(Form1.ShpID);
   Flag:=False;
   for i:=0 to Form1.St.Count-1 do
    begin
     sID:=Form1.St.Strings[i];
     if StrToInt(sID)=Form1.ShpID then
      begin
       CheckBox1.Checked:=True;
       Flag:=True;
      end;
    end;
   if Flag=False then
    CheckBox1.Checked:=False;
   ListView1.Clear;
   DB:=TDBF.Create(Form3);
   TableName:=Form1.ShapeLayer[Form1.count_s_info].sname;
   Delete(TableName,Length(TableName)-3,4);
   TableName:=TableName+'.dbf';
   DB.TableName:=TableName;
   DB.Open;
   for i:=1 to DB.FieldCount do
    begin
    ListView1.Items.Add.Caption:=DB.GetFieldName(i);
    ListView1.Items.Item[i-1].SubItems.Add(Form1.ShapeLayer[Form1.count_s_info].shp.CellValue[i-1,Form1.ShpID]);
    end;
end;

Все просто тут, отображаем в заголовке формы номер объекта, на который мы получаем данные, затем определяем имя таблицы (DBF-файла), из которого нам необходимо будет вытаскивать данные и в цикле я получаю их в самом конце обработчика.

Вот и все, вот что получается в итоге:

Неплохо, правда ли. Теперь с данным слоем можно делать все, что Вы захотите, раскрашивать, скрывать, ставить метку, удалять и так далее. Я Вам показал, как можно выделять данные на слое с помощью перемещения мыши, а на основе данного примера Вы можете сделать выделение именно определенной области с помощью специального курсора выделения.

Исходных кодов, я никаких не прикладываю, а только лишь оставляю ссылку на полный проект MapWindow GIS в Delphi, Вы же можете посмотреть, как это все реализовано и ждать следующей серии постов. Удачи Вам!

Также можно посмотреть список статей, доступных по MapWindow GIS

Метки: , , , , ,




К записи “Работа с MapWindow GIS. Выделение отдельных областей слоя и получение данных областей” оставлено комментариев: 5.

  1. Alexandr:

    Вот спасибо огромное то что нужно!

  2. Артём:

    Спасибо!
    Работает почти всё..
    Проблема такая: пока двигаю мышью по слою, всё выделяется нормально (закрашивается в красный при наведении), как только нажимаю — выскакивает окошко с информацией об объекте, закрываю его, и теперь начинается жесть — объекты больше не раскрашиваются, карта подвисает.. что с этим можно сделать?

    • Артём:

      Разобрался — у меня открывалась не форма, а ShowMessage(), вот из-за этого и глючило.

      Вопрос теперь такой: если много объектов, то при движении мыши по карте, курсор постоянно меняется на круглый (загрузка), можно как-нибудь запретить изменение курсора?

Оставить комментарий на Andrey

Вы можете использовать следующие теги:

*