맵 렌더링 및 출력

입력 데이터를 렌더링해야 할 때 일반적으로 2가지 접근법을 사용합니다. QgsMapRenderer 클래스를 이용하는 빠른 방법이 있고, 또는 QgsComposition 클래스와 그 프렌드(friend) 클래스로 맵을 작성해서 좀 더 미세조정(fine-tuned)된 산출물을 생산하는 방법이 있습니다.

단순 렌더링

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 = QgsRectangle(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")

서로 다른 좌표계를 가진 레이어들을 렌더링하기

레이어가 여러 개인데 서로 다른 좌표계를 가지고 있을 경우, 앞에서 본 단순 렌더링 예시는 작동 않을 것입니다. 범위 계산에서 올바른 값을 얻으려면 다음 예시 코드에서처럼 최종 CRS를 명확하게 지정하고 실시간(OTF, on the fly) 재투영을 활성화해야 합니다. (렌더러 설정 부분만 가져왔습니다.)

...
# 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. Also check the Python documentation of the implementation of QGraphicView.

맵 구성기의 중심 클래스는 QGraphicsScene 클래스에서 파생된 QgsComposition 클래스입니다. 이 클래스를 생성해봅시다.

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

맵 구성에 QgsMapRenderer 클래스 인스턴스가 필요하다는 사실을 주의하십시오. 이 예시 코드는 QGIS 응용 프로그램 내부에서 실행한다고 가정하고 있기에 맵 캔버스의 맵 렌더러를 사용합니다. 맵 구성 작업에서 맵 렌더러의 다양한 파라미터를 쓰게 되는데, 가장 중요한 것은 맵 레이어와 현재 범위의 기본 세트입니다. 독립형 응용 프로그램에서 맵 구성기를 사용할 때, 앞 장에서와 동일한 방법으로 사용자 지정 맵 렌더러를 구현해서 구성 작업에 넘겨줄 수 있습니다.

구성 작업에서 다양한 요소들(맵, 라벨 등)을 추가할 수도 있습니다. 이 요소들은 QgsComposerItem 클래스에서 파생된 것이어야 합니다. 현재 버전에서 지원하는 아이템은 다음과 같습니다.

  • 맵 — 이 아이템은 라이브러리에게 맵 자체를 어디에 놓을지 말해줍니다. 다음은 맵을 생성해서 종이 크기 전체로 늘리는 예시 코드입니다.

    x, y = 0, 0
    w, h = c.paperWidth(), c.paperHeight()
    composerMap = QgsComposerMap(c, x ,y, w, h)
    c.addItem(composerMap)
    
  • 라벨 — 라벨을 표출할 수 있도록 해줍니다. 폰트, 색상, 정렬 및 여백을 수정할 수 있습니다.

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

    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)
    
  • 화살표

  • 그림

  • 도형

새로 생성된 맵 작성자 아이템의 위치 기본값은 (0,0), 즉 페이지의 좌상단이며 크기 기본값은 0입니다. 위치와 크기의 단위는 언제나 밀리미터입니다.

# 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)

기본적으로 모든 아이템 주위에 테두리(frame)가 그려집니다. 다음은 그 테두리를 없애는 방법입니다.

composerLabel.setFrame(False)

맵 구성기 아이템을 수동으로 생성하는 방법 외에도, QGIS는 근본적으로는 모든 구성기 아이템들로 구성될 수 있는 (XML 문법을 따르는) .qpt 파일의 형태로 저장되는 맵 구성기 템플릿을 지원합니다. 아쉽게도 아직 API에서 이 기능을 사용할 수는 없습니다.

(맵 구성기 아이템을 생성하고 구성에 추가하는) 구성 작업이 완료되면, 래스터 및 벡터 출력물을 생성하는 다음 단계로 넘어갈 수 있습니다.

구성 작업의 출력 설정 기본값은 종이 크기 A4, 해상도 300dpi입니다. 필요하다면 설정을 변경할 수도 있습니다. 종이 크기는 밀리미터 단위로 설정됩니다.

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()