예제 #1
0
파일: utils.py 프로젝트: m-kuhn/QGIS
def _unloadPluginModules(packageName):
    """ unload plugin package with all its modules (files) """
    global _plugin_modules
    mods = _plugin_modules[packageName]

    for mod in mods:
        if not mod in sys.modules:
            continue

        # if it looks like a Qt resource file, try to do a cleanup
        # otherwise we might experience a segfault next time the plugin is loaded
        # because Qt will try to access invalid plugin resource data
        try:
            if hasattr(sys.modules[mod], 'qCleanupResources'):
                sys.modules[mod].qCleanupResources()
        except:
            # Print stack trace for debug
            qDebug("qCleanupResources error:\n%s" % traceback.format_exc())

        # try removing path
        if hasattr(sys.modules[mod], '__path__'):
            for path in sys.modules[mod].__path__:
                try:
                    sys.path.remove(path)
                except ValueError:
                    # Discard if path is not there
                    pass

        # try to remove the module from python
        try:
            del sys.modules[mod]
        except:
            qDebug("Error when removing module:\n%s" % traceback.format_exc())
    # remove the plugin entry
    del _plugin_modules[packageName]
예제 #2
0
    def checkTest(self, **kwargs):
        self.lyr.writeToLayer(self.layer)

        ms = self._MapSettings  # class settings
        settings_type = 'Class'
        if self._TestMapSettings is not None:
            ms = self._TestMapSettings  # per test settings
            settings_type = 'Test'
        if 'PAL_VERBOSE' in os.environ:
            qDebug('MapSettings type: {0}'.format(settings_type))
            qDebug(mapSettingsString(ms))

        res_m, self._TestImage = self.get_composer_output(self._TestKind)
        self.assertTrue(res_m, 'Failed to retrieve/save output from composer')
        self.saveControlImage(self._TestImage)
        mismatch = 0
        if 'PAL_NO_MISMATCH' not in os.environ:
            # some mismatch expected
            mismatch = self._Mismatch if self._Mismatch else 20
            if self._TestGroup in self._Mismatches:
                mismatch = self._Mismatches[self._TestGroup]
        colortol = 0
        if 'PAL_NO_COLORTOL' not in os.environ:
            colortol = self._ColorTol if self._ColorTol else 0
            if self._TestGroup in self._ColorTols:
                colortol = self._ColorTols[self._TestGroup]
        self.assertTrue(*self.renderCheck(mismatch=mismatch,
                                          colortol=colortol,
                                          imgpath=self._TestImage))
예제 #3
0
    def checkTest(self, **kwargs):
        self.layer.setLabeling(QgsVectorLayerSimpleLabeling(self.lyr))

        ms = self._MapSettings  # class settings
        settings_type = 'Class'
        if self._TestMapSettings is not None:
            ms = self._TestMapSettings  # per test settings
            settings_type = 'Test'
        if 'PAL_VERBOSE' in os.environ:
            qDebug('MapSettings type: {0}'.format(settings_type))
            qDebug(mapSettingsString(ms))

        img = renderMapToImage(ms, parallel=False)
        self._TestImage = getTempfilePath('png')
        if not img.save(self._TestImage, 'png'):
            os.unlink(self._TestImage)
            raise OSError('Failed to save output from map render job')
        self.saveControlImage(self._TestImage)

        mismatch = 0
        if 'PAL_NO_MISMATCH' not in os.environ:
            # some mismatch expected
            mismatch = self._Mismatch if self._Mismatch else 0
            if self._TestGroup in self._Mismatches:
                mismatch = self._Mismatches[self._TestGroup]
        colortol = 0
        if 'PAL_NO_COLORTOL' not in os.environ:
            colortol = self._ColorTol if self._ColorTol else 0
            if self._TestGroup in self._ColorTols:
                colortol = self._ColorTols[self._TestGroup]
        self.assertTrue(*self.renderCheck(mismatch=mismatch,
                                          colortol=colortol,
                                          imgpath=self._TestImage))
예제 #4
0
    def setUp(self):
        testid = self.id().split(".")
        testheader = "\n#####_____ {0}.{1} _____#####\n".format(testid[1], testid[2])
        qDebug(testheader)

        if not self.authm.masterPasswordIsSet() or not self.authm.masterPasswordHashInDb():
            self.set_master_password()
    def checkTest(self, **kwargs):
        self.lyr.writeToLayer(self.layer)

        ms = self._MapSettings  # class settings
        settings_type = "Class"
        if self._TestMapSettings is not None:
            ms = self._TestMapSettings  # per test settings
            settings_type = "Test"
        if "PAL_VERBOSE" in os.environ:
            qDebug("MapSettings type: {0}".format(settings_type))
            qDebug(mapSettingsString(ms))

        img = renderMapToImage(ms, parallel=False)
        self._TestImage = getTempfilePath("png")
        if not img.save(self._TestImage, "png"):
            os.unlink(self._TestImage)
            raise OSError("Failed to save output from map render job")
        self.saveControlImage(self._TestImage)

        mismatch = 0
        if "PAL_NO_MISMATCH" not in os.environ:
            # some mismatch expected
            mismatch = self._Mismatch if self._Mismatch else 0
            if self._TestGroup in self._Mismatches:
                mismatch = self._Mismatches[self._TestGroup]
        colortol = 0
        if "PAL_NO_COLORTOL" not in os.environ:
            colortol = self._ColorTol if self._ColorTol else 0
            if self._TestGroup in self._ColorTols:
                colortol = self._ColorTols[self._TestGroup]
        self.assertTrue(*self.renderCheck(mismatch=mismatch, colortol=colortol, imgpath=self._TestImage))
예제 #6
0
    def setUp(self):
        testid = self.id().split('.')
        testheader = '\n#####_____ {0}.{1} _____#####\n'. \
            format(testid[1], testid[2])
        qDebug(testheader)

        if (not self.authm.masterPasswordIsSet() or
                not self.authm.masterPasswordHashInDatabase()):
            self.set_master_password()
    def replyFinished(self):
        reply = self.sender()
        url = reply.request().url().toString()
        self.log("replyFinished: %s" % url)
        if not url in self.fetchedFiles:
            self.fetchedFiles[url] = None
        self.requestingUrls.remove(url)
        self.replies.remove(reply)
        isFromCache = 0
        httpStatusCode = reply.attribute(QNetworkRequest.HttpStatusCodeAttribute)
        if reply.error() == QNetworkReply.NoError:
            self.fetchSuccesses += 1
            if reply.attribute(QNetworkRequest.SourceIsFromCacheAttribute):
                self.cacheHits += 1
                isFromCache = 1
            elif not reply.hasRawHeader("Cache-Control"):
                cache = QgsNetworkAccessManager.instance().cache()
                if cache:
                    metadata = cache.metaData(reply.request().url())
                    # self.log("Expiration date: " + metadata.expirationDate().toString().encode("utf-8"))
                    if metadata.expirationDate().isNull():
                        metadata.setExpirationDate(
                            QDateTime.currentDateTime().addSecs(self.default_cache_expiration * 60 * 60))
                        cache.updateMetaData(metadata)
                        self.log(
                            "Default expiration date has been set: %s (%d h)" % (url, self.default_cache_expiration))

            if reply.isReadable():
                data = reply.readAll()
                self.fetchedFiles[url] = data
            else:
                qDebug("http status code: " + str(httpStatusCode))
        else:
            if self.sync and httpStatusCode == 404:
                self.fetchedFiles[url] = self.NOT_FOUND
            self.fetchErrors += 1
            if self.errorStatus == self.NO_ERROR:
                self.errorStatus = self.UNKNOWN_ERROR

        self.replyFinished.emit(url, reply.error(), isFromCache)
        reply.deleteLater()

        if debug_mode:
            qDebug("queue: %d, requesting: %d" % (len(self.queue), len(self.requestingUrls)))

        if len(self.queue) + len(self.requestingUrls) == 0:
            # all replies have been received
            if self.sync:
                self.logT("eventLoop.quit()")
                self.eventLoop.quit()
            else:
                self.timer.stop()
        elif len(self.queue) > 0:
            # start fetching the next file
            self.fetchNext()
        self.log("replyFinished End: %s" % url)
 def setLayerType(self, layerType):
     qDebug(" setLayerType: %s" % layerType.layerTypeName)
     self.layerType = layerType
     self.setCustomProperty(OpenlayersLayer.LAYER_PROPERTY,
                            layerType.layerTypeName)
     coordRefSys = self.layerType.coordRefSys(None)  # FIXME
     self.setCrs(coordRefSys)
     # TODO: get extent from layer type
     self.setExtent(QgsRectangle(-20037508.34,
                                 -20037508.34, 20037508.34, 20037508.34))
예제 #9
0
파일: db_model.py 프로젝트: NyakudyaA/QGIS
 def refreshItem(self, item):
     if isinstance(item, TreeItem):
         # find the index for the tree item using the path
         index = self._rPath2Index(item.path())
     else:
         # find the index for the db item
         index = self._rItem2Index(item)
     if index.isValid():
         self._refreshIndex(index)
     else:
         qDebug("invalid index")
 def setLayerType(self, layerType):
     qDebug(" setLayerType: %s" % layerType.layerTypeName)
     self.layerType = layerType
     self.setCustomProperty(OpenlayersLayer.LAYER_PROPERTY,
                            layerType.layerTypeName)
     coordRefSys = self.layerType.coordRefSys(None)  # FIXME
     self.setCrs(coordRefSys)
     
     # set layer's full extent, 2014-06-12 minpa lee
     ext = self.layerType.fullExtent
     self.setExtent(QgsRectangle(ext[0], ext[1], ext[2], ext[3]))
예제 #11
0
    def configTest(self, prefix, abbr):
        """Call in setUp() function of test subclass"""
        self._TestGroupPrefix = prefix
        self._TestGroupAbbr = abbr

        # insert test's Class.function marker into debug output stream
        # this helps visually track down the start of a test's debug output
        testid = self.id().split(".")
        self._TestGroup = testid[1]
        self._TestFunction = testid[2]
        testheader = "\n#####_____ {0}.{1} _____#####\n".format(self._TestGroup, self._TestFunction)
        qDebug(testheader)

        # define the shorthand name of the test (to minimize file name length)
        self._Test = "{0}_{1}".format(self._TestGroupAbbr, self._TestFunction.replace("test_", ""))
예제 #12
0
    def testTable(self):
        """Test we can render a html table in a single frame."""
        layout_html = QgsLayoutItemHtml(self.layout)
        html_frame = QgsLayoutFrame(self.layout, layout_html)
        html_frame.attemptSetSceneRect(QRectF(0, 0, 100, 200))
        html_frame.setFrameEnabled(True)
        layout_html.addFrame(html_frame)
        layout_html.setUrl(self.htmlUrl())

        checker = QgsLayoutChecker('composerhtml_table', self.layout)
        checker.setControlPathPrefix("composer_html")
        myTestResult, myMessage = checker.testLayout()

        qDebug(myMessage)
        self.layout.removeMultiFrame(layout_html)
        assert myTestResult, myMessage
예제 #13
0
    def fetchFiles(self, urls):
        self.logT("TileLayer.fetchFiles() starts")
        # create a QEventLoop object that belongs to the current thread (if ver. > 2.1, it is render thread)
        eventLoop = QEventLoop()
        self.logT("Create event loop: " + str(eventLoop))  # DEBUG
        # QObject.connect(self, SIGNAL("allRepliesFinished()"), eventLoop.quit)
        self.allRepliesFinished.connect(eventLoop.quit)

        # create a timer to watch whether rendering is stopped
        watchTimer = QTimer()
        watchTimer.timeout.connect(eventLoop.quit)

        # send a fetch request to the main thread
        # self.emit(SIGNAL("fetchRequest(QStringList)"), urls)
        self.fetchRequest.emit(urls)

        # wait for the fetch to finish
        tick = 0
        interval = 500
        timeoutTick = self.downloadTimeout / interval
        watchTimer.start(interval)
        while tick < timeoutTick:
            # run event loop for 0.5 seconds at maximum
            eventLoop.exec_()

            if debug_mode:
                qDebug("watchTimerTick: %d" % tick)
                qDebug("unfinished downloads: %d" % self.downloader.unfinishedCount())

            if self.downloader.unfinishedCount() == 0 or self.renderContext.renderingStopped():
                break
            tick += 1
        watchTimer.stop()

        if tick == timeoutTick and self.downloader.unfinishedCount() > 0:
            self.log("fetchFiles timeout")
            # self.emitShowBarMessage("fetchFiles timeout", duration=5)   #DEBUG
            self.downloader.abort()
            self.downloader.errorStatus = Downloader.TIMEOUT_ERROR
        files = self.downloader.fetchedFiles

        watchTimer.timeout.disconnect(eventLoop.quit)  #
        # QObject.disconnect(self, SIGNAL("allRepliesFinished()"), eventLoop.quit)
        self.allRepliesFinished.disconnect(eventLoop.quit)

        self.logT("TileLayer.fetchFiles() ends")
        return files
    def testTable(self):
        """Test we can render a html table in a single frame."""
        composerHtml = QgsComposerHtml(self.mComposition, False)
        htmlFrame = QgsComposerFrame(self.mComposition, composerHtml, 0, 0, 100, 200)
        htmlFrame.setFrameEnabled(True)
        composerHtml.addFrame(htmlFrame)
        composerHtml.setUrl(self.htmlUrl())

        checker = QgsCompositionChecker("composerhtml_table", self.mComposition)
        checker.setControlPathPrefix("composer_html")
        myTestResult, myMessage = checker.testComposition()

        qDebug(myMessage)
        self.mComposition.removeMultiFrame(composerHtml)
        composerHtml = None

        assert myTestResult, myMessage
    def saveControlImage(self, tmpimg=''):
        # don't save control images for RenderVsOtherOutput (Vs) tests, since
        # those control images belong to a different test result
        if ('PAL_CONTROL_IMAGE' not in os.environ
                or 'Vs' in self._TestGroup):
            return
        imgpath = self.controlImagePath()
        testdir = os.path.dirname(imgpath)
        if not os.path.exists(testdir):
            os.makedirs(testdir)
        imgbasepath = \
            os.path.join(testdir,
                         os.path.splitext(os.path.basename(imgpath))[0])
        # remove any existing control images
        for f in glob.glob(imgbasepath + '.*'):
            if os.path.exists(f):
                os.remove(f)
        qDebug('Control image for {0}.{1}'.format(self._TestGroup,
                                                  self._TestFunction))

        if not tmpimg:
            # TODO: this can be deprecated, when per-base-test-class rendering
            #       in checkTest() is verified OK for all classes
            qDebug('Rendering control to: {0}'.format(imgpath))
            ms = self._MapSettings  # class settings
            """:type: QgsMapSettings"""
            settings_type = 'Class'
            if self._TestMapSettings is not None:
                ms = self._TestMapSettings  # per test settings
                settings_type = 'Test'
            qDebug('MapSettings type: {0}'.format(settings_type))

            img = renderMapToImage(ms, parallel=False)
            """:type: QImage"""
            tmpimg = getTempfilePath('png')
            if not img.save(tmpimg, 'png'):
                os.unlink(tmpimg)
                raise OSError('Control not created for: {0}'.format(imgpath))

        if tmpimg and os.path.exists(tmpimg):
            qDebug('Copying control to: {0}'.format(imgpath))
            shutil.copyfile(tmpimg, imgpath)
        else:
            raise OSError('Control not copied to: {0}'.format(imgpath))
예제 #16
0
    def checkTest(self, **kwargs):
        self.lyr.writeToLayer(self.layer)
        # save project file
        self._TestProj.write()
        # always restart FCGI before tests, so settings can be applied
        # MAPSERV.fcgi_server_process().start()
        # get server results
        # print self.params.__repr__()

        ms = self._MapSettings  # class settings
        settings_type = 'Class'
        if self._TestMapSettings is not None:
            ms = self._TestMapSettings  # per test settings
            settings_type = 'Test'
        if 'PAL_VERBOSE' in os.environ:
            qDebug('MapSettings type: {0}'.format(settings_type))
            qDebug(mapSettingsString(ms))

        res_m, self._TestImage, url = MAPSERV.get_map(
            self.get_request_params(), False)
        # print self._TestImage.__repr__()
        if 'PAL_VERBOSE' in os.environ:
            qDebug('GetMap request:\n  {0}\n'.format(url))
        self.saveControlImage(self._TestImage)
        self.assertTrue(res_m,
                        'Failed to retrieve/save image from test server')
        mismatch = 0
        if 'PAL_NO_MISMATCH' not in os.environ:
            # some mismatch expected
            mismatch = self._Mismatch if self._Mismatch else 20
            if self._TestGroup in self._Mismatches:
                mismatch = self._Mismatches[self._TestGroup]
        colortol = 0
        if 'PAL_NO_COLORTOL' not in os.environ:
            # some mismatch expected
            # colortol = self._ColorTol if self._ColorTol else 10
            if self._TestGroup in self._ColorTols:
                colortol = self._ColorTols[self._TestGroup]
        self.assertTrue(*self.renderCheck(
            mismatch=mismatch, colortol=colortol, imgpath=self._TestImage))
예제 #17
0
    def checkTest(self, **kwargs):
        self.sync_map_settings()
        self.lyr.writeToLayer(self.layer)
        # save project file
        self._TestProj.write()
        # always restart FCGI before tests, so settings can be applied
        # MAPSERV.fcgi_server_process().start()
        # get server results
        # print self.params.__repr__()

        ms = self._MapSettings  # class settings
        settings_type = 'Class'
        if self._TestMapSettings is not None:
            ms = self._TestMapSettings  # per test settings
            settings_type = 'Test'
        if 'PAL_VERBOSE' in os.environ:
            qDebug('MapSettings type: {0}'.format(settings_type))
            qDebug(mapSettingsString(ms))

        res_m, self._TestImage, url = MAPSERV.get_map(self.get_request_params(), False)
        # print self._TestImage.__repr__()
        if 'PAL_VERBOSE' in os.environ:
            qDebug('GetMap request:\n  {0}\n'.format(url))
        self.saveControlImage(self._TestImage)
        self.assertTrue(res_m, 'Failed to retrieve/save image from test server')
        mismatch = 0
        if 'PAL_NO_MISMATCH' not in os.environ:
            # some mismatch expected
            mismatch = self._Mismatch if self._Mismatch else 20
            if self._TestGroup in self._Mismatches:
                mismatch = self._Mismatches[self._TestGroup]
        colortol = 0
        if 'PAL_NO_COLORTOL' not in os.environ:
            # some mismatch expected
            # colortol = self._ColorTol if self._ColorTol else 10
            if self._TestGroup in self._ColorTols:
                colortol = self._ColorTols[self._TestGroup]
        self.assertTrue(*self.renderCheck(mismatch=mismatch,
                                          colortol=colortol,
                                          imgpath=self._TestImage))
예제 #18
0
    def _get_layout_pdf_image(self, width, height, dpi):
        pdfpath = getTempfilePath('pdf')
        temp_size = os.path.getsize(pdfpath)

        p = QPrinter()
        p.setOutputFormat(QPrinter.PdfFormat)
        p.setOutputFileName(pdfpath)
        p.setPaperSize(
            QSizeF(self._c.pageCollection().page(0).sizeWithUnits().width(),
                   self._c.pageCollection().page(0).sizeWithUnits().height()),
            QPrinter.Millimeter)
        p.setFullPage(True)
        p.setColorMode(QPrinter.Color)
        p.setResolution(self._c.renderContext().dpi())

        pdf_p = QPainter(p)
        # page_mm = p.pageRect(QPrinter.Millimeter)
        # page_px = p.pageRect(QPrinter.DevicePixel)
        # self._c.render(pdf_p, page_px, page_mm)
        exporter = QgsLayoutExporter(self._c)
        exporter.renderPage(pdf_p, 0)
        pdf_p.end()

        if temp_size == os.path.getsize(pdfpath):
            return False, ''

        filepath = getTempfilePath('png')
        # Poppler (pdftocairo or pdftoppm):
        # PDFUTIL -png -singlefile -r 72 -x 0 -y 0 -W 420 -H 280 in.pdf pngbase
        # muPDF (mudraw):
        # PDFUTIL -c rgb[a] -r 72 -w 420 -h 280 -o out.png in.pdf
        if PDFUTIL.strip().endswith('pdftocairo'):
            filebase = os.path.join(
                os.path.dirname(filepath),
                os.path.splitext(os.path.basename(filepath))[0])
            call = [
                PDFUTIL, '-png', '-singlefile', '-r',
                str(dpi), '-x', '0', '-y', '0', '-W',
                str(width), '-H',
                str(height), pdfpath, filebase
            ]
        elif PDFUTIL.strip().endswith('mudraw'):
            call = [
                PDFUTIL,
                '-c',
                'rgba',
                '-r',
                str(dpi),
                '-w',
                str(width),
                '-h',
                str(height),
                # '-b', '8',
                '-o',
                filepath,
                pdfpath
            ]
        else:
            return False, ''

        qDebug("_get_layout_pdf_image call: {0}".format(' '.join(call)))
        res = False
        try:
            subprocess.check_call(call)
            res = True
        except subprocess.CalledProcessError as e:
            qDebug("_get_layout_pdf_image failed!\n"
                   "cmd: {0}\n"
                   "returncode: {1}\n"
                   "message: {2}".format(e.cmd, e.returncode, e.message))

        if not res:
            os.unlink(filepath)
            filepath = ''

        return res, filepath
 def javaScriptConsoleMessage(self, message, lineNumber, sourceID):
     qDebug("%s[%d]: %s" % (sourceID, lineNumber, message))
def debug(msg, verbosity=1):
    if debuglevel >= verbosity:
        try:
            qDebug(msg)
        except Exception:
            pass
예제 #21
0
 def javaScriptConsoleMessage(self, message, lineNumber, sourceID):
     # For debugging purposes
     logEntry = "%s[%d]: %s" % (sourceID, lineNumber, message)
     qDebug(logEntry)
     '''
def debug(msg, verbosity=1):
    if debuglevel >= verbosity:
        try:
            qDebug(msg)
        except Exception:
            pass
 def javaScriptConsoleMessage(self, message, lineNumber, sourceID):
     qDebug("%s[%d]: %s" % (sourceID, lineNumber, message))
예제 #24
0
    def _get_composer_pdf_image(self, width, height, dpi):
        pdfpath = getTempfilePath('pdf')
        temp_size = os.path.getsize(pdfpath)

        p = QPrinter()
        p.setOutputFormat(QPrinter.PdfFormat)
        p.setOutputFileName(pdfpath)
        p.setPaperSize(QSizeF(self._c.paperWidth(), self._c.paperHeight()),
                       QPrinter.Millimeter)
        p.setFullPage(True)
        p.setColorMode(QPrinter.Color)
        p.setResolution(self._c.printResolution())

        pdf_p = QPainter(p)
        # page_mm = p.pageRect(QPrinter.Millimeter)
        # page_px = p.pageRect(QPrinter.DevicePixel)
        # self._c.render(pdf_p, page_px, page_mm)
        self._c.renderPage(pdf_p, 0)
        pdf_p.end()

        if temp_size == os.path.getsize(pdfpath):
            return False, ''

        filepath = getTempfilePath('png')
        # Poppler (pdftocairo or pdftoppm):
        # PDFUTIL -png -singlefile -r 72 -x 0 -y 0 -W 420 -H 280 in.pdf pngbase
        # muPDF (mudraw):
        # PDFUTIL -c rgb[a] -r 72 -w 420 -h 280 -o out.png in.pdf
        if PDFUTIL.strip().endswith('pdftocairo'):
            filebase = os.path.join(
                os.path.dirname(filepath),
                os.path.splitext(os.path.basename(filepath))[0]
            )
            call = [
                PDFUTIL, '-png', '-singlefile', '-r', str(dpi),
                '-x', '0', '-y', '0', '-W', str(width), '-H', str(height),
                pdfpath, filebase
            ]
        elif PDFUTIL.strip().endswith('mudraw'):
            call = [
                PDFUTIL, '-c', 'rgba',
                '-r', str(dpi), '-w', str(width), '-h', str(height),
                # '-b', '8',
                '-o', filepath, pdfpath
            ]
        else:
            return False, ''

        qDebug("_get_composer_pdf_image call: {0}".format(' '.join(call)))
        res = False
        try:
            subprocess.check_call(call)
            res = True
        except subprocess.CalledProcessError as e:
            qDebug("_get_composer_pdf_image failed!\n"
                   "cmd: {0}\n"
                   "returncode: {1}\n"
                   "message: {2}".format(e.cmd, e.returncode, e.message))

        if not res:
            os.unlink(filepath)
            filepath = ''

        return res, filepath
    def draw(self, renderContext):

        self.renderContext = renderContext
        extent = renderContext.extent()
        if extent.isEmpty() or extent.width() == float("inf"):
            qDebug("Drawing is skipped because map extent is empty or inf.")
            return True

        mapSettings = iface.mapCanvas().mapSettings()
        painter = renderContext.painter()
        isDpiEqualToCanvas = painter.device().logicalDpiX() == mapSettings.outputDpi()
        if isDpiEqualToCanvas or not self.useLastZoomForPrint:
            # calculate zoom level
            tile_mpp1 = self.layerDef.TSIZE1 / self.layerDef.TILE_SIZE
            viewport_mpp = extent.width() / painter.viewport().width()
            lg = math.log(float(tile_mpp1) / float(viewport_mpp), 2)
            zoom = int(math.modf(lg)[1]) + 1*(math.modf(lg)[0] > self.CHANGE_SCALE_VALUE) + 1
            zoom = max(0, min(zoom, self.layerDef.zmax))
            # zoom = max(self.layerDef.zmin, zoom)
        else:
            # for print composer output image, use last zoom level of map item on print composer (or map canvas)
            zoom = self.canvasLastZoom

        # zoom limit
        if zoom < self.layerDef.zmin:
            msg = self.tr("Current zoom level ({0}) is smaller than zmin ({1}): {2}").format(zoom,
                                                                                             self.layerDef.zmin,
                                                                                             self.layerDef.title)
            self.showBarMessage(msg, QgsMessageBar.INFO, 2)
            return True

        while True:
            # calculate tile range (yOrigin is top)
            size = self.layerDef.TSIZE1 / 2 ** (zoom - 1)
            matrixSize = 2 ** zoom
            ulx = max(0, int((extent.xMinimum() + self.layerDef.TSIZE1) / size))
            uly = max(0, int((self.layerDef.TSIZE1 - extent.yMaximum()) / size))
            lrx = min(int((extent.xMaximum() + self.layerDef.TSIZE1) / size), matrixSize - 1)
            lry = min(int((self.layerDef.TSIZE1 - extent.yMinimum()) / size), matrixSize - 1)

            # bounding box limit
            if self.layerDef.bbox:
                trange = self.layerDef.bboxDegreesToTileRange(zoom, self.layerDef.bbox)
                ulx = max(ulx, trange.xmin)
                uly = max(uly, trange.ymin)
                lrx = min(lrx, trange.xmax)
                lry = min(lry, trange.ymax)
                if lrx < ulx or lry < uly:
                    # tile range is out of the bounding box
                    return True

            # tile count limit
            tileCount = (lrx - ulx + 1) * (lry - uly + 1)
            if tileCount > self.MAX_TILE_COUNT:
                # as tile count is over the limit, decrease zoom level
                zoom -= 1

                # if the zoom level is less than the minimum, do not draw
                if zoom < self.layerDef.zmin:
                    msg = self.tr("Tile count is over limit ({0}, max={1})").format(tileCount, self.MAX_TILE_COUNT)
                    self.showBarMessage(msg, QgsMessageBar.WARNING, 4)
                    return True
                continue

            # zoom level has been determined
            break

        self.logT("TileLayer.draw: {0} {1} {2} {3} {4}".format(zoom, ulx, uly, lrx, lry))

        # save painter state
        painter.save()

        # set pen and font
        painter.setPen(Qt.black)
        font = QFont(painter.font())
        font.setPointSize(10)
        painter.setFont(font)

        if self.layerDef.serviceUrl[0] == ":":
            painter.setBrush(QBrush(Qt.NoBrush))
            self.drawDebugInfo(renderContext, zoom, ulx, uly, lrx, lry)
        else:
            # create Tiles class object and throw url into it
            tiles = Tiles(zoom, ulx, uly, lrx, lry, self.layerDef)
            urls = []
            cacheHits = 0
            for ty in range(uly, lry + 1):
                for tx in range(ulx, lrx + 1):
                    data = None
                    url = self.layerDef.tileUrl(zoom, tx, ty)
                    if self.tiles and zoom == self.tiles.zoom and url in self.tiles.tiles:
                        data = self.tiles.tiles[url].data
                    tiles.addTile(url, Tile(zoom, tx, ty, data))
                    if data is None:
                        urls.append(url)
                    elif data:  # memory cache exists
                        cacheHits += 1
                        # else:    # tile was not found (Downloader.NOT_FOUND=0)

            self.tiles = tiles
            if len(urls) > 0:
                # fetch tile data
                files = self.fetchFiles(urls)

                for url in list(files.keys()):
                    self.tiles.setImageData(url, files[url])

                if iface:
                    cacheHits += self.downloader.cacheHits
                    downloadedCount = self.downloader.fetchSuccesses - self.downloader.cacheHits
                    msg = self.tr("{0} files downloaded. {1} caches hit.").format(downloadedCount, cacheHits)
                    barmsg = None
                    if self.downloader.errorStatus != Downloader.NO_ERROR:
                        if self.downloader.errorStatus == Downloader.TIMEOUT_ERROR:
                            barmsg = self.tr("Download Timeout - {}").format(self.name())
                        else:
                            msg += self.tr(" {} files failed.").format(self.downloader.fetchErrors)
                            if self.downloader.fetchSuccesses == 0:
                                barmsg = self.tr("Failed to download all {0} files. - {1}").format(
                                    self.downloader.fetchErrors, self.name())
                    self.showStatusMessage(msg, 5000)
                    if barmsg:
                        self.showBarMessage(barmsg, QgsMessageBar.WARNING, 4)

            # apply layer style
            oldOpacity = painter.opacity()
            painter.setOpacity(0.01 * (100 - self.transparency))
            oldSmoothRenderHint = painter.testRenderHint(QPainter.SmoothPixmapTransform)
            if self.smoothRender:
                painter.setRenderHint(QPainter.SmoothPixmapTransform)

            # draw tiles
            if not renderContext.coordinateTransform():
                # no need to reproject tiles
                self.drawTiles(renderContext, self.tiles)
                # self.drawTilesDirectly(renderContext, self.tiles)
            else:
                # reproject tiles
                self.drawTilesOnTheFly(renderContext, self.tiles)

            # restore layer style
            painter.setOpacity(oldOpacity)
            if self.smoothRender:
                painter.setRenderHint(QPainter.SmoothPixmapTransform, oldSmoothRenderHint)

            # draw credit on the bottom right corner
            if self.creditVisibility and self.layerDef.credit:
                margin, paddingH, paddingV = (3, 4, 3)
                # scale
                scaleX, scaleY = self.getScaleToVisibleExtent(renderContext)
                scale = max(scaleX, scaleY)
                painter.scale(scale, scale)

                visibleSWidth = painter.viewport().width() * scaleX / scale
                visibleSHeight = painter.viewport().height() * scaleY / scale
                rect = QRect(0, 0, visibleSWidth - margin, visibleSHeight - margin)
                textRect = painter.boundingRect(rect, Qt.AlignBottom | Qt.AlignRight, self.layerDef.credit)
                bgRect = QRect(textRect.left() - paddingH, textRect.top() - paddingV, textRect.width() + 2 * paddingH,
                               textRect.height() + 2 * paddingV)
                painter.fillRect(bgRect, QColor(240, 240, 240, 150))  # 197, 234, 243, 150))
                painter.drawText(rect, Qt.AlignBottom | Qt.AlignRight, self.layerDef.credit)

        # restore painter state
        painter.restore()

        if isDpiEqualToCanvas:
            # save zoom level for printing (output with different dpi from map canvas)
            self.canvasLastZoom = zoom
        return True
    def draw(self, renderContext):

        self.renderContext = renderContext
        extent = renderContext.extent()
        if extent.isEmpty() or extent.width() == float("inf"):
            qDebug("Drawing is skipped because map extent is empty or inf.")
            return True

        mapSettings = self.iface.mapCanvas().mapSettings()
        painter = renderContext.painter()
        isDpiEqualToCanvas = painter.device().logicalDpiX(
        ) == mapSettings.outputDpi()
        if isDpiEqualToCanvas or not self.useLastZoomForPrint:
            # calculate zoom level
            tile_mpp1 = self.layerDef.tsize1 / self.layerDef.TILE_SIZE  # should be attribute, not method call..
            viewport_mpp = extent.width() / painter.viewport().width()
            lg = math.log(float(tile_mpp1) / float(viewport_mpp), 2)
            zoom = int(math.modf(
                lg)[1]) + 1 * (math.modf(lg)[0] > self.CHANGE_SCALE_VALUE) + 1
            zoom = max(0, min(zoom, self.layerDef.zmax))
            # zoom = max(self.layerDef.zmin, zoom)
        else:
            # for print composer output image, use last zoom level of map item on print composer (or map canvas)
            zoom = self.canvasLastZoom

        # zoom limit
        if zoom < self.layerDef.zmin:
            msg = self.tr(
                "Current zoom level ({0}) is smaller than zmin ({1}): {2}"
            ).format(zoom, self.layerDef.zmin, self.layerDef.title)
            self.emitShowBarMessage(msg, QGisMessageBarLevel.Info, 2)
            return True

        while True:
            # calculate tile range (yOrigin is top)
            size = self.layerDef.tsize1 / 2**(zoom - 1)
            if self.layerDef.tile_ranges is None:  # should add xOffset & yOffset in first part of conditional
                matrixSize = 2**zoom
                ulx = max(
                    0, int((extent.xMinimum() + self.layerDef.tsize1) / size))
                uly = max(
                    0, int((self.layerDef.tsize1 - extent.yMaximum()) / size))
                lrx = min(
                    int((extent.xMaximum() + self.layerDef.tsize1) / size),
                    matrixSize - 1)
                lry = min(
                    int((self.layerDef.tsize1 - extent.yMinimum()) / size),
                    matrixSize - 1)
            else:  # for tile_ranges
                xmin, xmax, ymin, ymax = self.layerDef.tile_ranges[zoom]

                ulx = max(
                    int((extent.xMinimum() - self.layerDef.originX) / size),
                    xmin)
                uly = max(
                    int((self.layerDef.originY - extent.yMaximum()) / size),
                    ymin)
                lrx = min(
                    int((extent.xMaximum() - self.layerDef.originX) / size),
                    xmax)
                lry = min(
                    int((self.layerDef.originY - extent.yMinimum()) / size),
                    ymax)

            # bounding box limit
            if self.layerDef.bbox:
                trange = self.layerDef.bboxDegreesToTileRange(
                    zoom, self.layerDef.bbox)
                ulx = max(ulx, trange.xmin)
                uly = max(uly, trange.ymin)
                lrx = min(lrx, trange.xmax)
                lry = min(lry, trange.ymax)
                if lrx < ulx or lry < uly:
                    # tile range is out of the bounding box
                    return True

            # tile count limit
            tileCount = (lrx - ulx + 1) * (lry - uly + 1)
            if tileCount > self.MAX_TILE_COUNT:
                # as tile count is over the limit, decrease zoom level
                zoom -= 1

                # if the zoom level is less than the minimum, do not draw
                if zoom < self.layerDef.zmin:
                    msg = self.tr(
                        "Tile count is over limit ({0}, max={1})").format(
                            tileCount, self.MAX_TILE_COUNT)
                    self.emitShowBarMessage(msg, QGisMessageBarLevel.Warning,
                                            4)
                    return True
                continue

            # zoom level has been determined
            break

        self.logT("TileLayer.draw: {0} {1} {2} {3} {4}".format(
            zoom, ulx, uly, lrx, lry))

        # save painter state
        painter.save()

        # set pen and font
        painter.setPen(Qt.black)
        font = QFont(painter.font())
        font.setPointSize(10)
        painter.setFont(font)

        if self.layerDef.serviceUrl[0] == ":":
            painter.setBrush(QBrush(Qt.NoBrush))
            self.drawDebugInfo(renderContext, zoom, ulx, uly, lrx, lry)
        else:
            # create Tiles class object and throw url into it
            tiles = Tiles(zoom, ulx, uly, lrx, lry, self.layerDef)
            urls = []
            cacheHits = 0
            for ty in range(uly, lry + 1):
                for tx in range(ulx, lrx + 1):
                    data = None
                    url = self.layerDef.tileUrl(zoom, tx, ty)
                    if self.tiles and zoom == self.tiles.zoom and url in self.tiles.tiles:
                        data = self.tiles.tiles[url].data
                    tiles.addTile(url, Tile(zoom, tx, ty, data))
                    if data is None:
                        urls.append(url)
                    elif data:  # memory cache exists
                        cacheHits += 1
                        # else:    # tile was not found (Downloader.NOT_FOUND=0)

            self.tiles = tiles
            if len(urls) > 0:
                # fetch tile data
                files = self.fetchFiles(urls)

                for url in files.keys():
                    self.tiles.setImageData(url, files[url])

                if self.iface:
                    cacheHits += self.downloader.cacheHits
                    downloadedCount = self.downloader.fetchSuccesses - self.downloader.cacheHits
                    msg = self.tr(
                        "{0} files downloaded. {1} cache hits.").format(
                            downloadedCount, cacheHits)
                    barmsg = None
                    if self.downloader.errorStatus != Downloader.NO_ERROR:
                        if self.downloader.errorStatus == Downloader.TIMEOUT_ERROR:
                            barmsg = self.tr("Download Timeout - {}").format(
                                self.name())
                        else:
                            msg += self.tr(" {} files failed.").format(
                                self.downloader.fetchErrors)
                            if self.downloader.fetchSuccesses == 0:
                                barmsg = self.tr(
                                    "Failed to download all {0} files. - {1}"
                                ).format(self.downloader.fetchErrors,
                                         self.name())
                    self.showStatusMessage(msg, 5000)
                    if barmsg:
                        self.emitShowBarMessage(barmsg,
                                                QGisMessageBarLevel.Warning, 4)

            # apply layer style
            oldOpacity = painter.opacity()
            painter.setOpacity(0.01 * (100 - self.transparency))
            oldSmoothRenderHint = painter.testRenderHint(
                QPainter.SmoothPixmapTransform)
            if self.smoothRender:
                painter.setRenderHint(QPainter.SmoothPixmapTransform)

            # draw tiles
            if not renderContext.coordinateTransform():
                # no need to reproject tiles
                self.drawTiles(renderContext, self.tiles)
                # self.drawTilesDirectly(renderContext, self.tiles)
            else:
                # reproject tiles
                self.drawTilesOnTheFly(renderContext, self.tiles)

            # restore layer style
            painter.setOpacity(oldOpacity)
            if self.smoothRender:
                painter.setRenderHint(QPainter.SmoothPixmapTransform,
                                      oldSmoothRenderHint)

            # draw credit on the bottom right corner
            if self.creditVisibility and self.layerDef.credit:
                margin, paddingH, paddingV = (3, 4, 3)
                # scale
                scaleX, scaleY = self.getScaleToVisibleExtent(renderContext)
                scale = max(scaleX, scaleY)
                painter.scale(scale, scale)

                visibleSWidth = painter.viewport().width() * scaleX / scale
                visibleSHeight = painter.viewport().height() * scaleY / scale
                rect = QRect(0, 0, visibleSWidth - margin,
                             visibleSHeight - margin)
                textRect = painter.boundingRect(rect,
                                                Qt.AlignBottom | Qt.AlignRight,
                                                self.layerDef.credit)
                bgRect = QRect(textRect.left() - paddingH,
                               textRect.top() - paddingV,
                               textRect.width() + 2 * paddingH,
                               textRect.height() + 2 * paddingV)
                painter.fillRect(bgRect, QColor(240, 240, 240,
                                                150))  # 197, 234, 243, 150))
                painter.drawText(rect, Qt.AlignBottom | Qt.AlignRight,
                                 self.layerDef.credit)

        # restore painter state
        painter.restore()

        if isDpiEqualToCanvas:
            # save zoom level for printing (output with different dpi from map canvas)
            self.canvasLastZoom = zoom
        return True
    def replyFinishedSlot(self):
        reply = self.sender()
        url = reply.request().url().toString()
        self.log("replyFinishedSlot: %s" % url)
        if not url in self.fetchedFiles:
            self.fetchedFiles[url] = None
        self.requestingUrls.remove(url)
        self.replies.remove(reply)
        isFromCache = 0
        httpStatusCode = reply.attribute(QNetworkRequest.HttpStatusCodeAttribute)
        if reply.error() == QNetworkReply.NoError:
            if httpStatusCode == 301:
                new_url = str(reply.rawHeader("Location"))
                self.addToQueue(new_url, url)
            else:
                self.fetchSuccesses += 1
                if reply.attribute(QNetworkRequest.SourceIsFromCacheAttribute):
                    self.cacheHits += 1
                    isFromCache = 1
                elif not reply.hasRawHeader("Cache-Control"):
                    cache = QgsNetworkAccessManager.instance().cache()
                    if cache:
                        metadata = cache.metaData(reply.request().url())
                        # self.log("Expiration date: " + metadata.expirationDate().toString().encode("utf-8"))
                        if metadata.expirationDate().isNull():
                            metadata.setExpirationDate(
                                QDateTime.currentDateTime().addSecs(self.default_cache_expiration * 60 * 60))
                            cache.updateMetaData(metadata)
                            self.log(
                                "Default expiration date has been set: %s (%d h)" % (url, self.default_cache_expiration))

                if reply.isReadable():
                    data = reply.readAll()
                    if self.redirected_urls.has_key(url):
                        url = self.redirected_urls[url]

                    self.fetchedFiles[url] = data
                else:
                    qDebug("http status code: " + str(httpStatusCode))

                # self.emit(SIGNAL('replyFinished(QString, int, int)'), url, reply.error(), isFromCache)
                self.replyFinished.emit(url, reply.error(), isFromCache)
        else:
            if self.sync and httpStatusCode == 404:
                self.fetchedFiles[url] = self.NOT_FOUND
            self.fetchErrors += 1
            if self.errorStatus == self.NO_ERROR:
                self.errorStatus = self.UNKNOWN_ERROR

        reply.deleteLater()

        if debug_mode:
            qDebug("queue: %d, requesting: %d" % (len(self.queue), len(self.requestingUrls)))

        if len(self.queue) + len(self.requestingUrls) == 0:
            # all replies have been received
            if self.sync:
                self.logT("eventLoop.quit()")
                self.eventLoop.quit()
            else:
                self.timer.stop()
        elif len(self.queue) > 0:
            # start fetching the next file
            self.fetchNext()
        self.log("replyFinishedSlot End: %s" % url)
 def logT(self, msg):
     if debug_mode:
         qDebug("%s: %s" % (str(threading.current_thread()), msg))
 def log(self, msg):
     if debug_mode:
         qDebug(msg)