/// <summary> Tries to download and store in cache remote tile. </summary>
 public IObservable<string> Get(Tile tile)
 {
     string filePath = Path.Combine(_cachePath, tile.QuadKey + FormatExtension);
     return FileSystemService.Exists(filePath)
         ? Observable.Return(filePath)
         : ObservableWWW
             .GetAndGetBytes(GetUrl(tile.QuadKey))
             .Select(bytes =>
             {
                 using(var stream = FileSystemService.WriteStream(filePath))
                     stream.Write(bytes, 0, bytes.Length);
                 return filePath;
             });
 }
        public GameObject Build(Tile tile, Element element)
        {
            GameObject gameObject = GameObject.CreatePrimitive(PrimitiveType.Cube);
            gameObject.name = GetName(element);

            var transform = gameObject.transform;
            transform.position = tile.Projection.Project(element.Geometry[0], GetMinHeight(element));
            transform.localScale = new Vector3(2, 2, 2);

            gameObject.GetComponent<MeshFilter>().mesh.uv = GetUV(element);
            gameObject.GetComponent<MeshRenderer>().sharedMaterial = GetMaterial(element);

            return gameObject;
        }
        /// <inheritdoc />
        public void BuildMesh(Tile tile, Mesh mesh)
        {
            var gameObject = new GameObject(mesh.Name);

            var uMesh = new UnityEngine.Mesh();
            uMesh.vertices = mesh.Vertices;
            uMesh.triangles = mesh.Triangles;
            uMesh.colors = mesh.Colors;
            uMesh.uv = new Vector2[mesh.Vertices.Length];

            uMesh.RecalculateNormals();

            gameObject.isStatic = true;
            gameObject.AddComponent<MeshFilter>().mesh = uMesh;
            gameObject.AddComponent<MeshRenderer>().sharedMaterial =
                _customizationService.GetSharedMaterial(@"Materials/Default");
            gameObject.AddComponent<MeshCollider>();
            gameObject.transform.parent = tile.GameObject.transform;
        }
        public void CanLoadMultipleTiles()
        {
            // ARRANGE
            SetupMapData(TestHelper.BerlinPbfData);
            var count = 10;
            var centerQuadKey = new QuadKey(35205, 21489, LevelOfDetails);

            // ACT & ASSERT
            for (var y = 0; y < count; ++y)
                for (var x = 0; x < count; ++x)
                {
                    var quadKey = new QuadKey(centerQuadKey.TileX + x, centerQuadKey.TileY + y, LevelOfDetails);
                    var tile = new Tile(quadKey, _tileController.Stylesheet, _tileController.Projection);
                    _mapDataLoader
                        .Load(tile)
                        .SubscribeOn(Scheduler.CurrentThread)
                        .ObserveOn(Scheduler.CurrentThread)
                        .Subscribe(AssertData);
                }
        }
        /// <summary> Downloads map data for given tile. </summary>
        private IObservable<Tile> CreateDownloadSequence(Tile tile)
        {
            // data exists in store
            if (CoreLibrary.HasData(tile.QuadKey))
                return Observable.Return(tile);

            // data exists in cache
            var filePath = GetCacheFilePath(tile);
            if (_fileSystemService.Exists(filePath))
            {
                var errorMsg = SaveTileDataInMemory(tile, filePath);
                return errorMsg == null
                    ? Observable.Return(tile)
                    : Observable.Throw<Tile>(new MapDataException(Strings.CannotAddDataToInMemoryStore, errorMsg));
            }

            // need to download from remote server
            return Observable.Create<Tile>(observer =>
            {
                double padding = 0.001;
                BoundingBox query = tile.BoundingBox;
                var queryString = String.Format(_mapDataServerQuery,
                    query.MinPoint.Latitude - padding, query.MinPoint.Longitude - padding,
                    query.MaxPoint.Latitude + padding, query.MaxPoint.Longitude + padding);
                var uri = String.Format("{0}{1}", _mapDataServerUri, Uri.EscapeDataString(queryString));
                Trace.Warn(TraceCategory, Strings.NoPresistentElementSourceFound, query.ToString(), uri);
                ObservableWWW.GetAndGetBytes(uri)
                    .ObserveOn(Scheduler.ThreadPool)
                    .Subscribe(bytes =>
                    {
                        Trace.Debug(TraceCategory, "saving bytes: {0}", bytes.Length.ToString());
                        lock (_lockObj)
                        {
                            if (!_fileSystemService.Exists(filePath))
                                using (var stream = _fileSystemService.WriteStream(filePath))
                                    stream.Write(bytes, 0, bytes.Length);
                        }

                        // try to add in memory store
                        var errorMsg = SaveTileDataInMemory(tile, filePath);
                        if (errorMsg != null)
                            observer.OnError(new MapDataException(String.Format(Strings.CannotAddDataToInMemoryStore, errorMsg)));
                        else
                        {
                            observer.OnNext(tile);
                            observer.OnCompleted();
                        }
                    });

                return Disposable.Empty;
            });
        }
 /// <inheritdoc />
 public IObservable<Union<Element, Mesh>> Load(Tile tile)
 {
     return CreateDownloadSequence(tile)
         //CreateElevationSequence(tile)
         //.SelectMany(t => _imaginaryProvider.Get(t).Select(_ => t))
         //.SelectMany(t => CreateDownloadSequence(t))
         .SelectMany(t => CreateLoadSequence(t));
 }
 public MapTileAdapter(Tile tile, IObserver<Union<Element, Mesh>> observer, ITrace trace)
 {
     _tile = tile;
     _observer = observer;
     _trace = trace;
 }
 public void SetUp()
 {
     var tile = new Tile(new QuadKey(), new Mock<Stylesheet>("").Object, new Mock<IProjection>().Object);
     _observer = new Mock<IObserver<Union<Element, Mesh>>>();
     _adapter = new MapTileAdapter(tile, _observer.Object, new DefaultTrace());
 }
 private bool ShouldPreload(Tile tile, Vector2 position)
 {
     return !tile.Contains(position, tile.Rectangle.Width * _offsetRatio);
 }
 /// <summary> Returns cache file name for given tile. </summary>
 private string GetCacheFilePath(Tile tile)
 {
     var cacheFileName = tile.QuadKey + _mapDataFormatExtension;
     return Path.Combine(_cachePath, cacheFileName);
 }
        /// <summary> Gets next quadkey. </summary>
        private QuadKey GetNextQuadKey(Tile tile, Vector2 position)
        {
            var quadKey = tile.QuadKey;

            // NOTE left-right and top-bottom orientation
            Vector2 topLeft = new Vector2(tile.Rectangle.Left, tile.Rectangle.Top);
            Vector2 topRight = new Vector2(tile.Rectangle.Right, tile.Rectangle.Top);

            // top
            if (IsPointInTriangle(position, tile.Rectangle.Center, topLeft, topRight))
                return new QuadKey(quadKey.TileX, quadKey.TileY - 1, quadKey.LevelOfDetail);

            Vector2 bottomLeft = new Vector2(tile.Rectangle.Left, tile.Rectangle.Bottom);

            // left
            if (IsPointInTriangle(position, tile.Rectangle.Center, topLeft, bottomLeft))
                return new QuadKey(quadKey.TileX - 1, quadKey.TileY, quadKey.LevelOfDetail);

            Vector2 bottomRight = new Vector2(tile.Rectangle.Right, tile.Rectangle.Bottom);

            // right
            if (IsPointInTriangle(position, tile.Rectangle.Center, topRight, bottomRight))
                return new QuadKey(quadKey.TileX + 1, quadKey.TileY, quadKey.LevelOfDetail);

            // bottom
            return new QuadKey(quadKey.TileX, quadKey.TileY + 1, quadKey.LevelOfDetail);
        }
 /// <inheritdoc />
 public void BuildMesh(Tile tile, Mesh mesh)
 {
 }
 /// <inheritdoc />
 public void BuildElement(Tile tile, Element element)
 {
 }
 /// <summary> Downloads elevation data for given tile. </summary>
 private IObservable<Tile> CreateElevationSequence(Tile tile)
 {
     return _elevationProvider.HasElevation(tile.BoundingBox)
         ? Observable.Return(tile)
         : _elevationProvider.Download(tile.BoundingBox).Select(_ => tile);
 }
 /// <summary> Loads tile for given quadKey. </summary>
 private void Load(QuadKey quadKey)
 {
     Tile tile = new Tile(quadKey, Stylesheet, Projection);
     _loadedTiles.Add(quadKey, tile); // TODO remove tile from hashmap if exception is raised
     _messageBus.Send(new TileLoadStartMessage(tile));
     _tileLoader
         .Load(tile)
         .SubscribeOn(Scheduler.ThreadPool)
         .ObserveOn(Scheduler.MainThread)
         .Subscribe(
             u => u.Match(e => _modelBuilder.BuildElement(tile, e), m => _modelBuilder.BuildMesh(tile, m)),
             () => _messageBus.Send(new TileLoadFinishMessage(tile)));
 }
        /// <summary> Creates <see cref="IObservable{T}"/> for loading element of given tile. </summary>
        private IObservable<Union<Element, Mesh>> CreateLoadSequence(Tile tile)
        {
            return Observable.Create<Union<Element, Mesh>>(observer =>
            {
                Trace.Info(TraceCategory, "loading tile: {0}", tile.ToString());
                var adapter = new MapTileAdapter(tile, observer, Trace);

                CoreLibrary.LoadQuadKey(
                    _pathResolver.Resolve(tile.Stylesheet.Path),
                    tile.QuadKey,
                    adapter.AdaptMesh,
                    adapter.AdaptElement,
                    adapter.AdaptError);

                Trace.Info(TraceCategory, "tile loaded: {0}", tile.ToString());
                observer.OnCompleted();

                return Disposable.Empty;
            });
        }
 private void PreloadNextTile(Tile tile, Vector2 position)
 {
     var quadKey = GetNextQuadKey(tile, position);
     if (!_loadedTiles.ContainsKey(quadKey))
         Load(quadKey);
 }
        /// <summary> Adds tile data into in memory storage. </summary>
        private string SaveTileDataInMemory(Tile tile, string filePath)
        {
            Trace.Info(TraceCategory, "try to save: {0} from {1}", tile.ToString(), filePath);
            string errorMsg = null;
            CoreLibrary.AddToStore(MapStorageType.InMemory,
                _pathResolver.Resolve(tile.Stylesheet.Path),
                _pathResolver.Resolve(filePath),
                tile.QuadKey,
                error => errorMsg = error);

            return errorMsg;
        }
 /// <inheritdoc />
 public void BuildElement(Tile tile, Element element)
 {
     if (element.Styles["builders"].Contains("info"))
         _placeElementBuilder.Build(tile, element).transform.parent = tile.GameObject.transform;
 }