Пример #1
0
    def updatecanvas(self, canvas):
        """
        Update the canvas object for the legend background.
        """
        if self._lastextent == canvas.extent():
            return

        self._lastextent = canvas.extent()
        if QGis.QGIS_VERSION_INT > 20200:
            from qgis.core import QgsMapRendererParallelJob, QgsMapSettings
            settings = canvas.mapSettings()
            extent = settings.extent()
            settings.setOutputSize(self.size())
            settings.setExtent(extent)
            #settings.setFlags(QgsMapSettings.Antialiasing | QgsMapSettings.DrawLabeling )
            self.renderjob = QgsMapRendererParallelJob(settings)
            self.renderjob.finished.connect(self._renderimage)
            self.renderjob.start()
        else:
            if canvas.isDrawing():
                return

            pixmap = QPixmap(self.size())
            pixmap.fill(canvas.canvasColor())
            painter = QPainter(pixmap)
            painter.setRenderHints(QPainter.Antialiasing)
            renderer = canvas.mapRenderer()
            renderer.render(painter)
            del painter
            self.canvasimage = pixmap.toImage()
            self.update()
Пример #2
0
class LegendWidget(Ui_legendsWidget, QWidget):
    showmap = pyqtSignal()

    def __init__(self, parent=None):
        super(LegendWidget, self).__init__(parent)
        self.setupUi(self)

        self.previewImage.mousePressEvent = self.previewImagePressEvent
        self.previewImage.resizeEvent = self.update
        self.btnExpand.pressed.connect(self.layerTree.expandAllNodes)
        self.btnCollapse.pressed.connect(self.layerTree.collapseAllNodes)
        self.canvas = None

    def init(self, canvas):
        self.canvas = canvas
        self.canvas.extentsChanged.connect(self.update_legend_map_data)

    def update_legend_map_data(self):
        if not self.layerTree.layerTreeModel():
            return
        self.layerTree.layerTreeModel().setLegendMapViewData(
            self.canvas.mapUnitsPerPixel(), self.canvas.mapSettings().outputDpi(), self.canvas.scale())

    def previewImagePressEvent(self, event):
        self.showmap.emit()

    def showEvent(self, showevent):
        self.canvas.renderStarting.connect(self.update)
        self.update()

    def hideEvent(self, hideevent):
        self.canvas.renderStarting.disconnect(self.update)

    def setRoot(self, root):
        model = QgsLayerTreeModel(root, self)
        model.setFlag(QgsLayerTreeModel.AllowNodeChangeVisibility)
        model.setFlag(QgsLayerTreeModel.ShowLegendAsTree)
        font = QFont()
        font.setPointSize(20)
        model.setLayerTreeNodeFont(QgsLayerTreeNode.NodeLayer, font)
        model.setLayerTreeNodeFont(QgsLayerTreeNode.NodeGroup, font)
        self.layerTree.setModel(model)

        for layer_node in model.rootGroup().findLayers():
            layer = layer_node.layer()
            if layer.type() == QgsMapLayer.VectorLayer:
                if layer.geometryType() == QgsWkbTypes.NullGeometry:
                    parent = layer_node.parent()
                    parent.removeLayer(layer)

    def _renderimage(self):
        image = self.renderjob.renderedImage()
        self.previewImage.setPixmap(QPixmap.fromImage(image))

    def update(self, *__args):
        settings = self.canvas.mapSettings()
        settings.setOutputSize(self.previewImage.size())
        self.renderjob = QgsMapRendererParallelJob(settings)
        self.renderjob.finished.connect(self._renderimage)
        self.renderjob.start()
Пример #3
0
    def getmap(self):
        if self.canvas:
            settings = self.canvas.mapSettings()
            layers = settings.layers()
            if GPS.isConnected:
                try:
                    gpslayer = QgsMapLayerRegistry.instance().mapLayersByName(
                        "__gps_layer")[0]
                except IndexError:
                    gpslayer = QgsVectorLayer("Point", "__gps_layer", "memory")
                    symbol = QgsMarkerSymbolV2.createSimple({
                        'name': 'circle',
                        'color': 'blue',
                        "size": '5'
                    })
                    gpslayer.rendererV2().setSymbol(symbol)
                    QgsMapLayerRegistry.instance().addMapLayer(gpslayer, False)

                layers.append(gpslayer.id())
                settings.setLayers(layers)

                map_pos = QgsPoint(GPS.gpsinfo("longitude"),
                                   GPS.gpsinfo("latitude"))
                # map_pos = QgsPoint(115.72589,-32.29597)
                geom = QgsGeometry.fromPoint(map_pos)
                feature = QgsFeature()
                feature.setGeometry(geom)
                gpslayer.startEditing()
                gpslayer.addFeature(feature)
                # gpslayer.commitChanges()

            self.renderjob = QgsMapRendererParallelJob(settings)
            self.renderjob.finished.connect(self.rendermap)
            self.renderjob.start()
    def createImageParams():
      def finished():
        def addParams():
          def getJsonImages():
            vmap = {}
            for l in self.paramProcess['layerImages']:
              vmap[ l.name() ] = l.source()
            return json.dumps( vmap )

          e =  self.paramProcess['canvas'].extent()
          imgWidth, imgHeight = image.width(), image.height()
          resX, resY = e.width() / imgWidth, e.height() / imgHeight
          self.paramProcess['json_images'] = getJsonImages()
          self.paramProcess['crs_map'] = self.paramProcess['canvas'].mapSettings().destinationCrs().authid()
          self.paramProcess['extent_map'] = e.asWktCoordinates() # xMin, yMin, xMax, yMax
          self.paramProcess['res'] = { 'X': resX, 'Y': resY } 

        image = job.renderedImage()
        if bool( self.paramProcess['canvas'].property('retro') ):
          image = image.scaled( image.width() / 3, image.height() / 3 )
          image = image.convertToFormat( QImage.Format_Indexed8, Qt.OrderedDither | Qt.OrderedAlphaDither )
        image.save( self.paramProcess['pathfileImage'], "TIFF", 100 ) # 100: Uncompressed
        addParams()

      settings = QgsMapSettings( self.paramProcess['canvas'].mapSettings() )
      settings.setBackgroundColor( QColor( Qt.transparent ) )
      
      layers = self.paramProcess['layerImages']
      if 'layerPolygon' in self.paramProcess:
        layers = [ self.paramProcess['layerPolygon'] ] + layers
      settings.setLayers( layers )
      job = QgsMapRendererParallelJob( settings ) 
      job.start()
      job.finished.connect( finished) 
      job.waitForFinished()
Пример #5
0
  def setMap(self):
    def finished():
      super(SwipeMap, self).setContent( job.renderedImage(), self.canvas.extent() )

    settings = QgsMapSettings( self.canvas.mapSettings() )
    settings.setLayers( self.layers )
    
    job = QgsMapRendererParallelJob( settings) 
    job.start()
    job.finished.connect( finished) 
    job.waitForFinished()
Пример #6
0
def export_data(layer: QgsVectorLayer, timezone_ids: List[str], timezone_colors: List[str],
                path: Path, image_height: int) -> None:
    """Saves the image and mapping file

    Args:
        layer (QgsVectorLayer): The layer to save
        timezone_ids (List[str]): A list of all timezone ids
        timezone_colors (List[str]): A list of all timezone colors
        path (Path): The folder to save the data to
        image_height (int): The height of the image to save
    """

    path.mkdir(parents=True, exist_ok=True)

    # We write the JSON dataset by hand, so that the order of all key -> value mappings inside the
    # file is consistent. Using JSON functions, the dictionaray would be written to the file in a
    # random order, making content versioning hard as the file would completely change each time
    # is generated.
    json_file = (path / "timezones.json").resolve()
    print(f"Saving mappings JSON file to: {json_file.absolute()}")
    with open(json_file, "w") as f:
        f.write("{\n")
        last = len(timezone_ids)
        for i in range(0, last):
            f.write("\"{}\": \"{}\"".format(timezone_colors[i],  timezone_ids[i]))
            if i < last - 1:
                f.write(",")
            f.write("\n")
        f.write("}\n")

    png_file = (path / "timezones.png").resolve()
    print(f"Saving PNG map to: {png_file.absolute()}")
    settings = QgsMapSettings()
    settings.setLayers([layer])
    settings.setBackgroundColor(QColor(255, 255, 255))
    settings.setOutputSize(QSize(image_height * 2, image_height))
    settings.setExtent(layer.extent())
    # Turn antialiasing off
    settings.setFlag(1, False)

    def finished() -> None:
        """Function to save the rendered map once it is done rendering"""
        img = render.renderedImage()
        img.save(str(png_file), "png")

    render = QgsMapRendererParallelJob(settings)
    render.finished.connect(finished)
    render.start()

    # This ensures that the program doesn't exit before the image is saved
    loop = QEventLoop()
    render.finished.connect(loop.quit)
    loop.exec_()
Пример #7
0
class LegendWidget(Ui_legendsWidget, QWidget):
    showmap = pyqtSignal()

    def __init__(self, parent=None):
        super(LegendWidget, self).__init__(parent)
        self.setupUi(self)
        self.canvasimage = QImage()
        self.items = {}
        self.framerect = QRect()

        self.legendareabrush = QBrush(QColor(255, 255, 255, 200))
        self.legendareapen = QPen(QColor(255, 255, 255, 20))
        self.legendareapen.setWidth(0.5)
        self.previewImage.mousePressEvent = self.previewImagePressEvent
        self.previewImage.resizeEvent = self.update
        self.btnExpand.pressed.connect(self.layerTree.expandAllNodes)
        self.btnCollapse.pressed.connect(self.layerTree.collapseAllNodes)
        self.canvas = None

    def init(self, canvas):
        self.canvas = canvas

    def previewImagePressEvent(self, event):
        self.showmap.emit()

    def showEvent(self, showevent):
        self.canvas.renderStarting.connect(self.update)
        self.update()

    def hideEvent(self, hideevent):
        self.canvas.renderStarting.disconnect(self.update)

    def setRoot(self, root):
        model = QgsLayerTreeModel(root, self)
        model.setFlag(QgsLayerTreeModel.AllowNodeChangeVisibility)
        model.setFlag(QgsLayerTreeModel.ShowLegendAsTree)
        font = QFont()
        font.setPointSize(20)
        model.setLayerTreeNodeFont(QgsLayerTreeNode.NodeLayer, font)
        model.setLayerTreeNodeFont(QgsLayerTreeNode.NodeGroup, font)
        self.layerTree.setModel(model)

    def _renderimage(self):
        image = self.renderjob.renderedImage()
        self.previewImage.setPixmap(QPixmap.fromImage(image))

    def update(self, *__args):
        settings = self.canvas.mapSettings()
        settings.setOutputSize(self.previewImage.size())
        self.renderjob = QgsMapRendererParallelJob(settings)
        self.renderjob.finished.connect(self._renderimage)
        self.renderjob.start()
Пример #8
0
def setupEnviroment(rasterTileLayer):
    bb = rasterTileLayer.extent()
    canvas.setExtent(bb)
    canvas.refresh()
    canvas.zoomToFullExtent()
    canvas.refreshAllLayers()
    vlayer = rasterTileLayer
    settings = QgsMapSettings()
    settings.setLayers([vlayer])
    settings.setBackgroundColor(QColor(255, 255, 255))
    settings.setOutputSize(QSize(256, 256))
    settings.setExtent(vlayer.extent())
    render = QgsMapRendererParallelJob(settings)
    render.start()
    return canvas
Пример #9
0
 def getmap(self):
     if self.canvas:
         if QGis.QGIS_VERSION_INT > 20200:
             from qgis.core import QgsMapRendererParallelJob
             self.renderjob = QgsMapRendererParallelJob(self.canvas.mapSettings())
             self.renderjob.finished.connect(self.rendermap)
             self.renderjob.start()
         else:
             pixmap = QPixmap(self.canvas.size())
             pixmap.fill(self.canvas.canvasColor())
             painter = QPainter(pixmap)
             renderer = self.canvas.mapRenderer()
             renderer.render(painter)
             del painter
             self.scribbleArea.addMap(pixmap)
Пример #10
0
    def getmap(self):
        if self.canvas:
            settings = self.canvas.mapSettings()
            layers = settings.layers()
            if GPS.isConnected:
                try:
                    gpslayer = QgsMapLayerRegistry.instance().mapLayersByName("__gps_layer")[0]
                except IndexError:
                    gpslayer = QgsVectorLayer("Point", "__gps_layer", "memory")
                    symbol = QgsMarkerSymbolV2.createSimple({'name': 'circle', 'color': 'blue', "size": '5'})
                    gpslayer.rendererV2().setSymbol(symbol)
                    QgsMapLayerRegistry.instance().addMapLayer(gpslayer, False)

                layers.append(gpslayer.id())
                settings.setLayers(layers)

                map_pos = QgsPoint(GPS.gpsinfo("longitude"), GPS.gpsinfo("latitude"))
                # map_pos = QgsPoint(115.72589,-32.29597)
                geom = QgsGeometry.fromPoint(map_pos)
                feature = QgsFeature()
                feature.setGeometry(geom)
                gpslayer.startEditing()
                gpslayer.addFeature(feature)
                # gpslayer.commitChanges()

            self.renderjob = QgsMapRendererParallelJob(settings)
            self.renderjob.finished.connect(self.rendermap)
            self.renderjob.start()
Пример #11
0
    def updatecanvas(self, canvas):
        """
        Update the canvas object for the legend background.
        """
        if self._lastextent == canvas.extent():
            return

        self._lastextent = canvas.extent()
        if QGis.QGIS_VERSION_INT > 20200:
            from qgis.core import QgsMapRendererParallelJob, QgsMapSettings
            settings = canvas.mapSettings()
            extent = settings.extent()
            settings.setOutputSize(self.size())
            settings.setExtent(extent)
            #settings.setFlags(QgsMapSettings.Antialiasing | QgsMapSettings.DrawLabeling )
            self.renderjob = QgsMapRendererParallelJob(settings)
            self.renderjob.finished.connect(self._renderimage)
            self.renderjob.start()
        else:
            if canvas.isDrawing():
                return

            pixmap = QPixmap(self.size())
            pixmap.fill(canvas.canvasColor())
            painter = QPainter(pixmap)
            painter.setRenderHints(QPainter.Antialiasing)
            renderer = canvas.mapRenderer()
            renderer.render(painter)
            del painter
            self.canvasimage = pixmap.toImage()
            self.update()
def render_layer():

    projectpath = Path('./data/france_parts.qgs')

    prj = QgsProject()
    prj.read(str(projectpath.absolute()))

    layers = layers = prj.mapLayersByName('france_parts')

    xt = layers[0].extent()
    width = 1200
    height = int(width * xt.height() / xt.width())

    options = QgsMapSettings()
    options.setLayers(layers)
    options.setBackgroundColor(QColor(255, 255, 255))
    options.setOutputSize(QSize(width, height))
    options.setExtent(xt)

    render = QgsMapRendererParallelJob(options)
    render.start()
    render.waitForFinished()

    image = render.renderedImage()

    return image
Пример #13
0
def render(name, settings):
    settings.setOutputSize(IMAGE_SIZE)
    job = QgsMapRendererParallelJob(settings)
    #job = QgsMapRendererSequentialJob(settings)
    job.start()
    job.waitForFinished()
    image = job.renderedImage()
    if not os.path.exists(image_path):
        os.mkdir(image_path)
    image.save(os.path.join(image_path, name + '.png'))
    return job.renderingTime()
Пример #14
0
    def test_render_via_job(self):
        """
        Test rendering an annotation layer via a map render job
        """
        layer = QgsAnnotationLayer('test', QgsAnnotationLayer.LayerOptions(QgsProject.instance().transformContext()))
        self.assertTrue(layer.isValid())

        item = QgsAnnotationPolygonItem(
            QgsPolygon(QgsLineString([QgsPoint(11.5, 13), QgsPoint(12, 13), QgsPoint(12, 13.5), QgsPoint(11.5, 13)])))
        item.setSymbol(
            QgsFillSymbol.createSimple({'color': '200,100,100', 'outline_color': 'black', 'outline_width': '2'}))
        item.setZIndex(1)
        i1_id = layer.addItem(item)

        item = QgsAnnotationLineItem(QgsLineString([QgsPoint(11, 13), QgsPoint(12, 13), QgsPoint(12, 15)]))
        item.setSymbol(QgsLineSymbol.createSimple({'color': '#ffff00', 'line_width': '3'}))
        item.setZIndex(2)
        i2_id = layer.addItem(item)

        item = QgsAnnotationMarkerItem(QgsPoint(12, 13))
        item.setSymbol(QgsMarkerSymbol.createSimple({'color': '100,200,200', 'size': '6', 'outline_color': 'black'}))
        item.setZIndex(3)
        i3_id = layer.addItem(item)

        layer.setCrs(QgsCoordinateReferenceSystem('EPSG:4326'))

        settings = QgsMapSettings()
        settings.setDestinationCrs(QgsCoordinateReferenceSystem('EPSG:4326'))
        settings.setExtent(QgsRectangle(10, 10, 18, 18))
        settings.setOutputSize(QSize(200, 200))
        settings.setLayers([layer])

        job = QgsMapRendererParallelJob(settings)
        job.start()
        job.waitForFinished()

        # check rendered item results
        item_results = job.takeRenderedItemResults()
        item_details = item_results.renderedItems()
        self.assertEqual(len(item_details), 3)
        self.assertEqual([i.layerId() for i in item_details], [layer.id()] * 3)
        self.assertCountEqual([i.itemId() for i in item_details], [i1_id, i2_id, i3_id])
        self.assertCountEqual(
            [i.itemId() for i in item_results.renderedAnnotationItemsInBounds(QgsRectangle(0, 0, 1, 1))], [])
        self.assertCountEqual(
            [i.itemId() for i in item_results.renderedAnnotationItemsInBounds(QgsRectangle(10, 10, 11, 18))], [i2_id])
        self.assertCountEqual(
            [i.itemId() for i in item_results.renderedAnnotationItemsInBounds(QgsRectangle(10, 10, 12, 18))],
            [i1_id, i2_id, i3_id])

        # bounds should be in map crs
        self.assertEqual([i.boundingBox() for i in item_details if i.itemId() == i1_id][0],
                         QgsRectangle(11.5, 13, 12, 13.5))
        self.assertEqual([i.boundingBox() for i in item_details if i.itemId() == i2_id][0],
                         QgsRectangle(11, 13, 12, 15))
        self.assertEqual([i.boundingBox().toString(1) for i in item_details if i.itemId() == i3_id][0],
                         '11.5,12.5 : 12.5,13.5')
Пример #15
0
def render(settings):
    """
    Render the given settings to a image and save to disk.
    name: The name of the final result file.
    settings: QgsMapSettings containing the settings to render
    exportpath: The folder for the images to be exported to.
    """
    job = QgsMapRendererParallelJob(settings)
    #job = QgsMapRendererSequentialJob(settings)
    job.start()
    job.waitForFinished()
    image = job.renderedImage()
    return image, job.renderingTime()
    def writeLegendPng(self, layer, mapTitle, mapAttr, operation):
        mapTitle = UTILS.normalizeName(mapTitle)
        mapAttr = UTILS.normalizeName(mapAttr)
        legendFolder = self.getPathForMap(mapTitle, mapAttr, operation)

        # e.g. vlayer = iface.activeLayer()
        options = QgsMapSettings()
        options.setLayers([layer])
        options.setBackgroundColor(QColor(255, 128, 255))
        options.setOutputSize(QSize(60, 60))
        options.setExtent(layer.extent())
        qgisRenderJob = QgsMapRendererParallelJob(options)

        def savePng():
            img = qgisRenderJob.renderedImage()
            # save the image; e.g. img.save("/Users/myuser/render.png","png")
            img.save(os.path.join(legendFolder, "legend.png"), "png")

        qgisRenderJob.finished.connect(savePng)
        qgisRenderJob.start()
Пример #17
0
def renderMapToImage(mapsettings, parallel=False):
    """
    Render current map to an image, via multi-threaded renderer
    :param QgsMapSettings mapsettings:
    :param bool parallel: Do parallel or sequential render job
    :rtype: QImage
    """
    if parallel:
        job = QgsMapRendererParallelJob(mapsettings)
    else:
        job = QgsMapRendererSequentialJob(mapsettings)
    job.start()
    job.waitForFinished()

    return job.renderedImage()
Пример #18
0
def render_wms_to_image(xyz=True, extent=EXTENT, width=64, height=60):
    """
    :type xyz: bool
    :type extent: tuple
    :type width: int
    :type height: int
    :rtype: QImage
    """
    # p = QgsProject.instance()
    # p = QgsProject()
    uri = QgsDataSourceUri()
    if xyz:
        uri.setParam('type', 'xyz')
        uri.setParam('crs', 'EPSG:3857')
        uri.setParam('format', '')
        # uri.setParam('zmin', '0')
        # uri.setParam('zmax', '18')
        uri.setParam('url', XYZ_URL)
    else:
        uri.setParam('tileMatrixSet', 'GoogleMapsCompatible23')
        uri.setParam('crs', 'EPSG:3857')
        uri.setParam('format', 'image/png')
        uri.setParam('styles', '')
        uri.setParam('layers', 'Combined scene layer')
        uri.setParam('url', WMS_URL)

    # Important to do this conversion, else WMS provider will double encode;
    #   instead of just `str(uri.encodedUri())`, which outputs "b'uri'"
    # This coerces QByteArray -> str ... assuming UTF-8 is valid.
    final_uri = bytes(uri.encodedUri()).decode("utf-8")
    layer = QgsRasterLayer(final_uri, "scene_layer", "wms")

    if not layer.isValid():
        print('Layer is not valid')
        return QImage()
    # p.addMapLayer(layer)

    settings = QgsMapSettings()
    settings.setExtent(QgsRectangle(*extent))
    settings.setOutputSize(QSize(width, height))
    settings.setLayers([layer])

    job = QgsMapRendererParallelJob(settings)
    job.start()

    # This blocks...
    # It should really be a QEventLoop or QTimer that checks for finished()
    # Any intermediate image can safely be pulled from renderedImage()
    job.waitForFinished()

    return job.renderedImage()
Пример #19
0
def render_layer(settings, layer, width, height):
    settings.setLayers([layer.id()])
    settings.setFlags(settings.flags() ^ QgsMapSettings.Antialiasing)
    settings.setOutputSize(QSize(width, height))
    job = QgsMapRendererParallelJob(settings)
    job.start()
    job.waitForFinished()
    image = job.renderedImage()
    # image.save(r"/media/nathan/Data/dev/qgis-term/{}.jpg".format(layer.name()))
    return image
Пример #20
0
def renderMapToImage(mapsettings, parallel=False):
    """
    Render current map to an image, via multi-threaded renderer
    :param QgsMapSettings mapsettings:
    :param bool parallel: Do parallel or sequential render job
    :rtype: QImage
    """
    if parallel:
        job = QgsMapRendererParallelJob(mapsettings)
    else:
        job = QgsMapRendererSequentialJob(mapsettings)
    job.start()
    job.waitForFinished()

    return job.renderedImage()
Пример #21
0
    def setMap(self):
        def finished():
            super(SwipeMap, self).setContent(job.renderedImage(),
                                             self.canvas.extent())

        if len(self.layers) == 0:
            return

        settings = QgsMapSettings(self.canvas.mapSettings())
        settings.setLayers(self.layers)

        job = QgsMapRendererParallelJob(settings)
        job.start()
        job.finished.connect(finished)
        job.waitForFinished()
Пример #22
0
    def render_qgis_map(self):
        logging.info("Rendering QGIS map")
        # Gross. Fix me
        if not self.settings and project:
            self.settings = project.map_settings

        # TODO We should only get visible layers here but this will do for now
        self.settings.setLayers(QgsMapLayerRegistry.instance().mapLayers().keys())
        self.settings.setFlags(self.settings.flags() ^ QgsMapSettings.Antialiasing)
        logging.info(self.settings.flags())
        logging.info(self.settings.testFlag(QgsMapSettings.Antialiasing))
        height, width = self.mapwin.getmaxyx()
        logging.info("Setting output size to {}, {}".format(width, height))
        self.settings.setOutputSize(QSize(width, height))
        job = QgsMapRendererParallelJob(self.settings)
        job.start()
        job.waitForFinished()
        image = job.renderedImage()
        logging.info("Saving rendered image for checks...")
        image.save(r"F:\dev\qgis-term\render.jpg")
        return image
Пример #23
0
    def create_image(self, extent, width, height, canvas_name, output_dir):
        '''
        This method create an image

        :param extent: Extent
        :param width: Output image width
        :param height: Output image height
        :param canvas_name: Map name
        :param output_dir: Output directory for image
        '''

        if not extent:
            raise_exception('extent is empty')

        if not width:
            raise_exception('width is empty')

        if not height:
            raise_exception('height is empty')

        if not canvas_name:
            raise_exception('canvas name is empty')

        if not output_dir:
            raise_exception('output_dir is empty')

        file_name = '{0}/{1}.png'.format(output_dir, canvas_name)
        settings = QgsMapSettings()
        settings.setLayers(iface.mapCanvas().layers())
        settings.setBackgroundColor(QColor(255, 255, 255))
        settings.setOutputSize(QSize(width, height))
        settings.setExtent(extent)
        render = QgsMapRendererParallelJob(settings)
        render.start()
        render.waitForFinished()
        image = render.renderedImage()
        image.save(file_name, "png")

        return file_name
    def _process(self):
        def finished():
            image = job.renderedImage()
            if bool(self.data['canvas'].property('retro')):
                image = image.scaled(image.width() / 3, image.height() / 3)
                image = image.convertToFormat(
                    QImage.Format_Indexed8,
                    Qt.OrderedDither | Qt.OrderedAlphaDither)
            image.save(self.data['filepath'], "TIFF", 100)  # 100: Uncompressed
            self._setParamsSendData(image)

        settings = QgsMapSettings(self.data['canvas'].mapSettings())
        settings.setBackgroundColor(QColor(Qt.transparent))

        settings.setLayers(self.data['layers'])
        job = QgsMapRendererParallelJob(settings)
        job.start()
        job.finished.connect(finished)
        job.waitForFinished()
Пример #25
0
def write_image(destination_crs, extent, filepath, imagetype):
    """
    Save current QGIS canvas to image file.
    """
    settings = QgsMapSettings()  # build settings
    settings.setDestinationCrs(destination_crs)  # set output crs
    settings.setExtent(extent)  # in destination_crs
    layers = iface.mapCanvas().layers()  # get visible layers
    settings.setLayers(layers)

    w = 1920
    h = int((extent.yMaximum() - extent.yMinimum()) /
            (extent.xMaximum() - extent.xMinimum()) * w)
    settings.setOutputSize(QSize(w, h))
    settings.setOutputDpi(200)

    render = QgsMapRendererParallelJob(settings)
    render.start()
    render.waitForFinished()
    image = render.renderedImage()
    try:
        image.save(filepath, imagetype)
    except IOError:
        raise QgsProcessingException(f"Image not writable at <{filepath}>")
Пример #26
0
def write_image(
    feedback,
    tex_layer,
    tex_pixel_size,
    destination_crs,
    destination_extent,
    filepath,
    imagetype,
):
    """
    Save current QGIS canvas to image file.
    """

    feedback.pushInfo("Rendering texture image (timeout in 30s)...")
    project = QgsProject.instance()

    # Get extent size in meters
    d = QgsDistanceArea()
    d.setSourceCrs(
        crs=destination_crs, context=QgsProject.instance().transformContext()
    )
    p00, p10, p01 = (
        QgsPointXY(destination_extent.xMinimum(), destination_extent.yMinimum()),
        QgsPointXY(destination_extent.xMaximum(), destination_extent.yMinimum()),
        QgsPointXY(destination_extent.xMinimum(), destination_extent.yMaximum()),
    )
    wm = d.measureLine(p00, p10)  # euclidean dist, extent width in m
    hm = d.measureLine(p00, p01)  # euclidean dist, extent height in m

    # Image settings and texture layer choice
    settings = QgsMapSettings()  # build settings
    settings.setDestinationCrs(destination_crs)  # set output crs
    settings.setExtent(destination_extent)  # in destination_crs
    if tex_layer:
        layers = (tex_layer,)  # chosen texture layer
    else:
        canvas = iface.mapCanvas()
        layers = canvas.layers()  # get visible layers
    wpix = int(wm / tex_pixel_size)
    hpix = int(hm / tex_pixel_size)
    settings.setOutputSize(QSize(wpix, hpix))
    settings.setLayers(layers)
    feedback.pushInfo(
        f"Requested texture size: {wm:.2f}x{hm:.2f} m, {wpix}x{hpix} pixels."
    )

    # Render and save image
    render = QgsMapRendererParallelJob(settings)
    render.start()
    t0 = time.time()
    while render.isActive():
        dt = time.time() - t0
        QCoreApplication.processEvents()
        if feedback.isCanceled():
            render.cancelWithoutBlocking()
            return
        if dt >= 30.0:
            render.cancelWithoutBlocking()
            feedback.reportError("Render timed out, no texture saved.")
            return
    image = render.renderedImage()
    try:
        image.save(filepath, imagetype)
    except IOError:
        raise QgsProcessingException(
            f"Texture not writable to <{filepath}>, cannot proceed."
        )
    feedback.pushInfo(f"Texture saved in {dt:.2f} seconds.")
Пример #27
0
    def update_summary_sheet(self,lyr=None, force=None):
        '''
        Creates a summary sheet with thumbnail, layer metadata and online view link
        '''
        #create a layer snapshot and upload it to google drive

        if not lyr:
            lyr = self.lyr

        mapbox_style = self.service_sheet.sheet_cell('settings!A5')
        if not mapbox_style:
            logger("migrating mapbox style")
            self.service_sheet.set_style_mapbox(self.layer_style_to_json(self.lyr))
        
        if not force and not self.dirty and not self.restyled:
            return

        if self.restyled:
            self.service_sheet.set_style_qgis(self.layer_style_to_xml(self.lyr))
            self.service_sheet.set_style_sld(self.SLD_to_xml(self.lyr))
            self.service_sheet.set_style_mapbox(self.layer_style_to_json(self.lyr))
            self.saveMetadataState()
        
        canvas = QgsMapCanvas()
        canvas.resize(QSize(600,600))
        canvas.setCanvasColor(Qt.white)
        canvas.setExtent(lyr.extent())
        canvas.setLayers([lyr])
        canvas.refresh()
        canvas.update()
        settings = canvas.mapSettings()
        settings.setLayers([lyr])
        job = QgsMapRendererParallelJob(settings)
        job.start()
        job.waitForFinished()
        image = job.renderedImage()

        transparent_image = QImage(image.width(), image.height(), QImage.Format_ARGB32)
        transparent_image.fill(Qt.transparent)
        p = QPainter(transparent_image)
        mask = image.createMaskFromColor(QColor(255, 255, 255).rgb(), Qt.MaskInColor)
        p.setClipRegion(QRegion(QBitmap(QPixmap.fromImage(mask))))
        p.drawPixmap(0, 0, QPixmap.fromImage(image))
        p.end()

        tmp_path = os.path.join(self.parent.plugin_dir,self.service_sheet.name+".png")
        transparent_image.save(tmp_path,"PNG")
        image_istances = self.service_drive.list_files(mimeTypeFilter='image/png',filename=self.service_sheet.name+".png")
        for imagename, image_props in image_istances.items():
            self.service_drive.delete_file(image_props['id'])
        result = self.service_drive.upload_image(tmp_path)
        self.service_drive.add_permission(result['id'],'anyone','reader')
        webLink = result['webContentLink'] #'https://drive.google.com/uc?export=view&id='+result['id']
        logger("webLink:" + webLink)
        canvas.setDestinationCrs(QgsCoordinateReferenceSystem(4326))
        worldfile = QgsMapSettingsUtils.worldFileContent(settings)
        lonlat_min = self.transformToWGS84(QgsPointXY(canvas.extent().xMinimum(), canvas.extent().yMinimum()))
        lonlat_max = self.transformToWGS84(QgsPointXY(canvas.extent().xMaximum(), canvas.extent().yMaximum()))
        keymap_extent = [lonlat_min.x(),lonlat_min.y(),lonlat_max.x(),lonlat_max.y()]
        
        os.remove(tmp_path)
        #update layer metadata
        summary_id = self.service_sheet.add_sheet('summary', no_grid=True)
        appPropsUpdate = [
            ["keymap",webLink],
            ["worldfile",pack(worldfile)],
            ["keymap_extent", json.dumps(keymap_extent)]
        ]
        res = self.service_sheet.update_appProperties(self.spreadsheet_id,appPropsUpdate)
        
        self.saveMetadataState(metadata=self.get_layer_metadata())
        self.parent.public_db.setKey(self.spreadsheet_id, dict(self.get_layer_metadata()+appPropsUpdate),only_update=True)
        #merge cells to visualize snapshot and aaply image snapshot
        request_body = {
            'requests': [{
                'mergeCells': {
                    "range": {
                        "sheetId": summary_id,
                        "startRowIndex": 9,
                        "endRowIndex": 32,
                        "startColumnIndex": 0,
                        "endColumnIndex": 9,
                    },
                "mergeType": 'MERGE_ALL'
                }
            }]
        }
        self.service_sheet.service.spreadsheets().batchUpdate(spreadsheetId=self.spreadsheet_id, body=request_body).execute()
        self.service_sheet.set_sheet_cell('summary!A10','=IMAGE("%s",3)' % webLink)

        permissions = self.service_drive.file_property(self.spreadsheet_id,'permissions')
        for permission in permissions:
            if permission['type'] == 'anyone':
                public = True
                break
            else:
                public = False
        if public:
            update_range = 'summary!A9:B9'
            update_body = {
                "range": update_range,
                "values": [['public link', "https://enricofer.github.io/gdrive_provider/weblink/converter.html?spreadsheet_id="+self.spreadsheet_id]]
            }
            self.service_sheet.service.spreadsheets().values().update(spreadsheetId=self.spreadsheet_id,range=update_range, body=update_body, valueInputOption='USER_ENTERED').execute()

        #hide worksheets except summary
        sheets = self.service_sheet.get_sheets()
        #self.service_sheet.toggle_sheet('summary', sheets['summary'], hidden=None)
        for sheet_name,sheet_id in sheets.items():
            if not sheet_name == 'summary':
                self.service_sheet.toggle_sheet(sheet_name, sheet_id, hidden=True)
    def responseComplete(self):
        request = self.serverInterface().requestHandler()
        params = request.parameterMap()

        # SERVICE=RENDERGEOJSON -- we are taking over
        if params.get('SERVICE', '').upper() == 'RENDERGEOJSON':
            request.clear()
            try:
                # Parse parameters
                geojson = params.get('GEOJSON')
                if not geojson:
                    raise ParameterError('Parameter GEOJSON must be set.')

                style = params.get('STYLE')
                if not style:
                    raise ParameterError('Parameter STYLE must be set.')

                try:
                    width = int(params.get('WIDTH'))
                except TypeError:
                    raise ParameterError('Parameter WIDTH must be integer.')
                try:
                    height = int(params.get('HEIGHT'))
                except TypeError:
                    raise ParameterError('Parameter HEIGHT must be integer.')

                try:
                    dpi = int(params.get('DPI', 96))
                except TypeError:
                    raise ParameterError('Parameter DPI must be integer.')

                try:
                    minx, miny, maxx, maxy = params.get('BBOX').split(',')
                    bbox = QgsRectangle(float(minx), float(miny), float(maxx),
                                        float(maxy))
                except (ValueError, AttributeError):
                    raise ParameterError(
                        'Parameter BBOX must be specified in the form `min_x,min_y,max_x,max_y`.'
                    )

                url = geojson
                geojson_file_name = self._resolve_url(geojson)

                if '$type' in style:
                    polygon_style = self._resolve_url(
                        style.replace('$type', 'polygons'))
                    line_style = self._resolve_url(
                        style.replace('$type', 'lines'))
                    point_style = self._resolve_url(
                        style.replace('$type', 'points'))
                else:
                    polygon_style = self._resolve_url(style)
                    line_style = polygon_style
                    point_style = polygon_style

                polygon_layer = QgsVectorLayer(
                    geojson_file_name + '|geometrytype=Polygon', 'polygons',
                    'ogr')
                self._load_style(polygon_layer, polygon_style)
                line_layer = QgsVectorLayer(
                    geojson_file_name + '|geometrytype=Line', 'lines', 'ogr')
                self._load_style(line_layer, line_style)
                point_layer = QgsVectorLayer(
                    geojson_file_name + '|geometrytype=Point', 'points', 'ogr')
                self._load_style(point_layer, point_style)

                settings = QgsMapSettings()
                settings.setOutputSize(QSize(width, height))
                settings.setOutputDpi(dpi)
                settings.setExtent(bbox)
                settings.setLayers([polygon_layer, line_layer, point_layer])
                settings.setBackgroundColor(QColor(Qt.transparent))
                renderer = QgsMapRendererParallelJob(settings)

                event_loop = QEventLoop()
                renderer.finished.connect(event_loop.quit)
                renderer.start()

                event_loop.exec_()

                img = renderer.renderedImage()
                img.setDotsPerMeterX(dpi * 39.37)
                img.setDotsPerMeterY(dpi * 39.37)
                image_data = QByteArray()
                buf = QBuffer(image_data)
                buf.open(QIODevice.WriteOnly)
                img.save(buf, 'PNG')

                request.setResponseHeader('Content-type', 'image/png')
                request.appendBody(image_data)
            except ParameterError as e:
                QgsMessageLog.logMessage(
                    "RenderGeojson.responseComplete :: ParameterError")
                request.setResponseHeader('Content-type', 'text/plain')
                request.appendBody(str(e).encode('utf-8'))
            except:
                QgsMessageLog.logMessage(
                    "RenderGeojson.responseComplete :: Exception")
                QgsMessageLog.logMessage(
                    "RenderGeojson.responseComplete ::   {}".format(
                        traceback.format_exc()))
                request.setResponseHeader('Content-type', 'text/plain')
                request.appendBody(b'Unhandled error')
                request.appendBody(traceback.format_exc().encode('utf-8'))
Пример #29
0
class DrawingPad(Ui_DrawingWindow, QWidget):
    def __init__(self, startimage=None, parent=None):
        super(DrawingPad, self).__init__(parent)
        self.saveAsActs = []

        self.setupUi(self)

        self.scribbleArea = ScribbleArea()
        self.scribbleArea.clearImage()

        self.frame.layout().addWidget(self.scribbleArea)
        self.createActions()

        self.actionRedPen.trigger()

        self.setWindowTitle("Scribble")
        self.resize(500, 500)

        self.openImage(startimage)
        self.colorPickerButton.clicked.connect(self.pickcolour)

        self.canvas = None

    @property
    def canvas(self):
        return self._canvas

    @canvas.setter
    def canvas(self, value):
        enabled = True if value else False
        self.actionMapSnapshot.setEnabled(enabled)
        self._canvas = value

    @property
    def pixmap(self):
        return self.scribbleArea.pixmap

    @pixmap.setter
    def pixmap(self, value):
        self.scribbleArea.pixmap = value

    def pickcolour(self):
        colour = QColorDialog.getColor(Qt.black, self)
        self.scribbleArea.setPenColor(colour)

    def rendermap(self):
        image = self.renderjob.renderedImage()
        self.scribbleArea.addMapImage(image)
        try:
            gpslayer = QgsMapLayerRegistry.instance().mapLayersByName(
                "__gps_layer")[0]
            gpslayer.rollBack()
        except IndexError:
            pass

    def getmap(self):
        if self.canvas:
            settings = self.canvas.mapSettings()
            layers = settings.layers()
            if GPS.isConnected:
                try:
                    gpslayer = QgsMapLayerRegistry.instance().mapLayersByName(
                        "__gps_layer")[0]
                except IndexError:
                    gpslayer = QgsVectorLayer("Point", "__gps_layer", "memory")
                    symbol = QgsMarkerSymbolV2.createSimple({
                        'name': 'circle',
                        'color': 'blue',
                        "size": '5'
                    })
                    gpslayer.rendererV2().setSymbol(symbol)
                    QgsMapLayerRegistry.instance().addMapLayer(gpslayer, False)

                layers.append(gpslayer.id())
                settings.setLayers(layers)

                map_pos = QgsPoint(GPS.gpsinfo("longitude"),
                                   GPS.gpsinfo("latitude"))
                # map_pos = QgsPoint(115.72589,-32.29597)
                geom = QgsGeometry.fromPoint(map_pos)
                feature = QgsFeature()
                feature.setGeometry(geom)
                gpslayer.startEditing()
                gpslayer.addFeature(feature)
                # gpslayer.commitChanges()

            self.renderjob = QgsMapRendererParallelJob(settings)
            self.renderjob.finished.connect(self.rendermap)
            self.renderjob.start()

    def openImage(self, image):
        if not image is None and os.path.exists(image):
            self.scribbleArea.openImage(image)

    def saveImage(self, filename):
        filename = filename + ".png"
        log(filename)
        return self.scribbleArea.saveImage(filename, "png")

    def setPen(self, color, size=3):
        self.scribbleArea.setPenWidth(size)
        self.scribbleArea.setPenColor(color)

    def createActions(self):
        self.actionClearDrawing.triggered.connect(self.scribbleArea.clearImage)
        self.toolClear.setDefaultAction(self.actionClearDrawing)

        self.actionRedPen.triggered.connect(
            functools.partial(self.setPen, QtCore.Qt.red, 3))
        self.toolRedPen.setDefaultAction(self.actionRedPen)

        self.actionBluePen.triggered.connect(
            functools.partial(self.setPen, QtCore.Qt.blue, 3))
        self.toolBluePen.setDefaultAction(self.actionBluePen)

        self.actionBlackPen.triggered.connect(
            functools.partial(self.setPen, QtCore.Qt.black, 3))
        self.toolBlackPen.setDefaultAction(self.actionBlackPen)

        self.actionEraser.triggered.connect(
            functools.partial(self.setPen, QtCore.Qt.white, 9))
        self.toolEraser.setDefaultAction(self.actionEraser)

        self.actionMapSnapshot.triggered.connect(self.getmap)
        self.toolMapSnapshot.setDefaultAction(self.actionMapSnapshot)

        self.toolSave.setDefaultAction(self.actionSave)
        self.toolCancel.setDefaultAction(self.actionCancel)
Пример #30
0
class DrawingPad(Ui_DrawingWindow, QWidget):
    def __init__(self, startimage=None, parent=None):
        super(DrawingPad, self).__init__(parent)
        self.saveAsActs = []
        
        self.setupUi(self)

        self.scribbleArea = ScribbleArea()
        self.scribbleArea.clearImage()
    
        self.frame.layout().addWidget(self.scribbleArea)
        self.createActions()

        self.actionRedPen.trigger()

        self.setWindowTitle("Scribble")
        self.resize(500, 500)

        self.openImage(startimage)
        self.colorPickerButton.clicked.connect(self.pickcolour)

        self.canvas = None

    @property
    def canvas(self):
        return self._canvas

    @canvas.setter
    def canvas(self, value):
        enabled = True if value else False
        self.actionMapSnapshot.setEnabled(enabled)
        self._canvas = value

    @property
    def pixmap(self):
        return self.scribbleArea.pixmap

    @pixmap.setter
    def pixmap(self, value):
        self.scribbleArea.pixmap = value

    def pickcolour(self):
        colour = QColorDialog.getColor(Qt.black, self)
        self.scribbleArea.setPenColor(colour)

    def rendermap(self):
        image = self.renderjob.renderedImage()
        self.scribbleArea.addMapImage(image)

    def getmap(self):
        if self.canvas:
            if QGis.QGIS_VERSION_INT > 20200:
                from qgis.core import QgsMapRendererParallelJob
                self.renderjob = QgsMapRendererParallelJob(self.canvas.mapSettings())
                self.renderjob.finished.connect(self.rendermap)
                self.renderjob.start()
            else:
                pixmap = QPixmap(self.canvas.size())
                pixmap.fill(self.canvas.canvasColor())
                painter = QPainter(pixmap)
                renderer = self.canvas.mapRenderer()
                renderer.render(painter)
                del painter
                self.scribbleArea.addMap(pixmap)

    def openImage(self, image):
        if not image is None and os.path.exists(image):
            self.scribbleArea.openImage(image)

    def saveImage(self, filename):
        filename = filename + ".png"
        log(filename)
        return self.scribbleArea.saveImage(filename, "png")

    def setPen(self, color, size=3):
        self.scribbleArea.setPenWidth(size)
        self.scribbleArea.setPenColor(color)

    def createActions(self):
        self.actionClearDrawing.triggered.connect(self.scribbleArea.clearImage)
        self.toolClear.setDefaultAction(self.actionClearDrawing)

        self.actionRedPen.triggered.connect(functools.partial(self.setPen, QtCore.Qt.red, 3))
        self.toolRedPen.setDefaultAction(self.actionRedPen)

        self.actionBluePen.triggered.connect(functools.partial(self.setPen, QtCore.Qt.blue, 3))
        self.toolBluePen.setDefaultAction(self.actionBluePen)

        self.actionBlackPen.triggered.connect(functools.partial(self.setPen, QtCore.Qt.black, 3))
        self.toolBlackPen.setDefaultAction(self.actionBlackPen)

        self.actionEraser.triggered.connect(functools.partial(self.setPen, QtCore.Qt.white, 9))
        self.toolEraser.setDefaultAction(self.actionEraser)

        self.actionMapSnapshot.triggered.connect(self.getmap)
        self.toolMapSnapshot.setDefaultAction(self.actionMapSnapshot)

        self.toolSave.setDefaultAction(self.actionSave)
        self.toolCancel.setDefaultAction(self.actionCancel)
Пример #31
0
    def _start_job(self):

        map_settings = None

        if self._node_type:
            log.debug(f'Rendering image for node type: {self._node_type}')

        # Not sure why this needs to be a string comparison, instead of enum
        if f'{self._node_type}' == 'PlanetNodeType.DAILY_SCENE':

            if not self._item_type_ids:
                log.debug('No item type_id keys list object passed')
                return
            if LOG_VERBOSE:
                log.debug(f'item_type_ids:\n{self._item_type_ids}')

            if not self._extent_json:
                log.debug('Extent is invalid')
                return
            if LOG_VERBOSE:
                log.debug(f'extent_json:\n{self._extent_json}')

            if not self._api_key:
                log.debug('No API in passed')
                return

            if self._width <= 0 or self._height <= 0:
                log.debug('Invalid output width or height')
                return

            log.debug(f'Starting render map setup for {self._item_key}')

            # noinspection PyArgumentList
            # p = QgsProject.instance()

            data_src_uri = tile_service_data_src_uri(
                self._item_type_ids, self._api_key, tile_hash=self._tile_hash)
            log.debug(f'Render data_src_uri:\n'
                      f'{data_src_uri}')

            if not data_src_uri:
                log.debug('Invalid data source URI returned')
                return

            self._rlayer: QgsRasterLayer = \
                QgsRasterLayer(data_src_uri, self._item_key, "wms")

            if not self._rlayer.isValid():
                log.debug('Render layer is not valid')
                return

            # p.addMapLayer(rlayer, False)

            ext: QgsRectangle = \
                qgsgeometry_from_geojson(self._extent_json).boundingBox()

            if ext.isEmpty():
                log.debug('Extent bounding box is empty or null')
                return

            if ext.width() > ext.height():
                self._height = int(ext.height() / ext.width() * self._height)
            elif ext.height() > ext.width():
                self._width = int(ext.width() / ext.height() * self._width)

            # noinspection PyArgumentList
            transform = QgsCoordinateTransform(
                QgsCoordinateReferenceSystem('EPSG:4326'),
                QgsCoordinateReferenceSystem(self._dest_crs),
                QgsProject.instance())

            transform_extent = transform.transformBoundingBox(ext)

            if transform_extent.isEmpty():
                log.debug('Transformed extent bounding box is empty or null')
                return

            map_settings = QgsMapSettings()
            map_settings.setExtent(transform_extent)
            map_settings.setOutputSize(QSize(self._width, self._height))
            map_settings.setLayers([self._rlayer])

            log.debug(f'QgsMapSettings set for {self._item_key}')

        if map_settings is not None:

            self._job = QgsMapRendererParallelJob(map_settings)

            # noinspection PyUnresolvedReferences
            self._job.finished.connect(self._job_finished)

            self._has_job = True

            log.debug(f'Render job initialized for {self._item_key}')
        else:
            log.debug(f'No render job initialized for {self._item_key}')

        self._job.start()
Пример #32
0
class PlanetQgisRenderJob(PlanetRenderJob):
    """
    Generic wrapper class for QGIS isolated map renderer jobs
    """

    fetchShouldCancel = pyqtSignal()

    # Base class for parallel and sequential jobs
    # _job: Type[QgsMapRendererQImageJob]
    _job: Optional[QgsMapRendererParallelJob]

    def __init__(self, item_key: str,
                 api_key: str,
                 extent_json: Optional[Union[str, dict]] = None,
                 dest_crs: Optional[str] = 'EPSG:3857',
                 item_id: Optional[str] = None,
                 item_type: Optional[str] = None,
                 item_type_ids: Optional[List[str]] = None,
                 item_properties: Optional[dict] = None,
                 node_type: Optional[PlanetNodeType] = None,
                 image_url: Optional[str] = None,
                 width: int = 256,
                 height: int = 256,
                 api_client: Optional[ClientV1] = None,
                 cache_dir: Optional[str] = None,
                 parent: Optional[QObject] = None):

        super().__init__(parent=parent)

        self._id = self._item_key = item_key
        self._api_key = api_key
        self._extent_json = extent_json
        self._dest_crs = dest_crs

        self._item_id = item_id
        self._item_type = item_type
        self._item_type_ids = item_type_ids
        self._item_properties = item_properties
        self._node_type = node_type
        self._image_url = image_url
        self._width = width
        self._height = height

        self._job = None
        self._has_job = False
        self._rlayer: Optional[QgsRasterLayer] = None

        self._tile_hash = None

        self._json_handler = JsonDownloadHandler()
        self._json_handler.aborted.connect(self._json_aborted)
        self._json_handler.errored.connect(self._json_errored)
        # connect 'finished' signal on a need-as basis

        self._api_client = api_client
        self._cache_dir = cache_dir

        self._watcher = PlanetCallbackWatcher(
            parent=self, timeout=RESPONSE_TIMEOUT)
        # self._watcher.responseRegistered.connect(self._some_slot)
        self._watcher.responseCancelled.connect(self._json_cancelled)
        self._watcher.responseTimedOut[int].connect(self._json_timed_out)
        self._watcher.responseFinished['PyQt_PyObject'].connect(
            self._json_tile_hash_finished_wbody)
        self.fetchShouldCancel.connect(self._watcher.cancel_response)

    def id(self) -> str:
        return self._id

    def has_job(self):
        return self._has_job

    @pyqtSlot()
    def start(self) -> None:
        if not self._api_key:
            log.debug('No API key, skip fetching tile hash')
            return None

        if not self._item_type_ids:
            log.debug('No item type:ids passed, skip fetching tile hash')
            return None

        # item_type_ids_reverse = list(self._item_type_ids)
        # item_type_ids_reverse.reverse()
        # data = {'ids': item_type_ids_reverse}
        data = {'ids': self._item_type_ids[::-1]}
        json_data = json.dumps(data)
        if LOG_VERBOSE:
            log.debug(f'json_data: {json_data}')

        tile_url = TILE_SERVICE_URL.format('')

        if USE_JSON_HANDLER:

            headers = dict()
            headers['Content-Type'] = 'application/json'
            bauth = bytes(f'{self._api_key}:', encoding='ascii')
            # auth = 'Basic {0}'.format(base64.b64encode(bauth))
            # headers['Authorization'] = f'Basic {self._api_key}:'
            base64_auth = base64.b64encode(bauth).decode("ascii")
            # headers['Authorization'] = f'api-key {base64_auth}'
            headers['Authorization'] = f'Basic {base64_auth}'

            self._json_handler.finished.connect(self._json_tile_hash_finished)

            self._json_handler.post(
                tile_url,
                headers,
                data=json_data,
            )
        else:  # use async dispatcher

            auth = HTTPBasicAuth(self._api_key, '')
            self._api_client.dispatcher.session.auth = auth

            resp = self._api_client.dispatcher.response(
                api_models.Request(
                    tile_url,
                    self._api_client.auth,
                    # None,
                    body_type=api_models.JSON,
                    method='POST',
                    data=json_data,
                )
            )

            resp.get_body_async(
                handler=partial(dispatch_callback, watcher=self._watcher))

            self._watcher.register_response(resp)

    @pyqtSlot()
    def _start_job(self):

        map_settings = None

        if self._node_type:
            log.debug(f'Rendering image for node type: {self._node_type}')

        # Not sure why this needs to be a string comparison, instead of enum
        if f'{self._node_type}' == 'PlanetNodeType.DAILY_SCENE':

            if not self._item_type_ids:
                log.debug('No item type_id keys list object passed')
                return
            if LOG_VERBOSE:
                log.debug(f'item_type_ids:\n{self._item_type_ids}')

            if not self._extent_json:
                log.debug('Extent is invalid')
                return
            if LOG_VERBOSE:
                log.debug(f'extent_json:\n{self._extent_json}')

            if not self._api_key:
                log.debug('No API in passed')
                return

            if self._width <= 0 or self._height <= 0:
                log.debug('Invalid output width or height')
                return

            log.debug(f'Starting render map setup for {self._item_key}')

            # noinspection PyArgumentList
            # p = QgsProject.instance()

            data_src_uri = tile_service_data_src_uri(
                self._item_type_ids, self._api_key, tile_hash=self._tile_hash)
            log.debug(f'Render data_src_uri:\n'
                      f'{data_src_uri}')

            if not data_src_uri:
                log.debug('Invalid data source URI returned')
                return

            self._rlayer: QgsRasterLayer = \
                QgsRasterLayer(data_src_uri, self._item_key, "wms")

            if not self._rlayer.isValid():
                log.debug('Render layer is not valid')
                return

            # p.addMapLayer(rlayer, False)

            ext: QgsRectangle = \
                qgsgeometry_from_geojson(self._extent_json).boundingBox()

            if ext.isEmpty():
                log.debug('Extent bounding box is empty or null')
                return

            if ext.width() > ext.height():
                self._height = int(ext.height() / ext.width() * self._height)
            elif ext.height() > ext.width():
                self._width = int(ext.width() / ext.height() * self._width)

            # noinspection PyArgumentList
            transform = QgsCoordinateTransform(
                QgsCoordinateReferenceSystem('EPSG:4326'),
                QgsCoordinateReferenceSystem(self._dest_crs),
                QgsProject.instance())

            transform_extent = transform.transformBoundingBox(ext)

            if transform_extent.isEmpty():
                log.debug('Transformed extent bounding box is empty or null')
                return

            map_settings = QgsMapSettings()
            map_settings.setExtent(transform_extent)
            map_settings.setOutputSize(QSize(self._width, self._height))
            map_settings.setLayers([self._rlayer])

            log.debug(f'QgsMapSettings set for {self._item_key}')

        if map_settings is not None:

            self._job = QgsMapRendererParallelJob(map_settings)

            # noinspection PyUnresolvedReferences
            self._job.finished.connect(self._job_finished)

            self._has_job = True

            log.debug(f'Render job initialized for {self._item_key}')
        else:
            log.debug(f'No render job initialized for {self._item_key}')

        self._job.start()

    @pyqtSlot()
    @pyqtSlot(str)
    def cancel(self, item_key: Optional[str] = None) -> None:
        self.fetchShouldCancel.emit()

        if self._job:
            self._job.cancelWithoutBlocking()
            log.debug('Job cancelled (without blocking)')
            # self.jobFinished.emit(None)
            # self.jobFinishedWithId.emit(self._id, None)
        else:
            log.debug('No job to cancel')

        # self._job = None
        # self._has_job = False
        self.jobCancelled.emit()
        if item_key and item_key != self._id:
            return
        self.jobCancelledWithId.emit(self._id)

    @pyqtSlot('PyQt_PyObject')
    def _json_tile_hash_finished_wbody(self, body: api_models.JSON):

        fetch = 'Render job JSON tile hash fetch'
        log.debug(f'{fetch} finished')

        if body is None or not hasattr(body, 'response'):
            log.debug(f'{fetch} failed: no response')
            return

        resp: ReqResponse = body.response
        log.debug(requests_response_metadata(resp))

        if not resp.ok:
            log.debug(f'{fetch} failed: response not ok')
            return

        json_body = body.get()
        if 'name' in json_body:
            log.debug(f'{fetch} succeeded')
            self._tile_hash = json_body['name']
            self._start_job()
        else:
            log.debug(f'{fetch} failed')
            return

    @pyqtSlot()
    def _json_tile_hash_finished(self):
        log.debug(f'Render job JSON tile hash fetch finished')
        self._json_handler.finished.disconnect(self._json_tile_hash_finished)

        json_body = self._json_handler.json
        if 'name' in json_body:
            log.debug(f'Render job JSON tile hash fetch succeeded')
            self._tile_hash = json_body['name']
            self._start_job()
        else:
            log.debug(f'Render job JSON tile hash fetch failed')
            return

    @pyqtSlot()
    def _json_cancelled(self):
        log.debug(f'Render job JSON fetch cancelled')
        self.jobCancelled.emit()
        self.jobCancelledWithId.emit(self._id)

    @pyqtSlot()
    def _json_aborted(self):
        log.debug(f'Render job JSON fetch aborted')
        self.jobCancelled.emit()
        self.jobCancelledWithId.emit(self._id)

    @pyqtSlot()
    def _json_errored(self):
        log.debug(f'Render job JSON fetch errored')
        self.jobCancelled.emit()
        self.jobCancelledWithId.emit(self._id)

    @pyqtSlot()
    def _json_timed_out(self) -> None:
        log.debug(f'Render job JSON fetch timed out')
        self.jobTimedOut.emit()
        self.jobTimedOutWithId.emit(self._id)

    @pyqtSlot()
    def _job_finished(self):

        log.debug(f'Job rendering time (seconds): '
                  f'{self._job.renderingTime() / 1000}')

        item_path = None

        if self._job:
            img: QImage = self._job.renderedImage()
            if not img.isNull():

                # TODO: Composite (centered) over top of full width/height
                #       Image is unlikely to be square at this point, after
                #       being clipped to transformed AOI bounding box.
                #
                #       Or, do this as a standard operation in
                #       PlanetThumbnailCache._thumbnail_job_finished()?

                cache_dir = self._cache_dir
                if f'{self._node_type}' in [
                    'PlanetNodeType.DAILY_SCENE',
                ]:
                    # Don't pollute user-defined cache with ephemeral thumbs
                    cache_dir = TEMP_CACHE_DIR

                # Write .png image to cache directory
                item_path = os.path.join(cache_dir, f'{self._id}{THUMB_EXT}')

                if os.path.exists(item_path):
                    log.debug(f'Removing existing job at:\n{item_path}')
                    os.remove(item_path)

                log.debug(f'Saving thumbnail job to:\n{item_path}')
                img.save(item_path, 'PNG')
            else:
                log.debug('Rendered QImage is null')

        self.jobFinished.emit()
        self.jobFinishedWithId.emit(self._id, item_path)

    def create_job(*args, **kwargs):
        """Job factory for PlanetThumbnailCache"""
        return PlanetQgisRenderJob(*args, **kwargs)
Пример #33
0
class DrawingPad(Ui_DrawingWindow, QWidget):
    def __init__(self, startimage=None, parent=None):
        super(DrawingPad, self).__init__(parent)
        self.saveAsActs = []
        
        self.setupUi(self)

        self.scribbleArea = ScribbleArea()
        self.scribbleArea.clearImage()
    
        self.frame.layout().addWidget(self.scribbleArea)
        self.createActions()

        self.actionRedPen.trigger()

        self.setWindowTitle("Scribble")
        self.resize(500, 500)

        self.openImage(startimage)
        self.colorPickerButton.clicked.connect(self.pickcolour)

        self.canvas = None

    @property
    def canvas(self):
        return self._canvas

    @canvas.setter
    def canvas(self, value):
        enabled = True if value else False
        self.actionMapSnapshot.setEnabled(enabled)
        self._canvas = value

    @property
    def pixmap(self):
        return self.scribbleArea.pixmap

    @pixmap.setter
    def pixmap(self, value):
        self.scribbleArea.pixmap = value

    def pickcolour(self):
        colour = QColorDialog.getColor(Qt.black, self)
        self.scribbleArea.setPenColor(colour)

    def rendermap(self):
        image = self.renderjob.renderedImage()
        self.scribbleArea.addMapImage(image)
        try:
            gpslayer = QgsMapLayerRegistry.instance().mapLayersByName("__gps_layer")[0]
            gpslayer.rollBack()
        except IndexError:
            pass

    def getmap(self):
        if self.canvas:
            settings = self.canvas.mapSettings()
            layers = settings.layers()
            if GPS.isConnected:
                try:
                    gpslayer = QgsMapLayerRegistry.instance().mapLayersByName("__gps_layer")[0]
                except IndexError:
                    gpslayer = QgsVectorLayer("Point", "__gps_layer", "memory")
                    symbol = QgsMarkerSymbolV2.createSimple({'name': 'circle', 'color': 'blue', "size": '5'})
                    gpslayer.rendererV2().setSymbol(symbol)
                    QgsMapLayerRegistry.instance().addMapLayer(gpslayer, False)

                layers.append(gpslayer.id())
                settings.setLayers(layers)

                map_pos = QgsPoint(GPS.gpsinfo("longitude"), GPS.gpsinfo("latitude"))
                # map_pos = QgsPoint(115.72589,-32.29597)
                geom = QgsGeometry.fromPoint(map_pos)
                feature = QgsFeature()
                feature.setGeometry(geom)
                gpslayer.startEditing()
                gpslayer.addFeature(feature)
                # gpslayer.commitChanges()

            self.renderjob = QgsMapRendererParallelJob(settings)
            self.renderjob.finished.connect(self.rendermap)
            self.renderjob.start()

    def openImage(self, image):
        if not image is None and os.path.exists(image):
            self.scribbleArea.openImage(image)

    def saveImage(self, filename):
        filename = filename + ".png"
        log(filename)
        return self.scribbleArea.saveImage(filename, "png")

    def setPen(self, color, size=3):
        self.scribbleArea.setPenWidth(size)
        self.scribbleArea.setPenColor(color)

    def createActions(self):
        self.actionClearDrawing.triggered.connect(self.scribbleArea.clearImage)
        self.toolClear.setDefaultAction(self.actionClearDrawing)

        self.actionRedPen.triggered.connect(functools.partial(self.setPen, QtCore.Qt.red, 3))
        self.toolRedPen.setDefaultAction(self.actionRedPen)

        self.actionBluePen.triggered.connect(functools.partial(self.setPen, QtCore.Qt.blue, 3))
        self.toolBluePen.setDefaultAction(self.actionBluePen)

        self.actionBlackPen.triggered.connect(functools.partial(self.setPen, QtCore.Qt.black, 3))
        self.toolBlackPen.setDefaultAction(self.actionBlackPen)

        self.actionEraser.triggered.connect(functools.partial(self.setPen, QtCore.Qt.white, 9))
        self.toolEraser.setDefaultAction(self.actionEraser)

        self.actionMapSnapshot.triggered.connect(self.getmap)
        self.toolMapSnapshot.setDefaultAction(self.actionMapSnapshot)

        self.toolSave.setDefaultAction(self.actionSave)
        self.toolCancel.setDefaultAction(self.actionCancel)
Пример #34
0
class LegendWidget(Ui_legendsWidget, QWidget):
    showmap = pyqtSignal()

    def __init__(self, parent=None):
        super(LegendWidget, self).__init__(parent)
        self.setupUi(self)
        self.canvasimage = QImage()
        self.items = {}
        self.framerect = QRect()
        self._lastextent = None

        self.legendareabrush = QBrush(QColor(255, 255, 255, 200))
        self.legendareapen = QPen(QColor(255, 255, 255, 20))
        self.legendareapen.setWidth(0.5)

    def paintEvent(self, event):
        def itemlist():
            for layer, items in self.items.iteritems():
                if len(items) == 1 and not layer.startswith("~"):
                    yield layer, items[0][1]
                else:
                    for text, icon in items:
                        if not text or text.startswith("~"):
                            continue
                        yield text, icon

        def _drawitem(pixmap, text, itempostion):
            painter.drawPixmap(itempostion, pixmap)
            textrect = QRectF(pixmap.width() + currentx + 10, itempostion.y(),
                              event.rect().width() - pixmap.width() - OFFSET_X,
                              pixmap.height())
            painter.drawText(textrect, text, QTextOption(Qt.AlignVCenter))

        def calcitems():
            font = painter.font()
            metrices = QFontMetrics(font)
            maxwidth = 0
            maxheight = 0
            for item, _ in itemlist():
                maxwidth = max(metrices.boundingRect(item).width(), maxwidth)
                maxheight = max(
                    metrices.boundingRect(item).height(), maxheight)
            return maxwidth, maxheight

        if not self.canvasimage:
            return

        painter = QPainter(self)
        painter.setRenderHints(QPainter.Antialiasing)
        painter.drawImage(event.rect(), self.canvasimage)

        itemwidths, itemmaxheight = calcitems()
        OFFSET_X = 30
        OFFSET_Y = itemmaxheight + 10
        rect = event.rect()
        neededheight = (len(self.items) * OFFSET_Y)
        columns = 1
        if neededheight > rect.height():
            columns = math.ceil(neededheight / float(rect.height()))

        framerect = QRect(rect)
        framewidth = (itemwidths + OFFSET_X + ICON_SIZE.width() +
                      100) * columns
        framerect.setWidth(framewidth)
        painter.setBrush(self.legendareabrush)
        painter.setPen(self.legendareapen)
        painter.drawRect(framerect)
        self.framerect = framerect

        painter.setPen(Qt.black)
        currenty = OFFSET_Y
        currentx = OFFSET_X
        position = rect.topLeft() + QPoint(OFFSET_X, currenty)
        for text, icon in itemlist():
            itempostion = QPoint(position)
            if currenty > rect.height():
                currentx = itemwidths + OFFSET_X + 100
                currenty = itemmaxheight + 10

            itempostion.setX(currentx)
            itempostion.setY(currenty)

            _drawitem(icon, text, itempostion)
            currenty += OFFSET_Y

        position.setY(currenty + OFFSET_Y)

    def mousePressEvent(self, event):
        if self.framerect.contains(event.pos()):
            return

        self.showmap.emit()

    def updateitems(self, layers):
        self.items = {}
        for layer in layers:
            if not layer.type() == QgsMapLayer.VectorLayer:
                continue

            try:
                items = layer.rendererV2().legendSymbologyItems(ICON_SIZE)
            except AttributeError:
                continue
            self.items[layer.name()] = items
        self.update()

    def _renderimage(self):
        image = self.renderjob.renderedImage()
        self.canvasimage = image
        self.update()

    def updatecanvas(self, canvas):
        """
        Update the canvas object for the legend background.
        """
        if self._lastextent == canvas.extent():
            return

        self._lastextent = canvas.extent()
        if QGis.QGIS_VERSION_INT > 20200:
            from qgis.core import QgsMapRendererParallelJob, QgsMapSettings
            settings = canvas.mapSettings()
            extent = settings.extent()
            settings.setOutputSize(self.size())
            settings.setExtent(extent)
            #settings.setFlags(QgsMapSettings.Antialiasing | QgsMapSettings.DrawLabeling )
            self.renderjob = QgsMapRendererParallelJob(settings)
            self.renderjob.finished.connect(self._renderimage)
            self.renderjob.start()
        else:
            if canvas.isDrawing():
                return

            pixmap = QPixmap(self.size())
            pixmap.fill(canvas.canvasColor())
            painter = QPainter(pixmap)
            painter.setRenderHints(QPainter.Antialiasing)
            renderer = canvas.mapRenderer()
            renderer.render(painter)
            del painter
            self.canvasimage = pixmap.toImage()
            self.update()
Пример #35
0
 def update(self, *__args):
     settings = self.canvas.mapSettings()
     settings.setOutputSize(self.previewImage.size())
     self.renderjob = QgsMapRendererParallelJob(settings)
     self.renderjob.finished.connect(self._renderimage)
     self.renderjob.start()
Пример #36
0
def write_texture(
    feedback,
    tex_layer,
    tex_extent,
    tex_pixel_size,
    utm_crs,  # destination_crs
    filepath,
    imagetype,
):
    """
    Crop and save texture to image file.
    """

    # Calc tex_extent size in meters (it is in utm)
    tex_extent_xm = tex_extent.xMaximum() - tex_extent.xMinimum()
    tex_extent_ym = tex_extent.yMaximum() - tex_extent.yMinimum()

    # Calc tex_extent size in pixels
    tex_extent_xpix = int(tex_extent_xm / tex_pixel_size)
    tex_extent_ypix = int(tex_extent_ym / tex_pixel_size)

    # Choose exporting layers
    if tex_layer:  # use user tex layer
        layers = (tex_layer,)
    else:  # no user tex layer, use map canvas
        canvas = iface.mapCanvas()
        layers = canvas.layers()

    # Image settings and texture layer choice
    settings = QgsMapSettings()  # build settings
    settings.setDestinationCrs(utm_crs)  # set output crs
    settings.setExtent(tex_extent)  # in utm_crs
    settings.setOutputSize(QSize(tex_extent_xpix, tex_extent_ypix))
    settings.setLayers(layers)

    feedback.pushInfo(
        f"Texture size: {tex_extent_xpix} x {tex_extent_ypix} pixels, {tex_extent_xm:.1f} x {tex_extent_ym:.1f} meters"
    )

    # Render and save image
    render = QgsMapRendererParallelJob(settings)
    render.start()
    t0 = time.time()
    while render.isActive():
        dt = time.time() - t0
        QCoreApplication.processEvents()
        if feedback.isCanceled():
            render.cancelWithoutBlocking()
            return
        if dt >= 30.0:
            render.cancelWithoutBlocking()
            feedback.reportError("Texture render timed out, no texture saved.")
            return
    image = render.renderedImage()
    try:
        image.save(filepath, imagetype)
    except IOError:
        raise QgsProcessingException(
            f"Texture not writable to <{filepath}>, cannot proceed."
        )
    feedback.pushInfo(f"Saved (in {dt:.2f} s): <{filepath}>")
Пример #37
0
class LegendWidget(Ui_legendsWidget, QWidget):
    showmap = pyqtSignal()

    def __init__(self, parent=None):
        super(LegendWidget, self).__init__(parent)
        self.setupUi(self)
        self.canvasimage = QImage()
        self.items = {}
        self.framerect = QRect()
        self._lastextent = None

        self.legendareabrush = QBrush(QColor(255,255,255,200))
        self.legendareapen = QPen(QColor(255,255,255,20))
        self.legendareapen.setWidth(0.5)

    def paintEvent(self, event):
        def itemlist():
            for layer, items in self.items.iteritems():
                if len(items) == 1:
                    yield layer, items[0][1]
                else:
                    for text, icon in items:
                        if not text:
                            continue
                        yield text, icon

        def _drawitem(pixmap, text, itempostion):
            painter.drawPixmap(itempostion, pixmap)
            textrect = QRectF(pixmap.width() + currentx + 10,
                              itempostion.y(),
                              event.rect().width() - pixmap.width() - OFFSET_X,
                              pixmap.height())
            painter.drawText(textrect, text, QTextOption(Qt.AlignVCenter))

        def calcitems():
            font = painter.font()
            metrices = QFontMetrics(font)
            maxwidth = 0
            maxheight = 0
            for item, _ in itemlist():
                maxwidth = max(metrices.boundingRect(item).width(), maxwidth)
                maxheight = max(metrices.boundingRect(item).height(), maxheight)
            return maxwidth, maxheight

        if not self.canvasimage:
            return

        painter = QPainter(self)
        painter.setRenderHints(QPainter.Antialiasing)
        painter.drawImage(event.rect(), self.canvasimage)

        itemwidths, itemmaxheight = calcitems()
        OFFSET_X = 30
        OFFSET_Y = itemmaxheight + 10
        rect = event.rect()
        neededheight = (len(self.items) * OFFSET_Y)
        columns = 1
        if neededheight > rect.height():
            columns = math.ceil(neededheight / float(rect.height()))

        framerect = QRect(rect)
        framewidth = (itemwidths + OFFSET_X + ICON_SIZE.width() + 100) * columns
        framerect.setWidth(framewidth)
        painter.setBrush(self.legendareabrush)
        painter.setPen(self.legendareapen)
        painter.drawRect(framerect)
        self.framerect = framerect

        painter.setPen(Qt.black)
        currenty = OFFSET_Y
        currentx = OFFSET_X
        position = rect.topLeft() + QPoint(OFFSET_X, currenty)
        for text, icon in itemlist():
            itempostion = QPoint(position)
            if currenty > rect.height():
                currentx = itemwidths + OFFSET_X + 100
                currenty = itemmaxheight + 10

            itempostion.setX(currentx)
            itempostion.setY(currenty)

            _drawitem(icon, text, itempostion)
            currenty += OFFSET_Y

        position.setY(currenty + OFFSET_Y)


    def mousePressEvent(self, event):
        if self.framerect.contains(event.pos()):
            return

        self.showmap.emit()


    def updateitems(self, layers):
        self.items = {}
        for layer in layers:
            if not layer.type() == QgsMapLayer.VectorLayer:
                continue

            try:
                items = layer.rendererV2().legendSymbologyItems(ICON_SIZE)
            except AttributeError:
                continue
            self.items[layer.name()] = items
        self.update()

    def _renderimage(self):
        image = self.renderjob.renderedImage()
        self.canvasimage = image
        self.update()

    def updatecanvas(self, canvas):
        """
        Update the canvas object for the legend background.
        """
        if self._lastextent == canvas.extent():
            return

        self._lastextent = canvas.extent()
        if QGis.QGIS_VERSION_INT > 20200:
            from qgis.core import QgsMapRendererParallelJob, QgsMapSettings
            settings = canvas.mapSettings()
            extent = settings.extent()
            settings.setOutputSize(self.size())
            settings.setExtent(extent)
            #settings.setFlags(QgsMapSettings.Antialiasing | QgsMapSettings.DrawLabeling )
            self.renderjob = QgsMapRendererParallelJob(settings)
            self.renderjob.finished.connect(self._renderimage)
            self.renderjob.start()
        else:
            if canvas.isDrawing():
                return

            pixmap = QPixmap(self.size())
            pixmap.fill(canvas.canvasColor())
            painter = QPainter(pixmap)
            painter.setRenderHints(QPainter.Antialiasing)
            renderer = canvas.mapRenderer()
            renderer.render(painter)
            del painter
            self.canvasimage = pixmap.toImage()
            self.update()
Пример #38
0
    def make_coco_dataset(self):
        layers = QgsProject.instance().mapLayers().values()
        vectorlayers = [layer for layer in layers if layer.type() == VectorLayer]
        rasterLayers = [layer for layer in layers if layer.type() == RasterLayer]

        vectorlayer = vectorlayers[self.dlg.vectorlayerselector.currentIndex()]
        rasterlayer = rasterLayers[self.dlg.rasterlayerselector.currentIndex()]

        img_size = int(self.dlg.imagesizeselector.currentText())
        scale_realworld = int(self.dlg.zoomlevel.currentText())
        buffer_size = int(self.dlg.buffersize.text()) / 100
        target_dir = self.dlg.dirselectline.text()
        dataset_description = str(self.dlg.datasetdescription.text())
        contributor = str(self.dlg.creatorname.text())
        url = str(self.dlg.url.text())
        version = str(self.dlg.datasetversion.text())
        license = str(self.dlg.licenseselector.currentText())

        # to implement
        scale_to_fit = self.dlg.scaletofit.isChecked()
        use_fieldcategory = bool(self.dlg.usecategoryfields.isChecked())
        categoryfield = self.dlg.categoryfields.currentText()


        QgsMessageLog.logMessage(str(scale_to_fit), "cocotrainer", level=Qgis.Info)
        QgsMessageLog.logMessage(str(use_fieldcategory), "cocotrainer", level=Qgis.Info)

        # prepare directories
        os.makedirs(os.path.join(target_dir, "train"), exist_ok=True)
        os.makedirs(os.path.join(target_dir, "val"), exist_ok=True)

        QgsMessageLog.logMessage("====================================", "cocotrainer", level=Qgis.Info)

        # TODO:
        #  - use field categories

        features_iterator = vectorlayer.getFeatures(QgsFeatureRequest().setFilterExpression('$area > 1'))

        features = [feature for feature in features_iterator]
        QgsMessageLog.logMessage("vector layer: " + vectorlayer.name(), "cocotrainer", level=Qgis.Info)
        QgsMessageLog.logMessage("Filtered polygons smaller than 1m2 from data", "cocotrainer", level=Qgis.Info)
        QgsMessageLog.logMessage("features: " + str(len(list(features))), "cocotrainer", level=Qgis.Info)

        options = QgsMapSettings()
        options.setLayers([rasterlayer])
        options.setOutputSize(QSize(int(img_size), int(img_size)))

        QgsMessageLog.logMessage(str(scale_realworld), "cocotrainer", level=Qgis.Info)
        QgsMessageLog.logMessage(str(img_size), "cocotrainer", level=Qgis.Info)

        date = datetime.now()

        cat_dict = {}
        if use_fieldcategory:
            categories = []
            # uniqueprovider = rasterlayer.dataProvider()
            fields = vectorlayer.fields()
            id = fields.indexFromName(categoryfield)
            uniquevalues = vectorlayer.uniqueValues(id)
            for i, val in enumerate(uniquevalues):
                categories.append({"supercategory": "object", "id": i, "name": str(val)})
                cat_dict[str(val)] = i
        else:
            categories = [{"supercategory": "object", "id": 0, "name": "CATEGORYNAME"}]
            cat_dict["CATEGORYNAME"] = 0

        coco_annotation = {
            "info": {
                "description": dataset_description,
                "url": url,
                "version": version,
                "year": int(date.strftime("%Y")),
                "contributor": contributor,
                "date_created": date.strftime("%d/%m/%y")
            },
            "licenses": [self.license_dict[license]],
            "images": [],
            "annotations": [],
            "categories": categories,
            # < -- Not in Captionsannotations
            "segment_info": []  # < -- Only in Panoptic annotations
        }

        bboxes, polygons = {}, {}
        for i, feature in enumerate(features):
            polygons[i] = feature.geometry()
            bboxes[i] = feature.geometry().boundingBox()

        if use_fieldcategory:
            cats = {}
            for i, feature in enumerate(features):
                cats[i] = str(feature[categoryfield])

        image_id = 0
        annotation_id = 0

        for i, feature in enumerate(features):
            self.dlg.progressBar.setValue((i / len(polygons) * 100))
            QgsMessageLog.logMessage("something is happening", "cocotrainer", level=Qgis.Info)
            geoms = feature.geometry()
            bbox = geoms.boundingBox()

            if scale_to_fit:
                xmax = bbox.xMaximum()
                ymax = bbox.yMaximum()
                ymin = bbox.yMinimum()
                xmin = bbox.xMinimum()

                diffx = xmax - xmin
                diffy = ymax - ymin
                xmax = xmax + (buffer_size * diffx)
                xmin = xmin - (buffer_size * diffx)
                ymax = ymax + (buffer_size * diffy)
                ymin = ymin - (buffer_size * diffy)

            else:
                midpoint = bbox.center()
                QgsMessageLog.logMessage("trying to log centerpoint", "cocotrainer", level=Qgis.Info)
                QgsMessageLog.logMessage(str(midpoint.x()), "cocotrainer", level=Qgis.Info)
                QgsMessageLog.logMessage(str(midpoint.y()), "cocotrainer", level=Qgis.Info)
                xmin = midpoint.x() - scale_realworld / 2
                xmax = midpoint.x() + scale_realworld / 2
                ymin = midpoint.y() - scale_realworld / 2
                ymax = midpoint.y() + scale_realworld / 2

            extent = QgsRectangle(xmin, ymin, xmax, ymax)

            fileid = "{}_{}_{}".format(xmin, ymin, scale_realworld)

            image = {
                "license": 0,
                "file_name": fileid + ".png",
                "coco_url": None,
                "height": img_size,
                "width": img_size,
                "date_captured": date.strftime("%d/%m/%y"),
                "flickr_url": None,
                "id": image_id
            }

            coco_annotation["images"].append(image)

            for j, geo in bboxes.items():
                if extent.contains(geo) or extent.intersects(geo):
                    points = polygons[j].asMultiPolygon()
                    xs = np.array([p.x() - xmin for p in points[0][0]])
                    ys = np.array([(ymax - ymin) - (p.y() - ymin) for p in points[0][0]])

                    xs *= (img_size / scale_realworld)
                    xs = np.round(xs)
                    xs = xs.astype(np.int32)

                    ys *= (img_size / scale_realworld)
                    ys = np.round(ys)
                    ys = ys.astype(np.int32)

                    ys[ys < 0] = 0
                    xs[xs < 0] = 0

                    polygon = [[int(p[0]), int(p[1])] for p in zip(xs, ys)]
                    flat_list = [item for sublist in polygon for item in sublist]
                    QgsMessageLog.logMessage(str(flat_list), "cocotrainer", level=Qgis.Info)

                    pixelposbbox = [int(np.min(xs)), (int(np.min(ys))), geo.width() * (img_size / scale_realworld),
                                    geo.height() * (img_size / scale_realworld)]
                    pixelposbbox = [number if number > 0 else 0 for number in pixelposbbox]

                    QgsMessageLog.logMessage(str([geo.xMinimum(), geo.yMaximum(), geo.width(), geo.height()]),
                                             "cocotrainer", level=Qgis.Info)
                    QgsMessageLog.logMessage(str(pixelposbbox), "cocotrainer", level=Qgis.Info)

                    annotation = {
                        "segmentation": [flat_list],  # format is [x1,y1,x2,y2]
                        "area": polygons[j].area(),
                        "iscrowd": 0,
                        "image_id": image_id,
                        "bbox": pixelposbbox,  # format is [top left x position, top left y position, width, height]
                        "category_id": cat_dict[cats[j]],
                        "id": annotation_id
                    }
                    annotation_id += 1
                    coco_annotation["annotations"].append(annotation)

            options.setExtent(extent)
            render = QgsMapRendererParallelJob(options)

            def finished():
                QgsMessageLog.logMessage("saving image to disk", "cocotrainer", level=Qgis.Info)
                fname = os.path.join(target_dir, 'train', "{}.png".format(fileid))
                img = render.renderedImage()
                if not img.save(fname, "png"):
                    print("Error saving image")

            render.finished.connect(finished)
            render.start()
            render.waitForFinished()
            image_id += 1

        QgsMessageLog.logMessage(str(coco_annotation), "cocotrainer", level=Qgis.Info)

        with open(os.path.join(target_dir, "annotation.json"), "w") as outfile:
            json.dump(coco_annotation, outfile)

        self.dlg.progressBar.setValue(100)
        self.dlg.finished_label.setText("parsed {} features".format(len(list(features))))