Merge tag 'geocsv-0.1.1'
This commit is contained in:
commit
576db9e88f
24
README.md
24
README.md
|
@ -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.
|
||||||
|
|
2
env/dev/cljs/geocsv/app.cljs
vendored
2
env/dev/cljs/geocsv/app.cljs
vendored
|
@ -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]))
|
||||||
|
|
2
env/prod/cljs/geocsv/app.cljs
vendored
2
env/prod/cljs/geocsv/app.cljs
vendored
|
@ -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
13
package-lock.json
generated
|
@ -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=="
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
28
project.clj
28
project.clj
|
@ -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"]])
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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>
|
||||||
|
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 5.1 KiB |
Binary file not shown.
636
resources/public/js/lib/node_modules/leaflet/dist/leaflet.css
generated
vendored
636
resources/public/js/lib/node_modules/leaflet/dist/leaflet.css
generated
vendored
|
@ -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;
|
|
||||||
}
|
|
3
resources/public/js/lib/package-lock.json
generated
Normal file
3
resources/public/js/lib/package-lock.json
generated
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"lockfileVersion": 1
|
||||||
|
}
|
3
resources/public/js/package-lock.json
generated
Normal file
3
resources/public/js/package-lock.json
generated
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"lockfileVersion": 1
|
||||||
|
}
|
40
src/clj/geocsv/handler serves resources.clj
Normal file
40
src/clj/geocsv/handler serves resources.clj
Normal 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))
|
|
@ -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
|
||||||
|
|
|
@ -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`."
|
||||||
|
|
|
@ -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} %)))
|
|
@ -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))
|
|
@ -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})
|
|
@ -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 _]
|
|
@ -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))))
|
|
@ -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."
|
Loading…
Reference in a new issue