Closest Point on a Line in SQL Server Spatial 2008 R2 - sql-server-2008-r2

In an ideal world I would be running on SQL Server 2012 and be able to use the .ShortestLineTo() function to find the closest point on a line, to that of another point. I currently am able to find the closest line to my point - but now I need to find the coordinates of the point closest to my point in question.
Unfortunately I am stuck on SQL Server 2008 R2, so don't have the option to use .ShortestLineTo().
How do others achieve this in SQL Server Spatial Geometry types?
Cheers,
Matt

Late answer, but if it still helps (or for anyone else) you should be able to do the following with SQL 2008.
DECLARE #point GEOMETRY = GEOMETRY::STPointFromText('POINT(0 0)', 0);
DECLARE #line GEOMETRY = GEOMETRY::STLineFromText('POINT(10 10, 20 20)', 0);
SELECT STIntersection(#point.STBuffer(#point.STDistance(#line)));
Essentially, you calculate the distance between the two geometries,use that as a buffer on the point which should result in the geometries touching, and take the intersection (point) of that.

Note #Road is the same as geography MULTILINESTRING
SET INITIAL ROAD SEGMENTSET #Road = geography::STMLineFromText('MULTILINESTRING ((-79.907603999999992 32.851905999999985, -79.907708999999983 32.851751, -79.907879999999992 32.851555999999995, -79.907889999999981 32.851542999999992, -79.907995999999983 32.851461, -79.90813399999999 32.851373999999986, -79.90833499999998 32.851286999999992, -79.908529 32.85121, -79.909110999999982 32.850974))', 4269);
GET START/END POINTS OF ROAD SEGMENT FOR NEW MIDPOINT CALCULATION
SET #x1 = CAST(#Road.STStartPoint().Lat AS decimal(11,6))
SET #y1 = CAST(#Road.STStartPoint().Long AS decimal(12,6))
SET #x2 = CAST(#Road.STEndPoint().Lat AS decimal(11,6))
SET #y2 = CAST(#Road.STEndPoint().Long AS decimal(12,6))
ASSIGN ROAD SEGMENT MIDPOINT LAT/LON
SET #MidPointLat = CAST( ((#x1 + #x2) / 2) AS nvarchar(11))
SET #MidPointLon = CAST( ((#y1 + #y2) / 2) AS nvarchar(12))
SET INITIAL OFF ROAD CENTROID
SET #RoadMidPt = geography::STPointFromText('POINT(' + #MidPointLon + ' ' + #MidPointLat + ')', 4269)
CALCULATE BUFFER DISTANCE BACK TO ROAD FOR .STIntersection (add .02 to ensure intersect)
SET #RoadMidPtBuffer = #RoadMidPt.STBuffer(#RoadMidPt.STDistance(#Road) + .02)
Might intersect at multiple points! Use 1st point that intersects road as centroid
SET #RoadCentroid = #RoadMidPtBuffer.STIntersection(#Road).STPointN(1);
FINAL NEW LAT/LON OF CLOSEST ROAD/LINE CENTROID
SELECT #RoadCentroid.Lat, #RoadCentroid.Long

Related

Why is the distance between a point and a polygon 0 here?

I am using PostGIS, here is the SQL that I am using -
SELECT ST_Distance(
'SRID=4326;POINT(0 55)'::geography,
'SRID=4326;POLYGON((-180 45,-10 45,-10 -45,-180 -45,-180 45))'::geography
);
Visualizing these objects on a map suggests to me that the distance should be non-zero.

ST_MinimumBoundingCircle ellpse instead of circle

I use postgis and I want to calculate minimum bounding circle for my geometry.
My points are:
I use ST_MinimumBoundingCircle function, but it shows the ellipse instead of a circle (see ).
The following sample is to reproduce:
select ST_Centroid(collection), ST_MinimumBoundingCircle(collection) from (
select ST_COLLECT(point) as collection from (
select ST_SetSRID(ST_MakePoint(20.513371, 54.720205),4326) as point
UNION ALL
select ST_SetSRID(ST_MakePoint(20.493725, 54.717761),4326) as point
UNION ALL
select ST_SetSRID(ST_MakePoint(20.495189, 54.726808),4326) as point
UNION ALL
select ST_SetSRID(ST_MakePoint(20.501414, 54.716445),4326) as point
UNION ALL
select ST_SetSRID(ST_MakePoint(20.509221, 54.719836),4326) as point
) a
)b
I could not understand what I did wrong.
You're not doing anything wrong. You might see the buffer as an ellipse because the points you used to create it are pretty far from the equator (Kaliningrad). Keep in mind that you're projecting an ellipsoid into a 2D flat structure, therefore such distortions are just normal.
WITH j (geom) AS (
VALUES
('SRID=4326;POINT(20.513371 54.720205)'),
('SRID=4326;POINT(20.493725 54.717761)'),
('SRID=4326;POINT(20.495189 54.726808)'),
('SRID=4326;POINT(20.501414 54.716445)'),
('SRID=4326;POINT(20.509221 54.719836)')
)
SELECT ST_MinimumBoundingCircle(ST_Collect(geom::geometry))
FROM j;
But if you draw a similar buffer closer to the equator the distortion won't be so visible. See example bellow (north of Brazil):
WITH j (geom) AS (
VALUES
('SRID=4326;POINT(-56.30 1.55)'),
('SRID=4326;POINT(-56.63 1.14)'),
('SRID=4326;POINT(-55.95 0.70)'),
('SRID=4326;POINT(-55.57 1.38)')
)
SELECT ST_MinimumBoundingCircle(ST_Collect(geom::geometry))
FROM j;
Further reading: Buffers (Circle) in PostGIS
In order to avoid the distortion, transform to a coordinate system that represents lenghts more correctly before creating the buffer.

How to get coords between 2 points

I cannot find exactly what I'm looking for or reading google documentation I missed it, I just need a function or whatever to submit 2 point, start and end, and get X waypoint in between.
Is there some api like "www.somesite.com/api.php?start=43.12,12.23&end=44.12,12.23&number_of_waypoints=5" that return some json?
thank you!
First of all, this will require working with geodesics, which are the shortest lines passing through two points around the Earth, assuming the Earth is an ellipsoid. WGS84 is the standard coordinate system you will see most widely used for "latitude + longitude" coordinates, and this assumes the Earth is an ellipsoid.
To solve this problem, you first need to find the azimuth (bearing angle from north) and distance between two coordinates. The way to calculate this is by solving the inverse geodesic problem.
Once we have this distance (let's say in metres), we can divide it by n, where n - 1 is the number of waypoints we want in between the line. This gives us the distance d metres between each waypoint.
Now, we need to plot points at intervals of d metres along this line. To do this, we can solve the direct geodesic problem. This gives us a new set of coordinates after moving a given distance from a given point with a given azimuth. We can do this repeatedly to get new points moving d metres from the previous point each time. One thing to note with this is that the resultant azimuth to the end of the line from different points within the line will vary, so the destination azimuth must be obtained after each stage and used for calculating the next point.
Solving the direct and inverse geodesic problems requires mathematical formulas, of which multiple are available. However, for your PHP application, you are probably best not trying to implement these yourself, but instead use a library which can do this for you. One popular library for PHP which does this is called phpgeo.
Here's an example of how this might be implemented with phpgeo:
<?php
use Location\Coordinate;
use Location\Distance\Vincenty;
use Location\Bearing\BearingEllipsoidal;
$numPoints = 5;
$coordsA = new Coordinate(50.0, 0.0);
$coordsB = new Coordinate(51.0, 1.0);
$bearingCalculator = new BearingEllipsoidal();
$distanceCalculator = new Vincenty();
// Inverse geodesic problem
// Calculate total length of line between coords
$totalDistance = $distanceCalculator->getDistance($coordsA, $coordsB);
$intervalDistance = $totalDistance / ($numPoints + 1);
// Inverse geodesic problem
// Calculate angle to destination
$currentBearing = $bearingCalculator->calculateBearing($coordsA, $coordsB);
$currentCoords = $coordsA;
$points = [];
for ($i = 0; $i < $numPoints; $i++) {
// Direct geodesic problem
// Calculate new point along line
$currentCoords =
$bearingCalculator->calculateDestination($currentCoords,
$currentBearing,
$intervalDistance);
// Add these new coordinates to the list
array_push($points, $currentCoords);
// Inverse geodesic problem
// Recalculate angle to destination
$currentBearing =
$bearingCalculator->calculateBearing($currentCoords,
$coordsB);
}
// Print out the list of points
foreach ($points as $point) {
echo "{$point->getLat()}, {$point->getLng()}\n";
}

How to snap line string start & end vertex to the nearest point?

I have a point layer and a line layer. The points are road junctions, collected using DGPS, while the lines are the connecting roads, collected through Handheld GPS in tracking mode. As a result, the lines are not actually connected to the points.
Now I want to:
Remove all intermediate nodes from each line string, to make it a straight line.
Snap the start & end vertex of lines to the nearest points.
I am using PostGIS 2.0.This by far what I have done:
UPDATE line
SET geom = ST_Simplify(geom, 1000);
Q. Is there any other better way to accomplish it?(Since I am using an absurd tolerance)
UPDATE line
SET geom = ST_AddPoint(
(SELECT geom FROM line WHERE id = 1),
(SELECT p.geom FROM point AS p, line AS l
ORDER BY ST_Distance(p.geom,(SELECT ST_StartPoint(l.geom) FROM lt WHERE l.id=1)) LIMIT 1),
0)
WHERE id=1;
This will extend the line(with id=1) to the nearest point(point added at the beginning of the line).
Q. The above looks a bit complicated, is there any other efficient method/function available?
It seems to be reasonable to do both operations on the same query (not tested):
UPDATE line l
SET geom = ST_MakeLine(
(SELECT geom FROM point p ORDER BY ST_Distance(p.geom, ST_StartPoint(l.geom)) LIMIT 1),
(SELECT geom FROM point p ORDER BY ST_Distance(p.geom, ST_EndPoint(l.geom)) LIMIT 1)
);
If data set is small and you are running query only once, performance isn't issue - but you could add additional bbox comparision to speed things up:
SELECT geom FROM point p WHERE p.geom && ST_Expand(ST_StartPoint(l.geom), 100) ORDER BY ST_Distance(p.geom, ST_StartPoint(l.geom)) LIMIT 1

Two closest points on boundary of Postgis geometry

I have a table geofences which stores geometry of polygon.
I also have a point A which is inside the geometry. What I have to do is find the two closest points from point A that lie on the surface of the polygon geometry.
Function in PostGIS:
CREATE OR REPLACE FUNCTION accuracyCheck(Polygon geometry
,decimal lat
,decimal lon)
RETURNS VARCHAR AS
$BODY$
DECLARE height DECIMAL;
DECLARE accuracy VARCHAR(250);
BEGIN
CREATE TEMPORARY TABLE closePointStorage AS
SELECT ST_AsText(ST_ClosestPoint(geometry
,ST_GeomFromText('POINT(lat lon)',0)
)
) AS closestPoint
FROM (
SELECT ST_GeomFromText(geometry) as geometry
FROM gfe_geofences
WHERE is_active=true
) As tempName;
CREATE TEMPORARY TABLE areaStorage ON COMMIT DROP AS
SELECT ST_Area(ST_GeomFromText('Polygon((23.0808622876029 96.1304006624291
,28.0808622876029 99.1304006624291
,100 200
,23.0808622876029 96.1304006624291
))'
,0)
) AS area;
CREATE TEMPORARY TABLE distanceStorage ON COMMIT DROP AS
SELECT ST_Distance(
ST_GeomFromText('POINT(23.0808622876029 96.1304006624291)',-1)
,ST_GeomFromText('POINT(28.0808622876029 99.1304006624291)',-1)
) AS distance;
height = (SELECT area FROM areaStorage)
/(0.5*(SELECT distance FROM distanceStorage));
IF height < (SELECT radius_meters
FROM gfe_geofences Where is_active=true) THEN
accuracy = "FullConfirm";
RETURN accuracy;
ELSE
accuracy = "PartiallyConfirm";
RETURN accuracy;
END IF;
END;
$BODY$ LANGUAGE plpgsql;
I just want to find two points on boundary of polygon geometry. Just like I have found one from the query:
CREATE TEMPORARY TABLE closePointStorage AS
SELECT ST_AsText(ST_ClosestPoint(geometry
,ST_GeomFromText('POINT(lat lon)',0)
)
) AS closestPoint
FROM (
SELECT ST_GeomFromText(geometry) as geometry
FROM gfe_geofences
WHERE is_active=true
)
AS tempName;
Other then this point I have to find one more with distance greater then the point find above but smaller then the rest of points.
Use ST_DumpPoints() to dump the points of the polygon, then select from that order by ST_Distance to A limit 2. ?
So it is something like
SELECT * from ST_DumpPoints(poly) order by ST_Distance(A,geom) asc limit 2;
(assumes that this is an inner select where poly is the polygon, A is the point to compare to and geom is the geom column of one of the points in the poly being compared)
There generally is no second closest point on the boundary polygon, if you include the lines. Just like there is no real number second closest to zero.
Either you only wish to consider the points at the corners, like Markus suggests.
Or you have only one closest point.
1) Kind of a left-field idea, but to find the second-closest point to your destination, why not find the closest point to the point you already found?
2) Or, more germaine to your specific question,
find the set of points within some reasonable range of the point,
find the intersection of that set with the set of points lying on the polygon border (which I am guessing may be another PostGIS function; haven't used postG in a while so I'm not sure)
3) Farther into left field, dump some of your dataset into Mongo and use the $near function... http://docs.mongodb.org/manual/reference/operator/near/
I am assuming you want to find the edge of the polygon that passes the closest to the point in question
To obtain the distance 'd' of point 'C' from line [A,B]
First translate all points so A is at 0,0
B -= A //vector subtraction
C -= A
Then normalize B so it is of length 1.0
len = sqrt( B . B) //dotproduct of two vectors is the length squared
B /= len //scalar divide by length
Find length from A that is at right angles to C
dotp = B . C //dot product again
closestPointOnLine = B * dotp //scalar multiply
Now get the distance
diff = (C - ClosestPointOnLine)
d = sqrt(diff . diff)
Not sure how to do that in SQL. You will need to do the above for each edge on your polygon, and then find the smallest value 'd'
By the way the sign of the cross-product of B and C will now tell you whether the point is on the inside of the polygon or not