-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmap-demo.html
8995 lines (8925 loc) · 704 KB
/
map-demo.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
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Displaying Maps in Clojure</title>
<meta name="generator" content="Org mode">
<meta name="author" content="George Kontsevich">
<link rel="stylesheet" type="text/css" href="../web/geokon.css" />
<link rel="shortcut icon" href="../web/panda.svg" type="image/x-icon">
<style>
html {
margin: 0;
overflow-x: hidden;
}
body {
font: 14px/20px italic Times, sans-serif;
margin-left: 0px;
margin-right: 0px;
margin-left: 5vw;
margin-right: 5vw;}
#table-of-contents {display: none} /* disable for now.. till I have a better plan */
pre.src {
margin-left: -5vw;
margin-right: -5vw;
font-family: monospace;
font-size: 100%;
background: #FFFFEE;
overflow: auto;
position: static;
border-top: 1px solid #e5e5d6;
border-bottom: 1px solid #e5e5d6;
}
pre.src-org {
color: red;
background: #FFFFFF;
}
blockquote {
width: 100%;
padding-left: 0.5em;
border-left: solid;
border-left-width: 4px;
border-left-color: grey;
font: 14px/20px italic Times, serif;
background-color: #eefaf6; /*#f1ffee;*/
margin: 0;
.org-center {
text-align: center;}
}
svg { /* newer inline SVGs */
width: 100vw;
overflow: hidden;
height: auto;
margin-left: -5vw;
margin-right: 0;
}
.org-svg { /* non-inlined SVGs */
width: 100%;
max-height: 350px;
display: block;
margin-left: auto;
margin-right: auto;
}
img { /* static images */
width: 100%;
max-width: max-content;}
ul {padding-left: 1.5em;
border-left: solid;
border-left-width: 4px;
border-left-color: grey;}
h1 {color: #596060;}
h2 {color: black;
padding-top: 0.5em;
border-bottom: 4px solid #aaaaaa;}
h3 {color: black;
border-bottom: 2px dotted #aaaaaa;}
b, i{
font-family: serif ;}
sup { /* fixes line spacing issues due to superscripts */
font-size: 0.8em;
line-height: 0;
position: relative;
vertical-align: baseline;
top: -0.5em;
}
sub { /* if these are missing then lines with subscripts take up more space */
font-size: 0.8em;
line-height: 0;
}
</style>
</head>
<body>
<div id="content">
<h1 class="title">Displaying Maps in Clojure</h1>
<div id="table-of-contents">
<h2>Table of Contents</h2>
<div id="text-table-of-contents">
<ul>
<li><a href="#org1cd74f0">Intro</a>
<ul>
<li><a href="#org44cb9b6">Loading Libraries</a></li>
<li><a href="#orgcfe3f28">SVG Utils</a></li>
</ul>
</li>
<li><a href="#org931e15f">Shapefiles</a></li>
<li><a href="#org2f186f7">World Shorelines</a></li>
<li><a href="#org2286c2e">Regions of Interest</a></li>
<li><a href="#orgaa33eec">Conversion</a></li>
<li><a href="#orgb1421e2">Rain Data</a>
<ul>
<li><a href="#org6c1cd8a">GeoTIFF</a></li>
</ul>
</li>
<li><a href="#org84b0c38">Point of interest</a></li>
</ul>
</div>
</div>
<div id="outline-container-org1cd74f0" class="outline-2">
<h2 id="org1cd74f0">Intro</h2>
<div class="outline-text-2" id="text-org1cd74f0">
<p>
This is a demo of displaying a simple map using Clojure. It's done in a single-file orgmode document. For setup see: <a href="https://geokon-gh.github.io/literate-clojure.html">https://geokon-gh.github.io/literate-clojure.html</a>
</p>
</div>
<div id="outline-container-org44cb9b6" class="outline-3">
<h3 id="org44cb9b6">Loading Libraries</h3>
<div class="outline-text-3" id="text-org44cb9b6">
<p>
I use the <code>geo</code> library to work with GIS files and then I use <code>thing/geom</code> to display the result as an inline SVG
</p>
<div class="org-src-container">
<pre class="src src-clojure"> (use 'clojure.tools.deps.alpha.repl)
</pre>
</div>
<div class="org-src-container">
<pre class="src src-clojure"><span style="color: #ff0000;">;; </span><span style="color: #ff0000; font-style: italic;">Add libraries for plotting</span>
(add-libs {'<span style="color: #74747F; font-style: italic;">thi.ng</span>/geom {<span style="color: #61616A;">:</span><span style="color: #74747F; font-style: italic;">mvn</span><span style="color: #3C3C42; background-color: #F1F1FF;">/</span><span style="color: #61616A;">version</span> <span style="color: #74747F;">"1.0.0-RC4"</span>}})
(add-libs {'<span style="color: #74747F; font-style: italic;">thi.ng</span>/math {<span style="color: #61616A;">:</span><span style="color: #74747F; font-style: italic;">mvn</span><span style="color: #3C3C42; background-color: #F1F1FF;">/</span><span style="color: #61616A;">version</span> <span style="color: #74747F;">"0.3.0"</span>}})
(add-libs {'<span style="color: #74747F; font-style: italic;">thi.ng</span>/ndarray {<span style="color: #61616A;">:</span><span style="color: #74747F; font-style: italic;">mvn</span><span style="color: #3C3C42; background-color: #F1F1FF;">/</span><span style="color: #61616A;">version</span> <span style="color: #74747F;">"0.3.2"</span>}})
(add-libs {'<span style="color: #74747F; font-style: italic;">thi.ng</span>/color {<span style="color: #61616A;">:</span><span style="color: #74747F; font-style: italic;">mvn</span><span style="color: #3C3C42; background-color: #F1F1FF;">/</span><span style="color: #61616A;">version</span> <span style="color: #74747F;">"1.4.0"</span>}})
(add-libs {'<span style="color: #74747F; font-style: italic;">factual</span>/geo {<span style="color: #61616A;">:</span><span style="color: #74747F; font-style: italic;">mvn</span><span style="color: #3C3C42; background-color: #F1F1FF;">/</span><span style="color: #61616A;">version</span> <span style="color: #74747F;">"3.0.1"</span>}})
(add-libs {'<span style="color: #74747F; font-style: italic;">com.github.jai-imageio</span>/jai-imageio-core {<span style="color: #61616A;">:</span><span style="color: #74747F; font-style: italic;">mvn</span><span style="color: #3C3C42; background-color: #F1F1FF;">/</span><span style="color: #61616A;">version</span> <span style="color: #74747F;">"1.4.0"</span>}})
(add-libs {'<span style="color: #74747F; font-style: italic;">clj-curl</span>/clj-curl {<span style="color: #61616A;">:</span><span style="color: #74747F; font-style: italic;">mvn</span><span style="color: #3C3C42; background-color: #F1F1FF;">/</span><span style="color: #61616A;">version</span> <span style="color: #74747F;">"1.1.2"</span>}})
(add-libs {'<span style="color: #74747F; font-style: italic;">uncomplicate</span>/neanderthal {<span style="color: #61616A;">:</span><span style="color: #74747F; font-style: italic;">mvn</span><span style="color: #3C3C42; background-color: #F1F1FF;">/</span><span style="color: #61616A;">version</span> <span style="color: #74747F;">"0.43.3"</span>}})
</pre>
</div>
<div class="org-src-container">
<pre class="src src-clojure"><span style="color: #ff0000;">;; </span><span style="color: #ff0000; font-style: italic;">Add them to the default ~user~ namespace</span>
(require '[clojure.java.io <span style="color: #61616A;">:as</span> io]
'(thi.ng.geom [core <span style="color: #61616A;">:as</span> geom]
[matrix <span style="color: #61616A;">:as</span> matrix])
'[thi.ng.geom.viz.core <span style="color: #61616A;">:as</span> viz]
'[thi.ng.geom.svg.core <span style="color: #61616A;">:as</span> svg]
'[thi.ng.math.core <span style="color: #61616A;">:as</span> math]
'[thi.ng.ndarray.core <span style="color: #61616A;">:as</span> ndarray]
'[thi.ng.color.core <span style="color: #61616A;">:as</span> col]
'[thi.ng.math.noise <span style="color: #61616A;">:as</span> noise]
'[clj-curl.easy <span style="color: #61616A;">:as</span> curl-easy]
'[clj-curl.opts <span style="color: #61616A;">:as</span> curl-opts])
(use 'geo.io)
(use 'geo.jts)
(use '[uncomplicate.neanderthal core native])
</pre>
</div>
</div>
</div>
<div id="outline-container-orgcfe3f28" class="outline-3">
<h3 id="orgcfe3f28">SVG Utils</h3>
<div class="outline-text-3" id="text-orgcfe3f28">
<div class="org-src-container">
<pre class="src src-clojure"> (<span style="color: #74747F;">defn</span> <span style="color: #3C3C42;">svg2org</span>
<span style="color: #ff0000; font-style: italic;">"Takes the SVG hiccup,</span>
<span style="color: #ff0000; font-style: italic;"> - Serializes it to XML</span>
<span style="color: #ff0000; font-style: italic;"> - Breaks it up along XML nodes</span>
<span style="color: #ff0000; font-style: italic;"> - outputs as symbols that can be output to org</span>
<span style="color: #ff0000; font-style: italic;"> The result can then be exported to HTML"</span>
[svg-hiccup]
(<span style="color: #74747F;">-></span> svg-hiccup
<span style="color: #74747F; font-style: italic;">svg</span>/serialize
(#(<span style="color: #74747F; font-style: italic;">clojure.string</span>/replace <span style="color: #252528;">%</span>
#<span style="color: #74747F;">"><"</span>
<span style="color: #74747F;">"></span><span style="color: #74747F; font-weight: bold;">\n</span><span style="color: #74747F;"><"</span>))
symbol))
</pre>
</div>
<pre class="example">
#'user/svg2org
</pre>
</div>
</div>
</div>
<div id="outline-container-org931e15f" class="outline-2">
<h2 id="org931e15f">Shapefiles</h2>
<div class="outline-text-2" id="text-org931e15f">
<p>
A lot of geographic data is in <b>shapefiles</b>
</p>
<p>
However the <code>geo</code> library operates with GeoJSON files. These are much larger in size, but easier to manage
</p>
<p>
The <code>geo</code> maintainer explains in this talk how shapefiles are difficult to read: <a href="https://www.youtube.com/watch?v=d628Oggm-nU">https://www.youtube.com/watch?v=d628Oggm-nU</a>
</p>
<p>
(Maybe they can be read in through some secondary library?)
</p>
</div>
</div>
<div id="outline-container-org2f186f7" class="outline-2">
<h2 id="org2f186f7">World Shorelines</h2>
<div class="outline-text-2" id="text-org2f186f7">
<p>
For a base layer on which to build, it'd be useful to simply display the world map. To do that we display the global shorlines. Shorelines of the world can be downloaded from <a href="https://www.ngdc.noaa.gov/mgg/shorelines/">https://www.ngdc.noaa.gov/mgg/shorelines/</a>
Or also from <a href="https://www.soest.hawaii.edu/pwessel/gshhg/">https://www.soest.hawaii.edu/pwessel/gshhg/</a> (I think these two are actually the same)
</p>
<p>
You should get the <b>ESRI shapefile</b> version. The other formats (like NetCDF) seem to be even more complex.
</p>
<p>
The shorelines come at multiple resolutions and are split into different groupings. The README explains what the different files contain.
</p>
</div>
</div>
<div id="outline-container-org2286c2e" class="outline-2">
<h2 id="org2286c2e">Regions of Interest</h2>
<div class="outline-text-2" id="text-org2286c2e">
<p>
We probably want to first designate some regions of interest
</p>
<div class="org-src-container">
<pre class="src src-clojure">(<span style="color: #74747F;">def</span> <span style="color: #252528;">two-seas-region</span> {<span style="color: #61616A;">:start-lat</span> -5.9
<span style="color: #61616A;">:start-lon</span> 85
<span style="color: #61616A;">:ended-lat</span> 23
<span style="color: #61616A;">:ended-lon</span> 119})
(<span style="color: #74747F;">def</span> <span style="color: #252528;">krabi-region</span> {<span style="color: #61616A;">:start-lat</span> 6
<span style="color: #61616A;">:start-lon</span> 95
<span style="color: #61616A;">:ended-lat</span> 11
<span style="color: #61616A;">:ended-lon</span> 103})
(<span style="color: #74747F;">def</span> <span style="color: #252528;">taiwan-region</span> {<span style="color: #61616A;">:start-lat</span> 21.7
<span style="color: #61616A;">:start-lon</span> 119.7
<span style="color: #61616A;">:ended-lat</span> 25.4
<span style="color: #61616A;">:ended-lon</span> 122.3})
(<span style="color: #74747F;">def</span> <span style="color: #252528;">minnan-region</span> {<span style="color: #61616A;">:start-lat</span> 21.7
<span style="color: #61616A;">:start-lon</span> 116.5
<span style="color: #61616A;">:ended-lat</span> 26.0
<span style="color: #61616A;">:ended-lon</span> 125})
<span style="color: #ff0000;">;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;</span>
(<span style="color: #74747F;">def</span> <span style="color: #252528;">breaks-things</span> {<span style="color: #61616A;">:start-lat</span> 5 <span style="color: #ff0000;">;; </span><span style="color: #ff0000; font-style: italic;">creates a Point.. which I don't handle</span>
<span style="color: #61616A;">:start-lon</span> 85
<span style="color: #61616A;">:ended-lat</span> 15
<span style="color: #61616A;">:ended-lon</span> 119})
</pre>
</div>
</div>
</div>
<div id="outline-container-orgaa33eec" class="outline-2">
<h2 id="orgaa33eec">Conversion</h2>
<div class="outline-text-2" id="text-orgaa33eec">
<p>
Now we need to convert the shapefile to a GeoJSON. Unfortunately I'm not sure this can be done within the JVM. However there is a command line tool called <code>ogr2org</code> that will do this automatically for you. It's part of <b>GDAL</b>
</p>
<p>
<a href="https://gdal.org/programs/ogr2ogr.html">https://gdal.org/programs/ogr2ogr.html</a>
</p>
<p>
The tool docs don't directly mention GeoJSON for some reason, but you can find conversion one liners on the GeoJSON page
</p>
<p>
<a href="https://gdal.org/drivers/vector/geojson.html">https://gdal.org/drivers/vector/geojson.html</a>
</p>
<p>
For instance, I went into the shoreline subdirectory <code>GSHHS_shp/c</code> and ran
</p>
<div class="org-src-container">
<pre class="src src-shell">ogr2ogr -f GeoJSON shoreline.json GSHHS_i_L1.shp
</pre>
</div>
<p>
This gives you a GeoJSON of the shorelines of all the continents (this seem to also include small periheral islands as well). I loaded both in QGIS and at least to my eye they look the same.
</p>
<p>
To display them ourselves we can create a processing "pipeline"
</p>
<ul class="org-ul">
<li>construct a "cropping polygon" which bounds our region of interest</li>
<li>use the <code>intersect</code> function in <code>geo/jts</code> to crop out the region of interest and get back a GeoJSON</li>
<li>flatten the GeoJSON/region's geometries into a bare list of polygons.</li>
</ul>
<blockquote>
<p>
Sometimes <code>intersect</code> will cut a polygon into multiple polygons, and these will be grouped into a MultiPolygon object. You can also get GeometryCollection objects (unclear when). These too should be "unfolded". So that you are left with a long list of Polygons
</p>
<p>
<b>TODO</b>: There are still some corner cases where you get a <b>Point</b> instead of a Polygon. Or other stranger geometries that I am not handeling at the moment.
</p>
</blockquote>
<ul class="org-ul">
<li>Extract all the Polygons' coordinates and use them directly to create <code>thing/geom</code> SVG hiccup</li>
<li>The SVG hiccup can then be serialized and output as an SVG string (embedded in the HTML)</li>
</ul>
<blockquote>
<p>
There are some coordinate adjustments to rezero and flip the image
</p>
</blockquote>
<div class="org-src-container">
<pre class="src src-clojure">(<span style="color: #74747F;">defn</span> <span style="color: #3C3C42;">region-ranges</span>
[region]
(<span style="color: #74747F;">let</span> [{<span style="color: #61616A;">:keys</span> [start-lat
start-lon
ended-lat
ended-lon]} region]
[(<span style="color: #74747F; font-style: italic;">Math</span>/abs (- ended-lat
start-lat))
(<span style="color: #74747F; font-style: italic;">Math</span>/abs(- ended-lon
start-lon))]))
(<span style="color: #74747F;">defn</span> <span style="color: #3C3C42;">crop-shoreline-to-region</span>
[shoreline
region]
(<span style="color: #74747F;">let</span> [{<span style="color: #61616A;">:keys</span> [start-lat
start-lon
ended-lat
ended-lon]} region
[lat-range
lon-range] (region-ranges region)
area-of-interest (polygon-wkt [[start-lon start-lat
start-lon ended-lat
ended-lon ended-lat
ended-lon start-lat
start-lon start-lat]])]
(<span style="color: #74747F;">->></span> shoreline
(map (<span style="color: #74747F;">fn</span> [shoreline-poly]
(update shoreline-poly
<span style="color: #61616A;">:geometry</span>
#(intersection <span style="color: #252528;">%</span>
area-of-interest))))
(filter #(not (.isEmpty (<span style="color: #61616A;">:geometry</span> <span style="color: #252528;">%</span>)))))))
<span style="color: #ff0000;">;; </span><span style="color: #ff0000; font-style: italic;">(->> included-shorelines</span>
<span style="color: #ff0000;">;; </span><span style="color: #ff0000; font-style: italic;">to-geojson-feature-collection</span>
<span style="color: #ff0000;">;; </span><span style="color: #ff0000; font-style: italic;">(spit "trimmed-shorelines.json"))</span>
(<span style="color: #74747F;">defn</span> <span style="color: #3C3C42;">geojson-to-svg</span>
[geojson
region
attribs]
(<span style="color: #74747F;">let</span> [{<span style="color: #61616A;">:keys</span> [start-lat
start-lon
ended-lat
ended-lon]} region
[lat-range
lon-range] (region-ranges region)]
(<span style="color: #74747F;">->></span> geojson
(map <span style="color: #61616A;">:geometry</span>)
(reduce (<span style="color: #74747F;">fn</span> [polygon-seq <span style="color: #ff0000;">;; </span><span style="color: #ff0000; font-style: italic;">extract polygons from multipolygons</span>
next-geometry]
(println (.getGeometryType next-geometry))
(<span style="color: #74747F;">case</span> (.getGeometryType next-geometry)
<span style="color: #74747F;">"Polygon"</span>
(conj polygon-seq next-geometry)
<span style="color: #74747F;">"MultiPolygon"</span>
(apply conj polygon-seq (polygons next-geometry))
<span style="color: #74747F;">"GeometryCollection"</span> <span style="color: #ff0000;">;; </span><span style="color: #ff0000; font-style: italic;">This probably need to be recursive</span>
(apply conj polygon-seq (geometries next-geometry))
))
[])
(map coordinates)
(map coord-array)
(map (<span style="color: #74747F;">fn</span> [poly]
(map #(vector (- (.getX <span style="color: #252528;">%</span>) <span style="color: #ff0000;">;; </span><span style="color: #ff0000; font-style: italic;">zero to starting longitude</span>
start-lon)
(- lat-range <span style="color: #ff0000;">;; </span><span style="color: #ff0000; font-style: italic;">SVG's Y axis is inverted</span>
(- (.getY <span style="color: #252528;">%</span>) <span style="color: #ff0000;">;; </span><span style="color: #ff0000; font-style: italic;">zero to starting latitude</span>
start-lat)))
poly)))
(map #(<span style="color: #74747F; font-style: italic;">svg</span>/line-strip <span style="color: #252528;">%</span>
attribs))
(apply <span style="color: #74747F; font-style: italic;">svg</span>/group {}))))
(<span style="color: #74747F;">defn</span> <span style="color: #3C3C42;">plot-shoreline</span>
<span style="color: #ff0000; font-style: italic;">"blah blah"</span>
[shoreline-file
region]
(<span style="color: #74747F;">-></span> shoreline-file
slurp
read-geojson
(crop-shoreline-to-region region)
(geojson-to-svg region
{<span style="color: #61616A;">:stroke-width</span> <span style="color: #74747F;">"0.05px"</span>
<span style="color: #61616A;">:stroke</span> <span style="color: #74747F;">"black"</span>})))
(<span style="color: #74747F;">defn</span> <span style="color: #3C3C42;">plot-to-svg</span>
[plot
[lat-height
lon-width]]
(<span style="color: #74747F;">->></span> plot
(<span style="color: #74747F; font-style: italic;">svg</span>/svg {<span style="color: #61616A;">:width</span> lon-width
<span style="color: #61616A;">:height</span> lat-height
<span style="color: #61616A;">:viewBox</span> (str <span style="color: #74747F;">"0 0 "</span>
lon-width
<span style="color: #74747F;">" "</span>
lat-height)})
<span style="color: #74747F; font-style: italic;">svg</span>/serialize
(#(<span style="color: #74747F; font-style: italic;">clojure.string</span>/replace <span style="color: #252528;">%</span>
#<span style="color: #74747F;">"><"</span> <span style="color: #74747F;">"></span><span style="color: #74747F; font-weight: bold;">\n</span><span style="color: #74747F;"><"</span>))))
</pre>
</div>
<p>
A minimal example is cropping out the island of Taiwan
</p>
<div class="org-src-container">
<pre class="src src-clojure">(<span style="color: #74747F;">->></span> (plot-to-svg (plot-shoreline <span style="color: #74747F;">"/home/geokon/Downloads/coast/GSHHS_shp/l/shoreline.json"</span>
minnan-region)
(region-ranges minnan-region))
symbol)
</pre>
</div>
<?xml version="1.0"?>
<svg height="4.300000000000001" version="1.1" viewBox="0 0 8.5 4.300000000000001" width="8.5" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg">
<g>
<polyline fill="none" points="3.22,0.00 3.18,0.10 3.07,0.12 3.10,0.31 2.94,0.30 3.00,0.44 3.10,0.41 3.05,0.50 3.14,0.52 3.15,0.64 3.09,0.66 3.09,0.54 2.99,0.63 2.96,0.49 2.87,0.47 2.83,0.39 2.59,0.58 2.72,0.64 2.71,0.73 2.75,0.67 2.87,0.73 2.78,0.83 2.63,0.73 2.59,0.81 2.66,0.87 2.56,0.88 2.58,0.74 2.48,0.77 2.57,0.71 2.37,0.75 2.48,0.80 2.47,0.88 2.35,0.91 2.45,0.99 2.52,0.95 2.52,1.04 2.40,1.06 2.49,1.12 2.20,1.15 2.24,1.09 2.14,1.01 2.17,1.13 2.06,1.09 2.13,1.23 2.28,1.23 2.15,1.32 2.15,1.44 2.07,1.49 2.09,1.42 1.94,1.37 1.94,1.27 1.92,1.40 1.77,1.39 1.74,1.46 1.69,1.32 1.60,1.43 1.55,1.38 1.55,1.44 1.47,1.44 1.57,1.49 1.54,1.55 1.28,1.49 1.39,1.66 1.56,1.59 1.51,1.68 1.60,1.64 1.63,1.74 1.47,1.85 1.44,1.78 1.40,1.96 1.36,1.93 1.27,2.09 1.22,2.06 1.29,1.97 1.19,1.95 1.09,2.28 1.07,2.10 0.85,2.05 0.98,2.09 0.99,2.17 0.93,2.24 0.77,2.22 0.74,2.40 0.69,2.32 0.72,2.38 0.58,2.46 0.56,2.35 0.44,2.39 0.42,2.34 0.33,2.44 0.39,2.44 0.31,2.45 0.38,2.45 0.31,2.50 0.37,2.53 0.29,2.53 0.36,2.59 0.20,2.50 0.30,2.58 0.26,2.66 0.00,2.57 0.00,0.00 3.22,0.00" stroke-width="0.05px" stroke="black" />
<polyline fill="none" points="0.00,2.58 0.24,2.75 0.14,2.84 0.00,2.75 0.00,2.58" stroke-width="0.05px" stroke="black" />
<polyline fill="none" points="0.00,2.79 0.01,2.80 0.08,2.81 0.00,3.05 0.00,2.79" stroke-width="0.05px" stroke="black" />
<polyline fill="none" points="4.36,4.00 4.36,4.00 4.36,4.10 4.21,4.06 4.13,3.69 3.83,3.46 3.63,3.01 3.68,2.96 3.53,2.92 3.62,2.89 3.57,2.83 3.66,2.67 3.63,2.37 3.73,2.18 3.87,2.17 3.76,2.15 3.82,2.01 4.01,1.82 4.21,1.39 4.35,1.33 4.53,0.97 4.94,0.84 4.94,0.75 5.07,0.70 5.24,0.87 5.42,0.87 5.50,0.99 5.32,1.14 5.36,1.52 5.11,1.91 4.91,2.88 4.47,3.44 4.36,4.00" stroke-width="0.05px" stroke="black" />
<polyline fill="none" points="7.16,1.70 7.16,1.70 7.21,1.66 7.25,1.71 7.27,1.57 7.44,1.65 7.38,1.75 7.16,1.70" stroke-width="0.05px" stroke="black" />
<polyline fill="none" points="3.17,0.52 3.17,0.52 3.28,0.49 3.19,0.40 3.29,0.33 3.39,0.43 3.30,0.48 3.35,0.54 3.27,0.60 3.23,0.50 3.17,0.52" stroke-width="0.05px" stroke="black" />
<polyline fill="none" points="7.57,1.58 7.57,1.57 7.72,1.55 7.84,1.40 7.74,1.66 7.57,1.58" stroke-width="0.05px" stroke="black" />
<polyline fill="none" points="0.81,2.43 0.81,2.42 0.92,2.33 0.83,2.32 0.88,2.24 1.03,2.27 0.92,2.35 0.93,2.42 0.81,2.43" stroke-width="0.05px" stroke="black" />
<polyline fill="none" points="1.78,1.59 1.78,1.58 1.90,1.47 1.97,1.52 1.93,1.59 1.78,1.59" stroke-width="0.05px" stroke="black" />
<polyline fill="none" points="1.56,1.54 1.56,1.54 1.60,1.45 1.69,1.47 1.63,1.58 1.56,1.54" stroke-width="0.05px" stroke="black" />
<polyline fill="none" points="0.50,2.58 0.49,2.58 0.44,2.57 0.65,2.56 0.62,2.61 0.50,2.58" stroke-width="0.05px" stroke="black" />
<polyline fill="none" points="0.14,2.68 0.14,2.68 0.25,2.67 0.29,2.79 0.14,2.68" stroke-width="0.05px" stroke="black" />
<polyline fill="none" points="2.77,0.53 2.77,0.53 2.84,0.45 2.85,0.56 2.77,0.53" stroke-width="0.05px" stroke="black" />
<polyline fill="none" points="3.03,2.46 3.03,2.45 3.05,2.41 3.12,2.39 3.19,2.44 3.03,2.46" stroke-width="0.05px" stroke="black" />
<polyline fill="none" points="4.99,3.92 4.99,3.92 5.06,3.91 5.09,4.00 4.99,3.92" stroke-width="0.05px" stroke="black" />
<polyline fill="none" points="2.94,0.77 2.94,0.77 3.07,0.80 3.03,0.84 2.94,0.77" stroke-width="0.05px" stroke="black" />
<polyline fill="none" points="6.50,1.57 6.50,1.56 6.43,1.55 6.55,1.54 6.50,1.57" stroke-width="0.05px" stroke="black" />
<polyline fill="none" points="0.50,2.45 0.49,2.46 0.43,2.46 0.50,2.45" stroke-width="0.05px" stroke="black" />
<polyline fill="none" points="8.17,1.35 8.17,1.34 8.23,1.35 8.17,1.35" stroke-width="0.05px" stroke="black" />
<polyline fill="none" points="2.96,2.44 2.96,2.44 3.03,2.36 2.96,2.44" stroke-width="0.05px" stroke="black" />
<polyline fill="none" points="0.42,2.45 0.42,2.44 0.47,2.40 0.42,2.45" stroke-width="0.05px" stroke="black" />
<polyline fill="none" points="1.71,1.59 1.71,1.59 1.77,1.56 1.71,1.59" stroke-width="0.05px" stroke="black" />
<polyline fill="none" points="2.59,0.95 2.59,0.95 2.66,0.89 2.59,0.95" stroke-width="0.05px" stroke="black" />
<polyline fill="none" points="3.04,2.34 3.04,2.34 3.10,2.37 3.04,2.34" stroke-width="0.05px" stroke="black" />
<polyline fill="none" points="4.96,3.33 4.96,3.32 5.00,3.37 4.96,3.33" stroke-width="0.05px" stroke="black" />
<polyline fill="none" points="7.25,1.95 7.25,1.95 7.31,1.94 7.25,1.95" stroke-width="0.05px" stroke="black" />
<polyline fill="none" points="3.16,0.35 3.16,0.35 3.22,0.34 3.16,0.35" stroke-width="0.05px" stroke="black" />
<polyline fill="none" points="3.06,0.36 3.06,0.36 3.11,0.33 3.06,0.36" stroke-width="0.05px" stroke="black" />
<polyline fill="none" points="3.51,2.52 3.51,2.52 3.54,2.46 3.51,2.52" stroke-width="0.05px" stroke="black" />
<polyline fill="none" points="3.54,2.88 3.54,2.88 3.55,2.83 3.54,2.88" stroke-width="0.05px" stroke="black" />
</g>
</svg>
</div>
</div>
<div id="outline-container-orgb1421e2" class="outline-2">
<h2 id="orgb1421e2">Rain Data</h2>
<div class="outline-text-2" id="text-orgb1421e2">
<p>
Now we want to read in rain data and overlay it. Rain data can be retrieved from <code>arthurhou.pps.eosdis.nasa.gov</code> and required an account.
</p>
<p>
The path on the server will look something like this:
</p>
<p>
<code>ftp://arthurhou.pps.eosdis.nasa.gov/sm/730/gpmdata/2011/08/01/gis/3B-MO-GIS.MS.MRG.3IMERG.20110801-S000000-E235959.08.V06B.tif</code>
</p>
<p>
And in depth explanation of the data is in a PDF here: <a href="https://gpm.nasa.gov/resources/documents/imerg-geotiff-worldfile-documentation">https://gpm.nasa.gov/resources/documents/imerg-geotiff-worldfile-documentation</a>
</p>
<p>
While the document focuses on the GeoTIFF format, it also provides all the necessary background to understand how everything fits together. It's a very good write up. Here I just provide a very short summary.
</p>
<p>
Looking at the file/path in parts:
</p>
<ul class="org-ul">
<li><code>sm/730/</code> unclear</li>
<li><code>gpmdata/</code> GPM is the current satellite system that synthesizes different data to give these precipitation maps</li>
<li>The <code>gis</code> in the path means GeoTIFF - b/c the original meteorology format is "HDF5" and it's not GIS software-friendly</li>
<li>The folders are organized by days. Each day's folder will have 48 half-hour snapshots of the precipitation. These come in a format that looks like 3B-HHR-…</li>
<li>Each day also has a cumulative GeoTiff, of all the precipitation for that day. This file has a slightly different name 3B-DAY-…</li>
<li>The monthly precipitation is hidden in the first day of the month's folder :). Its format will be 3B-MO-… All the other days of the month won't have this file.</li>
</ul>
<p>
The data can then be downloaded using CURL. The username and passwords provided by the service is very inconvenient b/c they contain a <code>@</code> symbol, so you need to pass them in explicitely using the <code>--user</code> flag:
</p>
<div class="org-src-container">
<pre class="src src-shell">curl -4 --ftp-ssl --user <<email>>:<<email>> ftp://arthurhouftps.pps.eosdis.nasa.gov/sm/730/gpmdata/2011/08/01/gis/3B-MO-GIS.MS.MRG.3IMERG.20110801-S000000-E235959.08.V06B.tif -o output.tif
</pre>
</div>
<p>
We can do the same programmatically in Clojure
</p>
<div class="org-src-container">
<pre class="src src-clojure"><span style="color: #ff0000;">;; </span><span style="color: #ff0000; font-style: italic;">Based off of libary example:</span>
<span style="color: #ff0000;">;; </span><span style="color: #ff0000; font-style: italic;">https://github.com/lsevero/clj-curl/blob/master/examples/simple_ftp.clj</span>
(import (clj_curl.Handlers FileHandler))
(<span style="color: #74747F;">defn</span> <span style="color: #3C3C42;">download-rain</span>
<span style="color: #ff0000; font-style: italic;">"Downloads precipitation GeoTIFFs from `</span><span style="color: #61616A; font-style: italic;">arthurhou.pps.eosdis.nasa.gov</span><span style="color: #ff0000; font-style: italic;">`"</span>
[file-path
file-name
save-directory]
(<span style="color: #74747F;">let</span> [file-url (str <span style="color: #74747F;">"ftp://arthurhou.pps.eosdis.nasa.gov"</span>
file-path
file-name)
save-file (str save-directory
file-name)]
<span style="color: #ff0000;">;;</span><span style="color: #ff0000; font-style: italic;">FileHandler needs to be closed after you finished to use it.</span>
<span style="color: #ff0000;">;;</span><span style="color: #ff0000; font-style: italic;">creating it with 'with-open' does that automatically for you.</span>
(<span style="color: #74747F;">with-open</span> [filehldr (FileHandler. save-file)]
(<span style="color: #74747F;">let</span> [curl (<span style="color: #74747F; font-style: italic;">curl-easy</span>/init)]
(<span style="color: #74747F;">do</span>
(println <span style="color: #74747F;">"simple ftp"</span>)
(<span style="color: #74747F; font-style: italic;">curl-easy</span>/setopt curl
<span style="color: #74747F; font-style: italic;">curl-opts</span>/url
file-url)
(<span style="color: #74747F; font-style: italic;">curl-easy</span>/setopt curl
<span style="color: #74747F; font-style: italic;">curl-opts</span>/writefunction
filehldr)
(<span style="color: #74747F; font-style: italic;">curl-easy</span>/setopt curl
<span style="color: #74747F; font-style: italic;">curl-opts</span>/password
<span style="color: #74747F;">"[email protected]"</span>)
(<span style="color: #74747F; font-style: italic;">curl-easy</span>/setopt curl
<span style="color: #74747F; font-style: italic;">curl-opts</span>/username
<span style="color: #74747F;">"[email protected]"</span>)
(<span style="color: #74747F; font-style: italic;">curl-easy</span>/setopt curl
<span style="color: #74747F; font-style: italic;">curl-opts</span>/use-ssl
1) <span style="color: #ff0000;">;; </span><span style="color: #ff0000; font-style: italic;">magic value from curl header</span>
<span style="color: #ff0000;">;; </span><span style="color: #ff0000; font-style: italic;">https://github.com/curl/curl/blob/master/include/curl/curl.h#L875</span>
(<span style="color: #74747F; font-style: italic;">curl-easy</span>/perform curl)
(println <span style="color: #74747F;">"speed download: "</span>
(<span style="color: #74747F; font-style: italic;">curl-easy</span>/getinfo-double curl
<span style="color: #74747F; font-style: italic;">curl-opts</span>/speed-download))
(<span style="color: #74747F; font-style: italic;">curl-easy</span>/cleanup curl))))))
</pre>
</div>
<p>
Now to download a file we just run
</p>
<div class="org-src-container">
<pre class="src src-clojure">(download-rain <span style="color: #74747F;">"/sm/730/gpmdata/2011/08/01/gis/"</span>
<span style="color: #74747F;">"3B-MO-GIS.MS.MRG.3IMERG.20110801-S000000-E235959.08.V06B.tif"</span>
<span style="color: #74747F;">"/home/geokon/Junk/"</span>)
</pre>
</div>
<p>
To download every month's average from 2011 to 2020 we just need to generate the appropriate paths and file names
</p>
<div class="org-src-container">
<pre class="src src-clojure">(<span style="color: #74747F;">let</span> [years (range 2011 2021) <span style="color: #ff0000;">;; </span><span style="color: #ff0000; font-style: italic;">range is no inclusive of the `</span><span style="color: #61616A; font-style: italic;">end</span><span style="color: #ff0000; font-style: italic;">` term</span>
months [<span style="color: #74747F;">"01"</span> <span style="color: #74747F;">"02"</span> <span style="color: #74747F;">"03"</span> <span style="color: #74747F;">"04"</span> <span style="color: #74747F;">"05"</span> <span style="color: #74747F;">"06"</span> <span style="color: #74747F;">"07"</span> <span style="color: #74747F;">"08"</span> <span style="color: #74747F;">"09"</span> <span style="color: #74747F;">"10"</span> <span style="color: #74747F;">"11"</span> <span style="color: #74747F;">"12"</span>]
paths (flatten (mapv (<span style="color: #74747F;">fn</span> [year]
(mapv (<span style="color: #74747F;">fn</span> [month]
(str <span style="color: #74747F;">"/sm/730/gpmdata/"</span>
year
<span style="color: #74747F;">"/"</span>
month
<span style="color: #74747F;">"/01/gis/"</span>))
months))
years))
files (flatten (mapv (<span style="color: #74747F;">fn</span> [year]
(mapv (<span style="color: #74747F;">fn</span> [month]
(str <span style="color: #74747F;">"3B-MO-GIS.MS.MRG.3IMERG."</span>
year
month
<span style="color: #74747F;">"01-S000000-E235959."</span>
month
<span style="color: #74747F;">".V06B.tif"</span>))
months))
years))]
(<span style="color: #74747F;">dorun</span> (map (<span style="color: #74747F;">fn</span> [path
file]
(download-rain path
file
<span style="color: #74747F;">"/home/geokon/Junk/alldata/"</span>))
paths
files)))
</pre>
</div>
<p>
You will notice the file extension is <code>.tif</code>. Rain data arrived in two primary formats, HDF5 and GeoTIFF.
</p>
<dl class="org-dl">
<dt>HDF5</dt><dd>is a more meteorologically specific format. This is how the data arrives orginally</dd>
<dt>GeoTiff</dt><dd>is an additional format provided. It's an image file along with some metadata</dd>
</dl>
</div>
<div id="outline-container-org6c1cd8a" class="outline-3">
<h3 id="org6c1cd8a">GeoTIFF</h3>
<div class="outline-text-3" id="text-org6c1cd8a">
<p>
We can read in TIFF files directly in Java as long as <a href="https://docs.oracle.com/javase/9/docs/api/javax/imageio/package-summary.html">it's Java 9 or newer</a>. On older versions of Java you need to <a href="https://github.com/jai-imageio/jai-imageio-core">use a library/plugin</a> that adds support.
</p>
<p>
The files provide precipitation data as a grayscale image of the whole globe. The higher the value of a pixel, the more mm of precipitation. In these particular files each pixel represents a <code>0.1 x 0.1</code> degree square. You have 1800 vertically and 3600 pixels horizontally - corresponding to the 180° of latitude and 360° of longitude.
</p>
<p>
The image is 16bit and single channel. This is a bit unusual and Java only supports these <code>unsigned short</code> (ie. 16bit positive numbers) values indirectly. If you start looking at a BufferedImage DataBuffer you will see what looks like an array of <code>signed short</code> values that range from -32767 to 32767. But we will want them as <code>unsinged</code> ranging from 0 to 65535. The <a href="https://stackoverflow.com/questions/3153787/how-do-i-print-a-short-as-an-unsigned-short-in-java">easiest way</a> is to use a helper function provided by Java, <code>Short.toUnsignedInt</code>, that will extract the value as if it were an <code>unsigned short</code> and copy it into an Int. We can then go through and copy out the whole buffer into a buffer of Ints before displaying them (we're only looking at a small region anyway)
</p>
<blockquote>
<p>
<b>Coordinate Chaos</b>
</p>
<ul class="org-ul">
<li>A Java BufferedImage will naturally start with the <code>[0,0]</code> in the upper left-hand corner. This is the most common format for dealing with Images (and corresponds well to how you display things in a window - with the "root" always being the window's upper left-hand corner).</li>
<li>Geographic coordinates (such as in our GeoJSON files) come in <code>[lat,long]</code> pairs. The <code>[0,0]</code> point in the center (off the coast of Africa). Furthermore <code>lat</code> is the "height" on the map, while <code>long</code> is effectively the "width". This is the inverse of the usual ordering of <code>[width,height]</code>.</li>
<li>Plots usually are drawn with <code>[0,0]</code> in the bottom left corner. (here I use a "heatmap" plot to display the rain "grid" data)</li>
</ul>
</blockquote>
<p>
The code provides a pipeline to, crop out the region of interest, convert it to a matrix on integers, plot it as "heatmap" style svg image. Intensity is varied by changing each grid cell's luminosity
</p>
<blockquote>
<p>
<b>TODO</b>: see if I can instead modify the alpha transparancy - would make compositing plots easier
</p>
</blockquote>
<div class="org-src-container">
<pre class="src src-clojure">(<span style="color: #74747F;">defn</span> <span style="color: #3C3C42;">geotiff-subregion</span>
<span style="color: #ff0000; font-style: italic;">"Extract a subregion from a GeoTIFF</span>
<span style="color: #ff0000; font-style: italic;"> The GeoTIFF must cover the whole globe</span>
<span style="color: #ff0000; font-style: italic;"> The REGION is described as a vector:</span>
<span style="color: #ff0000; font-style: italic;"> [start-lat</span>
<span style="color: #ff0000; font-style: italic;"> start-long</span>
<span style="color: #ff0000; font-style: italic;"> ended-lat</span>
<span style="color: #ff0000; font-style: italic;"> ended-lon]</span>
<span style="color: #ff0000; font-style: italic;"> The result is a deep copy of the subregion in to a new `</span><span style="color: #61616A; font-style: italic;">BufferedImage</span><span style="color: #ff0000; font-style: italic;">`"</span>
[geotiff
[lat-res
lon-res]
region]
(<span style="color: #74747F;">let</span> [<span style="color: #ff0000;">;; </span><span style="color: #ff0000; font-style: italic;">A lat-lon region is a square from bottom left to upper right corner</span>
{<span style="color: #61616A;">:keys</span> [start-lat
start-lon
ended-lat
ended-lon]} region
pix-per-deg-lat (/ 1.0
lat-res)
pix-per-deg-lon (/ 1.0
lon-res)
<span style="color: #ff0000;">;; </span><span style="color: #ff0000; font-style: italic;">An image region is top-left to bottom-right pixel</span>
start-pix-x (* (+ 180
start-lon)
pix-per-deg-lon)
start-pix-y (* (- 90
ended-lat)
pix-per-deg-lat)
width-pix (- (* (+ 180
ended-lon)
pix-per-deg-lon)
start-pix-x)
height-pix (- (* (- 90
start-lat)
pix-per-deg-lat)
start-pix-y)]
<span style="color: #ff0000;">;; </span><span style="color: #ff0000; font-style: italic;">This could be done without a deep copy..</span>
<span style="color: #ff0000;">;; </span><span style="color: #ff0000; font-style: italic;">But then you'd need to be more careful when getting the pixel at a later stage</span>
<span style="color: #ff0000;">;; </span><span style="color: #ff0000; font-style: italic;">b/c the databuffer will still be from the larger image ..</span>
<span style="color: #ff0000;">;; </span><span style="color: #ff0000; font-style: italic;">The deep copy is done according to this:</span>
<span style="color: #ff0000;">;; </span><span style="color: #ff0000; font-style: italic;">https://stackoverflow.com/a/36475402/7024671</span>
(<span style="color: #74747F;">-></span> geotiff
(.getData (java.awt.Rectangle. start-pix-x
start-pix-y
width-pix
height-pix))
(.createWritableTranslatedChild 0 0)
(#(java.awt.image.BufferedImage. (.getColorModel geotiff)
<span style="color: #252528;">%</span>
(.isAlphaPremultiplied geotiff)
<span style="color: #61616A;">nil</span>)))))
(<span style="color: #74747F;">defn</span> <span style="color: #3C3C42;">geotiff-to-matrix</span>
<span style="color: #ff0000; font-style: italic;">"Take a GeoTIFF and convert it to a `</span><span style="color: #61616A; font-style: italic;">thing/ndarray</span><span style="color: #ff0000; font-style: italic;">` matrix</span>
<span style="color: #ff0000; font-style: italic;"> This is the format expected by the heatmap function"</span>
[geotiff]
(<span style="color: #74747F;">let</span> [int-buffer (<span style="color: #74747F;">->></span> geotiff
.getData
.getDataBuffer
.getData
(mapv #(<span style="color: #74747F; font-style: italic;">Short</span>/toUnsignedInt <span style="color: #252528;">%</span>)))
max-value (apply max int-buffer)
normalized-buffer (mapv #(* (/ 65535
max-value)
<span style="color: #252528;">%</span>)
int-buffer)]
(<span style="color: #74747F;">->></span> normalized-buffer
(#(<span style="color: #74747F; font-style: italic;">ndarray</span>/ndarray <span style="color: #61616A;">:int32</span>
<span style="color: #252528;">%</span>
[(.getHeight geotiff)
(.getWidth geotiff)])))))
(<span style="color: #74747F;">def</span> <span style="color: #252528;">grey-scale-for-shorts</span>
<span style="color: #ff0000; font-style: italic;">"A grayscale linear color map - over all possible `unsigned short` values</span>
<span style="color: #ff0000; font-style: italic;"> see: https://colorcet.com/userguide/index.html"</span>
(<span style="color: #74747F;">->></span> (range 1
0
(- (/ 1
65535)))
(mapv #(<span style="color: #74747F; font-style: italic;">col</span>/hsla 0
0
<span style="color: #252528;">%</span>
1))))
(<span style="color: #74747F;">defn</span> <span style="color: #3C3C42;">matrix-to-heatmap</span>
<span style="color: #ff0000; font-style: italic;">"Draw a `heatmap' using a matrix of data.</span>
<span style="color: #ff0000; font-style: italic;"> A width and height should be provided</span>
<span style="color: #ff0000; font-style: italic;"> This way the SVG points stay in units of degrees</span>
<span style="color: #ff0000; font-style: italic;"> This makes everything consistent (and it can be scaled later)"</span>
[matrix
[lat-height
lon-width]]
(<span style="color: #74747F;">let</span> [[height <span style="color: #ff0000;">;; </span><span style="color: #ff0000; font-style: italic;">which comes first was a bit unclear.. trial and error</span>
width] (<span style="color: #74747F; font-style: italic;">ndarray</span>/shape matrix)]
{<span style="color: #61616A;">:x-axis</span> (<span style="color: #74747F; font-style: italic;">viz</span>/linear-axis
{<span style="color: #61616A;">:domain</span> [0 width]
<span style="color: #61616A;">:range</span> [0 lon-width]
<span style="color: #61616A;">:visible</span> <span style="color: #61616A;">false</span>})
<span style="color: #61616A;">:y-axis</span> (<span style="color: #74747F; font-style: italic;">viz</span>/linear-axis
{<span style="color: #61616A;">:domain</span> [0 height]
<span style="color: #61616A;">:range</span> [0 lat-height]
<span style="color: #61616A;">:visible</span> <span style="color: #61616A;">false</span>})
<span style="color: #61616A;">:data</span> [{<span style="color: #61616A;">:matrix</span> matrix
<span style="color: #61616A;">:value-domain</span> [0 65535]
<span style="color: #61616A;">:palette</span> grey-scale-for-shorts
<span style="color: #61616A;">:palette-scale</span> <span style="color: #74747F; font-style: italic;">viz</span>/linear-scale
<span style="color: #61616A;">:layout</span> <span style="color: #74747F; font-style: italic;">viz</span>/svg-heatmap}]}))
(<span style="color: #74747F;">defn</span> <span style="color: #3C3C42;">plot-rain</span>
<span style="color: #ff0000; font-style: italic;">"Returns a `</span><span style="color: #61616A; font-style: italic;">thi.ng.geom</span><span style="color: #ff0000; font-style: italic;">` SVG hiccup (using a heatmap) of a GeoTIFF rain"</span>
[geotiff-file
[lat-res
lon-res]
region]
(<span style="color: #74747F;">-></span> geotiff-file
java.io.File.
<span style="color: #74747F; font-style: italic;">javax.imageio.ImageIO</span>/read
(geotiff-subregion [lat-res
lon-res]
region)
geotiff-to-matrix
(matrix-to-heatmap (region-ranges region))
<span style="color: #74747F; font-style: italic;">viz</span>/svg-plot2d-cartesian))
</pre>
</div>
<p>
We can then overlay the shorelines of the region of interest to see the rains for a particular month
</p>
<div class="org-src-container">
<pre class="src src-clojure">(<span style="color: #74747F;">->></span> (plot-to-svg (<span style="color: #74747F; font-style: italic;">svg</span>/group {}
(plot-rain <span style="color: #74747F;">"/home/geokon/Downloads/coast/rain/3B-MO-GIS.MS.MRG.3IMERG.20110301-S000000-E235959.03.V06B.tif"</span>
[0.1 0.1]
krabi-region)
(plot-shoreline <span style="color: #74747F;">"/home/geokon/Downloads/coast/GSHHS_shp/l/shoreline.json"</span>
krabi-region))
(region-ranges krabi-region))
symbol)
</pre>
</div>
<?xml version="1.0"?>
<svg height="5" version="1.1" viewBox="0 0 8 5" width="8" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg">
<g>
<g>
<g>
<polygon fill="hsl(0,0%,68%)" points="0.00,0.00 0.10,0.00 0.10,0.10 0.00,0.10" />
<polygon fill="hsl(0,0%,70%)" points="0.10,0.00 0.20,0.00 0.20,0.10 0.10,0.10" />
<polygon fill="hsl(0,0%,75%)" points="0.20,0.00 0.30,0.00 0.30,0.10 0.20,0.10" />
<polygon fill="hsl(0,0%,77%)" points="0.30,0.00 0.40,0.00 0.40,0.10 0.30,0.10" />
<polygon fill="hsl(0,0%,76%)" points="0.40,0.00 0.50,0.00 0.50,0.10 0.40,0.10" />
<polygon fill="hsl(0,0%,84%)" points="0.50,0.00 0.60,0.00 0.60,0.10 0.50,0.10" />
<polygon fill="hsl(0,0%,84%)" points="0.60,0.00 0.70,0.00 0.70,0.10 0.60,0.10" />
<polygon fill="hsl(0,0%,84%)" points="0.70,0.00 0.80,0.00 0.80,0.10 0.70,0.10" />
<polygon fill="hsl(0,0%,83%)" points="0.80,0.00 0.90,0.00 0.90,0.10 0.80,0.10" />
<polygon fill="hsl(0,0%,82%)" points="0.90,0.00 1.00,0.00 1.00,0.10 0.90,0.10" />
<polygon fill="hsl(0,0%,82%)" points="1.00,0.00 1.10,0.00 1.10,0.10 1.00,0.10" />
<polygon fill="hsl(0,0%,83%)" points="1.10,0.00 1.20,0.00 1.20,0.10 1.10,0.10" />
<polygon fill="hsl(0,0%,85%)" points="1.20,0.00 1.30,0.00 1.30,0.10 1.20,0.10" />
<polygon fill="hsl(0,0%,87%)" points="1.30,0.00 1.40,0.00 1.40,0.10 1.30,0.10" />
<polygon fill="hsl(0,0%,87%)" points="1.40,0.00 1.50,0.00 1.50,0.10 1.40,0.10" />
<polygon fill="hsl(0,0%,89%)" points="1.50,0.00 1.60,0.00 1.60,0.10 1.50,0.10" />
<polygon fill="hsl(0,0%,90%)" points="1.60,0.00 1.70,0.00 1.70,0.10 1.60,0.10" />
<polygon fill="hsl(0,0%,91%)" points="1.70,0.00 1.80,0.00 1.80,0.10 1.70,0.10" />
<polygon fill="hsl(0,0%,92%)" points="1.80,0.00 1.90,0.00 1.90,0.10 1.80,0.10" />
<polygon fill="hsl(0,0%,93%)" points="1.90,0.00 2.00,0.00 2.00,0.10 1.90,0.10" />
<polygon fill="hsl(0,0%,93%)" points="2.00,0.00 2.10,0.00 2.10,0.10 2.00,0.10" />
<polygon fill="hsl(0,0%,91%)" points="2.10,0.00 2.20,0.00 2.20,0.10 2.10,0.10" />
<polygon fill="hsl(0,0%,93%)" points="2.20,0.00 2.30,0.00 2.30,0.10 2.20,0.10" />
<polygon fill="hsl(0,0%,94%)" points="2.30,0.00 2.40,0.00 2.40,0.10 2.30,0.10" />
<polygon fill="hsl(0,0%,94%)" points="2.40,0.00 2.50,0.00 2.50,0.10 2.40,0.10" />
<polygon fill="hsl(0,0%,94%)" points="2.50,0.00 2.60,0.00 2.60,0.10 2.50,0.10" />
<polygon fill="hsl(0,0%,94%)" points="2.60,0.00 2.70,0.00 2.70,0.10 2.60,0.10" />
<polygon fill="hsl(0,0%,93%)" points="2.70,0.00 2.80,0.00 2.80,0.10 2.70,0.10" />
<polygon fill="hsl(0,0%,91%)" points="2.80,0.00 2.90,0.00 2.90,0.10 2.80,0.10" />
<polygon fill="hsl(0,0%,89%)" points="2.90,0.00 3.00,0.00 3.00,0.10 2.90,0.10" />
<polygon fill="hsl(0,0%,87%)" points="3.00,0.00 3.10,0.00 3.10,0.10 3.00,0.10" />
<polygon fill="hsl(0,0%,84%)" points="3.10,0.00 3.20,0.00 3.20,0.10 3.10,0.10" />
<polygon fill="hsl(0,0%,83%)" points="3.20,0.00 3.30,0.00 3.30,0.10 3.20,0.10" />
<polygon fill="hsl(0,0%,77%)" points="3.30,0.00 3.40,0.00 3.40,0.10 3.30,0.10" />
<polygon fill="hsl(0,0%,71%)" points="3.40,0.00 3.50,0.00 3.50,0.10 3.40,0.10" />
<polygon fill="hsl(0,0%,68%)" points="3.50,0.00 3.60,0.00 3.60,0.10 3.50,0.10" />
<polygon fill="hsl(0,0%,70%)" points="3.60,0.00 3.70,0.00 3.70,0.10 3.60,0.10" />
<polygon fill="hsl(0,0%,71%)" points="3.70,0.00 3.80,0.00 3.80,0.10 3.70,0.10" />
<polygon fill="hsl(0,0%,70%)" points="3.80,0.00 3.90,0.00 3.90,0.10 3.80,0.10" />
<polygon fill="hsl(0,0%,70%)" points="3.90,0.00 4.00,0.00 4.00,0.10 3.90,0.10" />
<polygon fill="hsl(0,0%,68%)" points="4.00,0.00 4.10,0.00 4.10,0.10 4.00,0.10" />
<polygon fill="hsl(0,0%,67%)" points="4.10,0.00 4.20,0.00 4.20,0.10 4.10,0.10" />
<polygon fill="hsl(0,0%,65%)" points="4.20,0.00 4.30,0.00 4.30,0.10 4.20,0.10" />
<polygon fill="hsl(0,0%,66%)" points="4.30,0.00 4.40,0.00 4.40,0.10 4.30,0.10" />
<polygon fill="hsl(0,0%,70%)" points="4.40,0.00 4.50,0.00 4.50,0.10 4.40,0.10" />
<polygon fill="hsl(0,0%,71%)" points="4.50,0.00 4.60,0.00 4.60,0.10 4.50,0.10" />
<polygon fill="hsl(0,0%,76%)" points="4.60,0.00 4.70,0.00 4.70,0.10 4.60,0.10" />
<polygon fill="hsl(0,0%,78%)" points="4.70,0.00 4.80,0.00 4.80,0.10 4.70,0.10" />
<polygon fill="hsl(0,0%,74%)" points="4.80,0.00 4.90,0.00 4.90,0.10 4.80,0.10" />
<polygon fill="hsl(0,0%,76%)" points="4.90,0.00 5.00,0.00 5.00,0.10 4.90,0.10" />
<polygon fill="hsl(0,0%,77%)" points="5.00,0.00 5.10,0.00 5.10,0.10 5.00,0.10" />
<polygon fill="hsl(0,0%,76%)" points="5.10,0.00 5.20,0.00 5.20,0.10 5.10,0.10" />
<polygon fill="hsl(0,0%,74%)" points="5.20,0.00 5.30,0.00 5.30,0.10 5.20,0.10" />
<polygon fill="hsl(0,0%,74%)" points="5.30,0.00 5.40,0.00 5.40,0.10 5.30,0.10" />
<polygon fill="hsl(0,0%,73%)" points="5.40,0.00 5.50,0.00 5.50,0.10 5.40,0.10" />
<polygon fill="hsl(0,0%,72%)" points="5.50,0.00 5.60,0.00 5.60,0.10 5.50,0.10" />
<polygon fill="hsl(0,0%,69%)" points="5.60,0.00 5.70,0.00 5.70,0.10 5.60,0.10" />
<polygon fill="hsl(0,0%,67%)" points="5.70,0.00 5.80,0.00 5.80,0.10 5.70,0.10" />
<polygon fill="hsl(0,0%,65%)" points="5.80,0.00 5.90,0.00 5.90,0.10 5.80,0.10" />
<polygon fill="hsl(0,0%,63%)" points="5.90,0.00 6.00,0.00 6.00,0.10 5.90,0.10" />
<polygon fill="hsl(0,0%,59%)" points="6.00,0.00 6.10,0.00 6.10,0.10 6.00,0.10" />
<polygon fill="hsl(0,0%,68%)" points="6.10,0.00 6.20,0.00 6.20,0.10 6.10,0.10" />
<polygon fill="hsl(0,0%,72%)" points="6.20,0.00 6.30,0.00 6.30,0.10 6.20,0.10" />
<polygon fill="hsl(0,0%,76%)" points="6.30,0.00 6.40,0.00 6.40,0.10 6.30,0.10" />
<polygon fill="hsl(0,0%,77%)" points="6.40,0.00 6.50,0.00 6.50,0.10 6.40,0.10" />
<polygon fill="hsl(0,0%,78%)" points="6.50,0.00 6.60,0.00 6.60,0.10 6.50,0.10" />
<polygon fill="hsl(0,0%,78%)" points="6.60,0.00 6.70,0.00 6.70,0.10 6.60,0.10" />
<polygon fill="hsl(0,0%,82%)" points="6.70,0.00 6.80,0.00 6.80,0.10 6.70,0.10" />
<polygon fill="hsl(0,0%,82%)" points="6.80,0.00 6.90,0.00 6.90,0.10 6.80,0.10" />
<polygon fill="hsl(0,0%,82%)" points="6.90,0.00 7.00,0.00 7.00,0.10 6.90,0.10" />
<polygon fill="hsl(0,0%,83%)" points="7.00,0.00 7.10,0.00 7.10,0.10 7.00,0.10" />
<polygon fill="hsl(0,0%,86%)" points="7.10,0.00 7.20,0.00 7.20,0.10 7.10,0.10" />
<polygon fill="hsl(0,0%,87%)" points="7.20,0.00 7.30,0.00 7.30,0.10 7.20,0.10" />
<polygon fill="hsl(0,0%,87%)" points="7.30,0.00 7.40,0.00 7.40,0.10 7.30,0.10" />
<polygon fill="hsl(0,0%,87%)" points="7.40,0.00 7.50,0.00 7.50,0.10 7.40,0.10" />
<polygon fill="hsl(0,0%,88%)" points="7.50,0.00 7.60,0.00 7.60,0.10 7.50,0.10" />
<polygon fill="hsl(0,0%,88%)" points="7.60,0.00 7.70,0.00 7.70,0.10 7.60,0.10" />
<polygon fill="hsl(0,0%,88%)" points="7.70,0.00 7.80,0.00 7.80,0.10 7.70,0.10" />
<polygon fill="hsl(0,0%,86%)" points="7.80,0.00 7.90,0.00 7.90,0.10 7.80,0.10" />
<polygon fill="hsl(0,0%,83%)" points="7.90,0.00 8.00,0.00 8.00,0.10 7.90,0.10" />
<polygon fill="hsl(0,0%,67%)" points="0.00,0.10 0.10,0.10 0.10,0.20 0.00,0.20" />
<polygon fill="hsl(0,0%,72%)" points="0.10,0.10 0.20,0.10 0.20,0.20 0.10,0.20" />
<polygon fill="hsl(0,0%,78%)" points="0.20,0.10 0.30,0.10 0.30,0.20 0.20,0.20" />
<polygon fill="hsl(0,0%,76%)" points="0.30,0.10 0.40,0.10 0.40,0.20 0.30,0.20" />
<polygon fill="hsl(0,0%,82%)" points="0.40,0.10 0.50,0.10 0.50,0.20 0.40,0.20" />
<polygon fill="hsl(0,0%,86%)" points="0.50,0.10 0.60,0.10 0.60,0.20 0.50,0.20" />
<polygon fill="hsl(0,0%,86%)" points="0.60,0.10 0.70,0.10 0.70,0.20 0.60,0.20" />
<polygon fill="hsl(0,0%,85%)" points="0.70,0.10 0.80,0.10 0.80,0.20 0.70,0.20" />
<polygon fill="hsl(0,0%,83%)" points="0.80,0.10 0.90,0.10 0.90,0.20 0.80,0.20" />
<polygon fill="hsl(0,0%,82%)" points="0.90,0.10 1.00,0.10 1.00,0.20 0.90,0.20" />
<polygon fill="hsl(0,0%,83%)" points="1.00,0.10 1.10,0.10 1.10,0.20 1.00,0.20" />
<polygon fill="hsl(0,0%,86%)" points="1.10,0.10 1.20,0.10 1.20,0.20 1.10,0.20" />
<polygon fill="hsl(0,0%,86%)" points="1.20,0.10 1.30,0.10 1.30,0.20 1.20,0.20" />
<polygon fill="hsl(0,0%,89%)" points="1.30,0.10 1.40,0.10 1.40,0.20 1.30,0.20" />
<polygon fill="hsl(0,0%,89%)" points="1.40,0.10 1.50,0.10 1.50,0.20 1.40,0.20" />
<polygon fill="hsl(0,0%,90%)" points="1.50,0.10 1.60,0.10 1.60,0.20 1.50,0.20" />
<polygon fill="hsl(0,0%,90%)" points="1.60,0.10 1.70,0.10 1.70,0.20 1.60,0.20" />
<polygon fill="hsl(0,0%,90%)" points="1.70,0.10 1.80,0.10 1.80,0.20 1.70,0.20" />
<polygon fill="hsl(0,0%,91%)" points="1.80,0.10 1.90,0.10 1.90,0.20 1.80,0.20" />
<polygon fill="hsl(0,0%,93%)" points="1.90,0.10 2.00,0.10 2.00,0.20 1.90,0.20" />
<polygon fill="hsl(0,0%,93%)" points="2.00,0.10 2.10,0.10 2.10,0.20 2.00,0.20" />
<polygon fill="hsl(0,0%,92%)" points="2.10,0.10 2.20,0.10 2.20,0.20 2.10,0.20" />
<polygon fill="hsl(0,0%,93%)" points="2.20,0.10 2.30,0.10 2.30,0.20 2.20,0.20" />
<polygon fill="hsl(0,0%,94%)" points="2.30,0.10 2.40,0.10 2.40,0.20 2.30,0.20" />
<polygon fill="hsl(0,0%,94%)" points="2.40,0.10 2.50,0.10 2.50,0.20 2.40,0.20" />
<polygon fill="hsl(0,0%,93%)" points="2.50,0.10 2.60,0.10 2.60,0.20 2.50,0.20" />
<polygon fill="hsl(0,0%,92%)" points="2.60,0.10 2.70,0.10 2.70,0.20 2.60,0.20" />
<polygon fill="hsl(0,0%,91%)" points="2.70,0.10 2.80,0.10 2.80,0.20 2.70,0.20" />
<polygon fill="hsl(0,0%,87%)" points="2.80,0.10 2.90,0.10 2.90,0.20 2.80,0.20" />
<polygon fill="hsl(0,0%,87%)" points="2.90,0.10 3.00,0.10 3.00,0.20 2.90,0.20" />
<polygon fill="hsl(0,0%,87%)" points="3.00,0.10 3.10,0.10 3.10,0.20 3.00,0.20" />
<polygon fill="hsl(0,0%,83%)" points="3.10,0.10 3.20,0.10 3.20,0.20 3.10,0.20" />
<polygon fill="hsl(0,0%,79%)" points="3.20,0.10 3.30,0.10 3.30,0.20 3.20,0.20" />
<polygon fill="hsl(0,0%,79%)" points="3.30,0.10 3.40,0.10 3.40,0.20 3.30,0.20" />
<polygon fill="hsl(0,0%,67%)" points="3.40,0.10 3.50,0.10 3.50,0.20 3.40,0.20" />
<polygon fill="hsl(0,0%,66%)" points="3.50,0.10 3.60,0.10 3.60,0.20 3.50,0.20" />
<polygon fill="hsl(0,0%,65%)" points="3.60,0.10 3.70,0.10 3.70,0.20 3.60,0.20" />
<polygon fill="hsl(0,0%,69%)" points="3.70,0.10 3.80,0.10 3.80,0.20 3.70,0.20" />
<polygon fill="hsl(0,0%,68%)" points="3.80,0.10 3.90,0.10 3.90,0.20 3.80,0.20" />
<polygon fill="hsl(0,0%,65%)" points="3.90,0.10 4.00,0.10 4.00,0.20 3.90,0.20" />
<polygon fill="hsl(0,0%,67%)" points="4.00,0.10 4.10,0.10 4.10,0.20 4.00,0.20" />
<polygon fill="hsl(0,0%,64%)" points="4.10,0.10 4.20,0.10 4.20,0.20 4.10,0.20" />
<polygon fill="hsl(0,0%,65%)" points="4.20,0.10 4.30,0.10 4.30,0.20 4.20,0.20" />
<polygon fill="hsl(0,0%,64%)" points="4.30,0.10 4.40,0.10 4.40,0.20 4.30,0.20" />
<polygon fill="hsl(0,0%,65%)" points="4.40,0.10 4.50,0.10 4.50,0.20 4.40,0.20" />
<polygon fill="hsl(0,0%,68%)" points="4.50,0.10 4.60,0.10 4.60,0.20 4.50,0.20" />
<polygon fill="hsl(0,0%,71%)" points="4.60,0.10 4.70,0.10 4.70,0.20 4.60,0.20" />
<polygon fill="hsl(0,0%,74%)" points="4.70,0.10 4.80,0.10 4.80,0.20 4.70,0.20" />
<polygon fill="hsl(0,0%,77%)" points="4.80,0.10 4.90,0.10 4.90,0.20 4.80,0.20" />
<polygon fill="hsl(0,0%,75%)" points="4.90,0.10 5.00,0.10 5.00,0.20 4.90,0.20" />
<polygon fill="hsl(0,0%,78%)" points="5.00,0.10 5.10,0.10 5.10,0.20 5.00,0.20" />
<polygon fill="hsl(0,0%,72%)" points="5.10,0.10 5.20,0.10 5.20,0.20 5.10,0.20" />
<polygon fill="hsl(0,0%,75%)" points="5.20,0.10 5.30,0.10 5.30,0.20 5.20,0.20" />
<polygon fill="hsl(0,0%,73%)" points="5.30,0.10 5.40,0.10 5.40,0.20 5.30,0.20" />
<polygon fill="hsl(0,0%,74%)" points="5.40,0.10 5.50,0.10 5.50,0.20 5.40,0.20" />
<polygon fill="hsl(0,0%,67%)" points="5.50,0.10 5.60,0.10 5.60,0.20 5.50,0.20" />
<polygon fill="hsl(0,0%,69%)" points="5.60,0.10 5.70,0.10 5.70,0.20 5.60,0.20" />
<polygon fill="hsl(0,0%,67%)" points="5.70,0.10 5.80,0.10 5.80,0.20 5.70,0.20" />
<polygon fill="hsl(0,0%,67%)" points="5.80,0.10 5.90,0.10 5.90,0.20 5.80,0.20" />
<polygon fill="hsl(0,0%,64%)" points="5.90,0.10 6.00,0.10 6.00,0.20 5.90,0.20" />
<polygon fill="hsl(0,0%,65%)" points="6.00,0.10 6.10,0.10 6.10,0.20 6.00,0.20" />
<polygon fill="hsl(0,0%,68%)" points="6.10,0.10 6.20,0.10 6.20,0.20 6.10,0.20" />
<polygon fill="hsl(0,0%,71%)" points="6.20,0.10 6.30,0.10 6.30,0.20 6.20,0.20" />
<polygon fill="hsl(0,0%,75%)" points="6.30,0.10 6.40,0.10 6.40,0.20 6.30,0.20" />
<polygon fill="hsl(0,0%,77%)" points="6.40,0.10 6.50,0.10 6.50,0.20 6.40,0.20" />
<polygon fill="hsl(0,0%,79%)" points="6.50,0.10 6.60,0.10 6.60,0.20 6.50,0.20" />
<polygon fill="hsl(0,0%,80%)" points="6.60,0.10 6.70,0.10 6.70,0.20 6.60,0.20" />
<polygon fill="hsl(0,0%,76%)" points="6.70,0.10 6.80,0.10 6.80,0.20 6.70,0.20" />
<polygon fill="hsl(0,0%,79%)" points="6.80,0.10 6.90,0.10 6.90,0.20 6.80,0.20" />
<polygon fill="hsl(0,0%,79%)" points="6.90,0.10 7.00,0.10 7.00,0.20 6.90,0.20" />
<polygon fill="hsl(0,0%,84%)" points="7.00,0.10 7.10,0.10 7.10,0.20 7.00,0.20" />
<polygon fill="hsl(0,0%,86%)" points="7.10,0.10 7.20,0.10 7.20,0.20 7.10,0.20" />
<polygon fill="hsl(0,0%,87%)" points="7.20,0.10 7.30,0.10 7.30,0.20 7.20,0.20" />
<polygon fill="hsl(0,0%,86%)" points="7.30,0.10 7.40,0.10 7.40,0.20 7.30,0.20" />
<polygon fill="hsl(0,0%,85%)" points="7.40,0.10 7.50,0.10 7.50,0.20 7.40,0.20" />
<polygon fill="hsl(0,0%,86%)" points="7.50,0.10 7.60,0.10 7.60,0.20 7.50,0.20" />
<polygon fill="hsl(0,0%,87%)" points="7.60,0.10 7.70,0.10 7.70,0.20 7.60,0.20" />
<polygon fill="hsl(0,0%,87%)" points="7.70,0.10 7.80,0.10 7.80,0.20 7.70,0.20" />
<polygon fill="hsl(0,0%,86%)" points="7.80,0.10 7.90,0.10 7.90,0.20 7.80,0.20" />
<polygon fill="hsl(0,0%,83%)" points="7.90,0.10 8.00,0.10 8.00,0.20 7.90,0.20" />
<polygon fill="hsl(0,0%,62%)" points="0.00,0.20 0.10,0.20 0.10,0.30 0.00,0.30" />
<polygon fill="hsl(0,0%,67%)" points="0.10,0.20 0.20,0.20 0.20,0.30 0.10,0.30" />
<polygon fill="hsl(0,0%,74%)" points="0.20,0.20 0.30,0.20 0.30,0.30 0.20,0.30" />
<polygon fill="hsl(0,0%,78%)" points="0.30,0.20 0.40,0.20 0.40,0.30 0.30,0.30" />
<polygon fill="hsl(0,0%,82%)" points="0.40,0.20 0.50,0.20 0.50,0.30 0.40,0.30" />
<polygon fill="hsl(0,0%,86%)" points="0.50,0.20 0.60,0.20 0.60,0.30 0.50,0.30" />
<polygon fill="hsl(0,0%,87%)" points="0.60,0.20 0.70,0.20 0.70,0.30 0.60,0.30" />
<polygon fill="hsl(0,0%,86%)" points="0.70,0.20 0.80,0.20 0.80,0.30 0.70,0.30" />
<polygon fill="hsl(0,0%,85%)" points="0.80,0.20 0.90,0.20 0.90,0.30 0.80,0.30" />
<polygon fill="hsl(0,0%,83%)" points="0.90,0.20 1.00,0.20 1.00,0.30 0.90,0.30" />
<polygon fill="hsl(0,0%,85%)" points="1.00,0.20 1.10,0.20 1.10,0.30 1.00,0.30" />
<polygon fill="hsl(0,0%,87%)" points="1.10,0.20 1.20,0.20 1.20,0.30 1.10,0.30" />
<polygon fill="hsl(0,0%,90%)" points="1.20,0.20 1.30,0.20 1.30,0.30 1.20,0.30" />
<polygon fill="hsl(0,0%,90%)" points="1.30,0.20 1.40,0.20 1.40,0.30 1.30,0.30" />
<polygon fill="hsl(0,0%,90%)" points="1.40,0.20 1.50,0.20 1.50,0.30 1.40,0.30" />
<polygon fill="hsl(0,0%,90%)" points="1.50,0.20 1.60,0.20 1.60,0.30 1.50,0.30" />
<polygon fill="hsl(0,0%,90%)" points="1.60,0.20 1.70,0.20 1.70,0.30 1.60,0.30" />
<polygon fill="hsl(0,0%,88%)" points="1.70,0.20 1.80,0.20 1.80,0.30 1.70,0.30" />
<polygon fill="hsl(0,0%,90%)" points="1.80,0.20 1.90,0.20 1.90,0.30 1.80,0.30" />
<polygon fill="hsl(0,0%,90%)" points="1.90,0.20 2.00,0.20 2.00,0.30 1.90,0.30" />
<polygon fill="hsl(0,0%,90%)" points="2.00,0.20 2.10,0.20 2.10,0.30 2.00,0.30" />
<polygon fill="hsl(0,0%,91%)" points="2.10,0.20 2.20,0.20 2.20,0.30 2.10,0.30" />