-- tg_pair
DROP TYPE IF EXISTS tg_pair CASCADE;
CREATE TYPE tg_pair AS ( -- timestamp-geometry pair type
t timestamp,
g geometry
);
--trajectory
DROP TYPE IF EXISTS trajectory CASCADE;
CREATE TYPE trajectory AS (
s_time TIMESTAMP,
e_time TIMESTAMP,
geom_type TEXT,
bbox GEOMETRY,
sampling_interval INTERVAL,
tr_data tg_pair[]);
-- _trajectory
DROP FUNCTION IF EXISTS _trajectory(tg_pair[]) CASCADE;
CREATE OR REPLACE FUNCTION _trajectory(tg_pair[]) RETURNS trajectory AS
$BODY$
DECLARE
t trajectory;
BEGIN
t.geom_type = tg_type($1);
IF t.geom_type = 'Invalid' THEN
RAISE EXCEPTION 'Mixed geometry type is not allowed';
--RETURN t;
END IF;
t.bbox = tg_mbr($1);
t.e_time = tg_end_time($1);
t.s_time = tg_start_time($1);
t.tr_data = array_sort($1);
IF array_length($1, 1) > 1 THEN
t.sampling_interval = (t.e_time - t.s_time) / (array_length($1, 1) - 1);
ELSE
t.sampling_interval = INTERVAL '-1 seconds';
END IF;
RETURN t;
END
$BODY$
LANGUAGE 'plpgsql';
--_trajectory_2
DROP FUNCTION IF EXISTS _trajectory_2(tg_pair[]) CASCADE;
CREATE OR REPLACE FUNCTION _trajectory_2(tg_pair[]) RETURNS VOID AS
$BODY$
DECLARE
geom_type TEXT;
s_time TIMESTAMP;
e_time TIMESTAMP;
tr_data tg_pair[];
sampling_interval INTERVAL;
bbox GEOMETRY;
BEGIN
geom_type = tg_type($1);
IF geom_type = 'Invalid'THEN
RAISE EXCEPTION 'Mixed geometry type is not allowed';
--RETURN t;
END IF;
bbox = tg_mbr($1);
e_time = tg_end_time($1);
s_time = tg_start_time($1);
tr_data = array_sort($1);
--sampling_interval = INTERVAL '-1 seconds'; --get_sampling_interval(t);
--RAISE NOTICE '%', tableName;
sampling_interval = (e_time - s_time) / (array_length($1, 1) - 1); --INTERVAL '-1 seconds'; --get_sampling_interval(t);
INSERT INTO trajectory_table (s_time,e_time,geom_type,bbox,sampling_interval,tr_data) VALUES (s_time, e_time, geom_type, bbox, sampling_interval, tr_data);
END
$BODY$
LANGUAGE 'plpgsql';
--t_add_head
DROP FUNCTION IF EXISTS t_add_head( tg_pair, trajectory );
CREATE OR REPLACE FUNCTION t_add_head(tgp tg_pair, tr trajectory)
RETURNS trajectory AS
$BODY$
DECLARE
j INTEGER;
len INTEGER;
new_tr_data tg_pair[] := '{}';
BEGIN
new_tr_data[1] = tgp;
len = ARRAY_LENGTH(tr.tr_data, 1);
--don't add at head if time of tg pair is after the first tg pair of trajectory
IF tgp.t > tr.tr_data[1].t THEN
RAISE EXCEPTION 'New tg pair must precede the first tg pair';
END IF;
--check whether same geometry
IF tr.geom_type <> getTrajectoryType(new_tr_data) THEN
RAISE EXCEPTION 'Different type of geometry can not be added at head';
END IF;
--If sampling interval of input trajectory = -1, that means that trajectory has only 1 tg pair. We can add our new tg pair in the head.
--Sampling interval of the input trajectory must be equal to the timestamp differene of new tg pair and 1st tg pair of tr_data
IF tr.sampling_interval <> INTERVAL '-1 seconds' AND tr.sampling_interval <> (tr.tr_data[1].t - new_tr_data[1].t) THEN
RAISE EXCEPTION 'Sampling interval of the input trajectory must be equal to the timestamp differene of new tg pair and
1st tg pair of the input trajectory';
END IF;
FOR j in 2..len+1 LOOP
new_tr_data[j] = tr.tr_data[j-1];
END LOOP;
RETURN _trajectory(new_tr_data);
END
$BODY$
LANGUAGE 'plpgsql';
-- t_drop_tail
DROP FUNCTION IF EXISTS t_drop_tail( trajectory );
CREATE OR REPLACE FUNCTION t_drop_tail(tr trajectory)
RETURNS trajectory AS
$BODY$
DECLARE
j INT;
len INT;
new_tr_data tg_pair[] := '{}';
BEGIN
len = ARRAY_LENGTH(tr.tr_data, 1);
IF len <= 1 THEN
RETURN _trajectory(new_tr_data);
END IF;
FOR j IN 1..len-1 LOOP
new_tr_data[j] = tr.tr_data[j];
END LOOP;
RETURN _trajectory(new_tr_data);
END
$BODY$
LANGUAGE 'plpgsql';
-- t_add_tail
DROP FUNCTION IF EXISTS t_add_tail( tg_pair, trajectory );
CREATE OR REPLACE FUNCTION t_add_tail(tgp tg_pair, tr trajectory)
RETURNS trajectory AS
$BODY$
DECLARE
j INTEGER;
len INTEGER;
temp_tr_data tg_pair[] := '{}';
BEGIN
len = ARRAY_LENGTH(tr.tr_data, 1);
--check if new tg pair appears before the last tg pair
IF tgp.t < tr.tr_data[len].t THEN
RAISE EXCEPTION 'New tg pair must follow the last tg pair';
END IF;
temp_tr_data[1] = tgp;
--check whether same geometry
IF tr.geom_type <> getTrajectoryType(temp_tr_data) THEN
RAISE EXCEPTION 'Different type of geometry can not be added at head';
END IF;
--If sampling interval of input trajectory = -1, that means that trajectory has only 1 tg pair. We can add our new tg pair in the head.
--Sampling interval of the input trajectory must be equal to the timestamp differene of new tg pair and 1st tg pair of tr_data
IF tr.sampling_interval <> INTERVAL '-1 seconds' AND tr.sampling_interval <> (tgp.t - tr.tr_data[len].t) THEN
RAISE EXCEPTION 'Sampling interval of the input trajectory must be equal
to the timestamp difference of last tg pair of the input trajectory and the new tg pair';
END IF;
tr.tr_data[len+1] = tgp;
RETURN _trajectory(tr.tr_data);
END
$BODY$
LANGUAGE 'plpgsql';
--t_drop_head
DROP FUNCTION IF EXISTS t_drop_head( trajectory );
CREATE OR REPLACE FUNCTION t_drop_head(tr trajectory)
RETURNS trajectory AS
$BODY$
DECLARE
new_tr_data tg_pair[] := '{}';
newIndex int;
tg tg_pair;
isFirst BOOL;
BEGIN
newIndex = 1;
if coalesce(array_length((tr.tr_data), 1), 0) <= 1 THEN
RETURN _trajectory(new_tr_data);
END IF;
isFirst = true;
FOREACH tg IN ARRAY tr.tr_data LOOP
IF NOT isFirst THEN
--new_tr_data := array_append(new_tr_data, tg);
new_tr_data[newIndex] = tg;
newIndex = newIndex + 1;
END IF;
isFirst = false;
END LOOP;
RETURN _trajectory(new_tr_data);
END
$BODY$
LANGUAGE 'plpgsql';DROP FUNCTION IF EXISTS t_update_geom_at( timestamp WITH TIME ZONE, GEOMETRY, trajectory );
CREATE OR REPLACE FUNCTION t_update_geom_at(ti timestamp WITH TIME ZONE, ge GEOMETRY, tr trajectory)
RETURNS trajectory AS
$BODY$
DECLARE
j INTEGER;
flag BOOLEAN;
len INTEGER;
temp_tgp tg_pair;
temp_tr_data tg_pair[] := '{}';
BEGIN
--temp_tr_data[1].t = ti;
--temp_tr_data[1].g =ge;
temp_tgp.t = ti;
temp_tgp.g = ge;
temp_tr_data[1] = temp_tgp;
--check whether same geometry
IF tr.geom_type <> getTrajectoryType(temp_tr_data) THEN
RAISE EXCEPTION 'Different type of geometry is not allowed';
END IF;
len = ARRAY_LENGTH(tr.tr_data, 1);
flag = FALSE;
FOR j IN 1..len LOOP
IF tr.tr_data[j].t = ti THEN
tr.tr_data[j] = temp_tgp;
flag = TRUE;
EXIT;
END IF;
END LOOP;
IF flag = FALSE THEN
RAISE EXCEPTION 'Valid Timestamp must be given for modifying trajectory';
END IF;
RETURN _trajectory(tr.tr_data);
END
$BODY$
LANGUAGE 'plpgsql';
DROP FUNCTION IF EXISTS tg_edit_distance_recursive( trajectory, trajectory, NUMERIC, BOOL );
CREATE OR REPLACE FUNCTION tg_edit_distance_recursive(tr1 trajectory, tr2 trajectory, e NUMERIC, zNormilized BOOL)
RETURNS FLOAT AS
$BODY$
DECLARE
BEGIN
IF tr1.geom_type != tr2.geom_type THEN
RETURN -1;
END IF;
if zNormilized THEN
--Add z Normilized version here
RETURN edit_distance_helper(tr1, tr2, e);
END IF;
RETURN edit_distance_helper(tr1, tr2, e);
END
$BODY$
LANGUAGE 'plpgsql';
-- edit_distance_helper
DROP FUNCTION IF EXISTS edit_distance_helper( trajectory, trajectory, NUMERIC );
CREATE OR REPLACE FUNCTION edit_distance_helper(tr1 trajectory, tr2 trajectory, e NUMERIC)
RETURNS FLOAT AS
$BODY$
DECLARE
geom1 GEOMETRY;
geom2 GEOMETRY;
distance FLOAT;
subcost INT;
tgp1 tg_pair;
m INT;
n INT;
BEGIN
m := findTimeLength(tr1);
n := findTimeLength(tr2);
IF m = 0 THEN
RETURN n;
END IF;
IF n = 0 THEN
RETURN m;
END IF;
geom1 = (head(tr1.tr_data)).g;
geom2 = (head(tr2.tr_data)).g;
--RAISE NOTICE 'distance %', distance;
subcost = 1;
if tg_ed_match(geom1, geom2, e) THEN
subcost = 0;
END IF;
RETURN LEAST(LEAST(edit_distance_helper(drop_head(tr1),drop_head(tr2), e) + subcost, edit_distance_helper(drop_head(tr1),tr2, e) + 1), edit_distance_helper(tr1,drop_head(tr2), e) + 1);
END
$BODY$
LANGUAGE 'plpgsql';
-- t_edit_distance
--SELECT tg_edit_distance(t1.tr, t2.tr, '1'::NUMERIC, TRUE), (t1.tr).geom_type, (t2.tr).geom_type from trajectory_table t1, trajectory_table t2 LIMIT 1;
--select findtimelength(t1.tr) from trajectory_table t1;
DROP FUNCTION IF EXISTS t_edit_distance( trajectory, trajectory, NUMERIC );
CREATE OR REPLACE FUNCTION t_edit_distance(tr1 trajectory, tr2 trajectory, e NUMERIC)
RETURNS FLOAT AS
$BODY$
DECLARE
D int[][];
v int;
m INT;
n INT;
geom1 GEOMETRY;
geom2 GEOMETRY;
subcost INT;
te TEXT;
BEGIN
IF tr1.geom_type != tr2.geom_type THEN
RETURN -1;
END IF;
m := t_length(tr1);
n := t_length(tr2);
--RAISE NOTICE 'i: %', m;
D := array_fill(0, ARRAY[m, n]);
FOR i IN 2..m LOOP
D[i][1] := n;
END LOOP;
FOR j IN 2..n LOOP
D[1][j] := m;
END LOOP;
FOR i IN 2..m LOOP
FOR j IN 2..n LOOP
geom1 = (tr1.tr_data)[i].g;
geom2 = (tr2.tr_data)[j].g;
subcost = 1;
if edit_match(geom1, geom2, e) THEN
subcost = 0;
END IF;
D[i][j] := LEAST(LEAST(D[i-1][j-1] + subcost, D[i-1][j] + 1), D[i][j-1] + 1);
--RAISE NOTICE 'i: %, j: %, D %', i, j, D[i][j];
END LOOP;
END LOOP;
--FOR i IN 1..m LOOP
--te := '';
--FOR j IN 1..n LOOP
-- te := te || ' ' || d[i][j];
--END LOOP;
--RAISE NOTICE '%', te;
--END LOOP;
RETURN D[m][n];
END
$BODY$
LANGUAGE 'plpgsql';
-- edit_polygon_distance
DROP FUNCTION IF EXISTS edit_polygon_distance( GEOMETRY, GEOMETRY );
CREATE OR REPLACE FUNCTION edit_polygon_distance(g1 GEOMETRY, g2 GEOMETRY)
RETURNS FLOAT AS
$BODY$
DECLARE
area_of_convexhull FLOAT;
area_of_union FLOAT;
convex_hull geometry;
BEGIN
g1 := st_convexhull(g1);
g2 := st_convexhull(g2);
area_of_union := st_area(st_union(g1, g2));
convex_hull := st_collect(g1, convex_hull);
convex_hull := st_collect(g2, convex_hull);
area_of_convexhull := st_area(st_convexhull(convex_hull));
--RAISE NOTICE 'union: % convex: %', area_of_union, area_of_convexhull;
RETURN (1 - (area_of_union/area_of_convexhull));
END
$BODY$
LANGUAGE 'plpgsql';
-- edit_match
DROP FUNCTION IF EXISTS edit_match( GEOMETRY, GEOMETRY, NUMERIC );
CREATE OR REPLACE FUNCTION edit_match(g1 GEOMETRY, g2 GEOMETRY, e NUMERIC)
RETURNS BOOL AS
$BODY$
DECLARE
BEGIN
IF ST_GeometryType(g1) = 'ST_Point' THEN
IF edit_point_distance(g1, g2) < e THEN
RETURN TRUE;
ELSE
RETURN FALSE;
END IF;
ELSE
IF edit_polygon_distance(g1, g2) < e THEN
RETURN TRUE;
ELSE
RETURN FALSE;
END IF;
END IF;
RETURN FALSE;
END
$BODY$
LANGUAGE 'plpgsql';
-- edit_point_distance
DROP FUNCTION IF EXISTS edit_point_distance( GEOMETRY, GEOMETRY );
CREATE OR REPLACE FUNCTION edit_point_distance(p1 GEOMETRY, p2 GEOMETRY)
RETURNS FLOAT AS
$BODY$
DECLARE
x1 FLOAT;
x2 FLOAT;
y1 FLOAT;
y2 FLOAT;
BEGIN
x1 = ST_X(p1);
x2 = ST_X(p2);
y1 = ST_Y(p1);
y2 = ST_Y(p2);
RETURN |/((x1 - x2)^2.0 + (y1 - y2)^2.0);
END
$BODY$
LANGUAGE 'plpgsql';
-- zNormalize
DROP FUNCTION IF EXISTS zNormalize(tg_pair[], INTEGER, INTEGER);
CREATE OR REPLACE FUNCTION zNormalize(tr_data tg_pair[], beginIndex INTEGER, endIndex INTEGER) RETURNS Geometry[] AS
$BODY$
DECLARE
j INTEGER;
count_of_geoms INTEGER;
sum_of_vals_x FLOAT;
sum_of_vals_y FLOAT;
x_vals FLOAT[];
y_vals FLOAT[];
mean_x FLOAT;
mean_y FLOAT;
variance_x FLOAT;
variance_y FLOAT;
sd_x FLOAT;
sd_y FLOAT;
z_x FLOAT;
z_y FLOAT;
temp_tr_geom Geometry;
new_point Geometry;
z_normalized_geom_array Geometry[];
BEGIN
--read values
count_of_geoms = 0;
FOR j IN beginIndex..endIndex LOOP
count_of_geoms = count_of_geoms + 1;
temp_tr_geom = tr_data[j].g;
x_vals[count_of_geoms] = ST_X(temp_tr_geom);
y_vals[count_of_geoms] = ST_Y(temp_tr_geom);
--RAISE NOTICE 'x % -->',count_of_geoms;
--RAISE NOTICE '%', x_vals[count_of_geoms];
--RAISE NOTICE 'y % -->',count_of_geoms;
--RAISE NOTICE '%', y_vals[count_of_geoms];
END LOOP;
--get mean_x and mean_y
sum_of_vals_x = 0;
sum_of_vals_y = 0;
FOR j IN 1..count_of_geoms LOOP
sum_of_vals_x = sum_of_vals_x + x_vals[j];
sum_of_vals_y = sum_of_vals_y + y_vals[j];
END LOOP;
mean_x = sum_of_vals_x/count_of_geoms;
mean_y = sum_of_vals_y/count_of_geoms;
--RAISE NOTICE 'mean_x: %', mean_x;
--RAISE NOTICE 'mean_y: %', mean_y;
--get sd_x and sd_y
sum_of_vals_x = 0;
sum_of_vals_y = 0;
FOR j IN 1..count_of_geoms LOOP
sum_of_vals_x = sum_of_vals_x + (x_vals[j] - mean_x)^2.0;
sum_of_vals_y = sum_of_vals_y + (y_vals[j] - mean_y)^2.0;
END LOOP;
variance_x = sum_of_vals_x/count_of_geoms;
variance_y = sum_of_vals_y/count_of_geoms;
sd_x = |/variance_x;
sd_y = |/variance_y;
--RAISE NOTICE 'sd_x: %', sd_x;
--RAISE NOTICE 'sd_y: %', sd_y;
--z normalize
FOR j IN 1..count_of_geoms LOOP
z_x = (x_vals[j] - mean_x)/sd_x;
z_y = (y_vals[j] - mean_y)/sd_y;
new_point = ST_MakePoint(z_x::double precision, z_y::double precision);
z_normalized_geom_array[j] = new_point;
--RAISE NOTICE ' zx --> %', ST_X(z_nomalized_geom_array[j]);
--RAISE NOTICE ' zy --> %', ST_Y(z_nomalized_geom_array[j]);
END LOOP;
-- intensional mistake
--RAISE NOTICE '%',ST_AsText(z_normalized_geom_array[j]);
RETURN z_normalized_geom_array;
END
$BODY$
LANGUAGE 'plpgsql' ;
-- t_euclidean_distance
DROP FUNCTION IF EXISTS t_euclidean_distance(trajectory, trajectory);
CREATE OR REPLACE FUNCTION t_euclidean_distance(tr1 trajectory, tr2 trajectory) RETURNS FLOAT AS
$BODY$
DECLARE
len1 INTEGER;
len2 INTEGER;
z1 Geometry[];
z2 Geometry[];
j INTEGER;
k INTEGER;
sum_distance FLOAT;
point_1 Geometry;
point_2 Geometry;
point_1_x FLOAT;
point_1_y FLOAT;
point_2_x FLOAT;
point_2_y FLOAT;
sliding_boundary INTEGER;
Euclidean_distance FLOAT;
zQ Geometry[];
zS Geometry[];
min_ED FLOAT;
temp_tgp tg_pair;
BEGIN
IF tr1.geom_type = 'Point' AND tr2.geom_type = 'Point' THEN
len1 = findTimeLength(tr1);
len2 = findTimeLength(tr2);
IF len1 = len2 THEN
--no sliding required; both trajectory need to be z normalized in full length
z1 = zNormalize(tr1.tr_data, 1, len1);
z2 = zNormalize(tr2.tr_data, 1, len2);
sum_distance = 0;
FOR j IN 1..len1 LOOP
--read both points from z1 and z2
point_1 = z1[j];
point_1_x = ST_X(point_1);
point_1_y = ST_Y(point_1);
point_2 = z2[j];
point_2_x = ST_X(point_2);
point_2_y = ST_Y(point_2);
sum_distance = sum_distance + (point_1_x - point_2_x)^2.0 + (point_1_y - point_2_y)^2.0;
END LOOP;
Euclidean_distance = |/sum_distance;
RETURN Euclidean_distance;
ELSIF len1 < len2 THEN
--tr1 is the query and tr2 is the series. We have to slide tr1 along tr2. Get EDs at each setting. Finally get the minimum ED.
zQ = zNormalize(tr1.tr_data, 1, len1);
min_ED = 1000000; -- infinity
sliding_boundary = len2 - len1 + 1;
FOR j IN 1..sliding_boundary LOOP
zS = zNormalize(tr2.tr_data, j, j+len1-1);
sum_distance = 0;
FOR k IN 1..len1 LOOP
point_1 = zQ[k];
point_1_x = ST_X(point_1);
point_1_y = ST_Y(point_1);
point_2 = zS[k];
point_2_x = ST_X(point_2);
point_2_y = ST_Y(point_2);
sum_distance = sum_distance + (point_1_x - point_2_x)^2.0 + (point_1_y - point_2_y)^2.0;
END LOOP;
Euclidean_distance = |/sum_distance;
--RAISE NOTICE 'current --> %', Euclidean_distance;
--PERFORM pg_sleep(5);
IF Euclidean_distance < min_ED THEN
min_ED = Euclidean_distance;
END IF;
END LOOP;
RETURN min_ED;
ELSIF len2 < len1 THEN
--tr2 is the query and tr1 is the series. We have to slide tr2 along tr1. Get EDs at each setting. Finally get the minimum ED.
zQ = zNormalize(tr2.tr_data, 1, len2);
min_ED = 1000000; -- infinity
sliding_boundary = len1 - len2 + 1;
FOR j IN 1..sliding_boundary LOOP
zS = zNormalize(tr1.tr_data, j, j+len2-1);
sum_distance = 0;
FOR k IN 1..len2 LOOP
point_1 = zQ[k];
point_1_x = ST_X(point_1);
point_1_y = ST_Y(point_1);
point_2 = zS[k];
point_2_x = ST_X(point_2);
point_2_y = ST_Y(point_2);
sum_distance = sum_distance + (point_1_x - point_2_x)^2.0 + (point_1_y - point_2_y)^2.0;
END LOOP;
Euclidean_distance = |/sum_distance;
--RAISE NOTICE 'current --> %', Euclidean_distance;
--PERFORM pg_sleep(5);
IF Euclidean_distance < min_ED THEN
min_ED = Euclidean_distance;
END IF;
END LOOP;
RETURN min_ED;
END IF; --len compare
ELSIF tr1.geom_type = 'Polygon' AND tr2.geom_type = 'Polygon' THEN
--Transforming both region trajectories into point trajectories by taking centroid of each polygon
len1 = ARRAY_LENGTH(tr1.tr_data, 1);
FOR j IN 1..len1 LOOP
temp_tgp.t = tr1.tr_data[j].t;
temp_tgp.g = ST_Centroid(tr1.tr_data[j].g);
tr1.tr_data[j] = temp_tgp;
END LOOP;
tr1 = _trajectory(tr1.tr_data);
len2 = ARRAY_LENGTH(tr2.tr_data, 1);
FOR j IN 1..len2 LOOP
temp_tgp.t = tr2.tr_data[j].t;
temp_tgp.g = ST_Centroid(tr2.tr_data[j].g);
tr2.tr_data[j] = temp_tgp;
END LOOP;
tr2 = _trajectory(tr2.tr_data);
RETURN EuclideanDistance(tr1, tr2);
ELSE
RAISE EXCEPTION 'Both trajectories must be of same type';
END IF;
END
$BODY$
LANGUAGE 'plpgsql';
-- t_record_at_interpolated
DROP FUNCTION IF EXISTS t_record_at_interpolated( trajectory, TIMESTAMP);
CREATE OR REPLACE FUNCTION t_record_at_interpolated(tr trajectory, t TIMESTAMP)
RETURNS GEOMETRY AS
$BODY$
DECLARE
tgp tg_pair;
geom1 GEOMETRY;
geom2 GEOMETRY;
BEGIN
if tr.s_time < t AND tr.e_time > t THEN
RETURN -1;
END IF;
FOREACH tgp IN ARRAY tr.tr_data
LOOP
IF tgp.t < t THEN
geom2 := tgp.t;
RETURN st_interpolatepoint(st_makeline(geom1, geom2), 0.5);
END IF;
geom1 := tgp.g;
END LOOP;
RETURN NULL;
END
$BODY$
LANGUAGE 'plpgsql';
-- t_m_distance
DROP FUNCTION IF EXISTS t_m_distance( trajectory, trajectory);
CREATE OR REPLACE FUNCTION t_m_distance(tr1 trajectory, tr2 trajectory)
RETURNS FLOAT[] AS
$BODY$
DECLARE
result FLOAT[] = '{}';
g GEOMETRY;
tgp tg_pair;
BEGIN
IF tr1.geom_type != st_geometrytype(st_makepoint(0,0)) OR tr2.geom_type != st_geometrytype(st_makepoint(0,0)) THEN
RETURN result;
END IF;
FOREACH tgp IN ARRAY tr1.tr_data
LOOP
g := t_record_at(tr1, tgp.t);
IF g IS NULL
THEN
result := result || tg_point_distance(tgp.g, g);
END IF;
END LOOP;
RETURN result;
END
$BODY$
LANGUAGE 'plpgsql';
-- t_distance
DROP FUNCTION IF EXISTS t_distance( trajectory );
CREATE OR REPLACE FUNCTION t_distance(tr trajectory)
RETURNS FLOAT AS
$BODY$
DECLARE
length FLOAT;
tgp tg_pair;
prev tg_pair;
BEGIN
if tr ISNULL OR tr.geom_type != st_geometrytype(st_makepoint(0,0)) OR tr.tr_data ISNULL THEN
RETURN -1;
END IF;
length = 0;
prev := head(tr.tr_data);
FOREACH tgp IN ARRAY tr.tr_data
LOOP
length = length + tg_point_distance(prev, tgp);
END LOOP;
RETURN length;
END
$BODY$
LANGUAGE 'plpgsql';
-- t_sampling_interval
DROP FUNCTION IF EXISTS t_sampling_interval( trajectory );
CREATE OR REPLACE FUNCTION t_sampling_interval(tr trajectory)
RETURNS INTERVAL AS
$BODY$
DECLARE
stepSize INTERVAL;
BEGIN
IF array_length(tr.tr_data, 1) > 1 THEN
stepSize = (tr.e_time - tr.s_time) / (array_length(tr.tr_data, 1) - 1);
ELSE
stepSize = INTERVAL '-1 seconds';
END IF;
RETURN stepSize;
END
$BODY$
LANGUAGE 'plpgsql';
-- tg_start_time
DROP FUNCTION IF EXISTS tg_start_time(tg_pair[]);
CREATE OR REPLACE FUNCTION tg_start_time(tr_data tg_pair[]) RETURNS TIMESTAMP AS
$BODY$
DECLARE
tgp tg_pair;
startTime TIMESTAMP;
BEGIN
IF tr_data ISNULL THEN
startTime := to_timestamp(-1)::TIMESTAMP;
RETURN startTime;
END IF;
--RAISE NOTICE 'my timestamp --> %', tgpairs[1].t;
startTime = tr_data[1].t;
FOREACH tgp IN ARRAY tr_data
LOOP
--RAISE NOTICE 'loop timestamp --> %', tgp.t;
IF startTime > tgp.t THEN
startTime = tgp.t;
END IF;
END LOOP;
RETURN startTime;
END
$BODY$
LANGUAGE 'plpgsql' ;
-- tg_mbr
DROP FUNCTION IF EXISTS tg_mbr(tg_pair[]) CASCADE;
--findMBR:: finds mbr of a particular trajectory specified by the tr_id
--@param tr_id:: an integer specifying the trajectory identifier
CREATE OR REPLACE FUNCTION tg_mbr(tr_data tg_pair[]) RETURNS GEOMETRY AS
$BODY$
DECLARE
tgp tg_pair;
mbr GEOMETRY;
BEGIN
IF tr_data ISNULL THEN
return mbr;
END IF;
FOREACH tgp IN ARRAY tr_data
LOOP
--RAISE NOTICE '%', tgp.t;
--RAISE NOTICE '%', ST_astext(tgp.g);
mbr := st_collect(tgp.g, mbr);
--RAISE NOTICE 'collection==> %', ST_astext(mbr);
END LOOP;
mbr := st_envelope(mbr);
--RAISE NOTICE 'mbr %', ST_astext(mbr);
RETURN mbr;
END
$BODY$
LANGUAGE 'plpgsql' ;
-- tg_end_time
DROP FUNCTION IF EXISTS tg_end_time(tg_pair[]);
CREATE OR REPLACE FUNCTION tg_end_time(tr_data tg_pair[]) RETURNS timestamp AS
$BODY$
DECLARE
tgp tg_pair;
endTime timestamp;
BEGIN
IF tr_data ISNULL THEN
endTime := to_timestamp(-1)::TIMESTAMP;
RETURN endTime;
END IF;
--RAISE NOTICE 'my timestamp --> %', tgpairs[1].t;
endTime := tr_data[1].t;
FOREACH tgp IN ARRAY tr_data
LOOP
--RAISE NOTICE 'loop timestamp --> %', tgp.t;
IF endTime < tgp.t THEN
endTime := tgp.t;
END IF;
END LOOP;
RETURN endTime;
END
$BODY$
LANGUAGE 'plpgsql' ;
-- tg_type
DROP FUNCTION IF EXISTS tg_type(tg_pair[]);
CREATE OR REPLACE FUNCTION tg_type(tr_data tg_pair[]) RETURNS Text AS
$BODY$
DECLARE
tgp tg_pair;
--flag BOOLEAN;
type_of_first TEXT;
number_of_Different int;
BEGIN
--Simpler way to do this
--
number_of_Different := (SELECT COUNT(*) FROM (SELECT DISTINCT ST_GeometryType((unnest(tr_data)).g)) AS X);
IF number_of_Different = 1 THEN
RETURN (SELECT ST_GeometryType((unnest(tr_data)).g) LIMIT 1);
END IF;
RETURN 'Invalid';
END
$BODY$
LANGUAGE 'plpgsql';
-- t_union_area
DROP FUNCTION IF EXISTS t_union_area( trajectory, trajectory );
CREATE OR REPLACE FUNCTION t_union_area(tr1 trajectory, tr2 trajectory)
RETURNS FLOAT AS
$BODY$
DECLARE
tgp tg_pair;
tgp2 tg_pair;
g GEOMETRY;
area FLOAT := 0;
BEGIN
IF tr1 ISNULL OR tr2 ISNULL OR tr1.tr_data ISNULL OR tr2.tr_data ISNULL
THEN
RETURN 0;
END IF;
--For Jaccard calculation
FOREACH tgp IN ARRAY tr1.tr_data
LOOP
g := t_record_at(tr2, tgp.t);
IF g IS NOT NULL
THEN
area := area + st_area(ST_Union(tgp.g, g));
END IF;
IF g IS NULL
THEN
area := area + st_area(tgp.g);
END IF;
END LOOP;
FOREACH tgp IN ARRAY tr2.tr_data
LOOP
g := t_record_at(tr1, tgp.t);
IF g IS NULL
THEN
--RAISE NOTICE 'loop timestamp --> %', area;
area := area + st_area(tgp.g);
END IF;
END LOOP;
RETURN area;
END
$BODY$
LANGUAGE 'plpgsql';
-- t_intersection_area
DROP FUNCTION IF EXISTS t_intersection_area( trajectory, trajectory );
CREATE OR REPLACE FUNCTION t_intersection_area(tr1 trajectory, tr2 trajectory)
RETURNS FLOAT AS
$BODY$
DECLARE
tgp1 tg_pair;
tgp2 tg_pair;
area FLOAT;
tgpairs1 tg_pair [];
tgpairs2 tg_pair [];
endTime TIMESTAMP;
BEGIN
IF tr1 ISNULL OR tr2 ISNULL OR tr1.tr_data ISNULL OR tr2.tr_data ISNULL
THEN
RETURN 0;
END IF;
IF tr1.e_time < tr2.s_time OR tr1.s_time > tr2.e_time
THEN
RETURN 0;
END IF;
IF NOT st_intersects(tr1.bbox, tr2.bbox)
THEN
RETURN 0;
END IF;
area = 0;
FOREACH tgp1 IN ARRAY tr1.tr_data
LOOP
FOREACH tgp2 IN ARRAY tr2.tr_data
LOOP
IF tgp1.t = tgp2.t
THEN
area := area + st_area(st_intersection(tgp1.g, tgp2.g));
END IF;
END LOOP;
END LOOP;
RETURN area;
END
$BODY$
LANGUAGE 'plpgsql';
--t_omax
DROP FUNCTION IF EXISTS t_omax( trajectory, trajectory );
CREATE OR REPLACE FUNCTION t_omax(tr1 trajectory, tr2 trajectory)
RETURNS FLOAT AS
$BODY$
DECLARE
intersection_area FLOAT;
area1 FLOAT;
area2 FLOAT;
max_area FLOAT;
BEGIN
intersection_area = t_intersection_area(tr1, tr2);
IF intersection_area = 0
THEN
RETURN 0;
END IF;
area1 = t_area(tr1);
area2 = t_area(tr2);
IF area1 >= area2
THEN
max_area = area1;
ELSE
max_area = area2;
END IF;
RETURN intersection_area / max_area;
END
$BODY$
LANGUAGE 'plpgsql';
--t_ts_union_area
DROP FUNCTION IF EXISTS t_ts_union_area( trajectory, trajectory );
CREATE OR REPLACE FUNCTION t_ts_union_area(tr1 trajectory, tr2 trajectory)
RETURNS FLOAT AS
$BODY$
DECLARE
tgp1 tg_pair;
tgp2 tg_pair;
union_pairs tg_pair [];
area FLOAT := 0;
BEGIN
IF tr1 ISNULL OR tr2 ISNULL OR tr1.tr_data ISNULL OR tr2.tr_data ISNULL
THEN
RETURN area;
END IF;
IF tr1.e_time < tr2.s_time OR tr1.s_time > tr2.e_time
THEN
RETURN area;
END IF;
IF NOT st_intersects(tr1.bbox, tr2.bbox)
THEN
RETURN area;
END IF;
--For Jaccard calculation
FOREACH tgp1 IN ARRAY tr1.tr_data
LOOP
FOREACH tgp2 IN ARRAY tr2.tr_data LOOP
IF tgp1.t = tgp2.t AND ST_intersects(tgp1.g, tgp2.g)
THEN
area := area + st_area(ST_Union(tgp1.g, tgp2.g));
END IF;
END LOOP;
END LOOP;
RETURN area;
END
$BODY$
LANGUAGE 'plpgsql';
--t_jaccard_star
DROP FUNCTION IF EXISTS t_jaccard_star( trajectory, trajectory );
CREATE OR REPLACE FUNCTION t_jaccard_star(tr1 trajectory, tr2 trajectory)
RETURNS FLOAT AS
$BODY$
DECLARE
intersection_area FLOAT;
union_area FLOAT;
BEGIN
intersection_area := t_intersection_area(tr1, tr2);
IF intersection_area = 0
THEN
RETURN 0;
END IF;
union_area := t_ts_union_area(tr1, tr2);
--RAISE NOTICE 'intersection_area %', intersection_area;
--RAISE NOTICE 'union_area %', union_area;
IF union_area = 0
THEN
RETURN 0;
END IF;
RETURN intersection_area / union_area;
END
$BODY$
LANGUAGE 'plpgsql';
--t_jaccard
DROP FUNCTION IF EXISTS t_jaccard( trajectory, trajectory );
CREATE OR REPLACE FUNCTION t_jaccard(tr1 trajectory, tr2 trajectory)
RETURNS FLOAT AS
$BODY$
DECLARE
intersection_area FLOAT;
union_area FLOAT;
BEGIN
intersection_area := t_intersection_area(tr1, tr2);
IF intersection_area = 0.0
THEN
RETURN 0;
END IF;
union_area := t_union_area(tr1, tr2);
IF union_area = 0.0
THEN
RETURN 0;
END IF;
RETURN intersection_area / union_area;
END
$BODY$
LANGUAGE 'plpgsql';
--tg_head
DROP FUNCTION IF EXISTS tg_head( tg_pairs[] );
--Since array indexing mechanism in plpgsql is a mystery,
-- we shouldn't use static number to get first element
CREATE OR REPLACE FUNCTION tg_head(tg_pairs tg_pair[])
RETURNS tg_pair AS
$BODY$
DECLARE
tg tg_pair;
BEGIN
if tg_pairs ISNULL THEN
RETURN tg;
END IF;
FOREACH tg IN ARRAY tg_pairs LOOP
RETURN tg;
END LOOP;
END
$BODY$
LANGUAGE 'plpgsql';-- by Craig Ringer
--array_sort
DROP FUNCTION IF EXISTS array_sort(ANYARRAY);
CREATE OR REPLACE FUNCTION array_sort (ANYARRAY)
RETURNS ANYARRAY
AS $$
SELECT array_agg(x ORDER BY x) FROM unnest($1) x;
$$
LANGUAGE 'sql';
--t_area
DROP FUNCTION IF EXISTS t_area(trajectory);
CREATE OR REPLACE FUNCTION t_area(tr trajectory) RETURNS float AS
$BODY$
DECLARE
area float;
tgp tg_pair;
BEGIN
area = 0;
if tr ISNULL OR tr.tr_data ISNULL THEN
return 0;
END IF;
FOREACH tgp IN ARRAY tr.tr_data
LOOP
RAISE NOTICE 'loop timestamp --> %', ST_Area(tgp.g);
area = area + ST_Area(tgp.g);
END LOOP;
RETURN area;
END
$BODY$
LANGUAGE 'plpgsql' ;
--t_union
DROP FUNCTION IF EXISTS t_union( trajectory, trajectory );
CREATE OR REPLACE FUNCTION t_union(tr1 trajectory, tr2 trajectory)
RETURNS trajectory AS
$BODY$
DECLARE
tgp tg_pair;
tgp2 tg_pair;
temp_pair tg_pair;
g GEOMETRY;
union_pairs tg_pair [] = '{}';
union_tr trajectory;
BEGIN
union_tr.tr_data = union_pairs;
IF tr1 ISNULL OR tr2 ISNULL OR (tr1.tr_data ISNULL AND tr2.tr_data ISNULL)
THEN
RETURN union_tr;
END IF;
IF tr1.tr_data ISNULL
THEN
union_tr.tr_data = tr2.tr_data;
RETURN _trajectory(union_pairs);
END IF;
IF tr2.tr_data ISNULL
THEN
union_tr.tr_data = tr1.tr_data;
RETURN _trajectory(union_pairs);
END IF;
--For Jaccard calculation
FOREACH tgp IN ARRAY tr1.tr_data
LOOP
g = t_record_at(tr2, tgp.t);
IF g IS NOT NULL
THEN
--RAISE NOTICE 'loop timestamp --> %', tgp;
temp_pair.t = tgp.t;
temp_pair.g = ST_Union(tgp.g, g);
END IF;
IF g IS NULL
THEN
union_pairs := union_pairs || tgp;
END IF;
END LOOP;
FOREACH tgp IN ARRAY tr2.tr_data
LOOP
g = t_record_at(tr1, tgp.t);
IF g IS NULL
THEN
temp_pair.t = tgp.t;
temp_pair.g = tgp.g;
union_pairs := union_pairs || temp_pair;
END IF;
END LOOP;
union_tr = _trajectory(union_pairs);
RETURN union_tr;
END
$BODY$
LANGUAGE 'plpgsql';
--t_record_at
DROP FUNCTION IF EXISTS t_record_at(trajectory, TIMESTAMP);
CREATE OR REPLACE FUNCTION t_record_at(tr trajectory, t TIMESTAMP) RETURNS Geometry AS
$BODY$
DECLARE
tgp1 tg_pair;
BEGIN
--PERFORM ASSERT IS NOT NULL tr;
FOREACH tgp1 IN ARRAY tr.tr_data
LOOP
IF tgp1.t = t THEN
RETURN tgp1.g;
END IF;
END LOOP;
IF tr.geom_type = ST_GeometryType(ST_MakePoint(0,0)) THEN
RETURN tg_record_at_interpolated(tr, t);
END IF;
RETURN NULL;
END
$BODY$
LANGUAGE 'plpgsql' ;
--t_length
DROP FUNCTION IF EXISTS t_length( trajectory );
CREATE OR REPLACE FUNCTION t_length(tr trajectory)
RETURNS INTEGER AS
$BODY$
DECLARE
time_count INTEGER;
tgp tg_pair;
BEGIN
if tr.tr_data ISNULL THEN
RETURN 0;
END IF;
time_count = 0;
FOREACH tgp IN ARRAY tr.tr_data
LOOP
time_count = time_count + 1;
END LOOP;
RETURN time_count;
END
$BODY$
LANGUAGE 'plpgsql';
--t_equals
DROP FUNCTION IF EXISTS t_equals( trajectory, trajectory );
CREATE OR REPLACE FUNCTION t_equals(tr1 trajectory, tr2 trajectory)
RETURNS BOOL AS
$BODY$
DECLARE
tgp tg_pair;
g GEOMETRY;
BEGIN
IF tr1 ISNULL AND tr2 ISNULL
THEN
RETURN TRUE;
END IF;
IF tr1.tr_data ISNULL AND tr2.tr_data ISNULL
THEN
RETURN TRUE;
END IF;
IF tr1.tr_data ISNULL
THEN
RETURN FALSE;
END IF;
IF tr2.tr_data ISNULL
THEN
RETURN FALSE;
END IF;
IF t_length(tr1) <> t_length(tr2) THEN
RETURN FALSE;
END IF;
--For Jaccard calculation
FOREACH tgp IN ARRAY tr1.tr_data
LOOP
g = t_record_at(tr2, tgp.t);
IF g ISNULL AND tgp IS NOT NULL THEN
RETURN FALSE;
END IF;
IF g IS NOT NULL AND tgp ISNULL THEN
RETURN FALSE;
END IF;
IF NOT st_equals(tgp.g, g) THEN
--RAISE NOTICE '1: %, 2: %', st_astext(g), st_astext(tgp.g);
RETURN FALSE;
END IF;
END LOOP;
RETURN TRUE;
END
$BODY$
LANGUAGE 'plpgsql';
--t_duration
DROP FUNCTION IF EXISTS t_duration(trajectory);
CREATE OR REPLACE FUNCTION t_duration(tr trajectory) RETURNS INTERVAL AS
$BODY$
DECLARE
BEGIN
if tr ISNULL THEN
return '-1'::INTERVAL;
END IF;
RETURN tr.e_time - tr.s_time;
END
$BODY$
LANGUAGE 'plpgsql' ;
--t_intersection
DROP FUNCTION IF EXISTS t_intersection( trajectory, trajectory );
CREATE OR REPLACE FUNCTION t_intersection(tr1 trajectory, tr2 trajectory)
RETURNS trajectory AS
$BODY$
DECLARE
tgp1 tg_pair;
tgp2 tg_pair;
temp_pair tg_pair;
intersecting_pairs tg_pair [] = '{}';
endTime TIMESTAMP;
result trajectory;
BEGIN
result.tr_data = intersecting_pairs;
IF tr1 ISNULL OR tr2 ISNULL OR tr1.tr_data ISNULL OR tr2.tr_data ISNULL
THEN
RETURN result;
END IF;
IF tr1.e_time < tr2.s_time OR tr1.s_time > tr2.e_time
THEN
RETURN result;
END IF;
IF NOT st_intersects(tr1.bbox, tr2.bbox)
THEN
RETURN result;
END IF;
--For Jaccard calculation
--indexOfIntersection = 1;
--RAISE NOTICE 'my timestamp --> %', tgpairs[1].t;
FOREACH tgp1 IN ARRAY tr1.tr_data
LOOP
FOREACH tgp2 IN ARRAY tr2.tr_data
LOOP
IF tgp1.t = tgp2.t
THEN
temp_pair.t := tgp1.t;
temp_pair.g := st_intersection(tgp1.g, tgp2.g);
intersecting_pairs := intersecting_pairs || temp_pair;
END IF;
END LOOP;
END LOOP;
result := _trajectory(intersecting_pairs);
RETURN result;
END
$BODY$
LANGUAGE 'plpgsql';