Spatial data in SQL Server 2008

Using the SQL Server types

Instantiating the types

The Geometry and Geography data types are implemented as System Defined Types written in .NET. They are automatically installed with the server, and are available for use by any SQL Server 2008 database. For this post, I will use the Geometry type to demonstrate capability (the primary difference between the two types is how distance and area is calculated).

Any variable, parameter, or table column can be declared as type Geometry. Notice that the type name is not case sensitive.

DECLARE @g Geometry

As mentioned before, Geometry by itself is abstract. In order to do something interesting, you need to instantiate the type as one of the concrete subclasses of the Geometry type. Adhering to the OGC standards, you can use the STGeomFromText() static method to parse data provided in valid WKT syntax, and then your variable will take on the characteristics of the defined subclass (i.e., it will be instantiated as one of the concrete types). Note that in SQL syntax, CLR method names are case sensitive, so stgeomfromtext() will not work.

Now, in C# and VB.NET, it is common to invoke static methods of a type using a dot notation, as in geometry.STGeomFromText(). However, this is not the case in T-SQL, because that particular syntax implies that you're calling a User Defined Function belonging to the "geometry" schema. Instead, when calling a static method belonging to a type, you separate the type name and the method name using two colons (::).

For example, to use STGeomFromText() to create a LineString, you would do the following:

DECLARE @g Geometry
SET @g = Geometry::STGeomFromText('LINESTRING(0 0, 10 10, 21 2)',
                                  0)

You might be wondering, "What's that weird zero at the end all about?" In Part 1, I touched on the concept of a Spatial Reference System that defines things such as the unit of measure and the dimensions of the world being represented, etc. The zero in this method call is the Spatial Reference ID (SRID) parameter, and it is required that the SRID being used is declared alongside any piece of spatial information.

SQL Server 2008 will not perform calculations on pieces of spatial information that belong to separate Spatial Reference Systems (because one system may use centimeters, and another may use miles, and SQL Server simply does not have the means to automatically convert units). For the Geometry type, it is common to just use zero for the SRID when all of your data is from the same Spatial Reference System (Geography uses 4326 as the default, to be explained later).

Note: A linebreak was inserted between the parameters in order to make it obvious to the eye. There is no requirement in T-SQL that requires the linebreak.

Since we were declaring a LineString specifically, we could have also used a static method that only accepts valid LineStrings as input:

DECLARE @g Geometry
SET @g = Geometry::STLineFromText('LINESTRING(0 0, 10 10, 21 2)',
                                  0)

If we tried to supply something that was not valid WKT for a LineString, like POINT(0 0),then a .NET FormatException would have been thrown:

Msg 6522, Level 16, State 1, Line 2
A .NET Framework error occurred during execution of user-defined routine or aggregate "geometry":
System.FormatException: 24142: Expected LINESTRING at position 0. The input has POINT(0 0).
System.FormatException:
   at Microsoft.SqlServer.Types.OpenGisWktReader.RecognizeToken(String token)
   at Microsoft.SqlServer.Types.OpenGisWktReader.ParseLineStringTaggedText()
   at Microsoft.SqlServer.Types.OpenGisWktReader.ReadLineString()
   at Microsoft.SqlServer.Types.SqlGeometry.STLineFromText(SqlChars lineStringTaggedText, Int32 srid)

The entire list of OGC standard static methods on the Geometry type include: STGeomFromText, STPointFromText, STLineFromText, STPolyFromText, STMPointFromText, STMLineFromText, STMPolyFromText, STGeomCollFromText, STGeomFromWKB, STPointFromWKB, STLineFromWKB, STPolyFromWKB, STMPointFromWKB, STMLineFromWKB, STMPolyFromWKB, and STGeomCollFromWKB. Note: The "FromWKB" methods are the "Well-Known Binary" equivalents to the "FromText" methods, and accept a specially crafted array of bytes as the input. Using binary representations for initialization improves performance, but is much harder for humans to work with and comprehend.

There is one other trick to be aware of when initializing a spatial data type. By contract, a UDT in SQLCLR must support serialization to and from a string. That is, a UDT must implement a ToString() method and a Parse(string) method that are called implicitly when a conversion is required, but these two methods can also be explicitly invoked. It just so happens that the string format used by the Geometry type is WKT (actually, the Parse() method is identical to STGeomFromText() with an implicit SRID of zero).

All of the following are functionally equivalent:

DECLARE @g Geometry

SET @g = Geometry::STGeomFromText('LINESTRING(0 0, 10 10, 21 2)',
                                  0)
SET @g = Geometry::Parse('LINESTRING(0 0, 10 10, 21 2)')
SET @g = 'LINESTRING(0 0, 10 10, 21 2)'

Note: The third example implicitly invokes the Parse(string) method.

Working with a UDT Instance

Once you have created an instance, then you can use a dot notation to access instance properties and methods:

DECLARE @g Geometry
SET @g = Geometry::STLineFromText('LINESTRING(0 0, 10 10, 21 2)',
                                  0)

PRINT @g.STLength() -- Result: 27.7436

Up to this point, my example code has been declaring and instantiating spatial data as variables within a batch. But, columns in a table can also be declared as spatial, and queries can access instance properties and methods:

CREATE TABLE #demo
(
    ID    INT IDENTITY(1,1) NOT NULL,
    G     GEOMETRY
)

INSERT INTO #demo (G)
VALUES    ('LINESTRING(0 0, 10 10, 21 2)'),
          ('LINESTRING(1 1, 11 11, 22 3)'),
          ('POINT(5 5)')

SELECT ID, G.ToString() AS WKT, G.STLength() AS LENGTH
FROM #demo

DROP TABLE #demo

Results:

ID WKT LENGTH
1 LINESTRING (0 0, 10 10, 21 2) 27.7436061324664
2 LINESTRING (1 1, 11 11, 22 3) 27.7436061324664
3 POINT (5 5) 0

Instance methods are the exciting part of using the spatial data types in SQL Server 2008.

You might also like...

Comments

About the author

Jason Follas United States

Jason Follas is the President of the Northwest Ohio .NET User Group (NWNUG) and is a Technical Architect for Perficient...

Interested in writing for us? Find out more.

Contribute

Why not write for us? Or you could submit an event or a user group in your area. Alternatively just tell us what you think!

Our tools

We've got automatic conversion tools to convert C# to VB.NET, VB.NET to C#. Also you can compress javascript and compress css and generate sql connection strings.

“Programming today is a race between software engineers striving to build bigger and better idiot-proof programs, and the Universe trying to produce bigger and better idiots. So far, the Universe is winning.” - Rich Cook