Topic: Elevation data caching
Hey,
Would be great to cache height data if elevation is set. Currently in offline mode only flat tiles.
You are not logged in. Please login or register.
Infinity Code Forum → Feature Requests → Elevation data caching
Hey,
Would be great to cache height data if elevation is set. Currently in offline mode only flat tiles.
Hello.
Caching Bing Maps Elevation data is a very bad idea.
This will create a ton of problems.
But this does not mean that it is impossible to make caching elevation data.
Just need to use a more suitable data source, such as mapbox.
Example:
#if !UNITY_WEBPLAYER && ((!UNITY_WP_8_1 && !UNITY_WEBGL) || UNITY_EDITOR)
#define ALLOW_FILECACHE
#endif
using System;
using System.Text;
using UnityEngine;
#if ALLOW_FILECACHE
using System.IO;
#endif
public class MapboxElevation : MonoBehaviour
{
public string mapboxKey;
public bool useCache = true;
public string fileCacheTilePath = "ElevationCache/{zoom}/{x}/{y}";
public int countX = 3;
public int countY = 3;
public int zoomOffset = 3;
private OnlineMapsTile[] tiles;
private OnlineMaps map;
private OnlineMapsTileSetControl control;
private Vector2 ctl;
private Vector2 cbr;
private void DownloadElevation(OnlineMapsTile tile)
{
if (useCache && TryLoadElevationsFromCache(tile)) return;
string url = new StringBuilder("https://api.mapbox.com/v4/mapbox.terrain-rgb/")
.Append(tile.zoom).Append("/").Append(tile.x).Append("/").Append(tile.y)
.Append(".pngraw?access_token=").Append(mapboxKey).ToString();
OnlineMapsWWW www = OnlineMapsUtils.GetWWW(url);
www.OnComplete += delegate
{
tile.customData = new CData(www.bytes);
if (useCache) SaveElevationsToCache(tile, www.bytes);
OnGetElevation(ctl, cbr);
};
}
private StringBuilder GetTilePath(OnlineMapsTile tile)
{
#if ALLOW_FILECACHE
int startIndex = 0;
StringBuilder builder = new StringBuilder(Application.persistentDataPath).Append("/");
int l = fileCacheTilePath.Length;
for (int i = 0; i < l; i++)
{
char c = fileCacheTilePath[i];
if (c == '{')
{
for (int j = i + 1; j < l; j++)
{
c = fileCacheTilePath[j];
if (c == '}')
{
builder.Append(fileCacheTilePath.Substring(startIndex, i - startIndex));
string v = fileCacheTilePath.Substring(i + 1, j - i - 1).ToLower();
if (v == "zoom" || v == "z") builder.Append(tile.zoom);
else if (v == "x") builder.Append(tile.x);
else if (v == "y") builder.Append(tile.y);
else builder.Append(v);
i = j;
startIndex = j + 1;
break;
}
}
}
}
builder.Append(fileCacheTilePath.Substring(startIndex, l - startIndex));
return builder;
#else
return null;
#endif
}
private void OnGetElevation(Vector2 tl, Vector2 br)
{
ctl = tl;
cbr = br;
double tlx, tly, brx, bry;
map.projection.CoordinatesToTile(tl.x, tl.y, map.zoom, out tlx, out tly);
map.projection.CoordinatesToTile(br.x, br.y, map.zoom, out brx, out bry);
int scale = 1 << zoomOffset;
int zoom = map.zoom - zoomOffset;
short[,] heights = new short[32, 32];
double rx = (brx - tlx) / 31;
double ry = (bry - tly) / 31;
for (int x = 0; x < 32; x++)
{
double tx = (rx * x + tlx) / scale;
for (int y = 0; y < 32; y++)
{
double ty = (ry * y + tly) / scale;
OnlineMapsTile tile = OnlineMapsTile.GetTile(zoom, (int)tx, (int)ty);
if (tile == null)
{
heights[x, y] = 0;
continue;
}
CData data = tile.customData as CData;
if (data == null)
{
heights[x, y] = 0;
continue;
}
heights[x, 31 - y] = data.GetElevation(tx, ty);
}
}
control.SetElevationData(heights);
}
private void OnTileDownloaded(OnlineMapsTile tile)
{
for (int i = 0; i < tiles.Length; i++)
{
if (tiles[i] == tile)
{
DownloadElevation(tile);
return;
}
}
}
private void Start()
{
map = OnlineMaps.instance;
control = OnlineMapsTileSetControl.instance;
control.OnGetElevation += OnGetElevation;
OnlineMapsTile.OnTileDownloaded += OnTileDownloaded;
if (OnlineMapsCache.instance != null) OnlineMapsCache.instance.OnLoadedFromCache += OnTileDownloaded;
map.OnMapUpdated += UpdateTiles;
int countTiles = countX * countY;
tiles = new OnlineMapsTile[countTiles];
}
private bool TryLoadElevationsFromCache(OnlineMapsTile tile)
{
Debug.Log("Try load: " + tile);
#if ALLOW_FILECACHE
StringBuilder filename = GetTilePath(tile);
string fn = filename.ToString();
if (File.Exists(fn))
{
Debug.Log("Tile loaded: " + tile);
tile.customData = new CData(File.ReadAllBytes(fn));
OnGetElevation(ctl, cbr);
return true;
}
#endif
return false;
}
private void SaveElevationsToCache(OnlineMapsTile tile, byte[] bytes)
{
Debug.Log("Save: " + tile);
#if ALLOW_FILECACHE
StringBuilder filename = GetTilePath(tile);
string fn = filename.ToString();
if (!File.Exists(fn))
{
FileInfo fileInfo = new FileInfo(fn);
if (!Directory.Exists(fileInfo.DirectoryName)) Directory.CreateDirectory(fileInfo.DirectoryName);
File.WriteAllBytes(fn, bytes);
}
#endif
}
private void UpdateTiles()
{
for (int i = 0; i < tiles.Length; i++) if (tiles[i] != null) tiles[i].Unblock(this);
int zoom = map.zoom - zoomOffset;
if (zoom < 3) zoom = 3;
double tx, ty;
map.GetTilePosition(out tx, out ty, zoom);
int itx = Mathf.RoundToInt((float)(tx - countX / 2f));
int ity = Mathf.RoundToInt((float)(ty - countY / 2f));
int max = 1 << zoom;
double tlx, tly, brx, bry;
map.GetCorners(out tlx, out tly, out brx, out bry);
for (int x = 0; x < countX; x++)
{
int tileX = itx + x;
if (tileX >= max) tileX -= max;
for (int y = 0; y < countY; y++)
{
int tileY = ity + y;
if (tileY >= max) tileY -= max;
OnlineMapsTile tile = OnlineMapsTile.GetTile(zoom, tileX, tileY);
if (tile == null)
{
OnlineMapsTile parentTile = OnlineMapsTile.GetTile(zoom - 1, tileX / 2, tileY / 2);
tile = new OnlineMapsTile(tileX, tileY, zoom, map, parentTile);
}
int tileIndex = x * countY + y;
tiles[tileIndex] = tile;
tile.Block(this);
}
}
}
internal class CData
{
private short[,] heights;
public CData(byte[] bytes)
{
Texture2D texture = new Texture2D(256, 256, TextureFormat.RGB24, false);
texture.LoadImage(bytes);
Color[] colors = texture.GetPixels();
const int res = 256;
heights = new short[res, res];
for (int y = 0; y < res; y++)
{
int py = (255 - y) * res;
for (int x = 0; x < res; x++)
{
Color c = colors[py + x];
double height = -10000 + (c.r * 255 * 256 * 256 + c.g * 255 * 256 + c.b * 255) * 0.1;
heights[x, y] = (short)Math.Round(height);
}
}
}
public short GetElevation(double tx, double ty)
{
double rx = tx - Math.Floor(tx);
double ry = ty - Math.Floor(ty);
int x = (int)Math.Round(rx * 256);
int y = (int)Math.Round(ry * 256);
if (x > 255) x = 255;
if (y > 255) y = 255;
return heights[x, y];
}
}
}
Something strange happens when I'm trying to zoom in or zoom out. Seems script calculate elevation only for initial zoom level( if zoom not touched at runtime everything seems good). (Online maps version 2.5.18.1)
Fixed.
Try it:
#if !UNITY_WEBPLAYER && ((!UNITY_WP_8_1 && !UNITY_WEBGL) || UNITY_EDITOR)
#define ALLOW_FILECACHE
#endif
using System;
using System.Text;
using UnityEngine;
#if ALLOW_FILECACHE
using System.IO;
#endif
public class MapboxElevation : MonoBehaviour
{
public string mapboxKey;
public bool useCache = true;
public string fileCacheTilePath = "ElevationCache/{zoom}/{x}/{y}";
public int countX = 3;
public int countY = 3;
public int zoomOffset = 3;
private OnlineMapsTile[] tiles;
private OnlineMaps map;
private OnlineMapsTileSetControl control;
private Vector2 ctl;
private Vector2 cbr;
private void DownloadElevation(OnlineMapsTile tile)
{
if (useCache && TryLoadElevationsFromCache(tile)) return;
string url = new StringBuilder("https://api.mapbox.com/v4/mapbox.terrain-rgb/")
.Append(tile.zoom).Append("/").Append(tile.x).Append("/").Append(tile.y)
.Append(".pngraw?access_token=").Append(mapboxKey).ToString();
OnlineMapsWWW www = OnlineMapsUtils.GetWWW(url);
www.OnComplete += delegate
{
CData cdata = tile.customData as CData;
if (cdata == null)
{
cdata = new CData();
tile.customData = cdata;
}
cdata.Load(www.bytes);
if (useCache) SaveElevationsToCache(tile, www.bytes);
OnGetElevation(ctl, cbr);
};
}
private StringBuilder GetTilePath(OnlineMapsTile tile)
{
#if ALLOW_FILECACHE
int startIndex = 0;
StringBuilder builder = new StringBuilder(Application.persistentDataPath).Append("/");
int l = fileCacheTilePath.Length;
for (int i = 0; i < l; i++)
{
char c = fileCacheTilePath[i];
if (c == '{')
{
for (int j = i + 1; j < l; j++)
{
c = fileCacheTilePath[j];
if (c == '}')
{
builder.Append(fileCacheTilePath.Substring(startIndex, i - startIndex));
string v = fileCacheTilePath.Substring(i + 1, j - i - 1).ToLower();
if (v == "zoom" || v == "z") builder.Append(tile.zoom);
else if (v == "x") builder.Append(tile.x);
else if (v == "y") builder.Append(tile.y);
else builder.Append(v);
i = j;
startIndex = j + 1;
break;
}
}
}
}
builder.Append(fileCacheTilePath.Substring(startIndex, l - startIndex));
return builder;
#else
return null;
#endif
}
private void OnGetElevation(Vector2 tl, Vector2 br)
{
ctl = tl;
cbr = br;
double tlx, tly, brx, bry;
map.projection.CoordinatesToTile(tl.x, tl.y, map.zoom, out tlx, out tly);
map.projection.CoordinatesToTile(br.x, br.y, map.zoom, out brx, out bry);
int scale = 1 << zoomOffset;
int zoom = map.zoom - zoomOffset;
short[,] heights = new short[32, 32];
double rx = (brx - tlx) / 31;
double ry = (bry - tly) / 31;
for (int x = 0; x < 32; x++)
{
double tx = (rx * x + tlx) / scale;
for (int y = 0; y < 32; y++)
{
double ty = (ry * y + tly) / scale;
OnlineMapsTile tile = OnlineMapsTile.GetTile(zoom, (int)tx, (int)ty);
if (tile == null)
{
heights[x, 31 - y] = 0;
continue;
}
CData data = tile.customData as CData;
if (data != null) heights[x, 31 - y] = data.GetElevation(tx, ty);
else heights[x, 31 - y] = 0;
}
}
control.SetElevationData(heights);
}
private void Start()
{
map = OnlineMaps.instance;
control = OnlineMapsTileSetControl.instance;
control.OnGetElevation += OnGetElevation;
map.OnMapUpdated += UpdateTiles;
int countTiles = countX * countY;
tiles = new OnlineMapsTile[countTiles];
}
private bool TryLoadElevationsFromCache(OnlineMapsTile tile)
{
Debug.Log("Try load: " + tile);
#if ALLOW_FILECACHE
StringBuilder filename = GetTilePath(tile);
string fn = filename.ToString();
if (File.Exists(fn))
{
Debug.Log("Tile loaded: " + tile);
(tile.customData as CData).Load(File.ReadAllBytes(fn));
OnGetElevation(ctl, cbr);
return true;
}
#endif
return false;
}
private void SaveElevationsToCache(OnlineMapsTile tile, byte[] bytes)
{
Debug.Log("Save: " + tile);
#if ALLOW_FILECACHE
StringBuilder filename = GetTilePath(tile);
string fn = filename.ToString();
if (!File.Exists(fn))
{
FileInfo fileInfo = new FileInfo(fn);
if (!Directory.Exists(fileInfo.DirectoryName)) Directory.CreateDirectory(fileInfo.DirectoryName);
File.WriteAllBytes(fn, bytes);
}
#endif
}
private void UpdateTiles()
{
for (int i = 0; i < tiles.Length; i++) if (tiles[i] != null) tiles[i].Unblock(this);
int zoom = map.zoom - zoomOffset;
if (zoom < 3) zoom = 3;
double tx, ty;
map.GetTilePosition(out tx, out ty, zoom);
int itx = Mathf.RoundToInt((float)(tx - countX / 2f));
int ity = Mathf.RoundToInt((float)(ty - countY / 2f));
int max = 1 << zoom;
double tlx, tly, brx, bry;
map.GetCorners(out tlx, out tly, out brx, out bry);
for (int x = 0; x < countX; x++)
{
int tileX = itx + x;
if (tileX >= max) tileX -= max;
for (int y = 0; y < countY; y++)
{
int tileY = ity + y;
if (tileY >= max) tileY -= max;
OnlineMapsTile tile = OnlineMapsTile.GetTile(zoom, tileX, tileY);
if (tile == null)
{
OnlineMapsTile parentTile = OnlineMapsTile.GetTile(zoom - 1, tileX / 2, tileY / 2);
tile = new OnlineMapsTile(tileX, tileY, zoom, map, parentTile);
}
else if (tile.customData == null)
{
CData cdata = new CData();
tile.customData = cdata;
DownloadElevation(tile);
}
int tileIndex = x * countY + y;
tiles[tileIndex] = tile;
tile.Block(this);
}
}
}
internal class CData
{
private short[,] heights;
public CData()
{
}
public void Load(byte[] bytes)
{
Texture2D texture = new Texture2D(256, 256, TextureFormat.RGB24, false);
texture.LoadImage(bytes);
Color[] colors = texture.GetPixels();
OnlineMapsUtils.DestroyImmediate(texture);
const int res = 256;
heights = new short[res, res];
for (int y = 0; y < res; y++)
{
int py = (255 - y) * res;
for (int x = 0; x < res; x++)
{
Color c = colors[py + x];
double height = -10000 + (c.r * 255 * 256 * 256 + c.g * 255 * 256 + c.b * 255) * 0.1;
heights[x, y] = (short)Math.Round(height);
}
}
}
public short GetElevation(double tx, double ty)
{
if (heights == null) return 0;
double rx = tx - Math.Floor(tx);
double ry = ty - Math.Floor(ty);
int x = (int)Math.Round(rx * 256);
int y = (int)Math.Round(ry * 256);
if (x > 255) x = 255;
if (y > 255) y = 255;
return heights[x, y];
}
}
}
Now works perfectly. A big thank you to Alex.
Now works perfectly. A big thank you to Alex.
Would you pleased to describe how you using the codes?Should I attach a script which contains the codes to the map?
Would you pleased to describe how you using the codes?Should I attach a script which contains the codes to the map?
Yes, you must add the script to the map (or any other) GameObject and enter your Mapbox Access Token.
I'm trying to use this script to achieve the same thing but it doesn't seem to work, i attached the script to the map object and then supplied the mapbox key but i get an error "Array index out of range".
Hello.
Please attach a stack trace of the error.
Hi ,
I'have this error using this script
This is a script for Online Maps v2.
In Online Maps v3, this is much easier:
using System.IO;
using UnityEngine;
public class CacheMapboxElevation: MonoBehaviour
{
private string GetTilePath(OnlineMapsTiledElevationManager<OnlineMapsMapboxElevationManager>.Tile tile)
{
return Application.persistentDataPath + "/Elevation Cache/" + tile.zoom + "/" + tile.x + "/" + tile.y + ".png";
}
private void OnDownload(OnlineMapsTiledElevationManager<OnlineMapsMapboxElevationManager>.Tile tile)
{
string path = GetTilePath(tile);
if (!File.Exists(path))
{
OnlineMapsMapboxElevationManager.instance.StartDownloadElevationTile(tile);
return;
}
Texture2D texture = new Texture2D(256, 256);
texture.LoadImage(File.ReadAllBytes(path));
OnlineMapsMapboxElevationManager.instance.SetElevationTexture(tile, texture);
OnlineMapsUtils.Destroy(texture);
Debug.Log("Loaded from cache: " + path);
}
private void OnDownloadSuccess(OnlineMapsTiledElevationManager<OnlineMapsMapboxElevationManager>.Tile tile, OnlineMapsWWW www)
{
string path = GetTilePath(tile);
FileInfo info = new FileInfo(path);
if (!info.Directory.Exists) info.Directory.Create();
File.WriteAllBytes(path, www.bytes);
Debug.Log("Elevation saved: " + path);
}
private void Start()
{
OnlineMapsMapboxElevationManager.instance.OnDownload += OnDownload;
OnlineMapsMapboxElevationManager.instance.OnDownloadSuccess += OnDownloadSuccess;
}
}
it's possible to have elevation data caching for ArcGIS.
THANK YOUU
Yes it is possible.
But this will not work with Horizon, which you asked for in another thread.
Horizon already has everything to implement caching.
You just need to add a save array to the file, check for the existence of the file, and read the array from the file.
It is so easy that I believe that you can do it.
If you still have problems implementing this, write again and I will do it for you.
yes i think i need your help if possible .I still do not master the horizon scripts so it's a little complicated.
Thank you any way
http://forum.infinity-code.com/viewtopi … 5279#p5279
Is it possible to add a function in the CacheMapboxElevation (Online Maps v3) for requesting real world elevation for specific coordinates(Lon, Lat) at specific zoom?
Thank you!
CacheMapboxElevation only saves and reads elevation data for the tile from the cache.
What is the point of this function if you can get elevation data using OnlineMapsMapboxElevationManager?!
Im sorry, let me explain.
I want to know the real elevation for my markers on the map. Is it possible to read elevation from tiles for a position on map by just providing latitude, longitude and zoom?
I try the following code:
Vector2 center = marker.position;
float unscaledElevation = OnlineMapsElevationManagerBase.GetUnscaledElevation(center.x, center.y); // real elevation value
But the problem is each zoom gives different elevation for my markers. Requesting elevations from tiles would be better?
The problem is that GetUnscaledElevation requires a position in the scene, relative to the top-left corner of the map.
http://infinity-code.com/doxygen/online … c87e6b8540
You pass geographic coordinates.
You need to first convert the coordinates to a position in the scene.
http://infinity-code.com/doxygen/online … 369ba44098
Infinity Code Forum → Feature Requests → Elevation data caching
Powered by PunBB, supported by Informer Technologies, Inc.