| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137 |
- (defun ts->ms (ts)
- (+ (* 1000 (local-time:timestamp-to-unix ts))
- (floor (local-time:nsec-of ts) 1000000)))
- (defun ms->ts (ms)
- (multiple-value-bind (unix msec) (floor ms 1000)
- (local-time:unix-to-timestamp unix :nsec (* msec 1000000))))
- (defun $between (a from to)
- (kv ($>= a from) ($< a to)))
- (defun reverse-geocode (lat lon)
- (let* ((data (yason:parse
- (flexi-streams:octets-to-string
- (drakma:http-request
- (format nil
- "http://open.mapquestapi.com/nominatim/v1/reverse.php?format=json&lat=~A&lon=~A"
- lat lon)))))
- (address (gethash "address" data)))
- (values (gethash "display_name" data)
- address)))
- (defun make-place-doc (loc)
- (let* ((point (cdr loc))
- (lat (geo:latitude-deg point))
- (lon (geo:longitude-deg point)))
- (multiple-value-bind (text address) (reverse-geocode lat lon)
- (kv
- (kv :ts (ts->ms (car loc)))
- (kv :type "place")
- (kv :title text)
- (kv :language (gethash "country_code" address))
- (kv :loc (kv (kv :type "Point") ; GeoJSON Point
- (kv :coordinates (list lon lat))))))))
- (defun save-places (places)
- (loop for place in places
- do (db.insert "events" (make-place-doc place))))
- (defun extract-places (locs)
- (loop
- for loc in locs
- for point = (cdr loc)
- with last-place and last-loc and moved and need-add
- when last-loc do (setf moved
- (geo:distance>=
- (geo:distance-between
- point
- (cdr last-loc))
- (geo:distance-meters 100)))
- when moved do (setf last-loc loc ; Store loc as possible place
- need-add t)
- when (and need-add last-place last-loc
- (geo:distance<
- (geo:distance-between
- (cdr last-place)
- (cdr last-loc))
- (geo:distance-meters 200))) do (setf need-add nil) ; Do not add same place after glitch
- when (and need-add
- (> (local-time:timestamp-difference
- (car loc)
- (car last-loc))
- 400)) ; When stayed in 100m for 6.6mins
- collect last-loc
- and do (setf need-add nil) (setf last-place last-loc)
- when (not last-loc) do (setf last-loc loc)))
- (defun doc->point (doc)
- (let ((coord (get-element "coordinates"
- (get-element "loc" doc))))
- (geo:point-deg (second coord) (first coord))))
- (defun doc->ts (doc)
- (local-time:unix-to-timestamp
- (floor (get-element "timestampMs" doc) 1000)))
- (defun docs->locs (docs)
- (loop for doc in docs
- collect (cons (doc->ts doc) (doc->point doc))))
- (defun ts->kv (field ts)
- (let ((nd (local-time:timestamp+ ts 1 :day)))
- ($between field
- (* 1000 (local-time:timestamp-to-unix ts))
- (* 1000 (local-time:timestamp-to-unix nd)))))
- (defun takedown-import (from-date)
- (let ((now (local-time:now)))
- (loop
- for ts = (local-time:parse-timestring from-date) then (local-time:timestamp+ ts 1 :day)
- for locs = (docs->locs (docs (db.sort "locations" (ts->kv "timestampMs" ts) :field "timestampMs" :limit 1440)))
- for places = (extract-places locs)
- when places do
- (format t "Inserting ~A places for ~A~%" (length places) ts)
- (save-places places)
- until (local-time:timestamp> ts now)
- finally (return ts))))
- (defvar *google-cookie* "__utma=237271068.1241244678.1400710443.1400710443.1400710443.1; __utmb=237271068.1.10.1400710443; __utmc=237271068; __utmz=237271068.1400710443.1.1.utmcsr=accounts.google.com|utmccn=(referral)|utmcmd=referral|utmcct=/CheckCookie; HSID=AHszUCr82JTQW17_e; SSID=ApnZu-rKMHkdGKhs8; APISID=UuaDeGjPNgssr6MD/ADVZqAW8jHCM6k3hB; SAPISID=G3CQ4IwqoD0VeqwU/AvbYPYWQ4j7TfA-Me; SID=DQAAAOoAAABmxJYlkDzluCCTEiRrYpicGjIpxCcFVOp8teXB5_LK__YNh1ic_r3o73qT6XTw6dtyGBMU6f6S2xEWUTzQ3WyGTHQDU9iA4-JDWKOEKL30gbJe--j7o6JcVRMHDtusEVnIXGR5lfWZbqJiQ1rcHEyfOdwa_YFHDNGxXZX8CqxOb2XbJg4LsCV2QAkAgPtnttzJy5L39JvVngkoTIf78_CAyVqZb-UPmWAHAMedw3pMjZNuK-vmEpABNiGyDPf6jXn8rSLFMd4VOxhruIsWttSxjS6w2docgpgaL2SDD8HLGeUuxqK7Ww4c_WLhkOiWF1g; NID=67=i0y21lGpfKdTt8LRVZKl-mUp54GsSFLb8FGLVzLFiU1gG4KbQ52HNNPf3-HrXhvmN81AxzGwQPPOIRQ6Z8ciMsG-_Ev7mbeoDtfw8gzLnCiaReU8vVayS2NSFQi6_zg5pStsIlG91eRyYjgy3CxpU-vnO_qYVTs4i03j3XQ")
- (defvar *google-manual-header* "GUem3al6owB4SvGgikUOjC6MFOw:1400710441096")
- (defun load-history (from &optional (to (local-time:timestamp+ from 1 :day)) (trim nil))
- (loop for loc in (cadadr (yason:parse
- (flexi-streams:octets-to-string
- (drakma:http-request
- "https://maps.google.com/locationhistory/b/0/apps/pvjson?t=0"
- :method :post
- :user-agent "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.114 Safari/537.36"
- :content (format nil "[null,~A,~A,~A]"
- (ts->ms from)
- (ts->ms to)
- (if trim "true" "false"))
- :additional-headers (list (cons "x-manualheader" *google-manual-header*)
- (cons "cookie" *google-cookie*)
- '("referer" . "https://maps.google.com/locationhistory/b/0"))))))
- collect (cons (ms->ts (parse-integer (cadr loc)))
- (geo:point-deg (caddr loc) (cadddr loc)))))
- (defun make-location-doc (loc)
- (let* ((point (cdr loc))
- (lat (geo:latitude-deg point))
- (lon (geo:longitude-deg point)))
- (kv
- (kv "timestampMs" (ts->ms (car loc)))
- (kv "loc" (kv (kv :type "Point") ; GeoJSON Point
- (kv :coordinates (list lon lat)))))))
- (defun save-locations (locations)
- (loop for loc in locations
- do (db.insert "locations" (make-location-doc loc))))
- (defun json-import (from &optional (to (local-time:timestamp+ from 1 :day)))
- (let* ((locs (load-history from to nil))
- (places (extract-places locs)))
- (format t "Saving ~A locations for ~A~%" (length locs) from)
- (save-locations locs)
- (format t "Saving ~A place events for ~A~%" (length places) from)
- (save-places places)))
|