MySQL implementacja algorytmu Ray-casting?

Musimy znaleźć szybką i dość dokładną metodę dla punktu w wielokątu dla wartości lat / long i wielokątów w google maps. Po kilku badaniach - natknąłem się na kilka postów na temat rozszerzeń geometrycznych mysql i też to zaimplementowałem -

SELECT id, Contains( PolyFromText( 'POLYGON(".$polygonpath.")' ) , PointFromText( concat( \"POINT(\", latitude, \" \", longitude, \")\" ) ) ) AS
            CONTAINS
FROM tbl_points

To jednak nie zadziałało z wielokątami złożonymi z dużej liczby punktów: (

Po kilku dalszych badaniach-natknąłem się na standardowy algorytm zwany algorytmem odlewania promieni, ale zanim spróbowałem opracować Zapytanie o to w MySQL, chciałem zaryzykować, jeśli ktoś już przez to przeszedł lub natknął się na przydatny link, który pokazuje, jak zaimplementować algorytm w MySQL / SQL-server.

A więc krótko mówiąc-pytanie brzmi:

Czy ktoś może podać implementację MySQL/SQL-server algorytmu Ray casting?

Dodatkowy szczegół:

    Wielokąty są wklęsłe, wypukłe lub złożone.
  • szybkie wykonanie ponad 100% dokładność.
Author: Johan, 2011-09-27

7 answers

Następująca funkcja (MYSQL Wersja algorytmu Raycasting) wstrząsnęła moim światem:

CREATE FUNCTION myWithin(p POINT, poly POLYGON) RETURNS INT(1) DETERMINISTIC 
BEGIN 
DECLARE n INT DEFAULT 0; 
DECLARE pX DECIMAL(9,6); 
DECLARE pY DECIMAL(9,6); 
DECLARE ls LINESTRING; 
DECLARE poly1 POINT; 
DECLARE poly1X DECIMAL(9,6); 
DECLARE poly1Y DECIMAL(9,6); 
DECLARE poly2 POINT; 
DECLARE poly2X DECIMAL(9,6); 
DECLARE poly2Y DECIMAL(9,6); 
DECLARE i INT DEFAULT 0; 
DECLARE result INT(1) DEFAULT 0; 
SET pX = X(p); 
SET pY = Y(p); 
SET ls = ExteriorRing(poly); 
SET poly2 = EndPoint(ls); 
SET poly2X = X(poly2); 
SET poly2Y = Y(poly2); 
SET n = NumPoints(ls); 
WHILE i<n DO 
SET poly1 = PointN(ls, (i+1)); 
SET poly1X = X(poly1); 
SET poly1Y = Y(poly1); 
IF ( ( ( ( poly1X <= pX ) && ( pX < poly2X ) ) || ( ( poly2X <= pX ) && ( pX < poly1X ) ) ) && ( pY > ( poly2Y - poly1Y ) * ( pX - poly1X ) / ( poly2X - poly1X ) + poly1Y ) ) THEN 
SET result = !result; 
END IF; 
SET poly2X = poly1X; 
SET poly2Y = poly1Y; 
SET i = i + 1; 
END WHILE; 
RETURN result; 
End; 

Dodaj

  DELIMITER ;; 

Przed funkcją zgodnie z wymaganiami. Użycie funkcji to:

 SELECT myWithin(point, polygon) as result;

Gdzie

 point  = Point(lat,lng) 
 polygon = Polygon(lat1 lng1, lat2 lng2, lat3 lng3, .... latn lngn, lat1 lng1)

Należy pamiętać, że wielokąt powinien być zamknięty (zwykle jest zamknięty, jeśli pobierasz standardowe dane kml lub googlemap, ale upewnij się, że tak jest - uwaga zestaw lat1 lng1 jest powtarzany na końcu)

Nie miałem punktów i wielokątów w bazie danych jako pola geometryczne, więc musiałem zrobić coś w stylu:

 Select myWithin(PointFromText( concat( "POINT(", latitude, " ", longitude, ")" ) ),PolyFromText( 'POLYGON((lat1 lng1, ..... latn lngn, lat1 lng1))' ) ) as result
Mam nadzieję, że to komuś pomoże.
 18
Author: zarun,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-10-10 06:17:44

Napisałbym Niestandardowy UDF, który implementuje algorytm rzucania promieni w C lub Delphi lub jakiekolwiek narzędzie wysokiego poziomu, którego używasz:

Linki do pisania UDF
Oto kod źródłowy dla implementacji GIS MySQL, która wyszukuje punkt na kuli (użyj go jako szablonu, aby zobaczyć, jak współdziałać z przestrzennymi typami danych w MySQL).
http://www.lenzg.net/archives/220-New-UDF-for-MySQL-5.1-provides-GIS-functions-distance_sphere-and-distance_spheroid.html{[8]

Od Instrukcja MySQL:
http://dev.mysql.com/doc/refman/5.0/en/adding-functions.html

UDF tutorial dla MS Visual C++
http://rpbouman.blogspot.com/2007/09/creating-mysql-udfs-with-microsoft.html

UDF tutorial w Delphi:
Tworzenie UDF dla MySQL w Delphi

Kod źródłowy dotyczący algorytmu odlewania promieni
Pseudo-kod: http://rosettacode.org/wiki/Ray-casting_algorithm
Artykuł w drDobbs (Uwaga link do kodu na górze artykułu): http://drdobbs.com/cpp/184409586
Delphi (właściwie FreePascal): http://www.cabiatl.com/mricro/raycast/

 5
Author: Johan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:17:36

Na wszelki wypadek jedna funkcja MySQL, która przyjmuje MULTIPOLYGON jako wejście: http://forums.mysql.com/read.php?23,286574,286574

DELIMITER $$

CREATE DEFINER=`root`@`localhost` FUNCTION `GISWithin`(pt POINT, mp MULTIPOLYGON) RETURNS int(1)
    DETERMINISTIC
BEGIN 

DECLARE str, xy TEXT; 
DECLARE x, y, p1x, p1y, p2x, p2y, m, xinters DECIMAL(16, 13) DEFAULT 0; 
DECLARE counter INT DEFAULT 0; 
DECLARE p, pb, pe INT 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; 
SET str = REPLACE(str, 'POLYGON((',''); 
SET str = REPLACE(str, '))', ''); 
SET str = CONCAT(str, ','); 

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; 

RETURN counter % 2; 

END
 2
Author: Igor,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-11-02 13:58:29

Chciałem użyć powyższej procedury składowanej mywithin na tabeli wielokątów, więc oto polecenia, aby to zrobić.

Po zaimportowaniu pliku shape zawierającego wielokąty do mysql przy użyciu ogr2ogr w następujący sposób

ogr2ogr -f "mysql" MYSQL:"mydbname,host=localhost,user=root,password=mypassword,port=3306" -nln "mytablename" -a_srs "EPSG:4326" /path/to/shapefile.shp

Możesz następnie użyć MBRwithin do wstępnego filtrowania tabeli i mywithin, aby zakończyć w następujący sposób

DROP TEMPORARY TABLE IF EXISTS POSSIBLE_POLYS;
CREATE TEMPORARY TABLE POSSIBLE_POLYS(OGR_FID INT,SHAPE POLYGON);
INSERT INTO POSSIBLE_POLYS (OGR_FID, SHAPE) SELECT mytablename.OGR_FID,mytablename.SHAPE FROM mytablename WHERE MBRwithin(@testpoint,mytablename.SHAPE);

DROP TEMPORARY TABLE IF EXISTS DEFINITE_POLY;
CREATE TEMPORARY TABLE DEFINITE_POLY(OGR_FID INT,SHAPE POLYGON);
INSERT INTO DEFINITE_POLY (OGR_FID, SHAPE) SELECT POSSIBLE_POLYS.OGR_FID,POSSIBLE_POLYS.SHAPE FROM POSSIBLE_POLYS WHERE mywithin(@testpoint,POSSIBLE_POLYS.SHAPE);

Gdzie tworzony jest @testpoint, na przykład z

SET @longitude=120;
SET @latitude=-30;
SET @testpoint =(PointFromText( concat( "POINT(", @longitude, " ", @latitude, ")" ) ));
 1
Author: Mr Purple,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-03 22:44:28

Jest teraz rozszerzeniem przestrzennym od MySQL5.6.1 i wyżej. Zobacz function_st-contains at Docs.

 1
Author: Nasser Al-Wohaibi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-19 11:23:17

W odpowiedzi na funkcję zarun do znajdowania lat / long wewnątrz wielokąta.

Miałem tabelę właściwości z informacjami o latach / długościach. Musiałem więc zdobyć rekordy, których lat / long leży w wielokątach lat/longs (które dostałem z Google API). Na początku byłem głupi, jak korzystać z funkcji Zarun. Oto Zapytanie o rozwiązanie.

  • tabela: właściwości
  • pola: id, latitude, longitude, beds itp...
  • Query:
SELECT id
FROM properties
WHERE myWithin(
    PointFromText(concat( "POINT(", latitude, " ", longitude, ")")),
    PolyFromText('POLYGON((37.628134 -77.458334,37.629867 -77.449021,37.62324 -77.445416,37.622424 -77.457819,37.628134 -77.458334))' )
) = 1 limit 0,50;

Hope it oszczędzi czas takim głupkom jak ja;)

 1
Author: Nasir Hussain,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-21 12:48:29

Oto wersja, która działa z wielokątami (adaptacja Zaruna, która działa tylko dla wielokątów).

CREATE FUNCTION GISWithin(p POINT, multipoly MULTIPOLYGON) RETURNS INT(1) DETERMINISTIC 
BEGIN 
DECLARE n,i,m,x INT DEFAULT 0; 
DECLARE pX,pY,poly1X,poly1Y,poly2X,poly2Y DECIMAL(13,10); 
DECLARE ls LINESTRING; 
DECLARE poly MULTIPOLYGON;
DECLARE poly1,poly2 POINT; 
DECLARE result INT(1) DEFAULT 0; 
SET pX = X(p); 
SET pY = Y(p); 
SET m = NumGeometries(multipoly);
WHILE x<m DO 
SET poly = GeometryN(multipoly,x);
SET ls = ExteriorRing(poly); 
SET poly2 = EndPoint(ls); 
SET poly2X = X(poly2); 
SET poly2Y = Y(poly2); 
SET n = NumPoints(ls); 
WHILE i<n DO 
SET poly1 = PointN(ls, (i+1)); 
SET poly1X = X(poly1); 
SET poly1Y = Y(poly1); 
IF ( ( ( ( poly1X <= pX ) && ( pX < poly2X ) ) || ( ( poly2X <= pX ) && ( pX < poly1X ) ) ) && ( pY > ( poly2Y - poly1Y ) * ( pX - poly1X ) / ( poly2X - poly1X ) + poly1Y ) ) THEN 
SET result = !result; 
END IF; 
SET poly2X = poly1X; 
SET poly2Y = poly1Y; 
SET i = i + 1; 
END WHILE; 
SET x = x + 1;
END WHILE;
RETURN result; 
End; 
 1
Author: daamsie,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-08 04:18:45