https://wiki.gis-lab.info/api.php?action=feedcontributions&user=%D0%90%D0%BB%D0%B5%D0%BA%D1%81%D0%B0%D0%BD%D0%B4%D1%80+%D0%9C%D1%83%D1%80%D1%8B%D0%B9&feedformat=atomGIS-Lab - Вклад [ru]2024-03-28T11:03:43ZВкладMediaWiki 1.39.6https://wiki.gis-lab.info/index.php?title=%D0%9F%D1%80%D0%B0%D0%BA%D1%82%D0%B8%D0%BA%D0%B0_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F_raster_tools&diff=26380Практика использования raster tools2018-07-30T20:40:07Z<p>Александр Мурый: </p>
<hr />
<div>{{Статья|Опубликована|raster-tools}}<br />
{{Аннотация|В статье рассматриваются примеры практического использования набора растровых инструментов raster_tools.}}<br />
<br />
<br />
== Знакомство с raster_tools ==<br />
<br />
В течении продолжительного времени автор статьи развивает инструментарий для препарирования георастров. Он является набором рецептов, упрощающих взаимодействие с гибкой, но сложной структурой Python + GDAL. К сегодняшнему времени '''raster_tools''' напоминает некий «швейцарский нож» с разнообразным функционалом. <br />
Код проекта расположен на [https://github.com/oldbay/raster_tools https://github.com/oldbay/raster_tools] , самый простой способ его установки в каталоге пользователя - при помощи pip:<br />
<br />
<syntaxhighlight lang="bash"><br />
pip2.7 install --user git+https://github.com/oldbay/raster_tools<br />
</syntaxhighlight><br />
<br />
'''raster_tools''' состоит из следующих классов:<br />
<br />
'''raster2array''' - Базовый класс, отвечающий за весь функционал загрузки растра и сохранение numpy-массива выбранного канала(band). Выгруженный из растра массив может быть преобразован методами данного класса. <br />
<br />
'''array2raster(raster2array)''' - Субкласс, отвечающий за сохранение обработанного массива в файле георастра. Он также может сформировать виртуальный растр в памяти с возможностью обработки методами базового класса.<br />
<br />
'''raster2transform(raster2array)''' - Субкласс, отвечающий за трансформацию растра: изменение размерности массива и перепроецирование. Также есть поддержка методов базового класса.<br />
<br />
'''raster2calc(raster2array)''' - Класс итерационного калькулятора. Он не наследует базовый класс raster2array, но использует некоторые его методы опосредованно.<br />
<br />
'''raster2multiarray(raster2array)''' - Класс обработки мультирастров. Позволяет миксовать каналы мультиканального растра и обрабатывать его некоторыми методами raster2array.<br />
<br />
'''multiarray2multiraster''' - Класс сохранения массивов, обработанных методами raster2multiarray, в мультиканальный растр.<br />
<br />
Основной смысл существования всего этого «зоопарка» классов - это загрузка, обработка и выгрузка двухмерного numpy-массива георастров. Помимо объекта 2-х мерного numpy-массива некоторые методы raster2array позволяют выгружать данные в формате самоназванного «стандартного словаря», имеющего следующий вид:<br />
<br />
<syntaxhighlight lang="python"><br />
{<br />
"array": объект numpy.ndarray,<br />
"shape": кортеж (rows(Y), cols(X)) = numpy.ndarray.shape = (gdal.dataset.RasterYSize(), gdal.dataset.RasterXSize()),<br />
"transform": кортеж gdal.dataset.GetGeoTransform(),<br />
"projection": объект gdal.dataset.GetProjection()<br />
}<br />
</syntaxhighlight> <br />
<br />
Эта конструкция несёт в себе всю необходимую информацию для создания нового георастра, отличного от исходного.<br />
<br />
Углубляться в подробное описание структуры классов и методов raster_tools нет необходимости - достаточно полная базовая документация есть в README.rst на [https://github.com/oldbay/raster_tools https://github.com/oldbay/raster_tools]. В данной статье хотелось бы показать «боевое» применение raster_tools на практике.<br />
<br />
Для этих экспериментов был создан репозиторий с тестовыми данными, загрузить которые можно командой:<br />
<br />
<syntaxhighlight lang="bash"><br />
git clone https://gitlab.com/oldbay/raster_tools_examples.git<br />
</syntaxhighlight><br />
<br />
В ветке по умолчанию (master) расположены только тестовые скрипты и исходные данные. Чтобы заранее изучить результаты тестов, нужно перейти в ветку "result":<br />
<br />
<syntaxhighlight lang="bash"><br />
git checkout origin/result<br />
</syntaxhighlight><br />
<br />
Тестовый репозиторий состоит из:<br />
* Корень: содержит тестовые примеры, выполнение которых необходимо производить из текущего каталога;<br />
* Каталог data: содержит исходные данные для экспериментов;<br />
* Каталог logs: содержит результаты тестов расхода памяти и времени выполнения (branch result);<br />
* Каталог module: содержит дополнительные Python-модули, необходимые для выполнение некоторых тестовых скриптов(в статье не описаны);<br />
* Каталог results: содержит результаты экспериментов (branch result).<br />
<br />
== Растровый калькулятор. ==<br />
<br />
Первоначально raster_tools был частью растрового калькулятора, сейчас отдельный проект raster_calc, поэтому исторически начнём с вычислений.<br />
<br />
Пример простого растрового калькулятора представлен в calc.py (list 1) . В данном скрипте мультирастр разбирается на спектральные каналы, после чего вычисляется вегетативный индекс на основе rgb - TGI ([https://gist.github.com/merkato/0cd894f19518496171afd7425e09ed88 украдено отсюда]), Источником данных является data/multi.tif (img 1).<br />
<br />
<br />
img 1 - data/multi.tif<br />
<br />
[[Файл:Multi.png|500px]]<br />
<br />
<br />
list 1 - calc.py<br />
<syntaxhighlight lang="python"><br />
#!/usr/bin/python2<br />
# -*- coding: utf-8 -*-<br />
<br />
from raster_tools import raster2array, array2raster<br />
import numpy as np<br />
<br />
in_file = "data/multi.tif"<br />
out_file = "result/calc.tif"<br />
<br />
# загрузка каналов<br />
red = raster2array(in_file, 1)<br />
green = raster2array(in_file, 2)<br />
blue = raster2array(in_file, 3)<br />
<br />
# сохранение numpy массивов каналов в переменные.<br />
r = red()<br />
g = green()<br />
b = blue()<br />
<br />
# вычисление TGI<br />
calc = np.choose(<br />
np.not_equal(g-r+b-255.0, 0.0),<br />
(<br />
-9999.0,<br />
np.subtract(<br />
g,<br />
np.multiply(0.39, r),<br />
np.multiply(0.61, b)<br />
)<br />
)<br />
)<br />
<br />
# сохранение вычисленного массива в растр.<br />
array2raster(red, calc, out_file)<br />
</syntaxhighlight><br />
<br />
В результате выполнения calc.py создается растр result/calc.tif (img 2).<br />
<br />
<br />
img 2 - result/calc.tif<br />
<br />
[[Файл:Calc.png|500px]]<br />
<br />
<br />
У метода есть существенный минус - высокое потребление памяти. Фактически в памяти на пике потребления присутствуют сразу 4 массива растров: каналов r, g, b и вычисляемого индекса. При работе с большими георастрами это часто неприемлемо. Поэтому был разработан итерационный метод работы растрового калькулятора. Данный метод работает с растром посекторно в цикле. При таких вычислениях сильно снижается потребление памяти, но падает производительность.<br />
<br />
Итерационный калькулятор реализован в calc_iter.py (list 2). В отличии от простого калькулятора здесь формула вычислений(в формате lambda) и переменные для вычислений(в виде объектов raster2array) передаются классу raster2calc.<br />
<br />
list 2 - calc_iter.py<br />
<syntaxhighlight lang="python"><br />
#!/usr/bin/python2<br />
# -*- coding: utf-8 -*-<br />
<br />
from raster_tools import raster2array, array2raster, raster2calc<br />
import numpy as np<br />
<br />
in_file = "data/multi.tif"<br />
out_file = "result/calc_iter.tif"<br />
<br />
# загрузка каналов<br />
red = raster2array(in_file, 1)<br />
green = raster2array(in_file, 2)<br />
blue = raster2array(in_file, 3)<br />
<br />
# описание формулы вычисления TGI<br />
calc_func = lambda r,g,b: np.choose(<br />
np.not_equal(g-r+b-255.0, 0.0),<br />
(<br />
-9999.0,<br />
np.subtract(<br />
g,<br />
np.multiply(0.39, r),<br />
np.multiply(0.61, b)<br />
)<br />
)<br />
)<br />
<br />
# вычисление TGI<br />
# и сохраниение результата в формате<br />
# стандартного словаря<br />
calc = raster2calc()<br />
out = calc(<br />
calc_func,<br />
r=red,<br />
g=green,<br />
b=blue<br />
)<br />
<br />
# сохранение стандартного словаря в растр.<br />
array2raster(None, out, out_file)<br />
</syntaxhighlight><br />
<br />
raster2calc возвращает «стандартный словарь», преобразуемый классом array2raster в файл result/calc_iter.tif.<br />
<br />
Если выполнить calc.py и calc_iter.py в обёртке memory_profiler - получаем графики потребления памяти:<br />
<br />
[[Файл:Calc_mprof.png|600px]]<br />
[[Файл:Calc_iter_mprof.png|600px]]<br />
<br />
Видно что итерационный метод выигрывает в потреблении памяти, но теряет в скорости расчётов. На текущем примере потери скорости не очень существенны, но при массовой обработке небольших георастров бывает выгоднее пожертвовать памятью.<br />
<br />
== Вырезание области растра. ==<br />
<br />
Иногда растровые вычисления необходимо производить только с некоторой выделенной областью георастра. Для «фигурного» вырезания областей, ограниченных координатами точек или охватом полигонов, у raster2array есть ряд методов:<br />
* cut_area(координаты точек) - метод вырезает область растра по координатам (не менее 2-х точек);<br />
* cut_shp_file(имя shp файла) - метод вырезает растр по полигонам первого слоя shp-файла;<br />
* cut_ogr_geometry(wkt, geojson, gml, wkb) - метод вырезает растр по полигону загруженной геометрии.<br />
<br />
Все перечисленные методы возвращают «стандартный словарь», так как они изменяют размерность массива растра, делая необходимым изменение «transform» у сохраняемого георастра.<br />
<br />
Стандартный словарь применим и в растровом калькуляторе, для подобных вычислений необходимо вернуть numpy массив по ключу «array» list 1.1<br />
<br />
list 1.1<br />
<syntaxhighlight lang="python"><br />
<br />
# загрузка каналов в формате стандартного словаря<br />
# выгрузка аналогична методам обрезки, но для<br />
# всего исходного растра<br />
red = raster2array(in_file, 1).get_std_dict()<br />
green = raster2array(in_file, 2).get_std_dict()<br />
blue = raster2array(in_file, 3).get_std_dict()<br />
<br />
# сохранение numpy-массивов каналов в переменные.<br />
r = red()["array"]<br />
g = green()["array"]<br />
b = blue()["array"]<br />
<br />
# вычисление TGI (как в list 1)<br />
<br />
........<br />
<br />
<br />
# сохранение вычисленного массива в стандартный словарь:<br />
out_std_dict = red<br />
out_std_dict["array"] = calc<br />
<br />
# сохранение стандартного словаря в растр:<br />
array2raster(None, out_std_dict, out_file)<br />
</syntaxhighlight><br />
<br />
Теперь рассмотрим работу каждого метода обрезки растра.<br />
<br />
<br />
=== Метод cut_area ===<br />
<br />
Позволяет обрезать растр по набору координат точек. Применяется когда необходимо обрезать область растра по массиву точек - для дальнейшей обработки. Пример использования приведён в cut_coords.py (list 3), в качестве исходных данных используется result/calc.tif.<br />
<br />
list 3 - cut_coords.py<br />
<syntaxhighlight lang="python"><br />
#!/usr/bin/python2<br />
# -*- coding: utf-8 -*-<br />
<br />
from raster_tools import raster2array, array2raster<br />
<br />
in_file = "result/calc.tif"<br />
out_file = "result/cut_coords.tif"<br />
<br />
# вырезание области растра по координатам<br />
# и сохранение в формате стандартного словаря<br />
std_dict = raster2array(in_file).cut_area(<br />
(296153.369,7137678.937),<br />
(296203.959,7137570.986),<br />
(296256.938,7137645.476)<br />
)<br />
<br />
# сохранение стандартного словаря в растр<br />
array2raster(None, std_dict, out_file)<br />
</syntaxhighlight><br />
<br />
Результат выполнения метода сохраняется в out: result/cut_coords.tif (img 3.1 ,img 3.2)<br />
<br />
img 3.1 - отображение области на исходном растре<br />
<br />
[[Файл:Cut_coords_screenshot.png|500px]]<br />
<br />
img 3.2 - result/cut_coords.tif<br />
<br />
[[Файл:Cut_coords.png|200px]]<br />
<br />
<br />
=== Метод cut_shp_file. ===<br />
<br />
Часто необходимо обрезать растр по геометрии слоя shp файла. Пример выполнения такой задачи приведён в cut_shp.py (list 4). Помимо исходного георастра result/calc.tif , скрипт использует шаблона обрезки - data/cut.shp.<br />
<br />
list 4 - cut_shp.py<br />
<syntaxhighlight lang="python"><br />
#!/usr/bin/python2<br />
# -*- coding: utf-8 -*-<br />
<br />
from raster_tools import raster2array, array2raster<br />
<br />
shp_file = "data/cut.shp"<br />
in_file = "result/calc.tif"<br />
out_file = "result/cut_shp.tif"<br />
<br />
# вырезание области растра по полигону shp-файла<br />
# и сохранение в формате стандартного словаря<br />
std_dict = raster2array(in_file).cut_shp_file(shp_file)<br />
<br />
# сохранение стандартного словаря в растр<br />
array2raster(None, std_dict, out_file)<br />
</syntaxhighlight><br />
<br />
Результат сохраняется в георастр result/cut_shp.tif (img 4.1, img 4.2).<br />
<br />
<br />
img 4.1 - геометрия из shp-файла на фоне исходного растра<br />
<br />
[[Файл:Cut_shp_screenshot.png|300px]]<br />
<br />
<br />
img 4.2 - result/cut_shp.tif<br />
<br />
[[Файл:Cut_shp.png|200px]]<br />
<br />
<br />
Обратите внимание, что растр обрезан непосредственно по полигону, а не только по координатам охвата объекта.<br />
<br />
=== Метод cut_ogr_geometry ===<br />
<br />
Применяется, если необходима обрезка георастра по геометрия в формате wkt, geojson, gml или wkb. Наиболее типичный способ использования - это вырезание растра на по wkt-геометрии, возвращённой из PostGIS.<br />
<br />
Для выполнения примера следует провести подготовку(все примеры работы с БД приведены для Linux): <br />
* Установить PostgreSQL c расширением PostGIS. <br />
* В СУБД создать роль, из-под которой возможен логин в БД для загрузки дампа:<br />
<pre><br />
# su - postgres<br />
$ psql<br />
create role gis with login password 'gis';<br />
create database doc_example owner gis;<br />
ctrl-d<br />
</pre><br />
* Имя пользователя, пароль и имя базы можно использовать произвольные, но тогда необходимо будет изменить в list 5 переменные: dbname, dbuser, dbpass.<br />
* Подключить расширение PostGIS в БД.<br />
<pre><br />
$ psql -d doc_example<br />
create extension postgis<br />
ctrl-d<br />
</pre><br />
* Развернуть дамп из data/crowns.sql<br />
<pre><br />
psql -d doc_example < data/crowns.sql<br />
</pre><br />
* Проверить наличие Python-модуля "psycopg2" - драйвера PostgreSQL (необходим для интерфейса запросов modules.db_interface)<br />
<br />
После успешного завершения подготовительного этапа можно выполнять тестовый пример cut_wkt.py (list 5)<br />
<br />
list 5 - cut_wkt.py<br />
<syntaxhighlight lang="python"><br />
#! /usr/bin/python2<br />
# -*- coding: utf-8 -*-<br />
<br />
from raster_tools import raster2array, array2raster<br />
from modules import psql<br />
<br />
# загрузка растра в объект raster2array<br />
in_file = "result/calc.tif"<br />
out_file = "result/cut_wkt.tif"<br />
<br />
# установка параметров подключения в БД<br />
dbhost = "localhost"<br />
dbname = "dok_example"<br />
dbuser = "gis"<br />
dbpass = "gis"<br />
<br />
# параметры поиска полигона в БД<br />
geom_table = "crowns"<br />
geom_id = 55775<br />
<br />
# запрос в БД, возвращающий полигон в формате WKT<br />
SQL = """<br />
select ST_AsText(wkb_geometry)<br />
from {0}<br />
where ogc_fid = {1}<br />
""".format(<br />
geom_table,<br />
geom_id<br />
)<br />
_psql = psql(<br />
dbhost=dbhost,<br />
dbname=dbname,<br />
dbuser=dbuser,<br />
dbpass=dbpass<br />
)<br />
_psql.sql(SQL)<br />
wkt_geom = _psql.fetchone()[0]<br />
_psql.close()<br />
<br />
# вырезание области растра по полигону WKT<br />
# и сохранение в формате стандартного словаря<br />
std_dict = raster2array(in_file).cut_ogr_geometry(wkt_geom)<br />
<br />
# сохранеие стандартного словаря в растр<br />
array2raster(None, std_dict, out_file)<br />
</syntaxhighlight><br />
<br />
В результате выполнения примера должен быть создан result/cut_wkt.tif (img 5.1, img 5.2).<br />
<br />
<br />
img 5.1 - отображение геометрии из postgis на фоне исходного растра<br />
<br />
[[Файл:Cut_wkt_screenshot.png|400px]]<br />
<br />
<br />
img 5.2 - result/cut_wkt.tif<br />
<br />
[[Файл:Cut_wkt.png|200px]]<br />
<br />
== Трансформация ==<br />
<br />
Иногда приходится вычислять индексы, или иным способом сравнивать георастры из различных источников. Входные данные могут быть в разном масштабе и соответственно иметь разную размерность массивов исследуемой области. В результате вычисление индексов средствами numpy становится невозможным, а многие другие операции затруднительны. <br />
<br />
Решить подобною проблему призван класс raster2transform. При помощи него можно изменить размерность всего массива, или только его части.<br />
Для работы тестового примера resize.py (list 6) требуется установка matplotlib и графической среды пользователя, исходным георастром является result/cut_shp.tif.<br />
<br />
list 6 - resize.py<br />
<syntaxhighlight lang="python"><br />
#! /usr/bin/python2<br />
# -*- coding: utf-8 -*-<br />
<br />
from raster_tools import raster2array, raster2transform<br />
from matplotlib import pyplot as plt<br />
<br />
in_raster = "result/cut_shp.tif"<br />
out_raster = "result/resize.tif"<br />
<br />
# вывод массива в matplotlib<br />
def plt_show(obj):<br />
plt.title("show")<br />
plt.imshow(obj.array(), cmap='gray')<br />
plt.show()<br />
<br />
# загрузка растра в raster2array<br />
raster = raster2array(in_raster)<br />
print raster.rows, raster.cols<br />
<br />
# кроме того возможна работа с:<br />
#raster = in_raster #именем растрового файла<br />
#raster = raster2array(in_raster).get_std_dict() #стандартным словарём<br />
<br />
# изменение размерности массива (row, col) в цикле<br />
for row, col in [(253, 210), (125, 105), (300, 300)]:<br />
raster = raster2transform(raster, row, col)<br />
print raster.rows, raster.cols<br />
plt_show(raster)<br />
<br />
# сохранение растра c последней размерностью массива<br />
raster.save(out_raster)<br />
</syntaxhighlight><br />
<br />
В результате работы скрипта 3 раза отобразится окно matplotlib.pyplot с изображением массива растра в следующих размерностях: (253,210)-img 6.1, (125,105)-img 6.2, (300,300)-img 6.3. Последний массив сохранится в растр result/resize.tif (img 6.3).<br />
<br />
<br />
img 6.1 - разрешение (253x210)<br />
<br />
[[Файл:Resize_253_210.png|500px]]<br />
<br />
<br />
img 6.1 - разрешение (125x105)<br />
<br />
[[Файл:Resize_125_105.png|500px]]<br />
<br />
<br />
img 6.1 - разрешение (300x300)<br />
<br />
[[Файл:Resize_300_300.png|500px]]<br />
<br />
== Мультирастры ==<br />
<br />
Для вычисления индексов удобно работать с одноканальными растрами, представляя каждый канал как отдельно взятую сущность. Но иногда требуется создавать и работать с многоканальными конструкциями. Для это создана пара мультиканальных классов: <br />
<br />
* raster2multiarray - Класс, умеющий изменять очерёдность и количество каналов, при этом преобразующий многоканальный растр в стандартный словарь расширенного типа. От обычного он отличается тем, что в поле «array» сохраняется не двухмерный (x, y), а трёхмерный numpy-массив с индексом (bands, x, y). Для определения типа массива добавлено поле «multi_type», принимающее значения: None - с массивом уже указанного индекса и «cv» - с индексом (x, y, bands). Массив типа multi_type = «cv» аналогичен возвращаемому функцией cv2.imread. Кроме того, у raster2multiarray имеется набор методов обрезки растра, аналогичных raster2array.<br />
<br />
* multiarray2multiraster - Класс, сохраняющий многоканальный стандартный словарь в мультиканальный растр.<br />
<br />
Предлагаю два варианта использования данной пары классов, исходным растром для примеров послужит data/multi.tif:<br />
<br />
<br />
=== Изменение последовательности каналов ===<br />
<br />
Для данного примера в RGB-мультирастре переставим местами 1-й и 3-й каналы. Тестовый скрипт представлен в multi_rebands.py (list 7). <br />
<br />
list 7 - multi_rebands.py<br />
<syntaxhighlight lang="python"><br />
#!/usr/bin/python2<br />
# -*- coding: utf-8 -*-<br />
<br />
from raster_tools import raster2multiarray, multiarray2multiraster<br />
<br />
img_in = 'data/multi.tif'<br />
img_out = 'result/multi_rebands.tif'<br />
<br />
# загрузка многоканального в объект raster2multiarray<br />
# и изменение последовательности каналов 1-3, 2-2, 3-1<br />
img = raster2multiarray(img_in, 3, 2, 1)<br />
<br />
# выгрузка многоканального стандартного словаря<br />
img = img.get_std_dict()<br />
<br />
# cохранение многоканального стандартного словаря<br />
# в мультирастр<br />
multiarray2multiraster(img_out, img)<br />
</syntaxhighlight><br />
<br />
Результатом выполнения будет создание result/multi_rebands.tif (img 7).<br />
<br />
<br />
img 7 - result/multi_rebands.tif<br />
<br />
[[Файл:Multi_rebands.png|500px]]<br />
<br />
<br />
=== Обработка муьтирастров в opencv ===<br />
<br />
Теперь усложним задачу: удалим альфа-канал мультирастра, передадим массив на обработку OpenCV и сохраним полученный результат. Для этого примера необходимо установить Python-модули OpenCV версии 2 или 3. Тестовый скрипт представлен в multi_opencv.py (list 8).<br />
<br />
list 8 - multi_opencv.py<br />
<syntaxhighlight lang="python"><br />
#!/usr/bin/python2<br />
# -*- coding: utf-8 -*-<br />
<br />
import numpy as np<br />
<br />
from raster_tools import raster2multiarray, multiarray2multiraster<br />
import cv2<br />
<br />
img_in = 'data/multi.tif'<br />
img_out = 'result/multi_opencv.tif'<br />
<br />
# загрузка многоканального в объект raster2multiarray<br />
# для обработки через cv2.bilateralFilter необходима загрузка только 1,2,3 канала<br />
img = raster2multiarray(img_in, 1, 2, 3)<br />
<br />
# установка типа возвращаемого многоканального массива<br />
# в формат opencv<br />
img.multi_type = "cv"<br />
<br />
# установка типа данных возвращаемого массива как uint8<br />
# необходимо для обработки данных в opencv<br />
img.codage = np.uint8<br />
<br />
# выгрузка многоканального стандартого словаря<br />
std_dict = img.get_std_dict()<br />
<br />
# работа в OpenCV c многоканальным массивом<br />
std_dict["array"] = cv2.bilateralFilter(std_dict["array"],9,75,75)<br />
<br />
# сохранение многоканального стандартного словаря<br />
# в мультирастр<br />
multiarray2multiraster(img_out, std_dict)<br />
</syntaxhighlight><br />
<br />
Результат будет сохранён в result/multi_opencv.tif (img 8).<br />
<br />
<br />
img 8 - result/multi_opencv.tif<br />
<br />
[[Файл:Multi_opencv.png|500px]]<br />
<br />
<br />
==Работа с точками.==<br />
<br />
Неочевидный, но временами очень важный метод препарирования георастров. Постановка задачи: запись значений точек растра с определёнными координатами в CSV, БД или в виде поля в слой shp-файла. Реализация последнего примера представлена в point_data_load_array.py (list 9). Исходными данными здесь выступают: георастр result/calc.tif и векторный файл со слоем точек data/tops.shp.<br />
<br />
list 9 - point_data_load_array.py<br />
<syntaxhighlight lang="python"><br />
#!/usr/bin/python2<br />
# -*- coding: utf-8 -*-<br />
<br />
from raster_tools import raster2array<br />
import ogr<br />
import shutil<br />
<br />
raster = "result/calc.tif"<br />
<br />
# копируем тестовый shp-файл с точками в каталог result<br />
_filename = "tops"<br />
_indir = "data"<br />
_outdir = "result"<br />
for _ex in ["dbf", "prj", "qpj", "shp", "shx"]:<br />
shutil.copyfile(<br />
"{0}/{1}.{2}".format(_indir, _filename, _ex),<br />
"{0}/{1}.{2}".format(_outdir, _filename, _ex),<br />
)<br />
<br />
shp_file = "{0}/{1}.shp".format(_outdir, _filename)<br />
<br />
# загрузка растра в объект raster2array<br />
raster = raster2array(raster)<br />
<br />
# сохранение numpy массива в объекте raster2array<br />
raster.np_array_load()<br />
<br />
# загрузка shp-файла<br />
_shp = ogr.Open(shp_file, update=1)<br />
layer = _shp.GetLayerByIndex(0)<br />
<br />
# добавление в shp-файл нового поля<br />
# для записи выгруженных значений растра<br />
new_field = "POINT_DATA"<br />
if layer.FindFieldIndex(new_field, 1) < 1:<br />
field = ogr.FieldDefn(new_field, ogr.OFTReal)<br />
layer.CreateField(field)<br />
<br />
# обход циклом точек shp-файла<br />
for geometry in layer:<br />
<br />
# определение координат точки векторного файла<br />
x, _, y, _ = geometry.GetGeometryRef().GetEnvelope()<br />
<br />
# возврат значения данных точки растра<br />
# по координатам точки векторного файла<br />
raster_data = raster.get_pixel_value(float(x), float(y))<br />
<br />
# сохранение выгруженных из растра данных<br />
# в новое поле shp-файла<br />
geometry.SetField(new_field, raster_data)<br />
layer.SetFeature(geometry)<br />
</syntaxhighlight><br />
<br />
В ходе выполнения скрипта исходный векторный файл data/tops.shp копируется в result/tops.shp, в который добавляется поле «POINT_DATA». В добавленном поле сохраняются значения точек растра по координатам геометрий векторного слоя. При помощи полученного векторного файла можно будет производить интерполяцию значений поля «POINT_DATA»(пример дальше по тексту).<br />
<br />
В list 9 есть одна «хитрость»:<br />
<br />
<syntaxhighlight lang="python"><br />
# сохранение numpy массива в объекте raster2array<br />
raster.np_array_load()<br />
</syntaxhighlight><br />
<br />
Такая конструкция загружает numpy-массив растра непосредственно в экземпляр класса raster2array, что уменьшает время выполнения операций, увеличивая потребление памяти. Для сравнения: в репозитории есть аналогичный скрипт point_data_dont_load_array.py, где данная строка отсутствует. Сравнение графиков выполнения обоих скриптов в memory_profiler выглядит следующим образом: <br />
<br />
[[Файл:Point_data_load_array_mprof.png|600px]]<br />
[[Файл:Point_data_dont_load_array_mprof.png|600px]]<br />
<br />
<br />
== "Ремонт" георастров ==<br />
<br />
Иногда процедурная генерация георастров некоторыми утилитами порождает «мутантов». При их отображении в ПО ГИС проблема не заметна - так как геоданные будут правильно позиционированы. Но при выгрузке из такого георастра numpy-массива оказывается, что он зеркально отображён относительно оси X или Y. Примером утилиты с такой странной процедурной генерации является gdal_grid ([https://gis-lab.info/forum/viewtopic.php?f=30&t=20283 сообщение о проблеме на форуме]).<br />
<br />
Чтобы не быть голословными, выполним весь цикл процедур: интерполяция данных из предыдущего примера result/tops.shp при помощи gdal_grid, проверка полученного процедурного растра на «зеркальность», исправление его при помощи raster_tools. В качестве исходных данных будут использованы: result/tops.shp (источник данных для интерполяции) и result/calc.tif (для сравнения). Обработку произведём скриптом repair.py (list 11). Для работы repair.py необходим модуль modules.grid_utils и сама утилита gdal_grid. <br />
<br />
list 11 - repair.py<br />
<syntaxhighlight lang="python"><br />
#!/usr/bin/python2<br />
# -*- coding: utf-8 -*-<br />
<br />
from raster_tools import raster2array, array2raster<br />
from modules import grid_utils<br />
<br />
out_dir = "result"<br />
shp_file = "result/tops.shp"<br />
field_name = "POINT_DATA"<br />
raster_file = "result/calc.tif"<br />
grid_file = "result/{}.tif".format(field_name)<br />
repair_file = "result/repair.tif"<br />
<br />
# Интерполяция на основе shp-файла<br />
grid_utils(raster_file, shp_file, field_name, out_dir)<br />
<br />
# загрузка результатов интерполяции в raster2array<br />
grid = raster2array(grid_file)<br />
<br />
# проверка "валидности" массива в растре<br />
print grid.is_valid()<br />
<br />
# возврат "отремонтированного" растра в формате стандартного словаря<br />
grid = grid.repair()<br />
<br />
# запись исправленного растра<br />
array2raster(None, grid, repair_file)<br />
</syntaxhighlight><br />
<br />
В результате работы скрипта были созданы 2 растра: result/POINT_DATA.tif - результат работы gdal_grid и исправленный растр result/repair.tif. Сравним вывод информации о растрах через gdalinfo:<br />
<br />
<pre><br />
$ gdalinfo result/POINT_DATA.tif<br />
...<br />
Pixel Size = (0.640086707766337,0.633172658674583)<br />
Metadata:<br />
AREA_OR_POINT=Area<br />
Image Structure Metadata:<br />
INTERLEAVE=BAND<br />
Corner Coordinates:<br />
Upper Left ( 296008.694, 7137566.459) ( 40d46'52.75"E, 64d18'11.32"N)<br />
Lower Left ( 296008.694, 7137767.175) ( 40d46'51.76"E, 64d18'17.78"N)<br />
Upper Right ( 296387.625, 7137566.459) ( 40d47'20.87"E, 64d18'12.13"N)<br />
Lower Right ( 296387.625, 7137767.175) ( 40d47'19.88"E, 64d18'18.60"N)<br />
Center ( 296198.159, 7137666.817) ( 40d47' 6.31"E, 64d18'14.96"N)<br />
Band 1 Block=592x1 Type=Float64, ColorInterp=Gray<br />
</pre><br />
<br />
<pre><br />
$ gdalinfo result/repair.tif<br />
...<br />
Pixel Size = (0.640086707766337,-0.633172658674583)<br />
Metadata:<br />
AREA_OR_POINT=Area<br />
Image Structure Metadata:<br />
COMPRESSION=LZW<br />
INTERLEAVE=BAND<br />
Corner Coordinates:<br />
Upper Left ( 296008.694, 7137767.175) ( 40d46'51.76"E, 64d18'17.78"N)<br />
Lower Left ( 296008.694, 7137566.459) ( 40d46'52.75"E, 64d18'11.32"N)<br />
Upper Right ( 296387.625, 7137767.175) ( 40d47'19.88"E, 64d18'18.60"N)<br />
Lower Right ( 296387.625, 7137566.459) ( 40d47'20.87"E, 64d18'12.13"N)<br />
Center ( 296198.159, 7137666.817) ( 40d47' 6.31"E, 64d18'14.96"N)<br />
Band 1 Block=256x256 Type=Float64, ColorInterp=Gray<br />
</pre><br />
<br />
и в качестве контрольной пробы:<br />
<br />
<pre><br />
$ gdalinfo result/calc.tif<br />
...<br />
Pixel Size = (0.064274599999999,-0.064274599999986)<br />
Metadata:<br />
AREA_OR_POINT=Area<br />
Image Structure Metadata:<br />
COMPRESSION=LZW<br />
INTERLEAVE=BAND<br />
Corner Coordinates:<br />
Upper Left ( 296007.704, 7137768.705) ( 40d46'51.68"E, 64d18'17.83"N)<br />
Lower Left ( 296007.704, 7137564.891) ( 40d46'52.68"E, 64d18'11.27"N)<br />
Upper Right ( 296388.595, 7137768.705) ( 40d47'19.94"E, 64d18'18.65"N)<br />
Lower Right ( 296388.595, 7137564.891) ( 40d47'20.95"E, 64d18'12.08"N)<br />
Center ( 296198.149, 7137666.798) ( 40d47' 6.31"E, 64d18'14.96"N)<br />
Band 1 Block=256x256 Type=Float64, ColorInterp=Gray<br />
</pre><br />
<br />
Обратите внимание на второе значение Pixel Size(ось y): у растра, сгенерированного gdal_grid, это значение положительное в отличие от исходного и исправленного георастров. Что и позволяет отображать POINT_DATA.tif в ПО ГИС нормально, несмотря на наличие зеркально отображённого через ось Y массива. То же самое сообщает нам вывод метода raster2array.is_valid():<br />
<br />
<syntaxhighlight lang="python"><br />
# загрузка результатов интерполяции в raster2array<br />
grid = raster2array(grid_file)<br />
# проверка "валидности" массива в растре<br />
print grid.is_valid()<br />
</syntaxhighlight><br />
<br />
Вывод строки:<br />
<br />
<syntaxhighlight lang="python"><br />
[True, False]<br />
</syntaxhighlight><br />
<br />
Здесь ориентация растра по оси X истинное и ложное по оси Y.<br />
<br />
<br />
== Послесловие ==<br />
<br />
Заканчивая описание способов употребления raster_tools, хочется отметить, что инструмент будет и дальше находиться в состоянии постоянного развития. По мере поступления задач в него будут добавляться какие-то новые функции, при этом с сохранением старых методов применения (чтобы не потерять совместимость с уже завершёнными проектами). Статья описывает состояние raster_tools версии 0.4 - на текущий момент последняя стабильная версия.<br />
<br />
У raster_tools имеются следующие недостатки: <br />
* проблемное перепроецирование векторных слоёв-шаблонов в проекцию растра, поэтому логично использовать единую проекцию;<br />
* обрезка по shp-файлам «прибита гвоздями» к первому векторному слою;<br />
* инструмент рассчитан на формат GeoTIFF; работа с другими растровыми форматами файлов в зачаточном состоянии;<br />
* недостаточно оптимизирована работа с мультирастрами (нечасто пока требуются);</div>Александр Мурыйhttps://wiki.gis-lab.info/index.php?title=%D0%9F%D1%80%D0%B0%D0%BA%D1%82%D0%B8%D0%BA%D0%B0_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F_raster_tools&diff=26379Практика использования raster tools2018-07-30T20:34:20Z<p>Александр Мурый: /* Знакомство с raster_tools */</p>
<hr />
<div>{{Статья|Черновик}}<br />
{{Аннотация|В статье рассматриваются примеры практического использования набора растровых инструментов raster_tools.}}<br />
<br />
<br />
== Знакомство с raster_tools ==<br />
<br />
В течении продолжительного времени автор статьи развивает инструментарий для препарирования георастров. Он является набором рецептов, упрощающих взаимодействие с гибкой, но сложной структурой Python + GDAL. К сегодняшнему времени '''raster_tools''' напоминает некий «швейцарский нож» с разнообразным функционалом. <br />
Код проекта расположен на [https://github.com/oldbay/raster_tools https://github.com/oldbay/raster_tools] , самый простой способ его установки в каталоге пользователя - при помощи pip:<br />
<br />
<syntaxhighlight lang="bash"><br />
pip2.7 install --user git+https://github.com/oldbay/raster_tools<br />
</syntaxhighlight><br />
<br />
'''raster_tools''' состоит из следующих классов:<br />
<br />
'''raster2array''' - Базовый класс, отвечающий за весь функционал загрузки растра и сохранение numpy-массива выбранного канала(band). Выгруженный из растра массив может быть преобразован методами данного класса. <br />
<br />
'''array2raster(raster2array)''' - Субкласс, отвечающий за сохранение обработанного массива в файле георастра. Он также может сформировать виртуальный растр в памяти с возможностью обработки методами базового класса.<br />
<br />
'''raster2transform(raster2array)''' - Субкласс, отвечающий за трансформацию растра: изменение размерности массива и перепроецирование. Также есть поддержка методов базового класса.<br />
<br />
'''raster2calc(raster2array)''' - Класс итерационного калькулятора. Он не наследует базовый класс raster2array, но использует некоторые его методы опосредованно.<br />
<br />
'''raster2multiarray(raster2array)''' - Класс обработки мультирастров. Позволяет миксовать каналы мультиканального растра и обрабатывать его некоторыми методами raster2array.<br />
<br />
'''multiarray2multiraster''' - Класс сохранения массивов, обработанных методами raster2multiarray, в мультиканальный растр.<br />
<br />
Основной смысл существования всего этого «зоопарка» классов - это загрузка, обработка и выгрузка двухмерного numpy-массива георастров. Помимо объекта 2-х мерного numpy-массива некоторые методы raster2array позволяют выгружать данные в формате самоназванного «стандартного словаря», имеющего следующий вид:<br />
<br />
<syntaxhighlight lang="python"><br />
{<br />
"array": объект numpy.ndarray,<br />
"shape": кортеж (rows(Y), cols(X)) = numpy.ndarray.shape = (gdal.dataset.RasterYSize(), gdal.dataset.RasterXSize()),<br />
"transform": кортеж gdal.dataset.GetGeoTransform(),<br />
"projection": объект gdal.dataset.GetProjection()<br />
}<br />
</syntaxhighlight> <br />
<br />
Эта конструкция несёт в себе всю необходимую информацию для создания нового георастра, отличного от исходного.<br />
<br />
Углубляться в подробное описание структуры классов и методов raster_tools нет необходимости - достаточно полная базовая документация есть в README.rst на [https://github.com/oldbay/raster_tools https://github.com/oldbay/raster_tools]. В данной статье хотелось бы показать «боевое» применение raster_tools на практике.<br />
<br />
Для этих экспериментов был создан репозиторий с тестовыми данными, загрузить которые можно командой:<br />
<br />
<syntaxhighlight lang="bash"><br />
git clone https://gitlab.com/oldbay/raster_tools_examples.git<br />
</syntaxhighlight><br />
<br />
В ветке по умолчанию (master) расположены только тестовые скрипты и исходные данные. Чтобы заранее изучить результаты тестов, нужно перейти в ветку "result":<br />
<br />
<syntaxhighlight lang="bash"><br />
git checkout origin/result<br />
</syntaxhighlight><br />
<br />
Тестовый репозиторий состоит из:<br />
* Корень: содержит тестовые примеры, выполнение которых необходимо производить из текущего каталога;<br />
* Каталог data: содержит исходные данные для экспериментов;<br />
* Каталог logs: содержит результаты тестов расхода памяти и времени выполнения (branch result);<br />
* Каталог module: содержит дополнительные Python-модули, необходимые для выполнение некоторых тестовых скриптов(в статье не описаны);<br />
* Каталог results: содержит результаты экспериментов (branch result).<br />
<br />
== Растровый калькулятор. ==<br />
<br />
Первоначально raster_tools был частью растрового калькулятора, сейчас отдельный проект raster_calc, поэтому исторически начнём с вычислений.<br />
<br />
Пример простого растрового калькулятора представлен в calc.py (list 1) . В данном скрипте мультирастр разбирается на спектральные каналы, после чего вычисляется вегетативный индекс на основе rgb - TGI ([https://gist.github.com/merkato/0cd894f19518496171afd7425e09ed88 украдено отсюда]), Источником данных является data/multi.tif (img 1).<br />
<br />
<br />
img 1 - data/multi.tif<br />
<br />
[[Файл:Multi.png|500px]]<br />
<br />
<br />
list 1 - calc.py<br />
<syntaxhighlight lang="python"><br />
#!/usr/bin/python2<br />
# -*- coding: utf-8 -*-<br />
<br />
from raster_tools import raster2array, array2raster<br />
import numpy as np<br />
<br />
in_file = "data/multi.tif"<br />
out_file = "result/calc.tif"<br />
<br />
# загрузка каналов<br />
red = raster2array(in_file, 1)<br />
green = raster2array(in_file, 2)<br />
blue = raster2array(in_file, 3)<br />
<br />
# сохранение numpy массивов каналов в переменные.<br />
r = red()<br />
g = green()<br />
b = blue()<br />
<br />
# вычисление TGI<br />
calc = np.choose(<br />
np.not_equal(g-r+b-255.0, 0.0),<br />
(<br />
-9999.0,<br />
np.subtract(<br />
g,<br />
np.multiply(0.39, r),<br />
np.multiply(0.61, b)<br />
)<br />
)<br />
)<br />
<br />
# сохранение вычисленного массива в растр.<br />
array2raster(red, calc, out_file)<br />
</syntaxhighlight><br />
<br />
В результате выполнения calc.py создается растр result/calc.tif (img 2).<br />
<br />
<br />
img 2 - result/calc.tif<br />
<br />
[[Файл:Calc.png|500px]]<br />
<br />
<br />
У метода есть существенный минус - высокое потребление памяти. Фактически в памяти на пике потребления присутствуют сразу 4 массива растров: каналов r, g, b и вычисляемого индекса. При работе с большими георастрами это часто неприемлемо. Поэтому был разработан итерационный метод работы растрового калькулятора. Данный метод работает с растром посекторно в цикле. При таких вычислениях сильно снижается потребление памяти, но падает производительность.<br />
<br />
Итерационный калькулятор реализован в calc_iter.py (list 2). В отличии от простого калькулятора здесь формула вычислений(в формате lambda) и переменные для вычислений(в виде объектов raster2array) передаются классу raster2calc.<br />
<br />
list 2 - calc_iter.py<br />
<syntaxhighlight lang="python"><br />
#!/usr/bin/python2<br />
# -*- coding: utf-8 -*-<br />
<br />
from raster_tools import raster2array, array2raster, raster2calc<br />
import numpy as np<br />
<br />
in_file = "data/multi.tif"<br />
out_file = "result/calc_iter.tif"<br />
<br />
# загрузка каналов<br />
red = raster2array(in_file, 1)<br />
green = raster2array(in_file, 2)<br />
blue = raster2array(in_file, 3)<br />
<br />
# описание формулы вычисления TGI<br />
calc_func = lambda r,g,b: np.choose(<br />
np.not_equal(g-r+b-255.0, 0.0),<br />
(<br />
-9999.0,<br />
np.subtract(<br />
g,<br />
np.multiply(0.39, r),<br />
np.multiply(0.61, b)<br />
)<br />
)<br />
)<br />
<br />
# вычисление TGI<br />
# и сохраниение результата в формате<br />
# стандартного словаря<br />
calc = raster2calc()<br />
out = calc(<br />
calc_func,<br />
r=red,<br />
g=green,<br />
b=blue<br />
)<br />
<br />
# сохранение стандартного словаря в растр.<br />
array2raster(None, out, out_file)<br />
</syntaxhighlight><br />
<br />
raster2calc возвращает «стандартный словарь», преобразуемый классом array2raster в файл result/calc_iter.tif.<br />
<br />
Если выполнить calc.py и calc_iter.py в обёртке memory_profiler - получаем графики потребления памяти:<br />
<br />
[[Файл:Calc_mprof.png|600px]]<br />
[[Файл:Calc_iter_mprof.png|600px]]<br />
<br />
Видно что итерационный метод выигрывает в потреблении памяти, но теряет в скорости расчётов. На текущем примере потери скорости не очень существенны, но при массовой обработке небольших георастров бывает выгоднее пожертвовать памятью.<br />
<br />
== Вырезание области растра. ==<br />
<br />
Иногда растровые вычисления необходимо производить только с некоторой выделенной областью георастра. Для «фигурного» вырезания областей, ограниченных координатами точек или охватом полигонов, у raster2array есть ряд методов:<br />
* cut_area(координаты точек) - метод вырезает область растра по координатам (не менее 2-х точек);<br />
* cut_shp_file(имя shp файла) - метод вырезает растр по полигонам первого слоя shp-файла;<br />
* cut_ogr_geometry(wkt, geojson, gml, wkb) - метод вырезает растр по полигону загруженной геометрии.<br />
<br />
Все перечисленные методы возвращают «стандартный словарь», так как они изменяют размерность массива растра, делая необходимым изменение «transform» у сохраняемого георастра.<br />
<br />
Стандартный словарь применим и в растровом калькуляторе, для подобных вычислений необходимо вернуть numpy массив по ключу «array» list 1.1<br />
<br />
list 1.1<br />
<syntaxhighlight lang="python"><br />
<br />
# загрузка каналов в формате стандартного словаря<br />
# выгрузка аналогична методам обрезки, но для<br />
# всего исходного растра<br />
red = raster2array(in_file, 1).get_std_dict()<br />
green = raster2array(in_file, 2).get_std_dict()<br />
blue = raster2array(in_file, 3).get_std_dict()<br />
<br />
# сохранение numpy-массивов каналов в переменные.<br />
r = red()["array"]<br />
g = green()["array"]<br />
b = blue()["array"]<br />
<br />
# вычисление TGI (как в list 1)<br />
<br />
........<br />
<br />
<br />
# сохранение вычисленного массива в стандартный словарь:<br />
out_std_dict = red<br />
out_std_dict["array"] = calc<br />
<br />
# сохранение стандартного словаря в растр:<br />
array2raster(None, out_std_dict, out_file)<br />
</syntaxhighlight><br />
<br />
Теперь рассмотрим работу каждого метода обрезки растра.<br />
<br />
<br />
=== Метод cut_area ===<br />
<br />
Позволяет обрезать растр по набору координат точек. Применяется когда необходимо обрезать область растра по массиву точек - для дальнейшей обработки. Пример использования приведён в cut_coords.py (list 3), в качестве исходных данных используется result/calc.tif.<br />
<br />
list 3 - cut_coords.py<br />
<syntaxhighlight lang="python"><br />
#!/usr/bin/python2<br />
# -*- coding: utf-8 -*-<br />
<br />
from raster_tools import raster2array, array2raster<br />
<br />
in_file = "result/calc.tif"<br />
out_file = "result/cut_coords.tif"<br />
<br />
# вырезание области растра по координатам<br />
# и сохранение в формате стандартного словаря<br />
std_dict = raster2array(in_file).cut_area(<br />
(296153.369,7137678.937),<br />
(296203.959,7137570.986),<br />
(296256.938,7137645.476)<br />
)<br />
<br />
# сохранение стандартного словаря в растр<br />
array2raster(None, std_dict, out_file)<br />
</syntaxhighlight><br />
<br />
Результат выполнения метода сохраняется в out: result/cut_coords.tif (img 3.1 ,img 3.2)<br />
<br />
img 3.1 - отображение области на исходном растре<br />
<br />
[[Файл:Cut_coords_screenshot.png|500px]]<br />
<br />
img 3.2 - result/cut_coords.tif<br />
<br />
[[Файл:Cut_coords.png|200px]]<br />
<br />
<br />
=== Метод cut_shp_file. ===<br />
<br />
Часто необходимо обрезать растр по геометрии слоя shp файла. Пример выполнения такой задачи приведён в cut_shp.py (list 4). Помимо исходного георастра result/calc.tif , скрипт использует шаблона обрезки - data/cut.shp.<br />
<br />
list 4 - cut_shp.py<br />
<syntaxhighlight lang="python"><br />
#!/usr/bin/python2<br />
# -*- coding: utf-8 -*-<br />
<br />
from raster_tools import raster2array, array2raster<br />
<br />
shp_file = "data/cut.shp"<br />
in_file = "result/calc.tif"<br />
out_file = "result/cut_shp.tif"<br />
<br />
# вырезание области растра по полигону shp-файла<br />
# и сохранение в формате стандартного словаря<br />
std_dict = raster2array(in_file).cut_shp_file(shp_file)<br />
<br />
# сохранение стандартного словаря в растр<br />
array2raster(None, std_dict, out_file)<br />
</syntaxhighlight><br />
<br />
Результат сохраняется в георастр result/cut_shp.tif (img 4.1, img 4.2).<br />
<br />
<br />
img 4.1 - геометрия из shp-файла на фоне исходного растра<br />
<br />
[[Файл:Cut_shp_screenshot.png|300px]]<br />
<br />
<br />
img 4.2 - result/cut_shp.tif<br />
<br />
[[Файл:Cut_shp.png|200px]]<br />
<br />
<br />
Обратите внимание, что растр обрезан непосредственно по полигону, а не только по координатам охвата объекта.<br />
<br />
=== Метод cut_ogr_geometry ===<br />
<br />
Применяется, если необходима обрезка георастра по геометрия в формате wkt, geojson, gml или wkb. Наиболее типичный способ использования - это вырезание растра на по wkt-геометрии, возвращённой из PostGIS.<br />
<br />
Для выполнения примера следует провести подготовку(все примеры работы с БД приведены для Linux): <br />
* Установить PostgreSQL c расширением PostGIS. <br />
* В СУБД создать роль, из-под которой возможен логин в БД для загрузки дампа:<br />
<pre><br />
# su - postgres<br />
$ psql<br />
create role gis with login password 'gis';<br />
create database doc_example owner gis;<br />
ctrl-d<br />
</pre><br />
* Имя пользователя, пароль и имя базы можно использовать произвольные, но тогда необходимо будет изменить в list 5 переменные: dbname, dbuser, dbpass.<br />
* Подключить расширение PostGIS в БД.<br />
<pre><br />
$ psql -d doc_example<br />
create extension postgis<br />
ctrl-d<br />
</pre><br />
* Развернуть дамп из data/crowns.sql<br />
<pre><br />
psql -d doc_example < data/crowns.sql<br />
</pre><br />
* Проверить наличие Python-модуля "psycopg2" - драйвера PostgreSQL (необходим для интерфейса запросов modules.db_interface)<br />
<br />
После успешного завершения подготовительного этапа можно выполнять тестовый пример cut_wkt.py (list 5)<br />
<br />
list 5 - cut_wkt.py<br />
<syntaxhighlight lang="python"><br />
#! /usr/bin/python2<br />
# -*- coding: utf-8 -*-<br />
<br />
from raster_tools import raster2array, array2raster<br />
from modules import psql<br />
<br />
# загрузка растра в объект raster2array<br />
in_file = "result/calc.tif"<br />
out_file = "result/cut_wkt.tif"<br />
<br />
# установка параметров подключения в БД<br />
dbhost = "localhost"<br />
dbname = "dok_example"<br />
dbuser = "gis"<br />
dbpass = "gis"<br />
<br />
# параметры поиска полигона в БД<br />
geom_table = "crowns"<br />
geom_id = 55775<br />
<br />
# запрос в БД, возвращающий полигон в формате WKT<br />
SQL = """<br />
select ST_AsText(wkb_geometry)<br />
from {0}<br />
where ogc_fid = {1}<br />
""".format(<br />
geom_table,<br />
geom_id<br />
)<br />
_psql = psql(<br />
dbhost=dbhost,<br />
dbname=dbname,<br />
dbuser=dbuser,<br />
dbpass=dbpass<br />
)<br />
_psql.sql(SQL)<br />
wkt_geom = _psql.fetchone()[0]<br />
_psql.close()<br />
<br />
# вырезание области растра по полигону WKT<br />
# и сохранение в формате стандартного словаря<br />
std_dict = raster2array(in_file).cut_ogr_geometry(wkt_geom)<br />
<br />
# сохранеие стандартного словаря в растр<br />
array2raster(None, std_dict, out_file)<br />
</syntaxhighlight><br />
<br />
В результате выполнения примера должен быть создан result/cut_wkt.tif (img 5.1, img 5.2).<br />
<br />
<br />
img 5.1 - отображение геометрии из postgis на фоне исходного растра<br />
<br />
[[Файл:Cut_wkt_screenshot.png|400px]]<br />
<br />
<br />
img 5.2 - result/cut_wkt.tif<br />
<br />
[[Файл:Cut_wkt.png|200px]]<br />
<br />
== Трансформация ==<br />
<br />
Иногда приходится вычислять индексы, или иным способом сравнивать георастры из различных источников. Входные данные могут быть в разном масштабе и соответственно иметь разную размерность массивов исследуемой области. В результате вычисление индексов средствами numpy становится невозможным, а многие другие операции затруднительны. <br />
<br />
Решить подобною проблему призван класс raster2transform. При помощи него можно изменить размерность всего массива, или только его части.<br />
Для работы тестового примера resize.py (list 6) требуется установка matplotlib и графической среды пользователя, исходным георастром является result/cut_shp.tif.<br />
<br />
list 6 - resize.py<br />
<syntaxhighlight lang="python"><br />
#! /usr/bin/python2<br />
# -*- coding: utf-8 -*-<br />
<br />
from raster_tools import raster2array, raster2transform<br />
from matplotlib import pyplot as plt<br />
<br />
in_raster = "result/cut_shp.tif"<br />
out_raster = "result/resize.tif"<br />
<br />
# вывод массива в matplotlib<br />
def plt_show(obj):<br />
plt.title("show")<br />
plt.imshow(obj.array(), cmap='gray')<br />
plt.show()<br />
<br />
# загрузка растра в raster2array<br />
raster = raster2array(in_raster)<br />
print raster.rows, raster.cols<br />
<br />
# кроме того возможна работа с:<br />
#raster = in_raster #именем растрового файла<br />
#raster = raster2array(in_raster).get_std_dict() #стандартным словарём<br />
<br />
# изменение размерности массива (row, col) в цикле<br />
for row, col in [(253, 210), (125, 105), (300, 300)]:<br />
raster = raster2transform(raster, row, col)<br />
print raster.rows, raster.cols<br />
plt_show(raster)<br />
<br />
# сохранение растра c последней размерностью массива<br />
raster.save(out_raster)<br />
</syntaxhighlight><br />
<br />
В результате работы скрипта 3 раза отобразится окно matplotlib.pyplot с изображением массива растра в следующих размерностях: (253,210)-img 6.1, (125,105)-img 6.2, (300,300)-img 6.3. Последний массив сохранится в растр result/resize.tif (img 6.3).<br />
<br />
<br />
img 6.1 - разрешение (253x210)<br />
<br />
[[Файл:Resize_253_210.png|500px]]<br />
<br />
<br />
img 6.1 - разрешение (125x105)<br />
<br />
[[Файл:Resize_125_105.png|500px]]<br />
<br />
<br />
img 6.1 - разрешение (300x300)<br />
<br />
[[Файл:Resize_300_300.png|500px]]<br />
<br />
== Мультирастры ==<br />
<br />
Для вычисления индексов удобно работать с одноканальными растрами, представляя каждый канал как отдельно взятую сущность. Но иногда требуется создавать и работать с многоканальными конструкциями. Для это создана пара мультиканальных классов: <br />
<br />
* raster2multiarray - Класс, умеющий изменять очерёдность и количество каналов, при этом преобразующий многоканальный растр в стандартный словарь расширенного типа. От обычного он отличается тем, что в поле «array» сохраняется не двухмерный (x, y), а трёхмерный numpy-массив с индексом (bands, x, y). Для определения типа массива добавлено поле «multi_type», принимающее значения: None - с массивом уже указанного индекса и «cv» - с индексом (x, y, bands). Массив типа multi_type = «cv» аналогичен возвращаемому функцией cv2.imread. Кроме того, у raster2multiarray имеется набор методов обрезки растра, аналогичных raster2array.<br />
<br />
* multiarray2multiraster - Класс, сохраняющий многоканальный стандартный словарь в мультиканальный растр.<br />
<br />
Предлагаю два варианта использования данной пары классов, исходным растром для примеров послужит data/multi.tif:<br />
<br />
<br />
=== Изменение последовательности каналов ===<br />
<br />
Для данного примера в RGB-мультирастре переставим местами 1-й и 3-й каналы. Тестовый скрипт представлен в multi_rebands.py (list 7). <br />
<br />
list 7 - multi_rebands.py<br />
<syntaxhighlight lang="python"><br />
#!/usr/bin/python2<br />
# -*- coding: utf-8 -*-<br />
<br />
from raster_tools import raster2multiarray, multiarray2multiraster<br />
<br />
img_in = 'data/multi.tif'<br />
img_out = 'result/multi_rebands.tif'<br />
<br />
# загрузка многоканального в объект raster2multiarray<br />
# и изменение последовательности каналов 1-3, 2-2, 3-1<br />
img = raster2multiarray(img_in, 3, 2, 1)<br />
<br />
# выгрузка многоканального стандартного словаря<br />
img = img.get_std_dict()<br />
<br />
# cохранение многоканального стандартного словаря<br />
# в мультирастр<br />
multiarray2multiraster(img_out, img)<br />
</syntaxhighlight><br />
<br />
Результатом выполнения будет создание result/multi_rebands.tif (img 7).<br />
<br />
<br />
img 7 - result/multi_rebands.tif<br />
<br />
[[Файл:Multi_rebands.png|500px]]<br />
<br />
<br />
=== Обработка муьтирастров в opencv ===<br />
<br />
Теперь усложним задачу: удалим альфа-канал мультирастра, передадим массив на обработку OpenCV и сохраним полученный результат. Для этого примера необходимо установить Python-модули OpenCV версии 2 или 3. Тестовый скрипт представлен в multi_opencv.py (list 8).<br />
<br />
list 8 - multi_opencv.py<br />
<syntaxhighlight lang="python"><br />
#!/usr/bin/python2<br />
# -*- coding: utf-8 -*-<br />
<br />
import numpy as np<br />
<br />
from raster_tools import raster2multiarray, multiarray2multiraster<br />
import cv2<br />
<br />
img_in = 'data/multi.tif'<br />
img_out = 'result/multi_opencv.tif'<br />
<br />
# загрузка многоканального в объект raster2multiarray<br />
# для обработки через cv2.bilateralFilter необходима загрузка только 1,2,3 канала<br />
img = raster2multiarray(img_in, 1, 2, 3)<br />
<br />
# установка типа возвращаемого многоканального массива<br />
# в формат opencv<br />
img.multi_type = "cv"<br />
<br />
# установка типа данных возвращаемого массива как uint8<br />
# необходимо для обработки данных в opencv<br />
img.codage = np.uint8<br />
<br />
# выгрузка многоканального стандартого словаря<br />
std_dict = img.get_std_dict()<br />
<br />
# работа в OpenCV c многоканальным массивом<br />
std_dict["array"] = cv2.bilateralFilter(std_dict["array"],9,75,75)<br />
<br />
# сохранение многоканального стандартного словаря<br />
# в мультирастр<br />
multiarray2multiraster(img_out, std_dict)<br />
</syntaxhighlight><br />
<br />
Результат будет сохранён в result/multi_opencv.tif (img 8).<br />
<br />
<br />
img 8 - result/multi_opencv.tif<br />
<br />
[[Файл:Multi_opencv.png|500px]]<br />
<br />
<br />
==Работа с точками.==<br />
<br />
Неочевидный, но временами очень важный метод препарирования георастров. Постановка задачи: запись значений точек растра с определёнными координатами в CSV, БД или в виде поля в слой shp-файла. Реализация последнего примера представлена в point_data_load_array.py (list 9). Исходными данными здесь выступают: георастр result/calc.tif и векторный файл со слоем точек data/tops.shp.<br />
<br />
list 9 - point_data_load_array.py<br />
<syntaxhighlight lang="python"><br />
#!/usr/bin/python2<br />
# -*- coding: utf-8 -*-<br />
<br />
from raster_tools import raster2array<br />
import ogr<br />
import shutil<br />
<br />
raster = "result/calc.tif"<br />
<br />
# копируем тестовый shp-файл с точками в каталог result<br />
_filename = "tops"<br />
_indir = "data"<br />
_outdir = "result"<br />
for _ex in ["dbf", "prj", "qpj", "shp", "shx"]:<br />
shutil.copyfile(<br />
"{0}/{1}.{2}".format(_indir, _filename, _ex),<br />
"{0}/{1}.{2}".format(_outdir, _filename, _ex),<br />
)<br />
<br />
shp_file = "{0}/{1}.shp".format(_outdir, _filename)<br />
<br />
# загрузка растра в объект raster2array<br />
raster = raster2array(raster)<br />
<br />
# сохранение numpy массива в объекте raster2array<br />
raster.np_array_load()<br />
<br />
# загрузка shp-файла<br />
_shp = ogr.Open(shp_file, update=1)<br />
layer = _shp.GetLayerByIndex(0)<br />
<br />
# добавление в shp-файл нового поля<br />
# для записи выгруженных значений растра<br />
new_field = "POINT_DATA"<br />
if layer.FindFieldIndex(new_field, 1) < 1:<br />
field = ogr.FieldDefn(new_field, ogr.OFTReal)<br />
layer.CreateField(field)<br />
<br />
# обход циклом точек shp-файла<br />
for geometry in layer:<br />
<br />
# определение координат точки векторного файла<br />
x, _, y, _ = geometry.GetGeometryRef().GetEnvelope()<br />
<br />
# возврат значения данных точки растра<br />
# по координатам точки векторного файла<br />
raster_data = raster.get_pixel_value(float(x), float(y))<br />
<br />
# сохранение выгруженных из растра данных<br />
# в новое поле shp-файла<br />
geometry.SetField(new_field, raster_data)<br />
layer.SetFeature(geometry)<br />
</syntaxhighlight><br />
<br />
В ходе выполнения скрипта исходный векторный файл data/tops.shp копируется в result/tops.shp, в который добавляется поле «POINT_DATA». В добавленном поле сохраняются значения точек растра по координатам геометрий векторного слоя. При помощи полученного векторного файла можно будет производить интерполяцию значений поля «POINT_DATA»(пример дальше по тексту).<br />
<br />
В list 9 есть одна «хитрость»:<br />
<br />
<syntaxhighlight lang="python"><br />
# сохранение numpy массива в объекте raster2array<br />
raster.np_array_load()<br />
</syntaxhighlight><br />
<br />
Такая конструкция загружает numpy-массив растра непосредственно в экземпляр класса raster2array, что уменьшает время выполнения операций, увеличивая потребление памяти. Для сравнения: в репозитории есть аналогичный скрипт point_data_dont_load_array.py, где данная строка отсутствует. Сравнение графиков выполнения обоих скриптов в memory_profiler выглядит следующим образом: <br />
<br />
[[Файл:Point_data_load_array_mprof.png|600px]]<br />
[[Файл:Point_data_dont_load_array_mprof.png|600px]]<br />
<br />
<br />
== "Ремонт" георастров ==<br />
<br />
Иногда процедурная генерация георастров некоторыми утилитами порождает «мутантов». При их отображении в ПО ГИС проблема не заметна - так как геоданные будут правильно позиционированы. Но при выгрузке из такого георастра numpy-массива оказывается, что он зеркально отображён относительно оси X или Y. Примером утилиты с такой странной процедурной генерации является gdal_grid ([https://gis-lab.info/forum/viewtopic.php?f=30&t=20283 сообщение о проблеме на форуме]).<br />
<br />
Чтобы не быть голословными, выполним весь цикл процедур: интерполяция данных из предыдущего примера result/tops.shp при помощи gdal_grid, проверка полученного процедурного растра на «зеркальность», исправление его при помощи raster_tools. В качестве исходных данных будут использованы: result/tops.shp (источник данных для интерполяции) и result/calc.tif (для сравнения). Обработку произведём скриптом repair.py (list 11). Для работы repair.py необходим модуль modules.grid_utils и сама утилита gdal_grid. <br />
<br />
list 11 - repair.py<br />
<syntaxhighlight lang="python"><br />
#!/usr/bin/python2<br />
# -*- coding: utf-8 -*-<br />
<br />
from raster_tools import raster2array, array2raster<br />
from modules import grid_utils<br />
<br />
out_dir = "result"<br />
shp_file = "result/tops.shp"<br />
field_name = "POINT_DATA"<br />
raster_file = "result/calc.tif"<br />
grid_file = "result/{}.tif".format(field_name)<br />
repair_file = "result/repair.tif"<br />
<br />
# Интерполяция на основе shp-файла<br />
grid_utils(raster_file, shp_file, field_name, out_dir)<br />
<br />
# загрузка результатов интерполяции в raster2array<br />
grid = raster2array(grid_file)<br />
<br />
# проверка "валидности" массива в растре<br />
print grid.is_valid()<br />
<br />
# возврат "отремонтированного" растра в формате стандартного словаря<br />
grid = grid.repair()<br />
<br />
# запись исправленного растра<br />
array2raster(None, grid, repair_file)<br />
</syntaxhighlight><br />
<br />
В результате работы скрипта были созданы 2 растра: result/POINT_DATA.tif - результат работы gdal_grid и исправленный растр result/repair.tif. Сравним вывод информации о растрах через gdalinfo:<br />
<br />
<pre><br />
$ gdalinfo result/POINT_DATA.tif<br />
...<br />
Pixel Size = (0.640086707766337,0.633172658674583)<br />
Metadata:<br />
AREA_OR_POINT=Area<br />
Image Structure Metadata:<br />
INTERLEAVE=BAND<br />
Corner Coordinates:<br />
Upper Left ( 296008.694, 7137566.459) ( 40d46'52.75"E, 64d18'11.32"N)<br />
Lower Left ( 296008.694, 7137767.175) ( 40d46'51.76"E, 64d18'17.78"N)<br />
Upper Right ( 296387.625, 7137566.459) ( 40d47'20.87"E, 64d18'12.13"N)<br />
Lower Right ( 296387.625, 7137767.175) ( 40d47'19.88"E, 64d18'18.60"N)<br />
Center ( 296198.159, 7137666.817) ( 40d47' 6.31"E, 64d18'14.96"N)<br />
Band 1 Block=592x1 Type=Float64, ColorInterp=Gray<br />
</pre><br />
<br />
<pre><br />
$ gdalinfo result/repair.tif<br />
...<br />
Pixel Size = (0.640086707766337,-0.633172658674583)<br />
Metadata:<br />
AREA_OR_POINT=Area<br />
Image Structure Metadata:<br />
COMPRESSION=LZW<br />
INTERLEAVE=BAND<br />
Corner Coordinates:<br />
Upper Left ( 296008.694, 7137767.175) ( 40d46'51.76"E, 64d18'17.78"N)<br />
Lower Left ( 296008.694, 7137566.459) ( 40d46'52.75"E, 64d18'11.32"N)<br />
Upper Right ( 296387.625, 7137767.175) ( 40d47'19.88"E, 64d18'18.60"N)<br />
Lower Right ( 296387.625, 7137566.459) ( 40d47'20.87"E, 64d18'12.13"N)<br />
Center ( 296198.159, 7137666.817) ( 40d47' 6.31"E, 64d18'14.96"N)<br />
Band 1 Block=256x256 Type=Float64, ColorInterp=Gray<br />
</pre><br />
<br />
и в качестве контрольной пробы:<br />
<br />
<pre><br />
$ gdalinfo result/calc.tif<br />
...<br />
Pixel Size = (0.064274599999999,-0.064274599999986)<br />
Metadata:<br />
AREA_OR_POINT=Area<br />
Image Structure Metadata:<br />
COMPRESSION=LZW<br />
INTERLEAVE=BAND<br />
Corner Coordinates:<br />
Upper Left ( 296007.704, 7137768.705) ( 40d46'51.68"E, 64d18'17.83"N)<br />
Lower Left ( 296007.704, 7137564.891) ( 40d46'52.68"E, 64d18'11.27"N)<br />
Upper Right ( 296388.595, 7137768.705) ( 40d47'19.94"E, 64d18'18.65"N)<br />
Lower Right ( 296388.595, 7137564.891) ( 40d47'20.95"E, 64d18'12.08"N)<br />
Center ( 296198.149, 7137666.798) ( 40d47' 6.31"E, 64d18'14.96"N)<br />
Band 1 Block=256x256 Type=Float64, ColorInterp=Gray<br />
</pre><br />
<br />
Обратите внимание на второе значение Pixel Size(ось y): у растра, сгенерированного gdal_grid, это значение положительное в отличие от исходного и исправленного георастров. Что и позволяет отображать POINT_DATA.tif в ПО ГИС нормально, несмотря на наличие зеркально отображённого через ось Y массива. То же самое сообщает нам вывод метода raster2array.is_valid():<br />
<br />
<syntaxhighlight lang="python"><br />
# загрузка результатов интерполяции в raster2array<br />
grid = raster2array(grid_file)<br />
# проверка "валидности" массива в растре<br />
print grid.is_valid()<br />
</syntaxhighlight><br />
<br />
Вывод строки:<br />
<br />
<syntaxhighlight lang="python"><br />
[True, False]<br />
</syntaxhighlight><br />
<br />
Здесь ориентация растра по оси X истинное и ложное по оси Y.<br />
<br />
<br />
== Послесловие ==<br />
<br />
Заканчивая описание способов употребления raster_tools, хочется отметить, что инструмент будет и дальше находиться в состоянии постоянного развития. По мере поступления задач в него будут добавляться какие-то новые функции, при этом с сохранением старых методов применения (чтобы не потерять совместимость с уже завершёнными проектами). Статья описывает состояние raster_tools версии 0.4 - на текущий момент последняя стабильная версия.<br />
<br />
У raster_tools имеются следующие недостатки: <br />
* проблемное перепроецирование векторных слоёв-шаблонов в проекцию растра, поэтому логично использовать единую проекцию;<br />
* обрезка по shp-файлам «прибита гвоздями» к первому векторному слою;<br />
* инструмент рассчитан на формат GeoTIFF; работа с другими растровыми форматами файлов в зачаточном состоянии;<br />
* недостаточно оптимизирована работа с мультирастрами (нечасто пока требуются);</div>Александр Мурыйhttps://wiki.gis-lab.info/index.php?title=%D0%9F%D1%80%D0%B0%D0%BA%D1%82%D0%B8%D0%BA%D0%B0_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F_raster_tools&diff=26378Практика использования raster tools2018-07-30T20:30:23Z<p>Александр Мурый: /* Метод cut_ogr_geometry */</p>
<hr />
<div>{{Статья|Черновик}}<br />
{{Аннотация|В статье рассматриваются примеры практического использования набора растровых инструментов raster_tools.}}<br />
<br />
<br />
== Знакомство с raster_tools ==<br />
<br />
В течении продолжительного времени развиваю инструментарий для препарирования георастров. Который является набором рецептов, упрощающих взаимодействие с гибкой, но сложной структурой Python + GDAL. К сегодняшнему времени '''raster_tools''' напоминает некий «швейцарский нож» с разнообразным функционалом. <br />
Код проекта расположен на [https://github.com/oldbay/raster_tools https://github.com/oldbay/raster_tools] , самый простой способ его установки в каталоге пользователя - при помощи pip:<br />
<br />
<syntaxhighlight lang="bash"><br />
pip2.7 install --user git+https://github.com/oldbay/raster_tools<br />
</syntaxhighlight><br />
<br />
'''raster_tools''' состоит из следующих классов:<br />
<br />
'''raster2array''' - Базовый класс, отвечающий за весь функционал загрузки растра и сохранение numpy-массива выбранного канала(band). Выгруженный из растра массив может быть преобразован методами данного класса. <br />
<br />
'''array2raster(raster2array)''' - Субкласс, отвечающий за сохранение обработанного массива в файле георастра. Он также может сформировать виртуальный растр в памяти с возможностью обработки методами базового класса.<br />
<br />
'''raster2transform(raster2array)''' - Субкласс, отвечающий за трансформацию растра: изменение размерности массива и перепроецирование. Также есть поддержка методов базового класса.<br />
<br />
'''raster2calc(raster2array)''' - Класс итерационного калькулятора. Он не наследует базовый класс raster2array, но использует некоторые его методы опосредованно.<br />
<br />
'''raster2multiarray(raster2array)''' - Класс обработки мультирастров. Позволяет миксовать каналы мультиканального растра и обрабатывать его некоторыми методами raster2array.<br />
<br />
'''multiarray2multiraster''' - Класс сохранения массивов, обработанных методами raster2multiarray, в мультиканальный растр.<br />
<br />
Основной смысл существования всего этого «зоопарка» классов - это загрузка, обработка и выгрузка двухмерного numpy-массива георастров. Помимо объекта 2-х мерного numpy-массива некоторые методы raster2array позволяют выгружать данные в формате самоназванного «стандартного словаря», имеющего следующий вид:<br />
<br />
<syntaxhighlight lang="python"><br />
{<br />
"array": объект numpy.ndarray,<br />
"shape": кортеж (rows(Y), cols(X)) = numpy.ndarray.shape = (gdal.dataset.RasterYSize(), gdal.dataset.RasterXSize()),<br />
"transform": кортеж gdal.dataset.GetGeoTransform(),<br />
"projection": объект gdal.dataset.GetProjection()<br />
}<br />
</syntaxhighlight> <br />
<br />
Эта конструкция несёт в себе всю необходимую информацию для создания нового георастра, отличного от исходного.<br />
<br />
Углубляться в подробное описание структуры классов и методов raster_tools нет необходимости - достаточно полная базовая документация есть в README.rst на [https://github.com/oldbay/raster_tools https://github.com/oldbay/raster_tools]. В данной статье хотелось бы показать «боевое» применение raster_tools на практике.<br />
<br />
Для этих экспериментов был создан репозиторий с тестовыми данными, загрузить которые можно командой:<br />
<br />
<syntaxhighlight lang="bash"><br />
git clone https://gitlab.com/oldbay/raster_tools_examples.git<br />
</syntaxhighlight><br />
<br />
В ветке по умолчанию (master) расположены только тестовые скрипты и исходные данные. Чтобы заранее изучить результаты тестов, нужно перейти в ветку "result":<br />
<br />
<syntaxhighlight lang="bash"><br />
git checkout origin/result<br />
</syntaxhighlight><br />
<br />
Тестовый репозиторий состоит из:<br />
* Корень: содержит тестовые примеры, выполнение которых необходимо производить из текущего каталога;<br />
* Каталог data: содержит исходные данные для экспериментов;<br />
* Каталог logs: содержит результаты тестов расхода памяти и времени выполнения (branch result);<br />
* Каталог module: содержит дополнительные Python-модули, необходимые для выполнение некоторых тестовых скриптов(в статье не описаны);<br />
* Каталог results: содержит результаты экспериментов (branch result).<br />
<br />
<br />
== Растровый калькулятор. ==<br />
<br />
Первоначально raster_tools был частью растрового калькулятора, сейчас отдельный проект raster_calc, поэтому исторически начнём с вычислений.<br />
<br />
Пример простого растрового калькулятора представлен в calc.py (list 1) . В данном скрипте мультирастр разбирается на спектральные каналы, после чего вычисляется вегетативный индекс на основе rgb - TGI ([https://gist.github.com/merkato/0cd894f19518496171afd7425e09ed88 украдено отсюда]), Источником данных является data/multi.tif (img 1).<br />
<br />
<br />
img 1 - data/multi.tif<br />
<br />
[[Файл:Multi.png|500px]]<br />
<br />
<br />
list 1 - calc.py<br />
<syntaxhighlight lang="python"><br />
#!/usr/bin/python2<br />
# -*- coding: utf-8 -*-<br />
<br />
from raster_tools import raster2array, array2raster<br />
import numpy as np<br />
<br />
in_file = "data/multi.tif"<br />
out_file = "result/calc.tif"<br />
<br />
# загрузка каналов<br />
red = raster2array(in_file, 1)<br />
green = raster2array(in_file, 2)<br />
blue = raster2array(in_file, 3)<br />
<br />
# сохранение numpy массивов каналов в переменные.<br />
r = red()<br />
g = green()<br />
b = blue()<br />
<br />
# вычисление TGI<br />
calc = np.choose(<br />
np.not_equal(g-r+b-255.0, 0.0),<br />
(<br />
-9999.0,<br />
np.subtract(<br />
g,<br />
np.multiply(0.39, r),<br />
np.multiply(0.61, b)<br />
)<br />
)<br />
)<br />
<br />
# сохранение вычисленного массива в растр.<br />
array2raster(red, calc, out_file)<br />
</syntaxhighlight><br />
<br />
В результате выполнения calc.py создается растр result/calc.tif (img 2).<br />
<br />
<br />
img 2 - result/calc.tif<br />
<br />
[[Файл:Calc.png|500px]]<br />
<br />
<br />
У метода есть существенный минус - высокое потребление памяти. Фактически в памяти на пике потребления присутствуют сразу 4 массива растров: каналов r, g, b и вычисляемого индекса. При работе с большими георастрами это часто неприемлемо. Поэтому был разработан итерационный метод работы растрового калькулятора. Данный метод работает с растром посекторно в цикле. При таких вычислениях сильно снижается потребление памяти, но падает производительность.<br />
<br />
Итерационный калькулятор реализован в calc_iter.py (list 2). В отличии от простого калькулятора здесь формула вычислений(в формате lambda) и переменные для вычислений(в виде объектов raster2array) передаются классу raster2calc.<br />
<br />
list 2 - calc_iter.py<br />
<syntaxhighlight lang="python"><br />
#!/usr/bin/python2<br />
# -*- coding: utf-8 -*-<br />
<br />
from raster_tools import raster2array, array2raster, raster2calc<br />
import numpy as np<br />
<br />
in_file = "data/multi.tif"<br />
out_file = "result/calc_iter.tif"<br />
<br />
# загрузка каналов<br />
red = raster2array(in_file, 1)<br />
green = raster2array(in_file, 2)<br />
blue = raster2array(in_file, 3)<br />
<br />
# описание формулы вычисления TGI<br />
calc_func = lambda r,g,b: np.choose(<br />
np.not_equal(g-r+b-255.0, 0.0),<br />
(<br />
-9999.0,<br />
np.subtract(<br />
g,<br />
np.multiply(0.39, r),<br />
np.multiply(0.61, b)<br />
)<br />
)<br />
)<br />
<br />
# вычисление TGI<br />
# и сохраниение результата в формате<br />
# стандартного словаря<br />
calc = raster2calc()<br />
out = calc(<br />
calc_func,<br />
r=red,<br />
g=green,<br />
b=blue<br />
)<br />
<br />
# сохранение стандартного словаря в растр.<br />
array2raster(None, out, out_file)<br />
</syntaxhighlight><br />
<br />
raster2calc возвращает «стандартный словарь», преобразуемый классом array2raster в файл result/calc_iter.tif.<br />
<br />
Если выполнить calc.py и calc_iter.py в обёртке memory_profiler - получаем графики потребления памяти:<br />
<br />
[[Файл:Calc_mprof.png|600px]]<br />
[[Файл:Calc_iter_mprof.png|600px]]<br />
<br />
Видно что итерационный метод выигрывает в потреблении памяти, но теряет в скорости расчётов. На текущем примере потери скорости не очень существенны, но при массовой обработке небольших георастров бывает выгоднее пожертвовать памятью.<br />
<br />
== Вырезание области растра. ==<br />
<br />
Иногда растровые вычисления необходимо производить только с некоторой выделенной областью георастра. Для «фигурного» вырезания областей, ограниченных координатами точек или охватом полигонов, у raster2array есть ряд методов:<br />
* cut_area(координаты точек) - метод вырезает область растра по координатам (не менее 2-х точек);<br />
* cut_shp_file(имя shp файла) - метод вырезает растр по полигонам первого слоя shp-файла;<br />
* cut_ogr_geometry(wkt, geojson, gml, wkb) - метод вырезает растр по полигону загруженной геометрии.<br />
<br />
Все перечисленные методы возвращают «стандартный словарь», так как они изменяют размерность массива растра, делая необходимым изменение «transform» у сохраняемого георастра.<br />
<br />
Стандартный словарь применим и в растровом калькуляторе, для подобных вычислений необходимо вернуть numpy массив по ключу «array» list 1.1<br />
<br />
list 1.1<br />
<syntaxhighlight lang="python"><br />
<br />
# загрузка каналов в формате стандартного словаря<br />
# выгрузка аналогична методам обрезки, но для<br />
# всего исходного растра<br />
red = raster2array(in_file, 1).get_std_dict()<br />
green = raster2array(in_file, 2).get_std_dict()<br />
blue = raster2array(in_file, 3).get_std_dict()<br />
<br />
# сохранение numpy-массивов каналов в переменные.<br />
r = red()["array"]<br />
g = green()["array"]<br />
b = blue()["array"]<br />
<br />
# вычисление TGI (как в list 1)<br />
<br />
........<br />
<br />
<br />
# сохранение вычисленного массива в стандартный словарь:<br />
out_std_dict = red<br />
out_std_dict["array"] = calc<br />
<br />
# сохранение стандартного словаря в растр:<br />
array2raster(None, out_std_dict, out_file)<br />
</syntaxhighlight><br />
<br />
Теперь рассмотрим работу каждого метода обрезки растра.<br />
<br />
<br />
=== Метод cut_area ===<br />
<br />
Позволяет обрезать растр по набору координат точек. Применяется когда необходимо обрезать область растра по массиву точек - для дальнейшей обработки. Пример использования приведён в cut_coords.py (list 3), в качестве исходных данных используется result/calc.tif.<br />
<br />
list 3 - cut_coords.py<br />
<syntaxhighlight lang="python"><br />
#!/usr/bin/python2<br />
# -*- coding: utf-8 -*-<br />
<br />
from raster_tools import raster2array, array2raster<br />
<br />
in_file = "result/calc.tif"<br />
out_file = "result/cut_coords.tif"<br />
<br />
# вырезание области растра по координатам<br />
# и сохранение в формате стандартного словаря<br />
std_dict = raster2array(in_file).cut_area(<br />
(296153.369,7137678.937),<br />
(296203.959,7137570.986),<br />
(296256.938,7137645.476)<br />
)<br />
<br />
# сохранение стандартного словаря в растр<br />
array2raster(None, std_dict, out_file)<br />
</syntaxhighlight><br />
<br />
Результат выполнения метода сохраняется в out: result/cut_coords.tif (img 3.1 ,img 3.2)<br />
<br />
img 3.1 - отображение области на исходном растре<br />
<br />
[[Файл:Cut_coords_screenshot.png|500px]]<br />
<br />
img 3.2 - result/cut_coords.tif<br />
<br />
[[Файл:Cut_coords.png|200px]]<br />
<br />
<br />
=== Метод cut_shp_file. ===<br />
<br />
Часто необходимо обрезать растр по геометрии слоя shp файла. Пример выполнения такой задачи приведён в cut_shp.py (list 4). Помимо исходного георастра result/calc.tif , скрипт использует шаблона обрезки - data/cut.shp.<br />
<br />
list 4 - cut_shp.py<br />
<syntaxhighlight lang="python"><br />
#!/usr/bin/python2<br />
# -*- coding: utf-8 -*-<br />
<br />
from raster_tools import raster2array, array2raster<br />
<br />
shp_file = "data/cut.shp"<br />
in_file = "result/calc.tif"<br />
out_file = "result/cut_shp.tif"<br />
<br />
# вырезание области растра по полигону shp-файла<br />
# и сохранение в формате стандартного словаря<br />
std_dict = raster2array(in_file).cut_shp_file(shp_file)<br />
<br />
# сохранение стандартного словаря в растр<br />
array2raster(None, std_dict, out_file)<br />
</syntaxhighlight><br />
<br />
Результат сохраняется в георастр result/cut_shp.tif (img 4.1, img 4.2).<br />
<br />
<br />
img 4.1 - геометрия из shp-файла на фоне исходного растра<br />
<br />
[[Файл:Cut_shp_screenshot.png|300px]]<br />
<br />
<br />
img 4.2 - result/cut_shp.tif<br />
<br />
[[Файл:Cut_shp.png|200px]]<br />
<br />
<br />
Обратите внимание, что растр обрезан непосредственно по полигону, а не только по координатам охвата объекта.<br />
<br />
=== Метод cut_ogr_geometry ===<br />
<br />
Применяется, если необходима обрезка георастра по геометрия в формате wkt, geojson, gml или wkb. Наиболее типичный способ использования - это вырезание растра на по wkt-геометрии, возвращённой из PostGIS.<br />
<br />
Для выполнения примера следует провести подготовку(все примеры работы с БД приведены для Linux): <br />
* Установить PostgreSQL c расширением PostGIS. <br />
* В СУБД создать роль, из-под которой возможен логин в БД для загрузки дампа:<br />
<pre><br />
# su - postgres<br />
$ psql<br />
create role gis with login password 'gis';<br />
create database doc_example owner gis;<br />
ctrl-d<br />
</pre><br />
* Имя пользователя, пароль и имя базы можно использовать произвольные, но тогда необходимо будет изменить в list 5 переменные: dbname, dbuser, dbpass.<br />
* Подключить расширение PostGIS в БД.<br />
<pre><br />
$ psql -d doc_example<br />
create extension postgis<br />
ctrl-d<br />
</pre><br />
* Развернуть дамп из data/crowns.sql<br />
<pre><br />
psql -d doc_example < data/crowns.sql<br />
</pre><br />
* Проверить наличие Python-модуля "psycopg2" - драйвера PostgreSQL (необходим для интерфейса запросов modules.db_interface)<br />
<br />
После успешного завершения подготовительного этапа можно выполнять тестовый пример cut_wkt.py (list 5)<br />
<br />
list 5 - cut_wkt.py<br />
<syntaxhighlight lang="python"><br />
#! /usr/bin/python2<br />
# -*- coding: utf-8 -*-<br />
<br />
from raster_tools import raster2array, array2raster<br />
from modules import psql<br />
<br />
# загрузка растра в объект raster2array<br />
in_file = "result/calc.tif"<br />
out_file = "result/cut_wkt.tif"<br />
<br />
# установка параметров подключения в БД<br />
dbhost = "localhost"<br />
dbname = "dok_example"<br />
dbuser = "gis"<br />
dbpass = "gis"<br />
<br />
# параметры поиска полигона в БД<br />
geom_table = "crowns"<br />
geom_id = 55775<br />
<br />
# запрос в БД, возвращающий полигон в формате WKT<br />
SQL = """<br />
select ST_AsText(wkb_geometry)<br />
from {0}<br />
where ogc_fid = {1}<br />
""".format(<br />
geom_table,<br />
geom_id<br />
)<br />
_psql = psql(<br />
dbhost=dbhost,<br />
dbname=dbname,<br />
dbuser=dbuser,<br />
dbpass=dbpass<br />
)<br />
_psql.sql(SQL)<br />
wkt_geom = _psql.fetchone()[0]<br />
_psql.close()<br />
<br />
# вырезание области растра по полигону WKT<br />
# и сохранение в формате стандартного словаря<br />
std_dict = raster2array(in_file).cut_ogr_geometry(wkt_geom)<br />
<br />
# сохранеие стандартного словаря в растр<br />
array2raster(None, std_dict, out_file)<br />
</syntaxhighlight><br />
<br />
В результате выполнения примера должен быть создан result/cut_wkt.tif (img 5.1, img 5.2).<br />
<br />
<br />
img 5.1 - отображение геометрии из postgis на фоне исходного растра<br />
<br />
[[Файл:Cut_wkt_screenshot.png|400px]]<br />
<br />
<br />
img 5.2 - result/cut_wkt.tif<br />
<br />
[[Файл:Cut_wkt.png|200px]]<br />
<br />
== Трансформация ==<br />
<br />
Иногда приходится вычислять индексы, или иным способом сравнивать георастры из различных источников. Входные данные могут быть в разном масштабе и соответственно иметь разную размерность массивов исследуемой области. В результате вычисление индексов средствами numpy становится невозможным, а многие другие операции затруднительны. <br />
<br />
Решить подобною проблему призван класс raster2transform. При помощи него можно изменить размерность всего массива, или только его части.<br />
Для работы тестового примера resize.py (list 6) требуется установка matplotlib и графической среды пользователя, исходным георастром является result/cut_shp.tif.<br />
<br />
list 6 - resize.py<br />
<syntaxhighlight lang="python"><br />
#! /usr/bin/python2<br />
# -*- coding: utf-8 -*-<br />
<br />
from raster_tools import raster2array, raster2transform<br />
from matplotlib import pyplot as plt<br />
<br />
in_raster = "result/cut_shp.tif"<br />
out_raster = "result/resize.tif"<br />
<br />
# вывод массива в matplotlib<br />
def plt_show(obj):<br />
plt.title("show")<br />
plt.imshow(obj.array(), cmap='gray')<br />
plt.show()<br />
<br />
# загрузка растра в raster2array<br />
raster = raster2array(in_raster)<br />
print raster.rows, raster.cols<br />
<br />
# кроме того возможна работа с:<br />
#raster = in_raster #именем растрового файла<br />
#raster = raster2array(in_raster).get_std_dict() #стандартным словарём<br />
<br />
# изменение размерности массива (row, col) в цикле<br />
for row, col in [(253, 210), (125, 105), (300, 300)]:<br />
raster = raster2transform(raster, row, col)<br />
print raster.rows, raster.cols<br />
plt_show(raster)<br />
<br />
# сохранение растра c последней размерностью массива<br />
raster.save(out_raster)<br />
</syntaxhighlight><br />
<br />
В результате работы скрипта 3 раза отобразится окно matplotlib.pyplot с изображением массива растра в следующих размерностях: (253,210)-img 6.1, (125,105)-img 6.2, (300,300)-img 6.3. Последний массив сохранится в растр result/resize.tif (img 6.3).<br />
<br />
<br />
img 6.1 - разрешение (253x210)<br />
<br />
[[Файл:Resize_253_210.png|500px]]<br />
<br />
<br />
img 6.1 - разрешение (125x105)<br />
<br />
[[Файл:Resize_125_105.png|500px]]<br />
<br />
<br />
img 6.1 - разрешение (300x300)<br />
<br />
[[Файл:Resize_300_300.png|500px]]<br />
<br />
== Мультирастры ==<br />
<br />
Для вычисления индексов удобно работать с одноканальными растрами, представляя каждый канал как отдельно взятую сущность. Но иногда требуется создавать и работать с многоканальными конструкциями. Для это создана пара мультиканальных классов: <br />
<br />
* raster2multiarray - Класс, умеющий изменять очерёдность и количество каналов, при этом преобразующий многоканальный растр в стандартный словарь расширенного типа. От обычного он отличается тем, что в поле «array» сохраняется не двухмерный (x, y), а трёхмерный numpy-массив с индексом (bands, x, y). Для определения типа массива добавлено поле «multi_type», принимающее значения: None - с массивом уже указанного индекса и «cv» - с индексом (x, y, bands). Массив типа multi_type = «cv» аналогичен возвращаемому функцией cv2.imread. Кроме того, у raster2multiarray имеется набор методов обрезки растра, аналогичных raster2array.<br />
<br />
* multiarray2multiraster - Класс, сохраняющий многоканальный стандартный словарь в мультиканальный растр.<br />
<br />
Предлагаю два варианта использования данной пары классов, исходным растром для примеров послужит data/multi.tif:<br />
<br />
<br />
=== Изменение последовательности каналов ===<br />
<br />
Для данного примера в RGB-мультирастре переставим местами 1-й и 3-й каналы. Тестовый скрипт представлен в multi_rebands.py (list 7). <br />
<br />
list 7 - multi_rebands.py<br />
<syntaxhighlight lang="python"><br />
#!/usr/bin/python2<br />
# -*- coding: utf-8 -*-<br />
<br />
from raster_tools import raster2multiarray, multiarray2multiraster<br />
<br />
img_in = 'data/multi.tif'<br />
img_out = 'result/multi_rebands.tif'<br />
<br />
# загрузка многоканального в объект raster2multiarray<br />
# и изменение последовательности каналов 1-3, 2-2, 3-1<br />
img = raster2multiarray(img_in, 3, 2, 1)<br />
<br />
# выгрузка многоканального стандартного словаря<br />
img = img.get_std_dict()<br />
<br />
# cохранение многоканального стандартного словаря<br />
# в мультирастр<br />
multiarray2multiraster(img_out, img)<br />
</syntaxhighlight><br />
<br />
Результатом выполнения будет создание result/multi_rebands.tif (img 7).<br />
<br />
<br />
img 7 - result/multi_rebands.tif<br />
<br />
[[Файл:Multi_rebands.png|500px]]<br />
<br />
<br />
=== Обработка муьтирастров в opencv ===<br />
<br />
Теперь усложним задачу: удалим альфа-канал мультирастра, передадим массив на обработку OpenCV и сохраним полученный результат. Для этого примера необходимо установить Python-модули OpenCV версии 2 или 3. Тестовый скрипт представлен в multi_opencv.py (list 8).<br />
<br />
list 8 - multi_opencv.py<br />
<syntaxhighlight lang="python"><br />
#!/usr/bin/python2<br />
# -*- coding: utf-8 -*-<br />
<br />
import numpy as np<br />
<br />
from raster_tools import raster2multiarray, multiarray2multiraster<br />
import cv2<br />
<br />
img_in = 'data/multi.tif'<br />
img_out = 'result/multi_opencv.tif'<br />
<br />
# загрузка многоканального в объект raster2multiarray<br />
# для обработки через cv2.bilateralFilter необходима загрузка только 1,2,3 канала<br />
img = raster2multiarray(img_in, 1, 2, 3)<br />
<br />
# установка типа возвращаемого многоканального массива<br />
# в формат opencv<br />
img.multi_type = "cv"<br />
<br />
# установка типа данных возвращаемого массива как uint8<br />
# необходимо для обработки данных в opencv<br />
img.codage = np.uint8<br />
<br />
# выгрузка многоканального стандартого словаря<br />
std_dict = img.get_std_dict()<br />
<br />
# работа в OpenCV c многоканальным массивом<br />
std_dict["array"] = cv2.bilateralFilter(std_dict["array"],9,75,75)<br />
<br />
# сохранение многоканального стандартного словаря<br />
# в мультирастр<br />
multiarray2multiraster(img_out, std_dict)<br />
</syntaxhighlight><br />
<br />
Результат будет сохранён в result/multi_opencv.tif (img 8).<br />
<br />
<br />
img 8 - result/multi_opencv.tif<br />
<br />
[[Файл:Multi_opencv.png|500px]]<br />
<br />
<br />
==Работа с точками.==<br />
<br />
Неочевидный, но временами очень важный метод препарирования георастров. Постановка задачи: запись значений точек растра с определёнными координатами в CSV, БД или в виде поля в слой shp-файла. Реализация последнего примера представлена в point_data_load_array.py (list 9). Исходными данными здесь выступают: георастр result/calc.tif и векторный файл со слоем точек data/tops.shp.<br />
<br />
list 9 - point_data_load_array.py<br />
<syntaxhighlight lang="python"><br />
#!/usr/bin/python2<br />
# -*- coding: utf-8 -*-<br />
<br />
from raster_tools import raster2array<br />
import ogr<br />
import shutil<br />
<br />
raster = "result/calc.tif"<br />
<br />
# копируем тестовый shp-файл с точками в каталог result<br />
_filename = "tops"<br />
_indir = "data"<br />
_outdir = "result"<br />
for _ex in ["dbf", "prj", "qpj", "shp", "shx"]:<br />
shutil.copyfile(<br />
"{0}/{1}.{2}".format(_indir, _filename, _ex),<br />
"{0}/{1}.{2}".format(_outdir, _filename, _ex),<br />
)<br />
<br />
shp_file = "{0}/{1}.shp".format(_outdir, _filename)<br />
<br />
# загрузка растра в объект raster2array<br />
raster = raster2array(raster)<br />
<br />
# сохранение numpy массива в объекте raster2array<br />
raster.np_array_load()<br />
<br />
# загрузка shp-файла<br />
_shp = ogr.Open(shp_file, update=1)<br />
layer = _shp.GetLayerByIndex(0)<br />
<br />
# добавление в shp-файл нового поля<br />
# для записи выгруженных значений растра<br />
new_field = "POINT_DATA"<br />
if layer.FindFieldIndex(new_field, 1) < 1:<br />
field = ogr.FieldDefn(new_field, ogr.OFTReal)<br />
layer.CreateField(field)<br />
<br />
# обход циклом точек shp-файла<br />
for geometry in layer:<br />
<br />
# определение координат точки векторного файла<br />
x, _, y, _ = geometry.GetGeometryRef().GetEnvelope()<br />
<br />
# возврат значения данных точки растра<br />
# по координатам точки векторного файла<br />
raster_data = raster.get_pixel_value(float(x), float(y))<br />
<br />
# сохранение выгруженных из растра данных<br />
# в новое поле shp-файла<br />
geometry.SetField(new_field, raster_data)<br />
layer.SetFeature(geometry)<br />
</syntaxhighlight><br />
<br />
В ходе выполнения скрипта исходный векторный файл data/tops.shp копируется в result/tops.shp, в который добавляется поле «POINT_DATA». В добавленном поле сохраняются значения точек растра по координатам геометрий векторного слоя. При помощи полученного векторного файла можно будет производить интерполяцию значений поля «POINT_DATA»(пример дальше по тексту).<br />
<br />
В list 9 есть одна «хитрость»:<br />
<br />
<syntaxhighlight lang="python"><br />
# сохранение numpy массива в объекте raster2array<br />
raster.np_array_load()<br />
</syntaxhighlight><br />
<br />
Такая конструкция загружает numpy-массив растра непосредственно в экземпляр класса raster2array, что уменьшает время выполнения операций, увеличивая потребление памяти. Для сравнения: в репозитории есть аналогичный скрипт point_data_dont_load_array.py, где данная строка отсутствует. Сравнение графиков выполнения обоих скриптов в memory_profiler выглядит следующим образом: <br />
<br />
[[Файл:Point_data_load_array_mprof.png|600px]]<br />
[[Файл:Point_data_dont_load_array_mprof.png|600px]]<br />
<br />
<br />
== "Ремонт" георастров ==<br />
<br />
Иногда процедурная генерация георастров некоторыми утилитами порождает «мутантов». При их отображении в ПО ГИС проблема не заметна - так как геоданные будут правильно позиционированы. Но при выгрузке из такого георастра numpy-массива оказывается, что он зеркально отображён относительно оси X или Y. Примером утилиты с такой странной процедурной генерации является gdal_grid ([https://gis-lab.info/forum/viewtopic.php?f=30&t=20283 сообщение о проблеме на форуме]).<br />
<br />
Чтобы не быть голословными, выполним весь цикл процедур: интерполяция данных из предыдущего примера result/tops.shp при помощи gdal_grid, проверка полученного процедурного растра на «зеркальность», исправление его при помощи raster_tools. В качестве исходных данных будут использованы: result/tops.shp (источник данных для интерполяции) и result/calc.tif (для сравнения). Обработку произведём скриптом repair.py (list 11). Для работы repair.py необходим модуль modules.grid_utils и сама утилита gdal_grid. <br />
<br />
list 11 - repair.py<br />
<syntaxhighlight lang="python"><br />
#!/usr/bin/python2<br />
# -*- coding: utf-8 -*-<br />
<br />
from raster_tools import raster2array, array2raster<br />
from modules import grid_utils<br />
<br />
out_dir = "result"<br />
shp_file = "result/tops.shp"<br />
field_name = "POINT_DATA"<br />
raster_file = "result/calc.tif"<br />
grid_file = "result/{}.tif".format(field_name)<br />
repair_file = "result/repair.tif"<br />
<br />
# Интерполяция на основе shp-файла<br />
grid_utils(raster_file, shp_file, field_name, out_dir)<br />
<br />
# загрузка результатов интерполяции в raster2array<br />
grid = raster2array(grid_file)<br />
<br />
# проверка "валидности" массива в растре<br />
print grid.is_valid()<br />
<br />
# возврат "отремонтированного" растра в формате стандартного словаря<br />
grid = grid.repair()<br />
<br />
# запись исправленного растра<br />
array2raster(None, grid, repair_file)<br />
</syntaxhighlight><br />
<br />
В результате работы скрипта были созданы 2 растра: result/POINT_DATA.tif - результат работы gdal_grid и исправленный растр result/repair.tif. Сравним вывод информации о растрах через gdalinfo:<br />
<br />
<pre><br />
$ gdalinfo result/POINT_DATA.tif<br />
...<br />
Pixel Size = (0.640086707766337,0.633172658674583)<br />
Metadata:<br />
AREA_OR_POINT=Area<br />
Image Structure Metadata:<br />
INTERLEAVE=BAND<br />
Corner Coordinates:<br />
Upper Left ( 296008.694, 7137566.459) ( 40d46'52.75"E, 64d18'11.32"N)<br />
Lower Left ( 296008.694, 7137767.175) ( 40d46'51.76"E, 64d18'17.78"N)<br />
Upper Right ( 296387.625, 7137566.459) ( 40d47'20.87"E, 64d18'12.13"N)<br />
Lower Right ( 296387.625, 7137767.175) ( 40d47'19.88"E, 64d18'18.60"N)<br />
Center ( 296198.159, 7137666.817) ( 40d47' 6.31"E, 64d18'14.96"N)<br />
Band 1 Block=592x1 Type=Float64, ColorInterp=Gray<br />
</pre><br />
<br />
<pre><br />
$ gdalinfo result/repair.tif<br />
...<br />
Pixel Size = (0.640086707766337,-0.633172658674583)<br />
Metadata:<br />
AREA_OR_POINT=Area<br />
Image Structure Metadata:<br />
COMPRESSION=LZW<br />
INTERLEAVE=BAND<br />
Corner Coordinates:<br />
Upper Left ( 296008.694, 7137767.175) ( 40d46'51.76"E, 64d18'17.78"N)<br />
Lower Left ( 296008.694, 7137566.459) ( 40d46'52.75"E, 64d18'11.32"N)<br />
Upper Right ( 296387.625, 7137767.175) ( 40d47'19.88"E, 64d18'18.60"N)<br />
Lower Right ( 296387.625, 7137566.459) ( 40d47'20.87"E, 64d18'12.13"N)<br />
Center ( 296198.159, 7137666.817) ( 40d47' 6.31"E, 64d18'14.96"N)<br />
Band 1 Block=256x256 Type=Float64, ColorInterp=Gray<br />
</pre><br />
<br />
и в качестве контрольной пробы:<br />
<br />
<pre><br />
$ gdalinfo result/calc.tif<br />
...<br />
Pixel Size = (0.064274599999999,-0.064274599999986)<br />
Metadata:<br />
AREA_OR_POINT=Area<br />
Image Structure Metadata:<br />
COMPRESSION=LZW<br />
INTERLEAVE=BAND<br />
Corner Coordinates:<br />
Upper Left ( 296007.704, 7137768.705) ( 40d46'51.68"E, 64d18'17.83"N)<br />
Lower Left ( 296007.704, 7137564.891) ( 40d46'52.68"E, 64d18'11.27"N)<br />
Upper Right ( 296388.595, 7137768.705) ( 40d47'19.94"E, 64d18'18.65"N)<br />
Lower Right ( 296388.595, 7137564.891) ( 40d47'20.95"E, 64d18'12.08"N)<br />
Center ( 296198.149, 7137666.798) ( 40d47' 6.31"E, 64d18'14.96"N)<br />
Band 1 Block=256x256 Type=Float64, ColorInterp=Gray<br />
</pre><br />
<br />
Обратите внимание на второе значение Pixel Size(ось y): у растра, сгенерированного gdal_grid, это значение положительное в отличие от исходного и исправленного георастров. Что и позволяет отображать POINT_DATA.tif в ПО ГИС нормально, несмотря на наличие зеркально отображённого через ось Y массива. То же самое сообщает нам вывод метода raster2array.is_valid():<br />
<br />
<syntaxhighlight lang="python"><br />
# загрузка результатов интерполяции в raster2array<br />
grid = raster2array(grid_file)<br />
# проверка "валидности" массива в растре<br />
print grid.is_valid()<br />
</syntaxhighlight><br />
<br />
Вывод строки:<br />
<br />
<syntaxhighlight lang="python"><br />
[True, False]<br />
</syntaxhighlight><br />
<br />
Здесь ориентация растра по оси X истинное и ложное по оси Y.<br />
<br />
<br />
== Послесловие ==<br />
<br />
Заканчивая описание способов употребления raster_tools, хочется отметить, что инструмент будет и дальше находиться в состоянии постоянного развития. По мере поступления задач в него будут добавляться какие-то новые функции, при этом с сохранением старых методов применения (чтобы не потерять совместимость с уже завершёнными проектами). Статья описывает состояние raster_tools версии 0.4 - на текущий момент последняя стабильная версия.<br />
<br />
У raster_tools имеются следующие недостатки: <br />
* проблемное перепроецирование векторных слоёв-шаблонов в проекцию растра, поэтому логично использовать единую проекцию;<br />
* обрезка по shp-файлам «прибита гвоздями» к первому векторному слою;<br />
* инструмент рассчитан на формат GeoTIFF; работа с другими растровыми форматами файлов в зачаточном состоянии;<br />
* недостаточно оптимизирована работа с мультирастрами (нечасто пока требуются);</div>Александр Мурыйhttps://wiki.gis-lab.info/index.php?title=%D0%9F%D1%80%D0%B0%D0%BA%D1%82%D0%B8%D0%BA%D0%B0_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F_raster_tools&diff=26377Практика использования raster tools2018-07-30T20:29:03Z<p>Александр Мурый: /* Метод cut_shp_file. */</p>
<hr />
<div>{{Статья|Черновик}}<br />
{{Аннотация|В статье рассматриваются примеры практического использования набора растровых инструментов raster_tools.}}<br />
<br />
<br />
== Знакомство с raster_tools ==<br />
<br />
В течении продолжительного времени развиваю инструментарий для препарирования георастров. Который является набором рецептов, упрощающих взаимодействие с гибкой, но сложной структурой Python + GDAL. К сегодняшнему времени '''raster_tools''' напоминает некий «швейцарский нож» с разнообразным функционалом. <br />
Код проекта расположен на [https://github.com/oldbay/raster_tools https://github.com/oldbay/raster_tools] , самый простой способ его установки в каталоге пользователя - при помощи pip:<br />
<br />
<syntaxhighlight lang="bash"><br />
pip2.7 install --user git+https://github.com/oldbay/raster_tools<br />
</syntaxhighlight><br />
<br />
'''raster_tools''' состоит из следующих классов:<br />
<br />
'''raster2array''' - Базовый класс, отвечающий за весь функционал загрузки растра и сохранение numpy-массива выбранного канала(band). Выгруженный из растра массив может быть преобразован методами данного класса. <br />
<br />
'''array2raster(raster2array)''' - Субкласс, отвечающий за сохранение обработанного массива в файле георастра. Он также может сформировать виртуальный растр в памяти с возможностью обработки методами базового класса.<br />
<br />
'''raster2transform(raster2array)''' - Субкласс, отвечающий за трансформацию растра: изменение размерности массива и перепроецирование. Также есть поддержка методов базового класса.<br />
<br />
'''raster2calc(raster2array)''' - Класс итерационного калькулятора. Он не наследует базовый класс raster2array, но использует некоторые его методы опосредованно.<br />
<br />
'''raster2multiarray(raster2array)''' - Класс обработки мультирастров. Позволяет миксовать каналы мультиканального растра и обрабатывать его некоторыми методами raster2array.<br />
<br />
'''multiarray2multiraster''' - Класс сохранения массивов, обработанных методами raster2multiarray, в мультиканальный растр.<br />
<br />
Основной смысл существования всего этого «зоопарка» классов - это загрузка, обработка и выгрузка двухмерного numpy-массива георастров. Помимо объекта 2-х мерного numpy-массива некоторые методы raster2array позволяют выгружать данные в формате самоназванного «стандартного словаря», имеющего следующий вид:<br />
<br />
<syntaxhighlight lang="python"><br />
{<br />
"array": объект numpy.ndarray,<br />
"shape": кортеж (rows(Y), cols(X)) = numpy.ndarray.shape = (gdal.dataset.RasterYSize(), gdal.dataset.RasterXSize()),<br />
"transform": кортеж gdal.dataset.GetGeoTransform(),<br />
"projection": объект gdal.dataset.GetProjection()<br />
}<br />
</syntaxhighlight> <br />
<br />
Эта конструкция несёт в себе всю необходимую информацию для создания нового георастра, отличного от исходного.<br />
<br />
Углубляться в подробное описание структуры классов и методов raster_tools нет необходимости - достаточно полная базовая документация есть в README.rst на [https://github.com/oldbay/raster_tools https://github.com/oldbay/raster_tools]. В данной статье хотелось бы показать «боевое» применение raster_tools на практике.<br />
<br />
Для этих экспериментов был создан репозиторий с тестовыми данными, загрузить которые можно командой:<br />
<br />
<syntaxhighlight lang="bash"><br />
git clone https://gitlab.com/oldbay/raster_tools_examples.git<br />
</syntaxhighlight><br />
<br />
В ветке по умолчанию (master) расположены только тестовые скрипты и исходные данные. Чтобы заранее изучить результаты тестов, нужно перейти в ветку "result":<br />
<br />
<syntaxhighlight lang="bash"><br />
git checkout origin/result<br />
</syntaxhighlight><br />
<br />
Тестовый репозиторий состоит из:<br />
* Корень: содержит тестовые примеры, выполнение которых необходимо производить из текущего каталога;<br />
* Каталог data: содержит исходные данные для экспериментов;<br />
* Каталог logs: содержит результаты тестов расхода памяти и времени выполнения (branch result);<br />
* Каталог module: содержит дополнительные Python-модули, необходимые для выполнение некоторых тестовых скриптов(в статье не описаны);<br />
* Каталог results: содержит результаты экспериментов (branch result).<br />
<br />
<br />
== Растровый калькулятор. ==<br />
<br />
Первоначально raster_tools был частью растрового калькулятора, сейчас отдельный проект raster_calc, поэтому исторически начнём с вычислений.<br />
<br />
Пример простого растрового калькулятора представлен в calc.py (list 1) . В данном скрипте мультирастр разбирается на спектральные каналы, после чего вычисляется вегетативный индекс на основе rgb - TGI ([https://gist.github.com/merkato/0cd894f19518496171afd7425e09ed88 украдено отсюда]), Источником данных является data/multi.tif (img 1).<br />
<br />
<br />
img 1 - data/multi.tif<br />
<br />
[[Файл:Multi.png|500px]]<br />
<br />
<br />
list 1 - calc.py<br />
<syntaxhighlight lang="python"><br />
#!/usr/bin/python2<br />
# -*- coding: utf-8 -*-<br />
<br />
from raster_tools import raster2array, array2raster<br />
import numpy as np<br />
<br />
in_file = "data/multi.tif"<br />
out_file = "result/calc.tif"<br />
<br />
# загрузка каналов<br />
red = raster2array(in_file, 1)<br />
green = raster2array(in_file, 2)<br />
blue = raster2array(in_file, 3)<br />
<br />
# сохранение numpy массивов каналов в переменные.<br />
r = red()<br />
g = green()<br />
b = blue()<br />
<br />
# вычисление TGI<br />
calc = np.choose(<br />
np.not_equal(g-r+b-255.0, 0.0),<br />
(<br />
-9999.0,<br />
np.subtract(<br />
g,<br />
np.multiply(0.39, r),<br />
np.multiply(0.61, b)<br />
)<br />
)<br />
)<br />
<br />
# сохранение вычисленного массива в растр.<br />
array2raster(red, calc, out_file)<br />
</syntaxhighlight><br />
<br />
В результате выполнения calc.py создается растр result/calc.tif (img 2).<br />
<br />
<br />
img 2 - result/calc.tif<br />
<br />
[[Файл:Calc.png|500px]]<br />
<br />
<br />
У метода есть существенный минус - высокое потребление памяти. Фактически в памяти на пике потребления присутствуют сразу 4 массива растров: каналов r, g, b и вычисляемого индекса. При работе с большими георастрами это часто неприемлемо. Поэтому был разработан итерационный метод работы растрового калькулятора. Данный метод работает с растром посекторно в цикле. При таких вычислениях сильно снижается потребление памяти, но падает производительность.<br />
<br />
Итерационный калькулятор реализован в calc_iter.py (list 2). В отличии от простого калькулятора здесь формула вычислений(в формате lambda) и переменные для вычислений(в виде объектов raster2array) передаются классу raster2calc.<br />
<br />
list 2 - calc_iter.py<br />
<syntaxhighlight lang="python"><br />
#!/usr/bin/python2<br />
# -*- coding: utf-8 -*-<br />
<br />
from raster_tools import raster2array, array2raster, raster2calc<br />
import numpy as np<br />
<br />
in_file = "data/multi.tif"<br />
out_file = "result/calc_iter.tif"<br />
<br />
# загрузка каналов<br />
red = raster2array(in_file, 1)<br />
green = raster2array(in_file, 2)<br />
blue = raster2array(in_file, 3)<br />
<br />
# описание формулы вычисления TGI<br />
calc_func = lambda r,g,b: np.choose(<br />
np.not_equal(g-r+b-255.0, 0.0),<br />
(<br />
-9999.0,<br />
np.subtract(<br />
g,<br />
np.multiply(0.39, r),<br />
np.multiply(0.61, b)<br />
)<br />
)<br />
)<br />
<br />
# вычисление TGI<br />
# и сохраниение результата в формате<br />
# стандартного словаря<br />
calc = raster2calc()<br />
out = calc(<br />
calc_func,<br />
r=red,<br />
g=green,<br />
b=blue<br />
)<br />
<br />
# сохранение стандартного словаря в растр.<br />
array2raster(None, out, out_file)<br />
</syntaxhighlight><br />
<br />
raster2calc возвращает «стандартный словарь», преобразуемый классом array2raster в файл result/calc_iter.tif.<br />
<br />
Если выполнить calc.py и calc_iter.py в обёртке memory_profiler - получаем графики потребления памяти:<br />
<br />
[[Файл:Calc_mprof.png|600px]]<br />
[[Файл:Calc_iter_mprof.png|600px]]<br />
<br />
Видно что итерационный метод выигрывает в потреблении памяти, но теряет в скорости расчётов. На текущем примере потери скорости не очень существенны, но при массовой обработке небольших георастров бывает выгоднее пожертвовать памятью.<br />
<br />
== Вырезание области растра. ==<br />
<br />
Иногда растровые вычисления необходимо производить только с некоторой выделенной областью георастра. Для «фигурного» вырезания областей, ограниченных координатами точек или охватом полигонов, у raster2array есть ряд методов:<br />
* cut_area(координаты точек) - метод вырезает область растра по координатам (не менее 2-х точек);<br />
* cut_shp_file(имя shp файла) - метод вырезает растр по полигонам первого слоя shp-файла;<br />
* cut_ogr_geometry(wkt, geojson, gml, wkb) - метод вырезает растр по полигону загруженной геометрии.<br />
<br />
Все перечисленные методы возвращают «стандартный словарь», так как они изменяют размерность массива растра, делая необходимым изменение «transform» у сохраняемого георастра.<br />
<br />
Стандартный словарь применим и в растровом калькуляторе, для подобных вычислений необходимо вернуть numpy массив по ключу «array» list 1.1<br />
<br />
list 1.1<br />
<syntaxhighlight lang="python"><br />
<br />
# загрузка каналов в формате стандартного словаря<br />
# выгрузка аналогична методам обрезки, но для<br />
# всего исходного растра<br />
red = raster2array(in_file, 1).get_std_dict()<br />
green = raster2array(in_file, 2).get_std_dict()<br />
blue = raster2array(in_file, 3).get_std_dict()<br />
<br />
# сохранение numpy-массивов каналов в переменные.<br />
r = red()["array"]<br />
g = green()["array"]<br />
b = blue()["array"]<br />
<br />
# вычисление TGI (как в list 1)<br />
<br />
........<br />
<br />
<br />
# сохранение вычисленного массива в стандартный словарь:<br />
out_std_dict = red<br />
out_std_dict["array"] = calc<br />
<br />
# сохранение стандартного словаря в растр:<br />
array2raster(None, out_std_dict, out_file)<br />
</syntaxhighlight><br />
<br />
Теперь рассмотрим работу каждого метода обрезки растра.<br />
<br />
<br />
=== Метод cut_area ===<br />
<br />
Позволяет обрезать растр по набору координат точек. Применяется когда необходимо обрезать область растра по массиву точек - для дальнейшей обработки. Пример использования приведён в cut_coords.py (list 3), в качестве исходных данных используется result/calc.tif.<br />
<br />
list 3 - cut_coords.py<br />
<syntaxhighlight lang="python"><br />
#!/usr/bin/python2<br />
# -*- coding: utf-8 -*-<br />
<br />
from raster_tools import raster2array, array2raster<br />
<br />
in_file = "result/calc.tif"<br />
out_file = "result/cut_coords.tif"<br />
<br />
# вырезание области растра по координатам<br />
# и сохранение в формате стандартного словаря<br />
std_dict = raster2array(in_file).cut_area(<br />
(296153.369,7137678.937),<br />
(296203.959,7137570.986),<br />
(296256.938,7137645.476)<br />
)<br />
<br />
# сохранение стандартного словаря в растр<br />
array2raster(None, std_dict, out_file)<br />
</syntaxhighlight><br />
<br />
Результат выполнения метода сохраняется в out: result/cut_coords.tif (img 3.1 ,img 3.2)<br />
<br />
img 3.1 - отображение области на исходном растре<br />
<br />
[[Файл:Cut_coords_screenshot.png|500px]]<br />
<br />
img 3.2 - result/cut_coords.tif<br />
<br />
[[Файл:Cut_coords.png|200px]]<br />
<br />
<br />
=== Метод cut_shp_file. ===<br />
<br />
Часто необходимо обрезать растр по геометрии слоя shp файла. Пример выполнения такой задачи приведён в cut_shp.py (list 4). Помимо исходного георастра result/calc.tif , скрипт использует шаблона обрезки - data/cut.shp.<br />
<br />
list 4 - cut_shp.py<br />
<syntaxhighlight lang="python"><br />
#!/usr/bin/python2<br />
# -*- coding: utf-8 -*-<br />
<br />
from raster_tools import raster2array, array2raster<br />
<br />
shp_file = "data/cut.shp"<br />
in_file = "result/calc.tif"<br />
out_file = "result/cut_shp.tif"<br />
<br />
# вырезание области растра по полигону shp-файла<br />
# и сохранение в формате стандартного словаря<br />
std_dict = raster2array(in_file).cut_shp_file(shp_file)<br />
<br />
# сохранение стандартного словаря в растр<br />
array2raster(None, std_dict, out_file)<br />
</syntaxhighlight><br />
<br />
Результат сохраняется в георастр result/cut_shp.tif (img 4.1, img 4.2).<br />
<br />
<br />
img 4.1 - геометрия из shp-файла на фоне исходного растра<br />
<br />
[[Файл:Cut_shp_screenshot.png|300px]]<br />
<br />
<br />
img 4.2 - result/cut_shp.tif<br />
<br />
[[Файл:Cut_shp.png|200px]]<br />
<br />
<br />
Обратите внимание, что растр обрезан непосредственно по полигону, а не только по координатам охвата объекта.<br />
<br />
=== Метод cut_ogr_geometry ===<br />
<br />
Применяется, если необходима обрезка георастра по геометрия в формате wkt, geojson, gml или wkb. Наиболее типичный способ использования - это вырезание растра на по wkt-геометрии, возвращённой из PostGIS.<br />
<br />
Для выполнения примера следует провести подготовку(все примеры работы с БД приведены для Linux): <br />
* Установить PostgreSQL c расширением PostGIS. <br />
* В СУБД создать роль, из-под которой возможен логин в БД для загрузки дампа:<br />
<pre><br />
# su - postgres<br />
$ psql<br />
create role gis with login password 'gis';<br />
create database dok_example owner gis;<br />
ctrl-d<br />
</pre><br />
* Имя пользователя, пароль и имя базы можно использовать произвольные - но тогда необходимо будет изменить в list 5 переменные: dbname, dbuser, dbpass.<br />
* Создать расширение postgis в БД.<br />
<pre><br />
$ psql -d doc_example<br />
create extension postgis<br />
ctrl-d<br />
</pre><br />
* Развернуть дамп из data/crowns.sql<br />
<pre><br />
psql -d doc_example < data/crowns.sql<br />
</pre><br />
* Проверить наличие Python-модуля "psycopg2" - драйвера PostgreSQL (необходим для интерфейса запросов modules.db_interface)<br />
<br />
После успешного завершения подготовительного этапа можно выполнять тестовый пример cut_wkt.py (list 5)<br />
<br />
list 5 - cut_wkt.py<br />
<syntaxhighlight lang="python"><br />
#! /usr/bin/python2<br />
# -*- coding: utf-8 -*-<br />
<br />
from raster_tools import raster2array, array2raster<br />
from modules import psql<br />
<br />
# загрузка растра в объект raster2array<br />
in_file = "result/calc.tif"<br />
out_file = "result/cut_wkt.tif"<br />
<br />
# установка параметров подключения в БД<br />
dbhost = "localhost"<br />
dbname = "dok_example"<br />
dbuser = "gis"<br />
dbpass = "gis"<br />
<br />
# параметры поиска полигона в БД<br />
geom_table = "crowns"<br />
geom_id = 55775<br />
<br />
# запрос в БД, возвращающий полигон в формате WKT<br />
SQL = """<br />
select ST_AsText(wkb_geometry)<br />
from {0}<br />
where ogc_fid = {1}<br />
""".format(<br />
geom_table,<br />
geom_id<br />
)<br />
_psql = psql(<br />
dbhost=dbhost,<br />
dbname=dbname,<br />
dbuser=dbuser,<br />
dbpass=dbpass<br />
)<br />
_psql.sql(SQL)<br />
wkt_geom = _psql.fetchone()[0]<br />
_psql.close()<br />
<br />
# вырезание области растра по полигону WKT<br />
# и сохранение в формате стандартного словаря<br />
std_dict = raster2array(in_file).cut_ogr_geometry(wkt_geom)<br />
<br />
# сохранеие стандартного словаря в растр<br />
array2raster(None, std_dict, out_file)<br />
</syntaxhighlight><br />
<br />
В результате выполнения примера должен быть создан result/cut_wkt.tif (img 5.1, img 5.2).<br />
<br />
<br />
img 5.1 - отображение геометрии из postgis на фоне исходного растра<br />
<br />
[[Файл:Cut_wkt_screenshot.png|400px]]<br />
<br />
<br />
img 5.2 - result/cut_wkt.tif<br />
<br />
[[Файл:Cut_wkt.png|200px]]<br />
<br />
<br />
== Трансформация ==<br />
<br />
Иногда приходится вычислять индексы, или иным способом сравнивать георастры из различных источников. Входные данные могут быть в разном масштабе и соответственно иметь разную размерность массивов исследуемой области. В результате вычисление индексов средствами numpy становится невозможным, а многие другие операции затруднительны. <br />
<br />
Решить подобною проблему призван класс raster2transform. При помощи него можно изменить размерность всего массива, или только его части.<br />
Для работы тестового примера resize.py (list 6) требуется установка matplotlib и графической среды пользователя, исходным георастром является result/cut_shp.tif.<br />
<br />
list 6 - resize.py<br />
<syntaxhighlight lang="python"><br />
#! /usr/bin/python2<br />
# -*- coding: utf-8 -*-<br />
<br />
from raster_tools import raster2array, raster2transform<br />
from matplotlib import pyplot as plt<br />
<br />
in_raster = "result/cut_shp.tif"<br />
out_raster = "result/resize.tif"<br />
<br />
# вывод массива в matplotlib<br />
def plt_show(obj):<br />
plt.title("show")<br />
plt.imshow(obj.array(), cmap='gray')<br />
plt.show()<br />
<br />
# загрузка растра в raster2array<br />
raster = raster2array(in_raster)<br />
print raster.rows, raster.cols<br />
<br />
# кроме того возможна работа с:<br />
#raster = in_raster #именем растрового файла<br />
#raster = raster2array(in_raster).get_std_dict() #стандартным словарём<br />
<br />
# изменение размерности массива (row, col) в цикле<br />
for row, col in [(253, 210), (125, 105), (300, 300)]:<br />
raster = raster2transform(raster, row, col)<br />
print raster.rows, raster.cols<br />
plt_show(raster)<br />
<br />
# сохранение растра c последней размерностью массива<br />
raster.save(out_raster)<br />
</syntaxhighlight><br />
<br />
В результате работы скрипта 3 раза отобразится окно matplotlib.pyplot с изображением массива растра в следующих размерностях: (253,210)-img 6.1, (125,105)-img 6.2, (300,300)-img 6.3. Последний массив сохранится в растр result/resize.tif (img 6.3).<br />
<br />
<br />
img 6.1 - разрешение (253x210)<br />
<br />
[[Файл:Resize_253_210.png|500px]]<br />
<br />
<br />
img 6.1 - разрешение (125x105)<br />
<br />
[[Файл:Resize_125_105.png|500px]]<br />
<br />
<br />
img 6.1 - разрешение (300x300)<br />
<br />
[[Файл:Resize_300_300.png|500px]]<br />
<br />
== Мультирастры ==<br />
<br />
Для вычисления индексов удобно работать с одноканальными растрами, представляя каждый канал как отдельно взятую сущность. Но иногда требуется создавать и работать с многоканальными конструкциями. Для это создана пара мультиканальных классов: <br />
<br />
* raster2multiarray - Класс, умеющий изменять очерёдность и количество каналов, при этом преобразующий многоканальный растр в стандартный словарь расширенного типа. От обычного он отличается тем, что в поле «array» сохраняется не двухмерный (x, y), а трёхмерный numpy-массив с индексом (bands, x, y). Для определения типа массива добавлено поле «multi_type», принимающее значения: None - с массивом уже указанного индекса и «cv» - с индексом (x, y, bands). Массив типа multi_type = «cv» аналогичен возвращаемому функцией cv2.imread. Кроме того, у raster2multiarray имеется набор методов обрезки растра, аналогичных raster2array.<br />
<br />
* multiarray2multiraster - Класс, сохраняющий многоканальный стандартный словарь в мультиканальный растр.<br />
<br />
Предлагаю два варианта использования данной пары классов, исходным растром для примеров послужит data/multi.tif:<br />
<br />
<br />
=== Изменение последовательности каналов ===<br />
<br />
Для данного примера в RGB-мультирастре переставим местами 1-й и 3-й каналы. Тестовый скрипт представлен в multi_rebands.py (list 7). <br />
<br />
list 7 - multi_rebands.py<br />
<syntaxhighlight lang="python"><br />
#!/usr/bin/python2<br />
# -*- coding: utf-8 -*-<br />
<br />
from raster_tools import raster2multiarray, multiarray2multiraster<br />
<br />
img_in = 'data/multi.tif'<br />
img_out = 'result/multi_rebands.tif'<br />
<br />
# загрузка многоканального в объект raster2multiarray<br />
# и изменение последовательности каналов 1-3, 2-2, 3-1<br />
img = raster2multiarray(img_in, 3, 2, 1)<br />
<br />
# выгрузка многоканального стандартного словаря<br />
img = img.get_std_dict()<br />
<br />
# cохранение многоканального стандартного словаря<br />
# в мультирастр<br />
multiarray2multiraster(img_out, img)<br />
</syntaxhighlight><br />
<br />
Результатом выполнения будет создание result/multi_rebands.tif (img 7).<br />
<br />
<br />
img 7 - result/multi_rebands.tif<br />
<br />
[[Файл:Multi_rebands.png|500px]]<br />
<br />
<br />
=== Обработка муьтирастров в opencv ===<br />
<br />
Теперь усложним задачу: удалим альфа-канал мультирастра, передадим массив на обработку OpenCV и сохраним полученный результат. Для этого примера необходимо установить Python-модули OpenCV версии 2 или 3. Тестовый скрипт представлен в multi_opencv.py (list 8).<br />
<br />
list 8 - multi_opencv.py<br />
<syntaxhighlight lang="python"><br />
#!/usr/bin/python2<br />
# -*- coding: utf-8 -*-<br />
<br />
import numpy as np<br />
<br />
from raster_tools import raster2multiarray, multiarray2multiraster<br />
import cv2<br />
<br />
img_in = 'data/multi.tif'<br />
img_out = 'result/multi_opencv.tif'<br />
<br />
# загрузка многоканального в объект raster2multiarray<br />
# для обработки через cv2.bilateralFilter необходима загрузка только 1,2,3 канала<br />
img = raster2multiarray(img_in, 1, 2, 3)<br />
<br />
# установка типа возвращаемого многоканального массива<br />
# в формат opencv<br />
img.multi_type = "cv"<br />
<br />
# установка типа данных возвращаемого массива как uint8<br />
# необходимо для обработки данных в opencv<br />
img.codage = np.uint8<br />
<br />
# выгрузка многоканального стандартого словаря<br />
std_dict = img.get_std_dict()<br />
<br />
# работа в OpenCV c многоканальным массивом<br />
std_dict["array"] = cv2.bilateralFilter(std_dict["array"],9,75,75)<br />
<br />
# сохранение многоканального стандартного словаря<br />
# в мультирастр<br />
multiarray2multiraster(img_out, std_dict)<br />
</syntaxhighlight><br />
<br />
Результат будет сохранён в result/multi_opencv.tif (img 8).<br />
<br />
<br />
img 8 - result/multi_opencv.tif<br />
<br />
[[Файл:Multi_opencv.png|500px]]<br />
<br />
<br />
==Работа с точками.==<br />
<br />
Неочевидный, но временами очень важный метод препарирования георастров. Постановка задачи: запись значений точек растра с определёнными координатами в CSV, БД или в виде поля в слой shp-файла. Реализация последнего примера представлена в point_data_load_array.py (list 9). Исходными данными здесь выступают: георастр result/calc.tif и векторный файл со слоем точек data/tops.shp.<br />
<br />
list 9 - point_data_load_array.py<br />
<syntaxhighlight lang="python"><br />
#!/usr/bin/python2<br />
# -*- coding: utf-8 -*-<br />
<br />
from raster_tools import raster2array<br />
import ogr<br />
import shutil<br />
<br />
raster = "result/calc.tif"<br />
<br />
# копируем тестовый shp-файл с точками в каталог result<br />
_filename = "tops"<br />
_indir = "data"<br />
_outdir = "result"<br />
for _ex in ["dbf", "prj", "qpj", "shp", "shx"]:<br />
shutil.copyfile(<br />
"{0}/{1}.{2}".format(_indir, _filename, _ex),<br />
"{0}/{1}.{2}".format(_outdir, _filename, _ex),<br />
)<br />
<br />
shp_file = "{0}/{1}.shp".format(_outdir, _filename)<br />
<br />
# загрузка растра в объект raster2array<br />
raster = raster2array(raster)<br />
<br />
# сохранение numpy массива в объекте raster2array<br />
raster.np_array_load()<br />
<br />
# загрузка shp-файла<br />
_shp = ogr.Open(shp_file, update=1)<br />
layer = _shp.GetLayerByIndex(0)<br />
<br />
# добавление в shp-файл нового поля<br />
# для записи выгруженных значений растра<br />
new_field = "POINT_DATA"<br />
if layer.FindFieldIndex(new_field, 1) < 1:<br />
field = ogr.FieldDefn(new_field, ogr.OFTReal)<br />
layer.CreateField(field)<br />
<br />
# обход циклом точек shp-файла<br />
for geometry in layer:<br />
<br />
# определение координат точки векторного файла<br />
x, _, y, _ = geometry.GetGeometryRef().GetEnvelope()<br />
<br />
# возврат значения данных точки растра<br />
# по координатам точки векторного файла<br />
raster_data = raster.get_pixel_value(float(x), float(y))<br />
<br />
# сохранение выгруженных из растра данных<br />
# в новое поле shp-файла<br />
geometry.SetField(new_field, raster_data)<br />
layer.SetFeature(geometry)<br />
</syntaxhighlight><br />
<br />
В ходе выполнения скрипта исходный векторный файл data/tops.shp копируется в result/tops.shp, в который добавляется поле «POINT_DATA». В добавленном поле сохраняются значения точек растра по координатам геометрий векторного слоя. При помощи полученного векторного файла можно будет производить интерполяцию значений поля «POINT_DATA»(пример дальше по тексту).<br />
<br />
В list 9 есть одна «хитрость»:<br />
<br />
<syntaxhighlight lang="python"><br />
# сохранение numpy массива в объекте raster2array<br />
raster.np_array_load()<br />
</syntaxhighlight><br />
<br />
Такая конструкция загружает numpy-массив растра непосредственно в экземпляр класса raster2array, что уменьшает время выполнения операций, увеличивая потребление памяти. Для сравнения: в репозитории есть аналогичный скрипт point_data_dont_load_array.py, где данная строка отсутствует. Сравнение графиков выполнения обоих скриптов в memory_profiler выглядит следующим образом: <br />
<br />
[[Файл:Point_data_load_array_mprof.png|600px]]<br />
[[Файл:Point_data_dont_load_array_mprof.png|600px]]<br />
<br />
<br />
== "Ремонт" георастров ==<br />
<br />
Иногда процедурная генерация георастров некоторыми утилитами порождает «мутантов». При их отображении в ПО ГИС проблема не заметна - так как геоданные будут правильно позиционированы. Но при выгрузке из такого георастра numpy-массива оказывается, что он зеркально отображён относительно оси X или Y. Примером утилиты с такой странной процедурной генерации является gdal_grid ([https://gis-lab.info/forum/viewtopic.php?f=30&t=20283 сообщение о проблеме на форуме]).<br />
<br />
Чтобы не быть голословными, выполним весь цикл процедур: интерполяция данных из предыдущего примера result/tops.shp при помощи gdal_grid, проверка полученного процедурного растра на «зеркальность», исправление его при помощи raster_tools. В качестве исходных данных будут использованы: result/tops.shp (источник данных для интерполяции) и result/calc.tif (для сравнения). Обработку произведём скриптом repair.py (list 11). Для работы repair.py необходим модуль modules.grid_utils и сама утилита gdal_grid. <br />
<br />
list 11 - repair.py<br />
<syntaxhighlight lang="python"><br />
#!/usr/bin/python2<br />
# -*- coding: utf-8 -*-<br />
<br />
from raster_tools import raster2array, array2raster<br />
from modules import grid_utils<br />
<br />
out_dir = "result"<br />
shp_file = "result/tops.shp"<br />
field_name = "POINT_DATA"<br />
raster_file = "result/calc.tif"<br />
grid_file = "result/{}.tif".format(field_name)<br />
repair_file = "result/repair.tif"<br />
<br />
# Интерполяция на основе shp-файла<br />
grid_utils(raster_file, shp_file, field_name, out_dir)<br />
<br />
# загрузка результатов интерполяции в raster2array<br />
grid = raster2array(grid_file)<br />
<br />
# проверка "валидности" массива в растре<br />
print grid.is_valid()<br />
<br />
# возврат "отремонтированного" растра в формате стандартного словаря<br />
grid = grid.repair()<br />
<br />
# запись исправленного растра<br />
array2raster(None, grid, repair_file)<br />
</syntaxhighlight><br />
<br />
В результате работы скрипта были созданы 2 растра: result/POINT_DATA.tif - результат работы gdal_grid и исправленный растр result/repair.tif. Сравним вывод информации о растрах через gdalinfo:<br />
<br />
<pre><br />
$ gdalinfo result/POINT_DATA.tif<br />
...<br />
Pixel Size = (0.640086707766337,0.633172658674583)<br />
Metadata:<br />
AREA_OR_POINT=Area<br />
Image Structure Metadata:<br />
INTERLEAVE=BAND<br />
Corner Coordinates:<br />
Upper Left ( 296008.694, 7137566.459) ( 40d46'52.75"E, 64d18'11.32"N)<br />
Lower Left ( 296008.694, 7137767.175) ( 40d46'51.76"E, 64d18'17.78"N)<br />
Upper Right ( 296387.625, 7137566.459) ( 40d47'20.87"E, 64d18'12.13"N)<br />
Lower Right ( 296387.625, 7137767.175) ( 40d47'19.88"E, 64d18'18.60"N)<br />
Center ( 296198.159, 7137666.817) ( 40d47' 6.31"E, 64d18'14.96"N)<br />
Band 1 Block=592x1 Type=Float64, ColorInterp=Gray<br />
</pre><br />
<br />
<pre><br />
$ gdalinfo result/repair.tif<br />
...<br />
Pixel Size = (0.640086707766337,-0.633172658674583)<br />
Metadata:<br />
AREA_OR_POINT=Area<br />
Image Structure Metadata:<br />
COMPRESSION=LZW<br />
INTERLEAVE=BAND<br />
Corner Coordinates:<br />
Upper Left ( 296008.694, 7137767.175) ( 40d46'51.76"E, 64d18'17.78"N)<br />
Lower Left ( 296008.694, 7137566.459) ( 40d46'52.75"E, 64d18'11.32"N)<br />
Upper Right ( 296387.625, 7137767.175) ( 40d47'19.88"E, 64d18'18.60"N)<br />
Lower Right ( 296387.625, 7137566.459) ( 40d47'20.87"E, 64d18'12.13"N)<br />
Center ( 296198.159, 7137666.817) ( 40d47' 6.31"E, 64d18'14.96"N)<br />
Band 1 Block=256x256 Type=Float64, ColorInterp=Gray<br />
</pre><br />
<br />
и в качестве контрольной пробы:<br />
<br />
<pre><br />
$ gdalinfo result/calc.tif<br />
...<br />
Pixel Size = (0.064274599999999,-0.064274599999986)<br />
Metadata:<br />
AREA_OR_POINT=Area<br />
Image Structure Metadata:<br />
COMPRESSION=LZW<br />
INTERLEAVE=BAND<br />
Corner Coordinates:<br />
Upper Left ( 296007.704, 7137768.705) ( 40d46'51.68"E, 64d18'17.83"N)<br />
Lower Left ( 296007.704, 7137564.891) ( 40d46'52.68"E, 64d18'11.27"N)<br />
Upper Right ( 296388.595, 7137768.705) ( 40d47'19.94"E, 64d18'18.65"N)<br />
Lower Right ( 296388.595, 7137564.891) ( 40d47'20.95"E, 64d18'12.08"N)<br />
Center ( 296198.149, 7137666.798) ( 40d47' 6.31"E, 64d18'14.96"N)<br />
Band 1 Block=256x256 Type=Float64, ColorInterp=Gray<br />
</pre><br />
<br />
Обратите внимание на второе значение Pixel Size(ось y): у растра, сгенерированного gdal_grid, это значение положительное в отличие от исходного и исправленного георастров. Что и позволяет отображать POINT_DATA.tif в ПО ГИС нормально, несмотря на наличие зеркально отображённого через ось Y массива. То же самое сообщает нам вывод метода raster2array.is_valid():<br />
<br />
<syntaxhighlight lang="python"><br />
# загрузка результатов интерполяции в raster2array<br />
grid = raster2array(grid_file)<br />
# проверка "валидности" массива в растре<br />
print grid.is_valid()<br />
</syntaxhighlight><br />
<br />
Вывод строки:<br />
<br />
<syntaxhighlight lang="python"><br />
[True, False]<br />
</syntaxhighlight><br />
<br />
Здесь ориентация растра по оси X истинное и ложное по оси Y.<br />
<br />
<br />
== Послесловие ==<br />
<br />
Заканчивая описание способов употребления raster_tools, хочется отметить, что инструмент будет и дальше находиться в состоянии постоянного развития. По мере поступления задач в него будут добавляться какие-то новые функции, при этом с сохранением старых методов применения (чтобы не потерять совместимость с уже завершёнными проектами). Статья описывает состояние raster_tools версии 0.4 - на текущий момент последняя стабильная версия.<br />
<br />
У raster_tools имеются следующие недостатки: <br />
* проблемное перепроецирование векторных слоёв-шаблонов в проекцию растра, поэтому логично использовать единую проекцию;<br />
* обрезка по shp-файлам «прибита гвоздями» к первому векторному слою;<br />
* инструмент рассчитан на формат GeoTIFF; работа с другими растровыми форматами файлов в зачаточном состоянии;<br />
* недостаточно оптимизирована работа с мультирастрами (нечасто пока требуются);</div>Александр Мурыйhttps://wiki.gis-lab.info/index.php?title=%D0%9F%D1%80%D0%B0%D0%BA%D1%82%D0%B8%D0%BA%D0%B0_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F_raster_tools&diff=26376Практика использования raster tools2018-07-30T20:24:25Z<p>Александр Мурый: </p>
<hr />
<div>{{Статья|Черновик}}<br />
{{Аннотация|В статье рассматриваются примеры практического использования набора растровых инструментов raster_tools.}}<br />
<br />
<br />
== Знакомство с raster_tools ==<br />
<br />
В течении продолжительного времени развиваю инструментарий для препарирования георастров. Который является набором рецептов, упрощающих взаимодействие с гибкой, но сложной структурой Python + GDAL. К сегодняшнему времени '''raster_tools''' напоминает некий «швейцарский нож» с разнообразным функционалом. <br />
Код проекта расположен на [https://github.com/oldbay/raster_tools https://github.com/oldbay/raster_tools] , самый простой способ его установки в каталоге пользователя - при помощи pip:<br />
<br />
<syntaxhighlight lang="bash"><br />
pip2.7 install --user git+https://github.com/oldbay/raster_tools<br />
</syntaxhighlight><br />
<br />
'''raster_tools''' состоит из следующих классов:<br />
<br />
'''raster2array''' - Базовый класс, отвечающий за весь функционал загрузки растра и сохранение numpy-массива выбранного канала(band). Выгруженный из растра массив может быть преобразован методами данного класса. <br />
<br />
'''array2raster(raster2array)''' - Субкласс, отвечающий за сохранение обработанного массива в файле георастра. Он также может сформировать виртуальный растр в памяти с возможностью обработки методами базового класса.<br />
<br />
'''raster2transform(raster2array)''' - Субкласс, отвечающий за трансформацию растра: изменение размерности массива и перепроецирование. Также есть поддержка методов базового класса.<br />
<br />
'''raster2calc(raster2array)''' - Класс итерационного калькулятора. Он не наследует базовый класс raster2array, но использует некоторые его методы опосредованно.<br />
<br />
'''raster2multiarray(raster2array)''' - Класс обработки мультирастров. Позволяет миксовать каналы мультиканального растра и обрабатывать его некоторыми методами raster2array.<br />
<br />
'''multiarray2multiraster''' - Класс сохранения массивов, обработанных методами raster2multiarray, в мультиканальный растр.<br />
<br />
Основной смысл существования всего этого «зоопарка» классов - это загрузка, обработка и выгрузка двухмерного numpy-массива георастров. Помимо объекта 2-х мерного numpy-массива некоторые методы raster2array позволяют выгружать данные в формате самоназванного «стандартного словаря», имеющего следующий вид:<br />
<br />
<syntaxhighlight lang="python"><br />
{<br />
"array": объект numpy.ndarray,<br />
"shape": кортеж (rows(Y), cols(X)) = numpy.ndarray.shape = (gdal.dataset.RasterYSize(), gdal.dataset.RasterXSize()),<br />
"transform": кортеж gdal.dataset.GetGeoTransform(),<br />
"projection": объект gdal.dataset.GetProjection()<br />
}<br />
</syntaxhighlight> <br />
<br />
Эта конструкция несёт в себе всю необходимую информацию для создания нового георастра, отличного от исходного.<br />
<br />
Углубляться в подробное описание структуры классов и методов raster_tools нет необходимости - достаточно полная базовая документация есть в README.rst на [https://github.com/oldbay/raster_tools https://github.com/oldbay/raster_tools]. В данной статье хотелось бы показать «боевое» применение raster_tools на практике.<br />
<br />
Для этих экспериментов был создан репозиторий с тестовыми данными, загрузить которые можно командой:<br />
<br />
<syntaxhighlight lang="bash"><br />
git clone https://gitlab.com/oldbay/raster_tools_examples.git<br />
</syntaxhighlight><br />
<br />
В ветке по умолчанию (master) расположены только тестовые скрипты и исходные данные. Чтобы заранее изучить результаты тестов, нужно перейти в ветку "result":<br />
<br />
<syntaxhighlight lang="bash"><br />
git checkout origin/result<br />
</syntaxhighlight><br />
<br />
Тестовый репозиторий состоит из:<br />
* Корень: содержит тестовые примеры, выполнение которых необходимо производить из текущего каталога;<br />
* Каталог data: содержит исходные данные для экспериментов;<br />
* Каталог logs: содержит результаты тестов расхода памяти и времени выполнения (branch result);<br />
* Каталог module: содержит дополнительные Python-модули, необходимые для выполнение некоторых тестовых скриптов(в статье не описаны);<br />
* Каталог results: содержит результаты экспериментов (branch result).<br />
<br />
<br />
== Растровый калькулятор. ==<br />
<br />
Первоначально raster_tools был частью растрового калькулятора, сейчас отдельный проект raster_calc, поэтому исторически начнём с вычислений.<br />
<br />
Пример простого растрового калькулятора представлен в calc.py (list 1) . В данном скрипте мультирастр разбирается на спектральные каналы, после чего вычисляется вегетативный индекс на основе rgb - TGI ([https://gist.github.com/merkato/0cd894f19518496171afd7425e09ed88 украдено отсюда]), Источником данных является data/multi.tif (img 1).<br />
<br />
<br />
img 1 - data/multi.tif<br />
<br />
[[Файл:Multi.png|500px]]<br />
<br />
<br />
list 1 - calc.py<br />
<syntaxhighlight lang="python"><br />
#!/usr/bin/python2<br />
# -*- coding: utf-8 -*-<br />
<br />
from raster_tools import raster2array, array2raster<br />
import numpy as np<br />
<br />
in_file = "data/multi.tif"<br />
out_file = "result/calc.tif"<br />
<br />
# загрузка каналов<br />
red = raster2array(in_file, 1)<br />
green = raster2array(in_file, 2)<br />
blue = raster2array(in_file, 3)<br />
<br />
# сохранение numpy массивов каналов в переменные.<br />
r = red()<br />
g = green()<br />
b = blue()<br />
<br />
# вычисление TGI<br />
calc = np.choose(<br />
np.not_equal(g-r+b-255.0, 0.0),<br />
(<br />
-9999.0,<br />
np.subtract(<br />
g,<br />
np.multiply(0.39, r),<br />
np.multiply(0.61, b)<br />
)<br />
)<br />
)<br />
<br />
# сохранение вычисленного массива в растр.<br />
array2raster(red, calc, out_file)<br />
</syntaxhighlight><br />
<br />
В результате выполнения calc.py создается растр result/calc.tif (img 2).<br />
<br />
<br />
img 2 - result/calc.tif<br />
<br />
[[Файл:Calc.png|500px]]<br />
<br />
<br />
У метода есть существенный минус - высокое потребление памяти. Фактически в памяти на пике потребления присутствуют сразу 4 массива растров: каналов r, g, b и вычисляемого индекса. При работе с большими георастрами это часто неприемлемо. Поэтому был разработан итерационный метод работы растрового калькулятора. Данный метод работает с растром посекторно в цикле. При таких вычислениях сильно снижается потребление памяти, но падает производительность.<br />
<br />
Итерационный калькулятор реализован в calc_iter.py (list 2). В отличии от простого калькулятора здесь формула вычислений(в формате lambda) и переменные для вычислений(в виде объектов raster2array) передаются классу raster2calc.<br />
<br />
list 2 - calc_iter.py<br />
<syntaxhighlight lang="python"><br />
#!/usr/bin/python2<br />
# -*- coding: utf-8 -*-<br />
<br />
from raster_tools import raster2array, array2raster, raster2calc<br />
import numpy as np<br />
<br />
in_file = "data/multi.tif"<br />
out_file = "result/calc_iter.tif"<br />
<br />
# загрузка каналов<br />
red = raster2array(in_file, 1)<br />
green = raster2array(in_file, 2)<br />
blue = raster2array(in_file, 3)<br />
<br />
# описание формулы вычисления TGI<br />
calc_func = lambda r,g,b: np.choose(<br />
np.not_equal(g-r+b-255.0, 0.0),<br />
(<br />
-9999.0,<br />
np.subtract(<br />
g,<br />
np.multiply(0.39, r),<br />
np.multiply(0.61, b)<br />
)<br />
)<br />
)<br />
<br />
# вычисление TGI<br />
# и сохраниение результата в формате<br />
# стандартного словаря<br />
calc = raster2calc()<br />
out = calc(<br />
calc_func,<br />
r=red,<br />
g=green,<br />
b=blue<br />
)<br />
<br />
# сохранение стандартного словаря в растр.<br />
array2raster(None, out, out_file)<br />
</syntaxhighlight><br />
<br />
raster2calc возвращает «стандартный словарь», преобразуемый классом array2raster в файл result/calc_iter.tif.<br />
<br />
Если выполнить calc.py и calc_iter.py в обёртке memory_profiler - получаем графики потребления памяти:<br />
<br />
[[Файл:Calc_mprof.png|600px]]<br />
[[Файл:Calc_iter_mprof.png|600px]]<br />
<br />
Видно что итерационный метод выигрывает в потреблении памяти, но теряет в скорости расчётов. На текущем примере потери скорости не очень существенны, но при массовой обработке небольших георастров бывает выгоднее пожертвовать памятью.<br />
<br />
== Вырезание области растра. ==<br />
<br />
Иногда растровые вычисления необходимо производить только с некоторой выделенной областью георастра. Для «фигурного» вырезания областей, ограниченных координатами точек или охватом полигонов, у raster2array есть ряд методов:<br />
* cut_area(координаты точек) - метод вырезает область растра по координатам (не менее 2-х точек);<br />
* cut_shp_file(имя shp файла) - метод вырезает растр по полигонам первого слоя shp-файла;<br />
* cut_ogr_geometry(wkt, geojson, gml, wkb) - метод вырезает растр по полигону загруженной геометрии.<br />
<br />
Все перечисленные методы возвращают «стандартный словарь», так как они изменяют размерность массива растра, делая необходимым изменение «transform» у сохраняемого георастра.<br />
<br />
Стандартный словарь применим и в растровом калькуляторе, для подобных вычислений необходимо вернуть numpy массив по ключу «array» list 1.1<br />
<br />
list 1.1<br />
<syntaxhighlight lang="python"><br />
<br />
# загрузка каналов в формате стандартного словаря<br />
# выгрузка аналогична методам обрезки, но для<br />
# всего исходного растра<br />
red = raster2array(in_file, 1).get_std_dict()<br />
green = raster2array(in_file, 2).get_std_dict()<br />
blue = raster2array(in_file, 3).get_std_dict()<br />
<br />
# сохранение numpy-массивов каналов в переменные.<br />
r = red()["array"]<br />
g = green()["array"]<br />
b = blue()["array"]<br />
<br />
# вычисление TGI (как в list 1)<br />
<br />
........<br />
<br />
<br />
# сохранение вычисленного массива в стандартный словарь:<br />
out_std_dict = red<br />
out_std_dict["array"] = calc<br />
<br />
# сохранение стандартного словаря в растр:<br />
array2raster(None, out_std_dict, out_file)<br />
</syntaxhighlight><br />
<br />
Теперь рассмотрим работу каждого метода обрезки растра.<br />
<br />
<br />
=== Метод cut_area ===<br />
<br />
Позволяет обрезать растр по набору координат точек. Применяется когда необходимо обрезать область растра по массиву точек - для дальнейшей обработки. Пример использования приведён в cut_coords.py (list 3), в качестве исходных данных используется result/calc.tif.<br />
<br />
list 3 - cut_coords.py<br />
<syntaxhighlight lang="python"><br />
#!/usr/bin/python2<br />
# -*- coding: utf-8 -*-<br />
<br />
from raster_tools import raster2array, array2raster<br />
<br />
in_file = "result/calc.tif"<br />
out_file = "result/cut_coords.tif"<br />
<br />
# вырезание области растра по координатам<br />
# и сохранение в формате стандартного словаря<br />
std_dict = raster2array(in_file).cut_area(<br />
(296153.369,7137678.937),<br />
(296203.959,7137570.986),<br />
(296256.938,7137645.476)<br />
)<br />
<br />
# сохранение стандартного словаря в растр<br />
array2raster(None, std_dict, out_file)<br />
</syntaxhighlight><br />
<br />
Результат выполнения метода сохраняется в out: result/cut_coords.tif (img 3.1 ,img 3.2)<br />
<br />
img 3.1 - отображение области на исходном растре<br />
<br />
[[Файл:Cut_coords_screenshot.png|500px]]<br />
<br />
img 3.2 - result/cut_coords.tif<br />
<br />
[[Файл:Cut_coords.png|200px]]<br />
<br />
<br />
=== Метод cut_shp_file. ===<br />
<br />
Часто необходимо обрезать растр по геометрии слоя shp файла. Пример выполнения такой задачи приведён в cut_shp.py (list 4). Помимо исходного георастра result/calc.tif , скрипт использует шаблона обрезки - data/cut.shp.<br />
<br />
list 4 - cut_shp.py<br />
<syntaxhighlight lang="python"><br />
#!/usr/bin/python2<br />
# -*- coding: utf-8 -*-<br />
<br />
from raster_tools import raster2array, array2raster<br />
<br />
shp_file = "data/cut.shp"<br />
in_file = "result/calc.tif"<br />
out_file = "result/cut_shp.tif"<br />
<br />
# вырезание области растра по полигону shp-файла<br />
# и сохранение в формате стандартного словаря<br />
std_dict = raster2array(in_file).cut_shp_file(shp_file)<br />
<br />
# сохранение стандартного словаря в растр<br />
array2raster(None, std_dict, out_file)<br />
</syntaxhighlight><br />
<br />
Результат сохраняется в георастр result/cut_shp.tif (img 4.1, img 4.2).<br />
<br />
<br />
img 4.1 - геометрия из shp-файла на фоне исходного растра<br />
<br />
[[Файл:Cut_shp_screenshot.png|300px]]<br />
<br />
<br />
img 4.2 - result/cut_shp.tif<br />
<br />
[[Файл:Cut_shp.png|200px]]<br />
<br />
<br />
Обратите внимание что растр обрезан непосредственно по полигону, а не только по координатам охвата объекта.<br />
<br />
<br />
=== Метод cut_ogr_geometry ===<br />
<br />
Применяется, если необходима обрезка георастра по геометрия в формате wkt, geojson, gml или wkb. Наиболее типичный способ использования - это вырезание растра на по wkt-геометрии, возвращённой из PostGIS.<br />
<br />
Для выполнения примера следует провести подготовку(все примеры работы с БД приведены для Linux): <br />
* Установить PostgreSQL c расширением PostGIS. <br />
* В СУБД создать роль, из-под которой возможен логин в БД для загрузки дампа:<br />
<pre><br />
# su - postgres<br />
$ psql<br />
create role gis with login password 'gis';<br />
create database dok_example owner gis;<br />
ctrl-d<br />
</pre><br />
* Имя пользователя, пароль и имя базы можно использовать произвольные - но тогда необходимо будет изменить в list 5 переменные: dbname, dbuser, dbpass.<br />
* Создать расширение postgis в БД.<br />
<pre><br />
$ psql -d doc_example<br />
create extension postgis<br />
ctrl-d<br />
</pre><br />
* Развернуть дамп из data/crowns.sql<br />
<pre><br />
psql -d doc_example < data/crowns.sql<br />
</pre><br />
* Проверить наличие Python-модуля "psycopg2" - драйвера PostgreSQL (необходим для интерфейса запросов modules.db_interface)<br />
<br />
После успешного завершения подготовительного этапа можно выполнять тестовый пример cut_wkt.py (list 5)<br />
<br />
list 5 - cut_wkt.py<br />
<syntaxhighlight lang="python"><br />
#! /usr/bin/python2<br />
# -*- coding: utf-8 -*-<br />
<br />
from raster_tools import raster2array, array2raster<br />
from modules import psql<br />
<br />
# загрузка растра в объект raster2array<br />
in_file = "result/calc.tif"<br />
out_file = "result/cut_wkt.tif"<br />
<br />
# установка параметров подключения в БД<br />
dbhost = "localhost"<br />
dbname = "dok_example"<br />
dbuser = "gis"<br />
dbpass = "gis"<br />
<br />
# параметры поиска полигона в БД<br />
geom_table = "crowns"<br />
geom_id = 55775<br />
<br />
# запрос в БД, возвращающий полигон в формате WKT<br />
SQL = """<br />
select ST_AsText(wkb_geometry)<br />
from {0}<br />
where ogc_fid = {1}<br />
""".format(<br />
geom_table,<br />
geom_id<br />
)<br />
_psql = psql(<br />
dbhost=dbhost,<br />
dbname=dbname,<br />
dbuser=dbuser,<br />
dbpass=dbpass<br />
)<br />
_psql.sql(SQL)<br />
wkt_geom = _psql.fetchone()[0]<br />
_psql.close()<br />
<br />
# вырезание области растра по полигону WKT<br />
# и сохранение в формате стандартного словаря<br />
std_dict = raster2array(in_file).cut_ogr_geometry(wkt_geom)<br />
<br />
# сохранеие стандартного словаря в растр<br />
array2raster(None, std_dict, out_file)<br />
</syntaxhighlight><br />
<br />
В результате выполнения примера должен быть создан result/cut_wkt.tif (img 5.1, img 5.2).<br />
<br />
<br />
img 5.1 - отображение геометрии из postgis на фоне исходного растра<br />
<br />
[[Файл:Cut_wkt_screenshot.png|400px]]<br />
<br />
<br />
img 5.2 - result/cut_wkt.tif<br />
<br />
[[Файл:Cut_wkt.png|200px]]<br />
<br />
<br />
== Трансформация ==<br />
<br />
Иногда приходится вычислять индексы, или иным способом сравнивать георастры из различных источников. Входные данные могут быть в разном масштабе и соответственно иметь разную размерность массивов исследуемой области. В результате вычисление индексов средствами numpy становится невозможным, а многие другие операции затруднительны. <br />
<br />
Решить подобною проблему призван класс raster2transform. При помощи него можно изменить размерность всего массива, или только его части.<br />
Для работы тестового примера resize.py (list 6) требуется установка matplotlib и графической среды пользователя, исходным георастром является result/cut_shp.tif.<br />
<br />
list 6 - resize.py<br />
<syntaxhighlight lang="python"><br />
#! /usr/bin/python2<br />
# -*- coding: utf-8 -*-<br />
<br />
from raster_tools import raster2array, raster2transform<br />
from matplotlib import pyplot as plt<br />
<br />
in_raster = "result/cut_shp.tif"<br />
out_raster = "result/resize.tif"<br />
<br />
# вывод массива в matplotlib<br />
def plt_show(obj):<br />
plt.title("show")<br />
plt.imshow(obj.array(), cmap='gray')<br />
plt.show()<br />
<br />
# загрузка растра в raster2array<br />
raster = raster2array(in_raster)<br />
print raster.rows, raster.cols<br />
<br />
# кроме того возможна работа с:<br />
#raster = in_raster #именем растрового файла<br />
#raster = raster2array(in_raster).get_std_dict() #стандартным словарём<br />
<br />
# изменение размерности массива (row, col) в цикле<br />
for row, col in [(253, 210), (125, 105), (300, 300)]:<br />
raster = raster2transform(raster, row, col)<br />
print raster.rows, raster.cols<br />
plt_show(raster)<br />
<br />
# сохранение растра c последней размерностью массива<br />
raster.save(out_raster)<br />
</syntaxhighlight><br />
<br />
В результате работы скрипта 3 раза отобразится окно matplotlib.pyplot с изображением массива растра в следующих размерностях: (253,210)-img 6.1, (125,105)-img 6.2, (300,300)-img 6.3. Последний массив сохранится в растр result/resize.tif (img 6.3).<br />
<br />
<br />
img 6.1 - разрешение (253x210)<br />
<br />
[[Файл:Resize_253_210.png|500px]]<br />
<br />
<br />
img 6.1 - разрешение (125x105)<br />
<br />
[[Файл:Resize_125_105.png|500px]]<br />
<br />
<br />
img 6.1 - разрешение (300x300)<br />
<br />
[[Файл:Resize_300_300.png|500px]]<br />
<br />
== Мультирастры ==<br />
<br />
Для вычисления индексов удобно работать с одноканальными растрами, представляя каждый канал как отдельно взятую сущность. Но иногда требуется создавать и работать с многоканальными конструкциями. Для это создана пара мультиканальных классов: <br />
<br />
* raster2multiarray - Класс, умеющий изменять очерёдность и количество каналов, при этом преобразующий многоканальный растр в стандартный словарь расширенного типа. От обычного он отличается тем, что в поле «array» сохраняется не двухмерный (x, y), а трёхмерный numpy-массив с индексом (bands, x, y). Для определения типа массива добавлено поле «multi_type», принимающее значения: None - с массивом уже указанного индекса и «cv» - с индексом (x, y, bands). Массив типа multi_type = «cv» аналогичен возвращаемому функцией cv2.imread. Кроме того, у raster2multiarray имеется набор методов обрезки растра, аналогичных raster2array.<br />
<br />
* multiarray2multiraster - Класс, сохраняющий многоканальный стандартный словарь в мультиканальный растр.<br />
<br />
Предлагаю два варианта использования данной пары классов, исходным растром для примеров послужит data/multi.tif:<br />
<br />
<br />
=== Изменение последовательности каналов ===<br />
<br />
Для данного примера в RGB-мультирастре переставим местами 1-й и 3-й каналы. Тестовый скрипт представлен в multi_rebands.py (list 7). <br />
<br />
list 7 - multi_rebands.py<br />
<syntaxhighlight lang="python"><br />
#!/usr/bin/python2<br />
# -*- coding: utf-8 -*-<br />
<br />
from raster_tools import raster2multiarray, multiarray2multiraster<br />
<br />
img_in = 'data/multi.tif'<br />
img_out = 'result/multi_rebands.tif'<br />
<br />
# загрузка многоканального в объект raster2multiarray<br />
# и изменение последовательности каналов 1-3, 2-2, 3-1<br />
img = raster2multiarray(img_in, 3, 2, 1)<br />
<br />
# выгрузка многоканального стандартного словаря<br />
img = img.get_std_dict()<br />
<br />
# cохранение многоканального стандартного словаря<br />
# в мультирастр<br />
multiarray2multiraster(img_out, img)<br />
</syntaxhighlight><br />
<br />
Результатом выполнения будет создание result/multi_rebands.tif (img 7).<br />
<br />
<br />
img 7 - result/multi_rebands.tif<br />
<br />
[[Файл:Multi_rebands.png|500px]]<br />
<br />
<br />
=== Обработка муьтирастров в opencv ===<br />
<br />
Теперь усложним задачу: удалим альфа-канал мультирастра, передадим массив на обработку OpenCV и сохраним полученный результат. Для этого примера необходимо установить Python-модули OpenCV версии 2 или 3. Тестовый скрипт представлен в multi_opencv.py (list 8).<br />
<br />
list 8 - multi_opencv.py<br />
<syntaxhighlight lang="python"><br />
#!/usr/bin/python2<br />
# -*- coding: utf-8 -*-<br />
<br />
import numpy as np<br />
<br />
from raster_tools import raster2multiarray, multiarray2multiraster<br />
import cv2<br />
<br />
img_in = 'data/multi.tif'<br />
img_out = 'result/multi_opencv.tif'<br />
<br />
# загрузка многоканального в объект raster2multiarray<br />
# для обработки через cv2.bilateralFilter необходима загрузка только 1,2,3 канала<br />
img = raster2multiarray(img_in, 1, 2, 3)<br />
<br />
# установка типа возвращаемого многоканального массива<br />
# в формат opencv<br />
img.multi_type = "cv"<br />
<br />
# установка типа данных возвращаемого массива как uint8<br />
# необходимо для обработки данных в opencv<br />
img.codage = np.uint8<br />
<br />
# выгрузка многоканального стандартого словаря<br />
std_dict = img.get_std_dict()<br />
<br />
# работа в OpenCV c многоканальным массивом<br />
std_dict["array"] = cv2.bilateralFilter(std_dict["array"],9,75,75)<br />
<br />
# сохранение многоканального стандартного словаря<br />
# в мультирастр<br />
multiarray2multiraster(img_out, std_dict)<br />
</syntaxhighlight><br />
<br />
Результат будет сохранён в result/multi_opencv.tif (img 8).<br />
<br />
<br />
img 8 - result/multi_opencv.tif<br />
<br />
[[Файл:Multi_opencv.png|500px]]<br />
<br />
<br />
==Работа с точками.==<br />
<br />
Неочевидный, но временами очень важный метод препарирования георастров. Постановка задачи: запись значений точек растра с определёнными координатами в CSV, БД или в виде поля в слой shp-файла. Реализация последнего примера представлена в point_data_load_array.py (list 9). Исходными данными здесь выступают: георастр result/calc.tif и векторный файл со слоем точек data/tops.shp.<br />
<br />
list 9 - point_data_load_array.py<br />
<syntaxhighlight lang="python"><br />
#!/usr/bin/python2<br />
# -*- coding: utf-8 -*-<br />
<br />
from raster_tools import raster2array<br />
import ogr<br />
import shutil<br />
<br />
raster = "result/calc.tif"<br />
<br />
# копируем тестовый shp-файл с точками в каталог result<br />
_filename = "tops"<br />
_indir = "data"<br />
_outdir = "result"<br />
for _ex in ["dbf", "prj", "qpj", "shp", "shx"]:<br />
shutil.copyfile(<br />
"{0}/{1}.{2}".format(_indir, _filename, _ex),<br />
"{0}/{1}.{2}".format(_outdir, _filename, _ex),<br />
)<br />
<br />
shp_file = "{0}/{1}.shp".format(_outdir, _filename)<br />
<br />
# загрузка растра в объект raster2array<br />
raster = raster2array(raster)<br />
<br />
# сохранение numpy массива в объекте raster2array<br />
raster.np_array_load()<br />
<br />
# загрузка shp-файла<br />
_shp = ogr.Open(shp_file, update=1)<br />
layer = _shp.GetLayerByIndex(0)<br />
<br />
# добавление в shp-файл нового поля<br />
# для записи выгруженных значений растра<br />
new_field = "POINT_DATA"<br />
if layer.FindFieldIndex(new_field, 1) < 1:<br />
field = ogr.FieldDefn(new_field, ogr.OFTReal)<br />
layer.CreateField(field)<br />
<br />
# обход циклом точек shp-файла<br />
for geometry in layer:<br />
<br />
# определение координат точки векторного файла<br />
x, _, y, _ = geometry.GetGeometryRef().GetEnvelope()<br />
<br />
# возврат значения данных точки растра<br />
# по координатам точки векторного файла<br />
raster_data = raster.get_pixel_value(float(x), float(y))<br />
<br />
# сохранение выгруженных из растра данных<br />
# в новое поле shp-файла<br />
geometry.SetField(new_field, raster_data)<br />
layer.SetFeature(geometry)<br />
</syntaxhighlight><br />
<br />
В ходе выполнения скрипта исходный векторный файл data/tops.shp копируется в result/tops.shp, в который добавляется поле «POINT_DATA». В добавленном поле сохраняются значения точек растра по координатам геометрий векторного слоя. При помощи полученного векторного файла можно будет производить интерполяцию значений поля «POINT_DATA»(пример дальше по тексту).<br />
<br />
В list 9 есть одна «хитрость»:<br />
<br />
<syntaxhighlight lang="python"><br />
# сохранение numpy массива в объекте raster2array<br />
raster.np_array_load()<br />
</syntaxhighlight><br />
<br />
Такая конструкция загружает numpy-массив растра непосредственно в экземпляр класса raster2array, что уменьшает время выполнения операций, увеличивая потребление памяти. Для сравнения: в репозитории есть аналогичный скрипт point_data_dont_load_array.py, где данная строка отсутствует. Сравнение графиков выполнения обоих скриптов в memory_profiler выглядит следующим образом: <br />
<br />
[[Файл:Point_data_load_array_mprof.png|600px]]<br />
[[Файл:Point_data_dont_load_array_mprof.png|600px]]<br />
<br />
<br />
== "Ремонт" георастров ==<br />
<br />
Иногда процедурная генерация георастров некоторыми утилитами порождает «мутантов». При их отображении в ПО ГИС проблема не заметна - так как геоданные будут правильно позиционированы. Но при выгрузке из такого георастра numpy-массива оказывается, что он зеркально отображён относительно оси X или Y. Примером утилиты с такой странной процедурной генерации является gdal_grid ([https://gis-lab.info/forum/viewtopic.php?f=30&t=20283 сообщение о проблеме на форуме]).<br />
<br />
Чтобы не быть голословными, выполним весь цикл процедур: интерполяция данных из предыдущего примера result/tops.shp при помощи gdal_grid, проверка полученного процедурного растра на «зеркальность», исправление его при помощи raster_tools. В качестве исходных данных будут использованы: result/tops.shp (источник данных для интерполяции) и result/calc.tif (для сравнения). Обработку произведём скриптом repair.py (list 11). Для работы repair.py необходим модуль modules.grid_utils и сама утилита gdal_grid. <br />
<br />
list 11 - repair.py<br />
<syntaxhighlight lang="python"><br />
#!/usr/bin/python2<br />
# -*- coding: utf-8 -*-<br />
<br />
from raster_tools import raster2array, array2raster<br />
from modules import grid_utils<br />
<br />
out_dir = "result"<br />
shp_file = "result/tops.shp"<br />
field_name = "POINT_DATA"<br />
raster_file = "result/calc.tif"<br />
grid_file = "result/{}.tif".format(field_name)<br />
repair_file = "result/repair.tif"<br />
<br />
# Интерполяция на основе shp-файла<br />
grid_utils(raster_file, shp_file, field_name, out_dir)<br />
<br />
# загрузка результатов интерполяции в raster2array<br />
grid = raster2array(grid_file)<br />
<br />
# проверка "валидности" массива в растре<br />
print grid.is_valid()<br />
<br />
# возврат "отремонтированного" растра в формате стандартного словаря<br />
grid = grid.repair()<br />
<br />
# запись исправленного растра<br />
array2raster(None, grid, repair_file)<br />
</syntaxhighlight><br />
<br />
В результате работы скрипта были созданы 2 растра: result/POINT_DATA.tif - результат работы gdal_grid и исправленный растр result/repair.tif. Сравним вывод информации о растрах через gdalinfo:<br />
<br />
<pre><br />
$ gdalinfo result/POINT_DATA.tif<br />
...<br />
Pixel Size = (0.640086707766337,0.633172658674583)<br />
Metadata:<br />
AREA_OR_POINT=Area<br />
Image Structure Metadata:<br />
INTERLEAVE=BAND<br />
Corner Coordinates:<br />
Upper Left ( 296008.694, 7137566.459) ( 40d46'52.75"E, 64d18'11.32"N)<br />
Lower Left ( 296008.694, 7137767.175) ( 40d46'51.76"E, 64d18'17.78"N)<br />
Upper Right ( 296387.625, 7137566.459) ( 40d47'20.87"E, 64d18'12.13"N)<br />
Lower Right ( 296387.625, 7137767.175) ( 40d47'19.88"E, 64d18'18.60"N)<br />
Center ( 296198.159, 7137666.817) ( 40d47' 6.31"E, 64d18'14.96"N)<br />
Band 1 Block=592x1 Type=Float64, ColorInterp=Gray<br />
</pre><br />
<br />
<pre><br />
$ gdalinfo result/repair.tif<br />
...<br />
Pixel Size = (0.640086707766337,-0.633172658674583)<br />
Metadata:<br />
AREA_OR_POINT=Area<br />
Image Structure Metadata:<br />
COMPRESSION=LZW<br />
INTERLEAVE=BAND<br />
Corner Coordinates:<br />
Upper Left ( 296008.694, 7137767.175) ( 40d46'51.76"E, 64d18'17.78"N)<br />
Lower Left ( 296008.694, 7137566.459) ( 40d46'52.75"E, 64d18'11.32"N)<br />
Upper Right ( 296387.625, 7137767.175) ( 40d47'19.88"E, 64d18'18.60"N)<br />
Lower Right ( 296387.625, 7137566.459) ( 40d47'20.87"E, 64d18'12.13"N)<br />
Center ( 296198.159, 7137666.817) ( 40d47' 6.31"E, 64d18'14.96"N)<br />
Band 1 Block=256x256 Type=Float64, ColorInterp=Gray<br />
</pre><br />
<br />
и в качестве контрольной пробы:<br />
<br />
<pre><br />
$ gdalinfo result/calc.tif<br />
...<br />
Pixel Size = (0.064274599999999,-0.064274599999986)<br />
Metadata:<br />
AREA_OR_POINT=Area<br />
Image Structure Metadata:<br />
COMPRESSION=LZW<br />
INTERLEAVE=BAND<br />
Corner Coordinates:<br />
Upper Left ( 296007.704, 7137768.705) ( 40d46'51.68"E, 64d18'17.83"N)<br />
Lower Left ( 296007.704, 7137564.891) ( 40d46'52.68"E, 64d18'11.27"N)<br />
Upper Right ( 296388.595, 7137768.705) ( 40d47'19.94"E, 64d18'18.65"N)<br />
Lower Right ( 296388.595, 7137564.891) ( 40d47'20.95"E, 64d18'12.08"N)<br />
Center ( 296198.149, 7137666.798) ( 40d47' 6.31"E, 64d18'14.96"N)<br />
Band 1 Block=256x256 Type=Float64, ColorInterp=Gray<br />
</pre><br />
<br />
Обратите внимание на второе значение Pixel Size(ось y): у растра, сгенерированного gdal_grid, это значение положительное в отличие от исходного и исправленного георастров. Что и позволяет отображать POINT_DATA.tif в ПО ГИС нормально, несмотря на наличие зеркально отображённого через ось Y массива. То же самое сообщает нам вывод метода raster2array.is_valid():<br />
<br />
<syntaxhighlight lang="python"><br />
# загрузка результатов интерполяции в raster2array<br />
grid = raster2array(grid_file)<br />
# проверка "валидности" массива в растре<br />
print grid.is_valid()<br />
</syntaxhighlight><br />
<br />
Вывод строки:<br />
<br />
<syntaxhighlight lang="python"><br />
[True, False]<br />
</syntaxhighlight><br />
<br />
Здесь ориентация растра по оси X истинное и ложное по оси Y.<br />
<br />
<br />
== Послесловие ==<br />
<br />
Заканчивая описание способов употребления raster_tools, хочется отметить, что инструмент будет и дальше находиться в состоянии постоянного развития. По мере поступления задач в него будут добавляться какие-то новые функции, при этом с сохранением старых методов применения (чтобы не потерять совместимость с уже завершёнными проектами). Статья описывает состояние raster_tools версии 0.4 - на текущий момент последняя стабильная версия.<br />
<br />
У raster_tools имеются следующие недостатки: <br />
* проблемное перепроецирование векторных слоёв-шаблонов в проекцию растра, поэтому логично использовать единую проекцию;<br />
* обрезка по shp-файлам «прибита гвоздями» к первому векторному слою;<br />
* инструмент рассчитан на формат GeoTIFF; работа с другими растровыми форматами файлов в зачаточном состоянии;<br />
* недостаточно оптимизирована работа с мультирастрами (нечасто пока требуются);</div>Александр Мурыйhttps://wiki.gis-lab.info/index.php?title=%D0%9E%D1%86%D0%B5%D0%BD%D0%BA%D0%B0_%D1%82%D0%B5%D0%BC%D0%BF%D0%B5%D1%80%D0%B0%D1%82%D1%83%D1%80%D1%8B_%D0%BF%D0%BE%D0%B2%D0%B5%D1%80%D1%85%D0%BD%D0%BE%D1%81%D1%82%D0%B8_%D0%B8%D0%B7_%D1%81%D0%BD%D0%B8%D0%BC%D0%BA%D0%B0_Landsat-8_%D0%BF%D1%80%D0%B8_%D0%BF%D0%BE%D0%BC%D0%BE%D1%89%D0%B8_Land_Surface_Temperature_QGIS_Plugin&diff=26371Оценка температуры поверхности из снимка Landsat-8 при помощи Land Surface Temperature QGIS Plugin2018-07-05T07:01:07Z<p>Александр Мурый: </p>
<hr />
<div>{{Статья|Черновик}}<br />
<br />
В статье описан алгоритм оценки температуры земной поверхности (LST) по снимку Landsat-8 полуавтоматическим способом при помощи плагина QGIS ''Land Surface Temperature''. А также в целом рассмотрен процесс оценки температуры земной поверхности по данным Дистанционного зондирования Земли (ДЗЗ). По важным вопросам приводится спектр мнений специалистов, из которых делаются выводы.<br />
<br />
=== 1.Какие данные ДЗЗ о температуре земной поверхности существуют в открытом доступе? ===<br />
Температуру земной поверхности можно оценить по термальным спектральным каналам сенсора, то есть таким каналам, которые снимают земную поверхность в диапазоне Thermal Infrared Radiation (10-15 микрометров). Соответственно, эти каналы называются по-английски Thermal bands, TIRS Bands или Thermal Infrared bands. <br />
<br /><br />
Подробнее можно узнать в статье '''[http://wiki.gis-lab.info/w/ДЗЗ_для_экологических_задач._Часть_1:_Введение_в_теорию_ДЗЗ ДЗЗ для экологических задач. Часть 1: Введение в теорию ДЗЗ]''' (рис. 3)<br />
<br /><br />
<br /><br />
Термальные каналы имеются лишь у некоторых сенсоров. Поэтому немного и данных ДЗЗ, по которым можно оценить температуру земной поверхности за наше время и за прошлый период. В целом, существующие данные можно разделить на два класса масштаба (в скобках указано число метров поверхности в 1 пикселе снимка термального канала и годы получения данных):<br />
<br /><br />
<br /><br />
<br />
===== Данные ДЗЗ температуры земной поверхности мелкого масштаба =====<br />
* MODIS (500 и 1000 м, 2000 – н.в.), сенсоры спутников Terra, Aqua<br />
<br />
Температуру земной поверхности можно получить в готовом виде через продукты ''Thermal Anomalies and Fire''. <br />
Подробнее: [https://lpdaac.usgs.gov/dataset_discovery/modis/modis_products_table MODIS Products Table]<br />
<br />
* Suomi NPP (650 м, 2011 – н.в.)<br />
Подробнее: [http://gis-lab.info/qa/npp-suomi.html Suomi NPP: краткая характеристика]<br />
<br /><br />
<br />
===== Данные ДЗЗ температуры земной поверхности среднего масштаба =====<br />
* ASTER (90 м, 2000 – н.в.), сенсор спутника Terra<br />
Температуру земной поверхности можно получить в готовом виде через продукт ''AST_08 (ASTER L2 Surface Temperature V003)''. Подробнее: <br />
[https://lpdaac.usgs.gov/dataset_discovery/aster/aster_products_table/ast_08_v003 AST_08: ASTER Surface Kinetic Temperature V003]<br />
<br />
* Данные спутников Landsat:<br />
Landsat-8 (100 м, 2013 – н.в.)<br /><br />
Landsat-7 (60 м, 1999- н.в.)<br /><br />
Landsat-5 (120 м, 1984-2013)<br /><br />
Landsat-4 (120 м, 1982-1993)<br /><br />
<br /><br />
<br />
Для научных и практических исследований регионального масштаба наибольшую ценность представляют данные температуры земной поверхности среднего масштаба. <br />
На время написания статьи, Landsat-8 дает наилучшие такие данные. В целом данные о температуре земной поверхности можно получить с 1982 года (год запуска Landsat-4). <br />
<br />
В последние годы сенсор ASTER по неясным причинам значительно (вероятно, во много раз) сократил объем снимков. На многие или на все участки земной поверхности уже нельзя получить данные за любой интересующий период, наблюдаются провалы в несколько лет без снимков вообще. До 2013 года такого за данными ASTER не замечалось. <br />
<br />
Снимки Landsat-7 с 31 мая 2003 г идут с дефектом, который не отражается на точности данных, но значительно сокращает их объем и снижает удобство их использования (усложняет обработку). Однако, разрешение термального канала Landsat-7 (60 м) при этом остается самым высоким из когда-либо существовавших в открытом доступе. <br />
<br />
В статье мы рассмотрим расчет данных температуры земной поверхности по снимку Landsat-8 при использовании ''Land Surface Temperature'' QGIS plugin. Этот же плагин и изложенные аспекты оценки температуры земной поверхности можно применять и при расчете температуры по Landsat-4, Landsat-5 и Landsat-7 (в последнем не забывая о техническом дефекте, решение которого данная статья не рассматривает).<br />
<br />
=== 2.Факторы, которые необходимо учитывать при оценке температуры земной поверхности по снимкам Landsat===<br />
При оценке температуры земной поверхности по данным Landsat нужно учитывать два фактора, которые определяют ''разнообразие'' методов расчета температуры и, соответственно, различающийся ''итоговый результат'' и ''точность'' оценки. Это:<br />
<br /><br />
<br />
* '''Коррекция снимков Landsat'''<br />
* '''Учет разного характера излучательной способности земной поверхности (emissivity)'''<br />
<br />
Эти факторы мы рассмотрим в ходе соответствующих шагов по построению карты температуры.<br />
<br />
Из-за наличия этих факторов не существует единственного алгоритма расчета температуры земной поверхности по снимкам Landsat, на который просто можно дать ссылку. <br /><br />
В принципе, температуру земной поверхности по Landsat-8 (и Landsat-4,5,7) можно рассчитать в любом растровом калькуляторе. Именно это предлагают делать многочисленные руководства (видео-мануалы, блоги) легко находимые в Google по поисковым словам. Однако, если вы хотите воспользоваться таким руководством, разберитесь в сути формул, которые они используют, поскольку разные расчеты дают разные результаты и точность оценки температуры. Иначе говоря, разные пособия предлагают разные методы расчета температуры по снимкам Landsat. Пользователям при этом не сообщается о заложенном в данном пособии принципе и о том, что есть другие варианты расчетов.<br />
<br />
=== 3. Алгоритм вычисления температуры земной поверхности из снимка Landsat-8 при помощи Land Surface Temperature QGIS Plugin ===<br />
<br />
==== Шаг 1. Скачиваем снимок ==== <br />
<br />
Скачиваем снимок Landsat-8 на интересующую территорию и дату. Желательно выбирать снимки с отсутствием облаков или с небольшой (до 3-5%) облачностью, или же следить (через функции предпросмотра), чтобы облака не покрывали территорию интереса. Ссылки на некоторые популярные ресурсы выбора и скачивания снимков Landsat-8 представлены ниже:<br />
<br />
* EarthExplorer https://earthexplorer.usgs.gov<br />
* GloVis https://glovis.usgs.gov/app?fullscreen=1<br />
* Landsat on AWS https://landsatonaws.com<br />
* Данные Landsat-8, Sentinel-2, CBERS-4 (удобный предпросмотр) https://search.remotepixel.ca/#3/40/-70.5<br />
* Libra: https://libra.developmentseed.org<br />
<br /><br />
Мы будем строить карту по данным снимка '''[https://landsatonaws.com/L8/220/076/LC08_L1TP_220076_20180501_20180501_01_RT LC08_L1TP_220076_20180501_20180501_01_RT]''', который можно скачать или по данной ссылке, или, например, в [https://earthexplorer.usgs.gov EarthExplorer], пройдя по '''Data Sets > Landsat > Landsat Collection 1 Level-1'''<br />
<br />
Информация о дате и точном времени съемки можно найти в текстовом файле MLT, который приложен к общему набору скачиваемых данных. <br /><br />
Открываем его в Блокноте и видим, что снимок сделан '''1 мая 2018 года в 13:09:42'''.<br />
<br />
<pre>DATE_ACQUIRED = 2018-05-01, SCENE_CENTER_TIME = "13:09:42.0926400Z"</pre><br />
<br />
Время съемки указано в GMT (Greenwich Mean Time), о чем также говорит буква Z ("Zulu time", аналог GMT). Подробнее – [https://landsat.usgs.gov/how-can-i-find-acquisition-time-image здесь].<br />
<br />
==== Шаг 2. Узнаем про коррекции снимков Landsat ==== <br />
<br />
Рассмотрим первый "усложняющий жизнь" фактор – коррекции снимков Landsat.<br />
<br />
По ранним данным Landsat мы привыкли, что снимки Landsat поступают к пользователю в сыром виде и их нужно долго и страшно как-то корректировать. Однако с 2016 года всё стало значительно лучше. И теперь все данные Landsat, то есть Landsat-1,4,5,7 и 8 поставляются уже с геометрической и радиометрической коррекцией. Такими к нам поступают снимки, называющиеся в наборах<br />
'''Landsat Level-1 Data Processing Levels''' или '''Landsat Level-1 data product''' или '''L1TP'''.<br />
<br />
"L1TP" - входит в название скачиваемого продукта, например, как на нашем снимке по которому мы будем строить температуру:<br />
<br />
LC08_L1TP_220076_20180501_20180501_01_RT<br />
<br />
Если же снимки Landsat имеют в названии L1GT или L1GT, то это означает, что они не выдерживают критерии по пройденным коррекциям, которые имеет основная часть снимков Landsat (L1TP), то есть надо дополнительно изучать, как и для чего можно использовать такие данные. Для неискушенного пользователя можно сделать вывод – их не надо использовать. :)<br />
<br /><br />
<br /><br />
Подробнее:<br />
<br /><br />
'''[https://landsat.usgs.gov/landsat-processing-details Landsat Processing Details]'''<br /><br />
'''[https://landsat.usgs.gov/landsat-collections Landsat Collections]'''<br />
<br /><br />
<br /><br />
Таким образом, пользователь может провести сам еще только одну - '''''атмосферную коррекцию''''' снимков Landsat.<br /><br />
<br />
О том, что продукты L1TP не имеют атмосферной коррекции можно заключить из официального ответа на такой вопрос по ссылке:<br />
<br />
'''[https://landsat.usgs.gov/does-landsat-level-1-data-processing-include-atmospheric-correction Does Landsat Level-1 data processing include atmospheric correction?]'''<br />
<br /><br />
<br /><br />
По этой же ссылке упоминается продукт '''Landsat Level-2''' или '''Surface Reflectance Product''' для которого атмосферная коррекция проведена.<br />
Данный продукт содержит каналы с 3 по 7, и он не содержит термальных каналов. Поэтому его нельзя использовать для построения температуры земной поверхности.<br />
<br /><br />
<br />
<br />
'''Вывод:'''<br />
Для построения температуры земной поверхности нам нужен только продукт L1TP. Для более точного построения температуры, этот продукт можно провести через атмосферную коррекцию (как это сделать при помощи плагина QGIS "Land Surface Temperature" рассмотрено ниже).<br />
<br />
<br /><br />
<br /><br />
<small>'''ПРИМЕЧАНИЕ-1''' <br /><br />
'''Дискуссия о необходимости атмосферной коррекции в снимках Landsat'''<br />
<br /><br />
Некоторые специалисты считают, что атмосферную коррекцию проводить бессмысленно, а то и вредно для точности оценки температуры земной поверхности и другого использования Landsat, поскольку еще никто не научился определять точно такую вещь, как, например, количество жидкости в атмосфере (влажность), а этот фактор влияет на данные. Специалисты говорят, что давно ведутся попытки разработки метода оценки показателей атмосферы для коррекции, но пока ученые не достигли в этом особых результатов.<br />
Также есть мнение, что той базовой коррекции, которую делает Landsat (L1TP) для всех своих данных достаточно, и что дополнительная коррекция необходима сценам с большим количеством облачности. Соответственно, можно сделать вывод, что, если вы считаете по сценам без облачности, атмосферная коррекция не нужна. <br />
Данные мнения собраны при обсуждении вопроса атмосферной коррекции в сообществе GIS-Lab. Пытливый читатель может найти их подтверждения (или опровержения) в литературе и внести свой вклад в точность материала, передав ссылки автору.<br />
</small><br />
<br /><br />
<br /><br />
<br />
==== Шаг 3. Устанавливаем QGIS плагин "Land Surface Temperature" и разбираемся с формулами ====<br />
Открываем проект QGIS и загружаем плагин: В верхней панели ''Модули > Управление модулями'' > в окне ''Поиск'' вбиваем "Land Surface Temperature" >''Установить модуль''.<br />
После установки модуль открывается через верхнюю панель ''Растр'' > ''Land Surface Temperature''<br />
<br /><br />
<br /><br />
Принцип работы плагина заключается в следующем. Двигаясь слева направо по вкладкам: '''''Radiance > Brightness Temperature > NDVI > Land Surface Emissivity > Land Surface Temperature Algorithm''''' мы последовательно выполняем в каждой из них расчет требуемого растра на основе подгружения требуемых каналов снимка Landsat (скачанных и сохраненных на компьютере в отдельной папке "LC08_L1TP_220076_20180501_20180501_01_RT"). На последней вкладке производится расчет самой температуры земной поверхности (Land Surface Temperature, LST) на базе подгружаемых растров, созданных на предыдущих шагах. <br />
<br />
Плагин выполняет расчет LST по алгоритму и формулам, с которыми можно ознакомиться, например, здесь:<br />
<br /><br />
'''[http://semiautomaticclassificationmanual-v5.readthedocs.io/pt_BR/latest/remote_sensing.html#conversion-to-surface-temperature Estimation of Land Surface Temperature]'''<br />
<br /><br />
или здесь:<br />
<br /><br />
'''[http://semiautomaticclassificationmanual-v5.readthedocs.io/en/latest/thematic_tutorial_temperature.html Tutorial: Estimation of Land Surface Temperature with Landsat and ASTER]'''<br />
<br /><br />
<br />
===== Рассмотрим упрощенный алгоритм и формулы расчета температуры земной поверхности по снимку Landsat-8, которые можно выполнить в растровом калькуляторе QGIS или ArcGIS, разобранный на форуме Александром Черепановым: =====<br />
[https://gis-lab.info/forum/viewtopic.php?t=24286&start=15#p164249 источник]<br />
<br /><br />
Здесь:<br /><br />
'''DN''' - Исходные значения растра в каналах TIRS.<br /><br />
'''TOA brightness temperature''' - Top Of Atmosphere brightness temperature (температура черного тела на поверхности атмосферы).<br /><br />
'''LST''' - Land Surface Temperature (температуры земной поверхности).<br /><br />
'''emissivity''' - Отражательная способность земной поверхности.<br /><br />
'''NDVI''' - Normalized Difference Vegetation Index (вегетационный индекс). Подробнее об NDVI можно узнать здесь: [http://gis-lab.info/qa/ndvi.html NDVI - теория и практика]<br />
<br /><br />
<br /><br />
* Выберем один канал для расчета температуры (band 10 или band 11 в Landsat-8). Ряд источников рекомендует считать температуру по 10-му каналу. Например: [http://semiautomaticclassificationmanual-v5.readthedocs.io/en/latest/faq.html#why-using-only-landsat-8-band-10-in-the-estimation-of-surface-temperature Why using only Landsat 8 band 10 in the estimation of surface temperature?]¶<br />
<br /><br />
* Мы выбрали 10 канал и для него находим в метаданных параметр '''RADIANCE_MULT_BAND_10''' и '''RADIANCE_ADD_BAND_10''', обычно эти параметры равны 3.3420E-04 и 0.10000 и считаем по формуле:<br />
<br /><br />
<pre><br />
Lλ=RADIANCE_MULT_BAND_10∗BAND10+RADIANCE_ADD_BAND_10<br />
<br />
where:<br />
Lλ-TOA spectral radiance (Watts/(m2*srad*µm))<br />
</pre><br />
<br /><br />
* Находим в метаданных параметр '''K1_CONSTANT_BAND_10''' и '''K2_CONSTANT_BAND_10''', обычно эти параметры равны 774.8853 и 1321.0789. Теперь мы можем посчитать TOA brightness temperature по формуле:<br />
<br /><br />
<br />
<pre><br />
TB=K2/ln[(K1/Lλ)+1]<br />
<br />
where:<br />
TB-TOA brightness temperature (K)<br />
Lλ-TOA spectral radiance (Watts/(m2*srad*µm))<br />
K1-Band-specific thermal conversion constant from the metadata (K1_CONSTANT_BAND_x, where x is the thermal band number)<br />
K2-Band-specific thermal conversion constant from the metadata (K2_CONSTANT_BAND_x, where x is the thermal band number)<br />
</pre><br />
<br /><br />
* Переход от DN к TOA brightness temperature делается только так( [https://landsat.usgs.gov/using-usgs-landsat-8-product перейти]) никаких других вариантов быть не может. Для уменьшения числа операций можно объединить формулы:<br />
<br /><br />
<pre><br />
TB=K2/ln[(K1/(RADIANCE_MULT_BAND∗BAND10+RADIANCE_ADD_BAND))+1]<br />
</pre><br />
<br /><br />
* Готовимся к переходу к Land surface temperature и считаем по формуле:<br />
<br /><br />
<br />
<pre><br />
LST=TB/[1+(λ∗TB/c2)∗ln(emissivity)]<br />
<br />
where:<br />
λ-wavelength of emitted radiance, для 10 канала - значение 10.8<br />
c2=h∗c/s=1.4388∗10−2 m K = 14388 µm K<br />
h = Planck’s constant = 6.626∗10−34 J s<br />
s = Boltzmann constant = 1.38∗10−23 J/K<br />
c = velocity of light = 2.998∗108 m/s<br />
</pre><br />
<br /><br />
На этом этапе мы подошли ко второму "усложняющему жизнь фактору" в оценке LST, а именно – к оценке '''emissivity''' (отражающей способности земной поверхности). <br /><br />
Для того, чтобы рассчитать LST нам надо определить, что будет использоваться в качестве значения emissivity – классификация типов земной поверхности, значение на основе вегетационного индекса NDVI или константа. <br /><br />
Так, если emissivity ввести в виде константы равной 0.98 и добавить перевод в градусы Цельсия, то формула расчета LST получится такой:<br />
<pre><br />
LST=(TB/[1+(10.8∗TB/14388)∗ln(0.98))-273.15<br />
where:<br />
TB-TOA brightness temperature (K)<br />
LST -Land surface temperature (C)<br />
<br />
</pre><br />
<br /><br />
<small>'''ПРИМЕЧАНИЕ-2'''<br /><br />
'''Причина разнообразия методов оценки температуры земной поверхности по данным Landsat'''<br /><br />
Разнообразие методов оценки температуры земной поверхности обусловлено тем, что значения, которые показывает тепловой канал (после пересчета TOA brightness temperature) и температура земной поверхности (LST) связаны весьма косвенно. Представьте - у нас нет никакой информации о состояние поверхности и атмосферы, есть только излучение зафиксированное съемочной аппаратурой и мы должны определить как это излучение исказилось около земной поверхности и пройдя через атмосферу, прежде чем было зафиксировано аппратурой. Отсюда и множество подходов к оценке LST и подключение дополнительных источников информации о состоянии поверхности и атмосферы. Одни более простые и требуют меньше сторонних данных, другие более сложные (используют NDVI, или\и классификацию типов земной поверхности). Качеством получаемых результатов LST в абсолютных значениях, вероятно, далеки от заявленных 0.1-1 градуса чувствительности аппаратуры.<br />
<br /><br />
Именно по этой причине, нельзя использовать для расчета LST "первый попавшийся мануал" не разбираясь в том, какой алгоритм расчета он вам предлагает. Одни пособия считают значение emissivity постоянной величиной, другие используют NDVI, третьи предлагают вам предварительно заняться классификацией земной поверхности.</small><br />
<br /><br />
<br /><br />
<br />
==== Шаг 4. Забываем про формулы и просто считаем LST в плагине QGIS "Land Surface Temperature" ====<br />
<br /><br />
Разбор формул был необходим, чтобы понять, что именно делает плагин QGIS "Land Surface Temperature", что предлагают рассчитывать различные пособия и модули других ГИС софтов. Чтобы показать, что разными пособиями, тулбоксами, скриптами дается не один единственный способ вычисления LST из снимков Landsat, а даются разные способы такого вычисления. Разнообразие методов расчета обусловлено двумя факторами – способ учета emissivity и способ учета влияния атмосферы (атмосферная коррекция).<br />
<br /><br />
<br /><br />
При выборе пособия расчета LST или модуля для полуавтоматического расчета LST из снимков Landsat, следует узнать, какой именно алгоритм расчета LST используется и, если этот метод устраивает для выбранной задачи, то его использовать.<br /><br />
Плагин QGIS "Land Surface Temperature" был позитивно оценен специалистами дистанционного зондирования Земли (в частности, Александром Черепановым). <br /><br />
Видно, что плагин использует при расчете LST индекс NDVI (через него определяя значение emissivity), не использует для значения emissivity классификацию земной поверхности (для оценки растительного покрова NDVI это лучшее, для других сред, возможно, также его достаточно), и также плагин дает возможность внести атмосферные параметры (то есть провести и атмосферную коррекцию), но и имеет способ расчета LST без атмосферной коррекции.<br />
<br /><br />
<br /><br />
Теперь, когда мы все это поняли и выбрали плагин QGIS "Land Surface Temperature" как подходящий для наших задач оценки температуры земной поверхности из снимков Landsat-8, мы можем забыть о формулах и просто его использовать. Выполняя последовательно всё, что он от нас хочет. Там все очень просто. Приведу принт-скрины моего расчета с краткими пояснениями.<br />
<br /><br />
<br /><br />
<br />
Итак, считаем температуру земной поверхности (LST) по снимку '''[https://landsatonaws.com/L8/220/076/LC08_L1TP_220076_20180501_20180501_01_RT LC08_L1TP_220076_20180501_20180501_01_RT]''', все данные по ссылке скачаны и записаны в папку на компьютере с таким же названием. С учетом атмосферной коррекции нам нужно сделать 6 последовательных расчетов:<br />
<br /><br />
<br /><br />
===== Р1. Расчет Radiance =====<br />
<br /><br />
[[Файл:1_radiance.jpg]]<br />
<br /><br />
<br />
===== Р2. Расчет Brightness Temperature =====<br />
<br /><br />
[[Файл:2_brightness_temperature.jpg]]<br />
<br /><br />
<br />
===== Р3. Расчет NDVI =====<br />
Для OLI (Landsat-8) каналы для NDVI это: NIR band 5 (0.85 - 0.88 µm, Red band 4 (0.64-0.67 µm).<br /><br />
Источник: [https://landsat.usgs.gov/what-are-best-spectral-bands-use-my-study каналы Landsat]<br />
<br /><br />
[[Файл:3_ndvi.jpg]]<br />
<br /><br />
<br />
===== Р4. Расчет Land Surface Emissivity =====<br />
<br /><br />
Выбрала способ NDVI Threshhold Algorithm<br />
<br /><br />
[[Файл:4_emissivity.jpg]]<br />
<br /><br />
<br />
===== Р5. Расчет атмосферного профиля в Калькуляторе атмосферных параметров =====<br />
<br /><br />
Для того, чтобы получить атмосферные параметры, которые дадут возможность посчитать LST по способу с атмосферной коррекцией "Radiative transfer equation", воспользуемся <br /><br />
'''[https://atmcorr.gsfc.nasa.gov Atmospheric Correction Parameter Calculator]'''<br />
<br /><br />
Координаты указала той территории, для которой мне важно получиться температуру (а не для центра сцены). Это -22.8194, -47.1105 <br />
По карте рельефа и сводке погоды на 1 мая 2018 г в ближайшем городе я внесла в калькулятор эти параметры: Surface altitude 0.7 km, Surface pressure 1019 mb, Surface temperature 29 (C), Surface relative humidity 41%<br />
<br /><br />
Получилось так:<br /><br />
<br />
[[Файл:5_atmosph_parameters.jpg]]<br />
<br /><br />
Мы получили атмосферный профиль, из которого возьмем нужные параметры для атмосферной коррекции:<br />
[[Файл:6_atmosph_profile.jpg]]<br />
<br /><br />
<br />
===== Р6. Расчет температуры земной поверхности (LST) с использованием атмосферной коррекции методом Radiative transfer equation =====<br />
<br /><br />
Для того, чтобы построить LST с учетом атмосферной коррекции на последней стадии расчетов выберем метод "Radiative transfer equation". <br />
Я выбрала этот метод, поскольку только для него можно получить все необходимые параметры при помощи Калькулятора атмосферных параметров. Другие способы (Mono-Window Algorithm, Single Channel Algorithm)- содержат в себе параметры, которые нужно искать в других источиках. <br />
Для того, чтобы построить LST без атмосферной коррекции, на последней стадии выберете метод "Planck Equation".<br />
<br /><br />
Вношу нужные параметры из полученного атмосферного профиля: <br /><br />
<br />
Upwelling Radiance: 1.7, Downwelling Radiance: 2.81, Atmospheric Transmission: 0.80.<br />
<br /><br />
В графу Top of Atmosphere Radiance (TOA) подгружаю растр radiance, рассчитанный в первом расчете плагина.<br /><br />
Выбираю единицы температуры Celsius.<br />
<br /><br />
[[Файл:7_LST_calculation.jpg]]<br />
<br /><br />
<br /><br />
Итоговый растр LST_1may2018atm можно обрабатывать дальше в QGIS, а можно из QGIS пересохранить в Geotiff и открыть в ArcMap. Полученная карта температур может выглядеть таким образом:<br />
<br /><br />
[[Файл:MataSG_1may2018_LSTatmcor.jpg]]<br />
<br /><br />
Разрешение этой карты – 30 метров земной поверхности в 1 пикселе изображения. Термальный канал Landsat-8 имеет разрешение 100 метров, но карта температур строится при использовании параметров и других каналов, имеющих разрешение 30 метров (для NDVI использовались каналы 4 и 5). Поэтому выходное разрешение карты становится 30 метров.</div>Александр Мурыйhttps://wiki.gis-lab.info/index.php?title=%D0%9E%D0%B1%D1%89%D0%B5%D1%81%D1%82%D0%B2%D0%B5%D0%BD%D0%BD%D1%8B%D0%B5_%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D1%8B&diff=26369Общественные проекты2018-06-20T16:31:37Z<p>Александр Мурый: /* Текущие проекты */</p>
<hr />
<div>{{Статья|Опубликована|../projects}}<br />
<br />
{{Аннотация|Перечень проектов, выполненных совместными усилиями на GIS-LAB}}<br />
<br />
== Текущие проекты ==<br />
На ГИС-Лабе есть разные группы энтузиастов с разными интересами, а значит, в данный период времени может идти несколько параллельных проектов. Присоединяйтесь!<br />
<br />
'''[http://gis-lab.info/qa/oopt-reg.html Создание открытого слоя геоданных по ООПТ регионального и местного значения]'''<br />
<br />
'''[http://wiki.gis-lab.info/w/%D0%9E%D0%B1%D0%BD%D0%BE%D0%B2%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BF%D0%B5%D1%80%D0%B5%D0%B2%D0%BE%D0%B4%D0%B0_%D0%B4%D0%BE%D0%BA%D1%83%D0%BC%D0%B5%D0%BD%D1%82%D0%B0%D1%86%D0%B8%D0%B8_OSGeo_Live Обновление перевода документации OSGeo Live]'''<br />
<br />
Если у вас есть идея для проекта — предложите её на '''[http://gis-lab.info/forum/viewforum.php?f=3 форуме].''' <br />
<br />
<!-- [[Image:new.gif|27px|new]]'''Сейчас на GIS-Lab проектов нет, предложите свой на [http://gis-lab.info/forum/viewforum.php?f=3 форуме].''' --><br />
<br />
==Кратко о проектах==<br />
''Что такое проект?''<br/><br />
Проект это совместная работа 3 и более человек над какой-то конкретной задачей (переводом, созданием слоя данных, созданием материала, ПО), который так или иначе будет представлен на GIS-Lab. У проекта обязательно должен быть координатор, начало, конец и более или менее четкий результат.<br />
<br />
'''Как мне начать проект?'''<br/><br />
Предложите его на форуме, соберите единомышленников, обсудите детали, зафиксируйте их в [http://wiki.gis-lab.info/w/%D0%97%D0%B0%D0%B3%D0%BB%D0%B0%D0%B2%D0%BD%D0%B0%D1%8F_%D1%81%D1%82%D1%80%D0%B0%D0%BD%D0%B8%D1%86%D0%B0 вики].<br />
<br />
'''Почему я предложил проект, но никто не хочет участвовать?'''<br/><br />
Для того, чтобы проект "пошёл", нужно потратить значительное количество времени на его подготовку, привлечение внимания и собственно ведение. Что вы могли сделать не так:<br />
#Возможно вы плохо рассказали о проекте и никто не понял, что он прекрасный, попробуйте еще.<br />
#У вашего проекта очень сложная форма участия, требующая супер-навыков.<br />
#Ваш проект связан с действиями мутными с точки зрения лицензионных соглашений (например, копирование с проприетарных источников)<br />
#Вы не заметили, что на GIS-Lab уже идет один или несколько активных проектов (см. ниже), которые "оттягивают" на себя участников.<br />
Вы можете прямо спросить у сообщества, почему никто не участвует, наверняка, вам сразу ответят.<br />
<br />
'''Можно ли делать на GIS-Lab свой проект параллельно с уже идущими?'''<br/><br />
Если уже идёт какой-то большой проект, то вы не сможете получить для своего отдельный форум, упоминание на главной и т.п. бонусы, пока он не закончится. Однако, технически никто не может вам запретить начать реализовывать интересный вам проект параллельно. Пожалуйста, активнее принимайте участие в общих проектах и делайте свои!<br />
<br />
==Завершенные проекты==<br />
[http://gis-lab.info/qa/oopt-irk.html Создание открытого слоя геоданных границ особо охраняемых природных территорий регионального и местного значения Иркутской области (01-30.06.2017)]<br />
<br />
[http://gisconf.ru/ Конференция "Открытые ГИС" 2015] [[Image:forumq.gif|14px|http://gis-lab.info/forum/viewforum.php?f=51|Обсудить в форуме]]<br /> Вторая конференция по открытым ГИС.<br />
<br />
[http://gis-lab.info/qa/uikgeo.html УИК ГЕО - создание слоя данных по местоположениям УИК РФ] [[Image:forumq.gif|14px|http://gis-lab.info/forum/viewtopic.php?t=12594|Обсудить в форуме]]<br /> Коллективный проект по уточнению географического положения участковых избирательных комиссий по всей стране<br />
<br />
[http://gis-lab.info/qa/geodetdom.html Создание слоя детских учреждений Российской Федерации] [[Image:forumq.gif|14px|http://gis-lab.info/forum/viewtopic.php?t=12594|Обсудить в форуме]]<br /> Коллективный проект по уточнению географического положения учреждений и созданию геоданных.<br />
<br />
[http://gis-lab.info/qa/osgeo-live-docs-coord.html Перевод документации OSGeo-Live] [[Image:forumq.gif|14px|http://gis-lab.info/forum/viewtopic.php?t=11526|Обсудить в форуме]]<br /> Коллективный проект по переводу материалов OSGeo Live DVD.<br />
<br />
[http://2012.gisconf.ru/ru/ Конференция "Открытые ГИС" 2012] [[Image:forumq.gif|14px|http://gis-lab.info/forum/viewforum.php?f=51|Обсудить в форуме]]<br /> Целая конференция.<br />
<br />
[http://gis-lab.info/qa/prj-uik.html Проверка УИКов г. Москвы] [[Image:forumq.gif|14px|http://gis-lab.info/forum/viewtopic.php?t=9247|Обсудить в форуме]]<br /> Коллективный проект по созданию открытого слоя данных по расположению УИКов г. Москвы<br />
<br />
[http://gis-lab.info/qa/rusbounds-rosreestr.html Границы субъектов РФ] [[Image:forumq.gif|14px|http://gis-lab.info/forum/viewtopic.php?t=4703|Обсудить в форуме]]<br /> Открытый коллективный проект по созданию открытого слоя границ субъектов РФ, на базе данных Росреестра. Проект закончен. [http://gis-lab.info/qa/rusbounds-rosreestr.html Результаты], [http://gis-lab.info/projects/rusbounds-rosreestr.html координационная страница].<br />
<br />
[http://gis-lab.info/qa/geosample.html Geosample: Открытый набор геоданных для различного ПО ГИС] [[Image:forumq.gif|14px|http://gis-lab.info/forum/viewtopic.php?t=3728|Обсудить в форуме]]<br /> Набор для 14 ГИС, готовый к использованию.<br />
<br />
[http://gis-lab.info/projects/vmap0-trans/index.html Перевод названий населенных пунктов VMap0] [[Image:forumq.gif|14px|http://gis-lab.info/forum/viewtopic.php?t=3370|Обсудить в форуме]]<br /> Открытый коллективный проект по переводу базы данных VMap0, на данный момент - названий населенных пунктов, с уточнением топонимики.<br />
<br />
[http://gis-lab.info/projects/scgis/index.html Общество Природоохранных ГИС - Россия]<br />
<br />
Периодические (один раз в год) объявления о программе стипендий SCGIS, поддерживающего природоохранные проекты с применением ГИС и отбор заявок.</div>Александр Мурыйhttps://wiki.gis-lab.info/index.php?title=%D0%93%D1%83%D0%BB%D1%8F%D0%B5%D0%BC_%D0%BF%D0%BE_%D0%BF%D0%B5%D1%80%D0%B5%D1%81%D0%B5%D1%87%D0%B5%D0%BD%D0%BD%D0%BE%D0%B9_%D0%BC%D0%B5%D1%81%D1%82%D0%BD%D0%BE%D1%81%D1%82%D0%B8_%D0%B2_%D0%BE%D1%82%D0%BA%D1%80%D1%8B%D1%82%D1%8B%D1%85_%D0%93%D0%98%D0%A1:_%D0%BE_%D0%B1%D0%B0%D0%B7%D0%BE%D0%B2%D1%8B%D1%85_%D0%B2%D0%BE%D0%B7%D0%BC%D0%BE%D0%B6%D0%BD%D0%BE%D1%81%D1%82%D1%8F%D1%85_%D0%BC%D0%BE%D0%B4%D0%B5%D0%BB%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F_%D0%BF%D0%B5%D1%88%D0%B8%D1%85_%D0%BF%D0%B5%D1%80%D0%B5%D0%BC%D0%B5%D1%89%D0%B5%D0%BD%D0%B8%D0%B9_%D0%BD%D0%B0_%D0%BE%D1%81%D0%BD%D0%BE%D0%B2%D0%B5_QGIS,_GRASS_%D0%B8_SAGA&diff=26135Гуляем по пересеченной местности в открытых ГИС: о базовых возможностях моделирования пеших перемещений на основе QGIS, GRASS и SAGA2018-05-31T08:31:28Z<p>Александр Мурый: </p>
<hr />
<div><br />
{{Статья|Опубликована|qgis_walk_modeling}}<br />
<br />
{{Аннотация|Расчёт изохрон, оптимальных маршрутов и коридоров наименьших затрат для пеших перемещений на основе цифровых моделей рельефа в QGIS 3 (с использованием модулей GRASS и SAGA)}}<br />
<br />
В статье рассмотрены решения некоторых отдельных, наиболее популярных задач, связанных с моделированием пеших перемещений (людей или животных) по пересечённой местности. Потребность в такого рода моделировании часто возникает при необходимости проложить маршрут в пешем походе, определить области перемещений пределах заданных временных интервалов, а также в задачах археологии, зоологии и других дисциплин.<br />
<br />
Рассмотрим решения для трёх базовых проблем:<br />
# Построение изохрон перемещений по пересеченной местности относительно одной или множества исходных точек<br />
# Построение оптимального (с точки зрения временных затрат) маршрута по пересеченной местности между двумя точками<br />
# Построение коридора оптимальных временных затрат между двумя точками<br />
<br />
Для подготовки данных и моделирования будем использовать открытый пакет [https://qgis.org/ru/site/ QGIS] 3.0.2 с модулями [https://grass.osgeo.org/ GRASS] и [http://www.saga-gis.org/ SAGA], вызов которых доступен из панели анализа.<br />
Тестирование осуществлялось в средах Windows 10 x64 и Linux Mint 18.2 x64<br />
<br />
== Подготовка данных ==<br />
<br />
Для демонстрации будет использоваться набор данных для территории Республики Тыва, в горной местности к северо-востоку от Кызыла. У озера Маны-Холь. В качестве источников использовались:<br />
* Данные OpenStreetMap. Способы их загрузки неоднократно [http://gis-lab.info/qa/isochrone-map-grass-qgis.html#.D0.97.D0.B0.D0.B3.D1.80.D1.83.D0.B7.D0.BA.D0.B0_.D0.B4.D0.B0.D0.BD.D0.BD.D1.8B.D1.85_OSM описывались]. В статье использовалась [http://data.nextgis.com/osmshp/ архивная выгрузка с NextGIS] на Тыву. От OSM нам понадобятся границы озёр (а также, опционально, дороги, застройка, типы землепользования).<br />
* Данные о рельефе. Чем точнее ваши данные, тем лучше. В статье использовались данные с http://viewfinderpanoramas.org/dem3.html, где собраны данные на весь мир с разрешением 3 секунды.<br />
* Данные о типах подстилающей поверхности. В статье использовались данные [https://landcover.usgs.gov/global_climatology.php%20 MODIS Global Land Cover Climatology]. Можно обойтись и без этих данных, в статье они приводятся только в целях демонстрации принципов их учёта.<br />
<br />
Архив с данными для примера из статьи можно [https://drive.google.com/file/d/1jAjWFJJuXSk7x-AGpDejeQjlLBaanCE8/view?usp=sharing загрузить здесь].<br />
<br />
Итак, приступаем к подготовке данных. Для начала загрузим всё необходимое в QGIS:<br />
* Векторные слои с препятствиями. Это могут быть любые объекты, по которым нельзя ходить. В нашем случае это только площадная гидрография из OSM (озёра, реки). Потенциально это может быть что угодно - здания, закрытые территории карьеров, военные полигоны и прочее.<br />
* Загруженные данные по рельефу в .hgt<br />
* Данные по типам подстилающей поверхности<br />
* Спутниковая подложка Bing из плагина QuickMapServices для наглядности.<br />
<br />
<br />
[[Файл:Ogis_dem_walking_1.png|600px|thumb|center|]]<br />
<br />
<br />
Соберём данные по рельефу в единый растровый набор. Для этого воспользуемся функцией merge из GDAL - Raster miscellaneous.<br />
<br />
<br />
[[Файл:Ogis_dem_walking_2.png|600px|thumb|center|]]<br />
<br />
<br />
Получившийся временный слой сохраним в geotiff, с которым продолжим работу (для этого в контекстном меню слоя нужно выбрать "сохранить как". При этом в процессе сохранения произведем некоторые манипуляции. Во-первых, выберем правильную систему координат, например UTM для подходящей зоны. Для тестового участка это зона 47N (EPSG:32647). Во-вторых, для будущих задач нам потребуются данные о рельефе такие, у которых пространственное разрешение по X и Y совпадает (т.е. пиксели должны быть квадратными или почти квадратными, с пренебрежимо малой разницей). Для этого вручную устанавливаем одинаковое разрешение для обоих измерений. Проще всего приравнять большее разрешение к меньшему. Результат запишем в файл DEM.tif<br />
<br />
<br />
[[Файл:Ogis_dem_walking_3.png|600px|thumb|center|]] <br />
<br />
<br />
Теперь приведем к нужному виду данные о типах подстилающей поверхности. Через пункт контекстного меню "сохранить как" настраиваем нужное: устанавливаем систему координат UTM 47N и задаем новый охват равный охвату слоя с рельефом, для этого есть специальная кнопка. Всё лишнее будет отсечено. Результат сохраним в файл land_cover_cut.tif<br />
<br />
<br />
[[Файл:Ogis_dem_walking_4.png|600px|thumb|center|]] <br />
<br />
<br />
Слой с площадной гидрографией нам интересен во многих смыслах. Сохраним слой water-polygon в систему координат UTM 47N, называем water.geojson<br />
<br />
<br />
[[Файл:Ogis_dem_walking_5.png|600px|thumb|center|]]<br />
<br />
<br />
Одной из наших задач будет расчёт зон доступности озера Маны-Холь, поэтому по ходу дела получим точки его берега. Для этого выделяем в слое water это озеро, и с помощью инструмента Extract Verticies получаем слой с точками на береговой линии выделенного объекта. Сохраним его как lake-points.geojson<br />
<br />
<br />
[[Файл:Ogis_dem_walking_6.png|600px|thumb|center|]]<br />
<br />
<br />
Теперь поработаем со слоями препятствий на примере той же воды. На самом деле нам нужно в слое с ЦМР (DEM.tif) установить все пиксели с препятствиями в No Data. Сделать это можно множеством способов, посмотрим на один из них. Первый шаг - растеризация векторного слоя. Используем инструмент SAGA - Raster creation tools. Задаём в качестве исходного слоя water, Output Values устанавливаем в data/no-data, указываем охват равный охвату слоя DEM (это можно сделать через контекстное меню справа от поля ввода значений), а также задаём пространственное разрешение (cellsize) равным выбранному разрешению для слоя DEM, можно выбрать значение меньше.<br />
<br />
<br />
[[Файл:Ogis_dem_walking_7.png|600px|thumb|center|]]<br />
<br />
<br />
В результате получаем растровый слой, в котором на месте воды пиксели имеют значение 1, а во всех остальных местах - no data. <br />
<br />
<br />
[[Файл:Ogis_dem_walking_8.png|600px|thumb|center|]]<br />
<br />
<br />
Теперь обращаем значения data / no-data с помощью простого инструмента SAGA - Raster tools - Invert data/no-data.<br />
<br />
<br />
[[Файл:Ogis_dem_walking_9.png|600px|thumb|center|]]<br />
<br />
<br />
Теперь всё наоборот - там, где были объекты, теперь no-data. Во всех остальных местах пиксели имеют значение 1. Сохраним этот набор данных в water_inverted.tif<br />
<br />
Это то, что нам нужно. Теперь можно простым способом превратить соответствующие пиксели ЦМР в no data. Для этого открываем калькулятор растров (в меню Raster), и просто перемножаем слой с ЦМР и слой water_inverted. То есть задаём такое выражение, которое расчитаем в границах DEM.tif. Результат сохраним в DEM_masked.tif.<br />
<br />
<pre><br />
"DEM@1"*"water_inverted@1"<br />
</pre><br />
<br />
<br />
[[Файл:Ogis_dem_walking_10.png|600px|thumb|center|]]<br />
<br />
<br />
Результат - желанная ЦМР с "дырками".<br />
<br />
<br />
[[Файл:Ogis_dem_walking_11.png|600px|thumb|center|]]<br />
<br />
<br />
Аналогичным образом можно пометить как no-data любые другие векторные полигоны, в зависимости от ваших задач и территории. Чтобы закончить подготовку основных данных, создадим два векторных слоя (через меню Layer - Create Layer), каждый из которых будет содержать по одной точке. Один слой назовём start_point, второй end_point - это точки, между которыми мы будем строить оптимальный маршрут и коридор наименьших затрат. Также у нас уже есть слой с точками по берегу озера - относительно них будут рассчитываться зоны доступности.<br />
<br />
<br />
[[Файл:Ogis_dem_walking_12.png|600px|thumb|center|]]<br />
<br />
<br />
== Расчёт поля стоимости перемещений ==<br />
<br />
Основная задача, лежащая в основе многих последующих расчётов, это получение поля стоимости перемещений, то есть такого растра, в каждой ячейке которого хранилась бы стоимость (например, в минутах) достижения этой ячейки из одной или множества заданных в пространстве точек. Для расчёта такого поля существует два подхода:<br />
* Интерактивный расчёт стоимостей перемещения из заданных точек. Это более сложный подход, и он востребован в тех случаях, когда невозможно назначить каждой конкретной ячейке пространства определенную стоимость её пересечения (например, если пересечение ячейки в разные стороны стоит разную цену). Одна из вариаций такого подхода доступна в модуле GRASS [https://grass.osgeo.org/grass72/manuals/r.walk.html r.walk].<br />
* Назначение каждой ячейке базового пространства (например ЦМР) константной стоимости её пересечения, и расчёт поля стоимости от заданных точек по этим константным значениям. Это более простой подход, и его проще реализовать для произвольной функции перемещений в ГИС. В этом поможет модуль GRASS [https://grass.osgeo.org/grass72/manuals/r.cost.html r.cost].<br />
<br />
<br />
Попробуем разобраться с обоими подходами.<br />
<br />
<br />
=== С использованием модуля r.walk ===<br />
<br />
<br />
Модуль r.walk предназначен непосредственно для расчёта стоимости перемещений по пересеченной местности с учётом некоторых дополнительных условий. Остановимся на основных моментах. В QGIS запустить r.walk можно через панель анализа в GRASS - raster - r.walk.points<br />
<br />
Основные входные данные: ЦМР (elevation raster map) и слой с затратами, связанными с типом подстилающей поверхности (friction cost).<br />
<br />
С ЦМР всё ясно, она у нас уже готова. Значения в ячейках растра с friction cost будут учитываться так: это '''количество дополнительных секунд''', которые будут затрачиваться на преодоление 1 метра соответствующем пикселе. А вот такого набора данных мы ещё не подготовили! Об этом несколько позже, а пока разберёмся с другими параметрами.<br />
<br />
Start points - точки, относительно которых будет производиться расчёт стоимостей. End points - опциональный параметр, если указать какой-то слой, то расчёт стоимостей будет прекращен по достижению алгоритмом поиска точек в этом слое.<br />
<br />
Далее следуют настройки корневой формулы, по которой будет производиться расчёт. Процитируем, переведем и прокомментируем документацию:<br />
<br />
Главная формула расчёта:<br />
<br />
<pre>T = a*delta_S + b*delta_H_uphill + c*delta_H_moderate_downhill + d*delta_H_steep_downhill</pre><br />
<br />
где:<br />
<br />
T - время движения в секундах,<br />
<br />
delta S - преодолеваемое горизонтальное расстояние в метрах (берётся из ЦМР),<br />
<br />
delta H - преодолеваемая разница высот в метрах (берётся из ЦМР),<br />
<br />
a, b, c, d - коэффициенты, связанные с движением в разных условиях.<br />
<br />
<br />
a: время в секундах, которое требуется для преодоления 1 метра на ровной поверхности.<br />
<br />
b: дополнительное время в секундах, затрачиваемое на метр перемены высоты на подъёмах.<br />
<br />
c: дополнительное время в секундах, затрачиваемое на метр перемены высоты на плавных спусках (используются положительные значения для уменьшения цены)<br />
<br />
d: дополнительное время в секундах, затрачиваемое на метр перемены высоты на крутых спусках (используются отрицательные значения для увеличения стоимости)<br />
<br />
<br />
Значения по умолчанию были предложены как стандартные для среднестатистического человека. Вы можете, изменяя их, моделировать перемещения для специальных категорий людей или для определенных видов зверей.<br />
<br />
<br />
Далее определяется коэффициент Lambda для связи полученной по формуле выше T и поверхности friction cost. Связь осуществляется по формуле:<br />
<br />
<pre>total cost = movement time cost + lambda * friction costs * delta_S</pre><br />
где:<br />
<br />
movement time cost - время движения в секундах, рассчитанное по формуле выше,<br />
<br />
lamda - обсуждаемый коэффициент,<br />
<br />
friction costs - значение friction cost в текущем месте,<br />
<br />
delta S - преодолеваемое горизонтальное расстояние в метрах (берётся из ЦМР),<br />
<br />
<br />
Следующий параметр - slope factor. По сути, он участвует в формуле расчёта T как показатель перехода от плавного спуска (когда спуск ускоряет перемещение и приятен) к крутому спуску (когда уклон начинает работать наоборот, замедлять перемещение). Значение по умолчанию (-0.2125) соответствует тангенсу 12 градусов, т.е. угол склона меньше 12 градусов будет восприниматься как плавный, а больше 12 градусов - как крутой.<br />
<br />
<br />
Параметр Maximum cumulative cost опциональный и определяет цену, по достижению которой расчёты приостанавливаются. Например, вас интересуют только территории в пределах 2 часовой доступности, тогда следует установить этот параметр равным 7200 (в секундах).<br />
<br />
<br />
Таким образом, с помощью поверхности friction cost, коэффициентов a,b,c,d и фактора угла уклона можно достаточно гибко моделировать перемещения в рамках предлагаемой авторами модели.<br />
<br />
<br />
Оставим значения коэффициентов по умолчанию, и займемся подготовкой поверхности friction cost. Здесь всё зависит от выбранной вами модели типов подстилающей поверхности и собственных соображений о том, какой тип как влияет на скорость перемещения. Если совсем не охота об этом размышлять, можно не использовать никакой карты типов земель и просто создать в калькуляторе растров набор данных, состоящий из одних нулей. Если дать r.walk такой растр как friction cost, то на основную формулу просто не будет накладываться никаких дополнительных ограничений. Давайте попробуем вместе. В калькуляторе растров в границах слоя DEM создадим новый растр zero_frictions.tif по формуле<br />
<br />
<pre> 0 </pre><br />
<br />
<br />
[[Файл:Ogis_dem_walking_13.png|600px|thumb|center|]]<br />
<br />
<br />
И теперь запустим r.walk с этим растром в качестве friction cost, нашей ЦМР и точками береговой линии озера в качестве Start points. Все остальные параметры оставим по умолчанию, и посмотрим, какие результаты будут получены.<br />
<br />
<br />
[[Файл:Ogis_dem_walking_14.png|600px|thumb|center|]]<br />
<br />
<br />
После расчётов мы получаем два новых набора данных: Cumulative cost и Movement Directions.<br />
<br />
<br />
[[Файл:Ogis_dem_walking_15.png|600px|thumb|center|]] [[Файл:Ogis_dem_walking_16.png|600px|thumb|center|]]<br />
<br />
<br />
Они оба полезны и будут использованы в дальнейших расчётах. Растр Cumulative cost в каждой ячейке содержит минимальное время перемещения от озера до этой ячейки в секундах. Растр Movement Directions содержит в каждой ячейке направление, в которые был совершен переход при расчёте Cumulative cost. По растру Cumulative cost хорошо видны неоднородности, особенно в областях с наибольшими перепадами высот. Также очевидно, что при встрече с озером (пикселями no-data) алгоритму пришлось их обходить.<br />
<br />
<br />
Теперь попробуем учесть наши типы подстилающей поверхности. Согласно [https://landcover.usgs.gov/global_climatology.php источнику] данных, в растре содержатся номера классов от 0 до 16, означающие следующее:<br />
<br />
<pre><br />
0 Вода (Water)<br />
1 Вечнозелёные хвойные леса (Evergreen Needle leaf Forest)<br />
2 Вечнозелёные широколиственные леса (Evergreen Broadleaf Forest)<br />
3 Опадающие хвойные леса (Deciduous Needle leaf Forest)<br />
4 Опадающие широколиственные леса (Deciduous Broadleaf Forest)<br />
5 Смешанные леса (Mixed Forests)<br />
6 Плотные заросли кустарников (Closed Shrublands)<br />
7 Открытые заросли кустарников (Open Shrublands)<br />
8 Лесистные саванны (Woody Savannas)<br />
9 Саванны (Savannas)<br />
10 Поля (Grasslands)<br />
11 Постоянные болота (Permanent Wetland)<br />
12 Сельскохозяйственные поля (Croplands)<br />
13 Застройка, антропогенные сооружения (Urban and Built-Up)<br />
14 Смешанные земли с лесами, кустарниками, сельскохозяйственными полями и др. (Cropland/Natural Vegetation Mosaic)<br />
15 Снег и лёд (Snow and Ice)<br />
16 Бесплодные земли или мало покрытые растительностью (Barren or Sparsely Vegetated)<br />
</pre><br />
<br />
Некоторые комментарии к этим классам можно найти [http://studentclimatedata.unh.edu/climate/albedo/MODISLandcoverClass_definitions.pdf здесь] (на английском языке). Понятно, что в общем случае такая классификация достаточно условна, и желательно использовать более надежные источники, непосредственно связанные с территорией исследований, а не глобальные. Для демонстрации, впрочем, вполне подойдёт.<br />
<br />
<br />
Для того, чтобы учесть эти типы земель в модели r.walk, нужно из растра с ключами создать растр со "штрафами" в секундах на метр (как следует из природы friction cost), соответствующими типам. Для такой задачи воспользуемся старым добрым способом с калькулятором растров (модули типа [https://grass.osgeo.org/grass72/manuals/r.reclass.html r.reclass] не подходят, так как работают только с целыми числами). Запишем выражение:<br />
<br />
<pre><br />
("land_cover_cut@1" = 0) * 0 + <br />
("land_cover_cut@1" = 1) * 1.5 + <br />
("land_cover_cut@1" = 2) * 1.5 + <br />
("land_cover_cut@1" = 3) * 1.5 + <br />
("land_cover_cut@1" = 4) * 1.5 + <br />
("land_cover_cut@1" = 5) * 1.5 + <br />
("land_cover_cut@1" = 6) * 2.2 + <br />
("land_cover_cut@1" = 7) * 0.7 + <br />
("land_cover_cut@1" = 8) * 0.7 + <br />
("land_cover_cut@1" = 9) * 0.5 + <br />
("land_cover_cut@1" = 10) * 0.2 + <br />
("land_cover_cut@1" = 11) * 3 + <br />
("land_cover_cut@1" = 12) * 1 + <br />
("land_cover_cut@1" = 13) * 0 + <br />
("land_cover_cut@1" = 14) * 0.4 + <br />
("land_cover_cut@1" = 15) * 0 + <br />
("land_cover_cut@1" = 16) * 0<br />
</pre><br />
<br />
<br />
Логика такого выражения проста: из всех слагаемых общей суммы только одно окажется не нулевым, в нём выражение вида ("land_cover_cut@1" = N) вернёт единицу, и умножением на коэффициент мы превратим пиксель в желаемое число дополнительных секунд на метр перемещения. Коэффициенты в данном случае выбирались в целом умозрительно (на уровне "по болоту идти тяжелее всего", а заведомо отсутствующие или уже учтённые (вода) на территории классы помечены как 0), не следует использовать их как авторитетные. Подбирайте их с умом и исходя из условий своей задачи (кто перемещается и как). <br />
<br />
Результат сохраняем в land_cover_reclassified.tif<br />
<br />
<br />
[[Файл:Ogis_dem_walking_18.png|600px|thumb|center|]]<br />
<br />
<br />
В результате, если покрасить результат по красному градиенту, выходит такая картина (насыщеннее цвет - тяжелее идти):<br />
<br />
<br />
[[Файл:Ogis_dem_walking_19.png|600px|thumb|center|]]<br />
<br />
<br />
Если запустить r.walk с новой friction cost поверхностью, результат окажется совершенно иным (оставляем все настройки без изменения, только вместо zero_frictions выбираем land_cover_reclassified). Обратите внимание на то, что максимальное время увеличилось более чем в два раза.<br />
<br />
<br />
[[Файл:Ogis_dem_walking_21.png|600px|thumb|center|]]<br />
<br />
<br />
Инструмент r.walk очень мощное, классическое средство для моделирования перемещений по пересеченной местности. Однако правильный подбор коэффициентов и настройка поверхности friction cost - зачастую тема для отдельного исследования.<br />
<br />
<br />
=== С использованием альтернативных функций перемещения и r.cost ===<br />
<br />
<br />
Существуют и альтернативные функции для расчёта перемещений по пересеченной местности. Одной из популярных является функция Тоблера для походов (Tobler's hiking function). Она связывает уклон поверхности и скорость перемещения по ней следующей формулой:<br />
<br />
<br />
[[Файл:Ogis_dem_walking_22.png|400px|thumb|center|]]<br />
<br />
<br />
Где<br />
<br />
<br />
[[Файл:Ogis_dem_walking_23.png|400px|thumb|center|]]<br />
<br />
<br />
И приняты обозначения:<br />
<br />
<pre><br />
W - скорость движения (км/ч),<br />
dh - разница высот,<br />
dx - расстояние,<br />
S - уклон,<br />
θ - угол уклона.<br />
</pre><br />
<br />
<br />
Построим график этой функции, выделив вертикальной линией угол уклона в 0 градусов:<br />
<br />
<br />
[[Файл:Ogis_dem_walking_24.png|600px|thumb|center|]]<br />
<br />
<br />
Логика функции ясна. Она ассиметрична относительно плоскости и наибольшая скорость достигается при уклоне вниз на примерно 2.86 градуса. Функция достаточно популярная, можно найти [https://gis.e-education.psu.edu/sites/default/files/capstone/Irtenkauf_596B_20140430.docx научные публикации], связанные с оценкой её точности по эмпирическим данным.<br />
<br />
Однако её ассиметричность для моделирования в ГИС крайне неудобна, если учесть, каким именно образом мы считаем углы уклона по DEM (у каждого пикселя мы определяем интегральный угол уклона, при использовании данной функции же важно, в какую именно сторону мы движемся по этому склону). Для полноценной реализации необходима интерактивная функция, как r.walk. Тем не менее мы можем, совершив некоторые допущения, рассчитать необходимое и по статическим характеристикам. Давайте предположим, что в задаче нашего моделирования каждый пиксель DEM используется для навигации во все стороны, и вниз, и вверх (что справедливо, если мы рассчитываем стоимость передвижения не только от начальной точки, но и обратно к ней). Тогда на каждом значении уклона можно просто осреднять значения функции Тоблера для положительного и отрицательного значений этого уклона. Например, если по DEM в пикселе был расчитан угол уклона равный 11 градусов, искомой скоростью перемещения будет среднее из значений функции Тоблера для уклонов в 11 и -11 градусов. Посмотрим, как функция поведёт себя в таком случае:<br />
<br />
<br />
[[Файл:Ogis_dem_walking_25.png|600px|thumb|center|]]<br />
<br />
<br />
Возьмём такую "осредненную" функцию за основу и построим модель перемещений согласно ей. Для этого возвращаемся в QGIS, и для начала расчитаем уклоны по маскированной ЦМР (DEM_masked). Для этого воспользуемся функцией Raster Terrain Analysis - Slope из панели анализа.<br />
<br />
<br />
[[Файл:Ogis_dem_walking_26.png|600px|thumb|center|]]<br />
<br />
<br />
Полученный слой в каждой ячейке содержит угол уклона в градусах. С помощью калькулятора растров рассчитаем тангенсы эти углов, предварительно преобразовав их в радианы. Результирующий слой назовём Slope_real.tif<br />
<br />
<pre><br />
tan ("Slope@1" / 180.0 * 3.141592)<br />
</pre><br />
<br />
<br />
[[Файл:Ogis_dem_walking_27.png|600px|thumb|center|]]<br />
<br />
<br />
Посмотрим на результат:<br />
<br />
<br />
[[Файл:Ogis_dem_walking_28.png|600px|thumb|center|]]<br />
<br />
<br />
К такому слою уже можно применять усредненную функцию Тоблера. Отдельно рассчитывать тангенсы отрицательных уклонов не нужно благодаря свойству tan(-A) = -tan(A). Запишем наконец в калькуляторе растров полное выражение!<br />
<br />
<pre><br />
(6*(2.718281^(-3.5*abs("Slope_real@1"+0.05))) + 6*(2.718281^(-3.5*abs(-1.0*"Slope_real@1"+0.05)))) / 2.0<br />
</pre><br />
<br />
Здесь было замечено, что растровый калькулятор QGIS иногда генерирует странные значения на таких сложных выражениях, проверьте ваш результат, в нём не должно быть ячеек со значениями больше 5.5. Если они есть, то калькулятор дал сбой, но это не беда, всегда можно воспользоваться калькулятором растров SAGA, который доступен в панели анализа как SAGA - Raster calculus - Raster calculator. Там выражение несколько изменится, вместо Slope_real@1 запишем a, предварительно выбрав его в списке как основной растр для расчётов.<br />
<br />
<pre><br />
(6*(2.718281^(-3.5*abs(a+0.05))) + 6*(2.718281^(-3.5*abs(-1.0*a+0.05)))) / 2.0<br />
</pre><br />
<br />
<br />
[[Файл:Ogis_dem_walking_30.png|600px|thumb|center|]]<br />
<br />
<br />
Примечание: Некоторые версии SAGA отказываются выполнять расчёт, если ничего не выбрано в меню Additional layers. Если вы получаете ошибку keyerror "XGRIDS", просто выберите в это поле любой слой, в формуле же ссылаться на него совершенно не обязательно.<br />
<br />
<br />
Результат сохраним как Tobler_speed.tif, так как в результирующем растре каждая ячейка будет хранить усредненную скорость перемещения по ней. Выглядит он так. В данном случае, чем насыщеннее цвет, тем быстрее скорость перемещения.<br />
<br />
<br />
[[Файл:Ogis_dem_walking_31.png|600px|thumb|center|]]<br />
<br />
<br />
Теперь пересчитаем скорость перемещения во время с учётом пространственного разрешения. Пространственное разрешение смотрим в свойствах слоя, оно равно 59.129х59.129 метров. Для пересчёта в минуты воспользуемся простой формулой (вместо 0.059129 подставляете пространственное разрешение своего набора данных в километрах), в которой учтём увеличение преодолеваемого расстояния с увеличением уклона (для этого поделим разрешение на косинус уклона), и переведем часы в минуты:<br />
<br />
<pre><br />
(0.059129 / cos ("Slope@1"*3.141592/180.0)) / "Tobler_speed@1" * 60.0<br />
</pre><br />
<br />
Результат сохраняем как Tobler_minutes - теперь каждая ячейка содержит время в минутах, которое требуется для её преодоления.<br />
<br />
<br />
[[Файл:Ogis_dem_walking_32.png|600px|thumb|center|]]<br />
<br />
<br />
На этом этапе можно добавить коэффициенты, в зависимости от задач, которые вы решаете. Если вы хотите просто изменять скорость перемещений, в калькуляторе растров достаточно умножать растр скоростей или минут на нужный коэффициент. Если необходимо учесть типы ландшафтов, вы готовите растр с коэффициентами замедления путём реклассификации карты типов земель, как это было в случае с r.walk, и умножаете растр скоростей или минут на него. Поскольку ничего нового в этой процедуре уже нет, её осуществление оставим на самостоятельную работу. Помните также о том, что у нас в наличии есть слой с уклонами, который также можно использовать в формулах с коэффициентами.<br />
<br />
Основная задача - в конце концов получить растр, содержащий в каждой ячейке затраты на её преодоление в минутах. В базовом виде это набор данных Tobler_minutes, который мы только что рассчитали.<br />
<br />
<br />
Последний шаг - расчёт собственно ценовой поверхности перемещений относительно нашего озера. В этом поможет модуль [https://grass.osgeo.org/grass72/manuals/r.cost.html r.cost], доступный в GRASS - Raster - r.cost в панели анализа. Он устроен гораздо проще, чем r.walk, но требует на вход поверхность со стоимостями преодоления каждой ячейки (которая у нас уже есть). В качестве Unit cost layer выбираем Tobler_minutes, из многообразия способов задать начальные точки выбираем слой lake_points в поле Start points, также активируем флаг "Use the Knight's move" - он позволит получить более точные результаты. Запускаем!<br />
<br />
<br />
[[Файл:Ogis_dem_walking_33.png|600px|thumb|center|]]<br />
<br />
<br />
В результате получаем уже знакомые наборы данных - Cumulative cost относительно исходных точек, и Movement Directions. Но теперь мы использовали собственную функцию!<br />
<br />
Отметим ещё раз, что при использовании r.walk вы получите '''время в секундах''', а при использовании второго подхода с r.cost - в тех единицах, к которым приведёте скорости сами.<br />
<br />
== Построение изохрон ==<br />
<br />
<br />
При наличии растра Cumulative cost построение изохрон - тривиальная задача. Для этого можно воспользоваться любым штатным инструментом построения изолиний по поверхностям, например SAGA - Vector <-> Raster - Contour lines в панели анализа. Зададим расстояние между изолиниями в 120 минут, и построим их для поверхности Cumulative cost, полученной по функции Тоблера. С тем же успехом мы могли использовать результат, полученный в r.walk.<br />
<br />
<br />
[[Файл:Ogis_dem_walking_34.png|600px|thumb|center|]]<br />
<br />
<br />
Настроив отображение, получаем такую картину (участвуют слои Bing Satellite, озёра, Cumulative cost по Тоблеру и изолинии по нему же).<br />
<br />
<br />
[[Файл:Ogis_dem_walking_35.png|600px|thumb|center|]]<br />
<br />
== Построение оптимального маршрута ==<br />
<br />
<br />
Для построения оптимального маршрута из точки в точку понадобится дополнительный модуль r.drain, доступный в GRASS - Raster - [https://grass.osgeo.org/grass72/manuals/r.drain.html r.drain] в панели анализа. Для начала необходимо с использованием любого из описанных выше подходов (r.walk или r.cost) построить Cumulative cost поверхность для одной из точек. Используем для этого слой start_points и функцию Тоблера. Благо, у нас уже есть готовая ценовая поверхность Tobler_minutes, и остаётся лишь посчитать r.cost по ней из слоя start_points.<br />
<br />
<br />
[[Файл:Ogis_dem_walking_36.png|600px|thumb|center|]]<br />
<br />
<br />
По полученной поверхности Cumulative cost можно сразу посмотреть время, которое потребуется, чтобы добраться до точки в end_point - просто используем инструмент идентификации при активном слое Cumulative cost в расположении конечной точки. 862 минуты!<br />
<br />
<br />
[[Файл:Ogis_dem_walking_37.png|600px|thumb|center|]]<br />
<br />
<br />
Теперь запускаем модуль r.drain. В качестве слоя Elevation выбираем полученный для начальной точки Cumulative cost, затем выбираем соответствующий ей Movement Directions (оба эти слоя были получены на предыдущем шаге), затем в vector layer containing staring points выбираем векторный слой end_point, до которого будет считаться маршрут, и обязательно ставим флаг "The input raster map is a cost surface". Часто интерфейс r.drain в QGIS немного глючит, и не запускается с выбранным векторным слоем - требует явно указать координаты точки/точек в поле Map coordinates of starting point(s). Если вы наблюдаете такое поведение, то просто с помощью кнопки справа от этого поля перейдите к интерактивному целеуказанию по карте и укажите конечную точку вручную.<br />
<br />
<br />
[[Файл:Ogis_dem_walking_38.png|600px|thumb|center|]]<br />
<br />
<br />
В результате будут сгенерированы два слоя: Least cost path - растр, в котором ячейками со значением 1 показаны места, по которым нужно идти (все остальные ячейки no data), и Drain - векторная линия, соответствующая этому пути. Просто нарисуем Drain поверх подложки, вот и оптимальный маршрут с учётом всех условий расчёта Cumulative cost поверхности.<br />
<br />
<br />
[[Файл:Ogis_dem_walking_39.png|600px|thumb|center|]]<br />
<br />
<br />
== Расчёт коридора наименьших затрат ==<br />
<br />
Трудно рассчитывать, что перемещение будет осуществляться чётко по оптимальной линии - скорее всего это будет некоторая совокупность оптимальных маршрутов, или, вернее сказать, область их содержащая. Она называется коридором наименьших затрат (Least cost corridor). Особенно часто такие коридоры строят те, кто изучает животных, рассчитывает области, где их можно вероятнее всего встретить, просчитывает пути миграции. На основе достигнутых нами ранее результатов построить такой коридор очень просто. Построим его между точками в start_point и end_point. Строим одним из описанных выше способов Cumulative cost поверхность для обеих точек (для start_point мы уже построили в предыдущем разделе), назовём их для различия Cumulative cost SP (start point) и Cumulative cost EP (end point). А теперь их просто складываем в калькуляторе растров:<br />
<br />
<pre><br />
"Cumulative cost SP@1" + "Cumulative cost EP@1"<br />
</pre><br />
<br />
Записываем результат в Cumulative_cost_both<br />
<br />
<br />
[[Файл:Ogis_dem_walking_40.png|600px|thumb|center|]]<br />
<br />
<br />
Получается вот такая объединенная поверхность.<br />
<br />
<br />
[[Файл:Ogis_dem_walking_41.png|600px|thumb|center|]]<br />
<br />
<br />
Теперь вспомним, сколько времени занимал оптимальный маршрут - порядка 860 минут. Допустим, мы готовы дать свободу в 40 дополнительных минут и посмотреть, в каких точках тогда можно будет наблюдать перемещение. То есть устанавливаем предельную цену в 900 (860+40) минут. В калькуляторе растров превратим Cumulative_cost_both в бинарный растр по порогу 900:<br />
<br />
<pre><br />
"Cumulative_cost_both@1" >= 900<br />
</pre><br />
<br />
Сохраним результат в corridor_900. Получился вот такой растр, у него в области коридора значения 0, а во всех остальных 1.<br />
<br />
<br />
[[Файл:Ogis_dem_walking_42.png|600px|thumb|center|]]<br />
<br />
<br />
Теперь либо через стилизацию растра, либо через преобразование растра в вектор можно без труда получить карту и статистику по коридору. Давайте создадим вектор. Для это воспользуемся модулем Polygonize, который доступен в GDAL - Raster conversion - Polygonize в панели анализа<br />
<br />
<br />
[[Файл:Ogis_dem_walking_43.png|600px|thumb|center|]]<br />
<br />
<br />
Из результирующего векторного слоя удаляем все объекты, у которых значение атрибута DN = 1<br />
<br />
<br />
[[Файл:Ogis_dem_walking_44.png|600px|thumb|center|]]<br />
<br />
<br />
С оставшимися объектами получаем такую картину:<br />
<br />
<br />
[[Файл:Ogis_dem_walking_45.png|600px|thumb|center|]]<br />
<br />
<br />
Заодно можем оценить площадь коридора: 1182 квадратных километра.<br />
<br />
<br />
== Заключение ==<br />
<br />
Благодаря модулям GRASS в открытых ГИС доступны мощные инструменты для моделирования перемещений по пересеченной местности, которые можно гибко настраивать, использовать большое количество дополнительных данных и адаптировать под свои задачи. Все наиболее популярные задачи - построение изохрон, расчёт оптимальных маршрутов и коридоров наименьших затрат успешно решаются и могут быть, при необходимости, автоматизированы. <br />
<br />
<br />
== Дополнительные сведения ==<br />
<br />
Для QGIS 2.* существует [https://sigsemgrilhetas.wordpress.com/plugins-qgis/walking-time/ плагин Walking Time], использующий функцию Тоблера для построения маршрута.<br />
<br />
Иногда при установке QGIS 3 под Windows из панели анализа недоступны инструменты SAGA. Для того, чтобы это исправить, можно установить QGIS через OSGeo4W Installer, выбрав в разделе desktop saga-ltr вместо saga по умолчанию.<br />
<br />
Существует попытка переписать r.walk для моделирования по функции Тоблера, чтобы это работало честно в интерактивном режиме, а не с допущениями, принятыми нами. Познакомиться с кодом, чтобы попробовать собрать решение у себя на компьютере, вы можете здесь: https://github.com/fxi/AccessMod_r.walk</div>Александр Мурыйhttps://wiki.gis-lab.info/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:%D0%90%D0%BB%D0%B5%D0%BA%D1%81%D0%B0%D0%BD%D0%B4%D1%80_%D0%9C%D1%83%D1%80%D1%8B%D0%B9&diff=26134Участник:Александр Мурый2018-05-29T10:36:14Z<p>Александр Мурый: </p>
<hr />
<div>'''Александр Мурый'''<br />
<br />
----------------------------------------<br />
''Контакты'':<br />
<br />
''[http://gis-lab.info/forum/memberlist.php?mode=viewprofile&u=8430 Профиль]'' на ГИС-Лаб<br />
<br />
''E-mail'': amuriy AT gmail DOT com<br />
<br />
------------------------------------------<br />
''На ГИС-Лаб'':<br />
<br />
* Редактор материалов ГИС-Лаб ([http://wiki.gis-lab.info/w/%D0%9E%D0%B1%D1%8F%D0%B7%D0%B0%D0%BD%D0%BD%D0%BE%D1%81%D1%82%D0%B8_%D0%B8_%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D1%8B_%D0%BA%D0%BE%D0%BE%D1%80%D0%B4%D0%B8%D0%BD%D0%B0%D1%82%D0%BE%D1%80%D0%B0_-_%D0%A0%D0%B5%D0%B4%D0%B0%D0%BA%D1%82%D0%BE%D1%80_%D0%BC%D0%B0%D1%82%D0%B5%D1%80%D0%B8%D0%B0%D0%BB%D0%BE%D0%B2 список обязанностей]).<br />
<br />
* Один из [http://gis-lab.info/forum/memberlist.php?mode=leaders модераторов] форума ГИС-Лаб.<br />
<br />
------------------------------------------<br />
<br />
''Текущие проекты'':</div>Александр Мурыйhttps://wiki.gis-lab.info/index.php?title=%D0%97%D0%B0%D1%8F%D0%B2%D0%BA%D0%B8_%D0%BD%D0%B0_%D0%BF%D1%83%D0%B1%D0%BB%D0%B8%D0%BA%D0%B0%D1%86%D0%B8%D1%8E_%D1%81%D1%82%D0%B0%D1%82%D0%B5%D0%B9&diff=25820Заявки на публикацию статей2018-02-27T08:43:03Z<p>Александр Мурый: </p>
<hr />
<div>{{Организационное|articles-request}}<br />
<br />
Внизу вы видите таблицу заявок на публикацию статей на GIS-Lab, она показывает статьи, которые их авторы считают более-менее готовыми к публикации. Таблица создана, чтобы сделать процесс принятия статей более прозрачным для авторов и оптимизировать работу редакторов. <br />
<br />
Таблица будет обновляться ведущим по мере поступления заявок от авторов. Заявкой на публикацию считается:<br />
* если статья оформляется на вики GIS-Lab — создание автором темы на форуме для обсуждения статьи (в разделе [http://gis-lab.info/forum/viewforum.php?f=3 Материалы сайта]);<br />
* если статья оформляется НЕ на вики GIS-Lab — личное обращение автора к [http://gis-lab.info/qa/org.html редактору] с предоставлением текста статьи.<br />
<br />
<br />
{| class="wikitable sortable" <br />
|-<br />
! width="5%" | Дата поступления заявки<br />
! width="30%" | Ссылка на вики, название <br />
! width="25%" | Автор (имя / ID)<br />
! width="25%" | Тема обсуждения на форуме (номер / название)<br />
! width="10%" | Дата создания<br />
|-<br />
| 18.09.2017<br />
| [http://wiki.gis-lab.info/w/%D0%9E%D0%BF%D1%8B%D1%82_%D1%81%D0%BE%D0%B7%D0%B4%D0%B0%D0%BD%D0%B8%D1%8F_%D1%81%D1%85%D0%B5%D0%BC%D1%8B_%D0%BF%D0%B0%D1%82%D1%80%D1%83%D0%BB%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F_%D1%82%D0%B5%D1%80%D1%80%D0%B8%D1%82%D0%BE%D1%80%D0%B8%D0%B8_%D0%B7%D0%B0%D0%BF%D0%BE%D0%B2%D0%B5%D0%B4%D0%BD%D0%B8%D0%BA%D0%B0_%22%D0%94%D0%B5%D0%BD%D0%B5%D0%B6%D0%BA%D0%B8%D0%BD_%D0%9A%D0%B0%D0%BC%D0%B5%D0%BD%D1%8C%22_%D0%B2_%D1%81%D1%80%D0%B5%D0%B4%D0%B5_ArcGis Опыт создания схемы патрулирования территории заповедника "Денежкин Камень" в среде ArcGIS]<br />
| [https://gis-lab.info/forum/memberlist.php?mode=viewprofile&u=7540 7540 ] / nadiopt<br />
| [https://gis-lab.info/forum/viewtopic.php?f=3&t=23692 23692] / Опыт создания схемы патрулирования территории заповедника "Денежкин Камень" в среде ArcGis<br />
| 22.02.2018 <br />
<br />
<br />
<br />
[[Категория:Служебные]]</div>Александр Мурыйhttps://wiki.gis-lab.info/index.php?title=%D0%9E%D0%B1%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D0%BA%D0%B0_%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85_%D0%B0%D1%8D%D1%80%D0%BE%D1%84%D0%BE%D1%82%D0%BE%D1%81%D1%8A%D0%B5%D0%BC%D0%BA%D0%B8_%D1%81%D1%80%D0%B5%D0%B4%D1%81%D1%82%D0%B2%D0%B0%D0%BC%D0%B8_%D0%BE%D1%82%D0%BA%D1%80%D1%8B%D1%82%D0%BE%D0%B3%D0%BE_%D0%BF%D0%B0%D0%BA%D0%B5%D1%82%D0%B0_OpenDroneMap&diff=25701Обработка данных аэрофотосъемки средствами открытого пакета OpenDroneMap2017-10-21T09:29:19Z<p>Александр Мурый: </p>
<hr />
<div>{{Статья|Опубликована|opendronemap-intro}}<br />
<br />
{{Аннотация|Установка OpenDroneMap на виртуальную машину и расчёт базовых продуктов (ортофотоплан, ЦММ, 3D-модель) по данным аэрофотосъемки с использованием планово-высотного обоснования и без него}}<br />
<br />
В связи с бурным развитием как фотограмметрических технологий, так и индустрии простых в освоении БПЛА оснащенных фото/видео-аппаратурой, у специалистов самых разных профилей стал расти интерес к возможностям организации аэрофотосъемки и обработки получаемых данных для дальнейшей работы с географическими продуктами, такими как ортофотопланы, цифровые модели местности, трёхмерные модели. На рынке представлено большое количество решений как аппаратных (преимущественно БПЛА), так и программных. Программные продукты для фотограмметрической обработки данных стали разрабатывать практически все крупные вендоры (Autodesk, Trimble, ...), также появилось множество новых компаний, продвигающих собственные пакеты (Agisoft, Pix4D, DroneDeploy, ...). Параллельно начали развиваться и проекты с открытым исходным кодом. Установка и использование одного из наиболее удачных - [http://opendronemap.org/ OpenDroneMap] - рассмотрено в представленной статье.<br />
<br />
С учётом доступности дешевых, простых в освоении и эффективных для многих задач БПЛА ([http://gis-lab.info/qa/aerial_photo_DJI_Phantom_3.html см. статью]), наличие достаточно мощного бесплатного пакета для обработки данных оказывается очень важным, т.к. позволяет начать работу с фотограмметрическими технологиями с минимальными финансовыми вложениями, что актуально для большого количество людей, работающих или почти бесплатно, или имеющих крайне ограниченное финансирование (добровольные пожарные, археологи, независимые исследователи, научные сотрудники географических институтов...).<br />
<br />
OpenDroneMap - технически сложное программное обеспечение, относительно просто которое можно развернуть только в среде Linux Ubuntu. Для того, чтобы доступ к нему мог получить любой пользователь вне зависимости от используемой им операционной системы, в статье подробно описан процесс установки и настройки Ubuntu в виртуальной среде средствами продукта Oracle VirtualBox, использовать который можно на любой современной ОС. Использование виртуальной машины рекомендуется также и тем, кто использует в качестве основной ОС Ubuntu, в целях изоляции пакета с большим количеством зависимостей от основной рабочей среды.<br />
<br />
==Подготовка среды==<br />
<br />
===Установка Oracle VirtualBox===<br />
<br />
Oracle VirtualBox - самый популярный в мире открытый настольный гипервизор виртуальных машин, то есть специальное программное обеспечение, предназначенное для создания и запуска виртуальных машин, полноценных операционных систем, работающих внутри вашего основного окружения. С помощью него вы можете работать, к примеру, одновременно с Windows, Linux и MacOS в трёх отдельных окнах, при этом каждая из операционных систем будет считать, что она запущена на отдельном полноценном компьютере. Каждой из виртуальных машин вы можете полноценно управлять, выделять ей строго определенный ресурс и так далее. Большим преимуществом работы с виртуальной машиной является изоляция рабочих сред, так, если при работе с виртуальной системой что-то пошло не так, вы можете просто остановить её, и это никак не отразится на вашей основной системе (которая в терминологии виртуализации называется хостом).<br />
<br />
Для начала нужно загрузить на свой компьютер дистрибутив операционной системы, которую необходимо установить как виртуальную машину. В нашем случае это Linux Ubuntu 16.04 LTS - один из наиболее распространненных дистрибутивов Linux, последняя стабильная версия. Дистрибутив доступен на сайте [http://ubuntu.ru/get ubuntu.ru/get]. Если вы работаете в Windows, с большой долей вероятности вы сможете установить только 32-х разрядную версию Ubuntu, что связано с особенностями виртуализации на системах Microsoft. При работе с Linux-хостом таких проблем обычно не бывает. Для унификации в описываемом примере загрузим именно 32-х разрядную версию.<br />
<br />
[[Файл:Opendronemap_1.png|center|thumb|600px|Сайт с ссылками на дистрибутивы Linux Ubuntu]]<br />
<br />
Теперь загрузим установочный файл Oracle VirtualBox с сайта https://www.virtualbox.org/wiki/Downloads в соответствии с вашей основной операционной системой.<br />
<br />
[[Файл:Opendronemap_2.png|center|thumb|600px|Сайт с ссылками на дистрибутивы Oracle VirtualBox]]<br />
<br />
Если вы работаете в Linux, VirtualBox устанавливается через пакетный менеджер, например<br />
<br />
<pre><br />
sudo apt-get install virtualbox<br />
</pre><br />
<br />
В Windows нужно пройти стандартный мастер установки, оставляя все опции по умолчанию:<br />
<br />
[[Файл:Opendronemap_3.png|center|thumb|600px|Установка VirtualBox в Windows (нажмите на изображение, чтобы увеличить)]]<br />
<br />
После установки, если вы не сняли флаг на последнем шаге, гипервизор запустится автоматически. Также его в любое время можно запустить стандартными средствами операционной системы.<br />
<br />
===Установка Ubuntu 16.04 LTS на VirtualBox===<br />
<br />
Приступаем к созданию виртуальной машины. Нажимаем кнопку "Создать" на главной панели VirtualBox:<br />
<br />
[[Файл:Opendronemap_4.png|center|thumb|600px|Создание новой виртуальной машины]]<br />
<br />
На первом этапе необходимо задать имя новой виртуальной машины и её тип. Примем название Ubuntu ODM (от OpenDroneMap) и выберем в списке Linux - Ubuntu (32-bit)"<br />
<br />
[[Файл:Opendronemap_5.png|center|thumb|600px|Настройки новой виртуальной машины]]<br />
<br />
На следующем этапе задаётся объем оперативной памяти, которое виртуальная машина сможет занимать от доступного хосту объема. Рекомендуется предоставить ей больше 4-х гигабайт оперативной памяти, например, 6. Это имеет смысл даже при работе с 32-х разрядной версией ОС, т.к. Ubuntu умеет использовать память свыше лимита, привычного в Windows. Если вы решили установить 64-х разрядную версию Ubuntu, тем более можете указать любой объем (не рекомендуется выделять виртуальной машине >65% всей доступной памяти, но если не сильно нагружать хост при работе с виртуальной машиной, то ничего катастрофического не произойдёт). При недостатке оперативной памяти на виртуальной машине (<= 4 ГБ) некоторые компоненты OpenDroneMap, возможно, не смогут быть установлены.<br />
<br />
[[Файл:Opendronemap_6.png|center|thumb|600px|Настройки новой виртуальной машины]] <br />
<br />
Далее следуют настройки виртуального жесткого диска. Последовательно выбираем "Создать новый жесткий диск", VDI, "Динамический жесткий виртуальный диск": <br />
<br />
[[Файл:Opendronemap_7.png|center|thumb|600px|Настройки новой виртуальной машины]] <br />
<br />
[[Файл:Opendronemap_8.png|center|thumb|600px|Настройки новой виртуальной машины]] <br />
<br />
[[Файл:Opendronemap_9.png|center|thumb|600px|Настройки новой виртуальной машины]] <br />
<br />
На следующем этапе нужно определить максимальное физическое место на жестком диске, которое будет позволено занять виртуальной машине, а также расположение файла виртуального жесткого диска. Обратите внимание, что по умолчанию виртуальный жесткий диск будет сохранен в моих документах (для windows) или директории home (для Linux). Часто это неподходящий вариант, особенно если у вас под систему выделен небольшой SSD, и домашние директории расположены на нём. Для хранения виртуального диска можно выбрать любое другое расположение.<br />
<br />
[[Файл:Opendronemap_10.png|center|thumb|600px|Настройки новой виртуальной машины]] <br />
<br />
Виртуальная машина создана, теперь нужно инициализировать её, собственно установив Linux Ubuntu. В основном окне VirualBox в списке слева появилась Ubuntu ODM. В контекстном меню, вызываемом нажатием правой кнопки мыши, доступны все инструменты управления. Перед инициализацией зайдём в меню "Настроить".<br />
<br />
[[Файл:Opendronemap_11.png|center|thumb|600px|Контекстное меню виртуальной машины]] <br />
<br />
На различных вкладках меню настроек вы можете изменить выделенное количество оперативной памяти, ядер процессора, видеопамяти и так далее. Нас сейчас интересует вкладка "Общие папки": необходимо настроить какое-то общее файловое хранилище, чтобы из виртуальной среды мы могли осуществлять доступ к собственным файлам на хосте. Переходим к "Общие папки" и нажимаем кнопку "Добавить новую общую папку".<br />
<br />
[[Файл:Opendronemap_12.png|center|thumb|600px|Добавление новой общей папки]]<br />
<br />
В открывшемся меню выбираем путь до папки на хосте, к которой открывается доступ, и её название в виртуальной среде. Обязательно отмечаем галочку "авто-подключение".<br />
<br />
[[Файл:Opendronemap_13.png|center|thumb|600px|Настройки новой общей папки]] <br />
<br />
В списке общих папок теперь видим добавленную:<br />
<br />
[[Файл:Opendronemap_14.png|center|thumb|600px|Добавление новой общей папки]] <br />
<br />
Теперь возвращаемся в главное меню VirtualBox и активируем опцию "Запустить" для нашей виртуальной машины:<br />
<br />
[[Файл:Opendronemap_15.png|center|thumb|600px|Запуск виртуальной машины]]<br />
<br />
В первом диалоге необходимо выбрать дистрибутив, загруженный ранее, из которого будет производиться установка операционной системы. Выбираем загруженный файл формата .iso<br />
<br />
[[Файл:Opendronemap_16.png|center|thumb|600px|Выбор дистрибутива для установки]]<br />
<br />
[[Файл:Opendronemap_17.png|center|thumb|600px|Выбор дистрибутива для установки]]<br />
<br />
[[Файл:Opendronemap_18.png|center|thumb|600px|Выбор дистрибутива для установки]]<br />
<br />
Загружается установочный мастер Ubuntu. На первом шаге выбираем слева язык и, затем, "Установить Ubuntu":<br />
<br />
[[Файл:Opendronemap_19.png|center|thumb|600px|Установка Ubuntu]]<br />
<br />
На следующем шаге устанавливаем оба флага:<br />
<br />
[[Файл:Opendronemap_20.png|center|thumb|600px|Установка Ubuntu]]<br />
<br />
Затем указываем "Стереть диск и установить Ubuntu". Не беспокойтесь, речь идёт о виртуальном диске, созданном несколько шагов назад. Ваша основная система и файлы на ней не могут пострадать.<br />
<br />
[[Файл:Opendronemap_21.png|center|thumb|600px|Установка Ubuntu]]<br />
<br />
Соглашаемся с предупреждением:<br />
<br />
[[Файл:Opendronemap_22.png|center|thumb|600px|Установка Ubuntu]]<br />
<br />
Выбираем часовой пояс и раскладку клавиатуры:<br />
<br />
[[Файл:Opendronemap_23.png|center|thumb|600px|Установка Ubuntu]]<br />
<br />
[[Файл:Opendronemap_24.png|center|thumb|600px|Установка Ubuntu]]<br />
<br />
Задаём имя пользователя и пароль. Пароль вам понадобится для установки приложений, так что его следует запомнить.<br />
<br />
[[Файл:Opendronemap_25.png|center|thumb|600px|Установка Ubuntu]]<br />
<br />
Дальше установка будет произведена автоматически.<br />
<br />
[[Файл:Opendronemap_26.png|center|thumb|600px|Установка Ubuntu]]<br />
<br />
По окончанию установки нужно будет перезагрузиться, нажав Enter:<br />
<br />
[[Файл:Opendronemap_27.png|center|thumb|600px|Установка Ubuntu]]<br />
<br />
[[Файл:Opendronemap_28.png|center|thumb|600px|Установка Ubuntu]]<br />
<br />
После этого вы сразу попадёте в интерфейс операционной системы. Поздравляю, вы развернули Ubuntu на виртуальной машине!<br />
<br />
[[Файл:Opendronemap_29.png|center|thumb|600px|Ubuntu установлена!]]<br />
<br />
Важные сочетания клавиш при работе с virtual-box:<br />
*Правый Ctrl возвращает курсор мыши в хост<br />
*Правый Ctrl+F: развернуть окно с виртуальной машиной в полноэкранный режим.<br />
<br />
===Настройка Ubuntu 16.04===<br />
<br />
Пришло время подготовить нашу свежеиспеченную систему к работе. Очень важно освоить базовые навыки работы с терминалом (командной строкой). Вызвать его можно, набрав в строке поиске Terminal (строка поиска вызывается нажатием на логотип Ubuntu в левом верхнем углу или по нажатию кнопки Windows на клавиатуре), либо воспользовавшись горячими клавишами: Ctrl+Alt+T.<br />
<br />
[[Файл:Opendronemap_30.png|center|thumb|600px|Запуск терминала]]<br />
<br />
Терминал - основное средство взаимодействия с системой. Выглядит так:<br />
<br />
[[Файл:Opendronemap_31.png|center|thumb|600px|Терминал]]<br />
<br />
Осмотритесь в системе. В правом меню вы можете вызвать оконный файловый менеджер, и управлять файлами как в Windows. Однако вернёмся в терминал. Начнём готовить систему с помощью последовательности команд. Сначала обновим списки в репозиториях с программным обеспечением для пакетного менеджера. Понадобится ввести пароль.<br />
<br />
<pre><br />
sudo apt-get update<br />
</pre><br />
<br />
Затем установим программу git<br />
<br />
<pre><br />
sudo apt-get install git<br />
</pre><br />
<br />
Здесь пакетный менеджер спросит у вас разрешения на установку. Соглашайтесь! Принимается и "Д", и "Y" (без кавычек).<br />
<br />
[[Файл:Opendronemap_32.png|center|thumb|600px|Установка пакета]]<br />
<br />
Также установим пакет meshlab, он нам понадобится для работы с результатами обработки.<br />
<br />
<pre><br />
sudo apt-get install meshlab<br />
</pre><br />
<br />
Немного технических вещей. Чтобы мы имели доступ к настроенной ранее общей папке, нужно добавить нашего пользователя в группу vboxsf. Вместо ekazakov используйте своё имя пользователя, введенное на этапе установки.<br />
<br />
<pre><br />
sudo usermod -G vboxsf -a ekazakov<br />
</pre><br />
<br />
Также нужно установить пакет улучшений работы VirtualBox с Ubuntu. Для этого выходим из полноэкранного режима виртуальной машины (правый Ctrl+F), если вы были в нём, в окне виртуальной машины сверху находим пункт меню "Devices" и выбираем там опцию "Install Guest Additions CD Image". Если у вас русифицированы эти пункты меню, можно ожидать, что это будет "Устройства" - "Установить дополнение гостевой ОС".<br />
<br />
[[Файл:Opendronemap_33.png|center|thumb|600px|Установка дополнений]]<br />
<br />
Сразу при активации этой опции вы получите предложение запустить установку дополнений. Соглашаемся и смотрим на процесс установки.<br />
<br />
Перезагружаем Ubuntu. Управление выключением и перезагрузкой осуществляется с помощью меню в правом верхнем углу, справа от часов.<br />
<br />
[[Файл:Opendronemap_34.png|center|thumb|600px|Меню с выключением и перезагрузкой]]<br />
<br />
После перезагрузки в файловом менеджере вам будет доступна общая папка в меню слева (в моём случае пункт silent_sf). Система готова к дальнейшим приключениям.<br />
<br />
[[Файл:Opendronemap_35.png|center|thumb|600px|Файловый менеджер]]<br />
<br />
===Установка OpenDroneMap на Ubuntu 16.04 LTS===<br />
<br />
Снова откроем терминал. Создадим папку для размещения OpenDroneMap, назовём её odm:<br />
<br />
<pre><br />
mkdir odm<br />
</pre><br />
<br />
Перейдем во вновь созданную папку<br />
<br />
<pre><br />
cd odm<br />
</pre><br />
<br />
И склонируем в неё все файлы OpenDroneMap с помощью git (точка в конце команды имеет смысл, не забудьте её скопировать тоже)<br />
<br />
<pre><br />
git clone https://github.com/OpenDroneMap/OpenDroneMap.git .<br />
</pre><br />
<br />
Размер загружаемого пакета весьма велик, несколько больше 1 гигабайта.<br />
<br />
После окончания загрузки настроим переменные среды:<br />
<br />
<pre><br />
export PYTHONPATH=$PYTHONPATH:`pwd`/SuperBuild/install/lib/python2.7/dist-packages:`pwd`/SuperBuild/src/opensfm<br />
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:`pwd`/SuperBuild/install/lib<br />
</pre><br />
<br />
Вообще говоря, лучше добавить строчки управления переменными средами в файл .bashrc, чтобы они выполнялись каждый раз автоматически при запуске системы. Это не обязательно, если у вас одноразовая задача или вам не лень выполнять модернизацию переменных сред при каждом входе в систему. Для этого возвращаемся в домашнюю папку:<br />
<br />
<pre><br />
cd ..<br />
</pre><br />
<br />
И открываем файл .bashrc в текстовом редакторе<br />
<br />
<pre><br />
gedit .bashrc<br />
</pre><br />
<br />
У вас откроется обыкновенный блокнот с кодом на языке bash. В самый конец документа поместите поместите две строки, заменив ekazakov на своё имя пользователя:<br />
<br />
<pre><br />
export PYTHONPATH=$PYTHONPATH:/home/ekazakov/odm/SuperBuild/install/lib/python2.7/dist-packages:/home/ekazakov/odm/SuperBuild/src/opensfm<br />
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/ekazakov/odm/SuperBuild/install/lib<br />
</pre><br />
<br />
Сохраняемся, закрываем блокнот. Возвращаемся обратно в директорию, в которую загрузили OpenDroneMap:<br />
<br />
<pre><br />
cd odm<br />
</pre><br />
<br />
И, наконец, запустим настройку и конфигурирование OpenDroneMap<br />
<br />
<pre><br />
./configure.sh install<br />
</pre><br />
<br />
На этом этапе будет происходить очень много всего, но без вашего участия. Запаситесь терпением минут на 15-20-30. Установка завершена!<br />
<br />
<br />
==OpenDroneMap==<br />
<br />
Собственно обработка данных и расчёт производных продуктов осуществляется запуском одной единственной команды. В терминале из директории размещения OpenDroneMap (в моём случае /home/ekazakov/odm) вызывается модуль run.py:<br />
<pre><br />
python run.py <имя проекта><br />
</pre><br />
<br />
Все настройки, режимы, ссылки на исходные данные и так далее указываются в виде ключей к этой команде. Всё просто и удобно.<br />
<br />
===Подготовка данных для расчёта без опорных точек===<br />
<br />
Посмотрим на запуск подробнее на конкретном примере. Самый распространненный среди любителей, занимающихся аэрофотосъёмкой с около-топографическими целями, вариант - имеется просто набор фотографий с заданным перекрытием, сделанных с какого-нибудь коптера в надир или почти в надир. Никакого наземного планово-высотного обоснования не обеспечивалось.<br />
<br />
[https://drive.google.com/file/d/0BztNh0nXzyjbVE1ETWMxczJUX2c/view?usp=sharing ЗАГРУЗИТЬ ПРИМЕР ДАННЫХ (1)]<br />
<br />
Пример простой и содержит 33 фотографии, выполненных аппаратом DJI Phantom 3 Advanced с высоты 150 метров в августе 2016 года, камера работала в автоматическом режиме.<br />
<br />
Для того, чтобы приступить к обработке, нужно подготовить папку для данных OpenDroneMap, в которой будут размещаться наборы данных. Используя визуальный файловый менеджер, создадим в домашней папке новую директрию, например odm_projects, в которой создадим ещё одну директорию odm_non_gcp, а в ней - images (в моём случае получается /home/ekazakov/odm_projects/odm_non_gcp/images). У проектов OpenDroneMap строгая организация директорий, исходные снимки он будет искать именно в папке images в корневой папке конкретного проекта. Схема из оригинальной документации:<br />
<br />
<pre><br />
|-- /path/to/project/<br />
|-- images/<br />
|-- img-1234.jpg<br />
|-- ...<br />
</pre><br />
<br />
Используя общую папку с хостом, на котором и лежат ваши фотографии (если вы работаете с примером, можно загрузить тестовый набор через браузер прямо внутри виртуальной машины и обойтись без общей папки), копируем их в images. Вот как результат выглядит у меня (обратите внимание на пути):<br />
<br />
[[Файл:Opendronemap_36.png|center|thumb|600px|Организация файлов и директорий]]<br />
<br />
В общем-то, организация данных для этого простого варианта завершена, ведь больше никаких исходных данных у нас нет. Но и этого достаточно, чтобы получить приятные результаты.<br />
<br />
===Расчёт базовых продуктов без опорных точек===<br />
<br />
Открываем терминал и переходим в директорию OpenDroneMap<br />
<br />
<pre><br />
cd odm<br />
</pre><br />
<br />
Запускаем расчёты командой, заменив ekazakov на своё имя пользователя:<br />
<br />
<pre><br />
python run.py odm_non_gcp --project-path /home/ekazakov/odm_projects --dtm<br />
</pre><br />
<br />
Здесь происходит следующее: запускается обработка с настройками по умолчанию для данных из набора odm_non_gcp, при этом ключом --project-path указывается расположение наборов данных. То есть в директории указанной после --project-path будет искаться директирия odm_non_gcp, в ней images, и уже там - исходные фотографии. Ключ --dtm указан для того, чтобы в сгенерировать цифровую модель местности (в данном случае только поверхности, см. также параметры --dem и --dsm). Более подробно о возможных ключах - в разделе "Список настроек для запуска ODM".<br />
<br />
После запуска в консоли вы сможете наблюдать ход выполнения всех операций, начиная с загрузки и масштабирования фотографий, заканчивая построением моделей. По окончанию работы вы получите сообщение<br />
<pre><br />
[INFO] OpenDroneMap app finished<br />
</pre><br />
<br />
Будем смотреть, что же получилось! Если заглянуть в директорую с проектом, заметим, что добавлось много всего<br />
<br />
[[Файл:Opendronemap_37.png|center|thumb|600px|Директория с проектом]]<br />
<br />
Здесь вы сможете найти все промежуточные расчеты, преобразованные исходные фотографии, текструры и многое другое. Особый интерес представляют следующие файлы:<br />
* /odm_orthophoto/odm_orthophoto.tif - ортофотоплан, сразу в GeoTiff, если ваши фотографии имели координаты в exif. Собственно, один из основных результатов, можно сразу открывать в любой ГИС и работать.<br />
* /odm_orthophoto/odm_orthophoto.png - ортофотоплан в PNG. Открывается любым штатным просмотрщиком фотографий.<br />
<br />
<br />
[[Файл:Opendronemap_39.png|center|thumb|600px|Ортофотоплан открытый в QGIS]]<br />
<br />
<br />
* /odm_dem/dtm.tif - цифровая модель местности (только поверхность), также в GeoTiff<br />
<br />
<br />
[[Файл:Opendronemap_40.png|center|thumb|600px|Цифровая модель местности открытая в QGIS]]<br />
<br />
<br />
* /odm_texturing/odm_textured_model_geo.obj - трёхмерная текстурированная модель. Можно сразу открыть и покрутить в meshlab, который мы установили ранее. Всё очень просто, запускаете meshlab из окна поиска приложений (как запускался терминал в начале), и стандартной кнопкой "Import mesh" открываете файл odm_textured_model_geo.obj<br />
<br />
[[Файл:Opendronemap_38.png|center|thumb|600px|Крутим трёхмерную текстурированную модель в meshlab]]<br />
<br />
===Подготовка данных для расчёта с опорными точками===<br />
<br />
Обычно при проведении аэрофотосъемки также проводятся геодезические работы по созданию планово-высотного обоснования (ПВО), чтобы фотограмметрические модели уравнивались с учётом реальных координат отдельных точек. Это позволяет получать модели более строгие с топографической точки зрения, и в нужной системе координат. Обычно ПВО представляет собой размещение на местности заметных объектов (в моём случае белых тарелочек, важно, чтобы объекты были однозначно идентифицируемы на фотоснимках) и вычисление их координат с высокой точностью геодезическими методами (в моём случае статические ГНСС-наблюдения от местной геодезической сети или полигонометрия). Разберёмся, как в OpenDroneMap строить модели с учётом опорных точек. <br />
<br />
Решение этой задачи несложное технически, но потребует некоторых временных затрат. Необходимо создать текстовый файл следующего содержания:<br />
<br />
<pre><br />
Информация о системе координат<br />
"Координата X точки в указанной системе координат" "Координата Y точки в указанной системе координат" "Высота точки" "Координата X точки в системе координат фотографии" "Координата Y точки в системе координат фотографии" "Имя файла с фотографией, для которого указывались данные"<br />
...<br />
"Координата X точки в указанной системе координат" "Координата Y точки в указанной системе координат" "Высота точки" "Координата X точки в системе координат фотографии" "Координата Y точки в системе координат фотографии" "Имя файла с фотографией, для которого указывались данные"<br />
</pre><br />
<br />
Важные моменты. Система координат описывается строкой вида "WGS84 UTM 36N" или "EPSG:4326"<br />
Первые три координаты в строке - те самые, полученные геодезическими методами. Координаты же в системе координат фотоснимка представляют собой следующее. Единица - один пиксель. Начало системы координат в левом верхнем углу фотографии, направление X - вправо, направление Y - вверх. Так, например, пиксель в семнадцатой строке и двадцатом столбце фотографии будет иметь координаты (20, 17). <br />
<br />
Задача заключается в том, чтобы описать все появления опорных точек на всех фотографиях. Так, если у вас пять опорных точек и пять фотографий, на каждую из которых попадают все пять точек, то записей в файле будет 25 штук. Для удобства описания можно использовать такую структуру файла:<br />
<br />
<pre><br />
Информация о системе координат<br />
x1 y1 z1 photo_x1 photo_y1 photo_name1<br />
x1 y1 z1 photo_x2 photo_y2 photo_name2<br />
x1 y1 z1 photo_x3 photo_y3 photo_name3<br />
x2 y2 z2 photo_x1 photo_y1 photo_name1<br />
x2 y2 z2 photo_x2 photo_y2 photo_name2<br />
x2 y2 z2 photo_x3 photo_y3 photo_name3<br />
<br />
...<br />
</pre><br />
<br />
Решение задачи предполагает следующие шаги:<br />
* Выбор фотографии<br />
* Визуальная идентификация на фотографии опорных точек, на неё попавших<br />
* Вычисление координат этих точек в системе координат фотоснимка<br />
* Запись соответствующих строк в файл<br />
* Переход к следующей фотографии<br />
<br />
При этом критически важным оказывается количество и пространственное распределение опорных точек (точки должны быть равномерно распределены по всей площади съемки, точками должны быть обеспечены границы съемки). <br />
<br />
[https://drive.google.com/file/d/0BztNh0nXzyjbb3J6YlRudzJjVHM/view?usp=sharing ЗАГРУЗИТЬ ПРИМЕР ДАННЫХ (2)]<br />
<br />
Итак, для примера возьмём фотографию из тестового набора (2) с именем DJI_0256.JPG. На ней видно две опорные точки из трёх.<br />
<br />
<br />
[[Файл:Opendronemap_41.png|center|thumb|600px|Опорные точки на фотоснимке]]<br />
<br />
<br />
Координаты точки у реки:<br />
<pre><br />
X: 406085.553<br />
Y: 6170547.408<br />
Z: 118.076<br />
</pre><br />
<br />
Координаты точки у кустов:<br />
<pre><br />
X: 406161.126<br />
Y: 6170444.479<br />
Z: 117.515<br />
</pre><br />
<br />
Теперь нужно вычислить координаты точек в системе координат фотоснимка. Для этого можно открыть фотографию в любом адекватном растровом редакторе, который умеет показывать координаты курсора мыши в пикселях. Используйте любой, например, Pinta, простой растровый редактор для linux, похожий на Paint.NET в windows. Можем тут же установить этот пакет через терминал<br />
<br />
<pre><br />
sudo apt-get install pinta<br />
</pre><br />
<br />
Если открыть изображение в Pinta, координаты курсора мыши вы увидите на верхней панели. Приближаемся к опорной точке, наводим курсор в её центр, переписываем координаты пикселя.<br />
<br />
[[Файл:Opendronemap_42.png|center|thumb|600px|Извлечение координат точки на фотоснимке]]<br />
<br />
Существует множество других способов извлечь координаты точки, но этот самый бесхитростный. Удобно, к примеру, измерять координаты пикселей в QGIS, добавляя нужные фотографии в проект с выбором системы координат проекта - тогда QGIS их нарисует с началом в 0;0 и условием 1 пиксель - 1 единица системы координат.<br />
<br />
Для рассмотренных снимка и точки формируется такая строка:<br />
<br />
<pre><br />
406161.126 6170444.479 117.515 2422 821 DJI_0256.JPG<br />
</pre><br />
<br />
Обращаем внимание на то, что имя фотографии приводится без пути.<br />
<br />
Таким образом обрабатываются точки на всех фотографиях. Для тестового набора данных (2) содержимое текстового привязочного файла такое:<br />
<pre><br />
WGS84 UTM 36N<br />
406085.553 6170547.408 118.076 1909 662 DJI_0310.JPG<br />
406085.553 6170547.408 118.076 1953 1131 DJI_0311.JPG<br />
406085.553 6170547.408 118.076 2001 1600 DJI_0312.JPG<br />
406085.553 6170547.408 118.076 2057 2057 DJI_0313.JPG<br />
406085.553 6170547.408 118.076 2111 2512 DJI_0314.JPG<br />
406085.553 6170547.408 118.076 2429 2571 DJI_0280.JPG<br />
406085.553 6170547.408 118.076 2443 2237 DJI_0279.JPG<br />
406085.553 6170547.408 118.076 928 1694 DJI_0255.JPG<br />
406085.553 6170547.408 118.076 886 1236 DJI_0254.JPG<br />
406085.553 6170547.408 118.076 840 758 DJI_0253.JPG<br />
406085.553 6170547.408 118.076 795 293 DJI_0252.JPG<br />
406085.553 6170547.408 118.076 3472 2215 DJI_0221.JPG<br />
406085.553 6170547.408 118.076 3497 1862 DJI_0220.JPG<br />
406085.553 6170547.408 118.076 3523 1456 DJI_0219.JPG<br />
406085.553 6170547.408 118.076 3549 1062 DJI_0218.JPG<br />
406085.553 6170547.408 118.076 3578 685 DJI_0217.JPG<br />
406085.553 6170547.408 118.076 3615 300 DJI_0216.JPG<br />
406219.544 6170487.534 117.541 2757 327 DJI_0314.JPG<br />
406219.544 6170487.534 117.541 2804 799 DJI_0315.JPG<br />
406219.544 6170487.534 117.541 2856 1264 DJI_0316.JPG<br />
406219.544 6170487.534 117.541 1590 2719 DJI_0275.JPG<br />
406219.544 6170487.534 117.541 1599 2323 DJI_0274.JPG<br />
406219.544 6170487.534 117.541 1613 1913 DJI_0273.JPG<br />
406219.544 6170487.534 117.541 1623 1477 DJI_0272.JPG<br />
406161.126 6170444.479 117.515 3459 197 DJI_0312.JPG<br />
406161.126 6170444.479 117.515 3497 672 DJI_0313.JPG<br />
406161.126 6170444.479 117.515 3538 1146 DJI_0314.JPG<br />
406161.126 6170444.479 117.515 3580 1606 DJI_0315.JPG<br />
406161.126 6170444.479 117.515 3629 2060 DJI_0316.JPG<br />
406161.126 6170444.479 117.515 2575 2191 DJI_0259.JPG<br />
406161.126 6170444.479 117.515 1955 2205 DJI_0218.JPG<br />
406161.126 6170444.479 117.515 1969 1835 DJI_0217.JPG<br />
406161.126 6170444.479 117.515 1986 1463 DJI_0216.JPG<br />
406161.126 6170444.479 117.515 2004 1115 DJI_0215.JPG<br />
406043.604,6170505.052,117.40,2908,421,DJI_0218.JPG<br />
406043.604,6170505.052,117.40,2890,826,DJI_0219.JPG<br />
406043.604,6170505.052,117.40,2871,1245,DJI_0220.JPG<br />
406043.604,6170505.052,117.40,2850,1609,DJI_0221.JPG<br />
406199.778,6170539.616,117.35,2037,1200,DJI_0315.JPG<br />
406199.778,6170539.616,117.35,1992,716,DJI_0314.JPG<br />
406199.778,6170539.616,117.35,1937,237,DJI_0313.JPG<br />
406199.778,6170539.616,117.35,1042,1778,DJI_0259.JPG<br />
406198.261,6170377.141,117.82,931,2036,DJI_0216.JPG<br />
406198.261,6170377.141,117.82,924,2408,DJI_0217.JPG<br />
406198.261,6170377.141,117.82,3484,1048,DJI_0258.JPG<br />
406198.261,6170377.141,117.82,3535,1475,DJI_0259.JPG<br />
406081.978,6170388.068,117.61,1064,1786,DJI_0220.JPG<br />
406081.978,6170388.068,117.61,1074,1369,DJI_0219.JPG<br />
406081.978,6170388.068,117.61,1081,964,DJI_0218.JPG<br />
406081.978,6170388.068,117.61,1091,568,DJI_0217.JPG<br />
406100.288,6170641.896,117.80,510,1559,DJI_0312.JPG<br />
406100.288,6170641.896,117.80,577,2015,DJI_0313.JPG<br />
406100.288,6170641.896,117.80,644,2473,DJI_0314.JPG<br />
406100.288,6170641.896,117.80,699,2913,DJI_0315.JPG<br />
406012.886,6170449.777,116.78,2415,1261,DJI_0252.JPG<br />
406012.886,6170449.777,116.78,2446,1723,DJI_0253.JPG<br />
406012.886,6170449.777,116.78,2035,1091,DJI_0221.JPG<br />
406012.886,6170449.777,116.78,2048,722,DJI_0220.JPG<br />
406012.886,6170449.777,116.78,2483,2182,DJI_0254.JPG<br />
405995.006,6170599.982,117.75,1259,2151,DJI_0310.JPG<br />
405995.006,6170599.982,117.75,1314,2610,DJI_0311.JPG<br />
405995.006,6170599.982,117.75,3258,1214,DJI_0280.JPG<br />
405995.006,6170599.982,117.75,3282,861,DJI_0279.JPG<br />
405995.006,6170599.982,117.75,3307,453,DJI_0278.JPG<br />
</pre><br />
<br />
Не обязательно отмечать все появления всех точек на всех фотографиях, это, очевидно, займёт очень много времени. По окончанию создания файла он аккуратно сохраняется и кладётся куда-нибудь рядом с исходными фотографиями. В архиве с примером точки описаны в файле GCP_list.txt<br />
<br />
===Расчёт базовых продутов с опорными точками===<br />
<br />
Запуск расчётов с опорными точками производится полностью аналогично запуску без оных, с одним дополнением: ключом --gcp и путём до соответствующего файла.<br />
<br />
Создадим в папке odm_projects новую директорию odm_gcp, в ней images, куда скопируем фотографии из нового набора. В odm_gcp также скопируем файл GCP_list.txt. В терминале возвращаемся в исходное положение, то есть переходим в директорию odm<br />
<br />
<pre><br />
cd odm<br />
</pre><br />
<br />
И запускаем расчёты, не забыв сослаться на файл с опорными точками и заменить ekazakov на ваше имя пользователя. И на этот раз воспользуемся ключом dsm, чтобы построить более честную, не фильтрованную модель местности.<br />
<br />
<pre><br />
python run.py odm_gcp --project-path /home/ekazakov/odm_projects --gcp /home/ekazakov/odm_projects/odm_gcp/GCP_list.txt --dsm<br />
</pre><br />
<br />
По окончанию расчётов изучаем результаты в директории /home/ekazakov/odm_projects/odm_gcp, в тех же подпапках, что и ранее. Обратите внимание на файл odm_georeferencing.txt, который сгенерировался в папке odm_georeferencing, там вы найдёте приведенные оценки точности.<br />
<br />
===Список основных настроек для запуска ODM===<br />
Здесь приведен список основых опций, доступных при запуске OpenDroneMap.<br />
Полный список (на англ.) доступен в [https://github.com/OpenDroneMap/OpenDroneMap/wiki/Run-Time-Parameters официальной документации] или по команде run.py -h<br />
<br />
<pre><br />
-h или --help вывести в консоль сообщение с информацией о возможных<br />
опциях запуска команды run.py.<br />
--images <путь> или -i <путь><br />
путь до папки с исходными изображениями<br />
--project-path <путь><br />
путь до папки с проектом<br />
--resize-to <целое число><br />
масштабирование изображения (по большей стороне)<br />
--skip-resize<br />
пропустить шаг масштабирования изображений. Вместо масштабированных изображений <br />
будут использованы оригинальныеthe resized folder<br />
--start-with <параметр> или -s <параметр><br />
С помощью параметра указать, с какой операции начать обработку. Может быть<br />
полезно, если нужно быстро получить частный результат, не дожидаясь полной<br />
обработки. Параметр может быть: resize | opensfm | slam | cmvs | pmvs |<br />
odm_meshing | odm_25dmeshing | mvs_texturing | odm_georeferencing | odm_orthophoto<br />
--end-with <параметр> или -e <параметр><br />
Аналогично, какой операцией закончить:resize | opensfm | slam | cmvs | pmvs |<br />
odm_meshing | odm_25dmeshing | mvs_texturing | odm_georeferencing | odm_orthophoto<br />
--rerun <параметр><br />
Перезапустить конкретную операцию. <br />
Параметр может быть:resize | opensfm | slam | cmvs | pmvs |<br />
odm_meshing | odm_25dmeshing | mvs_texturing | odm_georeferencing | odm_orthophoto<br />
--rerun-all Перезапуск всех операций<br />
--rerun-from <параметр><br />
С помощью параметра указать, с какой операции начать перезапуск.<br />
Параметр может быть:resize | opensfm | slam | cmvs | pmvs |<br />
odm_meshing | odm_25dmeshing | mvs_texturing | odm_georeferencing | odm_orthophoto<br />
--video <путь> Путь до видео файла для обработки<br />
--slam-config <путь><br />
Путь до конфигурационного файла orb-slam<br />
--force-focal <вещественное число><br />
Перезаписать информацию о фокусном расстоянии (число вместо взятого из изображений)<br />
--force-ccd <вешщественное число><br />
Перезаписать информацию о ширине матрицы ПЗС (число вместо взятого из изображений)<br />
--min-num-features <целое число><br />
Минимальное количество особых точек, извлекаемых из<br />
отдельного изображения. Чем больше - тем лучше результат,<br />
но медленнее обработка.<br />
Значение по умолчанию: 4000<br />
--matcher-threshold <процент><br />
Игнорировать сопоставленные ключевые точки, если перекрывающиеся изображения делят<br />
менее указанного количества процентов ключевых точек.<br />
Значение по умолчанию: 2.0<br />
--matcher-ratio <вещественное число><br />
Отношение расстояния до следующей наиболее подходящей ключевой точки<br />
Значение по умолчанию: 0.6<br />
--matcher-neighbors <целое число><br />
Количество ближайших изображений для предварительного<br />
сопоставления основанного на данных GPS, извлеченных<br />
из exif фотографий. Установить на 0 для того, чтобы<br />
пропустить предварительное сопоставление. Значение по<br />
умолчанию: 8<br />
--matcher-distance <целое число><br />
Порог расстояния (в метрах) для поиска изображений для<br />
предварительного сопоставления, основанного на данных GPS,<br />
извлеченных из exif фотографий. Установить на 0 вместе с<br />
matcher-neighbors для того, чтобы пропустить предварительное<br />
сопоставление. Значение по умолчанию: 0<br />
--opensfm-processes <целое положительное число><br />
Максимальное количество процессов для рекострукции плотного облака<br />
(чем больше, тем быстрее, но интенсивнее задействуются компьютерные ресурсы)<br />
Значение по умолчанию: 1<br />
--use-25dmesh Экспериментальная опция. Использовать 2.5-мерную пространственную триангуляцию<br />
для построения ортофотоплана.<br />
Использование этой опции может обеспечить лучшие результаты для плоских поверхностей.<br />
--use-pmvs Использовать pmvs для расчёта облака точек<br />
--cmvs-maxImages <целое число><br />
Максимальное количество изображений в кластере. Значение по умолчанию: 500<br />
--mesh-size <целое положительное число><br />
Максимальное количество вершин в выходной пространственной триангуляции<br />
Значение по умолчанию: 100000<br />
--mesh-wlop-iterations <целое положительное число><br />
Количество итераций процедуры WLOP. Чем больше значения, тем более сглаженной<br />
получается пространственная триангуляция. Применяется к 2.5-мерной триангуляции.<br />
Значение по умолчанию: 35<br />
--texturing-tone-mapping <параметр><br />
Использовать тоновую коррекцию, если параметр указан gamma, не использовать,<br />
если none. Значение по умолчанию: none<br />
--gcp <path string> Путь до файла с информацией о ПВО. См. соответствующий раздел статьи.<br />
По умолчанию не используется. <br />
--use-exif Использовать данные из exif вместо GCP, даже если файл с ними существует<br />
--dem Построить Цифромую модель рельефа используя морфологической фильтер PDAL<br />
--dtm Построить Цифровую модель территории (только поверхность земли) с использованием<br />
прогрессивного морфометрического фильтра.<br />
--dsm Построить Цифровую модель местности (поверхность + объекты) <br />
--dem-approximate Использовать прогрессивный морфологический фильтр для ЦМР.<br />
Расчёты с ним быстрее, но менее точные.<br />
--orthophoto-resolution <вещественное число большее 0><br />
Пространственное разрешение ортофотоплана в пикселях на метр<br />
Значение по умолчанию: 20.0 (то есть пять сантиметров на пиксель)<br />
--orthophoto-compression <параметр><br />
Вид сжатия для ортофотоплана.<br />
Возможные значения: JPEG, LZW, PACKBITS, DEFLATE, LZMA, NONE.<br />
Значение по умолчанию: DEFLATE<br />
--orthophoto-bigtiff <параметр><br />
Управление созданием ортофотоплана в формате BigTIFF или<br />
классический TIFF. BigTIFF - вариант для файлов больших<br />
чем 4 ГБ. Возможные значения параметра: YES, NO, IF_NEEDED,<br />
IF_SAFER. Для деталей см. спецификацию GDAL:<br />
https://www.gdal.org/frmt_gtiff.html for more info.<br />
Значение по умолчанию: IF_SAFER<br />
--build-overviews Создать обзорные файлы для ортофотопланы с помощью утилиты gdaladdo.<br />
--zip-results сжать результаты с помощью gunzip<br />
--verbose или -v Выводить в консоль дополнительную информацию о ходе обработки<br />
--time Создать файл с информацией о затраченном на обработку времени<br />
--version Вывести в консоль номер версии OpenDroneMap<br />
</pre></div>Александр Мурыйhttps://wiki.gis-lab.info/index.php?title=%D0%9E%D1%81%D0%BD%D0%BE%D0%B2%D0%BD%D1%8B%D0%B5_%D0%B3%D0%B5%D0%BE%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D0%B5&diff=25697Основные геоданные2017-10-18T20:12:23Z<p>Александр Мурый: </p>
<hr />
<div>{{Статья|Опубликована|data}}<br />
{{Аннотация|Перечень основных геоданных GIS-Lab}}<br />
<br />
Здесь представлен перечень основных свободных/открытых геоданных, которые описаны и/или доступны через GIS-Lab. Этот список не является полным перечнем всех существующих данных и не представляет перечень всех данных, которые можно получить на GIS-Lab. Здесь представлены только основные базовые категории данных с которыми вам, скорее всего, придется столкнуться. Полный список статей (50+) посвященных геоданным можно найти [http://gis-lab.info/qa.html#gis_data здесь]. <br />
<br />
На GIS-Lab мы не ставим целью просто переместить данные из точки А в точку Б (на наш сайт) и у нас нет цели создать всеобъемлющий перечень этих наборов. Данные представленные на сайте либо созданы нами самим в рамках личных и коллективных [http://gis-lab.info/projects.html проектов], либо проведена существенная работа по их преобразованию в удобный вид и/или документированию. <br />
<br />
==Учебные данные==<br />
Геоданные для обучения - будут полезны, если вы только начинаете пользоваться ГИС, для небольших экспериментов и тестирования.<br />
<br />
'''Geosample''' — учебный набор данных, включающий готовые наборы векторных и растровых слоёв в распространённых ГИС-форматах на одну и ту же территорию (четыре субъекта Российской Федерации: Кемеровская и Новосибирская области, Алтайский край и Республика Алтай).<br />
<br />
[http://gis-lab.info/qa/geosample.html Подробнее]<br />
<br />
[[Image:geosample-01.gif|700px|center|thumb|Набор Geosample в QGIS]]<br />
<br />
==Базовая картография==<br />
Базовая картография - это набор слоёв, часто используемых на картах в качестве основы: дороги, дома, границы населенных пунктов.<br />
<br />
'''OpenStreetMap (OSM)''' - открытая картографическая основа, создаваемая силами энтузиастов. Распространяется в нескольких распространенных векторных форматах в нарезке по регионам и странам бСССР. Включает готовые проекты для QGIS.<br />
<br />
[http://gis-lab.info/qa/osmshp.html Подробнее], [http://beryllium.gis-lab.info/project/osmshp/ скачать ESRI Shape], [http://gis-lab.info/projects/osm_dump/ скачать OSM XML и PBF].<br />
<br />
'''VMap0''' — векторная топографическая основа масштаба 1:1000000 (в 1 см 10 км), созданная National Geospatial-Intelligence Agency, США.<br />
<br />
[http://gis-lab.info/qa/vmap0-about.html Подробнее], [http://gis-lab.info/qa/vmap0.html скачать].<br />
<br />
'''VMap1''' - векторная топографическая основа масштаба 1:250'000, по уровню детализации примерно соответствует ТК ГШ масштаба 1:500'000 (данные распространяются фрагментами).<br />
<br />
[http://gis-lab.info/qa/vmap1.html Подробно]<br />
<br />
[[Файл:OSM shp qgis mapnik lc.png|600px|thumb|center|Базовый проект с данными OpenStreetMap в формате ESRI Shape в QGIS]]<br />
<br />
==Рельеф==<br />
Данные о рельефе представляют собой растровые пространственные данные, где каждому элементу растра (пикселу) соответствует высота над поверхностью принятой модели геоида.<br />
<br />
'''SRTM''' (Shuttle Radar Topography Mission) — пожалуй, самая известная цифровая модель рельефа. Имеет глобальный охват, за исключением территорий севернее 60° с.ш. и южнее 54° ю.ш. Первая известная модель имела разрешение на большей части планеты порядка 90 м (3 угловые секунды), что было результатом намеренного загрубления исходных данных. 23 сентября 2014 года Белый дом (США) объявил об открытии исходных данных с разрешением порядка 30 метров (1 угловая секунда) для всей территории Земли, где проводилась съемка. В настоящее время большая территории планеты доступна с разрешением 1 угловая секунда.<br />
<br />
[http://gis-lab.info/qa/srtm.html Подробнее]<br />
<br />
'''ASTER GDEM''' (ASTER Global Digital Elevation Model) - растровые матрицы разрешением 15 м на пиксел, на весь мир без исключений. <br />
<br />
[http://gis-lab.info/qa/aster-gdem.html Подробнее]<br />
<br />
'''ETOPO2''' - глобальная цифровая модель рельефа, включающая как наземный, так и подводный рельеф.<br />
<br />
[http://gis-lab.info/qa/etopo2.html Подробнее]<br />
<br />
<center>[[Файл:Relief-combine-01.jpg|700px|thumb|center|Результат комбинированирования цветового рельефа и теневой отмывки по данным SRTM (Кроноцкий заповедник)]] </center><br />
<br />
==Карты для навигаторов==<br />
<br />
'''Карты OpenStreetMap для GARMIN''' - сборки карт для навигаторов Garmin на базе данных из проекта OpenStreetMap. Сборка карт осуществляется автоматически, по возможности ежедневно. <br />
<br />
[http://gis-lab.info/data/mp/ Подробнее]<br />
<br />
'''Карты OpenStreetMap для СитиГИД''' - карты из проекта OpenStreetMap, преобразованные для навигационной системы СитиГИД. Регионы России, ближнего зарубежья и некоторые европейские страны.<br />
<br />
[http://peirce.gis-lab.info/ Подробнее]<br />
<br />
==Административно-территориальное деление==<br />
<br />
'''АТД России из проекта OpenStreetMap''' - основным источником открытых данных по границам муниципальных районов, субъектов федерации и др. единиц административно-территориального деления является проект OpenStreetMap, где они в свою очередь появились после [http://gis-lab.info/qa/rusbounds-rosreestr.html одного] из коллективных проектов GIS-Lab. Данные редактируются самими пользователями.<br />
<br />
[http://gis-lab.info/qa/osm-adm.html Подробнее]<br />
<br />
'''Генерализованные границы АТД''' - если вам нужны упрощенные границы субъектов федерации для небольшой карты и вам не нужна максимальная детализация.<br />
<br />
[http://gis-lab.info/qa/rusbounds-rosreestr-gen.html Подробнее]<br />
<br />
[[Image:Atd-generalize-example.png|630px|thumb|center|АТД и их упрощение]]<br />
<br />
==Космическая съемка==<br />
<br />
'''Landsat''' - глобальные данные космического наблюдения поверхности Земли разрешения от 15 метров (панхроматическое), 30 метров (спектрозональное). Основной набор данных, к которому придется обратиться в первую очередь, если вас интересует более-менее значительный регион. Здесь можно найти данные в виде мозаик на большие участки суши. Получение свежих и архивных данных описано [http://gis-lab.info/qa/landsat-glovis.html отдельно].<br />
<br />
[http://gis-lab.info/qa/landsat-mosaics.html Подробнее]<br />
<br />
'''ASTER''' - так же глобальные данные, но с пробелами в покрытии, разрешение 15 метров (спектрозональное). Значительный объем исходных данных можно получить здесь на сайте.<br />
<br />
[http://gis-lab.info/projects/aster/about.html Подробнее]<br />
<br />
'''OrbView-3''' - данные высокого разрешения 1-2 метра, разумеется покрытие не сплошное, но данных очень много. Здесь можно ознакомиться с глобальным каталогом доступных данных и ссылками как их скачать.<br />
<br />
[http://gis-lab.info/qa/orbview3.html Подробнее]<br />
<br />
''' Corona ''' - источник исторической спутниковой информации среднего и высокого разрешения рассекреченный Министерством Обороны США и находящийся в открытом доступе.<br />
<br />
[http://gis-lab.info/qa/corona.html Подробнее]<br />
<br />
''' MODIS Blue Marble Next Generation ''' - набор данных о рельефе и растительном покрове Земли глобального охвата, в первую очередь может быть полезен для иллюстративных и образовательных целей.<br />
<br />
[http://gis-lab.info/qa/bluemarble.html Подробнее]<br />
<br />
[[Image:bluemarble-01.gif|750px|center|thumb|Разграфка данных MODIS Blue Marble]]<br />
<br />
==Тематические данные==<br />
<br />
''' Открытые данные по границам ООПТ федерального подчинения РФ''' - границы особо охраняемых природных территорий (ООПТ) федерального подчинения в пределах РФ.<br />
<br />
[http://gis-lab.info/qa/oopt.html Подробнее]<br />
<br />
[[Файл:Oopt map rus big.jpg|700px|thumb|center|Карта федеральных ООПТ выпущенная в 2005 г. на основе описываемых данных]]<br />
<br />
''' Источники метеорологических данных на территорию РФ ''' - краткая информация и перечень источников, где можно получить метеорологические данные по станциям на территории РФ.<br />
<br />
[http://gis-lab.info/qa/meteo-station-sources.html Подробнее]<br />
<br />
'''Почвенные карты глобального охвата''' - несколько источников с почвенными картами на весь мир масштабов: 1:1'000'000 - 1:25'000'000.<br />
<br />
[http://gis-lab.info/qa/world-soil-maps.html Подробнее]<br />
<br />
[[Image:soils-50km-fao-09.gif|750px|thumb|center|Гармонизированная карта почв]]<br />
<br />
==Разграфки==<br />
<br />
'''Номенклатурные сетки-разграфки карт''' - полигональные векторные слои, представляющие официальную номенклатуру, где каждый полигон описывает границы одного стандартного листа.<br />
Масштабы: 1:1 000 000, 1:500 000, 1:200 000, 1:100 000, 1:50 000, 1:25 000<br />
<br />
[http://gis-lab.info/qa/topogrids.html Подробнее]<br />
<br />
''' Сетка-разграфка 1х1 и 5х5 градусов для данных SRTM '''. <br />
<br />
[[Описание_и_получение_данных_SRTM#.D0.A1.D0.B5.D1.82.D0.BA.D0.B8-.D1.80.D0.B0.D0.B7.D0.B3.D1.80.D0.B0.D1.84.D0.BA.D0.B8|Получение данных]]<br />
<br />
''' Разграфка WRS-1 и WRS-2 для данных Landsat ''' - описание системы разграфки данных и сама схема для загрузки.<br />
<br />
[http://gis-lab.info/qa/l7pathrowmap.html Подробнее]<br />
<br />
''' Схема зон UTM/GK ''' - позволяет определить, в какой зоне находятся ваши объекты, можно использовать разграфку зон Гаусса-Крюгера, Universal Transverse Mercator. Есть возможность скачать разграфку в формате: ESIR Shape, Mapinfo TAB, KMZ. <br />
<br />
[http://gis-lab.info/qa/kmgrids.html#.D0.A1.D1.85.D0.B5.D0.BC.D0.B0_.D0.B7.D0.BE.D0.BD_GK.2FUTM Подробная информация и получение данных]<br />
<br />
''' Схема фрагментов для продуктов MODIS 2G, 3, и 4 ''' - описание системы "нарезки" данных и сама схема для загрузки.<br />
<br />
[http://gis-lab.info/qa/modis-tiles.html Подробнее]<br />
<br />
[[Image:topogrids1.gif|500px|center|thumb|Стандартная номенклатурная разграфка топографических карт]]<br />
<br />
== Другое ==<br />
''' База EPSG в формате SQLite '''<br />
<br />
[https://gis-lab.info/data/epsg/EPSG_v9_2.7z Загрузить]</div>Александр Мурыйhttps://wiki.gis-lab.info/index.php?title=%D0%97%D0%B0%D1%8F%D0%B2%D0%BA%D0%B8_%D0%BD%D0%B0_%D0%BF%D1%83%D0%B1%D0%BB%D0%B8%D0%BA%D0%B0%D1%86%D0%B8%D1%8E_%D1%81%D1%82%D0%B0%D1%82%D0%B5%D0%B9&diff=25669Заявки на публикацию статей2017-09-18T09:34:27Z<p>Александр Мурый: </p>
<hr />
<div>{{Организационное|articles-request}}<br />
<br />
Внизу вы видите таблицу заявок на публикацию статей на GIS-Lab, она показывает статьи, которые их авторы считают более-менее готовыми к публикации. Таблица создана, чтобы сделать процесс принятия статей более прозрачным для авторов и оптимизировать работу редакторов. <br />
<br />
Таблица будет обновляться ведущим по мере поступления заявок от авторов. Заявкой на публикацию считается:<br />
* если статья оформляется на вики GIS-Lab — создание автором темы на форуме для обсуждения статьи (в разделе [http://gis-lab.info/forum/viewforum.php?f=3 Материалы сайта]);<br />
* если статья оформляется НЕ на вики GIS-Lab — личное обращение автора к [http://gis-lab.info/qa/org.html редактору] с предоставлением текста статьи.<br />
<br />
<br />
{| class="wikitable sortable" <br />
|-<br />
! width="5%" | Дата поступления заявки<br />
! width="30%" | Ссылка на вики, название <br />
! width="25%" | Автор (имя / ID)<br />
! width="25%" | Тема обсуждения на форуме (номер / название)<br />
! width="10%" | Дата создания<br />
|-<br />
| 18.09.2017<br />
| [http://wiki.gis-lab.info/w/%D0%9E%D0%B1%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D0%BA%D0%B0_%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85_%D0%B0%D1%8D%D1%80%D0%BE%D1%84%D0%BE%D1%82%D0%BE%D1%81%D1%8A%D0%B5%D0%BC%D0%BA%D0%B8_%D1%81%D1%80%D0%B5%D0%B4%D1%81%D1%82%D0%B2%D0%B0%D0%BC%D0%B8_%D0%BE%D1%82%D0%BA%D1%80%D1%8B%D1%82%D0%BE%D0%B3%D0%BE_%D0%BF%D0%B0%D0%BA%D0%B5%D1%82%D0%B0_OpenDroneMap Обработка данных аэрофотосъемки средствами открытого пакета OpenDroneMap]<br />
| [https://gis-lab.info/forum/memberlist.php?mode=viewprofile&u=16669 16669] / Эдуард Казаков<br />
| [https://gis-lab.info/forum/viewtopic.php?f=3&t=23067 23067 ] / Обработка данных аэрофотосъемки средствами открытого пакета OpenDroneMap<br />
| 26.07.2017 <br />
<br />
<br />
<br />
[[Категория:Служебные]]</div>Александр Мурыйhttps://wiki.gis-lab.info/index.php?title=%D0%9F%D1%80%D0%B8%D0%BC%D0%B5%D1%80%D1%8B_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F_%D0%B8%D0%BD%D1%81%D1%82%D1%80%D1%83%D0%BC%D0%B5%D0%BD%D1%82%D0%BE%D0%B2_GDAL&diff=25566Примеры использования инструментов GDAL2017-08-09T09:43:46Z<p>Александр Мурый: /* Работа с цветовой таблицей */</p>
<hr />
<div>{{Статья|Опубликована|gdal-examples}}<br />
{{Аннотация|Перечь примеров для справки}}<br />
<br />
[http://www.gdal.org/index.html GDAL/OGR] - библиотека для работы с географическими форматами данных. GDAL представляет собой набор утилит для обработки растровых данных, в то время, как OGR предназначена для работы с векторными форматами. В статье рассматриваются некоторые практические примеры применения утилит этой библиотеки для работы с растровыми данными.<br />
<br />
С библиотекой GDAL так же поставляется утилита ogr2ogr, предназначенная для работы с векторными данными. Примеры использования этой утилиты приводятся в [http://gis-lab.info/qa/ogr2ogr-examples.html другой статье].<br />
<br />
Если у вас есть свои часто используемые примеры - присылайте автору или дописывайте прямо здесь. <br />
<br />
__TOC__<br />
<br />
== Общие сведения ==<br />
<br />
Утилиты GDAL предназначены для конвертации растровых данных из одного формата в другой и выполнения над ними различных операций. Установить утилиты GDAL для Windows можно с [http://gis-lab.info/qa/qgis-osgeo4w.html помощью OSGeo4W]. Удобный визуальный интерфейс для утилит имеется в [http://gis-lab.info/qa/gdaltools.html GDALTools для QGIS]. В комплект GDAL входят следующие утилиты:<br />
<br />
*[http://www.gdal.org/gdal_calc.html gdal_calc.py] - растровый калькулятор, арифметические операции с растрами;<br />
*[http://www.gdal.org/gdal-config.html gdal-config] - получить опции необходимые для создания ПО использующего GDAL;<br />
*[http://www.gdal.org/gdal_contour.html gdal_contour] - получение изолиний по цифровым моделям рельефа (ЦМР);<br />
*[http://www.gdal.org/gdal_edit.html gdal_edit.py ] - редактировать информацию о растре;<br />
*[http://www.gdal.org/gdal_fillnodata.html gdal_fillnodata.py] - заполнение областей имеющих значение NODATA;<br />
*[http://www.gdal.org/gdal_merge.html gdal_merge.py] - создание мозаик и композитных изображений;<br />
*[http://www.gdal.org/gdal_polygonize.html gdal_polygonize.py] - векторизовать растр с получением полигонального слоя;<br />
*[http://www.gdal.org/gdal_proximity.html gdal_proximity.py] - расчитать растр близости;<br />
*[http://www.gdal.org/gdal_rasterize.html gdal_rasterize] - растеризация векторных данных;<br />
*[http://www.gdal.org/gdal_retile.html gdal_retile.py] - создать новый набор тайлов и/или перестроить пирамидные слои;<br />
*[http://www.gdal.org/gdal_grid.html gdal_grid] - создание ЦМР из векторных данных;<br />
*[http://www.gdal.org/gdal_sieve.html gdal_sieve.py] - фильтрация осколочных объектов растра;<br />
*[http://www.gdal.org/gdal_translate.html gdal_translate] - конвертация растров из формата в формат;<br />
*[http://www.gdal.org/gdal2tiles.html gdal2tiles.py] - создание тайловой структуры, KML и простого просмотровщика;<br />
*[http://www.gdal.org/gdaladdo.html gdaladdo] - добавление пирамидных слоёв (overview);<br />
*[http://www.gdal.org/gdalbuildvrt.html gdalbuildvrt] - создание виртуального растра (VRT) из набора;<br />
*[http://www.gdal.org/gdalcompare.html gdalcompare.py ] - сравнение двух изображений;<br />
*[http://www.gdal.org/gdaldem.html gdaldem] - набор инструментов для анализа и визуализации ЦМР;<br />
*[http://www.gdal.org/gdalinfo.html gdalinfo] - информация о растре;<br />
*[http://www.gdal.org/gdallocationinfo.html gdallocationinfo] - запросы информации к растровыми файлам;<br />
*[http://www.gdal.org/gdalmanage.html gdalmanage] - управление растровыми файлами (копирование, переименование, удаление и т.д.);<br />
*[http://www.gdal.org/gdalmove.html gdalmove.py] - трансформирование системы координат растра без ресэмплирования;<br />
*[http://www.gdal.org/gdalsrsinfo.html gdalsrsinfo] - показывается информацию по системе координат в разных форматах (WKT, PROJ.4 и др.);<br />
*[http://www.gdal.org/gdaltindex.html gdaltindex] - построить индекс фрагментов (тайлов) MapServer;<br />
*[http://www.gdal.org/gdaltransform.html gdaltransform]- трансформация координат;<br />
*[http://www.gdal.org/gdalwarp.html gdalwarp] - трансформация изображения в новую систему координат;<br />
*[http://www.gdal.org/nearblack.html nearblack] - конвертация черных/белых границ в нужное значение;<br />
*[http://www.gdal.org/pct2rgb.html pct2rgb.py] - конвертация 8-битных изображений с палитрой в 24-битные RGB изображений;<br />
*[http://www.gdal.org/rgb2pct.html rgb2pct.py] - конвертация 24-битных RGB изображений в 8-битные с палитрой;<br />
<br />
Поддерживаемые форматы и используемые ключи можно узнать просто набрав в командной строке имя одной из утилит. <br />
<br />
<pre>gdalinfo</pre><br />
<br />
В результате будет получена справка по использованию этой программы:<br />
<br />
<pre>Usage: gdalinfo [--help-general] [-mm] [-stats] [-nogcp] [-nomd]<br />
[-noct] [-checksum] [-mdd domain]* datasetname</pre><br />
<br />
Версию GDAL можно посмотреть командой:<br />
<br />
<pre>gdalinfo --version</pre><br />
<br />
Список форматов поддерживаемых утилитами GDAL можно посмотреть следующим образом: <br />
<br />
<pre>gdalinfo --formats</pre><br />
<br />
Cписок поддерживаемых форматов (список может отличаться как в большую, так и в меньшую сторону, поскольку зависит от того, были ли подключены/отключены соответствующие модули при компиляции программы):<br />
<br />
*GRASS (ro): GRASS Database Rasters (5.7+)<br />
*VRT (rw+): Virtual Raster<br />
*GTiff (rw+): GeoTIFF<br />
*HFA (rw+): Erdas Imagine Images (.img)<br />
*AIG (ro): Arc/Info Binary Grid<br />
*AAIGrid (rw): Arc/Info ASCII Grid<br />
*JPEG (rw): JPEG JFIF<br />
*MEM (rw+): In Memory Raster<br />
*GIF (rw): Graphics Interchange Format (.gif)<br />
*BMP (rw+): MS Windows Device Independent Bitmap<br />
*DIMAP (ro): SPOT DIMAP<br />
*PCIDSK (rw+): PCIDSK Database File<br />
*SRTMHGT (rw): SRTMHGT File Format<br />
*GMT (rw): GMT NetCDF Grid Format<br />
*HDF4 (ro): Hierarchical Data Format Release 4<br />
*HDF4Image (rw+): HDF4 Dataset<br />
*ENVI (rw+): ENVI .hdr Labelled<br />
*EHdr (rw+): ESRI .hdr Labelled<br />
<br />
==Конвертация==<br />
<br />
Извлечь три канала с номерами 1, 2, 3 в новый файл из исходного с перекомбинацией, в котором каналов может быть больше. <br />
<br />
<pre>gdal_translate -b 3 -b 2 -b 1 output.tif input.tif</pre><br />
<br />
В результате в текущем каталоге появится результат 3-х канальный файл output.tif. Или в цикле для например 46-канального (win):<br />
<br />
<pre>for /L %i in (1,1,46) DO gdal_translate -b %i input.tif output_%i.tif</pre><br />
<br />
Конвертация с обрезкой по заданным координатам:<br />
<br />
<pre>gdal_translate -of GTiff -projwin 75.081940 57.250275 89.869980 49.083084 input.tif output.tiff</pre><br />
<br />
Конвертация с компрессией и созданием [http://gis-lab.info/qa/tfw.html world-файла]:<br />
<br />
<pre>gdal_translate -co "COMPRESS=LZW" -co "worldfile=yes" input.tif output.tiff</pre><br />
<br />
Конвертация 16 битного одноканального растра в 8 битный:<br />
<br />
<pre>gdal_translate -scale -ot Byte input_16bit.tif output.tif</pre><br />
<br />
Пакетная конвертация всех JPG в TIF (Windows):<br />
<pre>for %i in (*.jpg) do gdal_translate %i %~ni.tif </pre><br />
<br />
==Стэки, композиты, мозаики==<br />
<br />
Создание композитного изображения из серии отдельных растров, каждый из которых в своем файле TIF. Разрешение выходного файла устанавливается по первому их растров. Таким образом, если первый канал 15 м, а остальные 30 м, то последние будут пересчитаны на 15 м. Чтобы указать, что каждый исходный растр должен попасть в отдельный слой (layer-stacking), а не мозаицирование, используется ключ -separate: <br />
<br />
<pre>gdal_merge.py -o output.tif band1.tif band2.tif band3.tif band4.tif band5.tif -separate</pre><br />
<br />
Для мозаицирования (объединения растров располагающихся в пространстве рядом друг с другом), этот ключ нужно убрать. Например чтобы склеить соседние фрагменты (тайлы) рельефа в единое поле:<br />
<br />
<pre>gdal_merge -o altay.tif srtm_53_02.tif srtm_53_03.tif srtm_54_02.tif srtm_54_03.tif</pre><br />
<br />
А так можно указать разрешение выходного растра и то, что использовать определенное значение - не нужно (чтобы поля не перекрывали значащие части):<br />
<br />
<pre>gdal_merge -n 0 -ps 0.00416 0.00416 -o output.tif in1.tif in2.tif in3.tif in4.tif</pre><br />
<br />
==Работа с NODATA==<br />
Конвертация с заменой одного значения на другое (обычно используется для NODATA): <br />
<br />
<pre>gdalwarp -srcnodata -999 -dstnodata 0 input.tif output.tif</pre><br />
<br />
Если в исходном растре nodata записано как nan <code>NoData Value=nan</code>, то конвертировать лучше так:<br />
<br />
<pre>gdalwarp -dstnodata nan input.tif output.tif</pre><br />
<br />
Как снять флаг NODATA? Допустим есть растр в котором часть пикселей имеют значение 0 и на них установлен флаг NODATA и нужно его убрать. Делается это так:<br />
<br />
<pre>gdal_translate -a_nodata none input_with_NoData.tif output_without_NoData.tif</pre><br />
<br />
==Работа с системами координат==<br />
===Переназначение системы координат===<br />
Если вам нужно просто перепрописать систему координат, без пересчета самого растра:<br />
<br />
<pre>gdal_edit -a_srs "EPSG:4326" input.tif</pre><br />
<br />
Если код неизвестен, можно указать описание системы координат в формате Proj.4:<br />
<br />
<pre>gdal_edit -a_srs "+proj=sinu +lon_0=0 +x_0=0 +y_0=0 +a=6371007.181 +b=6371007.181 +units=m +no_defs" input.tif</pre><br />
<br />
Узнать строку описания системы координат можно следующим образом:<br />
<br />
<pre>gdalsrsinfo template.tif</pre><br />
<br />
===Конвертация с перепроецированием===<br />
<br />
gdalwarp позволяет не только конвертировать данные из одного формата в другой, но и одновременно произвести перепроецирование данных из одной системы координат в другую. Для этого используются параметры:<br />
<br />
*-a_srs используется для указания системы координат (СК) для данных<br />
*-s_srs используется для перезаписи информации о системе координат<br />
*-t_srs перепроецирования данных в требуемую систему координат<br />
<br />
В самом простейшем случае это делается следующим образом (ключ -tr указывает разрешение целевого растра):<br />
<br />
<pre>gdalwarp.exe -tr 0.0083 0.0083 -t_srs "EPSG:4326" in.tif out.tif</pre><br />
<br />
Если EPSG-кода у СК конечного растра нет, то указать целевую СК можно так: <br />
<br />
<pre>gdalwarp.exe -t_srs "+proj=aea +lat_1=52 +lat_2=64 +lat_0=0 +lon_0=45 +x_0=8500000 +y_0=0 +ellps=krass +units=m +towgs84=28,-130,-95,0,0,0,0 +no_defs" in.tif out.tif</pre><br />
<br />
==Обрезка==<br />
Обрезка по векторному контуру c уменьшением охвата растра (реальная обрезка, а не просто заполнение ненужных областей значениями NODATA):<br />
<pre>gdalwarp -cutline aoi.shp -crop_to_cutline input.tif output.tif</pre><br />
<br />
==Работа с рельефом==<br />
Теневая отмывка рельефа: <br />
<br />
<pre>gdaldem hillshade altay.tif altayhill.tif -z 5 -s 111120</pre><br />
Ключ <code>-s 111120</code> используется для пересчета для растров сделанных в EPSG:4326 в метровые СК. Если исходник уже находится в проекции, то он не нужен.<br />
<br />
Цветовая отмывка рельефа: <br />
<br />
<pre>gdaldem color-relief altay.tif ramp.txt altay-color.tif</pre><br />
<br />
Пример файла ramp.txt:<br />
<pre>5000 255 255 255<br />
1000 168 112 0<br />
650 198 165 48<br />
400 229 218 97<br />
200 218 229 97<br />
0 112 168 0</pre><br />
<br />
<br />
==Работа с цветовой таблицей==<br />
Если на входе есть растр с цветами в шкале серого (grayscale), а на выходе нужно получить изображение с индексированными цветами (палитрой),о можно воспользоваться подходом через VRT.<br />
<br />
=== С использованием VRT ===<br />
<br />
1. Создаем файл VRT:<br />
<pre>gdal_translate -of VRT input.tif input.vrt</pre><br />
<br />
2. Открываем файл VRT в любом текстовом редакторе, находим:<br />
<br />
<pre><ColorInterp>Gray</ColorInterp></pre><br />
<br />
и меняем на:<br />
<br />
<pre><ColorInterp>Palette</ColorInterp><br />
<ColorTable><br />
<Entry c1="0" c2="0" c3="128"/><br />
<Entry c1="0" c2="128" c3="0"/><br />
<Entry c1="0" c2="255" c3="0"/><br />
<Entry c1="153" c2="204" c3="0"/><br />
<Entry c1="131" c2="66" c3="37"/><br />
<Entry c1="51" c2="153" c3="102"/><br />
</ColorTable><br />
</pre><br />
<br />
Каждая Entry - это цвет, кодированный в RGB и соответствующий по порядку значениям от 0 до 255. Если цветов меньше 255, то остальные будут добавлены автоматически с RGB = 0,0,0 (черный).<br />
<br />
После того как таблица отредактирована, нужно сохранить ее обратно в растр:<br />
<pre>gdal_translate input.vrt result.tif</pre><br />
<br />
==Построение изолиний==<br />
Утилита gdal_contour используется для получения изолиний - линий равных значений по растровым данным.<br />
Полученные линии пересекают все пиксели с одинаковым значением, очерчивая при этом некоторую область. <br />
Чаще всего применяется для построения горизонталей рельефа из ЦМР.<br />
<br />
Построение контуров с интервалом в 5 единиц (Единица указывается в единицах измерения исходного растра):<br />
<br />
<pre>gdal_contour -i 5 mydem.tif contour.shp</pre><br />
<br />
Построение контуров из первого канала растра, с интервалом в 100 единиц начиная с 1200 и записью значения в поле elev:<br />
<br />
<pre>gdal_contour -b 1 -a elev -i 100 -off 1200 mydem.tif contour.shp</pre><br />
<br />
Построение только контуров с фиксированными значениями 1000, 1100 и 1120 и выводом результата в таблицу PostGIS<br />
<br />
<pre>gdal_contour -a elev -f PostgreSQL -fl 1000 1100 1120 -nln cont mydem.tif "PG:host=localhost user=iampg password=iampgpass dbname=iamgis"</pre><br />
<br />
==netCDF==<br />
<br />
Конвертирование в GeoTIFF:<br />
<br />
<pre>gdal_translate -of GTiff -b 1 NETCDF:precip.mon.mean.nc:precip b1.tif</pre><br />
<br />
Конвертирование в GeoTIFF с обрезкой по исходным координатам и созданием TFW (world-)файла:<br />
<br />
<pre>gdal_translate -of GTiff -srcwin 0 0 72 72 -co TFW=YES -b 1 NETCDF:precip.mon.mean.nc:precip b1.tif</pre><br />
<br />
Разбиение поднаборов данных ("SUBDATASET") на отдельные файлы netCDF (на выходе — файлы типа "example1", "example2" и т.д.):<br />
<pre>gdal_translate -sds example.nc example</pre><br />
<br />
==HDF4==<br />
<br />
В HDF4 распространяется множество данных дистанционного зондирования, например MODIS и ASTER. <br />
<br />
Импорт данных ASTER L1A: <br />
<br />
<pre>gdalwarp -overwrite -of GTiff HDF4_EOS:EOS_SWATH:"110601_081441.hdf":VNIR_Band1:ImageData b1.tif </pre><br />
<br />
Импорт данных MODIS Level 3,4:<br />
<br />
<pre>gdal_translate HDF4_EOS:EOS_GRID:"MCD12Q1.A2001001.h00v08.051.2014287161513.hdf":MOD12Q1:Land_Cover_Type_1 output.tif</pre><br />
<br />
Если одна из частей названия SDS (массива данных внутри HDF) имеет пробелы, ее нужно взять в кавычки. Использовать -geoloc для перепроецирования не нужно.<br />
<br />
==Расчеты==<br />
Для разнообразных пересчетов значений пикселей можно использовать gdal_calc. Например, такая команда сбросит в NODATA все пиксели чьё значение больше 16000:<br />
<br />
<pre>gdal_calc.bat -A input.tif --outfile=output.tif --calc="A*(A<16000)" --NoDataValue=0</pre><br />
<br />
Читается такое выражение следующим образом: для каждого пикселя растра input.tif, если его значение меньше 16000 - оставить его таким же, иначе - сбросить в 0.<br />
<br />
В выражениях могут использоваться логические "и" и "или", т.е. например в примере ниже: если значение пикселя больше или равно 249, но меньше 255 - сделать его единицей, остальные значения (включая 255) установить в 0.<br />
<br />
<pre>gdal_calc.bat -A input.tif --outfile=output.tif --calc="1*(logical_and(A>=249,A<255)) " --NoDataValue=0'</pre><br />
<br />
Следует обратить внимание, что часть ELSE выражения по умолчанию всегда будет сбрасывать остальные значения в 0. При этом, часть --NoDataValue=0 только говорит к какому значению нужно "приклеить" ярлык NODATA. Таким образом, если в выражении выше сказать, например, --NoDataValue=255, то, несмотря на то, что NODATA установится на 255, значения не удовлетворяющие условию в calc вовсе не станут равны 255, а останутся равны 0. Что бы установить остальные значения в число отличное от 0, нужно сделать это явным образом, например:<br />
<br />
<pre>gdal_calc --overwrite -A input.tif --outfile=input.tif --calc="1*(A==0) + 255*(A>0)" --NoDataValue=255</pre><br />
<br />
Присвоить пикселям со значением 0 значение 1, а остальным присвоить 255, установив на это значение тег NODATA.<br />
<br />
==Создание растров уменьшенного разрешения (т.н. quicklook, preview)==<br />
Создать для данных ДЗЗ высокого разрешения так называемый quicklook, т.е. привязанный растр для быстрого предварительного просмотра, можно с помощью gdal_translate, ему можно просто указать конечный размер процентах:<br />
<br />
<pre>gdal_translate -of "JPEG" -outsize 20% 20% ALOS_example.tif ALOS_example_preview.jpg -co "WORLDFILE=YES"</pre><br />
<br />
Или с помощью gdalwarp, ему можно передать нужный размер пикселя в единицах системы координат, так же можно указать метод, которым будет происходить объединение значений (например усреднение):<br />
<br />
<pre>gdalwarp -tr 8000 8000 -r average input.tif output.tif</pre><br />
<br />
==Ссылки по теме==<br />
*[http://www.gdal.org/index.html Библиотеки GDAL/OGR]<br />
*[http://gis-lab.info/qa/ogr2ogr-examples.html Примеры использования ogr2ogr]</div>Александр Мурыйhttps://wiki.gis-lab.info/index.php?title=%D0%97%D0%B0%D1%8F%D0%B2%D0%BA%D0%B8_%D0%BD%D0%B0_%D0%BF%D1%83%D0%B1%D0%BB%D0%B8%D0%BA%D0%B0%D1%86%D0%B8%D1%8E_%D1%81%D1%82%D0%B0%D1%82%D0%B5%D0%B9&diff=25565Заявки на публикацию статей2017-08-06T05:03:11Z<p>Александр Мурый: </p>
<hr />
<div>{{Организационное|articles-request}}<br />
<br />
Внизу вы видите таблицу заявок на публикацию статей на GIS-Lab, она показывает статьи, которые их авторы считают более-менее готовыми к публикации. Таблица создана, чтобы сделать процесс принятия статей более прозрачным для авторов и оптимизировать работу редакторов. <br />
<br />
Таблица будет обновляться ведущим по мере поступления заявок от авторов. Заявкой на публикацию считается:<br />
* если статья оформляется на вики GIS-Lab — создание автором темы на форуме для обсуждения статьи (в разделе [http://gis-lab.info/forum/viewforum.php?f=3 Материалы сайта]);<br />
* если статья оформляется НЕ на вики GIS-Lab — личное обращение автора к [http://gis-lab.info/qa/org.html редактору] с предоставлением текста статьи.<br />
<br />
<br />
{| class="wikitable sortable" <br />
|-<br />
! width="5%" | Дата поступления заявки<br />
! width="30%" | Ссылка на вики, название <br />
! width="25%" | Автор (имя / ID)<br />
! width="25%" | Тема обсуждения на форуме (номер / название)<br />
! width="10%" | Дата создания<br />
|-<br />
<br />
<br />
<br />
[[Категория:Служебные]]</div>Александр Мурыйhttps://wiki.gis-lab.info/index.php?title=%D0%91%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D1%8F_%D0%BE%D1%86%D0%B5%D0%BD%D0%BA%D0%B0_%D1%82%D1%80%D0%B0%D0%BD%D1%81%D0%BF%D0%BE%D1%80%D1%82%D0%BD%D0%BE%D0%B9_%D0%B4%D0%BE%D1%81%D1%82%D1%83%D0%BF%D0%BD%D0%BE%D1%81%D1%82%D0%B8_%D1%81%D1%80%D0%B5%D0%B4%D1%81%D1%82%D0%B2%D0%B0%D0%BC%D0%B8_GRASS_GIS_%D0%B8_QGIS&diff=25564Базовая оценка транспортной доступности средствами GRASS GIS и QGIS2017-08-06T05:02:51Z<p>Александр Мурый: </p>
<hr />
<div>{{Статья|Опубликована|isochrone-map-grass-qgis}}<br />
<br />
{{Аннотация|Построение карт транспортной доступности на основе данных OpenStreetMap средствами открытых геоинформационных систем GRASS GIS и QGIS}}<br />
<br />
Картографирование транспортной доступности на основе данных о дорожной сети - одна из классических задач ГИС. Наиболее распространенным способом моделирования транспортной доступности является построение изохрон - линий равных затрат времени на преодоление пространства относительно заданных точек. В представленной статье обсуждается алгоритм построения изохрон по данным [http://www.openstreetmap.org/ OpenStreetMap] с использованием открытых ГИС [https://grass.osgeo.org/ GRASS GIS] и [http://qgis.org/ru/site/ QGIS]. В QGIS будет осуществляться подготовка данных и картографическое представление результатов, а в GRASS собственно моделирование. Всю работу можно выполнить целиком в GRASS, но, по мнению автора, общие манипуляции геоданными и представление картографических материалов удачнее и удобнее реализованы в QGIS.<br />
<br />
Описываемые в статье действия выполнялись в средах Ubuntu 14.04 LTS и Windows 8.1 (x64), GRASS GIS 7.2, QGIS 2.14.<br />
<br />
==Получение и подготовка данных==<br />
<br />
Для осуществления расчётов нам потребуется набор векторных линейных геоданных, содержащий информацию о дорожно-транспортной сети исследуемой территории. Заполучить подобную информацию можно различными способами: приобрести у специализированных поставщиков, найти в одном из источников открытых данных, оцифровать атлас автомобильных дорог, нарисовать по космическому снимку, и так далее (не забывайте про лицензии данных!). В данном случае мы воспользуемся данными OSM как достаточно качественными и подробными, и, что важно, доступными бесплатно и легально. Начать знакомство с OSM вы можете с [http://wiki.openstreetmap.org/wiki/RU:%D0%9E_%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D0%B5 этой страницы]. Этап подготовки данных являются ключевым: помимо технических этапов, здесь нам предстоит определить транспортные характеристики, которые будут использованы для моделирования.<br />
<br />
Для демонстрации принципов базового транспортного моделирования рассмотрим Тосненский район Ленинградской области. Транспорт: автомобиль.<br />
<br />
===Загрузка данных OSM===<br />
<br />
Для загрузки данных OSM существует множество возможностей. Одним из наиболее простых способов является загрузка уже подготовленных наборов данных по слоям в формате ESRI Shapefile. Популярные сервисы в сети: [http://download.geofabrik.de/ Geofabrik], где вы можете найти комплекты данных на весь мир, и [http://data.nextgis.com/osmshp/ NextGIS], где вы найдёте вместе с данными оформленные QGIS-проекты по субъектам РФ и странам СНГ. Также удобный способ быстро получить данные OSM - воспользоваться одним из соответствующих плагинов для QGIS, например, [http://plugins.qgis.org/plugins/OSMDownloader/ OSMDownloader]. При его установке в интерфейс QGIS добавится кнопка выделения прямоугольной области, для которой и будут загружены данные в указанное вами расположение. Для демонстрации в этой статье используется набор данных OSM от NextGIS на территорию Ленинградской области от 13 ноября 2016 года, доступный бесплатно ([https://drive.google.com/file/d/0BztNh0nXzyjbTUpNWVl4bk15Sjg/view?usp=sharing загрузить]).<br />
<br />
Для нашей задачи понадобятся только данные о дорожной сети (в случае работы с выгрузками NextGIS - слой ''highway-line'' из директории ''data''; в случае работы с OSMDownloader - все загруженные объекты линейного типа, у которых в атрибуте "Highway" содержится какое-либо значение). Для оформительских задач также может быть использован набор геоданных с границами административно-территориального деления (например, для обрезки изохрон по границам района). <br />
<br />
===Первичная подготовка данных в QGIS===<br />
<br />
Для начала подготовим данные по дорожной сети: обрежем их по границам района интереса, спроецируем куда нужно, отфильтруем лишнее. Распаковываем архив ''RU-LEN.7z'' в удобное расположение, лучше без кириллицы и пробелов в путях. Затем запускаем QGIS и добавляем два векторных слоя из папки .../RU-LEN/data/:<br />
* highway-line<br />
* boundary-polygon<br />
<br />
Нужно проявить осторожность с кодировками, и добавлять слои с кодировкой UTF-8 (кодировку можно выбрать в меню выбора векторного слоя при его добавлении). Выглядит это как-то так (цвета могут быть произвольными):<br />
<br />
[[Файл:Grass_qgis_isochrones_map_raw.png|600px|thumb|center|]]<br />
<br />
В первую очередь, избавимся от всего лишнего. Найдём полигон Тосненского района и сохраним его в отдельный слой. Для этого в таблице атрибутов слоя ''boundary-polygon'' (доступно через контекстное меню слоя) найдём объект с соответствующим содержанием поля NAME. Для этого можно воспользоваться поиском по запросу, либо просто отсортировать колонку и выделить нужную строку. Затем через контекстное меню слоя выбираем "Сохранить как", определяя новое расположение для файла с границами Тосненского района (в моём случае Tosnensky_boundary.shp), и не забываем активировать флаг "Сохранить только выделенные объекты".<br />
<br />
<br />
[[Файл:Grass_qgis_isochrones_table_selection.png|400px|thumb|center|]]<br />
[[Файл:Grass_qgis_isochrones_save_as.png|400px|thumb|center|]]<br />
<br />
Затем необходимо обрезать набор геоданных по дорожной сети по этим границам. Для этого воспользуемся инструментом обрезки (Вектор - Инструменты геообработки - Обрезка). В качестве исходного слоя выбираем ''highway-line'', в качестве слоя обрезки ''Tosnensky_bondary'', результат сохраняем в удобное новое расположение (в моём случае Tosnensky_roads.shp).<br />
<br />
[[Файл:Grass_qgis_isochrones_clip.png|400px|thumb|center|]]<br />
<br />
Теперь, чтобы избежать возможных проблем с расчётом длин участков дорог (и некоторых других) в будущем, перепроецируем результирующий слой ''Tosnensky_roads'' в подходящую систему координат. Для расчётных задач рекомендуется использовать поперечную цилиндрическую проекцию (UTM или Гаусса-Крюгера) для вашей зоны. В данном случае используем WGS 84 / UTM zone 36N (EPSG:32636), для этого через меню "Сохранить как" создаем копию слоя ''Tosnensky_roads'', в выпадающем списке "Система координат", выбрав UTM zone 36N и задав удобное расположение для нового файла (Tosnensku_roads_utm).<br />
<br />
[[Файл:Grass_qgis_isochrones_reproject_roads.png|400px|thumb|center|]]<br />
<br />
Аналогичным образом перепроецируем слой с границами Тосненского района, на всякий случай (получаем дополнительно Tosnensky_boundary_utm). Данные почти готовы! Теперь нужно отфильтровать геоданные по дорогам с учётом требований к моделированию. Это первый тематический шаг подготовки: '''отбор только тех объектов дорожной сети, которые могут быть использованы для перемещения предполагаемыми транспортными средствами'''. Здесь вы должны решить, по объектам каких типов будет разрешено перемещаться, например, для моделирования пешеходного движения можно оставить все типы дорог; для легковых автомобилей исключить пешеходные дорожки, тропы, просеки; для грузовых автомобилей исключить дороги с соответствующими ограничениями на массу, и так далее. Шаг этот очень ответственный. При этом подход к фильтрации сильно зависит от качества исходных данных и их детализации. Вспоминаем, что имеем дело с OSM, где различия между объектами существуют на уровне тегов, которые записаны в таблице атрибутов в поле HIGHWAY. Посмотрим на список уникальных значений для нашего набора данных:<br />
<pre><br />
path steps footway residential primary service unclassified secondary road track construction tertiary raceway tertiary_link trunk secondary_link proposed pedestrian bridleway primary_link living_street trunk_link<br />
</pre><br />
<br />
Все эти теги [http://wiki.openstreetmap.org/wiki/Key:highway описаны на ресурсах OSM], даже с картинками. Нам ещё предстоит вернуться к этим описаниям, а сейчас важно определить, по объектам каких типов может нормально передвигаться легковой автомобиль, а по каким нет. Очевидно, что нужно исключить объекты следующих типов (пригодны только для пешеходов, проектируются, строятся и т.д.):<br />
<pre><br />
path, steps, footway, construction, proposed, pedestrian, bridleway<br />
</pre><br />
<br />
Также много объектов с типом ''unclassified''. При осуществлении серьезной работы, конечно, стоит прояснить природу каждого из таких объектов по вспомогательным данным (и внести информацию в OSM, конечно же). В данном случае оставим их как подходящие для работы. Объекты всех отобранных типов нужно удалить. Для этого переходим в таблицу атрибутов слоя Tosnensky_roads_utm, выбираем инструмент "Выбрать по выражению" и формируем запрос вида:<br />
<pre><br />
"HIGHWAY" IN ( 'path' , 'steps' , 'footway' , 'construction' , 'proposed' , 'pedestrian' , 'bridleway' )<br />
</pre><br />
<br />
отмечая в списке, соответственно, выбранные вами типы дорог, которые должны быть проигнорированы. Нажимаем кнопку "выбрать" и видим на карте лишние дороги.<br />
<br />
[[Файл:Grass_qgis_isochrones_for_deleting.png|600px|thumb|center|]]<br />
<br />
Переходим в режим редактирования и удаляем выбранные объекты, сохраняем изменения. Если что, все исходные данные остаются в других слоях. Общая подготовка закончена.<br />
<br />
<br />
===Определение транспортных характеристик данных о дорожной сети в QGIS===<br />
<br />
Второй этап подготовки является самым важным и трудоёмким в реальных условиях: необходимо каждому участку дороги назначить некоторую среднюю скорость перемещения по нему. Для примера мы используем простую логику: каждому семейству объектов (по тегу) назначим общую ожидаемую скорость, не вникая в каждый отдельный объект. При серьезном исследовании, опять же, стоит более внимательно отнестись к этому этапу, по возможности используя дополнительные материалы. Итак, рассмотрим те категории, которые у нас остались после фильтрации:<br />
<br />
* trunk - важнейшие и крупнейшие дороги, например, для нашей территории, Московское шоссе. Ожидаемая скорость 90 км/ч<br />
* primary - крупные шоссе, следующий уровень после trunk. Ожидаемая скорость 90 км/ч<br />
* secondary - относительно крупные дороги, следующий уровень после primary. Ожидаемая скорость 60 км/ч<br />
* tertiary - обычные автомобильные дороги между небольшими населенными пунктами. Ожидаемая скорость 60 км/ч <br />
* living_street - жилые зоны, где у пешеходов явное преимущество в праве передвижения. Ожидаемая скорость 15 км/ч<br />
* residential - автомобильные дороги в жилых кварталах. Ожидаемая скорость около 40 км/ч<br />
* service - сервисные подъезды, въезды и проч. Ожидаемая скорость 30 км/ч<br />
* road - автомобильная дорога неизвестного типа. Примем скорость 60 км/ч<br />
* track - грунтовые дороги, обычно для сельхоз-техники. Примем скорость 30 км/ч<br />
* raceway - дороги для автомобильных видов спорта. Примем скорость 90 км/ч <br />
* tertiary_link - участки, соединяющие tertiary с другими tertiary или дорогами других типов. Примем скорость 40 км/ч<br />
* secondary_link - участки, соединяющие secondary с другими secondary или дорогами других типов. Примем скорость 40 км/ч<br />
* primary_link - участки, соединяющие primary с другими primary или дорогами других типов. Примем скорость 40 км/ч<br />
* trunk_link - участки, соединяющие trunk с другими trunk или дорогами других типов. Примем скорость 40 км/ч<br />
* unclassified - дороги без тега. Примем скорость 40 км/ч<br />
<br />
Принятые значения достаточно условны, но позволят продемонстрировать принципы дальнейшей работы. Назначаемые скорости довольно существенно зависят от конкретной территории, местного законодательства и так далее. Важно заметить, что у некоторых участков (обычно их немного) в свойствах заполнено значение MAXSPEED, оно может быть как численным (в км/ч), так и вида RU:urban. Расшифровку этих обозначений можно найти, к примеру, [http://wiki.openstreetmap.org/wiki/RU:Key:source:maxspeed здесь] и использовать при определении скоростей. '''В целом этот этап является ключевым, и в боевых условиях здесь важно внимательно и тщательно определить характеристики дорожной сети'''.<br />
<br />
Для того, чтобы применить принятые характеристики, воспользуемся калькулятором полей, запустив его из таблицы атрибутов слоя ''Tosnensky_roads_utm''. Создадим новый атрибут SPEED как целочисленный и заполним его следующим выражением:<br />
<pre><br />
CASE<br />
WHEN "HIGHWAY" = 'trunk' THEN 90<br />
WHEN "HIGHWAY" = 'primary' THEN 90<br />
WHEN "HIGHWAY" = 'secondary' THEN 60<br />
WHEN "HIGHWAY" = 'tertiary' THEN 60<br />
WHEN "HIGHWAY" = 'living_street' THEN 15<br />
WHEN "HIGHWAY" = 'residential' THEN 40<br />
WHEN "HIGHWAY" = 'service' THEN 30<br />
WHEN "HIGHWAY" = 'road' THEN 60<br />
WHEN "HIGHWAY" = 'track' THEN 30<br />
WHEN "HIGHWAY" = 'raceway' THEN 90<br />
WHEN "HIGHWAY" = 'tertiary_link' THEN 40<br />
WHEN "HIGHWAY" = 'secondary_link' THEN 40<br />
WHEN "HIGHWAY" = 'primary_link' THEN 40<br />
WHEN "HIGHWAY" = 'trunk_link' THEN 40<br />
WHEN "HIGHWAY" = 'unclassified' THEN 40<br />
END<br />
</pre><br />
<br />
Здесь мы проверяем принадлежность каждого объекта слоя к одной из категорий и назначаем скорость в соответствии с определенным ранее значениями. Очень удобно использовать условный оператор CASE.<br />
<br />
[[Файл:Grass_qgis_isochrones_speed_calc.png|600px|thumb|center|]]<br />
<br />
После этой операции у каждого объекта слоя есть характеристика скорости. Теперь, при необходимости, можно поправить её для отдельных объектов в таблице атрибутов с учётом изучения реальных характеристик территории. Мы этот шаг пропустим и приступим к следующему - расчёту времени, - которое потребуется для преодоления каждого отдельного участка дорожной сети. Для этого нужно сначала рассчитать длины всех объектов слоя. В калькуляторе атрибутов создаем новое поле LENGTH как десятичное число по формуле<br />
<pre><br />
$length/1000<br />
</pre><br />
<br />
[[Файл:Grass_qgis_isochrones_length_calc.png|400px|thumb|center|]]<br />
<br />
По умолчанию расчёт длин и площадей производится в единицах проекции, т.е. в нашем случае в метрах (квадратных метрах для площади). Делением на 1000 мы приводим расстояния к километрам. Следующий шаг - расчёт времени (предпочтительно в минутах), которое потребуется для преодоления каждого отдельного участка дороги с учётом его длины и ожидаемой скорости. В калькуляторе атрибутов создаем новое поле TIME как десятичное число по формуле<br />
<pre><br />
"LENGTH"/"SPEED" * 60<br />
</pre><br />
<br />
[[Файл:Grass_qgis_isochrones_time_calc.png|400px|thumb|center|]]<br />
<br />
<br />
=== Подготовка данных через командную строку и утилиты GDAL/OGR ===<br />
<br />
Альтернативным подходом при подготовке данных является использование утилит командной строки. Такой способ удобно использовать, к примеру, для автоматизации пакетной обработки большого количества наборов данных. Для осуществления вышеописанных операций понадобятся две утилиты: [http://www.gdal.org/ogr2ogr.html ogr2ogr] и [http://www.gdal.org/ogrinfo.html ogrinfo]. Если вы работаете в среде Linux, при установленных QGIS и/или GRASS GIS, эти утилиты доступны вам из штатной системной командной строки (терминала). При работе в Windows наиболее простой способ доступа к ним - использование терминала OSGeo4W Shell, который автоматически устанавливается вместе с QGIS. Итак, после запуска терминала в Linux или OSGeo4W Shell в Windows выполняем следующие команды:<br />
<br />
1. Переходим в директорию, содержащую данные OSM, с помощью команды [https://en.wikipedia.org/wiki/Cd_(command) cd]:<br />
<pre>cd <путь до папки с распакованным архивом>/RU-LEN/data</pre><br />
<br />
2. С помощью утилиты ''ogr2ogr'' обрезаем слой дорог по границам Тосненского района, одновременно отфильтровывая ненужные объекты дорожной сети и проецируем результат в UTM 36N:<br />
<pre>ogr2ogr -skipfailures -t_srs "EPSG:32636" Tosnensky_roads_utm.shp highway-line.shp -clipsrc boundary-polygon.shp -clipsrcwhere "NAME = 'Тосненский район'" -nlt "LINESTRING" -sql "SELECT * FROM \"highway-line\" WHERE OGR_GEOMETRY='LINESTRING'" -sql "SELECT * FROM \"highway-line\" WHERE HIGHWAY NOT IN ('path','steps','footway','construction','proposed','pedestrian','bridleway')" -lco ENCODING=UTF-8</pre><br />
<br />
3. Конвертируем набор данных из ESRI Shapefile в SQLite для оптимизации дальнейшей работы:<br />
<pre>ogr2ogr -explodecollections -f "SQLite" -dsco SPATIALITE=YES Tosnensky_roads_utm.sqlite Tosnensky_roads_utm.shp</pre><br />
<br />
4. Добавляем в атрибуты колонки SPEED, LENGTH, TIME:<br />
<pre><br />
ogrinfo Tosnensky_roads_utm.sqlite -sql "ALTER TABLE tosnensky_roads_utm ADD COLUMN SPEED integer"<br />
ogrinfo Tosnensky_roads_utm.sqlite -sql "ALTER TABLE tosnensky_roads_utm ADD COLUMN LENGTH real"<br />
ogrinfo Tosnensky_roads_utm.sqlite -sql "ALTER TABLE tosnensky_roads_utm ADD COLUMN TIME real"<br />
</pre><br />
<br />
5. Рассчитываем значения скоростей для объектов исходя из атрибута "highway":<br />
<pre><br />
ogrinfo Tosnensky_roads_utm.sqlite -sql "UPDATE tosnensky_roads_utm SET SPEED = CASE WHEN "highway" = 'trunk' THEN 90 WHEN "highway" = 'primary' THEN 90 WHEN "highway" = 'secondary' THEN 60 WHEN "highway" = 'tertiary' THEN 60 WHEN "highway" = 'living_street' THEN 15 WHEN "highway" = 'residential' THEN 40 WHEN "highway" = 'service' THEN 30 WHEN "highway" = 'road' THEN 60 WHEN "highway" = 'track' THEN 30 WHEN "highway" = 'raceway' THEN 90 WHEN "highway" = 'tertiary_link' THEN 40 WHEN "highway" = 'secondary_link' THEN 40 WHEN "highway" = 'primary_link' THEN 40 WHEN "highway" = 'trunk_link' THEN 40 WHEN "highway" = 'unclassified' THEN 40 END"<br />
</pre><br />
<br />
6. Рассчитываем длины объектов дорожной сети<br />
<pre>ogrinfo Tosnensky_roads_utm.sqlite -sql "UPDATE tosnensky_roads_utm SET LENGTH = ST_length(GEOMETRY)/1000"</pre><br />
<br />
7. Рассчитываем, исходя из скоростей и длин, затраты времени на преодоление объектов дорожной сети:<br />
<pre>ogrinfo Tosnensky_roads_utm.sqlite -sql "UPDATE tosnensky_roads_utm SET TIME = LENGTH/SPEED*60"</pre><br />
<br />
<br />
Теперь у нас есть для каждого участка дорожной сети оценка времени, требуемая для его преодоления. На этом подготовка данных завершена, можно приступать к моделированию и переходить в GRASS. Если вы готовили данные в QGIS, не забудьте сохранить изменения в слое и выйти из режима редактирования.<br />
<br />
==Моделирование транспортной доступности==<br />
<br />
===Подготовка проекта и данных в GRASS===<br />
<br />
Запускаем графический интерфейс GRASS GIS. В первую очередь, необходимо создать так называемую "локацию" или "область". Если вы запускаете GRASS впервые, укажите папку для хранения данных (database directory). На скриншоте ниже вы видите, что в качестве такой папки выбрана E:\transport_article<br />
<br />
[[Файл:Grass_qgis_isochrones_project1.png|600px|thumb|center|]]<br />
<br />
Теперь нужно создать новую локацию (location). Для этого используем единственную активную кнопку ''New'' и попадаем в меню, где указываем название директории для хранения данных области внутри ''database directory'', и заголовок для неё. Для простоты в примере использованы те же названия: "transport_article". Таким образом, в папке E:\transport_article будет создана папка "transport_article", доступная для выбора по такому же заголовку.<br />
<br />
[[Файл:Grass_qgis_isochrones_project2.png|600px|thumb|center|]]<br />
<br />
Далее необходимо выбрать систему координат (мы уже знаем, что нам нужна UTM 36N, которую можно найти по номеру или по названию<br />
<br />
[[Файл:Grass_qgis_isochrones_project3.png|600px|thumb|center|]]<br />
<br />
Трансформацию датума на следующем шаге, при использовании UTM, можно выбрать произвольно, т.к. эффект будет одинаков (обратите внимание, что в опции с преобразованием все смещения нулевые).<br />
<br />
[[Файл:Grass_qgis_isochrones_project4.png|400px|thumb|center|]]<br />
<br />
Далее проверяем, что все параметры правильные, и завершаем создание области.<br />
<br />
[[Файл:Grass_qgis_isochrones_project5.png|600px|thumb|center|]]<br />
<br />
По завершению создания области будет предложено установить охват и разрешение региона. Это мы сделаем позднее, а сейчас откажемся.<br />
<br />
[[Файл:Grass_qgis_isochrones_project6.png|400px|thumb|center|]]<br />
<br />
На предложение же создать новый набор ("mapset") ответим утвердительно, и создадим его с именем ''tosno'', что будет отсылкой к конкретному району исследования.<br />
<br />
[[Файл:Grass_qgis_isochrones_project7.png|400px|thumb|center|]]<br />
<br />
В итоге в стартовом меню появляются созданные локация (tranport_article) и набор (tosno), выбрав которые можно запускать собственно GRASS (кнопка ''Start GRASS session'')<br />
<br />
[[Файл:Grass_qgis_isochrones_project8.png|600px|thumb|center|]]<br />
<br />
Наконец-то, начинается что-то интересное! Для начала нужно установить плагин, который и будет ответственным за построение изохрон: [https://grass.osgeo.org/grass72/manuals/addons/v.isochrones.html v.isochrones]. Для этого обратимся к меню ''Settings - Addons extensions - Install extensions from addons''.<br />
<br />
[[Файл:Grass_qgis_isochrones_extensions1.png|600px|thumb|center|]]<br />
<br />
В открывшемся меню в подгруппе ''vector'' находим модуль ''v.isochrones'' и устанавливаем его.<br />
<br />
[[Файл:Grass_qgis_isochrones_extensions2.png|600px|thumb|center|]]<br />
<br />
В консоли эта операция тоже выполняется просто:<br />
<pre>g.extension extension=v.isochrones operation=add</pre><br />
<br />
Если у вас возникают какие-либо проблемы с доступом к репозиторию с модулями GRASS (плагины не доступны для загрузки через меню "extensions"), это не критично: можно загрузить исходный Python-скрипт по [https://trac.osgeo.org/grass/browser/grass-addons/grass7/vector/v.isochrones/v.isochrones.py ссылке], и запускать модуль, обращаясь напрямую к скрипту через ''File - Launch script''. При таком сценарии у вас откроется тот же графический интерфейс, который будет рассмотрен в дальнейшем.<br />
<br />
Теперь давайте добавим в GRASS те данные, которые мы подготовили в QGIS или с помощью утилит GDAL/OGR. Для этого используем функцию импорта векторных данных ([https://grass.osgeo.org/grass72/manuals/v.in.ogr.html v.in.ogr]) или [https://grass.osgeo.org/grass72/manuals/v.import.html v.import]. В графическом интерфейсе искомое меню открывается через ''File - Import vector data - common import formats''. Выбираем наш набор геоданных "Tosnensky_roads_utm.shp" или "Tosnensky_roads_utm.sqlite":<br />
<br />
[[Файл:Grass_qgis_isochrones_import1.png|600px|thumb|center|]]<br />
<br />
<pre>v.import input=E:\qgis_grass_transport\data\Tosnensky_roads_utm.shp layer=Tosnensky_roads_utm output=Tosnensky_roads_utm</pre><br />
<br />
В консоли мы видим результат импорта и базовые сведения о данных, а в окне карты видим простую визуализацию.<br />
<br />
[[Файл:Grass_qgis_isochrones_map1.png|600px|thumb|center|]]<br />
<br />
Теперь создадим точечный слой, в котором будут содержаться местоположения, относительно которых требуется рассчитать изохроны. Это удобнее делать в интерактивном режиме в окне карты. Перейдем из режима 2D карты в режим оцифровки:<br />
<br />
[[Файл:grass_qgis_isochrones_digit.png|600px|thumb|center|]]<br />
<br />
Затем в крайнем слева выпадающем списке выберем опцию создания нового векторного слоя и зададим для него какое-нибудь подходящее название, например, ''start_points''. При этом атрибуты нам не нужны? и соответствующий флаг в меню создания слоя можно снять.<br />
<br />
[[Файл:grass_qgis_isochrones_digit2.png|600px|thumb|center|]]<br />
[[Файл:grass_qgis_isochrones_digit3.png|400px|thumb|center|]]<br />
<br />
Теперь выбираем инструмент "Создать новую точку" и добавляем интересующие нас локации. Для демонстрации поставим точки отсчёта в центры населенных пунктов г. Тосно и г. Любань. После этого выходим из режима оцифровки, соглашаясь на сохранение изменений.<br />
<br />
[[Файл:grass_qgis_isochrones_digit4.png|600px|thumb|center|]]<br />
<br />
Обратим внимание, что теперь на вкладке Слои (Layers) имеем два набора: импортированную дорожную сеть и созданные начальные точки.<br />
<br />
[[Файл:grass_qgis_isochrones_layers1.png|600px|thumb|center|]]<br />
<br />
Продолжаем подготовку данных. Поскольку в планах у нас занятия сетевым анализом, нужно набор линейных данных преобразовать в набор сетевых данных с помощью инструмента [https://grass.osgeo.org/grass72/manuals/v.net.html v.net], который доступен в меню Vector - Network analysis - Network preparation. Всё, что нам нужно, это применить операцию ''nodes'' к нашему слою "Tosnensky_roads_utm". На вкладке "Необходимо" выбираем метод ''nodes'', на вкладке ''Arcs'' выбираем слой "Tosnensky_roads_utm@tosno", на вкладке ''Опционный'' задаем имя для результирующего набора. Например, "Tosnensky_roads_utm_net".<br />
<br />
[[Файл:grass_qgis_isochrones_vnet1.png|600px|thumb|center|]]<br />
[[Файл:grass_qgis_isochrones_vnet2.png|600px|thumb|center|]]<br />
[[Файл:grass_qgis_isochrones_vnet3.png|600px|thumb|center|]]<br />
<br />
<pre>v.net -c input=Tosnensky_roads_utm operation=nodes output=Tosnensky_roads_utm_net</pre><br />
<br />
Следующий (и последний) шаг подготовки данных - это задание региона и разрешения для моделирования с помощью модуля [https://grass.osgeo.org/grass72/manuals/g.region.html g.region], который доступен в меню "Settings - Регион - Установить регион". На первой вкладке в выпадающем списке ''Set region to match vector maps'' выбираем слой "Tosnensky_roads_utm_net", созданный на предыдущем шаге, на вкладке "Границы" устанавливаем флаг "Подогнать регион под разрешение" и на вкладке "Разрешение" назначаем для "2D grid resolution" подходящее значение. Этот последний параметр очень важен - это размер ячеек сетки, до которых и будет производиться расчёт времени перемещения. Он должен быть достаточно небольшим, чтобы изохроны были плавными, но и не совсем маленьким, потому что чем подробнее сетка, тем более ресурсоёмкими получаются вычисления. Для территории подобной демонстрационной подходит размер 20 метров. Другие параметры не изменяются.<br />
<br />
[[Файл:grass_qgis_isochrones_gregion1.png|600px|thumb|center|]]<br />
[[Файл:grass_qgis_isochrones_gregion2.png|600px|thumb|center|]]<br />
[[Файл:grass_qgis_isochrones_gregion3.png|600px|thumb|center|]]<br />
<br />
<pre>g.region vector=Tosnensky_roads_utm_net res=20 -a</pre><br />
<br />
Теперь всё готово для создания самих изохрон и некоторых других выходных данных.<br />
<br />
===Создание изохрон===<br />
<br />
Модуль [https://grass.osgeo.org/grass72/manuals/addons/v.isochrones.html v.isochrones] позволяет работать в двух режимах: создавать непрерывные изохроны, то есть поля затрат на перемещение (с использованием модуля ''r.cost''), и просто откладывать преодолеваемое расстояние вдоль дорог (что хорошо подходит для случая, когда перемещение за пределами транспортной сети невозможно совсем - с использованиям модуля ''v.net.iso''). Нас интересуют непрерывные изохроны. Запускаем модуль v.isochrones, набрав такую команду в консоли:<br />
<pre>v.isochrones</pre><br />
Можно также запустить загруженный скрипт через ''File - Launch script''. На первой вкладке в качестве дорожной сети выбираем сетевой набор данных "Tosnensky_roads_utm_net@tosno", название атрибута с ценой - SPEED, стартовые точки - "start_points@tosno", выбираем удобное название для слоя с изохронами (например, "isochrones_rcost"), выбираем метод ''r.cost'' и (важное!) собственно значения изохрон в минутах через запятую. Здесь значения могут быть любыми в зависимости от ваших задач. Зададим следующие отсечки по времени: 15, 30, 60, 90, 120 и 150 минут. Далее, на вкладке ''r.cost'' задаются очень важные параметры: название для поверхности времени перемещений (опциональный продукт, но очень интересный, сохраним результат в слой timemap_rcost и посмотрим, что в нем будет), скорость для преодоления пространства вне дорожной сети (вышли на обочине и пошли пешком через поле, установим 5 км/ч) и доступная для расчётов память. Обычно хватает 1000 МБ.<br />
<br />
[[Файл:grass_qgis_isochrones_rcost1.png|600px|thumb|center|]]<br />
[[Файл:grass_qgis_isochrones_rcost2.png|600px|thumb|center|]]<br />
<br />
<pre>v.isochrones map=Tosnensky_roads_utm_net@tosno roads_layer=1 cost_column=SPEED start_points=start_points@tosno isochrones=isochrones_rcost time_steps=15,30,60,90,120,150 timemap=timemap_rcost memory=1000 method=r.cost</pre><br />
<br />
После выполнения обработки в дереве слоёв появляются два новых: изохроны и поверхность затрат времени. Это и есть искомый результат. Оформим его несколько позднее, пока просто взглянем:<br />
<br />
[[Файл:grass_qgis_isochrones_rcost3.png|600px|thumb|center|]]<br />
[[Файл:grass_qgis_isochrones_rcost4.png|600px|thumb|center|]]<br />
<br />
Выглядит неплохо! Изучим данные немного детальнее в QGIS, для этого экспортируем данные в стандартные ГИС-форматы с помощью команд [https://grass.osgeo.org/grass72/manuals/v.out.ogr.html v.out.ogr] и [https://grass.osgeo.org/grass72/manuals/r.out.gdal.html r.out.gdal], они доступы в меню File - экспорт векторного слоя и File - экспорт растрового слоя соответственно.<br />
<br />
[[Файл:grass_qgis_isochrones_export1.png|600px|thumb|center|]]<br />
<br />
<pre>v.out.ogr input=isochrones_rcost@tosno output=E:\qgis_grass_transport\data\isochrones.shp format=ESRI_Shapefile</pre><br />
<br />
[[Файл:grass_qgis_isochrones_export2.png|600px|thumb|center|]]<br />
<br />
<pre>r.out.gdal input=timemap_rcost@tosno output=E:\qgis_grass_transport\data\timemap.tif format=GTiff</pre><br />
<br />
Теперь можно использовать данные в других ГИС и делиться ими!<br />
<br />
==Представление результатов в QGIS==<br />
<br />
Посмотрим на данные в QGIS, и оформим простую карту. Сначала добавим растровый слой ''timemap.tif'', что он из себя представляет? Оказывается, это очень интересный набор данных, как бы предшествующий изохронам - растр с тем самым разрешением 20х20 метров, указанным на этапе задания области, в каждой ячейке которого хранится время в минутах, сколько туда добираться из указанных точек. Такой набор данных может быть очень полезен при разнообразном растровом анализе как один из входных слоёв.<br />
<br />
[[Файл:grass_qgis_isochrones_rcost_result1.png|600px|thumb|center|]]<br />
<br />
Слой же с изохронами содержит полигоны, охватывающие территории, соответствующие диапазонам времени перемещения: от 0 до 15, от 15 до 30, и так далее. Диапазоны мы ранее задавали самостоятельно. Важно, что в атрибутах хранятся данные о временном диапазоне, а внешние полигоны (более дальние) не содержат внутренних (ближних), такие данные открывают широкие возможности для разнообразного анализа.<br />
<br />
[[Файл:grass_qgis_isochrones_rcost_result2.png|600px|thumb|center|]]<br />
<br />
Применив простую заливку по уникальным значениям поля ''traveltime'' и подложив какую-нибудь базовую карту, можно быстро получить достаточно симпатичную визуализацию изохрон.<br />
<br />
[[Файл:grass_qgis_isochrones_rcost_result3.png|600px|thumb|center|]]<br />
<br />
Хотя ценнее в данном случае, конечно, сами данные.<br />
<br />
Итак, рассмотрен простой подход к транспортному моделированию способом построения изохрон в GRASS GIS. Возможности такого подхода весьма ограничены, особенно в аспектах, касающихся учёта разнородных препятствий, и так далее. Тем не менее, при внимательной подготовке исходных данных о дорожной сети, можно получать интересные результаты, которые применимы при различных сценариях анализа транспортной доступности.</div>Александр Мурыйhttps://wiki.gis-lab.info/index.php?title=%D0%91%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D1%8F_%D0%BE%D1%86%D0%B5%D0%BD%D0%BA%D0%B0_%D1%82%D1%80%D0%B0%D0%BD%D1%81%D0%BF%D0%BE%D1%80%D1%82%D0%BD%D0%BE%D0%B9_%D0%B4%D0%BE%D1%81%D1%82%D1%83%D0%BF%D0%BD%D0%BE%D1%81%D1%82%D0%B8_%D1%81%D1%80%D0%B5%D0%B4%D1%81%D1%82%D0%B2%D0%B0%D0%BC%D0%B8_GRASS_GIS_%D0%B8_QGIS&diff=25558Базовая оценка транспортной доступности средствами GRASS GIS и QGIS2017-07-31T09:37:58Z<p>Александр Мурый: /* Подготовка данных через командную строку и утилиты GDAL/OGR */</p>
<hr />
<div>{{Статья|Черновик}}<br />
<br />
{{Аннотация|Построение карт транспортной доступности на основе данных OpenStreetMap средствами открытых геоинформационных систем GRASS GIS и QGIS}}<br />
<br />
Картографирование транспортной доступности на основе данных о дорожной сети - одна из классических задач ГИС. Наиболее распространенным способом моделирования транспортной доступности является построение изохрон - линий равных затрат времени на преодоление пространства относительно заданных точек. В представленной статье обсуждается алгоритм построения изохрон по данным [http://www.openstreetmap.org/ OpenStreetMap] с использованием открытых ГИС [https://grass.osgeo.org/ GRASS GIS] и [http://qgis.org/ru/site/ QGIS]. В QGIS будет осуществляться подготовка данных и картографическое представление результатов, а в GRASS собственно моделирование. Всю работу можно выполнить целиком в GRASS, но, по мнению автора, общие манипуляции геоданными и представление картографических материалов удачнее и удобнее реализованы в QGIS.<br />
<br />
Описываемые в статье действия выполнялись в средах Ubuntu 14.04 LTS и Windows 8.1 (x64), GRASS GIS 7.2, QGIS 2.14.<br />
<br />
==Получение и подготовка данных==<br />
<br />
Для осуществления расчётов нам потребуется набор векторных линейных геоданных, содержащий информацию о дорожно-транспортной сети исследуемой территории. Заполучить подобную информацию можно различными способами: приобрести у специализированных поставщиков, найти в одном из источников открытых данных, оцифровать атлас автомобильных дорог, нарисовать по космическому снимку, и так далее (не забывайте про лицензии данных!). В данном случае мы воспользуемся данными OSM как достаточно качественными и подробными, и, что важно, доступными бесплатно и легально. Начать знакомство с OSM вы можете с [http://wiki.openstreetmap.org/wiki/RU:%D0%9E_%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D0%B5 этой страницы]. Этап подготовки данных являются ключевым: помимо технических этапов, здесь нам предстоит определить транспортные характеристики, которые будут использованы для моделирования.<br />
<br />
Для демонстрации принципов базового транспортного моделирования рассмотрим Тосненский район Ленинградской области. Транспорт: автомобиль.<br />
<br />
===Загрузка данных OSM===<br />
<br />
Для загрузки данных OSM существует множество возможностей. Одним из наиболее простых способов является загрузка уже подготовленных наборов данных по слоям в формате ESRI Shapefile. Популярные сервисы в сети: [http://download.geofabrik.de/ Geofabrik], где вы можете найти комплекты данных на весь мир, и [http://data.nextgis.com/osmshp/ NextGIS], где вы найдёте вместе с данными оформленные QGIS-проекты по субъектам РФ и странам СНГ. Также удобный способ быстро получить данные OSM - воспользоваться одним из соответствующих плагинов для QGIS, например, [http://plugins.qgis.org/plugins/OSMDownloader/ OSMDownloader]. При его установке в интерфейс QGIS добавится кнопка выделения прямоугольной области, для которой и будут загружены данные в указанное вами расположение. Для демонстрации в этой статье используется набор данных OSM от NextGIS на территорию Ленинградской области от 13 ноября 2016 года, доступный бесплатно ([https://drive.google.com/file/d/0BztNh0nXzyjbTUpNWVl4bk15Sjg/view?usp=sharing загрузить]).<br />
<br />
Для нашей задачи понадобятся только данные о дорожной сети (в случае работы с выгрузками NextGIS - слой ''highway-line'' из директории ''data''; в случае работы с OSMDownloader - все загруженные объекты линейного типа, у которых в атрибуте "Highway" содержится какое-либо значение). Для оформительских задач также может быть использован набор геоданных с границами административно-территориального деления (например, для обрезки изохрон по границам района). <br />
<br />
===Первичная подготовка данных в QGIS===<br />
<br />
Для начала подготовим данные по дорожной сети: обрежем их по границам района интереса, спроецируем куда нужно, отфильтруем лишнее. Распаковываем архив ''RU-LEN.7z'' в удобное расположение, лучше без кириллицы и пробелов в путях. Затем запускаем QGIS и добавляем два векторных слоя из папки .../RU-LEN/data/:<br />
* highway-line<br />
* boundary-polygon<br />
<br />
Нужно проявить осторожность с кодировками, и добавлять слои с кодировкой UTF-8 (кодировку можно выбрать в меню выбора векторного слоя при его добавлении). Выглядит это как-то так (цвета могут быть произвольными):<br />
<br />
[[Файл:Grass_qgis_isochrones_map_raw.png|600px|thumb|center|]]<br />
<br />
В первую очередь, избавимся от всего лишнего. Найдём полигон Тосненского района и сохраним его в отдельный слой. Для этого в таблице атрибутов слоя ''boundary-polygon'' (доступно через контекстное меню слоя) найдём объект с соответствующим содержанием поля NAME. Для этого можно воспользоваться поиском по запросу, либо просто отсортировать колонку и выделить нужную строку. Затем через контекстное меню слоя выбираем "Сохранить как", определяя новое расположение для файла с границами Тосненского района (в моём случае Tosnensky_boundary.shp), и не забываем активировать флаг "Сохранить только выделенные объекты".<br />
<br />
<br />
[[Файл:Grass_qgis_isochrones_table_selection.png|400px|thumb|center|]]<br />
[[Файл:Grass_qgis_isochrones_save_as.png|400px|thumb|center|]]<br />
<br />
Затем необходимо обрезать набор геоданных по дорожной сети по этим границам. Для этого воспользуемся инструментом обрезки (Вектор - Инструменты геообработки - Обрезка). В качестве исходного слоя выбираем ''highway-line'', в качестве слоя обрезки ''Tosnensky_bondary'', результат сохраняем в удобное новое расположение (в моём случае Tosnensky_roads.shp).<br />
<br />
[[Файл:Grass_qgis_isochrones_clip.png|400px|thumb|center|]]<br />
<br />
Теперь, чтобы избежать возможных проблем с расчётом длин участков дорог (и некоторых других) в будущем, перепроецируем результирующий слой ''Tosnensky_roads'' в подходящую систему координат. Для расчётных задач рекомендуется использовать поперечную цилиндрическую проекцию (UTM или Гаусса-Крюгера) для вашей зоны. В данном случае используем WGS 84 / UTM zone 36N (EPSG:32636), для этого через меню "Сохранить как" создаем копию слоя ''Tosnensky_roads'', в выпадающем списке "Система координат", выбрав UTM zone 36N и задав удобное расположение для нового файла (Tosnensku_roads_utm).<br />
<br />
[[Файл:Grass_qgis_isochrones_reproject_roads.png|400px|thumb|center|]]<br />
<br />
Аналогичным образом перепроецируем слой с границами Тосненского района, на всякий случай (получаем дополнительно Tosnensky_boundary_utm). Данные почти готовы! Теперь нужно отфильтровать геоданные по дорогам с учётом требований к моделированию. Это первый тематический шаг подготовки: '''отбор только тех объектов дорожной сети, которые могут быть использованы для перемещения предполагаемыми транспортными средствами'''. Здесь вы должны решить, по объектам каких типов будет разрешено перемещаться, например, для моделирования пешеходного движения можно оставить все типы дорог; для легковых автомобилей исключить пешеходные дорожки, тропы, просеки; для грузовых автомобилей исключить дороги с соответствующими ограничениями на массу, и так далее. Шаг этот очень ответственный. При этом подход к фильтрации сильно зависит от качества исходных данных и их детализации. Вспоминаем, что имеем дело с OSM, где различия между объектами существуют на уровне тегов, которые записаны в таблице атрибутов в поле HIGHWAY. Посмотрим на список уникальных значений для нашего набора данных:<br />
<pre><br />
path steps footway residential primary service unclassified secondary road track construction tertiary raceway tertiary_link trunk secondary_link proposed pedestrian bridleway primary_link living_street trunk_link<br />
</pre><br />
<br />
Все эти теги [http://wiki.openstreetmap.org/wiki/Key:highway описаны на ресурсах OSM], даже с картинками. Нам ещё предстоит вернуться к этим описаниям, а сейчас важно определить, по объектам каких типов может нормально передвигаться легковой автомобиль, а по каким нет. Очевидно, что нужно исключить объекты следующих типов (пригодны только для пешеходов, проектируются, строятся и т.д.):<br />
<pre><br />
path, steps, footway, construction, proposed, pedestrian, bridleway<br />
</pre><br />
<br />
Также много объектов с типом ''unclassified''. При осуществлении серьезной работы, конечно, стоит прояснить природу каждого из таких объектов по вспомогательным данным (и внести информацию в OSM, конечно же). В данном случае оставим их как подходящие для работы. Объекты всех отобранных типов нужно удалить. Для этого переходим в таблицу атрибутов слоя Tosnensky_roads_utm, выбираем инструмент "Выбрать по выражению" и формируем запрос вида:<br />
<pre><br />
"HIGHWAY" IN ( 'path' , 'steps' , 'footway' , 'construction' , 'proposed' , 'pedestrian' , 'bridleway' )<br />
</pre><br />
<br />
отмечая в списке, соответственно, выбранные вами типы дорог, которые должны быть проигнорированы. Нажимаем кнопку "выбрать" и видим на карте лишние дороги.<br />
<br />
[[Файл:Grass_qgis_isochrones_for_deleting.png|600px|thumb|center|]]<br />
<br />
Переходим в режим редактирования и удаляем выбранные объекты, сохраняем изменения. Если что, все исходные данные остаются в других слоях. Общая подготовка закончена.<br />
<br />
<br />
===Определение транспортных характеристик данных о дорожной сети в QGIS===<br />
<br />
Второй этап подготовки является самым важным и трудоёмким в реальных условиях: необходимо каждому участку дороги назначить некоторую среднюю скорость перемещения по нему. Для примера мы используем простую логику: каждому семейству объектов (по тегу) назначим общую ожидаемую скорость, не вникая в каждый отдельный объект. При серьезном исследовании, опять же, стоит более внимательно отнестись к этому этапу, по возможности используя дополнительные материалы. Итак, рассмотрим те категории, которые у нас остались после фильтрации:<br />
<br />
* trunk - важнейшие и крупнейшие дороги, например, для нашей территории, Московское шоссе. Ожидаемая скорость 90 км/ч<br />
* primary - крупные шоссе, следующий уровень после trunk. Ожидаемая скорость 90 км/ч<br />
* secondary - относительно крупные дороги, следующий уровень после primary. Ожидаемая скорость 60 км/ч<br />
* tertiary - обычные автомобильные дороги между небольшими населенными пунктами. Ожидаемая скорость 60 км/ч <br />
* living_street - жилые зоны, где у пешеходов явное преимущество в праве передвижения. Ожидаемая скорость 15 км/ч<br />
* residential - автомобильные дороги в жилых кварталах. Ожидаемая скорость около 40 км/ч<br />
* service - сервисные подъезды, въезды и проч. Ожидаемая скорость 30 км/ч<br />
* road - автомобильная дорога неизвестного типа. Примем скорость 60 км/ч<br />
* track - грунтовые дороги, обычно для сельхоз-техники. Примем скорость 30 км/ч<br />
* raceway - дороги для автомобильных видов спорта. Примем скорость 90 км/ч <br />
* tertiary_link - участки, соединяющие tertiary с другими tertiary или дорогами других типов. Примем скорость 40 км/ч<br />
* secondary_link - участки, соединяющие secondary с другими secondary или дорогами других типов. Примем скорость 40 км/ч<br />
* primary_link - участки, соединяющие primary с другими primary или дорогами других типов. Примем скорость 40 км/ч<br />
* trunk_link - участки, соединяющие trunk с другими trunk или дорогами других типов. Примем скорость 40 км/ч<br />
* unclassified - дороги без тега. Примем скорость 40 км/ч<br />
<br />
Принятые значения достаточно условны, но позволят продемонстрировать принципы дальнейшей работы. Назначаемые скорости довольно существенно зависят от конкретной территории, местного законодательства и так далее. Важно заметить, что у некоторых участков (обычно их немного) в свойствах заполнено значение MAXSPEED, оно может быть как численным (в км/ч), так и вида RU:urban. Расшифровку этих обозначений можно найти, к примеру, [http://wiki.openstreetmap.org/wiki/RU:Key:source:maxspeed здесь] и использовать при определении скоростей. '''В целом этот этап является ключевым, и в боевых условиях здесь важно внимательно и тщательно определить характеристики дорожной сети'''.<br />
<br />
Для того, чтобы применить принятые характеристики, воспользуемся калькулятором полей, запустив его из таблицы атрибутов слоя ''Tosnensky_roads_utm''. Создадим новый атрибут SPEED как целочисленный и заполним его следующим выражением:<br />
<pre><br />
CASE<br />
WHEN "HIGHWAY" = 'trunk' THEN 90<br />
WHEN "HIGHWAY" = 'primary' THEN 90<br />
WHEN "HIGHWAY" = 'secondary' THEN 60<br />
WHEN "HIGHWAY" = 'tertiary' THEN 60<br />
WHEN "HIGHWAY" = 'living_street' THEN 15<br />
WHEN "HIGHWAY" = 'residential' THEN 40<br />
WHEN "HIGHWAY" = 'service' THEN 30<br />
WHEN "HIGHWAY" = 'road' THEN 60<br />
WHEN "HIGHWAY" = 'track' THEN 30<br />
WHEN "HIGHWAY" = 'raceway' THEN 90<br />
WHEN "HIGHWAY" = 'tertiary_link' THEN 40<br />
WHEN "HIGHWAY" = 'secondary_link' THEN 40<br />
WHEN "HIGHWAY" = 'primary_link' THEN 40<br />
WHEN "HIGHWAY" = 'trunk_link' THEN 40<br />
WHEN "HIGHWAY" = 'unclassified' THEN 40<br />
END<br />
</pre><br />
<br />
Здесь мы проверяем принадлежность каждого объекта слоя к одной из категорий и назначаем скорость в соответствии с определенным ранее значениями. Очень удобно использовать условный оператор CASE.<br />
<br />
[[Файл:Grass_qgis_isochrones_speed_calc.png|600px|thumb|center|]]<br />
<br />
После этой операции у каждого объекта слоя есть характеристика скорости. Теперь, при необходимости, можно поправить её для отдельных объектов в таблице атрибутов с учётом изучения реальных характеристик территории. Мы этот шаг пропустим и приступим к следующему - расчёту времени, - которое потребуется для преодоления каждого отдельного участка дорожной сети. Для этого нужно сначала рассчитать длины всех объектов слоя. В калькуляторе атрибутов создаем новое поле LENGTH как десятичное число по формуле<br />
<pre><br />
$length/1000<br />
</pre><br />
<br />
[[Файл:Grass_qgis_isochrones_length_calc.png|400px|thumb|center|]]<br />
<br />
По умолчанию расчёт длин и площадей производится в единицах проекции, т.е. в нашем случае в метрах (квадратных метрах для площади). Делением на 1000 мы приводим расстояния к километрам. Следующий шаг - расчёт времени (предпочтительно в минутах), которое потребуется для преодоления каждого отдельного участка дороги с учётом его длины и ожидаемой скорости. В калькуляторе атрибутов создаем новое поле TIME как десятичное число по формуле<br />
<pre><br />
"LENGTH"/"SPEED" * 60<br />
</pre><br />
<br />
[[Файл:Grass_qgis_isochrones_time_calc.png|400px|thumb|center|]]<br />
<br />
<br />
=== Подготовка данных через командную строку и утилиты GDAL/OGR ===<br />
<br />
Альтернативным подходом при подготовке данных является использование утилит командной строки. Такой способ удобно использовать, к примеру, для автоматизации пакетной обработки большого количества наборов данных. Для осуществления вышеописанных операций понадобятся две утилиты: [http://www.gdal.org/ogr2ogr.html ogr2ogr] и [http://www.gdal.org/ogrinfo.html ogrinfo]. Если вы работаете в среде Linux, при установленных QGIS и/или GRASS GIS, эти утилиты доступны вам из штатной системной командной строки (терминала). При работе в Windows наиболее простой способ доступа к ним - использование терминала OSGeo4W Shell, который автоматически устанавливается вместе с QGIS. Итак, после запуска терминала в Linux или OSGeo4W Shell в Windows выполняем следующие команды:<br />
<br />
1. Переходим в директорию, содержащую данные OSM, с помощью команды [https://en.wikipedia.org/wiki/Cd_(command) cd]:<br />
<pre>cd <путь до папки с распакованным архивом>/RU-LEN/data</pre><br />
<br />
2. С помощью утилиты ''ogr2ogr'' обрезаем слой дорог по границам Тосненского района, одновременно отфильтровывая ненужные объекты дорожной сети и проецируем результат в UTM 36N:<br />
<pre>ogr2ogr -skipfailures -t_srs "EPSG:32636" Tosnensky_roads_utm.shp highway-line.shp -clipsrc boundary-polygon.shp -clipsrcwhere "NAME = 'Тосненский район'" -nlt "LINESTRING" -sql "SELECT * FROM \"highway-line\" WHERE OGR_GEOMETRY='LINESTRING'" -sql "SELECT * FROM \"highway-line\" WHERE HIGHWAY NOT IN ('path','steps','footway','construction','proposed','pedestrian','bridleway')" -lco ENCODING=UTF-8</pre><br />
<br />
3. Конвертируем набор данных из ESRI Shapefile в SQLite для оптимизации дальнейшей работы:<br />
<pre>ogr2ogr -explodecollections -f "SQLite" -dsco SPATIALITE=YES Tosnensky_roads_utm.sqlite Tosnensky_roads_utm.shp</pre><br />
<br />
4. Добавляем в атрибуты колонки SPEED, LENGTH, TIME:<br />
<pre><br />
ogrinfo Tosnensky_roads_utm.sqlite -sql "ALTER TABLE tosnensky_roads_utm ADD COLUMN SPEED integer"<br />
ogrinfo Tosnensky_roads_utm.sqlite -sql "ALTER TABLE tosnensky_roads_utm ADD COLUMN LENGTH real"<br />
ogrinfo Tosnensky_roads_utm.sqlite -sql "ALTER TABLE tosnensky_roads_utm ADD COLUMN TIME real"<br />
</pre><br />
<br />
5. Рассчитываем значения скоростей для объектов исходя из атрибута "highway":<br />
<pre><br />
ogrinfo Tosnensky_roads_utm.sqlite -sql "UPDATE tosnensky_roads_utm SET SPEED = CASE WHEN "highway" = 'trunk' THEN 90 WHEN "highway" = 'primary' THEN 90 WHEN "highway" = 'secondary' THEN 60 WHEN "highway" = 'tertiary' THEN 60 WHEN "highway" = 'living_street' THEN 15 WHEN "highway" = 'residential' THEN 40 WHEN "highway" = 'service' THEN 30 WHEN "highway" = 'road' THEN 60 WHEN "highway" = 'track' THEN 30 WHEN "highway" = 'raceway' THEN 90 WHEN "highway" = 'tertiary_link' THEN 40 WHEN "highway" = 'secondary_link' THEN 40 WHEN "highway" = 'primary_link' THEN 40 WHEN "highway" = 'trunk_link' THEN 40 WHEN "highway" = 'unclassified' THEN 40 END"<br />
</pre><br />
<br />
6. Рассчитываем длины объектов дорожной сети<br />
<pre>ogrinfo Tosnensky_roads_utm.sqlite -sql "UPDATE tosnensky_roads_utm SET LENGTH = ST_length(GEOMETRY)/1000"</pre><br />
<br />
7. Рассчитываем, исходя из скоростей и длин, затраты времени на преодоление объектов дорожной сети:<br />
<pre>ogrinfo Tosnensky_roads_utm.sqlite -sql "UPDATE tosnensky_roads_utm SET TIME = LENGTH/SPEED*60"</pre><br />
<br />
<br />
Теперь у нас есть для каждого участка дорожной сети оценка времени, требуемая для его преодоления. На этом подготовка данных завершена, можно приступать к моделированию и переходить в GRASS. Если вы готовили данные в QGIS, не забудьте сохранить изменения в слое и выйти из режима редактирования.<br />
<br />
==Моделирование транспортной доступности==<br />
<br />
===Подготовка проекта и данных в GRASS===<br />
<br />
Запускаем графический интерфейс GRASS GIS. В первую очередь, необходимо создать так называемую "локацию" или "область". Если вы запускаете GRASS впервые, укажите папку для хранения данных (database directory). На скриншоте ниже вы видите, что в качестве такой папки выбрана E:\transport_article<br />
<br />
[[Файл:Grass_qgis_isochrones_project1.png|600px|thumb|center|]]<br />
<br />
Теперь нужно создать новую локацию (location). Для этого используем единственную активную кнопку ''New'' и попадаем в меню, где указываем название директории для хранения данных области внутри ''database directory'', и заголовок для неё. Для простоты в примере использованы те же названия: "transport_article". Таким образом, в папке E:\transport_article будет создана папка "transport_article", доступная для выбора по такому же заголовку.<br />
<br />
[[Файл:Grass_qgis_isochrones_project2.png|600px|thumb|center|]]<br />
<br />
Далее необходимо выбрать систему координат (мы уже знаем, что нам нужна UTM 36N, которую можно найти по номеру или по названию<br />
<br />
[[Файл:Grass_qgis_isochrones_project3.png|600px|thumb|center|]]<br />
<br />
Трансформацию датума на следующем шаге, при использовании UTM, можно выбрать произвольно, т.к. эффект будет одинаков (обратите внимание, что в опции с преобразованием все смещения нулевые).<br />
<br />
[[Файл:Grass_qgis_isochrones_project4.png|400px|thumb|center|]]<br />
<br />
Далее проверяем, что все параметры правильные, и завершаем создание области.<br />
<br />
[[Файл:Grass_qgis_isochrones_project5.png|600px|thumb|center|]]<br />
<br />
По завершению создания области будет предложено установить охват и разрешение региона. Это мы сделаем позднее, а сейчас откажемся.<br />
<br />
[[Файл:Grass_qgis_isochrones_project6.png|400px|thumb|center|]]<br />
<br />
На предложение же создать новый набор ("mapset") ответим утвердительно, и создадим его с именем ''tosno'', что будет отсылкой к конкретному району исследования.<br />
<br />
[[Файл:Grass_qgis_isochrones_project7.png|400px|thumb|center|]]<br />
<br />
В итоге в стартовом меню появляются созданные локация (tranport_article) и набор (tosno), выбрав которые можно запускать собственно GRASS (кнопка ''Start GRASS session'')<br />
<br />
[[Файл:Grass_qgis_isochrones_project8.png|600px|thumb|center|]]<br />
<br />
Наконец-то, начинается что-то интересное! Для начала нужно установить плагин, который и будет ответственным за построение изохрон: [https://grass.osgeo.org/grass72/manuals/addons/v.isochrones.html v.isochrones]. Для этого обратимся к меню ''Settings - Addons extensions - Install extensions from addons''.<br />
<br />
[[Файл:Grass_qgis_isochrones_extensions1.png|600px|thumb|center|]]<br />
<br />
В открывшемся меню в подгруппе ''vector'' находим модуль ''v.isochrones'' и устанавливаем его.<br />
<br />
[[Файл:Grass_qgis_isochrones_extensions2.png|600px|thumb|center|]]<br />
<br />
В консоли эта операция тоже выполняется просто:<br />
<pre>g.extension extension=v.isochrones operation=add</pre><br />
<br />
Если у вас возникают какие-либо проблемы с доступом к репозиторию с модулями GRASS (плагины не доступны для загрузки через меню "extensions"), это не критично: можно загрузить исходный Python-скрипт по [https://trac.osgeo.org/grass/browser/grass-addons/grass7/vector/v.isochrones/v.isochrones.py ссылке], и запускать модуль, обращаясь напрямую к скрипту через ''File - Launch script''. При таком сценарии у вас откроется тот же графический интерфейс, который будет рассмотрен в дальнейшем.<br />
<br />
Теперь давайте добавим в GRASS те данные, которые мы подготовили в QGIS или с помощью утилит GDAL/OGR. Для этого используем функцию импорта векторных данных ([https://grass.osgeo.org/grass72/manuals/v.in.ogr.html v.in.ogr]) или [https://grass.osgeo.org/grass72/manuals/v.import.html v.import]. В графическом интерфейсе искомое меню открывается через ''File - Import vector data - common import formats''. Выбираем наш набор геоданных "Tosnensky_roads_utm.shp" или "Tosnensky_roads_utm.sqlite":<br />
<br />
[[Файл:Grass_qgis_isochrones_import1.png|600px|thumb|center|]]<br />
<br />
<pre>v.import input=E:\qgis_grass_transport\data\Tosnensky_roads_utm.shp layer=Tosnensky_roads_utm output=Tosnensky_roads_utm</pre><br />
<br />
В консоли мы видим результат импорта и базовые сведения о данных, а в окне карты видим простую визуализацию.<br />
<br />
[[Файл:Grass_qgis_isochrones_map1.png|600px|thumb|center|]]<br />
<br />
Теперь создадим точечный слой, в котором будут содержаться местоположения, относительно которых требуется рассчитать изохроны. Это удобнее делать в интерактивном режиме в окне карты. Перейдем из режима 2D карты в режим оцифровки:<br />
<br />
[[Файл:grass_qgis_isochrones_digit.png|600px|thumb|center|]]<br />
<br />
Затем в крайнем слева выпадающем списке выберем опцию создания нового векторного слоя и зададим для него какое-нибудь подходящее название, например, ''start_points''. При этом атрибуты нам не нужны? и соответствующий флаг в меню создания слоя можно снять.<br />
<br />
[[Файл:grass_qgis_isochrones_digit2.png|600px|thumb|center|]]<br />
[[Файл:grass_qgis_isochrones_digit3.png|400px|thumb|center|]]<br />
<br />
Теперь выбираем инструмент "Создать новую точку" и добавляем интересующие нас локации. Для демонстрации поставим точки отсчёта в центры населенных пунктов г. Тосно и г. Любань. После этого выходим из режима оцифровки, соглашаясь на сохранение изменений.<br />
<br />
[[Файл:grass_qgis_isochrones_digit4.png|600px|thumb|center|]]<br />
<br />
Обратим внимание, что теперь на вкладке Слои (Layers) имеем два набора: импортированную дорожную сеть и созданные начальные точки.<br />
<br />
[[Файл:grass_qgis_isochrones_layers1.png|600px|thumb|center|]]<br />
<br />
Продолжаем подготовку данных. Поскольку в планах у нас занятия сетевым анализом, нужно набор линейных данных преобразовать в набор сетевых данных с помощью инструмента [https://grass.osgeo.org/grass72/manuals/v.net.html v.net], который доступен в меню Vector - Network analysis - Network preparation. Всё, что нам нужно, это применить операцию ''nodes'' к нашему слою "Tosnensky_roads_utm". На вкладке "Необходимо" выбираем метод ''nodes'', на вкладке ''Arcs'' выбираем слой "Tosnensky_roads_utm@tosno", на вкладке ''Опционный'' задаем имя для результирующего набора. Например, "Tosnensky_roads_utm_net".<br />
<br />
[[Файл:grass_qgis_isochrones_vnet1.png|600px|thumb|center|]]<br />
[[Файл:grass_qgis_isochrones_vnet2.png|600px|thumb|center|]]<br />
[[Файл:grass_qgis_isochrones_vnet3.png|600px|thumb|center|]]<br />
<br />
<pre>v.net -c input=Tosnensky_roads_utm operation=nodes output=Tosnensky_roads_utm_net</pre><br />
<br />
Следующий (и последний) шаг подготовки данных - это задание региона и разрешения для моделирования с помощью модуля [https://grass.osgeo.org/grass72/manuals/g.region.html g.region], который доступен в меню "Settings - Регион - Установить регион". На первой вкладке в выпадающем списке ''Set region to match vector maps'' выбираем слой "Tosnensky_roads_utm_net", созданный на предыдущем шаге, на вкладке "Границы" устанавливаем флаг "Подогнать регион под разрешение" и на вкладке "Разрешение" назначаем для "2D grid resolution" подходящее значение. Этот последний параметр очень важен - это размер ячеек сетки, до которых и будет производиться расчёт времени перемещения. Он должен быть достаточно небольшим, чтобы изохроны были плавными, но и не совсем маленьким, потому что чем подробнее сетка, тем более ресурсоёмкими получаются вычисления. Для территории подобной демонстрационной подходит размер 20 метров. Другие параметры не изменяются.<br />
<br />
[[Файл:grass_qgis_isochrones_gregion1.png|600px|thumb|center|]]<br />
[[Файл:grass_qgis_isochrones_gregion2.png|600px|thumb|center|]]<br />
[[Файл:grass_qgis_isochrones_gregion3.png|600px|thumb|center|]]<br />
<br />
<pre>g.region vector=Tosnensky_roads_utm_net res=20 -a</pre><br />
<br />
Теперь всё готово для создания самих изохрон и некоторых других выходных данных.<br />
<br />
===Создание изохрон===<br />
<br />
Модуль [https://grass.osgeo.org/grass72/manuals/addons/v.isochrones.html v.isochrones] позволяет работать в двух режимах: создавать непрерывные изохроны, то есть поля затрат на перемещение (с использованием модуля ''r.cost''), и просто откладывать преодолеваемое расстояние вдоль дорог (что хорошо подходит для случая, когда перемещение за пределами транспортной сети невозможно совсем - с использованиям модуля ''v.net.iso''). Нас интересуют непрерывные изохроны. Запускаем модуль v.isochrones, набрав такую команду в консоли:<br />
<pre>v.isochrones</pre><br />
Можно также запустить загруженный скрипт через ''File - Launch script''. На первой вкладке в качестве дорожной сети выбираем сетевой набор данных "Tosnensky_roads_utm_net@tosno", название атрибута с ценой - SPEED, стартовые точки - "start_points@tosno", выбираем удобное название для слоя с изохронами (например, "isochrones_rcost"), выбираем метод ''r.cost'' и (важное!) собственно значения изохрон в минутах через запятую. Здесь значения могут быть любыми в зависимости от ваших задач. Зададим следующие отсечки по времени: 15, 30, 60, 90, 120 и 150 минут. Далее, на вкладке ''r.cost'' задаются очень важные параметры: название для поверхности времени перемещений (опциональный продукт, но очень интересный, сохраним результат в слой timemap_rcost и посмотрим, что в нем будет), скорость для преодоления пространства вне дорожной сети (вышли на обочине и пошли пешком через поле, установим 5 км/ч) и доступная для расчётов память. Обычно хватает 1000 МБ.<br />
<br />
[[Файл:grass_qgis_isochrones_rcost1.png|600px|thumb|center|]]<br />
[[Файл:grass_qgis_isochrones_rcost2.png|600px|thumb|center|]]<br />
<br />
<pre>v.isochrones map=Tosnensky_roads_utm_net@tosno roads_layer=1 cost_column=SPEED start_points=start_points@tosno isochrones=isochrones_rcost time_steps=15,30,60,90,120,150 timemap=timemap_rcost memory=1000 method=r.cost</pre><br />
<br />
После выполнения обработки в дереве слоёв появляются два новых: изохроны и поверхность затрат времени. Это и есть искомый результат. Оформим его несколько позднее, пока просто взглянем:<br />
<br />
[[Файл:grass_qgis_isochrones_rcost3.png|600px|thumb|center|]]<br />
[[Файл:grass_qgis_isochrones_rcost4.png|600px|thumb|center|]]<br />
<br />
Выглядит неплохо! Изучим данные немного детальнее в QGIS, для этого экспортируем данные в стандартные ГИС-форматы с помощью команд [https://grass.osgeo.org/grass72/manuals/v.out.ogr.html v.out.ogr] и [https://grass.osgeo.org/grass72/manuals/r.out.gdal.html r.out.gdal], они доступы в меню File - экспорт векторного слоя и File - экспорт растрового слоя соответственно.<br />
<br />
[[Файл:grass_qgis_isochrones_export1.png|600px|thumb|center|]]<br />
<br />
<pre>v.out.ogr input=isochrones_rcost@tosno output=E:\qgis_grass_transport\data\isochrones.shp format=ESRI_Shapefile</pre><br />
<br />
[[Файл:grass_qgis_isochrones_export2.png|600px|thumb|center|]]<br />
<br />
<pre>r.out.gdal input=timemap_rcost@tosno output=E:\qgis_grass_transport\data\timemap.tif format=GTiff</pre><br />
<br />
Теперь можно использовать данные в других ГИС и делиться ими!<br />
<br />
==Представление результатов в QGIS==<br />
<br />
Посмотрим на данные в QGIS, и оформим простую карту. Сначала добавим растровый слой ''timemap.tif'', что он из себя представляет? Оказывается, это очень интересный набор данных, как бы предшествующий изохронам - растр с тем самым разрешением 20х20 метров, указанным на этапе задания области, в каждой ячейке которого хранится время в минутах, сколько туда добираться из указанных точек. Такой набор данных может быть очень полезен при разнообразном растровом анализе как один из входных слоёв.<br />
<br />
[[Файл:grass_qgis_isochrones_rcost_result1.png|600px|thumb|center|]]<br />
<br />
Слой же с изохронами содержит полигоны, охватывающие территории, соответствующие диапазонам времени перемещения: от 0 до 15, от 15 до 30, и так далее. Диапазоны мы ранее задавали самостоятельно. Важно, что в атрибутах хранятся данные о временном диапазоне, а внешние полигоны (более дальние) не содержат внутренних (ближних), такие данные открывают широкие возможности для разнообразного анализа.<br />
<br />
[[Файл:grass_qgis_isochrones_rcost_result2.png|600px|thumb|center|]]<br />
<br />
Применив простую заливку по уникальным значениям поля ''traveltime'' и подложив какую-нибудь базовую карту, можно быстро получить достаточно симпатичную визуализацию изохрон.<br />
<br />
[[Файл:grass_qgis_isochrones_rcost_result3.png|600px|thumb|center|]]<br />
<br />
Хотя ценнее в данном случае, конечно, сами данные.<br />
<br />
Итак, рассмотрен простой подход к транспортному моделированию способом построения изохрон в GRASS GIS. Возможности такого подхода весьма ограничены, особенно в аспектах, касающихся учёта разнородных препятствий, и так далее. Тем не менее, при внимательной подготовке исходных данных о дорожной сети, можно получать интересные результаты, которые применимы при различных сценариях анализа транспортной доступности.</div>Александр Мурыйhttps://wiki.gis-lab.info/index.php?title=%D0%91%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D1%8F_%D0%BE%D1%86%D0%B5%D0%BD%D0%BA%D0%B0_%D1%82%D1%80%D0%B0%D0%BD%D1%81%D0%BF%D0%BE%D1%80%D1%82%D0%BD%D0%BE%D0%B9_%D0%B4%D0%BE%D1%81%D1%82%D1%83%D0%BF%D0%BD%D0%BE%D1%81%D1%82%D0%B8_%D1%81%D1%80%D0%B5%D0%B4%D1%81%D1%82%D0%B2%D0%B0%D0%BC%D0%B8_GRASS_GIS_%D0%B8_QGIS&diff=25557Базовая оценка транспортной доступности средствами GRASS GIS и QGIS2017-07-31T08:29:57Z<p>Александр Мурый: Александр Мурый переименовал страницу Базовая оценка транспортной доступности средствами GRASS GIS 7 и QGIS в [[Базовая оценка транспортной …</p>
<hr />
<div>{{Статья|Черновик}}<br />
<br />
{{Аннотация|Построение карт транспортной доступности на основе данных OpenStreetMap средствами открытых геоинформационных систем GRASS GIS и QGIS}}<br />
<br />
Картографирование транспортной доступности на основе данных о дорожной сети - одна из классических задач ГИС. Наиболее распространенным способом моделирования транспортной доступности является построение изохрон - линий равных затрат времени на преодоление пространства относительно заданных точек. В представленной статье обсуждается алгоритм построения изохрон по данным [http://www.openstreetmap.org/ OpenStreetMap] с использованием открытых ГИС [https://grass.osgeo.org/ GRASS GIS] и [http://qgis.org/ru/site/ QGIS]. В QGIS будет осуществляться подготовка данных и картографическое представление результатов, а в GRASS собственно моделирование. Всю работу можно выполнить целиком в GRASS, но, по мнению автора, общие манипуляции геоданными и представление картографических материалов удачнее и удобнее реализованы в QGIS.<br />
<br />
Описываемые в статье действия выполнялись в средах Ubuntu 14.04 LTS и Windows 8.1 (x64), GRASS GIS 7.2, QGIS 2.14.<br />
<br />
==Получение и подготовка данных==<br />
<br />
Для осуществления расчётов нам потребуется набор векторных линейных геоданных, содержащий информацию о дорожно-транспортной сети исследуемой территории. Заполучить подобную информацию можно различными способами: приобрести у специализированных поставщиков, найти в одном из источников открытых данных, оцифровать атлас автомобильных дорог, нарисовать по космическому снимку, и так далее (не забывайте про лицензии данных!). В данном случае мы воспользуемся данными OSM как достаточно качественными и подробными, и, что важно, доступными бесплатно и легально. Начать знакомство с OSM вы можете с [http://wiki.openstreetmap.org/wiki/RU:%D0%9E_%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D0%B5 этой страницы]. Этап подготовки данных являются ключевым: помимо технических этапов, здесь нам предстоит определить транспортные характеристики, которые будут использованы для моделирования.<br />
<br />
Для демонстрации принципов базового транспортного моделирования рассмотрим Тосненский район Ленинградской области. Транспорт: автомобиль.<br />
<br />
===Загрузка данных OSM===<br />
<br />
Для загрузки данных OSM существует множество возможностей. Одним из наиболее простых способов является загрузка уже подготовленных наборов данных по слоям в формате ESRI Shapefile. Популярные сервисы в сети: [http://download.geofabrik.de/ Geofabrik], где вы можете найти комплекты данных на весь мир, и [http://data.nextgis.com/osmshp/ NextGIS], где вы найдёте вместе с данными оформленные QGIS-проекты по субъектам РФ и странам СНГ. Также удобный способ быстро получить данные OSM - воспользоваться одним из соответствующих плагинов для QGIS, например, [http://plugins.qgis.org/plugins/OSMDownloader/ OSMDownloader]. При его установке в интерфейс QGIS добавится кнопка выделения прямоугольной области, для которой и будут загружены данные в указанное вами расположение. Для демонстрации в этой статье используется набор данных OSM от NextGIS на территорию Ленинградской области от 13 ноября 2016 года, доступный бесплатно ([https://drive.google.com/file/d/0BztNh0nXzyjbTUpNWVl4bk15Sjg/view?usp=sharing загрузить]).<br />
<br />
Для нашей задачи понадобятся только данные о дорожной сети (в случае работы с выгрузками NextGIS - слой ''highway-line'' из директории ''data''; в случае работы с OSMDownloader - все загруженные объекты линейного типа, у которых в атрибуте "Highway" содержится какое-либо значение). Для оформительских задач также может быть использован набор геоданных с границами административно-территориального деления (например, для обрезки изохрон по границам района). <br />
<br />
===Первичная подготовка данных в QGIS===<br />
<br />
Для начала подготовим данные по дорожной сети: обрежем их по границам района интереса, спроецируем куда нужно, отфильтруем лишнее. Распаковываем архив ''RU-LEN.7z'' в удобное расположение, лучше без кириллицы и пробелов в путях. Затем запускаем QGIS и добавляем два векторных слоя из папки .../RU-LEN/data/:<br />
* highway-line<br />
* boundary-polygon<br />
<br />
Нужно проявить осторожность с кодировками, и добавлять слои с кодировкой UTF-8 (кодировку можно выбрать в меню выбора векторного слоя при его добавлении). Выглядит это как-то так (цвета могут быть произвольными):<br />
<br />
[[Файл:Grass_qgis_isochrones_map_raw.png|600px|thumb|center|]]<br />
<br />
В первую очередь, избавимся от всего лишнего. Найдём полигон Тосненского района и сохраним его в отдельный слой. Для этого в таблице атрибутов слоя ''boundary-polygon'' (доступно через контекстное меню слоя) найдём объект с соответствующим содержанием поля NAME. Для этого можно воспользоваться поиском по запросу, либо просто отсортировать колонку и выделить нужную строку. Затем через контекстное меню слоя выбираем "Сохранить как", определяя новое расположение для файла с границами Тосненского района (в моём случае Tosnensky_boundary.shp), и не забываем активировать флаг "Сохранить только выделенные объекты".<br />
<br />
<br />
[[Файл:Grass_qgis_isochrones_table_selection.png|400px|thumb|center|]]<br />
[[Файл:Grass_qgis_isochrones_save_as.png|400px|thumb|center|]]<br />
<br />
Затем необходимо обрезать набор геоданных по дорожной сети по этим границам. Для этого воспользуемся инструментом обрезки (Вектор - Инструменты геообработки - Обрезка). В качестве исходного слоя выбираем ''highway-line'', в качестве слоя обрезки ''Tosnensky_bondary'', результат сохраняем в удобное новое расположение (в моём случае Tosnensky_roads.shp).<br />
<br />
[[Файл:Grass_qgis_isochrones_clip.png|400px|thumb|center|]]<br />
<br />
Теперь, чтобы избежать возможных проблем с расчётом длин участков дорог (и некоторых других) в будущем, перепроецируем результирующий слой ''Tosnensky_roads'' в подходящую систему координат. Для расчётных задач рекомендуется использовать поперечную цилиндрическую проекцию (UTM или Гаусса-Крюгера) для вашей зоны. В данном случае используем WGS 84 / UTM zone 36N (EPSG:32636), для этого через меню "Сохранить как" создаем копию слоя ''Tosnensky_roads'', в выпадающем списке "Система координат", выбрав UTM zone 36N и задав удобное расположение для нового файла (Tosnensku_roads_utm).<br />
<br />
[[Файл:Grass_qgis_isochrones_reproject_roads.png|400px|thumb|center|]]<br />
<br />
Аналогичным образом перепроецируем слой с границами Тосненского района, на всякий случай (получаем дополнительно Tosnensky_boundary_utm). Данные почти готовы! Теперь нужно отфильтровать геоданные по дорогам с учётом требований к моделированию. Это первый тематический шаг подготовки: '''отбор только тех объектов дорожной сети, которые могут быть использованы для перемещения предполагаемыми транспортными средствами'''. Здесь вы должны решить, по объектам каких типов будет разрешено перемещаться, например, для моделирования пешеходного движения можно оставить все типы дорог; для легковых автомобилей исключить пешеходные дорожки, тропы, просеки; для грузовых автомобилей исключить дороги с соответствующими ограничениями на массу, и так далее. Шаг этот очень ответственный. При этом подход к фильтрации сильно зависит от качества исходных данных и их детализации. Вспоминаем, что имеем дело с OSM, где различия между объектами существуют на уровне тегов, которые записаны в таблице атрибутов в поле HIGHWAY. Посмотрим на список уникальных значений для нашего набора данных:<br />
<pre><br />
path steps footway residential primary service unclassified secondary road track construction tertiary raceway tertiary_link trunk secondary_link proposed pedestrian bridleway primary_link living_street trunk_link<br />
</pre><br />
<br />
Все эти теги [http://wiki.openstreetmap.org/wiki/Key:highway описаны на ресурсах OSM], даже с картинками. Нам ещё предстоит вернуться к этим описаниям, а сейчас важно определить, по объектам каких типов может нормально передвигаться легковой автомобиль, а по каким нет. Очевидно, что нужно исключить объекты следующих типов (пригодны только для пешеходов, проектируются, строятся и т.д.):<br />
<pre><br />
path, steps, footway, construction, proposed, pedestrian, bridleway<br />
</pre><br />
<br />
Также много объектов с типом ''unclassified''. При осуществлении серьезной работы, конечно, стоит прояснить природу каждого из таких объектов по вспомогательным данным (и внести информацию в OSM, конечно же). В данном случае оставим их как подходящие для работы. Объекты всех отобранных типов нужно удалить. Для этого переходим в таблицу атрибутов слоя Tosnensky_roads_utm, выбираем инструмент "Выбрать по выражению" и формируем запрос вида:<br />
<pre><br />
"HIGHWAY" IN ( 'path' , 'steps' , 'footway' , 'construction' , 'proposed' , 'pedestrian' , 'bridleway' )<br />
</pre><br />
<br />
отмечая в списке, соответственно, выбранные вами типы дорог, которые должны быть проигнорированы. Нажимаем кнопку "выбрать" и видим на карте лишние дороги.<br />
<br />
[[Файл:Grass_qgis_isochrones_for_deleting.png|600px|thumb|center|]]<br />
<br />
Переходим в режим редактирования и удаляем выбранные объекты, сохраняем изменения. Если что, все исходные данные остаются в других слоях. Общая подготовка закончена.<br />
<br />
<br />
===Определение транспортных характеристик данных о дорожной сети в QGIS===<br />
<br />
Второй этап подготовки является самым важным и трудоёмким в реальных условиях: необходимо каждому участку дороги назначить некоторую среднюю скорость перемещения по нему. Для примера мы используем простую логику: каждому семейству объектов (по тегу) назначим общую ожидаемую скорость, не вникая в каждый отдельный объект. При серьезном исследовании, опять же, стоит более внимательно отнестись к этому этапу, по возможности используя дополнительные материалы. Итак, рассмотрим те категории, которые у нас остались после фильтрации:<br />
<br />
* trunk - важнейшие и крупнейшие дороги, например, для нашей территории, Московское шоссе. Ожидаемая скорость 90 км/ч<br />
* primary - крупные шоссе, следующий уровень после trunk. Ожидаемая скорость 90 км/ч<br />
* secondary - относительно крупные дороги, следующий уровень после primary. Ожидаемая скорость 60 км/ч<br />
* tertiary - обычные автомобильные дороги между небольшими населенными пунктами. Ожидаемая скорость 60 км/ч <br />
* living_street - жилые зоны, где у пешеходов явное преимущество в праве передвижения. Ожидаемая скорость 15 км/ч<br />
* residential - автомобильные дороги в жилых кварталах. Ожидаемая скорость около 40 км/ч<br />
* service - сервисные подъезды, въезды и проч. Ожидаемая скорость 30 км/ч<br />
* road - автомобильная дорога неизвестного типа. Примем скорость 60 км/ч<br />
* track - грунтовые дороги, обычно для сельхоз-техники. Примем скорость 30 км/ч<br />
* raceway - дороги для автомобильных видов спорта. Примем скорость 90 км/ч <br />
* tertiary_link - участки, соединяющие tertiary с другими tertiary или дорогами других типов. Примем скорость 40 км/ч<br />
* secondary_link - участки, соединяющие secondary с другими secondary или дорогами других типов. Примем скорость 40 км/ч<br />
* primary_link - участки, соединяющие primary с другими primary или дорогами других типов. Примем скорость 40 км/ч<br />
* trunk_link - участки, соединяющие trunk с другими trunk или дорогами других типов. Примем скорость 40 км/ч<br />
* unclassified - дороги без тега. Примем скорость 40 км/ч<br />
<br />
Принятые значения достаточно условны, но позволят продемонстрировать принципы дальнейшей работы. Назначаемые скорости довольно существенно зависят от конкретной территории, местного законодательства и так далее. Важно заметить, что у некоторых участков (обычно их немного) в свойствах заполнено значение MAXSPEED, оно может быть как численным (в км/ч), так и вида RU:urban. Расшифровку этих обозначений можно найти, к примеру, [http://wiki.openstreetmap.org/wiki/RU:Key:source:maxspeed здесь] и использовать при определении скоростей. '''В целом этот этап является ключевым, и в боевых условиях здесь важно внимательно и тщательно определить характеристики дорожной сети'''.<br />
<br />
Для того, чтобы применить принятые характеристики, воспользуемся калькулятором полей, запустив его из таблицы атрибутов слоя ''Tosnensky_roads_utm''. Создадим новый атрибут SPEED как целочисленный и заполним его следующим выражением:<br />
<pre><br />
CASE<br />
WHEN "HIGHWAY" = 'trunk' THEN 90<br />
WHEN "HIGHWAY" = 'primary' THEN 90<br />
WHEN "HIGHWAY" = 'secondary' THEN 60<br />
WHEN "HIGHWAY" = 'tertiary' THEN 60<br />
WHEN "HIGHWAY" = 'living_street' THEN 15<br />
WHEN "HIGHWAY" = 'residential' THEN 40<br />
WHEN "HIGHWAY" = 'service' THEN 30<br />
WHEN "HIGHWAY" = 'road' THEN 60<br />
WHEN "HIGHWAY" = 'track' THEN 30<br />
WHEN "HIGHWAY" = 'raceway' THEN 90<br />
WHEN "HIGHWAY" = 'tertiary_link' THEN 40<br />
WHEN "HIGHWAY" = 'secondary_link' THEN 40<br />
WHEN "HIGHWAY" = 'primary_link' THEN 40<br />
WHEN "HIGHWAY" = 'trunk_link' THEN 40<br />
WHEN "HIGHWAY" = 'unclassified' THEN 40<br />
END<br />
</pre><br />
<br />
Здесь мы проверяем принадлежность каждого объекта слоя к одной из категорий и назначаем скорость в соответствии с определенным ранее значениями. Очень удобно использовать условный оператор CASE.<br />
<br />
[[Файл:Grass_qgis_isochrones_speed_calc.png|600px|thumb|center|]]<br />
<br />
После этой операции у каждого объекта слоя есть характеристика скорости. Теперь, при необходимости, можно поправить её для отдельных объектов в таблице атрибутов с учётом изучения реальных характеристик территории. Мы этот шаг пропустим и приступим к следующему - расчёту времени, - которое потребуется для преодоления каждого отдельного участка дорожной сети. Для этого нужно сначала рассчитать длины всех объектов слоя. В калькуляторе атрибутов создаем новое поле LENGTH как десятичное число по формуле<br />
<pre><br />
$length/1000<br />
</pre><br />
<br />
[[Файл:Grass_qgis_isochrones_length_calc.png|400px|thumb|center|]]<br />
<br />
По умолчанию расчёт длин и площадей производится в единицах проекции, т.е. в нашем случае в метрах (квадратных метрах для площади). Делением на 1000 мы приводим расстояния к километрам. Следующий шаг - расчёт времени (предпочтительно в минутах), которое потребуется для преодоления каждого отдельного участка дороги с учётом его длины и ожидаемой скорости. В калькуляторе атрибутов создаем новое поле TIME как десятичное число по формуле<br />
<pre><br />
"LENGTH"/"SPEED" * 60<br />
</pre><br />
<br />
[[Файл:Grass_qgis_isochrones_time_calc.png|400px|thumb|center|]]<br />
<br />
<br />
=== Подготовка данных через командную строку и утилиты GDAL/OGR ===<br />
<br />
Альтернативным подходом при подготовке данных является использование утилит командной строки. Такой способ удобно использовать, к примеру, для автоматизации пакетной обработки большого количества наборов данных. Для осуществления вышеописанных операций понадобятся две утилиты: [http://www.gdal.org/ogr2ogr.html ogr2ogr] и [http://www.gdal.org/ogrinfo.html ogrinfo]. Если вы работаете в среде Linux, при установленных QGIS и/или GRASS GIS эти утилиты доступны вам из штатной системной командной строки (терминала). При работе в Windows наиболее простой способ доступа к ним - использование командной строки OSGeo4W Shell, которая автоматически устанавливается вместе с QGIS. Итак, после запуска терминала в Linux или OSGeo4W Shell в Windows выполняем следующие команды:<br />
<br />
1. Переходим в директорию, содержащую данные OSM, с помощью команды [https://en.wikipedia.org/wiki/Cd_(command) cd]:<br />
<pre>cd <путь до папки с распакованным архивом>/RU-LEN/data</pre><br />
<br />
2. С помощью утилиты ogr2ogr обрезаем слой дорог по границам Тосненского района, одновременно отфильтровывая ненужные объекты дорожной сети и проецируя результат в UTM 36N:<br />
<pre>ogr2ogr -skipfailures -t_srs "EPSG:32636" Tosnensky_roads_utm.shp highway-line.shp -clipsrc boundary-polygon.shp -clipsrcwhere "NAME = 'Тосненский район'" -nlt "LINESTRING" -sql "SELECT * FROM \"highway-line\" WHERE OGR_GEOMETRY='LINESTRING'" -sql "SELECT * FROM \"highway-line\" WHERE HIGHWAY NOT IN ('path','steps','footway','construction','proposed','pedestrian','bridleway')" -lco ENCODING=UTF-8</pre><br />
<br />
3. Конвертируем набор данных из ESRI Shapefile в SQLite для оптимизации дальнейшей работы:<br />
<pre>ogr2ogr -explodecollections -f "SQLite" -dsco SPATIALITE=YES Tosnensky_roads_utm.sqlite Tosnensky_roads_utm.shp</pre><br />
<br />
4. Добавляем в атрибуты колонки SPEED, LENGTH, TIME:<br />
<pre><br />
ogrinfo Tosnensky_roads_utm.sqlite -sql "ALTER TABLE tosnensky_roads_utm ADD COLUMN SPEED integer"<br />
ogrinfo Tosnensky_roads_utm.sqlite -sql "ALTER TABLE tosnensky_roads_utm ADD COLUMN LENGTH real"<br />
ogrinfo Tosnensky_roads_utm.sqlite -sql "ALTER TABLE tosnensky_roads_utm ADD COLUMN TIME real"<br />
</pre><br />
<br />
5. Расчитываем значения скоростей для объектов исходя из атрибута "highway":<br />
<pre><br />
ogrinfo Tosnensky_roads_utm.sqlite -sql "UPDATE tosnensky_roads_utm SET SPEED = CASE WHEN "highway" = 'trunk' THEN 90 WHEN "highway" = 'primary' THEN 90 WHEN "highway" = 'secondary' THEN 60 WHEN "highway" = 'tertiary' THEN 60 WHEN "highway" = 'living_street' THEN 15 WHEN "highway" = 'residential' THEN 40 WHEN "highway" = 'service' THEN 30 WHEN "highway" = 'road' THEN 60 WHEN "highway" = 'track' THEN 30 WHEN "highway" = 'raceway' THEN 90 WHEN "highway" = 'tertiary_link' THEN 40 WHEN "highway" = 'secondary_link' THEN 40 WHEN "highway" = 'primary_link' THEN 40 WHEN "highway" = 'trunk_link' THEN 40 WHEN "highway" = 'unclassified' THEN 40 END"<br />
</pre><br />
<br />
6. Расчитываем длины объектов дорожной сети<br />
<pre>ogrinfo Tosnensky_roads_utm.sqlite -sql "UPDATE tosnensky_roads_utm SET LENGTH = ST_length(GEOMETRY)/1000"</pre><br />
<br />
7. Расчитываем, исходя из скоростей и длин, затраты времени на преодоление объектов дорожной сети:<br />
<pre>ogrinfo Tosnensky_roads_utm.sqlite -sql "UPDATE tosnensky_roads_utm SET TIME = LENGTH/SPEED*60"</pre><br />
<br />
<br />
Теперь у нас есть для каждого участка дорожной сети оценка времени, требуемая для его преодоления. На этом подготовка данных завершена, можно приступать к моделированию и переходить в GRASS. Если вы готовили данные в QGIS, не забудьте сохранить изменения в слое и выйти из режима редактирования.<br />
<br />
<br />
==Моделирование транспортной доступности==<br />
<br />
===Подготовка проекта и данных в GRASS===<br />
<br />
Запускаем графический интерфейс GRASS GIS. В первую очередь, необходимо создать так называемую "локацию" или "область". Если вы запускаете GRASS впервые, укажите папку для хранения данных (database directory). На скриншоте ниже вы видите, что в качестве такой папки выбрана E:\transport_article<br />
<br />
[[Файл:Grass_qgis_isochrones_project1.png|600px|thumb|center|]]<br />
<br />
Теперь нужно создать новую локацию (location). Для этого используем единственную активную кнопку ''New'' и попадаем в меню, где указываем название директории для хранения данных области внутри ''database directory'', и заголовок для неё. Для простоты в примере использованы те же названия: "transport_article". Таким образом, в папке E:\transport_article будет создана папка "transport_article", доступная для выбора по такому же заголовку.<br />
<br />
[[Файл:Grass_qgis_isochrones_project2.png|600px|thumb|center|]]<br />
<br />
Далее необходимо выбрать систему координат (мы уже знаем, что нам нужна UTM 36N, которую можно найти по номеру или по названию<br />
<br />
[[Файл:Grass_qgis_isochrones_project3.png|600px|thumb|center|]]<br />
<br />
Трансформацию датума на следующем шаге, при использовании UTM, можно выбрать произвольно, т.к. эффект будет одинаков (обратите внимание, что в опции с преобразованием все смещения нулевые).<br />
<br />
[[Файл:Grass_qgis_isochrones_project4.png|400px|thumb|center|]]<br />
<br />
Далее проверяем, что все параметры правильные, и завершаем создание области.<br />
<br />
[[Файл:Grass_qgis_isochrones_project5.png|600px|thumb|center|]]<br />
<br />
По завершению создания области будет предложено установить охват и разрешение региона. Это мы сделаем позднее, а сейчас откажемся.<br />
<br />
[[Файл:Grass_qgis_isochrones_project6.png|400px|thumb|center|]]<br />
<br />
На предложение же создать новый набор ("mapset") ответим утвердительно, и создадим его с именем ''tosno'', что будет отсылкой к конкретному району исследования.<br />
<br />
[[Файл:Grass_qgis_isochrones_project7.png|400px|thumb|center|]]<br />
<br />
В итоге в стартовом меню появляются созданные локация (tranport_article) и набор (tosno), выбрав которые можно запускать собственно GRASS (кнопка ''Start GRASS session'')<br />
<br />
[[Файл:Grass_qgis_isochrones_project8.png|600px|thumb|center|]]<br />
<br />
Наконец-то, начинается что-то интересное! Для начала нужно установить плагин, который и будет ответственным за построение изохрон: [https://grass.osgeo.org/grass72/manuals/addons/v.isochrones.html v.isochrones]. Для этого обратимся к меню ''Settings - Addons extensions - Install extensions from addons''.<br />
<br />
[[Файл:Grass_qgis_isochrones_extensions1.png|600px|thumb|center|]]<br />
<br />
В открывшемся меню в подгруппе ''vector'' находим модуль ''v.isochrones'' и устанавливаем его.<br />
<br />
[[Файл:Grass_qgis_isochrones_extensions2.png|600px|thumb|center|]]<br />
<br />
В консоли эта операция тоже выполняется просто:<br />
<pre>g.extension extension=v.isochrones operation=add</pre><br />
<br />
Если у вас возникают какие-либо проблемы с доступом к репозиторию с модулями GRASS (плагины не доступны для загрузки через меню "extensions"), это не критично: можно загрузить исходный Python-скрипт по [https://trac.osgeo.org/grass/browser/grass-addons/grass7/vector/v.isochrones/v.isochrones.py ссылке], и запускать модуль, обращаясь напрямую к скрипту через ''File - Launch script''. При таком сценарии у вас откроется тот же графический интерфейс, который будет рассмотрен в дальнейшем.<br />
<br />
Теперь давайте добавим в GRASS те данные, которые мы подготовили в QGIS или с помощью утилит GDAL/OGR. Для этого используем функцию импорта векторных данных ([https://grass.osgeo.org/grass72/manuals/v.in.ogr.html v.in.ogr]) или [https://grass.osgeo.org/grass72/manuals/v.import.html v.import]. В графическом интерфейсе искомое меню открывается через ''File - Import vector data - common import formats''. Выбираем наш набор геоданных "Tosnensky_roads_utm.shp" или "Tosnensky_roads_utm.sqlite":<br />
<br />
[[Файл:Grass_qgis_isochrones_import1.png|600px|thumb|center|]]<br />
<br />
<pre>v.import input=E:\qgis_grass_transport\data\Tosnensky_roads_utm.shp layer=Tosnensky_roads_utm output=Tosnensky_roads_utm</pre><br />
<br />
В консоли мы видим результат импорта и базовые сведения о данных, а в окне карты видим простую визуализацию.<br />
<br />
[[Файл:Grass_qgis_isochrones_map1.png|600px|thumb|center|]]<br />
<br />
Теперь создадим точечный слой, в котором будут содержаться местоположения, относительно которых требуется рассчитать изохроны. Это удобнее делать в интерактивном режиме в окне карты. Перейдем из режима 2D карты в режим оцифровки:<br />
<br />
[[Файл:grass_qgis_isochrones_digit.png|600px|thumb|center|]]<br />
<br />
Затем в крайнем слева выпадающем списке выберем опцию создания нового векторного слоя и зададим для него какое-нибудь подходящее название, например, ''start_points''. При этом атрибуты нам не нужны? и соответствующий флаг в меню создания слоя можно снять.<br />
<br />
[[Файл:grass_qgis_isochrones_digit2.png|600px|thumb|center|]]<br />
[[Файл:grass_qgis_isochrones_digit3.png|400px|thumb|center|]]<br />
<br />
Теперь выбираем инструмент "Создать новую точку" и добавляем интересующие нас локации. Для демонстрации поставим точки отсчёта в центры населенных пунктов г. Тосно и г. Любань. После этого выходим из режима оцифровки, соглашаясь на сохранение изменений.<br />
<br />
[[Файл:grass_qgis_isochrones_digit4.png|600px|thumb|center|]]<br />
<br />
Обратим внимание, что теперь на вкладке Слои (Layers) имеем два набора: импортированную дорожную сеть и созданные начальные точки.<br />
<br />
[[Файл:grass_qgis_isochrones_layers1.png|600px|thumb|center|]]<br />
<br />
Продолжаем подготовку данных. Поскольку в планах у нас занятия сетевым анализом, нужно набор линейных данных преобразовать в набор сетевых данных с помощью инструмента [https://grass.osgeo.org/grass72/manuals/v.net.html v.net], который доступен в меню Vector - Network analysis - Network preparation. Всё, что нам нужно, это применить операцию ''nodes'' к нашему слою "Tosnensky_roads_utm". На вкладке "Необходимо" выбираем метод ''nodes'', на вкладке ''Arcs'' выбираем слой "Tosnensky_roads_utm@tosno", на вкладке ''Опционный'' задаем имя для результирующего набора. Например, "Tosnensky_roads_utm_net".<br />
<br />
[[Файл:grass_qgis_isochrones_vnet1.png|600px|thumb|center|]]<br />
[[Файл:grass_qgis_isochrones_vnet2.png|600px|thumb|center|]]<br />
[[Файл:grass_qgis_isochrones_vnet3.png|600px|thumb|center|]]<br />
<br />
<pre>v.net -c input=Tosnensky_roads_utm operation=nodes output=Tosnensky_roads_utm_net</pre><br />
<br />
Следующий (и последний) шаг подготовки данных - это задание региона и разрешения для моделирования с помощью модуля [https://grass.osgeo.org/grass72/manuals/g.region.html g.region], который доступен в меню "Settings - Регион - Установить регион". На первой вкладке в выпадающем списке ''Set region to match vector maps'' выбираем слой "Tosnensky_roads_utm_net", созданный на предыдущем шаге, на вкладке "Границы" устанавливаем флаг "Подогнать регион под разрешение" и на вкладке "Разрешение" назначаем для "2D grid resolution" подходящее значение. Этот последний параметр очень важен - это размер ячеек сетки, до которых и будет производиться расчёт времени перемещения. Он должен быть достаточно небольшим, чтобы изохроны были плавными, но и не совсем маленьким, потому что чем подробнее сетка, тем более ресурсоёмкими получаются вычисления. Для территории подобной демонстрационной подходит размер 20 метров. Другие параметры не изменяются.<br />
<br />
[[Файл:grass_qgis_isochrones_gregion1.png|600px|thumb|center|]]<br />
[[Файл:grass_qgis_isochrones_gregion2.png|600px|thumb|center|]]<br />
[[Файл:grass_qgis_isochrones_gregion3.png|600px|thumb|center|]]<br />
<br />
<pre>g.region vector=Tosnensky_roads_utm_net res=20 -a</pre><br />
<br />
Теперь всё готово для создания самих изохрон и некоторых других выходных данных.<br />
<br />
===Создание изохрон===<br />
<br />
Модуль [https://grass.osgeo.org/grass72/manuals/addons/v.isochrones.html v.isochrones] позволяет работать в двух режимах: создавать непрерывные изохроны, то есть поля затрат на перемещение (с использованием модуля ''r.cost''), и просто откладывать преодолеваемое расстояние вдоль дорог (что хорошо подходит для случая, когда перемещение за пределами транспортной сети невозможно совсем - с использованиям модуля ''v.net.iso''). Нас интересуют непрерывные изохроны. Запускаем модуль v.isochrones, набрав такую команду в консоли:<br />
<pre>v.isochrones</pre><br />
Можно также запустить загруженный скрипт через ''File - Launch script''. На первой вкладке в качестве дорожной сети выбираем сетевой набор данных "Tosnensky_roads_utm_net@tosno", название атрибута с ценой - SPEED, стартовые точки - "start_points@tosno", выбираем удобное название для слоя с изохронами (например, "isochrones_rcost"), выбираем метод ''r.cost'' и (важное!) собственно значения изохрон в минутах через запятую. Здесь значения могут быть любыми в зависимости от ваших задач. Зададим следующие отсечки по времени: 15, 30, 60, 90, 120 и 150 минут. Далее, на вкладке ''r.cost'' задаются очень важные параметры: название для поверхности времени перемещений (опциональный продукт, но очень интересный, сохраним результат в слой timemap_rcost и посмотрим, что в нем будет), скорость для преодоления пространства вне дорожной сети (вышли на обочине и пошли пешком через поле, установим 5 км/ч) и доступная для расчётов память. Обычно хватает 1000 МБ.<br />
<br />
[[Файл:grass_qgis_isochrones_rcost1.png|600px|thumb|center|]]<br />
[[Файл:grass_qgis_isochrones_rcost2.png|600px|thumb|center|]]<br />
<br />
<pre>v.isochrones map=Tosnensky_roads_utm_net@tosno roads_layer=1 cost_column=SPEED start_points=start_points@tosno isochrones=isochrones_rcost time_steps=15,30,60,90,120,150 timemap=timemap_rcost memory=1000 method=r.cost</pre><br />
<br />
После выполнения обработки в дереве слоёв появляются два новых: изохроны и поверхность затрат времени. Это и есть искомый результат. Оформим его несколько позднее, пока просто взглянем:<br />
<br />
[[Файл:grass_qgis_isochrones_rcost3.png|600px|thumb|center|]]<br />
[[Файл:grass_qgis_isochrones_rcost4.png|600px|thumb|center|]]<br />
<br />
Выглядит неплохо! Изучим данные немного детальнее в QGIS, для этого экспортируем данные в стандартные ГИС-форматы с помощью команд [https://grass.osgeo.org/grass72/manuals/v.out.ogr.html v.out.ogr] и [https://grass.osgeo.org/grass72/manuals/r.out.gdal.html r.out.gdal], они доступы в меню File - экспорт векторного слоя и File - экспорт растрового слоя соответственно.<br />
<br />
[[Файл:grass_qgis_isochrones_export1.png|600px|thumb|center|]]<br />
<br />
<pre>v.out.ogr input=isochrones_rcost@tosno output=E:\qgis_grass_transport\data\isochrones.shp format=ESRI_Shapefile</pre><br />
<br />
[[Файл:grass_qgis_isochrones_export2.png|600px|thumb|center|]]<br />
<br />
<pre>r.out.gdal input=timemap_rcost@tosno output=E:\qgis_grass_transport\data\timemap.tif format=GTiff</pre><br />
<br />
Теперь можно использовать данные в других ГИС и делиться ими!<br />
<br />
==Представление результатов в QGIS==<br />
<br />
Посмотрим на данные в QGIS, и оформим простую карту. Сначала добавим растровый слой ''timemap.tif'', что он из себя представляет? Оказывается, это очень интересный набор данных, как бы предшествующий изохронам - растр с тем самым разрешением 20х20 метров, указанным на этапе задания области, в каждой ячейке которого хранится время в минутах, сколько туда добираться из указанных точек. Такой набор данных может быть очень полезен при разнообразном растровом анализе как один из входных слоёв.<br />
<br />
[[Файл:grass_qgis_isochrones_rcost_result1.png|600px|thumb|center|]]<br />
<br />
Слой же с изохронами содержит полигоны, охватывающие территории, соответствующие диапазонам времени перемещения: от 0 до 15, от 15 до 30, и так далее. Диапазоны мы ранее задавали самостоятельно. Важно, что в атрибутах хранятся данные о временном диапазоне, а внешние полигоны (более дальние) не содержат внутренних (ближних), такие данные открывают широкие возможности для разнообразного анализа.<br />
<br />
[[Файл:grass_qgis_isochrones_rcost_result2.png|600px|thumb|center|]]<br />
<br />
Применив простую заливку по уникальным значениям поля ''traveltime'' и подложив какую-нибудь базовую карту, можно быстро получить достаточно симпатичную визуализацию изохрон.<br />
<br />
[[Файл:grass_qgis_isochrones_rcost_result3.png|600px|thumb|center|]]<br />
<br />
Хотя ценнее в данном случае, конечно, сами данные.<br />
<br />
Итак, рассмотрен простой подход к транспортному моделированию способом построения изохрон в GRASS GIS. Возможности такого подхода весьма ограничены, особенно в аспектах, касающихся учёта разнородных препятствий, и так далее. Тем не менее, при внимательной подготовке исходных данных о дорожной сети, можно получать интересные результаты, которые применимы при различных сценариях анализа транспортной доступности.</div>Александр Мурыйhttps://wiki.gis-lab.info/index.php?title=%D0%91%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D1%8F_%D0%BE%D1%86%D0%B5%D0%BD%D0%BA%D0%B0_%D1%82%D1%80%D0%B0%D0%BD%D1%81%D0%BF%D0%BE%D1%80%D1%82%D0%BD%D0%BE%D0%B9_%D0%B4%D0%BE%D1%81%D1%82%D1%83%D0%BF%D0%BD%D0%BE%D1%81%D1%82%D0%B8_%D1%81%D1%80%D0%B5%D0%B4%D1%81%D1%82%D0%B2%D0%B0%D0%BC%D0%B8_GRASS_GIS_%D0%B8_QGIS&diff=25552Базовая оценка транспортной доступности средствами GRASS GIS и QGIS2017-07-30T08:17:08Z<p>Александр Мурый: </p>
<hr />
<div>{{Статья|Черновик}}<br />
<br />
{{Аннотация|Построение карт транспортной доступности на основе данных OpenStreetMap средствами открытых геоинформационных систем GRASS GIS и QGIS}}<br />
<br />
Картографирование транспортной доступности на основе данных о дорожной сети - одна из классических задач ГИС. Наиболее распространенным способом моделирования транспортной доступности является построение изохрон - линий равных затрат времени на преодоление пространства относительно заданных точек. В представленной статье обсуждается алгоритм построения изохрон по данным [http://www.openstreetmap.org/ OpenStreetMap] с использованием открытых ГИС [https://grass.osgeo.org/ GRASS GIS] и [http://qgis.org/ru/site/ QGIS]. В QGIS будет осуществляться подготовка данных и картографическое представление результатов, а в GRASS собственно моделирование. Всю работу можно выполнить целиком в GRASS, но, по мнению автора, общие манипуляции геоданными и представление картографических материалов удачнее и удобнее реализованы в QGIS.<br />
<br />
Описываемые в статье действия выполнялись в средах Ubuntu 14.04 LTS и Windows 8.1 (x64), GRASS 7.2, QGIS 2.14.<br />
<br />
==Получение и подготовка данных==<br />
<br />
Для осуществления расчётов нам потребуется набор векторных линейных геоданных, содержащий информацию о дорожно-транспортной сети исследуемой территории. Заполучить подобную информацию можно различными способами: приобрести у специализированных поставщиков, найти в одном из источников открытых данных, оцифровать атлас автомобильных дорог, нарисовать по космическому снимку, и так далее (не забывайте про лицензии данных!). В данном случае мы воспользуемся данными OSM как достаточно качественными и подробными, и, что важно, доступными бесплатно и легально. Начать знакомство с OSM вы можете с [http://wiki.openstreetmap.org/wiki/RU:%D0%9E_%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D0%B5 этой страницы]. Этап подготовки данных являются ключевым: помимо технических этапов, здесь нам предстоит определить транспортные характеристики, которые будут использованы для моделирования.<br />
<br />
Для демонстрации принципов базового транспортного моделирования рассмотрим Тосненский район Ленинградской области. Транспорт: автомобиль.<br />
<br />
===Загрузка данных OSM===<br />
<br />
Для загрузки данных OSM существует множество возможностей. Одним из наиболее простых способов является загрузка уже подготовленных наборов данных по слоям в формате ESRI Shapefile. Популярные сервисы в сети: [http://download.geofabrik.de/ Geofabrik], где вы можете найти комплекты данных на весь мир, и [http://data.nextgis.com/osmshp/ NextGIS], где вы найдёте вместе с данными оформленные QGIS-проекты по субъектам РФ и странам СНГ. Также удобный способ быстро получить данные OSM - воспользоваться одним из соответствующих плагинов для QGIS, например, [http://plugins.qgis.org/plugins/OSMDownloader/ OSMDownloader]. При его установке в интерфейс QGIS добавится кнопка выделения прямоугольной области, для которой и будут загружены данные в указанное вами расположение. Для демонстрации в этой статье используется набор данных OSM от NextGIS на территорию Ленинградской области от 13 ноября 2016 года, доступный бесплатно ([https://drive.google.com/file/d/0BztNh0nXzyjbTUpNWVl4bk15Sjg/view?usp=sharing загрузить]).<br />
<br />
Для нашей задачи понадобятся только данные о дорожной сети (в случае работы с выгрузками NextGIS - слой ''highway-line'' из директории ''data''; в случае работы с OSMDownloader - все загруженные объекты линейного типа, у которых в атрибуте "Highway" содержится какое-либо значение). Для оформительских задач также может быть использован набор геоданных с границами административно-территориального деления (например, для обрезки изохрон по границам района). <br />
<br />
===Первичная подготовка данных===<br />
<br />
Для начала подготовим данные по дорожной сети: обрежем их по границам района интереса, спроецируем куда нужно, отфильтруем лишнее. Распаковываем архив ''RU-LEN.7z'' в удобное расположение, лучше без кириллицы и пробелов в путях. Затем запускаем QGIS и добавляем два векторных слоя из папки .../RU-LEN/data/:<br />
* highway-line<br />
* boundary-polygon<br />
<br />
Нужно проявить осторожность с кодировками, и добавлять слои с кодировкой UTF-8 (кодировку можно выбрать в меню выбора векторного слоя при его добавлении). Выглядит это как-то так (цвета могут быть произвольными):<br />
<br />
[[Файл:Grass_qgis_isochrones_map_raw.png|600px|thumb|center|]]<br />
<br />
В первую очередь, избавимся от всего лишнего. Найдём полигон Тосненского района и сохраним его в отдельный слой. Для этого в таблице атрибутов слоя ''boundary-polygon'' (доступно через контекстное меню слоя) найдём объект с соответствующим содержанием поля NAME. Для этого можно воспользоваться поиском по запросу, либо просто отсортировать колонку и выделить нужную строку. Затем через контекстное меню слоя выбираем "Сохранить как", определяя новое расположение для файла с границами Тосненского района (в моём случае Tosnensky_boundary.shp), и не забываем активировать флаг "Сохранить только выделенные объекты".<br />
<br />
<br />
[[Файл:Grass_qgis_isochrones_table_selection.png|400px|thumb|center|]]<br />
[[Файл:Grass_qgis_isochrones_save_as.png|400px|thumb|center|]]<br />
<br />
Затем необходимо обрезать набор геоданных по дорожной сети по этим границам. Для этого воспользуемся инструментом обрезки (Вектор - Инструменты геообработки - Обрезка). В качестве исходного слоя выбираем ''highway-line'', в качестве слоя обрезки ''Tosnensky_bondary'', результат сохраняем в удобное новое расположение (в моём случае Tosnensky_roads.shp).<br />
<br />
[[Файл:Grass_qgis_isochrones_clip.png|400px|thumb|center|]]<br />
<br />
Теперь, чтобы избежать возможных проблем с расчётом длин участков дорог (и некоторых других) в будущем, перепроецируем результирующий слой ''Tosnensky_roads'' в подходящую систему координат. Для расчётных задач рекомендуется использовать поперечную цилиндрическую проекцию (UTM или Гаусса-Крюгера) для вашей зоны. В данном случае используем WGS 84 / UTM zone 36N (EPSG:32636), для этого через меню "Сохранить как" создаем копию слоя ''Tosnensky_roads'', в выпадающем списке "Система координат", выбрав UTM zone 36N и задав удобное расположение для нового файла (Tosnensku_roads_utm).<br />
<br />
[[Файл:Grass_qgis_isochrones_reproject_roads.png|400px|thumb|center|]]<br />
<br />
Аналогичным образом перепроецируем слой с границами Тосненского района, на всякий случай (получаем дополнительно Tosnensky_boundary_utm). Данные почти готовы! Теперь нужно отфильтровать геоданные по дорогам с учётом требований к моделированию. Это первый тематический шаг подготовки: '''отбор только тех объектов дорожной сети, которые могут быть использованы для перемещения предполагаемыми транспортными средствами'''. Здесь вы должны решить, по объектам каких типов будет разрешено перемещаться, например, для моделирования пешеходного движения можно оставить все типы дорог; для легковых автомобилей исключить пешеходные дорожки, тропы, просеки; для грузовых автомобилей исключить дороги с соответствующими ограничениями на массу, и так далее. Шаг этот очень ответственный. При этом подход к фильтрации сильно зависит от качества исходных данных и их детализации. Вспоминаем, что имеем дело с OSM, где различия между объектами существуют на уровне тегов, которые записаны в таблице атрибутов в поле HIGHWAY. Посмотрим на список уникальных значений для нашего набора данных:<br />
<pre><br />
path steps footway residential primary service unclassified secondary road track construction tertiary raceway tertiary_link trunk secondary_link proposed pedestrian bridleway primary_link living_street trunk_link<br />
</pre><br />
<br />
Все эти теги [http://wiki.openstreetmap.org/wiki/Key:highway описаны на ресурсах OSM], даже с картинками. Нам ещё предстоит вернуться к этим описаниям, а сейчас важно определить, по объектам каких типов может нормально передвигаться легковой автомобиль, а по каким нет. Очевидно, что нужно исключить объекты следующих типов (пригодны только для пешеходов, проектируются, строятся и т.д.):<br />
<pre><br />
path, steps, footway, construction, proposed, pedestrian, bridleway<br />
</pre><br />
<br />
Также много объектов с типом ''unclassified''. При осуществлении серьезной работы, конечно, стоит прояснить природу каждого из таких объектов по вспомогательным данным (и внести информацию в OSM, конечно же). В данном случае оставим их как подходящие для работы. Объекты всех отобранных типов нужно удалить. Для этого переходим в таблицу атрибутов слоя Tosnensky_roads_utm, выбираем инструмент "Выбрать по выражению" и формируем запрос вида:<br />
<pre><br />
"HIGHWAY" IN ( 'path' , 'steps' , 'footway' , 'construction' , 'proposed' , 'pedestrian' , 'bridleway' )<br />
</pre><br />
<br />
отмечая в списке, соответственно, выбранные вами типы дорог, которые должны быть проигнорированы. Нажимаем кнопку "выбрать" и видим на карте лишние дороги.<br />
<br />
[[Файл:Grass_qgis_isochrones_for_deleting.png|600px|thumb|center|]]<br />
<br />
Переходим в режим редактирования и удаляем выбранные объекты, сохраняем изменения. Если что, все исходные данные остаются в других слоях. Общая подготовка закончена.<br />
<br />
<br />
===Определение транспортных характеристик данных о дорожной сети===<br />
<br />
Второй этап подготовки является самым важным и трудоёмким в реальных условиях: необходимо каждому участку дороги назначить некоторую среднюю скорость перемещения по нему. Для примера мы используем простую логику: каждому семейству объектов (по тегу) назначим общую ожидаемую скорость, не вникая в каждый отдельный объект. При серьезном исследовании, опять же, стоит более внимательно отнестись к этому этапу, по возможности используя дополнительные материалы. Итак, рассмотрим те категории, которые у нас остались после фильтрации:<br />
<br />
* trunk - важнейшие и крупнейшие дороги, например, для нашей территории, Московское шоссе. Ожидаемая скорость 90 км/ч<br />
* primary - крупные шоссе, следующий уровень после trunk. Ожидаемая скорость 90 км/ч<br />
* secondary - относительно крупные дороги, следующий уровень после primary. Ожидаемая скорость 60 км/ч<br />
* tertiary - обычные автомобильные дороги между небольшими населенными пунктами. Ожидаемая скорость 60 км/ч <br />
* living_street - жилые зоны, где у пешеходов явное преимущество в праве передвижения. Ожидаемая скорость 15 км/ч<br />
* residential - автомобильные дороги в жилых кварталах. Ожидаемая скорость около 40 км/ч<br />
* service - сервисные подъезды, въезды и проч. Ожидаемая скорость 30 км/ч<br />
* road - автомобильная дорога неизвестного типа. Примем скорость 60 км/ч<br />
* track - грунтовые дороги, обычно для сельхоз-техники. Примем скорость 30 км/ч<br />
* raceway - дороги для автомобильных видов спорта. Примем скорость 90 км/ч <br />
* tertiary_link - участки, соединяющие tertiary с другими tertiary или дорогами других типов. Примем скорость 40 км/ч<br />
* secondary_link - участки, соединяющие secondary с другими secondary или дорогами других типов. Примем скорость 40 км/ч<br />
* primary_link - участки, соединяющие primary с другими primary или дорогами других типов. Примем скорость 40 км/ч<br />
* trunk_link - участки, соединяющие trunk с другими trunk или дорогами других типов. Примем скорость 40 км/ч<br />
* unclassified - дороги без тега. Примем скорость 40 км/ч<br />
<br />
Принятые значения достаточно условны, но позволят продемонстрировать принципы дальнейшей работы. Назначаемые скорости довольно существенно зависят от конкретной территории, местного законодательства и так далее. Важно заметить, что у некоторых участков (обычно их немного) в свойствах заполнено значение MAXSPEED, оно может быть как численным (в км/ч), так и вида RU:urban. Расшифровку этих обозначений можно найти, к примеру, [http://wiki.openstreetmap.org/wiki/RU:Key:source:maxspeed здесь] и использовать при определении скоростей. '''В целом этот этап является ключевым, и в боевых условиях здесь важно внимательно и тщательно определить характеристики дорожной сети'''.<br />
<br />
Для того, чтобы применить принятые характеристики, воспользуемся калькулятором полей, запустив его из таблицы атрибутов слоя ''Tosnensky_roads_utm''. Создадим новый атрибут SPEED как целочисленный и заполним его следующим выражением:<br />
<pre><br />
CASE<br />
WHEN "HIGHWAY" = 'trunk' THEN 90<br />
WHEN "HIGHWAY" = 'primary' THEN 90<br />
WHEN "HIGHWAY" = 'secondary' THEN 60<br />
WHEN "HIGHWAY" = 'tertiary' THEN 60<br />
WHEN "HIGHWAY" = 'living_street' THEN 15<br />
WHEN "HIGHWAY" = 'residential' THEN 40<br />
WHEN "HIGHWAY" = 'service' THEN 30<br />
WHEN "HIGHWAY" = 'road' THEN 60<br />
WHEN "HIGHWAY" = 'track' THEN 30<br />
WHEN "HIGHWAY" = 'raceway' THEN 90<br />
WHEN "HIGHWAY" = 'tertiary_link' THEN 40<br />
WHEN "HIGHWAY" = 'secondary_link' THEN 40<br />
WHEN "HIGHWAY" = 'primary_link' THEN 40<br />
WHEN "HIGHWAY" = 'trunk_link' THEN 40<br />
WHEN "HIGHWAY" = 'unclassified' THEN 40<br />
END<br />
</pre><br />
<br />
Здесь мы проверяем принадлежность каждого объекта слоя к одной из категорий и назначаем скорость в соответствии с определенным ранее значениями. Очень удобно использовать условный оператор CASE.<br />
<br />
[[Файл:Grass_qgis_isochrones_speed_calc.png|600px|thumb|center|]]<br />
<br />
После этой операции у каждого объекта слоя есть характеристика скорости. Теперь, при необходимости, можно поправить её для отдельных объектов в таблице атрибутов с учётом изучения реальных характеристик территории. Мы этот шаг пропустим и приступим к следующему - расчёту времени, - которое потребуется для преодоления каждого отдельного участка дорожной сети. Для этого нужно сначала рассчитать длины всех объектов слоя. В калькуляторе атрибутов создаем новое поле LENGTH как десятичное число по формуле<br />
<pre><br />
$length/1000<br />
</pre><br />
<br />
[[Файл:Grass_qgis_isochrones_length_calc.png|400px|thumb|center|]]<br />
<br />
По умолчанию расчёт длин и площадей производится в единицах проекции, т.е. в нашем случае в метрах (квадратных метрах для площади). Делением на 1000 мы приводим расстояния к километрам. Следующий шаг - расчёт времени (предпочтительно в минутах), которое потребуется для преодоления каждого отдельного участка дороги с учётом его длины и ожидаемой скорости. В калькуляторе атрибутов создаем новое поле TIME как десятичное число по формуле<br />
<pre><br />
"LENGTH"/"SPEED" * 60<br />
</pre><br />
<br />
[[Файл:Grass_qgis_isochrones_time_calc.png|400px|thumb|center|]]<br />
<br />
<br />
Теперь у нас есть для каждого участка дорожной сети оценка времени, требуемая для его преодоления. На этом подготовка данных в QGIS завершена, можно приступать к моделированию и переходить в GRASS. Не забудьте сохранить изменения в слое и выйти из режима редактирования.<br />
<br />
==Моделирование транспортной доступности==<br />
<br />
===Подготовка проекта и данных в GRASS===<br />
<br />
Запускаем графический интерфейс GRASS GIS 7. В первую очередь, необходимо создать так называемую "локацию" или "область". Если вы запускаете GRASS впервые, укажите папку для хранения данных (database directory). На скриншоте ниже вы видите, что в качестве такой папки выбрана E:\transport_article<br />
<br />
[[Файл:Grass_qgis_isochrones_project1.png|600px|thumb|center|]]<br />
<br />
Теперь нужно создать новую локацию (location). Для этого используем единственную активную кнопку ''New'' и попадаем в меню, где указываем название директории для хранения данных области внутри ''database directory'', и заголовок для неё. Для простоты в примере использованы те же названия: "transport_article". Таким образом, в папке E:\transport_article будет создана папка "transport_article", доступная для выбора по такому же заголовку.<br />
<br />
[[Файл:Grass_qgis_isochrones_project2.png|600px|thumb|center|]]<br />
<br />
Далее необходимо выбрать систему координат (мы уже знаем, что нам нужна UTM 36N, которую можно найти по номеру или по названию<br />
<br />
[[Файл:Grass_qgis_isochrones_project3.png|600px|thumb|center|]]<br />
<br />
Трансформацию датума на следующем шаге, при использовании UTM, можно выбрать произвольно, т.к. эффект будет одинаков (обратите внимание, что в опции с преобразованием все смещения нулевые).<br />
<br />
[[Файл:Grass_qgis_isochrones_project4.png|400px|thumb|center|]]<br />
<br />
Далее проверяем, что все параметры правильные, и завершаем создание области.<br />
<br />
[[Файл:Grass_qgis_isochrones_project5.png|600px|thumb|center|]]<br />
<br />
По завершению создания области будет предложено установить охват и разрешение региона. Это мы сделаем позднее, а сейчас откажемся.<br />
<br />
[[Файл:Grass_qgis_isochrones_project6.png|400px|thumb|center|]]<br />
<br />
На предложение же создать новый набор ("mapset") ответим утвердительно, и создадим его с именем ''tosno'', что будет отсылкой к конкретному району исследования.<br />
<br />
[[Файл:Grass_qgis_isochrones_project7.png|400px|thumb|center|]]<br />
<br />
В итоге в стартовом меню появляются созданные локация (tranport_article) и набор (tosno), выбрав которые можно запускать собственно GRASS (кнопка ''Start GRASS session'')<br />
<br />
[[Файл:Grass_qgis_isochrones_project8.png|600px|thumb|center|]]<br />
<br />
Наконец-то, начинается что-то интересное! Для начала нужно установить плагин, который и будет ответственным за построение изохрон: [https://grass.osgeo.org/grass72/manuals/addons/v.isochrones.html v.isochrones]. Для этого обратимся к меню ''Settings - Addons extensions - Install extensions from addons''.<br />
<br />
[[Файл:Grass_qgis_isochrones_extensions1.png|600px|thumb|center|]]<br />
<br />
В открывшемся меню в подгруппе ''vector'' находим модуль ''v.isochrones'' и устанавливаем его.<br />
<br />
[[Файл:Grass_qgis_isochrones_extensions2.png|600px|thumb|center|]]<br />
<br />
В консоли эта операция тоже выполняется просто:<br />
<pre>g.extension extension=v.isochrones operation=add</pre><br />
<br />
Если у вас возникают какие-либо проблемы с доступом к репозиторию с модулями GRASS (плагины не доступны для загрузки через меню "extensions"), это не критично: можно загрузить исходный Python-скрипт по [https://trac.osgeo.org/grass/browser/grass-addons/grass7/vector/v.isochrones/v.isochrones.py ссылке], и запускать модуль, обращаясь напрямую к скрипту через ''File - Launch script''. При таком сценарии у вас откроется тот же графический интерфейс, который будет рассмотрен в дальнейшем.<br />
<br />
Теперь давайте добавим в GRASS те данные, которые мы подготовили в QGIS. Для этого используем функцию импорта векторных данных ([https://grass.osgeo.org/grass72/manuals/v.in.ogr.html v.in.ogr]) или [https://grass.osgeo.org/grass72/manuals/v.import.html v.import]. В графическом интерфейсе искомое меню открывается через ''File - Import vector data - common import formats''. Выбираем наш набор геоданных "Tosnensky_roads_utm.shp":<br />
<br />
[[Файл:Grass_qgis_isochrones_import1.png|600px|thumb|center|]]<br />
<br />
<pre>v.import input=E:\qgis_grass_transport\data\Tosnensky_roads_utm.shp layer=Tosnensky_roads_utm output=Tosnensky_roads_utm</pre><br />
<br />
В консоли мы видим результат импорта и базовые сведения о данных, а в окне карты видим простую визуализацию.<br />
<br />
[[Файл:Grass_qgis_isochrones_map1.png|600px|thumb|center|]]<br />
<br />
Теперь создадим точечный слой, в котором будут содержаться местоположения, относительно которых требуется рассчитать изохроны. Это удобнее делать в интерактивном режиме в окне карты. Перейдем из режима 2D карты в режим оцифровки:<br />
<br />
[[Файл:grass_qgis_isochrones_digit.png|600px|thumb|center|]]<br />
<br />
Затем в крайнем слева выпадающем списке выберем опцию создания нового векторного слоя и зададим для него какое-нибудь подходящее название, например, ''start_points''. При этом атрибуты нам не нужны? и соответствующий флаг в меню создания слоя можно снять.<br />
<br />
[[Файл:grass_qgis_isochrones_digit2.png|600px|thumb|center|]]<br />
[[Файл:grass_qgis_isochrones_digit3.png|400px|thumb|center|]]<br />
<br />
Теперь выбираем инструмент "Создать новую точку" и добавляем интересующие нас локации. Для демонстрации поставим точки отсчёта в центры населенных пунктов г. Тосно и г. Любань. После этого выходим из режима оцифровки, соглашаясь на сохранение изменений.<br />
<br />
[[Файл:grass_qgis_isochrones_digit4.png|600px|thumb|center|]]<br />
<br />
Обратим внимание, что теперь на вкладке Слои (Layers) имеем два набора: импортированную дорожную сеть и созданные начальные точки.<br />
<br />
[[Файл:grass_qgis_isochrones_layers1.png|600px|thumb|center|]]<br />
<br />
Продолжаем подготовку данных. Поскольку в планах у нас занятия сетевым анализом, нужно набор линейных данных преобразовать в набор сетевых данных с помощью инструмента [https://grass.osgeo.org/grass72/manuals/v.net.html v.net], который доступен в меню Vector - Network analysis - Network preparation. Всё, что нам нужно, это применить операцию ''nodes'' к нашему слою "Tosnensky_roads_utm". На вкладке "Необходимо" выбираем метод ''nodes'', на вкладке ''Arcs'' выбираем слой "Tosnensky_roads_utm@tosno", на вкладке ''Опционный'' задаем имя для результирующего набора. Например, "Tosnensky_roads_utm_net".<br />
<br />
[[Файл:grass_qgis_isochrones_vnet1.png|600px|thumb|center|]]<br />
[[Файл:grass_qgis_isochrones_vnet2.png|600px|thumb|center|]]<br />
[[Файл:grass_qgis_isochrones_vnet3.png|600px|thumb|center|]]<br />
<br />
<pre>v.net -c input=Tosnensky_roads_utm operation=nodes output=Tosnensky_roads_utm_net</pre><br />
<br />
Следующий (и последний) шаг подготовки данных - это задание региона и разрешения для моделирования с помощью модуля [https://grass.osgeo.org/grass72/manuals/g.region.html g.region], который доступен в меню "Settings - Регион - Установить регион". На первой вкладке в выпадающем списке ''Set region to match vector maps'' выбираем слой "Tosnensky_roads_utm_net", созданный на предыдущем шаге, на вкладке "Границы" устанавливаем флаг "Подогнать регион под разрешение" и на вкладке "Разрешение" назначаем для "2D grid resolution" подходящее значение. Этот последний параметр очень важен - это размер ячеек сетки, до которых и будет производиться расчёт времени перемещения. Он должен быть достаточно небольшим, чтобы изохроны были плавными, но и не совсем маленьким, потому что чем подробнее сетка, тем более ресурсоёмкими получаются вычисления. Для территории подобной демонстрационной подходит размер 20 метров. Другие параметры не изменяются.<br />
<br />
[[Файл:grass_qgis_isochrones_gregion1.png|600px|thumb|center|]]<br />
[[Файл:grass_qgis_isochrones_gregion2.png|600px|thumb|center|]]<br />
[[Файл:grass_qgis_isochrones_gregion3.png|600px|thumb|center|]]<br />
<br />
<pre>g.region vector=Tosnensky_roads_utm_net res=20 -a</pre><br />
<br />
Теперь всё готово для создания самих изохрон и некоторых других выходных данных.<br />
<br />
===Создание изохрон===<br />
<br />
Модуль [https://grass.osgeo.org/grass72/manuals/addons/v.isochrones.html v.isochrones] позволяет работать в двух режимах: создавать непрерывные изохроны, то есть поля затрат на перемещение (с использованием модуля ''r.cost''), и просто откладывать преодолеваемое расстояние вдоль дорог (что хорошо подходит для случая, когда перемещение за пределами транспортной сети невозможно совсем - с использованиям модуля ''v.net.iso''). Нас интересуют непрерывные изохроны. Запускаем модуль v.isochrones, набрав такую команду в консоли:<br />
<pre>v.isochrones</pre><br />
Можно также запустить загруженный скрипт через ''File - Launch script''. На первой вкладке в качестве дорожной сети выбираем сетевой набор данных "Tosnensky_roads_utm_net@tosno", название атрибута с ценой - SPEED, стартовые точки - "start_points@tosno", выбираем удобное название для слоя с изохронами (например, "isochrones_rcost"), выбираем метод ''r.cost'' и (важное!) собственно значения изохрон в минутах через запятую. Здесь значения могут быть любыми в зависимости от ваших задач. Зададим следующие отсечки по времени: 15, 30, 60, 90, 120 и 150 минут. Далее, на вкладке ''r.cost'' задаются очень важные параметры: название для поверхности времени перемещений (опциональный продукт, но очень интересный, сохраним результат в слой timemap_rcost и посмотрим, что в нем будет), скорость для преодоления пространства вне дорожной сети (вышли на обочине и пошли пешком через поле, установим 5 км/ч) и доступная для расчётов память. Обычно хватает 1000 МБ.<br />
<br />
[[Файл:grass_qgis_isochrones_rcost1.png|600px|thumb|center|]]<br />
[[Файл:grass_qgis_isochrones_rcost2.png|600px|thumb|center|]]<br />
<br />
<pre>v.isochrones map=Tosnensky_roads_utm_net@tosno roads_layer=1 cost_column=SPEED start_points=start_points@tosno isochrones=isochrones_rcost time_steps=15,30,60,90,120,150 timemap=timemap_rcost memory=1000 method=r.cost</pre><br />
<br />
После выполнения обработки в дереве слоёв появляются два новых: изохроны и поверхность затрат времени. Это и есть искомый результат. Оформим его несколько позднее, пока просто взглянем:<br />
<br />
[[Файл:grass_qgis_isochrones_rcost3.png|600px|thumb|center|]]<br />
[[Файл:grass_qgis_isochrones_rcost4.png|600px|thumb|center|]]<br />
<br />
Выглядит неплохо! Изучим данные немного детальнее в QGIS, для этого экспортируем данные в стандартные ГИС-форматы с помощью команд [https://grass.osgeo.org/grass72/manuals/v.out.ogr.html v.out.ogr] и [https://grass.osgeo.org/grass72/manuals/r.out.gdal.html r.out.gdal], они доступы в меню File - экспорт векторного слоя и File - экспорт растрового слоя соответственно.<br />
<br />
[[Файл:grass_qgis_isochrones_export1.png|600px|thumb|center|]]<br />
<br />
<pre>v.out.ogr input=isochrones_rcost@tosno output=E:\qgis_grass_transport\data\isochrones.shp format=ESRI_Shapefile</pre><br />
<br />
[[Файл:grass_qgis_isochrones_export2.png|600px|thumb|center|]]<br />
<br />
<pre>r.out.gdal input=timemap_rcost@tosno output=E:\qgis_grass_transport\data\timemap.tif format=GTiff</pre><br />
<br />
Теперь можно использовать данные в других ГИС и делиться ими!<br />
<br />
==Представление результатов в QGIS==<br />
<br />
Посмотрим на данные в QGIS, и оформим простую карту. Сначала добавим растровый слой ''timemap.tif'', что он из себя представляет? Оказывается, это очень интересный набор данных, как бы предшествующий изохронам - растр с тем самым разрешением 20х20 метров, указанным на этапе задания области, в каждой ячейке которого хранится время в минутах, сколько туда добираться из указанных точек. Такой набор данных может быть очень полезен при разнообразном растровом анализе как один из входных слоёв.<br />
<br />
[[Файл:grass_qgis_isochrones_rcost_result1.png|600px|thumb|center|]]<br />
<br />
Слой же с изохронами содержит полигоны, охватывающие территории, соответствующие диапазонам времени перемещения: от 0 до 15, от 15 до 30, и так далее. Диапазоны мы ранее задавали самостоятельно. Важно, что в атрибутах хранятся данные о временном диапазоне, а внешние полигоны (более дальние) не содержат внутренних (ближних), такие данные открывают широкие возможности для разнообразного анализа.<br />
<br />
[[Файл:grass_qgis_isochrones_rcost_result2.png|600px|thumb|center|]]<br />
<br />
Применив простую заливку по уникальным значениям поля ''traveltime'' и подложив какую-нибудь базовую карту, можно быстро получить достаточно симпатичную визуализацию изохрон.<br />
<br />
[[Файл:grass_qgis_isochrones_rcost_result3.png|600px|thumb|center|]]<br />
<br />
Хотя ценнее в данном случае, конечно, сами данные.<br />
<br />
Итак, рассмотрен простой подход к транспортному моделированию способом построения изохрон в GRASS GIS. Возможности такого подхода весьма ограничены, особенно в аспектах, касающихся учёта разнородных препятствий, и так далее. Тем не менее, при внимательной подготовке исходных данных о дорожной сети, можно получать интересные результаты, которые применимы при различных сценариях анализа транспортной доступности.</div>Александр Мурыйhttps://wiki.gis-lab.info/index.php?title=%D0%91%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D1%8F_%D0%BE%D1%86%D0%B5%D0%BD%D0%BA%D0%B0_%D1%82%D1%80%D0%B0%D0%BD%D1%81%D0%BF%D0%BE%D1%80%D1%82%D0%BD%D0%BE%D0%B9_%D0%B4%D0%BE%D1%81%D1%82%D1%83%D0%BF%D0%BD%D0%BE%D1%81%D1%82%D0%B8_%D1%81%D1%80%D0%B5%D0%B4%D1%81%D1%82%D0%B2%D0%B0%D0%BC%D0%B8_GRASS_GIS_%D0%B8_QGIS&diff=25551Базовая оценка транспортной доступности средствами GRASS GIS и QGIS2017-07-30T08:16:23Z<p>Александр Мурый: </p>
<hr />
<div>{{Статья|Черновик}}<br />
<br />
{{Аннотация|Построение карт транспортной доступности на основе данных OpenStreetMap средствами открытых геоинформационных систем GRASS GIS 7 и QGIS}}<br />
<br />
Картографирование транспортной доступности на основе данных о дорожной сети - одна из классических задач ГИС. Наиболее распространенным способом моделирования транспортной доступности является построение изохрон - линий равных затрат времени на преодоление пространства относительно заданных точек. В представленной статье обсуждается алгоритм построения изохрон по данным [http://www.openstreetmap.org/ OpenStreetMap] с использованием открытых ГИС [https://grass.osgeo.org/ GRASS GIS] и [http://qgis.org/ru/site/ QGIS]. В QGIS будет осуществляться подготовка данных и картографическое представление результатов, а в GRASS собственно моделирование. Всю работу можно выполнить целиком в GRASS, но, по мнению автора, общие манипуляции геоданными и представление картографических материалов удачнее и удобнее реализованы в QGIS.<br />
<br />
Описываемые в статье действия выполнялись в средах Ubuntu 14.04 LTS и Windows 8.1 (x64), GRASS 7.2, QGIS 2.14.<br />
<br />
==Получение и подготовка данных==<br />
<br />
Для осуществления расчётов нам потребуется набор векторных линейных геоданных, содержащий информацию о дорожно-транспортной сети исследуемой территории. Заполучить подобную информацию можно различными способами: приобрести у специализированных поставщиков, найти в одном из источников открытых данных, оцифровать атлас автомобильных дорог, нарисовать по космическому снимку, и так далее (не забывайте про лицензии данных!). В данном случае мы воспользуемся данными OSM как достаточно качественными и подробными, и, что важно, доступными бесплатно и легально. Начать знакомство с OSM вы можете с [http://wiki.openstreetmap.org/wiki/RU:%D0%9E_%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D0%B5 этой страницы]. Этап подготовки данных являются ключевым: помимо технических этапов, здесь нам предстоит определить транспортные характеристики, которые будут использованы для моделирования.<br />
<br />
Для демонстрации принципов базового транспортного моделирования рассмотрим Тосненский район Ленинградской области. Транспорт: автомобиль.<br />
<br />
===Загрузка данных OSM===<br />
<br />
Для загрузки данных OSM существует множество возможностей. Одним из наиболее простых способов является загрузка уже подготовленных наборов данных по слоям в формате ESRI Shapefile. Популярные сервисы в сети: [http://download.geofabrik.de/ Geofabrik], где вы можете найти комплекты данных на весь мир, и [http://data.nextgis.com/osmshp/ NextGIS], где вы найдёте вместе с данными оформленные QGIS-проекты по субъектам РФ и странам СНГ. Также удобный способ быстро получить данные OSM - воспользоваться одним из соответствующих плагинов для QGIS, например, [http://plugins.qgis.org/plugins/OSMDownloader/ OSMDownloader]. При его установке в интерфейс QGIS добавится кнопка выделения прямоугольной области, для которой и будут загружены данные в указанное вами расположение. Для демонстрации в этой статье используется набор данных OSM от NextGIS на территорию Ленинградской области от 13 ноября 2016 года, доступный бесплатно ([https://drive.google.com/file/d/0BztNh0nXzyjbTUpNWVl4bk15Sjg/view?usp=sharing загрузить]).<br />
<br />
Для нашей задачи понадобятся только данные о дорожной сети (в случае работы с выгрузками NextGIS - слой ''highway-line'' из директории ''data''; в случае работы с OSMDownloader - все загруженные объекты линейного типа, у которых в атрибуте "Highway" содержится какое-либо значение). Для оформительских задач также может быть использован набор геоданных с границами административно-территориального деления (например, для обрезки изохрон по границам района). <br />
<br />
===Первичная подготовка данных===<br />
<br />
Для начала подготовим данные по дорожной сети: обрежем их по границам района интереса, спроецируем куда нужно, отфильтруем лишнее. Распаковываем архив ''RU-LEN.7z'' в удобное расположение, лучше без кириллицы и пробелов в путях. Затем запускаем QGIS и добавляем два векторных слоя из папки .../RU-LEN/data/:<br />
* highway-line<br />
* boundary-polygon<br />
<br />
Нужно проявить осторожность с кодировками, и добавлять слои с кодировкой UTF-8 (кодировку можно выбрать в меню выбора векторного слоя при его добавлении). Выглядит это как-то так (цвета могут быть произвольными):<br />
<br />
[[Файл:Grass_qgis_isochrones_map_raw.png|600px|thumb|center|]]<br />
<br />
В первую очередь, избавимся от всего лишнего. Найдём полигон Тосненского района и сохраним его в отдельный слой. Для этого в таблице атрибутов слоя ''boundary-polygon'' (доступно через контекстное меню слоя) найдём объект с соответствующим содержанием поля NAME. Для этого можно воспользоваться поиском по запросу, либо просто отсортировать колонку и выделить нужную строку. Затем через контекстное меню слоя выбираем "Сохранить как", определяя новое расположение для файла с границами Тосненского района (в моём случае Tosnensky_boundary.shp), и не забываем активировать флаг "Сохранить только выделенные объекты".<br />
<br />
<br />
[[Файл:Grass_qgis_isochrones_table_selection.png|400px|thumb|center|]]<br />
[[Файл:Grass_qgis_isochrones_save_as.png|400px|thumb|center|]]<br />
<br />
Затем необходимо обрезать набор геоданных по дорожной сети по этим границам. Для этого воспользуемся инструментом обрезки (Вектор - Инструменты геообработки - Обрезка). В качестве исходного слоя выбираем ''highway-line'', в качестве слоя обрезки ''Tosnensky_bondary'', результат сохраняем в удобное новое расположение (в моём случае Tosnensky_roads.shp).<br />
<br />
[[Файл:Grass_qgis_isochrones_clip.png|400px|thumb|center|]]<br />
<br />
Теперь, чтобы избежать возможных проблем с расчётом длин участков дорог (и некоторых других) в будущем, перепроецируем результирующий слой ''Tosnensky_roads'' в подходящую систему координат. Для расчётных задач рекомендуется использовать поперечную цилиндрическую проекцию (UTM или Гаусса-Крюгера) для вашей зоны. В данном случае используем WGS 84 / UTM zone 36N (EPSG:32636), для этого через меню "Сохранить как" создаем копию слоя ''Tosnensky_roads'', в выпадающем списке "Система координат", выбрав UTM zone 36N и задав удобное расположение для нового файла (Tosnensku_roads_utm).<br />
<br />
[[Файл:Grass_qgis_isochrones_reproject_roads.png|400px|thumb|center|]]<br />
<br />
Аналогичным образом перепроецируем слой с границами Тосненского района, на всякий случай (получаем дополнительно Tosnensky_boundary_utm). Данные почти готовы! Теперь нужно отфильтровать геоданные по дорогам с учётом требований к моделированию. Это первый тематический шаг подготовки: '''отбор только тех объектов дорожной сети, которые могут быть использованы для перемещения предполагаемыми транспортными средствами'''. Здесь вы должны решить, по объектам каких типов будет разрешено перемещаться, например, для моделирования пешеходного движения можно оставить все типы дорог; для легковых автомобилей исключить пешеходные дорожки, тропы, просеки; для грузовых автомобилей исключить дороги с соответствующими ограничениями на массу, и так далее. Шаг этот очень ответственный. При этом подход к фильтрации сильно зависит от качества исходных данных и их детализации. Вспоминаем, что имеем дело с OSM, где различия между объектами существуют на уровне тегов, которые записаны в таблице атрибутов в поле HIGHWAY. Посмотрим на список уникальных значений для нашего набора данных:<br />
<pre><br />
path steps footway residential primary service unclassified secondary road track construction tertiary raceway tertiary_link trunk secondary_link proposed pedestrian bridleway primary_link living_street trunk_link<br />
</pre><br />
<br />
Все эти теги [http://wiki.openstreetmap.org/wiki/Key:highway описаны на ресурсах OSM], даже с картинками. Нам ещё предстоит вернуться к этим описаниям, а сейчас важно определить, по объектам каких типов может нормально передвигаться легковой автомобиль, а по каким нет. Очевидно, что нужно исключить объекты следующих типов (пригодны только для пешеходов, проектируются, строятся и т.д.):<br />
<pre><br />
path, steps, footway, construction, proposed, pedestrian, bridleway<br />
</pre><br />
<br />
Также много объектов с типом ''unclassified''. При осуществлении серьезной работы, конечно, стоит прояснить природу каждого из таких объектов по вспомогательным данным (и внести информацию в OSM, конечно же). В данном случае оставим их как подходящие для работы. Объекты всех отобранных типов нужно удалить. Для этого переходим в таблицу атрибутов слоя Tosnensky_roads_utm, выбираем инструмент "Выбрать по выражению" и формируем запрос вида:<br />
<pre><br />
"HIGHWAY" IN ( 'path' , 'steps' , 'footway' , 'construction' , 'proposed' , 'pedestrian' , 'bridleway' )<br />
</pre><br />
<br />
отмечая в списке, соответственно, выбранные вами типы дорог, которые должны быть проигнорированы. Нажимаем кнопку "выбрать" и видим на карте лишние дороги.<br />
<br />
[[Файл:Grass_qgis_isochrones_for_deleting.png|600px|thumb|center|]]<br />
<br />
Переходим в режим редактирования и удаляем выбранные объекты, сохраняем изменения. Если что, все исходные данные остаются в других слоях. Общая подготовка закончена.<br />
<br />
<br />
===Определение транспортных характеристик данных о дорожной сети===<br />
<br />
Второй этап подготовки является самым важным и трудоёмким в реальных условиях: необходимо каждому участку дороги назначить некоторую среднюю скорость перемещения по нему. Для примера мы используем простую логику: каждому семейству объектов (по тегу) назначим общую ожидаемую скорость, не вникая в каждый отдельный объект. При серьезном исследовании, опять же, стоит более внимательно отнестись к этому этапу, по возможности используя дополнительные материалы. Итак, рассмотрим те категории, которые у нас остались после фильтрации:<br />
<br />
* trunk - важнейшие и крупнейшие дороги, например, для нашей территории, Московское шоссе. Ожидаемая скорость 90 км/ч<br />
* primary - крупные шоссе, следующий уровень после trunk. Ожидаемая скорость 90 км/ч<br />
* secondary - относительно крупные дороги, следующий уровень после primary. Ожидаемая скорость 60 км/ч<br />
* tertiary - обычные автомобильные дороги между небольшими населенными пунктами. Ожидаемая скорость 60 км/ч <br />
* living_street - жилые зоны, где у пешеходов явное преимущество в праве передвижения. Ожидаемая скорость 15 км/ч<br />
* residential - автомобильные дороги в жилых кварталах. Ожидаемая скорость около 40 км/ч<br />
* service - сервисные подъезды, въезды и проч. Ожидаемая скорость 30 км/ч<br />
* road - автомобильная дорога неизвестного типа. Примем скорость 60 км/ч<br />
* track - грунтовые дороги, обычно для сельхоз-техники. Примем скорость 30 км/ч<br />
* raceway - дороги для автомобильных видов спорта. Примем скорость 90 км/ч <br />
* tertiary_link - участки, соединяющие tertiary с другими tertiary или дорогами других типов. Примем скорость 40 км/ч<br />
* secondary_link - участки, соединяющие secondary с другими secondary или дорогами других типов. Примем скорость 40 км/ч<br />
* primary_link - участки, соединяющие primary с другими primary или дорогами других типов. Примем скорость 40 км/ч<br />
* trunk_link - участки, соединяющие trunk с другими trunk или дорогами других типов. Примем скорость 40 км/ч<br />
* unclassified - дороги без тега. Примем скорость 40 км/ч<br />
<br />
Принятые значения достаточно условны, но позволят продемонстрировать принципы дальнейшей работы. Назначаемые скорости довольно существенно зависят от конкретной территории, местного законодательства и так далее. Важно заметить, что у некоторых участков (обычно их немного) в свойствах заполнено значение MAXSPEED, оно может быть как численным (в км/ч), так и вида RU:urban. Расшифровку этих обозначений можно найти, к примеру, [http://wiki.openstreetmap.org/wiki/RU:Key:source:maxspeed здесь] и использовать при определении скоростей. '''В целом этот этап является ключевым, и в боевых условиях здесь важно внимательно и тщательно определить характеристики дорожной сети'''.<br />
<br />
Для того, чтобы применить принятые характеристики, воспользуемся калькулятором полей, запустив его из таблицы атрибутов слоя ''Tosnensky_roads_utm''. Создадим новый атрибут SPEED как целочисленный и заполним его следующим выражением:<br />
<pre><br />
CASE<br />
WHEN "HIGHWAY" = 'trunk' THEN 90<br />
WHEN "HIGHWAY" = 'primary' THEN 90<br />
WHEN "HIGHWAY" = 'secondary' THEN 60<br />
WHEN "HIGHWAY" = 'tertiary' THEN 60<br />
WHEN "HIGHWAY" = 'living_street' THEN 15<br />
WHEN "HIGHWAY" = 'residential' THEN 40<br />
WHEN "HIGHWAY" = 'service' THEN 30<br />
WHEN "HIGHWAY" = 'road' THEN 60<br />
WHEN "HIGHWAY" = 'track' THEN 30<br />
WHEN "HIGHWAY" = 'raceway' THEN 90<br />
WHEN "HIGHWAY" = 'tertiary_link' THEN 40<br />
WHEN "HIGHWAY" = 'secondary_link' THEN 40<br />
WHEN "HIGHWAY" = 'primary_link' THEN 40<br />
WHEN "HIGHWAY" = 'trunk_link' THEN 40<br />
WHEN "HIGHWAY" = 'unclassified' THEN 40<br />
END<br />
</pre><br />
<br />
Здесь мы проверяем принадлежность каждого объекта слоя к одной из категорий и назначаем скорость в соответствии с определенным ранее значениями. Очень удобно использовать условный оператор CASE.<br />
<br />
[[Файл:Grass_qgis_isochrones_speed_calc.png|600px|thumb|center|]]<br />
<br />
После этой операции у каждого объекта слоя есть характеристика скорости. Теперь, при необходимости, можно поправить её для отдельных объектов в таблице атрибутов с учётом изучения реальных характеристик территории. Мы этот шаг пропустим и приступим к следующему - расчёту времени, - которое потребуется для преодоления каждого отдельного участка дорожной сети. Для этого нужно сначала рассчитать длины всех объектов слоя. В калькуляторе атрибутов создаем новое поле LENGTH как десятичное число по формуле<br />
<pre><br />
$length/1000<br />
</pre><br />
<br />
[[Файл:Grass_qgis_isochrones_length_calc.png|400px|thumb|center|]]<br />
<br />
По умолчанию расчёт длин и площадей производится в единицах проекции, т.е. в нашем случае в метрах (квадратных метрах для площади). Делением на 1000 мы приводим расстояния к километрам. Следующий шаг - расчёт времени (предпочтительно в минутах), которое потребуется для преодоления каждого отдельного участка дороги с учётом его длины и ожидаемой скорости. В калькуляторе атрибутов создаем новое поле TIME как десятичное число по формуле<br />
<pre><br />
"LENGTH"/"SPEED" * 60<br />
</pre><br />
<br />
[[Файл:Grass_qgis_isochrones_time_calc.png|400px|thumb|center|]]<br />
<br />
<br />
Теперь у нас есть для каждого участка дорожной сети оценка времени, требуемая для его преодоления. На этом подготовка данных в QGIS завершена, можно приступать к моделированию и переходить в GRASS. Не забудьте сохранить изменения в слое и выйти из режима редактирования.<br />
<br />
==Моделирование транспортной доступности==<br />
<br />
===Подготовка проекта и данных в GRASS===<br />
<br />
Запускаем графический интерфейс GRASS GIS 7. В первую очередь, необходимо создать так называемую "локацию" или "область". Если вы запускаете GRASS впервые, укажите папку для хранения данных (database directory). На скриншоте ниже вы видите, что в качестве такой папки выбрана E:\transport_article<br />
<br />
[[Файл:Grass_qgis_isochrones_project1.png|600px|thumb|center|]]<br />
<br />
Теперь нужно создать новую локацию (location). Для этого используем единственную активную кнопку ''New'' и попадаем в меню, где указываем название директории для хранения данных области внутри ''database directory'', и заголовок для неё. Для простоты в примере использованы те же названия: "transport_article". Таким образом, в папке E:\transport_article будет создана папка "transport_article", доступная для выбора по такому же заголовку.<br />
<br />
[[Файл:Grass_qgis_isochrones_project2.png|600px|thumb|center|]]<br />
<br />
Далее необходимо выбрать систему координат (мы уже знаем, что нам нужна UTM 36N, которую можно найти по номеру или по названию<br />
<br />
[[Файл:Grass_qgis_isochrones_project3.png|600px|thumb|center|]]<br />
<br />
Трансформацию датума на следующем шаге, при использовании UTM, можно выбрать произвольно, т.к. эффект будет одинаков (обратите внимание, что в опции с преобразованием все смещения нулевые).<br />
<br />
[[Файл:Grass_qgis_isochrones_project4.png|400px|thumb|center|]]<br />
<br />
Далее проверяем, что все параметры правильные, и завершаем создание области.<br />
<br />
[[Файл:Grass_qgis_isochrones_project5.png|600px|thumb|center|]]<br />
<br />
По завершению создания области будет предложено установить охват и разрешение региона. Это мы сделаем позднее, а сейчас откажемся.<br />
<br />
[[Файл:Grass_qgis_isochrones_project6.png|400px|thumb|center|]]<br />
<br />
На предложение же создать новый набор ("mapset") ответим утвердительно, и создадим его с именем ''tosno'', что будет отсылкой к конкретному району исследования.<br />
<br />
[[Файл:Grass_qgis_isochrones_project7.png|400px|thumb|center|]]<br />
<br />
В итоге в стартовом меню появляются созданные локация (tranport_article) и набор (tosno), выбрав которые можно запускать собственно GRASS (кнопка ''Start GRASS session'')<br />
<br />
[[Файл:Grass_qgis_isochrones_project8.png|600px|thumb|center|]]<br />
<br />
Наконец-то, начинается что-то интересное! Для начала нужно установить плагин, который и будет ответственным за построение изохрон: [https://grass.osgeo.org/grass72/manuals/addons/v.isochrones.html v.isochrones]. Для этого обратимся к меню ''Settings - Addons extensions - Install extensions from addons''.<br />
<br />
[[Файл:Grass_qgis_isochrones_extensions1.png|600px|thumb|center|]]<br />
<br />
В открывшемся меню в подгруппе ''vector'' находим модуль ''v.isochrones'' и устанавливаем его.<br />
<br />
[[Файл:Grass_qgis_isochrones_extensions2.png|600px|thumb|center|]]<br />
<br />
В консоли эта операция тоже выполняется просто:<br />
<pre>g.extension extension=v.isochrones operation=add</pre><br />
<br />
Если у вас возникают какие-либо проблемы с доступом к репозиторию с модулями GRASS (плагины не доступны для загрузки через меню "extensions"), это не критично: можно загрузить исходный Python-скрипт по [https://trac.osgeo.org/grass/browser/grass-addons/grass7/vector/v.isochrones/v.isochrones.py ссылке], и запускать модуль, обращаясь напрямую к скрипту через ''File - Launch script''. При таком сценарии у вас откроется тот же графический интерфейс, который будет рассмотрен в дальнейшем.<br />
<br />
Теперь давайте добавим в GRASS те данные, которые мы подготовили в QGIS. Для этого используем функцию импорта векторных данных ([https://grass.osgeo.org/grass72/manuals/v.in.ogr.html v.in.ogr]) или [https://grass.osgeo.org/grass72/manuals/v.import.html v.import]. В графическом интерфейсе искомое меню открывается через ''File - Import vector data - common import formats''. Выбираем наш набор геоданных "Tosnensky_roads_utm.shp":<br />
<br />
[[Файл:Grass_qgis_isochrones_import1.png|600px|thumb|center|]]<br />
<br />
<pre>v.import input=E:\qgis_grass_transport\data\Tosnensky_roads_utm.shp layer=Tosnensky_roads_utm output=Tosnensky_roads_utm</pre><br />
<br />
В консоли мы видим результат импорта и базовые сведения о данных, а в окне карты видим простую визуализацию.<br />
<br />
[[Файл:Grass_qgis_isochrones_map1.png|600px|thumb|center|]]<br />
<br />
Теперь создадим точечный слой, в котором будут содержаться местоположения, относительно которых требуется рассчитать изохроны. Это удобнее делать в интерактивном режиме в окне карты. Перейдем из режима 2D карты в режим оцифровки:<br />
<br />
[[Файл:grass_qgis_isochrones_digit.png|600px|thumb|center|]]<br />
<br />
Затем в крайнем слева выпадающем списке выберем опцию создания нового векторного слоя и зададим для него какое-нибудь подходящее название, например, ''start_points''. При этом атрибуты нам не нужны? и соответствующий флаг в меню создания слоя можно снять.<br />
<br />
[[Файл:grass_qgis_isochrones_digit2.png|600px|thumb|center|]]<br />
[[Файл:grass_qgis_isochrones_digit3.png|400px|thumb|center|]]<br />
<br />
Теперь выбираем инструмент "Создать новую точку" и добавляем интересующие нас локации. Для демонстрации поставим точки отсчёта в центры населенных пунктов г. Тосно и г. Любань. После этого выходим из режима оцифровки, соглашаясь на сохранение изменений.<br />
<br />
[[Файл:grass_qgis_isochrones_digit4.png|600px|thumb|center|]]<br />
<br />
Обратим внимание, что теперь на вкладке Слои (Layers) имеем два набора: импортированную дорожную сеть и созданные начальные точки.<br />
<br />
[[Файл:grass_qgis_isochrones_layers1.png|600px|thumb|center|]]<br />
<br />
Продолжаем подготовку данных. Поскольку в планах у нас занятия сетевым анализом, нужно набор линейных данных преобразовать в набор сетевых данных с помощью инструмента [https://grass.osgeo.org/grass72/manuals/v.net.html v.net], который доступен в меню Vector - Network analysis - Network preparation. Всё, что нам нужно, это применить операцию ''nodes'' к нашему слою "Tosnensky_roads_utm". На вкладке "Необходимо" выбираем метод ''nodes'', на вкладке ''Arcs'' выбираем слой "Tosnensky_roads_utm@tosno", на вкладке ''Опционный'' задаем имя для результирующего набора. Например, "Tosnensky_roads_utm_net".<br />
<br />
[[Файл:grass_qgis_isochrones_vnet1.png|600px|thumb|center|]]<br />
[[Файл:grass_qgis_isochrones_vnet2.png|600px|thumb|center|]]<br />
[[Файл:grass_qgis_isochrones_vnet3.png|600px|thumb|center|]]<br />
<br />
<pre>v.net -c input=Tosnensky_roads_utm operation=nodes output=Tosnensky_roads_utm_net</pre><br />
<br />
Следующий (и последний) шаг подготовки данных - это задание региона и разрешения для моделирования с помощью модуля [https://grass.osgeo.org/grass72/manuals/g.region.html g.region], который доступен в меню "Settings - Регион - Установить регион". На первой вкладке в выпадающем списке ''Set region to match vector maps'' выбираем слой "Tosnensky_roads_utm_net", созданный на предыдущем шаге, на вкладке "Границы" устанавливаем флаг "Подогнать регион под разрешение" и на вкладке "Разрешение" назначаем для "2D grid resolution" подходящее значение. Этот последний параметр очень важен - это размер ячеек сетки, до которых и будет производиться расчёт времени перемещения. Он должен быть достаточно небольшим, чтобы изохроны были плавными, но и не совсем маленьким, потому что чем подробнее сетка, тем более ресурсоёмкими получаются вычисления. Для территории подобной демонстрационной подходит размер 20 метров. Другие параметры не изменяются.<br />
<br />
[[Файл:grass_qgis_isochrones_gregion1.png|600px|thumb|center|]]<br />
[[Файл:grass_qgis_isochrones_gregion2.png|600px|thumb|center|]]<br />
[[Файл:grass_qgis_isochrones_gregion3.png|600px|thumb|center|]]<br />
<br />
<pre>g.region vector=Tosnensky_roads_utm_net res=20 -a</pre><br />
<br />
Теперь всё готово для создания самих изохрон и некоторых других выходных данных.<br />
<br />
===Создание изохрон===<br />
<br />
Модуль [https://grass.osgeo.org/grass72/manuals/addons/v.isochrones.html v.isochrones] позволяет работать в двух режимах: создавать непрерывные изохроны, то есть поля затрат на перемещение (с использованием модуля ''r.cost''), и просто откладывать преодолеваемое расстояние вдоль дорог (что хорошо подходит для случая, когда перемещение за пределами транспортной сети невозможно совсем - с использованиям модуля ''v.net.iso''). Нас интересуют непрерывные изохроны. Запускаем модуль v.isochrones, набрав такую команду в консоли:<br />
<pre>v.isochrones</pre><br />
Можно также запустить загруженный скрипт через ''File - Launch script''. На первой вкладке в качестве дорожной сети выбираем сетевой набор данных "Tosnensky_roads_utm_net@tosno", название атрибута с ценой - SPEED, стартовые точки - "start_points@tosno", выбираем удобное название для слоя с изохронами (например, "isochrones_rcost"), выбираем метод ''r.cost'' и (важное!) собственно значения изохрон в минутах через запятую. Здесь значения могут быть любыми в зависимости от ваших задач. Зададим следующие отсечки по времени: 15, 30, 60, 90, 120 и 150 минут. Далее, на вкладке ''r.cost'' задаются очень важные параметры: название для поверхности времени перемещений (опциональный продукт, но очень интересный, сохраним результат в слой timemap_rcost и посмотрим, что в нем будет), скорость для преодоления пространства вне дорожной сети (вышли на обочине и пошли пешком через поле, установим 5 км/ч) и доступная для расчётов память. Обычно хватает 1000 МБ.<br />
<br />
[[Файл:grass_qgis_isochrones_rcost1.png|600px|thumb|center|]]<br />
[[Файл:grass_qgis_isochrones_rcost2.png|600px|thumb|center|]]<br />
<br />
<pre>v.isochrones map=Tosnensky_roads_utm_net@tosno roads_layer=1 cost_column=SPEED start_points=start_points@tosno isochrones=isochrones_rcost time_steps=15,30,60,90,120,150 timemap=timemap_rcost memory=1000 method=r.cost</pre><br />
<br />
После выполнения обработки в дереве слоёв появляются два новых: изохроны и поверхность затрат времени. Это и есть искомый результат. Оформим его несколько позднее, пока просто взглянем:<br />
<br />
[[Файл:grass_qgis_isochrones_rcost3.png|600px|thumb|center|]]<br />
[[Файл:grass_qgis_isochrones_rcost4.png|600px|thumb|center|]]<br />
<br />
Выглядит неплохо! Изучим данные немного детальнее в QGIS, для этого экспортируем данные в стандартные ГИС-форматы с помощью команд [https://grass.osgeo.org/grass72/manuals/v.out.ogr.html v.out.ogr] и [https://grass.osgeo.org/grass72/manuals/r.out.gdal.html r.out.gdal], они доступы в меню File - экспорт векторного слоя и File - экспорт растрового слоя соответственно.<br />
<br />
[[Файл:grass_qgis_isochrones_export1.png|600px|thumb|center|]]<br />
<br />
<pre>v.out.ogr input=isochrones_rcost@tosno output=E:\qgis_grass_transport\data\isochrones.shp format=ESRI_Shapefile</pre><br />
<br />
[[Файл:grass_qgis_isochrones_export2.png|600px|thumb|center|]]<br />
<br />
<pre>r.out.gdal input=timemap_rcost@tosno output=E:\qgis_grass_transport\data\timemap.tif format=GTiff</pre><br />
<br />
Теперь можно использовать данные в других ГИС и делиться ими!<br />
<br />
==Представление результатов в QGIS==<br />
<br />
Посмотрим на данные в QGIS, и оформим простую карту. Сначала добавим растровый слой ''timemap.tif'', что он из себя представляет? Оказывается, это очень интересный набор данных, как бы предшествующий изохронам - растр с тем самым разрешением 20х20 метров, указанным на этапе задания области, в каждой ячейке которого хранится время в минутах, сколько туда добираться из указанных точек. Такой набор данных может быть очень полезен при разнообразном растровом анализе как один из входных слоёв.<br />
<br />
[[Файл:grass_qgis_isochrones_rcost_result1.png|600px|thumb|center|]]<br />
<br />
Слой же с изохронами содержит полигоны, охватывающие территории, соответствующие диапазонам времени перемещения: от 0 до 15, от 15 до 30, и так далее. Диапазоны мы ранее задавали самостоятельно. Важно, что в атрибутах хранятся данные о временном диапазоне, а внешние полигоны (более дальние) не содержат внутренних (ближних), такие данные открывают широкие возможности для разнообразного анализа.<br />
<br />
[[Файл:grass_qgis_isochrones_rcost_result2.png|600px|thumb|center|]]<br />
<br />
Применив простую заливку по уникальным значениям поля ''traveltime'' и подложив какую-нибудь базовую карту, можно быстро получить достаточно симпатичную визуализацию изохрон.<br />
<br />
[[Файл:grass_qgis_isochrones_rcost_result3.png|600px|thumb|center|]]<br />
<br />
Хотя ценнее в данном случае, конечно, сами данные.<br />
<br />
Итак, рассмотрен простой подход к транспортному моделированию способом построения изохрон в GRASS GIS. Возможности такого подхода весьма ограничены, особенно в аспектах, касающихся учёта разнородных препятствий, и так далее. Тем не менее, при внимательной подготовке исходных данных о дорожной сети, можно получать интересные результаты, которые применимы при различных сценариях анализа транспортной доступности.</div>Александр Мурыйhttps://wiki.gis-lab.info/index.php?title=%D0%97%D0%B0%D1%8F%D0%B2%D0%BA%D0%B8_%D0%BD%D0%B0_%D0%BF%D1%83%D0%B1%D0%BB%D0%B8%D0%BA%D0%B0%D1%86%D0%B8%D1%8E_%D1%81%D1%82%D0%B0%D1%82%D0%B5%D0%B9&diff=25542Заявки на публикацию статей2017-07-14T12:28:08Z<p>Александр Мурый: </p>
<hr />
<div>{{Организационное|articles-request}}<br />
<br />
Внизу вы видите таблицу заявок на публикацию статей на GIS-Lab, она показывает статьи, которые их авторы считают более-менее готовыми к публикации. Таблица создана, чтобы сделать процесс принятия статей более прозрачным для авторов и оптимизировать работу редакторов. <br />
<br />
Таблица будет обновляться ведущим по мере поступления заявок от авторов. Заявкой на публикацию считается:<br />
* если статья оформляется на вики GIS-Lab — создание автором темы на форуме для обсуждения статьи (в разделе [http://gis-lab.info/forum/viewforum.php?f=3 Материалы сайта]);<br />
* если статья оформляется НЕ на вики GIS-Lab — личное обращение автора к [http://gis-lab.info/qa/org.html редактору] с предоставлением текста статьи.<br />
<br />
<br />
{| class="wikitable sortable" <br />
|-<br />
! width="5%" | Дата поступления заявки<br />
! width="30%" | Ссылка на вики, название <br />
! width="25%" | Автор (имя / ID)<br />
! width="25%" | Тема обсуждения на форуме (номер / название)<br />
! width="10%" | Дата создания<br />
|-<br />
| 14.07.2017<br />
| [http://wiki.gis-lab.info/w/%D0%91%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D1%8F_%D0%BE%D1%86%D0%B5%D0%BD%D0%BA%D0%B0_%D1%82%D1%80%D0%B0%D0%BD%D1%81%D0%BF%D0%BE%D1%80%D1%82%D0%BD%D0%BE%D0%B9_%D0%B4%D0%BE%D1%81%D1%82%D1%83%D0%BF%D0%BD%D0%BE%D1%81%D1%82%D0%B8_%D1%81%D1%80%D0%B5%D0%B4%D1%81%D1%82%D0%B2%D0%B0%D0%BC%D0%B8_GRASS_GIS_7_%D0%B8_QGIS] Базовая оценка транспортной доступности средствами GRASS GIS 7 и QGIS <br />
| Эдуард Казаков / 16669<br />
| 22712 / Заметка про построение изохрон в GRASS/QGIS на основе OSM<br />
| 29.05.2017<br />
<br />
<br />
<br />
[[Категория:Служебные]]</div>Александр Мурыйhttps://wiki.gis-lab.info/index.php?title=%D0%97%D0%B0%D1%8F%D0%B2%D0%BA%D0%B8_%D0%BD%D0%B0_%D0%BF%D1%83%D0%B1%D0%BB%D0%B8%D0%BA%D0%B0%D1%86%D0%B8%D1%8E_%D1%81%D1%82%D0%B0%D1%82%D0%B5%D0%B9&diff=25421Заявки на публикацию статей2017-06-17T18:00:07Z<p>Александр Мурый: </p>
<hr />
<div>{{Организационное|articles-request}}<br />
<br />
Внизу вы видите таблицу заявок на публикацию статей на GIS-Lab, она показывает статьи, которые их авторы считают более-менее готовыми к публикации. Таблица создана, чтобы сделать процесс принятия статей более прозрачным для авторов и оптимизировать работу редакторов. <br />
<br />
Таблица будет обновляться ведущим по мере поступления заявок от авторов. Заявкой на публикацию считается:<br />
* если статья оформляется на вики GIS-Lab — создание автором темы на форуме для обсуждения статьи (в разделе [http://gis-lab.info/forum/viewforum.php?f=3 Материалы сайта]);<br />
* если статья оформляется НЕ на вики GIS-Lab — личное обращение автора к [http://gis-lab.info/qa/org.html редактору] с предоставлением текста статьи.<br />
<br />
<br />
{| class="wikitable sortable" <br />
|-<br />
! width="5%" | Дата поступления заявки<br />
! width="30%" | Ссылка на вики, название <br />
! width="25%" | Автор (имя / ID)<br />
! width="25%" | Тема обсуждения на форуме (номер / название)<br />
! width="10%" | Дата создания<br />
<br />
<br />
<br />
<br />
[[Категория:Служебные]]</div>Александр Мурыйhttps://wiki.gis-lab.info/index.php?title=%D0%9F%D1%80%D0%B8%D0%BE%D1%80%D0%B8%D1%82%D0%B5%D1%82_%D0%BE%D1%82%D1%80%D0%B8%D1%81%D0%BE%D0%B2%D0%BA%D0%B8_%D0%B2_OpenStreetMap&diff=25420Приоритет отрисовки в OpenStreetMap2017-06-17T17:10:45Z<p>Александр Мурый: /* Малонаселенные области */</p>
<hr />
<div>{{Статья|Опубликована|osm-mapping-priority}}<br />
<br />
{{Аннотация|Перевод статьи, в которой при помощи GRASS GIS и PostGIS вычисляются плохо отрисованные в OpenStreetMap малонаселенные области.}}<br />
<br />
Оригинал: [http://www.mapaou-web.fr/2016/02/18/osm-mapping-priority/ OpenStreetMap Mapping Priority] <br />
<br />
== Приоритет отрисовки в OpenStreetMap ==<br />
<br />
[[Файл:Osmmp_mapping_priority_map.jpeg|700px|Приоритет отрисовки в OpenStreetMap]]<br />
<br />
<br />
Работая в Французской Гвиане, автор узнал о проекте [http://mapazonia.org/ Mapazonia], целью которого является улучшение данных [http://www.openstreetmap.org/ OpenStreetMap] в регионе [http://wiki.openstreetmap.org/wiki/File:Amazonrivermap.svg Амазонии]. С начала участия в проекте автор научился использовать [http://tasks.hotosm.org/?sort_by=priority&direction=asc&search=mapazonia Менеджер задач OSM] - инструмент, который позволяет разбить AOI (Areas Of Interest - область интереса) на отдельные тайлы задач. Будучи заинтересованным в отрисовке [http://ru.wikipedia.org/wiki/Гвианское_плоскогорье Гвианского плоскогорья], автор задался вопросом: возможно ли использовать подобный подход для этой области?<br />
<br />
Как только тайлы задач были созданы (их насчиталось тысячи для такой большой области), возник вопрос: где наносить объекты в первую очередь?<br />
<br />
Чтобы ответить на этот вопрос, нужно учесть несколько подходов: <br />
<br />
* мы должны найти неотрисованные объекты там, где '''нет данных'''; <br />
* пустые тайлы в удаленных областях, где живёт немного людей, имеют меньший приоритет; <br />
* там, где исходные данные (спутниковые снимки) '''в низком разрешении''', только объекты, покрывающие большие площади, могут быть оцифрованы (так называемый ''[http://en.wikipedia.org/wiki/Scale_(map)#Large_scale.2C_medium_scale.2C_small_scale small scale]'' маппинг); <br />
* вносить данные нужно в первую очередь в '''центре AOI'''. <br />
<br />
А теперь давайте построим '''схему приоритета''' отрисовки тайлов, используя эти идеи!<br />
<br />
== Исходные данные ==<br />
<br />
Начнем с получения исходных данных. В итоге мы должны получить схему, похожую на [http://www.openstreetmap.org/user/tyr_asd/diary/22363 работу Мартина Райфера (Martin Raifer) по плотности узлов]. Данные OSM доступны на сайте [http://download.geofabrik.de/south-america-latest.osm.bz2 Geofabrik], и мы можем импортировать их в базу данных [http://postgis.net/ PostGIS], следуя инструкциям из [http://www.bostongis.com/?content_name=loading_osm_postgis#229 руководства Регины Обе (Regina Obe)]. Тайлы задач соответствуют [http://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#Zoom_levels 12 уровню] тайлов в [http://wiki.openstreetmap.org/wiki/EPSG:3857 проекции Web Mercator].<br />
<br />
Создадим таблицу тайлов:<br />
<br />
<syntaxhighlight lang="plsql"><br />
CREATE TABLE tile AS SELECT<br />
x, y<br />
FROM<br />
generate_series(0, pow(2, 12)::INT - 1) AS x,<br />
generate_series(0, pow(2, 12)::INT - 1) AS y ;<br />
COMMENT ON TABLE tile IS 'Map tiles' ;<br />
COMMENT ON COLUMN tile.x IS 'Tile coordinate' ;<br />
COMMENT ON COLUMN tile.y IS 'Tile coordinate' ;<br />
</syntaxhighlight><br />
<br />
<br />
и дополнительно построим их геометрии (таким образом мы будем использовать векторную и растровую модель представления данных):<br />
<br />
<syntaxhighlight lang="plsql"><br />
CREATE FUNCTION ST_GeomFromTileXY(z int, x int, y int, OUT tile_geom geometry(Polygon, 4326)) AS $$<br />
DECLARE<br />
side real;<br />
xmin real; ymin real; xmax real; ymax real;<br />
minlon real; maxlon real; maxlat real; minlat real;<br />
BEGIN<br />
side := pow(2, z);<br />
<br />
xmin := x::real/side - .5;<br />
ymin := .5 - y::real/side;<br />
xmax := (x + 1)::real/side - .5;<br />
ymax := .5 - (y + 1)::real/side;<br />
<br />
minlon := 360.*xmin;<br />
maxlon := 360.*xmax;<br />
maxlat := 90. - 360.*atan(exp(-ymin*2.*pi()))/pi();<br />
minlat := 90. - 360.*atan(exp(-ymax*2.*pi()))/pi();<br />
<br />
tile_geom := ST_MakeEnvelope(minlon, minlat, maxlon, maxlat, 4326);<br />
END; $$ LANGUAGE plpgsql;<br />
</syntaxhighlight><br />
<br />
<br />
Функция для получения X и Y координат тайла для заданной точки:<br />
<br />
<syntaxhighlight lang="plsql"><br />
CREATE FUNCTION TileCoord(z int, point geometry(Point, 4326),<br />
OUT tile_x int, OUT tile_y int) AS $$<br />
DECLARE<br />
tile_n real;<br />
sinphi real; x real; y real;<br />
BEGIN<br />
tile_n := power(2, z)::real;<br />
<br />
x := (ST_X(point) + 180.)/360.;<br />
sinphi = sin(ST_Y(point)*pi()/180.);<br />
y := .5 - ln((1. + sinphi)/(1. - sinphi))/(4.*pi());<br />
<br />
tile_x := CAST(trunc(tile_n*x + .5) AS int);<br />
tile_y := CAST(trunc(tile_n*y + .5) AS int);<br />
END; $$ LANGUAGE plpgsql;<br />
</syntaxhighlight><br />
<br />
<br />
Для вычисления плотности узлов сначала создадим временную таблицу, в которой будет содержаться информация о координатах тайла, в который попадает каждая точка:<br />
<br />
<syntaxhighlight lang="plsql"><br />
CREATE TEMP TABLE planet_osm_point_dump AS<br />
SELECT id, type, (coord).tile_x, (coord).tile_y, geom<br />
FROM<br />
(SELECT<br />
row_number() OVER (ORDER BY type, id, (dump).path) AS id,<br />
type,<br />
TileCoord(12, (dump).geom) AS coord,<br />
(dump).geom AS geom<br />
FROM<br />
(SELECT 0 AS type, osm_id AS id, ST_DumpPoints(way) AS dump<br />
FROM planet_osm_point<br />
UNION ALL<br />
SELECT 1 AS type, osm_id AS id, ST_DumpPoints(way) AS dump<br />
FROM planet_osm_line<br />
UNION ALL<br />
SELECT 2 AS type, osm_id AS id, ST_DumpPoints(way) AS dump<br />
FROM planet_osm_polygon<br />
) AS point<br />
) AS point_tiled;<br />
</syntaxhighlight><br />
<br />
<br />
и затем посчитаем число узлов в каждом тайле нашей AOI:<br />
<br />
<syntaxhighlight lang="plsql"><br />
ALTER TABLE tile ADD COLUMN node_count INT DEFAULT 0;<br />
<br />
UPDATE tile t SET node_count = count.num<br />
FROM (<br />
SELECT x, y, count(*) AS num<br />
FROM planet_osm_point_dump<br />
GROUP BY x, y ORDER BY x, y) AS count<br />
WHERE <br />
t.x = count.x AND t.y = count.y;<br />
</syntaxhighlight><br />
<br />
<br />
== Малонаселенные области ==<br />
<br />
Очевидно, что плотность населения выше вдоль побережья и главных рек, уменьшаясь вверх по течению реки. Мы будем моделировать '''удаленность''', используя '''гидрологическую дистанцию''', которая определяется [http://ru.wikipedia.org/wiki/Поверхностный_дренаж поверхностным дренажём]. Вычисления производятся в модуле [http://grasswiki.osgeo.org/wiki/R.stream.*#r.stream.distance r.stream.distance] системы [http://grass.osgeo.org/ GRASS GIS]. В гидрологическом моделировании обычно используется схема '''направлений потоков''', полученная из [http://en.wikipedia.org/wiki/Digital_elevation_model цифровой модели местности] (Digital Elevation Model - DEM): <br />
<br />
[[Файл:Osmmp_hydrology_map.jpeg|700px|Направления гидрологических потоков]]<br />
<br />
''Направления гидрологических потоков''<br />
<br />
USGS предоставляет набор данных [http://lta.cr.usgs.gov/HYDRO1K GTOPO30 HYDRO1k], который можно скачать с сайта [http://earthexplorer.usgs.gov/ Earth Explorer] (ID GT30H1KSA для Южной Америки). Набор данных представлен в азимутальной равновеликой проекции Ламберта (LAEA), которой соответствуют следующие параметры [http://proj4.org/ PROJ.4]:<br />
<br />
<pre>+proj=laea +ellps=sphere +lon_0=-60 +lat_0=-15 +x_0=0.0 +y_0=0.0 +units=m</pre><br />
<br />
Исходя из этих параметров создадим ''location'' с использованием модулей [http://grass.osgeo.org/grass71/manuals/g.proj.html g.proj] и ''mapset'':<br />
<br />
<syntaxhighlight lang="bash"><br />
g.proj -c proj4="+proj=laea +ellps=sphere +lon_0=-60 +lat_0=-15 +x_0=0.0 +y_0=0.0 +units=m" location=south_america<br />
g.mapset -c mapset=hydro location=south_america<br />
</syntaxhighlight><br />
<br />
<br />
Набор данных содержит несколько слоёв (см. [http://lta.cr.usgs.gov/HYDRO1KReadMe файл README]), но нам в основном интересен импорт растра высот и направлений течения:<br />
<br />
<syntaxhighlight lang="bash"><br />
r.in.gdal in=gt30h1ksa/sa_dem.bil out=elev<br />
r.in.gdal -e in=gt30h1ksa/sa_fd.bil out=dir_arc<br />
</syntaxhighlight><br />
<br />
<br />
Преобразуем коды направлений в формат GRASS GIS, используя таблицу сопоставления в файле arc2grass.lut:<br />
<br />
<pre><br />
255 = 0<br />
128 = 1<br />
64 = 2<br />
32 = 3<br />
16 = 4<br />
8 = 5<br />
4 = 6<br />
2 = 7<br />
1 = 8<br />
</pre><br />
<br />
<syntaxhighlight lang="bash"><br />
r.reclass in=dir_arc out=dir rules=arc2grass.lut<br />
r.stream.distance -o dir=dir elev=elev method=up dist=dir_dist<br />
</syntaxhighlight><br />
<br />
<br /><br />
<br />
[[Файл:Osmmp hydrology distance map.jpeg|700px|Расстояние до русла]]<br />
<br />
''Расстояние до русла''<br />
<br />
Для того, чтобы назначить расстояния тайлам, мы перепроецируем сетку из проекции LAEA в проекцию Web Mercator. На 15-м уровне длина стороны тайла примерно равна 1223 м, что наиболее близко к разрешению нашей сетки расстояний.<br />
Сначала создадим локацию (''прим. переводчика: "location" в терминологии GRASS GIS'') в этой проекции и перейдем к данной location:<br />
<br />
<syntaxhighlight lang="bash"><br />
g.proj -c epsg=3857 location=mercator<br />
g.mapset -c mapset=priority location=mercator<br />
</syntaxhighlight><br />
<br />
<br />
Затем создадим регион (''прим. переводчика: "region" в терминологии GRASS GIS'') на 15-м уровне и перепроецируем:<br />
<br />
<syntaxhighlight lang="bash"><br />
g.region w=-20037508.342789244 s=-20037508.342789244 e=20037508.342789244 n=20037508.342789244 rows=32768 cols=32768<br />
r.proj in=dir_dist loc=south_america mapset=hydro out=dir_dist_1k meth=near<br />
</syntaxhighlight><br />
<br />
<br />
Теперь установим для региона разрешение тайлов и выполним ресэмплинг сетки:<br />
<br />
<syntaxhighlight lang="bash"><br />
g.region w=-20037508.3427892 s=-20037508.3430388 e=20037508.3427892 n=20037508.3430388 rows=4096 cols=4096<br />
r.resamp.stats in=dir_dist_1k out=dir_dist meth=median<br />
</syntaxhighlight><br />
<br />
== Экспорт ==<br />
<br />
В конечном итоге мы получили данные, которые можем экспортировать в растровый файл:<br />
<br />
<syntaxhighlight lang="bash"><br />
r.mapcalc "dir_dist = if(isnull(dir_dist), -1, int(dir_dist))"<br />
r.out.gdal -t in=dir_dist out=hydro_dist.tif type=UInt32 nodata=4294967295<br />
</syntaxhighlight><br />
<br />
<br />
или в текстовый файл XYZ (перед импортом в PostGIS):<br />
<br />
<syntaxhighlight lang="bash"><br />
r.mapcalc "y = row() - 1"<br />
r.mapcalc "x = col() - 1"<br />
r.out.xyz in=y sep=space out=hydro_dist-y.xyz<br />
r.out.xyz in=x sep=space out=hydro_dist-x.xyz<br />
r.out.xyz in=dir_dist sep=space out=hydro_dist.xyz<br />
</syntaxhighlight><br />
<br /><br />
<syntaxhighlight lang="bash"><br />
paste hydro_dist-x.xyz hydro_dist-y.xyz hydro_dist.xyz | awk '!/-1$/ {print $3, $6, $9}' > hydro_dist.ssv<br />
</syntaxhighlight><br />
<br />
<br />
Обратите внимание, что NULL или MASK ячейки не будут экспортированы (см. [http://grass.osgeo.org/grass71/manuals/r.out.xyz.html r.out.xyz documentation]), что может привести процедуру импорта в базу данных к неожиданному результату.<br />
<br />
<syntaxhighlight lang="plsql"><br />
CREATE TEMP TABLE tile_attr (<br />
z INTEGER DEFAULT 12, x INTEGER, y INTEGER,<br />
value REAL);<br />
</syntaxhighlight><br />
<br /><br />
<syntaxhighlight lang="plsql"><br />
COPY tile_attr (x, y, value) FROM 'hydro_dist.ssv' CSV DELIMITER ' ' ;<br />
UPDATE tile t SET hydro_dist = a.value<br />
FROM tile_attr a<br />
WHERE t.level = a.z AND t.x = a.x AND t.y = a.y ;<br />
</syntaxhighlight><br />
<br />
<br />
== Расстояние до AOI ==<br />
<br />
Чтобы увеличить приоритет тайлов, которые находятся в центре нашей AOI, построим сетку весов. Вес в данном случае - это расстояние по прямой до границ AOI.<br />
<br />
Используя локацию в проекции Web Mercator с регионом на 12-м тайловом уровне, импортируем (используя [http://grass.osgeo.org/grass71/manuals/v.proj.html v.proj] при необходимости) и растеризуем нашу область интересов (AOI):<br />
<br />
<syntaxhighlight lang="bash"><br />
v.to.rast in=aoi type=line,area out=aoi use=val value=1<br />
</syntaxhighlight><br />
<br />
<br />
Рассчитаем расстояние до AOI для внешних ячеек:<br />
<br />
<syntaxhighlight lang="bash"><br />
r.grow.distance in=aoi dist=dist_outer_aoi<br />
</syntaxhighlight><br />
<br />
<br />
Выполним то же самое для внутренних ячеек:<br />
<br />
<syntaxhighlight lang="bash"><br />
r.mapcalc "aoi_inv = if(isnull(aoi), 1, null())"<br />
r.grow.distance in=aoi_inv dist=dist_inner_aoi<br />
</syntaxhighlight><br />
<br />
<br />
Комбинируем сетки расстояний в инвертированных значениях для внутренних тайлов:<br />
<br />
<syntaxhighlight lang="bash"><br />
r.mapcalc "dist_aoi = if(isnull(aoi), dist_outer_aoi, -dist_inner_aoi)"<br />
</syntaxhighlight><br />
<br />
<br />
В случае использования нескольких AOI построим растровую маску, сдвинем и масштабируем значения к диапазону [0, 1]:<br />
<br />
<syntaxhighlight lang="bash"><br />
r.mapcalc "roi = aoi_1 ||| aoi_2"<br />
r.grow in=roi out=roi old=1 new=1<br />
r.mask roi<br />
eval $(r.univar -g dist_aoi | grep "\(min\|max\)")<br />
r.mapcalc "dist_aoi = (dist_aoi - $min)/($max - $min)" --o<br />
</syntaxhighlight><br />
<br />
<br />
Вычислим средневзвешенное значение для каждого расстояния:<br />
<br />
<syntaxhighlight lang="bash"><br />
r.mapcalc "aois_dist = (dist_aoi_1 + dist_aoi_2)/2."<br />
</syntaxhighlight><br />
<br />
<br />
[[Файл:Osmmp weighted distance map.jpeg|700px|Взвешенное расстояние до центра AOI]]<br />
<br />
''Взвешенное расстояние до центра AOI''<br />
<br />
<br />
== Разрешение снимков ==<br />
<br />
Информация о разрешении снимков может быть получена, например, в [http://mvexel.dev.openstreetmap.org/bingimageanalyzer/?lat=0.882&lon=-71.132&zoom=8 инструменте анализа покрытия спутниковых снимков Bing] (см. статью [http://wiki.openstreetmap.org/wiki/Bing_Maps/Coverage Coverage] на OSM Wiki).<br />
<br />
<br />
Ниже пример на Python:<br />
<br />
<syntaxhighlight lang="python"><br />
PATTERN = "http://t{srv}.domain.tld/tile/{key}.jpg"<br />
HEADER = "Content-Length"; server = randint(0, 7)<br />
url = PATTERN.format(srv=server, key=quadkey)<br />
meta = urlopen(url).info()<br />
size = meta.getheaders(HEADER)[0]<br />
</syntaxhighlight><br />
<br />
<br />
Функция PostgreSQL, вычисляющая индекс [http://wiki.openstreetmap.org/wiki/QuadTiles quadkey] для заданного уровня (zoom level) и координат тайла (x, y):<br />
<br />
<syntaxhighlight lang="plsql"><br />
CREATE FUNCTION QuadKey(z int, x int, y int, OUT quadkey character varying(16)) AS $$<br />
DECLARE digit int; mask int; BEGIN<br />
quadkey := '';<br />
<br />
FOR i IN REVERSE z..1 LOOP<br />
digit := 0;<br />
mask := 1 << (i - 1);<br />
<br />
IF (x & mask) != 0 THEN digit := digit + 1; END IF;<br />
IF (y & mask) != 0 THEN digit := digit + 2; END IF;<br />
<br />
quadkey := quadkey || digit;<br />
END LOOP;<br />
END; $$ LANGUAGE plpgsql;<br />
</syntaxhighlight><br />
<br />
<br />
В областях с низким разрешением снимков Bing тайлы на высоких масштабных уровнях (zoom level) в основном отсутствуют. Давайте оценим качество покрытия снимками высокого разрешения путём подсчета ненулевых тайлов на 14-м уровне:<br />
<br />
<syntaxhighlight lang="plsql"><br />
SELECT<br />
substring(quadkey for 12) AS key,<br />
count(quadkey)/16 AS resolution_index<br />
FROM tile_bing WHERE z = 14 AND size > 0<br />
GROUP BY substring(quadkey for 12) ORDER BY key ;<br />
</syntaxhighlight><br />
<br />
<br />
[[Файл:Osmmp bing resolution map.jpeg|700px|Разрешение спутниковых снимков Bing]]<br />
<br />
''Разрешение спутниковых снимков Bing''<br />
<br />
<br />
== Создание индекса приоритетности ==<br />
<br />
Используя полученные ранее измерения для тайлов, мы можем создать индекс приоритетности в соответствии с нашими пожеланиями. Например, формула<br />
<br />
<pre>priority = ((1 - density) + dist_hydro + (1 - dist_aoi) + (1 - resolution))/4</pre><br />
<br />
<br />
устанавливает высокое значение приоритета для тайлов, которые не содержат узлов, расположены недалеко от береговой линии, но довольно близко к AOI, и покрыты снимками в низком разрашении.<br />
<br />
Для того, чтобы использовать схему приоритетов в [http://qgis.org/ QGIS], добавьте столбец ''status'' в таблицу ''tile'' и создайте в плагине [http://docs.qgis.org/2.0/en/docs/training_manual/databases/db_manager.html DB Manager] динамически обновляемый слой TODO на основе SQL-запроса: <br />
<br />
<syntaxhighlight lang="plsql"><br />
SELECT<br />
row_number() OVER (ORDER BY priority DESC) AS id,<br />
priority, x, y, ST_ExteriorRing(geom) AS geom<br />
FROM tile WHERE status <> 'DONE'<br />
ORDER BY priority DESC<br />
LIMIT 32 ;<br />
</syntaxhighlight><br />
<br />
<br />
Данный слой показывает следующие 32 тайла задач для отрисовки:<br />
<br />
<br />
[[Файл:Osmmp high priority tiles.jpeg|700px|Тайлы задач с наивысшим приоритетом]]<br />
<br />
''Тайлы задач с наивысшим приоритетом''<br />
<br />
<br />
Перед тем, как начать наносить объекты в [http://wiki.openstreetmap.org/wiki/JOSM JOSM], либо сохраните выбранный тайл в файл формата GPX (отметьте опцию FORCE_GPX_TRACK и необходимость использования [http://postgis.net/docs/ST_ExteriorRing.html ST_ExteriorRing]), или вызовите GDAL из [http://docs.qgis.org/2.8/en/docs/training_manual/create_vector_data/actions.html layer action]:<br />
<br />
<syntaxhighlight lang="bash"><br />
ogr2ogr -f GPX -sql "SELECT x || ' ' || y AS name, ST_ExteriorRing(geom) FROM tile WHERE x = '[% "x" %]' AND y = '[% "y" %]'" [% "x" %]_[% "y" %].gpx PG:dbname=osm -lco FORCE_GPX_TRACK=YES -nlt LINESTRING<br />
</syntaxhighlight><br />
<br />
<br />
Когда все интересующие вас объекты, входящие в границы тайла задания, добавлены, обновите атрибут его статуса в базе данных (status = DONE) и обновите экран QGIS (нажав клавишу F5).<br />
<br />
Используя данную схему приоритетов, автор замечал, что иногда [http://wiki.openstreetmap.org/wiki/DigitalGlobe снимки Mapbox] в более высоком разрешении, чем снимки Bing. Было бы полезно выяснить, предоставляют ли они схему разрешения снимков. Кроме того, две плитки последовательного ранга могут быть пространственно сильно удалены друг от друга, и было бы интересно найти способ их использования кластерами. Наконец, не следует ли включить приоритет непосредственно в Менеджер задач OSM?<br />
<br />
<br />
Успешного Вам маппинга!<br />
<br />
<br />
== Источники == <br />
<br />
Оригинал статьи: [http://www.mapaou-web.fr/2016/02/18/osm-mapping-priority/ OpenStreetMap Mapping Priority] <br />
<br />
Автор статьи: [http://www.openstreetmap.org/user/adrienandrem adrienandrem]<br />
<br />
Автор перевода: [http://www.openstreetmap.org/user/Sibri Sibri]</div>Александр Мурыйhttps://wiki.gis-lab.info/index.php?title=%D0%9F%D1%80%D0%B8%D0%BE%D1%80%D0%B8%D1%82%D0%B5%D1%82_%D0%BE%D1%82%D1%80%D0%B8%D1%81%D0%BE%D0%B2%D0%BA%D0%B8_%D0%B2_OpenStreetMap&diff=25419Приоритет отрисовки в OpenStreetMap2017-06-17T16:28:38Z<p>Александр Мурый: </p>
<hr />
<div>{{Статья|Опубликована|osm-mapping-priority}}<br />
<br />
{{Аннотация|Перевод статьи, в которой при помощи GRASS GIS и PostGIS вычисляются плохо отрисованные в OpenStreetMap малонаселенные области.}}<br />
<br />
Оригинал: [http://www.mapaou-web.fr/2016/02/18/osm-mapping-priority/ OpenStreetMap Mapping Priority] <br />
<br />
== Приоритет отрисовки в OpenStreetMap ==<br />
<br />
[[Файл:Osmmp_mapping_priority_map.jpeg|700px|Приоритет отрисовки в OpenStreetMap]]<br />
<br />
<br />
Работая в Французской Гвиане, автор узнал о проекте [http://mapazonia.org/ Mapazonia], целью которого является улучшение данных [http://www.openstreetmap.org/ OpenStreetMap] в регионе [http://wiki.openstreetmap.org/wiki/File:Amazonrivermap.svg Амазонии]. С начала участия в проекте автор научился использовать [http://tasks.hotosm.org/?sort_by=priority&direction=asc&search=mapazonia Менеджер задач OSM] - инструмент, который позволяет разбить AOI (Areas Of Interest - область интереса) на отдельные тайлы задач. Будучи заинтересованным в отрисовке [http://ru.wikipedia.org/wiki/Гвианское_плоскогорье Гвианского плоскогорья], автор задался вопросом: возможно ли использовать подобный подход для этой области?<br />
<br />
Как только тайлы задач были созданы (их насчиталось тысячи для такой большой области), возник вопрос: где наносить объекты в первую очередь?<br />
<br />
Чтобы ответить на этот вопрос, нужно учесть несколько подходов: <br />
<br />
* мы должны найти неотрисованные объекты там, где '''нет данных'''; <br />
* пустые тайлы в удаленных областях, где живёт немного людей, имеют меньший приоритет; <br />
* там, где исходные данные (спутниковые снимки) '''в низком разрешении''', только объекты, покрывающие большие площади, могут быть оцифрованы (так называемый ''[http://en.wikipedia.org/wiki/Scale_(map)#Large_scale.2C_medium_scale.2C_small_scale small scale]'' маппинг); <br />
* вносить данные нужно в первую очередь в '''центре AOI'''. <br />
<br />
А теперь давайте построим '''схему приоритета''' отрисовки тайлов, используя эти идеи!<br />
<br />
== Исходные данные ==<br />
<br />
Начнем с получения исходных данных. В итоге мы должны получить схему, похожую на [http://www.openstreetmap.org/user/tyr_asd/diary/22363 работу Мартина Райфера (Martin Raifer) по плотности узлов]. Данные OSM доступны на сайте [http://download.geofabrik.de/south-america-latest.osm.bz2 Geofabrik], и мы можем импортировать их в базу данных [http://postgis.net/ PostGIS], следуя инструкциям из [http://www.bostongis.com/?content_name=loading_osm_postgis#229 руководства Регины Обе (Regina Obe)]. Тайлы задач соответствуют [http://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#Zoom_levels 12 уровню] тайлов в [http://wiki.openstreetmap.org/wiki/EPSG:3857 проекции Web Mercator].<br />
<br />
Создадим таблицу тайлов:<br />
<br />
<syntaxhighlight lang="plsql"><br />
CREATE TABLE tile AS SELECT<br />
x, y<br />
FROM<br />
generate_series(0, pow(2, 12)::INT - 1) AS x,<br />
generate_series(0, pow(2, 12)::INT - 1) AS y ;<br />
COMMENT ON TABLE tile IS 'Map tiles' ;<br />
COMMENT ON COLUMN tile.x IS 'Tile coordinate' ;<br />
COMMENT ON COLUMN tile.y IS 'Tile coordinate' ;<br />
</syntaxhighlight><br />
<br />
<br />
и дополнительно построим их геометрии (таким образом мы будем использовать векторную и растровую модель представления данных):<br />
<br />
<syntaxhighlight lang="plsql"><br />
CREATE FUNCTION ST_GeomFromTileXY(z int, x int, y int, OUT tile_geom geometry(Polygon, 4326)) AS $$<br />
DECLARE<br />
side real;<br />
xmin real; ymin real; xmax real; ymax real;<br />
minlon real; maxlon real; maxlat real; minlat real;<br />
BEGIN<br />
side := pow(2, z);<br />
<br />
xmin := x::real/side - .5;<br />
ymin := .5 - y::real/side;<br />
xmax := (x + 1)::real/side - .5;<br />
ymax := .5 - (y + 1)::real/side;<br />
<br />
minlon := 360.*xmin;<br />
maxlon := 360.*xmax;<br />
maxlat := 90. - 360.*atan(exp(-ymin*2.*pi()))/pi();<br />
minlat := 90. - 360.*atan(exp(-ymax*2.*pi()))/pi();<br />
<br />
tile_geom := ST_MakeEnvelope(minlon, minlat, maxlon, maxlat, 4326);<br />
END; $$ LANGUAGE plpgsql;<br />
</syntaxhighlight><br />
<br />
<br />
Функция для получения X и Y координат тайла для заданной точки:<br />
<br />
<syntaxhighlight lang="plsql"><br />
CREATE FUNCTION TileCoord(z int, point geometry(Point, 4326),<br />
OUT tile_x int, OUT tile_y int) AS $$<br />
DECLARE<br />
tile_n real;<br />
sinphi real; x real; y real;<br />
BEGIN<br />
tile_n := power(2, z)::real;<br />
<br />
x := (ST_X(point) + 180.)/360.;<br />
sinphi = sin(ST_Y(point)*pi()/180.);<br />
y := .5 - ln((1. + sinphi)/(1. - sinphi))/(4.*pi());<br />
<br />
tile_x := CAST(trunc(tile_n*x + .5) AS int);<br />
tile_y := CAST(trunc(tile_n*y + .5) AS int);<br />
END; $$ LANGUAGE plpgsql;<br />
</syntaxhighlight><br />
<br />
<br />
Для вычисления плотности узлов сначала создадим временную таблицу, в которой будет содержаться информация о координатах тайла, в который попадает каждая точка:<br />
<br />
<syntaxhighlight lang="plsql"><br />
CREATE TEMP TABLE planet_osm_point_dump AS<br />
SELECT id, type, (coord).tile_x, (coord).tile_y, geom<br />
FROM<br />
(SELECT<br />
row_number() OVER (ORDER BY type, id, (dump).path) AS id,<br />
type,<br />
TileCoord(12, (dump).geom) AS coord,<br />
(dump).geom AS geom<br />
FROM<br />
(SELECT 0 AS type, osm_id AS id, ST_DumpPoints(way) AS dump<br />
FROM planet_osm_point<br />
UNION ALL<br />
SELECT 1 AS type, osm_id AS id, ST_DumpPoints(way) AS dump<br />
FROM planet_osm_line<br />
UNION ALL<br />
SELECT 2 AS type, osm_id AS id, ST_DumpPoints(way) AS dump<br />
FROM planet_osm_polygon<br />
) AS point<br />
) AS point_tiled;<br />
</syntaxhighlight><br />
<br />
<br />
и затем посчитаем число узлов в каждом тайле нашей AOI:<br />
<br />
<syntaxhighlight lang="plsql"><br />
ALTER TABLE tile ADD COLUMN node_count INT DEFAULT 0;<br />
<br />
UPDATE tile t SET node_count = count.num<br />
FROM (<br />
SELECT x, y, count(*) AS num<br />
FROM planet_osm_point_dump<br />
GROUP BY x, y ORDER BY x, y) AS count<br />
WHERE <br />
t.x = count.x AND t.y = count.y;<br />
</syntaxhighlight><br />
<br />
<br />
== Малонаселенные области ==<br />
<br />
Очевидно, что плотность населения выше вдоль побережья и главных рек, уменьшаясь вверх по течению реки. Мы будем моделировать '''удаленность''', используя '''гидрологическую дистанцию''', которая определяется [http://ru.wikipedia.org/wiki/Поверхностный_дренаж поверхностным дренажём]. Вычисления производятся в модуле [http://grasswiki.osgeo.org/wiki/R.stream.*#r.stream.distance r.stream.distance] системы [http://grass.osgeo.org/ GRASS GIS]. В гидрологическом моделировании обычно используется схема '''направлений потоков''', полученная из [http://en.wikipedia.org/wiki/Digital_elevation_model цифровой модели местности] (Digital Elevation Model - DEM): <br />
<br />
[[Файл:Osmmp_hydrology_map.jpeg|700px|Направления гидрологических потоков]]<br />
<br />
''Направления гидрологических потоков''<br />
<br />
USGS предоставляет набор данных [http://lta.cr.usgs.gov/HYDRO1K GTOPO30 HYDRO1k], который можно скачать с сайта [http://earthexplorer.usgs.gov/ Earth Explorer] (ID GT30H1KSA для Южной Америки). Набор данных представлен в азимутальной равновеликой проекции Ламберта (LAEA), которой соответствуют следующие параметры [http://proj4.org/ PROJ.4]:<br />
<br />
<pre>+proj=laea +ellps=sphere +lon_0=-60 +lat_0=-15 +x_0=0.0 +y_0=0.0 +units=m</pre><br />
<br />
Исходя из этих параметров создадим ''location'' с использованием модулей [http://grass.osgeo.org/grass71/manuals/g.proj.html g.proj] и ''mapset'':<br />
<br />
<syntaxhighlight lang="bash"><br />
g.proj -c proj4="+proj=laea +ellps=sphere +lon_0=-60 +lat_0=-15 +x_0=0.0 +y_0=0.0 +units=m" location=south_america<br />
g.mapset -c mapset=hydro location=south_america<br />
</syntaxhighlight><br />
<br />
<br />
Набор данных содержит несколько слоёв (см. [http://lta.cr.usgs.gov/HYDRO1KReadMe файл README]), но нам в основном интересен импорт растра высот и направлений течения:<br />
<br />
<syntaxhighlight lang="bash"><br />
r.in.gdal in=gt30h1ksa/sa_dem.bil out=elev<br />
r.in.gdal -e in=gt30h1ksa/sa_fd.bil out=dir_arc<br />
</syntaxhighlight><br />
<br />
<br />
Преобразуем коды направлений в формат GRASS GIS, используя таблицу сопоставления в файле arc2grass.lut:<br />
<br />
<pre><br />
255 = 0<br />
128 = 1<br />
64 = 2<br />
32 = 3<br />
16 = 4<br />
8 = 5<br />
4 = 6<br />
2 = 7<br />
1 = 8<br />
</pre><br />
<br />
<syntaxhighlight lang="bash"><br />
r.reclass in=dir_arc out=dir rules=arc2grass.lut<br />
r.stream.distance -o dir=dir elev=elev method=up dist=dir_dist<br />
</syntaxhighlight><br />
<br />
<br /><br />
<br />
[[Файл:Osmmp hydrology distance map.jpeg|700|Расстояние до русла]]<br />
<br />
''Расстояние до русла''<br />
<br />
Для того, чтобы назначить расстояния тайлам, мы перепроецируем сетку из проекции LAEA в проекцию Web Mercator. На 15-м уровне длина стороны тайла примерно равна 1223 м, что наиболее близко к разрешению нашей сетки расстояний.<br />
Сначала создадим локацию (''прим. переводчика: "location" в терминологии GRASS GIS'') в этой проекции и перейдем к данной location:<br />
<br />
<syntaxhighlight lang="bash"><br />
g.proj -c epsg=3857 location=mercator<br />
g.mapset -c mapset=priority location=mercator<br />
</syntaxhighlight><br />
<br />
<br />
Затем создадим регион (''прим. переводчика: "region" в терминологии GRASS GIS'') на 15-м уровне и перепроецируем:<br />
<br />
<syntaxhighlight lang="bash"><br />
g.region w=-20037508.342789244 s=-20037508.342789244 e=20037508.342789244 n=20037508.342789244 rows=32768 cols=32768<br />
r.proj in=dir_dist loc=south_america mapset=hydro out=dir_dist_1k meth=near<br />
</syntaxhighlight><br />
<br />
<br />
Теперь установим для региона разрешение тайлов и выполним ресэмплинг сетки:<br />
<br />
<syntaxhighlight lang="bash"><br />
g.region w=-20037508.3427892 s=-20037508.3430388 e=20037508.3427892 n=20037508.3430388 rows=4096 cols=4096<br />
r.resamp.stats in=dir_dist_1k out=dir_dist meth=median<br />
</syntaxhighlight><br />
<br />
<br />
== Экспорт ==<br />
<br />
В конечном итоге мы получили данные, которые можем экспортировать в растровый файл:<br />
<br />
<syntaxhighlight lang="bash"><br />
r.mapcalc "dir_dist = if(isnull(dir_dist), -1, int(dir_dist))"<br />
r.out.gdal -t in=dir_dist out=hydro_dist.tif type=UInt32 nodata=4294967295<br />
</syntaxhighlight><br />
<br />
<br />
или в текстовый файл XYZ (перед импортом в PostGIS):<br />
<br />
<syntaxhighlight lang="bash"><br />
r.mapcalc "y = row() - 1"<br />
r.mapcalc "x = col() - 1"<br />
r.out.xyz in=y sep=space out=hydro_dist-y.xyz<br />
r.out.xyz in=x sep=space out=hydro_dist-x.xyz<br />
r.out.xyz in=dir_dist sep=space out=hydro_dist.xyz<br />
</syntaxhighlight><br />
<br /><br />
<syntaxhighlight lang="bash"><br />
paste hydro_dist-x.xyz hydro_dist-y.xyz hydro_dist.xyz | awk '!/-1$/ {print $3, $6, $9}' > hydro_dist.ssv<br />
</syntaxhighlight><br />
<br />
<br />
Обратите внимание, что NULL или MASK ячейки не будут экспортированы (см. [http://grass.osgeo.org/grass71/manuals/r.out.xyz.html r.out.xyz documentation]), что может привести процедуру импорта в базу данных к неожиданному результату.<br />
<br />
<syntaxhighlight lang="plsql"><br />
CREATE TEMP TABLE tile_attr (<br />
z INTEGER DEFAULT 12, x INTEGER, y INTEGER,<br />
value REAL);<br />
</syntaxhighlight><br />
<br /><br />
<syntaxhighlight lang="plsql"><br />
COPY tile_attr (x, y, value) FROM 'hydro_dist.ssv' CSV DELIMITER ' ' ;<br />
UPDATE tile t SET hydro_dist = a.value<br />
FROM tile_attr a<br />
WHERE t.level = a.z AND t.x = a.x AND t.y = a.y ;<br />
</syntaxhighlight><br />
<br />
<br />
== Расстояние до AOI ==<br />
<br />
Чтобы увеличить приоритет тайлов, которые находятся в центре нашей AOI, построим сетку весов. Вес в данном случае - это расстояние по прямой до границ AOI.<br />
<br />
Используя локацию в проекции Web Mercator с регионом на 12-м тайловом уровне, импортируем (используя [http://grass.osgeo.org/grass71/manuals/v.proj.html v.proj] при необходимости) и растеризуем нашу область интересов (AOI):<br />
<br />
<syntaxhighlight lang="bash"><br />
v.to.rast in=aoi type=line,area out=aoi use=val value=1<br />
</syntaxhighlight><br />
<br />
<br />
Рассчитаем расстояние до AOI для внешних ячеек:<br />
<br />
<syntaxhighlight lang="bash"><br />
r.grow.distance in=aoi dist=dist_outer_aoi<br />
</syntaxhighlight><br />
<br />
<br />
Выполним то же самое для внутренних ячеек:<br />
<br />
<syntaxhighlight lang="bash"><br />
r.mapcalc "aoi_inv = if(isnull(aoi), 1, null())"<br />
r.grow.distance in=aoi_inv dist=dist_inner_aoi<br />
</syntaxhighlight><br />
<br />
<br />
Комбинируем сетки расстояний в инвертированных значениях для внутренних тайлов:<br />
<br />
<syntaxhighlight lang="bash"><br />
r.mapcalc "dist_aoi = if(isnull(aoi), dist_outer_aoi, -dist_inner_aoi)"<br />
</syntaxhighlight><br />
<br />
<br />
В случае использования нескольких AOI построим растровую маску, сдвинем и масштабируем значения к диапазону [0, 1]:<br />
<br />
<syntaxhighlight lang="bash"><br />
r.mapcalc "roi = aoi_1 ||| aoi_2"<br />
r.grow in=roi out=roi old=1 new=1<br />
r.mask roi<br />
eval $(r.univar -g dist_aoi | grep "\(min\|max\)")<br />
r.mapcalc "dist_aoi = (dist_aoi - $min)/($max - $min)" --o<br />
</syntaxhighlight><br />
<br />
<br />
Вычислим средневзвешенное значение для каждого расстояния:<br />
<br />
<syntaxhighlight lang="bash"><br />
r.mapcalc "aois_dist = (dist_aoi_1 + dist_aoi_2)/2."<br />
</syntaxhighlight><br />
<br />
<br />
[[Файл:Osmmp weighted distance map.jpeg|700px|Взвешенное расстояние до центра AOI]]<br />
<br />
''Взвешенное расстояние до центра AOI''<br />
<br />
<br />
== Разрешение снимков ==<br />
<br />
Информация о разрешении снимков может быть получена, например, в [http://mvexel.dev.openstreetmap.org/bingimageanalyzer/?lat=0.882&lon=-71.132&zoom=8 инструменте анализа покрытия спутниковых снимков Bing] (см. статью [http://wiki.openstreetmap.org/wiki/Bing_Maps/Coverage Coverage] на OSM Wiki).<br />
<br />
<br />
Ниже пример на Python:<br />
<br />
<syntaxhighlight lang="python"><br />
PATTERN = "http://t{srv}.domain.tld/tile/{key}.jpg"<br />
HEADER = "Content-Length"; server = randint(0, 7)<br />
url = PATTERN.format(srv=server, key=quadkey)<br />
meta = urlopen(url).info()<br />
size = meta.getheaders(HEADER)[0]<br />
</syntaxhighlight><br />
<br />
<br />
Функция PostgreSQL, вычисляющая индекс [http://wiki.openstreetmap.org/wiki/QuadTiles quadkey] для заданного уровня (zoom level) и координат тайла (x, y):<br />
<br />
<syntaxhighlight lang="plsql"><br />
CREATE FUNCTION QuadKey(z int, x int, y int, OUT quadkey character varying(16)) AS $$<br />
DECLARE digit int; mask int; BEGIN<br />
quadkey := '';<br />
<br />
FOR i IN REVERSE z..1 LOOP<br />
digit := 0;<br />
mask := 1 << (i - 1);<br />
<br />
IF (x & mask) != 0 THEN digit := digit + 1; END IF;<br />
IF (y & mask) != 0 THEN digit := digit + 2; END IF;<br />
<br />
quadkey := quadkey || digit;<br />
END LOOP;<br />
END; $$ LANGUAGE plpgsql;<br />
</syntaxhighlight><br />
<br />
<br />
В областях с низким разрешением снимков Bing тайлы на высоких масштабных уровнях (zoom level) в основном отсутствуют. Давайте оценим качество покрытия снимками высокого разрешения путём подсчета ненулевых тайлов на 14-м уровне:<br />
<br />
<syntaxhighlight lang="plsql"><br />
SELECT<br />
substring(quadkey for 12) AS key,<br />
count(quadkey)/16 AS resolution_index<br />
FROM tile_bing WHERE z = 14 AND size > 0<br />
GROUP BY substring(quadkey for 12) ORDER BY key ;<br />
</syntaxhighlight><br />
<br />
<br />
[[Файл:Osmmp bing resolution map.jpeg|700px|Разрешение спутниковых снимков Bing]]<br />
<br />
''Разрешение спутниковых снимков Bing''<br />
<br />
<br />
== Создание индекса приоритетности ==<br />
<br />
Используя полученные ранее измерения для тайлов, мы можем создать индекс приоритетности в соответствии с нашими пожеланиями. Например, формула<br />
<br />
<pre>priority = ((1 - density) + dist_hydro + (1 - dist_aoi) + (1 - resolution))/4</pre><br />
<br />
<br />
устанавливает высокое значение приоритета для тайлов, которые не содержат узлов, расположены недалеко от береговой линии, но довольно близко к AOI, и покрыты снимками в низком разрашении.<br />
<br />
Для того, чтобы использовать схему приоритетов в [http://qgis.org/ QGIS], добавьте столбец ''status'' в таблицу ''tile'' и создайте в плагине [http://docs.qgis.org/2.0/en/docs/training_manual/databases/db_manager.html DB Manager] динамически обновляемый слой TODO на основе SQL-запроса: <br />
<br />
<syntaxhighlight lang="plsql"><br />
SELECT<br />
row_number() OVER (ORDER BY priority DESC) AS id,<br />
priority, x, y, ST_ExteriorRing(geom) AS geom<br />
FROM tile WHERE status <> 'DONE'<br />
ORDER BY priority DESC<br />
LIMIT 32 ;<br />
</syntaxhighlight><br />
<br />
<br />
Данный слой показывает следующие 32 тайла задач для отрисовки:<br />
<br />
<br />
[[Файл:Osmmp high priority tiles.jpeg|700px|Тайлы задач с наивысшим приоритетом]]<br />
<br />
''Тайлы задач с наивысшим приоритетом''<br />
<br />
<br />
Перед тем, как начать наносить объекты в [http://wiki.openstreetmap.org/wiki/JOSM JOSM], либо сохраните выбранный тайл в файл формата GPX (отметьте опцию FORCE_GPX_TRACK и необходимость использования [http://postgis.net/docs/ST_ExteriorRing.html ST_ExteriorRing]), или вызовите GDAL из [http://docs.qgis.org/2.8/en/docs/training_manual/create_vector_data/actions.html layer action]:<br />
<br />
<syntaxhighlight lang="bash"><br />
ogr2ogr -f GPX -sql "SELECT x || ' ' || y AS name, ST_ExteriorRing(geom) FROM tile WHERE x = '[% "x" %]' AND y = '[% "y" %]'" [% "x" %]_[% "y" %].gpx PG:dbname=osm -lco FORCE_GPX_TRACK=YES -nlt LINESTRING<br />
</syntaxhighlight><br />
<br />
<br />
Когда все интересующие вас объекты, входящие в границы тайла задания, добавлены, обновите атрибут его статуса в базе данных (status = DONE) и обновите экран QGIS (нажав клавишу F5).<br />
<br />
Используя данную схему приоритетов, автор замечал, что иногда [http://wiki.openstreetmap.org/wiki/DigitalGlobe снимки Mapbox] в более высоком разрешении, чем снимки Bing. Было бы полезно выяснить, предоставляют ли они схему разрешения снимков. Кроме того, две плитки последовательного ранга могут быть пространственно сильно удалены друг от друга, и было бы интересно найти способ их использования кластерами. Наконец, не следует ли включить приоритет непосредственно в Менеджер задач OSM?<br />
<br />
<br />
Успешного Вам маппинга!<br />
<br />
<br />
== Источники == <br />
<br />
Оригинал статьи: [http://www.mapaou-web.fr/2016/02/18/osm-mapping-priority/ OpenStreetMap Mapping Priority] <br />
<br />
Автор статьи: [http://www.openstreetmap.org/user/adrienandrem adrienandrem]<br />
<br />
Автор перевода: [http://www.openstreetmap.org/user/Sibri Sibri]</div>Александр Мурыйhttps://wiki.gis-lab.info/index.php?title=%D0%9F%D1%80%D0%B8%D0%BE%D1%80%D0%B8%D1%82%D0%B5%D1%82_%D0%BE%D1%82%D1%80%D0%B8%D1%81%D0%BE%D0%B2%D0%BA%D0%B8_%D0%B2_OpenStreetMap&diff=25418Приоритет отрисовки в OpenStreetMap2017-06-17T16:27:35Z<p>Александр Мурый: </p>
<hr />
<div>{{Статья|Опубликована|osm-mapping-priority}}<br />
<br />
{{Аннотация|Перевод статьи, в которой при помощи GRASS GIS и PostGIS вычисляются плохо отрисованные в OpenStreetMap малонаселенные области.}}<br />
<br />
Оригинал: [http://www.mapaou-web.fr/2016/02/18/osm-mapping-priority/ OpenStreetMap Mapping Priority] <br />
<br />
== Приоритет отрисовки в OpenStreetMap ==<br />
<br />
[[Файл:Osmmp_mapping_priority_map.jpeg|500px|Приоритет отрисовки в OpenStreetMap]]<br />
<br />
<br />
Работая в Французской Гвиане, автор узнал о проекте [http://mapazonia.org/ Mapazonia], целью которого является улучшение данных [http://www.openstreetmap.org/ OpenStreetMap] в регионе [http://wiki.openstreetmap.org/wiki/File:Amazonrivermap.svg Амазонии]. С начала участия в проекте автор научился использовать [http://tasks.hotosm.org/?sort_by=priority&direction=asc&search=mapazonia Менеджер задач OSM] - инструмент, который позволяет разбить AOI (Areas Of Interest - область интереса) на отдельные тайлы задач. Будучи заинтересованным в отрисовке [http://ru.wikipedia.org/wiki/Гвианское_плоскогорье Гвианского плоскогорья], автор задался вопросом: возможно ли использовать подобный подход для этой области?<br />
<br />
Как только тайлы задач были созданы (их насчиталось тысячи для такой большой области), возник вопрос: где наносить объекты в первую очередь?<br />
<br />
Чтобы ответить на этот вопрос, нужно учесть несколько подходов: <br />
<br />
* мы должны найти неотрисованные объекты там, где '''нет данных'''; <br />
* пустые тайлы в удаленных областях, где живёт немного людей, имеют меньший приоритет; <br />
* там, где исходные данные (спутниковые снимки) '''в низком разрешении''', только объекты, покрывающие большие площади, могут быть оцифрованы (так называемый ''[http://en.wikipedia.org/wiki/Scale_(map)#Large_scale.2C_medium_scale.2C_small_scale small scale]'' маппинг); <br />
* вносить данные нужно в первую очередь в '''центре AOI'''. <br />
<br />
А теперь давайте построим '''схему приоритета''' отрисовки тайлов, используя эти идеи!<br />
<br />
== Исходные данные ==<br />
<br />
Начнем с получения исходных данных. В итоге мы должны получить схему, похожую на [http://www.openstreetmap.org/user/tyr_asd/diary/22363 работу Мартина Райфера (Martin Raifer) по плотности узлов]. Данные OSM доступны на сайте [http://download.geofabrik.de/south-america-latest.osm.bz2 Geofabrik], и мы можем импортировать их в базу данных [http://postgis.net/ PostGIS], следуя инструкциям из [http://www.bostongis.com/?content_name=loading_osm_postgis#229 руководства Регины Обе (Regina Obe)]. Тайлы задач соответствуют [http://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#Zoom_levels 12 уровню] тайлов в [http://wiki.openstreetmap.org/wiki/EPSG:3857 проекции Web Mercator].<br />
<br />
Создадим таблицу тайлов:<br />
<br />
<syntaxhighlight lang="plsql"><br />
CREATE TABLE tile AS SELECT<br />
x, y<br />
FROM<br />
generate_series(0, pow(2, 12)::INT - 1) AS x,<br />
generate_series(0, pow(2, 12)::INT - 1) AS y ;<br />
COMMENT ON TABLE tile IS 'Map tiles' ;<br />
COMMENT ON COLUMN tile.x IS 'Tile coordinate' ;<br />
COMMENT ON COLUMN tile.y IS 'Tile coordinate' ;<br />
</syntaxhighlight><br />
<br />
<br />
и дополнительно построим их геометрии (таким образом мы будем использовать векторную и растровую модель представления данных):<br />
<br />
<syntaxhighlight lang="plsql"><br />
CREATE FUNCTION ST_GeomFromTileXY(z int, x int, y int, OUT tile_geom geometry(Polygon, 4326)) AS $$<br />
DECLARE<br />
side real;<br />
xmin real; ymin real; xmax real; ymax real;<br />
minlon real; maxlon real; maxlat real; minlat real;<br />
BEGIN<br />
side := pow(2, z);<br />
<br />
xmin := x::real/side - .5;<br />
ymin := .5 - y::real/side;<br />
xmax := (x + 1)::real/side - .5;<br />
ymax := .5 - (y + 1)::real/side;<br />
<br />
minlon := 360.*xmin;<br />
maxlon := 360.*xmax;<br />
maxlat := 90. - 360.*atan(exp(-ymin*2.*pi()))/pi();<br />
minlat := 90. - 360.*atan(exp(-ymax*2.*pi()))/pi();<br />
<br />
tile_geom := ST_MakeEnvelope(minlon, minlat, maxlon, maxlat, 4326);<br />
END; $$ LANGUAGE plpgsql;<br />
</syntaxhighlight><br />
<br />
<br />
Функция для получения X и Y координат тайла для заданной точки:<br />
<br />
<syntaxhighlight lang="plsql"><br />
CREATE FUNCTION TileCoord(z int, point geometry(Point, 4326),<br />
OUT tile_x int, OUT tile_y int) AS $$<br />
DECLARE<br />
tile_n real;<br />
sinphi real; x real; y real;<br />
BEGIN<br />
tile_n := power(2, z)::real;<br />
<br />
x := (ST_X(point) + 180.)/360.;<br />
sinphi = sin(ST_Y(point)*pi()/180.);<br />
y := .5 - ln((1. + sinphi)/(1. - sinphi))/(4.*pi());<br />
<br />
tile_x := CAST(trunc(tile_n*x + .5) AS int);<br />
tile_y := CAST(trunc(tile_n*y + .5) AS int);<br />
END; $$ LANGUAGE plpgsql;<br />
</syntaxhighlight><br />
<br />
<br />
Для вычисления плотности узлов сначала создадим временную таблицу, в которой будет содержаться информация о координатах тайла, в который попадает каждая точка:<br />
<br />
<syntaxhighlight lang="plsql"><br />
CREATE TEMP TABLE planet_osm_point_dump AS<br />
SELECT id, type, (coord).tile_x, (coord).tile_y, geom<br />
FROM<br />
(SELECT<br />
row_number() OVER (ORDER BY type, id, (dump).path) AS id,<br />
type,<br />
TileCoord(12, (dump).geom) AS coord,<br />
(dump).geom AS geom<br />
FROM<br />
(SELECT 0 AS type, osm_id AS id, ST_DumpPoints(way) AS dump<br />
FROM planet_osm_point<br />
UNION ALL<br />
SELECT 1 AS type, osm_id AS id, ST_DumpPoints(way) AS dump<br />
FROM planet_osm_line<br />
UNION ALL<br />
SELECT 2 AS type, osm_id AS id, ST_DumpPoints(way) AS dump<br />
FROM planet_osm_polygon<br />
) AS point<br />
) AS point_tiled;<br />
</syntaxhighlight><br />
<br />
<br />
и затем посчитаем число узлов в каждом тайле нашей AOI:<br />
<br />
<syntaxhighlight lang="plsql"><br />
ALTER TABLE tile ADD COLUMN node_count INT DEFAULT 0;<br />
<br />
UPDATE tile t SET node_count = count.num<br />
FROM (<br />
SELECT x, y, count(*) AS num<br />
FROM planet_osm_point_dump<br />
GROUP BY x, y ORDER BY x, y) AS count<br />
WHERE <br />
t.x = count.x AND t.y = count.y;<br />
</syntaxhighlight><br />
<br />
<br />
== Малонаселенные области ==<br />
<br />
Очевидно, что плотность населения выше вдоль побережья и главных рек, уменьшаясь вверх по течению реки. Мы будем моделировать '''удаленность''', используя '''гидрологическую дистанцию''', которая определяется [http://ru.wikipedia.org/wiki/Поверхностный_дренаж поверхностным дренажём]. Вычисления производятся в модуле [http://grasswiki.osgeo.org/wiki/R.stream.*#r.stream.distance r.stream.distance] системы [http://grass.osgeo.org/ GRASS GIS]. В гидрологическом моделировании обычно используется схема '''направлений потоков''', полученная из [http://en.wikipedia.org/wiki/Digital_elevation_model цифровой модели местности] (Digital Elevation Model - DEM): <br />
<br />
[[Файл:Osmmp_hydrology_map.jpeg|500px|Направления гидрологических потоков]]<br />
<br />
''Направления гидрологических потоков''<br />
<br />
USGS предоставляет набор данных [http://lta.cr.usgs.gov/HYDRO1K GTOPO30 HYDRO1k], который можно скачать с сайта [http://earthexplorer.usgs.gov/ Earth Explorer] (ID GT30H1KSA для Южной Америки). Набор данных представлен в азимутальной равновеликой проекции Ламберта (LAEA), которой соответствуют следующие параметры [http://proj4.org/ PROJ.4]:<br />
<br />
<pre>+proj=laea +ellps=sphere +lon_0=-60 +lat_0=-15 +x_0=0.0 +y_0=0.0 +units=m</pre><br />
<br />
Исходя из этих параметров создадим ''location'' с использованием модулей [http://grass.osgeo.org/grass71/manuals/g.proj.html g.proj] и ''mapset'':<br />
<br />
<syntaxhighlight lang="bash"><br />
g.proj -c proj4="+proj=laea +ellps=sphere +lon_0=-60 +lat_0=-15 +x_0=0.0 +y_0=0.0 +units=m" location=south_america<br />
g.mapset -c mapset=hydro location=south_america<br />
</syntaxhighlight><br />
<br />
<br />
Набор данных содержит несколько слоёв (см. [http://lta.cr.usgs.gov/HYDRO1KReadMe файл README]), но нам в основном интересен импорт растра высот и направлений течения:<br />
<br />
<syntaxhighlight lang="bash"><br />
r.in.gdal in=gt30h1ksa/sa_dem.bil out=elev<br />
r.in.gdal -e in=gt30h1ksa/sa_fd.bil out=dir_arc<br />
</syntaxhighlight><br />
<br />
<br />
Преобразуем коды направлений в формат GRASS GIS, используя таблицу сопоставления в файле arc2grass.lut:<br />
<br />
<pre><br />
255 = 0<br />
128 = 1<br />
64 = 2<br />
32 = 3<br />
16 = 4<br />
8 = 5<br />
4 = 6<br />
2 = 7<br />
1 = 8<br />
</pre><br />
<br />
<syntaxhighlight lang="bash"><br />
r.reclass in=dir_arc out=dir rules=arc2grass.lut<br />
r.stream.distance -o dir=dir elev=elev method=up dist=dir_dist<br />
</syntaxhighlight><br />
<br />
<br /><br />
<br />
[[Файл:Osmmp hydrology distance map.jpeg|700|Расстояние до русла]]<br />
<br />
''Расстояние до русла''<br />
<br />
Для того, чтобы назначить расстояния тайлам, мы перепроецируем сетку из проекции LAEA в проекцию Web Mercator. На 15-м уровне длина стороны тайла примерно равна 1223 м, что наиболее близко к разрешению нашей сетки расстояний.<br />
Сначала создадим локацию (''прим. переводчика: "location" в терминологии GRASS GIS'') в этой проекции и перейдем к данной location:<br />
<br />
<syntaxhighlight lang="bash"><br />
g.proj -c epsg=3857 location=mercator<br />
g.mapset -c mapset=priority location=mercator<br />
</syntaxhighlight><br />
<br />
<br />
Затем создадим регион (''прим. переводчика: "region" в терминологии GRASS GIS'') на 15-м уровне и перепроецируем:<br />
<br />
<syntaxhighlight lang="bash"><br />
g.region w=-20037508.342789244 s=-20037508.342789244 e=20037508.342789244 n=20037508.342789244 rows=32768 cols=32768<br />
r.proj in=dir_dist loc=south_america mapset=hydro out=dir_dist_1k meth=near<br />
</syntaxhighlight><br />
<br />
<br />
Теперь установим для региона разрешение тайлов и выполним ресэмплинг сетки:<br />
<br />
<syntaxhighlight lang="bash"><br />
g.region w=-20037508.3427892 s=-20037508.3430388 e=20037508.3427892 n=20037508.3430388 rows=4096 cols=4096<br />
r.resamp.stats in=dir_dist_1k out=dir_dist meth=median<br />
</syntaxhighlight><br />
<br />
<br />
== Экспорт ==<br />
<br />
В конечном итоге мы получили данные, которые можем экспортировать в растровый файл:<br />
<br />
<syntaxhighlight lang="bash"><br />
r.mapcalc "dir_dist = if(isnull(dir_dist), -1, int(dir_dist))"<br />
r.out.gdal -t in=dir_dist out=hydro_dist.tif type=UInt32 nodata=4294967295<br />
</syntaxhighlight><br />
<br />
<br />
или в текстовый файл XYZ (перед импортом в PostGIS):<br />
<br />
<syntaxhighlight lang="bash"><br />
r.mapcalc "y = row() - 1"<br />
r.mapcalc "x = col() - 1"<br />
r.out.xyz in=y sep=space out=hydro_dist-y.xyz<br />
r.out.xyz in=x sep=space out=hydro_dist-x.xyz<br />
r.out.xyz in=dir_dist sep=space out=hydro_dist.xyz<br />
</syntaxhighlight><br />
<br /><br />
<syntaxhighlight lang="bash"><br />
paste hydro_dist-x.xyz hydro_dist-y.xyz hydro_dist.xyz | awk '!/-1$/ {print $3, $6, $9}' > hydro_dist.ssv<br />
</syntaxhighlight><br />
<br />
<br />
Обратите внимание, что NULL или MASK ячейки не будут экспортированы (см. [http://grass.osgeo.org/grass71/manuals/r.out.xyz.html r.out.xyz documentation]), что может привести процедуру импорта в базу данных к неожиданному результату.<br />
<br />
<syntaxhighlight lang="plsql"><br />
CREATE TEMP TABLE tile_attr (<br />
z INTEGER DEFAULT 12, x INTEGER, y INTEGER,<br />
value REAL);<br />
</syntaxhighlight><br />
<br /><br />
<syntaxhighlight lang="plsql"><br />
COPY tile_attr (x, y, value) FROM 'hydro_dist.ssv' CSV DELIMITER ' ' ;<br />
UPDATE tile t SET hydro_dist = a.value<br />
FROM tile_attr a<br />
WHERE t.level = a.z AND t.x = a.x AND t.y = a.y ;<br />
</syntaxhighlight><br />
<br />
<br />
== Расстояние до AOI ==<br />
<br />
Чтобы увеличить приоритет тайлов, которые находятся в центре нашей AOI, построим сетку весов. Вес в данном случае - это расстояние по прямой до границ AOI.<br />
<br />
Используя локацию в проекции Web Mercator с регионом на 12-м тайловом уровне, импортируем (используя [http://grass.osgeo.org/grass71/manuals/v.proj.html v.proj] при необходимости) и растеризуем нашу область интересов (AOI):<br />
<br />
<syntaxhighlight lang="bash"><br />
v.to.rast in=aoi type=line,area out=aoi use=val value=1<br />
</syntaxhighlight><br />
<br />
<br />
Рассчитаем расстояние до AOI для внешних ячеек:<br />
<br />
<syntaxhighlight lang="bash"><br />
r.grow.distance in=aoi dist=dist_outer_aoi<br />
</syntaxhighlight><br />
<br />
<br />
Выполним то же самое для внутренних ячеек:<br />
<br />
<syntaxhighlight lang="bash"><br />
r.mapcalc "aoi_inv = if(isnull(aoi), 1, null())"<br />
r.grow.distance in=aoi_inv dist=dist_inner_aoi<br />
</syntaxhighlight><br />
<br />
<br />
Комбинируем сетки расстояний в инвертированных значениях для внутренних тайлов:<br />
<br />
<syntaxhighlight lang="bash"><br />
r.mapcalc "dist_aoi = if(isnull(aoi), dist_outer_aoi, -dist_inner_aoi)"<br />
</syntaxhighlight><br />
<br />
<br />
В случае использования нескольких AOI построим растровую маску, сдвинем и масштабируем значения к диапазону [0, 1]:<br />
<br />
<syntaxhighlight lang="bash"><br />
r.mapcalc "roi = aoi_1 ||| aoi_2"<br />
r.grow in=roi out=roi old=1 new=1<br />
r.mask roi<br />
eval $(r.univar -g dist_aoi | grep "\(min\|max\)")<br />
r.mapcalc "dist_aoi = (dist_aoi - $min)/($max - $min)" --o<br />
</syntaxhighlight><br />
<br />
<br />
Вычислим средневзвешенное значение для каждого расстояния:<br />
<br />
<syntaxhighlight lang="bash"><br />
r.mapcalc "aois_dist = (dist_aoi_1 + dist_aoi_2)/2."<br />
</syntaxhighlight><br />
<br />
<br />
[[Файл:Osmmp weighted distance map.jpeg|500px|Взвешенное расстояние до центра AOI]]<br />
<br />
''Взвешенное расстояние до центра AOI''<br />
<br />
<br />
== Разрешение снимков ==<br />
<br />
Информация о разрешении снимков может быть получена, например, в [http://mvexel.dev.openstreetmap.org/bingimageanalyzer/?lat=0.882&lon=-71.132&zoom=8 инструменте анализа покрытия спутниковых снимков Bing] (см. статью [http://wiki.openstreetmap.org/wiki/Bing_Maps/Coverage Coverage] на OSM Wiki).<br />
<br />
<br />
Ниже пример на Python:<br />
<br />
<syntaxhighlight lang="python"><br />
PATTERN = "http://t{srv}.domain.tld/tile/{key}.jpg"<br />
HEADER = "Content-Length"; server = randint(0, 7)<br />
url = PATTERN.format(srv=server, key=quadkey)<br />
meta = urlopen(url).info()<br />
size = meta.getheaders(HEADER)[0]<br />
</syntaxhighlight><br />
<br />
<br />
Функция PostgreSQL, вычисляющая индекс [http://wiki.openstreetmap.org/wiki/QuadTiles quadkey] для заданного уровня (zoom level) и координат тайла (x, y):<br />
<br />
<syntaxhighlight lang="plsql"><br />
CREATE FUNCTION QuadKey(z int, x int, y int, OUT quadkey character varying(16)) AS $$<br />
DECLARE digit int; mask int; BEGIN<br />
quadkey := '';<br />
<br />
FOR i IN REVERSE z..1 LOOP<br />
digit := 0;<br />
mask := 1 << (i - 1);<br />
<br />
IF (x & mask) != 0 THEN digit := digit + 1; END IF;<br />
IF (y & mask) != 0 THEN digit := digit + 2; END IF;<br />
<br />
quadkey := quadkey || digit;<br />
END LOOP;<br />
END; $$ LANGUAGE plpgsql;<br />
</syntaxhighlight><br />
<br />
<br />
В областях с низким разрешением снимков Bing тайлы на высоких масштабных уровнях (zoom level) в основном отсутствуют. Давайте оценим качество покрытия снимками высокого разрешения путём подсчета ненулевых тайлов на 14-м уровне:<br />
<br />
<syntaxhighlight lang="plsql"><br />
SELECT<br />
substring(quadkey for 12) AS key,<br />
count(quadkey)/16 AS resolution_index<br />
FROM tile_bing WHERE z = 14 AND size > 0<br />
GROUP BY substring(quadkey for 12) ORDER BY key ;<br />
</syntaxhighlight><br />
<br />
<br />
[[Файл:Osmmp bing resolution map.jpeg|500px|Разрешение спутниковых снимков Bing]]<br />
<br />
''Разрешение спутниковых снимков Bing''<br />
<br />
<br />
== Создание индекса приоритетности ==<br />
<br />
Используя полученные ранее измерения для тайлов, мы можем создать индекс приоритетности в соответствии с нашими пожеланиями. Например, формула<br />
<br />
<pre>priority = ((1 - density) + dist_hydro + (1 - dist_aoi) + (1 - resolution))/4</pre><br />
<br />
<br />
устанавливает высокое значение приоритета для тайлов, которые не содержат узлов, расположены недалеко от береговой линии, но довольно близко к AOI, и покрыты снимками в низком разрашении.<br />
<br />
Для того, чтобы использовать схему приоритетов в [http://qgis.org/ QGIS], добавьте столбец ''status'' в таблицу ''tile'' и создайте в плагине [http://docs.qgis.org/2.0/en/docs/training_manual/databases/db_manager.html DB Manager] динамически обновляемый слой TODO на основе SQL-запроса: <br />
<br />
<syntaxhighlight lang="plsql"><br />
SELECT<br />
row_number() OVER (ORDER BY priority DESC) AS id,<br />
priority, x, y, ST_ExteriorRing(geom) AS geom<br />
FROM tile WHERE status <> 'DONE'<br />
ORDER BY priority DESC<br />
LIMIT 32 ;<br />
</syntaxhighlight><br />
<br />
<br />
Данный слой показывает следующие 32 тайла задач для отрисовки:<br />
<br />
<br />
[[Файл:Osmmp high priority tiles.jpeg|500px|Тайлы задач с наивысшим приоритетом]]<br />
<br />
''Тайлы задач с наивысшим приоритетом''<br />
<br />
<br />
Перед тем, как начать наносить объекты в [http://wiki.openstreetmap.org/wiki/JOSM JOSM], либо сохраните выбранный тайл в файл формата GPX (отметьте опцию FORCE_GPX_TRACK и необходимость использования [http://postgis.net/docs/ST_ExteriorRing.html ST_ExteriorRing]), или вызовите GDAL из [http://docs.qgis.org/2.8/en/docs/training_manual/create_vector_data/actions.html layer action]:<br />
<br />
<syntaxhighlight lang="bash"><br />
ogr2ogr -f GPX -sql "SELECT x || ' ' || y AS name, ST_ExteriorRing(geom) FROM tile WHERE x = '[% "x" %]' AND y = '[% "y" %]'" [% "x" %]_[% "y" %].gpx PG:dbname=osm -lco FORCE_GPX_TRACK=YES -nlt LINESTRING<br />
</syntaxhighlight><br />
<br />
<br />
Когда все интересующие вас объекты, входящие в границы тайла задания, добавлены, обновите атрибут его статуса в базе данных (status = DONE) и обновите экран QGIS (нажав клавишу F5).<br />
<br />
Используя данную схему приоритетов, автор замечал, что иногда [http://wiki.openstreetmap.org/wiki/DigitalGlobe снимки Mapbox] в более высоком разрешении, чем снимки Bing. Было бы полезно выяснить, предоставляют ли они схему разрешения снимков. Кроме того, две плитки последовательного ранга могут быть пространственно сильно удалены друг от друга, и было бы интересно найти способ их использования кластерами. Наконец, не следует ли включить приоритет непосредственно в Менеджер задач OSM?<br />
<br />
<br />
Успешного Вам маппинга!<br />
<br />
<br />
== Источники == <br />
<br />
Оригинал статьи: [http://www.mapaou-web.fr/2016/02/18/osm-mapping-priority/ OpenStreetMap Mapping Priority] <br />
<br />
Автор статьи: [http://www.openstreetmap.org/user/adrienandrem adrienandrem]<br />
<br />
Автор перевода: [http://www.openstreetmap.org/user/Sibri Sibri]</div>Александр Мурыйhttps://wiki.gis-lab.info/index.php?title=%D0%9F%D1%80%D0%B8%D0%BE%D1%80%D0%B8%D1%82%D0%B5%D1%82_%D0%BE%D1%82%D1%80%D0%B8%D1%81%D0%BE%D0%B2%D0%BA%D0%B8_%D0%B2_OpenStreetMap&diff=25417Приоритет отрисовки в OpenStreetMap2017-06-17T16:26:02Z<p>Александр Мурый: </p>
<hr />
<div>{{Статья|Опубликована|osm-mapping-priority}}<br />
<br />
{{Аннотация|Перевод статьи, в которой при помощи GRASS GIS и PostGIS вычисляются плохо отрисованные в OpenStreetMap малонаселенные области.}}<br />
<br />
Оригинал: [http://www.mapaou-web.fr/2016/02/18/osm-mapping-priority/ OpenStreetMap Mapping Priority] <br />
<br />
== Приоритет отрисовки в OpenStreetMap ==<br />
<br />
[[Файл:Osmmp_mapping_priority_map.jpeg|500|Приоритет отрисовки в OpenStreetMap]]<br />
<br />
<br />
Работая в Французской Гвиане, автор узнал о проекте [http://mapazonia.org/ Mapazonia], целью которого является улучшение данных [http://www.openstreetmap.org/ OpenStreetMap] в регионе [http://wiki.openstreetmap.org/wiki/File:Amazonrivermap.svg Амазонии]. С начала участия в проекте автор научился использовать [http://tasks.hotosm.org/?sort_by=priority&direction=asc&search=mapazonia Менеджер задач OSM] - инструмент, который позволяет разбить AOI (Areas Of Interest - область интереса) на отдельные тайлы задач. Будучи заинтересованным в отрисовке [http://ru.wikipedia.org/wiki/Гвианское_плоскогорье Гвианского плоскогорья], автор задался вопросом: возможно ли использовать подобный подход для этой области?<br />
<br />
Как только тайлы задач были созданы (их насчиталось тысячи для такой большой области), возник вопрос: где наносить объекты в первую очередь?<br />
<br />
Чтобы ответить на этот вопрос, нужно учесть несколько подходов: <br />
<br />
* мы должны найти неотрисованные объекты там, где '''нет данных'''; <br />
* пустые тайлы в удаленных областях, где живёт немного людей, имеют меньший приоритет; <br />
* там, где исходные данные (спутниковые снимки) '''в низком разрешении''', только объекты, покрывающие большие площади, могут быть оцифрованы (так называемый ''[http://en.wikipedia.org/wiki/Scale_(map)#Large_scale.2C_medium_scale.2C_small_scale small scale]'' маппинг); <br />
* вносить данные нужно в первую очередь в '''центре AOI'''. <br />
<br />
А теперь давайте построим '''схему приоритета''' отрисовки тайлов, используя эти идеи!<br />
<br />
== Исходные данные ==<br />
<br />
Начнем с получения исходных данных. В итоге мы должны получить схему, похожую на [http://www.openstreetmap.org/user/tyr_asd/diary/22363 работу Мартина Райфера (Martin Raifer) по плотности узлов]. Данные OSM доступны на сайте [http://download.geofabrik.de/south-america-latest.osm.bz2 Geofabrik], и мы можем импортировать их в базу данных [http://postgis.net/ PostGIS], следуя инструкциям из [http://www.bostongis.com/?content_name=loading_osm_postgis#229 руководства Регины Обе (Regina Obe)]. Тайлы задач соответствуют [http://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#Zoom_levels 12 уровню] тайлов в [http://wiki.openstreetmap.org/wiki/EPSG:3857 проекции Web Mercator].<br />
<br />
Создадим таблицу тайлов:<br />
<br />
<syntaxhighlight lang="plsql"><br />
CREATE TABLE tile AS SELECT<br />
x, y<br />
FROM<br />
generate_series(0, pow(2, 12)::INT - 1) AS x,<br />
generate_series(0, pow(2, 12)::INT - 1) AS y ;<br />
COMMENT ON TABLE tile IS 'Map tiles' ;<br />
COMMENT ON COLUMN tile.x IS 'Tile coordinate' ;<br />
COMMENT ON COLUMN tile.y IS 'Tile coordinate' ;<br />
</syntaxhighlight><br />
<br />
<br />
и дополнительно построим их геометрии (таким образом мы будем использовать векторную и растровую модель представления данных):<br />
<br />
<syntaxhighlight lang="plsql"><br />
CREATE FUNCTION ST_GeomFromTileXY(z int, x int, y int, OUT tile_geom geometry(Polygon, 4326)) AS $$<br />
DECLARE<br />
side real;<br />
xmin real; ymin real; xmax real; ymax real;<br />
minlon real; maxlon real; maxlat real; minlat real;<br />
BEGIN<br />
side := pow(2, z);<br />
<br />
xmin := x::real/side - .5;<br />
ymin := .5 - y::real/side;<br />
xmax := (x + 1)::real/side - .5;<br />
ymax := .5 - (y + 1)::real/side;<br />
<br />
minlon := 360.*xmin;<br />
maxlon := 360.*xmax;<br />
maxlat := 90. - 360.*atan(exp(-ymin*2.*pi()))/pi();<br />
minlat := 90. - 360.*atan(exp(-ymax*2.*pi()))/pi();<br />
<br />
tile_geom := ST_MakeEnvelope(minlon, minlat, maxlon, maxlat, 4326);<br />
END; $$ LANGUAGE plpgsql;<br />
</syntaxhighlight><br />
<br />
<br />
Функция для получения X и Y координат тайла для заданной точки:<br />
<br />
<syntaxhighlight lang="plsql"><br />
CREATE FUNCTION TileCoord(z int, point geometry(Point, 4326),<br />
OUT tile_x int, OUT tile_y int) AS $$<br />
DECLARE<br />
tile_n real;<br />
sinphi real; x real; y real;<br />
BEGIN<br />
tile_n := power(2, z)::real;<br />
<br />
x := (ST_X(point) + 180.)/360.;<br />
sinphi = sin(ST_Y(point)*pi()/180.);<br />
y := .5 - ln((1. + sinphi)/(1. - sinphi))/(4.*pi());<br />
<br />
tile_x := CAST(trunc(tile_n*x + .5) AS int);<br />
tile_y := CAST(trunc(tile_n*y + .5) AS int);<br />
END; $$ LANGUAGE plpgsql;<br />
</syntaxhighlight><br />
<br />
<br />
Для вычисления плотности узлов сначала создадим временную таблицу, в которой будет содержаться информация о координатах тайла, в который попадает каждая точка:<br />
<br />
<syntaxhighlight lang="plsql"><br />
CREATE TEMP TABLE planet_osm_point_dump AS<br />
SELECT id, type, (coord).tile_x, (coord).tile_y, geom<br />
FROM<br />
(SELECT<br />
row_number() OVER (ORDER BY type, id, (dump).path) AS id,<br />
type,<br />
TileCoord(12, (dump).geom) AS coord,<br />
(dump).geom AS geom<br />
FROM<br />
(SELECT 0 AS type, osm_id AS id, ST_DumpPoints(way) AS dump<br />
FROM planet_osm_point<br />
UNION ALL<br />
SELECT 1 AS type, osm_id AS id, ST_DumpPoints(way) AS dump<br />
FROM planet_osm_line<br />
UNION ALL<br />
SELECT 2 AS type, osm_id AS id, ST_DumpPoints(way) AS dump<br />
FROM planet_osm_polygon<br />
) AS point<br />
) AS point_tiled;<br />
</syntaxhighlight><br />
<br />
<br />
и затем посчитаем число узлов в каждом тайле нашей AOI:<br />
<br />
<syntaxhighlight lang="plsql"><br />
ALTER TABLE tile ADD COLUMN node_count INT DEFAULT 0;<br />
<br />
UPDATE tile t SET node_count = count.num<br />
FROM (<br />
SELECT x, y, count(*) AS num<br />
FROM planet_osm_point_dump<br />
GROUP BY x, y ORDER BY x, y) AS count<br />
WHERE <br />
t.x = count.x AND t.y = count.y;<br />
</syntaxhighlight><br />
<br />
<br />
== Малонаселенные области ==<br />
<br />
Очевидно, что плотность населения выше вдоль побережья и главных рек, уменьшаясь вверх по течению реки. Мы будем моделировать '''удаленность''', используя '''гидрологическую дистанцию''', которая определяется [http://ru.wikipedia.org/wiki/Поверхностный_дренаж поверхностным дренажём]. Вычисления производятся в модуле [http://grasswiki.osgeo.org/wiki/R.stream.*#r.stream.distance r.stream.distance] системы [http://grass.osgeo.org/ GRASS GIS]. В гидрологическом моделировании обычно используется схема '''направлений потоков''', полученная из [http://en.wikipedia.org/wiki/Digital_elevation_model цифровой модели местности] (Digital Elevation Model - DEM): <br />
<br />
[[Файл:Osmmp_hydrology_map.jpeg|500|Направления гидрологических потоков]]<br />
<br />
''Направления гидрологических потоков''<br />
<br />
USGS предоставляет набор данных [http://lta.cr.usgs.gov/HYDRO1K GTOPO30 HYDRO1k], который можно скачать с сайта [http://earthexplorer.usgs.gov/ Earth Explorer] (ID GT30H1KSA для Южной Америки). Набор данных представлен в азимутальной равновеликой проекции Ламберта (LAEA), которой соответствуют следующие параметры [http://proj4.org/ PROJ.4]:<br />
<br />
<pre>+proj=laea +ellps=sphere +lon_0=-60 +lat_0=-15 +x_0=0.0 +y_0=0.0 +units=m</pre><br />
<br />
Исходя из этих параметров создадим ''location'' с использованием модулей [http://grass.osgeo.org/grass71/manuals/g.proj.html g.proj] и ''mapset'':<br />
<br />
<syntaxhighlight lang="bash"><br />
g.proj -c proj4="+proj=laea +ellps=sphere +lon_0=-60 +lat_0=-15 +x_0=0.0 +y_0=0.0 +units=m" location=south_america<br />
g.mapset -c mapset=hydro location=south_america<br />
</syntaxhighlight><br />
<br />
<br />
Набор данных содержит несколько слоёв (см. [http://lta.cr.usgs.gov/HYDRO1KReadMe файл README]), но нам в основном интересен импорт растра высот и направлений течения:<br />
<br />
<syntaxhighlight lang="bash"><br />
r.in.gdal in=gt30h1ksa/sa_dem.bil out=elev<br />
r.in.gdal -e in=gt30h1ksa/sa_fd.bil out=dir_arc<br />
</syntaxhighlight><br />
<br />
<br />
Преобразуем коды направлений в формат GRASS GIS, используя таблицу сопоставления в файле arc2grass.lut:<br />
<br />
<pre><br />
255 = 0<br />
128 = 1<br />
64 = 2<br />
32 = 3<br />
16 = 4<br />
8 = 5<br />
4 = 6<br />
2 = 7<br />
1 = 8<br />
</pre><br />
<br />
<syntaxhighlight lang="bash"><br />
r.reclass in=dir_arc out=dir rules=arc2grass.lut<br />
r.stream.distance -o dir=dir elev=elev method=up dist=dir_dist<br />
</syntaxhighlight><br />
<br />
<br /><br />
<br />
[[Файл:Osmmp hydrology distance map.jpeg|700|Расстояние до русла]]<br />
<br />
''Расстояние до русла''<br />
<br />
Для того, чтобы назначить расстояния тайлам, мы перепроецируем сетку из проекции LAEA в проекцию Web Mercator. На 15-м уровне длина стороны тайла примерно равна 1223 м, что наиболее близко к разрешению нашей сетки расстояний.<br />
Сначала создадим локацию (''прим. переводчика: "location" в терминологии GRASS GIS'') в этой проекции и перейдем к данной location:<br />
<br />
<syntaxhighlight lang="bash"><br />
g.proj -c epsg=3857 location=mercator<br />
g.mapset -c mapset=priority location=mercator<br />
</syntaxhighlight><br />
<br />
<br />
Затем создадим регион (''прим. переводчика: "region" в терминологии GRASS GIS'') на 15-м уровне и перепроецируем:<br />
<br />
<syntaxhighlight lang="bash"><br />
g.region w=-20037508.342789244 s=-20037508.342789244 e=20037508.342789244 n=20037508.342789244 rows=32768 cols=32768<br />
r.proj in=dir_dist loc=south_america mapset=hydro out=dir_dist_1k meth=near<br />
</syntaxhighlight><br />
<br />
<br />
Теперь установим для региона разрешение тайлов и выполним ресэмплинг сетки:<br />
<br />
<syntaxhighlight lang="bash"><br />
g.region w=-20037508.3427892 s=-20037508.3430388 e=20037508.3427892 n=20037508.3430388 rows=4096 cols=4096<br />
r.resamp.stats in=dir_dist_1k out=dir_dist meth=median<br />
</syntaxhighlight><br />
<br />
<br />
== Экспорт ==<br />
<br />
В конечном итоге мы получили данные, которые можем экспортировать в растровый файл:<br />
<br />
<syntaxhighlight lang="bash"><br />
r.mapcalc "dir_dist = if(isnull(dir_dist), -1, int(dir_dist))"<br />
r.out.gdal -t in=dir_dist out=hydro_dist.tif type=UInt32 nodata=4294967295<br />
</syntaxhighlight><br />
<br />
<br />
или в текстовый файл XYZ (перед импортом в PostGIS):<br />
<br />
<syntaxhighlight lang="bash"><br />
r.mapcalc "y = row() - 1"<br />
r.mapcalc "x = col() - 1"<br />
r.out.xyz in=y sep=space out=hydro_dist-y.xyz<br />
r.out.xyz in=x sep=space out=hydro_dist-x.xyz<br />
r.out.xyz in=dir_dist sep=space out=hydro_dist.xyz<br />
</syntaxhighlight><br />
<br /><br />
<syntaxhighlight lang="bash"><br />
paste hydro_dist-x.xyz hydro_dist-y.xyz hydro_dist.xyz | awk '!/-1$/ {print $3, $6, $9}' > hydro_dist.ssv<br />
</syntaxhighlight><br />
<br />
<br />
Обратите внимание, что NULL или MASK ячейки не будут экспортированы (см. [http://grass.osgeo.org/grass71/manuals/r.out.xyz.html r.out.xyz documentation]), что может привести процедуру импорта в базу данных к неожиданному результату.<br />
<br />
<syntaxhighlight lang="plsql"><br />
CREATE TEMP TABLE tile_attr (<br />
z INTEGER DEFAULT 12, x INTEGER, y INTEGER,<br />
value REAL);<br />
</syntaxhighlight><br />
<br /><br />
<syntaxhighlight lang="plsql"><br />
COPY tile_attr (x, y, value) FROM 'hydro_dist.ssv' CSV DELIMITER ' ' ;<br />
UPDATE tile t SET hydro_dist = a.value<br />
FROM tile_attr a<br />
WHERE t.level = a.z AND t.x = a.x AND t.y = a.y ;<br />
</syntaxhighlight><br />
<br />
<br />
== Расстояние до AOI ==<br />
<br />
Чтобы увеличить приоритет тайлов, которые находятся в центре нашей AOI, построим сетку весов. Вес в данном случае - это расстояние по прямой до границ AOI.<br />
<br />
Используя локацию в проекции Web Mercator с регионом на 12-м тайловом уровне, импортируем (используя [http://grass.osgeo.org/grass71/manuals/v.proj.html v.proj] при необходимости) и растеризуем нашу область интересов (AOI):<br />
<br />
<syntaxhighlight lang="bash"><br />
v.to.rast in=aoi type=line,area out=aoi use=val value=1<br />
</syntaxhighlight><br />
<br />
<br />
Рассчитаем расстояние до AOI для внешних ячеек:<br />
<br />
<syntaxhighlight lang="bash"><br />
r.grow.distance in=aoi dist=dist_outer_aoi<br />
</syntaxhighlight><br />
<br />
<br />
Выполним то же самое для внутренних ячеек:<br />
<br />
<syntaxhighlight lang="bash"><br />
r.mapcalc "aoi_inv = if(isnull(aoi), 1, null())"<br />
r.grow.distance in=aoi_inv dist=dist_inner_aoi<br />
</syntaxhighlight><br />
<br />
<br />
Комбинируем сетки расстояний в инвертированных значениях для внутренних тайлов:<br />
<br />
<syntaxhighlight lang="bash"><br />
r.mapcalc "dist_aoi = if(isnull(aoi), dist_outer_aoi, -dist_inner_aoi)"<br />
</syntaxhighlight><br />
<br />
<br />
В случае использования нескольких AOI построим растровую маску, сдвинем и масштабируем значения к диапазону [0, 1]:<br />
<br />
<syntaxhighlight lang="bash"><br />
r.mapcalc "roi = aoi_1 ||| aoi_2"<br />
r.grow in=roi out=roi old=1 new=1<br />
r.mask roi<br />
eval $(r.univar -g dist_aoi | grep "\(min\|max\)")<br />
r.mapcalc "dist_aoi = (dist_aoi - $min)/($max - $min)" --o<br />
</syntaxhighlight><br />
<br />
<br />
Вычислим средневзвешенное значение для каждого расстояния:<br />
<br />
<syntaxhighlight lang="bash"><br />
r.mapcalc "aois_dist = (dist_aoi_1 + dist_aoi_2)/2."<br />
</syntaxhighlight><br />
<br />
<br />
[[Файл:Osmmp weighted distance map.jpeg|500|Взвешенное расстояние до центра AOI]]<br />
<br />
''Взвешенное расстояние до центра AOI''<br />
<br />
<br />
== Разрешение снимков ==<br />
<br />
Информация о разрешении снимков может быть получена, например, в [http://mvexel.dev.openstreetmap.org/bingimageanalyzer/?lat=0.882&lon=-71.132&zoom=8 инструменте анализа покрытия спутниковых снимков Bing] (см. статью [http://wiki.openstreetmap.org/wiki/Bing_Maps/Coverage Coverage] на OSM Wiki).<br />
<br />
<br />
Ниже пример на Python:<br />
<br />
<syntaxhighlight lang="python"><br />
PATTERN = "http://t{srv}.domain.tld/tile/{key}.jpg"<br />
HEADER = "Content-Length"; server = randint(0, 7)<br />
url = PATTERN.format(srv=server, key=quadkey)<br />
meta = urlopen(url).info()<br />
size = meta.getheaders(HEADER)[0]<br />
</syntaxhighlight><br />
<br />
<br />
Функция PostgreSQL, вычисляющая индекс [http://wiki.openstreetmap.org/wiki/QuadTiles quadkey] для заданного уровня (zoom level) и координат тайла (x, y):<br />
<br />
<syntaxhighlight lang="plsql"><br />
CREATE FUNCTION QuadKey(z int, x int, y int, OUT quadkey character varying(16)) AS $$<br />
DECLARE digit int; mask int; BEGIN<br />
quadkey := '';<br />
<br />
FOR i IN REVERSE z..1 LOOP<br />
digit := 0;<br />
mask := 1 << (i - 1);<br />
<br />
IF (x & mask) != 0 THEN digit := digit + 1; END IF;<br />
IF (y & mask) != 0 THEN digit := digit + 2; END IF;<br />
<br />
quadkey := quadkey || digit;<br />
END LOOP;<br />
END; $$ LANGUAGE plpgsql;<br />
</syntaxhighlight><br />
<br />
<br />
В областях с низким разрешением снимков Bing тайлы на высоких масштабных уровнях (zoom level) в основном отсутствуют. Давайте оценим качество покрытия снимками высокого разрешения путём подсчета ненулевых тайлов на 14-м уровне:<br />
<br />
<syntaxhighlight lang="plsql"><br />
SELECT<br />
substring(quadkey for 12) AS key,<br />
count(quadkey)/16 AS resolution_index<br />
FROM tile_bing WHERE z = 14 AND size > 0<br />
GROUP BY substring(quadkey for 12) ORDER BY key ;<br />
</syntaxhighlight><br />
<br />
<br />
[[Файл:Osmmp bing resolution map.jpeg|500|Разрешение спутниковых снимков Bing]]<br />
<br />
''Разрешение спутниковых снимков Bing''<br />
<br />
<br />
== Создание индекса приоритетности ==<br />
<br />
Используя полученные ранее измерения для тайлов, мы можем создать индекс приоритетности в соответствии с нашими пожеланиями. Например, формула<br />
<br />
<pre>priority = ((1 - density) + dist_hydro + (1 - dist_aoi) + (1 - resolution))/4</pre><br />
<br />
<br />
устанавливает высокое значение приоритета для тайлов, которые не содержат узлов, расположены недалеко от береговой линии, но довольно близко к AOI, и покрыты снимками в низком разрашении.<br />
<br />
Для того, чтобы использовать схему приоритетов в [http://qgis.org/ QGIS], добавьте столбец ''status'' в таблицу ''tile'' и создайте в плагине [http://docs.qgis.org/2.0/en/docs/training_manual/databases/db_manager.html DB Manager] динамически обновляемый слой TODO на основе SQL-запроса: <br />
<br />
<syntaxhighlight lang="plsql"><br />
SELECT<br />
row_number() OVER (ORDER BY priority DESC) AS id,<br />
priority, x, y, ST_ExteriorRing(geom) AS geom<br />
FROM tile WHERE status <> 'DONE'<br />
ORDER BY priority DESC<br />
LIMIT 32 ;<br />
</syntaxhighlight><br />
<br />
<br />
Данный слой показывает следующие 32 тайла задач для отрисовки:<br />
<br />
<br />
[[Файл:Osmmp high priority tiles.jpeg|500|Тайлы задач с наивысшим приоритетом]]<br />
<br />
''Тайлы задач с наивысшим приоритетом''<br />
<br />
<br />
Перед тем, как начать наносить объекты в [http://wiki.openstreetmap.org/wiki/JOSM JOSM], либо сохраните выбранный тайл в файл формата GPX (отметьте опцию FORCE_GPX_TRACK и необходимость использования [http://postgis.net/docs/ST_ExteriorRing.html ST_ExteriorRing]), или вызовите GDAL из [http://docs.qgis.org/2.8/en/docs/training_manual/create_vector_data/actions.html layer action]:<br />
<br />
<syntaxhighlight lang="bash"><br />
ogr2ogr -f GPX -sql "SELECT x || ' ' || y AS name, ST_ExteriorRing(geom) FROM tile WHERE x = '[% "x" %]' AND y = '[% "y" %]'" [% "x" %]_[% "y" %].gpx PG:dbname=osm -lco FORCE_GPX_TRACK=YES -nlt LINESTRING<br />
</syntaxhighlight><br />
<br />
<br />
Когда все интересующие вас объекты, входящие в границы тайла задания, добавлены, обновите атрибут его статуса в базе данных (status = DONE) и обновите экран QGIS (нажав клавишу F5).<br />
<br />
Используя данную схему приоритетов, автор замечал, что иногда [http://wiki.openstreetmap.org/wiki/DigitalGlobe снимки Mapbox] в более высоком разрешении, чем снимки Bing. Было бы полезно выяснить, предоставляют ли они схему разрешения снимков. Кроме того, две плитки последовательного ранга могут быть пространственно сильно удалены друг от друга, и было бы интересно найти способ их использования кластерами. Наконец, не следует ли включить приоритет непосредственно в Менеджер задач OSM?<br />
<br />
<br />
Успешного Вам маппинга!<br />
<br />
<br />
== Источники == <br />
<br />
Оригинал статьи: [http://www.mapaou-web.fr/2016/02/18/osm-mapping-priority/ OpenStreetMap Mapping Priority] <br />
<br />
Автор статьи: [http://www.openstreetmap.org/user/adrienandrem adrienandrem]<br />
<br />
Автор перевода: [http://www.openstreetmap.org/user/Sibri Sibri]</div>Александр Мурыйhttps://wiki.gis-lab.info/index.php?title=%D0%9F%D1%80%D0%B8%D0%BE%D1%80%D0%B8%D1%82%D0%B5%D1%82_%D0%BE%D1%82%D1%80%D0%B8%D1%81%D0%BE%D0%B2%D0%BA%D0%B8_%D0%B2_OpenStreetMap&diff=25416Приоритет отрисовки в OpenStreetMap2017-06-17T08:00:41Z<p>Александр Мурый: </p>
<hr />
<div>{{Статья|Черновик}}<br />
<br />
{{Аннотация|Перевод статьи, в которой при помощи GRASS GIS и PostGIS вычисляются плохо отрисованные в OpenStreetMap малонаселенные области.}}<br />
<br />
Оригинал: [http://www.mapaou-web.fr/2016/02/18/osm-mapping-priority/ OpenStreetMap Mapping Priority] <br />
<br />
== Приоритет отрисовки в OpenStreetMap ==<br />
<br />
[[Файл:Osmmp_mapping_priority_map.jpeg|700|Приоритет отрисовки в OpenStreetMap]]<br />
<br />
<br />
Работая в Французской Гвиане, автор узнал о проекте [http://mapazonia.org/ Mapazonia], целью которого является улучшение данных [http://www.openstreetmap.org/ OpenStreetMap] в регионе [http://wiki.openstreetmap.org/wiki/File:Amazonrivermap.svg Амазонии]. С начала участия в проекте автор научился использовать [http://tasks.hotosm.org/?sort_by=priority&direction=asc&search=mapazonia Менеджер задач OSM] - инструмент, который позволяет разбить AOI (Areas Of Interest - область интереса) на отдельные тайлы задач. Будучи заинтересованным в отрисовке [http://ru.wikipedia.org/wiki/Гвианское_плоскогорье Гвианского плоскогорья], автор задался вопросом: возможно ли использовать подобный подход для этой области?<br />
<br />
Как только тайлы задач были созданы (их насчиталось тысячи для такой большой области), возник вопрос: где наносить объекты в первую очередь?<br />
<br />
Чтобы ответить на этот вопрос, нужно учесть несколько подходов: <br />
<br />
* мы должны найти неотрисованные объекты там, где '''нет данных'''; <br />
* пустые тайлы в удаленных областях, где живёт немного людей, имеют меньший приоритет; <br />
* там, где исходные данные (спутниковые снимки) '''в низком разрешении''', только объекты, покрывающие большие площади, могут быть оцифрованы (так называемый ''[http://en.wikipedia.org/wiki/Scale_(map)#Large_scale.2C_medium_scale.2C_small_scale small scale]'' маппинг); <br />
* вносить данные нужно в первую очередь в '''центре AOI'''. <br />
<br />
А теперь давайте построим '''схему приоритета''' отрисовки тайлов, используя эти идеи!<br />
<br />
== Исходные данные ==<br />
<br />
Начнем с получения исходных данных. В итоге мы должны получить схему, похожую на [http://www.openstreetmap.org/user/tyr_asd/diary/22363 работу Мартина Райфера (Martin Raifer) по плотности узлов]. Данные OSM доступны на сайте [http://download.geofabrik.de/south-america-latest.osm.bz2 Geofabrik], и мы можем импортировать их в базу данных [http://postgis.net/ PostGIS], следуя инструкциям из [http://www.bostongis.com/?content_name=loading_osm_postgis#229 руководства Регины Обе (Regina Obe)]. Тайлы задач соответствуют [http://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#Zoom_levels 12 уровню] тайлов в [http://wiki.openstreetmap.org/wiki/EPSG:3857 проекции Web Mercator].<br />
<br />
Создадим таблицу тайлов:<br />
<br />
<syntaxhighlight lang="plsql"><br />
CREATE TABLE tile AS SELECT<br />
x, y<br />
FROM<br />
generate_series(0, pow(2, 12)::INT - 1) AS x,<br />
generate_series(0, pow(2, 12)::INT - 1) AS y ;<br />
COMMENT ON TABLE tile IS 'Map tiles' ;<br />
COMMENT ON COLUMN tile.x IS 'Tile coordinate' ;<br />
COMMENT ON COLUMN tile.y IS 'Tile coordinate' ;<br />
</syntaxhighlight><br />
<br />
<br />
и дополнительно построим их геометрии (таким образом мы будем использовать векторную и растровую модель представления данных):<br />
<br />
<syntaxhighlight lang="plsql"><br />
CREATE FUNCTION ST_GeomFromTileXY(z int, x int, y int, OUT tile_geom geometry(Polygon, 4326)) AS $$<br />
DECLARE<br />
side real;<br />
xmin real; ymin real; xmax real; ymax real;<br />
minlon real; maxlon real; maxlat real; minlat real;<br />
BEGIN<br />
side := pow(2, z);<br />
<br />
xmin := x::real/side - .5;<br />
ymin := .5 - y::real/side;<br />
xmax := (x + 1)::real/side - .5;<br />
ymax := .5 - (y + 1)::real/side;<br />
<br />
minlon := 360.*xmin;<br />
maxlon := 360.*xmax;<br />
maxlat := 90. - 360.*atan(exp(-ymin*2.*pi()))/pi();<br />
minlat := 90. - 360.*atan(exp(-ymax*2.*pi()))/pi();<br />
<br />
tile_geom := ST_MakeEnvelope(minlon, minlat, maxlon, maxlat, 4326);<br />
END; $$ LANGUAGE plpgsql;<br />
</syntaxhighlight><br />
<br />
<br />
Функция для получения X и Y координат тайла для заданной точки:<br />
<br />
<syntaxhighlight lang="plsql"><br />
CREATE FUNCTION TileCoord(z int, point geometry(Point, 4326),<br />
OUT tile_x int, OUT tile_y int) AS $$<br />
DECLARE<br />
tile_n real;<br />
sinphi real; x real; y real;<br />
BEGIN<br />
tile_n := power(2, z)::real;<br />
<br />
x := (ST_X(point) + 180.)/360.;<br />
sinphi = sin(ST_Y(point)*pi()/180.);<br />
y := .5 - ln((1. + sinphi)/(1. - sinphi))/(4.*pi());<br />
<br />
tile_x := CAST(trunc(tile_n*x + .5) AS int);<br />
tile_y := CAST(trunc(tile_n*y + .5) AS int);<br />
END; $$ LANGUAGE plpgsql;<br />
</syntaxhighlight><br />
<br />
<br />
Для вычисления плотности узлов сначала создадим временную таблицу, в которой будет содержаться информация о координатах тайла, в который попадает каждая точка:<br />
<br />
<syntaxhighlight lang="plsql"><br />
CREATE TEMP TABLE planet_osm_point_dump AS<br />
SELECT id, type, (coord).tile_x, (coord).tile_y, geom<br />
FROM<br />
(SELECT<br />
row_number() OVER (ORDER BY type, id, (dump).path) AS id,<br />
type,<br />
TileCoord(12, (dump).geom) AS coord,<br />
(dump).geom AS geom<br />
FROM<br />
(SELECT 0 AS type, osm_id AS id, ST_DumpPoints(way) AS dump<br />
FROM planet_osm_point<br />
UNION ALL<br />
SELECT 1 AS type, osm_id AS id, ST_DumpPoints(way) AS dump<br />
FROM planet_osm_line<br />
UNION ALL<br />
SELECT 2 AS type, osm_id AS id, ST_DumpPoints(way) AS dump<br />
FROM planet_osm_polygon<br />
) AS point<br />
) AS point_tiled;<br />
</syntaxhighlight><br />
<br />
<br />
и затем посчитаем число узлов в каждом тайле нашей AOI:<br />
<br />
<syntaxhighlight lang="plsql"><br />
ALTER TABLE tile ADD COLUMN node_count INT DEFAULT 0;<br />
<br />
UPDATE tile t SET node_count = count.num<br />
FROM (<br />
SELECT x, y, count(*) AS num<br />
FROM planet_osm_point_dump<br />
GROUP BY x, y ORDER BY x, y) AS count<br />
WHERE <br />
t.x = count.x AND t.y = count.y;<br />
</syntaxhighlight><br />
<br />
<br />
== Малонаселенные области ==<br />
<br />
Очевидно, что плотность населения выше вдоль побережья и главных рек, уменьшаясь вверх по течению реки. Мы будем моделировать '''удаленность''', используя '''гидрологическую дистанцию''', которая определяется [http://ru.wikipedia.org/wiki/Поверхностный_дренаж поверхностным дренажём]. Вычисления производятся в модуле [http://grasswiki.osgeo.org/wiki/R.stream.*#r.stream.distance r.stream.distance] системы [http://grass.osgeo.org/ GRASS GIS]. В гидрологическом моделировании обычно используется схема '''направлений потоков''', полученная из [http://en.wikipedia.org/wiki/Digital_elevation_model цифровой модели местности] (Digital Elevation Model - DEM): <br />
<br />
[[Файл:Osmmp_hydrology_map.jpeg|700|Направления гидрологических потоков]]<br />
<br />
''Направления гидрологических потоков''<br />
<br />
USGS предоставляет набор данных [http://lta.cr.usgs.gov/HYDRO1K GTOPO30 HYDRO1k], который можно скачать с сайта [http://earthexplorer.usgs.gov/ Earth Explorer] (ID GT30H1KSA для Южной Америки). Набор данных представлен в азимутальной равновеликой проекции Ламберта (LAEA), которой соответствуют следующие параметры [http://proj4.org/ PROJ.4]:<br />
<br />
<pre>+proj=laea +ellps=sphere +lon_0=-60 +lat_0=-15 +x_0=0.0 +y_0=0.0 +units=m</pre><br />
<br />
Исходя из этих параметров создадим ''location'' с использованием модулей [http://grass.osgeo.org/grass71/manuals/g.proj.html g.proj] и ''mapset'':<br />
<br />
<syntaxhighlight lang="bash"><br />
g.proj -c proj4="+proj=laea +ellps=sphere +lon_0=-60 +lat_0=-15 +x_0=0.0 +y_0=0.0 +units=m" location=south_america<br />
g.mapset -c mapset=hydro location=south_america<br />
</syntaxhighlight><br />
<br />
<br />
Набор данных содержит несколько слоёв (см. [http://lta.cr.usgs.gov/HYDRO1KReadMe файл README]), но нам в основном интересен импорт растра высот и направлений течения:<br />
<br />
<syntaxhighlight lang="bash"><br />
r.in.gdal in=gt30h1ksa/sa_dem.bil out=elev<br />
r.in.gdal -e in=gt30h1ksa/sa_fd.bil out=dir_arc<br />
</syntaxhighlight><br />
<br />
<br />
Преобразуем коды направлений в формат GRASS GIS, используя таблицу сопоставления в файле arc2grass.lut:<br />
<br />
<pre><br />
255 = 0<br />
128 = 1<br />
64 = 2<br />
32 = 3<br />
16 = 4<br />
8 = 5<br />
4 = 6<br />
2 = 7<br />
1 = 8<br />
</pre><br />
<br />
<syntaxhighlight lang="bash"><br />
r.reclass in=dir_arc out=dir rules=arc2grass.lut<br />
r.stream.distance -o dir=dir elev=elev method=up dist=dir_dist<br />
</syntaxhighlight><br />
<br />
<br /><br />
<br />
[[Файл:Osmmp hydrology distance map.jpeg|700|Расстояние до русла]]<br />
<br />
''Расстояние до русла''<br />
<br />
Для того, чтобы назначить расстояния тайлам, мы перепроецируем сетку из проекции LAEA в проекцию Web Mercator. На 15-м уровне длина стороны тайла примерно равна 1223 м, что наиболее близко к разрешению нашей сетки расстояний.<br />
Сначала создадим локацию (''прим. переводчика: "location" в терминологии GRASS GIS'') в этой проекции и перейдем к данной location:<br />
<br />
<syntaxhighlight lang="bash"><br />
g.proj -c epsg=3857 location=mercator<br />
g.mapset -c mapset=priority location=mercator<br />
</syntaxhighlight><br />
<br />
<br />
Затем создадим регион (''прим. переводчика: "region" в терминологии GRASS GIS'') на 15-м уровне и перепроецируем:<br />
<br />
<syntaxhighlight lang="bash"><br />
g.region w=-20037508.342789244 s=-20037508.342789244 e=20037508.342789244 n=20037508.342789244 rows=32768 cols=32768<br />
r.proj in=dir_dist loc=south_america mapset=hydro out=dir_dist_1k meth=near<br />
</syntaxhighlight><br />
<br />
<br />
Теперь установим для региона разрешение тайлов и выполним ресэмплинг сетки:<br />
<br />
<syntaxhighlight lang="bash"><br />
g.region w=-20037508.3427892 s=-20037508.3430388 e=20037508.3427892 n=20037508.3430388 rows=4096 cols=4096<br />
r.resamp.stats in=dir_dist_1k out=dir_dist meth=median<br />
</syntaxhighlight><br />
<br />
<br />
== Экспорт ==<br />
<br />
В конечном итоге мы получили данные, которые можем экспортировать в растровый файл:<br />
<br />
<syntaxhighlight lang="bash"><br />
r.mapcalc "dir_dist = if(isnull(dir_dist), -1, int(dir_dist))"<br />
r.out.gdal -t in=dir_dist out=hydro_dist.tif type=UInt32 nodata=4294967295<br />
</syntaxhighlight><br />
<br />
<br />
или в текстовый файл XYZ (перед импортом в PostGIS):<br />
<br />
<syntaxhighlight lang="bash"><br />
r.mapcalc "y = row() - 1"<br />
r.mapcalc "x = col() - 1"<br />
r.out.xyz in=y sep=space out=hydro_dist-y.xyz<br />
r.out.xyz in=x sep=space out=hydro_dist-x.xyz<br />
r.out.xyz in=dir_dist sep=space out=hydro_dist.xyz<br />
</syntaxhighlight><br />
<br /><br />
<syntaxhighlight lang="bash"><br />
paste hydro_dist-x.xyz hydro_dist-y.xyz hydro_dist.xyz | awk '!/-1$/ {print $3, $6, $9}' > hydro_dist.ssv<br />
</syntaxhighlight><br />
<br />
<br />
Обратите внимание, что NULL или MASK ячейки не будут экспортированы (см. [http://grass.osgeo.org/grass71/manuals/r.out.xyz.html r.out.xyz documentation]), что может привести процедуру импорта в базу данных к неожиданному результату.<br />
<br />
<syntaxhighlight lang="plsql"><br />
CREATE TEMP TABLE tile_attr (<br />
z INTEGER DEFAULT 12, x INTEGER, y INTEGER,<br />
value REAL);<br />
</syntaxhighlight><br />
<br /><br />
<syntaxhighlight lang="plsql"><br />
COPY tile_attr (x, y, value) FROM 'hydro_dist.ssv' CSV DELIMITER ' ' ;<br />
UPDATE tile t SET hydro_dist = a.value<br />
FROM tile_attr a<br />
WHERE t.level = a.z AND t.x = a.x AND t.y = a.y ;<br />
</syntaxhighlight><br />
<br />
<br />
== Расстояние до AOI ==<br />
<br />
Чтобы увеличить приоритет тайлов, которые находятся в центре нашей AOI, построим сетку весов. Вес в данном случае - это расстояние по прямой до границ AOI.<br />
<br />
Используя локацию в проекции Web Mercator с регионом на 12-м тайловом уровне, импортируем (используя [http://grass.osgeo.org/grass71/manuals/v.proj.html v.proj] при необходимости) и растеризуем нашу область интересов (AOI):<br />
<br />
<syntaxhighlight lang="bash"><br />
v.to.rast in=aoi type=line,area out=aoi use=val value=1<br />
</syntaxhighlight><br />
<br />
<br />
Рассчитаем расстояние до AOI для внешних ячеек:<br />
<br />
<syntaxhighlight lang="bash"><br />
r.grow.distance in=aoi dist=dist_outer_aoi<br />
</syntaxhighlight><br />
<br />
<br />
Выполним то же самое для внутренних ячеек:<br />
<br />
<syntaxhighlight lang="bash"><br />
r.mapcalc "aoi_inv = if(isnull(aoi), 1, null())"<br />
r.grow.distance in=aoi_inv dist=dist_inner_aoi<br />
</syntaxhighlight><br />
<br />
<br />
Комбинируем сетки расстояний в инвертированных значениях для внутренних тайлов:<br />
<br />
<syntaxhighlight lang="bash"><br />
r.mapcalc "dist_aoi = if(isnull(aoi), dist_outer_aoi, -dist_inner_aoi)"<br />
</syntaxhighlight><br />
<br />
<br />
В случае использования нескольких AOI построим растровую маску, сдвинем и масштабируем значения к диапазону [0, 1]:<br />
<br />
<syntaxhighlight lang="bash"><br />
r.mapcalc "roi = aoi_1 ||| aoi_2"<br />
r.grow in=roi out=roi old=1 new=1<br />
r.mask roi<br />
eval $(r.univar -g dist_aoi | grep "\(min\|max\)")<br />
r.mapcalc "dist_aoi = (dist_aoi - $min)/($max - $min)" --o<br />
</syntaxhighlight><br />
<br />
<br />
Вычислим средневзвешенное значение для каждого расстояния:<br />
<br />
<syntaxhighlight lang="bash"><br />
r.mapcalc "aois_dist = (dist_aoi_1 + dist_aoi_2)/2."<br />
</syntaxhighlight><br />
<br />
<br />
[[Файл:Osmmp weighted distance map.jpeg|700|Взвешенное расстояние до центра AOI]]<br />
<br />
''Взвешенное расстояние до центра AOI''<br />
<br />
<br />
== Разрешение снимков ==<br />
<br />
Информация о разрешении снимков может быть получена, например, в [http://mvexel.dev.openstreetmap.org/bingimageanalyzer/?lat=0.882&lon=-71.132&zoom=8 инструменте анализа покрытия спутниковых снимков Bing] (см. статью [http://wiki.openstreetmap.org/wiki/Bing_Maps/Coverage Coverage] на OSM Wiki).<br />
<br />
<br />
Ниже пример на Python:<br />
<br />
<syntaxhighlight lang="python"><br />
PATTERN = "http://t{srv}.domain.tld/tile/{key}.jpg"<br />
HEADER = "Content-Length"; server = randint(0, 7)<br />
url = PATTERN.format(srv=server, key=quadkey)<br />
meta = urlopen(url).info()<br />
size = meta.getheaders(HEADER)[0]<br />
</syntaxhighlight><br />
<br />
<br />
Функция PostgreSQL, вычисляющая индекс [http://wiki.openstreetmap.org/wiki/QuadTiles quadkey] для заданного уровня (zoom level) и координат тайла (x, y):<br />
<br />
<syntaxhighlight lang="plsql"><br />
CREATE FUNCTION QuadKey(z int, x int, y int, OUT quadkey character varying(16)) AS $$<br />
DECLARE digit int; mask int; BEGIN<br />
quadkey := '';<br />
<br />
FOR i IN REVERSE z..1 LOOP<br />
digit := 0;<br />
mask := 1 << (i - 1);<br />
<br />
IF (x & mask) != 0 THEN digit := digit + 1; END IF;<br />
IF (y & mask) != 0 THEN digit := digit + 2; END IF;<br />
<br />
quadkey := quadkey || digit;<br />
END LOOP;<br />
END; $$ LANGUAGE plpgsql;<br />
</syntaxhighlight><br />
<br />
<br />
В областях с низким разрешением снимков Bing тайлы на высоких масштабных уровнях (zoom level) в основном отсутствуют. Давайте оценим качество покрытия снимками высокого разрешения путём подсчета ненулевых тайлов на 14-м уровне:<br />
<br />
<syntaxhighlight lang="plsql"><br />
SELECT<br />
substring(quadkey for 12) AS key,<br />
count(quadkey)/16 AS resolution_index<br />
FROM tile_bing WHERE z = 14 AND size > 0<br />
GROUP BY substring(quadkey for 12) ORDER BY key ;<br />
</syntaxhighlight><br />
<br />
<br />
[[Файл:Osmmp bing resolution map.jpeg|700|Разрешение спутниковых снимков Bing]]<br />
<br />
''Разрешение спутниковых снимков Bing''<br />
<br />
<br />
== Создание индекса приоритетности ==<br />
<br />
Используя полученные ранее измерения для тайлов, мы можем создать индекс приоритетности в соответствии с нашими пожеланиями. Например, формула<br />
<br />
<pre>priority = ((1 - density) + dist_hydro + (1 - dist_aoi) + (1 - resolution))/4</pre><br />
<br />
<br />
устанавливает высокое значение приоритета для тайлов, которые не содержат узлов, расположены недалеко от береговой линии, но довольно близко к AOI, и покрыты снимками в низком разрашении.<br />
<br />
Для того, чтобы использовать схему приоритетов в [http://qgis.org/ QGIS], добавьте столбец ''status'' в таблицу ''tile'' и создайте в плагине [http://docs.qgis.org/2.0/en/docs/training_manual/databases/db_manager.html DB Manager] динамически обновляемый слой TODO на основе SQL-запроса: <br />
<br />
<syntaxhighlight lang="plsql"><br />
SELECT<br />
row_number() OVER (ORDER BY priority DESC) AS id,<br />
priority, x, y, ST_ExteriorRing(geom) AS geom<br />
FROM tile WHERE status <> 'DONE'<br />
ORDER BY priority DESC<br />
LIMIT 32 ;<br />
</syntaxhighlight><br />
<br />
<br />
Данный слой показывает следующие 32 тайла задач для отрисовки:<br />
<br />
<br />
[[Файл:Osmmp high priority tiles.jpeg|700|Тайлы задач с наивысшим приоритетом]]<br />
<br />
''Тайлы задач с наивысшим приоритетом''<br />
<br />
<br />
Перед тем, как начать наносить объекты в [http://wiki.openstreetmap.org/wiki/JOSM JOSM], либо сохраните выбранный тайл в файл формата GPX (отметьте опцию FORCE_GPX_TRACK и необходимость использования [http://postgis.net/docs/ST_ExteriorRing.html ST_ExteriorRing]), или вызовите GDAL из [http://docs.qgis.org/2.8/en/docs/training_manual/create_vector_data/actions.html layer action]:<br />
<br />
<syntaxhighlight lang="bash"><br />
ogr2ogr -f GPX -sql "SELECT x || ' ' || y AS name, ST_ExteriorRing(geom) FROM tile WHERE x = '[% "x" %]' AND y = '[% "y" %]'" [% "x" %]_[% "y" %].gpx PG:dbname=osm -lco FORCE_GPX_TRACK=YES -nlt LINESTRING<br />
</syntaxhighlight><br />
<br />
<br />
Когда все интересующие вас объекты, входящие в границы тайла задания, добавлены, обновите атрибут его статуса в базе данных (status = DONE) и обновите экран QGIS (нажав клавишу F5).<br />
<br />
Используя данную схему приоритетов, автор замечал, что иногда [http://wiki.openstreetmap.org/wiki/DigitalGlobe снимки Mapbox] в более высоком разрешении, чем снимки Bing. Было бы полезно выяснить, предоставляют ли они схему разрешения снимков. Кроме того, две плитки последовательного ранга могут быть пространственно сильно удалены друг от друга, и было бы интересно найти способ их использования кластерами. Наконец, не следует ли включить приоритет непосредственно в Менеджер задач OSM?<br />
<br />
<br />
Успешного Вам маппинга!<br />
<br />
<br />
== Источники == <br />
<br />
Оригинал статьи: [http://www.mapaou-web.fr/2016/02/18/osm-mapping-priority/ OpenStreetMap Mapping Priority] <br />
<br />
Автор статьи: [http://www.openstreetmap.org/user/adrienandrem adrienandrem]<br />
<br />
Автор перевода: [http://www.openstreetmap.org/user/Sibri Sibri]</div>Александр Мурыйhttps://wiki.gis-lab.info/index.php?title=%D0%9F%D1%80%D0%B8%D0%BE%D1%80%D0%B8%D1%82%D0%B5%D1%82_%D0%BE%D1%82%D1%80%D0%B8%D1%81%D0%BE%D0%B2%D0%BA%D0%B8_%D0%B2_OpenStreetMap&diff=25415Приоритет отрисовки в OpenStreetMap2017-06-17T07:39:43Z<p>Александр Мурый: </p>
<hr />
<div>{{Статья|Черновик}}<br />
<br />
{{Аннотация|Перевод статьи, в которой при помощи GRASS GIS и PostGIS вычисляются плохо отрисованные в OpenStreetMap малонаселенные области.}}<br />
<br />
Оригинал: [http://www.mapaou-web.fr/2016/02/18/osm-mapping-priority/ OpenStreetMap Mapping Priority] <br />
<br />
== Приоритет отрисовки в OpenStreetMap ==<br />
<br />
[[Файл:Osmmp_mapping_priority_map.jpeg|700|Приоритет отрисовки в OpenStreetMap]]<br />
<br />
<br />
Работая в Французской Гвиане, автор узнал о проекте [http://mapazonia.org/ Mapazonia], целью которого является улучшение данных [http://www.openstreetmap.org/ OpenStreetMap] в регионе [http://wiki.openstreetmap.org/wiki/File:Amazonrivermap.svg Амазонии]. С начала участия в проекте автор научился использовать [http://tasks.hotosm.org/?sort_by=priority&direction=asc&search=mapazonia Менеджер задач OSM] - инструмент, который позволяет разбить AOI (Areas Of Interest - область интереса) на отдельные участки по задачам. Будучи заинтересованным в отрисовке [http://ru.wikipedia.org/wiki/Гвианское_плоскогорье Гвианского плоскогорья], автор задался вопросом: возможно ли использовать подобный подход для этой области?<br />
<br />
Как только участки задач были созданы (их насчиталось тысячи для такой большой области), возник вопрос: где наносить объекты в первую очередь?<br />
<br />
Чтобы ответить на этот вопрос, нужно учесть несколько подходов: <br />
<br />
* мы должны найти неотрисованные объекты там, где '''нет данных'''; <br />
* пустые тайлы в удаленных областях, где живёт немного людей, имеют меньший приоритет; <br />
* там, где исходные данные (спутниковые снимки) '''в низком разрешении''', только объекты, покрывающие большие площади, могут быть оцифрованы (так называемый ''[http://en.wikipedia.org/wiki/Scale_(map)#Large_scale.2C_medium_scale.2C_small_scale small scale]'' маппинг); <br />
* вносить данные нужно в первую очередь в '''центре AOI'''. <br />
<br />
А теперь давайте построим '''схему приоритета''' отрисовки тайлов, используя эти идеи!<br />
<br />
== Исходные данные ==<br />
<br />
Начнем с получения исходных данных. В итоге мы должны получить схему, похожую на [http://www.openstreetmap.org/user/tyr_asd/diary/22363 работу Мартина Райфера (Martin Raifer) по плотности узлов]. Данные OSM доступны на сайте [http://download.geofabrik.de/south-america-latest.osm.bz2 Geofabrik], и мы можем импортировать их в базу данных [http://postgis.net/ PostGIS], следуя инструкциям из [http://www.bostongis.com/?content_name=loading_osm_postgis#229 руководства Регины Обе (Regina Obe)]. Тайлы задач соответствуют [http://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#Zoom_levels 12 уровню] тайлов в [http://wiki.openstreetmap.org/wiki/EPSG:3857 проекции веб-картографии].<br />
<br />
Создадим таблицу тайлов:<br />
<br />
<syntaxhighlight lang="plsql"><br />
CREATE TABLE tile AS SELECT<br />
x, y<br />
FROM<br />
generate_series(0, pow(2, 12)::INT - 1) AS x,<br />
generate_series(0, pow(2, 12)::INT - 1) AS y ;<br />
COMMENT ON TABLE tile IS 'Map tiles' ;<br />
COMMENT ON COLUMN tile.x IS 'Tile coordinate' ;<br />
COMMENT ON COLUMN tile.y IS 'Tile coordinate' ;<br />
</syntaxhighlight><br />
<br />
<br />
и дополнительно построим их геометрии (таким образом мы будем использовать векторную и растровую модель представления данных):<br />
<br />
<syntaxhighlight lang="plsql"><br />
CREATE FUNCTION ST_GeomFromTileXY(z int, x int, y int, OUT tile_geom geometry(Polygon, 4326)) AS $$<br />
DECLARE<br />
side real;<br />
xmin real; ymin real; xmax real; ymax real;<br />
minlon real; maxlon real; maxlat real; minlat real;<br />
BEGIN<br />
side := pow(2, z);<br />
<br />
xmin := x::real/side - .5;<br />
ymin := .5 - y::real/side;<br />
xmax := (x + 1)::real/side - .5;<br />
ymax := .5 - (y + 1)::real/side;<br />
<br />
minlon := 360.*xmin;<br />
maxlon := 360.*xmax;<br />
maxlat := 90. - 360.*atan(exp(-ymin*2.*pi()))/pi();<br />
minlat := 90. - 360.*atan(exp(-ymax*2.*pi()))/pi();<br />
<br />
tile_geom := ST_MakeEnvelope(minlon, minlat, maxlon, maxlat, 4326);<br />
END; $$ LANGUAGE plpgsql;<br />
</syntaxhighlight><br />
<br />
<br />
Функция для получения x и y координат тайла для заданной точки:<br />
<br />
<syntaxhighlight lang="plsql"><br />
CREATE FUNCTION TileCoord(z int, point geometry(Point, 4326),<br />
OUT tile_x int, OUT tile_y int) AS $$<br />
DECLARE<br />
tile_n real;<br />
sinphi real; x real; y real;<br />
BEGIN<br />
tile_n := power(2, z)::real;<br />
<br />
x := (ST_X(point) + 180.)/360.;<br />
sinphi = sin(ST_Y(point)*pi()/180.);<br />
y := .5 - ln((1. + sinphi)/(1. - sinphi))/(4.*pi());<br />
<br />
tile_x := CAST(trunc(tile_n*x + .5) AS int);<br />
tile_y := CAST(trunc(tile_n*y + .5) AS int);<br />
END; $$ LANGUAGE plpgsql;<br />
</syntaxhighlight><br />
<br />
<br />
Для вычисления плотности узлов сначала создадим временную таблицу, в которой будет содержаться информация о координатах тайла, в который попадает каждая точка:<br />
<br />
<syntaxhighlight lang="plsql"><br />
CREATE TEMP TABLE planet_osm_point_dump AS<br />
SELECT id, type, (coord).tile_x, (coord).tile_y, geom<br />
FROM<br />
(SELECT<br />
row_number() OVER (ORDER BY type, id, (dump).path) AS id,<br />
type,<br />
TileCoord(12, (dump).geom) AS coord,<br />
(dump).geom AS geom<br />
FROM<br />
(SELECT 0 AS type, osm_id AS id, ST_DumpPoints(way) AS dump<br />
FROM planet_osm_point<br />
UNION ALL<br />
SELECT 1 AS type, osm_id AS id, ST_DumpPoints(way) AS dump<br />
FROM planet_osm_line<br />
UNION ALL<br />
SELECT 2 AS type, osm_id AS id, ST_DumpPoints(way) AS dump<br />
FROM planet_osm_polygon<br />
) AS point<br />
) AS point_tiled;<br />
</syntaxhighlight><br />
<br />
<br />
и затем посчитаем число узлов в каждом тайле нашей AOI:<br />
<br />
<syntaxhighlight lang="plsql"><br />
ALTER TABLE tile ADD COLUMN node_count INT DEFAULT 0;<br />
<br />
UPDATE tile t SET node_count = count.num<br />
FROM (<br />
SELECT x, y, count(*) AS num<br />
FROM planet_osm_point_dump<br />
GROUP BY x, y ORDER BY x, y) AS count<br />
WHERE <br />
t.x = count.x AND t.y = count.y;<br />
</syntaxhighlight><br />
<br />
<br />
== Малонаселенные области ==<br />
<br />
По всей видимости плотность населения выше вдоль побережья и главных рек, уменьшаясь вверх по течению реки. Мы будем моделировать '''удаленность''' используя '''гидрологическую дистанцию''', которая определяется [http://ru.wikipedia.org/wiki/Поверхностный_дренаж поверхностным дренажем]. Вычисления производятся в модуле [http://grasswiki.osgeo.org/wiki/R.stream.*#r.stream.distance r.stream.distance] системы [http://grass.osgeo.org/ GRASS GIS]. В гидрологическом моделировании обычно используется схема '''направлений потоков''', полученная из [http://en.wikipedia.org/wiki/Digital_elevation_model цифровой модели местности] (Digital Elevation Model - DEM): <br />
<br />
[[Файл:Osmmp_hydrology_map.jpeg|700|Направления гидрологических потоков]]<br />
<br />
''Направления гидрологических потоков''<br />
<br />
<br />
USGS предоставляет набор данных [http://lta.cr.usgs.gov/HYDRO1K GTOPO30 HYDRO1k], который можно скачать с сайта [http://earthexplorer.usgs.gov/ Earth Explorer] (ID GT30H1KSA для Южной Америки). Набор данных представлен в азимутальной равновеликой проекции Ламберта (LAEA), которой соответствуют следующие параметры [http://proj4.org/ PROJ.4]:<br />
<br />
<pre>+proj=laea +ellps=sphere +lon_0=-60 +lat_0=-15 +x_0=0.0 +y_0=0.0 +units=m</pre><br />
<br />
Исходя из этих параметров создадим ''location'' с использованием [http://grass.osgeo.org/grass71/manuals/g.proj.html g.proj] и ''mapset'':<br />
<br />
<syntaxhighlight lang="bash"><br />
g.proj -c proj4="+proj=laea +ellps=sphere +lon_0=-60 +lat_0=-15 +x_0=0.0 +y_0=0.0 +units=m" location=south_america<br />
g.mapset -c mapset=hydro location=south_america<br />
</syntaxhighlight><br />
<br />
<br />
Набор данных содержит несколько слоев (смотрите [http://lta.cr.usgs.gov/HYDRO1KReadMe файл README]), но нам в основном интересен импорт высот и направлений течения:<br />
<br />
<syntaxhighlight lang="bash"><br />
r.in.gdal in=gt30h1ksa/sa_dem.bil out=elev<br />
r.in.gdal -e in=gt30h1ksa/sa_fd.bil out=dir_arc<br />
</syntaxhighlight><br />
<br />
<br />
Преобразуем коды направлений в формат GRASS GIS, используя таблицу сопоставления в файле arc2grass.lut:<br />
<br />
<pre><br />
255 = 0<br />
128 = 1<br />
64 = 2<br />
32 = 3<br />
16 = 4<br />
8 = 5<br />
4 = 6<br />
2 = 7<br />
1 = 8<br />
</pre><br />
<br />
<syntaxhighlight lang="bash"><br />
r.reclass in=dir_arc out=dir rules=arc2grass.lut<br />
r.stream.distance -o dir=dir elev=elev method=up dist=dir_dist<br />
</syntaxhighlight><br />
<br />
<br /><br />
<br />
[[Файл:Osmmp hydrology distance map.jpeg|700|Расстояние до русла]]<br />
<br />
''Расстояние до русла''<br />
<br />
<br />
Для того, чтобы назначить расстояния тайлам, мы перепроецируем сетку из проекции LAEA в проекцию Web Mercator. На 15 уровне длина стороны тайла примерно равна 1223 метрам, что наиболее близко к разрешению нашей сетки расстояний.<br />
Сначала создадим location в этой проекции и перейдем к данной location:<br />
<br />
<syntaxhighlight lang="bash"><br />
g.proj -c epsg=3857 location=mercator<br />
g.mapset -c mapset=priority location=mercator<br />
</syntaxhighlight><br />
<br />
<br />
Затем создадим region на 15-м уровне и перепроецируем:<br />
<br />
<syntaxhighlight lang="bash"><br />
g.region w=-20037508.342789244 s=-20037508.342789244 e=20037508.342789244 n=20037508.342789244 rows=32768 cols=32768<br />
r.proj in=dir_dist loc=south_america mapset=hydro out=dir_dist_1k meth=near<br />
</syntaxhighlight><br />
<br />
<br />
Теперь установим region-у разрешение тайлов и выполним ресамплинг сетки:<br />
<br />
<syntaxhighlight lang="bash"><br />
g.region w=-20037508.3427892 s=-20037508.3430388 e=20037508.3427892 n=20037508.3430388 rows=4096 cols=4096<br />
r.resamp.stats in=dir_dist_1k out=dir_dist meth=median<br />
</syntaxhighlight><br />
<br />
<br />
== Экспорт ==<br />
<br />
В конечном итоге мы получили данные, которые можем экспортировать в растровый файл:<br />
<br />
<syntaxhighlight lang="bash"><br />
r.mapcalc "dir_dist = if(isnull(dir_dist), -1, int(dir_dist))"<br />
r.out.gdal -t in=dir_dist out=hydro_dist.tif type=UInt32 nodata=4294967295<br />
</syntaxhighlight><br />
<br />
<br />
или в текст (перед тем как импортировать в PostGIS):<br />
<br />
<syntaxhighlight lang="bash"><br />
r.mapcalc "y = row() - 1"<br />
r.mapcalc "x = col() - 1"<br />
r.out.xyz in=y sep=space out=hydro_dist-y.xyz<br />
r.out.xyz in=x sep=space out=hydro_dist-x.xyz<br />
r.out.xyz in=dir_dist sep=space out=hydro_dist.xyz<br />
</syntaxhighlight><br />
<br /><br />
<syntaxhighlight lang="bash"><br />
paste hydro_dist-x.xyz hydro_dist-y.xyz hydro_dist.xyz | awk '!/-1$/ {print $3, $6, $9}' > hydro_dist.ssv<br />
</syntaxhighlight><br />
<br />
<br />
Обратите внимание, что NULL или MASK ячейки не будут экспортированы (см. [http://grass.osgeo.org/grass71/manuals/r.out.xyz.html r.out.xyz documentation]) что может привести процедуру вставки к неожиданному результату.<br />
<br />
<syntaxhighlight lang="plsql"><br />
CREATE TEMP TABLE tile_attr (<br />
z INTEGER DEFAULT 12, x INTEGER, y INTEGER,<br />
value REAL);<br />
</syntaxhighlight><br />
<br /><br />
<syntaxhighlight lang="plsql"><br />
COPY tile_attr (x, y, value) FROM 'hydro_dist.ssv' CSV DELIMITER ' ' ;<br />
UPDATE tile t SET hydro_dist = a.value<br />
FROM tile_attr a<br />
WHERE t.level = a.z AND t.x = a.x AND t.y = a.y ;<br />
</syntaxhighlight><br />
<br />
<br />
== Расстояние до AOI ==<br />
<br />
Чтобы увеличить приоритет тайлов, которые находятся в центре нашей AOI, построим сетку весов. Вес в данном случае - это расстояние по прямой до границ AOI.<br />
<br />
Используя location в проекции Web Mercator с region на уровне 12 импортируйте (используя [http://grass.osgeo.org/grass71/manuals/v.proj.html v.proj] при необходимости) и растеризуйте вашу AOI:<br />
<br />
<syntaxhighlight lang="bash"><br />
v.to.rast in=aoi type=line,area out=aoi use=val value=1<br />
</syntaxhighlight><br />
<br />
<br />
Рассчитаем расстояние до AOI для внешних ячеек:<br />
<br />
<syntaxhighlight lang="bash"><br />
r.grow.distance in=aoi dist=dist_outer_aoi<br />
</syntaxhighlight><br />
<br />
<br />
Выполним то же самое для внутренних ячеек:<br />
<br />
<syntaxhighlight lang="bash"><br />
r.mapcalc "aoi_inv = if(isnull(aoi), 1, null())"<br />
r.grow.distance in=aoi_inv dist=dist_inner_aoi<br />
</syntaxhighlight><br />
<br />
<br />
Комбинируем сетки расстояний в инвертированных значениях для внутренних тайлов:<br />
<br />
<syntaxhighlight lang="bash"><br />
r.mapcalc "dist_aoi = if(isnull(aoi), dist_outer_aoi, -dist_inner_aoi)"<br />
</syntaxhighlight><br />
<br />
<br />
В случае использования нескольких AOI построим mask, сдвинем и масштабируем значения к диапазону [0, 1]:<br />
<br />
<syntaxhighlight lang="bash"><br />
r.mapcalc "roi = aoi_1 ||| aoi_2"<br />
r.grow in=roi out=roi old=1 new=1<br />
r.mask roi<br />
eval $(r.univar -g dist_aoi | grep "\(min\|max\)")<br />
r.mapcalc "dist_aoi = (dist_aoi - $min)/($max - $min)" --o<br />
</syntaxhighlight><br />
<br />
<br />
и вычислим средневзвешенное значение для каждого расстояния:<br />
<br />
<syntaxhighlight lang="bash"><br />
r.mapcalc "aois_dist = (dist_aoi_1 + dist_aoi_2)/2."<br />
</syntaxhighlight><br />
<br />
<br />
[[Файл:Osmmp weighted distance map.jpeg|700|Взвешенное расстояние до центра AOI]]<br />
<br />
''Взвешенное расстояние до центра AOI''<br />
<br />
<br />
== Разрешение снимков ==<br />
<br />
Информация о разрешении снимков может быть получена, например, как в инструменте [http://mvexel.dev.openstreetmap.org/bingimageanalyzer/?lat=0.882&lon=-71.132&zoom=8 анализа покрытия спутниковых снимков Bing] (смотрите статью [http://wiki.openstreetmap.org/wiki/Bing_Maps/Coverage Coverage] на OSM Wiki).<br />
<br />
<br />
Ниже пример на Python:<br />
<br />
<syntaxhighlight lang="python"><br />
PATTERN = "http://t{srv}.domain.tld/tile/{key}.jpg"<br />
HEADER = "Content-Length"; server = randint(0, 7)<br />
url = PATTERN.format(srv=server, key=quadkey)<br />
meta = urlopen(url).info()<br />
size = meta.getheaders(HEADER)[0]<br />
</syntaxhighlight><br />
<br />
<br />
и функция PostgreSQL, вычисляющая индекс [http://wiki.openstreetmap.org/wiki/QuadTiles quadkey] для заданного уровня (zoom level) и координат тайла (x, y):<br />
<br />
<syntaxhighlight lang="plsql"><br />
CREATE FUNCTION QuadKey(z int, x int, y int, OUT quadkey character varying(16)) AS $$<br />
DECLARE digit int; mask int; BEGIN<br />
quadkey := '';<br />
<br />
FOR i IN REVERSE z..1 LOOP<br />
digit := 0;<br />
mask := 1 << (i - 1);<br />
<br />
IF (x & mask) != 0 THEN digit := digit + 1; END IF;<br />
IF (y & mask) != 0 THEN digit := digit + 2; END IF;<br />
<br />
quadkey := quadkey || digit;<br />
END LOOP;<br />
END; $$ LANGUAGE plpgsql;<br />
</syntaxhighlight><br />
<br />
<br />
В областях с низким разрешением снимков тайлы Bing на высоких уровнях (zoom level) в основном отсутствуют. Давайте оценим качество покрытия снимками высокого разрешения путем подсчета ненулевых тайлов на 14 уровне:<br />
<br />
<syntaxhighlight lang="plsql"><br />
SELECT<br />
substring(quadkey for 12) AS key,<br />
count(quadkey)/16 AS resolution_index<br />
FROM tile_bing WHERE z = 14 AND size > 0<br />
GROUP BY substring(quadkey for 12) ORDER BY key ;<br />
</syntaxhighlight><br />
<br />
<br />
[[Файл:Osmmp bing resolution map.jpeg|700|Разрешение спутниковых снимков Bing]]<br />
<br />
''Разрешение спутниковых снимков Bing''<br />
<br />
<br />
== Создание индекса приоритетности ==<br />
<br />
Используя полученные ранее измерения для тайлов мы можем создать индекс приоритетности в соответствии с нашими пожеланиями. Например формула:<br />
<br />
<pre>priority = ((1 - density) + dist_hydro + (1 - dist_aoi) + (1 - resolution))/4</pre><br />
<br />
<br />
устанавливает высокое значение приоритета для тайлов, которые не содержат узлов, расположены недалеко от береговой линии но довольно близко к AOI и покрыты снимками в низком разрашении.<br />
<br />
Для того, чтобы использовать схему приоритетов в [http://qgis.org/ QGIS], добавьте столбец status в таблицу tile и создайте в плагине [http://docs.qgis.org/2.0/en/docs/training_manual/databases/db_manager.html DB Manager] динамически обновляемый слой TODO на основе SQL запроса: <br />
<br />
<syntaxhighlight lang="plsql"><br />
SELECT<br />
row_number() OVER (ORDER BY priority DESC) AS id,<br />
priority, x, y, ST_ExteriorRing(geom) AS geom<br />
FROM tile WHERE status <> 'DONE'<br />
ORDER BY priority DESC<br />
LIMIT 32 ;<br />
</syntaxhighlight><br />
<br />
<br />
Данный слой показывает следующие 32 тайла задач для отрисовки:<br />
<br />
<br />
[[Файл:Osmmp high priority tiles.jpeg|700|Тайлы задач с наивысшим приоритетом]]<br />
<br />
''Тайлы задач с наивысшим приоритетом''<br />
<br />
<br />
Перед тем, как начать наносить объекты в [http://wiki.openstreetmap.org/wiki/JOSM JOSM], либо сохраните выбранный тайл в GPX (отметьте опцию FORCE_GPX_TRACK и необходимость использования [http://postgis.net/docs/ST_ExteriorRing.html ST_ExteriorRing]), или вызовите GDAL из [http://docs.qgis.org/2.8/en/docs/training_manual/create_vector_data/actions.html layer action]:<br />
<br />
<syntaxhighlight lang="bash"><br />
ogr2ogr -f GPX -sql "SELECT x || ' ' || y AS name, ST_ExteriorRing(geom) FROM tile WHERE x = '[% "x" %]' AND y = '[% "y" %]'" [% "x" %]_[% "y" %].gpx PG:dbname=osm -lco FORCE_GPX_TRACK=YES -nlt LINESTRING<br />
</syntaxhighlight><br />
<br />
<br />
Когда все интересующие Вас объекты, входящие в границы тайла задания, добавлены - обновите атрибут его статуса в базе данных (status = DONE) и обновите экран QGIS (нажав клавишу F5).<br />
<br />
Используя данную схему приоритетов я замечал, что иногда [http://wiki.openstreetmap.org/wiki/DigitalGlobe снимки Mapbox] в более высоком разрешении, чем снимки Bing. Было бы полезно выяснить, предоставляют ли они схему разрешения снимков. Кроме того, две плитки последовательного ранга могут быть пространственно сильно удалены друг от друга и было бы интересно найти способ их использования кластерами. Наконец, не следует ли включить приоритет непосредственно в Менеджер задач OSM?<br />
<br />
<br />
Успешного Вам мапинга!<br />
<br />
<br />
== Источник == <br />
<br />
Автор статьи: [http://www.openstreetmap.org/user/adrienandrem adrienandrem]<br />
<br />
Автор перевода: [http://www.openstreetmap.org/user/Sibri Sibri]</div>Александр Мурыйhttps://wiki.gis-lab.info/index.php?title=%D0%9F%D1%80%D0%B8%D0%BE%D1%80%D0%B8%D1%82%D0%B5%D1%82_%D0%BE%D1%82%D1%80%D0%B8%D1%81%D0%BE%D0%B2%D0%BA%D0%B8_%D0%B2_OpenStreetMap&diff=25414Приоритет отрисовки в OpenStreetMap2017-06-17T07:33:52Z<p>Александр Мурый: /* Источник */</p>
<hr />
<div>{{Статья|Черновик}}<br />
<br />
{{Аннотация|Перевод статьи, в которой вычисляют, при помощи GRASS GIS и PostGIS, плохо отрисованные малонаселенные области в проекте OpenStreetMap}}<br />
<br />
== Приоритет отрисовки в OpenStreetMap ==<br />
<br />
[[Файл:Osmmp_mapping_priority_map.jpeg|700|Приоритет отрисовки в OpenStreetMap]]<br />
<br />
''Приоритет отрисовки в OpenStreetMap''<br />
<br />
<br />
Работая в Французской Гвиане я узнал о проекте [http://mapazonia.org/ Mapazonia], целью которого является улучшение данных [http://www.openstreetmap.org/ OpenStreetMap] в регионе [http://wiki.openstreetmap.org/wiki/File:Amazonrivermap.svg Амазонии]. С начала участия в проекте я научился использовать [http://tasks.hotosm.org/?sort_by=priority&direction=asc&search=mapazonia Менеджер задач OSM] - инструмент, который позволяет разбить AOI (Areas Of Interest - область интереса) на отдельные плитки задач. Будучи заинтересованным в отрисовке [http://ru.wikipedia.org/wiki/Гвианское_плоскогорье Гвианского плоскогорья] я задался вопросом: смогу ли я использовать подобный подход для этой области?<br />
<br />
Как только плитки задач были созданы (их насчиталось тысячи для такой большой области) возник вопрос: '''где наносить объекты в первую очередь'''?<br />
<br />
Чтобы ответить на этот вопрос, нужно учесть несколько подходов: <br />
<br />
* мы должны найти не отрисованные объекты там, где '''нет данных'''; <br />
* пустые тайлы в удаленных областях, где живет немного людей, имеют меньший приоритет; <br />
* там, где исходные данные ('''спутниковые снимки''') '''в низком разрешении''', только объекты, покрывающие большие площади, могут быть оцифрованы (так называемый ''[http://en.wikipedia.org/wiki/Scale_(map)#Large_scale.2C_medium_scale.2C_small_scale small scale]'' маппинг); <br />
* вносить данные нужно в первую очередь в '''центре AOI'''. <br />
<br />
А теперь давайте построим '''схему приоритета''' тайлов задач, используя эти идеи!<br />
<br />
== Исходные данные ==<br />
<br />
Начнем с получения исходных данных. В итоге мы должны получить схему, похожую на [http://www.openstreetmap.org/user/tyr_asd/diary/22363 работу Мартина Райфера (Martin Raifer) по плотности узлов]. Данные OSM доступны на сайте [http://download.geofabrik.de/south-america-latest.osm.bz2 Geofabrik] и мы можем импортировать их в базу данных [http://postgis.net/ PostGIS] следуя инструкциям из [http://www.bostongis.com/?content_name=loading_osm_postgis#229 руководства Регины Обе (Regina Obe)]. Тайлы задач соответствуют [http://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#Zoom_levels 12 уровню] тайлов в [http://wiki.openstreetmap.org/wiki/EPSG:3857 проекции веб-картографии].<br />
<br />
Создадим таблицу тайлов:<br />
<br />
<syntaxhighlight lang="plsql"><br />
CREATE TABLE tile AS SELECT<br />
x, y<br />
FROM<br />
generate_series(0, pow(2, 12)::INT - 1) AS x,<br />
generate_series(0, pow(2, 12)::INT - 1) AS y ;<br />
COMMENT ON TABLE tile IS 'Map tiles' ;<br />
COMMENT ON COLUMN tile.x IS 'Tile coordinate' ;<br />
COMMENT ON COLUMN tile.y IS 'Tile coordinate' ;<br />
</syntaxhighlight><br />
<br />
<br />
и дополнительно построим их геометрии (таким образом мы будем использовать векторную и растровую модель представления данных):<br />
<br />
<syntaxhighlight lang="plsql"><br />
CREATE FUNCTION ST_GeomFromTileXY(z int, x int, y int, OUT tile_geom geometry(Polygon, 4326)) AS $$<br />
DECLARE<br />
side real;<br />
xmin real; ymin real; xmax real; ymax real;<br />
minlon real; maxlon real; maxlat real; minlat real;<br />
BEGIN<br />
side := pow(2, z);<br />
<br />
xmin := x::real/side - .5;<br />
ymin := .5 - y::real/side;<br />
xmax := (x + 1)::real/side - .5;<br />
ymax := .5 - (y + 1)::real/side;<br />
<br />
minlon := 360.*xmin;<br />
maxlon := 360.*xmax;<br />
maxlat := 90. - 360.*atan(exp(-ymin*2.*pi()))/pi();<br />
minlat := 90. - 360.*atan(exp(-ymax*2.*pi()))/pi();<br />
<br />
tile_geom := ST_MakeEnvelope(minlon, minlat, maxlon, maxlat, 4326);<br />
END; $$ LANGUAGE plpgsql;<br />
</syntaxhighlight><br />
<br />
<br />
Функция для получения x и y координат тайла для заданной точки:<br />
<br />
<syntaxhighlight lang="plsql"><br />
CREATE FUNCTION TileCoord(z int, point geometry(Point, 4326),<br />
OUT tile_x int, OUT tile_y int) AS $$<br />
DECLARE<br />
tile_n real;<br />
sinphi real; x real; y real;<br />
BEGIN<br />
tile_n := power(2, z)::real;<br />
<br />
x := (ST_X(point) + 180.)/360.;<br />
sinphi = sin(ST_Y(point)*pi()/180.);<br />
y := .5 - ln((1. + sinphi)/(1. - sinphi))/(4.*pi());<br />
<br />
tile_x := CAST(trunc(tile_n*x + .5) AS int);<br />
tile_y := CAST(trunc(tile_n*y + .5) AS int);<br />
END; $$ LANGUAGE plpgsql;<br />
</syntaxhighlight><br />
<br />
<br />
Для вычисления плотности узлов сначала создадим временную таблицу, в которой будет содержаться информация о координатах тайла, в который попадает каждая точка:<br />
<br />
<syntaxhighlight lang="plsql"><br />
CREATE TEMP TABLE planet_osm_point_dump AS<br />
SELECT id, type, (coord).tile_x, (coord).tile_y, geom<br />
FROM<br />
(SELECT<br />
row_number() OVER (ORDER BY type, id, (dump).path) AS id,<br />
type,<br />
TileCoord(12, (dump).geom) AS coord,<br />
(dump).geom AS geom<br />
FROM<br />
(SELECT 0 AS type, osm_id AS id, ST_DumpPoints(way) AS dump<br />
FROM planet_osm_point<br />
UNION ALL<br />
SELECT 1 AS type, osm_id AS id, ST_DumpPoints(way) AS dump<br />
FROM planet_osm_line<br />
UNION ALL<br />
SELECT 2 AS type, osm_id AS id, ST_DumpPoints(way) AS dump<br />
FROM planet_osm_polygon<br />
) AS point<br />
) AS point_tiled;<br />
</syntaxhighlight><br />
<br />
<br />
и затем посчитаем число узлов в каждом тайле нашей AOI:<br />
<br />
<syntaxhighlight lang="plsql"><br />
ALTER TABLE tile ADD COLUMN node_count INT DEFAULT 0;<br />
<br />
UPDATE tile t SET node_count = count.num<br />
FROM (<br />
SELECT x, y, count(*) AS num<br />
FROM planet_osm_point_dump<br />
GROUP BY x, y ORDER BY x, y) AS count<br />
WHERE <br />
t.x = count.x AND t.y = count.y;<br />
</syntaxhighlight><br />
<br />
<br />
== Малонаселенные области ==<br />
<br />
По всей видимости плотность населения выше вдоль побережья и главных рек, уменьшаясь вверх по течению реки. Мы будем моделировать '''удаленность''' используя '''гидрологическую дистанцию''', которая определяется [http://ru.wikipedia.org/wiki/Поверхностный_дренаж поверхностным дренажем]. Вычисления производятся в модуле [http://grasswiki.osgeo.org/wiki/R.stream.*#r.stream.distance r.stream.distance] системы [http://grass.osgeo.org/ GRASS GIS]. В гидрологическом моделировании обычно используется схема '''направлений потоков''', полученная из [http://en.wikipedia.org/wiki/Digital_elevation_model цифровой модели местности] (Digital Elevation Model - DEM): <br />
<br />
[[Файл:Osmmp_hydrology_map.jpeg|700|Направления гидрологических потоков]]<br />
<br />
''Направления гидрологических потоков''<br />
<br />
<br />
USGS предоставляет набор данных [http://lta.cr.usgs.gov/HYDRO1K GTOPO30 HYDRO1k], который можно скачать с сайта [http://earthexplorer.usgs.gov/ Earth Explorer] (ID GT30H1KSA для Южной Америки). Набор данных представлен в азимутальной равновеликой проекции Ламберта (LAEA), которой соответствуют следующие параметры [http://proj4.org/ PROJ.4]:<br />
<br />
<pre>+proj=laea +ellps=sphere +lon_0=-60 +lat_0=-15 +x_0=0.0 +y_0=0.0 +units=m</pre><br />
<br />
Исходя из этих параметров создадим ''location'' с использованием [http://grass.osgeo.org/grass71/manuals/g.proj.html g.proj] и ''mapset'':<br />
<br />
<syntaxhighlight lang="bash"><br />
g.proj -c proj4="+proj=laea +ellps=sphere +lon_0=-60 +lat_0=-15 +x_0=0.0 +y_0=0.0 +units=m" location=south_america<br />
g.mapset -c mapset=hydro location=south_america<br />
</syntaxhighlight><br />
<br />
<br />
Набор данных содержит несколько слоев (смотрите [http://lta.cr.usgs.gov/HYDRO1KReadMe файл README]), но нам в основном интересен импорт высот и направлений течения:<br />
<br />
<syntaxhighlight lang="bash"><br />
r.in.gdal in=gt30h1ksa/sa_dem.bil out=elev<br />
r.in.gdal -e in=gt30h1ksa/sa_fd.bil out=dir_arc<br />
</syntaxhighlight><br />
<br />
<br />
Преобразуем коды направлений в формат GRASS GIS, используя таблицу сопоставления в файле arc2grass.lut:<br />
<br />
<pre><br />
255 = 0<br />
128 = 1<br />
64 = 2<br />
32 = 3<br />
16 = 4<br />
8 = 5<br />
4 = 6<br />
2 = 7<br />
1 = 8<br />
</pre><br />
<br />
<syntaxhighlight lang="bash"><br />
r.reclass in=dir_arc out=dir rules=arc2grass.lut<br />
r.stream.distance -o dir=dir elev=elev method=up dist=dir_dist<br />
</syntaxhighlight><br />
<br />
<br /><br />
<br />
[[Файл:Osmmp hydrology distance map.jpeg|700|Расстояние до русла]]<br />
<br />
''Расстояние до русла''<br />
<br />
<br />
Для того, чтобы назначить расстояния тайлам, мы перепроецируем сетку из проекции LAEA в проекцию Web Mercator. На 15 уровне длина стороны тайла примерно равна 1223 метрам, что наиболее близко к разрешению нашей сетки расстояний.<br />
Сначала создадим location в этой проекции и перейдем к данной location:<br />
<br />
<syntaxhighlight lang="bash"><br />
g.proj -c epsg=3857 location=mercator<br />
g.mapset -c mapset=priority location=mercator<br />
</syntaxhighlight><br />
<br />
<br />
Затем создадим region на 15-м уровне и перепроецируем:<br />
<br />
<syntaxhighlight lang="bash"><br />
g.region w=-20037508.342789244 s=-20037508.342789244 e=20037508.342789244 n=20037508.342789244 rows=32768 cols=32768<br />
r.proj in=dir_dist loc=south_america mapset=hydro out=dir_dist_1k meth=near<br />
</syntaxhighlight><br />
<br />
<br />
Теперь установим region-у разрешение тайлов и выполним ресамплинг сетки:<br />
<br />
<syntaxhighlight lang="bash"><br />
g.region w=-20037508.3427892 s=-20037508.3430388 e=20037508.3427892 n=20037508.3430388 rows=4096 cols=4096<br />
r.resamp.stats in=dir_dist_1k out=dir_dist meth=median<br />
</syntaxhighlight><br />
<br />
<br />
== Экспорт ==<br />
<br />
В конечном итоге мы получили данные, которые можем экспортировать в растровый файл:<br />
<br />
<syntaxhighlight lang="bash"><br />
r.mapcalc "dir_dist = if(isnull(dir_dist), -1, int(dir_dist))"<br />
r.out.gdal -t in=dir_dist out=hydro_dist.tif type=UInt32 nodata=4294967295<br />
</syntaxhighlight><br />
<br />
<br />
или в текст (перед тем как импортировать в PostGIS):<br />
<br />
<syntaxhighlight lang="bash"><br />
r.mapcalc "y = row() - 1"<br />
r.mapcalc "x = col() - 1"<br />
r.out.xyz in=y sep=space out=hydro_dist-y.xyz<br />
r.out.xyz in=x sep=space out=hydro_dist-x.xyz<br />
r.out.xyz in=dir_dist sep=space out=hydro_dist.xyz<br />
</syntaxhighlight><br />
<br /><br />
<syntaxhighlight lang="bash"><br />
paste hydro_dist-x.xyz hydro_dist-y.xyz hydro_dist.xyz | awk '!/-1$/ {print $3, $6, $9}' > hydro_dist.ssv<br />
</syntaxhighlight><br />
<br />
<br />
Обратите внимание, что NULL или MASK ячейки не будут экспортированы (см. [http://grass.osgeo.org/grass71/manuals/r.out.xyz.html r.out.xyz documentation]) что может привести процедуру вставки к неожиданному результату.<br />
<br />
<syntaxhighlight lang="plsql"><br />
CREATE TEMP TABLE tile_attr (<br />
z INTEGER DEFAULT 12, x INTEGER, y INTEGER,<br />
value REAL);<br />
</syntaxhighlight><br />
<br /><br />
<syntaxhighlight lang="plsql"><br />
COPY tile_attr (x, y, value) FROM 'hydro_dist.ssv' CSV DELIMITER ' ' ;<br />
UPDATE tile t SET hydro_dist = a.value<br />
FROM tile_attr a<br />
WHERE t.level = a.z AND t.x = a.x AND t.y = a.y ;<br />
</syntaxhighlight><br />
<br />
<br />
== Расстояние до AOI ==<br />
<br />
Чтобы увеличить приоритет тайлов, которые находятся в центре нашей AOI, построим сетку весов. Вес в данном случае - это расстояние по прямой до границ AOI.<br />
<br />
Используя location в проекции Web Mercator с region на уровне 12 импортируйте (используя [http://grass.osgeo.org/grass71/manuals/v.proj.html v.proj] при необходимости) и растеризуйте вашу AOI:<br />
<br />
<syntaxhighlight lang="bash"><br />
v.to.rast in=aoi type=line,area out=aoi use=val value=1<br />
</syntaxhighlight><br />
<br />
<br />
Рассчитаем расстояние до AOI для внешних ячеек:<br />
<br />
<syntaxhighlight lang="bash"><br />
r.grow.distance in=aoi dist=dist_outer_aoi<br />
</syntaxhighlight><br />
<br />
<br />
Выполним то же самое для внутренних ячеек:<br />
<br />
<syntaxhighlight lang="bash"><br />
r.mapcalc "aoi_inv = if(isnull(aoi), 1, null())"<br />
r.grow.distance in=aoi_inv dist=dist_inner_aoi<br />
</syntaxhighlight><br />
<br />
<br />
Комбинируем сетки расстояний в инвертированных значениях для внутренних тайлов:<br />
<br />
<syntaxhighlight lang="bash"><br />
r.mapcalc "dist_aoi = if(isnull(aoi), dist_outer_aoi, -dist_inner_aoi)"<br />
</syntaxhighlight><br />
<br />
<br />
В случае использования нескольких AOI построим mask, сдвинем и масштабируем значения к диапазону [0, 1]:<br />
<br />
<syntaxhighlight lang="bash"><br />
r.mapcalc "roi = aoi_1 ||| aoi_2"<br />
r.grow in=roi out=roi old=1 new=1<br />
r.mask roi<br />
eval $(r.univar -g dist_aoi | grep "\(min\|max\)")<br />
r.mapcalc "dist_aoi = (dist_aoi - $min)/($max - $min)" --o<br />
</syntaxhighlight><br />
<br />
<br />
и вычислим средневзвешенное значение для каждого расстояния:<br />
<br />
<syntaxhighlight lang="bash"><br />
r.mapcalc "aois_dist = (dist_aoi_1 + dist_aoi_2)/2."<br />
</syntaxhighlight><br />
<br />
<br />
[[Файл:Osmmp weighted distance map.jpeg|700|Взвешенное расстояние до центра AOI]]<br />
<br />
''Взвешенное расстояние до центра AOI''<br />
<br />
<br />
== Разрешение снимков ==<br />
<br />
Информация о разрешении снимков может быть получена, например, как в инструменте [http://mvexel.dev.openstreetmap.org/bingimageanalyzer/?lat=0.882&lon=-71.132&zoom=8 анализа покрытия спутниковых снимков Bing] (смотрите статью [http://wiki.openstreetmap.org/wiki/Bing_Maps/Coverage Coverage] на OSM Wiki).<br />
<br />
<br />
Ниже пример на Python:<br />
<br />
<syntaxhighlight lang="python"><br />
PATTERN = "http://t{srv}.domain.tld/tile/{key}.jpg"<br />
HEADER = "Content-Length"; server = randint(0, 7)<br />
url = PATTERN.format(srv=server, key=quadkey)<br />
meta = urlopen(url).info()<br />
size = meta.getheaders(HEADER)[0]<br />
</syntaxhighlight><br />
<br />
<br />
и функция PostgreSQL, вычисляющая индекс [http://wiki.openstreetmap.org/wiki/QuadTiles quadkey] для заданного уровня (zoom level) и координат тайла (x, y):<br />
<br />
<syntaxhighlight lang="plsql"><br />
CREATE FUNCTION QuadKey(z int, x int, y int, OUT quadkey character varying(16)) AS $$<br />
DECLARE digit int; mask int; BEGIN<br />
quadkey := '';<br />
<br />
FOR i IN REVERSE z..1 LOOP<br />
digit := 0;<br />
mask := 1 << (i - 1);<br />
<br />
IF (x & mask) != 0 THEN digit := digit + 1; END IF;<br />
IF (y & mask) != 0 THEN digit := digit + 2; END IF;<br />
<br />
quadkey := quadkey || digit;<br />
END LOOP;<br />
END; $$ LANGUAGE plpgsql;<br />
</syntaxhighlight><br />
<br />
<br />
В областях с низким разрешением снимков тайлы Bing на высоких уровнях (zoom level) в основном отсутствуют. Давайте оценим качество покрытия снимками высокого разрешения путем подсчета ненулевых тайлов на 14 уровне:<br />
<br />
<syntaxhighlight lang="plsql"><br />
SELECT<br />
substring(quadkey for 12) AS key,<br />
count(quadkey)/16 AS resolution_index<br />
FROM tile_bing WHERE z = 14 AND size > 0<br />
GROUP BY substring(quadkey for 12) ORDER BY key ;<br />
</syntaxhighlight><br />
<br />
<br />
[[Файл:Osmmp bing resolution map.jpeg|700|Разрешение спутниковых снимков Bing]]<br />
<br />
''Разрешение спутниковых снимков Bing''<br />
<br />
<br />
== Создание индекса приоритетности ==<br />
<br />
Используя полученные ранее измерения для тайлов мы можем создать индекс приоритетности в соответствии с нашими пожеланиями. Например формула:<br />
<br />
<pre>priority = ((1 - density) + dist_hydro + (1 - dist_aoi) + (1 - resolution))/4</pre><br />
<br />
<br />
устанавливает высокое значение приоритета для тайлов, которые не содержат узлов, расположены недалеко от береговой линии но довольно близко к AOI и покрыты снимками в низком разрашении.<br />
<br />
Для того, чтобы использовать схему приоритетов в [http://qgis.org/ QGIS], добавьте столбец status в таблицу tile и создайте в плагине [http://docs.qgis.org/2.0/en/docs/training_manual/databases/db_manager.html DB Manager] динамически обновляемый слой TODO на основе SQL запроса: <br />
<br />
<syntaxhighlight lang="plsql"><br />
SELECT<br />
row_number() OVER (ORDER BY priority DESC) AS id,<br />
priority, x, y, ST_ExteriorRing(geom) AS geom<br />
FROM tile WHERE status <> 'DONE'<br />
ORDER BY priority DESC<br />
LIMIT 32 ;<br />
</syntaxhighlight><br />
<br />
<br />
Данный слой показывает следующие 32 тайла задач для отрисовки:<br />
<br />
<br />
[[Файл:Osmmp high priority tiles.jpeg|700|Тайлы задач с наивысшим приоритетом]]<br />
<br />
''Тайлы задач с наивысшим приоритетом''<br />
<br />
<br />
Перед тем, как начать наносить объекты в [http://wiki.openstreetmap.org/wiki/JOSM JOSM], либо сохраните выбранный тайл в GPX (отметьте опцию FORCE_GPX_TRACK и необходимость использования [http://postgis.net/docs/ST_ExteriorRing.html ST_ExteriorRing]), или вызовите GDAL из [http://docs.qgis.org/2.8/en/docs/training_manual/create_vector_data/actions.html layer action]:<br />
<br />
<syntaxhighlight lang="bash"><br />
ogr2ogr -f GPX -sql "SELECT x || ' ' || y AS name, ST_ExteriorRing(geom) FROM tile WHERE x = '[% "x" %]' AND y = '[% "y" %]'" [% "x" %]_[% "y" %].gpx PG:dbname=osm -lco FORCE_GPX_TRACK=YES -nlt LINESTRING<br />
</syntaxhighlight><br />
<br />
<br />
Когда все интересующие Вас объекты, входящие в границы тайла задания, добавлены - обновите атрибут его статуса в базе данных (status = DONE) и обновите экран QGIS (нажав клавишу F5).<br />
<br />
Используя данную схему приоритетов я замечал, что иногда [http://wiki.openstreetmap.org/wiki/DigitalGlobe снимки Mapbox] в более высоком разрешении, чем снимки Bing. Было бы полезно выяснить, предоставляют ли они схему разрешения снимков. Кроме того, две плитки последовательного ранга могут быть пространственно сильно удалены друг от друга и было бы интересно найти способ их использования кластерами. Наконец, не следует ли включить приоритет непосредственно в Менеджер задач OSM?<br />
<br />
<br />
Успешного Вам мапинга!<br />
<br />
<br />
== Источник == <br />
<br />
Автор статьи: [http://www.openstreetmap.org/user/adrienandrem adrienandrem]<br />
<br />
Автор перевода: [http://www.openstreetmap.org/user/Sibri Sibri]</div>Александр Мурыйhttps://wiki.gis-lab.info/index.php?title=%D0%97%D0%B0%D1%8F%D0%B2%D0%BA%D0%B8_%D0%BD%D0%B0_%D0%BF%D1%83%D0%B1%D0%BB%D0%B8%D0%BA%D0%B0%D1%86%D0%B8%D1%8E_%D1%81%D1%82%D0%B0%D1%82%D0%B5%D0%B9&diff=25290Заявки на публикацию статей2017-04-15T18:36:11Z<p>Александр Мурый: </p>
<hr />
<div>{{Организационное|articles-request}}<br />
<br />
Внизу вы видите таблицу заявок на публикацию статей на GIS-Lab, она показывает статьи, которые их авторы считают более-менее готовыми к публикации. Таблица создана, чтобы сделать процесс принятия статей более прозрачным для авторов и оптимизировать работу редакторов. <br />
<br />
Таблица будет обновляться ведущим по мере поступления заявок от авторов. Заявкой на публикацию считается:<br />
* если статья оформляется на вики GIS-Lab — создание автором темы на форуме для обсуждения статьи (в разделе [http://gis-lab.info/forum/viewforum.php?f=3 Материалы сайта]);<br />
* если статья оформляется НЕ на вики GIS-Lab — личное обращение автора к [http://gis-lab.info/qa/org.html редактору] с предоставлением текста статьи.<br />
<br />
<br />
{| class="wikitable sortable" <br />
|-<br />
! width="5%" | Дата поступления заявки<br />
! width="30%" | Ссылка на вики, название <br />
! width="25%" | Автор (имя / ID)<br />
! width="25%" | Тема обсуждения на форуме (номер / название)<br />
! width="10%" | Дата создания<br />
<br />
|-<br />
| 14.04.2017<br />
| [http://wiki.gis-lab.info/w/%D0%9F%D1%80%D0%B8%D0%BE%D1%80%D0%B8%D1%82%D0%B5%D1%82_%D0%BE%D1%82%D1%80%D0%B8%D1%81%D0%BE%D0%B2%D0%BA%D0%B8_%D0%B2_OpenStreetMap Приоритет отрисовки в OpenStreetMap]<br />
| Xmypblu / 15530<br />
| [http://gis-lab.info/forum/viewtopic.php?t=22353 22353] / Приоритет отрисовки в OpenStreetMap<br />
| 14.03.2017<br />
<br />
<br />
<br />
<br />
[[Категория:Служебные]]</div>Александр Мурыйhttps://wiki.gis-lab.info/index.php?title=%D0%97%D0%B0%D1%8F%D0%B2%D0%BA%D0%B8_%D0%BD%D0%B0_%D0%BF%D1%83%D0%B1%D0%BB%D0%B8%D0%BA%D0%B0%D1%86%D0%B8%D1%8E_%D1%81%D1%82%D0%B0%D1%82%D0%B5%D0%B9&diff=25281Заявки на публикацию статей2017-04-12T11:11:02Z<p>Александр Мурый: </p>
<hr />
<div>{{Организационное|articles-request}}<br />
<br />
Внизу вы видите таблицу заявок на публикацию статей на GIS-Lab, она показывает статьи, которые их авторы считают более-менее готовыми к публикации. Таблица создана, чтобы сделать процесс принятия статей более прозрачным для авторов и оптимизировать работу редакторов. <br />
<br />
Таблица будет обновляться ведущим по мере поступления заявок от авторов. Заявкой на публикацию считается:<br />
* если статья оформляется на вики GIS-Lab — создание автором темы на форуме для обсуждения статьи (в разделе [http://gis-lab.info/forum/viewforum.php?f=3 Материалы сайта]);<br />
* если статья оформляется НЕ на вики GIS-Lab — личное обращение автора к [http://gis-lab.info/qa/org.html редактору] с предоставлением текста статьи.<br />
<br />
<br />
{| class="wikitable sortable" <br />
|-<br />
! width="5%" | Дата поступления заявки<br />
! width="30%" | Ссылка на вики, название <br />
! width="25%" | Автор (имя / ID)<br />
! width="25%" | Тема обсуждения на форуме (номер / название)<br />
! width="10%" | Дата создания<br />
<br />
<br />
<br />
<br />
<br />
<br />
[[Категория:Служебные]]</div>Александр Мурыйhttps://wiki.gis-lab.info/index.php?title=%D0%97%D0%B0%D0%BA%D0%BE%D0%BD%D0%BE%D0%B4%D0%B0%D1%82%D0%B5%D0%BB%D1%8C%D1%81%D1%82%D0%B2%D0%BE_%D0%B8_%D0%BD%D0%BE%D1%80%D0%BC%D0%B0%D1%82%D0%B8%D0%B2%D0%BD%D1%8B%D0%B5_%D0%B4%D0%BE%D0%BA%D1%83%D0%BC%D0%B5%D0%BD%D1%82%D1%8B&diff=25229Законодательство и нормативные документы2017-03-02T16:58:55Z<p>Александр Мурый: /* Системы координат и навигация */</p>
<hr />
<div>{{Право|legislation}}<br />
<br />
В этом разделе собираются документы, позволяющие получить представление о том, что такое государственная тайна, секретность, лицензирование и т.п в областях связанных с геоинформатикой. Собираются только открытые документы, там где это возможно, даются ссылки на официальные сайты, на страницы, где эти документы расположены. Копии ГОСТов являются неофициальными.<br />
<br />
К сожалению, ситуация с этими вопросами далека от понятности, если у вас свое мнение по вопросам прав и обязанностей, пожалуйста, пишите в [http://gis-lab.info/forum/ форум GIS-Lab]. Надеемся, это поможет сделать нашу область деятельности более открытой и понятной.<br />
<br />
Некоторые документы являются первичным результатом распознавания и поэтому изобилуют ошибками. В случае для них были доступны исходные отсканированные страницы, они также добавлялись в архив. Если вы отредактировали эти документы, пожалуйста, пришлите их нам, чтобы они были обновлены и на сайте.<br />
<br />
==Общее законодательство==<br />
<br />
{| class="wikitable" width="100%"<br />
|-<br />
! width="90%" class="unsortable"|<br />
!! width="10%" class="unsortable"|<br />
<br />
|-<br />
| '''Гражданский кодекс РФ<br />'''от 30.11.1994 N 51-ФЗ (принят ГД 21.10.1994) (ред. от 27.12.2009)<br />
|[[Image:weblink.png|24px|link=http://www.consultant.ru/online/base/?req=doc;base=LAW;n=95574]]<br />
|-<br />
| '''Налоговый кодекс Российской Федерации'''<br /> часть первая от 31.07.1998 г. N 146-ФЗ и часть вторая от 5.08.2000 г. N 117-ФЗ (с изменениями и дополнениями по 29.12.2009 г. включительно).<br />
|<br />
[[Image:weblink.png|24px|link=http://www.interlaw.ru/law/docs/10800200/]]<br />
|-<br />
| '''Кодекс Российской Федерации об административных правонарушениях'''<br /> от 30.12.2001 N 195-ФЗ (принят ГД 20.12.2001)<br />
|<br />
[[Image:weblink.png|24px|link=http://www.consultant.ru/popular/koap/]]<br />
|-<br />
| '''Уголовный кодекс Российской Федерации (УК РФ)'''<br /> от 13.06.1996 N 63-ФЗ (принят ГД 24.05.1996)<br />
|<br />
[[Image:weblink.png|24px|link=http://www.consultant.ru/popular/ukrf/]]<br />
|-<br />
| '''Об информации, информационных технологиях и о защите информации'''<br /> от 27.07.2006 N 149-ФЗ (принят ГД 8.07.2006, одобрен СФ 14.07.2006).<br />
|<br />
[[Image:weblink.png|24px|link=http://www.consultant.ru/online/base/?req=doc;base=LAW;n=61798]]<br />
|-<br />
|<br />
'''Об информации, информационных технологиях и о защите информации. Статья 5. Информация как объект правовых отношений.'''<br /><br />
<br />
Информация может являться объектом публичных, гражданских<br />
и иных правовых отношений. Информация может свободно использоваться любым<br />
лицом и передаваться одним лицом другому лицу, если федеральными законами не<br />
установлены ограничения доступа к информации либо иные требования к порядку<br />
ее предоставления или распространения.<br />
|[[Image:weblink.png|24px|link=http://www.consultant.ru/online/base/?req=doc;base=LAW;n=61798]]<br />
|-<br />
| '''Градостроительный кодекс Российской Федерации '''<br /> Федеральный Закон от 29.12.2004 N 190-ФЗ (принят ГД ФС РФ 22.12.2004)<br />
|<br />
[[Image:weblink.png|24px|link=http://www.consultant.ru/popular/gskrf/]]<br />
|-<br />
|<br />
'''О наименованиях географических объектов<br />'''Федеральный Закон № 152-ФЗ от 18 декабря 1997 г.<br />
| width="80" |<br />
[[Image:weblink.png|24px|link=http://roskart.gov.ru/default.asp?cmd=008000000060000000F000000000000000000000/default.asp]]<br />
|-<br />
|<br />
'''Концепция создания и развития инфраструктуры пространственных данных Российской Федерации'''<br /> от 21.08.2006 г. N 1157-р<br /> одобрена распоряжением Правительства РФ от 21 августа 2006 г. N 1157-р<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/law/infra-geodata.zip|загрузить]] [[Image:weblink.png|24px|link=http://www.garant.ru/prime/20060829/12049036.htm|веб]]<br />
|-<br />
| '''О защите прав юридических лиц и индивидуальных предпринимателей при осуществлении государственного контроля (надзора) и муниципального контроля'''<br /> Федеральный закон Российской Федерации от 26 декабря 2008 г. N 294-ФЗ <br /><br />
|<br />
[[Image:weblink.png|24px|link=http://www.rg.ru/2008/12/30/prava-kontrol-dok.html]]<br />
|-<br />
| [[Image:new.gif|27px|new]]Об антикоррупционной экспертизе нормативных правовых актов и проектов нормативных правовых актов'''<br /> Федеральный закон Российской Федерации от 17 июля 2009 г. N 172-ФЗ<br />
|<br />
[[Image:weblink.png|24px|link=http://www.rg.ru/2009/07/22/expertiza-dok.html]]<br />
|}<br />
<br />
==Предпринимательство==<br />
<br />
{| class="wikitable" width="100%"<br />
|-<br />
! width="90%" class="unsortable"|<br />
!! width="10%" class="unsortable"|<br />
<br />
|-<br />
|<br />
'''Гражданский кодекс РФ. Статья 2. Отношения, регулируемые гражданским законодательством.'''<br /><br />
<br />
предпринимательской является самостоятельная, осуществляемая на<br />
свой риск деятельность, направленная на систематическое получение прибыли от<br />
пользования имуществом, продажи товаров, выполнения работ или оказания услуг<br />
лицами, зарегистрированными в этом качестве в установленном законом порядке.<br />
| width="80" |<br />
[[Image:weblink.png|24px|link=http://www.consultant.ru/online/base/?req=doc;base=LAW;n=95574]]<br />
|-<br />
|<br />
'''Налоговый кодекс Российской Федерации. Статья 11. Институты, понятия и термины, используемые в настоящем Кодексе.'''<br />
<br />
'''индивидуальные предприниматели''' - физические лица, зарегистрированные<br />
в установленном порядке и осуществляющие предпринимательскую деятельность без<br />
образования юридического лица, главы крестьянских (фермерских) хозяйств.<br />
Физические лица, осуществляющие предпринимательскую деятельность без образования<br />
юридического лица, но не зарегистрировавшиеся в качестве индивидуальных<br />
предпринимателей в нарушение требований гражданского законодательства<br />
Российской Федерации, при исполнении обязанностей, возложенных на них<br />
настоящим Кодексом, не вправе ссылаться на то, что они не являются<br />
индивидуальными предпринимателями;<br />
|<br />
[[Image:weblink.png|24px|link=http://www.interlaw.ru/law/docs/10800200-003.htm|веб]]<br />
|-<br />
|<br />
'''Кодекс Российской Федерации об административных правонарушениях'''. '''Статья 14.1. Осуществление предпринимательской деятельности без государственной регистрации или без специального разрешения (лицензии)'''<br /><br />
<br />
2. Осуществление предпринимательской деятельности без специального разрешения<br />
(лицензии), если такое разрешение (такая лицензия) обязательно (обязательна), -<br />
влечет наложение административного штрафа на граждан в размере от двух тысяч<br />
до двух тысяч пятисот рублей с конфискацией изготовленной продукции, орудий<br />
производства и сырья или без таковой; на должностных лиц - от четырех тысяч<br />
до пяти тысяч рублей с конфискацией изготовленной продукции, орудий<br />
производства и сырья или без таковой; на юридических лиц - от сорока тысяч<br />
до пятидесяти тысяч рублей с конфискацией изготовленной продукции, орудий<br />
производства и сырья или без таковой.<br />
|<br />
[[Image:weblink.png|24px|link=http://www.consultant.ru/popular/koap/13_15.html#p3757|веб]]<br />
|-<br />
|<br />
'''Уголовный кодекс Российской Федерации (УК РФ). Статья 171. Незаконное предпринимательство'''<br /><br />
<br />
Осуществление предпринимательской деятельности без регистрации или с<br />
нарушением правил регистрации,<br />
...<br />
либо осуществление предпринимательской деятельности без лицензии в случаях,<br />
когда такая лицензия обязательна, если это деяние причинило крупный ущерб<br />
гражданам, организациям или государству либо сопряжено с извлечением дохода<br />
в крупном размере.<br />
<br />
<br /><br />
|<br />
[[Image:weblink.png|24px|link=http://www.consultant.ru/popular/ukrf/10_31.html#p2347|веб]]<br />
|-<br />
|<br />
'''О судебной практике по делам о незаконном предпринимательстве и легализации (отмывании) денежных средств или иного имущества, приобретенных преступным путем<br />'''<br />
<br />
Постановление пленума верховного суда Российской Федерации от 18 Ноября 2004 г. N 23<br />
<br />
При решении вопроса о наличии в действиях лица признаков состава<br />
преступления, предусмотренного статьей 171 УК РФ, судам следует выяснять,<br />
соответствуют ли эти действия указанным в пункте 1 статьи 2 ГК РФ<br />
признакам предпринимательской деятельности, направленной на '''систематическое'''<br />
получение прибыли<br />
|<br />
[[Image:weblink.png|24px|link=http://www.mnogozakonov.ru/catalog/date/2004/11/18/27449/|веб]]<br />
|}<br />
<br />
==Авторское право==<br />
<br />
{| class="wikitable" width="100%"<br />
|-<br />
! width="90%" class="unsortable"|<br />
!! width="10%" class="unsortable"|<br />
<br />
|-<br />
| Постановление Правительства РФ от 3.08.2012 N 793 "О распоряжении исключительным правом Российской Федерации на результаты интеллектуальной деятельности в области геодезии и картографии" <br />
|[[Распоряжение исключительным правом РФ на геоданные, 2012|подробнее]]<br />
|-<br />
| Приказ Федерального агентства геодезии и картографии от 22 декабря 2008 г. № 105-пр "Об утверждении и введении в действие стандарта Федерального агентства геодезии и картографии "Произведения картографические. Общие требования и правила оформления знака охраны авторского права"'''<br /><br /><br />
|<br />
[[Image:weblink.png|24px|link=http://www.garant.ru/products/ipo/prime/doc/12064636/]]<br />
|-<br />
|<br />
'''Информационное письмо Руководителя Федерального агентства геодезии и картографии А.В. Бородко<br />'''от 11.08.2008<br />
<br />
Размещение в сети Интернет картографических произведений в соответствии с<br />
Частью четвертой Гражданского кодекса Российской Федерации (статья 1270)<br />
является одним из способов использования картографических произведений.<br />
| width="80" |<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/borodko-pismo_11.08.08.7z]] [[Image:weblink.png|24px|link=http://roskart.gov.ru/files/docs/pismo_11.08.08.doc]]<br />
|-<br />
|<br />
'''О размещении схем проезда в Интернете <br />'''Пресс-релиз Федеральной службы геодезии и картографии России № 11-12-3429 от 24.11.2003<br />
<br />
... использование картографического произведения или его части<br />
в любой форме и любым способом допускается только с разрешения правообладателя<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/borodko-pismo_11.08.08.7z]] [[Image:weblink.png|24px|link=http://wiki.gis-lab.info/w/Ð_ÑазмеÑении_ÑÑем_пÑоезда_в_ÐнÑеÑнеÑе,_2003]]<br />
|-<br />
|<br />
'''Гражданский кодекс РФ. Статья 1259. Объекты авторских прав '''<br /><br />
<br />
1. Объектами авторских прав являются произведения науки, литературы<br />
и искусства независимо от достоинств и назначения произведения,<br />
а также от способа его выражения:<br />
...<br />
географические, геологические и другие карты, планы, эскизы и пластические<br />
произведения, относящиеся к географии, топографии и к другим наукам;<br />
...<br />
<br />
6. Не являются объектами авторских прав:<br />
<br />
1) официальные документы государственных органов и органов местного<br />
самоуправления муниципальных образований, в том числе законы, другие<br />
нормативные акты, судебные решения, иные материалы законодательного,<br />
административного и судебного характера, официальные документы<br />
международных организаций, а также их официальные переводы;<br />
|<br />
[[Image:weblink.png|24px|link=http://www.consultant.ru/popular/gkrf4/79_2.html#p363]]<br />
|-<br />
| '''Гражданский кодекс РФ. Статья 1270'''<br /> Исключительное право на произведение. Описывает какими исключительными правами обладает правообладатель и как он может распоряжаться исключительным правом на произведение, в т.ч. картографическое.<br />
|<br />
[[Image:weblink.png|24px|link=http://www.consultant.ru/popular/gkrf4/79_2.html#p478]]<br />
|-<br />
| '''Об авторском праве и смежных правах'''<br /> Закон РФ от 09.07.1993 N 5351-1. Утратил силу согласно: Федерального закона от 18.12.2006 N 231-ФЗ. Вместо этого документа действует ч.4 ГК РФ.<br /><br />
|<br />
[[Image:weblink.png|24px|link=http://www.consultant.ru/popular/avtorpravo/]]<br />
|-<br />
|<br />
'''Бернская Конвенция по Охране Литературных и Художественных Произведений'''<br /> от 9 сентября 1886 г.<br /> Советский союз не присоединился, РФ присоединилась 13 марта 1995. [http://www.erudition.ru/referat/ref/id.45282_1.html Комментарий].<br />
|<br />
[[Image:weblink.png|24px|link=http://www.wipo.int/treaties/ru/ip/berne/berne.html]]<br />
|}<br />
<br />
==Государственная тайна и секретность==<br />
<br />
{| class="wikitable" width="100%"<br />
|-<br />
! width="90%" class="unsortable"|<br />
!! width="10%" class="unsortable"|<br />
<br />
|-<br />
|<br />
'''Федеральный Закон "О государственной тайне"<br />''' Вводится в действие постановлением ВС РФ от 21.07.93 N 5486-1 21 июля 1993 года N 5485-1 (изменения и дополнения по 15.11.2010 включительно).<br />
<br />
7. Сведения, не подлежащие отнесению к государственной тайне и засекречиванию<br />
Не подлежат отнесению к государственной тайне и засекречиванию сведения:<br />
<br />
о чрезвычайных происшествиях и катастрофах, угрожающих безопасности и<br />
здоровью граждан и их последствиях, а также о стихийных бедствиях, их<br />
официальных прогнозах и последствиях;<br />
о состоянии экологии, здравоохранения, санитарии, демографии, образования,<br />
культуры, сельского хозяйства, а также о состоянии преступности;<br />
| width="80" |<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/law-state-secret.zip|загрузить]] [[Image:weblink.png|24px|link=http://www.consultant.ru/online/base/?http://base.consultant.ru/cons/cgi/online.cgi?req=doc;base=LAW;n=106807;fld=134;dst=4294967295;from=89782-0|ссылка]]<br />
|-<br />
|<br />
'''Об утверждении перечня сведений, отнесенных к государственной тайне<br />'''Указ президента РФ от 11 февраля 2006 г. N 90<br />
<br />
60. Геопространственные сведения по территории РФ и другим районам Земли,<br />
раскрывающие результаты топографической, геодезической, картографической<br />
деятельности, имеющие важное оборонное или экономическое значение.<br />
61. Геопространственные сведения по территории РФ и другим районам Земли,<br />
раскрывающие результаты деятельности по дистанционному зондированию Земли,<br />
имеющие важное оборонное или экономическое значение.<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/state-secret-prezident-list06.zip]] [[Image:weblink.png|24px|link=http://document.kremlin.ru/doc.asp?ID=032374]]<br />
|-<br />
|<br />
'''Об утверждении перечня сведений, отнесенных к государственной тайне<br />'''Указ президента РФ от 30 ноября 1995 г. N 1203 (с изменениями от 24 января 1998 г., 6 июня, 10 сентября 2001 г., 29 мая 2002 г.)<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/state-secret-prezident-list.zip]]<br />
|-<br />
|<br />
'''Об утверждении правил отнесения сведений, составляющих государственную тайну, к различным степеням секретности<br />'''Постановление Правительства РФ от 04 сентября 1995 г. N 870<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/state-secret-rules95.zip|загрузить]] [[Image:weblink.png|24px|link=http://www.ekey.ru/lib/3/2|ссылка]]<br />
|-<br />
|<br />
'''Уголовный кодекс РФ, Глава 29, Статья 275: Государственная измена '''<br />
<br />
[http://www.labex.ru/page/kom_uk_275.html Комментарий],[http://www.az-design.ru/index.shtml?Projects&AZLibrCD&Law/CrimnLaw/UKRF97/ukrf275 Комментарий]<br />
<br />
Государственная измена, то есть шпионаж, выдача государственной тайны<br />
... иностранной организации или их представителям в проведении враждебной<br />
деятельности в ущерб внешней безопасности Российской Федерации, совершенная<br />
гражданином Российской Федерации.<br />
|<br />
[[Image:weblink.png|24px|link=http://www.consultant.ru/popular/ukrf/10_40.html#p4371|ссылка]]<br />
|-<br />
|<br />
'''Уголовный кодекс РФ, Глава 29, Статья 276: Шпионаж '''<br /><br />
<br />
Передача, а равно собирание, ... иностранной организации или их<br />
представителям сведений, составляющих государственную тайну...если эти деяния<br />
совершены иностранным гражданином или лицом без гражданства.<br />
<br />
[http://www.labex.ru/page/kom_uk_276.html Комментарий]. [http://www.az-design.ru/index.shtml?Projects&AZLibrCD&Law/CrimnLaw/UKRF97/ukrf276 Комментарий].<br />
|<br />
[[Image:weblink.png|24px|link=http://www.consultant.ru/popular/ukrf/10_40.html#p4379|ссылка]]<br />
|-<br />
|<br />
'''Уголовный кодекс РФ, Глава 29, Статья 283. Разглашение государственной тайны'''<br /><br />
<br />
Разглашение сведений, составляющих государственную тайну, лицом, которому<br />
она была доверена или стала известна по службе или работе, если эти сведения<br />
стали достоянием других лиц, при отсутствии признаков государственной измены.<br />
<br />
Цитата: [http://www.labex.ru/page/kom_uk_283.html Комментарий]<br />
|<br />
[[Image:weblink.png|24px|link=http://www.consultant.ru/popular/ukrf/10_40.html#p4486|ссылка]]<br />
|-<br />
|<br />
'''По делу о проверке конституционности статей 1 и 21 Закона Российской Федерации от 21 июля 1993 года "О государственной тайне" в связи с жалобами граждан В.М.Гурджиянца, В.Н.Синцова, В.Н.Бугрова и А.К.Никитина'''<br /> Постановление Конституционного Суда РФ от 27 марта 1996 г. N 8-П. [http://www.grani.ru/Society/Media/Freepress/m.58389.html Комментарий]<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/constitut-state-secret-27-03-1996.pdf|загрузить]] [[Image:weblink.png|24px|link=http://www.pravoteka.ru/pst/530/264810.html|ссылка]]<br />
|}<br />
<br />
==Открытое опубликование==<br />
<br />
{| class="wikitable" width="100%"<br />
|-<br />
! width="90%" class="unsortable"|<br />
!! width="10%" class="unsortable"|<br />
<br />
|-<br />
|<br />
[[Image:new.gif|27px|new]] '''Разъяснения о необходимости лицензирования и получения разрешения на открытое опубликование продукта, сделанного в OpenStreetMap.org пользователями.'''<br /> Разъяснение Юридического центра "Законный Бизнес". См. также [http://forum.openstreetmap.org/viewtopic.php?id=7600 обсуждение] на форуме OSM.<br />
<br />
Вывод: создание продукта OpenStreetMap.org не требует предварительного<br />
получения разрешений от государственных органов.<br />
<br />
| width="80" |<br />
<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/balagurov2010_openstreetmap.doc]][[Image:weblink.png|24px|link=http://forum.openstreetmap.org/viewtopic.php?id=7599ссылка]]<br />
|}<br />
<br />
==Перечни сведений, объектов, организаций==<br />
<br />
{| class="wikitable" width="100%"<br />
|<br />
'''Перечень объектов местности и элементов содержания топографических карт и планов, запрещенных для открытого опубликования.'''<br /> Утвержден Роскартографией 14.12.2000 №181пр, согл.с ГШ ВС РФ 02.11.2000 №320/2/2996<br />
|<br />
[http://gis-lab.info/docs/law/classified-objectlist-2000.html подробнее]<br />
|-<br />
|<br />
'''О внедрении Единой государственной картографической основы г. Москвы для решения задач управления городским хозяйством с использованием автоматизированных технологий.'''<br /> Постановление Правительства Москвы от 19.01.1999 N 24<br />
|<br />
[http://gis-lab.info/docs/law/implement-egko.html подробнее]<br />
|-<br />
|<br />
'''Перечень нормативно-технических документов, используемых при осуществлении геодезической и картографической деятельности'''.<br /><br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/law/list-docs-legislation.zip|загрузить]]<br />
|-<br />
|<br />
'''Приказ Роскартографии "Об утверждении Перечня организаций-фондодержателей подведомственных Роскартографии"<br />'''7 августа 2001г. N 158-пр<br /><br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/law/org-fond-roskart.zip|загрузить]] [[Image:weblink.png|24px|link=http://roskart.gov.ru/default.asp?cmd=00A0010007B0000F00F000000000000000000000/default.asp|ссылка]]<br />
|-<br />
|<br />
'''О контроле за перемещением картографических, топографических, аэрокосмических, геодезических и гравиметрических материалов<br />'''Письмо ГТК РФ от 11.08.1997г. N 01-15/15278<br /> Приложение 1: копия письма ФС ГиК от 25.03.96 N 3-02-670 и утвержденные ею 30.04.91 "Развернутый перечень сведений, подлежащих засекречиванию по системе Федеральной службы геодезии и картографии России" (приложение 2) и 19.12.96 "Перечень сведений по геодезии, топографии, картографии, аэросъемке и их носителей, отнесенных к служебной информации ограниченного распространения с пометкой "Для служебного пользования" (приложение 3).<br />
<br />
<br />
15. Сведения о местонахождении (координаты) геодезических пунктов и<br />
географических объектов, определенные с точностью 30 метров и точнее<br />
в государственной системе координат 1942 года и в геоцентрических системах<br />
координат ... - секретно.<br />
19. Исходные данные (ключи) местных систем координат, системы координат 1963<br />
года и материалы, раскрывающие переход от этих систем координат<br />
к государственной системе координат 1942 года - секретно.<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/law/customs-control-97.zip|загрузить]] [[Image:weblink.png|24px|link=http://www.sigma-soft.ru/txt/D0039/d_3958_1.shtml|веб]]<br />
|}<br />
<br />
==Кадастр==<br />
<br />
{| class="wikitable" width="100%"<br />
| '''Федеральная служба земельного кадастра России. '''Основные положения об опорной межевой сети. Утверждены приказом Росземкадастра №П/261 от 15 апреля 2002 года<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/law/mezhset-basics.7z|загрузить]]<br />
|-<br />
| '''Ответ Росреестра по доступу к кадастровым сведениям через ПКК'''<br /> 29.03.2012 №20-00270/12<br />
|<br />
[http://gis-lab.info/docs/law/pkk-rosreestr-answer.html подробнее]<br />
|}<br />
<br />
==Дистанционное зондирование (космическая деятельность)==<br />
<br />
{| class="wikitable" width="100%"<br />
|-<br />
! width="90%" class="unsortable"|<br />
!! width="10%" class="unsortable"|<br />
<br />
|-<br />
|<br />
'''Дистанционное зондирование Земли из космоса – обзор законодательства и правоприменительной практики''': Под общей редакцией А.А. Балагурова. М., 2009, Электронная версия [[Image:forumq.gif|14px|link=http://gis-lab.info/forum/viewtopic.php?f=26&t=3293|обсудить]]<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/books/balagurov05_rs-law-practice/balagurov05_rs-law-practice.pdf]]<br />
|-<br />
|<br />
[Image:new.gif|27px|new]] О космической деятельности<br /> ФЗ от 20 августа 1993 года N 5663-1<br /> (в ред. Федеральных законов от 29.11.1996 N 147-ФЗ, от 10.01.2003 N 15-ФЗ, от 05.03.2004 N 8-ФЗ, от 22.08.2004 N 122-ФЗ, от 02.02.2006 N 19-ФЗ, от 18.12.2006 N 231-ФЗ, от 30.12.2008 N 309-ФЗ, от 30.12.2008 N 313-ФЗ)<br /><br />
<br />
К основным направлениям космической деятельности относятся:<br />
...<br />
дистанционное зондирование Земли из космоса, включая экологический мониторинг и<br />
метеорологию;<br />
<br />
<br /><br />
|<br />
[[Image:weblink.png|24px|link=http://protect.gost.ru/v.aspx?control=8&baseC=6&page=0&month=5&year=2009&search=ÐÐСТ Ð http://www.consultant.ru/online/base/?req=doc;base=LAW;n=83341|ссылка]]<br />
|-<br />
|<br />
[Image:new.gif|27px|new]] Положение о Федеральном космическом агентстве <br /> Утверждено постановлением Правительства Российской Федерации от 26 июня 2004 г. N 314 г. Москва<br /><br />
<br />
3. Федеральное космическое агентство осуществляет лицензирование<br />
космической деятельности<br />
|<br />
[[Image:weblink.png|24px|link=http://protect.gost.ru/v.aspx?control=8&baseC=6&page=0&month=5&year=2009&search=ÐÐСТ Ð http://www.rg.ru/2004/06/30/kosmos-dok.html|ссылка]]<br />
|-<br />
|<br />
[Image:new.gif|27px|new]] Об утверждении Административного регламента Федерального космического агентства по исполнению государственной функции по осуществлению лицензирования космической деятельности'''<br /> Приказ Федерального космического агентства от 25 мая 2007 г. N 51. Зарегистрировано в Минюсте РФ 17 сентября 2007 г. Регистрационный N 10140.<br /><br />
<br />
1.1.2. Административный регламент Федерального космического агентства<br />
... - нормативный правовой акт, определяющий сроки, последовательность действий<br />
(административных процедур) и порядок взаимодействия между структурными<br />
подразделениями и должностными лицами при реализации государственной функции<br />
по осуществлению лицензирования космической деятельности.<br />
|<br />
[[Image:weblink.png|24px|link=http://protect.gost.ru/v.aspx?control=8&baseC=6&page=0&month=5&year=2009&search=ÐÐСТ Ð http://base.garant.ru/191819/|ссылка]]<br />
|-<br />
|<br />
[Image:new.gif|27px|new]]'''Принципы, касающиеся дистанционного зондирования Земли из космического пространства.'''<br /> Приняты резолюцией 41/65 на 95 пленарном заседании Генеральной Ассамблеи ООН от 3 декабря 1986 года<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/law/un-remote-sensing.7z|загрузить]] [[Image:weblink.png|24px|link=http://www.un.org/russian/documen/convents/earth_remote_sensing.html|ссылка]]<br />
|-<br />
|<br />
[[Image:new.gif|27px|new]] О лицензировании отдельных видов деятельности. Статья 17. Перечень видов деятельности, на осуществление которых требуются лицензии<br />
<br />
95) космическая деятельность;<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/law/law-license-some-activities.zip|загрузить]] [[Image:weblink.png|24px|link=http://www.consultant.ru/popular/license/38_1.html#p341|ссылка]]<br />
|-<br />
| '''Положение о лицензировании космической деятельности'''<br /> Утверждено постановлением правительства РФ от 22 февраля 2012 г. №160 «О лицензировании космической деятельности» <br /><br />
|<br />
[http://gis-lab.info/docs/law/license-kosmos-2012.html подробно]<br />
|-<br />
|<br />
'''Положение о лицензировании космической деятельности'''<br /> Утверждено постановлением Правительства РФ от 30 июня 2006 г. N 403. (в ред. Постановления Правительства РФ от 21.04.2010 N 268). Отменено. <br /><br />
<br />
Настоящее Положение определяет порядок лицензирования космической деятельности,<br />
осуществляемой юридическими лицами.<br />
|<br />
[[Image:weblink.png|24px|link=http://www.consultant.ru/online/base/?req=doc;base=LAW;n=100014#p31|ссылка]]<br />
|-<br />
| width="648" |<br />
'''ГОСТ Р 51833-2001 '''Фотограмметрия. Термины и определения.<br />
| width="60" |<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/gost51833-2001.rar]] [[Image:weblink.png|24px|link=http://protect.gost.ru/v.aspx?control=8&baseC=6&page=0&month=5&year=2009&search=ÐÐСТ Ð 51833-2001&RegNum=1&DocOnPageCount=15&id=122922|ссылка]]<br />
|-<br />
|<br />
'''ГОСТ 2819-84 '''Материалы фотографические. Метод определения разрешающей способности<br />
|<br />
[[Image:weblink.png|24px|link=http://protect.gost.ru/v.aspx?control=8&baseC=6&page=0&month=5&year=2009&search=ÐÐСТ 2819-84&RegNum=1&DocOnPageCount=15&id=136204|ссылка]]<br />
|-<br />
|<br />
'''Кодекс Российской Федерации об административных правонарушениях'''. '''Статья 11.17. Нарушение правил поведения граждан на железнодорожном, воздушном или водном транспорте'''<br /><br />
<br />
4. Нарушение правил фотографирования, видео- и киносъемки либо<br />
пользования средствами радиосвязи с борта воздушного судна - влечет<br />
предупреждение или наложение административного штрафа в размере ста рублей<br />
с конфискацией пленки.<br />
|<br />
[[Image:weblink.png|24px|link=http://www.consultant.ru/popular/koap/13_12.html#p2857|веб]]<br />
|-<br />
|<br />
'''Воздушный кодекс Российской Федерации. Статья 75. Использование фото- и киносъемки и других способов дистанционного зондирования земли с борта воздушного судна'''<br /><br />
<br />
Использование фото- и киносъемки и других способов дистанционного<br />
зондирования земли с борта воздушного судна допускается в порядке,<br />
установленном Правительством Российской Федерации.<br />
|<br />
[[Image:weblink.png|24px|link=http://www.rg.ru/2007/10/22/vozdushny-kodeks-dok.html|веб]]<br />
|-<br />
|<br />
'''Федеральные авиационные правила «Подготовка и выполнение полетов в гражданской авиации Российской Федерации»<br />'''утверждены приказом Минтранса РФ 31 июля 2009 г. N 128<br /><br />
<br />
VII. Правила выполнения видов авиационных работ<br />
Воздушные съемки<br />
<br />
7.12. К воздушным съемкам относятся: аэрофотосъемочные,<br />
поисково-съемочные и аэросъемочные полеты.<br />
|<br />
[[Image:weblink.png|24px|link=http://www.businesspravo.ru/Docum/DocumShow_DocumID_156251_DocumIsPrint__Page_1.html|веб]]<br />
|-<br />
| '''Положение о размещении и использовании на территории Российской Федерации, на континентальном шельфе и в исключительной экономической зоне Российской Федерации иностранных технических средств наблюдения и контроля'''<br /> Утверждено постановлением Правительства Российской Федерации от 29 августа 2001 г. № 633 (с изменениями и дополнениями от 23 мая 2005 г. № 322)<br />
|<br />
[[Image:weblink.png|24px|link=http://www.fstec.ru/_docs/doc_2_2_020.htm|веб]]<br />
|}<br />
<br />
==Географические информационные системы==<br />
<br />
{| class="wikitable" width="100%"<br />
|-<br />
! width="90%" class="unsortable"|<br />
!! width="10%" class="unsortable"|<br />
<br />
|-<br />
| width="649" |<br />
'''ГОСТ Р 50828-95 '''Геоинформационное картографирование. Пространственные данные, цифровые и электронные карты. Общие требования.<br />
| width="59" |<br />
[[Image:download.jpeg|24px|http://gis-lab.info/docs/law/gost50828-95.rar]] [[Image:weblink.png|24px|http://protect.gost.ru/v.aspx?control=8&baseC=6&page=0&month=5&year=2009&search=ÐÐСТ Ð 50828-95&RegNum=1&DocOnPageCount=15&id=126226|ссылка]]<br />
|-<br />
|<br />
'''ГОСТ Р 51353-99''' Геоинформационное картографирование. Метаданные электронных карт. Состав и содержание.<br />
| width="59" |<br />
[[Image:download.jpeg|24px|http://gis-lab.info/docs/law/gost51353-99.rar]][[Image:weblink.png|24px|http://protect.gost.ru/v.aspx?control=8&baseC=6&page=0&month=5&year=2009&search=ÐÐСТ Ð 51353-99&RegNum=1&DocOnPageCount=15&id=123630 |ссылка]]<br />
|-<br />
| '''ГОСТ Р 52573-06''' Географическая информация. Метаданные.<br />
|<br />
[[Image:download.jpeg|24px|http://gis-lab.info/docs/law/gost52573_2006.pdf]] [[Image:weblink.png|24px|http://protect.gost.ru/v.aspx?control=8&baseC=6&page=0&month=5&year=2009&search=%D0%93%D0%9E%D0%A1%D0%A2%20%D0%A0%2051353-http://gost.ruscable.ru/cgi-bin/catalog/catalog.cgi?i=3974&l=|ссылка]]<br />
|-<br />
|<br />
'''ГОСТ Р 52055-2003 '''Геоинформационное картографирование. Пространственные модели местности. Общие требования.<br />
|<br />
[[Image:download.jpeg|24px|http://gis-lab.info/docs/law/gost52055-2003.rar]] [[Image:weblink.png|24px|http://protect.gost.ru/v.aspx?control=8&baseC=6&page=0&month=5&year=2009&search=ÐÐСТ Ð 52055-2003&RegNum=1&DocOnPageCount=15&id=121940|ссылка]]<br />
|-<br />
|<br />
'''ГОСТ Р 52155-2003 '''Географические информационные системы федеральные, региональные, муниципальные. Общие технические требования.<br />
|<br />
[[Image:download.jpeg|24px|http://gis-lab.info/docs/law/gost52155-2003.rar|загрузить]] [[Image:weblink.png|24px|http://protect.gost.ru/document.aspx?control=7&id=129954|ссылка]]<br />
|-<br />
|<br />
'''ГОСТ Р ИСО 19105-2003 '''Географическая информация. Соответствие и тестирование. (эквивалент международного стандарта ISO 19105-2000 Geographic information -- Conformance and testing)<br />
|<br />
[[Image:download.jpeg|24px|http://gis-lab.info/docs/law/gostiso19105-2003.zip|загрузить]] [[Image:weblink.png|24px|http://protect.gost.ru/document.aspx?control=7&id=129954|ссылка]]<br />
|-<br />
|<br />
'''ГОСТ Р ИСО 19113-2003 '''Географическая информация. Принципы оценки качества (эквивалент международного стандарта ISO 19113-2002 Geographic information -- Quality principles)<br />
|<br />
[[Image:download.jpeg|24px|http://gis-lab.info/docs/law/gostiso19113-2003.zip|загрузить]] [[Image:weblink.png|24px|http://protect.gost.ru/document.aspx?control=7&baseC=6&page=0&month=5&year=2009&search=ÐÐСТ Ð ÐСР19113-2003&id=130055|ссылка]]<br />
|-<br />
|<br />
'''ГОСТ 52438-2005 '''Географические информационные системы. Термины и определения<br />
|<br />
[[Image:download.jpeg|24px|http://gis-lab.info/docs/law/gost52438-2005.zip|загрузить]] [[Image:weblink.png|24px|http://protect.gost.ru/v.aspx?control=8&baseC=6&page=0&month=5&year=2009&search=ÐÐСТ Ð 52438-2005&RegNum=1&DocOnPageCount=15&id=121612|ссылка]]<br />
|-<br />
|<br />
'''ГОСТ 52571-2006 '''Географические информационные системы. Совместимость пространственных данных. Общие требования [[Image:forumq.gif|14px|http://gis-lab.info/forum/viewtopic.php?t=3162|обсудить]]<br />
|<br />
[[Image:weblink.png|24px|http://protect.gost.ru/v.aspx?control=8&baseC=6&page=0&month=5&year=2009&search=ÐÐСТ Ð 52571-2006&RegNum=1&DocOnPageCount=15&id=121146|ссылка]]<br />
|-<br />
| '''ГОСТ 52572-2006 '''Географические информационные системы. Координатная основа. Общие требования<br />
|<br />
[[Image:download.jpeg|24px|http://gis-lab.info/docs/law/gost52572-2006.zip|загрузить]]<br />
|-<br />
|<br />
'''ГОСТ Р 52293-2004 '''Геоинформационное картографирование. Система электронных карт. Карты электронные топографические. Общие требования [[Image:forumq.gif|14px|http://gis-lab.info/forum/viewtopic.php?t=3169|обсудить]]<br />
|<br />
[[Image:weblink.png|24px|http://protect.gost.ru/v.aspx?control=8&baseC=6&page=0&month=5&year=2009&search=ÐÐСТ Ð 52293-2004&RegNum=1&DocOnPageCount=15&id=121892|ссылка]]<br />
|-<br />
|<br />
[[Image:new.gif|27px|new]]'''ГОСТ 34.601-90''' Автоматизированные системы. Стадии создания. Дата введения 01.01.92. ИПК Издательство Стандартов, 1990<br />
|<br />
[http://gis-lab.info/docs/law/gost34.601-90.html подробно]<br />
|}<br />
<br />
==Системы координат и навигация==<br />
<br />
{| class="wikitable" width="100%"<br />
|-<br />
! width="90%" class="unsortable"|<br />
!! width="10%" class="unsortable"|<br />
<br />
|-<br />
|'''О введении единой системы геодезических координат и высот на территории СССР'''<br /> Постановление СМ СССР от 7 апреля 1946 г. N 760<br />
|[[Image:download.jpeg|24px|link=http://gis-lab.info/law/common-coord-46.zip|загрузить]]<br />
<br />
|-<br />
|'''О навигационной деятельности'''<br /> Федеральный закон Российской Федерации от 14 февраля 2009 г. N 22-ФЗ<br /><br />
Физические и юридические лица могут осуществлять навигационную деятельность<br />
для собственных нужд и оказание услуг в сфере навигационной деятельности<br />
на всей территории Российской Федерации без ограничения точности определения<br />
координат объектов навигационной деятельности, за исключением территорий<br />
и объектов, для которых законодательством Российской Федерации установлен<br />
особый режим безопасного функционирования и перечень которых утверждается<br />
Правительством Российской Федерации.<br />
|[[Image:weblink.png|24px|link=http://www.rg.ru/2009/02/18/navigaciya-dok.html|web]]<br />
<br />
|-<br />
|[[Image:new.gif|27px|new]]О территориях, на которых вводятся ограничения на точность определения координат объектами навигационной деятельности <br /> Постановление правительства РФ от 5 июля 2010 г. N 503<br /><br />
<br />
Установить, что закрытые административно-территориальные образования<br />
относятся к территориям, на которых вводятся ограничения на точность определения<br />
координат объектами навигационной деятельности.<br />
|[[Image:weblink.png|24px|link=http://www.consultant.ru/online/base/?req=doc;base=LAW;n=102298|web]]<br />
<br />
|-<br />
| '''Об утверждении особых условий приобретения радиоэлектронных средств и высокочастотных устройств'''<br /> (Редакция на 18.10.2003) <br /> Постановление правительства РФ 17 июля 1996 г. N 832 (ЮИАИ)<br />
|<br />
[[Image:weblink.png|24px|link=http://protect.gost.ru/v.aspx?control=8&baseC=6&page=0&month=5&year=2009&search=%D0%93%D0%9E%D0%A1%D0%A2%20http://www.innovbusiness.ru/pravo/DocumShow_DocumID_92238.html|ссылка]]<br />
<br />
|-<br />
| '''Положение о порядке изготовления, ввоза в Российскую Федерацию и использования на территории фонда Российской Федерации радиоэлектронных средств (высокочастотных устройств) <br />''' Постановление правительства РФ 5 июня 1994 г. N 643 (ЭЖ 94-26)<br />
|<br />
[[Image:weblink.png|24px|link=http://protect.gost.ru/v.aspx?control=8&baseC=6&page=0&month=5&year=2009&search=ÐÐСТ http://www.innovbusiness.ru/pravo/DocumShow_DocumID_92238.html|ссылка]]<br />
<br />
|-<br />
|<br />
'''ГОСТ 19156-79 '''Аппаратура навигационная наземная одометрическая. Термины и определения<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/gost51794-2001.zip|загрузить]] [[Image:weblink.png|24px|link=http://protect.gost.ru/v.aspx?control=8&baseC=6&page=0&month=5&year=2009&search=ÐÐСТ 19156-79&RegNum=1&DocOnPageCount=15&id=145400|ссылка]]<br />
|-<br />
|<br />
'''ГОСТ Р 51794-2001 '''Аппаратура радионавигационная глобальной навигационной спутниковой системы и глобальной системы позиционирования. Системы координат. Методы преобразования координат определяемых точек<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/gost51794-2001.zip|загрузить]] [[Image:weblink.png|24px|link=http://protect.gost.ru/v.aspx?control=8&baseC=6&page=0&month=-1&year=-1&search=&id=122872&pageK=40D02125-B236-4E2A-ADE9-96337A28FF9E|ссылка]]<br />
|-<br />
|<br />
'''ГОСТ Р 51794-2008 '''Глобальные навигационные спутниковые системы. Системы координат. Методы преобразований координат определяемых точек.<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/gost51794-2008.7z|загрузить]] [[Image:weblink.png|24px|link=http://protect.gost.ru/document.aspx?control=7&baseC=6&page=0&month=9&year=2009&search=&id=174517|ссылка]]<br />
|-<br />
|<br />
'''Система геодезических параметров земли "Параметры Земли 1990 года" (ПЗ-90) '''<br /> Галазин В.Ф., Каплан Б.Л., Лебедев М.Г., Максимов В.Г., Петров Н.В., Сидорова-Бирюкова Т.Л./ Под ред. Хвостова В.В. - М. Координационный научно-информационный центр, 1998.<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/books/pz90/pz90.zip]]<br />
|-<br />
|<br />
'''Постановление Правительства РФ "Об утверждении Правил установления местных систем координат"<br />'''от 3 марта 2007 г. N 139 <br /><br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/law/set-local-coords.zip|загрузить]] [[Image:weblink.png|24px|link=http://www.businesspravo.ru/Docum/DocumShow_DocumID_121378.html|ссылка]]<br />
|-<br />
|<br />
'''Постановление Правительства РФ "Об установлении единых государственных систем координат"<br />'''от 28 июля 2000 г. N 568<br /><br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/law/common-coords.zip|загрузить]] [[Image:weblink.png|24px|link=http://www.businesspravo.ru/Docum/DocumShow_DocumID_16578.html|ссылка]]<br />
|-<br />
| ГОСТ Р 52454-2005 Глобальная навигационная спутниковая система и глобальная система позиционирования. Приемник персональный. Технические требования<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/law/gost52454-2005.pdf|загрузить]] [[Image:weblink.png|24px|link=http://protect.gost.ru/v.aspx?control=8&baseC=6&page=0&month=5&year=2009&search=ÐÐСТ Ð 52454-2005&RegNum=1&DocOnPageCount=15&id=121505|web]]<br />
|-<br />
| ГОСТ Р 52455-2005 Глобальная навигационная спутниковая система и глобальная система позиционирования. Приемник морской общего пользования. Технические требования<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/law/gost52455-2005.pdf|загрузить]] [[Image:weblink.png|24px|link=http://protect.gost.ru/v.aspx?control=8&baseC=6&page=0&month=5&year=2009&search=ÐÐСТ Ð 52455-2005&RegNum=1&DocOnPageCount=15&id=121414|web]]<br />
|-<br />
| ГОСТ Р 52456-2005 Глобальная навигационная спутниковая система и глобальная система позиционирования. Приемник индивидуальный для автомобильного транспорта. Технические требования<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/law/gost52456-2005.pdf|загрузить]] [[Image:weblink.png|24px|link=http://protect.gost.ru/v.aspx?control=8&baseC=6&page=0&month=5&year=2009&search=ÐÐСТ Ð 52456-2005&RegNum=1&DocOnPageCount=15&id=121316|web]]<br />
|-<br />
| ГОСТ Р 52457-2005 Глобальная навигационная спутниковая система. Аппаратура потребителей. Классификация<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/law/gost52457-2005.pdf|загрузить]] [[Image:weblink.png|24px|link=http://protect.gost.ru/v.aspx?control=8&baseC=6&page=0&month=5&year=2009&search=ÐÐСТ Ð 52457-2005&RegNum=1&DocOnPageCount=15&id=121668|web]]<br />
|-<br />
| ГОСТ Р 52864-2007 Глобальная навигационная спутниковая система. Аппаратура потребителей навигационная гражданского назначения для ракет-носителей, разгонных блоков и космических аппаратов.<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/law/gost52864-2005.pdf|загрузить]] [[Image:weblink.png|24px|link=http://protect.gost.ru/v.aspx?control=8&baseC=6&page=0&month=5&year=2009&search=ÐÐСТ Ð 52864-2007&RegNum=1&DocOnPageCount=15&id=165788|web]]<br />
|-<br />
| ГОСТ Р 52865-2007 Глобальная навигационная спутниковая система. Параметры радионавигационного поля. Технические требования и методы испытаний<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/law/gost52865-2005.pdf|загрузить]] [[Image:weblink.png|24px|link=http://protect.gost.ru/v.aspx?control=8&baseC=6&page=0&month=5&year=2009&search=ÐÐСТ Ð 52865-2007&RegNum=1&DocOnPageCount=15&id=165794|web]]<br />
|-<br />
| '''ГОСТ Р 52866-2007''' Глобальная навигационная спутниковая система. Станция контрольно-корректирующая локальная гражданского назначения. Технические требования<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/law/gost52866-2005.pdf|загрузить]] [[Image:weblink.png|24px|link=http://protect.gost.ru/v.aspx?control=8&baseC=6&page=0&month=5&year=2009&search=ÐÐСТ Ð 52866-2007&RegNum=1&DocOnPageCount=15&id=165815|web]]<br />
|-<br />
| '''ГОСТ Р 52928-2008''' Система спутниковая навигационная глобальная. Термины и определения<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/law/gost52928-2005.pdf|загрузить]] [[Image:weblink.png|24px|link=http://protect.gost.ru/v.aspx?control=8&baseC=6&page=0&month=5&year=2009&search=%13%1E!|web]]<br />
|-<br />
| [[Image:new.gif|27px|new]] '''ГОСТ 32453-2013''' Глобальная навигационная спутниковая система. Системы координат. Методы преобразований координат определяемых точек.<br />
|<br />
[[Image:weblink.png|24px|link=http://protect.gost.ru/v.aspx?control=7&id=187112|web]]<br />
<br />
|}<br />
<br />
==Лицензирование и сертификация==<br />
<br />
{| class="wikitable" width="100%"<br />
|-<br />
! width="90%" class="unsortable"|<br />
!! width="10%" class="unsortable"|<br />
<br />
|-<br />
| <br />
'''Кодекс Российской Федерации об административных правонарушениях'''. '''Статья 19.20. Осуществление деятельности, не связанной с извлечением прибыли, без специального разрешения (лицензии)<br />'''<br />
<br />
1. Осуществление деятельности, не связанной с извлечением прибыли,<br />
без специального разрешения (лицензии), если такое разрешение (лицензия)<br />
обязательно (обязательна), -<br />
...<br />
2. Осуществление деятельности, не связанной с извлечением прибыли, с<br />
нарушением требований или условий специального разрешения (лицензии),<br />
если такое разрешение (лицензия) обязательно (обязательна), -<br />
...<br />
3. Осуществление деятельности, не связанной с извлечением прибыли, с<br />
грубым нарушением требований или условий специального разрешения (лицензии),<br />
если такое разрешение (лицензия) обязательно (обязательна)<br />
|<br />
[[Image:weblink.png|24px|link=http://www.consultant.ru/popular/koap/13_20.html#p6210]]<br />
|-<br />
|<br />
[[Image:new.gif|27px|new]]О защите прав юридических лиц и индивидуальных предпринимателей при осуществлении государственного контроля (надзора) и муниципального контроля. Статья 1. Сфера применения настоящего Федерального закона'''<br /><br />
<br />
2. Настоящим Федеральным законом устанавливаются:<br />
...<br />
4. Особенности организации и проведения проверок при осуществлении таможенного,<br />
антимонопольного, экспортного контроля, контроля и надзора в сфере миграции,<br />
государственного контроля (надзора) за деятельностью саморегулируемых<br />
организаций, лицензионного контроля, ...<br />
<br />
<br /><br />
|<br />
[[Image:weblink.png|24px|link=http://www.rg.ru/2008/12/30/prava-kontrol-dok.html]]<br />
|-<br />
|<br />
'''Об утверждении единого перечня продукции, подлежащей обязательной сертификации, и единого перечня продукции, подтверждение соответствия которой осуществляется в форме принятия декларации о соответствии'''<br /> Постановление Правительства РФ от 1 декабря 2009 г. №982<br /><br />
<br />
9551 Карты справочные<br />
9552 Карты листовые учебные<br />
9553 Цифровые карты, планы<br />
9554 Карты и планы листовые, складные, брошюры, буклеты топографические,<br />
топографические планы и схемы<br />
9555 Атласы<br />
9557 Глобусы<br />
9558 Карты рельефные<br />
|<br />
[[Image:weblink.png|24px|link=http://government.ru/gov/results/8561/]]<br />
|-<br />
|<br />
'''О внесении изменений в Постановление Правительства Российской Федерации от 1 декабря 2009 г. N 982'''<br /> Постановление Правительства РФ от 13.11.2010 № 906<br /><br />
<br />
53) разделы 9551 - 9555, 9557 и 9558 исключить;<br />
|<br />
[[Image:weblink.png|24px|link=http://www.rnk.ru/documents/new/document158509.phtml]]<br />
|-<br />
| '''Обращение «Дискус Медиа» в Росреестр'''<br /> Разъяснение действия механизмов сертификации картографической продукции в связи со вступлением в силу Постановления Правительства РФ от 1 декабря 2009 г. № 982 «Об утверждении Единого перечня продукции, подлежащей обязательной сертификации, и Единого перечня продукции, подтверждение соответствия которой осуществляется в форме принятия декларации о соответствии»<br />
|<br />
[[Image:weblink.png|24px|link=http://www.gisa.ru/65761.html]] [[Image:download.jpeg|24px|link=http://gis-lab.info/law/discuss-rosreestr.pdf|загрузить]]<br />
|-<br />
|<br />
'''О геодезии и картографии. Статья 3. Геодезическая и картографическая деятельность.<br />'''Федеральный закон от 26.12.1995 № 209-ФЗ (с изменениями и дополнениями по 30.12.2008 включительно)<br />
<br />
1. Геодезическая и картографическая деятельность исходя из назначения<br />
выполняемых работ включает в себя:<br />
геодезические и картографические работы федерального назначения, результаты<br />
которых имеют общегосударственное, межотраслевое значение;<br />
<br />
геодезические и картографические работы специального (отраслевого) назначения,<br />
необходимость проведения которых определяется потребностями субъектов Российской<br />
Федерации, муниципальных образований, отдельных отраслей, граждан и юридических<br />
лиц.<br />
|<br />
[[Image:weblink.png|24px|link=http://docs.kodeks.ru/document/9015033]]<br />
|-<br />
|<br />
'''О геодезии и картографии. Статья 12. Лицензирование геодезической и картографической деятельности.<br />'''Федеральный закон от 26.12.1995 № 209-ФЗ (с изменениями и дополнениями по 30.12.2008 включительно)<br />
<br />
Геодезическая и картографическая деятельность подлежит лицензированию<br />
в соответствии с законодательством РФ.<br />
|<br />
[[Image:weblink.png|24px|link=http://docs.kodeks.ru/document/9015033]]<br />
|-<br />
|<br />
'''О лицензировании отдельных видов деятельности. Статья 12. Перечень видов деятельности, на которые требуются лицензии<br />'''от 04.05.2011 N 99-ФЗ (принят ГД ФС РФ 22.04.2011)<br />
<br />
42) геодезические и картографические работы федерального назначения,<br />
результаты которых имеют общегосударственное, межотраслевое значение (за<br />
исключением указанных видов деятельности, осуществляемых в ходе инженерных<br />
изысканий, выполняемых для подготовки проектной документации, строительства,<br />
реконструкции, капитального ремонта объектов капитального строительства);<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/law/law-license-some-activities.zip|загрузить]] [[Image:weblink.png|24px|link=http://www.consultant.ru/popular/license/]]<br />
|-<br />
|<br />
'''О лицензировании отдельных видов деятельности. Статья 1. Сфера применения настоящего Федерального закона<br />'''от 08.08.2001 N 128-ФЗ (принят ГД ФС РФ 13.07.2001)'''<br />'''(с изменениями и дополнениями по 27.12.2009 включительно)<br />
<br />
2. Действие настоящего Федерального закона не<br />
распространяется на следующие виды деятельности:<br />
...<br />
использование результатов интеллектуальной деятельности лицами,<br />
обладающими правами на такое использование в силу федерального закона или<br />
договора;<br />
...<br />
образовательная деятельность;<br />
...<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/law/law-license-some-activities.zip|загрузить]] [[Image:weblink.png|24px|link=http://www.consultant.ru/popular/license/38_1.html#p38|ссылка]]<br />
|-<br />
|<br />
'''О лицензировании отдельных видов деятельности. Статья 17. Перечень видов деятельности, на осуществление которых требуются лицензии<br />'''от 08.08.2001 N 128-ФЗ (принят ГД ФС РФ 13.07.2001)'''<br />'''(с изменениями и дополнениями по 27.12.2009 включительно)<br />
<br />
Лицензирование геодезической и картографической деятельности прекращается<br />
со дня вступления в силу технических регламентов, устанавливающих обязательные<br />
требования к лицензируемым видам деятельности (пункт 7 статьи 18 данного документа).<br />
42) геодезическая деятельность;<br />
43) картографическая деятельность;<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/law/law-license-some-activities.zip|загрузить]] [[Image:weblink.png|24px|link=http://www.consultant.ru/popular/license/|ссылка]]<br />
|-<br />
|<br />
'''О лицензировании отдельных видов деятельности. Статья 18. Переходные и заключительные положения (в ред. Федерального закона от 30.12.2008 N 309-ФЗ)<br />'''от 08.08.2001 N 128-ФЗ (принят ГД ФС РФ 13.07.2001)'''<br />'''(с изменениями и дополнениями по 27.12.2009 включительно)<br />
<br />
7. Со дня вступления в силу технических регламентов,<br />
устанавливающих обязательные требования к лицензируемым видам деятельности,<br />
прекращается лицензирование следующих указанных в пункте 1 статьи 17<br />
настоящего Федерального закона видов деятельности:<br />
...<br />
геодезическая деятельность;<br />
картографическая деятельность;<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/law/law-license-some-activities.zip|загрузить]] [[Image:weblink.png|24px|link=http://www.consultant.ru/popular/license/38_1.html#p521|ссылка]]<br />
|-<br />
|<br />
'''О саморегулируемых организациях'''<br /> Федеральный закон от 1 декабря 2007 г. (с изменениями и дополнениями по 27.12.2009 включительно).<br /><br />
<br />
Под саморегулированием понимается самостоятельная и инициативная деятельность,<br />
которая осуществляется субъектами предпринимательской или профессиональной<br />
деятельности и содержанием которой являются разработка и установление<br />
стандартов и правил указанной деятельности, а также контроль за соблюдением<br />
требований указанных стандартов и правил.<br />
|<br />
[[Image:weblink.png|24px|link=http://docs.cntd.ru/document/902074540|ссылка]]<br />
|-<br />
|<br />
'''Положение о лицензировании картографической деятельности'''. <br /> Утверждено постановлением Правительства Российской Федерации от 21 ноября 2006 г. №705<br />
<br />
Настоящее Положение определяет порядок лицензирования картографической<br />
деятельности, осуществляемой юридическими лицами и индивидуальными<br />
предпринимателями.<br />
<br />
<br /><br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/law/polozh-carto-license.zip|загрузить]] [[Image:weblink.png|24px|link=http://base.consultant.ru/cons/cgi/online.cgi?req=doc;base=LAW;n=105457;fld=134;dst=4294967295;from=64030-0|ссылка]]<br />
|-<br />
|<br />
'''Положение о лицензировании геодезической деятельности'''. <br /> Утверждено постановлением Правительства Российской Федерации от 21 ноября 2006 г. №705<br /><br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/law/polozh-geodez-license.zip|загрузить]] [[Image:weblink.png|24px|link=http://base.consultant.ru/cons/cgi/online.cgi?req=doc;base=LAW;n=105457;fld=134;dst=4294967295;from=64030-0|ссылка]]<br />
|-<br />
|<br />
'''О введении в действие рекомендаций по отнесению работ к геодезической и картографической деятельности при организации работ по лицензированию'''<br />Приказ Минтранса РФ от 06 Мая 2008 г. N 74<br />
<br />
2. При организации лицензирования картографической деятельности<br />
'''рекомендуется''' к картографическим работам отнести следующие виды работ:<br />
...<br />
4) проектирование, составление и издание общегеографических, политико-<br />
административных, научно-справочных и других тематических карт и атласов<br />
межотраслевого назначения, учебных картографических пособий;<br />
...<br />
11) создание и ведение географических информационных систем специального<br />
назначения;<br />
12) создание тематических карт, планов и атласов специального назначения<br />
в графической, цифровой и иных формах, издание этих карт, планов и атласов;<br />
13) выполнение научно-исследовательских и опытно-конструкторских работ по<br />
картографической деятельности.<br />
|<br />
[[Image:weblink.png|24px|link=http://www.mnogozakonov.ru/catalog/date/2008/5/6/8975/|ссылка]]<br />
|-<br />
|<br />
'''Перечень федеральных органов исполнительной власти, осуществляющих лицензирование<br />'''Утвержден постановлением Правительства Российской Федерации от 11 февраля 2002 г. №135<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/license-orgs-list.zip]]<br />
|-<br />
|<br />
'''О лицензировании деятельности в области геодезии и картографии'''<br /> Постановление Правительства РФ от 28 мая 2002г. №360<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/license-gov-mapping&geodesy.rar]] [[Image:weblink.png|24px|link=http://www.roskart.ru/law/p_licenz_kart.htm#postlic]]<br />
|-<br />
|<br />
'''Положение о лицензировании картографической деятельности'''<br /> Утверждено постановлением Правительства Российской Федерации от 28 мая 2002 г. №360<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/license-act-mapping.rar]] [[Image:weblink.png|24px|link=http://www.roskart.ru/law/p_licenz_kart.htm#polkart]]<br />
|-<br />
|<br />
'''Перечень конкретных видов работ, входящих в состав лицензируемой картографической деятельности<br />'''(из перечня работ, указанных в статье 3 Федерального закона “О геодезии и картографии”),<br /> и полномочия лицензирующих органов Федеральной службы геодезии и картографии России по осуществлению лицензирования этой деятельности)<br /> Утверждено приказом Федеральной службы геодезии и картографии России от 05 июля 2002 года № 95-пр<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/license-mapping-activities.rar]] [[Image:weblink.png|24px|link=http://www.roskart.ru/law/applickart.htm]]<br />
|-<br />
| '''Положение о лицензировании геодезической и картографической деятельности<br />'''Утверждено постановлением Правительства Российской Федерации от 8 июня 2001 г. №453 "Об утверждении положения о лицензировании геодезической и картографической деятельности".'''<br />'''<br />
|<br />
[[Image:weblink.png|24px|link=http://www.businesspravo.ru/Docum/DocumShow_DocumID_844.html|ссылка]]<br />
|-<br />
| '''Положение о лицензировании топографо-геодезической и картографической деятельности в Российской Федерации'''<br /> Утверждено постановлением Правительства Российской Федерации от 26 августа 1995 г. №847 "Об утверждении положения о лицензировании топографо-геодезической и картографической деятельности в Российской Федерации".<br />
|<br />
[[Image:weblink.png|24px|link=http://www.businesspravo.ru/Docum/DocumShow_DocumID_378.html|ссылка]]<br />
|-<br />
| '''О лицензировании деятельности предприятий, учреждений и организаций по проведению работ, связанных с использованием сведений, составляющих государственную тайну, созданием средств защиты информации, а также осуществлением мероприятий и (или) оказанием услуг по защите государственной тайны.<br />'''Постановление Правительства Российской Федерации от 15 апреля 1995 г, №333<br />
|<br />
[[Image:weblink.png|24px|link=http://www.fsb.ru/fsb/npd/more.htm!id=10342824@fsbNpa.html]]<br />
|-<br />
|<br />
'''Приказ Роскартографии "Об утверждении "Положения о системе сертификации геодезической, топографической и картографической продукции"<br />'''от 4 августа 2000 г. N 99-пр<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/license-roscart-certif-system.zip]]<br />
|-<br />
|<br />
'''Положение о системе сертификации геодезической, топографической и картографической продукции<br />'''Утверждено приказом Роскартографии от 4 августа 2000 г. N 99-пр <br /> Зарегистрировано Министерством юстиции РФ 14 сентября 2000г. регистрационный №2382.<br /> Зарегистрировано Госстандартом России в Государственном реестре 11 октября 2000г. №РОСС RU.0008.01КР00<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/license-roscart-certif-system.zip]] [[Image:weblink.png|24px|link=http://www.ggc.ru/docs/poloz_setr.htm]]<br />
|-<br />
|<br />
'''Информационное письмо Заместителя Руководителя Федеральной службы геодезии и картографии России А.Н. Прусакова<br />'''О системе сертификации геодезической, топографической и картографической продукции, аккредитованных центрах сертификации, касается всех предприятий и организаций, имеющих Лицензию Роскартографии на осуществление геодезической и картографической деятельности.<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/prusakov-certif-roskart.zip]] [[Image:weblink.png|24px|link=http://www.ggc.ru/docs/pismo_prus.htm]]<br />
|-<br />
|<br />
'''Образец лицензии на картографическую деятельность<br />'''Выдается Федеральной службой геодезии и картографии России (Роскартографией) на 5 лет. На обороте - разрешенный список работ.<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/license-sample.rar]]<br />
|-<br />
|<br />
'''Образец свидетельства №___ о регистрации работ на создание географических информационных систем (ГИС)''' [[Image:forumq.gif|14px|link=http://gis-lab.info/forum/viewtopic.php?t=2917|обсудить]]<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/svid-register-gis.pdf]]<br />
|-<br />
|<br />
'''Положение о контроле качества картографических работ <br />'''Неофициальное [[Image:forumq.gif|14px|link=http://gis-lab.info/forum/viewtopic.php?t=3049|24px|обсудить]]<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/polozh-quality-carto.7z]]<br />
|-<br />
|<br />
'''О порядке получения, использования и предоставления геопространственной информации'''<br /> Постановление Правительства РФ от 28 Мая 2007 г. N 326<br />
<br />
... устанавливает порядок получения, использования и предоставления<br />
геопространственной информации со средств дистанционного зондирования Земли из космоса.<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/law/geodata-use-326.zip|загрузить]] [[Image:weblink.png|24px|link=http://www.mnogozakonov.ru/catalog/date/2007/5/28/37441/|ссылка]]<br />
|-<br />
|<br />
'''[[Image:new.gif|27px|new]]Проект положения о лицензировании геодезических и картографических работ федерального назначения, результаты которых имеют общегосударственное, межотраслевое значение (за исключением указанных видов деятельности, осуществляемых в ходе инженерных изысканий, выполняемых для подготовки проектной документации, строительства, реконструкции, капитального ремонта объектов капитального строительства)'''<br /> Постановление Правительства РФ от ... ...<br />
<br />
Перечень геодезических и картографических работ федерального назначения,<br />
результаты которых имеют общегосударственное, межотраслевое значение<br />
...<br />
6) создание и ведение географических информационных систем<br />
федерального и регионального назначения;<br />
...<br />
7) проектирование, составление и издание общегеографических,<br />
политико-административных, научно-справочных и других тематических<br />
карт и атласов межотраслевого назначения, учебных картографических пособий;<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/law/license-polozh-project.zip|загрузить]] [[Image:weblink.png|24px|link=http://www.economy.gov.ru/minec/about/structure/depgosregulirineconomy/doc20110927_01?presentationtemplate=docHTMLTemplate1&presentationtemplateid=2dd7bc8044687de796f0f7af753c8a7e&WCM_Page.ResetAll=TRUE&CACHE=NONE&CONTENTCACHE=NONE&CONNECTORCACHE=NONE|ссылка]]<br />
|}<br />
<br />
==Классификаторы==<br />
<br />
{| class="wikitable" width="100%"<br />
|-<br />
! width="90%" class="unsortable"|<br />
!! width="10%" class="unsortable"|<br />
<br />
|-<br />
|<br />
'''[[Image:new.gif|27px|new]] Правила стандартизации. Основные положения и порядок проведения работ по разработке, ведению и применению общероссийских классификаторов ПР 50.1.024-2005 <br />'''Приказ Федерального агентства по техническому регулированию и метрологии от 14.12.2005 г. N 311-ст (Д)<br /><br />
<br />
7.3. Библиографическая информация об общероссийских классификаторах<br />
предоставляется бесплатно.<br />
Тексты общероссийских классификаторов и внесенных в них изменений предоставляются<br />
в порядке, устанавливаемом Минпромэнерго России совместно с Росстатом по согласованию<br />
с федеральными органами исполнительной власти, указанными в 7.1.<br />
|<br />
[[Image:weblink.png|24px|link=http://www.businesspravo.ru/Docum/DocumShow_DocumID_113401_DocumIsPrint__Page_1.html]]<br />
|-<br />
|<br />
'''[[Image:new.gif|27px|new]] Об опубликовании национальных стандартов и общероссийских классификаторов технико-экономической и социальной информации'''<br /> Постановление Правительства РФ от 25.09.2003 № 594<br />
<br />
3. Официальному опубликованию подлежат:<br />
а) тексты национальных стандартов и общероссийских классификаторов -<br />
в печатных изданиях (книгах, брошюрах, сборниках) и информационной системе<br />
общего пользования - на официальном сайте Государственного комитета Российской<br />
Федерации по стандартизации и метрологии в сети Интернет;<br />
б) уведомления об утверждении национальных стандартов, информация о внесении<br />
в них изменений, дополнений, поправок<br />
|<br />
[[Image:weblink.png|24px|link=http://www.rg.ru/oficial/doc/postan_rf/594-03.shtm]]<br />
|-<br />
| [[Image:new.gif|27px|new]] Об общероссийских классификаторах технико-экономической и социальной информации в социально-экономической области <br /> Постановление Правительства Российской Федерации от 10 ноября 2003 г. N 677 г.<br /> Список классификаторов (включая ОКАТО) в приложении.<br />
|<br />
[[Image:weblink.png|24px|link=http://www.rg.ru/2003/12/10/klassifikatory.html]]<br />
|-<br />
| [[Image:new.gif|27px|new]] Общероссийский классификатор объектов административно-территориального деления ОК-019-95<br /> Утвержден Постановлением Госстандарта РФ от 31.07.1995 N 413<br />
|<br />
[[Image:weblink.png|24px|link=http://base.consultant.ru/cons/cgi/online.cgi?req=doc;base=LAW;n=112411;fld=134;dst=4294967295;from=111252-0]]<br />
|}<br />
<br />
==Картографическая и геодезическая деятельность==<br />
<br />
{| class="wikitable" width="100%"<br />
|-<br />
! width="90%" class="unsortable"|<br />
!! width="10%" class="unsortable"|<br />
<br />
|-<br />
|<br />
'''О геодезии и картографии<br />'''Федеральный закон от 26 декабря 1995 г. № 209-ФЗ (по состоянию на 01.01.2009 в ред. ФЗ до 30.12.2008 включительно)<br />
|<br />
[[Image:weblink.png|24px|link=http://www.legis.ru/misc/doc/552/]]<br />
|-<br />
| '''Об утверждении требований к составу, структуре, порядку ведения и использования единой электронной картографической основы федерального, регионального и муниципального назначения'''<br /> Приказ Министерства экономического развития Российской Федерации (Минэкономразвития Pocсии) от 24 декабря 2008 г. N 467 г. Москва<br />
|<br />
[[Image:weblink.png|24px|link=http://www.rg.ru/2009/02/27/minekonom-kartograf-dok.html]]<br />
|-<br />
| '''Об утверждении административного регламента федерального агентства геодезии и картографии по предоставлению государственной услуги по обеспечению заинтересованных лиц государственными пографическими картами и планами в графической, цифровой, фотографической и иных формах.'''<br /> Приказ Министерства транспорта Российской Федерации от 28 сентября 2007 г. № 137. Регламент в приложении.<br />
|<br />
[[Image:weblink.png|24px|link=http://www.rosreestr.ru/document/reglament/1006866/]]<br />
|-<br />
|<br />
'''Об утверждении Инструкции о порядке предоставления в пользование и использования материалов и данных федерального картографо-геодезического фонда"'''<br /> Приказ ФГУГК от 5 августа 2002 г. №114-пр. Зарегистрировано в Минюсте РФ 20.08.2002 N 3713.'''<br />'''<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/law/instr-data-fedfond.zip|загрузить]] [[Image:weblink.png|24px|link=http://zakon.chuvashia.com/review_rus.asp?id=36913&s_page=165&s_sort=name|ссылка]]<br />
|-<br />
|<br />
'''Инструкция О порядке осуществления государственного геодезического надзора в Российской Федерации. ГКИНП-17-002-93'''. <br /> Утверждено руководителем ФГУГК Н.Д. Ждановым 15 окрября 1993 г. Зарегистрировано в Минюсте РФ 8 декабря 1993 г. N 425'''<br />'''<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/law/instr-gos-geodes-nadzor.zip|загрузить]] [[Image:weblink.png|24px|link=http://www.ab-pro.ru/zakon.html|ссылка]]<br />
|-<br />
|<br />
'''Об утверждении Инструкции о порядке передачи сведений о координатах геодезических пунктов и географических объектов территории Российской Федерации иностранным государствам и международным организациям'''<br /> Постановление Правительства Российской Федерации №120 от 08.02.1996<br /><br />
<br />
Воспроизводить ранее опубликованные в печати сведения о координатах<br />
геодезических пунктов и географических объектов территории Российской<br />
Федерации, отнесенные в установленном порядке к государственной тайне<br />
либо к служебной информации ограниченного распространения, не разрешается.<br />
|<br />
[[Image:weblink.png|24px|link=http://www.to33.rosreestr.ru/blanks/blanks_norm/1029249/|ссылка]]<br />
|-<br />
|<br />
'''Инструкция о порядке составления и издания планов городов и других населенных пунктов, предназначенных для открытого опубликования и с грифом "для служебного пользования" (СПГ-88) ГКИНП-14-221-88'''. <br /> Утверждено ГУГК '''<br />'''<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/law/instr-city-plans-dsp.zip|загрузить]] [[Image:weblink.png|24px|link=http://law-news.ru/cp/c7/post_1166209200.html|ссылка]]<br />
|-<br />
|<br />
'''Инструкция по оформлению выходных сведений в картографических изданиях. ГКИНП(ГНТА)-15-256-02'''. <br /> Утверждено приказом ФГУГК от 4 февраля 2002 г. № 15-пр. Вводится в действие с 1 мая 2002 г. <br /><br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/law/instr-materials-carto.zip|загрузить]] [[Image:weblink.png|24px|link=http://www.inpravo.ru/data/base803/text803v759i268.htm|ссылка]]<br />
|-<br />
| '''ГКИНП(ГНТА)-01-006-03 Основные положения о государственной геодезической сети Российской Федерации.'''<br /> Утверждены Приказом Федеральной службы геодезии и картографии России от 17 июня 2003 г. N 101-пр<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/gkinp01-006-03.7z|загрузить]] [[Image:weblink.png|24px|link=http://lawrussia.ru/texts/legal_790/doc790a570x935.htm|ссылка]]<br />
|-<br />
| [[Image:new.gif|27px|new]] Инструкция о построении государственной геодезической сети СССР'''<br /> Издание второе, исправленное и дополненное. Москва, "Недра", 1966<br />
|<br />
[http://gis-lab.info/docs/law/build-gosgedezset-66.html подробнее]<br />
|-<br />
|<br />
'''Инструкция по составлению и подготовке к изданию листов Государственной геологической карты Российской Федерации масштаба 1 : 200 000 (Роскомнедра) М., 1995. 244 с. '''<br /><br />
|<br />
[[Image:weblink.png|24px|link=http://info.geol.msu.ru/db/msg.html?mid=1179096&uri=index.html|ссылка]]<br />
|-<br />
|<br />
'''Приказ Минтранса РФ "Об утверждении Административного регламента Федерального агентства геодезии и картографии по предоставлению государственной услуги по обеспечению заинтересованных лиц государственными топографическими картами и планами в графической, цифровой, фотографической и иных формах"<br />'''от 28 сентября 2007 г. N 137<br /> Утверждено постановлением Правительства Российской Федерации от 21 ноября 2006 г. № 705<br /><br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/law/admin-regl-roskart.zip|загрузить]] [[Image:weblink.png|24px|link=http://www.garant.ru/prime/20080201/12058480.htm|ссылка]]<br />
|-<br />
|<br />
'''ГОСТ 21667-76 '''Картография. Термины и определения. Москва, 1976<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/gost21667-76.zip]]<br />
|-<br />
|<br />
'''ГОСТ 28441-99''' Картография цифровая. Термины и определения. Москва, 1999<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/gost28441-99.zip]]<br />
|-<br />
|<br />
'''ГОСТ 51605-2000''' Карты цифровые топографические. Общие требования.<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/gost51605-00.zip]] [[Image:weblink.png|24px|link=http://protect.gost.ru/v.aspx?control=8&baseC=6&page=0&month=5&year=2009&search=ÐÐСТ Ð 51605-2000&RegNum=1&DocOnPageCount=15&id=130477|web]]<br />
|-<br />
|<br />
'''ГОСТ 51606-2000''' Карты цифровые топографические. Система классификации и кодирования цифровой картографической информации. Общие требования.<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/gost51606-00.zip]] [[Image:weblink.png|24px|link=http://protect.gost.ru/v.aspx?control=8&baseC=6&page=0&month=5&year=2009&search=ÐÐСТ Ð 51606-2000&RegNum=1&DocOnPageCount=15&id=130556|web]]<br />
|-<br />
|<br />
'''ГОСТ 51607-2000''' Карты цифровые топографические. Правила цифрового описания картографической информации. Общие требования.<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/gost51607-00.zip]] [[Image:weblink.png|24px|link=http://protect.gost.ru/v.aspx?control=8&baseC=6&page=0&month=5&year=2009&search=ÐÐСТ Ð 51607-2000&RegNum=1&DocOnPageCount=15&id=130638|web]]<br />
|-<br />
|<br />
'''ГОСТ 51608-2000''' Карты цифровые топографические. Требования к качеству.<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/gost51608-00.zip]] [[Image:weblink.png|24px|link=http://protect.gost.ru/v.aspx?control=8&baseC=6&page=0&month=5&year=2009&search=ÐÐСТ Ð 51608-2000&RegNum=1&DocOnPageCount=15&id=130380|web]]<br />
|-<br />
|<br />
'''ГОСТ Р 50836-95''' Геологическая картография. Условные обозначения на картах геологического содержания. Общие правила изображения. [[Image:forumq.gif|14px|link=http://gis-lab.info/forum/viewtopic.php?t=3169|обсудить]]<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/gost50836-95.pdf]] [[Image:weblink.png|24px|link=http://protect.gost.ru/v.aspx?control=8&baseC=6&page=0&month=5&year=2009&search=ÐÐСТ Ð 50836-95&RegNum=1&DocOnPageCount=15&id=126256|web]]<br />
|-<br />
|<br />
'''ГОСТ 22268-76''' Геодезия. Термины и определения<br />
|<br />
[[Image:weblink.png|24px|link=http://protect.gost.ru/v.aspx?control=8&baseC=6&page=0&month=5&year=2009&search=%D0%93%D0%9E%D0%A1%D0%A2%2022268-76&RegNum=1&DocOnPageCount=15&id=153037|web]]<br />
|-<br />
|<br />
'''ГОСТ 23543-88''' Приборы геодезические. Общие технические условия<br />
|<br />
[[Image:weblink.png|24px|link=http://protect.gost.ru/v.aspx?control=8&baseC=6&page=0&month=5&year=2009&search=ÐÐСТ 23543-88&RegNum=1&DocOnPageCount=15&id=131788|web]]<br />
|-<br />
|<br />
'''ГОСТ 4.417-86''' Система показателей качества продукции. Приборы геодезические. Номенклатура показателей.<br />
|<br />
[[Image:weblink.png|24px|link=http://protect.gost.ru/v.aspx?control=8&baseC=6&page=0&month=5&year=2009&search=СиÑÑема показаÑелей каÑеÑÑва пÑодÑкÑии_ ÐÑибоÑÑ Ð³ÐµÐ¾Ð´ÐµÐ·Ð¸ÑеÑкие_ ÐоменклаÑÑÑа показаÑелей&RegNum=1&DocOnPageCount=15&id=133475|web]]<br />
|-<br />
|<br />
'''ГОСТ 1339-79''' Бумага картографическая. Технические условия<br />
|<br />
[[Image:weblink.png|24px|link=http://protect.gost.ru/v.aspx?control=8&baseC=6&page=0&month=5&year=2009&search=ÐÐСТ 1339-79&RegNum=1&DocOnPageCount=15&id=146087|web]]<br />
|-<br />
|<br />
'''ГКИНП (ГНТА)-17-004-99''' Инструкция о порядке контроля и приемке геодезических, топографических и картографических работ.<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/gkinp17-004-99.pdf|загрузить]][[Image:weblink.png|24px|link=http://geo-book.ru/instrukcii001.htm|web]]<br />
|-<br />
| '''ГКИНП-02-033-83''' Инструкция по топографической съемке в масштабах 1:5 000, 1:2 000, 1:1 000 и 1:500. Утверждена ГУГК 05.10.79 г. Введена в действие с 01.01.83 г. с поправками, утвержденными ГУГК 09.09.82 г. (приказ № 436 п). М.: Недра, 1985 (сфера действия общеобязательная)<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/gkinp02-033-82.7z|загрузить]]<br />
|-<br />
| '''ГКИНП (ГНТА)-03-010-02''' Инструкция по нивелированию I, II, III и IV классов. М.: ЦНИИГАиК, 2003<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/gkinp03-010-02.pdf|загрузить]]<br />
|-<br />
| '''ГКИНП-17-241-94''' Инструкция о порядке проектирования и финансирования топографо-геодезических и картографических работ, выполняемых предприятиями и организациями федеральной службы геодезии и картографии России для федеральных государственных нужд. Утверждена приказом N77п, Федеральной службы Геодезии и Картографии 04.08.1994 г<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/gkinp17-241-94.7z|загрузить]]<br />
|-<br />
|<br />
'''ОСТ 68-3.1-98''' Карты цифровые топографические. Общие требования.<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/ost68-31-98.zip]]<br />
|-<br />
|<br />
'''ОСТ 68-3.2-98''' Карты цифровые топографические. Система классификации и кодирования цифровой картографической информации. Общие требования.<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/ost68-32-98.zip]]<br />
|-<br />
|<br />
'''ОСТ 68-3.3-98''' Карты цифровые топографические. Правила цифрового описания картографической информации. Общие требования.<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/ost68-33-98.zip]]<br />
|-<br />
|<br />
'''ОСТ 68-3.4.1-03''' Карты цифровые. Оценка качества данных. Основные положения. Москва, ЦНИИГАиК, 2003<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/ost68-3.4.1-03.rar|загрузить]] [[Image:weblink.png|24px|link=http://www.miit-geo.ru/workers/references|web]]<br />
|-<br />
|<br />
'''ОСТ 68-3.4.2-03''' Карты цифровые. Методы оценки качества данных. Общие требования. Москва, ЦНИИГАиК, 2003<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/ost68-3.4.2-03.rar|загрузить]] [[Image:weblink.png|24px|link=http://www.miit-geo.ru/workers/references|web]]<br />
|-<br />
|<br />
'''ОСТ 68-3.7.1-03''' Цифровые модели местности. Каталог объектов местности. Состав и содержание. Москва, ЦНИИГАиК, 2003<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/ost68-3.7.1-03.rar|загрузить]] [[Image:weblink.png|24px|link=http://www.miit-geo.ru/workers/references|web]]<br />
|-<br />
|<br />
'''ОСТ 68-3.8-03''' Карты цифровые. Программные средства создания цифровой картографической продукции открытого пользования. Общие технические требования. Москва, ЦНИИГАиК, 2003<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/ost68-3.8-03.rar|загрузить]] [[Image:weblink.png|24px|link=http://www.miit-geo.ru/workers/references|web]]<br />
|-<br />
|<br />
'''ОСТ 68-3.4-98''' Карты цифровые топографические. Требования к качеству цифровых топографических карт.<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/ost68-34-98.zip|загрузить]]<br />
|-<br />
|<br />
'''ОСТ 68-3.5-99''' «Карты цифровые топографические. Обменный формат. Общие требования»<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/ost68-35-99.zip]]<br />
|-<br />
|<br />
'''ОСТ 68-3.6-99''' Карты цифровые топографические. Формы представления. Общие требования. Москва, ЦНИИГАиК, 1999<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/ost68-36-99.zip]]<br />
|-<br />
|<br />
'''РТМ 68-3.01-99''' Порядок создания и контроля цифровой картографической продукции открытого пользования. Москва, ЦНИИГАиК, 2000<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/rtm68-3.01-99.rar|download]] [[Image:weblink.png|24px|link=http://www.miit-geo.ru/workers/references|web]]<br />
|-<br />
|<br />
'''РТМ 68-13-99''' Условные графические изображения в документации геодезического и топографического производства. Москва, ЦНИИГАиК, 2000<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/rtm68-13-99.zip|download]]<br />
|-<br />
|<br />
'''РТМ 68-14-01''' Спутниковая технология геодезических работ. Термины и определения. Москва, ЦНИИГАиК, 2001<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/rtm68-14-01.rar|download]] [[Image:weblink.png|24px|link=http://www.miit-geo.ru/workers/references|web]]<br />
|-<br />
|<br />
'''ОСТ 68-14-99''' Виды и процессы геодезической и картографической производственной деятельности. Термины и определения. Москва, ЦНИИГАиК, 2000<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/ost68-14-99.rar|загрузить]] [[Image:weblink.png|24px|link=http://www.miit-geo.ru/workers/references|web]]<br />
|-<br />
|<br />
'''ОСТ 68-15-01''' Измерения геодезические. Термины и определения. Москва, ЦНИИГАиК, 2001<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/ost68-15-01.rar|загрузить]] [[Image:weblink.png|24px|link=http://www.miit-geo.ru/workers/references|web]]<br />
|-<br />
|<br />
'''ОСТ 68-16-02''' Приборы картографические. Общие технические условия. Москва, ЦНИИГАиК, 2002<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/ost68-16-02.rar|загрузить]] [[Image:weblink.png|24px|link=http://www.miit-geo.ru/workers/references|web]]<br />
|-<br />
|<br />
'''ОСТ 68-17-03''' Издания в системе Роскартографии. Термины и определения. Москва, ЦНИИГАиК, 2003<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/ost68-17-03.rar|загрузить]] [[Image:weblink.png|24px|link=http://www.miit-geo.ru/workers/references|web]]<br />
|-<br />
| '''Положение об охранных зонах и охране геодезических пунктов на территории Российской Федерации.'''<br /> Утверждено постановлением правительства РФ № 1170 от 7 октября 1996 г.<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/geodes-protect-n1170-1996.7z|загрузить]] [[Image:weblink.png|24px|link=http://www.businesspravo.ru/Docum/DocumShow_DocumID_47620.html|web]]<br />
|}<br />
<br />
==Территориальное планирование==<br />
{| class="wikitable" width="100%"<br />
|-<br />
! width="90%" class="unsortable"|<br />
!! width="10%" class="unsortable"|<br />
|-<br />
|'''Об утверждении требований к описанию и отображению в документах территориального планирования объектов федерального значения, объектов регионального значения, объектов местного значения'''<br/><br />
Приказ Министерства регионального развития РФ от 30 января 2012 г. N 19<br />
определяет требования к цифровому описанию и отображению объектов на картах, <br />
входящих в состав документов территориального планирования Российской Федерации, <br />
субъектов Российской Федерации и муниципальных образований<br />
| [[Требования к описанию и отображению объектов ТП, 2012|подробнее]]<br />
|-<br />
|<br />
'''Градостроительный кодекс Российской Федерации. Статья 15. Подготовка и утверждение схем территориального планирования субъектов Российской Федерации'''<br />
<br />
2. Подготовка схем территориального планирования субъектов Российской<br />
Федерации осуществляется на основании результатов инженерных изысканий, ...<br />
а также с учетом предложений заинтересованных лиц.<br />
...<br />
4. Проект схемы территориального планирования субъекта Российской Федерации<br />
подлежит опубликованию в порядке, установленном для официального опубликования<br />
нормативных правовых актов органов государственной власти субъекта Российской<br />
Федерации, иной официальной информации, не менее чем за три месяца до ее<br />
утверждения и размещается на официальном сайте субъекта Российской<br />
Федерации в сети "Интернет". Опубликованию и размещению подлежат проект<br />
положений о территориальном планировании, предусмотренных частью 5 статьи 14<br />
настоящего Кодекса, и проекты карты (схемы) или нескольких карт (схем),<br />
на которых отображена информация, предусмотренная частью 6 статьи 14<br />
настоящего Кодекса.<br />
5. Схема ....[аналогично 4]<br />
|<br />
[[Image:weblink.png|24px|link=http://www.consultant.ru/popular/gskrf/15_3.html#p409]]<br />
|}<br />
<br />
==Решения судов и сопутствующая информация==<br />
<br />
Примечание: собранные здесь материалы не всегда проверены и являются официальными решениями суда. Если вам известны другие источники, пожалуйста, присылайте ссылки и сами материалы, если они открыты.<br />
<br />
{| class="wikitable" width="100%"<br />
|-<br />
! width="90%" class="unsortable"|<br />
!! width="10%" class="unsortable"|<br />
<br />
|-<br />
| '''И. Сорока VS Следственный отдел по Выборгскому району следственного управления следственного комитета при прокуратуре РФ по Санкт-Петербургу.''' г. Санкт-Петербург. Возбуждено уголовное дело по признакам преступления, предусмотренного п. «в» ч.3 ст.146 УК РФ (нарушение авторских и смежных прав).<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/soroka-25-01-2010.pdf|загрузить]] [[Image:weblink.png|24px|link=http://www.suskpspb.ru/node/837|web]]<br />
|-<br />
| '''Центр прикладной геоинформатики "Терра-Спейс" VS Московская территориальная инспекция государственного геодезического надзора.''' г. Москва. Постановление кассационной инстанции по проверке законности и обоснованности решений (определений, постановлений) арбитражных судов, вступивших в законную силу от 27 апреля 2005 г. Дело N КА-А40/3057-05<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/moscow-27-04-2005.pdf|загрузить]] [[Image:weblink.png|24px|link=http://lawmix.ru/moskow_jude.php?id=8639|web]]<br />
|-<br />
|<br />
'''Издательство "Экспресс" VS ФГУП "Верхневолжское аэрогеодезическое предприятие". '''г. Киров. Постановление Президиума ВАС РФ от 26.06.2007 N 2096/07.<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/kirov-26-06-2007.pdf|загрузить]] [[Image:weblink.png|24px|link=http://www.arbitr.ru/?id_sec=353&id_doc=2241&id_src=ABE30938F2DCD10B23FF3AB8ABE5F080&p=2|web]]<br />
|-<br />
| '''ООО «Землеустроитель» (г. Махачкала) VS Северо-Кавказское межрегиональное управление геодезии и картографии (г. Пятигорск).''' г. Махачкала. 31 июля 2008 г. Дело № А15-1215/2008<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/makhachkala-31-07-2008.pdf|загрузить]] [[Image:weblink.png|24px|link=http://209.85.129.132/search?q=cache:nZDcb9d6paMJ:mahachkala.arbitr.ru/_inc/ais_croc/binfile.asp?id_ac=15&ID=cebfb998-9c6c-4192-9eab-d37bba1ce8da+ÑÑд+ÑеÑÑиÑоÑиалÑной+инÑпекÑии+ÐоÑгеонадзоÑа&cd=20&hl=ru&ct=clnk&gl=ru|web]]<br />
|-<br />
| '''А.Сакун VS ФСБ России.'''<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/sakun-year-unknown.pdf|загрузить]] [[Image:weblink.png|24px|link=http://www.redstar.ru/2007/10/12_10/1_03.html|web]]<br />
|-<br />
| '''Э.Шильников VS ФСБ России'''. г. Москва. Московской окружной военный суд. 29.10.2009<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/shilnikov-29.10.2009.pdf|загрузить]] [[Image:weblink.png|24px|link=http://kp.ru/daily/24386.4/565272/|web]]<br />
|-<br />
| '''К.Веселов VS ФСБ России.''' г. Москва. Московский окружной военный суд (МОВС) 10.10.2007<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/veselov-10-10-2007.pdf|загрузить]] [[Image:weblink.png|24px|link=http://www.fsb.ru/fsb/comment/remark/single.htm!id=10362553@fsbComment.html|web]]<br />
|-<br />
| '''И.Аблинов VS ФСБ России.''' г. Йошкар-Ола. Верховный суд Марий Эл 22.04.2005<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/ablinov-22-04-2005.pdf|загрузить]] [[Image:weblink.png|24px|link=http://www.fsb.ru/fsb/comment/ufsb/single.htm!_print=true&id=10315797@fsbComment.html|web]]<br />
|-<br />
| '''В.Ткаченко и др. VS ФСБ России.''' г. Одинцово. Военный суд РВСН, 1990-е (?)<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/tkachenko-etal-90s.pdf|загрузить]] [[Image:weblink.png|24px|link=http://palker.ru/vasha-karta-bita-50944969.html|web]]<br />
|-<br />
| '''Мосводоканал VS УФСБ по Смоленской области.''' г. Москва. 04.04.2002<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/mosvodokanal-04-04-2002.pdf|загрузить]] [[Image:weblink.png|24px|link=http://www.fsb.ru/fsb/comment/ufsb/single.htm!_print=true&id=10311880@fsbComment.html|web]]<br />
|-<br />
| '''Г.Сипачев VS ФСБ России.''' г. Москва. Московский городской суд. 13.05.2010<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/sipachev-13-05-2010.pdf|загрузить]] [[Image:weblink.png|24px|link=http://www.mos-gorsud.ru/news/?id=282|web]]<br />
|-<br />
| '''В.Лазарь VS ФСБ России.''' г. Москва. Московский городской суд. (дело направлено в суд)<br />
|<br />
[http://gis-lab.info/docs/law/lazar.html подробнее]<br />
|-<br />
| '''Центр геоэкологических исследований Восточно-Сибирского государственного технологического университета (ВСГТУ) VS Управление ФСБ РФ по Республике Бурятия.''' г. Москва. Московский окружной военный суд (МОВС) 10.10.2007<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/shapkhaev-03-06-2005.pdf|загрузить]] [[Image:weblink.png|24px|link=http://www.chekist.ru/print/784|web]]<br />
|-<br />
| '''Экоцентр Дронт VS ФСБ России.''' г. Нижний Новгород.<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/dront-18-09-2008.pdf|загрузить]] [[Image:weblink.png|24px|link=http://enwl.bellona.ru/pipermail/enwl/2008-September/000128.html|web]]<br />
|-<br />
| ''' «Ди-Лито» VS ЗАО «Экспо-Телеком».''' г. Москва. Постановление кассационной инстанции по проверке законности и обоснованности решений (определений, постановлений) арбитражных судов, вступивших в законную силу 13 июня 2007 г. Дело N КГ-А40/4440-07<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/lito-13-06-2007.pdf|загрузить]] [[Image:weblink.png|24px|link=http://www.dom-i-zakon.ru/base/dokument.php?id=25387|web]]<br />
|-<br />
| '''Жучков А.А. (председатель Комитета по управлению муниципальным имуществом и земельным отношениям администрации муниципального образования «Барышский район») VS Юдин П.Г. (заместитель руководителя Управления Федеральной антимонопольной службы по Ульяновской области) г. Барыш Ульяновской области. ''' Решение 19 ноября 2009 года Барышского городского суда Ульяновской области<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/yudin1-19-11-2009.pdf|загрузить]] [[Image:weblink.png|24px|link=http://209.85.229.132/search?q=cache:SiClLtPjLvsJ:ulyanovsk.fas.gov.ru/get_file.php?id=1233&ext=doc+%D0%A1%D0%83%D0%A1%D1%93%D0%A0%D2%91+%D0%A0%D1%9B%D0%A0%D0%8E%D0%A0%D0%88%D0%A0%C2%A9%D0%A0%E2%80%A2%D0%A0%D0%8E%D0%A0%D1%9E%D0%A0%E2%80%99%D0%A0%E2%80%BA%D0%A0%E2%80%A2%D0%A0%D1%9C%D0%A0%C2%98%D0%A0%E2%80%A2+%D0%A0%E2%80%9C%D0%A0%E2%80%A2%D0%A0%D1%9B%D0%A0%E2%80%9D%D0%A0%E2%80%A2%D0%A0%E2%80%94%D0%A0%C2%98%D0%A0%C2%A7%D0%A0%E2%80%A2%D0%A0%D0%8E%D0%A0%D1%99%D0%A0%D1%9B%D0%A0%E2%84%A2+%D0%A0%C2%98+%D0%A0%D1%99%D0%A0%D1%92%D0%A0%C2%A0%D0%A0%D1%9E%D0%A0%D1%9B%D0%A0%|web]]<br />
|-<br />
| '''Жучков А.А. (председатель Комитета по управлению муниципальным имуществом и земельным отношениям администрации муниципального образования «Барышский район») VS Юдин П.Г. (заместитель руководителя Управления Федеральной антимонопольной службы по Ульяновской области) г. Ульяновск. ''' Решение 15 декабря 2009 года Ульяновского областного суда.<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/yudin2-15-12-2009.pdf|загрузить]] [[Image:weblink.png|24px|link=http://uloblsud.ru/index.php?option=com_content&task=view&id=192&Itemid=63&idCard=14327|web]]<br />
|-<br />
| '''"Челябинский дом печати" VS "Абрис". ''' Постановление Федерального арбитражного суда Уральского округа от 29 января 2008 г. N Ф09-27/08/07-С6<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/abris-29-01-2008.zip|загрузить]] [[Image:weblink.png|24px|link=http://www.garant-e.ru/index.php/filemanager/download/1676/name|web]]<br />
|-<br />
| '''Роскартография VS Некто. '''<br /> Блог-пост. 29 мая 2007 г. Находка<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/nakhodka-29-05-2007.pdf|загрузить]] [[Image:weblink.png|24px|link=http://blog.22design.ru/2007/05/blog-post_29.html|web]]<br />
|-<br />
| '''Юкон Инжиниринг VS Верхневолжское окружное управление геодезии и картографии Федерального агентства геодезии и картографии России '''<br /> Постановление ФАС Волго-Вятского округа от 29.06.2006 по делу N А43-3435/2006-10-120<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/yukon-29-06-2006.pdf|загрузить]] [[Image:weblink.png|24px|link=http://www.rdelo.ru/docs/9DB82141E947ECC8C325740D003B8E90.html|web]]<br />
|-<br />
| '''[[Image:new.gif|27px|new]]Сладкая жизнь VS Верхневолжское окружное управление геодезии и картографии Федерального агентства геодезии и картографии России '''<br /> Постановление ФАС Волго-Вятского округа от 16.10.2006 по делу N А43-3307/2006-10-106<br />
|<br />
[[Image:weblink.png|24px|link=http://www.rdelo.ru/docs/1659450C1DD73C36C325740D003C5D75.html|web]]<br />
|-<br />
| '''[[Image:new.gif|27px|new]]Камчатский печатный двор VS Федеральное агентство геодезии и картографии'''<br /> Постановление ФАС Дальневосточного округа от 31 октября 2006 года Дело N Ф03-А24/06-1/3483<br />
|<br />
[[Image:weblink.png|24px|link=http://dokumentiadvokat.ru/documents/id/15201|web]]<br />
|-<br />
| '''«Компания Волгокарт» VS Администрация г. Волгограда '''<br /> Постановление ФАС Поволжского округа от 21.09.2006 по делу N А12-20561/05-С19<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/volgokart-21-09-2006.pdf|загрузить]] [[Image:weblink.png|24px|link=http://www.adviser.su/?option=com_dbase&task=view_doc&id=355952|web]]<br />
|-<br />
| ''' «Компания Волгокарт» VS Администрация г. Волгограда '''<br /> Решение арбитражного суда Волгоградской области. г. Волгоград 10.07.2007 Дело А12-17784/06-С24<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/volgokart-10-07-2007.pdf|загрузить]] [[Image:weblink.png|24px|link=http://www.volgograd.arbitr.ru/svd/cnt/practic/actsdata/tperiodicdocument4735500|web]]<br />
|-<br />
| '''"Карта" Лтд VS Музей-заповедник "Петергоф" '''<br /> Постановление арбитражного суда Северо-Западного округа от 23 декабря 2005 года. Дело N А56-34784/03<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/karta-23-12-2005.pdf|загрузить]] [[Image:weblink.png|24px|link=http://www.zonazakona.ru/law/jude_szo/1157/|web]]<br />
|-<br />
| '''"Дальстройкомплекс" VS Дальневосточная территориальная инспекция геодезического надзора Федеральной службы геодезии и картографии России '''<br /> Постановление ФАС ДВО от 30.12.2004 № Ф03-А73/04-2/3649<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/dalstroy-13-08-2004.pdf|загрузить]] [[Image:weblink.png|24px|link=http://www.khblaws.ru/index.php?ds=52782|web]]<br />
|-<br />
| '''Metro Cash&amp;Carry VS Федеральное государственное унитарное Верхневолжское аэрогеодезическое предприятие '''<br /> Арбитражный суд Нижегородской области, 30 января 2006<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/metro-30-01-2006.pdf|загрузить]] [[Image:weblink.png|24px|link=http://www.adme.ru/dela/karta-bita-10-60375/|web]]<br />
|-<br />
| '''Компания Альянс VS Федеральное государственное унитарное Верхневолжское аэрогеодезическое предприятие'''<br /> Федеральный арбитражный суд Волго-Вятского округа, 8 апреля 2002 года Дело N А43-6349/01-22-210<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/aliance-08-04-2002.pdf|загрузить]] [[Image:weblink.png|24px|link=http://www.i-p.ru/trials/copyright/8/|web]]<br />
|-<br />
| '''Раменка VS Федеральное государственное унитарное Верхневолжское аэрогеодезическое предприятие'''<br /> Арбитражный суд Нижегородской области, от 21 декабря 2004 года Дело N А43-10724/2004-22-285<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/ramenka-09-06-2005.pdf|загрузить]] [[Image:weblink.png|24px|link=http://www.electrocit.ru/index.php?ds=29695|web]]<br />
|-<br />
| '''Связьинвест-Медиа-Волга VS Федеральное государственное унитарное Верхневолжское аэрогеодезическое предприятие'''<br /> Арбитражный суд кассационной инстанции, от 15 июля 2005 года Дело N А43-11517/2004-22-343<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/ramenka-09-06-2005.pdf|загрузить]] [[Image:weblink.png|24px|link=http://www.adviser.su/?option=com_dbase&task=view_doc&id=15514|web]]<br />
|-<br />
| '''Роскартография VS Соломатин, Стыслович'''<br /> 20.10.2006 Московский городской суд<br />
|<br />
[[Image:download.jpeg|24px|link=http://gis-lab.info/docs/law/solomatin-20-10-2006.pdf|загрузить]] [[Image:weblink.png|24px|link=http://www.zemsell.ru/readtext.asp?id=37&type=news|web]]<br />
|}<br />
<br />
==Разыскиваемая нормативно-правовая документация==<br />
<br />
[http://gis-lab.info/qa/law2find.html Список] разыскиваемых документов, помогите нам их найти.<br />
<br />
[[Категория:Право]]</div>Александр Мурыйhttps://wiki.gis-lab.info/index.php?title=%D0%A0%D0%B0%D0%B1%D0%BE%D1%82%D0%B0_%D1%81_%D1%80%D0%B5%D1%81%D1%83%D1%80%D1%81%D0%B0%D0%BC%D0%B8_%D0%B4%D0%BB%D1%8F_%D0%BE%D0%BF%D1%82%D0%B8%D0%BC%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_MapInfo&diff=25226Работа с ресурсами для оптимизации MapInfo2017-02-17T08:09:21Z<p>Александр Мурый: </p>
<hr />
<div>{{Статья|Опубликована|mapinfo-resources}}<br />
<br />
'''Внимание! Действия, производимые в статье, могут нарушать [http://www.pitneybowes.com/us/license-terms-of-use/software-and-data-end-user-license-agreement.html лицензионное соглашение] разработчиков MapInfo — компании Pitney Bowes. Поэтому любые изменения файлов и (или) их дальнейшая публикация производятся на свой страх и риск.'''<br />
<br />
В этой заметке описан способ редактирования диалоговых окон, текстовых объектов и других объектов интерфейса, находящихся в ресурсах динамических библиотек MapInfo.<br />
<br />
Для работы потребуется утилита [http://angusj.com/resourcehacker/resource_hacker.zip Resource Hacker], которая является бесплатной и не требует установки.<br />
<br />
== Работа с диалоговыми окнами ==<br />
<br />
Диалоговые окна в MapInfo — тема довольно интересная. Связано это с тем, что диалоговые окна по большей части являются статическими — их невозможно ни развернуть, ни растянуть, ни запомнить их последующее положение. Практически все окна в MapInfo небольшие по размеру. Возможно, это связано с тем, чтобы окна полностью уменьшались на экране монитора даже на самом низком разрешении. Однако, с развитием технологий и увеличением разрешения экрана, размер окон остался неизменным и всё таким же неудобным. Существуют возможности исправить эти недочёты, о чём и будет сказано в этом разделе.<br />
<br />
После распаковки и запуска программы Resource Hacker открываем файл '''mires.dll''', находящийся в корневой директории MapInfo. Для написании данной статьи использовалась 32-х битная версия MapInfo 15.0. Но файл mires.dll принципиально ничем не отличается в различных версиях, поэтому информация будет также актуальной и для старых версий MapInfo.<br />
<br />
После того, как файл mires.dll будет открыт, необходимо перейти в директорию Dialog. Далее, для примера, поработаем с окном "Закрыть таблицу". Для того, чтобы быстро найти нужный диалог и вдобавок узнать номер ресурса, можно воспользоваться поиском. Диалог и его описание выглядят так:<br />
<br />
[[Файл:Resources1.png|400px|center|Диалог "Закрыть таблицу"]]<br />
<br />
'''2520 DIALOG 21, 30, 203, 98''' — Здесь следует объявление номера ресурса, а также начальные координаты появления окна на экране и размеры окна.<br />
<br />
'''STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU''' —Объявление стилей согласно Win32 API. Полное их описание можно почитать на [https://msdn.microsoft.com/ru-ru/library/windows/desktop/ms632600(v=vs.85).aspx сайте MSDN].<br />
<br />
'''CAPTION "Закрыть таблицу"''' — Заголовок окна<br />
<br />
'''LANGUAGE LANG_RUSSIAN, 0x0''' — Объявление используемого языка ресурса. В изменении нет необходимости<br />
<br />
'''FONT 8, "Helv"''' — Объявление стиля и размера используемого в диалоге шрифта<br />
<br />
Далее следуют объявления компонентов диалога. Рассмотрим только создание статической надписи:<br />
<br />
'''CONTROL "&Закрыть таблицы:"''' — Объявление заголовка для элемента диалога. Подходит преимущественно для текстовых надписей или кнопок.<br />
<br />
'''-1''' - идентификатор элемента диалога, и при этом самая важная его часть. Если для статических надписей это число всегда равно "-1", то для остальных элементов это число уникально. Или почти уникально, в зависимости от выполняемой операции. Например, если в диалоге присутствует кнопка "ОК" или "Сохранить" (для диалога "Сохранить таблицу") или "Восстановить" (для диалога "Восстановить таблицу"), то идентификатор этой кнопки всегда будет "1", что будет означать, что пользователь указал всё необходимое в диалоге и готов применить изменения. Для "Отмены" и "Справки" идентификаторы равны 2 и 5 соответственно, причём в любом диалоге. Например, список типов открытия таблицы ("В новой карте", "В активной карте" и т.д.) фигурирует в нескольких диалогах и в каждом из них имеет один и тот же идентификатор, так как список передаётся туда программно.<br />
<br />
'''STATIC''' - тип элемента диалога. В данном случае статическая строка, на которую ничего не передаётся из другого элемента управления.<br />
<br />
Далее следуют знакомые уже типы стилей оформления и координаты.<br />
<br />
Элементы диалога возможно изменять как путём перемещения и растягивания оных с помощью указателя, так и задавая точные необходимые значения. Например, если необходимо будет поменять размер и сдвинуть какие-то кнопки, то проще это сделать путём редактирования координат и размеров, представленных в числовом виде. При этом необходимо в конце редактирования нажимать клавишу F5 для компиляции ресурса, чтобы изменения сразу вступили в силу, и их можно было увидеть. В случае изменения объектов указателем нажимать каждый раз F5 необязательно (так как изменения буду стразу же видны), однако, это всё равно потребуется в конце всех операций. В конечном итоге, может получиться подобный результат:<br />
<br />
[[Файл:Resources2.png|center|Измененный диалог в MapInfo]]<br />
<br />
После компиляции всех изменений файл '''mires.dll''' необходимо сохранить. Сохранение возможно только при закрытом MapInfo.<br />
<br />
== Работа с динамическими элементами диалога ==<br />
<br />
В диалогах MapInfo не все элементы являются статическими. Некоторые элементы подгружаются посредством программного кода. Самый яркий пример — окно регистрации растра, которое является крайне неудобным для выполнения своих задач.<br />
<br />
[[Файл:Resources3.png|700px|center|Окно регистрации растра]]<br />
<br />
Чтобы его расширить, необходимо помимо растягивания главного окна диалога вытянуть по длине полосы вертикальной и горизонтальной прокрутки. После этого рабочее пространство привязки увеличится. А при желании можно поиграться с другими элементами управления и в итоге получить что-то вроде этого:<br />
<br />
[[Файл:Resources4.png|700px|center|Растянутое окно регистрации растра]]<br />
<br />
Как уже говорилось ранее, если окно растянуть до размеров экрана 1920х1280, то при разрешении экрана, например, 1280х960, окно выползет за пределы и работать станет практически невозможно. За этим необходимо следить.<br />
<br />
== Примеры настройки стилей в диалоговых окнах ==<br />
<br />
=== Пример 1. Стили полей ввода текста. ===<br />
<br />
[[Файл:Resources5.png|700px|center|Диалог правки текстовых объектов]]<br />
<br />
Речь пойдёт о поле ввода текста. Основное неудобство его в том, что диалог нельзя завершить путём нажатия клавиши Enter — необходимо нажимать мышкой на кнопку ОК. Либо посредством клавиши Tab переключаться между элементами диалога до тех пор, пока не дойдём до кнопки ОК. Тогда клавиша Enter сработает. Связано это с тем, что нажатие клавиши Enter в поле ввода текста приводит к началу новой строки и возврату «каретки». Однако эта особенность — не более чем один из стилей оформления данного элемента диалога. Чтобы изменить элемент, нужно кликнуть по нему правой кнопкой мыши и выбрать "Edit Control".<br />
<br />
[[Файл:Resources6.png|center|Окно редактирования стилей и других параметров элемента диалога]]<br />
<br />
Интересующий нас стиль называется '''ES_WANTRETURN'''. После его отключения, компиляции изменений и сохранения файла, текст в этом диалоге (уже в самом MapInfo) можно будет закрыть посредством нажатия клавиши Enter. А так как клавиша Enter больше не создаёт новую строку текста, то для её создания нужно использовать комбинацию Ctrl+Enter. Кроме того, поле ввода текста можно тоже увеличить и получить что-то подобное:<br />
<br />
[[Файл:Resources7.png|center|Растянутый диалог правки текстовых объектов]]<br />
<br />
По этой же технологии можно изменить поле ввода текста для диалога редактирования выносных подписей.<br />
<br />
=== Пример 2. Стили кнопочных элементов. ===<br />
<br />
Снова обратимся к диалогу "Закрыть таблицу" и к кнопкам "Закрыть" и "Отмена".<br />
<br />
'''CONTROL "Закрыть", 1, BUTTON, BS_DEFPUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_GROUP | WS_TABSTOP, 170, 19, 45, 14''' <br />
<br />
''' CONTROL "Отмена", 2, BUTTON, BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_GROUP | WS_TABSTOP, 170, 37, 45, 14''' <br />
<br />
Если нас не устраивает тот факт, что клавиша "Закрыть" подсвечена по-умолчанию, и при нажатии клавиши Enter выбранные таблицы будут закрыты, то можно изменить стиль оформления с '''BS_DEFPUSHBUTTON''' на '''BS_PUSHBUTTON''' у кнопки "Закрыть". При этом необходимо провести обратную операцию для кнопки "Отмена". В этом случае кнопкой по-умолчанию станет она, и результат будет достигнут.<br />
<br />
=== Пример 3. Дополнительные стили кнопочных элементов. ===<br />
<br />
Рассмотрим диалог под номером 9070 из файла '''mires.dll'''.<br />
<br />
[[Файл:Resources10.png|700px|center|Динамический диалог]]<br />
<br />
Диалог интересен тем, что является динамическим, то есть его содержимое меняется в зависимости от запросов MapInfo. Кроме того, программно устанавливается, какая кнопка будет главной (подсвеченной) по-умолчанию. Например, если в MapInfo подгружена только одна таблица, в которой произошли изменения, и есть необходимость восстановить её, то MapInfo выдаст соответствующий запрос, в котором предложит либо восстановить таблицу, либо отменить и закрыть диалог. При этом кнопка "Восстановить" (та же кнопка "ОК", только с заменённым текстом) будет в приоритете, хотя стиль '''BS_DEFPUSHBUTTON''' не установлен. В других случаях, таких как изменение типа полей таблицы, может возникать другое окно, где уже кнопка "Отмена" будет в приоритете.<br />
<br />
Предположим, что существует необходимость, чтобы кнопка "Отмена" никогда не бывала активной по-умолчанию. Но тут возникает интересная особенность. Кнопка "Отмена" вместо ID 2 имеет ID 4. Попытка смены ID даёт то, что кнопка перестаёт отображаться вообще. Попытка принудительно назначить кнопке "ОК" стиль '''BS_DEFPUSHBUTTON''' ничего не даёт. Поэтому альтернативным вариантом решения является возможность отключения этой кнопки, так как диалог в любом случае можно закрыть с помощью клавиши Esc. Чтобы отключить кнопку, необходимо применить для кнопки стиль '''WS_DISABLED'''. После чего можно получить такой результат:<br />
<br />
[[Файл:Resources11.png|center|Отключенная кнопка]]<br />
<br />
== Добавление и изменение текстовых констант ==<br />
<br />
В этом разделе речь пойдёт о текстовых строках, которые также содержатся в ресурсах. Основные файлы, где они хранятся — '''mires.dll''' и '''micore.dll'''. Рассматриваться будет '''micore.dll'''. Необходимо открыть файл, перейти в директорию '''String Table''' и открыть раздел строк под номером 393.<br />
<br />
[[Файл:Resources8.png|700px|center|Текстовые строки]]<br />
<br />
Как видно из примера, здесь представлено некоторое количество строк. Некоторые из них специальные, содержащие переменные, в которые будут подставлены названия таблиц или колонок. Однако, нас интересуют строки под номерами 6276-6279. Там представлены функции соответствующего списка из диалога "SQL-запрос". Если следовать представленному синтаксису, то можно дописать в этот раздел свои собственные функции. Например, можно добавить функции ObjectInfo и ObjectGeography в строку 6278. После сохранения и компиляции результат будет выглядеть так:<br />
<br />
[[Файл:Resources9.png|center|Новые добавленные функции]]<br />
<br />
== Заключение ==<br />
<br />
В статье показаны варианты редактирования диалоговых окон и их элементов. Возможности добавления своих элементов сильно ограничены, так как основная их часть должна быть запрограммирована для достижения тех или иных результатов, чего через ресурсы выполнить невозможно. Но тем не менее, при определённых навыках и творчестве можно получить интересные варианты.<br />
<br />
На этом всё. Успешной работы!</div>Александр Мурыйhttps://wiki.gis-lab.info/index.php?title=%D0%A0%D0%B0%D0%B1%D0%BE%D1%82%D0%B0_%D1%81_%D1%80%D0%B5%D1%81%D1%83%D1%80%D1%81%D0%B0%D0%BC%D0%B8_%D0%B4%D0%BB%D1%8F_%D0%BE%D0%BF%D1%82%D0%B8%D0%BC%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_MapInfo&diff=25225Работа с ресурсами для оптимизации MapInfo2017-02-17T08:02:34Z<p>Александр Мурый: </p>
<hr />
<div>{{Статья|Черновик}}<br />
<br />
'''Внимание! Действия, производимые в статье, могут нарушать [http://www.pitneybowes.com/us/license-terms-of-use/software-and-data-end-user-license-agreement.html лицензионное соглашение] разработчиков MapInfo — компании Pitney Bowes. Поэтому любые изменения файлов и (или) их дальнейшая публикация производятся на свой страх и риск.'''<br />
<br />
В этой заметке описан способ редактирования диалоговых окон, текстовых объектов и других объектов интерфейса, находящихся в ресурсах динамических библиотек MapInfo.<br />
<br />
Для работы потребуется утилита [http://angusj.com/resourcehacker/resource_hacker.zip Resource Hacker], которая является бесплатной и не требует установки.<br />
<br />
== Работа с диалоговыми окнами ==<br />
<br />
Диалоговые окна в MapInfo — тема довольно интересная. Связано это с тем, что диалоговые окна по большей части являются статическими — их невозможно ни развернуть, ни растянуть, ни запомнить их последующее положение. Практически все окна в MapInfo небольшие по размеру. Возможно, это связано с тем, чтобы окна полностью уменьшались на экране монитора даже на самом низком разрешении. Однако, с развитием технологий и увеличением разрешения экрана, размер окон остался неизменным и всё таким же неудобным. Существуют возможности исправить эти недочёты, о чём и будет сказано в этом разделе.<br />
<br />
После распаковки и запуска программы Resource Hacker открываем файл '''mires.dll''', находящийся в корневой директории MapInfo. Для написании данной статьи использовалась 32-х битная версия MapInfo 15.0. Но файл mires.dll принципиально ничем не отличается в различных версиях, поэтому информация будет также актуальной и для старых версий MapInfo.<br />
<br />
После того, как файл mires.dll будет открыт, необходимо перейти в директорию Dialog. Далее, для примера, поработаем с окном "Закрыть таблицу". Для того, чтобы быстро найти нужный диалог и вдобавок узнать номер ресурса, можно воспользоваться поиском. Диалог и его описание выглядят так:<br />
<br />
[[Файл:Resources1.png|400px|center|Диалог "Закрыть таблицу"]]<br />
<br />
'''2520 DIALOG 21, 30, 203, 98''' — Здесь следует объявление номера ресурса, а также начальные координаты появления окна на экране и размеры окна.<br />
<br />
'''STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU''' —Объявление стилей согласно Win32 API. Полное их описание можно почитать на [https://msdn.microsoft.com/ru-ru/library/windows/desktop/ms632600(v=vs.85).aspx сайте MSDN].<br />
<br />
'''CAPTION "Закрыть таблицу"''' — Заголовок окна<br />
<br />
'''LANGUAGE LANG_RUSSIAN, 0x0''' — Объявление используемого языка ресурса. В изменении нет необходимости<br />
<br />
'''FONT 8, "Helv"''' — Объявление стиля и размера используемого в диалоге шрифта<br />
<br />
Далее следуют объявления компонентов диалога. Рассмотрим только создание статической надписи:<br />
<br />
'''CONTROL "&Закрыть таблицы:"''' — Объявление заголовка для элемента диалога. Подходит преимущественно для текстовых надписей или кнопок.<br />
<br />
'''-1''' - идентификатор элемента диалога, и при этом самая важная его часть. Если для статических надписей это число всегда равно "-1", то для остальных элементов это число уникально. Или почти уникально, в зависимости от выполняемой операции. Например, если в диалоге присутствует кнопка "ОК" или "Сохранить" (для диалога "Сохранить таблицу") или "Восстановить" (для диалога "Восстановить таблицу"), то идентификатор этой кнопки всегда будет "1", что будет означать, что пользователь указал всё необходимое в диалоге и готов применить изменения. Для "Отмены" и "Справки" идентификаторы равны 2 и 5 соответственно, причём в любом диалоге. Например, список типов открытия таблицы ("В новой карте", "В активной карте" и т.д.) фигурирует в нескольких диалогах и в каждом из них имеет один и тот же идентификатор, так как список передаётся туда программно.<br />
<br />
'''STATIC''' - тип элемента диалога. В данном случае статическая строка, на которую ничего не передаётся из другого элемента управления.<br />
<br />
Далее следуют знакомые уже типы стилей оформления и координаты.<br />
<br />
Элементы диалога возможно изменять как путём перемещения и растягивания оных с помощью указателя, так и задавая точные необходимые значения. Например, если необходимо будет поменять размер и сдвинуть какие-то кнопки, то проще это сделать путём редактирования координат и размеров, представленных в числовом виде. При этом необходимо в конце редактирования нажимать клавишу F5 для компиляции ресурса, чтобы изменения сразу вступили в силу, и их можно было увидеть. В случае изменения объектов указателем нажимать каждый раз F5 необязательно (так как изменения буду стразу же видны), однако, это всё равно потребуется в конце всех операций. В конечном итоге, может получиться подобный результат:<br />
<br />
[[Файл:Resources2.png|center|Измененный диалог в MapInfo]]<br />
<br />
После компиляции всех изменений файл '''mires.dll''' необходимо сохранить. Сохранение возможно только при закрытом MapInfo.<br />
<br />
== Работа с динамическими элементами диалога ==<br />
<br />
В диалогах MapInfo не все элементы являются статическими. Некоторые элементы подгружаются посредством программного кода. Самый яркий пример — окно регистрации растра, которое является крайне неудобным для выполнения своих задач.<br />
<br />
[[Файл:Resources3.png|700px|center|Окно регистрации растра]]<br />
<br />
Чтобы его расширить, необходимо помимо растягивания главного окна диалога вытянуть по длине полосы вертикальной и горизонтальной прокрутки. После этого рабочее пространство привязки увеличится. А при желании можно поиграться с другими элементами управления и в итоге получить что-то вроде этого:<br />
<br />
[[Файл:Resources4.png|700px|center|Растянутое окно регистрации растра]]<br />
<br />
Как уже говорилось ранее, если окно растянуть до размеров экрана 1920х1280, то при разрешении экрана, например, 1280х960, окно выползет за пределы и работать станет практически невозможно. За этим необходимо следить.<br />
<br />
== Примеры настройки стилей в диалоговых окнах ==<br />
<br />
=== Пример 1. Стили полей ввода текста. ===<br />
<br />
[[Файл:Resources5.png|700px|center|Диалог правки текстовых объектов]]<br />
<br />
Речь пойдёт о поле ввода текста. Основное неудобство его в том, что диалог нельзя завершить путём нажатия клавиши Enter — необходимо нажимать мышкой на кнопку ОК. Либо посредством клавиши Tab переключаться между элементами диалога до тех пор, пока не дойдём до кнопки ОК. Тогда клавиша Enter сработает. Связано это с тем, что нажатие клавиши Enter в поле ввода текста приводит к началу новой строки и возврату «каретки». Однако эта особенность — не более чем один из стилей оформления данного элемента диалога. Чтобы изменить элемент, нужно кликнуть по нему правой кнопкой мыши и выбрать "Edit Control".<br />
<br />
[[Файл:Resources6.png|center|Окно редактирования стилей и других параметров элемента диалога]]<br />
<br />
Интересующий нас стиль называется '''ES_WANTRETURN'''. После его отключения, компиляции изменений и сохранения файла, текст в этом диалоге (уже в самом MapInfo) можно будет закрыть посредством нажатия клавиши Enter. А так как клавиша Enter больше не создаёт новую строку текста, то для её создания нужно использовать комбинацию Ctrl+Enter. Кроме того, поле ввода текста можно тоже увеличить и получить что-то подобное:<br />
<br />
[[Файл:Resources7.png|center|Растянутый диалог правки текстовых объектов]]<br />
<br />
По этой же технологии можно изменить поле ввода текста для диалога редактирования выносных подписей.<br />
<br />
=== Пример 2. Стили кнопочных элементов. ===<br />
<br />
Снова обратимся к диалогу "Закрыть таблицу" и к кнопкам "Закрыть" и "Отмена".<br />
<br />
'''CONTROL "Закрыть", 1, BUTTON, BS_DEFPUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_GROUP | WS_TABSTOP, 170, 19, 45, 14''' <br />
<br />
''' CONTROL "Отмена", 2, BUTTON, BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_GROUP | WS_TABSTOP, 170, 37, 45, 14''' <br />
<br />
Если нас не устраивает тот факт, что клавиша "Закрыть" подсвечена по-умолчанию, и при нажатии клавиши Enter выбранные таблицы будут закрыты, то можно изменить стиль оформления с '''BS_DEFPUSHBUTTON''' на '''BS_PUSHBUTTON''' у кнопки "Закрыть". При этом необходимо провести обратную операцию для кнопки "Отмена". В этом случае кнопкой по-умолчанию станет она, и результат будет достигнут.<br />
<br />
=== Пример 3. Дополнительные стили кнопочных элементов. ===<br />
<br />
Рассмотрим диалог под номером 9070 из файла '''mires.dll'''.<br />
<br />
[[Файл:Resources10.png|700px|center|Динамический диалог]]<br />
<br />
Диалог интересен тем, что является динамическим, то есть его содержимое меняется в зависимости от запросов MapInfo. Кроме того, программно устанавливается, какая кнопка будет главной (подсвеченной) по-умолчанию. Например, если в MapInfo подгружена только одна таблица, в которой произошли изменения, и есть необходимость восстановить её, то MapInfo выдаст соответствующий запрос, в котором предложит либо восстановить таблицу, либо отменить и закрыть диалог. При этом кнопка "Восстановить" (та же кнопка "ОК", только с заменённым текстом) будет в приоритете, хотя стиль '''BS_DEFPUSHBUTTON''' не установлен. В других случаях, таких как изменение типа полей таблицы, может возникать другое окно, где уже кнопка "Отмена" будет в приоритете.<br />
<br />
Предположим, что существует необходимость, чтобы кнопка "Отмена" никогда не бывала активной по-умолчанию. Но тут возникает интересная особенность. Кнопка "Отмена" вместо ID 2 имеет ID 4. Попытка смены ID даёт то, что кнопка перестаёт отображаться вообще. Попытка принудительно назначить кнопке "ОК" стиль '''BS_DEFPUSHBUTTON''' ничего не даёт. Поэтому альтернативным вариантом решения является возможность отключения этой кнопки, так как диалог в любом случае можно закрыть с помощью клавиши Esc. Чтобы отключить кнопку, необходимо применить для кнопки стиль '''WS_DISABLED'''. После чего можно получить такой результат:<br />
<br />
[[Файл:Resources11.png|center|Отключенная кнопка]]<br />
<br />
== Добавление и изменение текстовых констант ==<br />
<br />
В этом разделе речь пойдёт о текстовых строках, которые также содержатся в ресурсах. Основные файлы, где они хранятся — '''mires.dll''' и '''micore.dll'''. Рассматриваться будет '''micore.dll'''. Необходимо открыть файл, перейти в директорию '''String Table''' и открыть раздел строк под номером 393.<br />
<br />
[[Файл:Resources8.png|700px|center|Текстовые строки]]<br />
<br />
Как видно из примера, здесь представлено некоторое количество строк. Некоторые из них специальные, содержащие переменные, в которые будут подставлены названия таблиц или колонок. Однако, нас интересуют строки под номерами 6276-6279. Там представлены функции соответствующего списка из диалога "SQL-запрос". Если следовать представленному синтаксису, то можно дописать в этот раздел свои собственные функции. Например, можно добавить функции ObjectInfo и ObjectGeography в строку 6278. После сохранения и компиляции результат будет выглядеть так:<br />
<br />
[[Файл:Resources9.png|center|Новые добавленные функции]]<br />
<br />
== Заключение ==<br />
<br />
В статье показаны варианты редактирования диалоговых окон и их элементов. Возможности добавления своих элементов сильно ограничены, так как основная их часть должна быть запрограммирована для достижения тех или иных результатов, чего через ресурсы выполнить невозможно. Но тем не менее, при определённых навыках и творчестве можно получить интересные варианты.<br />
<br />
На этом всё. Успешной работы!</div>Александр Мурыйhttps://wiki.gis-lab.info/index.php?title=%D0%A0%D0%B0%D0%B1%D0%BE%D1%82%D0%B0_%D1%81_%D1%80%D0%B5%D1%81%D1%83%D1%80%D1%81%D0%B0%D0%BC%D0%B8_%D0%B4%D0%BB%D1%8F_%D0%BE%D0%BF%D1%82%D0%B8%D0%BC%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_MapInfo&diff=25222Работа с ресурсами для оптимизации MapInfo2017-01-25T12:57:51Z<p>Александр Мурый: </p>
<hr />
<div>{{Статья|Черновик}}<br />
<br />
'''Внимание! Действия, производимые в статье, могут нарушать [http://www.pitneybowes.com/us/license-terms-of-use/software-and-data-end-user-license-agreement.html лицензионное соглашение] разработчиков MapInfo — компании Pitney Bowes. Поэтому любые изменения файлов и (или) их дальнейшая публикация производятся на свой страх и риск.'''<br />
<br />
В этой заметке описан способ редактирования диалоговых окон, текстовых объектов и других объектов интерфейса, находящиеся в ресурсах динамических библиотек MapInfo.<br />
<br />
Для работы потребуется утилита [http://angusj.com/resourcehacker/resource_hacker.zip Resource Hacker], которая является бесплатной и не требует установки.<br />
<br />
== Работа с диалоговыми окнами ==<br />
<br />
Диалоговые окна в MapInfo — тема довольно интересная. Связано это с тем, что диалоговые окна по большей части являются статическими — их невозможно ни развернуть, ни растянуть, ни запомнить их последующее положение. Практически все окна в MapInfo небольшие по размеру. Возможно, это связано с тем, чтобы окна полностью уменьшались на экране монитора даже на самом низком разрешении. Однако, с развитием технологий и увеличением разрешения экрана, размер окон остался неизменным и всё таким же неудобным. Существуют возможности исправить эти недочёты, о чём и будет сказано в этом разделе.<br />
<br />
После распаковки и запуска программы Resource Hacker открываем файл '''mires.dll''', находящийся в корневой директории MapInfo. Для написании данной статьи использовалась 32-х битная версия MapInfo 15.0. Но файл mires.dll принципиально ничем не отличается в различных версиях, поэтому информация будет также актуальной и для старых версий MapInfo.<br />
<br />
После того, как файл mires.dll будет открыт, необходимо перейти в директорию Dialog. Далее, для примера, поработаем с окном "Закрыть таблицу". Для того, чтобы быстро найти нужный диалог и вдобавок узнать номер ресурса, можно воспользоваться поиском. Диалог и его описание выглядят так:<br />
<br />
[[Файл:Resources1.png|400px|center|Диалог "Закрыть таблицу"]]<br />
<br />
'''2520 DIALOG 21, 30, 203, 98''' — Здесь следует объявление номера ресурса, а также начальные координаты появления окна на экране и размеры окна.<br />
<br />
'''STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU''' —Объявление стилей согласно Win32 API. Полное их описание можно почитать на [https://msdn.microsoft.com/ru-ru/library/windows/desktop/ms632600(v=vs.85).aspx сайте MSDN].<br />
<br />
'''CAPTION "Закрыть таблицу"''' — Заголовок окна<br />
<br />
'''LANGUAGE LANG_RUSSIAN, 0x0''' — Объявление используемого языка ресурса. В изменении нет необходимости<br />
<br />
'''FONT 8, "Helv"''' — Объявление стиля и размера используемого в диалоге шрифта<br />
<br />
Далее следуют объявления компонентов диалога. Рассмотрим только создание статической надписи:<br />
<br />
'''CONTROL "&Закрыть таблицы:"''' — Объявление заголовка для элемента диалога. Подходит преимущественно для текстовых надписей или кнопок.<br />
<br />
'''-1''' - идентификатор элемента диалога, и при этом самая важная его часть. Если для статических надписей это число всегда равно "-1", то для остальных элементов это число уникально. Или почти уникально, в зависимости от выполняемой операции. Например, если в диалоге присутствует кнопка "ОК" или "Сохранить" (для диалога "Сохранить таблицу") или "Восстановить" (для диалога "Восстановить таблицу"), то идентификатор этой кнопки всегда будет "1", что будет означать, что пользователь указал всё необходимое в диалоге и готов применить изменения. Для "Отмены" и "Справки" идентификаторы равны 2 и 5 соответственно, причём в любом диалоге. Например, список типов открытия таблицы ("В новой карте", "В активной карте" и т.д.) фигурирует в нескольких диалогах и в каждом из них имеет один и тот же идентификатор, так как список передаётся туда программно.<br />
<br />
'''STATIC''' - тип элемента диалога. В данном случае статическая строка, на которую ничего не передаётся из другого элемента управления.<br />
<br />
Далее следуют знакомые уже типы стилей оформления и координаты.<br />
<br />
Элементы диалога возможно изменять как путём перемещения и растягивания оных с помощью указателя, так и задавая точные необходимые значения. Например, если необходимо будет поменять размер и сдвинуть какие-то кнопки, то проще это сделать путём редактирования координат и размеров, представленных в числовом виде. При этом необходимо в конце редактирования нажимать клавишу F5 для компиляции ресурса, чтобы изменения сразу вступили в силу, и их можно было увидеть. В случае изменения объектов указателем нажимать каждый раз F5 необязательно (так как изменения буду стразу же видны), однако, это всё равно потребуется в конце всех операций. В конечном итоге, может получиться подобный результат:<br />
<br />
[[Файл:Resources2.png|center|Измененный диалог в MapInfo]]<br />
<br />
После компиляции всех изменений файл '''mires.dll''' необходимо сохранить. Сохранение возможно только при закрытом MapInfo.<br />
<br />
== Работа с динамическими элементами диалога ==<br />
<br />
В диалогах MapInfo не все элементы являются статическими. Некоторые элементы подгружаются посредством программного кода. Самый яркий пример — окно регистрации растра, которое является крайне неудобным для выполнения своих задач.<br />
<br />
[[Файл:Resources3.png|700px|center|Окно регистрации растра]]<br />
<br />
Чтобы его расширить, необходимо помимо растягивания главного окна диалога вытянуть по длине полосы вертикальной и горизонтальной прокрутки. После этого рабочее пространство привязки увеличится. А при желании можно поиграться с другими элементами управления и в итоге получить что-то вроде этого:<br />
<br />
[[Файл:Resources4.png|700px|center|Растянутое окно регистрации растра]]<br />
<br />
Как уже говорилось ранее, если окно растянуть до размеров экрана 1920х1280, то при разрешении экрана, например, 1280х960, окно выползет за пределы и работать станет практически невозможно. За этим необходимо следить.<br />
<br />
== Примеры настройки стилей в диалоговых окнах ==<br />
<br />
=== Пример 1. Стили полей ввода текста. ===<br />
<br />
[[Файл:Resources5.png|700px|center|Диалог правки текстовых объектов]]<br />
<br />
Речь пойдёт о поле ввода текста. Основное неудобство его в том, что диалог нельзя завершить путём нажатия клавиши Enter — необходимо нажимать мышкой на кнопку ОК. Либо посредством клавиши Tab переключаться между элементами диалога до тех пор, пока не дойдём до кнопки ОК. Тогда клавиша Enter сработает. Связано это с тем, что нажатие клавиши Enter в поле ввода текста приводит к началу новой строки и возврату «каретки». Однако эта особенность — не более чем один из стилей оформления данного элемента диалога. Чтобы изменить элемент, нужно кликнуть по нему правой кнопкой мыши и выбрать "Edit Control".<br />
<br />
[[Файл:Resources6.png|center|Окно редактирования стилей и других параметров элемента диалога]]<br />
<br />
Интересующий нас стиль называется '''ES_WANTRETURN'''. После его отключения, компиляции изменений и сохранения файла, текст в этом диалоге (уже в самом MapInfo) можно будет закрыть посредством нажатия клавиши Enter. А так как клавиша Enter больше не создаёт новую строку текста, то для её создания нужно использовать комбинацию Ctrl+Enter. Кроме того, поле ввода текста можно тоже увеличить и получить что-то подобное:<br />
<br />
[[Файл:Resources7.png|center|Растянутый диалог правки текстовых объектов]]<br />
<br />
По этой же технологии можно изменить поле ввода текста для диалога редактирования выносных подписей.<br />
<br />
=== Пример 2. Стили кнопочных элементов. ===<br />
<br />
Снова обратимся к диалогу "Закрыть таблицу" и к кнопкам "Закрыть" и "Отмена".<br />
<br />
'''CONTROL "Закрыть", 1, BUTTON, BS_DEFPUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_GROUP | WS_TABSTOP, 170, 19, 45, 14''' <br />
<br />
''' CONTROL "Отмена", 2, BUTTON, BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_GROUP | WS_TABSTOP, 170, 37, 45, 14''' <br />
<br />
Если нас не устраивает тот факт, что клавиша "Закрыть" подсвечена по-умолчанию, и при нажатии клавиши Enter выбранные таблицы будут закрыты, то можно изменить стиль оформления с '''BS_DEFPUSHBUTTON''' на '''BS_PUSHBUTTON''' у кнопки "Закрыть". При этом необходимо провести обратную операцию для кнопки "Отмена". В этом случае кнопкой по-умолчанию станет она, и результат будет достигнут.<br />
<br />
=== Пример 3. Дополнительные стили кнопочных элементов. ===<br />
<br />
Рассмотрим диалог под номером 9070 из файла '''mires.dll'''.<br />
<br />
[[Файл:Resources10.png|700px|center|Динамический диалог]]<br />
<br />
Диалог интересен тем, что является динамическим, то есть его содержимое меняется в зависимости от запросов MapInfo. Кроме того, программно устанавливается, какая кнопка будет главной (подсвеченной) по-умолчанию. Например, если в MapInfo подгружена только одна таблица, в которой произошли изменения, и есть необходимость восстановить её, то MapInfo выдаст соответствующий запрос, в котором предложит либо восстановить таблицу, либо отменить и закрыть диалог. При этом кнопка "Восстановить" (та же кнопка "ОК", только с заменённым текстом) будет в приоритете, хотя стиль '''BS_DEFPUSHBUTTON''' не установлен. В других случаях, таких как изменение типа полей таблицы, может возникать другое окно, где уже кнопка "Отмена" будет в приоритете.<br />
<br />
Предположим, что существует необходимость, чтобы кнопка "Отмена" никогда не бывала активной по-умолчанию. Но тут возникает интересная особенность. Кнопка "Отмена" вместо ID 2 имеет ID 4. Попытка смены ID даёт то, что кнопка перестаёт отображаться вообще. Попытка принудительно назначить кнопке "ОК" стиль '''BS_DEFPUSHBUTTON''' ничего не даёт. Поэтому альтернативным вариантом решения является возможность отключения этой кнопки, так как диалог в любом случае можно закрыть с помощью клавиши Esc. Чтобы отключить кнопку, необходимо применить для кнопки стиль '''WS_DISABLED'''. После чего можно получить такой результат:<br />
<br />
[[Файл:Resources11.png|center|Отключенная кнопка]]<br />
<br />
== Добавление и изменение текстовых констант ==<br />
<br />
В этом разделе речь пойдёт о текстовых строках, которые также содержатся в ресурсах. Основные файлы, где они хранятся — '''mires.dll''' и '''micore.dll'''. Рассматриваться будет '''micore.dll'''. Необходимо открыть файл, перейти в директорию '''String Table''' и открыть раздел строк под номером 393.<br />
<br />
[[Файл:Resources8.png|700px|center|Текстовые строки]]<br />
<br />
Как видно из примера, здесь представлено некоторое количество строк. Некоторые из них специальные, содержащие переменные, в которые будут подставлены названия таблиц или колонок. Однако, нас интересуют строки под номерами 6276-6279. Там представлены функции соответствующего списка из диалога "SQL-запрос". Если следовать представленному синтаксису, то можно дописать в этот раздел свои собственные функции. Например, можно добавить функции ObjectInfo и ObjectGeography в строку 6278. После сохранения и компиляции результат будет выглядеть так:<br />
<br />
[[Файл:Resources9.png|center|Новые добавленные функции]]<br />
<br />
== Заключение ==<br />
<br />
В статье показаны варианты редактирования диалоговых окон и их элементов. Возможности добавления своих элементов сильно ограничены, так как основная их часть должна быть запрограммирована для достижения тех или иных результатов, чего через ресурсы выполнить невозможно. Но тем не менее, при определённых навыках и творчестве можно получить интересные варианты.<br />
<br />
На этом всё. Успешной работы!</div>Александр Мурыйhttps://wiki.gis-lab.info/index.php?title=%D0%A0%D0%B0%D0%B1%D0%BE%D1%82%D0%B0_%D1%81_%D1%80%D0%B5%D1%81%D1%83%D1%80%D1%81%D0%B0%D0%BC%D0%B8_%D0%B4%D0%BB%D1%8F_%D0%BE%D0%BF%D1%82%D0%B8%D0%BC%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_MapInfo&diff=25221Работа с ресурсами для оптимизации MapInfo2017-01-25T12:56:32Z<p>Александр Мурый: </p>
<hr />
<div>{{Статья|Черновик}}<br />
<br />
'''Внимание! Действия, производимые в статье, могут нарушать [http://www.pitneybowes.com/us/license-terms-of-use/software-and-data-end-user-license-agreement.html лицензионное соглашение] разработчиков MapInfo — компании Pitney Bowes. Поэтому любые изменения файлов и (или) их дальнейшая публикация производятся на свой страх и риск.'''<br />
<br />
В этой заметке описан способ редактирования диалоговых окон, текстовых объектов и других объектов интерфейса, находящиеся в ресурсах динамических библиотек MapInfo.<br />
<br />
Для работы потребуется утилита [http://angusj.com/resourcehacker/resource_hacker.zip Resource Hacker], которая является бесплатной и не требует установки.<br />
<br />
== Работа с диалоговыми окнами ==<br />
<br />
Диалоговые окна в MapInfo — тема довольно интересная. Связано это с тем, что диалоговые окна по большей части являются статическими — их невозможно ни развернуть, ни растянуть, ни запомнить их последующее положение. Практически все окна в MapInfo небольшие по размеру. Возможно, это связано с тем, чтобы окна полностью уменьшались на экране монитора даже на самом низком разрешении. Однако, с развитием технологий и увеличением разрешения экрана, размер окон остался неизменным и всё таким же неудобным. Существуют возможности исправить эти недочёты, о чём и будет сказано в этом разделе.<br />
<br />
После распаковки и запуска программы Resource Hacker открываем файл '''mires.dll''', находящийся в корневой директории MapInfo. Для написании данной статьи использовалась 32-х битная версия MapInfo 15.0. Но файл mires.dll принципиально ничем не отличается в различных версиях, поэтому информация будет также актуальной и для старых версий MapInfo.<br />
<br />
После того, как файл mires.dll будет открыт, необходимо перейти в директорию Dialog. Далее, для примера, поработаем с окном "Закрыть таблицу". Для того, чтобы быстро найти нужный диалог и вдобавок узнать номер ресурса, можно воспользоваться поиском. Диалог и его описание выглядят так:<br />
<br />
[[Файл:Resources1.png|400px|center|Диалог "Закрыть таблицу"]]<br />
<br />
'''2520 DIALOG 21, 30, 203, 98''' — Здесь следует объявление номера ресурса, а также начальные координаты появления окна на экране и размеры окна.<br />
<br />
'''STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU''' —Объявление стилей согласно Win32 API. Полное их описание можно почитать на [https://msdn.microsoft.com/ru-ru/library/windows/desktop/ms632600(v=vs.85).aspx сайте MSDN].<br />
<br />
'''CAPTION "Закрыть таблицу"''' — Заголовок окна<br />
<br />
'''LANGUAGE LANG_RUSSIAN, 0x0''' — Объявление используемого языка ресурса. В изменении нет необходимости<br />
<br />
'''FONT 8, "Helv"''' — Объявление стиля и размера используемого в диалоге шрифта<br />
<br />
Далее следуют объявления компонентов диалога. Рассмотрим только создание статической надписи:<br />
<br />
'''CONTROL "&Закрыть таблицы:"''' — Объявление заголовка для элемента диалога. Подходит преимущественно для текстовых надписей или кнопок.<br />
<br />
'''-1''' - идентификатор элемента диалога, и при этом самая важная его часть. Если для статических надписей это число всегда равно "-1", то для остальных элементов это число уникально. Или почти уникально, в зависимости от выполняемой операции. Например, если в диалоге присутствует кнопка "ОК" или "Сохранить" (для диалога "Сохранить таблицу") или "Восстановить" (для диалога "Восстановить таблицу"), то идентификатор этой кнопки всегда будет "1", что будет означать, что пользователь указал всё необходимое в диалоге и готов применить изменения. Для "Отмены" и "Справки" идентификаторы равны 2 и 5 соответственно, причём в любом диалоге. Например, список типов открытия таблицы ("В новой карте", "В активной карте" и т.д.) фигурирует в нескольких диалогах и в каждом из них имеет один и тот же идентификатор, так как список передаётся туда программно.<br />
<br />
'''STATIC''' - тип элемента диалога. В данном случае статическая строка, на которую ничего не передаётся из другого элемента управления.<br />
<br />
Далее следуют знакомые уже типы стилей оформления и координаты.<br />
<br />
Элементы диалога возможно изменять как путём перемещения и растягивания оных с помощью указателя, так и задавая точные необходимые значения. Например, если необходимо будет поменять размер и сдвинуть какие-то кнопки, то проще это сделать путём редактирования координат и размеров, представленных в числовом виде. При этом необходимо в конце редактирования нажимать клавишу F5 для компиляции ресурса, чтобы изменения сразу вступили в силу, и их можно было увидеть. В случае изменения объектов указателем нажимать каждый раз F5 необязательно (так как изменения буду стразу же видны), однако, это всё равно потребуется в конце всех операций. В конечном итоге, может получиться подобный результат:<br />
<br />
[[Файл:Resources2.png|center|Измененный диалог в MapInfo]]<br />
<br />
После компиляции всех изменений файл '''mires.dll''' необходимо сохранить. Сохранение возможно только при закрытом MapInfo.<br />
<br />
== Работа с динамическими элементами диалога ==<br />
<br />
В диалогах MapInfo не все элементы являются статическими. Некоторые элементы подгружаются посредством программного кода. Самый яркий пример — окно регистрации растра, которое является крайне неудобным для выполнения своих задач.<br />
<br />
[[Файл:Resources3.png|700px|center|Окно регистрации растра]]<br />
<br />
Чтобы его расширить, необходимо помимо растягивания главного окна диалога вытянуть по длине полосы вертикальной и горизонтальной прокрутки. После этого рабочее пространство привязки увеличится. А при желании можно поиграться с другими элементами управления и в итоге получить что-то вроде этого:<br />
<br />
[[Файл:Resources4.png|700px|center|Растянутое окно регистрации растра]]<br />
<br />
Как уже говорилось ранее, если окно растянуть до размеров экрана 1920х1280, то при разрешении экрана, например, 1280х960, окно выползет за пределы и работать станет практически невозможно. За этим необходимо следить.<br />
<br />
== Примеры настройки стилей в диалоговых окнах ==<br />
<br />
=== Пример 1. Стили полей ввода текста. ===<br />
<br />
[[Файл:Resources5.png|700px|center|Диалог правки текстовых объектов]]<br />
<br />
Речь пойдёт о поле ввода текста. Основное неудобство его в том, что диалог нельзя завершить путём нажатия клавиши Enter — необходимо нажимать мышкой на кнопку ОК. Либо посредством клавиши Tab переключаться между элементами диалога до тех пор, пока не дойдём до кнопки ОК. Тогда клавиша Enter сработает. Связано это с тем, что нажатие клавиши Enter в поле ввода текста приводит к началу новой строки и возврату «каретки». Однако эта особенность — не более чем один из стилей оформления данного элемента диалога. Чтобы изменить элемент, нужно кликнуть по нему правой кнопкой мыши и выбрать "Edit Control".<br />
<br />
[[Файл:Resources6.png|center|Окно редактирования стилей и других параметров элемента диалога]]<br />
<br />
Интересующий нас стиль называется '''ES_WANTRETURN'''. После его отключения, компиляции изменений и сохранения файла, текст в этом диалоге (уже в самом MapInfo) можно будет закрыть посредством нажатия клавиши Enter. А так как клавиша Enter больше не создаёт новую строку текста, то для её создания нужно использовать комбинацию Ctrl+Enter. Кроме того, поле ввода текста можно тоже увеличить и получить что-то подобное:<br />
<br />
[[Файл:Resources7.png|center|Растянутый диалог правки текстовых объектов]]<br />
<br />
По этой же технологии можно изменить поле ввода текста для диалога редактирования выносных подписей.<br />
<br />
=== Пример 2. Стили кнопочных элементов. ===<br />
<br />
Снова обратимся к диалогу "Закрыть таблицу" и к кнопкам "Закрыть" и "Отмена".<br />
<br />
'''CONTROL "Закрыть", 1, BUTTON, BS_DEFPUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_GROUP | WS_TABSTOP, 170, 19, 45, 14''' <br />
<br />
''' CONTROL "Отмена", 2, BUTTON, BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_GROUP | WS_TABSTOP, 170, 37, 45, 14''' <br />
<br />
Если нас не устраивает тот факт, что клавиша "Закрыть" подсвечена по-умолчанию, и при нажатии клавиши Enter выбранные таблицы будут закрыты, то можно изменить стиль оформления с '''BS_DEFPUSHBUTTON''' на '''BS_PUSHBUTTON''' у кнопки "Закрыть". При этом необходимо провести обратную операцию для кнопки "Отмена". В этом случае кнопкой по-умолчанию станет она, и результат будет достигнут.<br />
<br />
=== Пример 3. Дополнительные стили кнопочных элементов. ===<br />
<br />
Рассмотрим диалог под номером 9070 из файла '''mires.dll'''.<br />
<br />
[[Файл:Resources10.png|700px|center|Динамический диалог]]<br />
<br />
Диалог интересен тем, что является динамическим, то есть его содержимое меняется в зависимости от запросов MapInfo. Кроме того, программно устанавливается, какая кнопка будет главной (подсвеченной) по-умолчанию. Например, если в MapInfo подгружена только одна таблица, в которой произошли изменения, и есть необходимость восстановить её, то MapInfo выдаст соответствующий запрос, в котором предложит либо восстановить таблицу, либо отменить и закрыть диалог. При этом кнопка "Восстановить" (та же кнопка "ОК", только с заменённым текстом) будет в приоритете, хотя стиль '''BS_DEFPUSHBUTTON''' не установлен. В других случаях, таких как изменение типа полей таблицы, может возникать другое окно, где уже кнопка "Отмена" будет в приоритете.<br />
<br />
Предположим, что существует необходимость, чтобы кнопка "Отмена" никогда не бывала активной по-умолчанию. Но тут возникает интересная особенность. Кнопка "Отмена" вместо ID 2 имеет ID 4. Попытка смены ID даёт то, что кнопка перестаёт отображаться вообще. Попытка принудительно назначить кнопке "ОК" стиль '''BS_DEFPUSHBUTTON''' ничего не даёт. Поэтому альтернативным вариантом решения является возможность отключения этой кнопки, так как диалог в любом случае можно закрыть с помощью клавиши Esc. Чтобы отключить кнопку, необходимо применить для кнопки стиль '''WS_DISABLED'''. После чего можно получить такой результат:<br />
<br />
[[Файл:Resources11.png|center|Отключенная кнопка]]<br />
<br />
== Добавление и изменение текстовых констант ==<br />
<br />
В этом разделе речь пойдёт о текстовых строках, которые также содержатся в ресурсах. Основные файлы, где они хранятся — '''mires.dll''' и '''micore.dll'''. Рассматриваться будет '''micore.dll'''. Необходимо открыть файл, перейти в директорию '''String Table''' и открыть раздел строк под номером 393.<br />
<br />
[[Файл:Resources8.png|400px|center|Текстовые строки]]<br />
<br />
Как видно из примера, здесь представлено некоторое количество строк. Некоторые из них специальные, содержащие переменные, в которые будут подставлены названия таблиц или колонок. Однако, нас интересуют строки под номерами 6276-6279. Там представлены функции соответствующего списка из диалога "SQL-запрос". Если следовать представленному синтаксису, то можно дописать в этот раздел свои собственные функции. Например, можно добавить функции ObjectInfo и ObjectGeography в строку 6278. После сохранения и компиляции результат будет выглядеть так:<br />
<br />
[[Файл:Resources9.png|center|Новые добавленные функции]]<br />
<br />
== Заключение ==<br />
<br />
В статье показаны варианты редактирования диалоговых окон и их элементов. Возможности добавления своих элементов сильно ограничены, так как основная их часть должна быть запрограммирована для достижения тех или иных результатов, чего через ресурсы выполнить невозможно. Но тем не менее, при определённых навыках и творчестве можно получить интересные варианты.<br />
<br />
На этом всё. Успешной работы!</div>Александр Мурыйhttps://wiki.gis-lab.info/index.php?title=%D0%A0%D0%B0%D0%B1%D0%BE%D1%82%D0%B0_%D1%81_%D1%80%D0%B5%D1%81%D1%83%D1%80%D1%81%D0%B0%D0%BC%D0%B8_%D0%B4%D0%BB%D1%8F_%D0%BE%D0%BF%D1%82%D0%B8%D0%BC%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_MapInfo&diff=25220Работа с ресурсами для оптимизации MapInfo2017-01-25T12:51:56Z<p>Александр Мурый: </p>
<hr />
<div>{{Статья|Черновик}}<br />
<br />
'''Внимание! Действия, производимые в статье, могут нарушать [http://www.pitneybowes.com/us/license-terms-of-use/software-and-data-end-user-license-agreement.html лицензионное соглашение] разработчиков MapInfo — компании Pitney Bowes. Поэтому любые изменения файлов и (или) их дальнейшая публикация производятся на свой страх и риск.'''<br />
<br />
В этой заметке описан способ редактирования диалоговых окон, текстовых объектов и других объектов интерфейса, находящиеся в ресурсах динамических библиотек MapInfo.<br />
<br />
Для работы потребуется утилита [http://angusj.com/resourcehacker/resource_hacker.zip Resource Hacker], которая является бесплатной и не требует установки.<br />
<br />
== Работа с диалоговыми окнами ==<br />
<br />
Диалоговые окна в MapInfo — тема довольно интересная. Связано это с тем, что диалоговые окна по большей части являются статическими — их невозможно ни развернуть, ни растянуть, ни запомнить их последующее положение. Практически все окна в MapInfo небольшие по размеру. Возможно, это связано с тем, чтобы окна полностью уменьшались на экране монитора даже на самом низком разрешении. Однако, с развитием технологий и увеличением разрешения экрана, размер окон остался неизменным и всё таким же неудобным. Существуют возможности исправить эти недочёты, о чём и будет сказано в этом разделе.<br />
<br />
После распаковки и запуска программы Resource Hacker открываем файл '''mires.dll''', находящийся в корневой директории MapInfo. Для написании данной статьи использовалась 32-х битная версия MapInfo 15.0. Но файл mires.dll принципиально ничем не отличается в различных версиях, поэтому информация будет также актуальной и для старых версий MapInfo.<br />
<br />
После того, как файл mires.dll будет открыт, необходимо перейти в директорию Dialog. Далее, для примера, поработаем с окном "Закрыть таблицу". Для того, чтобы быстро найти нужный диалог и вдобавок узнать номер ресурса, можно воспользоваться поиском. Диалог и его описание выглядят так:<br />
<br />
[[Файл:Resources1.png|400px|center|Диалог "Закрыть таблицу"]]<br />
<br />
'''2520 DIALOG 21, 30, 203, 98''' — Здесь следует объявление номера ресурса, а также начальные координаты появления окна на экране и размеры окна.<br />
<br />
'''STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU''' —Объявление стилей согласно Win32 API. Полное их описание можно почитать на [https://msdn.microsoft.com/ru-ru/library/windows/desktop/ms632600(v=vs.85).aspx сайте MSDN].<br />
<br />
'''CAPTION "Закрыть таблицу"''' — Заголовок окна<br />
<br />
'''LANGUAGE LANG_RUSSIAN, 0x0''' — Объявление используемого языка ресурса. В изменении нет необходимости<br />
<br />
'''FONT 8, "Helv"''' — Объявление стиля и размера используемого в диалоге шрифта<br />
<br />
Далее следуют объявления компонентов диалога. Рассмотрим только создание статической надписи:<br />
<br />
'''CONTROL "&Закрыть таблицы:"''' — Объявление заголовка для элемента диалога. Подходит преимущественно для текстовых надписей или кнопок.<br />
<br />
'''-1''' - идентификатор элемента диалога, и при этом самая важная его часть. Если для статических надписей это число всегда равно "-1", то для остальных элементов это число уникально. Или почти уникально, в зависимости от выполняемой операции. Например, если в диалоге присутствует кнопка "ОК" или "Сохранить" (для диалога "Сохранить таблицу") или "Восстановить" (для диалога "Восстановить таблицу"), то идентификатор этой кнопки всегда будет "1", что будет означать, что пользователь указал всё необходимое в диалоге и готов применить изменения. Для "Отмены" и "Справки" идентификаторы равны 2 и 5 соответственно, причём в любом диалоге. Например, список типов открытия таблицы ("В новой карте", "В активной карте" и т.д.) фигурирует в нескольких диалогах и в каждом из них имеет один и тот же идентификатор, так как список передаётся туда программно.<br />
<br />
'''STATIC''' - тип элемента диалога. В данном случае статическая строка, на которую ничего не передаётся из другого элемента управления.<br />
<br />
Далее следуют знакомые уже типы стилей оформления и координаты.<br />
<br />
Элементы диалога возможно изменять как путём перемещения и растягивания оных с помощью указателя, так и задавая точные необходимые значения. Например, если необходимо будет поменять размер и сдвинуть какие-то кнопки, то проще это сделать путём редактирования координат и размеров, представленных в числовом виде. При этом необходимо в конце редактирования нажимать клавишу F5 для компиляции ресурса, чтобы изменения сразу вступили в силу, и их можно было увидеть. В случае изменения объектов указателем нажимать каждый раз F5 необязательно (так как изменения буду стразу же видны), однако, это всё равно потребуется в конце всех операций. В конечном итоге, может получиться подобный результат:<br />
<br />
[[Файл:Resources2.png|400px|center|Измененный диалог в MapInfo]]<br />
<br />
После компиляции всех изменений файл '''mires.dll''' необходимо сохранить. Сохранение возможно только при закрытом MapInfo.<br />
<br />
== Работа с динамическими элементами диалога ==<br />
<br />
В диалогах MapInfo не все элементы являются статическими. Некоторые элементы подгружаются посредством программного кода. Самый яркий пример — окно регистрации растра, которое является крайне неудобным для выполнения своих задач.<br />
<br />
[[Файл:Resources3.png|400px|center|Окно регистрации растра]]<br />
<br />
Чтобы его расширить, необходимо помимо растягивания главного окна диалога вытянуть по длине полосы вертикальной и горизонтальной прокрутки. После этого рабочее пространство привязки увеличится. А при желании можно поиграться с другими элементами управления и в итоге получить что-то вроде этого:<br />
<br />
[[Файл:Resources4.png|400px|center|Растянутое окно регистрации растра]]<br />
<br />
Как уже говорилось ранее, если окно растянуть до размеров экрана 1920х1280, то при разрешении экрана, например, 1280х960, окно выползет за пределы и работать станет практически невозможно. За этим необходимо следить.<br />
<br />
== Примеры настройки стилей в диалоговых окнах ==<br />
<br />
=== Пример 1. Стили полей ввода текста. ===<br />
<br />
[[Файл:Resources5.png|400px|center|Диалог правки текстовых объектов]]<br />
<br />
Речь пойдёт о поле ввода текста. Основное неудобство его в том, что диалог нельзя завершить путём нажатия клавиши Enter — необходимо нажимать мышкой на кнопку ОК. Либо посредством клавиши Tab переключаться между элементами диалога до тех пор, пока не дойдём до кнопки ОК. Тогда клавиша Enter сработает. Связано это с тем, что нажатие клавиши Enter в поле ввода текста приводит к началу новой строки и возврату «каретки». Однако эта особенность — не более чем один из стилей оформления данного элемента диалога. Чтобы изменить элемент, нужно кликнуть по нему правой кнопкой мыши и выбрать "Edit Control".<br />
<br />
[[Файл:Resources6.png|400px|center|Окно редактирования стилей и других параметров элемента диалога]]<br />
<br />
Интересующий нас стиль называется '''ES_WANTRETURN'''. После его отключения, компиляции изменений и сохранения файла, текст в этом диалоге (уже в самом MapInfo) можно будет закрыть посредством нажатия клавиши Enter. А так как клавиша Enter больше не создаёт новую строку текста, то для её создания нужно использовать комбинацию Ctrl+Enter. Кроме того, поле ввода текста можно тоже увеличить и получить что-то подобное:<br />
<br />
[[Файл:Resources7.png|400px|center|Растянутый диалог правки текстовых объектов]]<br />
<br />
По этой же технологии можно изменить поле ввода текста для диалога редактирования выносных подписей.<br />
<br />
=== Пример 2. Стили кнопочных элементов. ===<br />
<br />
Снова обратимся к диалогу "Закрыть таблицу" и к кнопкам "Закрыть" и "Отмена".<br />
<br />
'''CONTROL "Закрыть", 1, BUTTON, BS_DEFPUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_GROUP | WS_TABSTOP, 170, 19, 45, 14''' <br />
<br />
''' CONTROL "Отмена", 2, BUTTON, BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_GROUP | WS_TABSTOP, 170, 37, 45, 14''' <br />
<br />
Если нас не устраивает тот факт, что клавиша "Закрыть" подсвечена по-умолчанию, и при нажатии клавиши Enter выбранные таблицы будут закрыты, то можно изменить стиль оформления с '''BS_DEFPUSHBUTTON''' на '''BS_PUSHBUTTON''' у кнопки "Закрыть". При этом необходимо провести обратную операцию для кнопки "Отмена". В этом случае кнопкой по-умолчанию станет она, и результат будет достигнут.<br />
<br />
=== Пример 3. Дополнительные стили кнопочных элементов. ===<br />
<br />
Рассмотрим диалог под номером 9070 из файла '''mires.dll'''.<br />
<br />
[[Файл:Resources10.png|400px|center|Динамический диалог]]<br />
<br />
Диалог интересен тем, что является динамическим, то есть его содержимое меняется в зависимости от запросов MapInfo. Кроме того, программно устанавливается, какая кнопка будет главной (подсвеченной) по-умолчанию. Например, если в MapInfo подгружена только одна таблица, в которой произошли изменения, и есть необходимость восстановить её, то MapInfo выдаст соответствующий запрос, в котором предложит либо восстановить таблицу, либо отменить и закрыть диалог. При этом кнопка "Восстановить" (та же кнопка "ОК", только с заменённым текстом) будет в приоритете, хотя стиль '''BS_DEFPUSHBUTTON''' не установлен. В других случаях, таких как изменение типа полей таблицы, может возникать другое окно, где уже кнопка "Отмена" будет в приоритете.<br />
<br />
Предположим, что существует необходимость, чтобы кнопка "Отмена" никогда не бывала активной по-умолчанию. Но тут возникает интересная особенность. Кнопка "Отмена" вместо ID 2 имеет ID 4. Попытка смены ID даёт то, что кнопка перестаёт отображаться вообще. Попытка принудительно назначить кнопке "ОК" стиль '''BS_DEFPUSHBUTTON''' ничего не даёт. Поэтому альтернативным вариантом решения является возможность отключения этой кнопки, так как диалог в любом случае можно закрыть с помощью клавиши Esc. Чтобы отключить кнопку, необходимо применить для кнопки стиль '''WS_DISABLED'''. После чего можно получить такой результат:<br />
<br />
[[Файл:Resources11.png|400px|center|Отключенная кнопка]]<br />
<br />
== Добавление и изменение текстовых констант ==<br />
<br />
В этом разделе речь пойдёт о текстовых строках, которые также содержатся в ресурсах. Основные файлы, где они хранятся — '''mires.dll''' и '''micore.dll'''. Рассматриваться будет '''micore.dll'''. Необходимо открыть файл, перейти в директорию '''String Table''' и открыть раздел строк под номером 393.<br />
<br />
[[Файл:Resources8.png|400px|center|Текстовые строки]]<br />
<br />
Как видно из примера, здесь представлено некоторое количество строк. Некоторые из них специальные, содержащие переменные, в которые будут подставлены названия таблиц или колонок. Однако, нас интересуют строки под номерами 6276-6279. Там представлены функции соответствующего списка из диалога "SQL-запрос". Если следовать представленному синтаксису, то можно дописать в этот раздел свои собственные функции. Например, можно добавить функции ObjectInfo и ObjectGeography в строку 6278. После сохранения и компиляции результат будет выглядеть так:<br />
<br />
[[Файл:Resources9.png|400px|center|Новые добавленные функции]]<br />
<br />
== Заключение ==<br />
<br />
В статье показаны варианты редактирования диалоговых окон и их элементов. Возможности добавления своих элементов сильно ограничены, так как основная их часть должна быть запрограммирована для достижения тех или иных результатов, чего через ресурсы выполнить невозможно. Но тем не менее, при определённых навыках и творчестве можно получить интересные варианты.<br />
<br />
На этом всё. Успешной работы!</div>Александр Мурыйhttps://wiki.gis-lab.info/index.php?title=%D0%A0%D0%B0%D0%B1%D0%BE%D1%82%D0%B0_%D1%81_%D1%80%D0%B5%D1%81%D1%83%D1%80%D1%81%D0%B0%D0%BC%D0%B8_%D0%B4%D0%BB%D1%8F_%D0%BE%D0%BF%D1%82%D0%B8%D0%BC%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_MapInfo&diff=25219Работа с ресурсами для оптимизации MapInfo2017-01-25T12:49:32Z<p>Александр Мурый: </p>
<hr />
<div>{{Статья|Черновик}}<br />
<br />
'''Внимание! Действия, производимые в статье, могут нарушать [http://www.pitneybowes.com/us/license-terms-of-use/software-and-data-end-user-license-agreement.html лицензионное соглашение] разработчиков MapInfo — компании Pitney Bowes. Поэтому любые изменения файлов и (или) их дальнейшая публикация производятся на свой страх и риск.'''<br />
<br />
В этой заметке описан способ редактирования диалоговых окон, текстовых объектов и других объектов интерфейса, находящиеся в ресурсах динамических библиотек MapInfo.<br />
<br />
Для работы потребуется утилита [http://angusj.com/resourcehacker/resource_hacker.zip Resource Hacker], которая является бесплатной и не требует установки.<br />
<br />
== Работа с диалоговыми окнами ==<br />
<br />
Диалоговые окна в MapInfo — тема довольно интересная. Связано это с тем, что диалоговые окна по большей части являются статическими — их невозможно ни развернуть, ни растянуть, ни запомнить их последующее положение. Практически все окна в MapInfo небольшие по размеру. Возможно, это связано с тем, чтобы окна полностью уменьшались на экране монитора даже на самом низком разрешении. Однако, с развитием технологий и увеличением разрешения экрана, размер окон остался неизменным и всё таким же неудобным. Существуют возможности исправить эти недочёты, о чём и будет сказано в этом разделе.<br />
<br />
После распаковки и запуска программы Resource Hacker открываем файл '''mires.dll''', находящийся в корневой директории MapInfo. Для написании данной статьи использовалась 32-х битная версия MapInfo 15.0. Но файл mires.dll принципиально ничем не отличается в различных версиях, поэтому информация будет также актуальной и для старых версий MapInfo.<br />
<br />
После того, как файл mires.dll будет открыт, необходимо перейти в директорию Dialog. Далее, для примера, поработаем с окном "Закрыть таблицу". Для того, чтобы быстро найти нужный диалог и вдобавок узнать номер ресурса, можно воспользоваться поиском. Диалог и его описание выглядят так:<br />
<br />
[[Файл:Resources1.png|200px|thumb|center|Диалог "Закрыть таблицу"]]<br />
<br />
'''2520 DIALOG 21, 30, 203, 98''' — Здесь следует объявление номера ресурса, а также начальные координаты появления окна на экране и размеры окна.<br />
<br />
'''STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU''' —Объявление стилей согласно Win32 API. Полное их описание можно почитать на [https://msdn.microsoft.com/ru-ru/library/windows/desktop/ms632600(v=vs.85).aspx сайте MSDN].<br />
<br />
'''CAPTION "Закрыть таблицу"''' — Заголовок окна<br />
<br />
'''LANGUAGE LANG_RUSSIAN, 0x0''' — Объявление используемого языка ресурса. В изменении нет необходимости<br />
<br />
'''FONT 8, "Helv"''' — Объявление стиля и размера используемого в диалоге шрифта<br />
<br />
Далее следуют объявления компонентов диалога. Рассмотрим только создание статической надписи:<br />
<br />
'''CONTROL "&Закрыть таблицы:"''' — Объявление заголовка для элемента диалога. Подходит преимущественно для текстовых надписей или кнопок.<br />
<br />
'''-1''' - идентификатор элемента диалога, и при этом самая важная его часть. Если для статических надписей это число всегда равно "-1", то для остальных элементов это число уникально. Или почти уникально, в зависимости от выполняемой операции. Например, если в диалоге присутствует кнопка "ОК" или "Сохранить" (для диалога "Сохранить таблицу") или "Восстановить" (для диалога "Восстановить таблицу"), то идентификатор этой кнопки всегда будет "1", что будет означать, что пользователь указал всё необходимое в диалоге и готов применить изменения. Для "Отмены" и "Справки" идентификаторы равны 2 и 5 соответственно, причём в любом диалоге. Например, список типов открытия таблицы ("В новой карте", "В активной карте" и т.д.) фигурирует в нескольких диалогах и в каждом из них имеет один и тот же идентификатор, так как список передаётся туда программно.<br />
<br />
'''STATIC''' - тип элемента диалога. В данном случае статическая строка, на которую ничего не передаётся из другого элемента управления.<br />
<br />
Далее следуют знакомые уже типы стилей оформления и координаты.<br />
<br />
Элементы диалога возможно изменять как путём перемещения и растягивания оных с помощью указателя, так и задавая точные необходимые значения. Например, если необходимо будет поменять размер и сдвинуть какие-то кнопки, то проще это сделать путём редактирования координат и размеров, представленных в числовом виде. При этом необходимо в конце редактирования нажимать клавишу F5 для компиляции ресурса, чтобы изменения сразу вступили в силу, и их можно было увидеть. В случае изменения объектов указателем нажимать каждый раз F5 необязательно (так как изменения буду стразу же видны), однако, это всё равно потребуется в конце всех операций. В конечном итоге, может получиться подобный результат:<br />
<br />
[[Файл:Resources2.png|200px|thumb|center|Измененный диалог в MapInfo]]<br />
<br />
После компиляции всех изменений файл '''mires.dll''' необходимо сохранить. Сохранение возможно только при закрытом MapInfo.<br />
<br />
== Работа с динамическими элементами диалога ==<br />
<br />
В диалогах MapInfo не все элементы являются статическими. Некоторые элементы подгружаются посредством программного кода. Самый яркий пример — окно регистрации растра, которое является крайне неудобным для выполнения своих задач.<br />
<br />
[[Файл:Resources3.png|200px|thumb|center|Окно регистрации растра]]<br />
<br />
Чтобы его расширить, необходимо помимо растягивания главного окна диалога вытянуть по длине полосы вертикальной и горизонтальной прокрутки. После этого рабочее пространство привязки увеличится. А при желании можно поиграться с другими элементами управления и в итоге получить что-то вроде этого:<br />
<br />
[[Файл:Resources4.png|200px|thumb|center|Растянутое окно регистрации растра]]<br />
<br />
Как уже говорилось ранее, если окно растянуть до размеров экрана 1920х1280, то при разрешении экрана, например, 1280х960, окно выползет за пределы и работать станет практически невозможно. За этим необходимо следить.<br />
<br />
== Примеры настройки стилей в диалоговых окнах ==<br />
<br />
=== Пример 1. Стили полей ввода текста. ===<br />
<br />
[[Файл:Resources5.png|200px|thumb|center|Диалог правки текстовых объектов]]<br />
<br />
Речь пойдёт о поле ввода текста. Основное неудобство его в том, что диалог нельзя завершить путём нажатия клавиши Enter — необходимо нажимать мышкой на кнопку ОК. Либо посредством клавиши Tab переключаться между элементами диалога до тех пор, пока не дойдём до кнопки ОК. Тогда клавиша Enter сработает. Связано это с тем, что нажатие клавиши Enter в поле ввода текста приводит к началу новой строки и возврату «каретки». Однако эта особенность — не более чем один из стилей оформления данного элемента диалога. Чтобы изменить элемент, нужно кликнуть по нему правой кнопкой мыши и выбрать "Edit Control".<br />
<br />
[[Файл:Resources6.png|200px|thumb|center|Окно редактирования стилей и других параметров элемента диалога]]<br />
<br />
Интересующий нас стиль называется '''ES_WANTRETURN'''. После его отключения, компиляции изменений и сохранения файла, текст в этом диалоге (уже в самом MapInfo) можно будет закрыть посредством нажатия клавиши Enter. А так как клавиша Enter больше не создаёт новую строку текста, то для её создания нужно использовать комбинацию Ctrl+Enter. Кроме того, поле ввода текста можно тоже увеличить и получить что-то подобное:<br />
<br />
[[Файл:Resources7.png|200px|thumb|center|Растянутый диалог правки текстовых объектов]]<br />
<br />
По этой же технологии можно изменить поле ввода текста для диалога редактирования выносных подписей.<br />
<br />
=== Пример 2. Стили кнопочных элементов. ===<br />
<br />
Снова обратимся к диалогу "Закрыть таблицу" и к кнопкам "Закрыть" и "Отмена".<br />
<br />
'''CONTROL "Закрыть", 1, BUTTON, BS_DEFPUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_GROUP | WS_TABSTOP, 170, 19, 45, 14''' <br />
<br />
''' CONTROL "Отмена", 2, BUTTON, BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_GROUP | WS_TABSTOP, 170, 37, 45, 14''' <br />
<br />
Если нас не устраивает тот факт, что клавиша "Закрыть" подсвечена по-умолчанию, и при нажатии клавиши Enter выбранные таблицы будут закрыты, то можно изменить стиль оформления с '''BS_DEFPUSHBUTTON''' на '''BS_PUSHBUTTON''' у кнопки "Закрыть". При этом необходимо провести обратную операцию для кнопки "Отмена". В этом случае кнопкой по-умолчанию станет она, и результат будет достигнут.<br />
<br />
=== Пример 3. Дополнительные стили кнопочных элементов. ===<br />
<br />
Рассмотрим диалог под номером 9070 из файла '''mires.dll'''.<br />
<br />
[[Файл:Resources10.png|200px|thumb|center|Динамический диалог]]<br />
<br />
Диалог интересен тем, что является динамическим, то есть его содержимое меняется в зависимости от запросов MapInfo. Кроме того, программно устанавливается, какая кнопка будет главной (подсвеченной) по-умолчанию. Например, если в MapInfo подгружена только одна таблица, в которой произошли изменения, и есть необходимость восстановить её, то MapInfo выдаст соответствующий запрос, в котором предложит либо восстановить таблицу, либо отменить и закрыть диалог. При этом кнопка "Восстановить" (та же кнопка "ОК", только с заменённым текстом) будет в приоритете, хотя стиль '''BS_DEFPUSHBUTTON''' не установлен. В других случаях, таких как изменение типа полей таблицы, может возникать другое окно, где уже кнопка "Отмена" будет в приоритете.<br />
<br />
Предположим, что существует необходимость, чтобы кнопка "Отмена" никогда не бывала активной по-умолчанию. Но тут возникает интересная особенность. Кнопка "Отмена" вместо ID 2 имеет ID 4. Попытка смены ID даёт то, что кнопка перестаёт отображаться вообще. Попытка принудительно назначить кнопке "ОК" стиль '''BS_DEFPUSHBUTTON''' ничего не даёт. Поэтому альтернативным вариантом решения является возможность отключения этой кнопки, так как диалог в любом случае можно закрыть с помощью клавиши Esc. Чтобы отключить кнопку, необходимо применить для кнопки стиль '''WS_DISABLED'''. После чего можно получить такой результат:<br />
<br />
[[Файл:Resources11.png|200px|thumb|center|Отключенная кнопка]]<br />
<br />
== Добавление и изменение текстовых констант ==<br />
<br />
В этом разделе речь пойдёт о текстовых строках, которые также содержатся в ресурсах. Основные файлы, где они хранятся — '''mires.dll''' и '''micore.dll'''. Рассматриваться будет '''micore.dll'''. Необходимо открыть файл, перейти в директорию '''String Table''' и открыть раздел строк под номером 393.<br />
<br />
[[Файл:Resources8.png|200px|thumb|center|Текстовые строки]]<br />
<br />
Как видно из примера, здесь представлено некоторое количество строк. Некоторые из них специальные, содержащие переменные, в которые будут подставлены названия таблиц или колонок. Однако, нас интересуют строки под номерами 6276-6279. Там представлены функции соответствующего списка из диалога "SQL-запрос". Если следовать представленному синтаксису, то можно дописать в этот раздел свои собственные функции. Например, можно добавить функции ObjectInfo и ObjectGeography в строку 6278. После сохранения и компиляции результат будет выглядеть так:<br />
<br />
[[Файл:Resources9.png|200px|thumb|center|Новые добавленные функции]]<br />
<br />
== Заключение ==<br />
<br />
В статье показаны варианты редактирования диалоговых окон и их элементов. Возможности добавления своих элементов сильно ограничены, так как основная их часть должна быть запрограммирована для достижения тех или иных результатов, чего через ресурсы выполнить невозможно. Но тем не менее, при определённых навыках и творчестве можно получить интересные варианты.<br />
<br />
На этом всё. Успешной работы!</div>Александр Мурыйhttps://wiki.gis-lab.info/index.php?title=%D0%A0%D0%B0%D0%B1%D0%BE%D1%82%D0%B0_%D1%81_%D1%80%D0%B5%D1%81%D1%83%D1%80%D1%81%D0%B0%D0%BC%D0%B8_%D0%B4%D0%BB%D1%8F_%D0%BE%D0%BF%D1%82%D0%B8%D0%BC%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_MapInfo&diff=25218Работа с ресурсами для оптимизации MapInfo2017-01-25T12:48:45Z<p>Александр Мурый: </p>
<hr />
<div>{{Статья|Черновик}}<br />
<br />
'''Внимание! Действия, производимые в статье, могут нарушать [http://www.pitneybowes.com/us/license-terms-of-use/software-and-data-end-user-license-agreement.html лицензионное соглашение] разработчиков MapInfo - компании Pitney Bowes. Поэтому любые изменения файлов и (или) их дальнейшая публикация производятся на свой страх и риск.'''<br />
<br />
В этой заметке описан способ редактирования диалоговых окон, текстовых объектов и других объектов интерфейса, находящиеся в ресурсах динамических библиотек MapInfo.<br />
<br />
Для работы потребуется утилита [http://angusj.com/resourcehacker/resource_hacker.zip Resource Hacker], которая является бесплатной и не требует установки.<br />
<br />
== Работа с диалоговыми окнами ==<br />
<br />
Диалоговые окна в MapInfo — тема довольно интересная. Связано это с тем, что диалоговые окна по большей части являются статическими — их невозможно ни развернуть, ни растянуть, ни запомнить их последующее положение. Практически все окна в MapInfo небольшие по размеру. Возможно, это связано с тем, чтобы окна полностью уменьшались на экране монитора даже на самом низком разрешении. Однако, с развитием технологий и увеличением разрешения экрана, размер окон остался неизменным и всё таким же неудобным. Существуют возможности исправить эти недочёты, о чём и будет сказано в этом разделе.<br />
<br />
После распаковки и запуска программы Resource Hacker открываем файл '''mires.dll''', находящийся в корневой директории MapInfo. Для написании данной статьи использовалась 32-х битная версия MapInfo 15.0. Но файл mires.dll принципиально ничем не отличается в различных версиях, поэтому информация будет также актуальной и для старых версий MapInfo.<br />
<br />
После того, как файл mires.dll будет открыт, необходимо перейти в директорию Dialog. Далее, для примера, поработаем с окном "Закрыть таблицу". Для того, чтобы быстро найти нужный диалог и вдобавок узнать номер ресурса, можно воспользоваться поиском. Диалог и его описание выглядят так:<br />
<br />
[[Файл:Resources1.png|200px|thumb|center|Диалог "Закрыть таблицу"]]<br />
<br />
'''2520 DIALOG 21, 30, 203, 98''' — Здесь следует объявление номера ресурса, а также начальные координаты появления окна на экране и размеры окна.<br />
<br />
'''STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU''' —Объявление стилей согласно Win32 API. Полное их описание можно почитать на [https://msdn.microsoft.com/ru-ru/library/windows/desktop/ms632600(v=vs.85).aspx сайте MSDN].<br />
<br />
'''CAPTION "Закрыть таблицу"''' — Заголовок окна<br />
<br />
'''LANGUAGE LANG_RUSSIAN, 0x0''' — Объявление используемого языка ресурса. В изменении нет необходимости<br />
<br />
'''FONT 8, "Helv"''' — Объявление стиля и размера используемого в диалоге шрифта<br />
<br />
Далее следуют объявления компонентов диалога. Рассмотрим только создание статической надписи:<br />
<br />
'''CONTROL "&Закрыть таблицы:"''' — Объявление заголовка для элемента диалога. Подходит преимущественно для текстовых надписей или кнопок.<br />
<br />
'''-1''' - идентификатор элемента диалога, и при этом самая важная его часть. Если для статических надписей это число всегда равно "-1", то для остальных элементов это число уникально. Или почти уникально, в зависимости от выполняемой операции. Например, если в диалоге присутствует кнопка "ОК" или "Сохранить" (для диалога "Сохранить таблицу") или "Восстановить" (для диалога "Восстановить таблицу"), то идентификатор этой кнопки всегда будет "1", что будет означать, что пользователь указал всё необходимое в диалоге и готов применить изменения. Для "Отмены" и "Справки" идентификаторы равны 2 и 5 соответственно, причём в любом диалоге. Например, список типов открытия таблицы ("В новой карте", "В активной карте" и т.д.) фигурирует в нескольких диалогах и в каждом из них имеет один и тот же идентификатор, так как список передаётся туда программно.<br />
<br />
'''STATIC''' - тип элемента диалога. В данном случае статическая строка, на которую ничего не передаётся из другого элемента управления.<br />
<br />
Далее следуют знакомые уже типы стилей оформления и координаты.<br />
<br />
Элементы диалога возможно изменять как путём перемещения и растягивания оных с помощью указателя, так и задавая точные необходимые значения. Например, если необходимо будет поменять размер и сдвинуть какие-то кнопки, то проще это сделать путём редактирования координат и размеров, представленных в числовом виде. При этом необходимо в конце редактирования нажимать клавишу F5 для компиляции ресурса, чтобы изменения сразу вступили в силу, и их можно было увидеть. В случае изменения объектов указателем нажимать каждый раз F5 необязательно (так как изменения буду стразу же видны), однако, это всё равно потребуется в конце всех операций. В конечном итоге, может получиться подобный результат:<br />
<br />
[[Файл:Resources2.png|200px|thumb|center|Измененный диалог в MapInfo]]<br />
<br />
После компиляции всех изменений файл '''mires.dll''' необходимо сохранить. Сохранение возможно только при закрытом MapInfo.<br />
<br />
== Работа с динамическими элементами диалога ==<br />
<br />
В диалогах MapInfo не все элементы являются статическими. Некоторые элементы подгружаются посредством программного кода. Самый яркий пример — окно регистрации растра, которое является крайне неудобным для выполнения своих задач.<br />
<br />
[[Файл:Resources3.png|200px|thumb|center|Окно регистрации растра]]<br />
<br />
Чтобы его расширить, необходимо помимо растягивания главного окна диалога вытянуть по длине полосы вертикальной и горизонтальной прокрутки. После этого рабочее пространство привязки увеличится. А при желании можно поиграться с другими элементами управления и в итоге получить что-то вроде этого:<br />
<br />
[[Файл:Resources4.png|200px|thumb|center|Растянутое окно регистрации растра]]<br />
<br />
Как уже говорилось ранее, если окно растянуть до размеров экрана 1920х1280, то при разрешении экрана, например, 1280х960, окно выползет за пределы и работать станет практически невозможно. За этим необходимо следить.<br />
<br />
== Примеры настройки стилей в диалоговых окнах ==<br />
<br />
=== Пример 1. Стили полей ввода текста. ===<br />
<br />
[[Файл:Resources5.png|200px|thumb|center|Диалог правки текстовых объектов]]<br />
<br />
Речь пойдёт о поле ввода текста. Основное неудобство его в том, что диалог нельзя завершить путём нажатия клавиши Enter — необходимо нажимать мышкой на кнопку ОК. Либо посредством клавиши Tab переключаться между элементами диалога до тех пор, пока не дойдём до кнопки ОК. Тогда клавиша Enter сработает. Связано это с тем, что нажатие клавиши Enter в поле ввода текста приводит к началу новой строки и возврату «каретки». Однако эта особенность — не более чем один из стилей оформления данного элемента диалога. Чтобы изменить элемент, нужно кликнуть по нему правой кнопкой мыши и выбрать "Edit Control".<br />
<br />
[[Файл:Resources6.png|200px|thumb|center|Окно редактирования стилей и других параметров элемента диалога]]<br />
<br />
Интересующий нас стиль называется '''ES_WANTRETURN'''. После его отключения, компиляции изменений и сохранения файла, текст в этом диалоге (уже в самом MapInfo) можно будет закрыть посредством нажатия клавиши Enter. А так как клавиша Enter больше не создаёт новую строку текста, то для её создания нужно использовать комбинацию Ctrl+Enter. Кроме того, поле ввода текста можно тоже увеличить и получить что-то подобное:<br />
<br />
[[Файл:Resources7.png|200px|thumb|center|Растянутый диалог правки текстовых объектов]]<br />
<br />
По этой же технологии можно изменить поле ввода текста для диалога редактирования выносных подписей.<br />
<br />
=== Пример 2. Стили кнопочных элементов. ===<br />
<br />
Снова обратимся к диалогу "Закрыть таблицу" и к кнопкам "Закрыть" и "Отмена".<br />
<br />
'''CONTROL "Закрыть", 1, BUTTON, BS_DEFPUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_GROUP | WS_TABSTOP, 170, 19, 45, 14''' <br />
<br />
''' CONTROL "Отмена", 2, BUTTON, BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_GROUP | WS_TABSTOP, 170, 37, 45, 14''' <br />
<br />
Если нас не устраивает тот факт, что клавиша "Закрыть" подсвечена по-умолчанию, и при нажатии клавиши Enter выбранные таблицы будут закрыты, то можно изменить стиль оформления с '''BS_DEFPUSHBUTTON''' на '''BS_PUSHBUTTON''' у кнопки "Закрыть". При этом необходимо провести обратную операцию для кнопки "Отмена". В этом случае кнопкой по-умолчанию станет она, и результат будет достигнут.<br />
<br />
=== Пример 3. Дополнительные стили кнопочных элементов. ===<br />
<br />
Рассмотрим диалог под номером 9070 из файла '''mires.dll'''.<br />
<br />
[[Файл:Resources10.png|200px|thumb|center|Динамический диалог]]<br />
<br />
Диалог интересен тем, что является динамическим, то есть его содержимое меняется в зависимости от запросов MapInfo. Кроме того, программно устанавливается, какая кнопка будет главной (подсвеченной) по-умолчанию. Например, если в MapInfo подгружена только одна таблица, в которой произошли изменения, и есть необходимость восстановить её, то MapInfo выдаст соответствующий запрос, в котором предложит либо восстановить таблицу, либо отменить и закрыть диалог. При этом кнопка "Восстановить" (та же кнопка "ОК", только с заменённым текстом) будет в приоритете, хотя стиль '''BS_DEFPUSHBUTTON''' не установлен. В других случаях, таких как изменение типа полей таблицы, может возникать другое окно, где уже кнопка "Отмена" будет в приоритете.<br />
<br />
Предположим, что существует необходимость, чтобы кнопка "Отмена" никогда не бывала активной по-умолчанию. Но тут возникает интересная особенность. Кнопка "Отмена" вместо ID 2 имеет ID 4. Попытка смены ID даёт то, что кнопка перестаёт отображаться вообще. Попытка принудительно назначить кнопке "ОК" стиль '''BS_DEFPUSHBUTTON''' ничего не даёт. Поэтому альтернативным вариантом решения является возможность отключения этой кнопки, так как диалог в любом случае можно закрыть с помощью клавиши Esc. Чтобы отключить кнопку, необходимо применить для кнопки стиль '''WS_DISABLED'''. После чего можно получить такой результат:<br />
<br />
[[Файл:Resources11.png|200px|thumb|center|Отключенная кнопка]]<br />
<br />
== Добавление и изменение текстовых констант ==<br />
<br />
В этом разделе речь пойдёт о текстовых строках, которые также содержатся в ресурсах. Основные файлы, где они хранятся — '''mires.dll''' и '''micore.dll'''. Рассматриваться будет '''micore.dll'''. Необходимо открыть файл, перейти в директорию '''String Table''' и открыть раздел строк под номером 393.<br />
<br />
[[Файл:Resources8.png|200px|thumb|center|Текстовые строки]]<br />
<br />
Как видно из примера, здесь представлено некоторое количество строк. Некоторые из них специальные, содержащие переменные, в которые будут подставлены названия таблиц или колонок. Однако, нас интересуют строки под номерами 6276-6279. Там представлены функции соответствующего списка из диалога "SQL-запрос". Если следовать представленному синтаксису, то можно дописать в этот раздел свои собственные функции. Например, можно добавить функции ObjectInfo и ObjectGeography в строку 6278. После сохранения и компиляции результат будет выглядеть так:<br />
<br />
[[Файл:Resources9.png|200px|thumb|center|Новые добавленные функции]]<br />
<br />
== Заключение ==<br />
<br />
В статье показаны варианты редактирования диалоговых окон и их элементов. Возможности добавления своих элементов сильно ограничены, так как основная их часть должна быть запрограммирована для достижения тех или иных результатов, чего через ресурсы выполнить невозможно. Но тем не менее, при определённых навыках и творчестве можно получить интересные варианты.<br />
<br />
На этом всё. Успешной работы!</div>Александр Мурыйhttps://wiki.gis-lab.info/index.php?title=%D0%97%D0%B0%D1%8F%D0%B2%D0%BA%D0%B8_%D0%BD%D0%B0_%D0%BF%D1%83%D0%B1%D0%BB%D0%B8%D0%BA%D0%B0%D1%86%D0%B8%D1%8E_%D1%81%D1%82%D0%B0%D1%82%D0%B5%D0%B9&diff=25216Заявки на публикацию статей2017-01-23T09:07:58Z<p>Александр Мурый: </p>
<hr />
<div>{{Организационное|articles-request}}<br />
<br />
Внизу вы видите таблицу заявок на публикацию статей на GIS-Lab, она показывает статьи, которые их авторы считают более-менее готовыми к публикации. Таблица создана, чтобы сделать процесс принятия статей более прозрачным для авторов и оптимизировать работу редакторов. <br />
<br />
Таблица будет обновляться ведущим по мере поступления заявок от авторов. Заявкой на публикацию считается:<br />
* если статья оформляется на вики GIS-Lab — создание автором темы на форуме для обсуждения статьи (в разделе [http://gis-lab.info/forum/viewforum.php?f=3 Материалы сайта]);<br />
* если статья оформляется НЕ на вики GIS-Lab — личное обращение автора к [http://gis-lab.info/qa/org.html редактору] с предоставлением текста статьи.<br />
<br />
<br />
{| class="wikitable sortable" <br />
|-<br />
! width="5%" | Дата поступления заявки<br />
! width="30%" | Ссылка на вики, название <br />
! width="25%" | Автор (имя / ID)<br />
! width="25%" | Тема обсуждения на форуме (номер / название)<br />
! width="10%" | Дата создания<br />
<br />
|-<br />
| 09.01.2017<br />
| [http://wiki.gis-lab.info/w/%D0%A0%D0%B0%D0%B1%D0%BE%D1%82%D0%B0_%D1%81_%D1%80%D0%B5%D1%81%D1%83%D1%80%D1%81%D0%B0%D0%BC%D0%B8_%D0%B4%D0%BB%D1%8F_%D0%BE%D0%BF%D1%82%D0%B8%D0%BC%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D1%8B_%D0%B2_MapInfo Работа с ресурсами для оптимизации MapInfo]<br />
| Slinger / 9620<br />
| 21916 / Работа с ресурсами для оптимизации MapInfo<br />
| 06.12.2016<br />
<br />
<br />
<br />
<br />
[[Категория:Служебные]]</div>Александр Мурыйhttps://wiki.gis-lab.info/index.php?title=%D0%9E%D0%BF%D1%8B%D1%82_%D0%BA%D0%BB%D0%B0%D1%81%D1%81%D0%B8%D1%84%D0%B8%D0%BA%D0%B0%D1%86%D0%B8%D0%B8_%D0%BA%D0%BE%D1%81%D0%BC%D0%BE%D1%81%D0%BD%D0%B8%D0%BC%D0%BA%D0%B0_Sentinel-_2a_%D1%81_%D0%BF%D0%BE%D0%BC%D0%BE%D1%89%D1%8C%D1%8E_Semi-Automatic_Classification_Plugin_%D0%B2_QGIS&diff=25215Опыт классификации космоснимка Sentinel- 2a с помощью Semi-Automatic Classification Plugin в QGIS2017-01-23T08:59:11Z<p>Александр Мурый: </p>
<hr />
<div>{{Статья|Опубликована|qgis-sacp-sentinel2a}}<br />
{{Аннотация|Данная статья описывает опыт работы с Semi-Automatic Classification Plugin для QGIS для классификации снимка Sentinel-2a с целью выявления лесонарушений на примере национального парка "Орловское полесье", а также содержит пошаговую инструкцию для лесного дешифрирования снимка с помощью данного плагина.}}<br />
<br />
'''''Карпачев Андрей Петрович, научный сотрудник- ГИС специалист НП "Орловское полесье"''<br />
<br />
= Цели проекта=<br />
<br />
1) Выявление участков нарушенного и усохшего леса на территории НП «Орловское полесье». <br />
<br />
2) Лесное дешифрирование (по породам) части территории Орловско-Брянско-Калужского региона.<br />
<br />
= Материалы и ПО=<br />
<br />
Для реализации классификации были выбраны тестовые участки на основе лесоустроительных материалов 2006 и 2016 годов и полевых обходов.<br />
<br />
В проекте был использован космоснимок Sentinel-2a на территорию нацпарка «Орловское полесье» за сентябрь 2016 года, предварительно загруженный с официального сайта.<br />
Программное обеспечение: QGIS 2.14.8 с интегрированным плагином SACP.<br />
<br />
<br />
= Подготовка к началу обработки, формирование директорий=<br />
<br />
Первым делом открываем космоснимок Sentinel-2a через плагин. <br />
<br />
[[Файл:1%29put%27-open_file.png|frame|300x300px|центр|Начало работы]]<br />
<br />
Откроется следующая форма:<br />
[[Файл:2%29open_form.png|frame|300x300px|центр|Форма загрузки космоснимка]]<br />
<br />
Далее нажимаем на иконку [[Файл:3_perehod_k_granulam.png]] и «идём» к папке GRANULE из распакованного архива космоснимка Sentinel.<br />
В форме откроются каналы (bands) космоснимка. Здесь же снимаем флажок с '''Create Band set and use Band set tools'''. Ставим флажок '''Apply DOS1 atmospheric correction'''.<br />
<br />
[[Файл:4otobrajenie_spiska_granul_b.png|frame|300x300px|центр|Открытие бандов]]<br />
<br />
В этой же форме открываем файл ''metadata'' (находится в распакованной папке из архива космоснимка)<br />
<br />
[[Файл:4.1metadata_open.png|frame|300x300px|центр|Открытие файла metadata]]<br />
<br />
Нажимаем на иконку [[Файл:IconRUN.png]] : Создаём папку, называем ''RT''. Далее мы наблюдаем ход атмосферной коррекции снимка, ждём окончания.<br />
<br />
[[Файл:6-_hod_atmcorrect.png|frame|40x40px|центр|Ход процесса коррекции]]<br />
<br />
После окончания мы можем заметить, что в списке слоёв QGIS слои снимка стали отображаться с префиксом ''RT''.<br />
<br />
[[Файл:7-_postcorrection_RT.png|frame|300x300px|центр|Слои RT после окончания атмосферной коррекции]]<br />
<br />
Обрезка снимка. В данном пункте мы должны обрезать космоснимок до квадрата границ национального парка. Для выполнения данной процедуры проходим:<br />
<br />
[[Файл:8-_way_cut_RT.png|frame|300x300px|центр|Путь к обрезке слоев космоснимка под квадрат территории наблюдения]]<br />
<br />
Откроется следующая форма:<br />
<br />
[[Файл:8.1-forma_cut.png|frame|300x300px|центр|Форма для настройки обрезки слоев космоснимка под квадрат территории наблюдения]]<br />
<br />
Нажимаем на иконку [[Файл:9-refresch_icon.png]]. <br />
<br />
Лист обновится. Выбираем все файлы RT иконкой [[Файл:10-selectall_icon.png]]. <br />
<br />
Нажатием на значком в виде крестика выбираем зону обрезки [[Файл:11-setareainthemap.png]]<br />
<br />
Выбранная зона выглядит следующим образом:<br />
<br />
[[Файл:12-select_area.png|frame|300x300px|центр|Выбранная территория]]<br />
<br />
Нажимаем на иконку [[Файл:IconRUN.png]], создаём папку ''CLIP'', указываем путь к ней и запускаем обрезку.<br />
<br />
Итог: В списке СЛОИ образуются новые обрезанные слои космоснимка с префиксом clip.<br />
<br />
[[Файл:14-clip_file.png|frame|400x400px|центр|Обрезанные слои космоснимка с префиксом clip]]<br />
<br />
= Формирование Bandset, ROI и SIG=<br />
Нажимаем на вкладку '''Bandset''' [[Файл:15-bandset_vkladka.png]],<br />
<br />
Далее необходимо обновить форму соответствующей иконкой [[Файл:16-refresch_l.png]],<br />
<br />
В форму подгрузятся файлы с префиксами ''clip'' и ''RT'':<br />
<br />
[[Файл:17-dawnload_clip_rt.png|frame|300x300px|центр|clip и RT]]<br />
<br />
Выбираем файлы только с префиксом ''clip'' [[Файл:18_file_for_bandset_icon_vibor.png]]<br />
<br />
Форма заполнится следующим образом:<br />
<br />
[[Файл:19_form_bandset_org.png|frame|300x300px|центр|Заполнение формы под bandset]]<br />
<br />
Далее выставляем показатель '''Quick wavelength settings''' по Sentinel-2 из выпадающего списка:<br />
<br />
[[Файл:20_configuration_of_sentinel2a.png|frame|300x300px|центр|Выбор настройки снимка под Sentinel-2a]]<br />
<br />
Форма изменит свой вид, добавятся значения '''center wavelength''':<br />
<br />
[[Файл:21_Zapolnrnie_pod_sentinel.png|frame|300x300px|центр|авто-заполнение формы под Sentinel-2a]]<br />
<br />
Далее нажимаем на иконку [[Файл:22_createtnewraining_icon.png]], называем файл ''training'' и сохраняем его в папку (можно в заново созданную). <br />
<br />
Теперь нажимаем на генератор bandset (3-2-1), после чего автоматически образуется слой.<br />
<br />
[[Файл:23_sloy_bandset.png|frame|350x350px|центр|Bandset слой]]<br />
<br />
Слой ''band set.vrt'' образовался сразу же после первого введения композита.<br />
<br />
[[Файл:24_bs.png|frame|350x350px|центр|Bandset в вкладке слои]]<br />
<br />
В ходе эксперимента был выбран композит из каналов 4-6-12. Визуализация снимка опциональна, в зависимости от объекта дешифрирования и физических параметров органов зрения исполнителя.<br />
<br />
[[Файл:25 в 4-6-12_color_komposit.png|frame|350x350px|центр|Композит 4-6-12]]<br />
<br />
Теперь переходим на вкладку:<br />
<br />
[[Файл:26_vkladka_classificatdoc.png|frame|350x350px|центр|Вкладка классификации]]<br />
<br />
Откроется следующая форма:<br />
<br />
[[Файл:27_forma_class-do.png|frame|450x450px|центр|Форма ввода ROI]]<br />
<br />
Теперь нам необходимо набрать графические пробы (ROI) для осуществления полуавтоматической классификации.<br />
Выбираем на космоснимке участок водной поверхности и в верхней надстройке плагина нажимаем иконку «крестик»:<br />
<br />
[[Файл:28_water.png|frame|450x450px|центр|Водный объект. Захват пикселей]]<br />
<br />
Выделяем пиксели; для наилучшего захвата в соседнем с «крестиком» окошке вводим дистанцию захвата пикселей:<br />
<br />
[[Файл:29_water_pxl.png|frame|400x400px|центр|"Захваченные" пиксели воды]]<br />
<br />
Дистанция захвата подбирается опционально, до полной удовлетворенности результатом захватывания. Этот принцип будет также использоваться в наборе пикселей застройки, дорожной инфраструктуры, аграрного сектора и леса.<br />
Во вкладке ROI creation вводим классы, порядковые номера классов и подклассов и жмём на «дискету»:<br />
<br />
[[Файл:30_vkl_roicreation.png|frame|350x350px|центр|Подписывание классов пикселей]]<br />
<br />
Файл записывается в ''classification doc''. Здесь сразу меняем цвет на эквивалентный объекту (вода - синий):<br />
<br />
[[Файл:31_nastroyka_color.png|frame|400x400px|центр|Замена цвета пикселей объекта]]<br />
<br />
Следующим шагом заходим в ''macroclasses'' и также меняем цвет:<br />
<br />
[[Файл:32_color_macrocklass.png|frame|400x400px|центр|Замена цвета макрокласса]]<br />
<br />
Далее определяем пиксели застройки на космоснимке. В композите 4-3-2 отлично читаются элементы дорог и застройки :<br />
<br />
[[Файл:33_komposit_building.png|frame|350x350px|центр|Композит 4-3-2. Застройка и элементы дорожной сети. Параметры захвата в изображении]]<br />
<br />
[[Файл:34_zahvat_zastroyki.png|frame|350x350px|центр|Захват пикселей застройки и дорог]]<br />
<br />
Помимо застройки, произошёл захват и пикселей дорожных полотен. Далее нам необходимо внести пиксели аграрных ландшафтов. Выбираем любой наиболее читаемый композит и поочередно выделяем пиксели. Так как обрабатываемые сельскохозяйственные территории имеют разные цвета, после отметки и захвата пикселей называем макроклассы каким-нибудь одним типом префикса, меняя только порядковый номер. В примере это ''sh1-sh6*''. В видеопримере разработчика данный набор пикселей называется "bare soil".<br />
<br />
[[Файл:35-vidi_bare_soil.png|frame|450x450px|центр|Пиксельные подклассы почвы]]<br />
<br />
Следующим шагом будет непосредственное определение усыханий еловых насаждений; разбиение снимка на хвою и листву, травянистую растительность. В проекте используются следующие сокращения: <br />
h- хвоя<br />
l- листва<br />
d- усыхание<br />
gras- трава<br />
<br />
Перед захватом необходимых групп пикселей сверим снимок со слоем лесоустройства. Включаем подписи по породам, это необходимо для того, чтобы понять местоположение выделов ели и сосны. Мертвопокровная сосна ухудшает качество дешифрирования, т.к цвет её пикселей частично совпадает с цветом пикселей усыхания, именно поэтому необходимо знать повыдельное расположение культур.<br />
<br />
[[Файл:36-LU_sravnenie.png|frame|450x450px|центр|Наложение лесоустроительных материалов на космоснимок]]<br />
<br />
[[Файл:37-vegklass-lhgd.png|frame|400x400px|центр|Выбранные классы растительности]]<br />
<br />
После выбранных групп пикселей (ROI) мы можем сделать предпросмотр перед осуществлением классификации, для чего нажимаем на иконку «цветной плюс».<br />
<br />
[[Файл:38-prew.png|frame|350x350px|центр|Предпросмотр]]<br />
<br />
После нажатия на эту иконку нажимаем на космоснимок для оценки результативности отбора ROI [[Файл:39_prosmotr.png]].<br />
После выбираем алгоритм классификации снимка:<br />
<br />
[[Файл:39.1_algoritm.png|frame|350x350px|центр|Выбор алгоритма обработки]]<br />
<br />
*В работе был выбран алгоритм максимального правдоподобия. Перед выполнением классификации были залиты белым цветом группы пикселей застройки, дорог, сельскохозяйственных зон, голых почв, лугов, т.к в работе нас интересует исключительно лес.<br />
<br />
[[Файл:40-maximum_lik.png|frame|350x350px|центр|Алгоритм- максимальное правдоподобие. Результат]]<br />
<br />
Следующим шагом заходим в настройки [[Файл:41-save_settings.png]] и нажимаем на вкладку Processing:<br />
<br />
<br />
[[Файл:42-processing.png|frame|350x350px|центр|Настройка вывода 1]]<br />
<br />
Здесь мы выставляем следующий параметр — RAM=1024 — и закрываем вкладку.<br />
<br />
[[Файл:43_RAM.png|frame|350x350px|центр|Настройка вывода 2]]<br />
<br />
Далее заходим на вкладку ''classification output'', жмём ''run'', создаём папку ''classification'' и сохраняем; жмём на кнопку ''Cохранить'' и ждём окончания процесса.<br />
<br />
[[Файл:44_outpoot.png|frame|350x350px|центр|Вывод]]<br />
<br />
''*при подготовке выяснилось, что первый канал «шумит», так что он был исключён из синтеза''<br />
<br />
= Показатель успешности =<br />
<br />
Изначальный снимок + слой ''лу2006'' (лесоустройство 2006 г.). Фиолетовый цвет под слоем лесоустройства — предполагаемое усыхание.<br />
<br />
[[Файл:Problem-snimok.png|frame|550x550px|центр|Космоснимок до алгоритма]]<br />
<br />
Классифицированный снимок:<br />
<br />
[[Файл:Problem_classif.png|frame|550x550px|центр|Классифицированный снимок.<br />
Оранжевый цвет(def)- лесонарушение]]<br />
<br />
Попадание по породам:<br />
<br />
[[Файл:Popadporod.png|frame|550x550px|центр|Совпадение выделов слоя лесоустройства с классифицированным снимком]]<br />
<br />
=Вывод=<br />
<br />
Ранее уже предпринималась попытка использования Semi-Automatic Classification Plugin для классификации космоснимка Landsat (http://gis-lab.info/qa/landsat_qgis_scp.html), и обработка была успешной. Однако, результаты после обработки снимка Sentinel-2a стали более чёткими, и совпадение с графическими материалами лесоустройства стало более точным. Данное отличие обусловлено параметрами видимых каналов космоснимков: у Landsat он 30 м, у Sentinel-2a — 10 м, поэтому использование Sentinel в работах лесохозяйственного направления будет более целесообразно. Снимки Sentinel-2a дают возможность отображения различий в состоянии растительности, в том числе, и временные изменения.<br />
<br />
=Литература=<br />
<br />
1.http://semiautomaticclassificationmanual-v4.readthedocs.org/en/latest/Tutorials.html<br />
<br />
2.Видеоматериалы от Luca Congedo.<br />
<br />
3.Карпачев А.П. Опыт классификации космоснимка Landsat с помощью Semi-Automatic Classification Plugin в QGIS http://gis-lab.info/qa/landsat_qgis_scp.html<br />
<br />
4.Крылов А. М., Соболев А. А., Владимирова Н. А. Выявление очагов короеда-типографа в Московской области с использованием снимков Landsat //Вестник Московского государственного университета леса - Лесной вестник. – 2011. – №. 4. – С. 54-60<br />
<br />
5.Крылов А.М., Владимирова Н.А., Малахова Е.Г. Использование свободных ГИС в системе дистанционного лесопатологического мониторинга // Вестник Московского государственного университета леса - Лесной Вестник, №1 2012. С. 148-152<br />
<br />
6.Крылов А.М., Владимирова Н.А. Дистанционный мониторинг состояния лесов по данным космической съемки // "ГЕОМАТИКА" №3(12), 2011 г. с. 53-57</div>Александр Мурыйhttps://wiki.gis-lab.info/index.php?title=%D0%9E%D0%BF%D1%8B%D1%82_%D0%BA%D0%BB%D0%B0%D1%81%D1%81%D0%B8%D1%84%D0%B8%D0%BA%D0%B0%D1%86%D0%B8%D0%B8_%D0%BA%D0%BE%D1%81%D0%BC%D0%BE%D1%81%D0%BD%D0%B8%D0%BC%D0%BA%D0%B0_Sentinel-_2a_%D1%81_%D0%BF%D0%BE%D0%BC%D0%BE%D1%89%D1%8C%D1%8E_Semi-Automatic_Classification_Plugin_%D0%B2_QGIS&diff=25214Опыт классификации космоснимка Sentinel- 2a с помощью Semi-Automatic Classification Plugin в QGIS2017-01-23T08:45:07Z<p>Александр Мурый: </p>
<hr />
<div>{{Статья|Опубликована|qgis-sacp-sentinel2a}}<br />
{{Аннотация|Данная статья описывает опыт работы с Semi-Automatic Classification Plugin для QGIS для классификации снимка Sentinel-2a с целью выявления лесонарушений на примере национального парка "Орловское полесье", а также содержит пошаговую инструкцию для лесного дешифрирования снимка с помощью данного плагина.}}<br />
<br />
'''''Карпачев Андрей Петрович, научный сотрудник- ГИС специалист НП "Орловское полесье"''<br />
<br />
= Цели проекта=<br />
<br />
1) Выявление участков нарушенного и усохшего леса на территории НП «Орловское полесье». <br />
<br />
2) Лесное дешифрирование (по породам) части территории Орловско-Брянско-Калужского региона.<br />
<br />
= Материалы и ПО=<br />
<br />
Для реализации классификации были выбраны тестовые участки на основе лесоустроительных материалов 2006 и 2016 года и полевых обходов.<br />
<br />
В проекте был использован космоснимок Sentinel-2a на территорию нацпарка за сентябрь 2016 года, предварительно скачанный с официального сайта.<br />
Программное обеспечение: <br />
Qgis 2.14.8 с интегрированным SAC плагином; <br />
<br />
<br />
= Подготовка к началу обработки, формирование директорий=<br />
<br />
Первым пунктом загружаем заранее скачанный снимок в плагин <br />
<br />
[[Файл:1%29put%27-open_file.png|frame|300x300px|центр|Начало работы]]<br />
<br />
Откроется следующая форма:<br />
[[Файл:2%29open_form.png|frame|300x300px|центр|Форма загрузки космоснимка]]<br />
<br />
Далее нажимаем на иконку [[Файл:3_perehod_k_granulam.png]] и «идём» к папке GRANULE из распакованного архива космоснимка Sentinel.<br />
В форме откроются каналы (bands) космоснимка. Здесь же снимаем флажок с Create Band set and use Band set tools. Ставим флажок Apply DOS1 atmospheric correction.<br />
<br />
[[Файл:4otobrajenie_spiska_granul_b.png|frame|300x300px|центр|Открытие бандов]]<br />
<br />
В этой же форме открываем файл metadata (находится в распакованной папке из архива космоснимка)<br />
<br />
[[Файл:4.1metadata_open.png|frame|300x300px|центр|Открытие файла metadata]]<br />
<br />
Нажимаем на иконку [[Файл:IconRUN.png]] : Создаём папку, называем RT. Далее мы наблюдаем ход атмосферной коррекции снимка, ждём окончания.<br />
<br />
[[Файл:6-_hod_atmcorrect.png|frame|40x40px|центр|Ход процесса коррекции]]<br />
<br />
После окончания в части Qgis СЛОИ, мы можем заметить, что слои снимка стали отображаться с префиксом RT.<br />
<br />
[[Файл:7-_postcorrection_RT.png|frame|300x300px|центр|Слои RT после окончания атмосферной коррекции]]<br />
<br />
Обрезка снимка. В данном пункте мы должны обрезать космоснимок до квадрата границ национального парка. Для выполнения данной процедуры, проходим:<br />
<br />
[[Файл:8-_way_cut_RT.png|frame|300x300px|центр|Путь к обрезке слоев космоснимка под квадрат территории наблюдения]]<br />
<br />
Откроется следующая форма:<br />
<br />
[[Файл:8.1-forma_cut.png|frame|300x300px|центр|Форма для настройки обрезки слоев космоснимка под квадрат территории наблюдения]]<br />
<br />
Нажимаем на иконку [[Файл:9-refresch_icon.png]]. <br />
<br />
Лист обновится. Выбираем все файлы RT иконкой [[Файл:10-selectall_icon.png]]. <br />
<br />
Иконкой крестик выбираем зону обрезки [[Файл:11-setareainthemap.png]]<br />
<br />
Выбранная зона выглядит следующим образом:<br />
<br />
[[Файл:12-select_area.png|frame|300x300px|центр|Выбранная территория]]<br />
<br />
Нажимаем на иконку [[Файл:IconRUN.png]], создаём папку CLIP, указываем путь к ней и запускаем обрезку.<br />
<br />
Итог: В списке СЛОИ образуются новые обрезанные слои космоснимка с префиксом clip.<br />
<br />
[[Файл:14-clip_file.png|frame|400x400px|центр|Обрезанные слои космоснимка с префиксом clip]]<br />
<br />
= Формирование Bandset, ROI и SIG=<br />
Нажимаем на вкладку Bandset [[Файл:15-bandset_vkladka.png]],<br />
<br />
Далее необходимо обновить форму соответствующей иконкой [[Файл:16-refresch_l.png]],<br />
<br />
В форму подгрузятся файлы с префиксами clip и RT:<br />
<br />
[[Файл:17-dawnload_clip_rt.png|frame|300x300px|центр|clip и RT]]<br />
<br />
Выбираем файлы только с префиксом clip [[Файл:18_file_for_bandset_icon_vibor.png]]<br />
<br />
Форма заполнится следующим образом:<br />
<br />
[[Файл:19_form_bandset_org.png|frame|300x300px|центр|Заполнение формы под bandset]]<br />
<br />
Далее выставляем показатель Quick wavelength settings по Sentinel-2 из выпадающего списка:<br />
<br />
[[Файл:20_configuration_of_sentinel2a.png|frame|300x300px|центр|Выбор настройки снимка под Sentinel-2a]]<br />
<br />
Форма изменит свой вид, добавятся значения center wavelength:<br />
<br />
[[Файл:21_Zapolnrnie_pod_sentinel.png|frame|300x300px|центр|авто-заполнение формы под Sentinel-2a]]<br />
<br />
Далее нажимаем на иконку [[Файл:22_createtnewraining_icon.png]], называем файл training и сохраняем его в папку (можно в заново созданную). <br />
<br />
Теперь нажимаем на генератор bandset (3-2-1), после чего автоматически образуется слой.<br />
<br />
[[Файл:23_sloy_bandset.png|frame|350x350px|центр|Bandset слой]]<br />
<br />
Слой band set.vrt образовался сразу же после первого введения композита.<br />
<br />
[[Файл:24_bs.png|frame|350x350px|центр|Bandset в вкладке слои]]<br />
<br />
В ходе эксперимента был выбран композит из каналов 4-6-12. Визуализация снимка опциональна, в зависимости от объекта дешифрирования и физических параметров органов зрения исполнителя.<br />
<br />
[[Файл:25 в 4-6-12_color_komposit.png|frame|350x350px|центр|Композит 4-6-12]]<br />
<br />
Теперь переходим на вкладку:<br />
<br />
[[Файл:26_vkladka_classificatdoc.png|frame|350x350px|центр|Вкладка классификации]]<br />
<br />
Откроется следующая форма:<br />
<br />
[[Файл:27_forma_class-do.png|frame|450x450px|центр|Форма ввода ROI]]<br />
<br />
Теперь нам необходимо набрать графические пробы (ROI) для осуществления полуавтоматической классификации.<br />
Выбираем на космоснимке участок водной поверхности и в верхней надстройке плагина нажимаем иконку «крестик»:<br />
<br />
[[Файл:28_water.png|frame|450x450px|центр|Водный объект. Захват пикселей]]<br />
<br />
Выделяем пиксели; для наилучшего захвата в соседнем с «крестиком» окошке вводим дистанцию захвата пикселей:<br />
<br />
[[Файл:29_water_pxl.png|frame|400x400px|центр|"Захваченные" пиксели воды]]<br />
<br />
Дистанция захвата подбирается опционально, до полной удовлетворенности результатом захватывания. Этот принцип будет также использоваться в наборе пикселей застройки, дорожной инфраструктуры, аграрного сектора и леса.<br />
Во вкладке ROI creation вводим классы, порядковые номера классов и подклассов и жмём на «дискету»:<br />
<br />
[[Файл:30_vkl_roicreation.png|frame|350x350px|центр|Подписывание классов пикселей]]<br />
<br />
Файл записывается в classification doc. Здесь сразу меняем цвет на эквивалентный объекту (вода - синий):<br />
<br />
[[Файл:31_nastroyka_color.png|frame|400x400px|центр|Замена цвета пикселей объекта]]<br />
<br />
Следующим шагом заходим в macroclasses и также меняем цвет:<br />
<br />
[[Файл:32_color_macrocklass.png|frame|400x400px|центр|Замена цвета макрокласса]]<br />
<br />
Далее определяем пиксели застройки на космоснимке. В композите 4-3-2 отлично читаются элементы дорог и застройки :<br />
<br />
[[Файл:33_komposit_building.png|frame|350x350px|центр|Композит 4-3-2. Застройка и элементы дорожной сети. Параметры захвата в изображении]]<br />
<br />
[[Файл:34_zahvat_zastroyki.png|frame|350x350px|центр|Захват пикселей застройки и дорог]]<br />
<br />
Помимо застройки, произошёл захват и пикселей дорожных полотен. Далее нам необходимо внести пиксели аграрных ландшафтов. Выбираем любой наиболее читаемый композит и поочередно выделяем пиксели. Так как обрабатываемые сельскохозяйственные территории имеют разные цвета, после отметки и захвата пикселей, называем макроклассы каким-нибудь одним типом префикса, меняя только порядковый номер. В примере sh1-sh6*. В видеопримере разработчика данный набор пикселей называется bare soil.<br />
<br />
[[Файл:35-vidi_bare_soil.png|frame|450x450px|центр|Пиксельные подклассы почвы]]<br />
<br />
Следующим шагом будет непосредственное определение усыханий еловых насаждений; разбиение снимка на хвою и листву, травянистую растительность. В проекте используются следующие сокращения: <br />
h- хвоя<br />
l- листва<br />
d- усыхание<br />
gras- трава<br />
<br />
Перед захватом необходимых групп пикселей сверим снимок с слоем лесоустройства. Включаем подписи по породам, это необходимо для того, чтобы понять местоположение выделов ели и сосны. Мертвопокровная сосна ухудшает качество дешифрирования, т.к цвет её пикселей частично совпадает с цветом пикселей усыхания, именно поэтому необходимо знать повыдельное расположение культур.<br />
<br />
[[Файл:36-LU_sravnenie.png|frame|450x450px|центр|Наложение лесоустроительных материалов на космоснимок]]<br />
<br />
[[Файл:37-vegklass-lhgd.png|frame|400x400px|центр|Выбранные классы растительности]]<br />
<br />
После выбранных групп пикселей (ROI) мы можем сделать предпросмотр, перед осуществлением классификации, нажимаем на иконку «цветной плюс»<br />
<br />
[[Файл:38-prew.png|frame|350x350px|центр|Предпросмотр]]<br />
<br />
После нажатия на эту иконку нажимаем на космоснимок для оценки результативности отбора ROI [[Файл:39_prosmotr.png]].<br />
После выбираем алгоритм классификации снимка:<br />
<br />
[[Файл:39.1_algoritm.png|frame|350x350px|центр|Выбор алгоритма обработки]]<br />
<br />
*В работе был выбран алгоритм максимального правдоподобия. Перед выполнением классификации были залиты белым цветом группы пикселей застройки, дорог, сельскохозяйственных зон, голых почв, лугов, т.к в работе нас интересует исключительно лес.<br />
<br />
[[Файл:40-maximum_lik.png|frame|350x350px|центр|Алгоритм- максимальное правдоподобие. Результат]]<br />
<br />
Следующим шагом заходим в настройки [[Файл:41-save_settings.png]] и нажимаем на вкладку Processing:<br />
<br />
<br />
[[Файл:42-processing.png|frame|350x350px|центр|Настройка вывода 1]]<br />
<br />
Здесь мы выставляем следующий параметр: RAM=1024 и закрываем вкладку.<br />
<br />
[[Файл:43_RAM.png|frame|350x350px|центр|Настройка вывода 2]]<br />
<br />
Далее заходим во вкладку classification output, жмём run, создаём папку classification и сохраняем; жмём на кнопку сохранить и ждём окончания процесса.<br />
<br />
[[Файл:44_outpoot.png|frame|350x350px|центр|Вывод]]<br />
<br />
''*при подготовке выяснилось, что первый канал шумит, и я его из синтеза исключил''<br />
<br />
= Показатель успешности =<br />
<br />
Изначальный снимок + слой лу2006 (лесоустройство 2006 г.). Фиолетовый цвет под слоем лесоустройства – предполагаемое усыхание<br />
<br />
[[Файл:Problem-snimok.png|frame|550x550px|центр|Космоснимок до алгоритма]]<br />
<br />
Классифицированный снимок:<br />
<br />
[[Файл:Problem_classif.png|frame|550x550px|центр|Классифицированный снимок.<br />
Оранжевый цвет(def)- лесонарушение]]<br />
<br />
Попадание по породам:<br />
<br />
[[Файл:Popadporod.png|frame|550x550px|центр|Совпадение выделов слоя лесоустройства с классифицированным снимком]]<br />
<br />
=Вывод=<br />
<br />
Ранее уже предпринималась попытка использования Semi-Automatic Classification Plugin в классификации космоснимка Landsat (http://gis-lab.info/qa/landsat_qgis_scp.html) и обработка была успешной. Однако, результаты после обработки снимка Sentinel-2a стали более чёткими и совпадение с графическими материалами лесоустройства стало более точным. Данное отличие обусловлено параметрами видимых каналов космоснимков: у Landsat он 30 м, у Sentinel-2a - 10 м, поэтому использование последних в работах лесохозяйственного направления будет более целесообразно. Снимки Sentinel-2a дают возможность отображения различий в состоянии растительности, в том числе и временные изменения.<br />
<br />
=Литература=<br />
<br />
1.http://semiautomaticclassificationmanual-v4.readthedocs.org/en/latest/Tutorials.html<br />
<br />
2.Видеоматериалы от Luca Congedo<br />
<br />
3.Карпачев А.П. Опыт классификации космоснимка Landsat с помощью Semi-Automatic Classification Plugin в QGIS http://gis-lab.info/qa/landsat_qgis_scp.html<br />
<br />
4.Крылов А. М., Соболев А. А., Владимирова Н. А. Выявление очагов короеда-типографа в Московской области с использованием снимков Landsat //Вестник Московского государственного университета леса - Лесной вестник. – 2011. – №. 4. – С. 54-60<br />
<br />
5.Крылов А.М., Владимирова Н.А., Малахова Е.Г. Использование свободных ГИС в системе дистанционного лесопатологического мониторинга // Вестник Московского государственного университета леса - Лесной Вестник, №1 2012. С. 148-152<br />
<br />
6.Крылов А.М., Владимирова Н.А. Дистанционный мониторинг состояния лесов по данным космической съемки // "ГЕОМАТИКА" №3(12), 2011 г. с. 53-57</div>Александр Мурыйhttps://wiki.gis-lab.info/index.php?title=%D0%A0%D0%B0%D0%B1%D0%BE%D1%82%D0%B0_%D1%81_%D1%80%D0%B5%D1%81%D1%83%D1%80%D1%81%D0%B0%D0%BC%D0%B8_%D0%B4%D0%BB%D1%8F_%D0%BE%D0%BF%D1%82%D0%B8%D0%BC%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_MapInfo&diff=25204Работа с ресурсами для оптимизации MapInfo2017-01-18T06:52:59Z<p>Александр Мурый: </p>
<hr />
<div>{{Статья|Черновик}}<br />
<br />
'''Внимание! Действия, производимые в статье, могут нарушать [http://www.pitneybowes.com/us/license-terms-of-use/software-and-data-end-user-license-agreement.html лицензионное соглашение] разработчиков MapInfo - компании Pitney Bowes. Поэтому любые изменения файлов и (или) их дальнейшая публикация производятся на свой страх и риск.'''<br />
<br />
В этой заметке будет описан способ редактирования диалоговых окон, текстовых объектов и других объектов интерфейса, находящиеся в ресурсах динамических библиотек, подгружаемых MapInfo.<br />
<br />
Для работы потребуется утилита [http://angusj.com/resourcehacker/resource_hacker.zip Resource Hacker], которая является бесплатной и не требует установки.<br />
<br />
== Работа с диалоговыми окнами ==<br />
<br />
Диалоговые окна в MapInfo — тема довольно интересная. Связано это с тем, что диалоговые окна по большей части являются статическими — их невозможно ни развернуть, ни растянуть, ни запомнить их последующее положение. Практически все окна в MapInfo небольшие по размеру. Возможно, это связано с тем, чтобы окна полностью уменьшались на экране монитора даже на самом низком разрешении. Однако, с развитием технологий и увеличением разрешения экрана, размер окон остался неизменным и всё таким же неудобным. Но существуют возможности исправить эти недочёты, о чём и будет сказано в этом разделе.<br />
<br />
После распаковки и запуска программы Resource Hacker, открываем файл '''mires.dll''', находящийся в корневой директории с MapInfo. Для примера написания данной статьи взята последняя 32-х битная версия MapInfo, а именно 15.0. Но файл mires.dll принципиально ничем не отличается, поэтому информация будет также актуальной и для старых версий MapInfo.<br />
<br />
После того, как файл mires.dll будет открыт, необходимо перейти в директорию Dialog. Далее, для примера, поработаем с окном "Закрыть таблицу". Для того, чтобы быстро найти нужный диалог и вдобавок узнать номер ресурса, можно воспользоваться поиском. Диалог и его описание выглядят так:<br />
<br />
[[Файл:Resources1.png|200px|thumb|center|Диалог "Закрыть таблицу"]]<br />
<br />
'''2520 DIALOG 21, 30, 203, 98''' - здесь следует объявление номера ресурса, а также начальные координаты появления окна на экране и размеры окна.<br />
<br />
'''STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU''' - объявление стилей согласно Win32 API. Полное их описание можно почитать на [https://msdn.microsoft.com/ru-ru/library/windows/desktop/ms632600(v=vs.85).aspx сайте MSDN]<br />
<br />
'''CAPTION "Закрыть таблицу"''' - Заголовок окна<br />
<br />
'''LANGUAGE LANG_RUSSIAN, 0x0''' - объявление используемого языка ресурса. В изменении нет необходимости<br />
<br />
'''FONT 8, "Helv"''' - объявление стиля и размера используемого в диалоге шрифта<br />
<br />
Далее следуют объявления компонентов диалога. Рассмотрим только создание статической надписи:<br />
<br />
'''CONTROL "&Закрыть таблицы:"''' - объявление заголовка для элемента диалога. Подходит преимущественно для текстовых надписей или кнопок.<br />
<br />
'''-1''' - идентификатор элемента диалога и при этом самая важная его часть. Если для статических надписей это число всегда равно "-1", то для остальных элементов это число уникально. Или почти уникально, в зависимости от выполняемой операции. Например, если в диалоге присутствует кнопка "ОК", или "Сохранить" (для диалога "Сохранить таблицу"), или "Восстановить" (для диалога "Восстановить таблицу"), то идентификатор этой кнопки всегда будет "1", что будет означать, что пользователь указал всё необходимое в диалоге и готов применить изменения. Для "Отмены" и "Справки" идентификаторы равны 2 и 5 соответственно, в любом диалоге. Или, например, список типов открытия таблицы ("В новой карте", "В активной карте" и т.д.) фигурирует в нескольких диалогах и в каждом из них имеет один и тот же идентификатор, так как список передаётся туда программно.<br />
<br />
'''STATIC''' - тип элемента диалога. В данном случае статическая строка, на которую ничего не передаётся из другого элемента управления.<br />
<br />
Далее следуют знакомые уже типы стилей оформления и координаты.<br />
<br />
Элементы диалога возможно изменять как путём перемещения и растягивания оных с помощью мышки, так и задавая точные необходимые значения. Например, если необходимо будет поменять размер и сдвинуть какие-то кнопки, то проще это сделать путём редактирования координат и размеров, представленных в числовом виде. При этом необходимо в конце редактирования нажимать клавишу F5 для компиляции ресурса, чтобы изменения сразу вступили в силу и их можно было увидеть. В случае изменения объектов мышкой нажимать каждый раз F5 необязательно (так как изменения буду стразу же видны), однако, это всё равно потребуется в конце всех операций. В конечном итоге может получиться подобный результат:<br />
<br />
[[Файл:Resources2.png|200px|thumb|center|Измененный диалог в MapInfo]]<br />
<br />
После компиляции всех изменений, файл '''mires.dll''' необходимо сохранить. Сохранение возможно только при закрытом MapInfo.<br />
<br />
== Работа с динамическими элементами диалога ==<br />
<br />
В диалогах MapInfo не все элементы являются статическими. Некоторые элементы подгружаются посредством программного кода. Самый яркий пример - окно регистрации растра, которое является крайне неудобным для выполнения своих задач.<br />
<br />
[[Файл:Resources3.png|200px|thumb|center|Окно регистрации растра]]<br />
<br />
Чтобы его расширить, необходимо помимо растягивания главного окна диалога вытянуть по длине полосы вертикальной и горизонтальной прокрутки. После этого рабочее пространство привязки увеличится. А при желании можно поиграться с другими элементами управления и в итоге получить что-то вроде этого:<br />
<br />
[[Файл:Resources4.png|200px|thumb|center|Растянутое окно регистрации растра]]<br />
<br />
Как уже говорилось ранее, если окно растянуть до размеров экрана 1920х1280, то при разрешении экрана, например, 1280х960, окно выползет за пределы и работать станет практически невозможно. За этим необходимо следить.<br />
<br />
== Примеры настройки стилей в диалоговых окнах ==<br />
<br />
'''''Пример 1. Стили полей ввода текста.'''''<br />
<br />
[[Файл:Resources5.png|200px|thumb|center|Диалог правки текстовых объектов]]<br />
<br />
Речь пойдёт о поле ввода текста. Основное неудобство его в том, что диалог нельзя закончить путём нажатия клавиши Enter - необходимо нажимать мышкой на кнопку ОК. Либо посредством клавиши Tab переключаться между элементами диалога до тех пор, пока не дойдём до кнопки ОК. Тогда клавиша Enter сработает. Связано это с тем, что нажатие клавиши Enter в поле ввода текста приводит к началу новой строки и возврату каретки. Однако эта особенность - не более чем один из стилей оформления данного элемента диалога. Чтобы изменить элемент, нужно кликнуть по нему правой кнопкой мыши и выбрать "Edit Control".<br />
<br />
[[Файл:Resources6.png|200px|thumb|center|Окно редактирования стилей и других параметров элемента диалога]]<br />
<br />
Интересующий нас стиль называется '''ES_WANTRETURN'''. После его отключения, компиляции изменений и сохранения файла, текст в этом диалоге (уже в самом MapInfo) можно будет закрыть посредством нажатия Enter. А так как клавиша Enter больше не создаёт новую строку текста, то для её создания нужно использовать комбинацию Ctrl+Enter. Кроме того, поле ввода текста можно тоже увеличить и получить что-то подобное:<br />
<br />
[[Файл:Resources7.png|200px|thumb|center|Растянутый диалог правки текстовых объектов]]<br />
<br />
По этой же технологии можно изменить поле ввода текста для диалога редактирования выносных подписей.<br />
<br />
'''''Пример 2. Стили кнопочных элементов.'''''<br />
<br />
Снова обратимся к диалогу "Закрыть таблицу" и к кнопкам "Закрыть" и "Отмена".<br />
<br />
'''CONTROL "Закрыть", 1, BUTTON, BS_DEFPUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_GROUP | WS_TABSTOP, 170, 19, 45, 14''' <br />
<br />
''' CONTROL "Отмена", 2, BUTTON, BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_GROUP | WS_TABSTOP, 170, 37, 45, 14''' <br />
<br />
Если нас не устраивает тот факт, что клавиша "Закрыть" подсвечена по-умолчанию и при нажатии клавиши Enter выбранные таблицы будут закрыты, то можно изменить стиль оформления с '''BS_DEFPUSHBUTTON''' на '''BS_PUSHBUTTON''' у кнопки "Закрыть". При этом необходимо провести обратную операцию для кнопки "Отмена". В этом случае кнопкой по-умолчанию станет она и результат будет достигнут.<br />
<br />
'''''Пример 3. Дополнительные стили кнопочных элементов.'''''<br />
<br />
Рассмотрим диалог под номером 9070 из файла '''mires.dll'''.<br />
<br />
[[Файл:Resources10.png|200px|thumb|center|Динамический диалог]]<br />
<br />
Диалог интересен тем, что является динамическим, то есть его содержимое меняется в зависимости от запросов MapInfo. Кроме того, программно устанавливается, какая кнопка будет главной (подсвеченной) по-умолчанию. Например, если в MapInfo подгружена только одна таблица, в которой произошли изменения и есть необходимость восстановить её, то MapInfo выдаст соответствующий запрос, в котором предложит либо восстановить таблицу, либо отменить и закрыть диалог. При этом кнопка "Восстановить" (та же кнопка "ОК", только с заменённым текстом) будет в приоритете, хотя стиль '''BS_DEFPUSHBUTTON''' не установлен. В других случаях, таких как изменение типа полей таблицы, может возникать другое окно, где уже кнопка "Отмена" будет в приоритете.<br />
<br />
Предположим, что существует необходимость, чтобы кнопка "Отмена" никогда не бывала активной по-умолчанию. Но тут возникает интересная особенность. Кнопка "Отмена" вместо ID 2 имеет ID 4. Попытка смены ID даёт то, что кнопка перестаёт отображаться вообще. Попытка принудительно назначить на кнопку "ОК" стиль '''BS_DEFPUSHBUTTON''' ничего не даёт. Поэтому альтернативным вариантом решения является возможность отключения этой кнопки, так как диалог в любом случае можно закрыть с помощью клавиши Esc. Чтобы отключить кнопку необходимо применить для кнопки стиль '''WS_DISABLED'''. После чего можно получить такой результат:<br />
<br />
[[Файл:Resources11.png|200px|thumb|center|Отключенная кнопка]]<br />
<br />
== Добавление и изменение текстовых констант ==<br />
<br />
В этом разделе речь пойдёт о текстовых строках, которые также содержатся в ресурсах. Основные файлы, где они хранятся, это '''mires.dll''' и '''micore.dll'''. Рассматриваться будет '''micore.dll'''. Необходимо открыть файл, перейти в директорию '''String Table''' и открыть раздел строк под номером 393.<br />
<br />
[[Файл:Resources8.png|200px|thumb|center|Текстовые строки]]<br />
<br />
Как видно из примера, здесь представлено некоторое количество строк. Некоторые из них специальные, содержащие переменные, в которые будут подставлены названия таблиц или колонок. Однако, нас интересуют строки под номерами 6276-6279. Там представлены функции соответствующего списка из диалога "SQL-запрос". Если следовать представленному синтаксису, то можно дописать в этот раздел свои собственные функции. Например, можно добавить функции ObjectInfo и ObjectGeography в строку 6278. После сохранения и компиляции результат будет выглядеть так:<br />
<br />
[[Файл:Resources9.png|200px|thumb|center|Новые добавленные функции]]<br />
<br />
== Заключение ==<br />
<br />
В статье показаны варианты редактирования диалоговых окон и их элементов. Возможности добавления своих элементов сильно ограничены, так как основная их часть должна быть запрограммирована для достижения тех или иных результатов, чего через ресурсы выполнить невозможно. Но тем не менее при определённых навыках и творчестве можно получить интересные варианты.<br />
<br />
На этом всё. Успешной работы!<br />
<br />
{{Статья|Черновик}}</div>Александр Мурыйhttps://wiki.gis-lab.info/index.php?title=%D0%97%D0%B0%D1%8F%D0%B2%D0%BA%D0%B8_%D0%BD%D0%B0_%D0%BF%D1%83%D0%B1%D0%BB%D0%B8%D0%BA%D0%B0%D1%86%D0%B8%D1%8E_%D1%81%D1%82%D0%B0%D1%82%D0%B5%D0%B9&diff=25180Заявки на публикацию статей2017-01-09T07:06:03Z<p>Александр Мурый: </p>
<hr />
<div>{{Организационное|articles-request}}<br />
<br />
Внизу вы видите таблицу заявок на публикацию статей на GIS-Lab, она показывает статьи, которые их авторы считают более-менее готовыми к публикации. Таблица создана, чтобы сделать процесс принятия статей более прозрачным для авторов и оптимизировать работу редакторов. <br />
<br />
Таблица будет обновляться ведущим по мере поступления заявок от авторов. Заявкой на публикацию считается:<br />
* если статья оформляется на вики GIS-Lab — создание автором темы на форуме для обсуждения статьи (в разделе [http://gis-lab.info/forum/viewforum.php?f=3 Материалы сайта]);<br />
* если статья оформляется НЕ на вики GIS-Lab — личное обращение автора к [http://gis-lab.info/qa/org.html редактору] с предоставлением текста статьи.<br />
<br />
<br />
{| class="wikitable sortable" <br />
|-<br />
! width="5%" | Дата поступления заявки<br />
! width="30%" | Ссылка на вики, название <br />
! width="25%" | Автор (имя / ID)<br />
! width="25%" | Тема обсуждения на форуме (номер / название)<br />
! width="10%" | Дата создания<br />
<br />
|-<br />
| 02.01.2017<br />
| [http://wiki.gis-lab.info/w/%D0%9E%D0%BF%D1%8B%D1%82_%D0%BA%D0%BB%D0%B0%D1%81%D1%81%D0%B8%D1%84%D0%B8%D0%BA%D0%B0%D1%86%D0%B8%D0%B8_%D0%BA%D0%BE%D1%81%D0%BC%D0%BE%D1%81%D0%BD%D0%B8%D0%BC%D0%BA%D0%B0_Sentinel-_2a_%D1%81_%D0%BF%D0%BE%D0%BC%D0%BE%D1%89%D1%8C%D1%8E_Semi-Automatic_Classification_Plugin_%D0%B2_QGIS Опыт классификации космоснимка Sentinel- 2a с помощью Semi-Automatic Classification Plugin в QGIS]<br />
| Zubr / 16406<br />
| 21907 / Опыт класс-и снимка Sentinel-2a в SACplugin QGis<br />
| 01.01.2017<br />
|-<br />
| 09.01.2017<br />
| [http://wiki.gis-lab.info/w/%D0%A0%D0%B0%D0%B1%D0%BE%D1%82%D0%B0_%D1%81_%D1%80%D0%B5%D1%81%D1%83%D1%80%D1%81%D0%B0%D0%BC%D0%B8_%D0%B4%D0%BB%D1%8F_%D0%BE%D0%BF%D1%82%D0%B8%D0%BC%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D1%8B_%D0%B2_MapInfo Работа с ресурсами для оптимизации MapInfo]<br />
| Slinger / 9620<br />
| 21916 / Работа с ресурсами для оптимизации MapInfo<br />
| 06.12.2016<br />
<br />
<br />
<br />
<br />
[[Категория:Служебные]]</div>Александр Мурыйhttps://wiki.gis-lab.info/index.php?title=%D0%9C%D0%BE%D0%B4%D0%B5%D0%BB%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%86%D0%B8%D0%B9_%D0%BE%D1%80%D0%B1%D0%B8%D1%82_%D0%98%D0%A1%D0%97_%D0%BD%D0%B0_%D0%BF%D0%BE%D0%B2%D0%B5%D1%80%D1%85%D0%BD%D0%BE%D1%81%D1%82%D1%8C_%D0%97%D0%B5%D0%BC%D0%BB%D0%B8_%D0%BD%D0%B0_Python_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%BC%D0%BE%D0%B4%D0%B5%D0%BB%D0%B8_SGP4_%D0%B8_API_space-track.org&diff=25179Моделирование проекций орбит ИСЗ на поверхность Земли на Python с использованием модели SGP4 и API space-track.org2017-01-09T07:01:56Z<p>Александр Мурый: </p>
<hr />
<div>{{Статья|Опубликована|python-orbital-tracks}}<br />
<br />
{{Аннотация|Определение положения ИСЗ по орбитальным данным на заданное время по модели SGP4. Автоматизированное получение орбитальных данных с помощью API сервиса space-track.org. Пример реализации на языке Python.}}<br />
<br />
Задачу определения положения того или иного искусственного спутника Земли в заданный момент времени (в прошлом или недалёком будущем) приходится решать для самых разнообразных целей, в том числе связанных с дистанционным зондированием Земли из космоса. Часть данных (например, многие продукты MODIS) распространяется без строгой географической привязки, а лишь с указанием времени непосредственного наблюдения территории для каждой сцены, — и для автоматизации поиска и загрузки таких данных требуется вычислять время пролёта спутника над исследуемыми объектами. Часто возникает и потребность определить время зондирования заданной территории в будущем - чаще всего для проведения подспутниковых наблюдений (в целях верификации, атмосферной коррекции и пр.).<br />
<br />
В статье описывается подход к моделированию проекций орбит ИСЗ на поверхность Земли с использованием доступных средств: библиотек языка Python и API сервиса space-track.org.<br />
<br />
== Входные параметры модели SGP4 ==<br />
<br />
Наиболее распространенной моделью для определения положения спутников на орбите является SGP (Simplified General Perturbations), различные модификации которой используются в оперативной работе по всему миру начиная с 70-х годов. Главная задача модели - вычислить скорость и геоцентрические координаты ИСЗ (X, Y, Z) на заданный момент времени, которые нетрудно пересчитать на поверхность эллипсоида, получив географические координаты проекции положения ИСЗ (широта, долгота). Сама модель достаточно сложна, хотя и сводится к линейным расчётам и удобна для алгоритмизации. Её описание и оригинальный FORTRAN-код можно найти в соответствующих документах [1,2].<br />
<br />
В качестве входных параметров SGP использует данные телеметрии спутников в формате TLE (two-line element sets): это две линии по 69 символов, описывающие основные метаданные спутника и параметры телеметрии [3]. Содержание первой линии:<br />
<br />
{| class="wikitable"<br />
|-<br />
! Номер !! Положение !! Содержание !! Пример<br />
|-<br />
| 1 || 01-01 || Номер строки || 1<br />
|-<br />
| 2 || 03-07 || Номер спутника в базе данных NORAD || 25994<br />
|-<br />
| 3 || 08-08 || Классификация (U=Unclassified — не секретный) || U<br />
|-<br />
| 4 || 10-11 || Международное обозначение (последние две цифры года запуска) || 99<br />
|-<br />
| 5 || 12-14 || Международное обозначение (номер запуска в этом году) || 068<br />
|-<br />
| 6 || 15-17 || Международное обозначение (часть запуска) || A<br />
|-<br />
| 7 || 19-20 || Год эпохи (последние две цифры) || 16<br />
|-<br />
| 8 || 21-32 || Время эпохи (целая часть — номер дня в году, дробная — часть дня) || 052.07623983<br />
|-<br />
| 9 || 34-43 || Первая производная от среднего движения (ускорение), деленная на два [виток/день^2] || .00001336 <br />
|-<br />
| 10 || 45-52 || Вторая производная от среднего движения, деленная на шесть (подразумевается, что число начинается с десятичного разделителя) [виток/день^3] || 00000-0<br />
|-<br />
| 11 || 54-61 || Коэффициент торможения B* (подразумевается, что число начинается с десятичного разделителя) || 30635-3<br />
|-<br />
| 12 || 63-63 || Изначально — типы эфемерид, сейчас — всегда число 0 || 0<br />
|-<br />
| 13 || 65-68 || Номер (версия) элемента || 999<br />
|-<br />
| 14 || 69-69 || Контрольная сумма по модулю 10 || 6<br />
|}<br />
<br />
Собранный пример: 1 25994U 99068A 16052.07623983 .00001336 00000-0 30635-3 0 9996<br />
<br />
Содержание второй линии:<br />
{| class="wikitable"<br />
|-<br />
! Номер !! Положение !! Содержание !! Пример<br />
|-<br />
| 1 || 01-01 || Номер строки || 1<br />
|-<br />
| 2 || 03-07 || Номер спутника в базе данных NORAD || 25994<br />
|-<br />
| 3 || 09-16 || Наклонение в градусах || 98.1986 <br />
|-<br />
| 4 || 18-25 || Долгота восходящего узла в градусах || 128.0087 <br />
|-<br />
| 5 || 27-33 || Эксцентриситет (подразумевается, что число начинается с десятичного разделителя) || 0001485 <br />
|-<br />
| 6 || 35-42 || Аргумент перицентра в градусах || 109.3968<br />
|-<br />
| 7 || 44-51 || Средняя аномалия в градусах || 250.7393 <br />
|-<br />
| 8 || 53-63 || Частота обращения (оборотов в день) (среднее движение) [виток/день] || 14.57136668<br />
|-<br />
| 9 || 64-68 || Номер витка на момент эпохи || 86046<br />
|-<br />
| 10 || 69-69 || Контрольная сумма по модулю 10 || 2<br />
|}<br />
<br />
Собранный пример: 2 25994 98.1986 128.0087 0001485 109.3968 250.7393 14.57136668860462<br />
<br />
Важно понимать, что такие эфемериды описывают мгновенное состояние ИСЗ, и, хотя описывают его поведение с довольно высокой точностью, при увеличении дальности прогноза (относительно данной эпохи) будут давать всё большую и большую ошибку. <br />
<br />
=== Получение данных TLE ===<br />
<br />
Данные TLE сегодня публикуются многими поставщиками (например, [http://eostation.scanex.ru/schedule/tle/new.tle последние данные TLE по ряду спутников ДЗЗ на сайте ScanEx]), но нам нужно получать не только свежие данные, но и архивные, для моделирования положений спутников в прошлом.<br />
<br />
Одним из лучших в сети ресурсов представляется портал [[space-track.org]], предоставляющий доступ к обширной информации о спутниках различного назначения. Очень важно, что space-track имеет REST API, позволяющее получать нужные данные максимально удобно. Требуется авторизация (и для доступа к интерфейсу, и для программного обращения к API), регистрация при этом бесплатная и открытая. Забегая вперёд, скажем в пользу space-track ещё то, что для работы с его API существует открытая python-библиотека.<br />
<br />
<br />
[[Файл:Sgp_spacetrack_screen.png|center|thumb|600px|Главная страница портала Space-Track.org]]<br />
<br />
<br />
Непосредственно в интерфейсе сайта можно запрашивать данные TLE (в разделе [https://www.space-track.org/#/tle Retrieve TLE Data by Satellite Catalog Number]), заполнив небольшую форму с указанием названия или идентификатора спутника, а также интересующего вас периода времени. Для примера запросим данные TLE для спутника AQUA на середину мая 2012 года:<br />
<br />
<br />
[[Файл:Sgp_spacetrack_iface_ex1.png|center|thumb|600px|Запрос данных в интерфейсе сайта и ответ]]<br />
<br />
<br />
Результат вы получаете мгновенно. Примечательно, что сразу же при выдаче ответа сервис выводит команду API, соответствующую вашему запросу - это позволяет очень быстро разобраться в том, как оно организовано и как с ним работать.<br />
<br />
<pre><br />
https://www.space-track.org/basicspacedata/query/class/tle/EPOCH/2012-05-11--2012-05-12/NORAD_CAT_ID/27424/orderby/TLE_LINE1 ASC/format/tle<br />
</pre><br />
<br />
<br />
Выполнив этот запрос тут же в адресной строке браузера (т.е. реализовав простой HTTP-запрос), можно увидеть, что при работе с API данные представляются в незамысловатом текстовом виде, в котором их, учитывая строгую структуру формата, несложно интерпретировать программно.<br />
<br />
<br />
[[Файл:Sgp_api_browser_ex.png|center|thumb|600px|Запрос данных напрямую и ответ]]<br />
<br />
<br />
В целом API [https://www.space-track.org/documentation#/api подробно документировано]. Для нашей задачи вполне достаточно рассмотреть тот пример, который был получен для майских приключений спутника AQUA. Изменяемыми в этой строке запроса будут всего два параметра:<br />
* Диапазон дат (2012-05-11--2012-05-12), формат yyyy-mm-dd--yyyy-mm-dd;<br />
* Идентификатор ИСЗ (27424).<br />
<br />
Идентификатор нужного вам ИСЗ можно найти там же, на space-track, в разделе [https://www.space-track.org/#/catalog SATCAT], в удобном интерактивном интерфейсе. Нас интересует первая колонка таблицы результатов поиска. Например, поищем идентификаторы спутников программы Landsat:<br />
<br />
<br />
[[Файл:Sgp_satcat_ex.png|center|thumb|600px|Результаты поиска по ключевому слову Landsat]]<br />
<br />
<br />
Landsat 8 соотвествует номеру 39084. Попробуем найти актуальные TLE для этого спутника, заодно посмотрев, как изменится структура запроса при использовании не диапазона дат, а опции "Latest", т.е. "последние данные". Запрос:<br />
<br />
<pre><br />
https://www.space-track.org/basicspacedata/query/class/tle_latest/ORDINAL/1/NORAD_CAT_ID/39084/orderby/TLE_LINE1%20ASC/format/tle<br />
</pre><br />
<br />
и ответ:<br />
<br />
<pre><br />
1 39084U 13008A 16354.79369944 .00000065 00000-0 24444-4 0 9999<br />
2 39084 98.2045 61.9547 0001318 94.3108 265.8241 14.57119154204938<br />
</pre><br />
<br />
Как видно, порядок аргументов в запросе изменился.<br />
<br />
<br />
== Программная реализация ==<br />
<br />
Открытая программная реализация модели SGP4 доступна для [https://www.danrw.com/sgp4/ C++] и [https://pypi.python.org/pypi/pyorbital Python (библиотека pyorbital)]. Для примера будем использовать именно Python и pyorbital (есть и другая реализация на Python'e: [https://github.com/brandon-rhodes/python-sgp4 python-sgp4]). Для получения данных от API space-track.org доступна [http://pythonhosted.org/spacetrack/index.html специальная библиотека]. Чтобы представить результат в формате геоданных применим библиотеку [https://pypi.python.org/pypi/pyshp pyshp]. Поскольку за нас уже почти всё сделали, код очень прост. Разберём его по разделам.<br />
<br />
=== Установка необходимых библиотек ===<br />
<br />
Все библиотеки доступны в основном репозитории Python и устанавливаются очень просто<br />
<br />
<pre><br />
pip install pyorbital<br />
pip install spacetrack<br />
pip install pyshp<br />
</pre><br />
<br />
=== Получение данных space-track.org ===<br />
<source lang="python"><br />
# Импортируем библиотеки<br />
# Штатная библиотека для работы со временем<br />
from datetime import datetime, date<br />
# Собственно клиент для space-track<br />
# Набор операторов для управления запросами. Отсюда нам понадобится время<br />
import spacetrack.operators as op<br />
# Главный класс для работы с space-track<br />
from spacetrack import SpaceTrackClient<br />
<br />
# Имя пользователя и пароль сейчас опишем как константы<br />
USERNAME = <YOUR SPACE-TRACK USERNAME><br />
PASSWORD = <YOUR SPACE-TRACK PASSWORD><br />
<br />
# Для примера реализуем всё в виде одной простой функции<br />
# На вход она потребует идентификатор спутника, диапазон дат, имя пользователя и пароль. Опциональный флаг для последних данных tle<br />
def get_spacetrack_tle (sat_id, start_date, end_date, username, password, latest=False):<br />
# Реализуем экземпляр класса SpaceTrackClient, инициализируя его именем пользователя и паролем<br />
st = SpaceTrackClient(identity=username, password=password)<br />
# Выполнение запроса для диапазона дат:<br />
if not latest:<br />
# Определяем диапазон дат через оператор библиотеки<br />
daterange = op.inclusive_range(start_date, end_date)<br />
# Собственно выполняем запрос через st.tle<br />
data = st.tle(norad_cat_id=sat_id, orderby='epoch desc', limit=1, format='tle', epoch = daterange)<br />
# Выполнение запроса для актуального состояния<br />
else:<br />
# Выполняем запрос через st.tle_latest<br />
data = st.tle_latest(norad_cat_id=sat_id, orderby='epoch desc', limit=1, format='tle')<br />
<br />
# Если данные недоступны<br />
if not data:<br />
return 0, 0<br />
<br />
# Иначе возвращаем две строки<br />
tle_1 = data[0:69]<br />
tle_2 = data[70:139]<br />
return tle_1, tle_2<br />
</source><br />
<br />
Представлена очень простая функция, использующая клиентскую библиотеку space-track для получения одного (первого из запроса) набора tle. Пример её использования:<br />
<br />
<source lang="python"><br />
# Запросим данные о положении Landsat 8 11 мая 2016 года<br />
# Обратите внимание, что даты указываем в формате date(y,m,d)<br />
tle_1, tle_2 = get_spacetrack_tle (39084, date(2016,5,11), date(2016,5,12), USERNAME, PASSWORD)<br />
print tle_1, tle_2<br />
<br />
>>> 1 39084U 13008A 16132.92196421 +.00000109 +00000-0 +34320-4 0 9999<br />
>>> 2 39084 098.2260 203.0765 0001471 094.1169 266.0197 14.57124417160799<br />
<br />
# А теперь данные об актуальном положении<br />
tle_1, tle_2 = get_spacetrack_tle (39084, None, None, USERNAME, PASSWORD, True)<br />
print tle_1, tle_2<br />
<br />
>>> 1 39084U 13008A 16354.79369944 .00000065 00000-0 24444-4 0 9999<br />
>>> 2 39084 98.2045 61.9547 0001318 94.3108 265.8241 14.57119154204938<br />
</source><br />
<br />
=== Расчёт координат проекции спутника ===<br />
<source lang="python"><br />
# Импортируем библиотеки<br />
# Штатная библиотека для работы со временем<br />
from datetime import datetime, date<br />
# Ключевой класс библиотеки pyorbital<br />
from pyorbital.orbital import Orbital<br />
<br />
# Ещё одна простая функция, для демонстрации принципа.<br />
# На вход она потребует две строки tle и время utc в формате datetime.datetime<br />
def get_lat_lon_sgp (tle_1, tle_2, utc_time):<br />
# Инициализируем экземпляр класса Orbital двумя строками TLE<br />
orb = Orbital("N", line1=tle_1, line2=tle_2)<br />
# Вычисляем географические координаты функцией get_lonlatalt, её аргумент - время в UTC.<br />
lon, lat, alt = orb.get_lonlatalt(utc_time)<br />
return lon, lat<br />
</source><br />
<br />
Пример использования:<br />
<source lang="python"><br />
# Используем данные TLE полученные вручную на space-track.org для спутника Terra<br />
tle_1 = '1 25994U 99068A 16355.18348138 .00000089 00000-0 29698-4 0 9992'<br />
tle_2 = '2 25994 98.2045 66.7824 0000703 69.9253 290.2059 14.57115924904601'<br />
# Нас интересует текущий момент времени<br />
utc_time = datetime.utcnow()<br />
# Обращаемся к фукнции и выводим результат<br />
lon, lat = get_lat_lon_sgp (tle_1, tle_2, utc_time)<br />
print lon, lat<br />
<br />
>>> 175.589796941 -13.6408377148<br />
</source><br />
<br />
=== Создание набора геоданных с треком спутника ===<br />
<br />
Теперь объединим получение данных space-track и расчёт положения спутника, добавив создание точечного шейп-файла. Зададимся целью написать функцию, которая бы создавала точечный шейп-файл с положениями спутника в течение указанных суток с заданным шагом по времени, в атрибуты каждой точки записывая широту, долготу и время пролёта.<br />
<br />
<source lang="python"><br />
# Импортируем библиотеки - для начала оговоренные ранее<br />
from datetime import datetime, date, timedelta<br />
import spacetrack.operators as op<br />
from spacetrack import SpaceTrackClient<br />
from pyorbital.orbital import Orbital<br />
<br />
# И pyshp, которая понадобится для создания шейп-файла<br />
import shapefile<br />
<br />
# Имя пользователя и пароль<br />
USERNAME = <YOUR SPACE-TRACK USERNAME><br />
PASSWORD = <YOUR SPACE-TRACK PASSWORD><br />
<br />
# Уже описанная ранее функция get_spacetrack_tle может использоваться без изменений<br />
def get_spacetrack_tle (sat_id, start_date, end_date, username, password, latest=False):<br />
st = SpaceTrackClient(identity=username, password=password)<br />
if not latest:<br />
daterange = op.inclusive_range(start_date, end_date)<br />
data = st.tle(norad_cat_id=sat_id, orderby='epoch desc', limit=1, format='tle', epoch = daterange)<br />
else:<br />
data = st.tle_latest(norad_cat_id=sat_id, orderby='epoch desc', limit=1, format='tle')<br />
<br />
if not data:<br />
return 0, 0<br />
<br />
tle_1 = data[0:69]<br />
tle_2 = data[70:139]<br />
return tle_1, tle_2<br />
<br />
# А вот функция get_lat_lon_sgp нам уже не пригодится в своём виде<br />
# ведь создавать экземпляр класса Orbital для каждого момента времени<br />
# не очень-то хочется<br />
<br />
# На вход будем требовать идентификатор спутника, день (в формате date (y,m,d))<br />
# шаг в минутах для определения положения спутника, путь для результирующего файла<br />
def create_orbital_track_shapefile_for_day (sat_id, track_day, step_minutes, output_shapefile):<br />
# Для начала получаем TLE <br />
# Если запрошенная дата наступит в будущем, то запрашиваем самые последний набор TLE <br />
if track_day > date.today():<br />
tle_1, tle_2 = get_spacetrack_tle (sat_id, None, None, USERNAME, PASSWORD, True)<br />
# Иначе на конкретный период, формируя запрос для указанной даты и дня после неё<br />
else:<br />
tle_1, tle_2 = get_spacetrack_tle (sat_id, track_day, track_day + timedelta(days = 1), USERNAME, PASSWORD, False)<br />
<br />
# Если не получилось добыть <br />
if not tle_1 or not tle_2:<br />
print 'Impossible to retrieve TLE' <br />
return<br />
<br />
# Создаём экземляр класса Orbital<br />
orb = Orbital("N", line1=tle_1, line2=tle_2)<br />
<br />
# Создаём экземпляр класса Writer для создания шейп-файла, указываем тип геометрии<br />
track_shape = shapefile.Writer(shapefile.POINT)<br />
<br />
# Добавляем поля - идентификатор, время, широту и долготу<br />
# N - целочисленный тип, C - строка, F - вещественное число<br />
# Для времени придётся использовать строку, т.к. нет поддержки формата "дата и время"<br />
track_shape.field('ID','N',40)<br />
track_shape.field('TIME','C',40)<br />
track_shape.field('LAT','F',40)<br />
track_shape.field('LON','F',40)<br />
<br />
# Объявляем счётчики, i для идентификаторов, minutes для времени<br />
i = 0<br />
minutes = 0<br />
<br />
# Простой способ пройти сутки - с заданным в минутах шагом дойти до 1440 минут.<br />
# Именно столько их в сутках!<br />
while minutes < 1440:<br />
# Расчитаем час, минуту, секунду (для текущего шага)<br />
utc_hour = int(minutes // 60)<br />
utc_minutes = int((minutes - (utc_hour*60)) // 1)<br />
utc_seconds = int(round((minutes - (utc_hour*60) - utc_minutes)*60))<br />
<br />
# Сформируем строку для атрибута<br />
utc_string = str(utc_hour) + '-' + str(utc_minutes) + '-' + str(utc_seconds)<br />
# И переменную с временем текущего шага в формате datetime<br />
utc_time = datetime(track_day.year,track_day.month,track_day.day,utc_hour,utc_minutes,utc_seconds)<br />
<br />
# Считаем положение спутника<br />
lon, lat, alt = orb.get_lonlatalt(utc_time)<br />
<br />
# Создаём в шейп-файле новый объект<br />
# Определеяем геометрию<br />
track_shape.point(lon,lat)<br />
# и атрибуты<br />
track_shape.record(i,utc_string,lat,lon)<br />
<br />
# Не забываем про счётчики<br />
i += 1<br />
minutes += step_minutes<br />
<br />
# Вне цикла нам осталось записать созданный шейп-файл на диск.<br />
# Т.к. мы знаем, что координаты положений ИСЗ были получены в WGS84<br />
# можно заодно создать файл .prj с нужным описанием<br />
<br />
try:<br />
# Создаем файл .prj с тем же именем, что и выходной .shp<br />
prj = open("%s.prj" % output_shapefile.replace('.shp',''), "w")<br />
# Создаем переменную с описанием EPSG:4326 (WGS84)<br />
wgs84_wkt = 'GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["degree",0.0174532925199433]]'<br />
# Записываем её в файл .prj <br />
prj.write(wgs84_wkt)<br />
# И закрываем его<br />
prj.close()<br />
# Функцией save также сохраняем и сам шейп.<br />
track_shape.save(output_shapefile)<br />
except:<br />
# Вдруг нет прав на запись или вроде того...<br />
print 'Unable to save shapefile'<br />
return<br />
</source><br />
<br />
Вот и всё, давайте проверим, как это работает, и проверим корректность! Данные о положении спутника Terra с частотой 5 минут публикуются на [https://www.ssec.wisc.edu/datacenter/terra/ специальном сервисе Space Science and Engineering Data Center], с ним и сверимся. Смоделируем положения на 15 декабря 2016 года и визуализируем получившийся набор геоданных в QGIS.<br />
<br />
<source lang="python"><br />
create_orbital_track_shapefile_for_day(25994, date(2016,12,15), 5, '/home/silent/space/terra_15_12_2016_5min.shp')<br />
</source><br />
<br />
Настроив в QGIS подписи и подложив OSM получим следующую картинку:<br />
<br />
<br />
[[Файл:sgp_test_terra15122016.png|center|thumb|600px|Положения спутника Terra на 15.12.2016 (полученные нами)]]<br />
<br />
<br />
Найдём данные на тот же день у Space Science and Engineering Data Center:<br />
<br />
<br />
[[Файл:Sgp_ssec_terra_151212016.gif|center|thumb|600px|Положения спутника Terra на 15.12.2016 (по данным SSEC)]]<br />
<br />
<br />
Всё прекрасно сходится! Узнаем, где будет Landsat 8 в будущем? Например, 22 декабря 2016.<br />
<br />
<source lang="python"><br />
create_orbital_track_shapefile_for_day(39084, date(2016,12,22), 5, '/home/silent/space/landsat_8_22_12_2016_5min.shp')<br />
</source><br />
<br />
<br />
[[Файл:sgp_landsat8_test_25122016.png|center|thumb|600px|Положения спутника Landsat 8 на 22.12.2016]]<br />
<br />
<br />
Представляя себе полосу съемки, можно оценить охват снятой территории за 1 день.<br />
<br />
В коде показан механизм, который несложно приспособить под собственные задачи. Таким образом можно осуществлять автоматизацию поиска и загрузки архивных данных, прогнозирование пролётов. Важно помнить, что в зависимости от особенностей аппаратуры, установленной на спутнике, соотношение между треком пролёта и снятой территорий будет сильно разниться. К примеру, Sentinel-1 оснащен радиолокатором бокового обзора, его наблюдения не надирные; полосы съемки MODIS (Terra) и ETM+ (Landsat) отличаются на порядки по степени охвата (хотя треки похожи); и так далее. <br />
<br />
== Источники ==<br />
1. [http://celestrak.com/NORAD/documentation/spacetrk.pdf Felix R. Hoots, Ronald L. Roehrich. SPACETRACK REPORT NO. 3 - Models for Propagation of NORAD Element Sets. December 1980]<br />
<br />
2. [http://www.centerforspace.com/downloads/files/pubs/AIAA-2008-6770.pdf David A. Vallado, Paul Crawford. SGP4 Orbit Determination]<br />
<br />
3. [https://www.celestrak.com/NORAD/documentation/tle-fmt.asp NORAD Two-Line Element Set Format]</div>Александр Мурыйhttps://wiki.gis-lab.info/index.php?title=%D0%97%D0%B0%D1%8F%D0%B2%D0%BA%D0%B8_%D0%BD%D0%B0_%D0%BF%D1%83%D0%B1%D0%BB%D0%B8%D0%BA%D0%B0%D1%86%D0%B8%D1%8E_%D1%81%D1%82%D0%B0%D1%82%D0%B5%D0%B9&diff=25043Заявки на публикацию статей2017-01-02T09:22:57Z<p>Александр Мурый: </p>
<hr />
<div>{{Организационное|articles-request}}<br />
<br />
Внизу вы видите таблицу заявок на публикацию статей на GIS-Lab, она показывает статьи, которые их авторы считают более-менее готовыми к публикации. Таблица создана, чтобы сделать процесс принятия статей более прозрачным для авторов и оптимизировать работу редакторов. <br />
<br />
Таблица будет обновляться ведущим по мере поступления заявок от авторов. Заявкой на публикацию считается:<br />
* если статья оформляется на вики GIS-Lab — создание автором темы на форуме для обсуждения статьи (в разделе [http://gis-lab.info/forum/viewforum.php?f=3 Материалы сайта]);<br />
* если статья оформляется НЕ на вики GIS-Lab — личное обращение автора к [http://gis-lab.info/qa/org.html редактору] с предоставлением текста статьи.<br />
<br />
<br />
{| class="wikitable sortable" <br />
|-<br />
! width="5%" | Дата поступления заявки<br />
! width="30%" | Ссылка на вики, название <br />
! width="25%" | Автор (имя / ID)<br />
! width="25%" | Тема обсуждения на форуме (номер / название)<br />
! width="10%" | Дата создания<br />
<br />
|-<br />
| 20.12.2016<br />
| [http://wiki.gis-lab.info/w/%D0%9C%D0%BE%D0%B4%D0%B5%D0%BB%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%86%D0%B8%D0%B9_%D0%BE%D1%80%D0%B1%D0%B8%D1%82_%D0%98%D0%A1%D0%97_%D0%BD%D0%B0_%D0%BF%D0%BE%D0%B2%D0%B5%D1%80%D1%85%D0%BD%D0%BE%D1%81%D1%82%D1%8C_%D0%97%D0%B5%D0%BC%D0%BB%D0%B8_%D0%BD%D0%B0_Python_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%BC%D0%BE%D0%B4%D0%B5%D0%BB%D0%B8_SGP4_%D0%B8_API_space-track.org Моделирование проекций орбит ИСЗ на поверхность Земли на Python с использованием модели SGP4 и API space-track.org]<br />
| Эдуард Казаков / 16669<br />
| 21875 / Статья про автоматизированное моделирование треков спутников<br />
| 21.06.2016<br />
|-<br />
| 02.01.2017<br />
| [http://wiki.gis-lab.info/w/%D0%9E%D0%BF%D1%8B%D1%82_%D0%BA%D0%BB%D0%B0%D1%81%D1%81%D0%B8%D1%84%D0%B8%D0%BA%D0%B0%D1%86%D0%B8%D0%B8_%D0%BA%D0%BE%D1%81%D0%BC%D0%BE%D1%81%D0%BD%D0%B8%D0%BC%D0%BA%D0%B0_Sentinel-_2a_%D1%81_%D0%BF%D0%BE%D0%BC%D0%BE%D1%89%D1%8C%D1%8E_Semi-Automatic_Classification_Plugin_%D0%B2_QGIS Опыт классификации космоснимка Sentinel- 2a с помощью Semi-Automatic Classification Plugin в QGIS]<br />
| Zubr / 16406<br />
| 21907 / Опыт класс-и снимка Sentinel-2a в SACplugin QGis<br />
| 01.01.2017<br />
[[Категория:Служебные]]</div>Александр Мурыйhttps://wiki.gis-lab.info/index.php?title=%D0%97%D0%B0%D1%8F%D0%B2%D0%BA%D0%B8_%D0%BD%D0%B0_%D0%BF%D1%83%D0%B1%D0%BB%D0%B8%D0%BA%D0%B0%D1%86%D0%B8%D1%8E_%D1%81%D1%82%D0%B0%D1%82%D0%B5%D0%B9&diff=24896Заявки на публикацию статей2016-12-28T13:42:16Z<p>Александр Мурый: </p>
<hr />
<div>{{Организационное|articles-request}}<br />
<br />
Внизу вы видите таблицу заявок на публикацию статей на GIS-Lab, она показывает статьи, которые их авторы считают более-менее готовыми к публикации. Таблица создана, чтобы сделать процесс принятия статей более прозрачным для авторов и оптимизировать работу редакторов. <br />
<br />
Таблица будет обновляться ведущим по мере поступления заявок от авторов. Заявкой на публикацию считается:<br />
* если статья оформляется на вики GIS-Lab — создание автором темы на форуме для обсуждения статьи (в разделе [http://gis-lab.info/forum/viewforum.php?f=3 Материалы сайта]);<br />
* если статья оформляется НЕ на вики GIS-Lab — личное обращение автора к [http://gis-lab.info/qa/org.html редактору] с предоставлением текста статьи.<br />
<br />
<br />
{| class="wikitable sortable" <br />
|-<br />
! width="5%" | Дата поступления заявки<br />
! width="30%" | Ссылка на вики, название <br />
! width="25%" | Автор (имя / ID)<br />
! width="25%" | Тема обсуждения на форуме (номер / название)<br />
! width="10%" | Дата создания<br />
<br />
|-<br />
| 20.12.2016<br />
| [http://wiki.gis-lab.info/w/%D0%9C%D0%BE%D0%B4%D0%B5%D0%BB%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%86%D0%B8%D0%B9_%D0%BE%D1%80%D0%B1%D0%B8%D1%82_%D0%98%D0%A1%D0%97_%D0%BD%D0%B0_%D0%BF%D0%BE%D0%B2%D0%B5%D1%80%D1%85%D0%BD%D0%BE%D1%81%D1%82%D1%8C_%D0%97%D0%B5%D0%BC%D0%BB%D0%B8_%D0%BD%D0%B0_Python_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%BC%D0%BE%D0%B4%D0%B5%D0%BB%D0%B8_SGP4_%D0%B8_API_space-track.org Моделирование проекций орбит ИСЗ на поверхность Земли на Python с использованием модели SGP4 и API space-track.org]<br />
| Эдуард Казаков / 16669<br />
| 21875 / Статья про автоматизированное моделирование треков спутников<br />
| 21.06.2016<br />
<br />
<br />
[[Категория:Служебные]]</div>Александр Мурыйhttps://wiki.gis-lab.info/index.php?title=%D0%98%D0%BC%D0%BF%D0%BE%D1%80%D1%82_%D0%B8_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7_%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85_%D0%BB%D0%B8%D0%B4%D0%B0%D1%80%D0%BD%D0%BE%D0%B9_%D1%81%D1%8A%D0%B5%D0%BC%D0%BA%D0%B8_%D0%B2_PostgreSQL/PostGIS_%D1%81_%D0%BF%D0%BE%D0%BC%D0%BE%D1%89%D1%8C%D1%8E_PDAL&diff=24895Импорт и анализ данных лидарной съемки в PostgreSQL/PostGIS с помощью PDAL2016-12-28T13:24:38Z<p>Александр Мурый: </p>
<hr />
<div>{{Статья|Черновик}}<br />
{{Аннотация|Рассмотрен процесс хранения и анализа данных лидарной съемки в PostgreSQL с использованием технологии PointCloud и импорта данных с помощью библиотеки PDAL}}<br />
<br />
== Введение ==<br />
Лидар (LIDAR - Light Identification, Detection and Ranging) – это технология получения и обработки информации дистанционного зондирования с проведением высокоточных измерений X, Y, Z координат. Обработанные и пространственно организованные данные лидарной съемки называют «облаком точек» – это огромные наборы высотных 3D точек, имеющих дополнительную атрибутику.<br /><br />
Данные LIDAR на территории городов или регионов могут содержать в себе миллиарды точек. Хранение их в базе данных в классическом виде как тип геометрии «точка» представляется возможным, но очень ресурсозатратным. Во-первых, огромное количество записей в таблице, что увеличивает время обработки данных. Во-вторых, большой объем памяти, занятый хранилищем.<br /><br />
Для решения этих проблем существует технология хранения данных лидарной съемки в PostgreSQL/PostGIS c использованием встроенного расширения pointcloud и импорта таких данных в базу с помощью библиотеки PDAL (Point Data Abstract Library)<ref name="ttttest">[http://www.pdal.io www.pdal.io], Официальная страница PDAL.</ref>.<br /><br />
В статье использованы материалы лидарной съемки на территорию Нижнего Новгорода, представленные файлами в формате LAS (Logging ASCII Standard) на площади размером 1×1 км.<br /><br />
<br /><br />
[[Файл:Lidar-data-nn-1.png|600px]]<br /><br />
== Создание хранилища ==<br />
Установка PostgreSQL/PostGIS для Windows описана на странице http://gis-lab.info/qa/postgis-install.html#02.<br /><br />
Прежде чем загрузить данные LIDAR в таблицу PostgreSQL, а затем сделать пространственный анализ с помощью PostGIS, необходимо создать новую базу данных с соответствующими расширениями:<br /><br />
# Создать новую базу данных с именем «Lidar».<br />
# Активировать расширения pointcloud, postgis и pointcloud_postgis<ref name="tttest">[http://s3.cleverelephant.ca/foss4gna2013-pointcloud.pdf s3.cleverelephant.ca], LIDAR in PostgreSQL with PointCloud.</ref>.<br />
<syntaxhighlight lang="sql"><br />
CREATE EXTENSION postgis;<br />
CREATE EXTENSION pointcloud;<br />
CREATE EXTENSION pointcloud_postgis;</syntaxhighlight><br />
''Примечание: в ОС Windows расширения pointcloud и pointcloud_postgis устанавливается по умолчанию с PostGIS (http://postgis.net/windows_downloads/). Дополнительные настройки не требуются. При возникновении ошибок возможна отдельная загрузка расширения(https://github.com/pgpointcloud/pointcloud).''<br /><br />
<br /><br />
Объектами в pointcloud для PostGIS являются точки (PcPoint) - базовый объект, и блоки (PcPatch) - объединенные в группы PcPoint. Вместо таблицы миллиардов отдельных записей PcPoint, набор данных LIDAR может быть представлен в базе как гораздо меньшая коллекция (10 миллионов) из PcPatch записей.<br /><br />
Расширение pointcloud_postgis добавляет функции, которые позволяют выполнять обработку облака точек, преобразовывать PcPoint и PcPatch в геометрию и осуществлять пространственную фильтрацию данных. Примеры использования таких функций рассмотрены в главе ''Пространственный анализ''. <br />
== Импорт данных ==<br />
Импорт данных происходит с помощью библиотеки PDAL. PDAL это библиотека для манипулирования данными облака точек. Она очень похожа на библиотеку GDAL, которая обрабатывает растровые и векторные данные. В дополнение к коду библиотеки, PDAL предоставляет набор приложений командной строки, которые пользователи могут удобно использовать для обработки облака точек. <br /><br />
=== Установка PDAL ===<br />
Установить PDAL можно через OSGeo4W<ref name="ttttestt">[https://trac.osgeo.org/osgeo4w/ trac.osgeo.org/osgeo4w], Официальная страница OSGeo4W.</ref>.<br /><br />
Для этого нужно скачать установщик с официального сайта OSGeo4W - https://trac.osgeo.org/osgeo4w/ и запустить его. Выбрать '''расширенную установку''' и следовать указаниям установщика. На вкладке «Выберите пакеты» в строке поиска ввести ''PDAL'' и начать установку.<br /><br />
<br /><br />
[[Файл:Window-install-pdal.png|600px]]<br /><br />
<br /><br />
Для проверки корректной работы PDAL, после установки, можно прочитать метаданные LAS-файла, выполнив команду:<br />
<pre>pdal info --input H:\LIDAR\I_+1_+2_10.las --schema</pre><br />
Результат выполнения команды показан на рисунке. Команда –schema показывает размеры и типы полей: X, Y, Z, Intensity, ReturnNumber, NumberOfReturns, ScanDirectionFlag, EdgeOfFlightLine, Classification, ScanAngleRank, UserData, PointSourceId и Time.<br /><br />
<br /><br />
[[Файл:Metadata-las-2_2.png]]<br /><br />
<br /><br />
=== Импорт данных в PostgreSQL через PDAL ===<br />
Теперь необходимо создать файл загрузки для чтения LAS-файла, разбиения облака точек на блоки (например, по 400 точек), а затем записи блоков в базу данных.<br /><br />
Файлом загрузки PDAL является XML-файл, который описывает обработку данных. Для загрузки нам понадобятся три драйвера PDAL:<br />
*readers.las – для чтения LAS-файла<br />
*filters.chipper - для разбиения облака точек на блоки<br />
*writers.pgpointcloud – для записи LAS-файла в базу данных <br />
Создаем новый текстовый документ с именем «las2pg.xml» и расширением XML следующего вида<ref name="test">[http://workshops.boundlessgeo.com/tutorial-lidar/#loading-lidar-into-the-database workshops.boundlessgeo.com], Analyzing and Visualizing LIDAR.</ref>:<br />
<syntaxhighlight lang="xml"><br />
<?xml version="1.0" encoding="utf-8"?><br />
<Pipeline version="1.0"><br />
<Writer type=" writers.pgpointcloud "><br />
<Option name="connection">dbname=lidar user=postgres</Option><br />
<Option name="table">medford</Option><br />
<Option name="srid">4326</Option><br />
<Filter type=" filters.chipper"><br />
<Option name="capacity">400</Option><br />
<Reader type="readers.las"><br />
<Option name="filename"> H:\LIDAR\I_+1_+2_10.las</Option><br />
<Option name="spatialreference">EPSG:4326</Option><br />
</Reader><br />
</Filter><br />
</Writer><br />
</Pipeline></syntaxhighlight><br />
''Примечание: названия драйверов могут отличаться. Для своей версии PDAL узнать доступные драйвера можно с помощью команды pdal --drivers.''<br /><br />
<br /><br />
Теперь мы можем загрузить LAS-файл в базу данных. Для этого необходимо выполнить команду:<br />
<pre>pdal pipeline H:\LIDAR\las2pg.xml</pre><br />
После завершения процесса загрузки в базе данных «LIDAR» появится новая таблица «Medford» следующей структуры:<br />
<pre> Table "public.medford"<br />
Column | Type | Modifiers<br />
--------+------------+------------------------------------------------------<br />
id | integer | default nextval('medford_id_seq'::regclass)<br />
pa | pcpatch |<br />
Indexes:<br />
"medford_pkey" PRIMARY KEY(id)</pre><br />
Хоть таблица и состоит из двух полей ID и PA, но в поле PA закодирована информация о точках лидара: X, Y, Z, Intensity, ReturnNumber, NumberOfReturns, Classification, ScanAngleRank, Red, Green, Blue. В одной записи хранится пространственная и атрибутивная информация четырехсот точек. <br /><br />
Для визуализации результата импорта можно воспользоваться любой ГИС с возможностью подключения к базе данных. Мы используем QGIS<ref name="teeest">[http://www.qgis.org/ru/site/ www.qgis.org], Официальная страница QGIS.</ref>, где создано соединение с базой данных «Lidar».<br /><br />
<br /><br />
[[Файл:Look-patch-lidar-qgis-4.png|600px]]<br /><br />
<br /><br />
Результатом такой технологии импорта данных LIDAR является существенное уменьшение размерности, по сравнению с обычным импортом графических слоев. Характеристики сравнения представлены в таблице.<br /><br />
{| class="wikitable"<br />
|-<br />
! Показатель !! Импорт в PostgreSQL/PostGIS через PDAL !! Импорт в PostgreSQL/PostGIS LAS-файла<br />
|-<br />
| Количество записей || 1 864 || 931 919<br />
|-<br />
| Размер таблицы, MB || 9,9 || 197<br />
|}<br />
Таким образом был импортирован один LAS-файл. Если таких файлов множество, есть два пути решения. Первый – объединить все LAS-файлы в один, например, с помощью набора программного обеспечения LAStools (LAStools(с) by Martin Isenburg), в частности, программы las2las. Второй – написать собственный скрипт, который генерирует файлы загрузки и выполняет команду PDAL.<br />
== Пространственный анализ ==<br />
Формат вывода получившейся таблицы в PostgreSQL труден для восприятия, поэтому для пространственного анализа можно отформатировать таблицу с данными LIDAR.<br /><br />
Для обработки и анализа импортированных данных мы можем использовать встроенные функции в расширении pointcloud<ref name="test" />. Далее приведены примеры использования таких функций.<br />
<syntaxhighlight lang="sql"><br />
-- Сколько точек в облаке (931919)<br />
SELECT Sum(PC_NumPoints(pa))<br />
FROM medford;<br />
<br />
-- Какова средняя высота первого блока? (151.95)<br />
WITH pts AS (<br />
SELECT PC_Explode(pa) AS pt<br />
FROM medford LIMIT 1<br />
)<br />
SELECT Avg(PC_Get(pt,'Z')) FROM pts;<br />
<br />
-- Какой вид имеет первая точка?<br />
WITH pts AS (<br />
SELECT PC_Explode(pa) AS pt<br />
FROM medford LIMIT 1<br />
)<br />
SELECT PC_AsText(pt) FROM pts LIMIT 1;<br />
-- {<br />
-- "pcid":1,<br />
-- "pt":[255,1,1,0,0,2,0,0,10,556976,2250.04,1253.42,151.95]<br />
-- }<br />
<br />
-- Сколько блоков? (1864)<br />
SELECT Count(*)<br />
FROM medford;<br />
<br />
-- Какая минимальная и максимальная высота в блоке? (141.78/ 204.33)<br />
SELECT<br />
Min(PC_PatchMin(pa, 'z')) AS min,<br />
Max(PC_PatchMax(pa, 'z')) AS max<br />
FROM medford;<br />
-- Как выглядит геометрия блока?<br />
SELECT st_asewkt(pa::geometry) FROM medford LIMIT 1;<br />
-- SRID=4326;POLYGON((2250.04 1250.2,2250.04 1253.82,2258.88 1253.82,<br />
-- 2258.88 1250.2,2250.04 1250.2))<br />
<br />
-- Как выглядит геометрия точки?<br />
WITH pts AS (<br />
SELECT PC_Explode(pa) AS pt<br />
FROM medford LIMIT 1<br />
)<br />
SELECT ST_AsEWKT(pt::geometry) FROM pts LIMIT 1;<br />
-- SRID=4326;POINT(2250.04 1253.42 151.95)<br />
<br />
-- Выбрать атрибуты точек: тип классификации, координаты Z,X,Y<br />
WITH pts AS (<br />
SELECT PC_Explode(pa) AS pt<br />
FROM medford <br />
)<br />
SELECT PC_Get(pt,'Classification'),PC_Get(pt,'Z'), PC_Get(pt,'X'), PC_Get(pt,'Y') FROM pts LIMIT 1;<br />
-- 2<br />
-- 151.95<br />
-- 2250.04<br />
-- 1253.42</syntaxhighlight><br />
''Примечание: более подробную информацию о расширении pointcloud и функциях SQL смотри в документации (https://github.com/pgpointcloud/pointcloud, http://suite.opengeo.org/docs/latest/dataadmin/pointcloud/postgis.html).''<br />
<br /><br />
<br /><br />
== Ссылки ==<br />
<references /></div>Александр Мурыйhttps://wiki.gis-lab.info/index.php?title=%D0%98%D0%BC%D0%BF%D0%BE%D1%80%D1%82_%D0%B8_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7_%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85_%D0%BB%D0%B8%D0%B4%D0%B0%D1%80%D0%BD%D0%BE%D0%B9_%D1%81%D1%8A%D0%B5%D0%BC%D0%BA%D0%B8_%D0%B2_PostgreSQL/PostGIS_%D1%81_%D0%BF%D0%BE%D0%BC%D0%BE%D1%89%D1%8C%D1%8E_PDAL&diff=24894Импорт и анализ данных лидарной съемки в PostgreSQL/PostGIS с помощью PDAL2016-12-28T13:12:52Z<p>Александр Мурый: </p>
<hr />
<div>{{Статья|Черновик}}<br />
{{Аннотация|Рассмотрен процесс хранения и анализа данных лидарной съемки в PostgreSQL с использованием технологии PointCloud и импорта данных с помощью библиотеки PDAL}}<br />
<br />
== Введение ==<br />
Лидар (LIDAR - Light Identification, Detection and Ranging) – это технология получения и обработки информации дистанционного зондирования с проведением высокоточных измерений X, Y, Z координат. Обработанные и пространственно организованные данные лидарной съемки называют облаком точек – это огромные наборы высотных 3D точек, имеющих дополнительную атрибутику.<br /><br />
Данные LIDAR на территории городов или регионов могут содержать в себе миллиарды точек местности. Хранение их в базе данных в классическом виде как тип геометрии «точка» представляется возможным, но очень ресурсозатратным. Во-первых, огромное количество записей в таблице, что увеличивает время обработки данных. Во-вторых, большой объем памяти, занятый хранилищем.<br /><br />
Для решения этих проблем существует технология хранения данных лидарной съемки в PostgreSQL/PostGIS c использованием встроенного расширения pointcloud и импорта таких данных в базу с помощью библиотеки PDAL (Point Data Abstract Library)<ref name="ttttest">[http://www.pdal.io www.pdal.io], Официальная страница PDAL.</ref>.<br /><br />
В статье использованы материалы лидарной съемки на территорию Нижнего Новгорода, представленные файлами в формате LAS (Logging ASCII Standard) на площади размером 1×1 км.<br /><br />
<br /><br />
[[Файл:Lidar-data-nn-1.png]]<br /><br />
== Создание хранилища ==<br />
Установка PostgreSQL/PostGIS для Windows описана на странице http://gis-lab.info/qa/postgis-install.html#02.<br /><br />
Прежде чем загрузить данные LIDAR в таблицу PostgreSQL, а затем сделать пространственный анализ с помощью PostGIS, необходимо создать новую базу данных с соответствующими расширениями:<br /><br />
# Создать новую базу данных с именем «Lidar».<br />
# Активировать расширения pointcloud, postgis и pointcloud_postgis<ref name="tttest">[http://s3.cleverelephant.ca/foss4gna2013-pointcloud.pdf s3.cleverelephant.ca], LIDAR in PostgreSQL with PointCloud.</ref>.<br />
<syntaxhighlight lang="sql"><br />
CREATE EXTENSION postgis;<br />
CREATE EXTENSION pointcloud;<br />
CREATE EXTENSION pointcloud_postgis;</syntaxhighlight><br />
''Примечание: расширения pointcloud и pointcloud_postgis устанавливается по умолчанию с PostGIS (http://postgis.net/windows_downloads/). Дополнительные настройки не требуются. При возникновении ошибок возможна отдельная загрузка расширения(https://github.com/pgpointcloud/pointcloud).''<br /><br />
<br /><br />
Объектами в pointcloud для PostGIS являются точки (PcPoint) - базовый объект, и блоки (PcPatch) - объединенные в группы PcPoint. Вместо таблицы миллиардов отдельных записей PcPoint, набор данных LIDAR может быть представлен в базе как гораздо меньшая коллекция (10 миллионов) из PcPatch записей.<br /><br />
Расширение pointcloud_postgis добавляет функции, которые позволяют выполнять обработку облака точек, преобразовывать PcPoint и PcPatch в геометрию и осуществлять пространственную фильтрацию данных. Примеры использования таких функций рассмотрены в главе ''Пространственный анализ''. <br />
== Импорт данных ==<br />
Импорт данных происходит с помощью библиотеки PDAL. PDAL это библиотека для манипулирования данными облака точек. Она очень похожа на библиотеку GDAL, которая обрабатывает растровые и векторные данные. В дополнение к коду библиотеки, PDAL предоставляет набор приложений командной строки, которые пользователи могут удобно использовать для обработки облака точек. <br /><br />
=== Установка PDAL ===<br />
Установить PDAL можно через OSGeo4W<ref name="ttttestt">[https://trac.osgeo.org/osgeo4w/ trac.osgeo.org/osgeo4w], Официальная страница OSGeo4W.</ref>.<br /><br />
Для этого нужно скачать установщик с официального сайта OSGeo4W - https://trac.osgeo.org/osgeo4w/ и запустить его. Выбрать '''расширенную установку''' и следовать указаниям установщика. На вкладке «Выберите пакеты» в строке поиска ввести ''PDAL'' и начать установку.<br /><br />
<br /><br />
[[Файл:Window-install-pdal.png|600px]]<br /><br />
<br /><br />
Для проверки корректной работы PDAL, после установки, можно прочитать метаданные LAS-файла, выполнив команду:<br />
<pre>pdal info --input H:\LIDAR\I_+1_+2_10.las --schema</pre><br />
Результат выполнения команды показан на рисунке. Команда –schema показывает размеры и типы полей: X, Y, Z, Intensity, ReturnNumber, NumberOfReturns, ScanDirectionFlag, EdgeOfFlightLine, Classification, ScanAngleRank, UserData, PointSourceId и Time.<br /><br />
<br /><br />
[[Файл:Metadata-las-2_2.png]]<br /><br />
<br /><br />
=== Импорт данных в PostgreSQL через PDAL ===<br />
Теперь необходимо создать файл загрузки для чтения LAS-файла, разбиения облака точек на блоки (например, по 400 точек), а затем записи блоков в базу данных.<br /><br />
Файлом загрузки PDAL является XML-файл, который описывает обработку данных. Для загрузки нам понадобятся три драйвера PDAL:<br />
*readers.las – для чтения LAS-файла<br />
*filters.chipper - для разбиения облака точек на блоки<br />
*writers.pgpointcloud – для записи LAS-файла в базу данных <br />
Создаем новый текстовый документ с именем «las2pg.xml» и расширением XML следующего вида<ref name="test">[http://workshops.boundlessgeo.com/tutorial-lidar/#loading-lidar-into-the-database workshops.boundlessgeo.com], Analyzing and Visualizing LIDAR.</ref>:<br />
<syntaxhighlight lang="xml"><br />
<?xml version="1.0" encoding="utf-8"?><br />
<Pipeline version="1.0"><br />
<Writer type=" writers.pgpointcloud "><br />
<Option name="connection">dbname=lidar user=postgres</Option><br />
<Option name="table">medford</Option><br />
<Option name="srid">4326</Option><br />
<Filter type=" filters.chipper"><br />
<Option name="capacity">400</Option><br />
<Reader type="readers.las"><br />
<Option name="filename"> H:\LIDAR\I_+1_+2_10.las</Option><br />
<Option name="spatialreference">EPSG:4326</Option><br />
</Reader><br />
</Filter><br />
</Writer><br />
</Pipeline></syntaxhighlight><br />
''Примечание: названия драйверов могут отличаться. Для своей версии PDAL узнать доступные драйвера можно с помощью команды pdal --drivers.''<br /><br />
<br /><br />
Теперь мы можем загрузить LAS-файл в базу данных. Для этого необходимо выполнить команду:<br />
<pre>pdal pipeline H:\LIDAR\las2pg.xml</pre><br />
После завершения процесса загрузки в базе данных «LIDAR» появится новая таблица «Medford» следующей структуры:<br />
<pre> Table "public.medford"<br />
Column | Type | Modifiers<br />
--------+------------+------------------------------------------------------<br />
id | integer | default nextval('medford_id_seq'::regclass)<br />
pa | pcpatch |<br />
Indexes:<br />
"medford_pkey" PRIMARY KEY(id)</pre><br />
Хоть таблица и состоит из двух полей ID и PA, но в поле PA закодирована информация о точках лидара: X, Y, Z, Intensity, ReturnNumber, NumberOfReturns, Classification, ScanAngleRank, Red, Green, Blue. В одной записи хранится пространственная и атрибутивная информация четырехсот точек. <br /><br />
Для визуализации результата импорта можно воспользоваться любой ГИС с возможностью подключения к базе данных. Мы используем QGIS<ref name="teeest">[http://www.qgis.org/ru/site/ www.qgis.org], Официальная страница QGIS.</ref>, где создано соединение с базой данных «Lidar».<br /><br />
<br /><br />
[[Файл:Look-patch-lidar-qgis-4.png|600px]]<br /><br />
<br /><br />
Результатом такой технологии импорта данных LIDAR является существенное уменьшение размерности, по сравнению с обычным импортом графических слоев. Характеристики сравнения представлены в таблице.<br /><br />
{| class="wikitable"<br />
|-<br />
! Показатель !! Импорт в PostgreSQL/PostGIS через PDAL !! Импорт в PostgreSQL/PostGIS LAS-файла<br />
|-<br />
| Количество записей || 1 864 || 931 919<br />
|-<br />
| Размер таблицы, MB || 9,9 || 197<br />
|}<br />
Таким образом импортирован один LAS-файл. Если таких файлов множество, есть два пути решения. Первый – объединить все LAS-файлы в один, например, с помощью набора программного обеспечения LAStools (LAStools(с) by Martin Isenburg), в частности программы las2las. Второй – написать собственный скрипт, который генерирует файлы загрузки и выполняет команду PDAL.<br />
== Пространственный анализ ==<br />
Формат вывода получившейся таблицы в PostgreSQL труден для восприятия, поэтому для пространственного анализа можно отформатировать таблицу с данными LIDAR.<br /><br />
Для обработки и анализа импортированных данных мы можем использовать встроенные функции в расширении pointcloud<ref name="test" />. Далее приведены примеры использования таких функций.<br />
<syntaxhighlight lang="sql"><br />
-- Сколько точек в облаке (931919)<br />
SELECT Sum(PC_NumPoints(pa))<br />
FROM medford;<br />
<br />
-- Какова средняя высота первого блока? (151.95)<br />
WITH pts AS (<br />
SELECT PC_Explode(pa) AS pt<br />
FROM medford LIMIT 1<br />
)<br />
SELECT Avg(PC_Get(pt,'Z')) FROM pts;<br />
<br />
-- Какой вид имеет первая точка?<br />
WITH pts AS (<br />
SELECT PC_Explode(pa) AS pt<br />
FROM medford LIMIT 1<br />
)<br />
SELECT PC_AsText(pt) FROM pts LIMIT 1;<br />
-- {<br />
-- "pcid":1,<br />
-- "pt":[255,1,1,0,0,2,0,0,10,556976,2250.04,1253.42,151.95]<br />
-- }<br />
<br />
-- Сколько блоков? (1864)<br />
SELECT Count(*)<br />
FROM medford;<br />
<br />
-- Какая минимальная и максимальная высота в блоке? (141.78/ 204.33)<br />
SELECT<br />
Min(PC_PatchMin(pa, 'z')) AS min,<br />
Max(PC_PatchMax(pa, 'z')) AS max<br />
FROM medford;<br />
-- Как выглядит геометрия блока?<br />
SELECT st_asewkt(pa::geometry) FROM medford LIMIT 1;<br />
-- SRID=4326;POLYGON((2250.04 1250.2,2250.04 1253.82,2258.88 1253.82,<br />
-- 2258.88 1250.2,2250.04 1250.2))<br />
<br />
-- Как выглядит геометрия точки?<br />
WITH pts AS (<br />
SELECT PC_Explode(pa) AS pt<br />
FROM medford LIMIT 1<br />
)<br />
SELECT ST_AsEWKT(pt::geometry) FROM pts LIMIT 1;<br />
-- SRID=4326;POINT(2250.04 1253.42 151.95)<br />
<br />
-- Выбрать атрибуты точек: тип классификации, координаты Z,X,Y<br />
WITH pts AS (<br />
SELECT PC_Explode(pa) AS pt<br />
FROM medford <br />
)<br />
SELECT PC_Get(pt,'Classification'),PC_Get(pt,'Z'), PC_Get(pt,'X'), PC_Get(pt,'Y') FROM pts LIMIT 1;<br />
-- 2<br />
-- 151.95<br />
-- 2250.04<br />
-- 1253.42</syntaxhighlight><br />
''Примечание: более подробную информацию о расширении pointcloud и функциях SQL смотри в документации (https://github.com/pgpointcloud/pointcloud, http://suite.opengeo.org/docs/latest/dataadmin/pointcloud/postgis.html).''<br />
<br /><br />
<br /><br />
== Ссылки ==<br />
<references /></div>Александр Мурыйhttps://wiki.gis-lab.info/index.php?title=%D0%97%D0%B0%D1%8F%D0%B2%D0%BA%D0%B8_%D0%BD%D0%B0_%D0%BF%D1%83%D0%B1%D0%BB%D0%B8%D0%BA%D0%B0%D1%86%D0%B8%D1%8E_%D1%81%D1%82%D0%B0%D1%82%D0%B5%D0%B9&diff=24870Заявки на публикацию статей2016-12-21T09:30:54Z<p>Александр Мурый: </p>
<hr />
<div>{{Организационное|articles-request}}<br />
<br />
Внизу вы видите таблицу заявок на публикацию статей на GIS-Lab, она показывает статьи, которые их авторы считают более-менее готовыми к публикации. Таблица создана, чтобы сделать процесс принятия статей более прозрачным для авторов и оптимизировать работу редакторов. <br />
<br />
Таблица будет обновляться ведущим по мере поступления заявок от авторов. Заявкой на публикацию считается:<br />
* если статья оформляется на вики GIS-Lab — создание автором темы на форуме для обсуждения статьи (в разделе [http://gis-lab.info/forum/viewforum.php?f=3 Материалы сайта]);<br />
* если статья оформляется НЕ на вики GIS-Lab — личное обращение автора к [http://gis-lab.info/qa/org.html редактору] с предоставлением текста статьи.<br />
<br />
<br />
{| class="wikitable sortable" <br />
|-<br />
! width="5%" | Дата поступления заявки<br />
! width="30%" | Ссылка на вики, название <br />
! width="25%" | Автор (имя / ID)<br />
! width="25%" | Тема обсуждения на форуме (номер / название)<br />
! width="10%" | Дата создания<br />
|-<br />
| 08.11.2016<br />
| [http://wiki.gis-lab.info/w/%D0%98%D0%BC%D0%BF%D0%BE%D1%80%D1%82_%D0%B8_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7_%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85_%D0%BB%D0%B8%D0%B4%D0%B0%D1%80%D0%BD%D0%BE%D0%B9_%D1%81%D1%8A%D0%B5%D0%BC%D0%BA%D0%B8_%D0%B2_PostgreSQL/PostGIS_%D1%81_%D0%BF%D0%BE%D0%BC%D0%BE%D1%89%D1%8C%D1%8E_PDAL Импорт и анализ данных лидарной съемки в PostgreSQL/PostGIS с помощью PDAL]<br />
| Анна Горева / 19401<br />
| 21684 / Импорт данных LIDAR в PostGIS с помощью PDAL <br />
| 07.11.2016<br />
|-<br />
| 20.12.2016<br />
| [http://wiki.gis-lab.info/w/%D0%9C%D0%BE%D0%B4%D0%B5%D0%BB%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%86%D0%B8%D0%B9_%D0%BE%D1%80%D0%B1%D0%B8%D1%82_%D0%98%D0%A1%D0%97_%D0%BD%D0%B0_%D0%BF%D0%BE%D0%B2%D0%B5%D1%80%D1%85%D0%BD%D0%BE%D1%81%D1%82%D1%8C_%D0%97%D0%B5%D0%BC%D0%BB%D0%B8_%D0%BD%D0%B0_Python_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%BC%D0%BE%D0%B4%D0%B5%D0%BB%D0%B8_SGP4_%D0%B8_API_space-track.org Моделирование проекций орбит ИСЗ на поверхность Земли на Python с использованием модели SGP4 и API space-track.org]<br />
| Эдуард Казаков / 16669<br />
| 21875 / Статья про автоматизированное моделирование треков спутников<br />
| 21.06.2016<br />
<br />
<br />
[[Категория:Служебные]]</div>Александр Мурый