Рендерінг карти та друк

Загалом існує два способи отримати друковану карту: швидкий, за допомогою QgsMapRenderer, або підготовка макета за допомогою QgsComposition та супутніх класів.

Просте відображення

Відобразити шари за допомогою QgsMapRenderer дуже просто — створюється вихідний пристрій (QImage, QPainter тощо), вказується список шарів, охоплення, розмір вихідного зображення та запускається рендерінг

# create image
img = QImage(QSize(800, 600), QImage.Format_ARGB32_Premultiplied)

# set image's background color
color = QColor(255, 255, 255)
img.fill(color.rgb())

# create painter
p = QPainter()
p.begin(img)
p.setRenderHint(QPainter.Antialiasing)

render = QgsMapRenderer()

# set layer set
lst = [layer.getLayerID()]  # add ID of every layer
render.setLayerSet(lst)

# set extent
rect = QgsRect(render.fullExtent())
rect.scale(1.1)
render.setExtent(rect)

# set output size
render.setOutputSize(img.size(), img.logicalDpiX())

# do the rendering
render.render(p)

p.end()

# save image
img.save("render.png","png")

Rendering layers with different CRS

If you have more than one layer and they have a different CRS, the simple example above will probably not work: to get the right values from the extent calculations you have to explicitly set the destination CRS and enable OTF reprojection as in the example below (only the renderer configuration part is reported)

...
# set layer set
layers = QgsMapLayerRegistry.instance().mapLayers()
lst = layers.keys()
render.setLayerSet(lst)

# Set destination CRS to match the CRS of the first layer
render.setDestinationCrs(layers.values()[0].crs())
# Enable OTF reprojection
render.setProjectionsEnabled(True)
...

Відображення за допомогою макетів

Редактор макетів стає у нагоді коли вам необхідно отримати щось складніше, ніж дозволяє простий рендерінг, описаний вище. Використовуючи редактор макетів можна створити складний макет, що містить декілька карт, підписи, легенду, таблиці та інші елементи, які ми звичайно бачимо на друкованих картах. Макети можна експортувати у PDF, растрові зображення або роздруковувати напряму.

The composer consists of a bunch of classes. They all belong to the core library. QGIS application has a convenient GUI for placement of the elements, though it is not available in the GUI library. If you are not familiar with Qt Graphics View framework, then you are encouraged to check the documentation now, because the composer is based on it.

Основним класом редактора макетів є QgsComposition, який походить від QGraphicsScene. Створимо екземпляр цього класу

mapRenderer = iface.mapCanvas().mapRenderer()
c = QgsComposition(mapRenderer)
c.setPlotStyle(QgsComposition.Print)

Зверніть увагу, що макет приймає в якості параметра екземпляр QgsMapRenderer. Ми припускаємо, зо код буде виконуватися безпосередньо в QGIS, тому використовуємо ренедерер активної карти. Макет використовує різноманітні параметри рендерера, найголовніші з них — список шарів карти та поточне охоплення. Якщо макети використовуються в автономній програмі, вам необхідно створити свій власний екземпляр рендерера, як було показано в попередньому розділі, та передати його до макета.

На макет можна додавати різні елементи (карту, підписи, ...) — ці елементи є похідними від класу QgsComposerItem. На сьогодні доступні такі елементи:

  • map — this item tells the libraries where to put the map itself. Here we create a map and stretch it over the whole paper size

    x, y = 0, 0
    w, h = c.paperWidth(), c.paperHeight()
    composerMap = QgsComposerMap(c, x ,y, w, h)
    c.addItem(composerMap)
    
  • label — allows displaying labels. It is possible to modify its font, color, alignment and margin

    composerLabel = QgsComposerLabel(c)
    composerLabel.setText("Hello world")
    composerLabel.adjustSizeToText()
    c.addItem(composerLabel)
    
  • legend

    legend = QgsComposerLegend(c)
    legend.model().setLayerSet(mapRenderer.layerSet())
    c.addItem(legend)
    
  • scale bar

    item = QgsComposerScaleBar(c)
    item.setStyle('Numeric') # optionally modify the style
    item.setComposerMap(composerMap)
    item.applyDefaultSize()
    c.addItem(item)
    
  • стрілка

  • зображення

  • фігура

  • таблиця

За замовчанням щойно створені елементи мають нульове положення (лівий верхній куточок сторінки) та нульовий розмір. Положення та розміри завжди задаються у міліметрах

# set label 1cm from the top and 2cm from the left of the page
composerLabel.setItemPosition(20, 10)
# set both label's position and size (width 10cm, height 3cm)
composerLabel.setItemPosition(20, 10, 100, 30)

Також за замовчанням навколо кожного елемента відображається рамка. Прибрати її можна так

composerLabel.setFrame(False)

Крім створення макетів вручну QGIS має підтримку шаблонів макетів. Шаблони це звичайні макети, збережені у вигляді файлів .qpt (формат XML). На жаль, цей функціонал поки недоступний через API.

Після того як макет підготований (всі елементи створено та розміщено в необхідних місцях), можна переходити до генерації вихідного растрового чи векторного файлу.

За замовчанням для виводу використовується аркуш розміру A4 та роздільна здатність 300 DPI. При необхідності ці параметри змінюються. Розмір паперу задається в міліметрах

c.setPaperSize(width, height)
c.setPrintResolution(dpi)

Друк у растр

Наступний фрагмент коду показує як згенерувати растрове представлення макету

dpi = c.printResolution()
dpmm = dpi / 25.4
width = int(dpmm * c.paperWidth())
height = int(dpmm * c.paperHeight())

# create output image and initialize it
image = QImage(QSize(width, height), QImage.Format_ARGB32)
image.setDotsPerMeterX(dpmm * 1000)
image.setDotsPerMeterY(dpmm * 1000)
image.fill(0)

# render the composition
imagePainter = QPainter(image)
sourceArea = QRectF(0, 0, c.paperWidth(), c.paperHeight())
targetArea = QRectF(0, 0, width, height)
c.render(imagePainter, targetArea, sourceArea)
imagePainter.end()

image.save("out.png", "png")

Друк у PDF

Наступний фрагмент коду показує як отримати файл формату PDF

printer = QPrinter()
printer.setOutputFormat(QPrinter.PdfFormat)
printer.setOutputFileName("out.pdf")
printer.setPaperSize(QSizeF(c.paperWidth(), c.paperHeight()), QPrinter.Millimeter)
printer.setFullPage(True)
printer.setColorMode(QPrinter.Color)
printer.setResolution(c.printResolution())

pdfPainter = QPainter(printer)
paperRectMM = printer.pageRect(QPrinter.Millimeter)
paperRectPixel = printer.pageRect(QPrinter.DevicePixel)
c.render(pdfPainter, paperRectPixel, paperRectMM)
pdfPainter.end()