Быстрый старт
================

Данное руководство описывает действия, необходимые для открытия источника с данными и произведения с ними базовых манипуляций.
Примеры, приведенные в данном руководстве можно выполнить в редакторе скриптов питона, который является частью приложения Axioma.GIS.

**1. Чтение данных**
-------------------------

**1.1. Открытие источника**
++++++++++++++++++++++++++++++

1.1.1.  Файловый источник данных 
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Для открытия таблицы с данными и последующей регистрации ее в каталоге с данными необходимо выполнить следующее:

.. code-block:: python

	import axioma.core
	json = {"src":"C:\\SubjectRF.TAB"}
	table = axioma.core.open_json(json)
	axioma.app.mainWindow.registerDataObject(table)
	
1.1.2.  СУБД
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^


При открытии таблицы из базы данных задание источника данных может выглядеть так:

.. code-block:: python

	json = {
		"openWith": "PgDataProvider",
		"src": "localhost",
		"port": "5432",
		"db": "esti",
		"user": "postgres",
		"password": "",
		"sql": "select * from subjectrf",
	}
	table = axioma.core.open_json(json)

**1.2. Запрос записей**
++++++++++++++++++++++++++

К полученному открытому источнику данных table для получения информации можно сделать запрос данных.

Для запроса всех записей в таблице используем метод :meth:`~axioma.core.dp.Table.allFeatures`:

.. code-block:: python

	from axioma.core.dp import *
	features = table.allFeatures()

Если необходимо ограничить количество полей указанным списком, то с методе :meth:`~axioma.core.dp.Table.selectToFeatureList` задаем перечень полей:
	
.. code-block:: python

	features = table.selectToFeatureList(QuerySelectAll(["object", "OBL_NAME"]))

Получение перечня атрибутов таблицы :meth:`~axioma.core.dp.TableSchema.attributeNames`:

.. code-block:: python

	print(table.tableSchema().attributeNames())

Если необходимо ограничить количество запрашиваемых записей, используем :class:`~axioma.core.dp.PageRange`:

.. code-block:: python

	features = table.selectToFeatureList(QuerySelectAll(table.tableSchema().attributeNames()), PageRange(0, 10))
	
Это может быть полезно, если есть необходимость чтения данных порциями.
	
Если необходимо выполнить запрос с наложением фильтра в ограничивающем прямоугольнике, :class:`~axioma.core.dp.GeoRect` используем метод :meth:`~axioma.core.dp.Table.selectFeaturesInMbr`:
	
.. code-block:: python

	geoRect  = GeoRect(QRectF(33, 57, 10, 5), CoordSysFactory.defaultCoordSysFactory().LatLongCoordSystem())
	features = table.selectFeaturesInMbr(geoRect)
	
**1.3. Получение метаданных**
++++++++++++++++++++++++++++++++++

Для определения положения атрибута с геометрией и ее оформления в структуре таблицы используем методы :meth:`~axioma.core.dp.TableSchema.geometryIndex` и :meth:`~axioma.core.dp.TableSchema.styleIndex`:

.. code-block:: python

	geomIndex = table.tableSchema().geometryIndex()
	styleIndex = table.tableSchema().styleIndex()
	
Координатную систему таблицы получаем с помощью метода и :meth:`~axioma.core.dp.Table.coordSystem`:

.. code-block:: python

	from axioma.cs import *
	cs = table.coordSystem()
	print("КС таблицы:", cs.prjStr())
	
Так же метаданные по геометрическому атрибуту можно получить из схемы таблицы  :class:`~axioma.core.dp.GeometryAttributeDefinitionInterface`

.. code-block:: python

	geomDef = table.tableSchema().at(geomIndex)
	cs = geomDef.coordSystem()
	rect = geomDef.boundingRectF()

Для доступа к каждому элементу после получения списка записей можно использовать цикл:
	
.. code-block:: python

	for feature in features:
		print("id={} name={}".format(feature.id(), feature.getAttribute('OBL_NAME')))
		
**1.4. Чтение атрибутов**
++++++++++++++++++++++++++++

Получить атрибут записи можно либо по имени либо по его индексу :meth:`~axioma.core.dp.Feature.getAttribute`. Геометрия и ее стиль определяется аналогично:

.. code-block:: python

	simpleAttr = feature.getAttribute('OBL_NAME')
	simpleAttr = feature.getAttribute(2)
	geomAttr = feature.getAttribute(geomIndex)
	geomAttr = feature.getAttribute("object")
	styleAttr = feature.getAttribute(styleIndex)
	
Полученные стиль и геометрию для наглядности можно представить в текстовом формате:

.. code-block:: python

	from axioma.mapinfo import MapBasicStyle
	print("Геометрия в формате WKT", geomAttr.exportToWkt())
	print ("Стиль в формате строки MapBasic", MapBasicStyle().stringFromStyle(styleAttr))


**2. Изменение данных**
-------------------------

Перед проведением модификаций необходимо убедиться, что таблица поддерживает возможность изменения:

.. code-block:: python

	if isinstance(table, TransactionalTable):
		print('Таблица доступна для изменений')

Проведем операцию вставки новой записи. Для этого вначале создадим новую запись  :class:`~axioma.core.dp.Feature`, используя структуру (схему) открытой для изменения таблицы:

.. code-block:: python

	new_feature = Feature.createFeature(table.tableSchema())

Создадим геометрический объект-полигон и пометим его как измененный:

.. code-block:: python

	from PyQt5.QtGui import QPolygonF
	from axioma.core.geometry import *

	poly = QPolygonF();
	poly << QPointF(50, 50) << QPointF(80, 50) << QPointF(80, 40) << QPointF(50, 40) << QPointF(50, 50)
	polygon = Polygon(table.coordSystem(), poly)
	new_feature.setAttribute(geomIndex, polygon)
	new_feature.setModified(geomIndex, True)

Аналогично для стиля оформления:

.. code-block:: python

	style = MapBasicStyle().styleFromString('Pen (1,2,0) Brush (2, 65280, 16777215)')
	new_feature.setAttribute(styleIndex, style)
	new_feature.setModified(styleIndex, True)

И простой текстовый атрибут:

.. code-block:: python

	new_feature.setAttribute('OBL_NAME', 'Новый объект')
	new_feature.setModified('OBL_NAME', True)

Далее сделаем вставку в транзакционную таблицу. При этом создается файл транзакций и основной файл остается неизменным.

.. code-block:: python

	table.insert([new_feature])

После проведения всех необходимых изменений сохраняем их в файле.

.. code-block:: python
	
	table.commit()

**3. Представление данных**
-----------------------------


**3.1. Вывод в растровый файл**
+++++++++++++++++++++++++++++++

3.1.1. Простой вывод
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Отрисуем данные таблицы в растровый файл.

Для этого для начала создадим слой  :class:`~axioma.render.Layer` для отрисовки на базе таблицы :meth:`~axioma.render.Render.createLayerForDataObject`:

.. code-block:: python

	from axioma.render import *
	layer = axioma.app.render.createLayerForDataObject(table)

Задаем прямоугольник на карте для отрисовки:

.. code-block:: python

	br = layer.boundingRect()
	sceneRect = QRectF(br.left(), br.top(), br.width(), br.height())

И прямоугольник результирующего растра:

.. code-block:: python

	size = QSize(1000, 1000 * br.height() / br.width())
	imRect = QRect(0, 0, size.width(), size.height())

На базе этой информации создаем :class:`~axioma.render.MapViewport`:

.. code-block:: python

	viewport = MapViewport(QRectF(imRect), sceneRect, layer.coordSystem())

Создаем результирующий растр:

.. code-block:: python

	image = QImage(imRect.size(), QImage.Format_ARGB32_Premultiplied)
	image.fill(Qt.white)
	painter = QPainter(image)

Контекст, куда рисуем :class:`~axioma.render.MapContext`:

.. code-block:: python

	context = MapContext(painter, viewport)

Непосредственно отрисовка слоя в контексте:

.. code-block:: python

	layer.render(context)

Сохранение результатов в файловой системе:

.. code-block:: python

	image.save('/tmp/out_raster.png')
	
3.1.2. Многопоточный вывод
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
	
Был представлен простейший вариант. Слои в данном случае отрисовываются последовательно друг за другом. Если мы хотим использовать параллельную отрисовку слоев, используя мультипроцессорную архитектуру, необходимо сделать следующее:

Создать объект-карту :class:`~axioma.render.Map` и заполнить его слоями:

.. code-block:: python

	map = Map()
	map.rootLayerGroup().append(layer)
	map.rootLayerGroup().append(anotherLayer)

Далее, создаем объект, занимающийся многопоточной отрисовкой :class:`~axioma.render.ConcurrentMapRenderer` и производим многопоточную отрисовку:

.. code-block:: python

	renderer = ConcurrentMapRenderer()
	renderer.blockingRender(map, context)
	
3.1.3. Вывод в координатную систему, которая отличается от табличной
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Если мы хотим произвести вывод в координатной системе, отличной от КС таблицы, то изменения в коде будут следующие:

Создаем требуемую координатную систему посредством фабрики :class:`~axioma.cs.CoordSysFactory`:

.. code-block:: python

	from axioma.cs import *
	cs_merc = CoordSysFactory.defaultCoordSysFactory().createFromPrj("Earth Projection 10, 104, \"m\", 0")

Если это необходимо, производим преобразование ограничивающего прямоугольника таблицы в данную координатную систему:
	
.. code-block:: python

	transform = CoordTransform(layer.coordSystem(), cs_merc)
	sceneRectMerc = transform.forward(sceneRect)
	
Или же задаем требуемый нам явно.

И наш :class:`~axioma.render.MapViewport` будет выглядеть так:
	
.. code-block:: python

	viewport = MapViewport(QRectF(imRect), sceneRectMerc, cs_merc)

	
**3.2. Создание и печать отчета**
++++++++++++++++++++++++++++++++++++++

Создадим простой отчет размером 2 на 2 листа формата A4 с одним геометрическим элементом. Для начала нужно создать объект принтера. Печать будем производить в pdf файл.

.. code-block:: python

	from PyQt5.QtPrintSupport import QPrinter
	printer = QPrinter(QPrinter.HighResolution)
	printer.setPaperSize(QPrinter.A4)
	printer.setOutputFormat(QPrinter.PdfFormat)
	printer.setOutputFileName('/tmp/out_report.pdf')

Далее сам отчет :class:`~axioma.render.Report`:

.. code-block:: python

	from axioma.render import *
	report = axioma.render.Report()
	report.setHorisontalPagesCount(2)
	report.setVerticalPageCount(2)
	report.setOptions(printer)

Создаем контекст для вывода:

.. code-block:: python

	deviceRect = sceneRect = QRectF(0, 0, 100, 100)
	viewport = Viewport(deviceRect, sceneRect)
	context = Context(painter, viewport)

Создаем элемент отчета типа геометрия и добавляем его в отчет:
	
.. code-block:: python

	cs_ne = CoordSysFactory.defaultCoordSysFactory().createFromPrj("CoordSys Nonearth Units \"m\"" )
	poly = QPolygonF()
	poly << QPointF(20, 20) << QPointF(80, 50) << QPointF(120, 20) << QPointF(320, 320) << QPointF(120, 180) << QPointF(20,  20)
	geom = Polygon(cs_ne, poly)
	style = MapBasicStyle().styleFromString("Pen (1, 5, 16711935) Brush (8, 255, 16777215)")
	geomItem = GeometryReportItem(report)
	geomItem.setGeometry(geom)
	geomItem.setStyle(style)
	report.addItem(geomItem)

Отрисовка:

.. code-block:: python

	report.render(context)
	painter = None

	
**4. Интерфейс**
-----------------------------

**4.2. Добавление карты и окна просмотре информации в главном окне приложения**
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Откроем таблицу и добавим ее в окно карты и покажем в виде таблицы просмотра

  .. code-block:: python
  
		import axioma.app
		from PyQt5.QtCore import QRectF, QPointF

		# Открываем таблицу
		table = axioma.core.open_json({"src" : "/tmp/world.tab"})
		# Если нужно, чтобы пользователь видел таблицу в списке открытых данных,
		# добавляем её в каталог приложения
		axioma.app.mainWindow.dataCatalog().addDataObject(table) 
		# Создаём слой для таблицы
		layer = axioma.app.render.createLayerForDataObject(table)
		# Создаём окно карты и показываем его в главном окне
		mapView = axioma.app.mainWindow.createMapViewForLayerList([layer], 'Моя карта')
		# Показываем таблицу в виде списка
		axioma.app.mainWindow.showTableData(table)

**4.2. Добавление кнопки в панель инструментов**
++++++++++++++++++++++++++++++++++++++++++++++++++

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


.. code-block:: python

	import axioma.gui
	import PyQt5.QtGui

	# Объявляем свой класс расширения
	class ExampleActionExtension(axioma.gui.NativeActionExtension):

	# Необходимо переопределить метод customizeAction, задав параметры для создаваемого объекта QAction
	    def customizeAction(self, action):
	        action.setText("Пример действия")
	        action.setIcon(PyQt5.QtGui.QIcon(":/icons/share/32px/run.png"))
	        action.triggered.connect(self.slot);

	# Действие по нажатию
	    def slot(self):
	        print('Нажали кнопку')

	# Регистрация в системе на новой закладке
	ribbonExt = axioma.gui.extension.RibbonExtension()
	ribbonExt.addTab("ExamplePluginTab", "Пример модуля")
	actionExt = ExampleActionExtension("ActionExtensionId", axioma.gui.RibbonActionInfo()) 
	ribbonExt.addAction("ActionExtensionId", "ExamplePluginTab", "") # Добавление в интерфейс
	axioma.app.gui.prependExtensions([ribbonExt, actionExt]) # Регистрация



**4.3. Добавление кнопки-инструмента для работы с окном карты**
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Добавим кнопку - инструмент окна карты в панель инструментов. Если выбрать данный инструмент и щелкнуть мышью в окне карты, будут выведены координаты этого места в координатах текущей проекции карты.


.. code-block:: python

	from PyQt5.QtGui import QMouseEvent, QIcon
	from PyQt5.QtCore import Qt
	from axioma.gui import *

	# Определяем свой класс инструмента
	class ExampleTool(Tool):

	    def mousePressEvent(self, event):
	        if event.button() != Qt.LeftButton:
	            return
	        posOnMap = self.widget().viewport().mapToScene(event.pos()) # Преобразуем координаты из окна в координаты карты
	        print("Координаты точки карты ({}, {})".format(posOnMap.x(), posOnMap.y())) # выводим полученную координату
	        return Tool.PassEvent

	# Регистрируем наш инструемнт в виде расширения в системе.
	ext = axioma.gui.extension.BasicToolExtension(ExampleTool, axioma.gui.MapView, "ExampleToolId", icon=QIcon.fromTheme("info"), text="Координаты точки карты")
	axioma.app.gui.prependExtension(ext)

	# Добавляем инструмент в закладку карты
	ribbonExt = axioma.gui.extension.RibbonExtension()
	ribbonExt.addAction("ExampleToolId", "map", "operations")
	axioma.app.gui.prependExtension(ribbonExt)
