process-locations.lisp 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. (defun ts->ms (ts)
  2. (+ (* 1000 (local-time:timestamp-to-unix ts))
  3. (floor (local-time:nsec-of ts) 1000000)))
  4. (defun ms->ts (ms)
  5. (multiple-value-bind (unix msec) (floor ms 1000)
  6. (local-time:unix-to-timestamp unix :nsec (* msec 1000000))))
  7. (defun doc->point (doc)
  8. (geo:point-deg
  9. (/ (get-element "latitudeE7" doc) 1d7)
  10. (/ (get-element "longitudeE7" doc) 1d7)))
  11. (defun doc->ts (doc)
  12. (local-time:unix-to-timestamp
  13. (floor (get-element "timestampMs" doc) 1000)))
  14. (defun docs->locs (docs)
  15. (loop for doc in docs
  16. collect (cons (doc->ts doc) (doc->point (doc)))))
  17. (defun day-kv (date)
  18. (ts->kv (local-time:parse-timestring date)))
  19. (defun $between (a from to)
  20. (kv ($>= a from) ($< a to)))
  21. (defun ts->kv (ts)
  22. (let ((nd (local-time:timestamp+ ts 1 :day)))
  23. ($between "timestampMs"
  24. (* 1000 (local-time:timestamp-to-unix ts))
  25. (* 1000 (local-time:timestamp-to-unix nd)))))
  26. (defun reverse-geocode (lat lon)
  27. (let* ((data (yason:parse
  28. (flexi-streams:octets-to-string
  29. (drakma:http-request
  30. (format nil
  31. "http://open.mapquestapi.com/nominatim/v1/reverse.php?format=json&lat=~A&lon=~A"
  32. lat lon)))))
  33. (address (gethash "address" data)))
  34. (values (gethash "display_name" data)
  35. address)))
  36. (defun make-place-doc (ts point)
  37. (let ((lat (geo:latitude-deg point))
  38. (lon (geo:longitude-deg point)))
  39. (multiple-value-bind (text address) (reverse-geocode lat lon)
  40. (kv
  41. (kv :ts (ts->ms ts))
  42. (kv :type "place")
  43. (kv :title text)
  44. (kv :language (gethash "country_code" address))
  45. (kv :loc (kv (kv :type "Point") ; GeoJSON Point
  46. (kv :coordinates (list lon lat))))))))
  47. (defun extract-places (locs)
  48. (loop
  49. for loc in locs
  50. for point = (cdr loc)
  51. with last-place and moved and need-add
  52. when last-place do (setf moved
  53. (geo:distance>=
  54. (geo:distance-between
  55. point
  56. (cdr last-place))
  57. (geo:distance-meters 100)))
  58. when moved do (setf last-place loc
  59. need-add t)
  60. when (and need-add
  61. (> (local-time:timestamp-difference
  62. (car loc)
  63. (car last-place))
  64. 400)) ; When stayed in 100m for 6.6mins
  65. collect last-place
  66. and do (setf need-add nil)
  67. when (not last-place) do (setf last-place loc)))
  68. (defun takedown-import (from-date)
  69. (let ((now (local-time:now)))
  70. (loop
  71. for ts = (local-time:parse-timestring from-date) then (local-time:timestamp+ ts 1 :day)
  72. for locs = (docs->locs (docs (db.sort "locations" (ts->kv ts) :field "timestampMs" :limit 1440)))
  73. for places = (extract-places locs)
  74. when places do
  75. (format t "Inserting ~A places for ~A~%" (length places) ts)
  76. (loop
  77. for place in places
  78. do (db.insert "events" (make-place-doc (car place) (cdr place))))
  79. until (local-time:timestamp> ts now)
  80. finally (return ts))))
  81. (defvar *google-cookie* "HSID=Aki-sRjrdW5Ikzy3v; SSID=Anhcow9ItEBTeSEg7; APISID=-70vjTcatbI3TD06/AwZG1FFCCDcT4k9fE; SAPISID=dZqsEdkN1kgwG5XK/AHKoDsreJPB4Vw_Ua; NID=67=iUAiGWHq9qJ29H3u0bbOwMDP4jQ8Ef3bL6w9u_eKXRwhTlVyyjsIIWQ11OgszFDjNrdSK_ibUXJot6keGMAqDetXyRTnTyQlR5E0a0DoTbOrul4bX7WEvBr-A24va66oFrMCQYwjSVgR4BK2Q5A1tVcw5tX_iFOH3HfwRZFXzUG6IHHI-96X1XPRoK0TH4D9LVBESdlf-XZLJ8yPv2s7b7NVVgFM1rYwsSbyvA-ps0CTC0DrCDL4L6MOixw0PtuWPsCE7F4-LFzAWAb-7yVhLpLJ; SID=DQAAALEBAAAQi6d7KrAJlOV7d9a8PsJSV0PmImGKfIQ-SUbWmiN8oyGMDYdPdM7Og6dF6VE45MMhzJ5Ckwselasme4hxuTOHqYJfPr7oCj8rz_71TIn1WO-GAkG3nYSh_6wvsG4L2ediBbHJB0LYRMfxYMynr5WEjHm1xDiRGyeIKnY3aZTp6h6rFFPg4ansPuPrig9cSBPpA0vLW5mUko9adS_NpWN8BAY9AQK01GyPq_QV1fDp-hTXwXTFNrYupU576r9-47xDzuG9GPecsihi8WBeFMwyvQBGixmlmxT8eFO6oh4p6Ocp_Ed3OVN-HLeZ0zO0oJSWR8rG_mUAmX9sAG80dEtGMvXvYPnza86nsQNgMyK82GQSHW083W3zxoDE3RoTb6sZh49J8TKPBRzMWL7d7CnUxahmUf-Ro06LLAt6rOOpw5JJzBLZr3UzvKzJWV-2WzEyJlNwmD8fkVFL5qyYBsihhdxWYENR3l3TRjQ5vzLB9QyoBgyka56oVreDAgtnQYB7M8CpY1TnBb_jHJbKSL9nAJYpW8xAwfte5PTb7px5K1rGj9JwSvwYhYwbfJIHh1N4ClwkdLlHBiTvVHkW9QDx")
  82. (defvar *google-manual-header* "EI9X7MP_UN6KaVDOoQ4mMn91XdQ:1400594464924")
  83. (defun load-history (from to &key (trim nil))
  84. (loop for loc in (cadadr (yason:parse
  85. (flexi-streams:octets-to-string
  86. (drakma:http-request
  87. "https://maps.google.com/locationhistory/b/1/apps/pvjson?t=0"
  88. :method :post
  89. :content (format nil "[null,~A,~A,~A]"
  90. (ts->ms from)
  91. (ts->ms to)
  92. (if trim "true" "false"))
  93. :additional-headers (list (cons "x-manualheader" *google-manual-header*)
  94. (cons "cookie" *google-cookie*))))))
  95. collect (cons (ms->ts (parse-integer (cadr loc)))
  96. (geo:point-deg (caddr loc) (cadddr loc)))))