OK, this is fucking impressive if I say so myself.

This commit is contained in:
Simon Brooke 2020-02-22 12:43:29 +00:00
parent 64fd9ffb5b
commit d001338a49
No known key found for this signature in database
GPG key ID: A7A4F18D1D4DF987
8 changed files with 154 additions and 57 deletions

2
.gitignore vendored
View file

@ -14,3 +14,5 @@ pom.xml
.nrepl-port
resources/public/data/
.rebel_readline_history

View file

@ -1,2 +1,46 @@
/* some style */
body {
font-family: Helvetica, Ariel, sans-serif;
padding-bottom: 4em;
}
div#app {
margin-left: 10%;
}
footer {
clear: both;
font-size: smaller;
text-align: center;
color:white;
background-color: #3298dc;
width: 100%;
margin: 0;
padding: 0.25em 0;
bottom:0;
position:fixed;
vertical-align: top;
z-index:150;
_position:absolute;
_top:expression(eval(document.documentElement.scrollTop+
(document.documentElement.clientHeight-this.offsetHeight)));
}
footer a {
color: #ddd;
}
h1 {
width: 100%;
margin: 0;
padding-left: 10%;
text-align: left;
background-color: #3298dc;
color: white;
}
samp {
background-color: #b0b0ff;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

View file

@ -1,10 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<title>GeoCSV Lite</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="css/style.css" rel="stylesheet" type="text/css">
<link rel="icon" href="https://clojurescript.org/images/cljs-logo-icon-32.png">
<link rel="icon" href="favicon.ico">
<!-- if you prefer loading libraries from standard hosts, un comment this and
comment out the local source -->
<!-- link rel="stylesheet" href="https://unpkg.com/leaflet@1.6.0/dist/leaflet.css"
@ -13,11 +14,50 @@
<link rel="stylesheet" href="vendor/node_modules/leaflet/dist/leaflet.css" type="text/css">
</head>
<body>
<h1>GeoCSV Lite</h1>
<div id="app">
<h2>Figwheel template</h2>
<p id="message">Checkout your developer console.</p>
<div id="map" style="height: 600px; width: 80%; margin-left: 10%; border: thin solid gray;"></div>
<p id="message"></p>
<p id="error"></p>
<div id="map" style="height: 600px; width: 80%; border: thin solid gray;"></div>
</div>
<div id="doc">
<p>
Use this page as a crib for how to use <b>GeoCSV Lite</b> in your projects. In order
for it to work, you need:
<ol>
<li>Javascript dependencies:
<ol>
<li><a href="">Leaflet</a> (provides mapping);</li>
<li><a href="">PapaParse</a> (provides CSV parsing);</li>
</ol>
</li>
<li>
A line which includes the geocsv_lite library, which should be
<em>after</em> those including the dependencies: <br/>
<samp>&lt;script src="js/compiled/geocsv_lite.js"
type="text/javascript"&gt;&lt;/script&gt;</samp>
</li>
<li>One (or more) <code>div</code> elements to contain your maps. It
is a requirement of Leaflet that these must have a fixed pixel width.
Each must have a distinct <code>id</code>;</li>
<li>
For each <code>div</code> which you wish to contain a map view,
an invocation of the function
<code>geocsv_lite.core.initialise_map_element(id, data-source)</code>: <br/>
<samp>&lt;script&gt;geocsv_lite.core.initialise_map_element("map", "/data/data.csv");&lt;/script&gt;</samp>
</li>
</ol>
</p>
</div>
<footer>
<div id="credits">
<div>
<img height="16" width="16" alt="Clojure" src="img/credits/clojure-icon.gif"/> Powered by <a href="http://clojure.org">Clojure</a> ||
<img height="16" width="16" alt="GitHub" src="img/credits/github-logo-transparent.png"/>Find me/fork me on <a href="https://github.com/simon-brooke/geocsv-lite">Github</a> ||
<img height="16" width="16" alt="Free Software Foundation" src="img/credits/gnu.small.png"/>Licensed under the <a href="http://www.gnu.org/licenses/gpl-2.0.html">GNU General Public License version 2.0</a>
</div>
</div>
</footer>
<!-- if you prefer loading libraries from standard hosts, un comment this and
comment out the local source -->
<!-- script src="https://unpkg.com/leaflet@1.6.0/dist/leaflet.js"
@ -27,5 +67,8 @@
<script src="vendor/node_modules/leaflet/dist/leaflet.js"></script>
<script src="vendor/node_modules/papaparse/papaparse.min.js"></script>
<script src="js/compiled/geocsv_lite.js" type="text/javascript"></script>
<script>
geocsv_lite.core.initialise_map_element("map", "/data/data.csv");
</script>
</body>
</html>

View file

@ -6,14 +6,16 @@
[geocsv-lite.gis :as gis]
[geocsv-lite.map :as m]))
(enable-console-print!)
(println "This text is printed from src/geocsv-lite/core.cljs. Go ahead and edit it and see reloading in action.")
;; define your app data so that it doesn't get over-written on reload
(defn ^:export initialise-map-element
"Create a map view in the element with this `id` and decorate it with
pins showing locations from this `data-source`."
[id data-source]
(js/console.log (str "geocsv-lite.core.initialise-map-element called with args id: " id "; data-source: " data-source "."))
(let [sid (str id)
kid (keyword sid)
v (m/add-view sid 55 -4 10)]
(.whenReady v (fn [] (get-data kid data-source)))))
(defonce app-state (atom {:text "Hello world!"}))
@ -21,11 +23,4 @@
;; optionally touch your app-state to force rerendering depending on
;; your application
;; (swap! app-state update-in [:__figwheel_counter] inc)
(m/add-view "map" 55 -4 10)
(let [query (get-query-part-as-map)
uri (get-csv-url query)
records (get-data :map)]
(dom/set-text (.getElementById js/document "message")
(str "Query was: " query "; uri was: " uri))))
)

View file

@ -8,20 +8,12 @@
[geocsv-lite.map :refer [get-view]]
))
;; function getQueryVariable(variable)
;; {
;; var query = window.location.search.substring(1);
;; var vars = query.split("&");
;; for (var i=0;i<vars.length;i++) {
;; var pair = vars[i].split("=");
;; if(pair[0] == variable){return pair[1];}
;; }
;; return(false);
;; }
(defn get-query-part-as-map
"Returns the query part of the current document URL as a keyword-string map."
;; not actually used in the current incarnation
[]
(let [query-nvs (map #(cs/split % "=") (cs/split (subs js/window.location.search 1) "&"))]
(when (every? #(= (count %) 2) query-nvs)
@ -35,6 +27,7 @@
* `:uri` whose value is the URI of a JSON or CSV file.
If either of these keys is found, returns an appropriate URL, else nil."
;; not actually used in the current incarnation
[query]
(when (map? query)
(cond
@ -46,13 +39,26 @@
(defn default-handler
"When data is received from a URL, it is received asynchronously. This
is the default callback called with the `response` of the HTTP request,
and the keyword `k` identifying the map view, to populate the map with
data."
[response k]
(if
(= (:status response) 200)
(let [content (:body response)
data (js->clj (.-data (.parse js/Papa content)))
data (map
#(merge %
{:longitude (js/Number (:longitude %))
:latitide (js/Number (:latitude %))})
(js->clj (.-data (.parse js/Papa content (clj->js {:dynamicTyping true})))))
cols (map
#(let [n (cs/lower-case (cs/replace (cs/trim %) #"[^\w\d]+" "-"))]
#(let [n (when-not
(empty? %)
(when (string? %)
(cs/lower-case
(cs/replace
% #"[^\w\d]+" "-"))))]
(keyword
(if (empty? n)
(gensym)
@ -62,19 +68,21 @@
(fn [r] (zipmap cols (map str r)))
(rest data))
]
;; (println records)
(gis/refresh-map-pins (get-view k) records))
(println (str "Bad response from server: " (:status response)))))
(js/console.error (str "Bad response from server: " (:status response)))))
(defn get-data
[k]
"Get data for the view identified by this keyword `k` from this `data-source`.
In this initial release the data source must be a URL, but in future releases
I intend that it may also be a list of maps representing records, or a CSV or
JSON formatted string."
[k data-source]
(let
[uri (get-csv-url (get-query-part-as-map))]
[uri data-source]
(go (let [response (<! (http/get uri {:with-credentials? "false"
:access-control-allow-credentials "true"
:origin js/window.location.hostname}))]
(println (cs/join " " ["tx:" uri "rx:" (:status response)]))
(default-handler response k)))))
(defn get-data-with-uri-and-handler
@ -83,7 +91,7 @@
(apply handler-fn (list response k)))))
(go (let [uri "http://localhost:3449/data/data.csv"
response (<! (http/get uri))]
(when (= (:status response) 200)
(default-handler response :map))))
;; (go (let [uri "http://localhost:3449/data/data.csv"
;; response (<! (http/get uri))]
;; (when (= (:status response) 200)
;; (default-handler response :map))))

View file

@ -116,8 +116,8 @@
"Add an appropriate map-pin for this `record` in this map `view`, if it
has a valid `:latitude` and `:longitude`."
[record index view]
(let [lat (:latitude record)
lng (:longitude record)]
(let [lat (js/Number (:latitude record))
lng (js/Number (:longitude record))]
(if
(and
(number? lat)
@ -170,10 +170,10 @@
centre of the locations of these records as indicated by the values of their
`:latitude` and `:longitude` keys."
[records]
(let [lats (filter number? (map :latitude records))
(let [lats (map js/Number (map :latitude records))
min-lat (apply min lats)
max-lat (apply max lats)
lngs (filter number? (map :longitude records))
lngs (filter js/Number (map :longitude records))
min-lng (apply min lngs)
max-lng (apply max lngs)]
(if-not
@ -187,9 +187,10 @@
"Refresh the map pins on the current map. Side-effecty; liable to be
problematic."
[view records]
(js/console.log "refresh-map-pins called")
(let [view (map-remove-pins view)
centre (compute-centre records)]
(js/console.log (str "refresh-map-pins called; " (count records) " records, centre at " centre))
(js/console.log (str "Type of longitude " (:longitude (first records)) " is: " (type (:longitude (first records)))))
(if
view
(let [added (remove nil? (map #(add-map-pin %1 %2 view) records (range)))]

View file

@ -72,16 +72,20 @@
(defn add-view
[id lat lng zoom]
(let [k (keyword id)]
(when-not
(let [k (keyword id)
v (or
(@views k)
(swap! views assoc k (map-did-mount id lat lng zoom)))
(views k)))
(map-did-mount id lat lng zoom))]
(js/console.log (str "Added Leaflet view to element with id `" id "`"))
(swap! views assoc k v)
v))
(defn get-view
[k]
(@views k))
(when-not (keyword? k) (js/console.log "Key `" k "` passed to get-view is not a keyword"))
(when-not (k @views) (js/console.log "Key `" k "` does not identify a known view"))
(k @views))