问题
I have the created the table below
CREATE TABLE geom (g GEOMETRY);
and have inserted many rows, example below:
INSERT INTO geom (g)
VALUES(PolygonFromText('POLYGON((
9.190586853 45.464518970,
9.190602686 45.463993916,
9.191572471 45.464001929,
9.191613325 45.463884676,
9.192136130 45.463880767,
9.192111509 45.464095594,
9.192427961 45.464117804,
9.192417811 45.464112862,
9.192509035 45.464225851,
9.192493139 45.464371079,
9.192448471 45.464439002,
9.192387444 45.464477861,
9.192051402 45.464483037,
9.192012814 45.464643592,
9.191640825 45.464647090,
9.191622331 45.464506215,
9.190586853 45.464518970))')
);
Now I want to search all the data and return the entries where a lat / long I have falls withn any of the polygons.
How can this be done using mysql? or is anyone aware of any links that will point me in the right direction?
回答1:
MySQL as of v5.1 only supports operations on the minimum bounding rectangles (MBR). While there is a "Contains" function which would do what you need, it is not fully implemented and falls back to using MBRContains
From the relevant manual page
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. This includes functions in the following list other than Distance() and Related().
These functions may be implemented in future releases with full support for spatial analysis, not just MBR-based support.
What you could do is let MySQL give you an approximate result based on MBR, and then post process it to perform a more accurate test. Alternatively, switch to PostGIS!
(Update May 2012 - thanks Mike Toews)
MySQL 5.6.1+ offers functions which use object shapes rather than MBR
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.
回答2:
If you cannot change dbs to one that has spatial operators implemented correctly like PostgreSQL's PostGIS extension http://postgis.refractions.net/
, you can solve this problem using a two-part approach.
First let MySQL give you an bounding box pre-filtering result based on the bounding box (that is what it does by default) using their intersects operator (http://dev.mysql.com/doc/refman/5.1/en/functions-that-test-spatial-relationships-between-geometries.html#function_intersects
).
If queries are slow, make sure that you have an index on your geometry field first.
Then hydrate the original geometry that you used in your query into a geometry object of GIS geometry library like GEOS (http://trac.osgeo.org/geos/
) (C++ based, although it also has bindings for different languages like Python), Shapely (http://trac.gispython.org/lab/wiki/Shapely)
, OGR ( or the Java Topology Suite (JTS) http://www.vividsolutions.com/jts/jtshome.htm
).
Test each of the geometries that you get back from your query result using the appropriate operator like within or intersects. Any of these libraries will give you a boolean result.
Personally, I would look at the samples for OGR since it has a big community that is ready to help.
Oh yeah, and sorry for putting the links like that... I guess since I am "new" I can only post one link (?)
回答3:
The function given in this post on the MySQL forums works perfectly for me.
It's not very quick and you have to ensure the parameter 'mp' is the same type as the spatial column you are using (I used ogr2ogr to import an Ordnance Survey shapefile into MySQL, so had to change it from 'MULTIPOLYGON' to 'GEOMETRY')
回答4:
I have rewritten function that was given in previous post by @danherd, so it can work with real multipolygons which consist from more that one polygon. For those of you who still keep using old MySql version it should help.
Here it is:
DELIMITER //
CREATE FUNCTION GISWithin(pt POINT, mp MULTIPOLYGON) RETURNS INT(1) DETERMINISTIC
BEGIN
DECLARE str_big, str, xy LONGTEXT;
DECLARE x, y, p1x, p1y, p2x, p2y, m, xinters DECIMAL(16, 13) DEFAULT 0;
DECLARE counter INT DEFAULT 0;
DECLARE p, pb, pe, sb, se, ct DECIMAL(16, 0) DEFAULT 0;
SELECT MBRWithin(pt, mp) INTO p;
IF p != 1 OR ISNULL(p) THEN
return p;
END IF;
SELECT X(pt), Y(pt), ASTEXT(mp) INTO x, y, str_big;
SET str_big = REPLACE(str_big, 'MULTIPOLYGON(((','');
SET str_big = REPLACE(str_big, ')))', '');
SET str_big = REPLACE(str_big, ')),((', '|');
SET str_big = CONCAT(str_big, '|');
SET sb = 1;
SET se = LOCATE('|', str_big);
SET str = SUBSTRING(str_big, sb, se - sb);
WHILE se > 0 DO
SET ct = ct + 1;
SET str = SUBSTRING(str_big, sb, se - sb);
SET pb = 1;
SET pe = LOCATE(',', str);
SET xy = SUBSTRING(str, pb, pe - pb);
SET p = INSTR(xy, ' ');
SET p1x = SUBSTRING(xy, 1, p - 1);
SET p1y = SUBSTRING(xy, p + 1);
SET str = CONCAT(str, xy, ',');
WHILE pe > 0 DO
SET xy = SUBSTRING(str, pb, pe - pb);
SET p = INSTR(xy, ' ');
SET p2x = SUBSTRING(xy, 1, p - 1);
SET p2y = SUBSTRING(xy, p + 1);
IF p1y < p2y THEN SET m = p1y; ELSE SET m = p2y; END IF;
IF y > m THEN
IF p1y > p2y THEN SET m = p1y; ELSE SET m = p2y; END IF;
IF y <= m THEN
IF p1x > p2x THEN SET m = p1x; ELSE SET m = p2x; END IF;
IF x <= m THEN
IF p1y != p2y THEN
SET xinters = (y - p1y) * (p2x - p1x) / (p2y - p1y) + p1x;
END IF;
IF p1x = p2x OR x <= xinters THEN
SET counter = counter + 1;
END IF;
END IF;
END IF;
END IF;
SET p1x = p2x;
SET p1y = p2y;
SET pb = pe + 1;
SET pe = LOCATE(',', str, pb);
END WHILE;
SET sb = se + 1;
SET se = LOCATE('|', str_big, sb);
END WHILE;
RETURN counter % 2;
END
DELIMITER ;
来源:https://stackoverflow.com/questions/1078386/see-if-lat-long-falls-within-a-polygon-using-mysql