-
Notifications
You must be signed in to change notification settings - Fork 33
/
builder.html
307 lines (276 loc) · 17.1 KB
/
builder.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
<!DOCTYPE html>
<html lang="en">
<head>
<title>Hex map builder</title>
<link rel="icon" href="https://open-innovations.org/resources/images/logos/oi-square-10.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta charset="utf-8" />
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:site" content="@OpenInnovates">
<meta name="twitter:url" property="og:url" content="https://open-innovations.org/projects/hexmaps/builder">
<meta name="twitter:title" property="og:title" content="Hex map builder">
<meta name="twitter:description" property="og:description" content="Create your own point-topped hex map from a data file.">
<meta name="twitter:image" property="og:image" content="https://open-innovations.org/projects/hexmaps/hexmap.png">
<link rel="StyleSheet" href="resources/style.css" type="text/css" />
<script type="text/javascript" src="resources/oi.hexmap.js"></script>
<script type="text/javascript" src="resources/oi.hexmap-search.js"></script>
<script type="text/javascript" src="resources/builder.js"></script>
<script>
var builder;
OI.ready(function(){
builder = new HexBuilder(document.getElementById('builder'),{
'width':document.getElementById('builder').offsetWidth
}).init();
});
</script>
<style>
section { margin-bottom: 1em; }
.chooser { text-align: left; display: grid; grid-template-columns: 1fr auto 1fr; line-height: 1.5em; }
.chooser input { font-size:1em; }
.chooser .vs { display: inline; margin: 2em 0.5em 0 0.5em; font-family: serif; font-style: italic; color: #666666; text-align: center; vertical-align: middle; }
.chooser .part { position: relative; margin: 0px; width: 100%; display: inline; padding: 1em; line-height: 1.5em; }
.chooser .part input { padding: 0px 2px; }
.chooser .part label { display: block; }
.chooser .choose { display: inline; }
.chooser .choose button { height: 100%; margin-left: 1em; }
#url {
line-height: 1.5em;
width: 100%;
}
form {
position: relative;
}
.dropzone {
line-height: 1.5em;
position: absolute;
left: 0px;
top: 0px;
width: 100%;
height: 100%;
padding: 1em;
box-sizing: border-box;
}
.dropzone.loaded {
border: 2px solid transparent;
}
.dropzone.loaded .helpertext {
display: none;
}
.dropzone.drop {
background-color: #efefef;
outline: 2px dashed #999999;
}
.dropzone input {
position: absolute;
left: 0px;
top: 0px;
width: 100%;
height: 100%;
opacity: 0.01;
}
.spinner { position: absolute; left: 50%; top: 50%; transform: translate3d(-50%,-50%,0); z-index: 1000; }
.spinner img { width: 5em; margin: auto; }
button.icon { padding-left: 0.5em; padding-right: 0.5em; }
button.icon svg { width: 1.5em; height: 1.5em; }
.hexmap {
margin: auto;
width: 100%;
height: auto;
max-width: 100vw;
position: relative;
}
.hexmap svg { margin: auto; display: block; overflow: hidden; }
.hex-cell { fill-opacity: 1; }
.hex-label { fill: white; }
.highlighted .hex-cell { stroke: black!important; stroke-width: 2px!important; }
.not-highlighted .hex-cell { opacity: 0.3!important; }
.hex-cell.hover { stroke: white!important; stroke-width: 5px!important; opacity:1!important; fill-opacity: 1!important; }
.hex-label.hover { fill: white!important; }
.hex-cell.selected {
stroke: #222!important; stroke-dasharray: 4 8; stroke-width: 3px; stroke-linecap: round;
animation: rotate-border 1s linear infinite;
}
@keyframes rotate-border {
from { stroke-dashoffset: 0; }
to { stroke-dashoffset: 12; }
}
.hex-control { position: sticky; top: 0; left: 0; display: inline-block; z-index: 100; width: 100%; }
.hex-menu { position: absolute; top: 0; right: 1em; display: flex; }
.hex-menu-item { display: inline-block; margin: 0.25em; }
.hex-expand { margin-right: 0; }
.hex-colour-picker { margin-right: 0; }
.hex-colour-picker-inner { display: grid; grid-template-columns: 1fr auto auto; grid-gap: 0.25em; }
.hex-colour-picker-inner input, .hex-colour-picker-inner label { width: 2.5em; height: 2.5em; border: 0; cursor: pointer; }
.hex-search { position: absolute; max-width: 400px; }
.hex-search .search-inner { position: relative; }
.hex-search label {
position: absolute;
left: 0px;
top: 0px;
z-index: 100;
font-size: 1em;
display: inline-block;
background: black;
color: white;
padding: 1.13em;
font-family: "Source Sans Pro", "Helvetica Neue", Helvetica, Arial, sans-serif;
line-height: 1;
text-transform: uppercase;
padding: 0.5em 1em;
font-weight: bold;
text-decoration: none;
letter-spacing: 0.05em;
line-height: 2em;
width: 3em;
height: 3em;
}
.hex-search label svg { }
.hex-search input {
font-size: 1em;
background: rgba(255,255,255,0.7) !important;
border: solid 0.5em black;
border-radius: 0px;
display: none;
color: black;
padding: 0 0.5em;
height: 3em;
width: 100%;
position: relative;
left: 0em;
top: 0px;
padding-left: 3em;
z-index: 99;
}
.hex-search svg { position: relative; top: 50%; transform: translate3d(0,-50%,0); }
.hex-search.searching input { display: inline-block; }
.hex-search .search-results { margin: 0px!important; list-style: none!important; }
.hex-search .search-results li a { padding: 0.5em 0.5em 0.5em 0.5em; text-align: left; background-color: rgba(223,223,223,0.92); display: block; color: inherit; text-decoration: none; }
.hex-search .search-results li a:hover, .hex-search .search-results li a:focus, .hex-search .search-results li a.selected { background-color: #000000; color: white; }
.infobubble {
position: absolute;
z-index: 3;
top: calc(100% + 0.5em);
right: 0;
width: 240px;
max-height: 40vh;
text-align: center;
box-shadow: 5px 5px 5px rgba(0,0,0,0.15);
background-color: white;
padding: 0.5em 1em;
overflow-y: auto;
color: black; line-height: 1.25em;
}
.tooltip { background-color: black; color: white; padding: 0.325em 0.5em; transform: translate3d(-50%,100%,0); z-index: 100; position: absolute; z-index: 98; }
.tooltip:before { content: ""; border: 0.5em solid transparent; border-bottom: 0.5em solid black; border-top: 0px; left: 50%; top: 0; transform: translate3d(-50%,-0.5em,0); position: absolute; display: block; width: 0px; height: 0px; }
.options button { margin-right: 0.25em; }
.options .save { margin-top: 1em; }
select,input { line-height: 2em; height: 2em; min-width: 2em; font-size: 1em; }
.config { line-height: 2em; }
.config .row { display: inline-block; margin: 0 0.5em 0.5em 0; }
.config .row input, .config .row select { float: right; }
.config label { margin-left: 1em; margin-right: 0.25em; line-height: 2em; display: inline-block; }
ul { list-style: disc; }
ul li {
line-height: 2em;
margin-left: 2em;
}
code { background: #444; color: inherit; }
#scale-names code { display: inline-block; line-height: 1em; }
.scale-preview { width: 100%; height: 0.5em; display: block; }
footer p { margin-bottom: 1em; }
footer h2 { margin-top: 2em; }
</style>
</head>
<body class="b1-bg">
<div id="main" class="b6-bg">
<header>
<div class="b1-bg">
<div class="holder" style="text-align:center;">
<a href="https://open-innovations.org/"><img src="https://open-innovations.org/resources/images/logos/oi-square-10.svg" width="100px" /></a>
<h1>Hex cartogram builder</h1>
</div>
</div>
</header>
<div class="c10-bg">
<div id="messages"></div>
</div>
<div class="warning">
<div class="holder padded" style="text-align:center;margin-bottom:1rem;">To visualise data on a hex map we suggest using our <a href="viewer.html" style="font-style:italic;">Hexviz</a> tool.</div>
</div>
<div class="holder">
<p>We like <a href="https://odileeds.org/blog/2017-05-08-mapping-election-with-hexes">creating hex cartograms</a> and this tool will <em>help you design hex layouts</em> from your own data. Read the <a href="#notes">notes below for more details</a>.</p>
<form id="validation_form" name="validation_form" submit="return true;" method="POST" enctype="multipart/form-data" accept-charset="UTF-8" >
<section class="chooser">
<div class="part b5-bg">
<!--<label for="url">URL</label><input id="url" type="url" name="url" class="" placeholder="e.g. https://odileeds.github.io/hexmaps/data/wards-leeds.csv" pattern="https://.*" />-->
<label for="url"><strong>Load URL</strong> (CSV or <a href="hexjson.html">HexJSON</a>)</label><input id="url" type="text" name="url" class="" placeholder="e.g. https://open-innovations.org/projects/hexmaps/data/wards-leeds.csv" />
</div>
<div class="vs">or</div>
<div class="part b5-bg dropzone" id="drop_zone">
<label for="standard_files"><strong>Load file</strong></label>
<div class="helpertext">Drop a .csv or .hexjson file here (or click to browse files)</div>
<input id="standard_files" class="fileselect" type="file" title="browse" name="file" accept=".csv, .hexjson">
</div>
</section>
<button id="btnSubmit" type="submit" class="button c10-bg" style="float:right;">Hexify</button>
<button id="reset" type="reset" class="c8-bg">Reset</button>
<button id="example" class="c8-bg">Use an example</button>
</form>
</div>
<div id="builder">
<div class="padded">
<div class="hexmap">
</div>
</div>
<div class="scale holder"></div>
<div class="options holder">
<h3>Cartogram options</h3>
</div>
</div>
</div>
<footer class="b1-bg" id="notes">
<div class="holder">
<h2>Notes</h2>
<h3>Loading data</h3>
<p>You can either read in an existing <a href="hexjson.html">HexJSON</a> file or start creating your own hex cartogram from a CSV file. These can either be loaded from a URL or from a local file. No data will be sent to our server; it all stays in this page. If you are using a URL it will need to be publicly accessible and <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS">CORS will need to be enabled</a> on the remote server (otherwise this page isn't allowed to access the data).</p>
<p>The CSV should contain a column with the heading <code>id</code> that uniquely identifies each hexagon. Other columns will be added as properties of each hexagon. You can specify the position of the hexagon using the column headings <code>q</code> and <code>r</code> (they must be separate columns). A column with the heading <code>n</code> or <code>name</code> will set the displayed name of the hexagon. You <em>can</em> choose to manually set the colour of each hexagon by including a column headed <code>colour</code>.</p>
<h3>Colouring hexagons</h3>
<p>Hexagons can be coloured by a data attribute using one of our defined colour scales. For instance, if your CSV file contains a column titled <code>Population</code> you could choose this attribute and set the colour scale to <code>Viridis8</code>.</p>
<p>When building a hexagon layout you may want to set default colours for hexagons. If you have one or more hexagons selected you can choose to change their default colour by using the colour picker tool that appears in the top right of the screen. Note that custom colours set using this method will <em>not</em> be available in <a href="#share">sharing links</a> and are only retained in exported versions.</p>
<h3>Moving hexagons</h3>
<p>To move one or more hexagons you must first select them then click in an empty hex in the grid. Pressing <code>c</code> on your keyboard will select all hexagons of the same colour or you can use the <code>Select by colour</code> in the colour picker at the top right. When you have everything where you want it, save the output. Note that this tool is for creating layouts rather than fully interactive visualisations.</p>
<h3>Using existing layouts</h3>
<p>We already know about defined hex layouts for <a href="maps/uk-local-authority-districts-2021.hexjson">UK local authorities</a>, <a href="maps/constituencies.hexjson">UK Parliamentary constituencies</a>, <a href="maps/uk-nuts3.hexjson">UK NUTS3 regions</a>, <a href="maps/uk-upper-tier-local-authorities.hexjson">UK Upper Tier Local Authorities</a>, <a href="https://raw.githubusercontent.com/houseofcommonslibrary/uk-hex-cartograms-noncontiguous/main/hexjson/msoa_hex_coords.hexjson">MSOAs</a>, <a href="maps/nhs-icb-2022.hexjson">NHS Integrated Care Boards</a>, and <a href="maps/us-states.hexjson">US States</a>. If you have a CSV that appears to have a column with codes for one of those, the Builder will try to load an existing hex map layout for you and add the data to that. That means you can use this tool as a simple visualisation tool for CSV files on the web too. For instance, there is <a href="https://github.com/CivilServiceUSA/us-states/blob/master/data/states.csv">a CSV of US State data on Github</a> which has state 2-letter codes so we can load it straight into the Builder and show <a href="https://open-innovations.org/projects/hexmaps/builder.html?https://raw.githubusercontent.com/CivilServiceUSA/us-states/master/data/states.csv&attribute=admission_number">US States coloured by admission number</a>. You can build your own links to this tool of the form:</p>
<pre>https://open-innovations.org/projects/hexmaps/builder.html?<em>https://raw.githubusercontent.com/odileeds/covid-19/main/vaccines/data/vaccinations-MSOA-latest.csv</em>&attribute=admission_number&colourscale=Viridis&labels=false&keepmissing=false</pre>
<p>where you can replace <code>https://raw.githubusercontent.com/odileeds/covid-19/main/vaccines/data/vaccinations-MSOA-latest.csv</code> with the URL of a CSV file and you can add the following properties:</p>
<ul>
<li><code>attribute=</code> is the column heading for the column to use for the colour scale - we can cope with numeric valus and ISO8601 dates</li>
<li><code>borders=</code> can be set to <code>true</code> or <code>false</code> to turn the hex borders on or off</li>
<li><code>colourscale=</code> is one of our named colour scales (<span id="scale-names"></span>)</li>
<li><code>labels=</code> can be set to <code>true</code> or <code>false</code> to turn the hex labels on or off - turning them off can speed up rendering time for big maps</li>
<li><code>keepmissing=</code> can be set to <code>true</code> or <code>false</code> to show or not show hexes that aren't referred to in the data</li>
</ul>
<h3 id="google-sheet">Using data in a Google Sheet</h3>
<p>If you have data in a <em>publicly-visible</em> Google Sheet you can add the parameter <code>gsheetid=1hqFqAyRp7JfHu7jmZK-tBtiU89B28rgfTKHofK16PMw</code> (replace <code>1hqFqAyRp7JfHu7jmZK-tBtiU89B28rgfTKHofK16PMw</code> with your Sheet ID) to the URL and the Builder will attempt to use your Google Sheet e.g. load a simple Google Sheet, hide hex labels, and remove hex borders.</p>
<pre>https://open-innovations.org/projects/hexmaps/builder.html?<em>gsheetid=1hqFqAyRp7JfHu7jmZK-tBtiU89B28rgfTKHofK16PMw</em>&attribute=Date&labels=false&borders=false</pre>
<p>Warning: there are several things that might stop this working because it relies on this page being able to see your Google Sheet and for it to be very straightforward. If your sheet is protected or complicated, this is likely to fail.</p>
<h3 id="export">Exporting the map</h3>
<p>You can export the map in four ways:</p>
<ol style="margin: 1em 0 1em 2em;">
<li>HexJSON. This is the prefered output as it can be loaded back in to this tool again.</li>
<li>SVG. This will save the image created in the browser. If you output as SVG you won't be able to load it back into this tool.</li>
<li>GeoJSON. A cartogram is not geographic but some people have requested "geographic formats" so they can use these hex cartograms with specific GIS software. To make it "geographic" we've done a very simple projection at Null Island (0°, 0°) keeping the result to approximately 0.1°×0.1° to try to reduce distortions. If you output as GeoJSON you won't be able to load it back into this tool.</li>
<li>PNG. This will save the map (without the grid) as an image that you can use elsewhere. You won't be able to load it back into this tool but it is useful for screenshots.</li>
</ol>
<h3 id="share">Linking to a view</h3>
<p>If your CSV file exists at an accessible URL, it may be possible to create a link to a view with your data and selections. However, if you are using a local file you won't be able to do that because the data is not uploaded anywhere. If you are using a local file and want to show a hex cartogram to others you will have to <a href="#export">export the map</a> in order to share it via other means.</p>
<h2>Credits</h2>
© 2017-24 <a href="https://odileeds.org/">Open Innovations</a>. Released under an MIT license. <a href="https://github.com/odileeds/hexmaps">Source on Github</a>.
</div>
</footer>
<script src="https://open-innovations.org/resources/oi.log.js"></script>
<script>
OI.log.setup({'id':'odileeds','target':['open-innovations.org','odileeds.org','odileeds.github.io']}).add('action=view');
</script>
</body>
</html>