Permalink
Browse files

Merge pull request #44 from xapix-io/feature/geo-functions

Rewamp custom functions
  • Loading branch information...
DeLaGuardo committed Dec 12, 2018
2 parents 510a09c + 56ed5b4 commit 19d6625cd9594af323b16559cc318365bcdb5022
@@ -80,7 +80,7 @@ jobs:
name: Test clojurescript
type: shell
command: |
clojure -A:cljs:test:cljs-runner
clojure -A:cljs:with-optional-deps:test:cljs-runner
- save_cache:
paths:
@@ -102,7 +102,7 @@ jobs:
name: Build JavaScript bundle
type: shell
command: |
clojure -A:cljs -m cljs.main -co axel_f.min.js.edn --compile
clojure -A:with-optional-deps:cljs -m cljs.main -co axel_f.min.js.edn --compile
- run:
name: Prepare workspace for publishing
@@ -0,0 +1 @@
{:paths ["src"]}
@@ -1,7 +1,6 @@
(ns axel-f.functions.convert
(:require [axel-f.macros #?(:clj :refer :cljs :refer-macros) [def-excel-fn]]
#?(:clj [cheshire.core :as json]
:cljs [goog.crypt.base64 :as b64]))
(ns axel-f.base64
(:require [axel-f.macros #?(:clj :refer :cljs :refer-macros) [def-excel-fn] :as m]
#?(:cljs [goog.crypt.base64 :as b64]))
#?(:clj (:import java.util.Base64)))

(defn base64-encode [to-encode]
@@ -16,38 +15,28 @@
:cljs
(b64/decodeString to-decode)))

(defn json-encode [to-encode]
#?(:clj
(json/generate-string to-encode)
:cljs
(js/JSON.stringify (clj->js to-encode))))

(defn json-decode [to-decode]
#?(:clj
(json/parse-string to-decode)
:cljs
(js->clj (js/JSON.parse to-decode))))

(def-excel-fn base64encode
(def-excel-fn base64.encode
"Creates a base-64 encoded ASCII string from a String"
{:args [{:desc "String to encode"}]}
[s]
(base64-encode s))

(def-excel-fn base64decode
(def-excel-fn base64encode
"Creates a base-64 encoded ASCII string from a String"
{:args [{:desc "String to encode"}]
:deprecated true}
[s]
((m/find-impl "BASE64.ENCODE") s))

(def-excel-fn base64.decode
"Decodes a string of data which has been encoded using base-64 encoding"
{:args [{:desc "String to decode"}]}
[s]
(base64-decode s))

(def-excel-fn jsonencode
"Returns a JSON-encoding String for the given object."
{:args [{:desc "Object to be encoded"}]}
[obj]
(json-encode obj))

(def-excel-fn jsondecode
"Returns an object corresponding to the given JSON-encoded string."
{:args [{:desc "JSON-encoded string to be decoded"}]}
(def-excel-fn base64decode
"Decodes a string of data which has been encoded using base-64 encoding"
{:args [{:desc "String to decode"}]
:deprecated true}
[s]
(json-decode s))
((m/find-impl "BASE64.DECODE") s))
@@ -11,7 +11,9 @@
:aliases
{:master {:override-deps {org.clojure/clojure {:mvn/version "1.10.0-master-SNAPSHOT"}}}

:with-optional-deps {:extra-deps {cheshire {:mvn/version "5.8.1"}}}
:with-optional-deps {:extra-deps {io.xapix/axel-f.base64 {:local/root "./base64"}
io.xapix/axel-f.json {:local/root "./json"}
io.xapix/axel-f.geo {:local/root "./geo"}}}

:cljs {:extra-deps {org.clojure/clojurescript {:mvn/version "1.10.439"}}}

@@ -0,0 +1 @@
{:paths ["src"]}
@@ -0,0 +1,61 @@
(ns axel-f.geo
(:require [axel-f.macros :refer [def-excel-fn]]
#?(:cljs [goog.math :as math])))

(def earth-radius 6371.0)

(defn to-radians
"Converts an angle measured in degrees to an approximately
equivalent angle measured in radians."
[x]
#?(:clj (Math/toRadians x)
:cljs (math/toRadians x)))

(defn asin
"Returns the arc sine of `x`."
[x]
#?(:clj (Math/asin x)
:cljs (.asin js/Math x)))

(defn sin
"Returns the sine of `x`."
[x]
#?(:clj (Math/sin x)
:cljs (.sin js/Math x)))

(defn cos
"Returns the cosine of `x`."
[x]
#?(:clj (Math/cos x)
:cljs (.cos js/Math x)))

(defn sqrt
"Returns the square root of `x`."
[x]
#?(:clj (Math/sqrt x)
:cljs (.sqrt js/Math x)))

(defn distance
"Returns the distance from `point-1` to `point-2`, in km using the
Haversine formula."
[[lat-1 lon-1] [lat-2 lon-2]]
(let [d-lat (to-radians (- lat-2 lat-1))
d-lon (to-radians (- lon-2 lon-1))
lat-1 (to-radians lat-1)
lat-2 (to-radians lat-2)
a (+ (* (sin (/ d-lat 2))
(sin (/ d-lat 2)))
(* (sin (/ d-lon 2))
(sin (/ d-lon 2))
(cos lat-1)
(cos lat-2)))]
(* earth-radius 2 (asin (sqrt a)))))

(def-excel-fn geo.distance
"Calculate the distance for the path described as a list of geo points
Each point must a tuple of two or three float numbers."
{:args [{:desc "List of points. Each point must be a tuple of latitude and longitude"}]}
[points]
(->> points
(partition 2 1)
(reduce #(+ %1 (apply distance %2)) 0)))
@@ -0,0 +1,3 @@
{:deps {cheshire {:mvn/version "5.8.1"}}

:paths ["src"]}
@@ -0,0 +1,41 @@
(ns axel-f.json
(:require [axel-f.macros #?(:clj :refer :cljs :refer-macros) [def-excel-fn] :as m]
#?(:clj [cheshire.core :as json])))

(defn json-encode [to-encode]
#?(:clj
(json/generate-string to-encode)
:cljs
(js/JSON.stringify (clj->js to-encode))))

(defn json-decode [to-decode]
#?(:clj
(json/parse-string to-decode)
:cljs
(js->clj (js/JSON.parse to-decode))))

(def-excel-fn json.encode
"Returns a JSON-encoding String for the given object."
{:args [{:desc "Object to be encoded"}]}
[obj]
(json-encode obj))

(def-excel-fn jsonencode
"Returns a JSON-encoding String for the given object."
{:args [{:desc "Object to be encoded"}]
:deprecated true}
[obj]
((m/find-impl "JSON.ENCODE") obj))

(def-excel-fn json.decode
"Returns an object corresponding to the given JSON-encoded string."
{:args [{:desc "JSON-encoded string to be decoded"}]}
[s]
(json-decode s))

(def-excel-fn jsondecode
"Returns an object corresponding to the given JSON-encoded string."
{:args [{:desc "JSON-encoded string to be decoded"}]
:deprecated true}
[s]
((m/find-impl "JSON.DECODE") s))
@@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>io.xapix</groupId>
<artifactId>axel-f</artifactId>
<version>0.3.0</version>
<version>0.3.1-SNAPSHOT</version>
<name>axel-f</name>
<dependencies>
<dependency>
@@ -1,6 +1,6 @@
{
"name": "axel-f",
"version": "0.3.0",
"version": "0.3.1-SNAPSHOT",
"description": "axel-f is an engine that can evaluate excel-like expressions.",
"homepage": "https://github.com/xapix-io/axel-f",
"author": "Kirill Chernyshov (https://github.com/DeLaGuardo)",
@@ -2,7 +2,9 @@
(:require [axel-f.core :as axel-f]
[axel-f.autocomplete :as autocomplete]
axel-f.functions
axel-f.functions.convert))
axel-f.base64
axel-f.json
axel-f.geo))

(defn- fix-regex-in-exception [exception-data]
(update exception-data
@@ -38,7 +38,7 @@
KEYWORD ::= <colon> SYMBOL ( <div-op> SYMBOL_FIELD )?
BOOL ::= #'TRUE|FALSE|True|False|true|false'
FNCALL ::= FN <opening-parenthesis> ARGUMENTS <closing-parenthesis>
FN ::= #'[A-Z0-9]+'
FN ::= #'[A-Z0-9.]+'
ARGUMENTS ::= ARGUMENT {<comma> ARGUMENT}
ARGUMENT ::= EXPR | Epsilon
OBJREF ::= FIELD (( <dot> FIELD ) | ( <dot>? ARRAY_FIELD ) )*
@@ -0,0 +1,18 @@
(ns axel-f.base64-test
(:require #?(:clj [clojure.test :as t]
:cljs [cljs.test :as t :include-macros true])
[axel-f.core :as af]
[axel-f.base64 :as sut]))

(t/deftest base64
(t/testing "Encode string"
(t/is (= "cXdl" (sut/base64-encode "qwe"))))

(t/testing "Decode string"
(t/is (= "qwe" (sut/base64-decode "cXdl")))))

(t/deftest BASE64ENCODE
(t/is (= "cXdl" (af/run "BASE64.ENCODE('qwe')"))))

(t/deftest BASE64DECODE
(t/is (= "qwe" (af/run "BASE64.DECODE('cXdl')"))))
@@ -2,33 +2,8 @@
(:require #?(:clj [clojure.test :as t]
:cljs [cljs.test :as t :include-macros true])
[axel-f.core :as af]
[axel-f.functions.convert :as sut]))

(t/deftest base64
(t/testing "Encode string"
(t/is (= "cXdl" (sut/base64-encode "qwe"))))

(t/testing "Decode string"
(t/is (= "qwe" (sut/base64-decode "cXdl")))))

(t/deftest json
(t/testing "Encode object"
(t/is (= "{\"foo\":1}" (sut/json-encode {:foo 1}))))

(t/testing "Decode string"
(t/is (= {"foo" 1} (sut/json-decode "{\"foo\":1}")))))

(t/deftest BASE64ENCODE
(t/is (= "cXdl" (af/run "BASE64ENCODE('qwe')"))))

(t/deftest BASE64DECODE
(t/is (= "qwe" (af/run "BASE64DECODE('cXdl')"))))

(t/deftest JSONENCODE
(t/is (= "{\"foo\":1}" (af/run "JSONENCODE(_)" {:foo 1}))))

(t/deftest JSONDECODE
(t/is (= {"foo" 1} (af/run "JSONDECODE('{\"foo\":1}')"))))
axel-f.json
axel-f.base64))

(t/deftest complex-example
(t/is (= 1 (af/run "JSONDECODE(BASE64DECODE(_)).foo" "eyJmb28iOjF9"))))
(t/is (= 1 (af/run "JSON.DECODE(BASE64.DECODE(_)).foo" "eyJmb28iOjF9"))))
@@ -0,0 +1,27 @@
(ns axel-f.geo-test
(:require #?(:clj [clojure.test :as t]
:cljs [cljs.test :as t :include-macros true])
[axel-f.core :as af]
[axel-f.geo :as sut]))

(t/deftest to-radians
(t/are [deg rad] (= (sut/to-radians deg) rad)
0 0.0
0.0 0.0
90 1.5707963267948966
180 3.141592653589793 ;; ~= Pi
))

(t/deftest distance
(t/testing "Calculate distance for given path"
(t/are [path dist] (= (af/run "GEO.DISTANCE(_)" path) dist)
[[55.751244 37.618423] ;; Moscow
[52.520008 13.404954] ;; Berlin
]
1608.8794097160353

[[13.736717 100.523186] ;; Bangkok
[55.751244 37.618423] ;; Moscow
[52.520008 13.404954] ;; Berlin
]
8676.241667937587)))
@@ -0,0 +1,18 @@
(ns axel-f.json-test
(:require #?(:clj [clojure.test :as t]
:cljs [cljs.test :as t :include-macros true])
[axel-f.core :as af]
[axel-f.json :as sut]))

(t/deftest json
(t/testing "Encode object"
(t/is (= "{\"foo\":1}" (sut/json-encode {:foo 1}))))

(t/testing "Decode string"
(t/is (= {"foo" 1} (sut/json-decode "{\"foo\":1}")))))

(t/deftest JSONENCODE
(t/is (= "{\"foo\":1}" (af/run "JSON.ENCODE(_)" {:foo 1}))))

(t/deftest JSONDECODE
(t/is (= {"foo" 1} (af/run "JSON.DECODE('{\"foo\":1}')"))))

0 comments on commit 19d6625

Please sign in to comment.