SpatialDB Advisor
Sometimes, some things are simple and prosaic rather than clever and complicated.
Building solutions in TSQL requires one to build a prior collection of handy functions before one can be really productive.
Here is a function I use called isGeo which if provided the name of a table/view and a column name returns 1 (true) if the column exists and is of type geometry or geography.
USE [GISDB] /* Replace this with the name of your database */
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
schema_name nvarchar(128),
object_name nvarchar(128),
column_name nvarchar(128))
RETURNS int
WITH EXECUTE AS CALLER,
RETURNS NULL ON NULL INPUT
AS
BEGIN
DECLARE v_result int;
SELECT v_result=1
FROM (SELECT sc.name as schema_name,
ta.name as object_name,
co.name as column_name,
ty.name as column_type,
'TABLE' as object_type
FROM sys.tables ta
JOIN sys.schemas sc ON sc.schema_id = ta.schema_id
JOIN sys.columns co ON ta.object_id = co.object_id
JOIN sys.types ty ON co.user_type_id = ty.user_type_id
WHERE ty.name IN ('geography','geometry')
UNION ALL
SELECT sc.name as schema_name,
va.name as object_name,
co.name as column_name,
ty.name as column_type,
'VIEW' as object_type
FROM sys.views va
JOIN sys.schemas sc ON sc.schema_id = va.schema_id
JOIN sys.columns co ON va.object_id = co.object_id
JOIN sys.types ty ON co.user_type_id = ty.user_type_id
WHERE ty.name IN ('geography','geometry')
) a
WHERE a.schema_name = schema_name
AND a.object_name = object_name
AND a.column_name = column_name ;
Return case when v_result is null then 0 else v_result end;
END;
Now, some tests…
-- Check an existing geometry column in a physical table
select dbo.isGeo('dbo','ADMIN','GEOM') as Result;
Result
1
-- Check an existing geography column in a View
select dbo.isGeo('dbo','vw_terminal','shape') as Result;
Result
1
-- Try again with a non-existing schema
select dbo.isGeo('simon','ADMIN','GEOM') as Result;
Result
0
-- Check action of "RETURNS NULL ON NULL INPUT" function option
select dbo.isGeo(NULL,'ADMIN','GEOM') as Result;
Result
NULL
-- Check again with connected schema provided by appropriate function
select dbo.isGeo(SCHEMA_NAME(),'ADMIN','GEOM') as Result;
Result
1
I hope this helps someone.


















Ever needed to extract a textual description of all geometry types in a single geometry/geography in SQL Server 2008?
Recently I had cause to do so and created the following function that implements this need.
/*********************************************************************************
** @function : Geometry_Type
** @precis : Returns geometry type of passed in geometry as string.
** @description : Returns list of geometry types (from OGC STGeometryType function)
** that describe the contents of the passed in geometry.
** Any GeometryCollection is "exploded" to examine the geometry types
** within it.
** Geography objects can be processed by converting to geometry using
** dbo.toGeometry() function.
** @example : select dbo.geometry_type(geometry::STGeomFromText('POINT(0 1 2)',0));
** @param : p_geom : the geometry object to be processed.
** @param_type : p_geom : geometry
** @history : Simon Greener, The Spatial DB Advisor, September 2009
** @copyright : Licensed under a Creative Commons Attribution-Share Alike 2.5 Australia License.
** (http://creativecommons.org/licenses/by-sa/2.5/au/)
**/
Now let’s test it against a selection of all possible geometry types
select dbo.geometry_type(geometry::STGeomFromText('
POINT(0 1 2)',0)) as gtype
union all select dbo.geometry_type(geometry::STGeomFromText('
MULTIPOINT((0 0 0),
(1 1 1),
(2 2 2),
(3 3 3))',0)) as gtype
union all select dbo.geometry_type(geometry::STGeomFromText('
LINESTRING(0 0, 1 1)',0)) as gtype
union all select dbo.geometry_type(geometry::STGeomFromText('
MULTILINESTRING(
(0 0,1 1,1 2),
(2 3,3 2,5 4))',0)) as gtype
union all select dbo.geometry_type(geometry::STGeomFromText('
POLYGON(
(326000.0 5455000.0,327000.0 5455000.0,326500.0 5456000.0,326000.0 5455000.0),
(326500.0 5455500.0,326550.0 5455200.0,326450.0 5455200.0,326500.0 5455500.0))',0)) as gtype
union all SELECT dbo.geometry_type(geometry::STGeomFromText('
MULTIPOLYGON(
((326000.0 5455000.0,327000.0 5455000.0,326500.0 5456000.0,326000.0 5455000.0),
(326500.0 5455500.0,326550.0 5455200.0,326450.0 5455200.0,326500.0 5455500.0)),
((321000.0 5450000.0,322000.0 5450000.0,321500.0 5451000.0,321000.0 5450000.0)))',0)) as gtype
union all select dbo.geometry_type(geometry::STGeomFromText('
GEOMETRYCOLLECTION(
POINT(2 3 4),
LINESTRING(2 3 4,3 4 5),
POLYGON((326000.0 5455000.0,327000.0 5455000.0,326500.0 5456000.0,326000.0 5455000.0)),
POINT(4 5))',0));
| gtype |
|---|
| Point |
| MultiPoint |
| LineString |
| MultiLineString |
| Polygon |
| MultiPolygon |
| GeometryCollection:Point:LineString:Polygon |
But what if I want to create a single string that describes all the geometries in a particular table?
Here is one way to do this via XML aggregation.
WITH geometry_Types AS
(
SELECT distinct dbo.geometry_type(a.geom) as gtype
FROM (select geometry::STGeomFromText('
POINT(0 1 2)',0) as geom
union all select geometry::STGeomFromText('
MULTIPOINT((0 0 0),
(1 1 1),
(2 2 2),
(3 3 3))',0) as geom
union all select geometry::STGeomFromText('
LINESTRING(0 0, 1 1)',0) as geom
union all select geometry::STGeomFromText('
MULTILINESTRING(
(0 0,1 1,1 2),
(2 3,3 2,5 4))',0) as geom
union all select geometry::STGeomFromText('
POLYGON(
(326000.0 5455000.0,327000.0 5455000.0,326500.0 5456000.0,326000.0 5455000.0),
(326500.0 5455500.0,326550.0 5455200.0,326450.0 5455200.0,326500.0 5455500.0))',0) as geom
union all SELECT geometry::STGeomFromText('
MULTIPOLYGON(
((326000.0 5455000.0,327000.0 5455000.0,326500.0 5456000.0,326000.0 5455000.0),
(326500.0 5455500.0,326550.0 5455200.0,326450.0 5455200.0,326500.0 5455500.0)),
((321000.0 5450000.0,322000.0 5450000.0,321500.0 5451000.0,321000.0 5450000.0)))',0) as geom
union all select geometry::STGeomFromText('
GEOMETRYCOLLECTION(
POINT(2 3 4),
LINESTRING(2 3 4,3 4 5),
POLYGON((326000.0 5455000.0,327000.0 5455000.0,326500.0 5456000.0,326000.0 5455000.0)),
POINT(4 5))',0) as geom
) a
)
SELECT (STUFF((SELECT DISTINCT ':' + a.gtype
FROM geometry_types a
ORDER BY ':' + a.gtype
FOR XML PATH(''), TYPE, ROOT).value('root[1]','nvarchar(max)'),1,1,'')
) as GeometryTypes;
| GeometryTypes |
|---|
| GeometryCollection:Point:LineString:Polygon:LineString:MultiLineString:MultiPoint:MultiPolygon:Point:Polygon |
Notice how the list is not unique. Here is one method of making it unique.
WITH geometries AS
(
select geometry::STGeomFromText('
POINT(0 1 2)',0) as geom
union all select geometry::STGeomFromText('
MULTIPOINT((0 0 0),
(1 1 1),
(2 2 2),
(3 3 3))',0) as geom
union all select geometry::STGeomFromText('
LINESTRING(0 0, 1 1)',0) as geom
union all select geometry::STGeomFromText('
MULTILINESTRING(
(0 0,1 1,1 2),
(2 3,3 2,5 4))',0) as geom
union all select geometry::STGeomFromText('
POLYGON(
(326000.0 5455000.0,327000.0 5455000.0,326500.0 5456000.0,326000.0 5455000.0),
(326500.0 5455500.0,326550.0 5455200.0,326450.0 5455200.0,326500.0 5455500.0))',0) as geom
union all SELECT geometry::STGeomFromText('
MULTIPOLYGON(
((326000.0 5455000.0,327000.0 5455000.0,326500.0 5456000.0,326000.0 5455000.0),
(326500.0 5455500.0,326550.0 5455200.0,326450.0 5455200.0,326500.0 5455500.0)),
((321000.0 5450000.0,322000.0 5450000.0,321500.0 5451000.0,321000.0 5450000.0)))',0) as geom
union all select geometry::STGeomFromText('
GEOMETRYCOLLECTION(
POINT(2 3 4),
LINESTRING(2 3 4,3 4 5),
POLYGON((326000.0 5455000.0,327000.0 5455000.0,326500.0 5456000.0,326000.0 5455000.0)),
POINT(4 5))',0) as geom
)
SELECT (STUFF((SELECT DISTINCT ':' + a.gtype
FROM ( select distinct t.token as gtype
from geometries a
cross apply
dbo.Tokenizer(dbo.geometry_type(a.geom),':') as t
) a
ORDER BY ':' + a.gtype
FOR XML PATH(''), TYPE, ROOT).value('root[1]','nvarchar(max)'),1,1,'')
) as GeometryTypes;
| GeometryTypes |
|---|
| GeometryCollection:LineString:MultiLineString:MultiPoint:MultiPolygon:Point:Polygon |
Finally, show how we can use the function with geography type data.
select dbo.Geometry_Type(
dbo.MBR2GEOMETRY(147,-44,148,-43,4326).STDifference(
dbo.MBR2GEOMETRY(147.4,-43.6,147.6,-43.2,4326) ).STUnion(
dbo.MBR2GEOMETRY(150,-44,151,-43,4326) ) ) as geogtypes;
| geogtypes |
|---|
| MultiPolygon |
I hope this function is useful to someone.


















I demonstrated in this article the usefulness of having a vectorising function in one’s spatial database kitbag (there are GetVector functions in my CENTROID and GEOM packages – in particular the GetVector function is vital to the implementation of the algorithm in my CENTROID.SDO_CENTROID function).
So, I have also added one to my SQL Server 2008 kitbag.
This function, as against the PostGIS and Oracle implementation, does not use any object types due to limitations in the SQL Server 2008 TSQL system (my implementation deliberately does not use the CLR system).
USE [GISDB] -- Change this to your database
GO
Testing
Here’s a bunch of tests.
select v.*
from dbo.GetVector(geometry::STGeomFromText(
'LINESTRING(0 0 10 10, 1 1 10 20, 2 2 10 30, 3 3 10 40)',0)) as v;
| sx | sy | sz | sm | ex | ey | ez | em |
|---|---|---|---|---|---|---|---|
| 0 | 0 | 10 | 10 | 1 | 1 | 10 | 20 |
| 1 | 1 | 10 | 20 | 2 | 2 | 10 | 30 |
| 2 | 2 | 10 | 30 | 3 | 3 | 10 | 40 |
select v.*
from dbo.GetVector(geometry::STGeomFromText(
'MULTILINESTRING((0 0,1 1,1 2),(2 3,3 2,5 4))',0)) as v;
| sx | sy | sz | sm | ex | ey | ez | em |
|---|---|---|---|---|---|---|---|
| 0 | 0 | NULL | NULL | 1 | 1 | NULL | NULL |
| 1 | 1 | NULL | NULL | 1 | 2 | NULL | NULL |
| 2 | 3 | NULL | NULL | 3 | 2 | NULL | NULL |
| 3 | 2 | NULL | NULL | 5 | 4 | NULL | NULL |
-- Ordinary polygon
--
select v.*
from dbo.GetVector(geometry::STGeomFromText(
'POLYGON((326000.0 5455000.0,327000.0 5455000.0,326500.0 5456000.0,326000.0 5455000.0))',0)) as v;
| sx | sy | sz | sm | ex | ey | ez | em |
|---|---|---|---|---|---|---|---|
| 326000 | 5455000 | NULL | NULL | 327000 | 5455000 | NULL | NULL |
| 327000 | 5455000 | NULL | NULL | 326500 | 5456000 | NULL | NULL |
| 326500 | 5456000 | NULL | NULL | 326000 | 5455000 | NULL | NULL |
-- Polygon with a hole
--
select v.*
from dbo.GetVector(geometry::STGeomFromText(
'POLYGON((326000.0 5455000.0,327000.0 5455000.0,326500.0 5456000.0,326000.0 5455000.0),
(326500.0 5455500.0,326550.0 5455200.0,326450.0 5455200.0,326500.0 5455500.0))',0)) as v;
| sx | sy | sz | sm | ex | ey | ez | em |
|---|---|---|---|---|---|---|---|
| 326000 | 5455000 | NULL | NULL | 327000 | 5455000 | NULL | NULL |
| 327000 | 5455000 | NULL | NULL | 326500 | 5456000 | NULL | NULL |
| 326500 | 5456000 | NULL | NULL | 326000 | 5455000 | NULL | NULL |
| 326500 | 5455500 | NULL | NULL | 326550 | 5455200 | NULL | NULL |
| 326550 | 5455200 | NULL | NULL | 326450 | 5455200 | NULL | NULL |
| 326450 | 5455200 | NULL | NULL | 326500 | 5455500 | NULL | NULL |
-- MultiPolygon With a hole
--
select v.*
from dbo.GetVector(geometry::STGeomFromText(
'MULTIPOLYGON(((326000.0 5455000.0,327000.0 5455000.0,326500.0 5456000.0,326000.0 5455000.0),
(326500.0 5455500.0,326550.0 5455200.0,326450.0 5455200.0,326500.0 5455500.0)),
((321000.0 5450000.0,322000.0 5450000.0,321500.0 5451000.0,321000.0 5450000.0)))',0)) as v;
| sx | sy | sz | sm | ex | ey | ez | em |
|---|---|---|---|---|---|---|---|
| 326000 | 5455000 | NULL | NULL | 327000 | 5455000 | NULL | NULL |
| 327000 | 5455000 | NULL | NULL | 326500 | 5456000 | NULL | NULL |
| 326500 | 5456000 | NULL | NULL | 326000 | 5455000 | NULL | NULL |
| 326500 | 5455500 | NULL | NULL | 326550 | 5455200 | NULL | NULL |
| 326550 | 5455200 | NULL | NULL | 326450 | 5455200 | NULL | NULL |
| 326450 | 5455200 | NULL | NULL | 326500 | 5455500 | NULL | NULL |
| 321000 | 5450000 | NULL | NULL | 322000 | 5450000 | NULL | NULL |
| 322000 | 5450000 | NULL | NULL | 321500 | 5451000 | NULL | NULL |
| 321500 | 5451000 | NULL | NULL | 321000 | 5450000 | NULL | NULL |
But what if you want the start and end of the vector to be geometry points?
-- Get as points
--
select geometry::Point(v.sx,v.sy,0).STAsText() as startPoint,
geometry::Point(v.ex,v.ey,0).STAsText() as endPoint
from dbo.GetVector(geometry::STGeomFromText(
'LINESTRING(0 0 10 10, 1 1 10 20, 2 2 10 30, 3 3 10 40)',0)) as v;
| startPoint | endPoint |
|---|---|
| POINT (0 0) | POINT (1 1) |
| POINT (1 1) | POINT (2 2) |
| POINT (2 2) | POINT (3 3) |
Finally, what if you want each vector to be represented as a single linestring and not a individual ordinates (sx,sy, …. ,ez,em)?
-- Get as linestring
--
select geometry::STGeomFromText(
'LINESTRING(' +
CAST(v.sx as varchar(10)) + ' ' + CAST(v.sy as varchar(10)) + ',' +
CAST(v.ex as varchar(10)) +' ' + CAST(v.ey as varchar(10)) + ')',0).STAsText() as vLine
from dbo.GetVector(geometry::STGeomFromText(
'LINESTRING(0 0 10 10, 1 1 10 20, 2 2 10 30, 3 3 10 40)',0)) as v;
| vLine |
|---|
| LINESTRING (0 0, 1 1) |
| LINESTRING (1 1, 2 2) |
| LINESTRING (2 2, 3 3) |
I hope this is useful to someone.


















Oracle has a polygon geometry type (non-standard) called an optimized rectangle. To define one all one has to do is supply it with the lower-left and upper-right coordinates. This is a much easier thing to do than having to supply 5 coordinates in anti-clockwise order.
PostGIS also has a bunch of bounding box functions that also are useful for when working with rectangular polygons or minimum bounding rectangles (MBRs).
I have been playing around a lot, recently, with grid generation in SQL Server 2008 (more on this in another post). And, in doing so, I decided to code some functions that would return SQL Server 2008 geometry and geography polygon objects given the lower-left and upper-right coordinates of a minimum bounding rectangle.
Here they are.
USE [GISDB] -- Change this to your database
GO
Testing
Testing is pretty easy.
-- First, let's create a simple polygon geometry
--
select dbo.MBR2GEOMETRY(0,0,100,100,0).STAsText() as geomWKT;
| geomWKT |
|---|
| POLYGON ((0 0, 100 0, 100 100, 0 100, 0 0)) |
-- Now, let's create a polygon with a hole
--
select dbo.MBR2GEOMETRY(0,0,100,100,0).STDifference(dbo.MBR2GEOMETRY(40,40,60,60,0)).STAsText() as geomWKT;
| geomWKT |
|---|
| POLYGON ((0 0, 100 0, 100 100, 0 100, 0 0), (40 40, 40 60, 60 60, 60 40, 40 40)) |
-- Now let's create a multipolygon with a hole
--
select dbo.MBR2GEOMETRY(0,0,100,100,0).STDifference(dbo.MBR2GEOMETRY(40,40,60,60,0)).STUnion(dbo.MBR2GEOMETRY(200,200,400,400,0)).STAsText() as geomWKT;
| geomWKT |
|---|
| MULTIPOLYGON (((200 200, 400 200, 400 400, 200 400, 200 200)), ((0 0, 100 0, 100 100, 0 100, 0 0), (40 40, 40 60, 60 60, 60 40, 40 40))) |
-- Finally, let's create a polygon with a hole using the mbr2geography function
--
select dbo.MBR2GEOMETRY(147,-44,148,-43,4326).STDifference(dbo.MBR2GEOMETRY(147.4,-43.6,147.6,-43.2,4326)).STAsText() as geogWKT;
| geogWKT |
|---|
| POLYGON ((147 -44, 148 -44, 148 -43, 147 -43, 147 -44), (147.40000000596046 -43.599999994039536, 147.40000000596046 -43.200000002980232, 147.59999999403954 -43.200000002980232, 147.59999999403954 -43.599999994039536, 147.40000000596046 -43.599999994039536)) |
I hope someone finds these functions useful.

















