понедельник, 16 февраля 2015 г.

Чтение и изменение азимута ориентированных точек Oracle Spatial

Известно, что в Oracle Spatial ориентированные точки задаются координатами точки и радиус-вектором относительно этой точки:

SDO_GEOMETRY(
    2001, -- точка на плоскости 
    NULL, -- номер проекции
    NULL, 
    SDO_ELEM_INFO_ARRAY(1,1,1, 3,1,0), 
    SDO_ORDINATE_ARRAY(12,14, 0.3,0.2))); -- декартовы координаты 12,14 - точки и 0.3,0.2 - радиус-вектора
Геоинформационные системы могут отображать и изменять ориентированные точки, если запрос к БД возвращает азимут
(угол от 0° до 360°). Для этого можно создать представление, использующее две функции:

CREATE OR REPLACE FUNCTION GETANGLE(GEOLOC IN MDSYS.SDO_GEOMETRY) RETURN NUMBER AS 
  bearing NUMBER;
  tilt    NUMBER;
BEGIN
  IF GEOLOC IS NULL OR GEOLOC.sdo_ordinates.count <> 4 THEN
    RETURN 0;
  ELSE
    SDO_UTIL.BEARING_TILT_FOR_POINTS(
      SDO_GEOMETRY(2001, 8307, SDO_POINT_TYPE(0, 0, NULL), NULL, NULL),
      SDO_GEOMETRY(2001, 8307, SDO_POINT_TYPE(GEOLOC.sdo_ordinates(3), GEOLOC.sdo_ordinates(4), NULL), NULL, NULL),
      0.05, bearing, tilt);
    RETURN ROUND(SDO_UTIL.CONVERT_UNIT(bearing, 'radian', 'degree'));
  END IF;  
END;
/

CREATE OR REPLACE FUNCTION SETANGLE(GEOLOC IN MDSYS.SDO_GEOMETRY, ANGLE IN NUMBER) RETURN MDSYS.SDO_GEOMETRY AS
  x0 NUMBER;
  y0 NUMBER;
  x1 NUMBER;
  y1 NUMBER;
  GEOLOC1 MDSYS.SDO_GEOMETRY;
BEGIN
  IF GEOLOC IS NULL OR GEOLOC.GET_DIMS() <> 2 OR GEOLOC.GET_GTYPE() <> 1 THEN
    RETURN GEOLOC;
  ELSE
    GEOLOC1 := SDO_UTIL.POINT_AT_BEARING(MDSYS.SDO_GEOMETRY(2001,8307,
    MDSYS.SDO_POINT_TYPE(0,0,NULL),NULL,NULL),SDO_UTIL.CONVERT_UNIT(NVL(ANGLE,0),'degree','radian'),100000);
    SELECT V.X, V.Y INTO x0, y0 FROM TABLE(SDO_UTIL.GETVERTICES(GEOLOC)) V;
    SELECT V.X, V.Y INTO x1, y1 FROM TABLE(SDO_UTIL.GETVERTICES(GEOLOC1)) V;
    RETURN MDSYS.SDO_GEOMETRY(2001,262148,NULL,MDSYS.SDO_ELEM_INFO_ARRAY(1,1,1,3,1,0),MDSYS.SDO_ORDINATE_ARRAY(x0,y0,x1,y1));
  END IF;
END;
/
Здесь 8307 - номер проекции "Longitude / Latitude (WGS 84)". Можно использовать другую геодезическую проекцию.
В таблице VIEWPOINT используется проекция 262148 "Non-Earth (meters)", её тоже можно изменить.
Представление может быть таким:

CREATE OR REPLACE VIEW VVIEWPOINT (ID, ANGLE, GEOLOC,
CONSTRAINT VVIEWPOINT_PK PRIMARY KEY (ID) RELY DISABLE NOVALIDATE) AS 
SELECT ID, GETANGLE(GEOLOC) AS ANGLE, GEOLOC FROM VIEWPOINT;
И для изменения координат и азимута можно создать триггер

CREATE OR REPLACE TRIGGER VVIEWPOINT
  INSTEAD OF INSERT OR UPDATE OR DELETE ON VVIEWPOINT 
  FOR EACH ROW 
BEGIN
  IF INSERTING THEN
    INSERT INTO VIEWPOINT (ID, GEOLOC) VALUES (:NEW.ID, SETANGLE(:NEW.GEOLOC, :NEW.ANGLE));
  ELSIF UPDATING THEN
    UPDATE VIEWPOINT SET GEOLOC=SETANGLE(:NEW.GEOLOC, :NEW.ANGLE) WHERE ID=:OLD.ID;
  ELSIF DELETING THEN
    DELETE FROM VIEWPOINT WHERE ID=:OLD.ID;
  END IF;
END;
/