5

I'm writing a C++ application using QGIS API and I would like to improve the performance.

I want to render 5 layers:

  • one raster layer using gdal to load Bing/Google Map tiles
  • 4 custom QgsMapLayer (using plugin layer type) to draw different lines/polygons/points in WGS84.

For now, each of these layer is added to the MapCanvas, using:

QgsMapLayerRegistry::instance()->addMapLayer(customLayer);
QList <QgsMapCanvasLayer> myLayerSet;
myLayerSet.append(QgsMapCanvasLayer(cutomLayer,TRUE));
setLayerSet(myLayerSet);

On the canvas, I can have map tools, like panning, zoom in/out. I use custom layers because I want to easily decorate lines and polygons, using QPainter. This works, and I'm able to display what i want, where i want (using coordinate transforms from WGS84 to pseudo mercator).

My problem is: one of my custom layers draws a GPS point with some circles around it. This layer is refreshed every second. To refresh my layer, I use the canvas->refresh(), but that refreshes all other layers as well, and by consequence, the GDAL layer. This takes a lot of time and it's not necessary.

I have read some posts here mentioning the use of layer->setImageCache(), or by sub-classing QgsMapCanvasItem. Unfortunately, none of theses solutions works as I expect. setImageCache apparently doesn't work at all, or I don't know how to use it properly.

Sub-classing QgsMapCanvasItem allows me to implement the layers that need frequent refresh, and refresh them without refresh Raster Layer but:

  1. when panning the map, drawn objects don't follow the map (as a rubberBand can).
  2. when i use zoom in/out, the selection rectangle erase what was drawn on my canvasItem, without triggering a redraw.

I don't see how to do that, even if this subject seems relatively common.

Am i on the right way or is there a better way?

EDIT :

thanks to Nathan, my problem is resolved, and i confirm that custom QgsMapCanvasItem is the solution to independently refresh some layers.
just keep in mind to use toCanvasCoordinates(), and implement boundingRect() properly.

5

Using QgsMapCanvasItem is the way to go here as you will not have to refresh the canvas. You just have to make sure your implementation is correct.

Here is a basic example (taken from https://github.com/DMS-Aus/Roam/blob/master/src/roam/gps_action.py)

class GPSMarker(QgsMapCanvasItem):
        def __init__(self, canvas):
            QgsMapCanvasItem.__init__(self, canvas)
            self.canvas = canvas
            self.size = 24
            self.map_pos = QgsPoint(0.0, 0.0)
            self.svgrender = QSvgRenderer(":/icons/gps_marker")

        def setSize(self, size):
            self.size = size

        def paint(self, painter, xxx, xxx2):
            self.setPos(self.toCanvasCoordinates(self.map_pos))

            halfSize = self.size / 2.0
            self.svgrender.render(painter, QRectF(0 - halfSize, 0 - halfSize, self.size, self.size ) )

        def boundingRect(self):
            halfSize = self.size / 2.0
            return QRectF(-halfSize, -halfSize, 2.0 * halfSize, 2.0 * halfSize)

        def setCenter(self, map_pos):
            self.map_pos = map_pos
            self.setPos(self.toCanvasCoordinates(self.map_pos))

        def updatePosition(self):
            self.setCenter(self.map_pos)
| improve this answer | |
  • Thanks Nathan, it's works. regarding that drawn object did not follow the map, when panning, it was due to a missed call to "toCanvasCoordinates". regarding my second point, the erased objects where due to a missed implementation of boudingRect() Method. the only problem i have now, is "How can i call toCanvasCoordinates directly on a custom QgsMapLayer" but i can move forward. – atonda Dec 30 '13 at 9:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.