예제 #1
0
        public void Example()
        {
            this.Name = "Elements_Topography";

            // <example>
            // Read topo elevations from a file.
            var data       = JsonConvert.DeserializeObject <Dictionary <string, double[]> >(File.ReadAllText("./elevations.json"));
            var elevations = data["points"];
            var tileSize   = WebMercatorProjection.GetTileSizeMeters(15);

            // Create a topography.
            var topo = new Topography(Vector3.Origin, tileSize, elevations);

            // </example>

            this.Model.AddElement(topo);
        }
예제 #2
0
        /// <summary>
        /// Scales a tile
        /// </summary>
        /// <param name="tileData">The tile to scale</param>
        /// <param name="zoomLevelDifference">The difference between the downloaded tile and the rqeuested tile zoom levels</param>
        /// <param name="ptHigherTile"></param>
        /// <param name="ptRequestedWorld"></param>
        /// <param name="numTilesAtFoundZoomLevel">The number of tiles for the higher zoom level</param>
        /// <returns>A scaled tile</returns>
        private byte[] ScaleTile(byte[] tileData, int zoomLevelDifference, Point ptHigherTile, Point ptRequestedWorld,
                                 int numTilesAtFoundZoomLevel)
        {
            // Calculate the tile coords of the BR corner of the higher zoom level tile
            // so that we can identify which sub-part of it to crop and scale
            Point ptHigherTileTopLeft  = ptHigherTile;
            Point ptHigherTileBotRight = new Point(ptHigherTile.y + 1, ptHigherTile.x + 1);

            // Calculate the sub-tile rectangle that we need to crop out of the  higher tile
            // using a simple proportion calculation based on which part of the higher tile
            // covers the original requested tile in world coordinates
            var ptHigherWorldTopLeft =
                WebMercatorProjection.PixelToWorld(WebMercatorProjection.TileToPixel(ptHigherTileTopLeft),
                                                   numTilesAtFoundZoomLevel);
            var ptHigherWorldBotRight =
                WebMercatorProjection.PixelToWorld(WebMercatorProjection.TileToPixel(ptHigherTileBotRight),
                                                   numTilesAtFoundZoomLevel);

            var ratioX = (ptRequestedWorld.x - ptHigherWorldTopLeft.x) /
                         (ptHigherWorldBotRight.x - ptHigherWorldTopLeft.x);
            var ratioY = (ptRequestedWorld.y - ptHigherWorldTopLeft.y) /
                         (ptHigherWorldBotRight.y - ptHigherWorldTopLeft.y);

            var startX = (int)Math.Floor(WebMercatorProjection.TILE_SIZE * ratioX);
            var startY = (int)Math.Floor(WebMercatorProjection.TILE_SIZE * ratioY);

            // Calculate how much up-scaling of higher level zoom tile we need to do
            // based on the difference between the requested and higher zoom levels
            var croppedTileSize = WebMercatorProjection.TILE_SIZE / (1 << zoomLevelDifference);

            // Set the crop rectangle and draw it into a new bitmap, scaling it up to the standard tile size
            var cropRect = new Rectangle(startX, startY, croppedTileSize, croppedTileSize);

            log.LogDebug("DxfTileExecutor: crop rectangle x = {0}, y = {1}, size = {2}", startX, startY,
                         croppedTileSize);

            //source bitmap
            using (var tileStream = new MemoryStream(tileData))
                using (var higherBitmap = Image.Load(tileStream))
                {
                    //destination bitmap
                    var target = higherBitmap.Clone(ctx => ctx.Crop(cropRect).Resize(WebMercatorProjection.TILE_SIZE, WebMercatorProjection.TILE_SIZE));
                    return(target.BitmapToByteArray());
                }
        }
        public virtual MapProjection GetProjection(string crsId)
        {
            MapProjection projection = null;

            switch (crsId)
            {
            case WorldMercatorProjection.DefaultCrsId:
                projection = new WorldMercatorProjection();
                break;

            case WebMercatorProjection.DefaultCrsId:
                projection = new WebMercatorProjection();
                break;

            case EquirectangularProjection.DefaultCrsId:
                projection = new EquirectangularProjection();
                break;

            case OrthographicProjection.DefaultCrsId:
                projection = new OrthographicProjection();
                break;

            case AutoEquirectangularProjection.DefaultCrsId:
                projection = new AutoEquirectangularProjection();
                break;

            case GnomonicProjection.DefaultCrsId:
                projection = new GnomonicProjection();
                break;

            case StereographicProjection.DefaultCrsId:
                projection = new StereographicProjection();
                break;

            case "EPSG:97003":     // proprietary CRS ID
                projection = new AzimuthalEquidistantProjection(crsId);
                break;

            default:
                break;
            }

            return(projection);
        }
예제 #4
0
        public void MapboxTopography()
        {
            this.Name = "MapboxTopography";

            // 0 1
            // 2 3

            var maps = new[] {
                "./Topography/Texture_f7c3dc2f-c47c-4638-a962-53ae31719cf5_0.jpg",
                "./Topography/Texture_df332cc3-62b0-42ac-9041-942e1fd985aa_1.jpg",
                "./Topography/Texture_12454f24-690a-43e2-826d-e4deae5eb82e_2.jpg",
                "./Topography/Texture_aa1b1148-0563-4b9d-b1c0-7138024dc974_3.jpg"
            };

            var topos = new[] {
                "./Topography/Topo_52f0cdf5-2d34-4e1c-927d-79fb5f2caf43_0.png",
                "./Topography/Topo_5f21ae5e-eeef-4346-af16-860afdc0e829_1.png",
                "./Topography/Topo_e056d328-5b7d-47d3-b6bc-42d6d245d8ec_2.png",
                "./Topography/Topo_0a91fd8a-d887-40c2-a729-aac47441f9d8_3.png"
            };

            var tiles = new[] {
                new Tuple <int, int>(20, 20),
                new Tuple <int, int>(21, 20),
                new Tuple <int, int>(20, 21),
                new Tuple <int, int>(21, 21),
            };

            // Web Mercator Tiling (x,y)
            // 0,0   1,0
            // 0,1   1,1

            // Image Coordinates (x,y)
            // 0,0   1,0
            // 0,1   1,1

            // Image Texture coordinates (u,v)
            // 0,1   1,1
            // 0,0   1,0

            // Mesh Coordinates
            // 0,1   1,1
            // 0,0   1,0

            var zoom           = 16;
            var selectedOrigin = WebMercatorProjection.TileIdToCenterWebMercator(tiles[0].Item1, tiles[0].Item2, zoom);
            var sampleSize     = 4;

            var topographies = new Topography[maps.Length];

            for (var i = 0; i < maps.Length; i++)
            {
                using (var topo = Image.Load <Rgba32>(topos[i]))
                {
                    var size          = topo.Width / sampleSize;
                    var elevationData = new double[(int)Math.Pow(size, 2)];

                    var idx = 0;
                    // Read the rows in reverse order as images
                    // start in the top left and meshes start
                    // in the lower left.
                    for (var y = topo.Width - 1; y >= 0; y -= sampleSize)
                    {
                        for (var x = 0; x < topo.Width; x += sampleSize)
                        {
                            var c      = topo[x, y];
                            var height = -10000 + ((c.R * 256 * 256 + c.G * 256 + c.B) * 0.1);
                            elevationData[idx] = height;
                            idx++;
                        }
                    }

                    var tileSize   = WebMercatorProjection.GetTileSizeMeters(zoom);
                    var origin     = WebMercatorProjection.TileIdToCenterWebMercator(tiles[i].Item1, tiles[i].Item2, zoom) - new Vector3(tileSize / 2.0, tileSize / 2.0) - selectedOrigin;
                    var material   = new Material($"Topo_{i}", Colors.White, 0.0f, 0.0f, maps[i]);
                    var topography = new Topography(origin, tileSize, elevationData, material);
                    this.Model.AddElement(topography);
                    topographies[i] = topography;
                }
            }

            // 1 2
            // 2 3

            topographies[0].AverageEdges(topographies[1], Units.CardinalDirection.East);
            topographies[0].AverageEdges(topographies[2], Units.CardinalDirection.South);
            topographies[1].AverageEdges(topographies[3], Units.CardinalDirection.South);
            topographies[2].AverageEdges(topographies[3], Units.CardinalDirection.East);
        }
예제 #5
0
        public void MapboxTopography()
        {
            this.Name = "Topography_Mapbox";

            // 0 1
            // 2 3

            var maps = new[] {
                "./Topography/Texture_f7c3dc2f-c47c-4638-a962-53ae31719cf5_0.jpg",
                "./Topography/Texture_df332cc3-62b0-42ac-9041-942e1fd985aa_1.jpg",
                "./Topography/Texture_12454f24-690a-43e2-826d-e4deae5eb82e_2.jpg",
                "./Topography/Texture_aa1b1148-0563-4b9d-b1c0-7138024dc974_3.jpg"
            };

            var topos = new[] {
                "./Topography/Topo_52f0cdf5-2d34-4e1c-927d-79fb5f2caf43_0.png",
                "./Topography/Topo_5f21ae5e-eeef-4346-af16-860afdc0e829_1.png",
                "./Topography/Topo_e056d328-5b7d-47d3-b6bc-42d6d245d8ec_2.png",
                "./Topography/Topo_0a91fd8a-d887-40c2-a729-aac47441f9d8_3.png"
            };

            var tiles = new[] {
                new Tuple <int, int>(20, 20),
                new Tuple <int, int>(21, 20),
                new Tuple <int, int>(20, 21),
                new Tuple <int, int>(21, 21),
            };

            // Web Mercator Tiling (x,y)
            // 0,0   1,0
            // 0,1   1,1

            // Image Coordinates (x,y)
            // 0,0   1,0
            // 0,1   1,1

            // Image Texture coordinates (u,v)
            // 0,1   1,1
            // 0,0   1,0

            // Mesh Coordinates
            // 0,1   1,1
            // 0,0   1,0

            var zoom           = 16;
            var selectedOrigin = WebMercatorProjection.TileIdToCenterWebMercator(tiles[0].Item1, tiles[0].Item2, zoom);
            var sampleSize     = 8;

            var topoImage = new Image <Rgba32>(512 * 2, 512 * 2);
            var mapImage  = new Image <Rgba32>(1024 * 2, 1024 * 2);

            using (var map0 = Image.Load <Rgba32>(maps[0]))
                using (var map1 = Image.Load <Rgba32>(maps[1]))
                    using (var map2 = Image.Load <Rgba32>(maps[2]))
                        using (var map3 = Image.Load <Rgba32>(maps[3]))
                            using (var topo0 = Image.Load <Rgba32>(topos[0]))
                                using (var topo1 = Image.Load <Rgba32>(topos[1]))
                                    using (var topo2 = Image.Load <Rgba32>(topos[2]))
                                        using (var topo3 = Image.Load <Rgba32>(topos[3]))
                                        {
                                            topoImage.Mutate(o => o
                                                             .DrawImage(topo0, new Point(0, 0), 1.0f)
                                                             .DrawImage(topo1, new Point(512, 0), 1.0f)
                                                             .DrawImage(topo2, new Point(0, 512), 1.0f)
                                                             .DrawImage(topo3, new Point(512, 512), 1.0f));

                                            mapImage.Mutate(o => o
                                                            .DrawImage(map0, new Point(0, 0), 1.0f)
                                                            .DrawImage(map1, new Point(1024, 0), 1.0f)
                                                            .DrawImage(map2, new Point(0, 1024), 1.0f)
                                                            .DrawImage(map3, new Point(1024, 1024), 1.0f));

                                            var mapImagePath = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}.png");
                                            mapImage.Save(mapImagePath);

                                            var size          = topoImage.Width / sampleSize;
                                            var elevationData = new double[(int)Math.Pow(size, 2)];

                                            var idx = 0;
                                            // Read the rows in reverse order as images
                                            // start in the top left and meshes start
                                            // in the lower left.
                                            for (var y = topoImage.Width - 1; y >= 0; y -= sampleSize)
                                            {
                                                if (y - sampleSize < 0)
                                                {
                                                    y = 0;
                                                }
                                                for (var x = 0; x < topoImage.Width; x += sampleSize)
                                                {
                                                    if (x + sampleSize > topoImage.Width - 1)
                                                    {
                                                        x = topoImage.Width - 1;
                                                    }
                                                    var color  = topoImage[x, y];
                                                    var height = -10000 + ((color.R * 256 * 256 + color.G * 256 + color.B) * 0.1);
                                                    elevationData[idx] = height;
                                                    idx++;
                                                }
                                            }

                                            var tileSize = WebMercatorProjection.GetTileSizeMeters(0, zoom);
                                            var a        = WebMercatorProjection.TileIdToCenterWebMercator(tiles[0].Item1, tiles[0].Item2, zoom);
                                            var b        = WebMercatorProjection.TileIdToCenterWebMercator(tiles[1].Item1, tiles[1].Item2, zoom);
                                            var c        = WebMercatorProjection.TileIdToCenterWebMercator(tiles[2].Item1, tiles[2].Item2, zoom);
                                            var d        = WebMercatorProjection.TileIdToCenterWebMercator(tiles[3].Item1, tiles[3].Item2, zoom);

                                            var material   = new Material($"Topo", Colors.White, 0.0f, 0.0f, mapImagePath);
                                            var topography = new Topography(new[] { a, b, c, d }.Average() - selectedOrigin, tileSize * 2, elevationData, material);
                                            this.Model.AddElement(topography);
                                        }
        }
예제 #6
0
        /// <summary>
        /// Converts the lat/lng point to pixels
        /// </summary>
        /// <param name="latitude">The latitude to convert in radians</param>
        /// <param name="longitude">The longitude to convert in radians</param>
        /// <param name="numTiles">The number of tiles</param>
        /// <returns>Pixel point</returns>
        public static Point LatLngToPixel(double latitude, double longitude, long numTiles)
        {
            var point = new Point(latitude.LatRadiansToDegrees(), longitude.LonRadiansToDegrees());

            return(WebMercatorProjection.LatLngToPixel(point, numTiles));
        }
예제 #7
0
        /// <summary>
        /// Adjust the bounding box to fit the requested tile size.
        /// </summary>
        /// <param name="parameters">The map parameters icluding the bounding box.</param>
        public void AdjustBoundingBoxToFit(MapParameters parameters)
        {
            log.LogInformation($"AdjustBoundingBoxToFit: requestedWidth={parameters.mapWidth}, requestedHeight={parameters.mapHeight}, bbox={parameters.bbox}");

            TryZoomIn(parameters, out int requiredWidth, out int requiredHeight, out var pixelMin, out var pixelMax);

            bool adjust = false;

            const double MARGIN_FACTOR          = 1.1; //10% margin
            var          adjustedRequiredWidth  = parameters.addMargin ? (int)Math.Round(requiredWidth * MARGIN_FACTOR) : requiredWidth;
            var          adjustedRequiredHeight = parameters.addMargin ? (int)Math.Round(requiredHeight * MARGIN_FACTOR) : requiredHeight;

            //Is it bigger or smaller than the requested size?
            if (adjustedRequiredWidth > parameters.mapWidth || adjustedRequiredHeight > parameters.mapHeight)
            {
                log.LogDebug("AdjustBoundingBoxToFit: scaling down");

                //We'll make the bounding box bigger in the same shape as the requested tile and scale down once the tile has been drawn.
                adjust = true;
                //Need to maintain aspect ratio. Figure out the ratio.
                double ratioX = (double)requiredWidth / (double)parameters.mapWidth;
                double ratioY = (double)requiredHeight / (double)parameters.mapHeight;
                // use whichever multiplier is bigger
                double ratio = ratioX > ratioY ? ratioX : ratioY;

                // now we can get the new height and width
                var factor    = parameters.addMargin ? MARGIN_FACTOR : 1.0;
                int newHeight = Convert.ToInt32(parameters.mapHeight * ratio * factor);
                int newWidth  = Convert.ToInt32(parameters.mapWidth * ratio * factor);

                var xDiff = Math.Abs(newWidth - requiredWidth) / 2;
                var yDiff = Math.Abs(newHeight - requiredHeight) / 2;

                //Pixel origin is top left
                pixelMin.x -= xDiff;
                pixelMax.x += xDiff;
                pixelMin.y += yDiff;
                pixelMax.y -= yDiff;

                //Adjust the tile width & height
                parameters.mapWidth  = (int)Math.Abs(pixelMax.x - pixelMin.x);
                parameters.mapHeight = (int)Math.Abs(pixelMax.y - pixelMin.y);
            }
            else
            {
                log.LogDebug("AdjustBoundingBoxToFit: expanding to fill tile ");

                //Expand the bounding box to fill the requested tile size
                if (adjustedRequiredWidth < parameters.mapWidth)
                {
                    double scaleWidth = (double)parameters.mapWidth / adjustedRequiredWidth;
                    adjustedRequiredWidth = (int)(scaleWidth * adjustedRequiredWidth);
                    double pixelCenterX = pixelMin.x + (pixelMax.x - pixelMin.x) / 2.0;
                    //Pixel origin is top left
                    pixelMin.x = pixelCenterX - adjustedRequiredWidth / 2.0;
                    pixelMax.x = pixelCenterX + adjustedRequiredWidth / 2.0;
                    adjust     = true;
                }

                if (adjustedRequiredHeight < parameters.mapHeight)
                {
                    double scaleHeight = (double)parameters.mapHeight / adjustedRequiredHeight;
                    adjustedRequiredHeight = (int)(scaleHeight * adjustedRequiredHeight);
                    double pixelCenterY = pixelMin.y + (pixelMax.y - pixelMin.y) / 2.0;
                    //Pixel origin is top left
                    pixelMin.y = pixelCenterY + adjustedRequiredHeight / 2.0;
                    pixelMax.y = pixelCenterY - adjustedRequiredHeight / 2.0;
                    adjust     = true;
                }
            }

            if (adjust)
            {
                //Convert the adjusted bbox to lat/lng
                var minLatLngDegrees = WebMercatorProjection.PixelToLatLng(pixelMin, parameters.numTiles);
                var maxLatLngDegrees = WebMercatorProjection.PixelToLatLng(pixelMax, parameters.numTiles);
                parameters.bbox.minLat = minLatLngDegrees.Latitude.LatDegreesToRadians();
                parameters.bbox.maxLat = maxLatLngDegrees.Latitude.LatDegreesToRadians();
                parameters.bbox.minLng = minLatLngDegrees.Longitude.LonDegreesToRadians();
                parameters.bbox.maxLng = maxLatLngDegrees.Longitude.LonDegreesToRadians();
            }
            log.LogInformation($"AdjustBoundingBoxToFit: returning mapWidth={parameters.mapWidth}, mapHeight={parameters.mapHeight}, bbox={parameters.bbox}");
        }
예제 #8
0
 public Vertex ProjectToGeographic()
 {
     this.Northing = WebMercatorProjection.yToLat(this.Northing);
     this.Easting  = WebMercatorProjection.xToLon(this.Easting);
     return(this);
 }
예제 #9
0
        protected override async Task <ContractExecutionResult> ProcessAsyncEx <T>(T item)
        {
            List <FileData>     files       = null;
            int                 zoomLevel   = 0;
            Point               topLeftTile = null;
            int                 numTiles    = 0;
            BoundingBox2DLatLon bbox        = null;

            if (item is DxfTileRequest request)
            {
                files = request.files?.ToList();
                bbox  = request.bbox;

                //Calculate zoom level
                zoomLevel = TileServiceUtils.CalculateZoomLevel(request.bbox.TopRightLat - request.bbox.BottomLeftLat,
                                                                request.bbox.TopRightLon - request.bbox.BottomLeftLon);
                log.LogDebug("DxfTileExecutor: BBOX differences {0} {1} {2}", request.bbox.TopRightLat - request.bbox.BottomLeftLat,
                             request.bbox.TopRightLon - request.bbox.BottomLeftLon, zoomLevel);
                numTiles = TileServiceUtils.NumberOfTiles(zoomLevel);
                Point topLeftLatLng = new Point(request.bbox.TopRightLat.LatRadiansToDegrees(),
                                                request.bbox.BottomLeftLon.LonRadiansToDegrees());
                topLeftTile = WebMercatorProjection.LatLngToTile(topLeftLatLng, numTiles);
                log.LogDebug($"DxfTileExecutor: zoomLevel={zoomLevel}, numTiles={numTiles}, xtile={topLeftTile.x}, ytile={topLeftTile.y}");
            }
            else if (item is DxfTile3dRequest request3d)
            {
                files = request3d.files?.ToList();

                zoomLevel   = request3d.zoomLevel;
                numTiles    = TileServiceUtils.NumberOfTiles(zoomLevel);
                topLeftTile = new Point {
                    x = request3d.xTile, y = request3d.yTile
                };
            }
            else
            {
                ThrowRequestTypeCastException <DxfTileRequest>();
            }

            log.LogDebug($"DxfTileExecutor: {files?.Count ?? 0} files");

            //Short circuit overlaying if there no files to overlay as ForAll is an expensive operation
            if (files == null || !files.Any())
            {
                byte[] emptyOverlayData;
                using (var bitmap = new Image <Rgba32>(WebMercatorProjection.TILE_SIZE, WebMercatorProjection.TILE_SIZE))
                {
                    emptyOverlayData = bitmap.BitmapToByteArray();
                }

                return(new TileResult(emptyOverlayData));
            }

            log.LogDebug(string.Join(",", files.Select(f => f.Name).ToList()));

            var          tileList = new List <byte[]>();
            const string DATA_OCEAN_ROOT_FOLDER_ID_KEY = "DATA_OCEAN_ROOT_FOLDER_ID";
            var          dataOceanRootFolder           = configStore.GetValueString(DATA_OCEAN_ROOT_FOLDER_ID_KEY);

            if (string.IsNullOrEmpty(dataOceanRootFolder))
            {
                throw new ArgumentException($"Missing environment variable {DATA_OCEAN_ROOT_FOLDER_ID_KEY}");
            }

            //For GeoTIFF files, use the latest version of a file
            var geoTiffFiles = files.Where(x => x.ImportedFileType == ImportedFileType.GeoTiff).ToList();

            if (geoTiffFiles.Any())
            {
                //Find any with multiple versions and remove old ones from the list
                var latestFiles = geoTiffFiles.GroupBy(g => g.Name).Select(g => g.OrderBy(o => o.SurveyedUtc).Last()).ToList();
                foreach (var geoTiffFile in geoTiffFiles)
                {
                    if (!latestFiles.Contains(geoTiffFile))
                    {
                        files.Remove(geoTiffFile);
                    }
                }
            }

            var fileTasks = files.Select(async file =>
            {
                //foreach (var file in request.files)
                //Check file type to see if it has tiles
                if (file.ImportedFileType == ImportedFileType.Linework ||
                    file.ImportedFileType == ImportedFileType.GeoTiff)
                {
                    var fullPath = DataOceanFileUtil.DataOceanPath(dataOceanRootFolder, file.CustomerUid, file.ProjectUid);
                    var fileName = DataOceanFileUtil.DataOceanFileName(file.Name,
                                                                       file.ImportedFileType == ImportedFileType.SurveyedSurface || file.ImportedFileType == ImportedFileType.GeoTiff,
                                                                       Guid.Parse(file.ImportedFileUid), file.SurveyedUtc);
                    fileName = DataOceanFileUtil.GeneratedFileName(fileName, file.ImportedFileType);

                    if (zoomLevel >= file.MinZoomLevel)
                    {
                        byte[] tileData = null;
                        if (zoomLevel <= file.MaxZoomLevel || file.MaxZoomLevel == 0) //0 means not calculated
                        {
                            tileData = await GetTileAtRequestedZoom(topLeftTile, zoomLevel, fullPath, fileName);
                        }
                        else if (zoomLevel - file.MaxZoomLevel <= 5) //Don't try to scale if the difference is too excessive
                        {
                            tileData = await GetTileAtHigherZoom(topLeftTile, zoomLevel, fullPath, fileName,
                                                                 file.MaxZoomLevel, numTiles);
                        }
                        else
                        {
                            log.LogDebug(
                                "DxfTileExecutor: difference between requested and maximum zooms too large; not even going to try to scale tile");
                        }

                        if (tileData != null && tileData.Length > 0)
                        {
                            tileList.Add(tileData);
                        }
                    }
                }
            });

            await Task.WhenAll(fileTasks);

            log.LogDebug($"DxfTileExecutor: Overlaying {tileList.Count} tiles");
            byte[] overlayData = TileOverlay.OverlayTiles(tileList);

            return(new TileResult(overlayData));
        }