Sunday, November 15, 2009

Add a Point in a Line String

程式碼來源: Pro Oracle Spatial for Oracle Database 11g
我覺得這段程式, 會在計算路徑時會用到, 先表列在這參考
因為程式的需要, 加入了只輸入單點與輸入2點的情況
單點, 就只回傳單點; 2個單點, 就組成一條線.

所以程式變成:

create or replace FUNCTION add_to_line(
geom SDO_GEOMETRY,
point SDO_GEOMETRY,
point_number NUMBER DEFAULT 0
) RETURN SDO_GEOMETRY
IS
g SDO_GEOMETRY; -- Updated geometry
d NUMBER; -- Number of dimensions in line geometry
t NUMBER; -- Geometry Type
p NUMBER; -- Insertion point into ordinates array
i NUMBER;
BEGIN
-- Check If geom is NULL then means this is the first point return point
IF geom IS NULL THEN
RETURN point;
END IF;
-- If geom is a point then construct and return a line
IF geom.SDO_GTYPE = 2001 THEN
g := SDO_GEOMETRY(2002, 8307,NULL,
SDO_ELEM_INFO_ARRAY(1, 2,1),
SDO_ORDINATE_ARRAY(
geom.SDO_POINT.X, geom.SDO_POINT.Y, point.SDO_POINT.X, point.SDO_POINT.Y
));
RETURN g;
END IF;
-- Get the number of dimensions from the gtype
d := SUBSTR(geom.SDO_GTYPE, 1, 1);

-- Get index in ordinates array
-- If 0, then we want the lst point
IF point_number = 0 THEN
p := geom.SDO_ORDINATES.COUNT() + 1;
ELSE
p := (point_number - 1) * d + 1;
END IF;

-- Verify that the insertion point exists
IF point_number <>0 THEN
IF p > geom.SDO_ORDINATES.LAST()
OR p < geom.SDO_ORDINATES.FIRST() THEN
RAISE_APPLICATION_ERROR(-20000, 'Invalid insertion point');
END IF;
END IF;

-- Initialize output line with input line
g := geom;

-- Step 1: Extend the ordinates array
g.SDO_ORDINATES.EXTEND(d);

-- Step 2: Shift the ordinates "down"
FOR i IN REVERSE p..g.SDO_ORDINATES.COUNT() - d LOOP
g.SDO_ORDINATES(i+d) := g.SDO_ORDINATES(i);
END LOOP;

-- Step 3: Store the new point
g.SDO_ORDINATES(p) := point.SDO_POINT.X;
g.SDO_ORDINATES(p+1) := point.SDO_POINT.Y;
IF d = 3 THEN
g.SDO_ORDINATES(p+2) := point.SDO_POINT.Z;
END IF;

-- Return the new Line String
RETURN g;
END;
/

Monday, November 09, 2009

搬移資料

car_trace_log是用來顯示"即時"車輛所在位置的table, 為了必免它長的太大, 而影響performance, 所以必須定時清理, 將資料搬至car_trace_log_his. car_trace_log與car_trace_log_his暫定長的一模一樣(因為偷懶, 不想先想).
1. 寫一支搬資料的stored procedure
2. 設定它每30分鐘跑起來搬資料(利用DBMS_SCHEDULER)

Stored Procedure
create or replace
PROCEDURE MOVE_CAR_TRACE_LOG_TO_HISTORY AS
cut_date DATE := sysdate -(1/48);
BEGIN
insert into car_trace_log_his(car_no, create_date, latitude, longitude, location, flag)
select car_no, create_date, Y, X, location, flag
from car_trace_log
where create_date <>
delete car_trace_log where create_date <>
commit;
END MOVE_CAR_TRACE_LOG_TO_HISTORY;

Run scheduler:

BEGIN
DBMS_SCHEDULER.CREATE_JOB(job_name =>'SP_Move_car_log_data',
job_type =>'STORED_PROCEDURE',
job_action =>'MOVE_CAR_TRACE_LOG_TO_HISTORY',
start_date => SYSTIMESTAMP,
repeat_interval =>'FREQ=MINUTELY; INTERVAL=30',
enabled=>TRUE);
END;

Sunday, November 08, 2009

Before INSERT OR INSERT Trigger

Trigger的名字叫做 SET_FLAG, 本來想先寫一個函數來檢查是不是在高速公路上. 查書後發現 SDO_GEOM.SDO_RELATE 配合 mask ANYINTERACT 就可以傳回TRUE or FALSE 然後直接改寫 FLAG 欄位

Trigger coses list:

create or replace
TRIGGER SET_FLAG
BEFORE INSERT OR UPDATE OF LOCATION ON CAR_TRACE_LOG
REFERENCING OLD AS old NEW AS new
FOR EACH ROW
BEGIN
select SDO_GEOM.RELATE(:NEW.location,'ANYINTERACT', H.WGS84, 0.005) into :NEW.FLAG from tw_highway H where H.AROAD_ID=999999;
END;

顯示車子在不在高速公路上

之前所做的東西 後補 如果有空的話 ^.^

老師要求要能在地圖上以不同顏色標示車子是否在高速公路上.

目前, 前端的顯示是直接讀取資料庫的表格, 要能夠顯示, 則需要在表格上直接有註記.

所以在輸入車子的位置時就需要立刻對其位置進行運算, 以判別該車的位置是否位於高速公路上.
方式有三種:

1. 在車機上植入高速公路的資料, 然後直接進行判斷, 然後對回傳的位置註明是否在高速公路上. 但是目前的車機沒有高速公路的資料, 而且需要大量改寫程式. =.=|||
2. 送回資料庫判斷, 每次都要問, 會增加車機與主機的通訊次數, 還要克服通訊失敗的問題.
3. 由資料庫自行判斷.

在不影響車機的效能, 不需要每次都向Server問自己是否已在高速公路上, 所以打算以table trigger方式, 直接於資料庫上進行判別. 這樣一來, 車機的程式不需要更動, 但是會增加資料庫的負載. 目前當然沒什麼工作量, 但是要考慮如何可以讓對資料庫的效能影響最小.

想法:
1. 建立一個On_Highway(point SDO_GEOMETRY) 的 function return "TRUE" or NULL
2. Create an Insert/Update trigger , call On_Highway function and update table Flag(column)