1 ;;; PHOROS -- Photogrammetric Road Survey
2 ;;; Copyright (C) 2010, 2011, 2012 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.
20 (hunchentoot:define-easy-handler
(phoros.js
) ()
21 "Serve some Javascript."
22 (assert-authentication)
25 (setf debug-info
(@ *open-layers
*console info
))
27 (defmacro inner-html-with-id
(id)
28 "innerHTML of element with id=\"id\"."
29 `(chain document
(get-element-by-id ,id
) inner-h-t-m-l
))
31 (defmacro value-with-id
(id)
32 "Value of element with id=\"id\"."
33 `(chain document
(get-element-by-id ,id
) value
))
35 (defmacro checkbox-status-with-id
(id)
36 "Whether checkbox with id=\"id\" is checked or not."
37 `(chain document
(get-element-by-id ,id
) checked
))
43 (:p
"User role. \"Read\" can't write or modify anything.
44 \"Write\" may write user points and edit/delete their own
45 ones (and ownerless points). \"Admin\" may write user
46 points and edit/delete points written by anyone."))
47 :presentation-project-name
49 (:p
"Presentation project name."))
50 :presentation-project-emptiness
52 (:p
"This presentation project is empty. You can't do much
54 :recommend-fresh-login
56 (:p
"Sorry, but you are no longer authenticated. Your
57 session may have expired due to prolonged inactivity, or an
58 administrator has kicked you out by restarting the server.")
59 (:p
"Please repeat the login process."))
62 (:p
"Caching images.")
63 (:p
"As I'm currently idle, I'm preemptively putting images
64 into your browser's cache which later on may help speed up
67 (:code
"about:cache?device=disk")
68 (:p
"into your address bar to see what's going on there.")
69 (:p
"Your browser cache size should be set to 2000 GB or
70 bigger. Bigger is better."))
73 (:p
"Phoros version.")
74 (:p
"In a version string A.B.C, changes in A denote
75 incompatible changes in data (you can't access a database
76 set up by a different version of Phoros); changes in B mean
77 user-visible changes in feature set; changes in C denote
78 bug fixes and minor improvements."))
81 (:p
"Current action."))
82 :multiple-points-viewer
84 (:p
"Try reading the text under mouse pointer."))
87 (:p
"Delete current point."))
90 (:p
"Store user point with its attributes kind,
91 numeric-description and description, and with its auxiliary
92 data into database; warn if the given set of attributes
94 :suggest-unique-button
96 (:h3
"Non-unique set of user point attributes")
97 (:p
"Recommend a set of user point attributes that is
98 unique among the currently defined user points, preferably
99 by incrementing a portion of attribute numeric-description
100 that looks like a number."))
101 :force-duplicate-button
103 (:h3
"Non-unique set of user point attributes")
104 (:p
"Store user point with its attributes kind,
105 numeric-description and description, and with its auxiliary
106 data into database; don't care whether the given set of
107 attributes is unique."))
108 :download-user-points-button
110 (:p
"Download all user points as GeoJSON-fomatted text
111 file. Do this regularly if you don't want to lose your
112 work due to server crashes or major Phoros updates.")
113 (:p
"Points saved this way can be fed back into your
114 project using the command line interface (on server or on
115 any other host where the database is reachable)."))
119 (:p
"The standard ones, polygon, polyline, and solitary are
120 rendered as asterisk, square, and triangle respectively.
121 Anything else is rendered as an X."))
124 (:h3
"\"description\"")
125 (:p
"Optional textual description of the set of user points
126 the current point belongs to."))
127 :point-numeric-description
129 (:h3
"\"numeric-description\"")
130 (:p
"Optional description of the current user point. It is
131 occasionally used to label representations of this point in
132 streetmap and in images.")
133 (:p
"It should contain a numeric part, possibly with
134 leading zeros, which will be incremented automatically to
135 make the attribute sets of points with otherwise identical
136 attributes unique."))
139 (:p
"Creation date of current user point. Will be updated
140 when you change this point."))
143 (:p
"Check this if the user point being created is to
144 include auxiliary data."))
145 :display-nearest-aux-data
147 (:p
"Check this to see auxiliary data near streetmap
149 (:p
"You need to uncheck this before you can select user
150 points in streetmap."))
153 (:p
"Select a set of auxiliary data by its distance (in
154 metres) from the current estimated position if any, or its
155 distance from streetmap cursor otherwise.")
156 (:p
"Alternatively, a set of auxiliary data is also
157 selectable by clicking its representation in streetmap."))
160 (:p
"Auxiliary data connected to this presentation project;
161 all the numeric values followed by all the text values if
165 (:p
"Creator of current user point. Will be updated when
166 you change this point."))
167 :remove-work-layers-button
169 (:p
"Discard the current, unstored user point or unselect
170 any selected user points. Zoom out all images. Keep
171 the rest of the workspace untouched."))
174 (:p
"View some info about Phoros."))
177 (:p
"Finish this session after storing current streetmap
178 zoom status and your cursor position.")
179 (:p
"Fresh login is required to continue."))
182 (:p
"Clicking into the streetmap fetches images which most
183 probably feature the clicked point.")
184 (:p
"To pan the map, drag the mouse. To zoom, spin the
185 mouse wheel, or hold shift down whilst dragging a box, or
186 double-click (shift double-click for larger zoom steps) a
187 point of interest."))
190 (:p
"Clicking into an image sets or resets the active point
191 there. Once a feature is marked by active points in more
192 than one image, the estimated position is calculated.")
193 (:p
"To pan an image, drag the mouse. To zoom, spin the
194 mouse wheel, or hold shift down whilst dragging a box, or
195 double-click (shift double-click for larger zoom steps) a
196 point of interest."))
197 ol-Control-Pan-West-Item-Inactive
199 (:p
"Move viewport left."))
200 ol-Control-Pan-East-Item-Inactive
202 (:p
"Move viewport right."))
203 ol-Control-Pan-North-Item-Inactive
205 (:p
"Move viewport up."))
206 ol-Control-Pan-South-Item-Inactive
208 (:p
"Move viewport down."))
209 ol-Control-Zoom-In-Item-Inactive
212 ol-Control-Zoom-Out-Item-Inactive
215 streetmap-Zoom-To-Max-Extent-Item-Inactive
217 (:p
"Zoom to the extent of presentation project."))
218 ol-Control-Zoom-To-Max-Extent-Item-Inactive
220 (:p
"Zoom out completely, restoring the original view."))
221 :zoom-images-to-max-extent
223 (:p
"Zoom all images out completely, restoring the original
227 (:p
"I haven't been able to display a set of images that
228 cover a common area because I couldn't find the necessary
229 information. As a fallback, I'm displaying a set of images
230 with points of view close to the point you selected.")
231 (:p
"The server is probably trying to remedy this problem
232 but this may take some time."))
236 (:p
"Check this to automatically zoom into images once they
237 get an estimated position."))
240 (:p
"Check this to have underexposed images brightened up.")
241 (:p
"Brightening starts with the next set of images and may
242 slow things down a bit."))
245 (:p
"Check this to snap your current position onto a line
246 along points of auxiliary data, and to keep streetmap
247 centered around current position."))
250 (:p
"Decrease step size. Double-click to decrease harder."))
253 (:p
"Step size in metres. Click to increase; double-click
254 to increase harder."))
257 (:p
"Increase step size. Double-click to increase harder."))
260 (:p
"Move your position by one step on a line along points
261 of auxiliary data. Double-click to change direction."))
262 :image-layer-switcher
264 (:p
"Toggle display of image."))
267 (:p
"No photogrammetric survey possible as there isn't any
268 usable calibration data available for this image.")
269 (:p
"This means no image footprints can be calculated
270 either which prevents me from selecting images covering a
274 (:p
"Time this image was taken."))
277 (:p
"Choose a background streetmap."))
280 (:p
"Toggle visibility of data layers."))
281 :unselect-all-restrictions-button
283 (:h3
"Image Restrictions")
284 (:p
"Remove all image restrictions."))
287 (:h3
"Image Restrictions")
288 (:p
"Select one ore more of the restrictions in order to
289 consider only a subset of the images available. No
290 selection at all means no restriction.")
291 (:p
"Shift-click selects a range of restrictions,
292 control-click selects or unselects a particular
293 restriction, click selects a restriction unselecting
297 (:p
"Click to re-center streetmap, or drag the red
299 :streetmap-mouse-position
301 (:p
"Cursor position in geographic coordinates when cursor
305 (:p
"Hints on Phoros' displays and controls are shown here
306 while hovering over the respective elements."))))
308 (defun add-help-topic (topic element
)
309 "Add mouse events to DOM element that initiate display of a
312 (setf (@ element onmouseover
)
314 (lambda () (show-help x
)))
316 (setf (@ element onmouseout
) show-help
)))
318 (defun add-help-events ()
319 "Add mouse events to DOM elements that initiate display of a
322 (topic *help-topics
*)
323 (add-help-topic topic
(chain document
(get-element-by-id topic
)))
324 (dolist (element (chain document
(get-elements-by-class-name topic
)))
325 (add-help-topic topic element
))))
327 (defun show-help (&optional topic
)
328 "Put text on topic into help-display"
329 (setf (inner-html-with-id "help-display")
330 (let ((help-body (getprop *help-topics
* topic
)))
331 (if (undefined help-body
)
335 (defvar *click-control
*
339 (@ *open-layers
*control
)
347 (apply this arguments
))
348 (setf (@ this handler
)
349 (new (chain *open-layers
353 :click
(@ this trigger
)))))))))))
355 (defvar +unix-epoch
+ (lisp *unix-epoch
*)
356 "Seconds between Lisp epoch and UNIX epoch.")
358 (new (chain *open-layers
(*projection
"EPSG:4326"))))
359 (defvar +spherical-mercator
+
360 (new (chain *open-layers
(*projection
"EPSG:900913"))))
364 "First element of URL path; defaults to phoros but may be
365 turned into something different by an HTTP proxy
368 (defvar +user-name
+ (lisp (hunchentoot:session-value
'user-name
))
369 "User's (short) name.")
370 (defvar +user-role
+ (lisp (string-downcase (hunchentoot:session-value
372 "User's permissions.")
374 (defvar +presentation-project-bbox-text
+
375 (lisp (hunchentoot:session-value
'presentation-project-bbox
)))
377 (defvar +presentation-project-bounds
+
378 (chain (new (chain *open-layers
381 (or +presentation-project-bbox-text
+
382 "-180,-89,180,89"))))
383 (transform +geographic
+ +spherical-mercator
+))
384 "Bounding box of the entire presentation project.")
387 (lisp (hunchentoot:session-value
'aux-data-p
)))
389 (defvar +aux-numeric-labels
+
390 (lisp (when *aux-numeric-labels
*
391 (coerce *aux-numeric-labels
* 'vector
))))
393 (defvar +aux-text-labels
+
394 (lisp (when *aux-text-labels
*
395 (coerce *aux-text-labels
* 'vector
))))
397 (defvar *images
* (array) "Collection of the photos currently shown.")
399 (defvar *streetmap
* undefined
400 "The streetmap shown to the user.")
402 (defvar *aux-point-distance-select
* undefined
403 "The HTML element for selecting one of a few nearest
406 (defvar *global-position
* undefined
407 "Coordinates of the current estimated position")
409 (defvar *linestring-step-ratio
* 4
410 "Look for auxiliary points to include into linestring within
411 a radius of *linestring-step-ratio* multilied by multiplied by
414 (defvar *current-nearest-aux-point
*
415 (create attributes
(create aux-numeric undefined
417 "Attributes of currently selected point of auxiliary data.")
419 (defvar *bbox-strategy
* (@ *open-layers
*strategy
*bbox
*))
420 (setf (@ *bbox-strategy
* prototype ratio
) 1.5)
421 (setf (@ *bbox-strategy
* prototype res-factor
) 1.5)
423 (defvar *json-parser
* (new (chain *open-layers
*format
*json
*)))
425 (defvar *geojson-parser
* (new (chain *open-layers
*format
*geo-j-s-o-n
)))
427 (defvar *geojson-format
* (chain *open-layers
*format
*geo-j-s-o-n
))
428 (setf (@ *geojson-format
* prototype ignore-extra-dims
)
429 t
) ;doesn't handle height anyway
430 (setf (@ *geojson-format
* prototype external-projection
)
432 (setf (@ *geojson-format
* prototype internal-projection
)
436 (new (chain *open-layers
439 (create external-projection
+geographic
+
440 internal-projection
+spherical-mercator
+)))))
442 (defvar *http-protocol
* (chain *open-layers
*protocol
*http
*))
443 (setf (chain *http-protocol
* prototype format
) (new *geojson-format
*))
445 (defvar *pristine-images-p
* t
446 "T if none of the current images has been clicked into yet.")
448 (defvar *current-user-point
* undefined
449 "The currently selected user-point.")
451 (defun write-permission-p (&optional
(current-owner +user-name
+))
452 "Nil if current user can't edit stuff created by
453 current-owner or, without arguments, new stuff."
454 (or (equal +user-role
+ "admin")
455 (and (equal +user-role
+ "write")
456 (or (equal +user-name
+ current-owner
)
457 (not current-owner
)))))
460 "Anything necessary to deal with a photo."
466 (create projection
+spherical-mercator
+
468 controls
(array (new (chain *open-layers
470 (*navigation
)))))))))
471 (setf (@ this dummy
) false
) ;TODO why? (omitting splices map components directly into *image)
474 (setf (@ *image prototype delete-photo
)
476 (setf (@ *image prototype photop
)
478 (setf (@ *image prototype show-photo
)
480 (setf (@ *image prototype draw-epipolar-line
)
482 (setf (@ *image prototype draw-active-point
)
484 (setf (@ *image prototype draw-estimated-positions
)
485 draw-estimated-positions
)
487 (defun photo-path (photo-parameters)
488 "Create from stuff found in photo-parameters and in checkbox
489 brighten-images-p a path with parameters for use in an image
493 (@ photo-parameters directory
) "/"
494 (@ photo-parameters filename
) "/"
495 (@ photo-parameters byte-position
) ".png"
496 "?mounting-angle=" (@ photo-parameters mounting-angle
)
497 "&bayer-pattern=" (@ photo-parameters bayer-pattern
)
498 "&color-raiser=" (@ photo-parameters color-raiser
)
499 (if (checkbox-status-with-id "brighten-images-p")
503 (defun has-layer-p (map layer-name
)
504 "False if map doesn't have a layer called layer-name."
505 (chain map
(get-layers-by-name layer-name
) length
))
507 (defun some-active-point-p ()
508 "False if no image in *images* has an Active Point."
510 for i across
*images
*
511 sum
(has-layer-p (@ i map
) "Active Point")))
513 (defun remove-layer (map layer-name
)
514 "Destroy layer layer-name in map."
515 (when (has-layer-p map layer-name
)
516 (chain map
(get-layers-by-name layer-name
) 0 (destroy))))
518 (defun remove-any-layers (layer-name)
519 "Destroy in all *images* and in *streetmap* the layer named layer-name."
521 for i across
*images
* do
522 (remove-layer (@ i map
) layer-name
))
523 (remove-layer *streetmap
* layer-name
))
525 (defun reset-controls ()
526 (disable-element-with-id "finish-point-button")
527 (disable-element-with-id "delete-point-button")
528 (disable-element-with-id "remove-work-layers-button")
529 (setf (inner-html-with-id "creator") nil
)
530 (setf (inner-html-with-id "point-creation-date") nil
)
531 (hide-aux-data-choice)
532 (setf (inner-html-with-id "aux-numeric-list") nil
)
533 (setf (inner-html-with-id "aux-text-list") nil
))
535 (defun disable-streetmap-nearest-aux-points-layer ()
536 "Get (@ *streetmap* nearest-aux-points-layer) out of the way,
537 i.e., remove features and disable feature select control so
538 it won't shadow any other control."
539 (chain *streetmap
* nearest-aux-points-layer
(remove-all-features))
540 (chain *streetmap
* nearest-aux-points-select-control
(deactivate))
541 (chain *streetmap
* user-points-select-control
(activate)))
543 (defun reset-layers-and-controls ()
544 "Destroy user-generated layers in *streetmap* and in all
545 *images*, and put controls into pristine state."
546 (remove-any-layers "Epipolar Line")
547 (remove-any-layers "Active Point")
548 (remove-any-layers "Estimated Position")
549 (remove-any-layers "User Point")
550 (chain *streetmap
* user-points-select-control
(unselect-all))
551 (when (and (not (equal undefined
*current-user-point
*))
552 (@ *current-user-point
* layer
))
554 user-points-select-control
555 (unselect *current-user-point
*)))
558 (switch-phoros-controls-to "aux-data-viewer")
559 (switch-phoros-controls-to "point-creator"))
560 (setf *pristine-images-p
* t
)
561 (if (and +aux-data-p
+
562 (checkbox-status-with-id "display-nearest-aux-data-p"))
563 (request-aux-points-near-cursor 30)
564 (disable-streetmap-nearest-aux-points-layer))
565 (zoom-images-to-max-extent))
567 (defun enable-element-with-id (id)
568 "Activate HTML element with id=\"id\". Return t if element
569 was greyed out before."
571 (chain document
(get-element-by-id id
) disabled
)
572 (setf (chain document
(get-element-by-id id
) disabled
) nil
)))
574 (defun enable-elements-of-class (class-name)
575 "Activate HTML elements with class=\"class\"."
577 for element in
(chain document
578 (get-elements-by-class-name class-name
))
579 do
(setf (@ element disabled
) nil
)))
581 (defun disable-element-with-id (id)
582 "Grey out HTML element with id=\"id\". Return t if element
585 (not (chain document
(get-element-by-id id
) disabled
))
586 (setf (chain document
(get-element-by-id id
) disabled
) t
)))
588 (defun hide-element-with-id (id)
589 "Hide HTML element with id=\"id\"."
590 (setf (chain document
(get-element-by-id id
) style display
)
593 (defun hide-elements-of-class (class-name)
594 "Hide HTML elements with class=\"class\"."
596 for element in
(chain document
597 (get-elements-by-class-name class-name
))
598 do
(setf (@ element style display
) "none")))
600 (defun reveal-element-with-id (id)
601 "Reveal HTML element with id=\"id\"."
602 (setf (chain document
(get-element-by-id id
) style display
)
605 (defun reveal-elements-of-class (class-name)
606 "Reveal HTML elements with class=\"class\"."
608 for element in
(chain document
609 (get-elements-by-class-name class-name
))
610 do
(setf (@ element style display
) "")))
612 (defun switch-phoros-controls-to (class-name)
613 "Reveal elements of class class-name; hide anything else.
614 Unless there is auxiliary data available, hide the related
616 (let ((phoros-controls-classes
617 '("point-creator" "point-editor" "point-viewer"
618 "multiple-points-viewer" "aux-data-viewer")))
619 (dolist (c phoros-controls-classes
)
620 (unless (equal c class-name
) (hide-elements-of-class c
))))
621 (reveal-elements-of-class class-name
)
623 (hide-elements-of-class "aux-data-dependent")))
625 (defun hide-aux-data-choice ()
626 "Disable selector for auxiliary data."
627 (hide-element-with-id "include-aux-data")
628 (hide-element-with-id "aux-point-distance")
629 (setf (chain document
630 (get-element-by-id "aux-point-distance")
635 (defun refresh-layer (layer)
636 "Have layer re-request and redraw features."
637 (chain layer
(refresh (create :force t
))))
639 (defun present-photos ()
640 "Handle the response triggered by request-photos-for-point."
641 (let ((photo-parameters
644 photo-request-response response-text
)))))
646 for i across
*images
*
647 do
(chain i
(delete-photo)))
648 (if (@ photo-parameters
0 footprintp
)
649 (hide-element-with-id "no-footprints-p")
650 (reveal-element-with-id "no-footprints-p"))
652 for p across photo-parameters
653 for i across
*images
*
655 (setf (@ i photo-parameters
) p
)
656 (chain i
(show-photo)))))
658 (defun recommend-fresh-login ()
659 "Notify user about invalid authentication."
660 (setf (inner-html-with-id "recommend-fresh-login")
661 "(not authenticated)")
662 (disable-element-with-id "download-user-points-button")
663 (disable-element-with-id "blurb-button")
664 (hide-element-with-id "phoros-controls")
665 (hide-element-with-id "images"))
667 (defun consolidate-combobox (combobox-id)
668 "Help faking a combobox: copy selected option into input."
669 (let* ((combobox-select (+ combobox-id
"-select"))
670 (combobox-input (+ combobox-id
"-input"))
671 (combobox-selected-index
673 (get-element-by-id combobox-select
)
675 (when (< -
1 combobox-selected-index
)
676 (setf (value-with-id combobox-input
)
677 (getprop (chain document
678 (get-element-by-id combobox-select
)
680 combobox-selected-index
683 (get-element-by-id combobox-input
)
686 (defun unselect-combobox-selection (combobox-id)
687 "Help faking a combobox: unset selected option so any
688 selection there will trigger an onchange event."
689 (let ((combobox-select (+ combobox-id
"-select")))
690 (setf (chain document
691 (get-element-by-id combobox-select
)
695 (defun stuff-combobox (combobox-id values
&optional
(selection -
1))
696 "Stuff combobox with values. If selection is a non-negative
697 integer, select the respective item."
698 (let ((combobox-select (+ combobox-id
"-select"))
699 (combobox-input (+ combobox-id
"-input")))
700 (setf (chain document
701 (get-element-by-id combobox-select
)
706 (loop for i in values do
708 (chain document
(create-element "option")))
709 (setf (@ combobox-item text
) i
)
711 (get-element-by-id combobox-select
)
712 (add combobox-item null
)))
713 (setf (chain document
714 (get-element-by-id combobox-select
)
717 (consolidate-combobox combobox-id
)))
719 (defun stuff-user-point-comboboxes (&optional selectp
)
720 "Stuff user point attribute comboboxes with sensible values.
721 If selectp it t, select the most frequently used one."
725 user-point-choice-response response-text
))))
727 (chain response kinds
(map (lambda (x)
730 (chain response descriptions
(map (lambda (x)
731 (@ x description
)))))
733 (best-used-description -
1))
737 for i across
(@ response descriptions
)
739 do
(when (< maximum
(@ i count
))
740 (setf maximum
(@ i count
))
741 (setf best-used-description k
)))
744 for i across
(@ response kinds
)
746 do
(when (< maximum
(@ i count
))
747 (setf maximum
(@ i count
))
748 (setf best-used-kind k
))))
750 "point-kind" kinds best-used-kind
)
752 "point-description" descriptions best-used-description
)))
754 (defun request-user-point-choice (&optional selectp
)
755 "Stuff user point attribute comboboxes with sensible values.
756 If selectp it t, select the most frequently used one."
757 (setf (@ *streetmap
* user-point-choice-response
)
762 (create :url
(+ "/" +proxy-root
+
763 "/lib/user-point-attributes.json")
765 :headers
(create "Content-type" "text/plain")
767 (stuff-user-point-comboboxes selectp
))
768 :failure recommend-fresh-login
)))))
770 (defun stuff-restriction-select ()
771 "Stuff available restriction IDs into restriction-select."
775 restriction-select-choice-response
777 (restriction-select-options
779 (get-element-by-id "restriction-select")
782 for restriction in response
784 do
(setf (elt restriction-select-options i
)
785 (new (chain (*option restriction
)))))))
787 (defun request-restriction-select-choice ()
788 "Stuff available restriction IDs into restriction-select."
789 (setf (@ *streetmap
* restriction-select-choice-response
)
794 (create :url
(+ "/" +proxy-root
+
795 "/lib/selectable-restrictions.json")
797 :headers
(create "Content-type" "text/plain")
798 :success stuff-restriction-select
799 :failure recommend-fresh-login
)))))
801 (defun selected-restrictions ()
802 "Return list of restriction IDs selected by user."
803 (let ((restriction-select-options
805 (get-element-by-id "restriction-select")
808 for restriction in restriction-select-options
809 when
(@ restriction selected
)
810 collect
(@ restriction text
))))
812 (defun unselect-all-restrictions ()
813 "Clear any selected restrictions."
815 for option across
(chain document
816 (get-element-by-id "restriction-select")
818 do
(setf (@ option selected
) f
))
821 (defun request-photos-after-click (event)
822 "Handle the response to a click into *streetmap*; fetch photo
823 data. Set or update streetmap cursor."
824 (request-photos (chain *streetmap
*
825 (get-lon-lat-from-pixel (@ event xy
)))))
827 (defun request-photos (&optional lonlat
)
828 "Set streetmap cursor to lonlat if provided. Fetch photo
829 data for a point near streetmap cursor."
831 (setf (@ *streetmap
* clicked-lonlat
) lonlat
))
832 (if (checkbox-status-with-id "walk-p")
833 (request-aux-data-linestring-for-point
834 (@ *streetmap
* clicked-lonlat
))
835 (request-photos-for-point))
836 (request-cache-fodder (@ *streetmap
* clicked-lonlat
)))
838 (defun request-aux-data-linestring-for-point (lonlat-spherical-mercator)
839 "Fetch a linestring along auxiliary points near
840 lonlat-spherical-mercator."
841 (let ((lonlat-geographic
842 (chain lonlat-spherical-mercator
844 (transform +spherical-mercator
+ +geographic
+))))
845 (request-aux-data-linestring (@ lonlat-geographic lon
)
846 (@ lonlat-geographic lat
)
847 (* *linestring-step-ratio
*
849 (step-size-degrees))))
851 (defun request-photos-for-point ()
852 "Fetch photo data near (@ *streetmap* clicked-lonlat); set or
853 update streetmap cursor."
854 (remove-any-layers "Estimated Position")
855 (disable-streetmap-nearest-aux-points-layer)
856 (reset-layers-and-controls)
857 (let* ((lonlat-spherical-mercator
858 (@ *streetmap
* clicked-lonlat
))
860 (chain lonlat-spherical-mercator
862 (transform +spherical-mercator
+ +geographic
+)))
866 (create :longitude
(@ lonlat-geographic lon
)
867 :latitude
(@ lonlat-geographic lat
)
868 :zoom
(chain *streetmap
* (get-zoom))
869 :count
(lisp *number-of-images
*)
870 :selected-restriction-ids
871 (selected-restrictions))))))
874 (remove-all-features))
878 (new (chain *open-layers
884 (*point
(@ lonlat-spherical-mercator
886 (@ lonlat-spherical-mercator
889 overview-cursor-layer
890 (remove-all-features))
892 overview-cursor-layer
894 (new (chain *open-layers
900 (*point
(@ lonlat-spherical-mercator
902 (@ lonlat-spherical-mercator
904 (setf (@ *streetmap
* photo-request-response
)
910 :url
(+ "/" +proxy-root
+ "/lib/nearest-image-data")
912 :headers
(create "Content-type" "text/plain"
913 "Content-length" (@ content length
))
914 :success present-photos
915 :failure recommend-fresh-login
))))))
917 (defvar *cache-stuffer
*
918 (create xhr undefined
;instance of XMLHttpRequest
919 cache-fodder-request-response undefined
920 photo-url-ingredients undefined
921 index undefined
;current element of
922 ; photo-url-ingredients
924 cache-size
(* 2084000 1024)
925 ;we assume cache-size is set
926 ; to 2000MB by browser user
927 average-image-size undefined
928 current-center undefined
929 cache-photo-timeout undefined
930 request-cache-fodder-group-timeout undefined
)
931 "Things used to preemptively stuff the browser cache.")
933 (defun request-cache-fodder (lonlat-spherical-mercator)
934 "Abort any previous cache stuffing activities, wait a few
935 seconds, and start a new cache stuffing session centered at
936 lonlat-spherical-mercator."
937 (setf (@ *cache-stuffer
* current-center
)
938 (chain lonlat-spherical-mercator
940 (transform +spherical-mercator
+ +geographic
+)))
941 (setf (@ *cache-stuffer
* average-image-size
) 0)
942 (clear-timeout (@ *cache-stuffer
* cache-photo-timeout
))
943 (clear-timeout (@ *cache-stuffer
* request-cache-fodder-group-timeout
))
944 (hide-element-with-id "caching-indicator")
945 (setf (@ *cache-stuffer
* request-cache-fodder-group-timeout
)
946 (set-timeout request-cache-fodder-group
15000)))
948 (defun request-cache-fodder-group ()
949 "Request a bunch of image url ingredients, initiate caching
950 of the respective images. Keep trying if unsuccessful."
955 :longitude
(@ *cache-stuffer
* current-center lon
)
956 :latitude
(@ *cache-stuffer
* current-center lat
))))))
957 (setf (@ *cache-stuffer
* cache-fodder-request-response
)
963 :url
(+ "/" +proxy-root
+ "/lib/nearest-image-urls")
965 :headers
(create "Content-type" "text/plain"
966 "Content-length" (@ content length
))
967 :success handle-request-cache-fodder-group
969 (if (= (@ *cache-stuffer
* cache-fodder-request-response status
) 504)
973 request-cache-fodder-group-timeout
))
974 (setf (@ *cache-stuffer
*
975 request-cache-fodder-group-timeout
)
976 (set-timeout request-cache-fodder-group
978 (recommend-fresh-login)))))))))
980 (defun handle-request-cache-fodder-group ()
981 "Handle the response triggered by request-cache-fodder-group."
982 (when (setf (@ *cache-stuffer
* photo-url-ingredients
)
984 (read (@ *cache-stuffer
*
985 cache-fodder-request-response
987 ;; otherwise preemptive caching is probably suppressed by server
988 (setf (@ *cache-stuffer
* index
) 0)
989 (reveal-element-with-id "caching-indicator")
992 (defun cache-photo ()
993 "Cache another image if the previous one is done."
994 (if (and (< (@ *cache-stuffer
* index
)
995 (length (@ *cache-stuffer
* photo-url-ingredients
)))
996 (< (* (@ *cache-stuffer
* index
)
997 (@ *cache-stuffer
* average-image-size
))
998 (* .5 (@ *cache-stuffer
* cache-size
))))
999 (if (@ *cache-stuffer
* caching-photo-p
)
1001 (clear-timeout (@ *cache-stuffer
* cache-photo-timeout
))
1002 (setf (@ *cache-stuffer
* cache-photo-timeout
)
1003 (set-timeout cache-photo
3000)))
1005 (setf (@ *cache-stuffer
* caching-photo-p
) t
)
1006 (setf (@ *cache-stuffer
* xhr
) (new (*x-m-l-http-request
)))
1007 (chain *cache-stuffer
*
1011 (aref (@ *cache-stuffer
* photo-url-ingredients
)
1012 (@ *cache-stuffer
* index
)))
1014 (setf (@ *cache-stuffer
* xhr onload
)
1016 (setf (@ *cache-stuffer
* average-image-size
)
1017 (/ (+ (* (@ *cache-stuffer
* average-image-size
)
1018 (@ *cache-stuffer
* index
))
1019 (@ event total
)) ;bytes received
1020 (1+ (@ *cache-stuffer
* index
))))
1021 (setf (@ *cache-stuffer
* caching-photo-p
) nil
)
1022 (incf (@ *cache-stuffer
* index
))))
1023 ;; We do our best to have the browser use its cache.
1024 ;; Note however that in certain cases use of the
1025 ;; cache may be hampered by pressing the browser's
1027 (chain *cache-stuffer
*
1031 (+ "max-age=" (lisp *browser-cache-max-age
*))))
1032 (chain *cache-stuffer
* xhr
(send))
1033 (clear-timeout (@ *cache-stuffer
* cache-photo-timeout
))
1034 (setf (@ *cache-stuffer
* cache-photo-timeout
)
1036 cache-photo
;come back quickly in case
1037 500)))) ; photo is already in cache
1038 (hide-element-with-id "caching-indicator")))
1040 (defun draw-epipolar-line ()
1041 "Draw an epipolar line from response triggered by clicking
1042 into a (first) photo."
1043 (disable-streetmap-nearest-aux-points-layer)
1044 (enable-element-with-id "remove-work-layers-button")
1045 (switch-phoros-controls-to "point-creator")
1046 (let* ((epipolar-line
1047 (chain *json-parser
*
1049 (@ this epipolar-request-response response-text
))))
1051 (chain epipolar-line
1053 (new (chain *open-layers
1056 (@ x
:m
) (@ x
:n
))))))))
1058 (new (chain *open-layers
1064 (*line-string points
))))))))
1065 (setf (@ feature render-intent
) "temporary")
1066 (chain this epipolar-layer
1067 (add-features feature
))))
1069 (defun request-aux-points-near-cursor (count)
1070 "Draw into streetmap the count nearest points of auxiliary
1071 data around streetmap cursor."
1072 (let ((lonlat-geographic
1073 (chain (@ *streetmap
* clicked-lonlat
)
1075 (transform +spherical-mercator
+ +geographic
+))))
1076 (request-nearest-aux-points
1077 (create :longitude
(@ lonlat-geographic lon
)
1078 :latitude
(@ lonlat-geographic lat
))
1081 (defun request-nearest-aux-points (global-position count
)
1082 "Draw into streetmap the count nearest points of auxiliary
1083 data around global-position."
1084 (let ((global-position-etc global-position
)
1086 (setf (@ global-position-etc count
) count
)
1087 (setf content
(chain *json-parser
*
1088 (write global-position-etc
)))
1089 (setf (@ *streetmap
* aux-local-data-request-response
)
1093 (create :url
(+ "/" +proxy-root
+
1094 "/lib/aux-local-data")
1096 :headers
(create "Content-type" "text/plain"
1099 :success draw-nearest-aux-points
1100 :failure recommend-fresh-login
))))))
1102 (defun request-aux-data-linestring (longitude latitude radius step-size
)
1103 "Draw into streetmap a piece of linestring threaded along the
1104 nearest points of auxiliary data inside radius."
1105 (let* ((payload (create longitude longitude
1109 azimuth
(@ *streetmap
*
1110 linestring-central-azimuth
)))
1111 (content (chain *json-parser
* (write payload
))))
1112 (setf (@ *streetmap
* aux-data-linestring-request-response
)
1116 (create :url
(+ "/" +proxy-root
+
1117 "/lib/aux-local-linestring.json")
1119 :headers
(create "Content-type" "text/plain"
1122 :success draw-aux-data-linestring
1123 :failure recommend-fresh-login
))))))
1125 (defun draw-estimated-positions ()
1126 "Draw into streetmap and into all images points at Estimated
1127 Position. Estimated Position is the point returned so far
1128 from photogrammetric calculations that are triggered by
1129 clicking into another photo. Also draw into streetmap the
1130 nearest auxiliary points to Estimated Position."
1131 (when (write-permission-p)
1132 (setf (chain document
1133 (get-element-by-id "finish-point-button")
1135 (lambda () (finish-point #'store-point
)))
1136 (enable-element-with-id "finish-point-button"))
1137 (let* ((estimated-positions-request-response
1138 (chain *json-parser
*
1141 estimated-positions-request-response
1143 (estimated-positions
1144 (aref estimated-positions-request-response
1))
1145 (estimated-position-style
1146 (create stroke-color
(chain *open-layers
1149 style
"temporary" stroke-color
)
1152 (setf *global-position
*
1153 (aref estimated-positions-request-response
0))
1160 (new (chain *open-layers
1163 (@ *global-position
* longitude
)
1164 (@ *global-position
* latitude
))))
1165 (transform +geographic
+ +spherical-mercator
+)))))))
1166 (setf (@ feature render-intent
) "temporary")
1167 (setf (@ *streetmap
* estimated-position-layer
)
1168 (new (chain *open-layers
1171 "Estimated Position"
1172 (create display-in-layer-switcher nil
)))))
1173 (setf (@ *streetmap
* estimated-position-layer style
)
1174 estimated-position-style
)
1175 (chain *streetmap
* estimated-position-layer
(add-features feature
))
1177 (add-layer (@ *streetmap
* estimated-position-layer
))))
1178 (request-nearest-aux-points *global-position
* 7)
1181 for p in estimated-positions
1183 (when p
;otherwise a photogrammetry error has occured
1184 (setf (@ i estimated-position-layer
)
1189 "Estimated Position"
1190 (create display-in-layer-switcher nil
)))))
1191 (setf (@ i estimated-position-lonlat
)
1192 (new (chain *open-layers
(*lon-lat
(@ p m
)
1194 (setf (@ i estimated-position-layer style
)
1195 estimated-position-style
)
1198 (chain *open-layers
*geometry
(*point
(@ p m
)
1202 (chain *open-layers
*feature
(*vector point
)))))
1204 (add-layer (@ i estimated-position-layer
)))
1205 (chain i estimated-position-layer
1206 (add-features feature
))))))
1207 (zoom-anything-to-point)
1209 (get-element-by-id "finish-point-button")
1212 (defun draw-nearest-aux-points ()
1213 "Draw a few auxiliary points into streetmap."
1215 (chain *json-parser
*
1218 aux-local-data-request-response
1221 (disable-streetmap-nearest-aux-points-layer)
1222 (chain *streetmap
* user-points-select-control
(deactivate))
1223 (chain *streetmap
* nearest-aux-points-select-control
(activate))
1224 (chain *streetmap
* nearest-aux-points-hover-control
(activate))
1225 (setf (@ *aux-point-distance-select
* options length
)
1235 (*point
(@ i geometry coordinates
0)
1236 (@ i geometry coordinates
1))))
1237 (transform +geographic
+ +spherical-mercator
+)))
1240 (chain *open-layers
*feature
(*vector point
)))))
1241 (setf (@ feature attributes
)
1243 (setf (@ feature fid
) ;this is supposed to correspond to
1244 n
) ; option of *aux-point-distance-select*
1246 nearest-aux-points-layer
1247 (add-features feature
))
1248 (setf aux-point-distance-item
1249 (chain document
(create-element "option")))
1250 (setf (@ aux-point-distance-item text
)
1253 n
;let's hope add-features alway stores features in order of arrival
1257 (format (@ i properties distance
) 3 ""))))
1258 (chain *aux-point-distance-select
*
1259 (add aux-point-distance-item null
))))
1261 nearest-aux-points-select-control
1264 (elt (@ *streetmap
* nearest-aux-points-layer features
)
1266 (enable-element-with-id "aux-point-distance")))
1268 (defun draw-aux-data-linestring ()
1269 "Draw a piece of linestring along a few auxiliary points into
1270 streetmap. Pan streetmap accordingly."
1273 aux-data-linestring-request-response
1276 (chain *json-parser
* (read data
) linestring
))
1278 (chain *json-parser
* (read data
) current-point
))
1280 (chain *json-parser
* (read data
) previous-point
))
1282 (chain *json-parser
* (read data
) next-point
))
1284 (chain *json-parser
* (read data
) azimuth
))
1286 (chain *wkt-parser
* (read linestring-wkt
)))
1288 (chain *wkt-parser
* (read current-point-wkt
)))
1290 (chain *wkt-parser
* (read previous-point-wkt
)))
1292 (chain *wkt-parser
* (read next-point-wkt
)))
1293 (current-point-lonlat
1294 (new (chain *open-layers
1295 (*lon-lat
(@ current-point geometry x
)
1296 (@ current-point geometry y
))))))
1297 (chain *streetmap
* (pan-to current-point-lonlat
))
1298 (setf (@ *streetmap
* clicked-lonlat
) current-point-lonlat
)
1299 (setf (@ *streetmap
* linestring-central-azimuth
) azimuth
)
1300 (request-photos-for-point)
1301 (setf (@ *streetmap
* step-back-point
) previous-point
)
1302 (setf (@ *streetmap
* step-forward-point
) next-point
)
1303 (chain *streetmap
* aux-data-linestring-layer
(remove-all-features))
1305 aux-data-linestring-layer
1306 (add-features linestring
))))
1308 (defun step (&optional back-p
)
1309 "Enable walk-mode if necessary, and do a step along
1310 aux-data-linestring."
1311 (if (checkbox-status-with-id "walk-p")
1312 (let ((next-point-geometry
1315 (if (< (- (@ *streetmap
* linestring-central-azimuth
) pi
) 0)
1316 (setf (@ *streetmap
* linestring-central-azimuth
)
1317 (+ (@ *streetmap
* linestring-central-azimuth
) pi
))
1318 (setf (@ *streetmap
* linestring-central-azimuth
)
1319 (- (@ *streetmap
* linestring-central-azimuth
) pi
)))
1324 (transform +spherical-mercator
+ +geographic
+)))
1329 (transform +spherical-mercator
+ +geographic
+)))))
1330 (request-aux-data-linestring (@ next-point-geometry x
)
1331 (@ next-point-geometry y
)
1332 (* *linestring-step-ratio
*
1333 (step-size-degrees))
1334 (step-size-degrees)))
1336 (setf (checkbox-status-with-id "walk-p") t
) ;doesn't seem to trigger event
1337 (flip-walk-mode)))) ; so we have to do it explicitly
1339 (defun step-size-degrees ()
1340 "Return inner-html of element step-size (metres) converted
1341 into map units (degrees). You should be close to the
1343 (/ (inner-html-with-id "step-size") 1855.325 60))
1345 (defun decrease-step-size ()
1346 (when (> (inner-html-with-id "step-size") 0.5)
1347 (setf (inner-html-with-id "step-size")
1348 (/ (inner-html-with-id "step-size") 2))))
1350 (defun increase-step-size ()
1351 (when (< (inner-html-with-id "step-size") 100)
1352 (setf (inner-html-with-id "step-size")
1353 (* (inner-html-with-id "step-size") 2))))
1355 (defun user-point-style-map (label-property)
1356 "Create a style map where styles dispatch on feature property
1357 \"kind\" and features are labelled after feature
1358 property label-property."
1359 (let* ((symbolizer-property "kind")
1361 (new (chain *open-layers
1363 (*comparison
(create type
(chain *open-layers
1367 property symbolizer-property
1368 value
"solitary")))))
1370 (new (chain *open-layers
1372 (*comparison
(create type
(chain *open-layers
1376 property symbolizer-property
1377 value
"polyline")))))
1379 (new (chain *open-layers
1381 (*comparison
(create type
(chain *open-layers
1385 property symbolizer-property
1386 value
"polygon")))))
1388 (new (chain *open-layers
1390 filter solitary-filter
1392 graphic-name
"triangle"))))))
1394 (new (chain *open-layers
1396 filter polyline-filter
1398 graphic-name
"square"
1399 point-radius
4))))))
1401 (new (chain *open-layers
1403 filter polygon-filter
1405 graphic-name
"star"))))))
1407 (new (chain *open-layers
1411 graphic-name
"x"))))))
1412 (user-point-default-style
1415 (*style
(create stroke-color
"OrangeRed"
1416 fill-color
"OrangeRed"
1419 font-color
"OrangeRed"
1420 font-family
"'andale mono', 'lucida console', monospace"
1425 (create rules
(array solitary-rule
1429 (user-point-select-style
1432 (*style
(create stroke-opacity
1
1433 label label-property
)
1434 (create rules
(array solitary-rule
1438 (user-point-temporary-style
1441 (*style
(create fill-opacity
.5)
1442 (create rules
(array solitary-rule
1446 (new (chain *open-layers
1448 (create "default" user-point-default-style
1449 "temporary" user-point-temporary-style
1450 "select" user-point-select-style
))))))
1452 (defun draw-user-points ()
1453 "Draw currently selected user points into all images."
1454 (let* ((user-point-positions-response
1455 (chain *json-parser
*
1457 (@ *user-point-in-images-response
* response-text
))))
1458 (user-point-collections
1459 (chain user-point-positions-response image-points
))
1461 (chain user-point-positions-response user-point-count
))
1463 (when (> user-point-count
1) "${numericDescription}")))
1466 for user-point-collection in user-point-collections
1468 (when i
;otherwise a photogrammetry error has occured
1472 (@ user-point-collection features
)
1475 (@ raw-feature geometry coordinates
0))
1477 (@ raw-feature geometry coordinates
1))
1479 (new (chain *open-layers
1485 (@ raw-feature properties
))
1487 (new (chain *open-layers
1489 (*vector point attributes
)))))
1490 (setf (@ feature fid
) fid
)
1491 (setf (@ feature render-intent
) "select")
1494 (@ i user-point-layer
)
1495 (new (chain *open-layers
1499 (create display-in-layer-switcher nil
1500 style-map
(user-point-style-map
1502 (chain i map
(add-layer (@ i user-point-layer
)))
1503 (chain i user-point-layer
(add-features features
)))))))
1505 (defun finish-point (database-writer)
1506 "Try, with some user interaction, to uniquify user-point
1507 attributes and call database-writer."
1509 (create user-point-id
(if (defined *current-user-point
*)
1510 (@ *current-user-point
* fid
)
1513 (value-with-id "point-kind-input")
1515 (value-with-id "point-description-input")
1517 (value-with-id "point-numeric-description")))
1519 (chain *json-parser
*
1520 (write point-data
)))
1521 (delete-point-button-active-p
1522 (disable-element-with-id "delete-point-button")))
1523 (disable-element-with-id "finish-point-button")
1524 (setf *uniquify-point-attributes-response
* nil
)
1525 (setf *uniquify-point-attributes-response
*
1531 :url
(+ "/" +proxy-root
+ "/lib/uniquify-point-attributes")
1533 :headers
(create "Content-type" "text/plain"
1534 "Content-length" (@ content
1538 (enable-element-with-id "finish-point-button")
1539 (when delete-point-button-active-p
1540 (enable-element-with-id "delete-point-button"))
1545 (@ *uniquify-point-attributes-response
*
1547 (if (equal null response
)
1553 "force-duplicate-button")
1556 (hide-element-with-id "uniquify-buttons")
1557 (reveal-element-with-id "finish-point-button")
1559 (hide-element-with-id "finish-point-button")
1560 (reveal-element-with-id "uniquify-buttons")))))
1561 :failure recommend-fresh-login
))))))
1563 (defun insert-unique-suggestion ()
1564 "Insert previously received set of unique user-point
1565 attributes into their respective input elements; switch
1566 buttons accordingly."
1568 (create user-point-id
(if (defined *current-user-point
*)
1569 (@ *current-user-point
* fid
)
1572 (value-with-id "point-kind-input")
1574 (value-with-id "point-description-input")
1576 (value-with-id "point-numeric-description")))
1578 (chain *json-parser
*
1579 (write point-data
)))
1580 (delete-point-button-active-p
1581 (disable-element-with-id "delete-point-button")))
1582 (disable-element-with-id "finish-point-button")
1583 (hide-element-with-id "uniquify-buttons")
1584 (reveal-element-with-id "finish-point-button")
1585 (setf *uniquify-point-attributes-response
* nil
)
1586 (setf *uniquify-point-attributes-response
*
1593 "/lib/uniquify-point-attributes")
1595 :headers
(create "Content-type" "text/plain"
1596 "Content-length" (@ content
1600 (enable-element-with-id "finish-point-button")
1601 (when delete-point-button-active-p
1602 (enable-element-with-id "delete-point-button"))
1607 (@ *uniquify-point-attributes-response
*
1609 (unless (equal null response
)
1610 (setf (value-with-id
1611 "point-numeric-description")
1612 (@ response numeric-description
)))))
1613 :failure recommend-fresh-login
))))))
1615 (defun store-point ()
1616 "Send freshly created user point to the database."
1617 (let ((global-position-etc *global-position
*))
1618 (setf (@ global-position-etc kind
)
1619 (value-with-id "point-kind-input"))
1620 (setf (@ global-position-etc description
)
1621 (value-with-id "point-description-input"))
1622 (setf (@ global-position-etc numeric-description
)
1623 (value-with-id "point-numeric-description"))
1624 (when (checkbox-status-with-id "include-aux-data-p")
1625 (setf (@ global-position-etc aux-numeric
)
1626 (@ *current-nearest-aux-point
*
1629 (setf (@ global-position-etc aux-text
)
1630 (@ *current-nearest-aux-point
*
1634 (chain *json-parser
*
1635 (write global-position-etc
))))
1636 (disable-element-with-id "finish-point-button")
1641 (create :url
(+ "/" +proxy-root
+ "/lib/store-point")
1643 :headers
(create "Content-type" "text/plain"
1644 "Content-length" (@ content length
))
1647 (@ *streetmap
* user-point-layer
))
1648 (reset-layers-and-controls)
1649 (request-user-point-choice))
1650 :failure recommend-fresh-login
))))))
1652 (defun update-point ()
1653 "Send changes to currently selected user point to database."
1655 (create user-point-id
(@ *current-user-point
* fid
)
1657 (value-with-id "point-kind-input")
1659 (value-with-id "point-description-input")
1661 (value-with-id "point-numeric-description")))
1663 (chain *json-parser
*
1664 (write point-data
))))
1665 (disable-element-with-id "finish-point-button")
1666 (disable-element-with-id "delete-point-button")
1670 (create :url
(+ "/" +proxy-root
+ "/lib/update-point")
1672 :headers
(create "Content-type" "text/plain"
1673 "Content-length" (@ content
1677 (@ *streetmap
* user-point-layer
))
1678 (reset-layers-and-controls)
1679 (request-user-point-choice))
1680 :failure recommend-fresh-login
)))))
1682 (defun delete-point ()
1683 "Purge currently selected user point from database."
1684 (let* ((user-point-id (@ *current-user-point
* fid
))
1686 (chain *json-parser
*
1687 (write user-point-id
))))
1688 (disable-element-with-id "finish-point-button")
1689 (disable-element-with-id "delete-point-button")
1693 (create :url
(+ "/" +proxy-root
+ "/lib/delete-point")
1695 :headers
(create "Content-type" "text/plain"
1696 "Content-length" (@ content
1700 (@ *streetmap
* user-point-layer
))
1701 (reset-layers-and-controls)
1702 (request-user-point-choice true
))
1703 :failure recommend-fresh-login
)))))
1705 (defun draw-active-point ()
1706 "Draw an Active Point, i.e. a point used in subsequent
1707 photogrammetric calculations."
1711 (new (chain *open-layers
1714 (new (chain *open-layers
1717 (@ this photo-parameters m
)
1718 (@ this photo-parameters n
))))))))))
1720 (defun image-click-action (clicked-image)
1722 "Do appropriate things when an image is clicked into."
1724 (chain clicked-image map
(get-lon-lat-from-view-port-px
1727 (@ clicked-image photo-parameters
))
1728 pristine-image-p content request
)
1729 (when (and (@ photo-parameters usable
)
1730 (chain clicked-image
(photop)))
1731 (setf (@ photo-parameters m
) (@ lonlat lon
)
1732 (@ photo-parameters n
) (@ lonlat lat
))
1733 (remove-layer (@ clicked-image map
) "Active Point")
1734 (remove-any-layers "Epipolar Line")
1735 (setf *pristine-images-p
* (not (some-active-point-p)))
1736 (setf (@ clicked-image active-point-layer
)
1737 (new (chain *open-layers
1739 (*vector
"Active Point"
1740 (create display-in-layer-switcher
1742 (chain clicked-image
1744 (add-layer (@ clicked-image active-point-layer
)))
1745 (chain clicked-image
(draw-active-point))
1750 (remove-any-layers "User Point") ;from images
1752 ;; There's something in the following line that
1753 ;; restores layer "User Point" and removes layer
1754 ;; "Active Point" when coming from directly a
1755 ;; point-editor situation.
1756 (chain *streetmap
* user-points-select-control
(unselect-all))
1758 for i across
*images
* do
1759 (when (and (not (equal i clicked-image
))
1762 (@ i epipolar-layer
)
1763 (new (chain *open-layers
1765 (*vector
"Epipolar Line"
1767 display-in-layer-switcher nil
))))
1768 content
(chain *json-parser
*
1770 (append (array photo-parameters
)
1771 (@ i photo-parameters
))))
1772 (@ i epipolar-request-response
)
1776 (create :url
(+ "/" +proxy-root
+
1777 "/lib/epipolar-line")
1780 "Content-type" "text/plain"
1783 :success
(@ i draw-epipolar-line
)
1784 :failure recommend-fresh-login
1788 (add-layer (@ i epipolar-layer
))))))
1790 (remove-any-layers "Epipolar Line")
1791 (remove-any-layers "Estimated Position")
1792 (let* ((active-pointed-photo-parameters
1794 for i across
*images
*
1795 when
(has-layer-p (@ i map
) "Active Point")
1796 collect
(@ i photo-parameters
)))
1798 (chain *json-parser
*
1800 (list active-pointed-photo-parameters
1805 photo-parameters
)))))))))
1806 (setf (@ clicked-image estimated-positions-request-response
)
1810 (create :url
(+ "/" +proxy-root
+
1811 "/lib/estimated-positions")
1814 "Content-type" "text/plain"
1817 :success
(@ clicked-image
1818 draw-estimated-positions
)
1819 :failure recommend-fresh-login
1820 :scope clicked-image
)))))))))))
1822 (defun iso-time-string (lisp-time)
1823 "Return Lisp universal time formatted as ISO time string"
1824 (let* ((unix-time (- lisp-time
+unix-epoch
+))
1825 (js-date (new (*date
(* 1000 unix-time
)))))
1826 (chain *open-layers
*date
(to-i-s-o-string js-date
))))
1828 (defun delete-photo ()
1829 "Delete this object's photo."
1831 repeat
(chain this map
(get-num-layers))
1832 do
(chain this map layers
0 (destroy)))
1833 (hide-element-with-id (@ this usable-id
))
1834 (setf (@ this trigger-time-div inner-h-t-m-l
) nil
))
1837 "Check if this object contains a photo."
1838 (@ this trigger-time-div inner-h-t-m-l
))
1840 (defun show-photo ()
1841 "Show the photo described in this object's photo-parameters."
1842 (let ((image-div-width
1843 (parse-int (chain (get-computed-style (@ this map div
) nil
)
1846 (parse-int (chain (get-computed-style (@ this map div
) nil
)
1849 (@ this photo-parameters sensor-width-pix
))
1851 (@ this photo-parameters sensor-height-pix
)))
1861 (photo-path (@ this photo-parameters
))
1862 (new (chain *open-layers
1865 (+ image-width
.5) (+ image-height
.5))))
1866 (new (chain *open-layers
1867 (*size image-div-width
1870 max-resolution
(chain
1873 (/ image-width image-div-width
)
1874 (/ image-height image-div-height
)))))))))
1875 (when (@ this photo-parameters rendered-footprint
)
1876 (setf (@ this footprint-layer
)
1880 (*vector
"Footprint"
1881 (create display-in-layer-switcher nil
1882 style
(create stroke-color
"yellow"
1884 stroke-opacity
.3))))))
1888 (chain *geojson-parser
*
1891 rendered-footprint
)))))
1894 (add-layer (@ this footprint-layer
))))
1895 (chain this map
(zoom-to-max-extent))
1896 (if (@ this photo-parameters usable
)
1897 (hide-element-with-id (@ this usable-id
))
1898 (reveal-element-with-id (@ this usable-id
)))
1899 (setf (@ this trigger-time-div inner-h-t-m-l
)
1900 (iso-time-string (@ this photo-parameters trigger-time
)))))
1902 (defun zoom-images-to-max-extent ()
1903 "Zoom out all images."
1905 for i across
*images
*
1906 do
(when (> (@ i map layers length
) 0)
1907 (chain i map
(zoom-to-max-extent)))))
1909 (defun zoom-anything-to-point ()
1910 "For streetmap and for images that have an Active Point or an
1911 Estimated Position, zoom in and recenter."
1912 (when (checkbox-status-with-id "zoom-to-point-p")
1914 (new (chain *open-layers
1915 (*lon-lat
(@ *global-position
* longitude
)
1916 (@ *global-position
* latitude
))
1917 (transform +geographic
+ +spherical-mercator
+)))))
1920 (set-center point-lonlat
18 nil t
))))
1921 (loop for i across
*images
* do
1924 ((has-layer-p (@ i map
) "Active Point")
1925 (new (chain *open-layers
(*lon-lat
1926 (@ i photo-parameters m
)
1927 (@ i photo-parameters n
)))))
1928 ((has-layer-p (@ i map
) "Estimated Position")
1929 (@ i estimated-position-lonlat
))
1932 (chain i map
(set-center point-lonlat
4 nil t
)))))))
1934 (defun initialize-image (image-index)
1935 "Create an image usable for displaying photos at position
1936 image-index in array *images*."
1937 (setf (aref *images
* image-index
) (new *image
))
1938 (setf (@ (aref *images
* image-index
) usable-id
)
1939 (+ "image-" image-index
"-usable"))
1940 (hide-element-with-id (+ "image-" image-index
"-usable"))
1941 (setf (@ (aref *images
* image-index
) trigger-time-div
)
1944 (get-element-by-id (+ "image-" image-index
"-trigger-time"))))
1945 (setf (@ (aref *images
* image-index
) image-click-action
)
1946 (image-click-action (aref *images
* image-index
)))
1947 (setf (@ (aref *images
* image-index
) click
)
1948 (new (*click-control
*
1949 (create :trigger
(@ (aref *images
* image-index
)
1950 image-click-action
)))))
1951 (chain (aref *images
* image-index
)
1954 (@ (aref *images
* image-index
) click
)))
1955 (chain (aref *images
* image-index
) click
(activate))
1956 ;;(chain (aref *images* image-index)
1959 ;; (new (chain *open-layers
1965 ;; (get-element-by-id
1966 ;; (+ "image-" image-index "-zoom")))))))))
1967 (chain (aref *images
* image-index
)
1970 (new (chain *open-layers
1977 (+ "image-" image-index
"-layer-switcher")))
1978 rounded-corner nil
))))))
1979 (let ((pan-west-control
1980 (new (chain *open-layers
*control
(*pan
"West"))))
1982 (new (chain *open-layers
*control
(*pan
"North"))))
1984 (new (chain *open-layers
*control
(*pan
"South"))))
1986 (new (chain *open-layers
*control
(*pan
"East"))))
1988 (new (chain *open-layers
*control
(*zoom-in
))))
1990 (new (chain *open-layers
*control
(*zoom-out
))))
1991 (zoom-to-max-extent-control
1992 (new (chain *open-layers
*control
(*zoom-to-max-extent
))))
1994 (new (chain *open-layers
2001 (+ "image-" image-index
"-zoom")))))))))
2002 (chain (aref *images
* image-index
)
2004 (add-control pan-zoom-panel
))
2005 (chain pan-zoom-panel
2006 (add-controls (array pan-west-control
2012 zoom-to-max-extent-control
))))
2013 (chain (aref *images
* image-index
)
2015 (render (chain document
2017 (+ "image-" image-index
))))))
2019 (defun user-point-selected (event)
2020 "Things to do once a user point is selected."
2021 (remove-any-layers "Active Point")
2022 (remove-any-layers "Epipolar Line")
2023 (remove-any-layers "Estimated Position")
2024 (unselect-combobox-selection "point-kind")
2025 (unselect-combobox-selection "point-description")
2026 (user-point-selection-changed))
2028 (defun user-point-unselected (event)
2029 "Things to do once a user point is unselected."
2031 (user-point-selection-changed))
2033 (defun user-point-selection-changed ()
2034 "Things to do once a user point is selected or unselected."
2035 (setf *current-user-point
*
2036 (@ *streetmap
* user-point-layer selected-features
0))
2037 (let ((selected-features-count
2038 (@ *streetmap
* user-point-layer selected-features length
)))
2039 (setf (@ *streetmap
* user-point-layer style-map
)
2040 (user-point-style-map
2041 (when (> selected-features-count
1)
2042 "${numericDescription}")))
2044 ((> selected-features-count
1)
2045 (switch-phoros-controls-to "multiple-points-viewer"))
2046 ((= selected-features-count
1)
2047 (setf (value-with-id "point-kind-input")
2048 (@ *current-user-point
* attributes kind
))
2049 (setf (value-with-id "point-description-input")
2050 (@ *current-user-point
* attributes description
))
2051 (setf (value-with-id "point-numeric-description")
2052 (@ *current-user-point
* attributes numeric-description
))
2053 (setf (inner-html-with-id "point-creation-date")
2054 (@ *current-user-point
* attributes creation-date
))
2055 (setf (inner-html-with-id "aux-numeric-list")
2057 (@ *current-user-point
* attributes aux-numeric
)
2058 +aux-numeric-labels
+))
2059 (setf (inner-html-with-id "aux-text-list")
2061 (@ *current-user-point
* attributes aux-text
)
2063 (switch-phoros-controls-to "point-editor")
2064 (if (write-permission-p
2065 (@ *current-user-point
* attributes user-name
))
2067 (setf (chain document
2068 (get-element-by-id "finish-point-button")
2070 (lambda () (finish-point #'update-point
)))
2071 (enable-element-with-id "finish-point-button")
2072 (enable-element-with-id "delete-point-button")
2073 (switch-phoros-controls-to "point-editor"))
2075 (disable-element-with-id "finish-point-button")
2076 (disable-element-with-id "delete-point-button")
2077 (switch-phoros-controls-to "point-viewer")))
2078 (setf (inner-html-with-id "creator")
2079 (if (@ *current-user-point
* attributes user-name
)
2081 (@ *current-user-point
* attributes user-name
)
2085 (reset-layers-and-controls))))
2086 (chain *streetmap
* user-point-layer
(redraw))
2087 (remove-any-layers "User Point") ;from images
2089 (chain *json-parser
*
2091 (array (chain *streetmap
*
2094 (map (lambda (x) (@ x fid
))))
2096 for i across
*images
*
2097 collect
(@ i photo-parameters
))))))
2098 (setf *user-point-in-images-response
*
2102 (create :url
(+ "/" +proxy-root
+
2103 "/lib/user-point-positions")
2105 :headers
(create "Content-type" "text/plain"
2106 "Content-length" (@ content
2108 :success draw-user-points
2109 :failure recommend-fresh-login
)))))
2111 (defun aux-point-distance-selected ()
2112 "Things to do on change of aux-point-distance select element."
2114 nearest-aux-points-select-control
2117 nearest-aux-points-select-control
2120 (elt (@ *streetmap
* nearest-aux-points-layer features
)
2121 (@ *aux-point-distance-select
*
2123 selected-index
))))))
2125 (defun enable-aux-point-selection ()
2126 "Check checkbox include-aux-data-p and act accordingly."
2127 (setf (checkbox-status-with-id "include-aux-data-p") t
)
2128 (flip-aux-data-inclusion))
2130 (defun flip-walk-mode ()
2131 "Query status of checkbox walk-p and induce first walking
2132 step if it's just been turned on. Otherwise delete our
2134 (if (checkbox-status-with-id "walk-p")
2135 (request-aux-data-linestring-for-point (@ *streetmap
*
2138 aux-data-linestring-layer
2139 (remove-all-features))))
2141 (defun flip-aux-data-inclusion ()
2142 "Query status of checkbox include-aux-data-p and act accordingly."
2143 (if (checkbox-status-with-id "include-aux-data-p")
2145 nearest-aux-points-layer
2148 nearest-aux-points-layer
2149 (set-visibility nil
))))
2151 (defun flip-nearest-aux-data-display ()
2152 "Query status of checkbox include-aux-data-p and act accordingly."
2153 (reset-layers-and-controls))
2155 (defun html-table (aux-data labels
)
2156 "Return an html-formatted table with a label column from
2157 labels and a data column from aux-data."
2161 :class
"aux-data-table"
2163 (reduce (lambda (x y i
)
2167 (:td
:class
"aux-data-label"
2174 (:td
:class
"aux-data-value"
2180 (defun nearest-aux-point-selected (event)
2181 "Things to do once a nearest auxiliary point is selected in streetmap."
2182 (setf *current-nearest-aux-point
* (@ event feature
))
2184 (@ event feature attributes aux-numeric
))
2186 (@ event feature attributes aux-text
))
2188 (@ event feature attributes distance
)))
2189 (setf (@ *aux-point-distance-select
* options selected-index
)
2190 (@ event feature fid
))
2191 (setf (inner-html-with-id "aux-numeric-list")
2192 (html-table aux-numeric
+aux-numeric-labels
+))
2193 (setf (inner-html-with-id "aux-text-list")
2194 (html-table aux-text
+aux-text-labels
+))))
2197 "Store user's current map extent and log out."
2198 (let* ((bbox (chain *streetmap
*
2200 (transform +spherical-mercator
+ +geographic
+)
2202 (href (+ "/" +proxy-root
+ "/lib/logout?bbox=" bbox
)))
2203 (when (@ *streetmap
* cursor-layer features length
)
2204 (let* ((lonlat-geographic (chain *streetmap
*
2210 (transform +spherical-mercator
+
2213 "&longitude=" (@ lonlat-geographic x
)
2214 "&latitude=" (@ lonlat-geographic y
)))))
2215 (setf (@ location href
) href
)))
2218 "Prepare user's playground."
2219 (unless +presentation-project-bbox-text
+
2220 (setf (inner-html-with-id "presentation-project-emptiness")
2226 (create projection
+geographic
+
2227 display-projection
+geographic
+
2228 controls
(array (new (chain *open-layers
2231 (new (chain *open-layers
2233 (*attribution
)))))))))
2234 (when (write-permission-p)
2235 (enable-elements-of-class "write-permission-dependent")
2236 (request-user-point-choice true
))
2237 (hide-element-with-id "no-footprints-p")
2238 (hide-element-with-id "caching-indicator")
2239 (hide-element-with-id "uniquify-buttons")
2240 (setf *aux-point-distance-select
*
2241 (chain document
(get-element-by-id "aux-point-distance")))
2242 (let ((cursor-layer-style
2245 external-graphic
(+ "/" +proxy-root
+
2246 "/lib/public_html/phoros-cursor.png"))))
2247 (setf (@ *streetmap
* cursor-layer
)
2253 style cursor-layer-style
)))))
2254 (setf (@ *streetmap
* overview-cursor-layer
)
2260 style cursor-layer-style
))))))
2261 (let ((survey-layer-style
2262 (create stroke-color
(chain *open-layers
*feature
*vector
2263 style
"default" stroke-color
)
2267 graphic-name
"circle")))
2268 (setf (@ *streetmap
* survey-layer
)
2274 strategies
(array (new (*bbox-strategy
*)))
2276 (new (*http-protocol
*
2277 (create :url
(+ "/" +proxy-root
+
2278 "/lib/points.json"))))
2279 style survey-layer-style
))))))
2280 (setf (@ *streetmap
* user-point-layer
)
2286 strategies
(array (new *bbox-strategy
*))
2288 (new (*http-protocol
*
2289 (create :url
(+ "/" +proxy-root
+ "/lib/user-points.json"))))
2290 style-map
(user-point-style-map nil
))))))
2291 (setf (@ *streetmap
* user-points-hover-control
)
2292 (new (chain *open-layers
2294 (*select-feature
(@ *streetmap
* user-point-layer
)
2295 (create render-intent
"temporary"
2297 highlight-only t
)))))
2298 (setf (@ *streetmap
* user-points-select-control
)
2299 (new (chain *open-layers
2301 (*select-feature
(@ *streetmap
* user-point-layer
)
2304 (let ((aux-layer-style
2305 (create stroke-color
"grey"
2309 graphic-name
"circle")))
2310 (setf (@ *streetmap
* aux-point-layer
)
2316 strategies
(array (new (*bbox-strategy
*)))
2318 (new (*http-protocol
*
2319 (create :url
(+ "/" +proxy-root
+
2320 "/lib/aux-points.json"))))
2321 style aux-layer-style
2322 visibility nil
))))))
2323 (let ((nearest-aux-point-layer-style-map
2324 (new (chain *open-layers
2327 (create stroke-color
"grey"
2331 graphic-name
"circle")
2333 (create stroke-color
"black"
2337 graphic-name
"circle")
2339 (create stroke-color
"grey"
2344 graphic-name
"circle")))))))
2345 (setf (@ *streetmap
* nearest-aux-points-layer
)
2346 (new (chain *open-layers
2349 "Nearest Aux Points"
2351 display-in-layer-switcher nil
2352 style-map nearest-aux-point-layer-style-map
2354 (setf (@ *streetmap
* nearest-aux-points-hover-control
)
2355 (new (chain *open-layers
2358 (@ *streetmap
* nearest-aux-points-layer
)
2359 (create render-intent
"temporary"
2361 highlight-only t
)))))
2362 (setf (@ *streetmap
* nearest-aux-points-select-control
)
2363 (new (chain *open-layers
2366 (@ *streetmap
* nearest-aux-points-layer
)))))
2367 (setf (@ *streetmap
* aux-data-linestring-layer
)
2368 (new (chain *open-layers
2371 "Aux Data Linestring"
2373 display-in-layer-switcher nil
2374 style-map nearest-aux-point-layer-style-map
2376 (setf (@ *streetmap
* google-streetmap-layer
)
2377 (new (chain *open-layers
2379 (*google
"Google Streets"
2380 (create num-zoom-levels
23)))))
2381 (setf (@ *streetmap
* osm-layer
)
2382 (new (chain *open-layers
2387 (create num-zoom-levels
23
2389 "Data CC-By-SA by openstreetmap.org")))))
2390 (setf (@ *streetmap
* overview-osm-layer
)
2391 (new (chain *open-layers
2393 (*osm
* "OpenStreetMap"))))
2394 (setf (@ *streetmap
* click-streetmap
)
2395 (new (*click-control
*
2396 (create :trigger request-photos-after-click
))))
2397 (setf (@ *streetmap
* nirvana-layer
)
2402 (create is-base-layer t
2403 projection
(@ *streetmap
* osm-layer projection
)
2404 max-extent
(@ *streetmap
* osm-layer max-extent
)
2405 max-resolution
(@ *streetmap
*
2408 units
(@ *streetmap
* osm-layer units
)
2409 num-zoom-levels
(@ *streetmap
*
2411 num-zoom-levels
))))))
2414 (new (chain *open-layers
2421 "streetmap-layer-switcher"))
2422 rounded-corner nil
))))))
2423 (let ((pan-west-control
2424 (new (chain *open-layers
*control
(*pan
"West"))))
2426 (new (chain *open-layers
*control
(*pan
"North"))))
2428 (new (chain *open-layers
*control
(*pan
"South"))))
2430 (new (chain *open-layers
*control
(*pan
"East"))))
2432 (new (chain *open-layers
*control
(*zoom-in
))))
2434 (new (chain *open-layers
*control
(*zoom-out
))))
2435 (zoom-to-max-extent-control
2441 display-class
"streetmapZoomToMaxExtent"
2445 +presentation-project-bounds
+))))))))
2447 (new (chain *open-layers
2454 "streetmap-zoom")))))))
2456 (new (chain *open-layers
2462 (@ *streetmap
* overview-osm-layer
)
2463 (@ *streetmap
* overview-cursor-layer
))
2469 "streetmap-overview")))))))
2470 (mouse-position-control
2471 (new (chain *open-layers
2474 (create div
(chain document
2476 "streetmap-mouse-position"))
2477 empty-string
"longitude, latitude")))))
2479 (new (chain *open-layers
2483 (add-control pan-zoom-panel
))
2484 (chain pan-zoom-panel
2485 (add-controls (array pan-west-control
2491 zoom-to-max-extent-control
)))
2493 (add-control (@ *streetmap
* click-streetmap
)))
2494 (chain *streetmap
* click-streetmap
(activate))
2499 (register "featureselected"
2500 (@ *streetmap
* user-point-layer
)
2501 user-point-selected
))
2505 (register "featureunselected"
2506 (@ *streetmap
* user-point-layer
)
2507 user-point-unselected
))
2509 nearest-aux-points-layer
2511 (register "featureselected"
2512 (@ *streetmap
* nearest-aux-points-layer
)
2513 nearest-aux-point-selected
))
2516 (@ *streetmap
* nearest-aux-points-hover-control
)))
2519 (@ *streetmap
* nearest-aux-points-select-control
)))
2522 (@ *streetmap
* user-points-hover-control
)))
2525 (@ *streetmap
* user-points-select-control
)))
2526 (chain *streetmap
* nearest-aux-points-hover-control
(activate))
2527 (chain *streetmap
* nearest-aux-points-select-control
(activate))
2528 (chain *streetmap
* user-points-hover-control
(activate))
2529 (chain *streetmap
* user-points-select-control
(activate))
2530 (chain *streetmap
* (add-layer (@ *streetmap
* osm-layer
)))
2531 (try (chain *streetmap
*
2532 (add-layer (@ *streetmap
* google-streetmap-layer
)))
2535 (remove-layer (@ *streetmap
*
2536 google-streetmap-layer
)))))
2537 (chain *streetmap
* (add-layer (@ *streetmap
* nirvana-layer
)))
2539 (add-layer (@ *streetmap
* nearest-aux-points-layer
)))
2540 (chain *streetmap
* (add-layer (@ *streetmap
* survey-layer
)))
2542 (add-layer (@ *streetmap
* cursor-layer
)))
2544 (add-layer (@ *streetmap
* aux-point-layer
)))
2546 (add-layer (@ *streetmap
* aux-data-linestring-layer
)))
2548 (add-layer (@ *streetmap
* user-point-layer
)))
2549 (setf (@ overview-map element
)
2550 (chain document
(get-element-by-id
2551 "streetmap-overview-element")))
2552 (chain *streetmap
* (add-control overview-map
))
2553 (chain *streetmap
* (add-control mouse-position-control
))
2554 (chain *streetmap
* (add-control scale-line-control
)))
2556 for i from
0 below
(lisp *number-of-images
*)
2557 do
(initialize-image i
))
2559 (request-restriction-select-choice)
2562 (if (lisp (stored-bbox))
2563 (new (chain *open-layers
2565 (from-string (lisp (stored-bbox)))
2566 (transform +geographic
+ +spherical-mercator
+)))
2567 +presentation-project-bounds
+)))
2568 (let ((stored-cursor (lisp (stored-cursor))))
2571 (new (chain *open-layers
2573 (from-string stored-cursor
)
2574 (transform +geographic
+
2575 +spherical-mercator
+))))))
2576 (reset-layers-and-controls)))))
2578 (pushnew (hunchentoot:create-regex-dispatcher
2579 (format nil
"/phoros/lib/phoros-~A-\\S*-\\S*\.js"
2582 hunchentoot
:*dispatch-table
*)