Ejemplo n.º 1
0
  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