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)
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()
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()
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)
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)
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
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
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()
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 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))))
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
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