2014-02-20 4 views
1

У меня есть границы города от переписи, загруженной в базу данных оракула. Я пытаюсь использовать данные для разрешения города и штата на любую широту или долготу. У меня есть заявление sql, чтобы уменьшить города, которые мне нужны для проверки области, однако мне пришлось написать свою собственную функцию pl/SQL, чтобы проверить, существует ли точка в городских пределах. Его алгоритм литья лучей, который я получил от версии ADA, приведенной здесь http://rosettacode.org/wiki/Ray-casting_algorithm. Проблема в том, что полойгон сложный (т. Е. Он имеет формы в форме), алгоритм возвращает неверные результаты. Может ли кто-нибудь предложить способ обойти эту проблему?Рэй-литье с функцией Pl/SQL

Вот Pl/SQL Polygon Функция:

CREATE OR REPLACE PACKAGE Polygons as 
    type Point is record 
    (
     X number, 
     Y number 
    ); 
    type Point_List is table of Point; 
    type poly_Segment is VARRAY(2) of Point; 
    type Polygon is TABLE of poly_Segment; 
    function Get_Point(x number,y number) return Point; 
    function Create_Polygon (listOfPoints Point_List) return Polygon; 
    function Is_Inside (pt2Check Point, shape2Check Polygon) return varchar2; 
end Polygon; 

/

CREATE OR REPLACE package body Polygons as 

    EPSILON CONSTANT number := 0.00001; 

    function Get_Point(x number,y number) return Point is 
     pnt2Rtn Point; 
    BEGIN 
     pnt2Rtn.x := x; 
     pnt2Rtn.y := y; 
     return pnt2Rtn; 
    END Get_Point; 

    function Ray_Intersects_Segment(Who Point, locSec poly_Segment) return Boolean 
    is 
     The_Point Point := Who; 
     Above Point; 
     Below Point; 
     M_Red number; 
     Red_Is_Infinity Boolean := False; 
     M_Blue number; 
     Blue_Is_Infinity Boolean := False; 
    begin 
     if locSec (1).Y < locSec (2).Y then 
      Above := locSec (2); 
      Below := locSec (1); 
     else 
      Above := locSec (1); 
      Below := locSec (2); 
     end if; 
     if The_Point.Y = Above.Y or The_Point.Y = Below.Y then 
      The_Point.Y := The_Point.Y + EPSILON; 
     end if; 
     if The_Point.Y < Below.Y or The_Point.Y > Above.Y then 
      return False; 
     elsif The_Point.X > Above.X and The_Point.X > Below.X then 
      return False; 
     elsif The_Point.X < Above.X and The_Point.X < Below.X then 
      return True; 
     else 
      if Above.X <> Below.X then 
       M_Red := (Above.Y - Below.Y)/(Above.X - Below.X); 
      else 
       Red_Is_Infinity := True; 
      end if; 
      if Below.X <> The_Point.X then 
       M_Blue := (The_Point.Y - Below.Y)/(The_Point.X - Below.X); 
      else 
       Blue_Is_Infinity := True; 
      end if; 
      if Blue_Is_Infinity then 
       return True; 
      elsif Red_Is_Infinity then 
       return False; 
      elsif M_Blue >= M_Red then 
       return True; 
      else 
       return False; 
      end if; 
     end if; 
    end Ray_Intersects_Segment; 

    function Create_Polygon (listOfPoints Point_List) return Polygon is 
     polyRes Polygon := new Polygon(); 
     Side poly_Segment; 
    begin 
     polyRes.extend(listOfPoints.Count); 
     for I in 1..listOfPoints.Count loop 
      Side := new poly_Segment(); 
      Side.extend(2); 
      Side (1) := listOfPoints (I); 
      --connect connect the lines if its the last item connect back to the first 
      if I = listOfPoints.COUNT then 
       Side (2) := listOfPoints (listOfPoints.FIRST); 
      else 
       Side (2) := listOfPoints (I + 1); 
      end if; 

      polyRes(I) := Side; 
     end loop; 

     return polyRes; 
    end Create_Polygon; 

    function Is_Inside (pt2Check Point, shape2Check Polygon) return varchar2 is 
     cnt4Intersect number := 0; 
    begin 
     for Side in 1..shape2Check.count loop 
      if Ray_Intersects_Segment (pt2Check, shape2Check (Side)) then 
      cnt4Intersect := cnt4Intersect + 1; 
      end if; 
     end loop; 

     if cnt4Intersect mod 2 = 0 then 
      return 'N'; 
     else 
      return 'Y'; 
     end if; 
    end Is_Inside; 

end Polygons; 
/

Вот ссылка на данные формы http://pastebin.com/yxrftXQn

Я тестирование, чтобы увидеть, если 33.66840, - 87.59806 был в городе (это, но по моей функции его нет)

EDIT Итак, после некоторых исследований я обнаружил, что проблема заключается в том, как я определяю формы многоугольников. Мне нужно было настроить фид данных.

EDIT Возможно ли сохранить пакет Polygon и использовать его SDO_RELATE и по-прежнему повысить эффективность (может ли кто-нибудь предоставить пример кода)?

+3

Вы уверены, что вам нужно заново изобрести это конкретное колесо, а не используя функциональность Oracle Spatial, встроенную в базу данных? –

+0

Для одной своей базы данных оракула 9 для двух пространственных пространств оракула есть собственный продукт и имеет свои собственные ограничения. Посмотрите руководство по покупке https://shop.oracle.com/pls/ostore/product?p1=OracleSpatialandGraph&p2. = & p3 = & p4 = & intcmp = ocom_Oracle_Spatial_and_Graph – Bostwick

+2

Даже Oracle 9i существует подмножество Spatial (называемое Locator), доступное в стандартной версии (см. http://docs.oracle.com/cd/B10501_01/appdev.920/a96630/sdo_locator .htm # i632018). Локатор включает в себя [SDO_RELATE] (http://docs.oracle.com/cd/B10501_01/appdev.920/a96630/sdo_operat.htm#i78531) оператор, который можно использовать для определения того, находится ли точка в многоугольнике. –

ответ

2

Алгоритм лучевого литья не проблема, он работает! Проблема заключалась в том, что мне нужно было разделить точки данных с помощью столбца частей, который не был включен (потому что было неясно, что это было бы полезно). Короче, данные были плохими, и мне нужно было правильно форматировать фигуры (у меня также было слишком много данных формы) из-за того, как я экспортировал файл .shp ..).

Каждая форма должна быть собственные полигоны и проверять, находится ли внутри форма, так же просто, как проверить, что все точки имеют форму ...

Я добавил несколько новых функций для многоугольника, чтобы поддержать то, что мне нужно было сделать. .ideally, я хотел бы перейти к использованию вверх по течению функции, но они не установлены на сервере я использую ..

Вот полный Polygon Пакет:

CREATE OR REPLACE PACKAGE Polygons as 

    type Point is record 
    (
     X number, 
     Y number 
    ); 

    type Point_List is table of Point; 

    type poly_Segment is VARRAY(2) of Point; 

    type Polygon is TABLE of poly_Segment; 

    type Polygons is TABLE of Polygon; 

    function Get_Point(x number,y number) return Point; 

    function Create_Polygon (listOfPoints Point_List) return Polygon; 

    function Is_Inside (pt2Check Point, shape2Check Polygon) return varchar2; 


    function Is_Inside_Polygon (pt2Check Point, shape2Check Polygon,shapes2Include Polygons) return varchar2; 

    function Is_Inside_Polygon (pt2Check Point, shape2Check Polygon,shapes2Exclude Polygons,shapes2Include Polygons) return varchar2; 

end Polygons; 


/
CREATE OR REPLACE package body Polygons as 

    EPSILON CONSTANT number := 0.00001; 

    function Get_Point(x number,y number) return Point is 
     pnt2Rtn Point; 
    BEGIN 
     pnt2Rtn.x := x; 
     pnt2Rtn.y := y; 
     return pnt2Rtn; 
    END Get_Point; 

    function Ray_Intersects_Segment(Who Point, locSec poly_Segment) return Boolean 
    is 
     The_Point Point := Who; 
     Above Point; 
     Below Point; 
     M_Red number; 
     Red_Is_Infinity Boolean := False; 
     M_Blue number; 
     Blue_Is_Infinity Boolean := False; 
    begin 
     if locSec (1).Y < locSec (2).Y then 
      Above := locSec (2); 
      Below := locSec (1); 
     else 
      Above := locSec (1); 
      Below := locSec (2); 
     end if; 
     if The_Point.Y = Above.Y or The_Point.Y = Below.Y then 
      The_Point.Y := The_Point.Y + EPSILON; 
     end if; 
     if The_Point.Y < Below.Y or The_Point.Y > Above.Y then 
      return False; 
     elsif The_Point.X > Above.X and The_Point.X > Below.X then 
      return False; 
     elsif The_Point.X < Above.X and The_Point.X < Below.X then 
      return True; 
     else 
      if Above.X <> Below.X then 
       M_Red := (Above.Y - Below.Y)/(Above.X - Below.X); 
      else 
       Red_Is_Infinity := True; 
      end if; 
      if Below.X <> The_Point.X then 
       M_Blue := (The_Point.Y - Below.Y)/(The_Point.X - Below.X); 
      else 
       Blue_Is_Infinity := True; 
      end if; 
      if Blue_Is_Infinity then 
       return True; 
      elsif Red_Is_Infinity then 
       return False; 
      elsif M_Blue >= M_Red then 
       return True; 
      else 
       return False; 
      end if; 
     end if; 
    end Ray_Intersects_Segment; 

    function Create_Polygon (listOfPoints Point_List) return Polygon is 
     polyRes Polygon := new Polygon(); 
     Side poly_Segment; 
    begin 
     polyRes.extend(listOfPoints.Count); 
     for I in 1..listOfPoints.Count loop 
      Side := new poly_Segment(); 
      Side.extend(2); 
      Side (1) := listOfPoints (I); 
      --connect connect the lines if its the last item connect back to the first 
      if I = listOfPoints.COUNT then 
       Side (2) := listOfPoints (listOfPoints.FIRST); 
      else 
       Side (2) := listOfPoints (I + 1); 
      end if; 

      polyRes(I) := Side; 
     end loop; 

     return polyRes; 
    end Create_Polygon; 

    function Is_Inside (pt2Check Point, shape2Check Polygon) return varchar2 is 
     cnt4Intersect number := 0; 
    begin 
     for Side in 1..shape2Check.count loop 
      if Ray_Intersects_Segment (pt2Check, shape2Check (Side)) then 
      cnt4Intersect := cnt4Intersect + 1; 
      end if; 
     end loop; 

     if cnt4Intersect mod 2 = 0 then 
      --return to_char(shape2Check.count); 
      return 'N'; 
     else 
      return 'Y'; 
     end if; 
    end Is_Inside; 

    function Is_Inside_Polygon (pt2Check Point, shape2Check Polygon,shapes2Include Polygons) return varchar2 is 
    begin 
     return Is_Inside_Polygon(pt2Check,shape2Check,new Polygons(),shapes2Include); 
    end Is_Inside_Polygon; 

    function Is_Inside_Polygon (pt2Check Point, shape2Check Polygon,shapes2Exclude Polygons,shapes2Include Polygons) return varchar2 is 
     foundInShape varchar2(4); 
     currentSet number := 1; 
     shape2BeExcluded varchar2(4); 
    begin 
     foundInShape := Is_Inside(pt2Check,shape2Check); 
     IF foundInShape = 'N' THEN 
      IF shapes2Include is not null THEN 
       for currentSet in 1..shapes2Include.count loop 
        foundInShape := Is_Inside(pt2Check,shapes2Include(currentSet)); 
        exit when foundInShape = 'Y'; 
       END Loop; 
      END IF; 
     END IF; 
     IF foundInShape = 'Y' THEN 
      IF shapes2Exclude is not null THEN 
       for currentSet in 1..shapes2Exclude.count loop 
        shape2BeExcluded := Is_Inside(pt2Check,shapes2Exclude(currentSet)); 
        IF shape2BeExcluded = 'Y' THEN 
         foundInShape := 'N'; 
         exit; 
        END IF; 
       END Loop; 
      END IF; 
     END IF; 
     return foundInShape; 
    end Is_Inside_Polygon; 

end Polygons; 
/
+0

Привет.Если этот ответ отвечает на ваш вопрос, вы должны «принять» свой собственный ответ. Это поставит вопрос как «ответ». Если нет, я предлагаю вам отредактировать свой вопрос. – lrineau

+1

У меня нет репутации, чтобы принять мой ответ владельца еще (мне нужно подождать 22 часа ..) .. – Bostwick

+0

Эй, Боствик - это выглядит потрясающе. Есть ли вероятность, что вы могли бы привести пример использования? – gruvn