Пример #1
0
  def setExtent(self, center, width, height, rotation=0):
    """ Set map extent to export settings.

    This is a convenience method to set map extent to export settings.
    Map settings should be set before this method is called.

    :param center: Center of the map extent in the map CRS.
    :type center: QgsPoint
    :param width: Width of the map extent in unit of the map CRS.
    :type width: float
    :param height: Height of the map extent in unit of the map CRS.
    :type height: float
    :param rotation: Rotation in degrees. Requires QGIS version 2.8 or later.
    :type rotation: float
    """
    if self.mapSettings is None:
      self.mapSettings = QgsMapSettings()

    if rotation:
      rect = RotatedRect(center, width, height, rotation)
      rect.toMapSettings(self.mapSettings)
    else:
      rect = QgsRectangle(center.x() - width / 2, center.y() - height / 2,
                          center.x() + width / 2, center.y() + height / 2)
      self.mapSettings.setExtent(rect)

    self.settings.setMapSettings(self.mapSettings)
Пример #2
0
    def setExtent(self, center, width, height, rotation=0):
        """ Set map extent to export settings.

    This is a convenience method to set map extent to export settings.
    Map settings should be set before this method is called.

    :param center: Center of the map extent in the map CRS.
    :type center: QgsPoint
    :param width: Width of the map extent in unit of the map CRS.
    :type width: float
    :param height: Height of the map extent in unit of the map CRS.
    :type height: float
    :param rotation: Rotation in degrees. Requires QGIS version 2.8 or later.
    :type rotation: float
    """
        if self.mapSettings is None:
            self.mapSettings = QgsMapSettings()

        if rotation:
            rect = RotatedRect(center, width, height, rotation)
            rect.toMapSettings(self.mapSettings)
        else:
            rect = QgsRectangle(center.x() - width / 2,
                                center.y() - height / 2,
                                center.x() + width / 2,
                                center.y() + height / 2)
            self.mapSettings.setExtent(rect)

        self.settings.setMapSettings(self.mapSettings)
Пример #3
0
    def updateQuads(self, v=None):
        isSimpleMode = self.radioButton_Simple.isChecked()
        if isSimpleMode:
            self.dialog.clearRubberBands()
            return

        apiChanged23 = QGis.QGIS_VERSION_INT >= 20300
        canvas = self.dialog.iface.mapCanvas()
        mapSettings = canvas.mapSettings(
        ) if apiChanged23 else canvas.mapRenderer()

        baseExtent = RotatedRect.fromMapSettings(mapSettings)
        p = {
            "lineEdit_centerX": self.lineEdit_centerX.text(),
            "lineEdit_centerY": self.lineEdit_centerY.text(),
            "lineEdit_rectWidth": self.lineEdit_rectWidth.text(),
            "lineEdit_rectHeight": self.lineEdit_rectHeight.text(),
            "spinBox_Height": self.spinBox_Height.value()
        }

        quadtree = createQuadTree(baseExtent, p)
        if quadtree:
            self.dialog.createRubberBands(baseExtent, quadtree)
            self.dialog.setWindowState(self.windowState() & ~Qt.WindowMinimized
                                       | Qt.WindowActive)
        else:
            self.dialog.clearRubberBands()
Пример #4
0
    def setMapSettings(self, settings):
        """settings: QgsMapSettings (QGIS >= 2.3) or QgsMapRenderer"""
        self.canvas = None
        self.mapSettings = settings

        self.baseExtent = RotatedRect.fromMapSettings(settings)
        self.crs = settings.destinationCrs()
Пример #5
0
  def __init__(self, settings, canvas, pluginManager, localBrowsingMode=True):
    self.data = settings
    self.timestamp = datetime.datetime.today().strftime("%Y%m%d%H%M%S")

    # output html file path
    htmlfilename = settings.get("OutputFilename")
    if not htmlfilename:
      htmlfilename = tools.temporaryOutputDir() + "/%s.html" % self.timestamp
    self.htmlfilename = htmlfilename
    self.path_root = os.path.splitext(htmlfilename)[0]
    self.htmlfiletitle = os.path.basename(self.path_root)
    self.title = self.htmlfiletitle

    # load configuration of the template
    self.templateName = settings.get("Template", "")
    templatePath = os.path.join(tools.templateDir(), self.templateName)
    self.templateConfig = tools.getTemplateConfig(templatePath)

    # MapTo3D object
    world = settings.get(ObjectTreeItem.ITEM_WORLD, {})
    baseSize = world.get("lineEdit_BaseSize", def_vals.baseSize)
    verticalExaggeration = world.get("lineEdit_zFactor", def_vals.zExaggeration)
    verticalShift = world.get("lineEdit_zShift", def_vals.zShift)
    self.mapTo3d = MapTo3D(canvas, float(baseSize), float(verticalExaggeration), float(verticalShift))

    self.coordsInWGS84 = world.get("radioButton_WGS84", False)

    self.canvas = canvas
    self.mapSettings = canvas.mapSettings() if apiChanged23 else canvas.mapRenderer()
    self.baseExtent = RotatedRect.fromMapSettings(self.mapSettings)

    self.pluginManager = pluginManager
    self.localBrowsingMode = localBrowsingMode

    self.crs = self.mapSettings.destinationCrs()

    wgs84 = QgsCoordinateReferenceSystem(4326)
    transform = QgsCoordinateTransform(self.crs, wgs84)
    self.wgs84Center = transform.transform(self.baseExtent.center())

    controls = settings.get(ObjectTreeItem.ITEM_CONTROLS, {})
    self.controls = controls.get("comboBox_Controls")
    if not self.controls:
      self.controls = QSettings().value("/Qgis2threejs/lastControls", "OrbitControls.js", type=unicode)

    self.demProvider = None
    self.quadtree = None

    if self.templateConfig.get("type") == "sphere":
      self.exportMode = ExportSettings.SPHERE
      return

    demProperties = settings.get(ObjectTreeItem.ITEM_DEM, {})
    self.demProvider = self.demProviderByLayerId(demProperties["comboBox_DEMLayer"])

    if demProperties.get("radioButton_Simple", False):
      self.exportMode = ExportSettings.PLAIN_SIMPLE
    else:
      self.exportMode = ExportSettings.PLAIN_MULTI_RES
      self.quadtree = createQuadTree(self.baseExtent, demProperties)
Пример #6
0
  def setMapSettings(self, settings):
    """settings: QgsMapSettings (QGIS >= 2.3) or QgsMapRenderer"""
    self.canvas = None
    self.mapSettings = settings

    self.baseExtent = RotatedRect.fromMapSettings(settings)
    self.crs = settings.destinationCrs()
Пример #7
0
    def _rect(self, startPoint, endPoint):
        if startPoint is None or endPoint is None:
            return None

        p0 = self.toCanvasCoordinates(startPoint)
        p1 = self.toCanvasCoordinates(endPoint)
        canvas_rect = QgsRectangle(QgsPoint(p0.x(), p0.y()),
                                   QgsPoint(p1.x(), p1.y()))
        center = QgsPoint((startPoint.x() + endPoint.x()) / 2,
                          (startPoint.y() + endPoint.y()) / 2)
        return RotatedRect(center, self.mupp * canvas_rect.width(),
                           self.mupp * canvas_rect.height()).rotate(
                               self.rotation, center)
Пример #8
0
  def __init__(self, mapSettings, planeWidth=100, verticalExaggeration=1, verticalShift=0):
    # map canvas
    self.rotation = mapSettings.rotation() if QGis.QGIS_VERSION_INT >= 20700 else 0
    self.mapExtent = RotatedRect.fromMapSettings(mapSettings)

    # 3d
    canvas_size = mapSettings.outputSize()
    self.planeWidth = planeWidth
    self.planeHeight = planeWidth * canvas_size.height() / float(canvas_size.width())

    self.verticalExaggeration = verticalExaggeration
    self.verticalShift = verticalShift

    self.multiplier = planeWidth / self.mapExtent.width()
    self.multiplierZ = self.multiplier * verticalExaggeration
Пример #9
0
    def __init__(self,
                 mapSettings,
                 planeWidth=100,
                 verticalExaggeration=1,
                 verticalShift=0):
        # map canvas
        self.rotation = mapSettings.rotation(
        ) if QGis.QGIS_VERSION_INT >= 20700 else 0
        self.mapExtent = RotatedRect.fromMapSettings(mapSettings)

        # 3d
        canvas_size = mapSettings.outputSize()
        self.planeWidth = planeWidth
        self.planeHeight = planeWidth * canvas_size.height() / float(
            canvas_size.width())

        self.verticalExaggeration = verticalExaggeration
        self.verticalShift = verticalShift

        self.multiplier = planeWidth / self.mapExtent.width()
        self.multiplierZ = self.multiplier * verticalExaggeration
Пример #10
0
  def updateQuads(self, v=None):
    isSimpleMode = self.radioButton_Simple.isChecked()
    if isSimpleMode:
      self.dialog.clearRubberBands()
      return

    apiChanged23 = QGis.QGIS_VERSION_INT >= 20300
    canvas = self.dialog.iface.mapCanvas()
    mapSettings = canvas.mapSettings() if apiChanged23 else canvas.mapRenderer()

    baseExtent = RotatedRect.fromMapSettings(mapSettings)
    p = {"lineEdit_centerX": self.lineEdit_centerX.text(),
         "lineEdit_centerY": self.lineEdit_centerY.text(),
         "lineEdit_rectWidth": self.lineEdit_rectWidth.text(),
         "lineEdit_rectHeight": self.lineEdit_rectHeight.text(),
         "spinBox_Height": self.spinBox_Height.value()}

    quadtree = createQuadTree(baseExtent, p)
    if quadtree:
      self.dialog.createRubberBands(baseExtent, quadtree)
      self.dialog.setWindowState(self.windowState() & ~Qt.WindowMinimized | Qt.WindowActive)
    else:
      self.dialog.clearRubberBands()
Пример #11
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
Пример #12
0
def writeSurroundingDEM(writer, layer, warp_dem, stats, properties, progress=None):
  settings = writer.settings
  mapSettings = settings.mapSettings
  mapTo3d = settings.mapTo3d
  baseExtent = settings.baseExtent
  progress = progress or dummyProgress

  # options
  size = properties["spinBox_Size"]
  roughening = properties["spinBox_Roughening"]
  transparency = properties["spinBox_demtransp"]
  transp_background = properties.get("checkBox_TransparentBackground", False)

  prop = DEMPropertyReader(properties)
  dem_width = (prop.width() - 1) / roughening + 1
  dem_height = (prop.height() - 1) / roughening + 1

  # texture image size
  canvas_size = mapSettings.outputSize()
  hpw = float(canvas_size.height()) / canvas_size.width()
  if hpw < 1:
    image_width = settings.image_basesize
    image_height = round(image_width * hpw)
    #image_height = settings.image_basesize * max(1, int(round(1 / hpw)))    # not rendered expectedly
  else:
    image_height = settings.image_basesize
    image_width = round(image_height / hpw)

  center = baseExtent.center()
  rotation = baseExtent.rotation()

  size2 = size * size
  for i in range(size2):
    progress(20 * i / size2 + 10)
    if i == (size2 - 1) / 2:    # center (map canvas)
      continue

    # block extent
    sx = i % size - (size - 1) / 2
    sy = i / size - (size - 1) / 2
    block_center = QgsPoint(center.x() + sx * baseExtent.width(), center.y() + sy * baseExtent.height())
    extent = RotatedRect(block_center, baseExtent.width(), baseExtent.height()).rotate(rotation, center)

    # warp dem
    dem_values = warp_dem.read(dem_width, dem_height, extent.geotransform(dem_width, dem_height))

    if stats is None:
      stats = {"max": max(dem_values), "min": min(dem_values)}
    else:
      stats["max"] = max(max(dem_values), stats["max"])
      stats["min"] = min(min(dem_values), stats["min"])

    # shift and scale
    if mapTo3d.verticalShift != 0:
      dem_values = map(lambda x: x + mapTo3d.verticalShift, dem_values)
    if mapTo3d.multiplierZ != 1:
      dem_values = map(lambda x: x * mapTo3d.multiplierZ, dem_values)

    # generate javascript data file
    # dem block
    block = {"width": dem_width, "height": dem_height}
    block["plane"] = {"width": mapTo3d.planeWidth, "height": mapTo3d.planeHeight,
                      "offsetX": mapTo3d.planeWidth * sx, "offsetY": mapTo3d.planeHeight * sy}

    # display type
    if properties.get("radioButton_MapCanvas", False):
      block["m"] = layer.materialManager.getMapImageIndex(image_width, image_height, extent, transparency, transp_background)

    elif properties.get("radioButton_LayerImage", False):
      layerids = properties.get("layerImageIds", [])
      block["m"] = layer.materialManager.getLayerImageIndex(layerids, image_width, image_height, extent, transparency, transp_background)

    elif properties.get("radioButton_SolidColor", False):
      block["m"] = layer.materialManager.getMeshLambertIndex(properties["lineEdit_Color"], transparency, True)

    # write block
    writer.write("bl = lyr.addBlock({0});\n".format(pyobj2js(block)))
    writer.write("bl.data = [{0}];\n".format(",".join(map(gdal2threejs.formatValue, dem_values))))
Пример #13
0
def surroundingDEMBlocks(writer, layer, provider, properties, progress=None):
    settings = writer.settings
    canvas_size = settings.mapSettings.outputSize()
    mapTo3d = settings.mapTo3d()
    baseExtent = settings.baseExtent
    progress = progress or dummyProgress

    # options
    size = properties["spinBox_Size"]
    roughening = properties["spinBox_Roughening"]
    texture_scale = properties.get("comboBox_TextureSize", 100) / 100
    transparency = properties.get("spinBox_demtransp", 0)
    transp_background = properties.get("checkBox_TransparentBackground", False)

    prop = DEMPropertyReader(properties)
    dem_size = prop.demSize(canvas_size)
    dem_width = (dem_size.width() - 1) / roughening + 1
    dem_height = (dem_size.height() - 1) / roughening + 1

    # texture size
    image_width = canvas_size.width() * texture_scale
    image_height = canvas_size.height() * texture_scale

    center = baseExtent.center()
    rotation = baseExtent.rotation()

    blocks = []
    size2 = size * size
    for i in range(size2):
        progress(20 * i / size2 + 10)
        if i == (size2 - 1) / 2:  # center (map canvas)
            continue

        # block extent
        sx = i % size - (size - 1) / 2
        sy = i / size - (size - 1) / 2
        block_center = QgsPoint(center.x() + sx * baseExtent.width(),
                                center.y() + sy * baseExtent.height())
        extent = RotatedRect(block_center, baseExtent.width(),
                             baseExtent.height()).rotate(rotation, center)

        # display type
        if properties.get("radioButton_MapCanvas", False):
            mat = layer.materialManager.getMapImageIndex(
                image_width, image_height, extent, transparency,
                transp_background)

        elif properties.get("radioButton_LayerImage", False):
            layerids = properties.get("layerImageIds", [])
            mat = layer.materialManager.getLayerImageIndex(
                layerids, image_width, image_height, extent, transparency,
                transp_background)

        else:  #.get("radioButton_SolidColor", False)
            mat = layer.materialManager.getMeshLambertIndex(
                properties.get("lineEdit_Color", ""), transparency, True)

        # DEM block
        dem_values = provider.read(dem_width, dem_height, extent)
        planeWidth, planeHeight = mapTo3d.planeWidth, mapTo3d.planeHeight
        offsetX, offsetY = planeWidth * sx, planeHeight * sy

        block = DEMBlock(dem_width, dem_height, dem_values, planeWidth,
                         planeHeight, offsetX, offsetY)
        block.zShift(mapTo3d.verticalShift)
        block.zScale(mapTo3d.multiplierZ)
        block.set("m", mat)

        blocks.append(block)

    return blocks
Пример #14
0
    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