MySQL Check Point Within a polygon stored within a table - mysql

A little out of my depth with this one.
I've ran the query:
SELECT ST_Contains(
PolygonFromText('
POLYGON((
60.930551 -0.775212,
60.332866 -0.417906,
59.935582 -0.887788,
59.837354 -1.156991,
58.919848 -2.577975,
58.378359 -2.935748,
57.934035 -3.75355,
57.761068 -3.563538,
57.859628 -1.643417,
57.538993 -1.423404,
56.382748 -2.522191,
55.846937 -1.945553,
55.618743 -2.262236,
55.463878 -2.131039,
55.42282 -2.162707,
54.968355 -3.03811,
54.960564 -3.232644,
54.929382 -3.449798,
54.540197 -4.141976,
54.467094 -5.149036,
55.657203 -6.713395,
56.947221 -7.700598,
57.681709 -7.656722,
58.298006 -7.239903,
60.930551 -0.775212))'),
PointFromText("POINT(57.05 -4.49)"));
Which returns a 1 to confirm that 57.05, -4.49 is indeed within the polygon.
I want to store the polygon data in my database and did so with:
INSERT INTO polygons (`ownerID`, `polygon`)
VALUES ("test", PolygonFromText('POLYGON((60.930551 -0.775212, 60.332866 -0.417906, 59.935582 -0.887788, 59.837354 -1.156991, 58.919848 -2.577975, 58.378359 -2.935748, 57.934035 -3.75355, 57.761068 -3.563538, 57.859628 -1.643417, 57.538993 -1.423404, 56.382748 -2.522191, 55.846937 -1.945553, 55.618743 -2.262236, 55.463878 -2.131039, 55.42282 -2.162707, 54.968355 -3.03811, 54.960564 -3.232644, 54.929382 -3.449798, 54.540197 -4.141976, 54.467094 -5.149036, 55.657203 -6.713395, 56.947221 -7.700598, 57.681709 -7.656722, 58.298006 -7.239903, 60.930551 -0.775212))')");
But I am now struggling to run the initial ST_Contains query whilst also returning the polygon from the table.
SELECT #Test := polygon FROM polygons where ownerID = 'test';
SELECT ST_CONTAINS(ST_GEOMFROMTEXT(#Test), PointFromText("POINT(57.05, -4.499)"));
Returns a null response and I'm sure there is a simple way to do this but unfortunately it evades my basic skills and googling has returned lots of examples where the polygon is defined in the query, but not where it is stored in a table.
How can I check to see if a point is within a polygon that is stored in the table polygons?
Thanks

When storing the value to your variable #Test you forgot to select the geom value as text and your POINT has a comma in it.
SELECT #Test := ST_AsText(`polygon`) FROM `polygons` WHERE ownerID = 'test';
SELECT ST_CONTAINS(ST_GEOMFROMTEXT(#Test), ST_PointFromText("POINT(57.05 -4.499)"));
Or without converting to WKT -
SELECT #Test := `polygon` FROM `polygons` WHERE ownerID = 'test';
SELECT ST_CONTAINS(#Test, ST_PointFromText("POINT(57.05 -4.499)"));
Or just -
SELECT *,
ST_AsText(`polygon`),
ST_CONTAINS(`polygon`, ST_GeomFromText("POINT(57.05 -4.499)"))
FROM `polygons`
WHERE `owner_id` = 'test';

Related

Mysql find Point in polygon.

i want to Select ID from table Locations where a point ( 25.10785 55.18114 ) exists in a polygon that.
I tried everything possible , while it returns null for everything.
Polygon((25.11031 55.18408,25.11109 55.18459,25.11288 55.18811,25.11653 55.19301,25.12197 55.20176, 25.1034 55.23103, 25.09471 55.24247, 25.0826 55.24944, 25.05652 55.24897, 25.05526 55.23672, 25.05306 55.22412, 25.06665 55.22071, 25.07212 55.21617, 25.07821 55.20957, 25.09016 55.19002, 25.09583 55.18403, 25.1046 55.17734, 25.10785 55.18114,25.11031 55.18408))
this is how the polygon looks like.
I want mysql to return ID if a point exists in the polygon
Have a look at the mysql functions Containt or ST_Contains. ST_Contains(g1, g2) returns 1 or 0 to indicate whether g1 completely contains g2.
In your example you could do something like:
SELECT id FROM your_table WHERE CONTAINS(your_polygon, PointFromText(POINT(25.10785 55.18114))
Make sure to check what mysql version you are using.

Mysql function MBRContains is not accurate

I have the following POLYGON (in the image you can see the area it covers)
POLYGON((-74.05100448502202 4.7239278424321,-74.05092938316898 4.7241416902206,-74.04830618275201 4.7237460717602,-74.04643668306903 4.7234306460692,-74.04635688735101 4.7234105978214,-74.04636526925401 4.7233310730989,-74.046191260944 4.72327293317,-74.04579027069599 4.7232007594583,-74.04141290558402 4.7214258184083,-74.03746201170497 4.7197791822891,-74.03565688503801 4.7189879401666,-74.033484295736 4.7180897723398,-74.03098447693401 4.7170526009038,-74.028731840457 4.7161167561787,-74.02852820211899 4.7150714370973,-74.026398371001 4.6877232674918,-74.02558060109601 4.6874859863574,-74.02454587610401 4.686797564651,-74.024665108676 4.6863189291555,-74.025470986757 4.6857975214267,-74.02585246812498 4.6846813784365,-74.02580479605103 4.6834369175226,-74.01962984798399 4.684922743491,-74.028472839649 4.6765444849623,-74.032273278366 4.6775012677607,-74.03825980124901 4.6799297676049,-74.048215993474 4.6850422042295,-74.05718496514402 4.6867981911917,-74.05100448502202 4.7239278424321))
When I execute MBRIntersect, MBRContains and Within functions they return that the green marker is inside of the polygon, but it is not (as you can see in the image). I'm executing the next sentence to get that:
SET #g1 = ST_GeomFromText('POLYGON((-74.05100448502202 4.7239278424321,-74.05092938316898 4.7241416902206,-74.04830618275201 4.7237460717602,-74.04643668306903 4.7234306460692,-74.04635688735101 4.7234105978214,-74.04636526925401 4.7233310730989,-74.046191260944 4.72327293317,-74.04579027069599 4.7232007594583,-74.04141290558402 4.7214258184083,-74.03746201170497 4.7197791822891,-74.03565688503801 4.7189879401666,-74.033484295736 4.7180897723398,-74.03098447693401 4.7170526009038,-74.028731840457 4.7161167561787,-74.02852820211899 4.7150714370973,-74.026398371001 4.6877232674918,-74.02558060109601 4.6874859863574,-74.02454587610401 4.686797564651,-74.024665108676 4.6863189291555,-74.025470986757 4.6857975214267,-74.02585246812498 4.6846813784365,-74.02580479605103 4.6834369175226,-74.01962984798399 4.684922743491,-74.028472839649 4.6765444849623,-74.032273278366 4.6775012677607,-74.03825980124901 4.6799297676049,-74.048215993474 4.6850422042295,-74.05718496514402 4.6867981911917,-74.05100448502202 4.7239278424321))', 4326);
SELECT MBRContains(#g1, ST_PointFromText('POINT(-74.051585 4.680108)', 4326)) g1,
st_distance(ST_PointFromText('POINT(-74.051585 4.680108)', 4326), #g1) distance
and I'm getting
g1 distance
1 | 0.005489581062607619
But I was expecting
g1 distance
0 | 0.005489581062607619
I have try the following cases:
Save geometry assigning 4326 SRID.
Using other functions, getting same response.
I'm Using 5.7.14 MySQL version
What am I doing wrong?
I worked around this issue just validating that there's no distance. But, why am I getting this result from that functions?
I don't have a MySQL to play with right now. So i first try to duplicate your query in a SQL Server Spatial.
DECLARE #g1 geometry
DECLARE #h1 geometry
SET #g1= geometry::STGeomFromText('POLYGON((-74.05100448502202 4.7239278424321,-74.05092938316898 4.7241416902206,-74.04830618275201 4.7237460717602,-74.04643668306903 4.7234306460692,-74.04635688735101 4.7234105978214,-74.04636526925401 4.7233310730989,-74.046191260944 4.72327293317,-74.04579027069599 4.7232007594583,-74.04141290558402 4.7214258184083,-74.03746201170497 4.7197791822891,-74.03565688503801 4.7189879401666,-74.033484295736 4.7180897723398,-74.03098447693401 4.7170526009038,-74.028731840457 4.7161167561787,-74.02852820211899 4.7150714370973,-74.026398371001 4.6877232674918,-74.02558060109601 4.6874859863574,-74.02454587610401 4.686797564651,-74.024665108676 4.6863189291555,-74.025470986757 4.6857975214267,-74.02585246812498 4.6846813784365,-74.02580479605103 4.6834369175226,-74.01962984798399 4.684922743491,-74.028472839649 4.6765444849623,-74.032273278366 4.6775012677607,-74.03825980124901 4.6799297676049,-74.048215993474 4.6850422042295,-74.05718496514402 4.6867981911917,-74.05100448502202 4.7239278424321))', 4326);
SET #h1 = geometry::STGeomFromText('POINT(-74.051585 4.680108)', 4326)
SELECT #g1.STContains(#h1) contain, #g1.STDistance(#h1) distance
And the result is what you may expect:
contain distance
0 | 0.005489581062607675
Here is the reason behind this:
I am using STContains not MBRContains based on your description of what you are looking for. MBRContains function first create a Minimal Bounding Rectangle over your polygon, and use that new polygon feature do the contain judegement. In your example, the point does fall into the MBR of your polygon so thats why your MySQL result is not what you expect. And STContains is the right function you are looking for.
Official reference: Mysql spatial Link

How to insert the polygon and checking if a passed polygon is inside it in mysql database?

Can I check if a polygon is inside another polygon in mysql query? What will be the query to get all the polygons which contains the passed polygon in the where string?
After googling, I found a query:-
SELECT *, AsText(poly) FROM geos
WHERE Contains(
GeomFromText('POLYGON((42.000497 -109.050149,
41.002380 -102.051881,
39.993237 -102.041959,
38.999037 -109.045220,
42.000497 -109.050149))'), poly );
I am not sure what it is doing basically? So what will the query to insert the polygon and checking if a passed polygon is inside it?
Thanks all in advance!
You can insert a polygon like this:
SET #g1 = 'POLYGON ((
//your points of polygon here
))';
INSERT INTO geos set poly=ST_GeomFromText(#g1);
And if you want to check another g2 if it contains g1:
SELECT * FROM geos WHERE ST_CONTAINS(ST_GeomFromText(#g2), poly);

How to fill holes in MULTIPOLYGON in SQL Server 2008 spatial CLR

I have multipolygon that looks like a star with an empty hole in a middle:
image
How do I transform this multipolygon into a single polygon or multipolygon that also includes the hole in a middle, e.g. fill the hole (using SQL Server 2008 CLR)?
WKT of the empty star:
MULTIPOLYGON (((-75.850724788384952 39.781027647924496, -75.847514688412119 39.777165541462658, -75.835440819564724 39.781232982437771, -75.850724788384952 39.781027647924496)), ((-75.861083592601616 39.772592386436379, -75.836642464570019 39.764081172000729, -75.847514688412119 39.777165541462658, -75.861083592601616 39.772592386436379, -75.861083592601616 39.772592386436379)), ((-75.866832035574916 39.780809081927849, -75.850724788384952 39.781027647924496, -75.857585145413083 39.78927966926625, -75.866832035574916 39.780809081927849)), ((-75.8843643235475 39.764740937261692, -75.861083592601616 39.772592386436379, -75.8717486771904 39.776304058191712, -75.8843643235475 39.764740937261692)), ((-75.884021002483152 39.780573380153484, -75.8717486771904 39.776304058191712, -75.866832035574916 39.780809081927849, -75.884021002483152 39.780573380153484)))
Thank you.
Maybe not the fastest way, but one way that you could do it:
Compute a geometry that contains your input: Use STConvexHull, which returns a single polygon that contains your entire input geometry, including both empty regions inside the geometry and concavities outside the geometry (such as the regions in between the points of your star).
Find the empty regions: Use STDifference, subtracting the input from the convex hull, which will return polygons for those empty regions previously mentioned.
Determine which empty regions are outside your input: Use STBoundary to get the bounds of the convex hull. A region is inside your input geometry if and only if it does not touch the bounds of the convex hull. Use STTouches to check.
Compute the union of your geometry and the inner empty regions: Use STUnion.
Here is an example script:
DECLARE #g geometry;
DECLARE #hull geometry;
DECLARE #regions geometry;
DECLARE #boundary geometry;
SET #g = geometry::STGeomFromText(...);
SET #hull = #g.STConvexHull();
SET #regions = #hull.STDifference(#g);
SET #boundary = #hull.STBoundary();
WITH Geoms AS
(
SELECT 1 i
UNION ALL
SELECT i + 1
FROM Geoms
WHERE i < #regions.STNumGeometries()
)
SELECT #g = #g.STUnion(#regions.STGeometryN(i))
FROM Geoms
WHERE #regions.STGeometryN(i).STTouches(#boundary) = 0
SELECT #g

MySQL - Perl: How to get array of zip codes within submitted "x" miles of submitted "zipcode" in Perl example

I have found many calculations here and some php examples and most are just over my head.
I found this example:
SELECT b.zip_code, b.state,
(3956 * (2 * ASIN(SQRT(
POWER(SIN(((a.lat-b.lat)*0.017453293)/2),2) +
COS(a.lat*0.017453293) *
COS(b.lat*0.017453293) *
POWER(SIN(((a.lng-b.lng)*0.017453293)/2),2))))) AS distance
FROM zips a, zips b
WHERE
a.zip_code = '90210' ## I would use the users submitted value
GROUP BY distance
having distance <= 5; ## I would use the users submitted value
But, I am having trouble understanding how to implement the query with my database.
It looks like that query has all I need.
However, I cannot even find/understand what b.zip_code actually is! (whats the b. and zips a, zips b?)
I also do not need the state in the query.
My mySQL db structure is like this:
ZIP | LAT | LONG
33416 | 26.6654 | -80.0929
I wrote this in attempt to return some kind of results (not based on above query) but, it only kicks out one zip code.
## Just for a test BUT, in reality I desire to SELECT a zip code WHERE ZIP = the users submitted zip code
## not by a submitted lat lon. I left off the $connect var, assume it's there.
my $set1 = (26.6654 - 0.20);
my $set2 = (26.6654 + 0.20);
my $set3 = (-80.0929 - 0.143);
my $set4 = (-80.0929 + 0.143);
my $test123 = $connect->prepare(qq{SELECT `ZIP` FROM `POSTAL`
WHERE `LAT` >= ? AND `LAT` <= ?
AND `LONG` >= ? AND `LONG` <= ?}) or die "$DBI::errstr";
$test123->execute("$set1","$set2","$set3","$set4") or die "$DBI::errstr";
my $cntr;
while(#zip = $test123->fetchrow_array()) {
print qq~$zip[$cntr]~;
push(#zips,$zip[$cntr]);
$cntr++;
}
As you can see, I am quite the novice so, I need some hand holding here with verbose explanation.
So, in Perl, how can I push zip codes into an array from a USER SUBMITTED ZIP CODE and user submitted DISTANCE in miles. Can be a square instead of a circle, not really that critical of a feature. Faster is better.
I'll tackle the small but crucial part of the question:
However, I cannot even find/understand what b.zip_code actually is! (whats the "b." and "zips a, zips b"?)
Basically, the query joins two tables. BUT, both tables being joined are in fact the same table - "zips" (in other words, it joins "zips" table to itself"). Of course, since the rest of the query needs to understand when you are referring to the first copy of the "zips" table and when to the second copy of the "zips" table, you are giving a table alias to each copy - to wit, "a" and "b"'.
So, "b.xxx" means "column xxx from table zips, from the SECOND instance of that table being joined".
I don't see what's wrong with your first query. You have latitude and longitude in your database (if I'm understanding, you're comparing a single entry to all others). You don't need to submit or return the state that's just part of the example. Make the first query work like this:
my $query = "SELECT b.zip_code,
(3956 * (2 * ASIN(SQRT(
POWER(SIN(((a.lat-b.lat)*0.017453293)/2),2) +
COS(a.lat*0.017453293) *
COS(b.lat*0.017453293) *
POWER(SIN(((a.lng-b.lng)*0.017453293)/2),2))))) AS distance
FROM zips a, zips b WHERE
a.zip_code = ?
GROUP BY distance having distance <= ?";
my $sth = $dbh->prepare($query);
$sth->execute( $user_submitted_zip, $user_submitted_distance );
while( my ($zip, $distance) = $sth->fetchrow() ) ) {
# do something
}
This won't be that fast, but if you have a small record set ( less than 30k rows ) it should be fine. If you really want to go faster you should look into a search engine such as Sphinx which will do this for you.
fetchrow_array returns a list of list references, essentially a two-dimensional array, where each row represents a different result from the database query and each column represents a field from the query (in your case, there is only one field, or column, per row).
Calling while ($test123->fetchrow_array()) will cause an infinite loop as your program executes the query over and over again. If the query returns results, then the while condition will be satisfied and the loop will repeat. The usual idiom would be to say something more like for my $row ($test123->fetchrow_array()) { ..., which will only execute the query once and then iterate over the results.
Each result is a list reference, and the zip code you are interested in is in the first (and only) column, so you could accumulate the results in an array like this:
my #zips = (); # for final results
for my $row ($test123->fetchrow_array()) {
push #zips, $row->[0];
}
or even more concisely with Perl's map statement:
my #zips = map { $_->[0] } $test123->fetchrow_array()
which does the same thing.