Всем доброго времени суток! Продолжаем цикл статей по MapWindow GIS в Delphi. На этот раз, как и обещал, будем учиться, как организовать поиск на слоях (то есть картах). Поиск будем организовывать следующим образом:

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

Если Вам понравился мой план, то можете его придерживаться. Теперь давайте создадим форму для поиска, она у меня выглядит следующим образом:

На данной форме у меня следующие компоненты:

  • TCombobox – для вывода списка полей, по которым возможно будет осуществлять поиск
  • TCombobox – для вывода условия поиска (=, <>, <, > и другие, можете сделать по своему желанию)
  • Tedit – для ввода значения, по которому будет осуществляться поиск
  • TListBox – список слоев, по которым мы сможем осуществлять поиск
  • TButton – осуществить поиск
  • TButton – посмотреть информацию о полях (будет выводиться сообщение)

Для доступа к dbf-файлам я использую компонент TDBF, Вы можете использовать любые другие компоненты, в том числе и стандартные, но я решил использовать именно данный компонент, мне он очень понравился. Для того чтобы получить список всех полей dbf-файла я на событие OnShow формы написал следующий код:

procedure TForm3.FormShow(Sender: TObject);
var
 i,count:integer;
begin
   for i:=0 to Form1.count-1 do
    begin
     ListBox1.Items.Add(ExtractFileName(Form1.ShapeLayer[i].sname));
    end;
   ListBox1.Selected[Form1.TreeView1.Selected.SelectedIndex]:=True;
   count:=ListBox1.ItemIndex;
   DB:=TDBF.Create(Form3);
   TableName:=Form1.ShapeLayer[count].sname;
   Delete(TableName,Length(TableName)-3,4);
   TableName:=TableName+'.dbf';
   DB.TableName:=TableName;
   DB.Open;
   for i:=1 to DB.FieldCount do
    ComboBox1.Items.Add(DB.GetFieldName(i));
   ComboBox1.ItemIndex:=0;
end;

В переменной count у меня храниться количество слоев (тех, сколько открыл пользователь), исходя из этого количества я добавляю список всех полей в TListBox. Далее создаю компонент для доступа к dbf-файлам и получаю имя dbf-файла. Имя получаю путем удаления расширения shp и добавления расширения dbf, так как эти файлы находятся по одному пути, открываю таблицу и в цикле считываю количество полей и добавляю их в TComboBox.

В другой TComboBox я добавил следующие условия поиска: =, >. Если есть желания добавьте еще какие-нибудь, на Ваш взгляд, например LIKE и другие.

На событие OnClick компонента TListBox я написал следующий код:

procedure TForm3.ListBox1Click(Sender: TObject);
var
 count,i:integer;
begin
   DB.Close;
   count:=ListBox1.ItemIndex;
   TableName:=Form1.ShapeLayer[count].sname;
   Delete(TableName,Length(TableName)-3,4);
   TableName:=TableName+'.dbf';
   DB.TableName:=TableName;
   DB.Open;
   ComboBox1.Clear;
   for i:=1 to DB.FieldCount do
    ComboBox1.Items.Add(DB.GetFieldName(i));
   ComboBox1.ItemIndex:=0;
end;

Это означает, что пользователь может искать на различных слоях, поэтому при выборе другого слоя в TListBox, мы заполняем новыми данным TCombBox со списком полей dbf-файла.

На событие OnClick кнопки «Посмотреть данные таблицы атрибутов» я написал следующий код:

procedure TForm3.BitBtn2Click(Sender: TObject);
begin
  DB.CodePage:=ANSI;
  ShowMessage('Имя поля'+#9+': '+DB.GetFieldName(ComboBox1.ItemIndex+1)+#13+
              'Размер поля'+#9+': '+IntToStr(DB.GetFieldSize(ComboBox1.ItemIndex+1))+#13+
              'Точность'+#9+': '+IntToStr(DB.GetFieldPrecision(ComboBox1.ItemIndex+1))+#13+
              'Данные первой строки'+#9+': '+DB.GetFieldData(ComboBox1.ItemIndex+1));
end;

В результате чего, у меня появляется следующее сообщение, когда я жму на кнопку:

На событие OnClick кнопки «Найти» я написал следующий код:

procedure TForm3.BitBtn1Click(Sender: TObject);
var
 i:integer;
begin
  case ComboBox2.ItemIndex of
   0: begin
   for i:=0 to Form1.ShapeLayer[Form1.count-1].shp.NumShapes-1 do
    begin
     if Edit1.Text=Form1.ShapeLayer[Form1.count-1].shp.CellValue[ComboBox1.ItemIndex,i] then
      begin
        Form1.Map1.NewDrawing(dlSpatiallyReferencedList);
        Form1.Map1.DrawCircle(Form1.ShapeLayer[Form1.count-1].shp.Shape[i].Point[0].x,Form1.ShapeLayer[Form1.count-1].shp.Shape[i].Point[0].y,0.5,clRed,True);
        //Break;
      end;
    end;
    end;
    1: begin
       for i:=0 to Form1.ShapeLayer[Form1.count-1].shp.NumShapes-1 do
    begin
     if Edit1.Text>Form1.ShapeLayer[Form1.count-1].shp.CellValue[ComboBox1.ItemIndex,i] then
      begin
        Form1.Map1.NewDrawing(dlSpatiallyReferencedList);
        Form1.Map1.DrawCircle(Form1.ShapeLayer[Form1.count-1].shp.Shape[i].Point[0].x,Form1.ShapeLayer[0].shp.Shape[Form1.count-1].Point[0].y,0.5,clRed,True);
        //Break;
      end;
    end;
    end;
    end;
   Close;
end;

В зависимости от выбранного пользователем условия поиска идем по той или иной ветки. Я также закомментировал break, что означает – каждый может выбрать поиск до первого, найденного и прекратить его (убрать комментарий break), либо же искать дальше. Вот, тогда что у меня, получается, по нажатию на кнопку «Найти»:

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

Еще один нюанс состоит в том, что в функции DrawCircle задается радиус круга. Если, вы поставили, например 0.5, то не факт, что радиус будет везде одинаковый, это в первую очередь будет зависеть от масштаба самого слоя и проекции, которая задается в файле prj. Поэтому для того, чтобы на разных настроенных проекциях, выводился круг одинакового размера, я делаю так:

Map1.CurrentScale/1000

Это значение я задаю в процедуре DrawCircle, где задается радиус, в примере Выше я задавал 0.5, но как говорил не факт, что на всех картах он будет рисоваться как 0.5. Поэтому поставьте следующий код:

Map1.CurrentScale/1000

И все будет замечательно и красиво, по крайней мере, у меня так, я это тестировал на 3-х разных масштабах и слоях, я разными проекциями, соответственно и файлами prj.

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

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

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

Вот такой у меня получился код, который позволяет это сделать:

procedure TForm1.Action12Execute(Sender: TObject);
var
 i:integer;
 shpNet:IShapeNetwork;
begin
   Map1.CursorMode:=cmNone;
   Map1.NewDrawing(dlSpatiallyReferencedList);
   Map1.MapUnits:=umMeters;
   for i:=0 to ShapeLayer[0].shp.NumShapes-1 do
    begin
     if ShapeLayer[0].shp.CellValue[0,i]='MOSKVA' then
      begin
       Map1.ZoomToShape(0,i);
       Map1.DrawCircle(ShapeLayer[0].shp.Shape[i].Point[0].x,ShapeLayer[0].shp.Shape[i].Point[0].y,Map1.CurrentScale/1000,clRed,True);
       Map1.CurrentScale:=2;
       break;
      end;
    end;  
end;

Понятно, я думаю все. Вначале мы убираем на карте курсоры, но это по желанию каждого, можете и не убирать, это по желанию каждого. Затем мы начинаем рисование. Как видно из кода программы, я задаю конкретный регион, естественно это можно сделать при помощи ввода пользователя, то есть, чтобы пользователь сам задавал критерии поиска.

Далее в цикле мы проходимся (ищем) и если нашли такое, то с помощью процедуры ZoomToShape увеличиваем данную область и плюс я еще рисую круг на ней, ну так, для разнообразия. С помощью CurrentScale задаем, на сколько нам увеличивать, в конце условия я ставлю break, так как я говорил выше: если будет несколько найденных, мы не сможем одновременно их увеличить, поэтому нам необходимо будет прекратить цикл.

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

Метки: , , , , ,

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

Вы можете использовать следующие теги: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

*