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)
STMorton: Creating a Morton Number Space Key Value for Grid Cells
Introduction
NOTE: This article is a copy of the original first written in 2010 from my old website.
Space curves are imaginary lines that systematically cover all tiles in an indexed space. The most well known in GIS is the Morton key. The utility of such a number can be seen though its implementing a form a space autocorrelation such that all geometries in an area are given a Morton number such that, when sorted, they appear next to each other in the sort order. Written to disk, such sorted geometries lie next to each other.
Function STMorton
USE [GISDB] -- Change this to your database GO SET ANSI_NULLS ON SET QUOTED_IDENTIFIER ON GO -- ============================================= -- Author: <Author,Simon Greener> -- Create date: <Create Date, 7th August 2010> -- Description: <Description, 7th August 2010> -- ============================================= CREATE FUNCTION [dbo].[STMorton] (@p_col INT, @p_row INT) RETURNS INT WITH EXECUTE AS CALLER, RETURNS NULL ON NULL INPUT AS BEGIN /* This procedure calculates the Morton number of a cell at the given row and column Written: D.M. Mark, Jan 1984; Converted to Vax/VMS: July 1985 Converted to Java: May 2007 (Simon Greener) Converted to PL/SQL: September 2009 (Simon Greener) Converted to TSQL: August 2010 (Simon Greener) */ BEGIN DECLARE @ROW INT = abs(@p_row), @col INT = abs(@p_col), @KEY INT = 0, @level INT = 0, @left_bit INT, @right_bit INT, @quadrant INT; BEGIN While ((@row>0) OR (@col>0)) BEGIN /* Split off the row (left_bit) and column (right_bit) bits and then combine them to form a bit-pair representing the quadrant */ SET @left_bit = @ROW % 2; SET @right_bit = @col % 2; SET @quadrant = @right_bit + 2*@left_bit; SET @KEY = @KEY + round(@quadrant * POWER(2,2*@level), 0, 1); /* row, column, and level are then modified before the loop continues */ IF ( @ROW = 1 AND @col = 1 ) BEGIN SET @ROW = 0; SET @col = 0; END ELSE BEGIN SET @ROW = @ROW / 2; SET @col = @col / 2; SET @level = @level + 1; END; END; END; RETURN @KEY; END; END GO
Examples
Now for a simple test. (See dbo.MBR2Geometry )
USE [GISDB] -- Change this to your database GO SELECT foo.MortonKey, foo.geom FROM (SELECT [GISDB].[dbo].STMorton( a.gcol, b.grow ) AS MortonKey, [GISDB].[dbo].MBR2GEOMETRY(a.gcol,b.grow,10.0,10.0,0) AS geom FROM (SELECT 0 + g.IntValue AS gcol FROM generate_series(0,7,1) AS g) AS a CROSS APPLY (SELECT 0 + g.IntValue AS grow FROM generate_series(0,7,1) AS g) AS b ) foo ORDER BY mortonkey;
This is what it looks like:
gcol | grow | mortonkey | wktgeom |
---|---|---|---|
integer | integer | integer | text |
0 | 0 | 0 | POLYGON ((0 0,0 0,0 0,0 0,0 0)) |
1 | 0 | 1 | POLYGON ((1 0,1 0,1 0,1 0,1 0)) |
0 | 1 | 2 | POLYGON ((0 1,0 1,0 1,0 1,0 1)) |
1 | 1 | 3 | POLYGON ((1 1,1 1,1 1,1 1,1 1)) |
2 | 0 | 4 | POLYGON ((2 0,2 0,2 0,2 0,2 0)) |
3 | 0 | 5 | POLYGON ((3 0,3 0,3 0,3 0,3 0)) |
2 | 1 | 6 | POLYGON ((2 1,2 1,2 1,2 1,2 1)) |
3 | 1 | 7 | POLYGON ((3 1,3 1,3 1,3 1,3 1)) |
0 | 2 | 8 | POLYGON ((0 2,0 2,0 2,0 2,0 2)) |
1 | 2 | 9 | POLYGON ((1 2,1 2,1 2,1 2,1 2)) |
0 | 3 | 10 | POLYGON ((0 3,0 3,0 3,0 3,0 3)) |
1 | 3 | 11 | POLYGON ((1 3,1 3,1 3,1 3,1 3)) |
2 | 2 | 12 | POLYGON ((2 2,2 2,2 2,2 2,2 2)) |
3 | 2 | 13 | POLYGON ((3 2,3 2,3 2,3 2,3 2)) |
2 | 3 | 14 | POLYGON ((2 3,2 3,2 3,2 3,2 3)) |
3 | 3 | 15 | POLYGON ((3 3,3 3,3 3,3 3,3 3)) |
4 | 0 | 16 | POLYGON ((4 0,4 0,4 0,4 0,4 0)) |
5 | 0 | 17 | POLYGON ((5 0,5 0,5 0,5 0,5 0)) |
4 | 1 | 18 | POLYGON ((4 1,4 1,4 1,4 1,4 1)) |
We can show the actual “curve” as a directed line through the morton numbers as follows. (The required function DumpPoints is described by following the link.)
WITH morton_grid AS ( SELECT foo.MortonKey, foo.geom FROM (SELECT [GISDB].[dbo].STMorton( a.gcol, b.grow ) AS MortonKey, [GISDB].[dbo].MBR2GEOMETRY(a.gcol,b.grow,10.0,10.0,0) AS geom FROM (SELECT 0 + g.IntValue AS gcol FROM [GISDB].[dbo].generate_series(0,7,1) AS g) AS a CROSS APPLY (SELECT 0 + g.IntValue AS grow FROM [GISDB].[dbo].generate_series(0,7,1) AS g) AS b ) foo ) SELECT geometry::STGeomFromText('LINESTRING(' + STUFF((SELECT ',' + a.coord FROM (SELECT m.MortonKey, STR(e.[x],5,1) + ' ' + STR(e.[y],5,1) AS coord FROM morton_grid m CROSS apply [GISDB].[dbo].DumpPoints(m.geom.STCentroid()) e ) a ORDER BY ',' + STR(a.mortonkey,12,0) FOR XML PATH(''), TYPE, ROOT).VALUE('root[1]','nvarchar(max)'),1,1,'') + ')',0) AS geom;
Superimposing the curve over the grid of Morton keys reveals:
Conclusion
I hope this is of use to someone out there.
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