From 51d71efdc96daacc88b190d9b96aea3eb13e63fe Mon Sep 17 00:00:00 2001 From: Simon Brooke Date: Mon, 25 Jun 2018 22:45:12 +0100 Subject: [PATCH] This is pretty awful I have a deadly embrace of mutual recursion. --- .gitignore | 4 + project.clj | 5 +- src/yyy_data/core.clj | 168 ++++++++++++++++++++++++++++-------------- 3 files changed, 122 insertions(+), 55 deletions(-) diff --git a/.gitignore b/.gitignore index da240e2..464a34b 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,7 @@ pom.xml.asc .lein-failures .nrepl-port .cpcache/ + +\.idea/ + +yyy-data\.iml diff --git a/project.clj b/project.clj index 0af7757..dd898a1 100644 --- a/project.clj +++ b/project.clj @@ -7,4 +7,7 @@ [generateme/fastmath "1.0.1"] [org.clojure/clojure "1.8.0"] [org.clojure/data.json "0.2.6"] - [net.mikera/core.matrix "0.62.0"]]) + [org.clojure/math.numeric-tower "0.0.4"] + [net.mikera/core.matrix "0.62.0"]] + :plugins [[lein-gorilla "0.4.0"]] + ) diff --git a/src/yyy_data/core.clj b/src/yyy_data/core.clj index d09c62a..0e16326 100644 --- a/src/yyy_data/core.clj +++ b/src/yyy_data/core.clj @@ -4,11 +4,41 @@ [fastmath.core :refer [radians degrees sin cos tan sqrt atan2]] [clojure.core.matrix :as mx] [clojure.data.json :as json] + [clojure.math.numeric-tower :refer [expt]] [clojure.string :as s])) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;; +;;;; yyy-data.core +;;;; +;;;; This program is free software; you can redistribute it and/or +;;;; modify it under the terms of the GNU General Public License +;;;; as published by the Free Software Foundation; either version 2 +;;;; of the License, or (at your option) any later version. +;;;; +;;;; This program is distributed in the hope that it will be useful, +;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;;;; GNU General Public License for more details. +;;;; +;;;; You should have received a copy of the GNU General Public License +;;;; along with this program; if not, write to the Free Software +;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, +;;;; USA. +;;;; +;;;; Copyright (C) 2018 Simon Brooke +;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;;; There are a number of bad design decisions in my implementation of this. +;;; Using protocols and records was probably a mistake. +;;; +;;; The decision not only to adopt but to extend the untypable variable names +;;; of the original was DEFINITELY a mistake. + + (declare geopoint-to-osgrid vector3d-to-geopoint geopoint-to-vector3d osgrid-to-geopoint) -;;; Coordinate system conversion cribbed from https://www.movable-type.co.uk/scripts/latlong-os-gridref.html (def ellipsoids "Ellipsoid parameters; major axis (a), minor axis (b), and flattening (f) for each ellipsoid." @@ -65,10 +95,12 @@ (defprotocol Transformable + "Something which can be transformed with a matrix transform." (apply-transform [this transform])) (defrecord Vector3d + ;; "A vector from the centre of the earth, which intercepts the surface at a point." [x y z] Location ;; datum is a bit meaningless for a Vector3d; get the default. @@ -77,11 +109,12 @@ (ellipsoid [x] (:ellipsoid (datum x))) ;; I already am vector3d; return myself (vector3d [x] x) - (geopoint [this datum] (vector3d-to-geopoint)) + (geopoint [this datum] (vector3d-to-geopoint this datum)) Transformable (apply-transform [this transform] + (println (str "(apply-transform " this " " transform ")")) (let - [s1 (+ (/ (:s transform) 1e6) 1) ;; scale + [s1 (+ (/ (:s transform) 1e6) 1) ;; scale rx (radians (/ (:rx transform) 3600)) ;; x-rotation: normalise arcseconds to radians ry (radians (/ (:ry transform) 3600)) ;; y-rotation: normalise arcseconds to radians rz (radians (/ (:rz transform) 3600)) ;; z-rotation: normalise arcseconds to radians @@ -117,7 +150,9 @@ #(if (number? (t %)) (hash-map % (- 0 (t %)))) ;; (hash-map % (t %))) (keys t)))) -(inverse-transform { :tx 89.5, :ty 93.8 :tz 123.1 :s -1.2 :rx 0.0 :ry 0.0 :rz 0.156 }) + +;; (inverse-transform { :tx 89.5, :ty 93.8 :tz 123.1 :s -1.2 :rx 0.0 :ry 0.0 :rz 0.156 }) + (defn to-fixed "Round the number `n` to `p` places after the decimal point." @@ -126,19 +161,13 @@ (double (/ (int (*' n m)) m)))) -(defn to-datum - ;; TODO: this obviously doesn't work but I'm trying to debug a compilation problem - [geopoint datum] - geopoint) - - ;; (to-fixed 1234.56789 4) + (defrecord GeoPoint [latitude longitude datum] - ;; "A point with an `x` co-ordinate, a `y` co-ordinate, and a datum `d`. We're - ;; agnostic as to whether `d` is passed as a keyword or a map, but it must be - ;; taken from `datums`, q.v." + ;; "A point with an `x` co-ordinate, a `y` co-ordinate, and a datum `d`. `d` + ;; must be a key taken from `datums`, q.v." Location (datum [location] (cond @@ -156,38 +185,37 @@ (:ellipsoid (datum [location]))) (vector3d [this] (geopoint-to-vector3d this)) (geopoint [location new-datum] - (let [od (datum location) - nd (if (keyword? new-datum) (datums new-datum) new-datum) - c (vector3d location)] - (cond - (= od nd) location - (= (:key od) :WGS84) (geopoint - (apply-transform c (:transform nd)) - (:datum location)) - (= (:key nd) :WGS84) (geopoint - (apply-transform - c - (inverse-transform (datums (:datum location)))) - (:datum location)) - true - (to-datum (to-datum location :WGS84) nd)))) + (println (str "(geopoint " location " " new-datum ")")) + (if + (= (:datum location) new-datum) + location + (let [od (datum location) + nd (datums new-datum) + c (vector3d location)] + (cond + (= od nd) location + (= (:key od) :WGS84) (geopoint + (apply-transform c (:transform nd)) + (:datum location)) + (= (:key nd) :WGS84) (geopoint + (apply-transform + c + (inverse-transform + (:transform od))) + (:datum location)) + true + (geopoint (geopoint location :WGS84) new-datum))))) (latitude [location] - (:latitude (to-datum location :WGS84))) + (:latitude (geopoint location :WGS84))) (longitude [location] - (:longitude (to-datum location :WGS84))) + (:longitude (geopoint location :WGS84))) (grid-x [location] (:e (osgrid location (:datum location)))) (grid-y [location] (:n (osgrid location (:datum location)))) (osgrid [location datum] - (geopoint-to-osgrid location (:datum location)))) + (geopoint-to-osgrid location datum))) -(def location (GeoPoint. 54.8240911 -3.9170342 :WGS84)) -(def od (datum location)) -(def nd (datums :NTF)) -(def c (vector3d location)) - -(apply-transform c (:transform nd)) (defrecord OsGrid [e n] @@ -207,11 +235,13 @@ (defn vector3d-to-geopoint + ;; My initial design decision to allow datums to be passed as keywords or as maps + ;; was WRONG: a datum SHALL BE passed as a map [this datum] (let - [a (:a (:ellipsoid datum)) - b (:b (:ellipsoid datum)) - f (:f (:ellipsoid datum)) + [a (:a (:ellipsoid (datums datum))) + b (:b (:ellipsoid (datums datum))) + f (:f (:ellipsoid (datums datum))) e² (- (* 2 f) (* f f)) ;; first eccentricity squared ε² (/ e² (- 1 e²)) ;; second eccentricity squared p (sqrt (+ (* (.x this) (.x this)) (* (.y this) (.y this)))) @@ -231,9 +261,9 @@ (defn geopoint-to-osgrid - [geopoint datum] + [gp datum] ;; for bizarrely over-complicated trigonometry, look no further. - (let [point (.geopoint geopoint (or datum :OSGB36)) + (let [point (geopoint gp datum) φ (radians (latitude point)) λ (radians (longitude point)) a 6377563.396 ;; Airy 1830 major & minor semi-axes @@ -259,7 +289,7 @@ cosφ (cos φ) v (* a (/ F0 (sqrt (- 1 (* e² sin²φ))))) ;; nu = transverse radius of curvature - ρ (/ (* a F0 (- 1 e²)) (pow (- 1 (* e² sin²φ)) 1.5)) + ρ (/ (* a F0 (- 1 e²)) (expt (- 1 (* e² sin²φ)) 1.5)) ;; rho = meridional radius of curvature η2 (/ v (- ρ 1)) ;; beware confusing η2 with n² Ma (* (+ 1 n (* (/ 5 4) n²) (* (/ 5 4) n³)) Δφ) @@ -299,11 +329,8 @@ (defn geopoint-to-vector3d [geopoint] - (let [datum (cond - (keyword? (:datum geopoint)) - (datums (:datum geopoint)) - (map? (:datum geopoint)) - (:datum geopoint)) + (println (str "(geopoint-to-vector3d " geopoint ")")) + (let [datum (datum geopoint) φ (radians (latitude geopoint)) λ (radians (longitude geopoint)) h 0 @@ -324,7 +351,7 @@ (defn osgrid-to-geopoint [osgrid datum] (let - [d (or datum :WGS84) + [d datum E (.e osgrid) N (.n osgrid) a 6377563.396 ;; Airy 1830 major semi-axis @@ -362,7 +389,7 @@ v⁵ (reduce * 1 (repeat 5 v)) v⁷ (reduce * 1 (repeat 7 v)) ;; nu = transverse radius of curvature - ρ (/ (* a F0 (- 1 e²)) (pow (- 1 (* e² sin²φ)) 1.5)) + ρ (/ (* a F0 (- 1 e²)) (expt (- 1 (* e² sin²φ)) 1.5)) ;; rho = meridional radius of curvature η2 (/ v (- ρ 1)) ;; beware confusing η2 with n² tanφ (tan φ) @@ -391,7 +418,7 @@ Δe⁶ (reduce * 1 (repeat 6 Δe)) Δe⁷ (reduce * 1 (repeat 6 Δe)) φ₁ (+ φ (- 0 (+ VII Δe²)) (* VIII Δe⁴) (- 0 (* IX Δe⁷))) - λ₁ (+ λ0 + (* X Δe) (- 0 (* XI Δe³)) (* IX Δe⁶) (- 0 (* XIIA Δe⁷)))] + λ₁ (+ λ0 (* X Δe) (- 0 (* XI Δe³)) (* IX Δe⁶) (- 0 (* XIIA Δe⁷)))] (.geopoint (GeoPoint. (degrees φ₁) (degrees λ₁) :OSGB36) datum))) @@ -416,11 +443,44 @@ :key-fn #(keyword (.toLowerCase %)))))))) -;; (def home (GeoPoint. 54.8240911 -3.9170342 :WGS84)) +;;(def home (GeoPoint. 54.8240911 -3.9170342 :WGS84)) -;; (.osgrid home :WGS84) +;; -;; (.datum home) +;; (:datum home) ;; (datums (:datum home)) ;; (:ellipsoid (datums (:datum home))) +;; @@ +;; => +;;; {"type":"html","content":"#'yyy-data.core/home","value":"#'yyy-data.core/home"} +;; <= +;; @@ +;;(.osgrid home :WGS84) +;; @@ + +;; @@ +;;(:datum home) + +;; @@ +;; => +;;; {"type":"html","content":":WGS84","value":":WGS84"} +;; <= + +;; @@ +;; (datums (:datum home)) +;; @@ +;; => +;;; {"type":"list-like","open":"{","close":"}","separator":", ","items":[{"type":"list-like","open":"","close":"","separator":" ","items":[{"type":"html","content":":key","value":":key"},{"type":"html","content":":WGS84","value":":WGS84"}],"value":"[:key :WGS84]"},{"type":"list-like","open":"","close":"","separator":" ","items":[{"type":"html","content":":ellipsoid","value":":ellipsoid"},{"type":"list-like","open":"{","close":"}","separator":", ","items":[{"type":"list-like","open":"","close":"","separator":" ","items":[{"type":"html","content":":a","value":":a"},{"type":"html","content":"6378137","value":"6378137"}],"value":"[:a 6378137]"},{"type":"list-like","open":"","close":"","separator":" ","items":[{"type":"html","content":":b","value":":b"},{"type":"html","content":"6356752.314245","value":"6356752.314245"}],"value":"[:b 6356752.314245]"},{"type":"list-like","open":"","close":"","separator":" ","items":[{"type":"html","content":":f","value":":f"},{"type":"html","content":"0.0033528106647474805","value":"0.0033528106647474805"}],"value":"[:f 0.0033528106647474805]"}],"value":"{:a 6378137, :b 6356752.314245, :f 0.0033528106647474805}"}],"value":"[:ellipsoid {:a 6378137, :b 6356752.314245, :f 0.0033528106647474805}]"},{"type":"list-like","open":"","close":"","separator":" ","items":[{"type":"html","content":":transform","value":":transform"},{"type":"list-like","open":"{","close":"}","separator":", ","items":[{"type":"list-like","open":"","close":"","separator":" ","items":[{"type":"html","content":":tx","value":":tx"},{"type":"html","content":"0.0","value":"0.0"}],"value":"[:tx 0.0]"},{"type":"list-like","open":"","close":"","separator":" ","items":[{"type":"html","content":":ty","value":":ty"},{"type":"html","content":"0.0","value":"0.0"}],"value":"[:ty 0.0]"},{"type":"list-like","open":"","close":"","separator":" ","items":[{"type":"html","content":":tz","value":":tz"},{"type":"html","content":"0.0","value":"0.0"}],"value":"[:tz 0.0]"},{"type":"list-like","open":"","close":"","separator":" ","items":[{"type":"html","content":":s","value":":s"},{"type":"html","content":"0.0","value":"0.0"}],"value":"[:s 0.0]"},{"type":"list-like","open":"","close":"","separator":" ","items":[{"type":"html","content":":rx","value":":rx"},{"type":"html","content":"0.0","value":"0.0"}],"value":"[:rx 0.0]"},{"type":"list-like","open":"","close":"","separator":" ","items":[{"type":"html","content":":ry","value":":ry"},{"type":"html","content":"0.0","value":"0.0"}],"value":"[:ry 0.0]"},{"type":"list-like","open":"","close":"","separator":" ","items":[{"type":"html","content":":rz","value":":rz"},{"type":"html","content":"0.0","value":"0.0"}],"value":"[:rz 0.0]"}],"value":"{:tx 0.0, :ty 0.0, :tz 0.0, :s 0.0, :rx 0.0, :ry 0.0, :rz 0.0}"}],"value":"[:transform {:tx 0.0, :ty 0.0, :tz 0.0, :s 0.0, :rx 0.0, :ry 0.0, :rz 0.0}]"}],"value":"{:key :WGS84, :ellipsoid {:a 6378137, :b 6356752.314245, :f 0.0033528106647474805}, :transform {:tx 0.0, :ty 0.0, :tz 0.0, :s 0.0, :rx 0.0, :ry 0.0, :rz 0.0}}"} +;; <= + +;; @@ +*ns* +;; @@ +;; => +;;; {"type":"html","content":"#namespace[yyy-data.core]","value":"#namespace[yyy-data.core]"} +;; <= + +;; @@ + +;; @@