GSoC Indoor Tagging Support for OSM2World

Hi all

In a couple of weeks I plan to start work on OSM2World for Google Summer of Code. OSM2World currently renders in 3D many outdoor features which have been mapped in OSM, but unfortunately there is no support for indoor features as of yet. Over the next 3 months I plan to add this functionality and hope to post at least weekly updates of my progress here.

Feedback would of course be much appreciated.

OSM2World: https://wiki.openstreetmap.org/wiki/OSM2World

OSM GSoC: https://summerofcode.withgoogle.com/organizations/5154733884440576/#projects

Dan

Looking forward to it. Sounds amazing.

Exams have finished slightly earlier than expected so I can now start work on my project. Here is a rough plan for the coming weeks:

As I am now starting earlier, (if all goes to plan) there should be an extra week free to add more features

Dan

Week 1

Week 1 had a bit of a slow start as I could not compile the latest version of OSM2World, but as soon as this was fixed I started work on rendering some basic walls. Most of this time was spent creating a good framework for adding later features, so hopefully this was spent well.

Rendering walls of just 2 nodes worked perfectly, but due to how walls (indoor/outdoor) are rendered, multi-node walls were a bit more of a challenge.

The easiest fix for this was to just split multi-node walls into segments of 2 nodes. This will need to be updated later however when adding doors and windows etc.

Rooms were the next problem for the week. These consist of a wall round the edge, a ceiling and a floor, all of which were created without too much hassle.

Finally, indoor areas were the last task. Areas define just a floor, so as this functionality had already been implemented, indoor areas were quite quick to get working.

Bottom right: a basic room, level 0
Bottom left: a room containing a wall, level 0
Top Left: a room, level 0 and area, level 1
Top Right: a room, level 0 and a room, level 1

All objects are placed at a height corresponding to their level, this assumes that all levels of a building are of equal heights which may not be the case.

This also works for any shaped room, wall or area:

Great to hear (your) progress on OSM2World. Might inspire me to have a clother look at this indoor mapping thing.
I guess currently here we only have some pretty raw mapping like passages through malls / levels of shops / … .
Do you think it would be possible to show also underground levels e.g. by multi level train stations etc.?

Week 2

Starting to pick up speed this week getting plenty of tasks done.

Hopefully the screenshots will look a little better as I am now using the default textures.

Corridors

To start off with, I implemented rendering of indoor corridors, this was relatively straight forward as corridors only consist of a floor and a ceiling which are tasks that I have already overcome.

Multilevel Rooms

Not all rooms, corridors or walls are a single level high. The tagging standard says that indoor features spanning multiple levels can be tagged in any of the following formats: level=-1;5 , level=-1-5 or level=-1–2 and as such all of these should be supported. The below screenshot is from the inside of a room tagged level=2-6, it may be a little hard to tell the scale so there is a screenshot for a single level room as well.

Indoor Ways Take Precedence

Something that I had not thought about was that not all walls of a room will be made of the same material. To map this, a wall placed at the same level as a room and using the same nodes could be given a different material to the room. My code from last week could just about show this as it simply rendered the wall in the same place as the room wall, but this caused some artifacts and was not particularly nice to look at. This would also not have allowed partially transparent textures as you would just see to the room wall underneath. So a slightly more intelligent system was needed.

Essentially, any room wall segment that is is also being used by a wall segment will not be rendered, allowing for only the wall at that location to be shown. This does mean that multilevel rooms are rendered in “strips” corresponding to each of their levels and as such multilevel room walls that are not overlapped by walls will be rendered in many strips. This is not the most efficient as they could be rendered as one large rectangle, but this could be improved at a later date.

indoor=level

The indoor=level tag can be used for an area that outlines a whole level. This element can be given data such as the level name or height. This height data is now used to vary the height of objects placed at that level. Any level that does not have height data is given a default height based on the given height data and the height of the building, meaning the overall height of the building will not change. This can be most easily shown using the same room as above, here level 4 was given a height of 5 meters:

Underground

Finally, support for underground levels was added. This works as you would expect, with elements tagged with a negative level being rendered under the terrain. It is a bit hard to make out was is going on in the below image but there are walls, rooms, areas and corridors all being rendered beneath the terrain (which can’t be seen from below).

A small issue with this however is that when using srtm data the terrain runs straight through buildings. I am attempting to fix this early this week, cutting holes in the terrain for buildings with indoor data that intersect the ground.

Real World Example

All of the screenshots that I have used so far have been of test data so I though I would add a couple of real world examples. The screenshots below are from Aachen University (https://openlevelup.net/?l=0#18/50.77959/6.07545)

In the second image there seems to be problems with some rooms not being rendered properly, this is currently being looked into.

Yes. As underground levels are now supported this should be possible. Make sure to add the “building:levels:underground” tag to the building outline. I am not sure what will happen for things such as tube stations in London that are fully underground, so I may play around with that this week. Currently this code is not fully integrated into OSM2World yet so it will not be the easiest to use, hopefully I will make a pull request that is accepted in the near future. https://github.com/tordanik/OSM2World

Week 3

This week the majority of the minimum functionality was completed, as well as testing most of the output formats. The min_level and max_level tags were also implemented, however their logic was implemented incorrectly and so I will be fixing this over the coming days. The other features added are below.

Cut holes in terrain

A problem identified last week was the fact that terrain would be rendered throughout a building. This is fine when you are only looking at the outside of buildings, however is obviously an issue when rendering indoor features. To avoid this, for any building in contact with the terrain and with indoor elements, the outline of that building will be cut from the terrain.

Config Options

To provide rendering options to the user a config file can be provided to OSM2World. This contains key value pairs that can be used to determine how (or if) objects are rendered. More information can be found here: https://wiki.openstreetmap.org/wiki/OSM2World/Configuration_file.

In previous weeks, to view indoor elements I would have to manually prevent outer walls from being rendered in the code, and it was difficult to view individual levels. To make it easier to do these things, as well as allowing a user to do these a few config options have been implemented:

noOuterWalls - Boolean
noRoofs - Boolean
renderLevels - List of Integer
notRenderLevels - List of Integer

With non of these options a building may be rendered like this:

noOuterWalls = true
noRoofs = true
renderLevels = 1,6,7,8,9,15

noOuterWalls = true
noRoofs = true
notRenderLevels = 1,2,3,4,5

If levels are defined in both renderLevels and notRenderLevels, notRenderLevels will take precedence and that level will not be rendered. In the following example only levels 8,9 and 15 are rendered.

noOuterWalls = true
noRoofs = true
renderLevels = 1,6,7,8,9,15
notRenderLevels = 1,6,7

If there are any other config options that you think would be useful, I would love to hear them.

Roof Levels

Roof levels have not been rendered up until now. Due to the possible complex roof shapes is is difficult to render an object with the right shape or even determine whether an object is contained within a roof. After some discussion with my mentor, roof levels will be rendered ignoring the shape of the roof. This will allow objects to protrude from the roof, or even not be contained within the roof at all. This may be fixed at a later date.

Week 4

Progress was a little slower this week as I missed the best part of one of the days and some time was spent thinking about how to best implement some of the planned features for the coming week.

min_level, max_level and non_existent_levels

As mentioned last week I had already implemented functionality for these tags, however, I had misinterpreted their meaning. These tags only change how you perceive the levels of a building e.g. a building with 3 above ground levels could be given the tags, min_level=5, max_level=8 and non_existent_levels=6. All indoor objects on the ground floor should be given the tag level=5 and all objects on the top (2nd) floor should be given the tag level=8.

Lots of buildings have been mapped with only these tags, and as such you can’t tell where the objects should be placed in the context of the rest of the building. With min_level=-1 you don’t know whether the lowest level should be underground or not. To render this properly S3DB (https://wiki.openstreetmap.org/wiki/Simple_3D_buildings) tags are also needed so that the placement of each level can be determined. If these are not provided or do not align with the min_level, max_level and non_existent_level tags then non of the indoor data will be rendered and a warning will be produced. The minimum tagging needed is “building:levels=*”.

For a building with tags:

building=yes
building:levels=3
min_level=5
max_level=8
non_existent_levels=6

Inside this building are 4 rooms with level tags of 5,6,7 and 8. The room tagged with level=6 is not rendered.

Surface Tag

Rooms, areas or corridors could be given a surface tag to define what material their floor is made out of. Here are a few examples of typically outdoor textures being used:

Testing

Finally, I added some unit tests for some of the more easily testable parts of the code. This found one small error which was easily fixed. There isn’t really an interesting screenshot I can put here, so I won’t put one.

Week 5

Much of this week was again spent thinking about how to solve/best implement some features. Some progress was also made towards allowing windows to be transparent, however this has not been completed yet.

Placement of Windows and Doors

To start the week I went about placing indoor windows and doors.

Before this week, walls made up of more than 2 nodes were split into many smaller walls each consisting of only 2 nodes. This allowed for simpler code, but would not allow for placement of windows or doors as to render either of these objects one continuous wall is needed. To allow this, walls first had to be split less aggressively meaning long runs of nodes in a straight line would be rendered as only 1 wall. The code to render both windows and doors had already been implemented for outdoor walls so after this first problem, rendering them was not much of an issue. And after accounting for the “repeat_on” tag this was the result.

There is currently and issue with the door texture not scaling to the size of the door, this happens in older versions of OSM2World and is being looked into.

Windows must be placed on both sides of a wall. As they have some depth to them, and because walls currently don’t you see the backside of one window over the front of the other. Hopefully, walls will be given some thickness in the near future which should prevent this issue.

Shape Walls to Roof

In week 3, support for indoor roof levels was added. All indoor objects were placed regardless of whether they were in the roof, outside the roof or if they were poking through the roof. Objects completely outside the roof will have been incorrectly mapped, however these will still be rendered. If an object is fully contained within the roof however, it should conform to the shape of the roof. After some thinking and discussion of how this could be done best, indoor walls within certain roof types will conform to the shape of the roof:

Here is a room on the top level of a gabled roof. The roof can’t be seen from beneath.

Walls on lower levels must also be limited to the height of the level it is on as well as the height of the roof.

The ceilings are currently rendered as they were before, hopefully this will not be the case by next week.

Week 6

This week I had scheduled to implement stairways, escalators and elevators. Stairways and escalators will be rendered similarly to the way other existing objects will be, however I am waiting on some code from my mentor Tordanik for this.

There have been plenty of features mentioned in previous weeks that still need to be done however. A little more progress was made towards making windows transparent, a possible solution to creating openings in inner walls has been thought of, which will require changing some existing code and adding in some new bits, and the door texturing issue from last week was fixed simply by using the more up to date textures. Another feature mentioned previously was giving walls thickness and this is what I spent most of the week doing.

Give Floors and Walls Thickness

I started by giving ceilings 20cm of depth. No depth was given to floors as this would interfere with placement of doors. There is a texturing issue with the sides, I think I know what the issue is but I haven’t got round to fixing it yet.

I then moved on to giving walls depth. This was much more time consuming as the intersections between wall sides needed to be found, otherwise there would be gaps on the outside of wall bends. Giving walls depth also meant that they needed to be given a top, bottom and sides.

I also gave walls a thickness of 20cm, this just happened to be the right depth to render windows perfectly on both sides of the wall. This prevents the issue of seeing the backside of a window from last week.

There are still a few issues that need to be fixed however. At junctions of more than 2 walls, intersections are found correctly but this leads to an area where there is “no wall” and therefor a hole. I have not thought of a good solution to this yet, but hopefully it can be fixed.

Outer walls are also not accounted for, allowing for inner walls to be rendered on the outside. This obviously shouldn’t happen and should be fixed over the coming week.

The tops of walls also have the same problem as ceilings from last week. These both have very similar solutions and so once one is fixed the other should come soon after.

Most of these problems are edge cases and so in the majority of cases most walls look fine.

Week 7

I spent this week attempting to fix problems that have been found in previous weeks. There are still plenty of issues left however, and I will be working on these over next week.

Ceilings Within The Roof

This issue was first noted 2 weeks ago. Ceilings within the roof did not conform to the shape of the roof, instead being rendered straight through the roof. I started this week by trying to fix this. This was the result:

Clearly the ceiling does not have the correct shape. This is due to the way the plane is triangulated, similar to the problems found with curved walls in week 1. This cannot currently be fixed particularly easily without changing how the plane is being triangulated so has been put on hold for the time being.

Moving Walls On The Building Edge Inside

I spent the rest of the week moving walls on the building edge inside. This ended up taking a lot of time due to the complicated nature of the problem and some edge cases that arose.

However, I did eventually get this working and walls that are shared with the building area outline are rendered inside the building. Their intersections with other walls are also (mostly) calculated correctly.

Another advantage of this is that walls could be moved slightly further inside the outer walls, this prevents flickering issues with indoor walls being placed in exactly the same place as outer walls which can be seen in screenshots from week 3. The outer edge of the floors can still be seen however.

This does not work 100% of the time though. In some of my real world test data, nodes associated with indoor elements on the edges of buildings were not part of the building area, this makes it hard to determine whether the element is on the edge of a building or not, currently resulting in the wall not being moved inside.

In this screenshot there are also some odd triangles that appear on the outside of the building. I have my suspicions as to what these are, but this needs to be investigated further.

Finally, it won’t be too uncommon to have indoor elements sharing nodes on the edges of 2 adjacent buildings. Currently, each indoor wall will find where it intersects with the walls connected to its end nodes no matter what building that wall is a part of. This results in the walls being shaped as pictured below:

I think that this could be fixed when fixing the issue with holes at wall intersections from last week.

Week 8

This week I fixed some issues created in previous weeks as well as finishing off some features that had not been fully finished. A pull request has also been created which can be found here https://github.com/tordanik/OSM2World/pull/168

Transparent Windows

To start off the week I decided to finally finish making windows transparent. I have worked on this in small amounts between larger tasks so it has taken a little while to get done. I am also not as familiar with this part of the code, which meant more time was spent understanding how it worked.

Defining a texture for transparent windows is done similarly to other materials in the config file. The material name to be used here is “GLASS_TRANSPARENT”

Having transparent windows wasn’t as simple as just defining a new texture though. You should only be able to see through a window if there is something on the other side of it and as such windows that do not have some indoor area element on either side do not use the transparent glass material.

In the image below is a room spanning 2 floors adjacent to a room spanning only 1. The window is tagged with “window=yes” and “repeat_on=0-1”. As the top window only has a room on one side it is opaque.

This also works if the window is on the outside of the building.

Currently the window tagging that is accepted follows this proposed schema https://wiki.openstreetmap.org/wiki/User:Tordanik/Window_tagging

One issue with this is that windows placed in indoor walls will not be transparent as they will not have an area on both sides. Windows within walls could always be transparent or always opaque (the current implementation), but it is hard to think of a rule to decide either way.

Window Door Offset

One issue that arose from giving walls thickness a couple of weeks ago was that windows and doors would be offset on either side of the wall. This is due to the wall having different lengths on either side. This was not a big issue before windows became transparent, but became quite obvious afterwards.

This ended up being quite a simple fix although it took me far too long to think of it.

Junction Polygons

Another issue that arose from giving walls thickness was that open areas would be present at wall junctions. To fix this, as wall intersections are calculated the edges of the polygon hole are collected. After all walls are rendered the edges are used to form a polygon that fits the hole and this can be drawn in.

These always use the brick texture and do not work 100% of the time. But they do work well in the majority of cases.

This reduces the issue encountered last week with adjacent buildings, but not by much. One of the polygons does not render at all and the other is not the ideal shape to fix the issue. There is also a slight issue with the way the texture is displayed on the edge of the polygon.

Another issue that I found over the week is pictured below. As part of the left wall is on the building edge, it is moved inwards. The way segment at the junction however is not on the building edge so the intersections are calculated as if it has not been moved. This results a break in the wall, and no valid polygon that can fill it.

This again is just an edge case and in the majority of cases walls are displayed as intended.

Finally, I added some unit tests for wall intersections.

Week 9

I spent the majority of this week placing objects that are currently rendered by OSM2World. This involved using the code that my mentor had been working on over the last couple of weeks. The main idea of this is to define a set of surfaces and give objects “connectors” that attach to the closest surface.

Attach Bench To Correct Level

The first object I attempted to attach this week was a bench. This involved making all floors “attachment surfaces” and giving benches “attachment connectors”. After figuring out the best way of doing this, benches quite successfully attach to floors. The bench must be given a “level=*” tag. In the examples below I also gave a direction tag.

If no level tag is given the bench will not get an attachment connector and so will be placed at the level of the terrain, in this case the terrain happens to be at the ground level.

With tagging “level=-1”

With tagging “level=2”

Barriers

I next went about the placement of some barriers. Currently this uses the level tag to determine what levels the barriers should attach to. If multiple levels are given the first node of the barrier is attached to the lowest level and the last node is attached to the highest level. This does not currently take into account the “incline=*” tag or the ways that barriers could be attached to. Over the next week I plan to allow for this tagging as well as giving the first and last nodes a specific level tag.

Billboards

I then moved on to attaching billboards. Billboards had already been given attachment connectors, so all that was needed was to make indoor walls attachment surfaces and allowing billboards to take into account their level tag. Billboards must be given the tags “support=wall” and “level=*” to attach to indoor walls.

Tagging “level=2”

Tagging “level=0”

It seems that there is no billboard at level 0, but it just happens to be on the other side of the wall. Currently there is no way determine which side of the wall the billboard is placed on and I think it just gets placed on the closest side. One solution to this could be to take into account the direction tag.

Stairs

Stairs took some more thought due to the way they are currently implement. Naively attaching stairs just involved moving the code from from the barriers to a different place, this had the effect of allowing more barrier types to attach to surfaces but also meant the same problems from before also apply to stairs. If mapped in the right way however stairs attach to the correct levels.

Playing around with some mapping I also attempted to map a spiral staircase. This sort of works but it seems stairs only expect to be going in a straight line.

Small Changes

On top of attaching the currently existing objects I made some other small changes. Outdoor windows must also take into account the “min_level”, “max_level” and “non_existent_level” tags of their associated building part, otherwise they will not be placed correctly. Indoor walls now use one common texture on their tops/bottoms, this prevents the odd changes between materials and look of bricks that don’t align with the direction of the wall.

Week 10

This week I improved the rendering of steps as well as rendering elevators. Some area objects now also take into account their level tags and can also be attached to roofs.

Stairs Logic and Back-side

Carrying on from the end of last week I improved upon the attachment of stairs. Along with the the level tags given to the way, end nodes and attached objects with level tags can be used to determine what levels stairs will attach to. For example, the node at one end of the steps way can be given the tag “level=2” and the other “level=4”, this should cause the steps to be rendered between these 2 levels. Currently, intermediate level tags are not used.

If neither the way nor the nodes are given any level tags then the level tags of attached objects (e.g. area, corridors) can be used to determine what levels the steps should attach between. This obviously cannot be relied on when mapping as there could be discrepancies between the tagging of attached objects.

As well as this, stairs have also been given a “back-side”. To help the look of gaps between individual steps an asphalt texture was rendered underneath the steps. Previously this did not use any of the level data but instead attached to the terrain. Level data is now taken into account and so stairs have been given and underneath and back-side.

This also improves the look of the spiral staircase from last week.

This is not a perfect solution currently, tight corners cause a few issues. One possible solution to this is to change the step shape to fill the gaps, this is quite a complicated problem however and could take some time.

Elevators

I then moved on to rendering elevators. Currently not many door types are rendered by OSM2World which is why elevators have been given a normal indoor door which has a wooden texture, once there are sliding doors these should be used for elevators. Elevators in shafts should be mapped as an area and tagged with “highway=elevator”, “indoor=room” and “level=". Doors should be mapped as nodes on this area and tagged with "door=” and “repeat_on=*”

Area Attachments

Some areas can now be attached to attachment surfaces. These include sports pitches e.g. tennis, football and volleyball which already have implementations, swimming pools, parking and some aeroway areas. Examples of a tennis court and swimming pool are below:

Roof Attachment Surfaces

Flat roofs are also now attachment surfaces. This means objects that can attach to roofs can now do so. To attach an area like to ones discussed above they should be given the tag “location=roof”. One current problem with this is where there are building parts on top of other building parts. Each building part is given its own roof, however objects will attach to the closest of the 2. This can cause an object to correctly attach to a roof, but this may be inside another building part. A proposed solution to this is to also tag the object with a level tag. This should be the level above the top indoor level.

Curved Walls

Finally, a problem that I came across earlier in the week was with curved walls. This was from from a very intricately mapped church with lots of tightly curving columns. For some reason only every other wall segment would render causing the issue pictured below.

After some experimenting the problem was eventually found and fixed resulting in some quite nice looking columns.

As I am coming into the final week of the project these are the sorts of problems I will be focusing on more. I will be mainly fixing some of the smaller issues rather than adding any new big features

Week 11

This was the final week that I spent working on my project. No new features were added as the main focus was on cleaning the code and fixing any errors that I could find. A final report can be found in my diary here:

https://www.openstreetmap.org/user/DWeaver/diary/394059