-- SPATIAL DATABASE FOR GPS WILDLIFE TRACKING DATA, F. Urbano and F. Cagnacci (eds.)
-- DOI: 10.1007/978-3-319-03743-1_4, Springer International Publishing Switzerland 2014

-- Code presented in Chapter 12
-- Authors: Anne Berger, Holger Dettki, Ferdinando Urbano
-- Version 1.0

-- The code in this book is free. You can copy, modify and distribute non-trivial part of the code 
-- with no restrictions, according to the terms of the Creative Commons CC0 1.0 licence
-- (https://creativecommons.org/publicdomain/zero/1.0/). 
-- Nevertheless, the acknowledgement of the authorship is appreciated.

-- Note: to run this code you need the database developed in the previous chapters (2,3,4,5,6,7,8,9).

-- The test data set is available in the Extra Material page of the book (http://extras.springer.com/)


-- You create a new table to accommodate information about activity sensors
CREATE TABLE main.activity_sensors(
  activity_sensors_id integer,
  vendor character varying,
  activity_sensors_code character varying,
  model character varying,
  insert_timestamp timestamp with time zone DEFAULT now(),
  update_timestamp timestamp with time zone DEFAULT now(),
  CONSTRAINT activity_sensors_pkey
    PRIMARY KEY (activity_sensors_id),
  CONSTRAINT activity_sensors_code_unique
    UNIQUE (activity_sensors_code)
);
COMMENT ON TABLE main.activity_sensors
IS 'Catalogue of activity sensors.';

CREATE TRIGGER update_timestamp
  BEFORE UPDATE
  ON main.activity_sensors
  FOR EACH ROW
  EXECUTE PROCEDURE tools.timestamp_last_update();
  
-- You populate it
INSERT INTO main.activity_sensors (activity_sensors_id, vendor, activity_sensors_code, model)
  VALUES (1, 'Vectronic', 'ACTIVITY01508', 'Basic model');

-- You define a table to store the deployment time range of the activity sensor on an animal
CREATE TABLE main.activity_sensors_animals(
  activity_sensors_animals_id serial,
  animals_id integer NOT NULL,
  activity_sensors_id integer NOT NULL,
  start_time timestamp with time zone NOT NULL,
  end_time timestamp with time zone,
  notes character varying,
  insert_timestamp timestamp with time zone DEFAULT now(),
  update_timestamp timestamp with time zone DEFAULT now(),
  CONSTRAINT activity_sensors_animals_pkey
    PRIMARY KEY (activity_sensors_animals_id ),
  CONSTRAINT activity_sensors_animals_animals_id_fkey
    FOREIGN KEY (animals_id)
    REFERENCES main.animals (animals_id)
    MATCH SIMPLE ON UPDATE NO ACTION ON DELETE CASCADE,
  CONSTRAINT activity_sensors_animals_activity_sensors_id_fkey
    FOREIGN KEY (activity_sensors_id)
    REFERENCES main.activity_sensors (activity_sensors_id)
    MATCH SIMPLE ON UPDATE NO ACTION ON DELETE CASCADE,
  CONSTRAINT time_interval_check
    CHECK (end_time > start_time)
);
COMMENT ON TABLE main.activity_sensors_animals
IS 'Table that stores information of deployments of activity sensors on animals.';

CREATE TRIGGER update_timestamp
  BEFORE UPDATE
  ON main.activity_sensors_animals
  FOR EACH ROW
  EXECUTE PROCEDURE tools.timestamp_last_update();

-- You now populate the table
INSERT INTO main.activity_sensors_animals(animals_id, activity_sensors_id, start_time, end_time, notes)
  VALUES (3,1,'2005-10-23 20:00:53 +0','2006-10-28 13:00:00 +0','Death of animal. Sensor recovered.');

-- You create the table to host the raw data coming from the activity sensor
CREATE TABLE main.activity_data(
  activity_data_id serial NOT NULL,
  activity_sensors_code character varying,
  utc_date date,
  utc_time time without time zone,
  lmt_date date,
  lmt_time time without time zone,
  activity_x integer,
  activity_y integer,
  temp double precision,
  insert_timestamp timestamp with time zone DEFAULT now(),
  acquisition_time timestamp with time zone,
  CONSTRAINT activity_data_pkey
    PRIMARY KEY (activity_data_id ),
  CONSTRAINT activity_data_sensors_fkey
    FOREIGN KEY (activity_sensors_code)
    REFERENCES main.activity_sensors (activity_sensors_code)
    MATCH SIMPLE ON UPDATE NO ACTION ON DELETE NO ACTION
);
COMMENT ON TABLE main.activity_data
IS 'Table that stores raw data as they come from the activity sensors (plus the ID of the sensor).';

CREATE INDEX activity_acquisition_time_index
  ON main.activity_data
  USING btree (acquisition_time );
CREATE INDEX activity_sensors_code_index
  ON main.activity_data
  USING btree (activity_sensors_code);
CREATE TRIGGER update_acquisition_time
  BEFORE INSERT
  ON main.activity_data
  FOR EACH ROW
  EXECUTE PROCEDURE tools.acquisition_time_update();

-- You have to create a table to store activity data associated to animals
CREATE TABLE main.activity_data_animals(
  activity_data_animals_id serial,
  activity_sensors_id integer,
  animals_id integer,
  acquisition_time timestamp with time zone,
  activity_x integer,
  activity_y integer,
  temp double precision,
  insert_timestamp timestamp with time zone DEFAULT now(),
  CONSTRAINT activity_data_animals_pkey
    PRIMARY KEY (activity_data_animals_id),
  CONSTRAINT activity_data_animals_animals_fkey
    FOREIGN KEY (animals_id)
    REFERENCES main.animals (animals_id)
    MATCH SIMPLE ON UPDATE NO ACTION ON DELETE NO ACTION,
  CONSTRAINT activity_data_animals_sensors_fkey
    FOREIGN KEY (activity_sensors_id)
    REFERENCES main.activity_sensors (activity_sensors_id)
    MATCH SIMPLE ON UPDATE NO ACTION ON DELETE NO ACTION
);
COMMENT ON TABLE main.activity_data_animals
IS 'Table that stores activity data associated to animals.';

CREATE INDEX activity_animals_acquisition_time_index
  ON main.activity_data_animals
  USING btree (acquisition_time );
CREATE INDEX activity_animals_id_index
  ON main.activity_data_animals
  USING btree (animals_id);
  
-- You import the raw data
COPY main.activity_data(
  activity_sensors_code, utc_date, utc_time, lmt_date, lmt_time, activity_x, activity_y, temp)
FROM
  'C:\tracking_db\data\sensors_data\ACTIVITY01508.csv'
  WITH CSV HEADER DELIMITER ';';

-- You  associate activity data to the animal into the table main.activity_data_animals
INSERT INTO main.activity_data_animals (
  animals_id,
  activity_sensors_id,
  acquisition_time,
  activity_x,
  activity_y,
  temp)
SELECT
  activity_sensors_animals.animals_id,
  activity_sensors_animals.activity_sensors_id,
  activity_data.acquisition_time,
  activity_data.activity_x,
  activity_data.activity_y,
  activity_data.temp
FROM
  main.activity_sensors_animals,
  main.activity_data,
  main.activity_sensors
WHERE
  activity_data.activity_sensors_code = activity_sensors.activity_sensors_code AND
  activity_sensors.activity_sensors_id = activity_sensors_animals.activity_sensors_id AND
  activity_data.acquisition_time >= activity_sensors_animals.start_time AND
  activity_data.acquisition_time <= activity_sensors_animals.end_time;

-- You retrieve activity and GPS data
SELECT
  gps_data_animals.animals_id AS id,
  gps_data_animals.acquisition_time AS acquisition_time_gps,
  activity_data_animals.acquisition_time::time AS time_act,
  ST_X(gps_data_animals.geom)::numeric(7,5) AS gps_x,
  ST_Y(gps_data_animals.geom)::numeric(7,5) AS gps_y,
  activity_data_animals.activity_x AS act_x,
  activity_data_animals.activity_y AS act_y
FROM
  main.gps_data_animals
INNER JOIN
  main.activity_data_animals
ON
  gps_data_animals.animals_id = activity_data_animals.animals_id AND
  ((gps_data_animals.acquisition_time - activity_data_animals.acquisition_time) < interval '150 second') AND
  ((gps_data_animals.acquisition_time - activity_data_animals.acquisition_time) > interval '-150 second') AND
  gps_validity_code = 1
LIMIT 10

-- You retrieve the average and standard deviation value for activity in the different land cover types
SELECT
  label3 AS land_cover,
  avg(activity_data_animals.activity_x)::numeric(5,2) AS avg_x,
  stddev(activity_data_animals.activity_x)::numeric(5,2) AS stddev_x,
  avg(activity_data_animals.activity_y)::numeric(5,2) AS avg_y,
  stddev(activity_data_animals.activity_y)::numeric(5,2) AS stddev_y,
  count(label3) AS num
FROM
  main.gps_data_animals,
  main.activity_data_animals,
  env_data.corine_land_cover_legend
WHERE
  gps_data_animals.animals_id = activity_data_animals.animals_id AND
  ((gps_data_animals.acquisition_time - activity_data_animals.acquisition_time) < interval '150 second') AND
  ((gps_data_animals.acquisition_time - activity_data_animals.acquisition_time) > interval '-150 second') AND
  gps_validity_code = 1 AND
  gps_data_animals.corine_land_cover_code = corine_land_cover_legend.grid_code
GROUP BY
  label3;

-- You retrieve the average activity value of the 6 activity records that are closest in time to each GPS position
SELECT
  gps_data_animals.gps_data_animals_id AS gps_id,
  gps_data_animals.animals_id AS animal,
  gps_data_animals.acquisition_time,
  avg(activity_data_animals.activity_x)::numeric(5,2) AS avg_act_x,
  avg(activity_data_animals.activity_y)::numeric(5,2) AS avg_act_y
FROM
  main.gps_data_animals
INNER JOIN
  main.activity_data_animals
ON
  gps_data_animals.animals_id = activity_data_animals.animals_id AND
  ((gps_data_animals.acquisition_time - activity_data_animals.acquisition_time) < interval '15 minute') AND
  ((gps_data_animals.acquisition_time - activity_data_animals.acquisition_time) > interval '-15 minute') AND
  gps_validity_code = 1
GROUP BY
  gps_data_animals.gps_data_animals_id,
  gps_data_animals.animals_id,
  gps_data_animals.acquisition_time
ORDER BY
  gps_data_animals.acquisition_time
LIMIT 10;