1 ;;; PHOROS -- Photogrammetric Road Survey
2 ;;; Copyright (C) 2010, 2011 Bert Burgemeister
4 ;;; This program is free software; you can redistribute it and/or modify
5 ;;; it under the terms of the GNU General Public License as published by
6 ;;; the Free Software Foundation; either version 2 of the License, or
7 ;;; (at your option) any later version.
9 ;;; This program is distributed in the hope that it will be useful,
10 ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
11 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 ;;; GNU General Public License for more details.
14 ;;; You should have received a copy of the GNU General Public License along
15 ;;; with this program; if not, write to the Free Software Foundation, Inc.,
16 ;;; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 (defun nuke-all-tables ()
22 "Drop all tables (except PostGIS helper tables) and sequences in current database. TODO: also drop our functions and types."
23 (let ((user-tables (list-tables))
24 (sequences (list-sequences))
26 (system-tables '(:spatial-ref-sys
:geometry-columns
)))
27 (dolist (system-table system-tables
)
28 (setf user-tables
(remove system-table user-tables
)))
29 (dolist (user-table user-tables
)
30 (execute (format nil
"DROP TABLE IF EXISTS ~A CASCADE" (s-sql:to-sql-name user-table
))))
31 (dolist (sequence sequences
)
32 (execute (format nil
"DROP SEQUENCE IF EXISTS ~A CASCADE" (s-sql:to-sql-name sequence
))))
34 (execute (format nil
"DROP VIEW IF EXISTS ~A CASCADE" (s-sql:to-sql-name view
))))))
36 ;;; Used only to add Spherical Mercator in case it's missing
37 (defclass spatial-ref-sys
()
42 :col-type
(or db-null
(varchar 256))
45 :col-type
(or db-null integer
)
48 :col-type
(or db-null
(varchar 2048))
51 :col-type
(or db-null
(varchar 2048))
53 (:metaclass dao-class
)
55 (:documentation
"PostGIS system table as defined in http://postgis.refractions.net/documentation/manual-1.3/ch04.html#id2571306"))
57 ;;TODO: make a spatial-ref-sys table
59 (defun add-spherical-mercator-ref ()
60 "Tell PostGIS about Spherical Mercator if necessary."
61 (let ((spherical-mercator
65 :auth-name
"spatialreferencing.org"
67 :srtext
"PROJCS[\"Popular Visualisation CRS / Mercator (deprecated)\",GEOGCS[\"Popular Visualisation CRS\",DATUM[\"Popular_Visualisation_Datum\",SPHEROID[\"Popular Visualisation Sphere\",6378137,0,AUTHORITY[\"EPSG\",\"7059\"]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY[\"EPSG\",\"6055\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.01745329251994328,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4055\"]],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],PROJECTION[\"Mercator_1SP\"],PARAMETER[\"central_meridian\",0],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0],AUTHORITY[\"EPSG\",\"3785\"],AXIS[\"X\",EAST],AXIS[\"Y\",NORTH]]"
68 :proj4text
"+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +units=m +k=1.0 +nadgrids=@null +no_defs")))
69 (unless (dao-exists-p spherical-mercator
)
70 (insert-dao spherical-mercator
))))
72 ;;TODO: make a geometry-columns table
74 ;;CREATE TABLE geometry_columns (
75 ;; f_table_catalog VARRCHAR(256) NOT NULL,
76 ;; f_table_schema VARCHAR(256) NOT NULL,
77 ;; f_table_nam VARCHAR(256) NOT NULL,
78 ;; f_geometry_column VARCHAR(256) NOT NULL,
79 ;; coord_dimension INTEGER NOT NULL,
80 ;; srid INTEGER NOT NULL,
81 ;; type VARCHAR(30) NOT NULL
89 :col-default
(:nextval
'sys-user-id-seq
))
93 :documentation
"This one is used for authentication.")
95 :writer
(setf user-password
)
97 :initarg
:user-password
)
99 :writer
(setf user-full-name
)
101 :initarg
:user-full-name
))
102 (:metaclass dao-class
)
104 (:documentation
"List of users of the presentation front end. This is certainly not a full-fledged authentication system."))
107 (:create-sequence
'sys-user-id-seq
)
110 (defclass sys-acquisition-project
()
111 ((acquisition-project-id
112 :reader acquisition-project-id
114 :col-default
(:nextval
'sys-acquisition-project-id-seq
))
117 :initarg
:common-table-name
118 :documentation
"Name of this project's data tables sans their canonical prefixes and suffixes. Serves as a human-readable acquisition procect identifier. Should be one table for all projects but this seems to come with a speed penalty."))
119 (:metaclass dao-class
)
120 (:keys acquisition-project-id
)
121 (:documentation
"An acquisition project is basically a set of measurements that is stored in a common table."))
123 (deftable sys-acquisition-project
124 (:create-sequence
'sys-acquisition-project-id-seq
)
127 `(:alter-table
,*table-name
* :add
:constraint
"common-table-name-unique"
128 :unique
'common-table-name
)))
130 (defclass sys-presentation-project
()
131 ((presentation-project-id
132 :reader presentation-project-id
134 :col-default
(:nextval
'sys-presentation-project-id-seq
))
135 (presentation-project-name
137 :initarg
:presentation-project-name
)
139 :col-type
(or db-null text
)
140 :accessor bounding-box
141 :documentation
"Extent of this presentation project."))
142 (:metaclass dao-class
)
143 (:keys presentation-project-name
))
145 (deftable sys-presentation-project
146 (:create-sequence
'sys-presentation-project-id-seq
)
148 (:alter-table sys-presentation-project
149 :add
:constraint
"presentation-project-id-unique"
150 :unique
'presentation-project-id
))
152 (defclass sys-user-role
()
156 (presentation-project-id
157 :initarg
:presentation-project-id
162 :documentation
"One of read, write, admin.")
164 :col-type
(or db-null text
)
165 :accessor bounding-box
166 :documentation
"Streetmap zoom extent last time user left Phoros.")
168 :col-type
(or db-null geometry
)
170 :documentation
"Point; users work coordinate in streetmap last time they left Phoros."))
171 (:metaclass dao-class
)
172 (:keys user-id presentation-project-id
))
174 (deftable sys-user-role
176 (!foreign
'sys-user
'user-id
:on-delete
:cascade
:on-update
:cascade
)
177 (!foreign
'sys-presentation-project
'presentation-project-id
178 :on-delete
:cascade
:on-update
:cascade
))
180 (defclass sys-measurement
()
182 :reader measurement-id
184 :col-default
(:nextval
'sys-measurement-id-seq
))
185 (acquisition-project-id
186 :initarg
:acquisition-project-id
192 "Below some universal root common to all measurements; excluding
193 `applanix/´ `images/´ etc.
195 The entire directory structure looks like this:
199 /some/path/in/our/system/this/measurement/blah/applanix/points/xyz-event1.txt
200 /some/path/in/our/system/this/measurement/blah/applanix/points/uvw-event2.txt
201 ---- +++++++++++++++ ----+++++ ++++
202 ^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^ ^
203 universal root stored here in event
204 slot directory number
208 /some/path/in/our/system/this/measurement/blah/images/front77.pictures
209 /some/path/in/our/system/this/measurement/blah/images/front78.pictures
211 ^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^
212 universal root stored here in stored file name
216 ---- means unimportant
218 TODO: /images/ part not currently enforced.")
220 :initarg
:cartesian-system
223 "Definition (as a Proj.4 string) of the coordinate system the recorded standard deviations are in."))
224 (:metaclass dao-class
)
225 (:keys measurement-id
)
226 (:documentation
"A measurement comprises .pictures files and one set of GPS event log files in a dedicated directory."))
228 (deftable sys-measurement
229 (:create-sequence
'sys-measurement-id-seq
)
231 (!index
'measurement-id
)
232 (!foreign
'sys-acquisition-project
'acquisition-project-id
233 :on-delete
:cascade
:on-update
:cascade
))
235 (defclass sys-presentation
()
236 ((presentation-project-id
237 :initarg
:presentation-project-id
240 :initarg
:measurement-id
242 (:metaclass dao-class
)
243 (:keys presentation-project-id measurement-id
)
244 (:documentation
"Tell us which measurements belong to which presentation project(s)."))
246 (deftable sys-presentation
248 (!index
'measurement-id
)
249 (!foreign
'sys-presentation-project
'presentation-project-id
250 :on-delete
:cascade
:on-update
:cascade
)
251 (!foreign
'sys-measurement
'measurement-id
252 :on-delete
:cascade
:on-update
:cascade
))
254 (defclass sys-camera-hardware
()
256 :reader camera-hardware-id
258 :col-default
(:nextval
'sys-camera-hardware-id-seq
))
264 :col-type double-float
)
271 :documentation
"Array of multipliers for red, green, blue.")
274 :documentation
"Array containing the colors of the first pixels of the first rows (actually, row, as postmodern can't handle two-dimensional arrays (and if it could, we wouldn't use them anyway)). Each pixel is to be interpreted as a three-byte RGB value, red in the least-significant byte.")
279 :description
"Camera type, manufacturer, etc."))
280 (:metaclass dao-class
)
281 (:keys camera-hardware-id
))
283 (deftable sys-camera-hardware
284 (:create-sequence
'sys-camera-hardware-id-seq
)
286 (!index
'camera-hardware-id
))
288 (defclass sys-lens
()
292 :col-default
(:nextval
'sys-lens-id-seq
))
294 :col-type double-float
295 :documentation
"Focal length. Only for human consumption.")
300 :documentation
"Lens type, manufacturer, etc."))
301 (:metaclass dao-class
)
305 (:create-sequence
'sys-lens-id-seq
)
308 (defclass sys-generic-device
()
310 :reader generic-device-id
312 :col-default
(:nextval
'sys-generic-device-id-seq
))
314 :initarg
:camera-hardware-id
315 :col-type
(or db-null integer
))
318 :col-type
(or db-null integer
))
321 :col-type
(or db-null integer
)
322 :documentation
"Scanners yet to be defined."))
323 (:metaclass dao-class
)
324 (:keys generic-device-id
)
325 (:documentation
"A row should describe either a camera with a lens, or a laser scanner."))
327 (deftable sys-generic-device
328 (:create-sequence
'sys-generic-device-id-seq
)
330 (!index
'generic-device-id
)
331 (!index
'camera-hardware-id
)
332 (!foreign
'sys-camera-hardware
'camera-hardware-id
333 :on-delete
:restrict
:on-update
:restrict
)
334 (!foreign
'sys-lens
'lens-id
:on-delete
:restrict
:on-update
:restrict
)
335 ;;;; Once we have a sys-scanner table:
336 ;;(!foreign 'sys-scanner 'scanner-id :on-delete :restrict :on-update :restrict)
339 (defclass sys-device-stage-of-life
()
340 ((device-stage-of-life-id
341 :reader device-stage-of-life-id
343 :col-default
(:nextval
'sys-device-stage-of-life-seq
))
346 :documentation
"Must be stored next to each data record. Example: in a .pictures file, this is the value of `cam=´.")
350 :documentation
"Identifier for the GPS event that triggers this device. Must correspond to the N the GPS file name: ...eventN.txt.")
357 :documentation
"Something like `upper rear left´ or maybe `1.2.1´")
360 :documentation
"Computer (or or other recording device) this device is connected to.")
361 (computer-interface-name
363 :documentation
"Things like `eth0´, `COM1´ etc.")
365 :col-type
:timestamp-with-time-zone
)
367 :col-type
(or db-null
:timestamp-with-time-zone
)
368 :documentation
"Date and time when this device was unmounted or altered in other ways that may render the current calibration invalid."))
369 (:metaclass dao-class
)
370 (:keys device-stage-of-life-id
)
371 (:documentation
"This data is to be collected on the measuring vehicle. There must be a new record for every relevant change (including accidental ones) in device configuration."))
373 (deftable sys-device-stage-of-life
374 (:create-sequence
'sys-device-stage-of-life-seq
)
376 (!index
'device-stage-of-life-id
)
377 (!index
'recorded-device-id
)
378 (!index
'mounting-date
)
379 (!index
'unmounting-date
)
380 (!foreign
'sys-generic-device
'generic-device-id
381 :on-delete
:restrict
:on-update
:restrict
))
383 (defclass sys-camera-calibration
()
384 ((device-stage-of-life-id
385 :reader device-stage-of-life-id
387 :documentation
"This tells us what hardware this calibration is for.")
390 :col-type
:timestamp-with-time-zone
)
395 :documentation
"Regarding this entire set of calibration data. Note the special-purpose description fields inner-orientation-description, outer-orientation-description, boresight-description.")
398 :documentation
"If false: just display images, don't perform photogrammetric calculations.")
401 :documentation
"If true: not for production use; may be altered or deleted at any time.")
402 (photogrammetry-version
404 :documentation
"Software version used to create this data.")
407 :documentation
"Head up = 0; right ear up = 90; left ear up = -90; head down = 180.")
408 (inner-orientation-description
410 :documentation
"Comments regarding inner orientation calibration.")
412 :col-type double-float
413 :documentation
"Inner orientation: focal length.")
415 :col-type double-float
416 :documentation
"Inner orientation: principal point displacement.")
418 :col-type double-float
419 :documentation
"Inner orientation: principal point displacement.")
421 :col-type double-float
422 :documentation
"Inner orientation: radial distortion.")
424 :col-type double-float
425 :documentation
"Inner orientation: radial distortion.")
427 :col-type double-float
428 :documentation
"Inner orientation: radial distortion.")
430 :col-type double-float
431 :documentation
"Inner orientation: asymmetric and tangential distortion.")
433 :col-type double-float
434 :documentation
"Inner orientation: asymmetric and tangential distortion.")
436 :col-type double-float
437 :documentation
"Inner orientation: affinity and shear distortion.")
439 :col-type double-float
440 :documentation
"Inner orientation: affinity and shear distortion.")
442 :col-type double-float
443 :documentation
"Inner orientation.")
444 (outer-orientation-description
446 :documentation
"Comments regarding outer orientation calibration.")
448 :col-type double-float
449 :documentation
"Outer orientation; in metres.")
451 :col-type double-float
452 :documentation
"Outer orientation; in metres.")
454 :col-type double-float
455 :documentation
"Outer orientation; in metres.")
457 :col-type double-float
458 :documentation
"Outer orientation.")
460 :col-type double-float
461 :documentation
"Outer orientation.")
463 :col-type double-float
464 :documentation
"Outer orientation.")
465 (boresight-description
467 :documentation
"Comments regarding boresight alignment calibration.")
469 :col-type double-float
470 :documentation
"Boresight alignment.")
472 :col-type double-float
473 :documentation
"Boresight alignment.")
475 :col-type double-float
476 :documentation
"Boresight alignment.")
478 :col-type double-float
479 :documentation
"Boresight alignment.")
481 :col-type double-float
482 :Documentation
"Boresight alignment.")
484 :col-type double-float
485 :documentation
"Boresight alignment.")
487 :col-type double-float
488 :documentation
"Boresight alignment.")
490 :col-type double-float
491 :documentation
"Boresight alignment.")
493 :col-type double-float
494 :documentation
"Boresight alignment.")
496 :col-type double-float
497 :documentation
"Boresight alignment.")
499 :col-type double-float
500 :documentation
"Boresight alignment.")
502 :col-type double-float
503 :documentation
"Boresight alignment.")
505 :col-type double-float
506 :documentation
"Component of unit vector of vehicle ground plane.")
508 :col-type double-float
509 :documentation
"Component of unit vector of vehicle ground plane.")
511 :col-type double-float
512 :documentation
"Component of unit vector of vehicle ground plane.")
514 :col-type double-float
515 :documentation
"Distance of vehicle ground plane."))
516 (:metaclass dao-class
)
517 (:keys device-stage-of-life-id date
)
518 (:documentation
"Camera calibration parameters."))
520 (deftable sys-camera-calibration
522 (!index
'device-stage-of-life-id
)
524 (!foreign
'sys-device-stage-of-life
'device-stage-of-life-id
525 :on-delete
:restrict
:on-update
:restrict
))
527 (defun phoros-db-major-version ()
528 "Get the version number of the database structure we are currently
529 connected to. It is supposed to be equal to the :major part of
532 (:select
'last-value
:from
'sys-phoros-major-version
)
535 (defun (setf phoros-db-major-version
) (major-version)
536 "Set the version number of the database structure we are currently
537 connected to. This can only be done once per database."
539 (:create-sequence
'sys-phoros-major-version
541 :max-value major-version
542 :start major-version
))
543 (phoros-db-major-version))
545 (defun assert-phoros-db-major-version ()
546 "Check if phoros version and version of the database we are
548 (assert (= (phoros-db-major-version) (phoros-version :major t
)) ()
549 "Can't use a Phoros database structure of version ~D. ~
550 It should be version ~D. ~
551 Either create a new database structure using this version of ~
552 Phoros, or use Phoros version ~2:*~D.x.x."
553 (phoros-db-major-version) (phoros-version :major t
)))
555 (defun create-sys-tables ()
556 "Create in current database a set of sys-* tables, i.e. tables that
557 are used by all projects. The database should probably be empty."
558 (setf (phoros-db-major-version) (phoros-version :major t
))
559 (create-table 'sys-user
)
560 (create-table 'sys-acquisition-project
)
561 (create-table 'sys-presentation-project
)
562 (create-table 'sys-user-role
)
563 (create-table 'sys-measurement
)
564 (create-table 'sys-presentation
)
565 (create-table 'sys-camera-hardware
)
566 (create-table 'sys-lens
)
567 (create-table 'sys-generic-device
)
568 (create-table 'sys-device-stage-of-life
)
569 (create-table 'sys-camera-calibration
)
570 (create-plpgsql-helpers))
572 (defun create-plpgsql-helpers ()
573 "Create in current database a few SQL types and functions."
578 (point1 GEOMETRY, point2 GEOMETRY, point3 GEOMETRY)
579 RETURNS DOUBLE PRECISION AS $$
582 RETURN abs(st_azimuth(point2, point3) - st_azimuth(point1, point2));
584 $$ LANGUAGE plpgsql;"
587 "DROP TYPE IF EXISTS point_bag;")
589 "CREATE TYPE point_bag AS (id int, coordinates GEOMETRY);"))
591 (defun !!index
(table field
&key
(index-type :btree
))
592 (format nil
"CREATE INDEX ~0@*~A_~1@*~A_index ON ~0@*~A USING ~2@*~A (~1@*~A)"
593 (s-sql:to-sql-name table
)
594 (s-sql:to-sql-name field
)
595 (s-sql:to-sql-name index-type
)))
597 (defclass point-template
()
598 (;; We need a slot point-id which is defined in our subclasses.
601 :initform
(random (expt 2 31))
602 :documentation
"Used for quickly getting an evenly distributed sample of all points.")
604 :writer
(setf measurement-id
)
607 :documentation
"Event that triggered this record. Taken from the GPS file name: ...eventN.txt gives an event number N. May be a string of any length.")
610 :documentation
"UTC calculated from GPS week time.")
612 :writer
(setf trigger-time
)
613 :col-type double-precision
614 :documentation
"Time in seconds from 1900. Values before 1980-01-06T00:00:00Z are considered invalid.")
616 :col-type double-precision
)
618 :col-type double-precision
)
620 :col-type double-precision
)
622 :col-type double-precision
)
624 :col-type double-precision
)
626 :col-type double-precision
)
628 :col-type double-precision
)
630 :col-type double-precision
)
632 :col-type double-precision
)
634 :col-type double-precision
)
636 :col-type double-precision
)
638 :col-type double-precision
)
641 :documentation
"Same content as in slot coordinates.")
644 :documentation
"Same content as in slot coordinates.")
646 :reader ellipsoid-height
647 :documentation
"Same content as in slot coordinates.")
649 :col-type
(or db-null geometry
)
650 :documentation
"Geographic coordinates.")
653 :documentation
"In the same coordinate system as the standard deviations.")
656 :documentation
"In the same coordinate system as the standard deviations.")
658 :reader cartesian-height
659 :documentation
"In the same coordinate system as the standard deviations."))
660 (:metaclass dao-class
)
662 (:documentation
"Information about one GPS point, originally from applanix/**/*event*.txt. There shouldn't be any point-id without a matching one in the *-image table. This can't be enforced on database level. Use (delete-imageless-points acquisition-project) to maintain referential integrity."))
664 (defclass image-template
()
666 :writer
(setf measurement-id
)
668 :documentation
"A primary key. We need to recognize images should they come in twice, perhaps with slightly changed point data. In such a case we want the old ones superseded.")
673 :documentation
"Name without any directory components.")
675 :reader image-byte-position
676 :initarg
:byte-position
678 :documentation
"Start of image in .pictures file named by slot filename.")
683 :initarg
:recorded-device-id
684 :reader recorded-device-id
686 :documentation
"As found in .pictures file, header tag `cam=´.")
689 :col-type
(or db-null geometry
)
690 :documentation
"Polygon on the ground describing the approximate area covered by this image.")
691 (footprint-device-stage-of-life-id
692 :initarg
:footprint-device-stage-of-life-id
693 :col-type
(or db-null integer
)
694 :documentation
"device-stage-of-life denoting the set of calibration data the footprint of this record has been calculated with.")
697 :col-type double-precision
698 :documentation
"Camera parameter. TODO: needs a decent definition")
701 :col-type double-precision
702 :documentation
"Camera parameter. TODO: needs a decent definition")
704 :initarg
:trigger-time
705 :accessor trigger-time
706 :documentation
"Time in seconds from 1900-01-01.")
708 :accessor fake-trigger-time-p
710 :documentation
"T if trigger-time has been reconstructed from adjacent data.")
712 :initarg
:camera-timestamp
713 :reader camera-timestamp
714 :documentation
"Some camera clocktick count starting at an unknown origin."))
715 (:metaclass dao-class
)
716 (:keys measurement-id filename byte-position
)
717 (:documentation
"One row per image, originating from a .pictures file."))
719 (defclass user-point-template
()
720 (;; We need a slot user-point-id which is defined in our subclasses.
723 :col-type
(or db-null
;when store-user-points is fed an unknown user-name
725 :documentation
"User who stored this point.")
729 :documentation
"Class of this user point.")
731 :initarg
:description
733 :documentation
"User comment regarding this point.")
735 :initarg
:numeric-description
737 :documentation
"User-generated point id regarding this point.")
739 :col-type
:timestamp-with-time-zone
740 :documentation
"Creation time of this point.")
742 :col-type
(or db-null geometry
)
743 :documentation
"Geographic coordinates.")
745 :initarg
:stdx-global
746 :col-type double-precision
747 :documentation
"Component of standard deviation, in metres.")
749 :initarg
:stdy-global
750 :col-type double-precision
751 :documentation
"Component of standard deviation, in metres.")
753 :initarg
:stdz-global
754 :col-type double-precision
755 :documentation
"Component of standard deviation, in metres.")
759 :documentation
"Number of points (from different images) used for calculation.")
761 :col-type
(or db-null numeric
[])
762 :documentation
"Arbitrary numeric values from auxiliary point table.")
764 :col-type
(or db-null text
[])
765 :documentation
"Arbitrary text values from auxiliary point table."))
766 (:metaclass dao-class
)
767 (:keys user-point-id
)
768 (:documentation
"Points defined by users."))
770 (defclass point-data
(point-template)
775 :col-default nil
) ;to be redefined
776 point-id-sequence-name
) ;to be redefined
777 (:metaclass dao-class
)
778 (:table-name nil
)) ;to be redefined
780 (defclass image-data
(image-template)
782 (:metaclass dao-class
)
783 (:table-name nil
)) ;to be redefined
785 (defclass user-point-data
(user-point-template)
787 :accessor user-point-id
790 :col-default nil
) ;to be redefined
791 user-point-id-sequence-name
) ;to be redefined)
792 (:metaclass dao-class
)
793 (:table-name nil
)) ;to be redefined
795 (let ((table-prefix "dat-"))
796 (defun point-data-table-name (common-table-name)
797 (make-symbol (format nil
"~A~A-point"
798 table-prefix common-table-name
)))
800 (defun image-data-table-name (common-table-name)
801 (make-symbol (format nil
"~A~A-image"
802 table-prefix common-table-name
)))
804 (defun point-id-seq-name (common-table-name)
805 (make-symbol (format nil
"~A~A-point-id-seq"
806 table-prefix common-table-name
)))
808 (defun aggregate-view-name (common-table-name)
809 (make-symbol (format nil
"~A~A-aggregate"
810 table-prefix common-table-name
)))
812 (defun aggregate-view-update-rule-name (common-table-name)
813 (make-symbol (format nil
"~A~A-aggregate-update"
814 table-prefix common-table-name
))))
816 (let ((table-prefix "usr-"))
817 (defun user-point-table-name (presentation-project-name)
818 (make-symbol (format nil
"~A~A-point"
819 table-prefix presentation-project-name
)))
821 (defun user-point-id-seq-name (presentation-project-name)
822 (make-symbol (format nil
"~A~A-point-id-seq"
823 table-prefix presentation-project-name
)))
825 (defun user-line-table-name (presentation-project-name)
826 (make-symbol (format nil
"~A~A-line"
827 table-prefix presentation-project-name
))))
829 (let ((table-prefix "phoros-"))
830 ;; This stuff may reside in a foreign database so we show explicitly
831 ;; what it belongs to.
832 (defun aux-point-view-name (presentation-project-name)
833 (make-symbol (format nil
"~A~A-aux-point"
834 table-prefix presentation-project-name
)))
836 (defun thread-aux-points-function-name (presentation-project-name)
837 (make-symbol (format nil
"~A~A-thread-aux-points"
838 table-prefix presentation-project-name
))))
840 (defun create-data-table-definitions (common-table-name)
841 "Define or redefine a bunch of dao-classes which can hold measuring
842 data and which are connected to database tables named
843 common-table-name plus type-specific prefix and suffix."
844 (let ((image-data-table-name (image-data-table-name common-table-name
))
845 (point-data-table-name (point-data-table-name common-table-name
))
846 (point-id-sequence-name (point-id-seq-name common-table-name
)))
848 `(defclass point-data
(point-template)
853 :col-default
(:nextval
,point-id-sequence-name
)) ; redefinition
854 (point-id-sequence-name
855 :initform
,(string point-id-sequence-name
) ; redefinition
856 :reader point-id-sequence-name
858 (:metaclass dao-class
)
859 (:table-name
,point-data-table-name
))) ;redefinition
861 (:create-sequence point-id-sequence-name
)
863 (!!index point-data-table-name
'random
)
864 (!!index point-data-table-name
'measurement-id
)
865 (!!index point-data-table-name
'trigger-time
)
866 (!!index point-data-table-name
'coordinates
:index-type
:gist
)
867 (!!index point-data-table-name
'point-id
)
868 ;; The following let shouldn't be necessary. (Wart In !foreign.)
869 (let ((*table-symbol
* point-data-table-name
)
870 (*table-name
* (s-sql:to-sql-name point-data-table-name
)))
871 (!foreign
'sys-measurement
'measurement-id
872 :on-delete
:cascade
:on-update
:cascade
)))
874 `(defclass image-data
(image-template)
876 (:metaclass dao-class
)
877 (:table-name
,image-data-table-name
))) ; redefintion
880 (!!index image-data-table-name
'measurement-id
)
881 (!!index image-data-table-name
'recorded-device-id
)
882 (!!index image-data-table-name
'point-id
)
883 ;; (!!index image-data-table-name 'gain)
884 ;; (!!index image-data-table-name 'shutter)
885 (!!index image-data-table-name
'footprint
:index-type
:gist
)
886 ;; The following let shouldn't be necessary. (Wart in !foreign.)
887 (let ((*table-symbol
* image-data-table-name
)
888 (*table-name
* (s-sql:to-sql-name image-data-table-name
)))
889 (!foreign point-data-table-name
'point-id
890 :on-delete
:cascade
:on-update
:cascade
)
891 (!foreign
'sys-measurement
'measurement-id
892 :on-delete
:cascade
:on-update
:cascade
)))))
894 (defun create-user-table-definition (presentation-project-name)
895 "Define or redefine a dao-class which can hold user points and which
896 is connected to a database table named presentation-project-name plus
897 type-specific prefix and suffix."
898 (let ((user-point-table-name (user-point-table-name presentation-project-name
))
899 (user-point-id-sequence-name (user-point-id-seq-name presentation-project-name
)))
901 `(defclass user-point
(user-point-template)
906 :col-default
(:nextval
,user-point-id-sequence-name
))) ; redefinition
907 (:metaclass dao-class
)
908 (:table-name
,user-point-table-name
))) ;redefinition
910 (:create-sequence user-point-id-sequence-name
)
912 (!!index user-point-table-name
'coordinates
:index-type
:gist
))))
914 (defun create-aggregate-view (common-table-name)
915 "Create a view of a set of measuring and calibration data
916 belonging to images."
917 (let ((image-data-table-name (image-data-table-name common-table-name
))
918 (point-data-table-name (point-data-table-name common-table-name
))
919 (aggregate-view-name (aggregate-view-name common-table-name
))
920 (aggregate-view-update-rule-name (aggregate-view-update-rule-name
927 'sys-device-stage-of-life.recorded-device-id
;debug
928 'sys-device-stage-of-life.device-stage-of-life-id
;debug
929 'sys-device-stage-of-life.generic-device-id
;debug
931 'presentation-project-id
933 (:dot
',image-data-table-name
'measurement-id
)
934 'filename
'byte-position
935 (:dot
',point-data-table-name
'point-id
)
936 'footprint
'footprint-device-stage-of-life-id
938 'coordinates
;the search target
939 (:as
(:st_x
(:st_transform
'coordinates
*standard-coordinates
*))
941 (:as
(:st_y
(:st_transform
'coordinates
*standard-coordinates
*))
943 (:as
(:st_z
(:st_transform
'coordinates
*standard-coordinates
*))
946 'east-sd
'north-sd
'height-sd
947 'roll
'pitch
'heading
'roll-sd
'pitch-sd
'heading-sd
949 'sensor-width-pix
'sensor-height-pix
'pix-size
950 'bayer-pattern
'color-raiser
952 'dx
'dy
'dz
'omega
'phi
'kappa
953 'c
'xh
'yh
'a1
'a2
'a3
'b1
'b2
'c1
'c2
'r0
954 'b-dx
'b-dy
'b-dz
'b-rotx
'b-roty
'b-rotz
955 'b-ddx
'b-ddy
'b-ddz
'b-drotx
'b-droty
'b-drotz
960 ',point-data-table-name
',image-data-table-name
961 'sys-device-stage-of-life
'sys-generic-device
'sys-camera-hardware
962 'sys-camera-calibration
965 (:= (:dot
',image-data-table-name
'measurement-id
)
966 'sys-presentation.measurement-id
)
967 (:= 'sys-presentation.measurement-id
968 'sys-measurement.measurement-id
)
969 (:= (:dot
',point-data-table-name
'point-id
)
970 (:dot
',image-data-table-name
'point-id
))
971 (:= (:dot
',image-data-table-name
'recorded-device-id
)
972 'sys-device-stage-of-life.recorded-device-id
)
973 (:= 'sys-generic-device.generic-device-id
974 'sys-device-stage-of-life.generic-device-id
)
975 (:= 'sys-camera-hardware.camera-hardware-id
976 'sys-generic-device.camera-hardware-id
)
977 (:= 'sys-device-stage-of-life.device-stage-of-life-id
978 'sys-camera-calibration.device-stage-of-life-id
)
979 (:= 'sys-device-stage-of-life.device-stage-of-life-id
982 (:select
'sys-camera-calibration.device-stage-of-life-id
983 :from
'sys-camera-calibration
985 (:= 'sys-device-stage-of-life.device-stage-of-life-id
986 'sys-camera-calibration.device-stage-of-life-id
))
989 (:<= (:extract
:epoch
'sys-device-stage-of-life.mounting-date
)
990 (:-
(:dot
',point-data-table-name
'trigger-time
)
992 (:or
(:is-null
'sys-device-stage-of-life.unmounting-date
)
993 (:>= (:extract
:epoch
'sys-device-stage-of-life.unmounting-date
)
994 (:-
(:dot
',point-data-table-name
'trigger-time
)
999 "CREATE OR REPLACE RULE ~A ~
1000 AS ON UPDATE TO ~A DO INSTEAD ~
1002 SET footprint = NEW.footprint, ~
1003 footprint_device_stage_of_life_id = OLD.device_stage_of_life_id
1004 WHERE byte_position = OLD.byte_position ~
1005 AND filename = OLD.filename ~
1006 AND measurement_id = OLD.measurement_id;"
1007 (s-sql:to-sql-name aggregate-view-update-rule-name
)
1008 (s-sql:to-sql-name aggregate-view-name
)
1009 (s-sql:to-sql-name image-data-table-name
)))))
1011 (defun aux-view-exists-p (presentation-project-name)
1012 "See if there is a view into auxiliary point table that belongs to
1013 presentation-project-name."
1014 (view-exists-p (aux-point-view-name presentation-project-name
)))
1016 (defun delete-aux-view (presentation-project-name)
1017 "Delete the view into auxiliary point table that belongs to
1018 presentation-project-name."
1019 (execute (format nil
"DROP VIEW ~A CASCADE;"
1020 (s-sql:to-sql-name
(aux-point-view-name
1021 presentation-project-name
))))
1023 (format nil
"DROP FUNCTION IF EXISTS ~
1024 ~A(GEOMETRY, DOUBLE PRECISION, INT, DOUBLE PRECISION);"
1025 (s-sql:to-sql-name
(thread-aux-points-function-name
1026 presentation-project-name
)))))
1028 (defun* create-aux-view
(presentation-project-name
1029 &key
(coordinates-column :the-geom
)
1030 numeric-columns text-columns
1031 &mandatory-key aux-table
)
1032 "Create a view into aux-table and an SQL function for threading
1033 aux-points into a linestring. coordinates-column goes into column
1034 coordinates, numeric-columns and text-columns go into arrays in
1035 aux-numeric and aux-text respectively.
1037 aux-table should have an index like so:
1039 CREATE INDEX idx_<aux-table>_the_geom
1041 USING gist (the_geom);
1043 VACUUM FULL ANALYZE <aux-table> (the_geom);"
1044 (create-plpgsql-helpers)
1045 (let ((aux-point-view-name
1046 (aux-point-view-name presentation-project-name
))
1047 (thread-aux-points-function-name
1048 (thread-aux-points-function-name presentation-project-name
)))
1049 (execute (format nil
"
1051 AS (SELECT ~A AS coordinates,
1052 ~:[NULL~;ARRAY[~:*~{~A~#^, ~}]~] AS aux_numeric,
1053 ~:[NULL~;ARRAY[~:*~{~A~#^, ~}]~] AS aux_text
1055 (s-sql:to-sql-name aux-point-view-name
)
1057 (mapcar #'s-sql
:to-sql-name numeric-columns
)
1058 (mapcar #'s-sql
:to-sql-name text-columns
)
1059 (s-sql:to-sql-name aux-table
)))
1060 (execute (format nil
"~
1061 CREATE OR REPLACE FUNCTION ~0@*~A
1062 (point GEOMETRY, sample_radius DOUBLE PRECISION, sample_size INT,
1063 step_size DOUBLE PRECISION, old_azimuth DOUBLE PRECISION,
1064 max_bend DOUBLE PRECISION,
1065 OUT threaded_points TEXT,
1066 OUT current_point TEXT,
1067 OUT back_point TEXT, OUT forward_point TEXT,
1068 OUT new_azimuth DOUBLE PRECISION)
1071 -- Phoros version ~2@*~A
1074 current_point_position DOUBLE PRECISION;
1075 location DOUBLE PRECISION;
1077 new_point point_bag%ROWTYPE;
1078 tried_point point_bag%ROWTYPE;
1079 previous_point point_bag%ROWTYPE;
1080 starting_point GEOMETRY;
1081 reversal_count INT DEFAULT 0;
1083 -- Muffle warnings about implicitly created stuff:
1084 SET client_min_messages TO ERROR;
1092 st_setsrid(st_makebox3d (st_translate (point,
1093 - sample_radius * 5,
1094 - sample_radius * 5, 0),
1095 st_translate (point,
1097 sample_radius * 5, 0)),
1099 ORDER BY st_distance(coordinates, point)
1102 CREATE TEMPORARY TABLE point_bag
1103 (id SERIAL primary key, coordinates GEOMETRY)
1106 INSERT INTO point_bag (coordinates)
1113 st_setsrid(st_makebox3d (st_translate (starting_point,
1115 - sample_radius, 0),
1116 st_translate (starting_point,
1120 AND st_distance (coordinates, starting_point) < sample_radius
1121 ORDER BY st_distance (coordinates, starting_point)
1124 point_bag_size := (SELECT count(*) from point_bag);
1126 -- emergency point_bag:
1127 IF point_bag_size < 5
1129 DROP TABLE point_bag;
1130 CREATE TEMPORARY TABLE point_bag
1131 (id SERIAL primary key, coordinates GEOMETRY)
1133 INSERT INTO point_bag (coordinates)
1139 st_setsrid(st_makebox3d (st_translate (point,
1140 - sample_radius * 100,
1141 - sample_radius * 100, 0),
1142 st_translate (point,
1143 sample_radius * 100,
1144 sample_radius * 100, 0)),
1146 ORDER BY st_distance (coordinates, starting_point)
1148 starting_point := (SELECT coordinates FROM point_bag where id = 3);
1152 (SELECT ROW(id, coordinates)
1154 ORDER BY st_distance (point_bag.coordinates, starting_point)
1157 DELETE FROM point_bag WHERE id = previous_point.id;
1160 (SELECT ROW(id, coordinates)
1162 ORDER BY st_distance (point_bag.coordinates, previous_point.coordinates)
1165 line := st_makeline(previous_point.coordinates,
1166 new_point.coordinates);
1169 st_azimuth(previous_point.coordinates, new_point.coordinates);
1171 IF abs(new_azimuth - old_azimuth) > radians(90)
1173 abs(new_azimuth - old_azimuth) < radians(270)
1176 st_azimuth(new_point.coordinates, previous_point.coordinates);
1177 line := st_reverse(line);
1180 DELETE FROM point_bag WHERE id = new_point.id;
1183 previous_point.coordinates := st_pointn(line,1);
1186 (SELECT ROW(id, coordinates)
1188 ORDER BY st_distance (coordinates, previous_point.coordinates)
1191 EXIT WHEN new_point IS NULL;
1193 IF bendedness(st_pointn(line, 2), st_pointn(line, 1),
1194 new_point.coordinates)
1195 < bendedness(st_pointn(line, st_npoints(line) - 1),
1196 st_pointn(line, st_npoints(line)), new_point.coordinates)
1198 bendedness(st_pointn(line, 2), st_pointn(line, 1),
1199 new_point.coordinates)
1202 line := st_addpoint(line, new_point.coordinates, 0);
1203 DELETE FROM point_bag WHERE id = new_point.id;
1206 line := st_reverse(line);
1208 reversal_count := reversal_count + 1 ;
1210 DELETE FROM point_bag WHERE id = tried_point.id;
1212 tried_point := new_point;
1215 IF mod(reversal_count, 2) = 1
1217 line := st_reverse(line);
1220 current_point_position :=
1221 st_line_locate_point(line, point);
1224 st_astext(st_line_interpolate_point(line, current_point_position));
1226 location := (current_point_position - (step_size / st_length(line)));
1227 IF location < 0 THEN location := 0; END IF;
1230 st_astext(st_line_interpolate_point(line, location));
1232 location := (current_point_position + (step_size / st_length(line)));
1233 IF location > 0 THEN location := 1; END IF;
1236 st_astext(st_line_interpolate_point(line, location));
1238 threaded_points := st_astext(line);
1242 $$ LANGUAGE plpgsql;"
1243 (s-sql:to-sql-name thread-aux-points-function-name
)
1244 (s-sql:to-sql-name aux-point-view-name
)
1245 (phoros-version)))))
1247 (defun create-acquisition-project (common-table-name)
1248 "Create in current database a fresh set of canonically named tables.
1249 common-table-name should in most cases resemble the project name and
1250 will be stored in table sys-acquisition-project, field
1252 (create-data-table-definitions common-table-name
)
1253 (handler-case (create-sys-tables) ;Create system tables if necessary.
1254 (cl-postgres-error:syntax-error-or-access-violation
() nil
))
1255 (assert-phoros-db-major-version)
1256 (when (select-dao 'sys-acquisition-project
1257 (:= 'common-table-name
1258 (s-sql:to-sql-name common-table-name
)))
1259 (error "There is already a row with a common-table-name of ~A in table ~A."
1261 (s-sql:to-sql-name
(dao-table-name 'sys-acquisition-project
))))
1262 (create-table 'point-data
)
1263 (create-table 'image-data
)
1264 (create-aggregate-view common-table-name
)
1266 (make-instance 'sys-acquisition-project
1267 :common-table-name common-table-name
)))
1269 (defun delete-acquisition-project (common-table-name)
1270 "Delete the acquisition project that uses common-table-name. Return
1271 nil if there wasn't any."
1272 (assert-phoros-db-major-version)
1274 (car (select-dao 'sys-acquisition-project
1275 (:= 'common-table-name common-table-name
)))))
1277 (delete-dao project
)
1278 (execute (:drop-view
1279 :if-exists
(aggregate-view-name common-table-name
)))
1280 (execute (:drop-table
1281 :if-exists
(image-data-table-name common-table-name
)))
1282 (execute (:drop-table
1283 :if-exists
(point-data-table-name common-table-name
)))
1284 (execute (:drop-sequence
1285 :if-exists
(point-id-seq-name common-table-name
))))))
1287 (defun delete-measurement (measurement-id)
1288 "Delete measurement with measurement-id if any; return nil if not."
1289 (assert-phoros-db-major-version)
1290 (let ((measurement (get-dao 'sys-measurement measurement-id
)))
1291 (when measurement
(delete-dao measurement
))))
1293 (defun create-presentation-project (project-name)
1294 "Create a fresh presentation project in current database. Return
1295 dao if one was created, or nil if it existed already."
1296 (assert-phoros-db-major-version)
1297 (unless (get-dao 'sys-presentation-project project-name
)
1298 (create-user-table-definition project-name
)
1299 (create-table 'user-point
)
1300 (create-presentation-project-trigger-function project-name
)
1301 (execute (format nil
"DROP TRIGGER IF EXISTS ~A ON ~:*~A;"
1302 (s-sql:to-sql-name
(user-point-table-name project-name
))))
1303 (execute (format nil
"
1305 AFTER INSERT OR UPDATE OR DELETE
1307 FOR EACH ROW EXECUTE PROCEDURE ~:*~A();"
1308 (s-sql:to-sql-name
(user-point-table-name project-name
))))
1309 (execute (sql-compile
1310 `(:create-table
,(user-line-table-name project-name
)
1311 ((description :type text
)
1312 ;; description would be a nice primary
1313 ;; key if it wasn't for QGIS which
1315 (id :type serial
:primary-key t
)
1316 (line :type geometry
)))))
1317 (insert-dao (make-instance 'sys-presentation-project
1318 :presentation-project-name project-name
))))
1320 (defun create-presentation-project-trigger-function
1321 (presentation-project
1322 &optional
(plpgsql-body
1324 nil
" RAISE NOTICE 'trigger fired: ~A';"
1325 (s-sql:to-sql-name
(user-point-table-name
1326 presentation-project
))))
1327 &rest plpgsql-body-args
)
1328 "(Re)create in current database an SQL trigger function with
1329 plpgsql-body (a format string that uses plpgsql-body-args)."
1332 CREATE OR REPLACE FUNCTION ~A() RETURNS trigger
1336 ------------------------------------------
1337 -- Define your trigger actions below:
1338 ------------------------------------------
1340 ------------------------------------------
1341 -- End of your trigger action definitions.
1342 ------------------------------------------
1345 $$ LANGUAGE plpgsql;"
1346 (s-sql:to-sql-name
(user-point-table-name presentation-project
))
1348 plpgsql-body-args
)))
1350 (defun fire-presentation-project-trigger-function (presentation-project)
1351 "Tickle user point table of presentation-project so it fires its
1353 (let ((user-point-table (user-point-table-name presentation-project
)))
1355 (:update user-point-table
1356 :set
'user-point-id
'user-point-id
1357 :where
(:= 'user-point-id
1358 (:limit
(:select
'user-point-id
1359 :from user-point-table
) 1))))))
1361 (defun delete-presentation-project (project-name)
1362 "Delete the presentation project project-name. Return nil if there
1364 (assert-phoros-db-major-version)
1365 (let ((project (get-dao 'sys-presentation-project project-name
)))
1367 (delete-dao project
)
1369 (:drop-table
:if-exists
(user-point-table-name project-name
)))
1371 (:drop-sequence
:if-exists
(user-point-id-seq-name project-name
)))
1373 (:drop-table
:if-exists
(user-line-table-name project-name
))))))
1375 (defun* create-user
(name &key
1376 presentation-projects
1381 "Create a fresh user entry or update an existing one with matching
1382 name. Assign it presentation-projects, deleting any previously
1383 existing assignments."
1384 (assert-phoros-db-major-version)
1385 (assert (or (string-equal "read" user-role
)
1386 (string-equal "write" user-role
)
1387 (string-equal "admin" user-role
))
1389 "~A is not a valid user-role." user-role
)
1390 (let ((user (or (car (select-dao 'sys-user
(:= 'user-name name
)))
1391 (make-instance 'sys-user
:user-name name
)))
1393 (setf (user-password user
) user-password
1394 (user-full-name user
) user-full-name
)
1395 (setf fresh-user-p
(save-dao user
))
1396 (mapcar #'delete-dao
(select-dao 'sys-user-role
1397 (:= 'user-id
(user-id user
))))
1398 (dolist (presentation-project-name presentation-projects
)
1399 (let ((presentation-project
1400 (get-dao 'sys-presentation-project presentation-project-name
)))
1401 (if presentation-project
1405 :user-id
(user-id user
)
1406 :presentation-project-id
1407 (presentation-project-id presentation-project
)
1408 :user-role
(string-downcase user-role
))) ;TODO: we should be able to set role per presentation-project.
1410 "There is no presentation project ~A" presentation-project-name
))))
1413 (defun delete-user (user-name)
1414 "Delete user user-name if any; return nil if not."
1415 (assert-phoros-db-major-version)
1416 (let ((user (car (select-dao 'sys-user
(:= 'user-name user-name
)))))
1417 (when user
(delete-dao user
))))
1419 (defun add-to-presentation-project (presentation-project-name
1420 &key measurement-ids acquisition-project
)
1421 "Add to presentation project presentation-project-name either a list
1422 of measurements (with measurement-id) or all measurements currently in
1423 acquisition-project (denoted by its common-table-name)."
1424 (assert-phoros-db-major-version)
1425 (let* ((presentation-project
1426 (car (select-dao 'sys-presentation-project
1427 (:= 'presentation-project-name
1428 presentation-project-name
))))
1429 (presentation-project-id
1430 (presentation-project-id presentation-project
)))
1431 (flet ((add-measurement (measurement-id)
1432 "Add one measurement to the given presentation-project."
1433 (unless (get-dao 'sys-presentation
1434 presentation-project-id
1437 (make-instance 'sys-presentation
1438 :presentation-project-id presentation-project-id
1439 :measurement-id measurement-id
)))))
1440 (cond (measurement-ids (mapc #'add-measurement measurement-ids
))
1441 (acquisition-project
1447 :from
'sys-measurement
'sys-acquisition-project
1449 (:= 'sys-acquisition-project.common-table-name
1450 acquisition-project
)
1451 (:= 'sys-measurement.acquisition-project-id
1452 'sys-acquisition-project.acquisition-project-id
)))
1454 (add-measurement measurement-id
)))
1456 "Don't know what to add. ~
1457 Need either measurement-id or acquisition-project."))))
1458 (let* ((common-table-names
1459 (common-table-names presentation-project-id
))
1460 (presentation-project-bounding-box
1461 (ignore-errors ;for empty presentation project
1469 (:st_extent
'coordinates
)
1473 for common-table-name in common-table-names
1474 for point-table-name
1475 = (point-data-table-name common-table-name
)
1476 ;; would have been nice, was too slow:
1477 ;; = (aggregate-view-name common-table-name)
1481 :from
',point-table-name
1482 :natural
:left-join
'sys-presentation
1484 (:= 'presentation-project-id
1485 ,presentation-project-id
))))
1488 (when presentation-project-bounding-box
1489 (setf (bounding-box presentation-project
)
1490 presentation-project-bounding-box
))
1491 (update-dao presentation-project
))))
1493 (defun remove-from-presentation-project (presentation-project-name
1494 &key measurement-ids acquisition-project
)
1495 "Remove from presentation project presentation-project-name either a
1496 list of measurements (with measurement-id) or all measurements
1497 currently in acquisition-project with (denoted by its
1498 common-table-name). Return nil if there weren't anything to remove."
1499 (assert-phoros-db-major-version)
1500 (let* ((presentation-project
1501 (car (select-dao 'sys-presentation-project
1502 (:= 'presentation-project-name
1503 presentation-project-name
))))
1504 (presentation-project-id
1505 (Presentation-project-id presentation-project
)))
1506 (flet ((remove-measurement (measurement-id)
1510 (:and
(:= 'measurement-id measurement-id
)
1511 (:= 'presentation-project-id
1512 presentation-project-id
))))))
1513 (when measurement
(delete-dao measurement
)))))
1514 (cond (measurement-ids (mapc #'remove-measurement measurement-ids
))
1515 (acquisition-project
1521 :from
'sys-measurement
'sys-acquisition-project
1523 (:= 'sys-acquisition-project.common-table-name
1524 acquisition-project
)
1525 (:= 'sys-measurement.acquisition-project-id
1526 'sys-acquisition-project.acquisition-project-id
)))
1528 (remove-measurement measurement-id
)))
1530 "Don't know what to remove. ~
1531 Need either measurement-id or acquisition-project."))))))