root/trunk/projects/bos/m2/map.lisp @ 2866

Revision 2866, 10.4 KB (checked in by hans, 2 years ago)

fix encoding errors

  • Property svn:eol-style set to native
  • Property svn:keywords set to author date id revision
Line 
1(in-package :bos.m2)
2
3;;;; ORIGINAL-MAP-TILE
4
5(define-persistent-class original-map-tile ()
6  ((x :read)
7   (y :read)
8   (image :update :relaxed-object-reference t))
9  (:default-initargs :image nil)
10  (:class-indices (tile-index :index-type array-index
11                              :slots (x y)
12                              :index-reader original-map-tile-at
13                              :index-initargs (:dimensions (list (/ +width+ +m2tile-width+) (/ +width+ +m2tile-width+))))))
14
15(defmethod print-object ((object original-map-tile) stream)
16  (print-unreadable-object (object stream :type t :identity nil)
17    (ignore-errors
18      (with-slots (x y image) object
19          (format stream "at (~D,~D) ~:[(no image)~;~A~]"
20                  (* x +m2tile-width+)
21                  (* y +m2tile-width+)
22                  image)))))
23
24(defun get-original-map-tile (x y)
25  (original-map-tile-at (list (floor x +m2tile-width+) (floor y +m2tile-width+))))
26
27(defun get-original-image-at (x y)
28  (let ((tile (get-original-map-tile x y)))
29    (and tile
30         (original-map-tile-image tile))))
31
32(defun ensure-original-map-tile (x y)
33  (or (get-original-map-tile x y)
34      (make-object 'original-map-tile
35                   :x (floor x +m2tile-width+)
36                   :y (floor y +m2tile-width+))))
37
38;;;; IMAGE-TILE
39;;;;
40;;;; Kachel mit Kartenausschnitt - Jede Kachel (tile) enthÀlt ein
41;;;; Image mit dem Ausschnitt aus dem Satellitenbild im Format 90x90
42;;;; Pixel.  Das Image ist im Slot 'original-image im Originalzustand
43;;;; abgelegt.  Im Slot 'current-images ist eine Hashtable mit Images
44;;;; in verschiedenen Renderings abgelegt.  Der Key der Hashtable ist
45;;;; der Typ des Derivats (nil => Originalbild, contracts => Originalbild
46;;;; mit kolorierten verkauften Gebieten etc).
47
48;;;; image-tiles verwalten die Erzeugung von Kacheln mit
49;;;; eingezeichneten VertrÀgen.  Die VertrÀge werden dabei farblich
50;;;; unterschiedlich markiert.  Die markierte Version der Kachel wird
51;;;; als transienter Slot mitgefÃŒhrt, d.h. bei einem Neustart des
52;;;; Systems werden die Kacheln beim Zugriff neu berechnet.
53
54;;;; Die Implementation der Bildverarbeitungsroutinen erfolgt dabei
55;;;; mit einer primitiven Image-Klasse, die ein Truecolor-Bild als
56;;;; 2D-Array von 32 bit langen RGBA-Werten behandelt.  Dies ist
57;;;; notwendig, da cl-gd den Zugriff auf einzelne Pixelwerte derzeit
58;;;; nur ÃŒber das FFI erlaubt, was extrem langsam ist.  Es gibt eine
59;;;; alternative API, mit der man schnell ÃŒber cl-gd-images iterieren
60;;;; kann, sie ist jedoch makrobasiert und nicht flexibel - Sie
61;;;; erlaubt ausschließlich das Iterieren ÃŒber ein Image.
62
63(defun colorize-pixel (pixel-rgb-value color-red color-green color-blue)
64  "Colorize the given PIXEL-RGB-VALUE in the COLOR given.
65PIXEL-RGB-VALUE is a raw truecolor pixel with RGB components.  COLOR
66is a truecolor PIXEL with the raw color to assign to the pixel.
67PIXEL-RGB-VALUE is converted to grayscale, the grayscale level is used
68to determine the intensity of the returned RGB value."
69  (declare (fixnum pixel-rgb-value))
70  (let* ((red (ldb (byte 8 16) pixel-rgb-value))
71         (green (ldb (byte 8 8) pixel-rgb-value))
72         (blue (ldb (byte 8 0) pixel-rgb-value))
73         (level (/ (+ (* 0.3 red) (* 0.59 green) (* 0.11 blue)) 255)))
74    (setq red (floor (* color-red level))
75          green (floor (* color-green level))
76          blue (floor (* color-blue level)))
77    (setf (ldb (byte 8 16) pixel-rgb-value) red)
78    (setf (ldb (byte 8 8) pixel-rgb-value) green)
79    (setf (ldb (byte 8 0) pixel-rgb-value) blue)
80    pixel-rgb-value))
81
82(defun point-in-any-allocation-area-p (x-coord y-coord)
83  (find-if #'(lambda (allocation-area)
84               ;; first check whether point is in bounding box, then do full polygon check
85               (and (point-in-polygon-p x-coord y-coord (allocation-area-bounding-box allocation-area))
86                    (point-in-polygon-p x-coord y-coord (allocation-area-vertices allocation-area))))
87           (store-objects-with-class 'allocation-area)))
88 
89(defclass image-tile (tile)
90  ((original-image :documentation "Original satellite image"
91                   :initform nil)
92   (changed-time :initarg :changed-time
93                 :accessor image-tile-changed-time
94                 :documentation "Timestamp of last change in contracts pointing to this tile")
95   (layers :initarg :layers :reader image-tile-layers))
96  (:default-initargs :type :png :changed-time (get-universal-time) :layers '(background areas contracts palette)))
97
98(defmethod initialize-instance :after ((tile image-tile) &key nw-x nw-y width &allow-other-keys)
99  (register-rect-subscriber *rect-publisher* tile
100                            (list nw-x nw-y width width) #'image-tile-changed))
101
102(defmethod image-tile-original-image ((tile image-tile))
103  (with-slots (original-image nw-x nw-y) tile
104    (unless original-image
105      (setf original-image (get-original-image-at nw-x nw-y)))
106    original-image))
107
108(defmethod image-tile-process ((tile image-tile) (operation (eql :background)))
109  (when (image-tile-original-image tile)
110    (with-store-image (original-image (image-tile-original-image tile))
111      (copy-image original-image *default-image* 0 0 0 0 (image-width) (image-height)))))
112
113(defmethod image-tile-process ((tile image-tile) (operation (eql :areas)))
114  (do-rows (y)
115    (do-pixels-in-row (x)
116      (when (point-in-any-allocation-area-p (tile-absolute-x tile x)
117                                            (tile-absolute-y tile y))
118        (setf (raw-pixel) (apply #'colorize-pixel (raw-pixel) '(220 220 220)))))))
119
120(defmethod image-tile-process ((tile image-tile) (operation (eql :contracts)))
121  (do-rows (y)
122    (do-pixels-in-row (x)
123      (let* ((m2 (object-at tile (tile-absolute-x tile x) (tile-absolute-y tile y)))
124             (contract (and m2 (m2-contract m2))))
125        (when (and contract (contract-paidp contract))
126          (setf (raw-pixel) (apply #'colorize-pixel (raw-pixel) (contract-color contract))))))))
127
128(defvar *tile-proc-statistics* (make-statistics-table))
129
130(defmethod image-tile-process ((tile image-tile) (operation (eql :palette)))
131  (true-color-to-palette))
132
133(defmethod image-tile-image ((tile image-tile) &optional imageproc-statements)
134  (unless imageproc-statements
135    (setq imageproc-statements '((:background) (:areas) (:contracts) (:palette))))
136  (let ((image (create-image (tile-width tile) (tile-height tile) t)))
137    (with-default-image (image)
138      (fill-image 0 0 :color (find-color 255 255 255))
139      (dolist (statement imageproc-statements)
140        (with-statistics-log (*tile-proc-statistics* (car statement))
141          (apply #'image-tile-process tile statement))))
142    image))
143
144(defgeneric generate-current-image (tile)
145  (:documentation "Generate the transient image"))
146
147(defmethod image-tile-changed ((image-tile image-tile) contract &optional (time (get-universal-time)))
148  (declare (ignore contract))
149  (setf (slot-value image-tile 'changed-time) time))
150
151(defun image-from-tiles (image-pathname tiles &key if-exists)
152  "Draw an image consisting of the given tiles on a new canvas which encloses all the tiles"
153  (multiple-value-bind
154        (left top width height)
155      (compute-bounding-box (mapcan #'(lambda (tile) (list (cons (tile-nw-x tile) (tile-nw-y tile))
156                                                           (cons (+ +m2tile-width+ (tile-nw-x tile)) (+ +m2tile-width+ (tile-nw-y tile)))))
157                                    tiles))
158    (let ((right (+ left width))
159          (bottom (+ top height)))
160      (with-image (resulting-image width height t)
161        (loop with tile-top = (* +m2tile-width+ (floor top +m2tile-width+))
162              for y from tile-top upto bottom by +m2tile-width+
163              do (loop with tile-left = (* +m2tile-width+ (floor left +m2tile-width+))
164                       for x from tile-left upto right by +m2tile-width+
165                       for tile = (get-map-tile x y)
166                       do (if tile
167                              (progn
168                                (copy-image (image-tile-image tile)
169                                            resulting-image
170                                            0 0
171                                            (- x tile-left) (- y tile-top)
172                                            +m2tile-width+ +m2tile-width+))
173                              (warn "tile at ~D/~D not found?" x y))))
174        (write-image-to-file image-pathname :image resulting-image :if-exists if-exists)
175        t))))
176
177;;; Aufteilen der Karte in Kacheln:
178
179; (split-map +overview-tile-width+ "/home/bknr/tiles-2700/" "/home/bknr/tiles-90/")
180
181;;; Laden den Kacheln als BLOBs in die Datenbank:
182
183; (import-tiles "/data/overview/")
184
185(defun namstring (x)
186  ;; XXX geht nicht, liefert sogar mit :FOR-INPUT NIL staendig NIL zurueck.
187  ;; (ext:unix-namestring x :for-input nil)
188  (namestring x))
189
190(defun import-tiles (directory)
191  (bknr.datastore::without-sync ()
192    (dolist (px (directory directory :all nil))
193      (print px)
194      (let ((x (parse-integer (car (last (pathname-directory px)))))
195            (i 0))
196        (dolist (image-pathname (directory px :all nil))
197          (handler-case
198              (let* ((y (parse-integer (pathname-name image-pathname)))
199                     (tile (ensure-original-map-tile x y)))
200                (when (zerop (mod i 100))
201                  (princ #\.))
202                (incf i)
203                (force-output)
204                (when (original-map-tile-image tile)
205                  (delete-object (original-map-tile-image tile)))
206                (change-slot-values tile 'image
207                                    (import-image image-pathname :name (format nil "tile-~D-~D" x y))))
208            (error (e)
209              (warn "failed to import ~A: ~A" image-pathname e))))))))
210
211(defun split-map (width input-directory output-directory)
212  (assert (zerop (mod 2700 width)))
213  (dotimes (y 4)
214    (dotimes (x 4)
215      (print (cons x y))
216      (force-output)
217      (split-image
218       output-directory
219       (merge-pathnames (format nil "sl_utm50s_~2,'0D.png" (+ (* y 4) x 1))
220                        input-directory)
221       width width
222       :offset-x (* x 2700)
223       :offset-y (* y 2700)))))
224
225(defun split-image (directory full-image-pathname tile-width tile-height
226                    &key (offset-x 0) (offset-y 0) (zoom 1))
227  (cl-gd:with-image-from-file (full full-image-pathname)
228    (cl-gd:with-image (part (* tile-width zoom) (* tile-height zoom) t)
229      (dotimes (i (truncate (cl-gd:image-width full) tile-width))
230        (princ i)
231        (dotimes (j (truncate (cl-gd:image-height full) tile-height))
232          (when (zerop (mod j 100))
233            (princ #\.))
234          (force-output)
235          (let* ((x (* i tile-width))
236                 (y (* j tile-height))
237                 (map-x (+ (* i tile-width) offset-x))
238                 (map-y (+ (* j tile-height) offset-y))
239                 (out (merge-pathnames (make-pathname :directory (list :relative (write-to-string map-x))
240                                                      :name (write-to-string map-y)
241                                                      :type "png")
242                                       directory)))
243            (apply #'cl-gd:copy-image
244                   full part x y 0 0 tile-width tile-height
245                   (if (eql zoom 1)
246                       nil
247                       (list :resize t
248                             :dest-width (* tile-width zoom)
249                             :dest-height (* tile-height zoom))))
250            (ensure-directories-exist out)
251            (cl-gd:write-image-to-file out :type :png :image part)))))))
Note: See TracBrowser for help on using the browser.