Top 5 Recent Articles
ARTICLES CATEGORIES
- Algorithms (22)
- All (399)
- Biography (1)
- Blog (44)
- Business Requirements (1)
- Commentary (1)
- Conversion (2)
- Customers (2)
- Data Models (1)
- Education (2)
- GeoRaptor (13)
- GPS (1)
- Image Processing (2)
- Import Export (8)
- Licensing (2)
- LiDAR (1)
- Linear Referencing (4)
- Manifold GIS (3)
- Mapping (1)
- MySQL Spatial (7)
- Networking and Routing (including Optimization) (5)
- Open Source (18)
- Oracle Spatial and Locator (194)
- Partitioning (1)
- PostGIS (36)
- Projections (1)
- Published Articles (1)
- qGIS (1)
- Recommendations (1)
- Services (1)
- Software Change Log (1)
- Source Code (37)
- Space Curves (9)
- Spatial Database Functions (109)
- Spatial DB comparison (1)
- Spatial XML Processing (11)
- SQL Server Spatial (92)
- Standards (3)
- Stored Procedure (17)
- Tessellation or Gridding (10)
- Tools (2)
- Topological Relationships (1)
- Training (2)
- Triangulation (2)
Bing Maps Tile System Functions for TSQL
This blog presents functions for Bing Maps Tile System for TSQL.
This support includes converting Latitude/Longitude pairs to Bing Maps QuadKeys.
The source for the functions is over at the Mircosoft site – an article by James Schwartz.
They are donated to the public domain.
use DEVDB go -- ***************************************************************** Drop Function If Exists [dbo].[STBingTileXYToQuadKey] ; GO Create Function [dbo].[STBingTileXYToQuadKey] ( @p_tileX integer, @p_tileY integer, @p_levelOfDetail integer = 23 ) returns varchar(100) As Begin Declare @i integer, @v_digit integer = 43, -- char(1) = '0', @v_mask integer, @v_quadKey varchar(100); SET @i = ISNULL(@p_levelOfDetail,23); WHILE (@i > 0) BEGIN SET @v_mask = [dbo].[shiftLeft](1,(@i - 1)); SET @v_digit = 0; IF ((@p_tileX & @v_mask) != 0) SET @v_digit += 1; IF ((@p_tileY & @v_mask) != 0) BEGIN SET @v_digit += 1; SET @v_digit += 1; END; -- Append @v_digit to end of key SET @v_quadKey = CONCAT(@v_quadKey,CAST(@v_digit as varchar(100))); SET @i -= 1; END; RETURN @v_quadKey; END; GO -- compared to Micorosft C# Class and is correct. -- ************************************************************************ Drop Function If Exists [dbo].[STBingLatLongToQuadKey]; GO Create Function [dbo].[STBingLatLongToQuadKey] ( @p_latitude float, @p_longitude float, @p_levelOfDetail integer = 23 ) returns varchar(100) AS BEGIN DECLARE @c_MinLatitude Float = -85.05112878, @c_MaxLatitude Float = 85.05112878, @c_MinLongitude Float = -180.0, @c_MaxLongitude Float = 180.0, @v_latitude float, @v_longitude float, @v_levelOfDetail integer = ISNULL(@p_levelOfDetail,23), @v_x float, @v_sinLatitude float, @v_y float, @v_mapSize bigint, /* actual uint */ @v_pixelX float, @v_pixelY float, @v_iPixelX integer, @v_iPixelY integer, @v_tileX integer, @v_tileY integer, @v_quadKey varchar(100); -- Clip latitude numbers to the specified minimum and maximum values. SET @v_latitude = case when @p_latitude > @c_MinLatitude then @p_latitude else @c_MinLatitude end; SET @v_latitude = case when @v_latitude < @c_MaxLatitude then @v_latitude else @c_MaxLatitude end; -- Clip longitude numbers to the specified minimum and maximum values. SET @v_longitude = case when @p_longitude > @c_MinLongitude then @p_longitude else @c_MinLongitude end; SET @v_longitude = case when @v_longitude < @c_MaxLongitude then @v_longitude else @c_MaxLongitude end; SET @v_x = (@v_longitude + 180.0) / 360.0; SET @v_sinLatitude = Sin(@v_latitude * PI() / 180.0); SET @v_y = 0.5 - LOG((1.0 + @v_sinLatitude) / (1.0 - @v_sinLatitude)) / (4.0 * PI()); SET @v_mapSize = [dbo].[shiftLeft](256,@v_levelOfDetail); -- Clip pixelX -- (int) Clip(x * mapSize + 0.5, 0, mapSize - 1); -- SET @v_pixelX = (@v_x * @v_mapSize + 0.5); SET @v_pixelX = case when @v_pixelX > 0.0 then @v_pixelX else 0.0 end; SET @v_iPixelX = case when @v_pixelX < (@v_mapSize - 1) then @v_pixelX else (@v_mapSize - 1) end; -- Clip pixelY -- SET @v_pixelY = (@v_y * @v_mapSize + 0.5); SET @v_pixelY = case when @v_pixelY > 0.0 then @v_pixelY else 0.0 end; SET @v_ipixelY = case when @v_pixelY < (@v_mapSize - 1) then @v_pixelY else (@v_mapSize - 1) end; -- Now convert PixelX/Y to TileXY SET @v_tileX = @v_iPixelX / 256; SET @v_tileY = @v_iPixelY / 256; -- Now convert TileXY to QuadKey SET @v_quadKey = [dbo].[STBingTileXYToQuadKey] (@v_tileX, @v_tileY, @v_levelOfDetail ); RETURN @v_quadKey; END; GO -- ************************************************************************ drop function if exists [dbo].[STBing2TileXY]; GO create function [dbo].[STBing2TileXY] ( @p_QuadKey varchar(100) ) returns @result table ( tileX integer, tileY integer, levelOfDetail integer ) AS -- public static void QuadKeyToTileXY(string quadKey, out int tileX, out int tileY, out int levelOfDetail) BEGIN Declare @v_tileX integer = 0, @v_tileY integer = 0, @v_levelOfDetail integer = 1, @v_i integer, @v_mask integer, @v_qk varchar(100); SET @v_levelOfDetail = LEN(@p_quadKey); SET @v_i = @v_levelOfDetail; WHILE ( @v_i > 0 ) BEGIN SET @v_mask = [dbo].[leftShift](1,(@v_i - 1)); SET @v_qk = SUBSTRING(@p_QuadKey,@v_levelOfDetail - @v_i + 1,1); IF ( @v_qk = '0' ) break; IF ( @v_qk = '1' ) BEGIN SET @v_tileX |= @v_mask; END; IF ( @v_qk = '2' ) BEGIN SET @v_tileY |= @v_mask; END; IF ( @v_qk = '3' ) BEGIN SET @v_tileX |= @v_mask; SET @v_tileY |= @v_mask; END; SET @v_i -= @v_i; END; INSERT INTO @result ( tileX, tileY, levelOfDetail ) VALUES ( @v_tileX, @v_tileY, @v_levelOfDetail ) RETURN; END; GO
Some tests include:
Select [dbo].[STBingLatLongToQuadKey] (-42.0,147.0,3) as qKey GO qKey 313 select * from [dbo].[STBing2TileXY]('313'); GO tileX tileY levelOfDetail 4 4 3 Select [dbo].[STBingTileXYToQuadKey] (7,5,3) as qKey GO qKey 313
I hope this is of use to someone.
Documentation
- MySQL Spatial General Functions
- Oracle LRS Objects
- Oracle Spatial Exporter (Java + pl/SQL)
- Oracle Spatial Object Functions
- Oracle Spatial Object Functions (Multi Page)
- PostGIS pl/pgSQL Functions
- SC4O Oracle Java Topology Suite (Java + pl/SQL)
- SQL Server Spatial General TSQL Functions
- SQL Server Spatial LRS TSQL Functions