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]
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))
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))
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))
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))
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]))
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_", ""))
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
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))
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))
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))
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
def javaScriptConsoleMessage(self, message, lineNumber, sourceID): # For debugging purposes logEntry = "%s[%d]: %s" % (sourceID, lineNumber, message) qDebug(logEntry) '''
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)