Marble ready for Vector Tiling!

August ended and so did the coding for GSoC. Now all the pieces have to fit perfectly and that’s what we are working on. By the time writing this entry, all the code has been submitted to review and some of the parts have already been included in Marble’s master branch.

Here are the review requests for parts in which the code has been separated:

Extension for the .osm file parser
Kothic Json parser for vector data tiles
New dgml tag , with its parser and new GeoScenes
Remaining code changes>

So for this entry, I will summarize all the process done during this GSoC and how the new features work.

  • Teaching Marble a new tag for it to create vector tile layers.

A new tag <vectortile> has been added to the dgml parser. When using it to create a vector tile layer, the backend needs to be set also to “vectortile” (just the same as when we use another tag like texture (for image tiles), filter…)

Booth <vectortile> (for data tiles) and <texture> (for image tiles) tags, extend an abstract class called GeoSceneTiled which contains the common information for tiled layers and then each one creates its GeoSceneVectorTile or GeoSceneTextureTile.

Tests where made with Kothic’s JS tiles hosted on http://osmosnimki.ru/vtile. These tiles have a JSON-P-like format similar to the one OSM Data Tile Server will have (http://wiki.openstreetmap.org/wiki/Google_Summer_of_Code/2012/Data_Tile_Service/Tile_Format)

Example tile

{
“scale”: 100,
“features”: [
{"geometry": {
"type": "Point",
"coordinates": [50, 50] },
“properties”: { “color”: “orange” }
},
{ “geometry”: {
“type”: “LineString”,
“coordinates”: [[25, 25], [75, 25], [75, 75]]},
“properties”: { “color”: “blue” }
},
{“geometry”: {
“type”: “Polygon”,
“coordinates”: [ [[10, 50], [50, 90], [10, 90], [10, 50]],
[[20, 70], [20, 80], [30, 80], [20, 70]]] },
“properties”: { “color”: “green” }
},
{ “geometry”: {
“type”: “MultiLineString”,
“coordinates”: [
[[80, 10], [80, 0]], [[100, 20], [90, 20]]] },
“properties”: { “color”: “purple” }
}
]
}

OpenStreetMap’s server still is not ready but once it is, only minor changes will be needed.

Here’s the dgml example:

<layer name=”vectorosm” backend=”vectortile” >
<vectortile name=”geojson_data” expire=”604800″ >
<sourcedir format=”JS”> earth/vectorosm </sourcedir>
<tileSize width=”256″ height=”256″ />
<storageLayout levelZeroColumns=”1″ levelZeroRows=”1″ maximumTileLevel=”18″ mode=”OpenStreetMap” />
<projection name=”Mercator” />
<downloadUrl protocol=”http” host=”osmosnimki.ru” path=”/vtile/” />
<downloadPolicy usage=”Browse” maximumConnections=”20″ />
<downloadPolicy usage=”Bulk” maximumConnections=”2″ />
</vectortile>
</layer>

Important tags:

backend: Needs to be set to “vectortile” just like when we use other tags like “texture” or “filter”. When trying to create the layer it will check that the inside tag (in this case <vectortile>) is the same as the backend value.

expire: Sets in seconds when a tile will expire, in other words, when it will be considered as outdated and downloaded a new version of it.

format: Currently the format is not taken into account as the layer will try to open the file with all the available parsers in Marble. But if in the future this seems to be a bad idea, only the parser able to open this file format could be used.

sourcedir: folder where the tiles will be downloaded.

tileSize: tile size is needed in order to calculate which zoom level is being displayed and tiles that have to be downloaded to fit the screen. For OpenStreetMap tiles, its 256×256.

For this piece of code to work, (as also is needed for OSM images layer) you will need to create a base 0 tile inside the maps folder. Here’s a new earth map based on atlas layer and this vector data layer that can be used. by extracting the zip file into maps/earth/ folder. (Note that this will only work once all the code has been merged to Marble’s branch).

EXAMPLE VECTOR TILE MAP FOR MARBLE

  • Now that a new layer has been parsed, it’s time to create it.

Marble already had a TextureLayer class for downloading and mapping the image tiles and depending on the selected map projection (Globe, Plane Map or Mercator), called a different image mapper (EquirectScanlineTextureMapper, MercatorScanlineTextureMapper or SphericalScanlineTextureMapper) that does different operations to generate the world’s image.

For data tiles, all vector data is inserted into the maps TreeModel (a class inside MarbleModel that renders vector data) and this class already manages projection changes. In addition, logic for vector tile downloading and storing is different (due to the amount of memory vector data needs compared to images). So in this case a new class VectorTileLayer completely independent from TextureLayer has been created.

When creating or changing the map theme, MarbleMap will ask if the new selected theme has any GeoSceneTiled. If it does it will iterate through them and by checking their <backend> tag it will distinguish between an image tile layer (texture) and vector tile layer (vectortile) adding them to the layer manager.

  • How VectorTileLayer works.

It has the logic for downloading, rendering and managing vector tiles. The main problem with this is the amount of memory and process it needs. The layer has a pointer to the maps TreeModel (the class that renders vector data) and an extra cache that will store all the tiles that are being displayed in screen in each moment.

When the map is moved every tile that doesn’t intersect with the screens bounding box will be removed from the extra cache and theTreeModel, to avoid having to process geometries that are not inside the screen. Tiles are only removed from the extra cache, not deleted because they will be also in Marble’s main cache.

Everytime the map is moved or updated, the layer will calculate if the zoom level has changed and if it did change, all tile geometries will be removed and new tiles for the new zoom will be loaded by calling VectorTileMapper class. If the zoom didn’t change, it will check the screen and tile’s intersection to remove not displaying tiles as previously explained.

  • VectorTileMapper downloads data tiles.

Image tile layers use different mappers depending on the maps projection but for vector tiles, changes between different projection are already managed by the TreeModel so only one mapper is enough. When the map is moved, VectorTileMapper will calculate the new screens X and Y. Comparing them to the previous ones it will know in which direction the map has been moved, download the required tiles and create a StackedTile. Marble’s main tile cache uses the class StackedTile to store tiles.

  • StackedTile “wraps” image and vector tiles.

The StackedTileLoader stores tiles, manages them and launches their download if missing or expired. In the beginning it only supported image tiles but now StackedTile supports two types of tiles TextureTile and VectorTile. Booth extend the Tile class, created to contain the common attributes from a tile such as format or tile ID. Then TextureTile contains an image and VectorTile geometries.

When a tile is missing, StackedTileLoader calls MergedLayerDecorator to download it, by looking at the GeoSceneTiled’s nodetype() method it chooses between creating a TextureTile (if it is a GeoSceneTextureTile) or a VectorTile (if it is a GeoSceneVectorTile) and it wraps it inside a StackedTile.

Some demos of how it works:

As you can see not all the tiles have been created yet.

There are tiles up to 5MB size and it takes a little to load them and move the map. With the new OSM server, tiles will be about 200KB big.

There are still some little changes that I want to work on but if anyone wants to know more about how vector tiles work or why some things have been made in a certain way they can contact us on IRC #marble channel (ander) or ander.pijoan(at)deusto.es

I would also like to thank everyone involved in the project, all the KDE and Marble community and specially my mentor Earthwings, Tackat, Idis, Shentey, Cezar, Javi and all the anonymous people who share their knowledge on the net.

VectorTileMapper for managing vector tiles

I spent last week and part of this one making tests to see how Marble gets on with Vector Tiles. There are vector tiles up to 5.8MB size depending on the planet zone and its detail level, so the need for a new logic for storing and managing tiles was obvious.

Vector Tile LayerAs talked in the previous blog entrance, TextureLayer keeps all the image tiles in memory and that would be impossible for vector tiles. Depending on the map projection selected (Marble’s map can be viewed as a Globe, Plane Map or Mercator) image tiles had three different mappers that “blended” the images to fit the map and a logic for downloading image tiles. In our case, parsed geometries are inserted into Marble’s treeModel which takes care of those three projections so we didn’t have to worry about that and it made possible to create a single mapper (VectorTileMapper) and create it’s logic in it.

VectorTileLayer2

When VectorTileLayer detects the map has moved, currently asks the VectorTileMapper to download all the tiles contained inside the screen’s LatLonBox and according to the screen’s zoom level. The following functions translate latitude and longitude coordinates to OpenStreetMaps X and Y coordinates.

int VectorTileMapper::RenderJob::lon2tilex(double lon, int zoom)
{ return (int)(floor((lon + 180.0) / 360.0 * pow(2.0, zoom))); }

int VectorTileMapper::RenderJob::lat2tiley(double lat, int zoom)
{ return (int)(floor((1.0 – log( tan(lat * M_PI/180.0) + 1.0 / cos(lat * M_PI/180.0)) / M_PI) / 2.0 * pow(2.0, zoom))); }

VectorTileMapper will ask Marble’s TileLoader for those tiles and the TileLoader will work as usual returning the StackedTile (if it was already parsed and stored in TileLoaders cache) or downloading, creating a new StackedTile from parsing the file, storing it in its cache and returning it. VectorTileMapper will then send the tile to VectorTileLayer.

Now that the tiles have arrived to the layer, their GeoDataDocuments are inserted into the TreeModel to be displayed and also to the layers QCache that will manage the displaying geometries. Currently for testing, everytime the map is moved, this QCache checks if the displaying GeoDataDocuments intersect with the screen’s LatLonBox and if they don’t they are removed from the TreeModel. For testing also, when changing between zoom levels, all GeoDataDocuments are removed from the TreeModel and QCache is cleared.

Here is a video test with the current status.

As you can see, although not downloading all the tiles in the screen sometimes it is difficult to move the map because Amsterdam has big detail level. Now we will have to start adjusting the download logic (to download more “neighbour” tiles), the parser and how many tiles can be kept in cache to find a performance balance.

New logic for vector tiles

Now that vector tiles can be downloaded and rendered, last week I made several tests to see how Marble deals with them and where the bottlenecks could be. Soon the (obvious) differences between managing image files and vector files (some of them up to 9MB size). Image tiles once downloaded are kept in memory all the time and that is impossible for vector tiles. So talking with some Marble experts they suggested creating a new layer that would have a new logic for vector tiles.

Vector Tile LayerThe code only supported one tiled layer so some changes have been made and now both layers work together. When writing these lines I thought about trying to use more than one texture layer and also a vector tile together because now all type of combinations are possible. Should I limit texture tiles just to one or this could bring more flexibility in order to create different maps?

Users could get confused and put together a tiled image layer and a static image layer which shouldn’t be made:

Mixing LayersHowever being able to use more than one image tile layer could also bring new possibilities to create hybrid maps by using transparency and blending them.

Mixing LayersComing back to the new logic, the plan now is to make VectorTileLayer save each vector tile’s latitude and longitude box and when the piece of map being displayed doesn’t contain that latlonbox or the map is in a different zoom level from the tile, remove it from memory.

First vector tiles rendering

I am going to have a happy weekend, at last the first vector tiles started to appear in Marble!!

I finished the parser for Osmosnimki.ru tile a few days ago and with it,independent .js files loaded from the computer where parsed and rendered. But the hard part was now to create all the code for managing vector tiles.

Json Parsing 1Json Parsing 2For DGML format, a new tag <vectortile> has been created for vector tilling.

Marble’s code had only TextureTile class for handling downloads, images, tile zoom levels and rendering. The best solution was to create a new abstract class called Tile than handles common tile manipulation and then make TextureTile (containing just the image) and a new VectorTile (containing a GeoDataDocument with all the vectordata) inherit from it. Some similar processes have been done in some other parts of the code.

Now when Marble reads the tag <vectortile>, it creates a VectorTile that downloads files just the same way as it used to do with images but now it will call all the available data parsers to extract vector data from the downloaded file. In the future if calling all the parser turns out to be excessive, maybe we could choose which parser to call by reading the format attribute in the DGML map and comparing it with the available parsers.
When each VectorTile downloads its file, it parses it and creates a GeoDataDocument which is sent to the TreeModel in order to be rendered.

Sounds quite simple but I must admit that there have been some big problems to make this possible.

This is a first view of the how vector tiles start to be downloaded and rendered when we travel through the globe:

Vector tilling demo 1

As you can see the server only has some tiles so not all of them are downloaded. There still lots of things to fix but at least for this week I’m very happy with the work done :)

The black planet

When discussing about which format we should use for vector tiles we had a look to another GSoC project which is being done in OpenStreetMap to create a data tile server.

This project is being developed by Michael Daines and with Darafei Praliaskouski assigned as his mentor. I got in touch with them to ask what the data tile server and its format would look like to see if we could make some first tests with it. They are going to create a JSON tile server with similar tiles as the ones exported from Xappy.js. This JSON tiles, have a schema very similar to the OSM XML tiles, separating data into nodes, ways and relations.

skeleton

    {
        "version": 0.6,
        "generator": "xappy.js",
        "xapi": {
             "planetDate": 201106161601,
             "copyright": "XXX"
        },
        "elements": [
            ...
            ...
            ...
        ]
    }

node

    {
        "type": "node",
        "id": 3596186,
        "lat": 53.4633699598014,
        "lon": -2.22667910006381,
        "timestamp": "2007-06-21T17:10:58+01:00",
        "version": 2,
        "changeset": 2213,
        "tags": [
            "amenity": "hospital",
            "name": "Manchester Royal Infirmary"
        ]
    }

way

    {
         "type": "way",
         "id": 4958218,
         "version": 3,
         "timestamp": "2007-07-25T01:55:35+01:00",
         "changeset": 2211,
         "nodes": [
             218963,
             331193
         ],
         "tags":[
             "landuse": "residential",
             "source": "landsat"
         ]
     }

relation

    {
        "type": "relation",
        "id": 2670,
        "timestamp": "2007-10-25T03:05:34Z",
        "version": 32,
        "changeset": 2211,
        "members": [
            {
                "type":"way",
                "ref":3992472,
                "role": ""
            },
            {
                "type":"way",
                "ref":3992524,
                "role": ""
            }
        ...
      ],
        "tags":[
            "name": "Fonnereau Way",
            "network": "Ipswich foothpaths",
            "type": "route"
        ]
    }

The idea of using JSON to reduce the data amount was good but this schema still has lots of data.

In fact there’s a data tile server for Russia called Osmosnimki.ru, created by Vladimir Agafonkin, Maksim Gurtovenko and Darafei Praliaskouski (previously mentioned GSoC project’s mentor) which has built its tiles using GeoJSON format. GeoJSON defines built geometries instead of having to build them ourselves by connecting nodes, ways and relations.

Here’s an example of what the file we send through the net would look like:

{“features”:[

      {"type":"Polygon",

            "properties":{"natural":"coastline"},

            "coordinates":[[[0,0],[0,10000],[10000,10000],[10000,0],[0,0]]]},

      “type”:”Polygon”,

            “properties”:{“admin_level”:8,”name:en”:”Minsk”,”boundary”:”administrative”},

            “coordinates”:[[[0,0],[0,10000],[10000,10000],[10000,0],[0,0]]]},

      “type”:”Polygon”,

            “properties”:{“admin_level”:2,”name:en”:”Republic of Belarus”,”boundary”:”administrative”},

            “coordinates”:[[[0,0],[0,10000],[10000,10000],[10000,0],[0,0]]]},

      “type”:”LineString”,

            “properties”:{“foot”:”yes”,”highway”:”footway”},

            “coordinates”:[[8653,10000],[7273,0]]}],

            “bbox”:[27.549934387207035,53.879654718982998,27.550277709960923,53.879857101499475],

            “granularity”:10000};

We decided to do some first tests using this server (http://osmosnimki.ru/vtile/) to see which attributes and what info our data tiles should have.

So it’s time to get the coding started. Currently the .dgml format doesn’t allow vector tiles and all the “texture maps” logic, is thought for image rendering. I started doing some changes for the .dgml parser and layer creation.

We have decided to create a <vectorTile> tag for vector tile maps. It will need all the same attributes as the <texture> tag such as sourcedir, tileSize, downloadUrl… but instead of creating a ImageTextureLayer it will create a VectorTextureLayer. At the time writting these lines, Marble downloads the GeoJSON files and tries to render them as if they were normal images, here we have our black planet:

GeoJSON downloading and not rendering exampleNext step is to dive through the code, prepare Marble for more tile types than just images and to parse the GeoJSON.

Vector data rendering and layout in Marble

Now that Marble’s OSM file parser is ready to understand the whole OSM Schema, I started messing around with the feature and geometries layout. This took me to a point where some important decisions have to be taken.

Currently the feature’s tags are stored and then some tag combinations (the most representative ones) are searched for translating them to the best fitting color and rendering style:

["landuse=landfill"] = createWayStyle( Qt::transparent, Qt::transparent, false, false );

["railway=rail"] = createStyle( 2, 5, “#989898″, “#E1E1E1″, true, true, Qt::SolidPattern, Qt::DashLine, Qt::FlatCap, true );

This has to be hardcoded and its not very flexible with strange tag combination that might be needed in some cases. I asked Earthwings and Tackat which color style Marble was following for rendering OpenStreetMap vector data and they told me it was Mapnik’s color style. Mapnik is an open source toolkit for rendering maps and OSM uses it for the standard layer you can find in their maps. It is possible to download Mapniks stylesheet (which is an XML file with the rendering rules) and modify it to render the way or the data you want.

Mapnik StylesheetSo one of my questions is how would it be to directly parse this stylesheet for rendering the data just the same as in Mapnik and, even more, for people to be able to create their own styles in the future? In fact that’s what other layers do for highlighting features they want.

Mapnik different layersThis question also takes me to the another one. As you might have noticed, mapnik stylesheets also define the zoom levels where features should be painted. Currently vector data is rendered no matter what zoom level the world is. For my GSoC I am going to create a tilling schema with a strategy for only downloading the necessary data for each zoom level so:

Should only the download process ask for some predefined data types depending on the zoom level and then Marble just render all the downloaded data following the stylesheet?

or

First parse the XML stylesheet to store all its values in memory and make users able to decide features’ zoom levels for when the download process asks for data?

First steps

Going through Marble source code for parsing .osm files I suddenly realized it was not parsing Relations. As the OSM Wiki explains, these elements are used to group different geometries that have tags in common or to build complex polygons.

OSM MultipolygonsIn fact, nowadays there’s a wide discussion whether relations should be used to reuse as much ways (OSM’s data primitive for representing lines) as possible when building geometries. Some people think it is better for search engines to have each geometry described as a way even if it creates overlapping linestrings when having one building next to the other.

Building geometries as WaysSome other people think it is better to reuse the ways by creating relations that group the ways needed to create the buildings geometry.

Building geometries as RelationsFollowing this technique we have the city of Girona in Spain which currently Marble’s .osm parser renders like this.

Girona in MarbleIn this first days I’ve been working on parsing relations to be able to render them as vectors. This requires to join the relations ways that build the geometry in the correct order, to distinguish whether it is an outer or an inner geometry and to read their tags in order to render the information. There are few little details that still need to be worked out but here you can see the result.

Girona in Marble after parsing RelationsRelations can also be used for grouping long ways such as highways or railways. By doing this, renderers, gps devices, etc. know that the whole geometry is obtained by joining small ways inside the relation. So if we download an area where one of these highways is located, it’s not neccesary to download the whole highway, we would just need to download the ways that are in our area.

The beginning

This morning, I woke up while the Internet was still asleep so I decided to write the first entry of this “logbook” that will record all the events and atmostphere during this Google Summer of Code development.

GSoC 2012

GSoC 2012

I will try to write a happy first entry, because as the project goes on and problems start to show up, I might not be so happy =).

My name is Ander and I am a 24 year old computer science engineering student at the University of Deusto in Bilbao (Spain). I am completing my diploma thesis with the Energy Lab of DeustoTech, the Technological Institute of the University of Deusto and due to some neccesities in one of our research projects I had to develop a tool to extract all the useful data from the Spanish Cadastre database to OpenStreetMap called Cat2Osm.

By changing the layer with the right top button, you can watch a Demo we made for Ciudad Real, a city in Spain.

http://130.206.138.173/OpenLayers/

Cat2Osm results comparison.
Cat2Osm results comparison.
Cat2Osm results comparison.

Cat2Osm results comparison.

Due to this project, my thesis director told me that I could try and take up a GSoC and so I did. I found a proposal from Torsten Rahn that had lots of things related with the work I made with Cat2Osm and can be very usefull for improving or validating the data exported from it.

Marble is an Open Source geographical tool for displaying and interacting with atlas data and different thematic maps. It currently supports .osm files but they have to be loaded manually and the zoom levels do not fix well. There is still a lot of work to be done to improve the .osm renderer and the focus is now set on the question whether to render the vector data or just to display the tile map and all the problems that would need to be faced. This project will enable interacting with real vector data, making more precise calculations or even 3d layers rendering based on .osm building tags.

OSM 3D rendering.

OSM 3D rendering.

So I wrote the proposal and… HERE I AM, prepared for an extreme summer working hard for this GSoC. I met yesterday Dennis, who is going to be my mentor and we are setting everything up to start as soon as possible!!

As soon as everything starts running, I will keep you all updated!! Take care!!