clojure-geohash / src / geohash / core.clj

(ns geohash.core)

(def base32-codes "0123456789bcdefghjkmnpqrstuvwxyz")

(defn- base32-index [ch]
  (. base32-codes indexOf (str ch)))

(defstruct geocode-data
  :maxlon :minlon :maxlat :minlat
  :islon :bits :hashcode :hashstring)

(defn- do-encode
  [lat lon prec geocode-data]
  (if (= (count (:hashstring geocode-data)) prec)
    (:hashstring geocode-data)
    (if (true? (:islon geocode-data))
      (let [
            mid (/ (+ (:maxlon geocode-data) (:minlon geocode-data)) 2)
            maxlon (if (> lon mid) (:maxlon geocode-data) mid)
            minlon (if (> lon mid) mid (:minlon geocode-data))
            hashcode (if (> lon mid) 
                       (+ (bit-shift-left (:hashcode geocode-data) 1) 1) 
                       (+ (bit-shift-left (:hashcode geocode-data) 1) 0))
            bits (inc (:bits geocode-data))
            hashstring (if (= bits 5) 
                         (str (:hashstring geocode-data) (nth base32-codes hashcode)) 
                         (:hashstring geocode-data))]
        (recur lat lon prec
          (assoc geocode-data
            :maxlon maxlon
            :minlon minlon
            :hashcode (if (= bits 5) 0 hashcode)
            :bits (if (= bits 5) 0 bits)
            :hashstring hashstring
            :islon (not (:islon geocode-data)))))
      (let [
            mid (/ (+ (:maxlat geocode-data) (:minlat geocode-data)) 2)
            maxlat (if (> lat mid) (:maxlat geocode-data) mid)
            minlat (if (> lat mid) mid (:minlat geocode-data))
            hashcode (if (> lat mid) 
                       (+ (bit-shift-left (:hashcode geocode-data) 1) 1) 
                       (+ (bit-shift-left (:hashcode geocode-data) 1) 0))
            bits (inc (:bits geocode-data))
            hashstring (if (= bits 5) 
                         (str (:hashstring geocode-data) (nth base32-codes hashcode)) 
                         (:hashstring geocode-data))]
        (recur lat lon prec
          (assoc geocode-data
            :maxlat maxlat
            :minlat minlat
            :hashcode (if (= bits 5) 0 hashcode)
            :bits (if (= bits 5) 0 bits)
            :hashstring hashstring
            :islon (not (:islon geocode-data))))))))

(defn- do-decode-hashcode
  [bits hash-code geocode-data]
  (loop [bits 4 geocode-data geocode-data]
    (if (< bits 0) geocode-data
      (if (true? (:islon geocode-data))
        (let [
              bit (bit-and (bit-shift-right hash-code bits) 1)
              mid (/ (+ (:maxlon geocode-data) (:minlon geocode-data)) 2)
              minlon (if (= bit 1) mid (:minlon geocode-data))
              maxlon (if (= bit 1) (:maxlon geocode-data) mid)]
          (recur (dec bits)
            (assoc geocode-data
              :minlon minlon
              :maxlon maxlon
              :islon (not (:islon geocode-data)))))
        (let [
              bit (bit-and (bit-shift-right hash-code bits) 1)
              mid (/ (+ (:maxlat geocode-data) (:minlat geocode-data)) 2)
              minlat (if (= bit 1) mid (:minlat geocode-data))
              maxlat (if (= bit 1) (:maxlat geocode-data) mid)]
          (recur (dec bits)
            (assoc geocode-data
              :minlat minlat
              :maxlat maxlat
              :islon (not (:islon geocode-data)))))))))

(defn- do-decode-bbox [hash-codes geocode-data]
  (if (empty? hash-codes)
    geocode-data
    (let [local-geocode-data (do-decode-hashcode 5 (first hash-codes) geocode-data)]
      (recur (rest hash-codes) local-geocode-data))))

(defn decode-bbox [hashstring]
  (let [
        hashcodes (map base32-index hashstring)
        bbox (do-decode-bbox hashcodes (struct geocode-data 180.0 -180.0 90.0 -90.0 true nil nil nil))]
    (dissoc bbox :islon :bits :hashcode :hashstring)))

(defn decode [hashstring]
  (let [
        hashcodes (map base32-index hashstring)
        bbox (do-decode-bbox hashcodes (struct geocode-data 180.0 -180.0 90.0 -90.0 true nil nil nil))
        lat (/ (+ (:maxlat bbox) (:minlat bbox)) 2)
        lon (/ (+ (:maxlon bbox) (:minlon bbox)) 2)
        lat-err (/ (- (:maxlat bbox) (:minlat bbox)) 2)
        lon-err (/ (- (:maxlon bbox) (:minlon bbox)) 2)]
    {:lat lat :lon lon :lat-err lat-err :lon-err lon-err}))

(defn encode [lat lon prec]
  (do-encode lat lon prec 
    (struct geocode-data 180 -180 90 -90 true 0 0 "")))
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.