Merge tag 'geocsv-0.1.1'

This commit is contained in:
Simon Brooke 2020-01-29 20:46:24 +00:00
commit 576db9e88f
No known key found for this signature in database
GPG key ID: A7A4F18D1D4DF987
22 changed files with 329 additions and 805 deletions

View file

@ -1,6 +1,6 @@
# geocsv # geocsv
GeoCSV is a wee tool to show comma-separated value data on a map. A wee tool to show comma-separated value data on a map.
The CSV file must have The CSV file must have
@ -20,18 +20,32 @@ If you run the server running **geocsv**, the simplest way to add CSV files is s
https://geocsv.example.com/ https://geocsv.example.com/
and the file you want to view is `myfile.csv`, then you would specify this as and the file you want to view is `myfile.csv`, then you would specify this as the value of `file` in the query part of the URL.
https://geocsv.example.com/?file=myfile.csv https://geocsv.example.com/?file=myfile.csv
### Loading CSV file onto another public server
If you're not running the **geocsv** server yourself, you can upload the CSV to another server which is accessible by the **geocsv** server. You can then map data from the CSV file by specifying the URL of the file as the value of `uri` in the query part of the URL:
https://geocsv.example.com/?uri=http://my.other.server/path/to/myfile.csv
### Using a Google spreadsheet ### Using a Google spreadsheet
If you use [Google Sheets](https://www.google.co.uk/sheets/about/), then every sheet has a 'document id', a long string of characters which uniquely identifies that sheet. Suppose your Google spreadsheet has a document id of `abcdefghijklmnopqrstuvwxyz-12345`, then you could pull data from this spreadsheet by specifying: If you use [Google Sheets](https://www.google.co.uk/sheets/about/), then every sheet has a 'document id', a long string of characters which uniquely identifies that sheet. Suppose your Google spreadsheet has a document id of `abcdefghijklmnopqrstuvwxyz-12345`, then you could pull data from this spreadsheet by specifying this as the value of `docid` in the query part of the URL:
https://geocsv.example.com/?docid=abcdefghijklmnopqrstuvwxyz-12345 https://geocsv.example.com/?docid=abcdefghijklmnopqrstuvwxyz-12345
The spreadsheet **must** be publicly readable. The spreadsheet **must** be publicly readable.
### Precedence
Nothing, of course, stops you from specifying multiple arguments in the query part of the URL, but only one will be used. The precedence is in this order:
1. `docid` is considered first, and overrides anything else;
2. `uri` is considered next, and overrides `file`;
3. the value of `file` is considered only if neither of the other two are present.
## Not yet working ## Not yet working
GeoCSV is at an early stage of development, and some features are not yet working. GeoCSV is at an early stage of development, and some features are not yet working.
@ -40,10 +54,6 @@ GeoCSV is at an early stage of development, and some features are not yet workin
At the current stage of development, if no appropriate image exists in the `resources/public/img/map-pins` folder, that's your problem. **TODO:** I intend at some point to make missing pin images default to `unknown-pin.png`, which does exist. At the current stage of development, if no appropriate image exists in the `resources/public/img/map-pins` folder, that's your problem. **TODO:** I intend at some point to make missing pin images default to `unknown-pin.png`, which does exist.
### Doesn't scale and centre the map to show the data in the sheet
Currently the map is initially centred roughly on the centre of Scotland, and scaled arbitrarily. It should compute an appropriate centre and scale from the data provided, but currently doesn't.
## Prerequisites ## Prerequisites
You will need [Leiningen][1] 2.0 or above installed. You will need [Leiningen][1] 2.0 or above installed.

View file

@ -1,6 +1,6 @@
(ns^:figwheel-no-load geocsv.app (ns^:figwheel-no-load geocsv.app
(:require (:require
[geocsv.core :as core] [geocsv.client.core :as core]
[cljs.spec.alpha :as s] [cljs.spec.alpha :as s]
[expound.alpha :as expound] [expound.alpha :as expound]
[devtools.core :as devtools])) [devtools.core :as devtools]))

View file

@ -1,5 +1,5 @@
(ns geocsv.app (ns geocsv.app
(:require [geocsv.core :as core])) (:require [geocsv.client.core :as core]))
;;ignore println statements in prod ;;ignore println statements in prod
(set! *print-fn* (fn [& _])) (set! *print-fn* (fn [& _]))

13
package-lock.json generated
View file

@ -1,13 +0,0 @@
{
"name": "geocsv",
"version": "0.1.0-SNAPSHOT",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"leaflet": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.3.1.tgz",
"integrity": "sha512-adQOIzh+bfdridLM1xIgJ9VnJbAUY3wqs/ueF+ITla+PLQ1z47USdBKUf+iD9FuUA8RtlT6j6hZBfZoA6mW+XQ=="
}
}
}

View file

@ -1,4 +1,4 @@
(defproject geocsv "0.1.0" (defproject geocsv "0.1.1"
:description "A wee tool to show comma-separated value data on a map." :description "A wee tool to show comma-separated value data on a map."
:url "http://example.com/FIXME" :url "http://example.com/FIXME"
@ -12,8 +12,9 @@
[com.cemerick/url "0.1.1"] [com.cemerick/url "0.1.1"]
[com.cognitect/transit-clj "0.8.319"] [com.cognitect/transit-clj "0.8.319"]
[compojure "1.6.1"] [compojure "1.6.1"]
[cpath-clj "0.1.2"]
[cprop "0.1.15"] [cprop "0.1.15"]
[csv2edn "0.1.5"] [csv2edn "0.1.6"]
[day8.re-frame/http-fx "0.1.6"] [day8.re-frame/http-fx "0.1.6"]
[expound "0.8.3"] [expound "0.8.3"]
[funcool/struct "1.4.0"] [funcool/struct "1.4.0"]
@ -32,6 +33,7 @@
[org.clojure/tools.cli "0.4.2"] [org.clojure/tools.cli "0.4.2"]
[org.clojure/tools.logging "0.5.0"] [org.clojure/tools.logging "0.5.0"]
[org.webjars.npm/bulma "0.8.0"] [org.webjars.npm/bulma "0.8.0"]
[org.webjars.npm/leaflet "1.6.0"]
[org.webjars.npm/material-icons "0.3.1"] [org.webjars.npm/material-icons "0.3.1"]
[org.webjars/webjars-locator "0.38"] [org.webjars/webjars-locator "0.38"]
[re-frame "0.10.9"] [re-frame "0.10.9"]
@ -43,7 +45,6 @@
[selmer "1.12.18"]] [selmer "1.12.18"]]
:min-lein-version "2.0.0" :min-lein-version "2.0.0"
:npm {:dependencies [[leaflet "1.3.1"]]}
:source-paths ["src/clj" "src/cljs" "src/cljc"] :source-paths ["src/clj" "src/cljs" "src/cljc"]
:test-paths ["test/clj"] :test-paths ["test/clj"]
@ -53,9 +54,11 @@
:plugins [[lein-cljsbuild "1.1.7"] :plugins [[lein-cljsbuild "1.1.7"]
[lein-codox "0.10.7"] [lein-codox "0.10.7"]
[lein-npm "0.6.2"]
[lein-release "1.0.5"]] [lein-release "1.0.5"]]
:deploy-repositories [["releases" :clojars]
["snapshots" :clojars]]
:clean-targets ^{:protect false} :clean-targets ^{:protect false}
[:target-path [:cljsbuild :builds :app :compiler :output-dir] [:cljsbuild :builds :app :compiler :output-to]] [:target-path [:cljsbuild :builds :app :compiler :output-dir] [:cljsbuild :builds :app :compiler :output-to]]
:figwheel :figwheel
@ -108,7 +111,7 @@
:cljsbuild{:builds :cljsbuild{:builds
{:app {:app
{:source-paths ["src/cljs" "src/cljc" "env/dev/cljs"] {:source-paths ["src/cljs" "src/cljc" "env/dev/cljs"]
:figwheel {:on-jsload "geocsv.core/mount-components"} :figwheel {:on-jsload "geocsv.client.core/mount-components"}
:compiler :compiler
{:output-dir "target/cljsbuild/public/js/out" {:output-dir "target/cljsbuild/public/js/out"
:closure-defines {"re_frame.trace.trace_enabled_QMARK_" true} :closure-defines {"re_frame.trace.trace_enabled_QMARK_" true}
@ -141,4 +144,17 @@
} }
:profiles/dev {} :profiles/dev {}
:profiles/test {}}) :profiles/test {}}
;; `lein release` doesn't play nice with `git flow release`. Run `lein release` in the
;; `develop` branch, then reset the `master` branch to the release tag.
:release-tasks [["vcs" "assert-committed"]
["clean"]
["codox"]
["change" "version" "leiningen.release/bump-version" "release"]
["vcs" "commit"]
["uberjar"]
["deploy" "clojars"]
["change" "version" "leiningen.release/bump-version"]
["vcs" "commit"]])

View file

@ -1,6 +1,6 @@
# geocsv # geocsv
GeoCSV is a wee tool to show comma-separated value data on a map. A wee tool to show comma-separated value data on a map.
The CSV file must have The CSV file must have
@ -12,42 +12,48 @@ The CSV file must have
Additionally, the value of the column `category`, if present, will be used to select map pins from the map pins folder, if a suitable pin is present. Thus is the value of `category` is `foo`, a map pin image with the name `Foo-pin.png` will be selected. Additionally, the value of the column `category`, if present, will be used to select map pins from the map pins folder, if a suitable pin is present. Thus is the value of `category` is `foo`, a map pin image with the name `Foo-pin.png` will be selected.
## Passing CSV files to the app
### Loading them onto the server
If you run the server running **geocsv**, the simplest way to add CSV files is simply to copy them into the directory `resourcs/data`. The default file is the one named `data.csv`, which is the one that will be served if nothing else is specified. Other files can be specifiec by appending `?file=filename` to the URL; so if the URL of your geocsv service is
https://geocsv.example.com/
and the file you want to view is `myfile.csv`, then you would specify this as the value of `file` in the query part of the URL.
https://geocsv.example.com/?file=myfile.csv
### Loading CSV file onto another public server
If you're not running the **geocsv** server yourself, you can upload the CSV to another server which is accessible by the **geocsv** server. You can then map data from the CSV file by specifying the URL of the file as the value of `uri` in the query part of the URL:
https://geocsv.example.com/?uri=http://my.other.server/path/to/myfile.csv
### Using a Google spreadsheet
If you use [Google Sheets](https://www.google.co.uk/sheets/about/), then every sheet has a 'document id', a long string of characters which uniquely identifies that sheet. Suppose your Google spreadsheet has a document id of `abcdefghijklmnopqrstuvwxyz-12345`, then you could pull data from this spreadsheet by specifying this as the value of `docid` in the query part of the URL:
https://geocsv.example.com/?docid=abcdefghijklmnopqrstuvwxyz-12345
The spreadsheet **must** be publicly readable.
### Precedence
Nothing, of course, stops you from specifying multiple arguments in the query part of the URL, but only one will be used. The precedence is in this order:
1. `docid` is considered first, and overrides anything else;
2. `uri` is considered next, and overrides `file`;
3. the value of `file` is considered only if neither of the other two are present.
## Not yet working ## Not yet working
GeoCSV is at an early stage of development, and some features are not yet working. GeoCSV is at an early stage of development, and some features are not yet working.
### Doesn't actually interpret CSV
I haven't yet found an easy way to parse CSV into EDN client side, so I've written a [separate library](https://github.com/simon-brooke/csv2edn) to do it server side. However, that library is not yet integrated. Currently the client side actually interprets JSON.
### Missing map pin images ### Missing map pin images
At the current stage of development, if no appropriate image exists in the `resources/public/img/map-pins` folder, that's your problem. **TODO:** I intend at some point to make missing pin images default to `unknown-pin.png`, which does exist. At the current stage of development, if no appropriate image exists in the `resources/public/img/map-pins` folder, that's your problem. **TODO:** I intend at some point to make missing pin images default to `unknown-pin.png`, which does exist.
### Doesn't scale and centre the map to show the data in the sheet
Currently the map is initially centred roughly on the centre of Scotland, and scaled arbitrarily. It should compute an appropriate centre and scale from the data provided, but currently doesn't.
### There's no way of linking your own data feed
Currently, the data is taken from the file `resources/public/data/data.json`. What I intend is that you should have a form which allows you to either
1. enter [the `DOCID` of your own (publicly readable) Google Sheets spreadsheet](https://stackoverflow.com/questions/33713084/download-link-for-google-spreadsheets-csv-export-with-multiple-sheets);
2. enter the URL of a CSV file publicly available on the web;
3. upload a CSV file to the server.
### There's no way of shareing the map of your own data with other people
Currently, the data that is shared is just the data that's present when the app is compiled. Ideally, there should be a way of generating a URL, which might take the form:
https://server.name/geocsv/docid/564747867
To show data from the first sheet of the Google Sheets spreadsheet whose `DOCID` is 564747867; or
https://server.name/geocsv?uri=https://address.of.another.server/path/to/csv-file.csv
to show the content of a publicly available CSV file.
## Prerequisites ## Prerequisites
You will need [Leiningen][1] 2.0 or above installed. You will need [Leiningen][1] 2.0 or above installed.

View file

@ -1,32 +1,31 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<meta charset="UTF-8"/> <meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>Welcome to geocsv</title> <title>Welcome to geocsv</title>
</head> </head>
<body> <body>
<div id="app"> <div id="app">
<div class="splash-screen"> <section class="section">
<div class="sk-fading-circle"> <div class="container is-fluid">
<div class="sk-circle1 sk-circle"></div> <div class="content">
<div class="sk-circle2 sk-circle"></div> <h4 class="title">Welcome to geocsv</h4>
<div class="sk-circle3 sk-circle"></div> <p>If you're seeing this message, that means you haven't yet compiled your ClojureScript!</p>
<div class="sk-circle4 sk-circle"></div> <p>Please run <code>lein figwheel</code> to start the ClojureScript compiler and reload the page.</p>
<div class="sk-circle5 sk-circle"></div> <h4>For better ClojureScript development experience in Chrome follow these steps:</h4>
<div class="sk-circle6 sk-circle"></div> <ul>
<div class="sk-circle7 sk-circle"></div> <li>Open DevTools
<div class="sk-circle8 sk-circle"></div> <li>Go to Settings ("three dots" icon in the upper right corner of DevTools > Menu > Settings F1 > General > Console)
<div class="sk-circle9 sk-circle"></div> <li>Check-in "Enable custom formatters"
<div class="sk-circle10 sk-circle"></div> <li>Close DevTools
<div class="sk-circle11 sk-circle"></div> <li>Open DevTools
<div class="sk-circle12 sk-circle"></div> </ul>
<p>See <a href="http://www.luminusweb.net/docs/clojurescript.md">ClojureScript</a> documentation for further details.</p>
</div>
</div> </div>
</div> </section>
<p class="footer">
<b>geocsv</b> is loading.
You must enable JavaScript to use <b>geocsv</b>.
</p>
</div> </div>
{% block foot %} {% block foot %}
@ -50,14 +49,14 @@
{% style "/css/geocsv.css" %} {% style "/css/geocsv.css" %}
<script type="text/javascript"> <script type="text/javascript">
var csrfToken = "{{csrf-token}}"; var csrfToken = "{{csrf-token}}";
</script> </script>
<!-- scripts and styles --> <!-- scripts and styles -->
<!-- ATTENTION \/ --> <!-- ATTENTION \/ -->
<!-- ATTENTION /\ --> <!-- ATTENTION /\ -->
<!-- Leaflet --> <!-- Leaflet -->
{% style "js/lib/node_modules/leaflet/dist/leaflet.css" %} {% style "/assets/leaflet/dist/leaflet.css" %}
{% script "/js/lib/node_modules/leaflet/dist/leaflet.js" %} {% script "/assets/leaflet/dist/leaflet.js" %}
{% script "/js/app.js" %} {% script "/js/app.js" %}
</body> </body>
</html> </html>

View file

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

View file

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

View file

@ -1,636 +0,0 @@
/* required styles */
.leaflet-pane,
.leaflet-tile,
.leaflet-marker-icon,
.leaflet-marker-shadow,
.leaflet-tile-container,
.leaflet-pane > svg,
.leaflet-pane > canvas,
.leaflet-zoom-box,
.leaflet-image-layer,
.leaflet-layer {
position: absolute;
left: 0;
top: 0;
}
.leaflet-container {
overflow: hidden;
}
.leaflet-tile,
.leaflet-marker-icon,
.leaflet-marker-shadow {
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
-webkit-user-drag: none;
}
/* Safari renders non-retina tile on retina better with this, but Chrome is worse */
.leaflet-safari .leaflet-tile {
image-rendering: -webkit-optimize-contrast;
}
/* hack that prevents hw layers "stretching" when loading new tiles */
.leaflet-safari .leaflet-tile-container {
width: 1600px;
height: 1600px;
-webkit-transform-origin: 0 0;
}
.leaflet-marker-icon,
.leaflet-marker-shadow {
display: block;
}
/* .leaflet-container svg: reset svg max-width decleration shipped in Joomla! (joomla.org) 3.x */
/* .leaflet-container img: map is broken in FF if you have max-width: 100% on tiles */
.leaflet-container .leaflet-overlay-pane svg,
.leaflet-container .leaflet-marker-pane img,
.leaflet-container .leaflet-shadow-pane img,
.leaflet-container .leaflet-tile-pane img,
.leaflet-container img.leaflet-image-layer {
max-width: none !important;
max-height: none !important;
}
.leaflet-container.leaflet-touch-zoom {
-ms-touch-action: pan-x pan-y;
touch-action: pan-x pan-y;
}
.leaflet-container.leaflet-touch-drag {
-ms-touch-action: pinch-zoom;
/* Fallback for FF which doesn't support pinch-zoom */
touch-action: none;
touch-action: pinch-zoom;
}
.leaflet-container.leaflet-touch-drag.leaflet-touch-zoom {
-ms-touch-action: none;
touch-action: none;
}
.leaflet-container {
-webkit-tap-highlight-color: transparent;
}
.leaflet-container a {
-webkit-tap-highlight-color: rgba(51, 181, 229, 0.4);
}
.leaflet-tile {
filter: inherit;
visibility: hidden;
}
.leaflet-tile-loaded {
visibility: inherit;
}
.leaflet-zoom-box {
width: 0;
height: 0;
-moz-box-sizing: border-box;
box-sizing: border-box;
z-index: 800;
}
/* workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=888319 */
.leaflet-overlay-pane svg {
-moz-user-select: none;
}
.leaflet-pane { z-index: 400; }
.leaflet-tile-pane { z-index: 200; }
.leaflet-overlay-pane { z-index: 400; }
.leaflet-shadow-pane { z-index: 500; }
.leaflet-marker-pane { z-index: 600; }
.leaflet-tooltip-pane { z-index: 650; }
.leaflet-popup-pane { z-index: 700; }
.leaflet-map-pane canvas { z-index: 100; }
.leaflet-map-pane svg { z-index: 200; }
.leaflet-vml-shape {
width: 1px;
height: 1px;
}
.lvml {
behavior: url(#default#VML);
display: inline-block;
position: absolute;
}
/* control positioning */
.leaflet-control {
position: relative;
z-index: 800;
pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */
pointer-events: auto;
}
.leaflet-top,
.leaflet-bottom {
position: absolute;
z-index: 1000;
pointer-events: none;
}
.leaflet-top {
top: 0;
}
.leaflet-right {
right: 0;
}
.leaflet-bottom {
bottom: 0;
}
.leaflet-left {
left: 0;
}
.leaflet-control {
float: left;
clear: both;
}
.leaflet-right .leaflet-control {
float: right;
}
.leaflet-top .leaflet-control {
margin-top: 10px;
}
.leaflet-bottom .leaflet-control {
margin-bottom: 10px;
}
.leaflet-left .leaflet-control {
margin-left: 10px;
}
.leaflet-right .leaflet-control {
margin-right: 10px;
}
/* zoom and fade animations */
.leaflet-fade-anim .leaflet-tile {
will-change: opacity;
}
.leaflet-fade-anim .leaflet-popup {
opacity: 0;
-webkit-transition: opacity 0.2s linear;
-moz-transition: opacity 0.2s linear;
-o-transition: opacity 0.2s linear;
transition: opacity 0.2s linear;
}
.leaflet-fade-anim .leaflet-map-pane .leaflet-popup {
opacity: 1;
}
.leaflet-zoom-animated {
-webkit-transform-origin: 0 0;
-ms-transform-origin: 0 0;
transform-origin: 0 0;
}
.leaflet-zoom-anim .leaflet-zoom-animated {
will-change: transform;
}
.leaflet-zoom-anim .leaflet-zoom-animated {
-webkit-transition: -webkit-transform 0.25s cubic-bezier(0,0,0.25,1);
-moz-transition: -moz-transform 0.25s cubic-bezier(0,0,0.25,1);
-o-transition: -o-transform 0.25s cubic-bezier(0,0,0.25,1);
transition: transform 0.25s cubic-bezier(0,0,0.25,1);
}
.leaflet-zoom-anim .leaflet-tile,
.leaflet-pan-anim .leaflet-tile {
-webkit-transition: none;
-moz-transition: none;
-o-transition: none;
transition: none;
}
.leaflet-zoom-anim .leaflet-zoom-hide {
visibility: hidden;
}
/* cursors */
.leaflet-interactive {
cursor: pointer;
}
.leaflet-grab {
cursor: -webkit-grab;
cursor: -moz-grab;
}
.leaflet-crosshair,
.leaflet-crosshair .leaflet-interactive {
cursor: crosshair;
}
.leaflet-popup-pane,
.leaflet-control {
cursor: auto;
}
.leaflet-dragging .leaflet-grab,
.leaflet-dragging .leaflet-grab .leaflet-interactive,
.leaflet-dragging .leaflet-marker-draggable {
cursor: move;
cursor: -webkit-grabbing;
cursor: -moz-grabbing;
}
/* marker & overlays interactivity */
.leaflet-marker-icon,
.leaflet-marker-shadow,
.leaflet-image-layer,
.leaflet-pane > svg path,
.leaflet-tile-container {
pointer-events: none;
}
.leaflet-marker-icon.leaflet-interactive,
.leaflet-image-layer.leaflet-interactive,
.leaflet-pane > svg path.leaflet-interactive {
pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */
pointer-events: auto;
}
/* visual tweaks */
.leaflet-container {
background: #ddd;
outline: 0;
}
.leaflet-container a {
color: #0078A8;
}
.leaflet-container a.leaflet-active {
outline: 2px solid orange;
}
.leaflet-zoom-box {
border: 2px dotted #38f;
background: rgba(255,255,255,0.5);
}
/* general typography */
.leaflet-container {
font: 12px/1.5 "Helvetica Neue", Arial, Helvetica, sans-serif;
}
/* general toolbar styles */
.leaflet-bar {
box-shadow: 0 1px 5px rgba(0,0,0,0.65);
border-radius: 4px;
}
.leaflet-bar a,
.leaflet-bar a:hover {
background-color: #fff;
border-bottom: 1px solid #ccc;
width: 26px;
height: 26px;
line-height: 26px;
display: block;
text-align: center;
text-decoration: none;
color: black;
}
.leaflet-bar a,
.leaflet-control-layers-toggle {
background-position: 50% 50%;
background-repeat: no-repeat;
display: block;
}
.leaflet-bar a:hover {
background-color: #f4f4f4;
}
.leaflet-bar a:first-child {
border-top-left-radius: 4px;
border-top-right-radius: 4px;
}
.leaflet-bar a:last-child {
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
border-bottom: none;
}
.leaflet-bar a.leaflet-disabled {
cursor: default;
background-color: #f4f4f4;
color: #bbb;
}
.leaflet-touch .leaflet-bar a {
width: 30px;
height: 30px;
line-height: 30px;
}
.leaflet-touch .leaflet-bar a:first-child {
border-top-left-radius: 2px;
border-top-right-radius: 2px;
}
.leaflet-touch .leaflet-bar a:last-child {
border-bottom-left-radius: 2px;
border-bottom-right-radius: 2px;
}
/* zoom control */
.leaflet-control-zoom-in,
.leaflet-control-zoom-out {
font: bold 18px 'Lucida Console', Monaco, monospace;
text-indent: 1px;
}
.leaflet-touch .leaflet-control-zoom-in, .leaflet-touch .leaflet-control-zoom-out {
font-size: 22px;
}
/* layers control */
.leaflet-control-layers {
box-shadow: 0 1px 5px rgba(0,0,0,0.4);
background: #fff;
border-radius: 5px;
}
.leaflet-control-layers-toggle {
background-image: url(images/layers.png);
width: 36px;
height: 36px;
}
.leaflet-retina .leaflet-control-layers-toggle {
background-image: url(images/layers-2x.png);
background-size: 26px 26px;
}
.leaflet-touch .leaflet-control-layers-toggle {
width: 44px;
height: 44px;
}
.leaflet-control-layers .leaflet-control-layers-list,
.leaflet-control-layers-expanded .leaflet-control-layers-toggle {
display: none;
}
.leaflet-control-layers-expanded .leaflet-control-layers-list {
display: block;
position: relative;
}
.leaflet-control-layers-expanded {
padding: 6px 10px 6px 6px;
color: #333;
background: #fff;
}
.leaflet-control-layers-scrollbar {
overflow-y: scroll;
overflow-x: hidden;
padding-right: 5px;
}
.leaflet-control-layers-selector {
margin-top: 2px;
position: relative;
top: 1px;
}
.leaflet-control-layers label {
display: block;
}
.leaflet-control-layers-separator {
height: 0;
border-top: 1px solid #ddd;
margin: 5px -10px 5px -6px;
}
/* Default icon URLs */
.leaflet-default-icon-path {
background-image: url(images/marker-icon.png);
}
/* attribution and scale controls */
.leaflet-container .leaflet-control-attribution {
background: #fff;
background: rgba(255, 255, 255, 0.7);
margin: 0;
}
.leaflet-control-attribution,
.leaflet-control-scale-line {
padding: 0 5px;
color: #333;
}
.leaflet-control-attribution a {
text-decoration: none;
}
.leaflet-control-attribution a:hover {
text-decoration: underline;
}
.leaflet-container .leaflet-control-attribution,
.leaflet-container .leaflet-control-scale {
font-size: 11px;
}
.leaflet-left .leaflet-control-scale {
margin-left: 5px;
}
.leaflet-bottom .leaflet-control-scale {
margin-bottom: 5px;
}
.leaflet-control-scale-line {
border: 2px solid #777;
border-top: none;
line-height: 1.1;
padding: 2px 5px 1px;
font-size: 11px;
white-space: nowrap;
overflow: hidden;
-moz-box-sizing: border-box;
box-sizing: border-box;
background: #fff;
background: rgba(255, 255, 255, 0.5);
}
.leaflet-control-scale-line:not(:first-child) {
border-top: 2px solid #777;
border-bottom: none;
margin-top: -2px;
}
.leaflet-control-scale-line:not(:first-child):not(:last-child) {
border-bottom: 2px solid #777;
}
.leaflet-touch .leaflet-control-attribution,
.leaflet-touch .leaflet-control-layers,
.leaflet-touch .leaflet-bar {
box-shadow: none;
}
.leaflet-touch .leaflet-control-layers,
.leaflet-touch .leaflet-bar {
border: 2px solid rgba(0,0,0,0.2);
background-clip: padding-box;
}
/* popup */
.leaflet-popup {
position: absolute;
text-align: center;
margin-bottom: 20px;
}
.leaflet-popup-content-wrapper {
padding: 1px;
text-align: left;
border-radius: 12px;
}
.leaflet-popup-content {
margin: 13px 19px;
line-height: 1.4;
}
.leaflet-popup-content p {
margin: 18px 0;
}
.leaflet-popup-tip-container {
width: 40px;
height: 20px;
position: absolute;
left: 50%;
margin-left: -20px;
overflow: hidden;
pointer-events: none;
}
.leaflet-popup-tip {
width: 17px;
height: 17px;
padding: 1px;
margin: -10px auto 0;
-webkit-transform: rotate(45deg);
-moz-transform: rotate(45deg);
-ms-transform: rotate(45deg);
-o-transform: rotate(45deg);
transform: rotate(45deg);
}
.leaflet-popup-content-wrapper,
.leaflet-popup-tip {
background: white;
color: #333;
box-shadow: 0 3px 14px rgba(0,0,0,0.4);
}
.leaflet-container a.leaflet-popup-close-button {
position: absolute;
top: 0;
right: 0;
padding: 4px 4px 0 0;
border: none;
text-align: center;
width: 18px;
height: 14px;
font: 16px/14px Tahoma, Verdana, sans-serif;
color: #c3c3c3;
text-decoration: none;
font-weight: bold;
background: transparent;
}
.leaflet-container a.leaflet-popup-close-button:hover {
color: #999;
}
.leaflet-popup-scrolled {
overflow: auto;
border-bottom: 1px solid #ddd;
border-top: 1px solid #ddd;
}
.leaflet-oldie .leaflet-popup-content-wrapper {
zoom: 1;
}
.leaflet-oldie .leaflet-popup-tip {
width: 24px;
margin: 0 auto;
-ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678)";
filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678);
}
.leaflet-oldie .leaflet-popup-tip-container {
margin-top: -1px;
}
.leaflet-oldie .leaflet-control-zoom,
.leaflet-oldie .leaflet-control-layers,
.leaflet-oldie .leaflet-popup-content-wrapper,
.leaflet-oldie .leaflet-popup-tip {
border: 1px solid #999;
}
/* div icon */
.leaflet-div-icon {
background: #fff;
border: 1px solid #666;
}
/* Tooltip */
/* Base styles for the element that has a tooltip */
.leaflet-tooltip {
position: absolute;
padding: 6px;
background-color: #fff;
border: 1px solid #fff;
border-radius: 3px;
color: #222;
white-space: nowrap;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
pointer-events: none;
box-shadow: 0 1px 3px rgba(0,0,0,0.4);
}
.leaflet-tooltip.leaflet-clickable {
cursor: pointer;
pointer-events: auto;
}
.leaflet-tooltip-top:before,
.leaflet-tooltip-bottom:before,
.leaflet-tooltip-left:before,
.leaflet-tooltip-right:before {
position: absolute;
pointer-events: none;
border: 6px solid transparent;
background: transparent;
content: "";
}
/* Directions */
.leaflet-tooltip-bottom {
margin-top: 6px;
}
.leaflet-tooltip-top {
margin-top: -6px;
}
.leaflet-tooltip-bottom:before,
.leaflet-tooltip-top:before {
left: 50%;
margin-left: -6px;
}
.leaflet-tooltip-top:before {
bottom: 0;
margin-bottom: -12px;
border-top-color: #fff;
}
.leaflet-tooltip-bottom:before {
top: 0;
margin-top: -12px;
margin-left: -6px;
border-bottom-color: #fff;
}
.leaflet-tooltip-left {
margin-left: -6px;
}
.leaflet-tooltip-right {
margin-left: 6px;
}
.leaflet-tooltip-left:before,
.leaflet-tooltip-right:before {
top: 50%;
margin-top: -6px;
}
.leaflet-tooltip-left:before {
right: 0;
margin-right: -12px;
border-left-color: #fff;
}
.leaflet-tooltip-right:before {
left: 0;
margin-left: -12px;
border-right-color: #fff;
}

View file

@ -0,0 +1,3 @@
{
"lockfileVersion": 1
}

3
resources/public/js/package-lock.json generated Normal file
View file

@ -0,0 +1,3 @@
{
"lockfileVersion": 1
}

View file

@ -0,0 +1,40 @@
(ns geocsv.handler
(:require [compojure.core :refer [routes wrap-routes]]
[geocsv.env :refer [defaults]]
[geocsv.middleware :as middleware]
[geocsv.layout :refer [error-page]]
[geocsv.routes.home :refer [home-routes]]
[geocsv.routes.json :refer [json-routes]]
[reitit.ring :as ring]
[ring.middleware.content-type :refer [wrap-content-type]]
[ring.middleware.webjars :refer [wrap-webjars]]
[mount.core :as mount]))
(mount/defstate init-app
:start ((or (:init defaults) (fn [])))
:stop ((or (:stop defaults) (fn []))))
(mount/defstate app-routes
:start
(ring/ring-handler
(ring/router
[(home-routes)
;; (-> #'json-routes
;; (wrap-routes middleware/wrap-csrf)
;; (wrap-routes middleware/wrap-formats))
])
(ring/routes
(ring/create-resource-handler
{:path "/"})
(wrap-content-type
(wrap-webjars (constantly nil)))
(ring/create-default-handler
{:not-found
(constantly (error-page {:status 404, :title "404 - Page not found"}))
:method-not-allowed
(constantly (error-page {:status 405, :title "405 - Not allowed"}))
:not-acceptable
(constantly (error-page {:status 406, :title "406 - Not acceptable"}))}))))
(defn app []
(middleware/wrap-base #'app-routes))

View file

@ -22,11 +22,14 @@
(routes (routes
(-> #'home-routes (-> #'home-routes
(wrap-routes middleware/wrap-csrf) (wrap-routes middleware/wrap-csrf)
(wrap-routes middleware/wrap-formats)) (wrap-routes middleware/wrap-formats)
wrap-webjars)
(-> #'rest-routes (-> #'rest-routes
(wrap-routes middleware/wrap-csrf) (wrap-routes middleware/wrap-csrf)
(wrap-routes middleware/wrap-formats)) (wrap-routes middleware/wrap-formats))
(route/resources "/") ;; (ring/create-resource-handler
;; {:path "/"})
(route/resources "/")
(route/not-found (route/not-found
(:body (:body
(error-page {:status 404 (error-page {:status 404

View file

@ -6,6 +6,7 @@
[clojure.java.io :as io] [clojure.java.io :as io]
[clojure.string :as s] [clojure.string :as s]
[clojure.tools.logging :as log] [clojure.tools.logging :as log]
[cpath-clj.core :as cp]
[compojure.core :refer [defroutes GET POST]] [compojure.core :refer [defroutes GET POST]]
[csv2edn.csv2edn :refer :all] [csv2edn.csv2edn :refer :all]
[noir.response :as nresponse] [noir.response :as nresponse]
@ -37,19 +38,18 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn get-pin-image-names (defn get-pin-image-names
"Return the category names for which we have pin images; `request` is ignored.
This looks odd - why not file-seq over the directory? - but the answer is we
may be running in a jar file, and if we are that will fail."
[request] [request]
(ar/do-or-server-fail (ar/do-or-server-fail
(map (map
#(s/replace (.getName %) #"-pin\.png$" "") #(s/replace (s/replace (str %) #"-pin\.png$" "") "/" "")
(let [grammar-matcher (.getPathMatcher
(java.nio.file.FileSystems/getDefault)
"glob:*-pin.png")]
(->> "public/img/map-pins" (->> "public/img/map-pins"
io/resource cp/resources
io/file keys
file-seq (filter #(re-find #".*-pin.png" %))))
(filter #(.isFile %))
(filter #(.matches grammar-matcher (.getFileName (.toPath %)))))))
200)) 200))
(defn get-data-uri (defn get-data-uri
@ -72,7 +72,7 @@
"Return JSON formatted data taken from the CSV file with the name `filename` "Return JSON formatted data taken from the CSV file with the name `filename`
in the directory `resources/public/data`." in the directory `resources/public/data`."
[filename] [filename]
(-> (str "public/data/" filename) io/resource io/file io/reader csv->json)) (-> (str "public/data/" filename) io/resource io/reader csv->json))
(defn get-data (defn get-data
"Return JSON formatted data from the source implied by this `request`." "Return JSON formatted data from the source implied by this `request`."

View file

@ -1,4 +1,4 @@
(ns geocsv.ajax (ns geocsv.client.ajax
(:require (:require
[ajax.core :as ajax] [ajax.core :as ajax]
[luminus-transit.time :as time] [luminus-transit.time :as time]
@ -6,11 +6,9 @@
[re-frame.core :as rf])) [re-frame.core :as rf]))
(defn local-uri? [{:keys [uri]}] (defn local-uri? [{:keys [uri]}]
(js/console.log (str "local-uri?: received `" (str uri) "` (type " (type uri) ") as uri"))
(not (re-find #"^\w+?://" (str uri)))) (not (re-find #"^\w+?://" (str uri))))
(defn default-headers [request] (defn default-headers [request]
(js/console.log (str "default-headers: received `" request "` as request"))
(if (local-uri? request) (if (local-uri? request)
(-> request (-> request
(update :headers #(merge {"x-csrf-token" js/csrfToken} %))) (update :headers #(merge {"x-csrf-token" js/csrfToken} %)))

View file

@ -1,17 +1,18 @@
(ns geocsv.core (ns geocsv.client.core
(:require (:require
[day8.re-frame.http-fx] [day8.re-frame.http-fx]
[reagent.core :as r] [reagent.core :as r]
[re-frame.core :as rf] [re-frame.core :as rf]
[geocsv.views.map :as mv] [geocsv.client.gis :as gis]
[geocsv.client.views.map :as mv]
[goog.events :as events] [goog.events :as events]
[goog.history.EventType :as HistoryEventType] [goog.history.EventType :as HistoryEventType]
[markdown.core :refer [md->html]] [markdown.core :refer [md->html]]
[geocsv.ajax :as ajax] [geocsv.client.ajax :as ajax]
[geocsv.events] [geocsv.client.events]
[reitit.core :as reitit] [reitit.core :as reitit]
[reitit.frontend.easy :as rfe] [reitit.frontend.easy :as rfe]
[clojure.string :as string]) [clojure.string :as s])
(:import goog.History)) (:import goog.History))
(defn nav-link [uri title page] (defn nav-link [uri title page]
@ -39,7 +40,31 @@
(defn about-page [] (defn about-page []
[:section.section>div.container>div.content [:section.section>div.container>div.content
[:img {:src "/img/warning_clojure.png"}]]) [:img {:src "/img/warning_clojure.png"}]
(when-let [images @(rf/subscribe [:available-pin-images])]
[:div
[:h2 "The following pin images are available on this server"]
(apply
vector
(cons
:ol
(map
#(vector
:ol
[:img
{:src
(str
"img/map-pins/"
(s/capitalize
(s/replace
(s/lower-case
(str %))
#"[^a-z0-9]" "-"))
"-pin.png")
:alt %}]
" "
%)
(sort images))))])])
(defn home-page [] (defn home-page []
[:section.section>div.container>div.content [:section.section>div.container>div.content
@ -90,6 +115,7 @@
(defn init! [] (defn init! []
(rf/dispatch-sync [:initialise-db]) (rf/dispatch-sync [:initialise-db])
(rf/dispatch [:fetch-pin-image-names])
(start-router!) (start-router!)
(ajax/load-interceptors!) (ajax/load-interceptors!)
(mount-components)) (mount-components))

View file

@ -1,6 +1,6 @@
(ns ^{:doc "geocsv app initial database." (ns ^{:doc "geocsv app initial database."
:author "Simon Brooke"} :author "Simon Brooke"}
geocsv.db) geocsv.client.db)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; ;;;;
@ -28,6 +28,14 @@
(def default-db (def default-db
{:page :home {:page :home
:available-pin-images #{"Planning authority" "unknown"} ;; need to be fetched from server side :available-pin-images #{"Planning-authority"
:map {:map-centre [56 -4] "Landowner"
:map-zoom 6}}) "Unknown"
"Anchor-customer"
"Investor"
"Broadband-supplier"
"Operator"
"Other-key-customers"
"Power-supplier"} ;; need to be fetched from server side
:latitude 56
:longitude -4})

View file

@ -1,9 +1,9 @@
(ns geocsv.events (ns geocsv.client.events
(:require [ajax.core :as ajax] (:require [ajax.core :as ajax]
[ajax.json :refer [json-request-format json-response-format]] [ajax.json :refer [json-request-format json-response-format]]
[cemerick.url :refer [url url-encode]] [cemerick.url :refer [url url-encode]]
[geocsv.db :refer [default-db]] [geocsv.client.db :refer [default-db]]
[geocsv.gis :refer [refresh-map-pins]] [geocsv.client.gis :refer [compute-centre refresh-map-pins]]
[re-frame.core :as rf] [re-frame.core :as rf]
[reitit.frontend.easy :as rfe] [reitit.frontend.easy :as rfe]
[reitit.frontend.controllers :as rfc])) [reitit.frontend.controllers :as rfc]))
@ -40,7 +40,6 @@
:query nil :query nil
:anchor nil)) :anchor nil))
;;dispatchers: keep in alphabetical order, please. ;;dispatchers: keep in alphabetical order, please.
(rf/reg-event-fx (rf/reg-event-fx
:bad-data :bad-data
@ -73,6 +72,24 @@
:on-failure [:bad-data]} :on-failure [:bad-data]}
:db db}))) :db db})))
(rf/reg-event-fx
:fetch-pin-image-names
(fn [{db :db} _]
(let [uri (assoc source-host
:path "/get-pin-image-names")]
(js/console.log
(str
"Fetching data: " uri))
;; we return a map of (side) effects
{:http-xhrio {:method :get
:uri uri
:format (json-request-format)
:response-format (json-response-format {:keywords? true})
:on-success [:process-pin-image-names]
;; ignore :on-failure for now
}
:db db})))
(rf/reg-event-fx (rf/reg-event-fx
:fetch-docs :fetch-docs
(fn [_ _] (fn [_ _]
@ -120,12 +137,26 @@
;; TODO: why is this an `-fx`? Does it need to be? ;; TODO: why is this an `-fx`? Does it need to be?
(fn (fn
[{db :db} [_ response]] [{db :db} [_ response]]
(let [data (js->clj response)] (let [db' (assoc db :data (js->clj response))]
(js/console.log (str "processing fetched JSON data")) (js/console.log (str "processing fetched JSON data"))
{:db (if-let [data (:data db')]
(let [centre (compute-centre data)]
(if
(:view db')
(refresh-map-pins (merge db' centre))
db)
db))})))
(rf/reg-event-fx
:process-pin-image-names
(fn
[{db :db} [_ response]]
(let [db' (assoc db :available-pin-images (set response))]
(js/console.log (str "processing pin images"))
{:db (if {:db (if
(:view db) (:view db')
(refresh-map-pins (assoc db :data data)) (refresh-map-pins db')
db)}))) db')})))
(rf/reg-event-db (rf/reg-event-db
:set-docs :set-docs
@ -167,11 +198,6 @@
(js/console.log (str "Fetching longitude" v)) (js/console.log (str "Fetching longitude" v))
v))) v)))
(rf/reg-sub
:map
(fn [db _]
(:map db)))
(rf/reg-sub (rf/reg-sub
:route :route
(fn [db _] (fn [db _]

View file

@ -1,6 +1,6 @@
(ns ^{:doc "geocsv app map stuff." (ns ^{:doc "geocsv app map stuff."
:author "Simon Brooke"} :author "Simon Brooke"}
geocsv.gis geocsv.client.gis
(:require [ajax.core :refer [GET]] (:require [ajax.core :refer [GET]]
[ajax.json :refer [json-request-format json-response-format]] [ajax.json :refer [json-request-format json-response-format]]
[cljs.reader :refer [read-string]] [cljs.reader :refer [read-string]]
@ -71,48 +71,56 @@
(defn pin-image (defn pin-image
"Return the name of a suitable pin image for this `record`." "Return the name of a suitable pin image for this `record`."
[record] [db record]
(let [available @(subscribe [:available-pin-images])] (let [available (:available-pin-images db)
(js/console.log (str "pin-image: available is of type `" (type available) "`; `(fn? available)` returns " (set? available))) category (s/capitalize
(s/replace
(s/lower-case
(str (:category record)))
#"[^a-z0-9]" "-"))]
(if (if
(contains? available (:category record)) (available category)
(str (str category "-pin")
(s/capitalize "Unknown-pin")))
(s/replace (s/lower-case (str (:category record))) #"[^a-z0-9]" "-")) "-pin")
"unknown-pin")))
(defn popup-content (defn popup-content
"Appropriate content for the popup of a map pin for this `record`." "Appropriate content for the popup of a map pin for this `record`."
[record] [record]
(str (if
"<h5>" (map? record) ;; which it should be!
(:name record) (str
"</h5><dl>" "<h5>"
(apply (:name record)
str "</h5><dl>"
(map #(str "<dt>" (name %) "</dt><dd>" (record %) "</dd>") (keys record))) (apply
"</dl>")) str
(map
#(str "<dt>" (name %) "</dt><dd>" (record %) "</dd>")
(filter #(record %) (keys record))))
"</dl>")))
(defn popup-table-content (defn popup-table-content
"Appropriate content for the popup of a map pin for this `record`, as a "Appropriate content for the popup of a map pin for this `record`, as a
table. Obviously this is semantically wrong, but for styling reasons it's table. Obviously this is semantically wrong, but for styling reasons it's
worth trying." worth trying."
[record] [record]
(str (if
"<h5>" (map? record) ;; which it should be!
(:name record) (str
"</h5><table>" "<h5>"
(apply (:name record)
str "</h5><table>"
(map (apply
#(str "<tr><th>" (name %) "</th><td>" (record %) "</td></tr>") str
(sort (keys record)))) (map
"</table>")) #(str "<tr><th>" (name %) "</th><td>" (record %) "</td></tr>")
(sort (filter #(record %) (keys record)))))
"</table>")))
(defn add-map-pin (defn add-map-pin
"Add an appropriate map-pin for this `record` in this map `view`, if it "Add an appropriate map-pin for this `record` in this map `view`, if it
has a valid `:latitude` and `:longitude`." has a valid `:latitude` and `:longitude`."
[record index view] [db record index view]
(let [lat (:latitude record) (let [lat (:latitude record)
lng (:longitude record)] lng (:longitude record)]
(if (if
@ -125,7 +133,7 @@
(clj->js (clj->js
{:iconAnchor [16 41] {:iconAnchor [16 41]
:iconSize [32 42] :iconSize [32 42]
:iconUrl (str "img/map-pins/" (pin-image record) ".png") :iconUrl (str "img/map-pins/" (pin-image db record) ".png")
:riseOnHover true :riseOnHover true
:shadowAnchor [16 23] :shadowAnchor [16 23]
:shadowSize [57 24] :shadowSize [57 24]
@ -150,17 +158,52 @@
(.removeLayer view %))) (.removeLayer view %)))
view)) view))
(defn compute-zoom
"See [explanation here](https://leafletjs.com/examples/zoom-levels/). Brief
summary: it's hard, but it doesn't need to be precise."
[min-lat max-lat min-lng max-lng]
(let [n (min (/ 360 (- max-lng min-lng)) (/ 180 (- max-lat min-lat)))]
(first
(remove
nil?
(map
#(if (> (reduce * (repeat 2 %)) n) %)
(range))))))
(defn compute-centre
"Compute, and return as a map with keys `:latitude` and `:longitude`, the
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))
min-lat (apply min lats)
max-lat (apply max lats)
lngs (filter number? (map :longitude records))
min-lng (apply min lngs)
max-lng (apply max lngs)]
(if-not
(or (empty? lats) (empty? lngs))
{:latitude (+ min-lat (/ (- max-lat min-lat) 2))
:longitude (+ min-lng (/ (- max-lng min-lng) 2))
:zoom (compute-zoom min-lat max-lat min-lng max-lng)}
{})))
(defn refresh-map-pins (defn refresh-map-pins
"Refresh the map pins on the current map. Side-effecty; liable to be "Refresh the map pins on the current map. Side-effecty; liable to be
problematic." problematic."
[db] [db]
(let [view (map-remove-pins @(subscribe [:view])) (let [view (map-remove-pins @(subscribe [:view]))
data (:data db)] data (:data db)
centre (compute-centre data)]
(if (if
view view
(let [added (remove nil? (map #(add-map-pin %1 %2 view) data (range)))] (let [added (remove nil? (map #(add-map-pin db %1 %2 view) data (range)))]
(js/console.log (str "Adding " (count added) " pins"))) (js/console.log (str "Adding " (count added) " pins"))
(js/console.log "View is not yet ready")) (if
db)) (:latitude centre)
(do
(js/console.log (str "computed centre: " centre))
(.setView view (clj->js [(:latitude centre) (:longitude centre)]) (:zoom centre))
(merge db centre))
db))
(do (js/console.log "View is not yet ready") db))))

View file

@ -1,11 +1,11 @@
(ns ^{:doc "a map onto which to project CSV data." (ns ^{:doc "a map onto which to project CSV data."
:author "Simon Brooke"} :author "Simon Brooke"}
geocsv.views.map geocsv.client.views.map
(:require [cljsjs.leaflet] (:require [cljsjs.leaflet]
[re-frame.core :refer [reg-sub subscribe dispatch dispatch-sync]] [re-frame.core :refer [reg-sub subscribe dispatch dispatch-sync]]
[reagent.core :as reagent] [reagent.core :as reagent]
[recalcitrant.core :refer [error-boundary]] [recalcitrant.core :refer [error-boundary]]
[geocsv.gis :refer [refresh-map-pins get-current-location]])) [geocsv.client.gis :refer [refresh-map-pins get-current-location]]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; ;;;;
@ -61,22 +61,14 @@
(let [view (.setView (let [view (.setView
(.map js/L (.map js/L
"map" "map"
;; (clj->js {:zoomControl false}) (clj->js {:zoomControl false}))
) #js [@(subscribe [:latitude]) @(subscribe [:longitude])]
#js [56 -4] ;;[@(subscribe [:latitude]) @(subscribe [:longitude])]
@(subscribe [:zoom]))] @(subscribe [:zoom]))]
(.addTo (.tileLayer js/L osm-url (.addTo (.tileLayer js/L osm-url
(clj->js {:attribution osm-attrib (clj->js {:attribution osm-attrib
:maxZoom 18})) :maxZoom 18}))
view) view)
(dispatch-sync [:set-view view]) (dispatch-sync [:set-view view])
;; (.on view "moveend"
;; (fn [_] (let [c (.getCenter view)]
;; (js/console.log (str "Moving centre to " c))
;; (dispatch-sync [:set-latitude (.-lat c)])
;; (dispatch-sync [:set-longitude (.-lng c)])
;; (dispatch [:fetch-data]))))
;; (refresh-map-pins)
view)) view))
(defn map-did-mount (defn map-did-mount
@ -91,7 +83,7 @@
(defn map-render (defn map-render
"Render the actual div containing the map." "Render the actual div containing the map."
[] []
[:div#map {:style {:height "1000px"}}]) [:div#map {:style {:height "800px"}}])
(defn panel (defn panel
"A reagent class for the map object." "A reagent class for the map object."