| | 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 |
|---|
| | 129 | (WEST NORTH) |
|---|
| | 130 | (POINT-LON-LAT (TOP-LEFT RECT)) |
|---|
| | 131 | (MULTIPLE-VALUE-BIND |
|---|
| | 132 | (EAST SOUTH) |
|---|
| | 133 | (POINT-LON-LAT (BOTTOM-RIGHT RECT)) |
|---|
| | 134 | (VALUES-NSEW)))) |
|---|
| | 135 | |
|---|
| | 136 | (defmethod bounding-box-x-y ((rect rectangle)) |
|---|
| | 137 | (multiple-value-bind |
|---|
| | 138 | (west north) |
|---|
| | 139 | (point-x-y (top-left rect)) |
|---|
| | 140 | (multiple-value-bind |
|---|
| | 141 | (east south) |
|---|
| | 142 | (point-x-y (bottom-right rect)) |
|---|
| | 143 | (values-nsew)))) |
|---|
| | 144 | |
|---|
| | 145 | |
|---|
| | 146 | ;; (defmethod split ((rect rectangle) side-num) |
|---|
| | 147 | ;; (let ((array (make-array (list side-num side-num)))) |
|---|
| | 148 | ;; (multiple-value-bind |
|---|
| | 149 | ;; (x y) |
|---|
| | 150 | ;; (point-x-y (top-left rect)) |
|---|
| | 151 | ;; (multiple-value-bind |
|---|
| | 152 | ;; (width height) |
|---|
| | 153 | ;; (width-height rect) |
|---|
| | 154 | ;; (let ((new-width (/ width side-num))) |
|---|
| | 155 | ;; (assert (integerp (/ width side-num))) |
|---|
| | 156 | ;; (assert (= width height)) |
|---|
| | 157 | ;; (dotimes (xind side-num) |
|---|
| | 158 | ;; (dotimes (yind side-num) |
|---|
| | 159 | ;; (setf (aref array xind yind) |
|---|
| | 160 | ;; (make-rectangle :x (+ x (* xind new-width)) :y |
|---|
| | 161 | ;; (+ y (* yind new-width)) :width new-width |
|---|
| | 162 | ;; :height new-width))))))) |
|---|
| | 163 | ;; array)) |
|---|
| | 164 | |
|---|
| | 165 | (defmethod quad-split ((rect rectangle) &optional (sub-rect-type 'rectangle)) |
|---|
| | 166 | (multiple-value-bind |
|---|
| | 167 | (x y) |
|---|
| | 168 | (point-x-y (top-left rect)) |
|---|
| | 169 | (multiple-value-bind |
|---|
| | 170 | (width height) |
|---|
| | 171 | (width-height rect) |
|---|
| | 172 | (let ((width1 (floor width 2))) |
|---|
| | 173 | (let ((width2 (ceiling width 2))) |
|---|
| | 174 | (let ((height1 (floor height 2))) |
|---|
| | 175 | (let ((height2 (ceiling height 2))) |
|---|
| | 176 | (assert (> width 1)) |
|---|
| | 177 | (assert (> height 1)) |
|---|
| | 178 | (list |
|---|
| | 179 | (make-rectangle :x x :y y :width width1 :height height1 :type |
|---|
| | 180 | sub-rect-type) |
|---|
| | 181 | (make-rectangle :x x :y (+ y height1) :width width1 :height |
|---|
| | 182 | height2 :type sub-rect-type) |
|---|
| | 183 | (make-rectangle :x (+ x width1) :y (+ y height1) :width width2 |
|---|
| | 184 | :height height2 :type sub-rect-type) |
|---|
| | 185 | (make-rectangle :x (+ x width1) :y y :width width2 :height height1 |
|---|
| | 186 | :type sub-rect-type))))))))) |
|---|
| | 187 | |
|---|
| | 188 | (defun point-in-rect-p (point rect) |
|---|
| | 189 | (multiple-value-bind |
|---|
| | 190 | (x y) |
|---|
| | 191 | (point-x-y point) |
|---|
| | 192 | (multiple-value-bind |
|---|
| | 193 | (r-x r-y) |
|---|
| | 194 | (point-x-y (top-left rect)) |
|---|
| | 195 | (multiple-value-bind |
|---|
| | 196 | (r-x2 r-y2) |
|---|
| | 197 | (point-x-y (bottom-right rect)) |
|---|
| | 198 | (and (<= r-x x (1- r-x2)) (<= r-y y (1- r-y2))))))) |
|---|
| | 199 | |
|---|
| | 200 | (defun contains-p (parent-rect rect) |
|---|
| | 201 | (and (point-in-rect-p (top-left rect) parent-rect) |
|---|
| | 202 | (point-in-rect-p (point1- (bottom-right rect)) parent-rect))) |
|---|
| | 203 | |
|---|
| | 204 | (defun intersects-p (parent-rect rect) |
|---|
| | 205 | (or (point-in-rect-p (top-left rect) parent-rect) |
|---|
| | 206 | (point-in-rect-p (point1- (bottom-right rect)) parent-rect))) |
|---|
| | 207 | |
|---|
| | 208 | (defun rectangle-union (rects) |
|---|
| | 209 | (let ((left (reduce #'min rects :key #'(lambda (r) (point-x-y (top-left r))))) |
|---|
| | 210 | (right (reduce #'max rects :key #'(lambda (r) (point-x-y (bottom-right r))))) |
|---|
| | 211 | (top (reduce #'min rects :key #'(lambda (r) (nth-value 1 (point-x-y (top-left r)))))) |
|---|
| | 212 | (bottom (reduce #'max rects :key #'(lambda (r) (nth-value 1 (point-x-y (bottom-right r))))))) |
|---|
| | 213 | (make-rectangle :x left :y top :width (- right left) :height (- bottom top)))) |
|---|
| | 214 | |
|---|
| | 215 | (defun rectangle-points (rect) |
|---|
| | 216 | (multiple-value-bind |
|---|
| | 217 | (r-x r-y) |
|---|
| | 218 | (point-x-y (top-left rect)) |
|---|
| | 219 | (multiple-value-bind |
|---|
| | 220 | (r-x2 r-y2) |
|---|
| | 221 | (point-x-y (bottom-right rect)) |
|---|
| | 222 | (loop for x from r-x below r-x2 for y from r-y below r-y2 collect |
|---|
| | 223 | (make-point :x x :y y))))) |
|---|
| | 224 | |
|---|
| | 225 | (defclass container () |
|---|
| | 226 | ((items :accessor items :initarg :items :initform nil))) |
|---|
| | 227 | |
|---|
| | 228 | (defclass quad () |
|---|
| | 229 | ((quads :accessor quads :initarg :quads :initform nil))) |
|---|
| | 230 | |
|---|
| | 231 | (defclass rectangle-container (rectangle container) |
|---|
| | 232 | ()) |
|---|
| | 233 | |
|---|
| | 234 | (defclass rectangle-quad (rectangle container quad) |
|---|
| | 235 | ()) |
|---|
| | 236 | |
|---|
| | 237 | (defmacro doarray ((array x y) &body body) |
|---|
| | 238 | `(destructuring-bind (xdim ydim) |
|---|
| | 239 | (array-dimensions ,array) |
|---|
| | 240 | (dotimes (,y ydim) |
|---|
| | 241 | (dotimes (,x xdim) |
|---|
| | 242 | ,@body)))) |
|---|
| | 243 | |
|---|
| | 244 | |
|---|
| | 245 | (defun float-text (float) |
|---|
| | 246 | (text (format nil "~F" float))) |
|---|
| | 247 | |
|---|
| | 248 | (defun integer-text (int) |
|---|
| | 249 | (text (format nil "~D" int))) |
|---|
| | 250 | |
|---|
| | 251 | (defun kml-format-points (points &optional (altitude 0)) |
|---|
| | 252 | (format nil "~:{~F,~F,~F ~}" |
|---|
| | 253 | (mapcar #'(lambda (p) (append (multiple-value-list (point-lon-lat p)) |
|---|
| | 254 | (list altitude))) |
|---|
| | 255 | points))) |
|---|
| | 256 | |
|---|
| | 257 | (defun kml-format-color (color &optional (opacity 255)) |
|---|
| | 258 | (format nil "~2,'0X~{~2,'0X~}" opacity (reverse color))) |
|---|
| | 259 | |
|---|
| | 260 | (defmethod kml-link ((href pathname)) |
|---|
| | 261 | (with-element "Link" |
|---|
| | 262 | (with-element "href" (text (enough-namestring href))) |
|---|
| | 263 | (with-element "viewRefreshMode" (text "onRegion")))) |
|---|
| | 264 | |
|---|
| | 265 | ;; (defmethod kml-link ((href puri:uri)) |
|---|
| | 266 | ;; (let ((string (with-output-to-string (out) |
|---|
| | 267 | ;; (puri:render-uri href out)))) |
|---|
| | 268 | ;; (kml-link string))) |
|---|
| | 269 | |
|---|
| | 270 | (defun kml-network-link (href rect lod) |
|---|
| | 271 | (with-element "NetworkLink" |
|---|
| | 272 | (kml-region rect lod) |
|---|
| | 273 | (kml-link href))) |
|---|
| | 274 | |
|---|
| | 275 | (defun kml-lat-lon-box (rect &optional (element "LatLonBox")) |
|---|
| | 276 | (bind-nsew (bounding-box-lon-lat rect) |
|---|
| | 277 | (with-element element |
|---|
| | 278 | (with-element "north" (float-text north)) |
|---|
| | 279 | (with-element "south" (float-text south)) |
|---|
| | 280 | (with-element "east" (float-text east)) |
|---|
| | 281 | (with-element "west" (float-text west))))) |
|---|
| | 282 | |
|---|
| | 283 | (defun kml-lat-lon-alt-box (rect) |
|---|
| | 284 | (kml-lat-lon-box rect "LatLonAltBox")) |
|---|
| | 285 | |
|---|
| | 286 | (defun kml-overlay (img-path rect &optional (drawOrder 0)) |
|---|
| | 287 | (with-element "GroundOverlay" |
|---|
| | 288 | (with-element "name" (text (file-namestring img-path))) |
|---|
| | 289 | (with-element "drawOrder" (integer-text drawOrder)) |
|---|
| | 290 | (with-element "Icon" |
|---|
| | 291 | (with-element "href" (text (enough-namestring img-path))) |
|---|
| | 292 | ;; (with-element "refreshMode" (text "...")) |
|---|
| | 293 | ) |
|---|
| | 294 | (kml-lat-lon-box rect))) |
|---|
| | 295 | |
|---|
| | 296 | (defun kml-region (rect lod) |
|---|
| | 297 | (with-element "Region" |
|---|
| | 298 | (kml-lat-lon-alt-box rect) |
|---|
| | 299 | (destructuring-bind (&key min max min-fade max-fade) lod |
|---|
| | 300 | (with-element "Lod" |
|---|
| | 301 | (when min (with-element "minLodPixels" (integer-text min))) |
|---|
| | 302 | (when max (with-element "maxLodPixels" (integer-text max))) |
|---|
| | 303 | (when min-fade (with-element "minFadeExtent" (integer-text min-fade))) |
|---|
| | 304 | (when max-fade (with-element "maxFadeExtent" (integer-text max-fade))))))) |
|---|
| | 305 | |
|---|
| | 306 | ;; end kml utils |
|---|