| 1 |
(in-package :bos.web) |
|---|
| 2 |
|
|---|
| 3 |
(defclass sat-node (node-extension) |
|---|
| 4 |
((image :accessor image :initarg :image))) |
|---|
| 5 |
|
|---|
| 6 |
(defpersistent-class sat-layer () |
|---|
| 7 |
((name :reader name :initarg :name |
|---|
| 8 |
:index-type unique-index |
|---|
| 9 |
:index-reader find-sat-layer) |
|---|
| 10 |
(year :accessor year :initarg :year :initform 2000) |
|---|
| 11 |
(geo-box :reader geo-box :initarg :geo-box) |
|---|
| 12 |
(local-draw-order :reader local-draw-order :initarg :local-draw-order))) |
|---|
| 13 |
|
|---|
| 14 |
(defmethod print-object ((obj sat-layer) stream) |
|---|
| 15 |
(print-unreadable-object (obj stream :type t :identity t) |
|---|
| 16 |
(format stream "name: ~s" (name obj)))) |
|---|
| 17 |
|
|---|
| 18 |
(defmethod destroy-object :before ((obj sat-layer)) |
|---|
| 19 |
(when (boundp '*quad-tree*) |
|---|
| 20 |
;; when the transaction log is being loaded, *quad-tree* is still |
|---|
| 21 |
;; unbound, because it is only initialized, when the entire store |
|---|
| 22 |
;; has been loaded -- an example for the fact that the quad-tree |
|---|
| 23 |
;; should have been implemented as a proper store index |
|---|
| 24 |
(assert (null (sat-layer-top-level-nodes obj)) nil |
|---|
| 25 |
"Please invoke (remove-sat-layer-from-quad-tree (find-store-object ~D)) before deleting ~s." |
|---|
| 26 |
(store-object-id obj) obj)) |
|---|
| 27 |
(dolist (sat-image (class-instances 'sat-image)) |
|---|
| 28 |
(when (eq obj (layer sat-image)) |
|---|
| 29 |
(delete-object sat-image)))) |
|---|
| 30 |
|
|---|
| 31 |
(defun remove-sat-layer-from-quad-tree (sat-layer) |
|---|
| 32 |
(let ((nodes (collect-nodes (constantly t) (first (sat-layer-top-level-nodes sat-layer))))) |
|---|
| 33 |
(mapc #'delete-node-extension nodes) |
|---|
| 34 |
(values))) |
|---|
| 35 |
|
|---|
| 36 |
(defun sat-layer-top-level-nodes (sat-layer) |
|---|
| 37 |
(check-type sat-layer sat-layer) |
|---|
| 38 |
(let ((nodes ()) |
|---|
| 39 |
top-level-depth |
|---|
| 40 |
(state 'no-layer-node)) |
|---|
| 41 |
(block collect |
|---|
| 42 |
(map-nodes (lambda (n) |
|---|
| 43 |
(let ((layer-node (find-if (lambda (e) (and (eql (name e) (name sat-layer)) |
|---|
| 44 |
(typep e 'sat-node))) |
|---|
| 45 |
(extensions n)))) |
|---|
| 46 |
(ecase state |
|---|
| 47 |
(no-layer-node |
|---|
| 48 |
(when layer-node |
|---|
| 49 |
(push layer-node nodes) |
|---|
| 50 |
(setq state 'got-top-level-layer-node) |
|---|
| 51 |
(setq top-level-depth (depth n)))) |
|---|
| 52 |
(got-top-level-layer-node |
|---|
| 53 |
(if (and layer-node (= (depth n) top-level-depth)) |
|---|
| 54 |
(push layer-node nodes) |
|---|
| 55 |
(return-from collect)))))) |
|---|
| 56 |
*quad-tree* |
|---|
| 57 |
:prune-test (lambda (n) (not (geo-box-intersect-p (geo-box n) (geo-box sat-layer)))) |
|---|
| 58 |
:order :breadth-first)) |
|---|
| 59 |
(nreverse nodes))) |
|---|
| 60 |
|
|---|
| 61 |
(defpersistent-class sat-image (store-image) |
|---|
| 62 |
((layer :reader layer :initarg :layer) |
|---|
| 63 |
(path :reader path :initarg :path) |
|---|
| 64 |
(image-geo-box :accessor image-geo-box |
|---|
| 65 |
:initarg :image-geo-box |
|---|
| 66 |
:type geo-box |
|---|
| 67 |
:documentation "can be different from base-node's geo-box"))) |
|---|
| 68 |
|
|---|
| 69 |
(defmethod print-object ((obj sat-image) stream) |
|---|
| 70 |
(print-unreadable-object (obj stream :type t :identity t) |
|---|
| 71 |
(format stream "~s of layer ~s" (path obj) (name (layer obj))))) |
|---|
| 72 |
|
|---|
| 73 |
(defun quad-tree-insert-sat-image (sat-image) |
|---|
| 74 |
(let ((node (ensure-node-with-path *quad-tree* (path sat-image)))) |
|---|
| 75 |
(make-instance 'sat-node |
|---|
| 76 |
:name (name (layer sat-image)) |
|---|
| 77 |
:base-node node |
|---|
| 78 |
:image sat-image))) |
|---|
| 79 |
|
|---|
| 80 |
(defun quad-tree-insert-sat-images () |
|---|
| 81 |
(mapc #'quad-tree-insert-sat-image (class-instances 'sat-image))) |
|---|
| 82 |
|
|---|
| 83 |
(register-transient-init-function 'quad-tree-insert-sat-images |
|---|
| 84 |
'make-quad-tree) |
|---|
| 85 |
|
|---|
| 86 |
(defmethod name ((obj sat-image)) |
|---|
| 87 |
(name (layer obj))) |
|---|
| 88 |
|
|---|
| 89 |
(defconstant +max-sat-image-tile-pixel-area+ (float (expt 256 2) 0d0)) |
|---|
| 90 |
|
|---|
| 91 |
(defun sat-image-tile-properties (image geo-box tile-geo-box &optional scaling) |
|---|
| 92 |
#+nil(declare (optimize speed)) |
|---|
| 93 |
;; (the (double-float 0d0 #.(float most-positive-fixnum 0d0)) ...) |
|---|
| 94 |
;; might be useful |
|---|
| 95 |
(let* ((gw (float (the (integer 1 #.most-positive-fixnum) (cl-gd:image-width image)) 0d0)) |
|---|
| 96 |
(gh (float (the (integer 1 #.most-positive-fixnum) (cl-gd:image-height image)) 0d0)) |
|---|
| 97 |
(w (geo-box-west geo-box)) |
|---|
| 98 |
(n (geo-box-north geo-box)) |
|---|
| 99 |
(e (geo-box-east geo-box)) |
|---|
| 100 |
(s (geo-box-south geo-box)) |
|---|
| 101 |
(bw (geo-box-west tile-geo-box)) |
|---|
| 102 |
(bn (geo-box-north tile-geo-box)) |
|---|
| 103 |
(be (geo-box-east tile-geo-box)) |
|---|
| 104 |
(bs (geo-box-south tile-geo-box)) |
|---|
| 105 |
(xu (/ (- e w) gw)) |
|---|
| 106 |
(yu (/ (- n s) gh)) |
|---|
| 107 |
(px (floor (/ (- bw w) xu))) |
|---|
| 108 |
(py (floor (/ (- n bn) yu))) |
|---|
| 109 |
(px2 (ceiling (/ (- be w) xu))) |
|---|
| 110 |
(py2 (ceiling (/ (- n bs) yu))) |
|---|
| 111 |
(pw (- px2 px)) |
|---|
| 112 |
(ph (- py2 py)) |
|---|
| 113 |
(rounded-geo-box (make-geo-box (+ (* px xu) w) |
|---|
| 114 |
(- n (* py yu)) |
|---|
| 115 |
(+ (* px2 xu) w) |
|---|
| 116 |
(- n (* py2 yu)))) |
|---|
| 117 |
(scaling (if scaling |
|---|
| 118 |
scaling |
|---|
| 119 |
(ceiling (sqrt (/ (* pw ph) +max-sat-image-tile-pixel-area+))))) |
|---|
| 120 |
(tw (round (/ pw scaling))) |
|---|
| 121 |
(th (round (/ ph scaling)))) |
|---|
| 122 |
(values scaling |
|---|
| 123 |
pw ph px py px2 py2 |
|---|
| 124 |
tw th rounded-geo-box))) |
|---|
| 125 |
|
|---|
| 126 |
(defun make-sat-image-tile (image geo-box quad-node tile-geo-box name max-scaling) |
|---|
| 127 |
(assert (find-sat-layer name)) |
|---|
| 128 |
(multiple-value-bind (scaling |
|---|
| 129 |
pw ph px py px2 py2 |
|---|
| 130 |
tw th rounded-geo-box) |
|---|
| 131 |
(sat-image-tile-properties image geo-box tile-geo-box max-scaling) |
|---|
| 132 |
(declare (ignore scaling px2 py2)) |
|---|
| 133 |
(let ((path (node-path quad-node))) |
|---|
| 134 |
(cl-gd:with-image (cl-gd:*default-image* tw th t) |
|---|
| 135 |
(cl-gd:copy-image image cl-gd:*default-image* |
|---|
| 136 |
px py 0 0 |
|---|
| 137 |
pw ph |
|---|
| 138 |
:resize t :resample t |
|---|
| 139 |
:dest-width tw :dest-height th) |
|---|
| 140 |
(quad-tree-insert-sat-image |
|---|
| 141 |
(make-store-image :class-name 'sat-image |
|---|
| 142 |
:name (format nil "~A-~{~D~}" name path) |
|---|
| 143 |
:type :jpg |
|---|
| 144 |
:initargs `(:path ,path |
|---|
| 145 |
:layer ,(find-sat-layer name) |
|---|
| 146 |
:image-geo-box ,rounded-geo-box))))))) |
|---|
| 147 |
|
|---|
| 148 |
(defun make-sat-image-tiles-for-depth (image geo-box layer start-depth) |
|---|
| 149 |
(labels ((layer-quad-nodes () |
|---|
| 150 |
(let (nodes) |
|---|
| 151 |
(ensure-intersecting-children *quad-tree* geo-box |
|---|
| 152 |
(lambda (n) (when (= start-depth (depth n)) |
|---|
| 153 |
(push n nodes))) |
|---|
| 154 |
(lambda (n) (= start-depth (depth n)))) |
|---|
| 155 |
(mapcar |
|---|
| 156 |
(lambda (quad-node) |
|---|
| 157 |
(list quad-node (geo-box-intersection geo-box (geo-box quad-node)))) |
|---|
| 158 |
nodes))) |
|---|
| 159 |
(quad-node (node) (first node)) |
|---|
| 160 |
(tile-geo-box (node) (second node)) |
|---|
| 161 |
(pw-ph-large-enough (node) |
|---|
| 162 |
(multiple-value-bind (scaling pw ph) |
|---|
| 163 |
(sat-image-tile-properties image geo-box (tile-geo-box node)) |
|---|
| 164 |
(declare (ignore scaling)) |
|---|
| 165 |
(and (> pw 1) (> ph 1)))) |
|---|
| 166 |
(max-scaling (nodes) |
|---|
| 167 |
(reduce #'max nodes |
|---|
| 168 |
:key (lambda (node) |
|---|
| 169 |
(sat-image-tile-properties image geo-box (tile-geo-box node)))))) |
|---|
| 170 |
(let* ((name (name layer)) |
|---|
| 171 |
(nodes (remove-if-not #'pw-ph-large-enough (layer-quad-nodes))) |
|---|
| 172 |
(max-scaling (max-scaling nodes))) |
|---|
| 173 |
(format t "; creating ~a at depth ~a~%" name start-depth) ; |
|---|
| 174 |
(dolist (node nodes layer) |
|---|
| 175 |
(make-sat-image-tile image geo-box (quad-node node) |
|---|
| 176 |
(tile-geo-box node) name max-scaling)) |
|---|
| 177 |
(unless (= 1 max-scaling) |
|---|
| 178 |
(make-sat-image-tiles-for-depth image geo-box layer (1+ start-depth)))))) |
|---|
| 179 |
|
|---|
| 180 |
(defun make-sat-layer (image geo-box name local-draw-order &optional (start-depth 0)) |
|---|
| 181 |
(check-type name symbol) |
|---|
| 182 |
(assert (not (find-sat-layer name)) (name) |
|---|
| 183 |
"A sat-layer of name ~S already exists." name) |
|---|
| 184 |
(check-type image cl-gd::image) |
|---|
| 185 |
(assert (geo-box-encloses-p *m2-geo-box* geo-box)) |
|---|
| 186 |
(check-type start-depth (integer 0)) |
|---|
| 187 |
(check-type local-draw-order (integer 0)) |
|---|
| 188 |
;; highest local-draw-order is reserved for the contract-tree |
|---|
| 189 |
(assert (< local-draw-order (1- +max-num-of-local-draw-order-levels+))) |
|---|
| 190 |
(when (find local-draw-order (class-instances 'sat-layer) :key #'local-draw-order) |
|---|
| 191 |
(cerror "create the new layer anyway" "There is already a sat-layer with the same local-draw-order '~A'." local-draw-order)) |
|---|
| 192 |
(let ((layer (make-instance 'sat-layer :name name :geo-box geo-box :local-draw-order local-draw-order))) |
|---|
| 193 |
(make-sat-image-tiles-for-depth image geo-box layer start-depth) |
|---|
| 194 |
layer)) |
|---|
| 195 |
|
|---|
| 196 |
;; (with-store-image (image (first (class-instances 'store-image))) |
|---|
| 197 |
;; (make-sat-layer image |
|---|
| 198 |
;; (rectangle-geo-box (make-rectangle :x 5400 :y 5400 :width 2000 :height 2000)) |
|---|
| 199 |
;; :sat1 |
|---|
| 200 |
;; 3)) |
|---|
| 201 |
|
|---|
| 202 |
|
|---|
| 203 |
;;; handlers |
|---|
| 204 |
|
|---|
| 205 |
(defclass sat-tree-kml-handler (page-handler) |
|---|
| 206 |
()) |
|---|
| 207 |
|
|---|
| 208 |
(defmethod handle ((handler sat-tree-kml-handler)) |
|---|
| 209 |
(with-query-params ((path) (name)) |
|---|
| 210 |
(let ((path (parse-path path)) |
|---|
| 211 |
(layer (find-sat-layer (intern (string-upcase name) #.(find-package "KEYWORD"))))) |
|---|
| 212 |
(assert layer nil "Cannnot find layer of name ~s." name) |
|---|
| 213 |
(let* ((quad-node (find-node-with-path *quad-tree* path)) |
|---|
| 214 |
(sat-node (find-if (lambda (e) (and (eql (name e) (name layer)) |
|---|
| 215 |
(typep e 'sat-node))) |
|---|
| 216 |
(extensions quad-node)))) |
|---|
| 217 |
(assert sat-node nil "There is no sat-node of name ~s at path ~s." name path) |
|---|
| 218 |
(let ((sat-image (image sat-node))) |
|---|
| 219 |
(hunchentoot:handle-if-modified-since (blob-timestamp sat-image)) |
|---|
| 220 |
(with-xml-response (:content-type "text/xml" #+nil"application/vnd.google-earth.kml+xml" |
|---|
| 221 |
:root-element "kml") |
|---|
| 222 |
(setf (hunchentoot:header-out :last-modified) |
|---|
| 223 |
(hunchentoot:rfc-1123-date (blob-timestamp sat-image))) |
|---|
| 224 |
(let ((lod (node-lod sat-node)) |
|---|
| 225 |
(rect (geo-box-rectangle (geo-box sat-node)))) |
|---|
| 226 |
(with-element "Document" |
|---|
| 227 |
(kml-region rect lod) |
|---|
| 228 |
(kml-overlay (format nil "http://~a/image/~d" (website-host) (store-object-id sat-image)) |
|---|
| 229 |
(geo-box-rectangle (image-geo-box sat-image)) |
|---|
| 230 |
:draw-order (compute-draw-order sat-node (local-draw-order layer)) |
|---|
| 231 |
;; :absolute 0 |
|---|
| 232 |
) |
|---|
| 233 |
(let ((*print-case* :downcase)) |
|---|
| 234 |
(dotimes (i 4) |
|---|
| 235 |
(let ((child (child sat-node i))) |
|---|
| 236 |
(when child |
|---|
| 237 |
(kml-network-link (format nil "http://~A/sat-tree-kml?name=~A&path=~{~D~}" |
|---|
| 238 |
(website-host) (name layer) (append path (list i))) |
|---|
| 239 |
:rect (geo-box-rectangle (geo-box child)) |
|---|
| 240 |
:lod (node-lod child)))))))))))))) |
|---|
| 241 |
|
|---|
| 242 |
(defclass sat-root-kml-handler (page-handler) |
|---|
| 243 |
()) |
|---|
| 244 |
|
|---|
| 245 |
(defmethod handle ((handler sat-root-kml-handler)) |
|---|
| 246 |
(with-query-params ((name)) |
|---|
| 247 |
(let ((*print-case* :downcase) |
|---|
| 248 |
(layer (find-sat-layer (intern (string-upcase name) #.(find-package "KEYWORD"))))) |
|---|
| 249 |
(assert layer nil "Cannnot find layer of name ~s." name) |
|---|
| 250 |
(let ((top-level-nodes (sat-layer-top-level-nodes layer))) |
|---|
| 251 |
(assert top-level-nodes) |
|---|
| 252 |
(hunchentoot:handle-if-modified-since (blob-timestamp (image (first top-level-nodes)))) |
|---|
| 253 |
(with-xml-response (:content-type "text/xml" #+nil"application/vnd.google-earth.kml+xml" |
|---|
| 254 |
:root-element "kml") |
|---|
| 255 |
(setf (hunchentoot:header-out :last-modified) |
|---|
| 256 |
(hunchentoot:rfc-1123-date (blob-timestamp (image (first top-level-nodes))))) |
|---|
| 257 |
(with-element "Document" |
|---|
| 258 |
(dolist (node top-level-nodes) |
|---|
| 259 |
(kml-network-link (format nil "http://~A/sat-tree-kml?name=~A&path=~{~D~}" |
|---|
| 260 |
(website-host) (name layer) (node-path node)) |
|---|
| 261 |
:rect (geo-box-rectangle (geo-box node)) |
|---|
| 262 |
:lod (node-lod node))))))))) |
|---|