PDA

View Full Version : Infinite (circular) QGraphics Scene



maitai
22nd March 2011, 10:52
Hi all,

Our application presents a world map with several items (boats, etc) inside a QGraphicsScene and an associated view. All is fine but we would like to be able somehow to scroll the view infinitely, i.e. when you go right (east), the scene should be "circular" and continuous, and in the end should reshow the same position because you made a complete world round-trip.

We have been thinking of differents solutions, for instance having 2 scenes (one centered on meridian, the other on antemeridian) and to switch from one to another "on the fly"... but it seems a bit heavy to say the less... (would imply also to duplicate all objects). Another idea would be to move everything in the scene and re-position the view whenever we reach a border, but again it seems a bit extreme...

Any clever idea about how to achieve this? Are we missing something obvious?

Thanks

wysota
22nd March 2011, 11:25
You don't need two scenes. What you need is "stitching" two ends of the scene together by changing the way the view paints the scene and interacts with it. It's likely you will have to reimplement all virtual methods in QGraphicsView and translate coordinates based on the current "rotation" (or "position" if you may) of the scene. You will also have to trigger an additional round of drawing items when the "wrapping" part of the scene is visible in the view. This is quite much of work and requires study of Qt's source code. It might prove easier to render the whole scene to a pixmap and do the wrapped scrolling using a custom view based on QAbstractScrollArea rather than QGraphicsView. It all depends how "interactive" your view should be.

maitai
22nd March 2011, 11:42
Thanks for the reply and the tips.

We will investigate this. The scene is relatively "static" so maybe adapting QGraphicsView is a good solution. Our previous version was rendering everything in a single pixmap as you suggested, but that implied a lot of painting that we were trying to avoid by using native QGraphics objects.

I'll post here if we make some progress on this...

wysota
22nd March 2011, 12:06
You don't have to render everything. You know which parts of the scene are visible (based on the scroll position) so you can request only those parts, stitch them together in one pixmap and render it to the view. You can even cache chunks of the final pixmap to avoid rerendering the parts you already have.

Added after 5 minutes:

You can also completely ignore graphics view and use a custom architecture like I did here:

6122

oxygen77
27th March 2011, 13:41
You can also completely ignore graphics view and use a custom architecture like I did here:

6122

And what was the general idea or your custom architecture ?

wysota
27th March 2011, 14:51
The general idea was to make the widget work in map (lat, long) coordinates and display tiles and additional symbols. It's basically a widget showing a bunch of pixmaps. Since I know which symbols are visible when (based on tile coordinates and double sorted list of items) graphics view doesn't provide any practical benefits.

SixDegrees
27th March 2011, 14:59
This problem is often handled by implementing a tile server. In the simplest form, you have a map at fairly high resolution that is accessed as an array of tiles. The tiles can vary in size, and in many cases can be precomputed, but your client program determines the center and extent of the view and the server figures out which tiles are required to fill in the resulting rectangle. Tile position can be handled in a number of ways, but is often embedded in the filename.

This can easily be modified to work locally and pull subsets out of a master image. It works extremely well for closeup views, and avoids much unecessary image duplication and waste. When zoomed out very far - views that encompass the entire map, or nearly so - there can be extensive duplication; this is solved by serving lower resolution images, either precomputed or generated on the fly.

oxygen77
27th March 2011, 15:15
We have already coded this part. We have a system that handle a double cache: one on the disk for already genereated tiles, another one in memory for the recently drawn tiles. For missing tiles we generate them using a multi-threaded system.

We are drawing other info on the map like wind, Point Of Interest, ...

Our current problem is to use the scene/view system in a way we can have a circular map

SixDegrees
27th March 2011, 15:38
We have already coded this part. We have a system that handle a double cache: one on the disk for already genereated tiles, another one in memory for the recently drawn tiles. For missing tiles we generate them using a multi-threaded system.

We are drawing other info on the map like wind, Point Of Interest, ...

Our current problem is to use the scene/view system in a way we can have a circular map

I understand that. Consider the simple case, where the view is only a small portion of the entire (world) map; simplify further and consider that the tiles consist only of a single row, instead of the two dimensional array that would really be used, and that there are only ten tiles, numbered 0-9. The view determines where it is centered and the extent of the view and sends that information off the the tile server. The server determines which tiles are needed to fill that particular view, and their arrangement. Let's say the server determines that tiles 4, 5 and 6 are required; it sends those tiles, along with some information about how they should be displayed, in this case simple ordering will do: 4 5 6. Now, if there are only ten tiles that span the entire map, let's look at the case where things need to "wrap around": the server determines that tiles 8, 9 and 0 are needed, and that they should be displayed in that order: 8 9 0. Or it determines that tiles 9, 0 and one are needed in the order 9 0 1. The wrapping problem is solved, and only the portions of the map needed (plus a small amount that slops over the view edges) are required.

This is how GIS handle such things.

If you are trying to visualize the entire span of the map, the same approach works, but your single tile is the size of the map. You can make the client smart enough to reuse that single tile without too much trouble.

oxygen77
27th March 2011, 15:44
I suspect that what is blocking us for now is that we use the scene/view mechanisms to handle part of the pan and zoom process. This way we need an almost infinite scene

wysota
27th March 2011, 16:23
This problem is often handled by implementing a tile server. In the simplest form, you have a map at fairly high resolution that is accessed as an array of tiles. The tiles can vary in size, and in many cases can be precomputed, but your client program determines the center and extent of the view and the server figures out which tiles are required to fill in the resulting rectangle. Tile position can be handled in a number of ways, but is often embedded in the filename.
That's exactly how my widget works. Thus handling wrapping is as simple as bounding the coordinates and mapping them to proper tiles. Since zooming works by replacing one tile with four other tiles Graphics View doesn't help much here. There is a map API in QtMobility that is based on Graphics View and you can implement your own tile content-provider for it but IMHO it's a bit of an overkill. Still it might be easier in terms of software development to implement a tile provider for a solution that already exists instead of trying to implement your own that works in a similar way.