root/trunk/projects/bos/web/kml-utils.lisp

Revision 3807, 10.8 kB (checked in by ksprotte, 2 months ago)

added a look-at key to kml-network-link that seems to work more reliably

Line 
1 (in-package :bos.web)
2
3 ;;; kml utils (including point / rect stuff) - for now
4
5 (defmacro values-nsew ()
6   '(values north south east west))
7
8 (defmacro bind-nsew (form &body body)
9   `(multiple-value-bind (north south east west)
10        ,form
11      ,@body))
12
13 (defmacro let-nsew ((north south east west) &body body)
14   `(let ((north ,north)
15          (south ,south)
16          (east ,east)
17          (west ,west))
18      ,@body))
19
20 (defclass point ()
21   ())
22
23 (defgeneric point-lon-lat (point))
24 (defgeneric point-x-y (point))
25
26 (defmethod print-object ((point point) stream)
27   (print-unreadable-object (point stream)
28     (multiple-value-bind (x y)
29         (point-x-y point)
30       (if (and (integerp x) (integerp y))
31           (format stream "~a,~a" x y)
32           (format stream "~,5f,~,5f" x y)))))
33
34 (defclass lon-lat-point (point)
35   ((lon :accessor %point-lon :initarg :lon)
36    (lat :accessor %point-lat :initarg :lat)))
37
38 (defmethod point-lon-lat ((p lon-lat-point))
39   (values (%point-lon p) (%point-lat p)))
40
41 (defmethod point-x-y ((p lon-lat-point))
42   (destructuring-bind (x y zone southhemi-p)
43       (geo-utm:lon-lat-to-utm-x-y (%point-lon p) (%point-lat p))
44     (assert (= +utm-zone+ zone))
45     (assert southhemi-p)
46     (values (- x +nw-utm-x+) (- +nw-utm-y+ y))))
47
48 (defclass x-y-point (point)
49   ((x :accessor %point-x :initarg :x)
50    (y :accessor %point-y :initarg :y)))
51
52 (defmethod point-x-y ((p x-y-point))
53   (values (%point-x p) (%point-y p)))
54
55 (defmethod point-lon-lat ((p x-y-point))
56   (destructuring-bind (lon lat)
57       (geo-utm:utm-x-y-to-lon-lat (+ +nw-utm-x+ (%point-x p))
58                                   (- +nw-utm-y+ (%point-y p))
59                                   +utm-zone+ t)
60     (values lon lat)))
61
62 (defun make-point (&key x y lon lat)
63   (cond
64     ((and x y (not lon) (not lat))
65      (make-instance 'x-y-point :x x :y y))
66     ((and lon lat (not x) (not y))
67      (make-instance 'lon-lat-point :lon lon :lat lat))
68     (t (error "Cannot make point"))))
69
70 (defun point-equal (a b)
71   (assert (and (typep a 'x-y-point) (typep b 'x-y-point))
72           nil
73           "point-equal only impl for 2 point-x-y")
74   (and (= (%point-x a) (%point-x b))
75        (= (%point-y a) (%point-y b))))
76
77 (defun point1- (point)
78   (multiple-value-bind (x y) (point-x-y point)
79     (make-point :x (1- x) :y (1- y))))
80
81 (defclass rectangle ()
82   ((top-left :accessor top-left :initarg :top-left)
83    (bottom-right :accessor bottom-right :initarg :bottom-right)))
84
85 (defmethod width-height ((rect rectangle))
86   (multiple-value-bind
87         (x y)
88       (point-x-y (top-left rect))
89     (multiple-value-bind
90           (x2 y2)
91         (point-x-y (bottom-right rect))
92       (let ((width (- x2 x))
93             (height (- y2 y)))
94         (values width height)))))
95
96 (defmethod size ((rect rectangle))
97   (multiple-value-bind (width height)
98       (width-height rect)
99     (max width height)))
100
101 (defmethod print-object ((rect rectangle) stream)
102   (print-unreadable-object (rect stream :type t :identity t)
103     (multiple-value-bind
104           (x y)
105         (point-x-y (top-left rect))
106       (multiple-value-bind
107             (x2 y2)
108           (point-x-y (bottom-right rect))
109         (let ((width (- x2 x)))
110           (let ((height (- y2 y)))
111             (format stream "~a,~a ~a x ~a" x y width height)))))))
112
113 (defun make-rectangle (&key x y width height (type 'rectangle))
114   (make-instance type
115                  :top-left (make-point :x x :y y)
116                  :bottom-right (make-point :x (+ x width) :y (+ y height))))
117
118 (defun make-rectangle2 (x-y-width-height)
119   (destructuring-bind (x y width height)
120       x-y-width-height
121     (make-rectangle :x x :y y :width width :height height)))
122
123 (defun rect-equal (a b)
124   (and (point-equal (top-left a) (top-left b))
125        (point-equal (bottom-right a) (bottom-right b))))
126
127 (defmethod bounding-box-lon-lat ((rect rectangle))
128   (multiple-value-bind (west north)
129       (point-lon-lat (top-left rect))
130     (multiple-value-bind (east south)
131         (point-lon-lat (bottom-right rect))
132       (values-nsew))))
133
134 (defmethod bounding-box-x-y ((rect rectangle))
135   (multiple-value-bind
136         (west north)
137       (point-x-y (top-left rect))
138     (multiple-value-bind
139           (east south)
140         (point-x-y (bottom-right rect))
141       (values-nsew))))
142
143
144 ;; (defmethod split ((rect rectangle) side-num)
145 ;;   (let ((array (make-array (list side-num side-num))))
146 ;;     (multiple-value-bind
147 ;;           (x y)
148 ;;         (point-x-y (top-left rect))
149 ;;       (multiple-value-bind
150 ;;             (width height)
151 ;;           (width-height rect)
152 ;;         (let ((new-width (/ width side-num)))
153 ;;           (assert (integerp (/ width side-num)))
154 ;;           (assert (= width height))
155 ;;           (dotimes (xind side-num)
156 ;;             (dotimes (yind side-num)
157 ;;               (setf (aref array xind yind)
158 ;;                     (make-rectangle :x (+ x (* xind new-width)) :y
159 ;;                                     (+ y (* yind new-width)) :width new-width
160 ;;                                     :height new-width)))))))
161 ;;     array))
162
163 (defmethod quad-split ((rect rectangle) &optional (sub-rect-type 'rectangle))
164   (multiple-value-bind
165         (x y)
166       (point-x-y (top-left rect))
167     (multiple-value-bind
168           (width height)
169         (width-height rect)
170       (let ((width1 (floor width 2)))
171         (let ((width2 (ceiling width 2)))
172           (let ((height1 (floor height 2)))
173             (let ((height2 (ceiling height 2)))
174               (assert (> width 1))
175               (assert (> height 1))
176               (list
177                (make-rectangle :x x :y y :width width1 :height height1 :type
178                                sub-rect-type)
179                (make-rectangle :x x :y (+ y height1) :width width1 :height
180                                height2 :type sub-rect-type)
181                (make-rectangle :x (+ x width1) :y (+ y height1) :width width2
182                                :height height2 :type sub-rect-type)
183                (make-rectangle :x (+ x width1) :y y :width width2 :height height1
184                                :type sub-rect-type)))))))))
185
186 (defun point-in-rect-p (point rect)
187   (multiple-value-bind
188         (x y)
189       (point-x-y point)
190     (multiple-value-bind
191           (r-x r-y)
192         (point-x-y (top-left rect))
193       (multiple-value-bind
194             (r-x2 r-y2)
195           (point-x-y (bottom-right rect))
196         (and (<= r-x x (1- r-x2)) (<= r-y y (1- r-y2)))))))
197
198 (defun contains-p (parent-rect rect)
199   (and (point-in-rect-p (top-left rect) parent-rect)
200        (point-in-rect-p (point1- (bottom-right rect)) parent-rect)))
201
202 (defun intersects-p (parent-rect rect)
203   (or (point-in-rect-p (top-left rect) parent-rect)
204       (point-in-rect-p (point1- (bottom-right rect)) parent-rect)))
205
206 (defun rectangle-union (rects)
207   (let ((left (reduce #'min rects :key #'(lambda (r) (point-x-y (top-left r)))))
208         (right (reduce #'max rects :key #'(lambda (r) (point-x-y (bottom-right r)))))
209         (top (reduce #'min rects :key #'(lambda (r) (nth-value 1 (point-x-y (top-left r))))))
210         (bottom (reduce #'max rects :key #'(lambda (r) (nth-value 1 (point-x-y (bottom-right r)))))))
211     (make-rectangle :x left :y top :width (- right left) :height (- bottom top))))
212
213 (defun rectangle-points (rect)
214   (multiple-value-bind
215         (r-x r-y)
216       (point-x-y (top-left rect))
217     (multiple-value-bind
218           (r-x2 r-y2)
219         (point-x-y (bottom-right rect))
220       (loop for x from r-x below r-x2 for y from r-y below r-y2 collect
221            (make-point :x x :y y)))))
222
223 (defclass container ()
224   ((items :accessor items :initarg :items :initform nil)))
225
226 (defclass quad ()
227   ((quads :accessor quads :initarg :quads :initform nil)))
228
229 (defclass rectangle-container (rectangle container)
230   ())
231
232 (defclass rectangle-quad (rectangle container quad)
233   ())
234
235 (defmacro doarray ((array x y) &body body)
236   `(destructuring-bind (xdim ydim)
237        (array-dimensions ,array)
238      (dotimes (,y ydim)
239        (dotimes (,x xdim)
240          ,@body))))
241
242
243 (defun float-text (float)
244   (text (format nil "~,20F" float)))
245
246 (defun integer-text (int)
247   (text (format nil "~D" int)))
248
249 (defun kml-format-points (points &optional (altitude 0))
250   (format nil "~:{~,20F,~,20F,~,20F ~}"
251           (mapcar #'(lambda (p) (append (multiple-value-list (point-lon-lat p))
252                                         (list altitude)))
253                   points)))
254
255 (defun kml-format-color (color &optional (opacity 255))
256   (format nil "~2,'0X~{~2,'0X~}" opacity (reverse color)))
257
258 (defmethod kml-link ((href string) &key refresh-on-region http-query)
259   (with-element "Link"
260     (with-element "href" (text href))
261     (when refresh-on-region
262       (with-element "viewRefreshMode" (text "onRegion")))
263     (when http-query
264       (with-element "httpQuery" (text http-query)))))
265
266 ;; (defmethod kml-link ((href puri:uri))
267 ;;   (let ((string (with-output-to-string (out)
268 ;;                   (puri:render-uri href out))))
269 ;;     (kml-link string)))
270
271
272 (defun kml-hide-children-style ()
273   (with-element "Style"
274     (with-element "ListStyle"
275       (with-element "listItemType" (text "checkHideChildren"))
276       (with-element "bgColor" (text "00ffffff")))))
277
278 (defun kml-network-link (href &key rect lod name
279                          fly-to-view hide-children
280                          look-at)
281   ;; http-query could be added to &key args
282   (with-element "NetworkLink"
283     (when name (with-element "name" (text name)))
284     (when rect (kml-region rect lod))
285     (when look-at (funcall look-at))
286     (when hide-children
287       (kml-hide-children-style))
288     (when fly-to-view (with-element "flyToView" (text "1")))
289     (kml-link href :refresh-on-region (and rect t))))
290
291 (defun kml-lat-lon-box (rect &optional (element "LatLonBox"))
292   (bind-nsew (bounding-box-lon-lat rect)
293     (with-element element
294       (with-element "north" (float-text north))
295       (with-element "south" (float-text south))
296       (with-element "east" (float-text east))
297       (with-element "west" (float-text west)))))
298
299 (defun kml-lat-lon-alt-box (rect)
300   (kml-lat-lon-box rect "LatLonAltBox"))
301
302 (defun kml-overlay (img-path rect &key (draw-order 0) absolute lod)
303   (with-element "GroundOverlay"
304     (with-element "name" (text (file-namestring img-path)))
305     (when lod (kml-region rect lod))
306     (with-element "drawOrder" (integer-text draw-order))
307     (with-element "Icon"
308       (with-element "href" (text img-path))
309       ;; (with-element "refreshMode" (text "..."))
310       )
311     (when absolute
312       (with-element "altitude" (text (princ-to-string absolute)))
313       (with-element "altitudeMode" (text "absolute")))
314     (kml-lat-lon-box rect)))
315
316 (defun kml-region (rect lod)
317   (with-element "Region"
318     (kml-lat-lon-alt-box rect)
319     (destructuring-bind (&key min max min-fade max-fade) lod
320       (with-element "Lod"
321         (when min (with-element "minLodPixels" (integer-text min)))
322         (when max (with-element "maxLodPixels" (integer-text max)))
323         (when min-fade (with-element "minFadeExtent" (integer-text min-fade)))
324         (when max-fade (with-element "maxFadeExtent" (integer-text max-fade)))))))
325
326 ;; end kml utils
Note: See TracBrowser for help on using the browser.