The WKB Format is binary representation of the geometry you are creating. Some bytes are used to identify what type of geometry it is and then others used to define the points for the geometry. Where a geometry consists of multiple geometries there are additional bytes used to define how many geometries there are.
There is also a byte used to define how the binary values are represented (i.e. whether 1 is 0x01000000 or 0x00000001 – big-endian or little-endian).
This is the structure of each WKB. You will see they all share the same common mechanism, [byte order][shape type]{[x][y]...}
- Point = [Byte order][Type][X value][Y value]
- MultiPoint = [Byte order][Type][PointCount]<Point1><Point2>
- Line = [Byte order][Type][PointCount][X Value][YValue][X value][Y value].....
- MultiLine = [Byte order][Type][LineCount]<Line1><Line2> .....
- Polygon = [Byte order][Type][Polygon Count][PointCount][X Value][YValue][X value][Y value].....
- MultiPolygon = [Byte order][Type][Polygon Count]<Polygon1><Polygon2>....
- Geometry Collection = [Byte order][Type][Shape Count]<Shape1><Shape2>.....
The types are as follows:
- Unknown = 0
- Point = 1
- LineString = 2
- Polygon = 3
- MultiPoint = 4
- MultiLineString = 5
- MultiPolygon = 6
- GeometryCollection = 7
The following example shows how a WKB is broken up. The letters above the binary value are explained below.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 |
declare @p geometry = geometry::STGeomFromWKB AABBBBBBBBCCCCCCCCDDDDDDDD (0x00000000030000000200000005 X1X1X1X1X1X1X1X1Y1Y1Y1Y1Y1Y1Y1Y1X2X2X2X2X2X2X2X2Y2Y2Y2Y2Y2Y2Y2Y2 0000000000000000000000000000000000000000000000004010000000000000 X3X3X3X3X3X3X3X3Y3Y3Y3Y3Y3Y3Y3Y3X4X4X4X4X4X4X4X4Y4Y4Y4Y4Y4Y4Y4Y4 4010000000000000401000000000000040100000000000000000000000000000 X5X5X5X5X5X5X5X5Y5Y5Y5Y5Y5Y5Y5Y5 00000000000000000000000000000000 DDDDDDDD 00000005 X1.... 3FF00000000000003FF00000000000003FF00000000000004008000000000000 4008000000000000400800000000000040080000000000003FF0000000000000 3FF00000000000003FF0000000000000,0) print @p.ToString() |
- A is the byte order
- B is the Shape type (in this case 3 - a polygon)
- C is the number of sub shapes (in this case 2 - the outer and the inner squares)
- D is the number of points in the shape (in this case 5 - for a polygon the last point has to be the same as the first)
- X1 and Y1 are the X and Y for the first point, X2 and Y2 the second etc.
- Once 5 points have been defined you will note that DDDD is repeated indicating the number of points in the second polygon.
Appendix B: ?Understandi?ng the Well Known Text (WKT) Format
The WKT format is much easier to read but much more difficult to create as you have to create strings, and worry about having commas and brackets in the right place.
The following are the points to note about the creating geometries in WKT:
- The prefix for a geometry is one of the following, POINT, MULTIPOINT, LINESTRING, MULTILINESTRING, POLYGON, MULTIPOLYGON, GEOMETRYCOLLECTION
- The prefix is NOT case sensitive
- A point (x and y) is represented by two numbers separated by whitespace
- The X and Y value represent the longitude and latitude for geography instances
- Z and M can be specified after the X and Y values.
- Z and M can be NULL, but X and Y cannot
- Points are separated by commas and a set of points are wrapped in parentheses
- When a geometry consists of multi-point sets, the point sets are wrapped in parentheses and separated by commas. This is the case for the Polygon,Multi... and GeometryCollection types
- Using the Geometry::Parse method and assigning a string to a Geometry variable both use the parse method, which results in an SRID of 0 (for geography types the default SRID is 4326)
Examples
1
2
3
4
5 |
declare @point geometry = 'POINT(100 10)' declare @line geometry = 'LINESTRING(0 0,10 10)' declare @square geometry = 'POLYGON((0 0,10 0,10 10,0 10,0 0))' declare @donught geometry = 'POLYGON((0 0,10 0,10 10,0 10,0 0),(4 4,4 6,6 6,6 4,4 4))' |
![]() |
---|
A polygon can have a number of sets of points. The first set defines the outer ring of the polygon. Subsequent shapes remove from or add to the inside of the polygon. This subsequent shapes must reside inside the first polygon and cannot overlap. |
In the donut shape above a 2x2 square is removed from the inside of a 10x10 square.
Examples of More Complex Shapes
1
2
3
4
5 |
declare @vs geometry = 'MULTILINESTRING((0 10,5 0,10 10),(20 10,25 0,30 10))' declare @rockets geometry = 'MULTIPOLYGON(((0 0,5 10,5 30,15 40,25 30,25 10,30 0,20 0,15 5, 5 0,0 0)),((10 0,15 10,15 30,25 40,35 30,35 10,40 0,40 0,25 5, 15 0,10 0)))' declare @glasses geometry = 'GEOMETRYCOLLECTION(LINESTRING(0 20,5 10),POLYGON((5 5,5 15,10 20,20 20,25 15,25 5,20 0,10 0,5 5)),POLYGON((35 5,35 15,40 20,50 20,55 15,55 5,50 0,40 0,35 5)),LINESTRING(55 10,60 20),LINESTRING(25 15,35 15))' |
Additionally, you can use this code to convert an Ink Stroke to a geometry:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 |
private SqlGeometry ConvertStrokeToGeometry(Stroke strokeAdded) { //Text representation of a line is LINESTRING (x1 y1,x2 y2 x3 y3....) //for a point is POINT(x y) //create an string array to store the points string[] pnts = new string[strokeAdded.StylusPoints.Count]; for (int i = 0; i < strokeAdded.StylusPoints.Count; i++) { StylusPoint p = strokeAdded.StylusPoints[i]; pnts[i] = string.Format("{0} {1}", p.X, p.Y); } string geomFmt; if (pnts.Length == 1) geomFmt = "POINT({0})"; else geomFmt = "LINESTRING({0})"; string geomText = String.Format(geomFmt, String.Join(",", pnts)); SqlGeometry strkGeom = SqlGeometry.Parse(geomText); return strkGeom; } |