How to calculate distance of two points along line in TSQL - sql-server

I have added line in the MSSQL database which represent a route in map.As,
update route
set locationArray=geography::STGeomFromText('LINESTRING(7.068419558716859 80.140939950943,7.070868430303301 80.14106869697571,7.072976578922316 80.14136910438538,7.074339417352194 80.14203429222107,7.07631978477513 80.14278531074524,7.078491790888004 80.14435172080994,7.078300143701023 80.14649748802185,7.078449202631107 80.14877200126648,7.076617903866453 80.15068173408508,7.074978246481405 80.15248417854309,7.074105179783337 80.15490889549255,7.0723803358444135 80.15643239021301)', 4326)
where routeID=45;
Now I chose two points in the line and I wand to calculate distance between those two lines along the line (Not the direct distance or displacement) similar to the image .
I have tried Both STLength() function and STDistance() functions. But both of them are providing direct distance. Can someone please help me to calculate the distance along the line with using TSQL?

STLength (geometry Data Type)
Returns the total length of the elements in a geometry instance.
STLength (geography Data Type)
Returns the total length of the elements in a geography instance or
the geography instances within a GeometryCollection.
I calculated the sum of simple lengths of each segment SQRT(X*X + Y*Y) in Excel and compared it to SQL Server. Excel gave the same result as STLength for geometry type. STLength does not calculate distance from start to end point, it calculates lengths of each segment and then sums them up, as expected.
DECLARE #G1 geometry = geometry::STGeomFromText ('LINESTRING(7.068419558716859 80.140939950943,7.070868430303301 80.14106869697571,7.072976578922316 80.14136910438538,7.074339417352194 80.14203429222107,7.07631978477513 80.14278531074524,7.078491790888004 80.14435172080994,7.078300143701023 80.14649748802185,7.078449202631107 80.14877200126648,7.076617903866453 80.15068173408508,7.074978246481405 80.15248417854309,7.074105179783337 80.15490889549255,7.0723803358444135 80.15643239021301)', 4326);
DECLARE #G2 geography = geography::STGeomFromText('LINESTRING(7.068419558716859 80.140939950943,7.070868430303301 80.14106869697571,7.072976578922316 80.14136910438538,7.074339417352194 80.14203429222107,7.07631978477513 80.14278531074524,7.078491790888004 80.14435172080994,7.078300143701023 80.14649748802185,7.078449202631107 80.14877200126648,7.076617903866453 80.15068173408508,7.074978246481405 80.15248417854309,7.074105179783337 80.15490889549255,7.0723803358444135 80.15643239021301)', 4326);
SELECT #G1.STLength() AS LGeometry, #G2.STLength() AS LGeography;
Result
LGeometry LGeography
0.0252888043080671 1809.85271737151
So, STLength works correctly, check your code. Check that you are using correct type (geometry vs geography).

Related

How to calculate distance in "km" using a points geometry column to the nearest linestring geometry column in SQL Server spatial

I am looking to create a field that tells me the nearest neighbour distance in km between 2 geometric columns.
I have a point dataset and multiline dataset both with the geometric fields.
I tried the following:
SELECT
p.GEOM.STDistance(l.GEOM) AS distance
FROM
[points] p, [dbo].[lines] l
after running this query I get the following message
Msg 6522, Level 16, State 1, Line 2
A .NET Framework error occurred during execution of user-defined routine or aggregate "geometry":
System.ArgumentException: 24144: This operation cannot be completed because the instance is not valid. Use MakeValid to convert the instance to a valid instance. Note that MakeValid may cause the points of a geometry instance to shift slightly.
System.ArgumentException:
Could you help me with this one please?
Much appreciated!
Thanks!
The error seems to indicate you have bad spatial data in one of the tables but the query you're using has issues as well. Run this
select ID, geom.STisValid() from lines
and
SELECT ID, geom.STisValid() from points
To see if you have any bad geometry records. If you do run
update table 'with bad spatial datatype'
set geom = geom.MakeValid()
Once you're sure the spatial data is valid rework your query to use a cross join (compare every line to every point (and get the distance) and store the results in a temp table.
Query the temp table for the min(distance) for each point or line. Provided you only need to know the closest feature and not a distance you can do all this with geometry data. If you need to have a measure in feet or meters you will need to convert your data from geometry to geography.
If you have a large number of features in either table the cross join could take forever. Try to work it out on a subset.

Merging SQL Spatial polygons where there is a calculated column

I'm having trouble getting polygons in a SQL Spatial table to merge where there is a calculated column.
The original query works great:
SELECT RollNumber, Geometry, geometry::UnionAggregate(Geometry_SPA.MakeValid()) AS Geometry_SPA
FROM dbo.LegalParcel
GROUP BY RollNumber, Geometry
It returns a set of polygons merged by their tax roll numbers.
However we want to know areas, so we added a computed column:
SELECT RollNumber, Geometry_SPA.STArea() AS SqMetres, Geometry, geometry::UnionAggregate(Geometry_SPA.MakeValid()) AS Geometry_SPA
FROM dbo.LegalParcel
GROUP BY RollNumber, Geometry, Geometry_SPA.STArea()
Since this required adding Geometry_SPA to the Group By, now the polygons which are supposed to merge come back as discrete records.
I attempted to add the aggregation onto the new SqMetres column
columngeometry::UnionAggregate(Geometry_SPA.STArea()) As SqMetres
However that ends with the error ‘Operand type clash: float is incompatible with geometry’
How can I get the necessary records to merge?
Note for anyone wondering why there are two Geometry columns: It is a requirement of our GIS software.
Apparently the issue traced back to some questionable polygons in the source table. After running
update dbo.legalparcel set Geometry_SPA=Geometry_SPA.MakeValid()
where Geometry_SPA.STIsValid() = 0
I'm now getting correct results using the original computed column.

Point in polygon algorithm for SQL Server

I'm trying to write a SQL query which determine if a given point is into a polygon. (I'm using SQL Server 2008 R2).
I was following this tutorial (just copy / paste it and change some table name) and it approximatively works BUT it is not precise at all. For example, let's considerate a given point which coordinates are :
P = 45.7664, 4.87383.
If you draw a little polygon (an approximate square) around this point with 4 vertices coordinates :
S = 45.97215 4.693909, 45.687 4.674683, 45.73302 5.460205, 46.05227 5.366821, 45.97215 4.693909
the procedure given in the link below answers the point is NOT in polygon, whereas it is...
This is the output (with my own formatting text) :
(Polygon 20 is the polygon above)
But if you enlarged the square (10 times bigger in my test), the procedure answers my point is in the square.
So, I'm seeking for another algorithm, more precise.
Here is my VERTICE table, containing all vertices coordinates of each polygon of my DB
I need to check, for ALL polygons (there are a few thousands) if a given point passed in parameter is in a polygon (and if yes, which one(s)). I can do the loop by myself, but I miss a correct Point in polygon algorithm.
Could someone help me ?
Thank you very much.
SUMMARY
The corresponding SQL fiddle : http://sqlfiddle.com/#!3/0caa4/1
Your problem is that you've defined the polygon backwards. Let's explore this with the native SQL geography types
declare #s geography, #t geography, #p geography;
select #s = geography::STPolyFromText('POLYGON((45.97215 4.693909, 45.687 4.674683, 45.73302 5.460205, 46.05227 5.366821, 45.97215 4.693909))', 4326);
select #t = geography::STPolyFromText('POLYGON((46.05227 5.366821, 45.73302 5.460205, 45.687 4.674683, 45.97215 4.693909, 46.05227 5.366821))', 4326);
select #p = geography::STPointFromText('POINT(45.7664 4.87383)', 4326);
select #p.STIntersects(#s), #p.STIntersects(#t);
select #p.STBuffer(10), #s, #t;
As you can see, #s holds pretty much the whole world. In the result set viewer, if you zoom into where your "square" should be, you'll find a hole. Contrast that with #t which is the area that you expect. Also note that I ran this in SQL 2012 which improved on a limitation that existed pre-2012 that says that a geospatial instance can't cross a hemisphere boundary. If you run the above on a 2008 instance, you'll likely get an error to that effect for #s. Comment out the line defining #s and it'll run.
There's a "right-hand" rule when defining geo(graphy/metry) polygons. Imagine that you were in a car visiting the points in the order you've specified. The area that you're defining is what you'd see if you were looking out the right side of the car.

Get intersecting lines in sql spatial

I'm using Entity Framework 5.0 and sql server geometry type.
I'm trying to take a point and find the lines that intersect a buffer of that point and meet certain attribute requirements. Is this possible?
I know of the STIntersect method but those appear to work as a comparison between two features. I have a line dataset and a single point. so i don't know how many results i could get.
You can use the STBuffer geometry method to get an area around a point and store that in a variable (or wherever!). From there, you can use STIntersects to find out if your lines. Like so:
DECLARE #g geometry, #l geometry;
SELECT #g = geometry::STGeomFromText('POINT(0 0)', 0),
#l = geometry::STGeomFromText('LINESTRING(0 1, 4 0)', 0);
SELECT #g, #l, #g.STBuffer(1), #l.STIntersects(#g.STBuffer(1));
Also, my standard caveat of using the right data type (geography vs geometry) applies if you're doing geospatial.

SQL Server Geography Multipoint Insert

I am using SQL Server 2012. I have a table where I am tracking single point instances in a geography column. Storing them as a single point is working fine but I am trying to group some of them together into a new table where they would be a multipoint. I can get it working by inserting lat and long into a multipoint column like this:
DECLARE #g geography;
SET #g = geography::STMPointFromText('MULTIPOINT(-104.952784 39.524092, -104.935269 39.542652)', 4326);
INSERT INTO test(loc) values(#g)
What I want to do is select the values from a table that are already a geography data type. I am not sure if I can do this with a basic query or if I have to build it with a loop? I cannot seem to get it working either way.
Also, after I have that is there a method that will return me the center point of a multipoint column? I have been playing with some of the methods like STStartPoint and STEndpoint but I cannot seem to find a methods that return the center point?
Any help on these questions would be great and highly appreciated.
Thanks!
What you're looking for is the STCentroid() method but, unfortunately, it doesn't work for MultiPoint objects. I raised a Connect issue for this a few years back which has been closed by Microsoft as "Won't Fix", but you're welcome to vote it up anyway: https://connect.microsoft.com/SQLServer/feedback/details/588316/make-geometry-stcentroid-method-work-on-geometries-other-than-polygons
Meanwhile, you'll have to manually sum and then average the X and Y coordinate values individually to get the average "centre" of a Multipoint.
If I'm understanding your first question correctly, you have two points, stored individually as geography instances. If that's the case, you can use the STUnion method to combine them into one geography multipoint instance.

Resources