/// <summary> /// Constructor for BingMapResources. /// </summary> public BingMapResources() { string[] lines = System.IO.File.ReadAllLines(@"Assets/config"); string apikey = lines[0].Substring(17, lines[0].Length - 18); restapiurl = new imageURLRequest(apikey); originQuadKey = QuadKeyFuncs.getQuadKey(Globals.latitude, Globals.longitude, 14); }
/// <summary> /// This function is the function run inside the Tile Prediction thread started in /// AppHandler's start function. It runs for the entire program, checking the current /// location of the camera and loading any tiles nearby that it can't find already into /// the database. Having the database work as a cache like this makes it easy to have tiles /// ready for the actual placement function later. /// </summary> async public void tilePredictor() { //Create a cache object to do a database check Cache cache = new Cache(); IMapResources BMR = new BingMapResources(); while (true) { // Check current position Vector3 currpos = Globals.position; int x = Convert.ToInt32(Math.Round(currpos[0] / 32)); int z = Convert.ToInt32(Math.Round(currpos[2] / 32)); //Check for tiles within needed range //We want all tiles in a 12x12 range for (int i = x - 6; i < x + 6; i++) { for (int j = z - 6; j < z + 6; j++) { //Check if current tile exists Tuple <int, int> tempTuple = new Tuple <int, int>(i, j); if (!active.Contains(tempTuple)) { //Make sure that the maps coordinates are converted from Unity to Bing Maps TileXY int mapi = i * 256; int mapj = -j * 256; //Check the database for current tile using quadkey //Calculate the quadkey for the given tile String originQuadKey = QuadKeyFuncs.getQuadKey(Globals.latitude, Globals.longitude, 14); //Use x and z to offset the quadkey int initx = 0; int initz = 0; int initChosenZoomLevel = 14; QuadKeyFuncs.QuadKeyToTileXY(originQuadKey, out initx, out initz, out initChosenZoomLevel); initx = initx + Convert.ToInt32(mapi) / 256; initz = initz + Convert.ToInt32(mapj) / 256; String newQuadKey = QuadKeyFuncs.TileXYToQuadKey(initx, initz, initChosenZoomLevel); bool inDatabase = cache.DBcheck(newQuadKey); //If it doesn't exist in the database, we will add it ourselves if (!inDatabase) { List <float> mesh = await getElevChunk((float)mapi, (float)mapj); var mat = await BMR.getSatelliteImagery((float)mapi, (float)mapj); cache.DBInsert(newQuadKey, mesh, mat); } } } } } }
/// <summary> /// This function takes in two Unity coordinates and finds the quadkey /// of those coordinates based on the Global starting latitude and longitude. /// It then checks to see if that quad key already exists in the database. /// If it doesn't find the entry, it will return an empty mesh and material /// with a bool to indicate that these are not database entries, but rather /// defaults /// </summary> /// <param name="x">The X coordinate for the searched position in unity coordinates</param> /// <param name="z">The Z coordinate for the searched position in unity coordinates</param> /// <returns></returns> public static Tuple <Mesh, Material, bool> BuildTile(float x, float z) { //Calculate the quadkey for the original starting tile String originQuadKey = QuadKeyFuncs.getQuadKey(Globals.latitude, Globals.longitude, 14); int initx = 0; int initz = 0; int initChosenZoomLevel = 14; //Find the X and Y coordinate in Bing Maps Tiles QuadKeyFuncs.QuadKeyToTileXY(originQuadKey, out initx, out initz, out initChosenZoomLevel); //Offset the BingMapsAPI coordinates from the starting point by x and z in unity initx = initx + Convert.ToInt32(x) / 256; initz = initz + Convert.ToInt32(z) / 256; //Get a quadkey for the new location String currQuadKey = QuadKeyFuncs.TileXYToQuadKey(initx, initz, initChosenZoomLevel); //Check DB for current entry Cache cache = new Cache(); bool entryExists = cache.DBcheck(currQuadKey); //Create an empty mesh and Material Mesh mesh = new Mesh(); Material mat = new Material(Shader.Find("Standard")); if (entryExists) { Tuple <List <float>, Texture> TileTuple = cache.DBGet(currQuadKey); //Ready the mesh //Perform array size calculations int length = TileTuple.Item1.Count; int side = (int)Mathf.Sqrt(length); int sqrPtCount = (int)(Mathf.Pow((side - 1), 2) * 6); //Create empty vertices, uv, and triangles arrays and set their values Vector3[] vertices = fillVert(length, side, TileTuple.Item1); Vector2[] uv = fillUv(length, side); int[] triangles = fillTri(sqrPtCount, side); //Create a new Mesh to render, assign its components, and recalculate //its normals for smooth rendering mesh.vertices = vertices; mesh.uv = uv; mesh.triangles = triangles; mesh.RecalculateNormals(); //Ready the material mat.mainTexture = TileTuple.Item2; mat.mainTextureScale = new Vector2((float)(1.0 / 32.0), (float)(1.0 / 32.0)); } return(new Tuple <Mesh, Material, bool>(mesh, mat, entryExists)); }
/// <summary> /// This is an asynchronous version of the getElevFunction in BingMapResources.cs. /// It gets an elevation chunk from BingMaps Rest API using an offset from the /// starting latitude and longitude in globals. /// </summary> /// <param name="x">The Unity coordinate for X</param> /// <param name="z">The Unity coordinate for Z</param> /// <returns></returns> async public Task <List <float> > getElevChunk(float x, float z) { String originQuadKey = QuadKeyFuncs.getQuadKey(Globals.latitude, Globals.longitude, 14); int initx = 0; int initz = 0; int initChosenZoomLevel = 14; QuadKeyFuncs.QuadKeyToTileXY(originQuadKey, out initx, out initz, out initChosenZoomLevel); initx = initx + Convert.ToInt32(x) / 256; initz = initz + Convert.ToInt32(z) / 256; //Get the upper left corner String newQuadKey = QuadKeyFuncs.TileXYToQuadKey(initx, initz, initChosenZoomLevel); double ucLat; double ucLong; QuadKeyFuncs.QuadKeyToLatLong(newQuadKey, out ucLat, out ucLong); double lcLat; double lcLong; //Get the lower right corner int tilex = 0; int tilez = 0; int chosenZoomLevel; QuadKeyFuncs.QuadKeyToTileXY(newQuadKey, out tilex, out tilez, out chosenZoomLevel); tilex = tilex + 1; tilez = tilez + 1; String lcquadkey = QuadKeyFuncs.TileXYToQuadKey(tilex, tilez, chosenZoomLevel); QuadKeyFuncs.QuadKeyToLatLong(lcquadkey, out lcLat, out lcLong); //Request Bing API elevations using the corners as a bounding box String requestString = "http://dev.virtualearth.net/REST/v1/Elevation/Bounds?bounds=" + (lcLat) + "," + (lcLong) + "," + (ucLat) + "," + (ucLong) + "&rows=32&cols=32&key=" + Globals.BingAPIKey; HttpClient client = new HttpClient(); var content = ""; bool tooManyRequestFlag = false; do { tooManyRequestFlag = false; try { content = await client.GetStringAsync(requestString); } catch (HttpRequestException e) { if (e.Message == "429 (Too Many Requests)") { tooManyRequestFlag = true; } else { throw e; } } } while (tooManyRequestFlag); int i; int j; int k; //Convert the retrieved elevations into a usable chunk int start = content.IndexOf("\"elevations\"") + 14; int end = content.IndexOf("\"zoomLevel\"") - 2; String elevation_string = content.Substring(start, end - start); List <String> elevation_strings = elevation_string.Split(',').ToList(); List <float> retrieved_chunk = new List <float>(); for (k = 0; k < elevation_strings.Count; k++) { retrieved_chunk.Add(Convert.ToSingle(elevation_strings[k])); } //Flip the values in the retrieved chunks so that it matches the order of satellite imagery //Note that elevations in Bing go from the bottom up in a square while the imagery uses the //top left corner as its baseline. We chose to change the order of the elevations List <float> newElevChunk = new List <float>(); for (i = 0; i < Math.Sqrt(retrieved_chunk.Count); i++) { for (j = 0; j < Math.Sqrt(retrieved_chunk.Count); j++) { newElevChunk.Add(retrieved_chunk[(31 - j) * Convert.ToInt32(Math.Sqrt(retrieved_chunk.Count)) + (31 - i)]); } } return(newElevChunk); }