def draw(self, 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 map2pixel = renderContext.mapToPixel() mupp = map2pixel.mapUnitsPerPixel() rotation = map2pixel.mapRotation() painter = renderContext.painter() viewport = painter.viewport() isWebMercator = True transform = renderContext.coordinateTransform() if transform: isWebMercator = transform.destCRS().postgisSrid() == 3857 # frame layer isn't drawn if the CRS is not web mercator or map is rotated if self.layerDef.serviceUrl[0] == ":" and "frame" in self.layerDef.serviceUrl: # or "number" in self.layerDef.serviceUrl: msg = "" if not isWebMercator: msg = self.tr("Frame layer is not drawn if the CRS is not EPSG:3857") elif rotation: msg = self.tr("Frame layer is not drawn if map is rotated") if msg: self.showMessageBar(msg, QgsMessageBar.INFO, 2) return True if not isWebMercator: # get extent in project CRS cx, cy = 0.5 * viewport.width(), 0.5 * viewport.height() center = map2pixel.toMapCoordinatesF(cx, cy) mapExtent = RotatedRect(center, mupp * viewport.width(), mupp * viewport.height(), rotation) if transform: transform = QgsCoordinateTransform(transform.destCRS(), transform.sourceCrs()) geometry = QgsGeometry.fromPolyline([map2pixel.toMapCoordinatesF(cx - 0.5, cy), map2pixel.toMapCoordinatesF(cx + 0.5, cy)]) geometry.transform(transform) mupp = geometry.length() # get bounding box of the extent in EPSG:3857 geometry = mapExtent.geometry() geometry.transform(transform) extent = geometry.boundingBox() else: qDebug("Drawing is skipped because CRS transformation is not ready.") return True elif rotation: # get bounding box of the extent mapExtent = RotatedRect(extent.center(), mupp * viewport.width(), mupp * viewport.height(), rotation) extent = mapExtent.boundingBox() # calculate zoom level tile_mpp1 = self.layerDef.TSIZE1 / self.layerDef.TILE_SIZE zoom = int(math.ceil(math.log(tile_mpp1 / mupp, 2) + 1)) zoom = max(0, min(zoom, self.layerDef.zmax)) #zoom = max(self.layerDef.zmin, zoom) # zoom limit if zoom < self.layerDef.zmin: if self.plugin.navigationMessagesEnabled: msg = self.tr("Current zoom level ({0}) is smaller than zmin ({1}): {2}").format(zoom, self.layerDef.zmin, self.layerDef.title) self.showMessageBar(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.showMessageBar(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 a Tiles object and a list of urls to fetch tile image data tiles = Tiles(zoom, ulx, uly, lrx, lry, self.layerDef) urls = [] cachedTiles = self.tiles 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 cachedTiles and zoom == cachedTiles.zoom and url in cachedTiles.tiles: data = cachedTiles.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 not found self.tiles = tiles if len(urls) > 0: # fetch tile data files = self.fetchFiles(urls, renderContext) for url, data in files.items(): tiles.setImageData(url, data) if self.iface: stats = self.downloader.stats() allCacheHits = cacheHits + stats["cacheHits"] msg = self.tr("{0} files downloaded. {1} caches hit.").format(stats["downloaded"], allCacheHits) barmsg = None if self.downloader.errorStatus != Downloader.NO_ERROR: if self.downloader.errorStatus == Downloader.TIMEOUT_ERROR: barmsg = self.tr("Download Timeout - {0}").format(self.name()) elif stats["errors"] > 0: msg += self.tr(" {0} files failed.").format(stats["errors"]) if stats["successed"] + allCacheHits == 0: barmsg = self.tr("Failed to download all {0} files. - {1}").format(stats["errors"], self.name()) self.showStatusMessage(msg, 5000) if barmsg: self.showMessageBar(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) # do not start drawing tiles if rendering has been stopped if renderContext.renderingStopped(): self.log("draw(): renderingStopped!") painter.restore() return True # draw tiles if isWebMercator and rotation == 0: self.drawTiles(renderContext, tiles) # self.drawTilesDirectly(renderContext, tiles) else: # reproject tiles self.drawTilesOnTheFly(renderContext, mapExtent, tiles) # restore old state painter.setOpacity(oldOpacity) if self.smoothRender: painter.setRenderHint(QPainter.SmoothPixmapTransform, oldSmoothRenderHint) # draw credit on the bottom right corner if self.creditVisibility and self.layerDef.attribution: 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.attribution) bgRect = QRect(textRect.left() - paddingH, textRect.top() - paddingV, textRect.width() + 2 * paddingH, textRect.height() + 2 * paddingV) painter.fillRect(bgRect, QColor(240, 240, 240, 150)) painter.drawText(rect, Qt.AlignBottom | Qt.AlignRight, self.layerDef.attribution) # restore painter state painter.restore() return True
def draw(self, renderContext): self.renderContext = renderContext painter = renderContext.painter() viewport = painter.viewport() extent = renderContext.extent() if extent.isEmpty() or extent.width() == float("inf"): qDebug("Drawing is skipped because map extent is empty or inf.") return True map2pixel = renderContext.mapToPixel() mupp = map2pixel.mapUnitsPerPixel() rotation = map2pixel.mapRotation() if self.plugin.apiChanged27 else 0.0 mpp = mupp # meters per pixel if rotation != 0.0: # get bounding box of the extent mapExtent = RotatedRect(extent.center(), mupp * viewport.width(), mupp * viewport.height(), rotation) extent = mapExtent.boundingBox() # calculate zoom level tile_mpp1 = self.layerDef.PSEUDO_MERCATOR_MAX_COORD / self.layerDef.TILE_SIZE self.zoom = int(math.ceil(math.log(tile_mpp1 / mpp, 2) + 1)) self.zoom = max(0, min(self.zoom, self.layerDef.zmax)) #if current zoom is not in layer range then bail if not self.zoom_is_in_range(): return True self.calculate_tile_range(extent) #if tile range is not good if not self.tile_range.valid: return True if not self.tilecount_is_in_range(): return True self.tile_manager.update(self.zoom, self.tile_range) tiles = self.tile_manager.get_tiles() # apply layer style # save painter state painter.save() 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 rotation == 0.0: # no need to reproject tiles self.render_tiles(renderContext, tiles) else: # reproject tiles cx, cy = 0.5 * viewport.width(), 0.5 * viewport.height() center = map2pixel.toMapCoordinatesF(cx, cy) mapExtent = RotatedRect(center, mupp * viewport.width(), mupp * viewport.height(), rotation) self.render_tiles_warp_on_the_fly(renderContext, mapExtent, tiles) # restore layer style painter.setOpacity(oldOpacity) if self.smoothRender: painter.setRenderHint(QPainter.SmoothPixmapTransform, oldSmoothRenderHint) # restore painter state painter.restore() return True