Common Geography Database Tasks
Information on a variety of tasks including getting table information for geometry columns, defining a geography column in a SQL table, creating a location, line, or polygon in portable SQL. This page also looks at the conversion of geography objects to well-known formats, comparing geographic objects using SQL, and returning database values into a geography object.
Alpha Anywhere SQL::TableInfo object describes a SQL table, its columns and indexes in a database independent way. Column definitions in the SQL::TableInfo objects can be populated:
- Directly in Xbasic using "intermediate" types - a rich set of types independent of any specific database
- From schema information for a SQL table using the SQL::Connection object's GetTableInfo function
- From a DBF table definition by calling the SQL::Connection object's GetTableInfoFromDBF function
dim TI as SQL::TableInfo Connection.GetTableInfo(TI, "MyTable") Connection.GetTableInfoFromDBF(TI, "MyTable")
There are several functions that can be used to create a table from a SQL::TableInfo object instance. The simplest is to use the connection object and call its CreateTable function as shown below.
dim TI as SQL::TableInfo Connection.CreateTable(TI)
The intermediate type for geography objects is predictably "geography". To set the value of a table info column you would write the following script:
dim Col as SQL::DataTypeInfo Col.Name = "Location" Col.IntermediateType = SQL::IntermediateType::Geography TI.AddColumn(Col)
Here is a more complete example of creating a table with a geometry column using Xbasic:
dim TI as SQL::TableInfo dim Col as SQL::DataTypeInfo dim PK as SQL::IndexInfo dim IXC as SQL::IndexcolumnInfo TI.Name = "GeogTest" Col = new SQL::DataTypeInfo() Col.Name = "KeyValue" Col.AlphaType = "C" Col.Length = 25 Col.Nullable = .f. TI.AddColumn(Col) Col = new SQL::DataTypeInfo() Col.Name = "Location" Col.IntermediateType = SQL::IntermediateType::Geography Col.Nullable = .f. TI.AddColumn(Col) PK.Name = "PK" PK.PrimaryKey = .t. PK.Unique = .t. IXC.Name = "KeyValue" PK.AddColumn(IXC) TI.AddIndex(PK) CreateTestTable = Connection.CreateTable(TI)
Location, Line and Polygon objects can be created using the portable SQL functions GeogCreateLocation, GeogCreateLine and GeogCreatePolygon respectively.
Each of the portable function can be used to construct a native geography object in SQL. The functions are expanded at run time into the native SQL for the target database (see the database specific notes for details). The object can then be inserted into a table column or passed to another function, such as a comparison or conversion function.
Here are the functions again, with some examples:
GeogCreateLocation as Geography ( Longitude as N, Latitude as N [,SpatialReferenceID as N])
Constructs a geographic location from a longitude/latitude pair. For example:
select first 1 GeogAsText(GeogCreateLocation(1,2, :SRID)) from GeogTest g select first 1 GeogAsText(GeogCreateLocation(1,2)) from GeogTest g select first 1 GeogDistanceBetween(GeogCreateLocation(1, 42, :SRID), GeogCreateLocation(10, 20, :SRID)) from GeogTest g
GeogCreateLine as Geography ( Longitude as N, Latitude as N [...] [,SpatialReferenceID as N])
Constructs a geographic line from two or more longitude/latitude pairs.
select first 1 GeogAsText(GeogCreateLine(1,2,3,4,5,6, :SRID)) from GeogTest g select first 1 GeogAsText(GeogCreateLine(1,2,3,4,5,6)) from GeogTest g
GeogCreatePolygon as Geography ( Longitude as N, Latitude as N [...] [,SpatialReferenceID as N])
Constructs a geographic polygon from three or more longitude/latitude pairs. For example:
select first 1 GeogAsText(GeogCreatePolygon(-70, 42, -70, 32, -60, 32, -60, 42, -70, 42, :SRID)) from GeogTest g select first 1 GeogAsText(GeogCreatePolygon(-70, 42, -70, 32, -60, 32, -60, 42, -70, 42)) from GeogTest gThe first and last point of the polygon must be the same, and the points must be arranged in counter-clockwise order.
The properties of geography objects are stored in a single database column. To retrieve these values, use the following functions:
The type function will return LOCATION, LINE, or POLYGON for any of these geographic objects. Although not all objects are directly supported, you may also see MULTILOCATION, MULTILINE and MULTIPOLYGON as well as other values returned. Only location, line and polygon are guaranteed to be correctly mapped for all databases. For example:
select GeogLongitude(g.Location), GeogLatitude(g.Location) from GeogTest g where GeogType(g.Location) = 'LOCATION'
The default on most databases is 4326 (1003 on DB2). For example:
select first 1 GeogSRID(GeogCreateLocation(50, 44, :SRID)) from GeogTest gUnless you have a specific reason to use a different SRID than the default, we recommend this one. Using the default will get you more consistent results between database vendors.
Returns the longitude value as mapped to X or Y (depending on the database implementation). For example:Some databases use the point value of X for longitude and some use Y. This function handles the inconsistencies.
Returns the latitude value as mapped to X or Y (depending on the database implementation). For example:Some databases use the point value of X for longitude and some use Y. This function handles the inconsistencies.
The Open Geospatial Consortium (OCG) standard defines several formats for serializing geography and geometry objects. There are portable SQL functions for two that are consistently supported by databases:
- Well-known-text (WKT) - for example 'POLYGON((100 200, 110 300, 120 500))'. This is a text-only format that defines each of the objects in a way that is very readable and easy to construct.
- Well-known-binary (WKB) - A binary equivalent of well-known-text. This format compresses the types and coordinates into standard binary format.
While you can certainly create your own well-known-text and well-known-binary strings, you will probably want to use these formats to retrieve and store values.
Here are the functions to convert objects to well-known-text and well-known-binary and back again, with some examples:
GeogAsBinary as C(Object as Geography)
Return the object description in the Well Known Binary (WKB) format.
select GeogAsBinary(g.Location) from GeogTest g
GeogAsText as C(Object as Geography)
Return the object description in the Well Known Text (WKT) format.
select GeogAsText(g.Location) from GeogTest g
GeogCreateFromBinary as Geography(Binary as B [, SpatialReferenceID as N])
Create a geography object from Well Known Binary (WKB) format. Here is an example of an Xbasic script passing a Blob representation of a point object to a SQL statement as an argument:
dim MyBlob as B = base64decode("AQEAAAAAAAAAAAAkwAAAAAAAAChA") args.add("WKB", MyBlob) Connection.Execute("select GeogAsText(GeogCreateFromBinary(:WKB))" + \ " from GeogTest g")
GeogCreateFromText as Geography(Text as C [, SpatialReferenceID as N])
Create a geography object from Well Known Text (WKT) format. For example:
select first 1 GeogAsText(GeogCreateFromText('POINT(-10 27)')) from GeogTest g
GeogDistanceBetween as N ( Object as Geography, Object as Geography)
Returns the distance between two objects in the default unit (generally meters).
GeogLocationIntersectsLine as L (Location as Geography, Line as Geography, Tolerance as N)
Returns true if the location intersects the line or is within the tolerance distance from it. For example:
select if(GeogLocationIntersectsLine( g.Location, GeogCreateLine(-60, 42, -70, 42, :SRID), 3000), 'It intersects', 'It does not intersect') from GeogTest g
GeogLocationIsWithinPolygon as L ( Location as Geography, Polygon as Geography, Tolerance as N)
Returns true if the location is contained within the polygon or within the tolerance distance from it. For example:
select if(GeogLocationIsWithinPolygon( g.Location, GeogCreatePolygon(-70, 44, -70, 34, -60, 34, -70, 44, :SRID), 5), 'It is within the polygon', 'It is not within the polygon') from GeogTest g
GeogLocationIsWithinRadius as L ( Location as Geography, Point as Geography, Radius as N, Tolerance as N)
Returns true if the location is within the radius defined and within the tolerance defined.
select if(GeogLocationIsWithinRadius( g.Location, GeogCreateLocation(-70, 41.99999, :SRID), 50), 'It is within the radius', 'It is not within the radius') from GeogTest gEach database handles tolerance differently (as it affects indexing performance).
Microsoft has made some of the user defined types used in SQL server available as a redistributable .Net assembly. Beginning with Alpha Five Version 11, this assembly is installed with Alpha Anywhere and the geography type is available. As a result, SQL result sets are now "geography aware". This means that they will attempt to return the .Net object of the type Microsoft::SQLServer::Types::SQLGeography wrapping database data whenever possible.
There are two functions that will try to do this:
When you call SQL::ResultSet::DataAsGeography, the column value will be retrieved and converted (if possible) to a Microsoft::SQLServer::Types::SQLGeography object. The input value can be well-known-text (WKT), well-known-binary (WKB), or (for some databases) the result of simply selecting a geography column. Selecting the column directly works in SQL Server, PostgreSQL, and MySQL.
In order to be portable, you will probably want to select the column using GeogAsText or GeogAsBinary.
Here is a simple example using Xbasic:
dim Result as C dim cn as sql::Connection dim Geography as A ' Note that we've dimmed this as A to show the true type. if cn.open(ConnectionString) Result = Result + "Connection opened successfully." + crlf() cn.portableSQLEnabled = .t. if cn.execute("select GeogAsText(g.Location) from GeogTest g") Result = Result + "Query executed successfully." + crlf() ResultSet = cn.ResultSet Geography = ResultSet.DataAsGeography(1, 4326) Result = Result + "Geography object is of type: " + typeof(Geography) + crlf() Result = Result + "Geography as XML: " + Geography.AsGML().Value + crlf() Result = Result + "Geography object as Text: " + Geography.ToString() + crlf() Result = Result + "Geography Longitude: " + Geography.Long.Value + crlf() Result = Result + "Geography Latitude: " + Geography.Lat.Value + crlf(2) else Result = Result + "Error executing query: " + CurrentQuery + crlf() \ + "Result is: " + cn.callresult.text + crlf(2) end if else Result = Result + "Error opening connection:: " \ + ConnectionString + crlf() + "Result is: " + cn.callresult.text + crlf(2) end if showvar(Result)
When you call SQL::ResultSet::Data, the column value will be retrieved and converted to a Microsoft::SQLServer::Types::SQLGeography object if and only if the database returns a geography type and the driver can tell that it has. When calling the Data() function, there is no guarantee that data returned will be an object. You must check the return type before calling any functions or accessing any properties on the object.
- Currently SQL Server is the only database that will return a recognizable geography object. It will also return a SQLGeometry object and a HierarchyId object. In the future, we hope to be able to return any SQL Server user defined type implemented in .NET for which there is a registered assembly on the client machine.
- MySQL returns a geometry which is converted to a SQLGeometry object (from the same SQL Server assembly). Note that you could convert one object to the other using WKT or WKB formats, but you can guarantee that you get the right object by using the DataAsGeography function instead.
- Geographic Databases
- Changes to SQL Objects for Geometry
- Portable SQL Functions for Geographies
- Common Geography Database Tasks
- Database-specific Geography Tasks
- DB2-specific Geography Tasks
- MySQL-specific Geography Tasks
- Oracle-specific Geography Tasks
- PostgreSQL-specific Geography Tasks
- SQL Server-specific Geography Tasks
- SQL Geography Examples