Refactor internal packages
[phoros.git] / phoros-js.lisp
blobb7e13406cea871803f236049d37d099c65902481
1 ;;; PHOROS -- Photogrammetric Road Survey
2 ;;; Copyright (C) 2010, 2011, 2012 Bert Burgemeister
3 ;;;
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.
8 ;;;
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.
13 ;;;
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.
18 (in-package :phoros)
20 (hunchentoot:define-easy-handler (phoros.js) ()
21 "Serve some Javascript."
22 (assert-authentication)
23 (ps*
24 '(progn
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))
39 (defvar *help-topics*
40 (create
41 :user-role
42 (who-ps-html
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
48 (who-ps-html
49 (:p "Presentation project name."))
50 :presentation-project-emptiness
51 (who-ps-html
52 (:p "This presentation project is empty. You can't do much
53 with it."))
54 :recommend-fresh-login
55 (who-ps-html
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."))
60 :caching-indicator
61 (who-ps-html
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
65 things a bit.")
66 (:p "Type")
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."))
71 :phoros-version
72 (who-ps-html
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."))
79 :h2-phoros-controls
80 (who-ps-html
81 (:p "Current action."))
82 :multiple-points-viewer
83 (who-ps-html
84 (:p "Try reading the text under mouse pointer."))
85 :delete-point-button
86 (who-ps-html
87 (:p "Delete current point."))
88 :finish-point-button
89 (who-ps-html
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
93 isn't unique."))
94 :suggest-unique-button
95 (who-ps-html
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
102 (who-ps-html
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
109 (who-ps-html
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)."))
116 :point-kind
117 (who-ps-html
118 (:h3 "\"kind\"")
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."))
122 :point-description
123 (who-ps-html
124 (:h3 "\"description\"")
125 (:p "Optional textual description of the set of user points
126 the current point belongs to."))
127 :point-numeric-description
128 (who-ps-html
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."))
137 :point-creation-date
138 (who-ps-html
139 (:p "Creation date of current user point. Will be updated
140 when you change this point."))
141 :include-aux-data
142 (who-ps-html
143 (:p "Check this if the user point being created is to
144 include auxiliary data."))
145 :display-nearest-aux-data
146 (who-ps-html
147 (:p "Check this to see auxiliary data near streetmap
148 cursor.")
149 (:p "You need to uncheck this before you can select user
150 points in streetmap."))
151 :aux-point-distance
152 (who-ps-html
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."))
158 :aux-data
159 (who-ps-html
160 (:p "Auxiliary data connected to this presentation project;
161 all the numeric values followed by all the text values if
162 any."))
163 :creator
164 (who-ps-html
165 (:p "Creator of current user point. Will be updated when
166 you change this point."))
167 :remove-work-layers-button
168 (who-ps-html
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."))
172 :blurb-button
173 (who-ps-html
174 (:p "View some info about Phoros."))
175 :logout-button
176 (who-ps-html
177 (:p "Finish this session after storing current streetmap
178 zoom status and your cursor position.")
179 (:p "Fresh login is required to continue."))
180 :streetmap
181 (who-ps-html
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."))
188 :image
189 (who-ps-html
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
198 (who-ps-html
199 (:p "Move viewport left."))
200 ol-Control-Pan-East-Item-Inactive
201 (who-ps-html
202 (:p "Move viewport right."))
203 ol-Control-Pan-North-Item-Inactive
204 (who-ps-html
205 (:p "Move viewport up."))
206 ol-Control-Pan-South-Item-Inactive
207 (who-ps-html
208 (:p "Move viewport down."))
209 ol-Control-Zoom-In-Item-Inactive
210 (who-ps-html
211 (:p "Zoom in."))
212 ol-Control-Zoom-Out-Item-Inactive
213 (who-ps-html
214 (:p "Zoom out."))
215 streetmap-Zoom-To-Max-Extent-Item-Inactive
216 (who-ps-html
217 (:p "Zoom to the extent of presentation project."))
218 ol-Control-Zoom-To-Max-Extent-Item-Inactive
219 (who-ps-html
220 (:p "Zoom out completely, restoring the original view."))
221 :zoom-images-to-max-extent
222 (who-ps-html
223 (:p "Zoom all images out completely, restoring the original
224 view."))
225 :no-footprints-p
226 (who-ps-html
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."))
233 :auto-zoom
234 (who-ps-html
235 (:h3 "Auto Zoom")
236 (:p "Check this to automatically zoom into images once they
237 get an estimated position."))
238 :brighten-images
239 (who-ps-html
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."))
243 :walk-mode
244 (who-ps-html
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."))
248 :decrease-step-size
249 (who-ps-html
250 (:p "Decrease step size. Double-click to decrease harder."))
251 :step-size
252 (who-ps-html
253 (:p "Step size in metres. Click to increase; double-click
254 to increase harder."))
255 :increase-step-size
256 (who-ps-html
257 (:p "Increase step size. Double-click to increase harder."))
258 :step-button
259 (who-ps-html
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
263 (who-ps-html
264 (:p "Toggle display of image."))
265 :image-usable
266 (who-ps-html
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
271 common area."))
272 :image-trigger-time
273 (who-ps-html
274 (:p "Time this image was taken."))
275 base-layers-div
276 (who-ps-html
277 (:p "Choose a background streetmap."))
278 data-layers-div
279 (who-ps-html
280 (:p "Toggle visibility of data layers."))
281 :unselect-all-restrictions-button
282 (who-ps-html
283 (:h3 "Image Restrictions")
284 (:p "Remove all image restrictions."))
285 :restriction-select
286 (who-ps-html
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
294 anything else."))
295 :streetmap-overview
296 (who-ps-html
297 (:p "Click to re-center streetmap, or drag the red
298 rectangle."))
299 :streetmap-mouse-position
300 (who-ps-html
301 (:p "Cursor position in geographic coordinates when cursor
302 is in streetmap."))
303 :h2-help
304 (who-ps-html
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
310 help message."
311 (when element
312 (setf (@ element onmouseover)
313 ((lambda (x)
314 (lambda () (show-help x)))
315 topic))
316 (setf (@ element onmouseout) show-help)))
318 (defun add-help-events ()
319 "Add mouse events to DOM elements that initiate display of a
320 help message."
321 (for-in
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)
333 help-body))))
335 (defvar *click-control*
336 (chain
337 *open-layers
338 (*class
339 (@ *open-layers *control)
340 (create
341 :initialize
342 (lambda (options)
343 (chain *open-layers
344 *control
345 prototype
346 initialize
347 (apply this arguments))
348 (setf (@ this handler)
349 (new (chain *open-layers
350 *handler
351 (*click this
352 (create
353 :click (@ this trigger)))))))))))
355 (defvar +unix-epoch+ (lisp *unix-epoch*)
356 "Seconds between Lisp epoch and UNIX epoch.")
357 (defvar +geographic+
358 (new (chain *open-layers (*projection "EPSG:4326"))))
359 (defvar +spherical-mercator+
360 (new (chain *open-layers (*projection "EPSG:900913"))))
362 (defvar +proxy-root+
363 (lisp *proxy-root*)
364 "First element of URL path; defaults to phoros but may be
365 turned into something different by an HTTP proxy
366 definition.")
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
371 'user-role)))
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
379 *bounds
380 (from-string
381 (or +presentation-project-bbox-text+
382 "-180,-89,180,89"))))
383 (transform +geographic+ +spherical-mercator+))
384 "Bounding box of the entire presentation project.")
386 (defvar +aux-data-p+
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
404 auxiliary points.")
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
412 step-size.")
414 (defvar *current-nearest-aux-point*
415 (create attributes (create aux-numeric undefined
416 aux-text 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)
431 +geographic+)
432 (setf (@ *geojson-format* prototype internal-projection)
433 +geographic+)
435 (defvar *wkt-parser*
436 (new (chain *open-layers
437 *format
438 (*wkt*
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)))))
459 (defun *image ()
460 "Anything necessary to deal with a photo."
461 (setf (@ this map)
462 (new
463 (chain
464 *open-layers
465 (*map
466 (create projection +spherical-mercator+
467 all-overlays t
468 controls (array (new (chain *open-layers
469 *control
470 (*navigation)))))))))
471 (setf (@ this dummy) false) ;TODO why? (omitting splices map components directly into *image)
474 (setf (@ *image prototype delete-photo)
475 delete-photo)
476 (setf (@ *image prototype photop)
477 photop)
478 (setf (@ *image prototype show-photo)
479 show-photo)
480 (setf (@ *image prototype draw-epipolar-line)
481 draw-epipolar-line)
482 (setf (@ *image prototype draw-active-point)
483 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
490 url."
491 (+ "/" +proxy-root+
492 "/lib/photo/"
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")
500 "&brightenp"
501 "")))
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."
509 (loop
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."
520 (loop
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))
553 (chain *streetmap*
554 user-points-select-control
555 (unselect *current-user-point*)))
556 (reset-controls)
557 (if +aux-data-p+
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."
570 (prog1
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\"."
576 (loop
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
583 was active before."
584 (prog1
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)
591 "none"))
593 (defun hide-elements-of-class (class-name)
594 "Hide HTML elements with class=\"class\"."
595 (loop
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)
603 ""))
605 (defun reveal-elements-of-class (class-name)
606 "Reveal HTML elements with class=\"class\"."
607 (loop
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
615 controls"
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)
622 (unless +aux-data-p+
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")
631 options
632 length)
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
642 (chain *json-parser*
643 (read (@ *streetmap*
644 photo-request-response response-text)))))
645 (loop
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"))
651 (loop
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
672 (chain document
673 (get-element-by-id combobox-select)
674 selected-index)))
675 (when (< -1 combobox-selected-index)
676 (setf (value-with-id combobox-input)
677 (getprop (chain document
678 (get-element-by-id combobox-select)
679 options)
680 combobox-selected-index
681 'value)))
682 (chain document
683 (get-element-by-id combobox-input)
684 (focus))))
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)
692 selected-index)
693 -1)))
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)
702 options
703 length)
706 (loop for i in values do
707 (setf combobox-item
708 (chain document (create-element "option")))
709 (setf (@ combobox-item text) i)
710 (chain document
711 (get-element-by-id combobox-select)
712 (add combobox-item null)))
713 (setf (chain document
714 (get-element-by-id combobox-select)
715 selected-index)
716 selection)
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."
722 (let* ((response
723 (chain *json-parser*
724 (read (@ *streetmap*
725 user-point-choice-response response-text))))
726 (kinds
727 (chain response kinds (map (lambda (x)
728 (@ x kind)))))
729 (descriptions
730 (chain response descriptions (map (lambda (x)
731 (@ x description)))))
732 (best-used-kind -1)
733 (best-used-description -1))
734 (when selectp
735 (loop
736 with maximum = 0
737 for i across (@ response descriptions)
738 for k from 0
739 do (when (< maximum (@ i count))
740 (setf maximum (@ i count))
741 (setf best-used-description k)))
742 (loop
743 with maximum = 0
744 for i across (@ response kinds)
745 for k from 0
746 do (when (< maximum (@ i count))
747 (setf maximum (@ i count))
748 (setf best-used-kind k))))
749 (stuff-combobox
750 "point-kind" kinds best-used-kind)
751 (stuff-combobox
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)
758 (chain
759 *open-layers
760 *request
761 (*post*
762 (create :url (+ "/" +proxy-root+
763 "/lib/user-point-attributes.json")
764 :data nil
765 :headers (create "Content-type" "text/plain")
766 :success (lambda ()
767 (stuff-user-point-comboboxes selectp))
768 :failure recommend-fresh-login)))))
770 (defun stuff-restriction-select ()
771 "Stuff available restriction IDs into restriction-select."
772 (let ((response
773 (chain *json-parser*
774 (read (@ *streetmap*
775 restriction-select-choice-response
776 response-text))))
777 (restriction-select-options
778 (chain document
779 (get-element-by-id "restriction-select")
780 options)))
781 (loop
782 for restriction in response
783 for i from 0
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)
790 (chain
791 *open-layers
792 *request
793 (*post*
794 (create :url (+ "/" +proxy-root+
795 "/lib/selectable-restrictions.json")
796 :data nil
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
804 (chain document
805 (get-element-by-id "restriction-select")
806 options)))
807 (loop
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."
814 (loop
815 for option across (chain document
816 (get-element-by-id "restriction-select")
817 options)
818 do (setf (@ option selected) f))
819 (request-photos))
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."
830 (when lonlat
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
843 (clone)
844 (transform +spherical-mercator+ +geographic+))))
845 (request-aux-data-linestring (@ lonlat-geographic lon)
846 (@ lonlat-geographic lat)
847 (* *linestring-step-ratio*
848 (step-size-degrees))
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))
859 (lonlat-geographic
860 (chain lonlat-spherical-mercator
861 (clone)
862 (transform +spherical-mercator+ +geographic+)))
863 (content
864 (chain *json-parser*
865 (write
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))))))
872 (chain *streetmap*
873 cursor-layer
874 (remove-all-features))
875 (chain *streetmap*
876 cursor-layer
877 (add-features
878 (new (chain *open-layers
879 *feature
880 (*vector
881 (new (chain
882 *open-layers
883 *geometry
884 (*point (@ lonlat-spherical-mercator
885 lon)
886 (@ lonlat-spherical-mercator
887 lat)))))))))
888 (chain *streetmap*
889 overview-cursor-layer
890 (remove-all-features))
891 (chain *streetmap*
892 overview-cursor-layer
893 (add-features
894 (new (chain *open-layers
895 *feature
896 (*vector
897 (new (chain
898 *open-layers
899 *geometry
900 (*point (@ lonlat-spherical-mercator
901 lon)
902 (@ lonlat-spherical-mercator
903 lat)))))))))
904 (setf (@ *streetmap* photo-request-response)
905 (chain
906 *open-layers
907 *request
908 (*post*
909 (create
910 :url (+ "/" +proxy-root+ "/lib/nearest-image-data")
911 :data content
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
923 caching-photo-p nil
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
939 (clone)
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."
951 (let ((content
952 (chain *json-parser*
953 (write
954 (create
955 :longitude (@ *cache-stuffer* current-center lon)
956 :latitude (@ *cache-stuffer* current-center lat))))))
957 (setf (@ *cache-stuffer* cache-fodder-request-response)
958 (chain
959 *open-layers
960 *request
961 (*post*
962 (create
963 :url (+ "/" +proxy-root+ "/lib/nearest-image-urls")
964 :data content
965 :headers (create "Content-type" "text/plain"
966 "Content-length" (@ content length))
967 :success handle-request-cache-fodder-group
968 :failure (lambda ()
969 (if (= (@ *cache-stuffer* cache-fodder-request-response status) 504)
970 (progn
971 (clear-timeout
972 (@ *cache-stuffer*
973 request-cache-fodder-group-timeout))
974 (setf (@ *cache-stuffer*
975 request-cache-fodder-group-timeout)
976 (set-timeout request-cache-fodder-group
977 5000)))
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)
983 (chain *json-parser*
984 (read (@ *cache-stuffer*
985 cache-fodder-request-response
986 response-text))))
987 ;; otherwise preemptive caching is probably suppressed by server
988 (setf (@ *cache-stuffer* index) 0)
989 (reveal-element-with-id "caching-indicator")
990 (cache-photo)))
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)
1000 (progn
1001 (clear-timeout (@ *cache-stuffer* cache-photo-timeout))
1002 (setf (@ *cache-stuffer* cache-photo-timeout)
1003 (set-timeout cache-photo 3000)))
1004 (progn
1005 (setf (@ *cache-stuffer* caching-photo-p) t)
1006 (setf (@ *cache-stuffer* xhr) (new (*x-m-l-http-request)))
1007 (chain *cache-stuffer*
1009 (open "GET"
1010 (photo-path
1011 (aref (@ *cache-stuffer* photo-url-ingredients)
1012 (@ *cache-stuffer* index)))
1014 (setf (@ *cache-stuffer* xhr onload)
1015 (lambda (event)
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
1026 ;; reload button.
1027 (chain *cache-stuffer*
1029 (set-request-header
1030 "Cache-control"
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)
1035 (set-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*
1048 (read
1049 (@ this epipolar-request-response response-text))))
1050 (points
1051 (chain epipolar-line
1052 (map (lambda (x)
1053 (new (chain *open-layers
1054 *geometry
1055 (*point
1056 (@ x :m) (@ x :n))))))))
1057 (feature
1058 (new (chain *open-layers
1059 *feature
1060 (*vector
1061 (new (chain
1062 *open-layers
1063 *geometry
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)
1074 (clone)
1075 (transform +spherical-mercator+ +geographic+))))
1076 (request-nearest-aux-points
1077 (create :longitude (@ lonlat-geographic lon)
1078 :latitude (@ lonlat-geographic lat))
1079 count)))
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)
1085 content)
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)
1090 (chain *open-layers
1091 *request
1092 (*post*
1093 (create :url (+ "/" +proxy-root+
1094 "/lib/aux-local-data")
1095 :data content
1096 :headers (create "Content-type" "text/plain"
1097 "Content-length"
1098 (@ content length))
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
1106 latitude latitude
1107 radius radius
1108 step-size step-size
1109 azimuth (@ *streetmap*
1110 linestring-central-azimuth)))
1111 (content (chain *json-parser* (write payload))))
1112 (setf (@ *streetmap* aux-data-linestring-request-response)
1113 (chain *open-layers
1114 *request
1115 (*post*
1116 (create :url (+ "/" +proxy-root+
1117 "/lib/aux-local-linestring.json")
1118 :data content
1119 :headers (create "Content-type" "text/plain"
1120 "Content-length"
1121 (@ content length))
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")
1134 onclick)
1135 (lambda () (finish-point #'store-point)))
1136 (enable-element-with-id "finish-point-button"))
1137 (let* ((estimated-positions-request-response
1138 (chain *json-parser*
1139 (read
1140 (@ this
1141 estimated-positions-request-response
1142 response-text))))
1143 (estimated-positions
1144 (aref estimated-positions-request-response 1))
1145 (estimated-position-style
1146 (create stroke-color (chain *open-layers
1147 *feature
1148 *vector
1149 style "temporary" stroke-color)
1150 point-radius 9
1151 fill-opacity 0)))
1152 (setf *global-position*
1153 (aref estimated-positions-request-response 0))
1154 (let ((feature
1155 (new
1156 (chain *open-layers
1157 *feature
1158 (*vector
1159 (chain
1160 (new (chain *open-layers
1161 *geometry
1162 (*point
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
1169 *layer
1170 (*vector
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))
1176 (chain *streetmap*
1177 (add-layer (@ *streetmap* estimated-position-layer))))
1178 (request-nearest-aux-points *global-position* 7)
1179 (loop
1180 for i in *images*
1181 for p in estimated-positions
1183 (when p ;otherwise a photogrammetry error has occured
1184 (setf (@ i estimated-position-layer)
1185 (new
1186 (chain *open-layers
1187 *layer
1188 (*vector
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)
1193 (@ p n)))))
1194 (setf (@ i estimated-position-layer style)
1195 estimated-position-style)
1196 (let* ((point
1197 (new
1198 (chain *open-layers *geometry (*point (@ p m)
1199 (@ p n)))))
1200 (feature
1201 (new
1202 (chain *open-layers *feature (*vector point)))))
1203 (chain i map
1204 (add-layer (@ i estimated-position-layer)))
1205 (chain i estimated-position-layer
1206 (add-features feature))))))
1207 (zoom-anything-to-point)
1208 (chain document
1209 (get-element-by-id "finish-point-button")
1210 (focus)))
1212 (defun draw-nearest-aux-points ()
1213 "Draw a few auxiliary points into streetmap."
1214 (let ((features
1215 (chain *json-parser*
1216 (read
1217 (@ *streetmap*
1218 aux-local-data-request-response
1219 response-text))
1220 features)))
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)
1227 (loop
1228 for i in features
1229 for n from 0 do
1230 (let* ((point
1231 (chain
1232 (new
1233 (chain *open-layers
1234 *geometry
1235 (*point (@ i geometry coordinates 0)
1236 (@ i geometry coordinates 1))))
1237 (transform +geographic+ +spherical-mercator+)))
1238 (feature
1239 (new
1240 (chain *open-layers *feature (*vector point)))))
1241 (setf (@ feature attributes)
1242 (@ i properties))
1243 (setf (@ feature fid) ;this is supposed to correspond to
1244 n) ; option of *aux-point-distance-select*
1245 (chain *streetmap*
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
1254 ") "
1255 (chain *open-layers
1256 *number
1257 (format (@ i properties distance) 3 ""))))
1258 (chain *aux-point-distance-select*
1259 (add aux-point-distance-item null))))
1260 (chain *streetmap*
1261 nearest-aux-points-select-control
1262 (select
1263 (chain
1264 (elt (@ *streetmap* nearest-aux-points-layer features)
1265 0))))
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."
1271 (let* ((data
1272 (@ *streetmap*
1273 aux-data-linestring-request-response
1274 response-text))
1275 (linestring-wkt
1276 (chain *json-parser* (read data) linestring))
1277 (current-point-wkt
1278 (chain *json-parser* (read data) current-point))
1279 (previous-point-wkt
1280 (chain *json-parser* (read data) previous-point))
1281 (next-point-wkt
1282 (chain *json-parser* (read data) next-point))
1283 (azimuth
1284 (chain *json-parser* (read data) azimuth))
1285 (linestring
1286 (chain *wkt-parser* (read linestring-wkt)))
1287 (current-point
1288 (chain *wkt-parser* (read current-point-wkt)))
1289 (previous-point
1290 (chain *wkt-parser* (read previous-point-wkt)))
1291 (next-point
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))
1304 (chain *streetmap*
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
1313 (if back-p
1314 (progn
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)))
1320 (chain *streetmap*
1321 step-back-point
1322 (clone)
1323 geometry
1324 (transform +spherical-mercator+ +geographic+)))
1325 (chain *streetmap*
1326 step-forward-point
1327 (clone)
1328 geometry
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)))
1335 (progn
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
1342 equator."
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")
1360 (solitary-filter
1361 (new (chain *open-layers
1362 *filter
1363 (*comparison (create type (chain *open-layers
1364 *filter
1365 *comparison
1366 *like*)
1367 property symbolizer-property
1368 value "solitary")))))
1369 (polyline-filter
1370 (new (chain *open-layers
1371 *filter
1372 (*comparison (create type (chain *open-layers
1373 *filter
1374 *comparison
1375 *like*)
1376 property symbolizer-property
1377 value "polyline")))))
1378 (polygon-filter
1379 (new (chain *open-layers
1380 *filter
1381 (*comparison (create type (chain *open-layers
1382 *filter
1383 *comparison
1384 *like*)
1385 property symbolizer-property
1386 value "polygon")))))
1387 (solitary-rule
1388 (new (chain *open-layers
1389 (*rule (create
1390 filter solitary-filter
1391 symbolizer (create
1392 graphic-name "triangle"))))))
1393 (polyline-rule
1394 (new (chain *open-layers
1395 (*rule (create
1396 filter polyline-filter
1397 symbolizer (create
1398 graphic-name "square"
1399 point-radius 4))))))
1400 (polygon-rule
1401 (new (chain *open-layers
1402 (*rule (create
1403 filter polygon-filter
1404 symbolizer (create
1405 graphic-name "star"))))))
1406 (else-rule
1407 (new (chain *open-layers
1408 (*rule (create
1409 else-filter t
1410 symbolizer (create
1411 graphic-name "x"))))))
1412 (user-point-default-style
1413 (new (chain
1414 *open-layers
1415 (*style (create stroke-color "OrangeRed"
1416 fill-color "OrangeRed"
1417 label-align "cb"
1418 label-y-offset 5
1419 font-color "OrangeRed"
1420 font-family "'andale mono', 'lucida console', monospace"
1421 stroke-opacity .5
1422 stroke-width 2
1423 point-radius 5
1424 fill-opacity 0)
1425 (create rules (array solitary-rule
1426 polyline-rule
1427 polygon-rule
1428 else-rule))))))
1429 (user-point-select-style
1430 (new (chain
1431 *open-layers
1432 (*style (create stroke-opacity 1
1433 label label-property)
1434 (create rules (array solitary-rule
1435 polyline-rule
1436 polygon-rule
1437 else-rule))))))
1438 (user-point-temporary-style
1439 (new (chain
1440 *open-layers
1441 (*style (create fill-opacity .5)
1442 (create rules (array solitary-rule
1443 polyline-rule
1444 polygon-rule
1445 else-rule)))))))
1446 (new (chain *open-layers
1447 (*style-map
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*
1456 (read
1457 (@ *user-point-in-images-response* response-text))))
1458 (user-point-collections
1459 (chain user-point-positions-response image-points))
1460 (user-point-count
1461 (chain user-point-positions-response user-point-count))
1462 (label
1463 (when (> user-point-count 1) "${numericDescription}")))
1464 (loop
1465 for i in *images*
1466 for user-point-collection in user-point-collections
1468 (when i ;otherwise a photogrammetry error has occured
1469 (let ((features
1470 (loop
1471 for raw-feature in
1472 (@ user-point-collection features)
1473 collect
1474 (let* ((x
1475 (@ raw-feature geometry coordinates 0))
1477 (@ raw-feature geometry coordinates 1))
1478 (point
1479 (new (chain *open-layers
1480 *geometry
1481 (*point x y))))
1482 (fid
1483 (@ raw-feature id))
1484 (attributes
1485 (@ raw-feature properties))
1486 (feature
1487 (new (chain *open-layers
1488 *feature
1489 (*vector point attributes)))))
1490 (setf (@ feature fid) fid)
1491 (setf (@ feature render-intent) "select")
1492 feature))))
1493 (setf
1494 (@ i user-point-layer)
1495 (new (chain *open-layers
1496 *layer
1497 (*vector
1498 "User Point"
1499 (create display-in-layer-switcher nil
1500 style-map (user-point-style-map
1501 label))))))
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."
1508 (let* ((point-data
1509 (create user-point-id (if (defined *current-user-point*)
1510 (@ *current-user-point* fid)
1511 nil)
1512 kind
1513 (value-with-id "point-kind-input")
1514 description
1515 (value-with-id "point-description-input")
1516 numeric-description
1517 (value-with-id "point-numeric-description")))
1518 (content
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*
1526 (chain
1527 *open-layers
1528 *request
1529 (*post*
1530 (create
1531 :url (+ "/" +proxy-root+ "/lib/uniquify-point-attributes")
1532 :data content
1533 :headers (create "Content-type" "text/plain"
1534 "Content-length" (@ content
1535 length))
1536 :success
1537 (lambda ()
1538 (enable-element-with-id "finish-point-button")
1539 (when delete-point-button-active-p
1540 (enable-element-with-id "delete-point-button"))
1541 (let ((response
1542 (chain
1543 *json-parser*
1544 (read
1545 (@ *uniquify-point-attributes-response*
1546 response-text)))))
1547 (if (equal null response)
1548 (database-writer)
1549 (progn
1550 (setf
1551 (chain document
1552 (get-element-by-id
1553 "force-duplicate-button")
1554 onclick)
1555 (lambda ()
1556 (hide-element-with-id "uniquify-buttons")
1557 (reveal-element-with-id "finish-point-button")
1558 (database-writer)))
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."
1567 (let* ((point-data
1568 (create user-point-id (if (defined *current-user-point*)
1569 (@ *current-user-point* fid)
1570 nil)
1571 kind
1572 (value-with-id "point-kind-input")
1573 description
1574 (value-with-id "point-description-input")
1575 numeric-description
1576 (value-with-id "point-numeric-description")))
1577 (content
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*
1587 (chain
1588 *open-layers
1589 *request
1590 (*post*
1591 (create :url (+ "/"
1592 +proxy-root+
1593 "/lib/uniquify-point-attributes")
1594 :data content
1595 :headers (create "Content-type" "text/plain"
1596 "Content-length" (@ content
1597 length))
1598 :success
1599 (lambda ()
1600 (enable-element-with-id "finish-point-button")
1601 (when delete-point-button-active-p
1602 (enable-element-with-id "delete-point-button"))
1603 (let ((response
1604 (chain
1605 *json-parser*
1606 (read
1607 (@ *uniquify-point-attributes-response*
1608 response-text)))))
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*
1627 attributes
1628 aux-numeric))
1629 (setf (@ global-position-etc aux-text)
1630 (@ *current-nearest-aux-point*
1631 attributes
1632 aux-text)))
1633 (let ((content
1634 (chain *json-parser*
1635 (write global-position-etc))))
1636 (disable-element-with-id "finish-point-button")
1637 (chain
1638 *open-layers
1639 *request
1640 (*post*
1641 (create :url (+ "/" +proxy-root+ "/lib/store-point")
1642 :data content
1643 :headers (create "Content-type" "text/plain"
1644 "Content-length" (@ content length))
1645 :success (lambda ()
1646 (refresh-layer
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."
1654 (let* ((point-data
1655 (create user-point-id (@ *current-user-point* fid)
1656 kind
1657 (value-with-id "point-kind-input")
1658 description
1659 (value-with-id "point-description-input")
1660 numeric-description
1661 (value-with-id "point-numeric-description")))
1662 (content
1663 (chain *json-parser*
1664 (write point-data))))
1665 (disable-element-with-id "finish-point-button")
1666 (disable-element-with-id "delete-point-button")
1667 (chain *open-layers
1668 *request
1669 (*post*
1670 (create :url (+ "/" +proxy-root+ "/lib/update-point")
1671 :data content
1672 :headers (create "Content-type" "text/plain"
1673 "Content-length" (@ content
1674 length))
1675 :success (lambda ()
1676 (refresh-layer
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))
1685 (content
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")
1690 (chain *open-layers
1691 *request
1692 (*post*
1693 (create :url (+ "/" +proxy-root+ "/lib/delete-point")
1694 :data content
1695 :headers (create "Content-type" "text/plain"
1696 "Content-length" (@ content
1697 length))
1698 :success (lambda ()
1699 (refresh-layer
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."
1708 (chain this
1709 active-point-layer
1710 (add-features
1711 (new (chain *open-layers
1712 *feature
1713 (*vector
1714 (new (chain *open-layers
1715 *geometry
1716 (*point
1717 (@ this photo-parameters m)
1718 (@ this photo-parameters n))))))))))
1720 (defun image-click-action (clicked-image)
1721 (lambda (event)
1722 "Do appropriate things when an image is clicked into."
1723 (let* ((lonlat
1724 (chain clicked-image map (get-lon-lat-from-view-port-px
1725 (@ event xy))))
1726 (photo-parameters
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
1738 *layer
1739 (*vector "Active Point"
1740 (create display-in-layer-switcher
1741 nil)))))
1742 (chain clicked-image
1744 (add-layer (@ clicked-image active-point-layer)))
1745 (chain clicked-image (draw-active-point))
1747 *pristine-images-p*
1748 (progn
1749 (reset-controls)
1750 (remove-any-layers "User Point") ;from images
1751 ;; TODO:
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))
1757 (loop
1758 for i across *images* do
1759 (when (and (not (equal i clicked-image))
1760 (chain i (photop)))
1761 (setf
1762 (@ i epipolar-layer)
1763 (new (chain *open-layers
1764 *layer
1765 (*vector "Epipolar Line"
1766 (create
1767 display-in-layer-switcher nil))))
1768 content (chain *json-parser*
1769 (write
1770 (append (array photo-parameters)
1771 (@ i photo-parameters))))
1772 (@ i epipolar-request-response)
1773 (chain *open-layers
1774 *request
1775 (*post*
1776 (create :url (+ "/" +proxy-root+
1777 "/lib/epipolar-line")
1778 :data content
1779 :headers (create
1780 "Content-type" "text/plain"
1781 "Content-length"
1782 (@ content length))
1783 :success (@ i draw-epipolar-line)
1784 :failure recommend-fresh-login
1785 :scope i))))
1786 (chain i
1788 (add-layer (@ i epipolar-layer))))))
1789 (progn
1790 (remove-any-layers "Epipolar Line")
1791 (remove-any-layers "Estimated Position")
1792 (let* ((active-pointed-photo-parameters
1793 (loop
1794 for i across *images*
1795 when (has-layer-p (@ i map) "Active Point")
1796 collect (@ i photo-parameters)))
1797 (content
1798 (chain *json-parser*
1799 (write
1800 (list active-pointed-photo-parameters
1801 (chain *images*
1802 (map
1803 #'(lambda (x)
1804 (@ x
1805 photo-parameters)))))))))
1806 (setf (@ clicked-image estimated-positions-request-response)
1807 (chain *open-layers
1808 *request
1809 (*post*
1810 (create :url (+ "/" +proxy-root+
1811 "/lib/estimated-positions")
1812 :data content
1813 :headers (create
1814 "Content-type" "text/plain"
1815 "Content-length"
1816 (@ content length))
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."
1830 (loop
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))
1836 (defun photop ()
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)
1844 width)))
1845 (image-div-height
1846 (parse-int (chain (get-computed-style (@ this map div) nil)
1847 height)))
1848 (image-width
1849 (@ this photo-parameters sensor-width-pix))
1850 (image-height
1851 (@ this photo-parameters sensor-height-pix)))
1852 (chain
1853 this
1855 (add-layer
1856 (new (chain
1857 *open-layers
1858 *layer
1859 (*image
1860 "Photo"
1861 (photo-path (@ this photo-parameters))
1862 (new (chain *open-layers
1863 (*bounds
1864 -.5 -.5
1865 (+ image-width .5) (+ image-height .5))))
1866 (new (chain *open-layers
1867 (*size image-div-width
1868 image-div-height)))
1869 (create
1870 max-resolution (chain
1871 *math
1872 (max
1873 (/ image-width image-div-width)
1874 (/ image-height image-div-height)))))))))
1875 (when (@ this photo-parameters rendered-footprint)
1876 (setf (@ this footprint-layer)
1877 (new (chain
1878 *open-layers
1879 *layer
1880 (*vector "Footprint"
1881 (create display-in-layer-switcher nil
1882 style (create stroke-color "yellow"
1883 stroke-width 1
1884 stroke-opacity .3))))))
1885 (chain this
1886 footprint-layer
1887 (add-features
1888 (chain *geojson-parser*
1889 (read (@ this
1890 photo-parameters
1891 rendered-footprint)))))
1892 (chain this
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."
1904 (loop
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")
1913 (let ((point-lonlat
1914 (new (chain *open-layers
1915 (*lon-lat (@ *global-position* longitude)
1916 (@ *global-position* latitude))
1917 (transform +geographic+ +spherical-mercator+)))))
1918 (when point-lonlat
1919 (chain *streetmap*
1920 (set-center point-lonlat 18 nil t))))
1921 (loop for i across *images* do
1922 (let ((point-lonlat
1923 (cond
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))
1930 (t false))))
1931 (when point-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)
1942 (chain
1943 document
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)
1953 (add-control
1954 (@ (aref *images* image-index) click)))
1955 (chain (aref *images* image-index) click (activate))
1956 ;;(chain (aref *images* image-index)
1957 ;; map
1958 ;; (add-control
1959 ;; (new (chain *open-layers
1960 ;; *control
1961 ;; (*mouse-position
1962 ;; (create
1963 ;; div (chain
1964 ;; document
1965 ;; (get-element-by-id
1966 ;; (+ "image-" image-index "-zoom")))))))))
1967 (chain (aref *images* image-index)
1969 (add-control
1970 (new (chain *open-layers
1971 *control
1972 (*layer-switcher
1973 (create
1974 div (chain
1975 document
1976 (get-element-by-id
1977 (+ "image-" image-index "-layer-switcher")))
1978 rounded-corner nil))))))
1979 (let ((pan-west-control
1980 (new (chain *open-layers *control (*pan "West"))))
1981 (pan-north-control
1982 (new (chain *open-layers *control (*pan "North"))))
1983 (pan-south-control
1984 (new (chain *open-layers *control (*pan "South"))))
1985 (pan-east-control
1986 (new (chain *open-layers *control (*pan "East"))))
1987 (zoom-in-control
1988 (new (chain *open-layers *control (*zoom-in))))
1989 (zoom-out-control
1990 (new (chain *open-layers *control (*zoom-out))))
1991 (zoom-to-max-extent-control
1992 (new (chain *open-layers *control (*zoom-to-max-extent))))
1993 (pan-zoom-panel
1994 (new (chain *open-layers
1995 *control
1996 (*panel
1997 (create div
1998 (chain
1999 document
2000 (get-element-by-id
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
2007 pan-north-control
2008 pan-south-control
2009 pan-east-control
2010 zoom-in-control
2011 zoom-out-control
2012 zoom-to-max-extent-control))))
2013 (chain (aref *images* image-index)
2015 (render (chain document
2016 (get-element-by-id
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."
2030 (reset-controls)
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}")))
2043 (cond
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")
2056 (html-table
2057 (@ *current-user-point* attributes aux-numeric)
2058 +aux-numeric-labels+))
2059 (setf (inner-html-with-id "aux-text-list")
2060 (html-table
2061 (@ *current-user-point* attributes aux-text)
2062 +aux-text-labels+))
2063 (switch-phoros-controls-to "point-editor")
2064 (if (write-permission-p
2065 (@ *current-user-point* attributes user-name))
2066 (progn
2067 (setf (chain document
2068 (get-element-by-id "finish-point-button")
2069 onclick)
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"))
2074 (progn
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)
2080 (+ "(by "
2081 (@ *current-user-point* attributes user-name)
2082 ")")
2083 "(ownerless)")))
2085 (reset-layers-and-controls))))
2086 (chain *streetmap* user-point-layer (redraw))
2087 (remove-any-layers "User Point") ;from images
2088 (setf content
2089 (chain *json-parser*
2090 (write
2091 (array (chain *streetmap*
2092 user-point-layer
2093 selected-features
2094 (map (lambda (x) (@ x fid))))
2095 (loop
2096 for i across *images*
2097 collect (@ i photo-parameters))))))
2098 (setf *user-point-in-images-response*
2099 (chain *open-layers
2100 *request
2101 (*post*
2102 (create :url (+ "/" +proxy-root+
2103 "/lib/user-point-positions")
2104 :data content
2105 :headers (create "Content-type" "text/plain"
2106 "Content-length" (@ content
2107 length))
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."
2113 (chain *streetmap*
2114 nearest-aux-points-select-control
2115 (unselect-all))
2116 (chain *streetmap*
2117 nearest-aux-points-select-control
2118 (select
2119 (chain
2120 (elt (@ *streetmap* nearest-aux-points-layer features)
2121 (@ *aux-point-distance-select*
2122 options
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
2133 walking path."
2134 (if (checkbox-status-with-id "walk-p")
2135 (request-aux-data-linestring-for-point (@ *streetmap*
2136 clicked-lonlat))
2137 (chain *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")
2144 (chain *streetmap*
2145 nearest-aux-points-layer
2146 (set-visibility t))
2147 (chain *streetmap*
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."
2158 (if aux-data
2159 (who-ps-html
2160 (:table
2161 :class "aux-data-table"
2162 (chain aux-data
2163 (reduce (lambda (x y i)
2164 (if y
2165 (+ x (who-ps-html
2166 (:tr
2167 (:td :class "aux-data-label"
2169 (if (and labels
2170 (elt labels i))
2171 (elt labels i)
2172 (+ "#" i))
2173 ":"))
2174 (:td :class "aux-data-value"
2175 y))))
2177 ""))))
2178 ""))
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))
2183 (let ((aux-numeric
2184 (@ event feature attributes aux-numeric))
2185 (aux-text
2186 (@ event feature attributes aux-text))
2187 (distance
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+))))
2196 (defun bye ()
2197 "Store user's current map extent and log out."
2198 (let* ((bbox (chain *streetmap*
2199 (get-extent)
2200 (transform +spherical-mercator+ +geographic+)
2201 (to-b-b-o-x)))
2202 (href (+ "/" +proxy-root+ "/lib/logout?bbox=" bbox)))
2203 (when (@ *streetmap* cursor-layer features length)
2204 (let* ((lonlat-geographic (chain *streetmap*
2205 cursor-layer
2206 features
2208 geometry
2209 (clone)
2210 (transform +spherical-mercator+
2211 +geographic+))))
2212 (setf href (+ href
2213 "&longitude=" (@ lonlat-geographic x)
2214 "&latitude=" (@ lonlat-geographic y)))))
2215 (setf (@ location href) href)))
2217 (defun init ()
2218 "Prepare user's playground."
2219 (unless +presentation-project-bbox-text+
2220 (setf (inner-html-with-id "presentation-project-emptiness")
2221 "(no data)"))
2222 (setf *streetmap*
2223 (new (chain
2224 *open-layers
2225 (*map "streetmap"
2226 (create projection +geographic+
2227 display-projection +geographic+
2228 controls (array (new (chain *open-layers
2229 *control
2230 (*navigation)))
2231 (new (chain *open-layers
2232 *control
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
2243 (create
2244 graphic-width 14
2245 external-graphic (+ "/" +proxy-root+
2246 "/lib/public_html/phoros-cursor.png"))))
2247 (setf (@ *streetmap* cursor-layer)
2248 (new (chain
2249 *open-layers *layer
2250 (*vector
2251 "you"
2252 (create
2253 style cursor-layer-style)))))
2254 (setf (@ *streetmap* overview-cursor-layer)
2255 (new (chain
2256 *open-layers *layer
2257 (*vector
2258 "you"
2259 (create
2260 style cursor-layer-style))))))
2261 (let ((survey-layer-style
2262 (create stroke-color (chain *open-layers *feature *vector
2263 style "default" stroke-color)
2264 stroke-width 1
2265 point-radius 2
2266 fill-opacity 0
2267 graphic-name "circle")))
2268 (setf (@ *streetmap* survey-layer)
2269 (new (chain
2270 *open-layers *layer
2271 (*vector
2272 "survey"
2273 (create
2274 strategies (array (new (*bbox-strategy*)))
2275 protocol
2276 (new (*http-protocol*
2277 (create :url (+ "/" +proxy-root+
2278 "/lib/points.json"))))
2279 style survey-layer-style))))))
2280 (setf (@ *streetmap* user-point-layer)
2281 (new (chain
2282 *open-layers *layer
2283 (*vector
2284 "user points"
2285 (create
2286 strategies (array (new *bbox-strategy*))
2287 protocol
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
2293 *control
2294 (*select-feature (@ *streetmap* user-point-layer)
2295 (create render-intent "temporary"
2296 hover t
2297 highlight-only t)))))
2298 (setf (@ *streetmap* user-points-select-control)
2299 (new (chain *open-layers
2300 *control
2301 (*select-feature (@ *streetmap* user-point-layer)
2302 (create toggle t
2303 multiple t)))))
2304 (let ((aux-layer-style
2305 (create stroke-color "grey"
2306 stroke-width 1
2307 point-radius 2
2308 fill-opacity 0
2309 graphic-name "circle")))
2310 (setf (@ *streetmap* aux-point-layer)
2311 (new (chain
2312 *open-layers *layer
2313 (*vector
2314 "auxiliary data"
2315 (create
2316 strategies (array (new (*bbox-strategy*)))
2317 protocol
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
2325 (*style-map
2326 (create "default"
2327 (create stroke-color "grey"
2328 stroke-width 1
2329 point-radius 5
2330 fill-opacity 0
2331 graphic-name "circle")
2332 "select"
2333 (create stroke-color "black"
2334 stroke-width 1
2335 point-radius 5
2336 fill-opacity 0
2337 graphic-name "circle")
2338 "temporary"
2339 (create stroke-color "grey"
2340 stroke-width 1
2341 point-radius 5
2342 fill-color "grey"
2343 fill-opacity 1
2344 graphic-name "circle")))))))
2345 (setf (@ *streetmap* nearest-aux-points-layer)
2346 (new (chain *open-layers
2347 *layer
2348 (*vector
2349 "Nearest Aux Points"
2350 (create
2351 display-in-layer-switcher nil
2352 style-map nearest-aux-point-layer-style-map
2353 visibility t))))))
2354 (setf (@ *streetmap* nearest-aux-points-hover-control)
2355 (new (chain *open-layers
2356 *control
2357 (*select-feature
2358 (@ *streetmap* nearest-aux-points-layer)
2359 (create render-intent "temporary"
2360 hover t
2361 highlight-only t)))))
2362 (setf (@ *streetmap* nearest-aux-points-select-control)
2363 (new (chain *open-layers
2364 *control
2365 (*select-feature
2366 (@ *streetmap* nearest-aux-points-layer)))))
2367 (setf (@ *streetmap* aux-data-linestring-layer)
2368 (new (chain *open-layers
2369 *layer
2370 (*vector
2371 "Aux Data Linestring"
2372 (create
2373 display-in-layer-switcher nil
2374 style-map nearest-aux-point-layer-style-map
2375 visibility t)))))
2376 (setf (@ *streetmap* google-streetmap-layer)
2377 (new (chain *open-layers
2378 *layer
2379 (*google "Google Streets"
2380 (create num-zoom-levels 23)))))
2381 (setf (@ *streetmap* osm-layer)
2382 (new (chain *open-layers
2383 *layer
2384 (*osm*
2385 "OpenStreetMap"
2387 (create num-zoom-levels 23
2388 attribution
2389 "Data CC-By-SA by openstreetmap.org")))))
2390 (setf (@ *streetmap* overview-osm-layer)
2391 (new (chain *open-layers
2392 *layer
2393 (*osm* "OpenStreetMap"))))
2394 (setf (@ *streetmap* click-streetmap)
2395 (new (*click-control*
2396 (create :trigger request-photos-after-click))))
2397 (setf (@ *streetmap* nirvana-layer)
2398 (new (chain
2399 *open-layers
2400 (*layer
2401 "Nirvana"
2402 (create is-base-layer t
2403 projection (@ *streetmap* osm-layer projection)
2404 max-extent (@ *streetmap* osm-layer max-extent)
2405 max-resolution (@ *streetmap*
2406 osm-layer
2407 max-resolution)
2408 units (@ *streetmap* osm-layer units)
2409 num-zoom-levels (@ *streetmap*
2410 osm-layer
2411 num-zoom-levels))))))
2412 (chain *streetmap*
2413 (add-control
2414 (new (chain *open-layers
2415 *control
2416 (*layer-switcher
2417 (create
2418 div (chain
2419 document
2420 (get-element-by-id
2421 "streetmap-layer-switcher"))
2422 rounded-corner nil))))))
2423 (let ((pan-west-control
2424 (new (chain *open-layers *control (*pan "West"))))
2425 (pan-north-control
2426 (new (chain *open-layers *control (*pan "North"))))
2427 (pan-south-control
2428 (new (chain *open-layers *control (*pan "South"))))
2429 (pan-east-control
2430 (new (chain *open-layers *control (*pan "East"))))
2431 (zoom-in-control
2432 (new (chain *open-layers *control (*zoom-in))))
2433 (zoom-out-control
2434 (new (chain *open-layers *control (*zoom-out))))
2435 (zoom-to-max-extent-control
2436 (new (chain
2437 *open-layers
2438 *control
2439 (*button
2440 (create
2441 display-class "streetmapZoomToMaxExtent"
2442 trigger (lambda ()
2443 (chain *streetmap*
2444 (zoom-to-extent
2445 +presentation-project-bounds+))))))))
2446 (pan-zoom-panel
2447 (new (chain *open-layers
2448 *control
2449 (*panel
2450 (create div
2451 (chain
2452 document
2453 (get-element-by-id
2454 "streetmap-zoom")))))))
2455 (overview-map
2456 (new (chain *open-layers
2457 *control
2458 (*overview-map
2459 (create
2461 layers (array
2462 (@ *streetmap* overview-osm-layer)
2463 (@ *streetmap* overview-cursor-layer))
2465 min-ratio 14
2466 max-ratio 16
2467 div (chain document
2468 (get-element-by-id
2469 "streetmap-overview")))))))
2470 (mouse-position-control
2471 (new (chain *open-layers
2472 *control
2473 (*mouse-position
2474 (create div (chain document
2475 (get-element-by-id
2476 "streetmap-mouse-position"))
2477 empty-string "longitude, latitude")))))
2478 (scale-line-control
2479 (new (chain *open-layers
2480 *control
2481 *scale-line))))
2482 (chain *streetmap*
2483 (add-control pan-zoom-panel))
2484 (chain pan-zoom-panel
2485 (add-controls (array pan-west-control
2486 pan-north-control
2487 pan-south-control
2488 pan-east-control
2489 zoom-in-control
2490 zoom-out-control
2491 zoom-to-max-extent-control)))
2492 (chain *streetmap*
2493 (add-control (@ *streetmap* click-streetmap)))
2494 (chain *streetmap* click-streetmap (activate))
2496 (chain *streetmap*
2497 user-point-layer
2498 events
2499 (register "featureselected"
2500 (@ *streetmap* user-point-layer)
2501 user-point-selected))
2502 (chain *streetmap*
2503 user-point-layer
2504 events
2505 (register "featureunselected"
2506 (@ *streetmap* user-point-layer)
2507 user-point-unselected))
2508 (chain *streetmap*
2509 nearest-aux-points-layer
2510 events
2511 (register "featureselected"
2512 (@ *streetmap* nearest-aux-points-layer)
2513 nearest-aux-point-selected))
2514 (chain *streetmap*
2515 (add-control
2516 (@ *streetmap* nearest-aux-points-hover-control)))
2517 (chain *streetmap*
2518 (add-control
2519 (@ *streetmap* nearest-aux-points-select-control)))
2520 (chain *streetmap*
2521 (add-control
2522 (@ *streetmap* user-points-hover-control)))
2523 (chain *streetmap*
2524 (add-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)))
2533 (:catch (c)
2534 (chain *streetmap*
2535 (remove-layer (@ *streetmap*
2536 google-streetmap-layer)))))
2537 (chain *streetmap* (add-layer (@ *streetmap* nirvana-layer)))
2538 (chain *streetmap*
2539 (add-layer (@ *streetmap* nearest-aux-points-layer)))
2540 (chain *streetmap* (add-layer (@ *streetmap* survey-layer)))
2541 (chain *streetmap*
2542 (add-layer (@ *streetmap* cursor-layer)))
2543 (chain *streetmap*
2544 (add-layer (@ *streetmap* aux-point-layer)))
2545 (chain *streetmap*
2546 (add-layer (@ *streetmap* aux-data-linestring-layer)))
2547 (chain *streetmap*
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)))
2555 (loop
2556 for i from 0 below (lisp *number-of-images*)
2557 do (initialize-image i))
2558 (add-help-events)
2559 (request-restriction-select-choice)
2560 (chain *streetmap*
2561 (zoom-to-extent
2562 (if (lisp (stored-bbox))
2563 (new (chain *open-layers
2564 *bounds
2565 (from-string (lisp (stored-bbox)))
2566 (transform +geographic+ +spherical-mercator+)))
2567 +presentation-project-bounds+)))
2568 (let ((stored-cursor (lisp (stored-cursor))))
2569 (when stored-cursor
2570 (request-photos
2571 (new (chain *open-layers
2572 *lon-lat
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"
2580 (phoros-version))
2581 'phoros.js)
2582 hunchentoot:*dispatch-table*)