26 October 2016

A HoloLens airplane tracker 6–adding an airport (and a tower)

Intro

This episode is nearly codeless, it’s all Unity and general geeky GIS stuff. We are going to cut out a piece of OpenStreetMap to depict the location of Schiphol. We are also putting a real 3D ATC tower on the map. If you look closely, it has only a very superficial liking to that of the actual Schiphol ATC tower, but those are just minor details ;)

schiphol[3]Getting and positioning the map

Getting it was easy enough. As I wrote, I went about to cut out a piece of rectangular OpenStreetMap that contains Schiphol airport and, incidentally and certainly not coincidentally, the Wortell offices. We will deal with that in the final episode of the series.

Now positioning that map is a little tricky, as I have set the center Unity’s coordinate system to the center of Schiphol according to Google Maps. But the center of Schiphol, according to this nifty site which also displays coordinates, is found at a location that is about 25% from the bottom of the image and a little to the left.

image

The way I solved this was with a little bit of knowledge of geo coordinates and an advanced technique I learned in my years in Geo Information Systems, which can be described as ‘fiddling until it fits’ ;)

First order of business is to try and find out as closely as you can the lat/lon coordinates of the upper left and the lower right corner of the image. I also used latlong.net for that. As far as I could see, top/left is (52.368739, 4.706697) and bottom right (52.285193, 4.811196), while the center of Schiphol is (52.307687, 4.767424)

Now we need to go back to a precursor to this series in which I explained how to convert lat/lon coordinates to Unity’s X/Y/Z coordinates using a tangential plane. We can convert these coordinates using this code

double x, y, z;

GpsUtils.GeodeticToEnu(52.368739, 4.706697, 0, 52.307687, 4.767424, 0, 
  out x, out y, out z);
Debug.WriteLine($"{x / 15000},{y / 15000}");

GpsUtils.GeodeticToEnu(52.285193, 4.811196, 0, 52.307687, 4.767424, 0, 
  out x, out y, out z);
Debug.WriteLine($"{x / 15000},{y / 15000}");

Note I ignore the Z coordinate, and I divide the result by 15000 as that is how I scaled the airplane data as well. The result is

-0.275750329947415,0.453014630195657
0.199135521004053,-0.166804762707821

So we now know the map in Unity is 0.474885850951468 (east-west) by 0.619819392903478 (north-south) meters. We will need this knowledge soon. But first, drag your map cut-out into your App/Textures folder in Unity:

image

Then proceed to create a material based upon this texture. In the Materials folder, right-click, hit Create/Material and call the material “SchipholMaterial” (or whatever it is you fancy). Then drag your Schiphol texture on top of the “Albedo map” square

image

Next, click the white rectangle to the right of the Albedo square. That pops up a color picker – set the color to B3B3B3FF to make the Schiphol map a bit less overly bright.

image

Then, in HologramCollection, create a 3D Plane object. Initially this will look like this:

image

What is important to wrap your head around is that at scale 1:1, a plane’s size is 10x10 of these blocks which is 10x10 meters in HoloLens. We need to reduce that size so that it’s about 0.62 by 0.475 meters as that is 1:15000 scale. As 10m = scale 1, we need to scale the plane by 0.0475 for X, and 0.062 for Z (leave Y to 1).

Now drag your SchipholMaterial below the “Add Component” button on your Plane. Yes! Unity shows or Schiphol map, with the right dimensions, the right size…

image

… upside down. Life – and Unity is - full of these little surprises. I have no idea why this is necessary, but if you go to the transform section of the object and hit “180” in the Rotation Y box,

image

… that will fix it. Unfortunately, we are still not done, and I will show you why. Add a Sphere to the HologramCollection, size it 0.015 in all directions, give it the Trail Line as material. You will notice a blue dot in the middle of the image if you look from above. That’s 0,0,0 according to Unity.

image

(I toned down the light a little to make this better visible). Unfortunately, as I stated before, 0,0,0 should be on another place, quite a bit down and to the right, near the “Schiphol” label. If we continue to use this, airplanes taking off won’t be properly aligned with the runways :(.

Back to what we found earlier. The corners of the map should be

-0.275750329947415,0.453014630195657
0.199135521004053,-0.166804762707821

on the tangential plane we created and used the center of Schiphol as found by Google Maps as 0,0 in the horizontal plane. Now what follows this might look odd, but there is logic to that.

  • Summarize both X values, and divide the result by two. That is half of the difference in distance to what should be the X-center. This is about –0.038
  • Summarize both Y values, and divide that result by two as well. That is half of the difference in distance to what should be the Z-center (yeah, gets me every time too – what you tend to think of as Y from your Math class, is Z in Unity). This is about 0.143
  • Add those values to X and Z position in the Plane’s transform:

image

And boom. Compare that to the Google maps marker coordinate and that seems to fit pretty well.

imageimage

Not an exact fit, but that’s because we manually determined the corner’s lat/lon positions. But we are not talking zoning permits here (lengthy legal battles have been fought over mere centimeters on those in my lovely country) so it will do for this app.

I left the sphere in the project as a reference but disabled it, as we don’t need it anymore but you can enable it again and see it indeed shows up at the center of the map.

So. Now we have a properly sized and positioned map. There is a tiny detail missing.

Adding an ATC tower

Once again we go to CGTrader and browse around for a tower. Unfortunately the iconic Schiphol ATC tower is not available (paid or free) so I settled for the Hong Kong ATC tower that bears some superficial resemblance to it. Unfortunately there is some building in the attached to the actual tower in model - that I neither need or want. I followed this rather crude work flow to deal with that:

  • I downloaded the model fbx model zip file
  • Unpacked it, and renamed 3d-model.fbx to tower.fbx
  • I created a folder “Tower” in my Assets folder in the Unity editor
  • I dragged the tower.fbx into that folder
  • And then I dragged the resulting tower prefab on the HologramCollection. It will be very large.
  • Set its position to far far away, like 5000,5000,5000 to be out of the way of what we have already created.
  • Double click on “Tower” to get in into view. I got about this:

image

Rotate around it till you get about this:

image

Now what we want to get rid off, is the building next to the tower. I don’t know if I am legally entitled to change a model, so I limit myself to hiding parts of it. This works as follows.

  • First, make sure this button is selected.

image

  • Select the model by clicking on the building. Not the tower. This will select the whole model nonetheless
  • Click the building again. This will only select the building
  • Go to the inspector. Uncheck the checkbox all the way to the top left

image

That will get rid of most of the unwanted stuff:

image

See? Rinse and repeat until all the stuff you don’t want is invisible. Remember: click once selects the whole model, click twice to select only what you want. Hidden too much? CTRL-Z is your friend. It’s a bit of a hassle, especially the small floating bits just right of the tower, but in the end you should be left with just this:

image

When you are done, scale the tower to 0.001,0.001,0.001 and put it’s location to 0,0,0. Select the Main Camera, then GameObject/Align View to Selected and you will see Schiphol again, with a tiny ATC tower standing on top of it. If you look at it almost dead from above, you will see that it’s a bit off to the right and the bottom, according to this aerial picture from Google Maps that ironically enough blocks the view of the actual tower itself with a label – so I marked it with a red circle

imageimage

As it is the wrong tower – and as it’s not to scale, I did not bother with calculating it exact position but dragged it around a bit until it looked like it was in the same spot, which was –0.0283 for X and 0.0316 for Z. Fiddling a bit around with the view spot so I could see the tower’s base hitting the ground made it look like this:

image

And that’s fine with me. I’ll call it a day.

Conclusion

I have shown you how to orientate, scale and properly position (geographical) maps and objects on those maps in Unity, using some GIS knowledge, calculus, common sense and fiddling around. This makes your app look just that little extra pretty, plus it bases it in ‘real space’ which makes it extra cool.

Code – well, more like assets and config, can be found here. And I’d like to say hi to my fellow MVP Tom Verhoeff and his trainee Ryan, who seem to like this tutorial so I hope I made both their days ;)

2 comments:

Rapuke said...

Got the same error here as in part 4

Exception thrown: 'Newtonsoft.Json.JsonReaderException' in Newtonsoft.Json.dll
Exception thrown: 'Microsoft.WindowsAzure.MobileServices.MobileServiceInvalidOperationException' in Microsoft.WindowsAzure.Mobile.dll
Exception thrown: 'Microsoft.WindowsAzure.MobileServices.MobileServiceInvalidOperationException' in mscorlib.ni.dll
Exception thrown: 'Microsoft.WindowsAzure.MobileServices.MobileServiceInvalidOperationException' in mscorlib.ni.dll
Exception thrown: 'Microsoft.WindowsAzure.MobileServices.MobileServiceInvalidOperationException' in mscorlib.ni.dll
Exception thrown: 'Microsoft.WindowsAzure.MobileServices.MobileServiceInvalidOperationException' in mscorlib.ni.dll
Exception thrown: 'Microsoft.WindowsAzure.MobileServices.MobileServiceInvalidOperationException' in mscorlib.ni.dll
The thread 0x808 has exited with code 0 (0x0).
Exception thrown: 'Newtonsoft.Json.JsonReaderException' in Newtonsoft.Json.dll
Exception thrown: 'Microsoft.WindowsAzure.MobileServices.MobileServiceInvalidOperationException' in Microsoft.WindowsAzure.Mobile.dll
Exception thrown: 'Microsoft.WindowsAzure.MobileServices.MobileServiceInvalidOperationException' in mscorlib.ni.dll

Joost van Schaik said...

I hope the e-mail I sent you helped solve the problem. You had an incorrect data URL in your project. Easy enough to fix. Thanks for the clear info. That helped a lot to pin-point the problem!