I have a database with a bunch of LineString's and I want to see if a Point is within, say, 50ft of that line.
Since it's a LineString, expecting ST_CONTAINS to return true for a point that's even 1ft away from the LineString seems unlikely BUT if there was a way to add an accepted error margin that'd be cool.
Here's the query I did:
SELECT *
FROM railroads
WHERE ST_CONTAINS(SHAPE, ST_GeomFromText('POINT(-10874300.116373 3537642.0497826)', 3857));
Unsurprisingly, it returns no results.
Any ideas? I'm running MySQL 8.0.22.
You need a condition like
ST_Distance(shape, geo_constant) < distance.
Another pattern to do it is
ST_Intersect(shape, ST_Buffer(geo_constant, distance)).
Buffer is expensive, so make sure you buffer a single constant, not the table column.
But there is another issue here, you are using projection 3857, so the distance is in projection units, not feet or meters. If you care about precision, transform Geometry to Geography type.
Related
I am just starting to work with spatial data in SQL Server (2008 r2). I am looking to calculate the distance between two coordinates (miles).
DECLARE #source geography
DECLARE #target geography
SET #source = geography::STGeomFromText('POINT (43.420026 -83.974472)', 4326);
SET #target = geography::STGeomFromText('POINT (43.458786 -84.029471)', 4326);
SELECT #source.STDistance(#target)/1609.344 -- meters to miles
My query results in a value of 3.827 miles but I checked it against the site linked below and they are returning a distance of 3.85 miles. Am I doing this incorrectly?
http://www.boulter.com/gps/distance/?from=43.420026+-83.974472&to=43.458786+-84.029471&units=m
I don't think you're doing anything wrong. Your SQL query looks reasonable to me. But…
I'm far from an expert on spatial reference systems and geodesic stuff (have you considered asking the expert folks over at GIS SE?); nevertheless, here's three possibilites that come to my mind:
Perhaps they calculate the distance along a straight line instead of the geodesic distance (i.e. the distance along a curved line, the Earth isn't flat after all). SQL Server's geography type should account for that.
This does not seem very plausible, given that "their" distance is greater than "yours": you'd expect straight-line distance to be smaller than geodesic distance.
Perhaps they can do the calculation more accurately than SQL Server (see note on the MSDN reference page for geography.STDistance):
"STDistance() returns the shortest LineString between two geography types. This is a close approximate to the geodesic distance. The deviation of STDistance() on common earth models from the exact geodesic distance is no more than .25%. This avoids confusion over the subtle differences between length and distance in geodesic types."
Or, both calculations are somewhat inaccurate, but in opposite directions. If I'm not mistaken, the two results you're citing differ by something like 0.5%, that could just be the sum of their deviation and SQL Server's.
But I could be completely wrong.
I try searching for this problem on the web, but to no avail. Seems like a basic task. I'm using PostGresql through SQLAlchemy with Geoalchemy to do geospatial queries. Technically, I'm using `Flask-SQLAlchemy. I can do a geospatial query. For example:
from geoalchemy2 import func
cls.db.session.query(cls).filter(func.ST_DWithin(cls.geoLoc, geo, meters))
where cls is the class I'm querying on. geo is the Geography point representing the center of the query and meters is the max distance to return results from. This query works, but returns results in an arbitrary order. I want them returned in increasing (decreasing is fine too) distance from the query point.
I assume I need to use an .order_by(...) at the end of the query, but I can't figure out what to put in there.
Alright, I figured it out, but wow that wasn't obvious, especially if you're not intimately familiar with Geoalchemy2.
In my case above, you have to tack:
.order_by(func.ST_Distance(cls.geoLoc, geo))
to the end of the code that's in my question. Basically, ST_Distance is a function that takes in a two points and returns the distance between them. So cls.geoLoc refers to the each object in question from the table and geo is the center from which I'm sorting by distance. This basically computes the distance for each element to geo and then sorts by it.
I have to discover if a given point is at least 500 meters (ou other distance) from a route line that exist in my database recorded using the MySQL Spatial.
I see there is no similar function in MySQL Spatial, and find a previous answer that doesn't work for me because the line is too big (more than 300km) to check with this solution point by point:
Find N Nearest LineString From A Point Using MySQL Spatial Extensions
I'm even can't create a buffer (a circle/polygon with a given radius) to the point to check if is even touch.
UPDATE - 12/7
I did it, but MySQL Spatial it is seams not trustable.
I made a createBuffer function, to create a 20 points Polygon around the given Point with a meters given distance for the radius: http://pastebin.com/xEFb8ZXi
I'm testing with the QGis the given results from this buffer, and everything is fine with the function (except the meters to decimal degress value that generate smaller then expected, but it is not the issue right now).
And made a few Intersects checks, and this is aways return true, even if the result polygon is not intersects the line.
I remade the same tests using just the center point, and the results is the same.
I discovery now that the INTERSECT doesn't check the LineString with the Point or Polygon, but the Bounding Box of the LineString, when a indicate a point OUTSIDE the Linestring BBox.
Intersects QUERY Where "rota" is the Linestring data:
SELECT Intersects(rota, createBuffer(GeomFromText('POINT(-19.7736 -43.7255)'),500))
FROM log_viagem WHERE rota IS NOT NULL;
How can I trust the MySQL Spatial now?
Or my concept about INTERSECTS is wrong?
SOLVED:
I didn't read the important note at 5.5 version of MySQL:
Note
Currently, MySQL does not implement these functions according to the specification. Those that are implemented return the same result as the corresponding MBR-based functions.
The Solution is taking with the server administrator to update to 5.6.1, there is an upgrade in the note
Note
MySQL originally implemented these functions such that they used
object bounding rectangles and returned the same result as the
corresponding MBR-based functions. As of MySQL 5.6.1, corresponding
versions are available that use precise object shapes. These versions
are named with an ST_ prefix. For example, Contains() uses object
bounding rectangles, whereas ST_Contains() uses object shapes.
As of MySQL 5.6.1, there are also ST_ aliases for existing spatial
functions that were already exact. For example, ST_IsEmpty() is an
alias for IsEmpty()
I have stored some map zones to a table using Geometry type field.
So the inserts are like this:
INSERT INTO zones (zoneName, coords) VALUES ('name',
PolygonFromText('POLYGON((
41.11396418691335 1.2562662363052368,
41.11370552595821 1.2560248374938965,
41.11851079510035 1.2459397315979004,
41.11880984984478 1.2461864948272705,
41.11396418691335 1.2562662363052368))'));
Then I have the user position, and I need to know if he is inside some zone. This works well with this:
SELECT id
FROM zones
WHERE MBRContains(coords,GeomFromText('POINT(41.117783 1.260590)'))
But sometimes, user position is not perfect, so I think its better to know wich zone is closest to user position.
That is the part that I don't have any idea about... I found some queries to get distance between two points, but not a point and polygons.
The MBR series of functions (like MBRContains) are unsuitable for what you're trying to do; they only test bounding rectangle inclusion.
You may wish to jump forward to MySQL 5.6.1, and use the ST_ functions, like ST_Contains. These functions actually test the geometry.
The problem you're working on can be defined as an uncertainty in the position of your POINT when you go to compare it to your collection of boundary POLYGON items.
Try this: create a POLYGON from your point that is a square with the size of your uncertainty. You can think of this square as a "fuzzy" point. (You could also use an octagon or another closer approximation of a circle in place of a rectangle, but your querying speed will slow.)
Then use ST_Within to see if you have a unique polygon that entirely contains your fuzzy point. If you get just one polygon, you're done.
If you get multiple polygons that entirely contain your fuzzy point, that means some of your boundary polygons overlap other ones. You need to figure out what this means in your problem space. If your data is intended to be properly structured cartographic boundary data, it means you have a data mistake. (NOTE: This is not unheard of :-)
If you get no polygons that entirely contain your fuzzy point, then your fuzzy point may or may not overlap the boundary of at least one polygon. Use ST_Overlaps to find those polygons.
If you get just one, you're done -- your fuzzy point is near the boundary of just one polygon.
If you get none, you're done -- your fuzzy point is away from the boundaries of all your polygons.
If you get more than one hit, you have an ambiguity -- your fuzzy point is near the boundary of more than one polygon.
This is the hard case to sort out. You could reduce the size of the fuzzy point and try again. This MIGHT yield just one polygon result. But, you could deceive yourself into thinking that your points are more accurate than they are by doing this.
MySQL doesn't have the geometric operator Area(Intersection(Polygon, FuzzyPoint)). If it did you could choose the polygon with the biggest area of intersection with your fuzzy point, and that would be a good disambiguator. But it would still be as inaccurate as the position of your point.
Maybe your application should handle the category of result "too near the boundary of A, B, and C to be sure."
I have been using the MBRWithin function for quite a lot of times. Suddenly I notice on google map this POINT(101.11857 4.34475) is out of the geo fence which I specify but it still give a value of 1 in mysql any reason or tweaking need to be done?
SELECT MBRWithin(GeomFromText('POINT(101.11857 4.34475)'),GeomFromText('POLYGON((101.12112522125244 4.3531723687957164,101.11846446990967 4.351417913665312,101.13138198852539 4.336397898951581,101.13477230072021 4.33211863778494,101.14065170288086 4.321933898868271,101.14992141723633 4.306699328215635,101.15455627441406 4.30978050198082,101.1397933959961 4.334600612212089,101.12112522125244 4.3531723687957164,101.12112522125244 4.3531723687957164))')) As geoFenceStatus
MySQL 5.6.1 and later have exact geometry algorithms in addition to the earlier functions that only operated on MBR.
You can use ST_WITHIN rather than MBR_WITHIN. See documentation. Like this
SELECT ST_Within(GeomFromText('POINT(101.11857 4.34475)'),
GeomFromText('POLYGON((101.12112522125244 4.3531723687957164,101.11846446990967
4.351417913665312,101.13138198852539 4.336397898951581,101.13477230072021
4.33211863778494,101.14065170288086 4.321933898868271,101.14992141723633
4.306699328215635,101.15455627441406 4.30978050198082,101.1397933959961
4.334600612212089,101.12112522125244 4.3531723687957164,101.12112522125244
4.3531723687957164))')) As geoFenceStatus
MBRWithin() will return results based on the minimum bounding rectangle of it's parameters. Your polygon contains both larger and smaller values for both coordinates than the point, so it will be within the polygon's MBR.
MySQL has no built-in point in polygon algorithm, so you'll either have to roll your own or find one elsewhere. This one seems to be a good candidate.