Date Редакция Категория edu Теги курсы

Расширение GIS дает возможность загружать в NetLogo данные из геоинформационных систем (ГИС), как векторные (точки, линии, полигоны), так и растровые (сетки — grids). Векторные данные поддерживаются в формате шейп-файлов ESRI (.shp) — наиболее распространенном формате хранения и обмена ГИС-данными. Растровые данные поддерживаются в форме Grid-файлов (.asc или .grd).

В Models Library, в разделе Code examples помещены два примера: GIS General Examples и GIS Gradient Example. Изучение этих примеров — самый удобный на данный момент способ изучения возможностей расширения GIS.

Все команды расширения GIS начинаются с префикса «gis:»

Как загружать геоданные

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

extensions [ gis ] ;; подключаем расширение gis

Затем нужно задать преобразование между пространством геоданных и игровым миром NetLogo. При этом можно указать картографическую проекцию, в которую будут преобразованы геоданные или указать используемую в них географическую систему координат.

Для указания географической системы координат используется команда gis:load-coordinate-system

; географическая СК в файле projection.prj
gis:load-coordinate-system "WGS84.prj"
; выбор ГСК с помощью переменной projection
gis:load-coordinate-system (word "data/" projection ".prj")

Команда word осуществляет конкатенацию строк, а значение переменной projection — строка, содержащая имя файла с картографической проекцией. В результате выполнения word формируется полный путь к файлу с проекцией.

Для задания картографической проекции геоданные должны содержать файлы .prj, одноименные с шейп-файлами. Если prj-файлы существуют, то они определяют, в какой картографической проекции будут представлены геоданные. Если эти файлы отсутствует, то предполагается, что набор данных уже спроектирован как нам нужно, и преобразуется в игровой мир NetLogo «как есть».

Как только определен способ преобразования геоданных, можно загружать наборы геоданных, используя команду gis:load-dataset:

gis:load-dataset "data/ukr_city.shp"

Эта команда возвращает или векторный или растровый набор данных, в зависимости от типа переданного ей файла.

Преобразование между пространством геоданных и игровым миром осуществляется так: берется множество «конвертов» (envelopes) (т.е. прямоугольников, образованных минимальными и максимальными значениями координат объекта или группы объектов) для всех используемых наборов данных, и отображается непосредственно в границах игрового мира NetLogo. Например, так (файл GIS_Cities_ukr0.nlogo. Все файлы примеров находятся в архиве):

extensions [ gis ] ;; подключаем расширение gis
globals [ cities-dataset ]

to setup
  clear-all
  ; Загрузим набор геоданных
  set cities-dataset gis:load-dataset "data/ukr_city.shp"
  ; Конверт из набора геоданных city-dataset становится "мировым"
  gis:set-world-envelope (gis:envelope-of cities-dataset)
end

Пояснений заслуживает строчка

gis:set-world-envelope (gis:envelope-of cities-dataset)

Команда gis:envelope-of объект возвращает конверт, т.е. замкнутый прямоугольник, содержащий четыре элемента: [ minimum-x maximum-x minimum-y maximum-y ] для объекта в ГИС-координатах. Объект может быть агентом, множеством агентов, векторным или растровым набором данных или векторным объектом.

В нашем примере

gis:envelope-of cities-dataset

возвращает конверт для набора данных cities-dataset.

Команда gis:set-world-envelope конверт определяет преобразование конверта, изображающего игровой мир NetLogo в конверт, определенный в ГИС-данных, при котором сохраняются одинаковые масштабы преобразований вдоль осей x и y. Этот примитив используется часто, поскольку обычно необходимо преобразовать игровой мир NetLogo в целом, а не какую-то из его частей.

У нас получается, что

gis:set-world-envelope <конверт координат городов>

Как узнать, что хранится в шейп-файле

Команда gis:feature-list-of НаборВекторныхДанных возвращает список свойств для заданного набора векторных геоданных. Если к рассмотренному выше коду добавить, например, такой:

; Какие данные о городах хранятся в шейп-файле?
to display-cities
  foreach gis:feature-list-of cities-dataset
  [ print ? ]
end

Это даст в результате:

l2_0.png

Команда gis:property-names ВекторныйНаборДанных возвращает список строк — полей заданного набора геоданных. Следовательно

print gis:property-names cities-dataset

Выдаст список полей данных о городах:

[UKR_CITY_I ID NAME]

Если нас интересуют координаты городов, можно поступить так (заменяем старый код display-cities новым):

; Отмечаем города, основываясь на данных из шейп-файла
to display-cities
  foreach gis:feature-list-of cities-dataset
      ; gis:vertex-lists-of возвращает для каждого города список списков точек,
      ; из которого нам нужны только два первых значения — координаты города.
      ; Поэтому первый first возвращает первый элемент — первый вложенный
      ; список, а второй first — элементы этого списка (координаты). 
      ; Координаты храним в локальной переменной location 
  [   let location gis:location-of (first (first (gis:vertex-lists-of ?)))
    ; выводим значения координат городов, отнесенных к игровому миру
      type gis:property-value ? "NAME"
      type ": "
      print location
      ]
end

Что дает

l2_1.png

Команда gis:vertex-lists-of ВекторныйОбъект возвращает список, состоящий из списков значений вершин. Для точечных данных, каждый список будет содержать ровно одну вершину: расположение данной точки.

gis:location-of Вершина — возвращает список из двух элементов, содержащий координаты x и y (именно в таком порядке) заданной вершины (vertex), переведенные в соответствующие координаты игрового мира, или пустой список, если данная вершина лежит за пределами игрового мира.

gis:property-value ВекторныйОбъект ИмяПоля — возвращает значение заданного поля указанного векторного объекта. Возвращаемая величина может быть числом, строкой или логической константой, в зависимости от типа поля в соответствующем файле данных.

В нашем примере: gis:property-value ? "NAME", т.е. объектом являются векторные данные о городе, а полем — "NAME" — название города.

Отображение точечных объектов

Примером точечных объектов могут служить города, попробуем изобразить расположение городов на карте мира.

extensions [ gis ]
globals [ cities-dataset ]
breed [ city-labels city-label ]

to display-cities
  foreach gis:feature-list-of cities-dataset
  [   let location gis:location-of (first (first (gis:vertex-lists-of ?)))
      ; Значение location может быть пустым списком, если данная точка
      ; лежит за пределами координат игрового мира. Поэтому устраиваем проверку.
      if not empty? location
      [ create-city-labels 1 ; создадим один элемент рода city-labels
        [ set xcor item 0 location ; значения координат возьмем из location
          set ycor item 1 location
          set label gis:property-value ? "NAME" 
          ; в данной точке поставим метку со значением поля "NAME"
          ] ] ]
end

label — встроенная переменная черепашек (связей). Может принимать значение любого типа (числовое, строковое, логическое). В результате выполнения set label возле изображения черепашки (связи) в игровом мире возникает надпись, значение которой равно значению переменой label. Еще один пример:

ask turtles [ set label who ]
;; все черепашки помечаются своими номерами

После задания координат xcor и ycor уместно добавить строку set size 0, чтобы отображались не сами черепашки, а только их метки. Получим в результате

l2_2.png

Теперь отобразим в игровом мире крупнейшие города. Если население города превышает 500 тыс. человек, то город изображается желтым кружком, диаметр которого пропорционален численности населения, а название города (метка) выводится красным цветом. Если население города меньше 500 тыс. человек, то выводится только название города (белым цветом) (GIS_Cities2.nlogo).

extensions [ gis ]
globals [ cities-dataset ]
breed [ city-labels city-label ]

to setup
  clear-all
  set cities-dataset gis:load-dataset "data/cities.shp"
  gis:set-world-envelope (gis:envelope-of cities-dataset)
end

to display-cities
  foreach gis:feature-list-of cities-dataset
  [  let location gis:location-of (first (first (gis:vertex-lists-of ?)))
      if not empty? location
      [ create-city-labels 1 ; создадим один элемент рода city-labels
        [ set xcor item 0 location ; значения координат возьмем из location
          set ycor item 1 location
          let population gis:property-value ? "POPULATION"
          ifelse (population >= 5E5)
          [ set color yellow
            set shape "circle"
            set size 2E-7 * (gis:property-value ? "POPULATION") 
            set label-color red ]
          [ set size 0]
          ; в данной точке поставим метку со значением поля "NAME"
          set label gis:property-value ? "NAME" 
          ] ] ]
end

l2_3.png

Отображение полигонов

Рассмотрим пример (GIS_Countries0.nlogo):

extensions [ gis ]
globals [ countries-dataset ]

to setup
  clear-all
  set countries-dataset gis:load-dataset "data/countries.shp"
  gis:set-world-envelope (gis:envelope-of countries-dataset)
end

; Рисование полигональных данных из шейп-файла
to display-countries
  gis:set-drawing-color white
  gis:draw countries-dataset 1
end

Существенно новым в примере является только код процедуры display-countries. Здесь используются:

  • gis:set-drawing-color цвет — устанавливает цвет, которым изображаются ГИС-объекты
  • gis:draw ВекторныеДанные ТолщинаЛинии — рисует указанные векторные данные линией заданной толщины и цветом, установленным для ГИС-объектов.

l2_4.png

Возникает вопрос: а нельзя ли аналогично отображать и города? Оказывается можно. Но, если понадобится выводить названия городов, тут не обойтись без gis:vertex-lists-of и пр.

Изобразим теперь кроме контура территории еще и название страны. Это можно сделать так:

extensions [ gis ]
globals [ countries-dataset ]
breed [ country-labels country-label ]

to startup ;; вызывается при первом запуске модели
  ; Загружаем геоданные
  set countries-dataset gis:load-dataset "data/countries.shp"
end

to setup
  clear-all-but-globals
  ; Задаем мировой "конверт" 
  gis:set-world-envelope (gis:envelope-of countries-dataset)
end

; Рисование полигональных данных из шейп-файла
to display-countries
  gis:set-drawing-color white
  gis:draw countries-dataset 1
  foreach gis:feature-list-of countries-dataset 
         ; находим координаты центроида, чтобы...
    [ let centroid gis:location-of gis:centroid-of ? ; поставить название страны
      if not empty? centroid
      [ create-country-labels 1
        [ set xcor item 0 centroid
          set ycor item 1 centroid
          set size 0
          set label gis:property-value ? "CNTRY_NAME" ] ] ]
end

to clear-all-but-globals 
  reset-ticks ct cp cd clear-links clear-all-plots clear-output 
end

Основная проблема здесь — указакть название страны в центре ее территории. Для этого используется команда gis:centroid-of ВекторныйОбъект. Она возвращает единственную точку — центроид (центр гравитации) для данного объекта. Для точечных наборов данных координаты центроида равны средним значение x- и y-координат объекта.

В результате работы программы, получим:

l2_5.png

Кроме того, в этом примере используются процедуры startup и clear-all-but-globals. Процедура startup вызывается автоматически при открытии файла с моделью. Это очень удобно для загрузки геоданных. Но если после этого в процедуре setup использовать clear-all, это очистит память и, среди прочего, сотрет загруженные геоданные (являющиеся глобальными переменными). Вместо этого используется процедура clear-all-but-globals, которая очищает то же, что и clear-all, за исключением глобальных переменных.

Загрузка нескольких наборов геоданных

До сих пор мы использовали наборы геоданных по одному. Если использовать несколько наборов (например, данные о городах и странах, как в примере ниже), то нужно объединить все имеющиеся у нас конверты с данными с помощью команды gis:envelope-union-of.

to setup
  clear-all
  clear-output
  ; Загрузим наборы геоданных
  set cities-dataset gis:load-dataset "data/cities.shp"
  set countries-dataset gis:load-dataset "data/countries.shp"  
  ; Мировой конверт представляет собой объединение конвертов наборов геоданных
  gis:set-world-envelope (gis:envelope-union-of (gis:envelope-of cities-dataset)
                                                (gis:envelope-of countries-dataset)) 
end

Отображение полилиний

Полилиниями, как правило, представляются реки и автодороги. Действуя аналогично тому, как мы это делали при отображении стран, получаем (GIS Rivers1.nlogo)

extensions [ gis ]
globals [ rivers-dataset ]

to setup
  clear-all
  ; Загружаем геоданные
  set rivers-dataset gis:load-dataset "data/rivers.shp"
  ; Задаем мировой "конверт"
  gis:set-world-envelope (gis:envelope-of rivers-dataset)
end

; Рисуем полилинии из шейп-файла
to display-rivers
  gis:set-drawing-color blue ; текущий цвет ГИС-объектов — синий
  gis:draw rivers-dataset 1 ; нарисовать линией толщины 1
end

l2_6.png

Отображаем название рек также как и для стран, используя центроид:

to display-rivers
  gis:set-drawing-color blue ; текущий цвет ГИС-объектов — синий
  gis:draw rivers-dataset 1 ; нарисовать линией толщины 1
  foreach gis:feature-list-of rivers-dataset ; название реки отмечается на карте с помощью центроида
    [ let centroid gis:location-of gis:centroid-of ?
      ; центроид может оказаться пустым, если он лежит за пределами
      ; игрового мира, определенными текущим преобразованием ГИС-координат в
      ; координаты игрового мира
      if not empty? centroid
      [ create-river-labels 1
          [ set xcor item 0 centroid
            set ycor item 1 centroid
            set size 0 ; создается "невидимая" черепашка, зато с подписью:
            set label gis:property-value ? "NAME" ] ] ]
end

l2_7.png

Если проделать все рассмотренные операции вместе для данных о городах, областях, реках, автодорогах, то должно получиться нечто вроде:

l2_8.png



Комментарии

comments powered by Disqus