파이썬 예제 (Google Map + Python)

개요

이번에는 파이썬 + 구글맵 + PyQt5를 이용해 아래와 같은 프로그램을 만들어 보았습니다.

지명이나 위경도 좌표를 입력하고, 그리기 버튼을 누르면 구글에 REST 기반 요청신호 (Http Request)를 보내 지도를 이미지로 다운로드 받아 화면에 표시하는 방식입니다.

프로그램 실행 화면




지도에 GPS센서로부터 수신된 위경도 좌표를 표시할 일이 생겼는데, NAVER, KAKAO 지도는 국내만 지원되어 제외하고 구글 맵으로 연동해 보았습니다.

이 예제를 실행하기 위해서는 Google Cloud Platform(GCP)에 사전에 가입하고 구글맵을 사용하기위한 Key를 발급받아야 합니다.
 
[GCP 소개 : 출처 GCP 홈]

이 키를 통해 구글로 정보를 요청하고 받아와서 출력하는 방식입니다.

GCP는 구글맵뿐만 아니라 컴퓨팅, 네트워킹, 빅테이터 관련한 다양한 서비스를 제공합니다.

구글의 클라우드 서버가 존재하는 지역정보입니다.

[GCP 리전 소개 : 출처 GCP 홈]

이 글에서 Google Maps Platform에 가입절차 및 Key를 받는 과정은 생략합니다.

아래 코드를 만들기 위해서는 반드시 선행되어야 하므로 인터넷 검색 등으로 찾아서 Google Map API 중 Static Maps API Key를 받아야 가능합니다.

이외에도 다양한 Map API 가 존재하지만, 제가 필요한 기능은 Static Map 정도면 충분하네요.

구글 맵 서비스를 이용하기 위해서는 사전에 API Key 를 받아야 하며, 비용이 발생합니다.

구글에서 안내하는 비용은 아래와 같습니다.


이미지 1,000장 다운로드시 2달러 입니다.

처음 구글 클라우딩 플랫폼에 가입시 US$ 300 불의 무료 크래딧을 제공하므로 비용을 걱정할 필요는 없습니다.

좀더 세부적인 Google Static Map API 사용법은 아래 링크를 참조 바랍니다.


인용 : Google Static map Guide

Http Requst 방식을 사용하며, 이미지가 리턴되고 API key가 필요하다고 합니다.


프로그램 사용법

완성된 프로그램을 사용하는 방법과 간단한 절차 입니다.

1. QLineEdit 입력창 주소 or 위경도 좌표 입력
ex) "Busan", "Seoul", "50.000000,50.000000"

2. QComboBox 줌 레벨 선택 (1~20 클수록 확대)

3. QPushButton(구글 맵 그리기) 클릭

4. Http를 통한 이미지 다운 및 QPixmap으로 그리기

지명(Busan)으로 그리기

위경도 좌표(그리니치 천문대)로 그리기


소스코드 분석

from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QHBoxLayout, QLineEdit, QPushButton, QLabel, QFrame, QComboBox
from PyQt5.QtGui import QPixmap
from PyQt5.QtCore import Qt
import requests
import sys

QApplication.setAttribute(Qt.AA_EnableHighDpiScaling, True)

class myWidget(QWidget):

    def __init__(self):
        super().__init__()
        self.setFixedSize(640,640)
        
        label = QLabel('위경도좌표(xx.xxxxxx,xx.xxxxxx) or 도시명(Busan)')
        self.lineEdit = QLineEdit(self)
        self.cmb = QComboBox(self)
        txt = [str(i) for i in range(1,21)]
        self.cmb.addItems(txt)
        self.cmb.setCurrentIndex(15)
        self.btn = QPushButton('구글 맵 그리기', self)

        hbox = QHBoxLayout()
        hbox.addWidget(label)
        hbox.addWidget(self.lineEdit)
        hbox.addWidget(self.cmb)
        hbox.addWidget(self.btn)

        self.img = QLabel('', self)
        self.img.setFrameStyle(QFrame.Box)

        vbox = QVBoxLayout()
        vbox.addLayout(hbox)
        vbox.addWidget(self.img)
        self.setLayout(vbox)

        # signal
        self.btn.clicked.connect(self.clickBtn)

    def clickBtn(self):
        BASE_URL    = 'https://maps.googleapis.com/maps/api/staticmap?'
        API_KEY     = '여기에 API KEY 입력'
        ZOOM        = self.cmb.currentIndex()+1
        CITY        = self.lineEdit.text()

        W = self.img.width()
        H = self.img.height()        

        URL = (BASE_URL 
       + f'center={CITY}'
       + f'&zoom={ZOOM}' 
       + f'&size={W}x{H}&scale=2' 
       + '&markers=color:red%7Clabel:S%7C'+CITY
       + f'&key={API_KEY}')

        # HTTP request
        response = requests.get(URL)

        # image scaled and draw
        img = QPixmap()
        img.loadFromData(response.content)
        img = img.scaled(img.width()//2, img.height()//2, Qt.KeepAspectRatio, Qt.SmoothTransformation)

        self.img.setPixmap(img)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    w = myWidget()
    w.show()
    sys.exit(app.exec_())
1~5번 라인에서 필요한 PyQt5 및 python 패키지를 불러옵니다.

모든 코드는 9~64번 라인까지 이어지는 myWindow라는 클래스에 구현되어 있습니다.

myWindow 는 QWidget에서 상속받도록 합니다.

상속관계를 갖는 클래스는 자신의 생성자 안에서 반드시 부모(super) 클래스의 생성자를 호출해야 합니다.

11~ 38번 라인은 myWindow class의 생성자 함수이며, 여기서 클래스의 객체가 초기화 됩니다.

주로 GUI 구성에 필요한 컨트롤 (QLineEdit, QComboBox, QPushButton) 등을 생성하고 Qt의 LayoutBox를 이용해 배치합니다.

38번 라인에서 버튼을 눌렀을때 호출되는 슬롯함수를 연결합니다. 이 버튼이 바로 구글 맵에 Http 요청 신호를 보내 그림을 받아오는 역할을 담당합니다.

40~64번 clickBtn() 함수는 버튼을 누르면 호출되는 함수입니다.

1. Google Map URL 변수 설정 (Line 41)

2. Google Map 인증 키 입력 (Line 42)

3. QComboBox에서 선택한 줌 레벨 (1~20) 얻기 (Line 43)

4. QLineEdit에 입력된 장소 or 위경도 좌표 얻기 (Line 44)

5. QLabel 영역의 너비, 높이 구하기 (Line 46~47)


6. 최종적으로 Http Request에 쓰일 URL 구하기 (Line 49~54)

URL = BASE URL + center(이미지 중심) + size(이미지 크기) + marker(이미지 마크) + API KEY

출력해보면 최종 URL은 아래와 같은 문자열이 됩니다.



7. Http Request 보내기 (Line 57)

8. Http Response 받아서 이미지 처리하기 (Line 60~64)

수신된 이미지는 response.content에 저장되며 바이트타입으로 저장된 바이너리 이미지입니다.

이를 QPixmap 클래스를 이용해 이미지로 만든 후 QLabel 너비와 높이에 맞게 리사이징합니다.

이때 이미지의 크기를 2로 나누는 이유는 52번 라인의 Http Request 중 "scale=2" 때문에 이미지의 해상도가 2배로 증가되었기 때문에 QLable에 크기에 맞도록 스케일을 바꿀 필요가 있기 때문입니다.

"scale=1" 로 설정하니 이미지 품질이 저수준이라 이런 코드를 넣어 보았습니다.

마지막으로 setPixmap() 함수를 이용해 라벨에 이미지를 띄워 줍니다.


이상으로 모든 코드 분석을 마칩니다.

개발 환경 : MS Window 10 Pro, Python 3.7(64bit), PyQt5(5.14.2)

감사합니다.

댓글

이 블로그의 인기 게시물

Qt Designer 설치하기

C++ 예제 (소켓 서버, 이미지, 파일전송)