-
Notifications
You must be signed in to change notification settings - Fork 34
Expand file tree
/
Copy pathplot.jl
More file actions
2330 lines (1994 loc) · 112 KB
/
plot.jl
File metadata and controls
2330 lines (1994 loc) · 112 KB
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
"""
plot(arg1::Array; kwargs...)
reads (x,y) pairs from files [or standard input] and generates PostScript code that will plot lines,
polygons, or symbols at those locations on a map.
See full GMT docs at [`psxy`]($(GMTdoc)plot.html)
Parameters
----------
- **A** | **steps** | **stairs** | **straight_lines** :: [Type => Str]
By default, geographic line segments are drawn as great circle arcs.
To draw them as straight lines, use this option.
- $(_opt_J)
- $(_opt_R)
- $(_opt_B)
- $(opt_C)
- **D** | **shift** | **offset** :: [Type => Str]
Offset the plot symbol or line locations by the given amounts dx/dy in cm, inch or points.
- **E** | **error** | **error_bars** :: [Type => Str]
Draw symmetrical error bars.
- **F** | **conn** | **connection** :: [Type => Str]
Alter the way points are connected
- **G** | **fill** | **markerfacecolor** | **MarkerFaceColor** | **markercolor** | **mc** :: [Type => Str]
Select color or pattern for filling of symbols or polygons. BUT WARN: the alias 'fill' will set the
color of polygons OR symbols but not the two together. If your plot has polygons and symbols, use
'fill' for the polygons and 'markerfacecolor' for filling the symbols. Same applyies for W bellow
- **I** | **intens** :: [Type => Str | number]
Use the supplied intens value (in the [-1 1] range) to modulate the fill color by simulating illumination.
- **L** | **close** | **polygon** :: [Type => Str]
Force closed polygons.
- **N** | **no_clip** | **noclip** :: [Type => Str or []]
Do NOT clip symbols that fall outside map border
- $(opt_P)
- **S** | **symbol** | **marker** | **Marker** :: [Type => Str]
Plot symbols (including vectors, pie slices, fronts, decorated or quoted lines).
Alternatively select a sub-set of symbols using the aliases: **marker** or **Marker** and values:
+ **-**, **x_dash**
+ **+**, **plus**
+ **a**, *, **star**
+ **c**, **circle**
+ **d**, **diamond**
+ **g**, **octagon**
+ **h**, **hexagon**
+ **i**, **v**, **inverted_tri**
+ **n**, **pentagon**
+ **p**, **.**, **point**
+ **r**, **rectangle**
+ **s**, **square**
+ **t**, **^**, **triangle**
+ **x**, **cross**
+ **y**, **y_dash**
and select their sizes with the **markersize** or **size** keyword [default is 7p].
The marker size can be a scalar or a vector with same size numeber of rows of data. Units are
points unless specified otherwise with (for example for cm) *par=(PROJ_LENGTH_UNIT="c")*
- **W** | **pen** | **markeredgecolor** | **mec** :: [Type => Str]
Set pen attributes for lines or the outline of symbols
WARNING: the pen attributes will set the pen of polygons OR symbols but not the two together.
If your plot has polygons and symbols, use **W** or **pen** for the polygons and
**markeredgecolor** for filling the symbols. Similar to S above.
- $(opt_U)
- $(opt_V)
- $(opt_X)
- $(opt_Y)
- **Z** | **level** :: [Type => Str | NamedTuple] `Arg = value|file[+f|+l] | (data=Array|Number, outline=_, fill=_)`
Paint polygons after the level given as a cte or a vector with same size of number of polygons. Needs a color map.
- **aspect** :: [Type => Str]
When equal to "equal" makes a square plot.
- $(opt_a)
- $(_opt_bi)
- $(_opt_di)
- $(opt_e)
- $(_opt_f)
- $(opt_g)
- $(_opt_h)
- $(_opt_i)
- $(_opt_p)
- $(_opt_t)
- $(opt_w)
- $(opt_swap_xy)
- $(opt_savefig)
"""
function plot(arg1; first=true, kw...)
d = KW(kw)
_plot(arg1, first==1, d)
end
function _plot(arg1, first::Bool, d::Dict{Symbol, Any})
# First check if arg1 is a GMTds of a linear fit and if yes, call the plotlinefit() fun
if (isa(arg1, GDtype) && is_in_dict(d, [:linefit :regress]; del=false) !== nothing)
att = isa(arg1, GMTdataset) ? arg1.attrib : arg1[1].attrib
(get(att, "linearfit", "") != "") && return plotlinefit(arg1; first=first, d...)
# If it didn't return above, see if we have a 'groupvar' request and if yes perform regression on grouops
arg1 = with_xyvar(d, arg1) # But first check if we have a column selection
gidx, gnames = get_group_indices(d, arg1)
cycle_colors = (numel(gidx) <= 7) ? matlab_cycle_colors : simple_distinct # Will blow if > 20
if (!isempty(gidx))
Dv = Vector{GMTdataset{Float64,2}}(undef, length(gidx))
for k = 1:numel(gidx)
Dv[k] = linearfitxy(mat2ds(arg1, (gidx[k], :)))
Dv[k].header = "-G"*cycle_colors[k] * " -W"*cycle_colors[k]
Dv[k].attrib["group_name"] = string(gnames[k])::String
end
Dv[1].ds_bbox = arg1.ds_bbox
return plotlinefit(Dv; first=first, d...)
end
end
common_plot_xyz("", Tables.istable(arg1) ? arg1 : cat_1_arg(arg1, true), "plot", first, false, d)
end
plot!(arg1; kw...) = plot(arg1; first=false, kw...)
function plot(f::Function, range_x=nothing; first=true, kw...)
rang = gen_coords4funs(range_x, "x"; kw...)
common_plot_xyz("", cat_2_arg2(rang, [f(x) for x in rang], true), "plot", first, false; kw...)
end
plot!(f::Function, rang=nothing; kw...) = plot(f, rang; first=false, kw...)
function plot(f1::Function, f2::Function, range_t=nothing; first=true, kw...) # Parametric version
common_plot_xyz("", mat2ds(help_parametric_2f(f1, f2, range_t; is3D=false, kw...)), "plot", first, false; kw...)
end
plot!(f1::Function, f2::Function, range_t=nothing; kw...) = plot(f1, f2, range_t; first=false, kw...)
plot(cmd0::String="", arg1=nothing; kw...) = common_plot_xyz(cmd0, mat2ds(arg1), "plot", true, false; kw...)
plot!(cmd0::String="", arg1=nothing; kw...) = common_plot_xyz(cmd0, mat2ds(arg1), "plot", false, false; kw...)
plot(arg1, arg2; kw...) = common_plot_xyz("", cat_2_arg2(arg1, arg2, true), "plot", true, false; kw...)
plot!(arg1, arg2; kw...) = common_plot_xyz("", cat_2_arg2(arg1, arg2, true), "plot", false, false; kw...)
plot(arg1::Number, arg2::Number; kw...) = common_plot_xyz("", cat_2_arg2([arg1], [arg2], true), "plot", true, false; kw...)
plot!(arg1::Number, arg2::Number; kw...) = common_plot_xyz("", cat_2_arg2([arg1], [arg2], true), "plot", false, false; kw...)
# ------------------------------------------------------------------------------------------------------
"""
plotyy(arg1, arg2; kwargs...)
Example:
```julia
plotyy([1 1; 2 2], [1.5 1.5; 3 3], R="0.8/3/0/5", title="Ai", ylabel=:Bla, xlabel=:Ble, seclabel=:Bli, show=true)
```
"""
function plotyy(arg1, arg2; first=true, kw...)
d = KW(kw)
(haskey(d, :xlabel)) ? (xlabel = string(d[:xlabel])::String; delete!(d, :xlabel)) : xlabel = "" # Only to used at the end
(haskey(d, :seclabel)) ? (seclabel = string(d[:seclabel])::String; delete!(d, :seclabel)) : seclabel = ""
fmt::String = ((val = find_in_dict(d, [:fmt])[1]) !== nothing) ? arg2str(val)::String : FMT[]::String
savefig = ((val = find_in_dict(d, [:savefig :figname :name])[1]) !== nothing) ? arg2str(val)::String : nothing
Vd = ((val = find_in_dict(d, [:Vd])[1]) !== nothing) ? val : 0
xaxis = find_in_dict(d, [:xaxis])[1]; xaxis2 = find_in_dict(d, [:xaxis2])[1] # These are only for -Bx
opt_B::String = parse_B(d, "", " -Baf -BW")[1] # This would not ignore :xaxis and :xaxis2 but that would screw WE axes
if (opt_B != " -Baf -BW")
if (occursin(" -Bx", opt_B) || occursin(" -By", opt_B) || occursin("+t", opt_B))
# OK, so here's the problem. Both title and label maybe multi-words, case in which they will have the
# form: "aa bb" but since they wont be fully parsed again by parse_B we loose the info about the sapces.
# The trick is to find and replace the sapces inside the " " chunks like parse_B does and let it undo
# the replacing without inserting extras -B, like the "aa -Bbb" that would otherwise result.
t = findall(isequal('"'), opt_B) # Find indices of the " and hope they came in pairs.
if (!isempty(t)) # Have a title and maybe an ylabel (or vice-versa)
opt_B = opt_B[1:t[1]] * replace(opt_B[t[1]+1:t[2]-1], ' '=>'\x7f') * opt_B[t[2]:end]
if (length(t) == 4) # Have also an ylabel
opt_B = opt_B[1:t[3]] * replace(opt_B[t[3]+1:t[4]-1], ' '=>'\x7f') * opt_B[t[4]:end]
end
end
d[:B] = replace(opt_B, "-B" => "")
else
d[:B] = " af W"
end
else
d[:B] = " af W"
end
(Vd != 0) && (d[:Vd] = Vd)
lw = get(d, :lw, nothing)
d[:lc] = "#0072BD"
do_show = ((val = find_in_dict(d, [:show])[1]) !== nothing && val != 0)
in_conf = get(d, :conf, nothing) # If there is a incomming one, save it to apply also to the xaxis (Date/time axis do that)
d[:par] = (MAP_FRAME_PEN="#0072BD", MAP_TICK_PEN="#0072BD", FONT_ANNOT_PRIMARY="#0072BD", FONT_LABEL="#0072BD")
r1 = common_plot_xyz("", cat_1_arg(arg1, true), "plotyy", first, false, d)
(Vd != 0) && (d[:Vd] = Vd)
(seclabel != "" && occursin(" ", seclabel)) && (seclabel = "\"" * seclabel * "\"")
(seclabel != "") && (seclabel = " y+l" * seclabel)
d[:B] = " af E" * seclabel # Also remember that previous -B was consumed in first call
d[:lc] = "#D95319"
d[:par] = (MAP_FRAME_PEN="#D95319", MAP_TICK_PEN="#D95319", FONT_ANNOT_PRIMARY="#D95319", FONT_LABEL="#D95319")
(lw !== nothing) && (d[:lw] = lw)
r2 = common_plot_xyz("", cat_1_arg(arg2, true), "plotyy", false, false, d)
(xlabel != "" && occursin(" ", xlabel)) && (xlabel = "\"" * xlabel * "\"")
opt_B = (xlabel != "") ? "af Sn x+l" * xlabel : "af Sn"
(xaxis !== nothing) && (dd = Dict{Symbol,Any}(:xaxis => xaxis); opt_B = parse_B(dd, opt_B, "")[1])
(xaxis2 !== nothing) && (dd = Dict{Symbol,Any}(:xaxis2 => xaxis2); opt_B = parse_B(dd, opt_B, "")[1])
opt_f = isa(arg1, GMTdataset) ? set_fT(arg1, "", "") : "" # See if Timecol is present and set -f0T if yes
_f = (opt_f !== "") ? opt_f[4:end] : ""
r3 = basemap!(J="", B=opt_B, conf=in_conf, f=_f, Vd=Vd, fmt=fmt, name=savefig, show=do_show)
return (Vd == 2) ? [r1;r2;r3] : nothing
end
# ------------------------------------------------------------------------------------------------------
"""
plot3d(arg1::Array; kwargs...)
reads (x,y,z) triplets and generates PostScript code that will plot lines,
polygons, or symbols at those locations in 3-D.
See full GMT docs at [`plot3d`]($(GMTdoc)plot3d.html)
Parameters
----------
- **A** | **steps** | **straight_lines** :: [Type => Str]
By default, geographic line segments are drawn as great circle arcs. To draw them as straight lines, use this option.
- $(_opt_J)
- $(opt_Jz)
- $(_opt_R)
- $(_opt_B)
- **C** | **color** :: [Type => Str]
Give a CPT or specify -Ccolor1,color2[,color3,...] to build a linear continuous CPT from those colors automatically.
- **D** | **offset** :: [Type => Str]
Offset the plot symbol or line locations by the given amounts dx/dy.
- **E** | **error_bars** :: [Type => Str]
Draw symmetrical error bars.
- **F** | **conn** | **connection** :: [Type => Str]
Alter the way points are connected
- **G** | **fill** | **markerfacecolor** | **MarkerFaceColor** | **markercolor** | **mc** :: [Type => Str]
Select color or pattern for filling of symbols or polygons. BUT WARN: the alias 'fill' will set the
color of polygons OR symbols but not the two together. If your plot has polygons and symbols, use
'fill' for the polygons and 'markerfacecolor' for filling the symbols. Same applyies for W bellow
- **I** | **intens** :: [Type => Str or number]
Use the supplied intens value (in the [-1 1] range) to modulate the fill color by simulating illumination.
- **L** | **closed_polygon** :: [Type => Str]
Force closed polygons.
- **N** | **no_clip** :: [Type => Str | []]
Do NOT clip symbols that fall outside map border
- $(opt_P)
- **S** | **symbol** | **marker** | **Marker** :: [Type => Str]
Plot symbols (including vectors, pie slices, fronts, decorated or quoted lines).
Alternatively select a sub-set of symbols using the aliases: **marker** or **Marker** and values:
+ **-**, **x_dash**
+ **+**, **plus**
+ **a**, *, **star**
+ **c**, **circle**
+ **d**, **diamond**
+ **g**, **octagon**
+ **h**, **hexagon**
+ **i**, **v**, **inverted_tri**
+ **n**, **pentagon**
+ **p**, **.**, **point**
+ **r**, **rectangle**
+ **s**, **square**
+ **t**, **^**, **triangle**
+ **x**, **cross**
+ **y**, **y_dash**
- **W** | **pen** | **line_attribs** | **markeredgecolor** | **MarkerEdgeColor** | **mec**:: [Type => Str]
Set pen attributes for lines or the outline of symbols
WARNING: the pen attributes will set the pen of polygons OR symbols but not the two together.
If your plot has polygons and symbols, use **W** or **line_attribs** for the polygons and
**markeredgecolor** or **MarkerEdgeColor** for filling the symbols. Similar to S above.
- $(opt_U)
- $(opt_V)
- $(opt_X)
- $(opt_Y)
- **Z** | **level** :: [Type => Str | NamedTuple] `Arg = value|file[+f|+l] | (data=Array|Number, outline=_, fill=_)`
Paint polygons after the level given as a cte or a vector with same size of number of polygons. Needs a color map.
- $(opt_a)
- $(_opt_bi)
- $(_opt_di)
- $(opt_e)
- $(_opt_f)
- $(opt_g)
- $(_opt_h)
- $(_opt_i)
- $(_opt_p)
- $(_opt_t)
- $(opt_w)
- $(opt_savefig)
### Example:
plot3d(x -> sin(x)*cos(10x), y -> sin(y)*sin(10y), z -> cos(z), 0:pi/100:pi, show=true, aspect3=:equal)
"""
plot3d(arg1; kw...) = common_plot_xyz("", cat_1_arg(arg1, true), "plot3d", true, true; kw...)
plot3d!(arg1; kw...) = common_plot_xyz("", cat_1_arg(arg1, true), "plot3d", false, true; kw...)
plot3d(cmd0::String="", arg1=nothing; kw...) = common_plot_xyz(cmd0, mat2ds(arg1), "plot3d", true, true; kw...)
plot3d!(cmd0::String="", arg1=nothing; kw...) = common_plot_xyz(cmd0, mat2ds(arg1), "plot3d", false, true; kw...)
# ------------------------------------------------------------------------------------------------------
function plot3d(arg1::AbstractArray, arg2::AbstractArray, arg3::AbstractArray; first=true, kw...)
common_plot_xyz("", mat2ds(hcat(arg1[:], arg2[:], arg3[:])), "plot3d", first, true; kw...)
end
plot3d!(arg1::AbstractArray, arg2::AbstractArray, arg3::AbstractArray; kw...) = plot3d(arg1, arg2, arg3; first=false, kw...)
function plot3d(f1::Function, f2::Function, range_t=nothing; first=true, kw...)
common_plot_xyz("", mat2ds(help_parametric_2f(f1, f2, range_t; kw...)), "plot3d", first, true; kw...)
end
plot3d!(f1::Function, f2::Function, range_t=nothing; kw...) = plot3d(f1, f2, range_t; first=false, kw...)
function plot3d(f1::Function, f2::Function, f3::Function, range_t=nothing; first=true, kw...)
common_plot_xyz("", mat2ds(help_parametric_3f(f1, f2, f3, range_t; kw...)), "plot3d", first, true; kw...)
end
plot3d!(f1::Function, f2::Function, f3::Function, range_t=nothing; kw...) = plot3d(f1, f2, f3, range_t; first=false, kw...)
const plot3 = plot3d # Alias
const plot3! = plot3d!
# ------------------------------------------------------------------------------------------------------
"""
scatter(cmd0::String="", arg1=nothing; kwargs...)
Reads (x,y) pairs and plot symbols at those locations on a map.
This module is a subset of ``plot`` to make it simpler to draw scatter plots. So many of
its (fine) controling parameters are not listed here. For a finer control, user should
consult the ``plot`` module.
Parameters
----------
- **G** | **fill** | **markerfacecolor** :: [Type => Str]
Select color or pattern for filling of symbols or polygons.
- **N** | **noclip** | **no_clip** :: [Type => Str | []]
Do NOT clip symbols that fall outside map border
- $(opt_P)
- **S** :: [Type => Str]
Plot symbols (including vectors, pie slices, fronts, decorated or quoted lines).
Alternatively select a sub-set of symbols using the aliases: **symbol** or **marker** and values:
+ **-**, **x_dash**
+ **+**, **plus**
+ **a**, *, **star**
+ **c**, **circle**
+ **d**, **diamond**
+ **g**, **octagon**
+ **h**, **hexagon**
+ **i**, **v**, **inverted_tri**
+ **n**, **pentagon**
+ **p**, **.**, **point**
+ **r**, **rectangle**
+ **s**, **square**
+ **t**, **^**, **triangle**
+ **x**, **cross**
+ **y**, **y_dash**
and select their sizes with the `markersize` or `size` keyword [default is 8p].
The marker size can be a scalar or a vector with same size numeber of rows of data. Units are
points unless specified otherwise with (for example for cm) *par=(PROJ_LENGTH_UNIT=:c,)*
- **W** | **pen** | **markeredgecolor** | **mec** :: [Type => Str]
Set pen attributes for lines or the outline of symbols
- $(opt_savefig)
"""
function scatter(f::Function, range_x=nothing; first=true, kw...)
rang = gen_coords4funs(range_x, "x"; kw...)
common_plot_xyz("", cat_2_arg2(rang, [f(x) for x in rang], true), "scatter", first, false; kw...)
end
scatter!(f::Function, rang=nothing; kw...) = scatter(f, rang; first=false, kw...)
function scatter(f1::Function, f2::Function, range_t=nothing; first=true, kw...) # Parametric version
common_plot_xyz("", mat2ds(help_parametric_2f(f1, f2, range_t; is3D=false, kw...)), "scatter", first, false; kw...)
end
scatter!(f1::Function, f2::Function, range_t=nothing; kw...) = scatter(f1, f2, range_t; first=false, kw...)
scatter(cmd0::String="", arg1=nothing; kw...) = common_plot_xyz(cmd0, mat2ds(arg1), "scatter", true, false; kw...)
scatter!(cmd0::String="", arg1=nothing; kw...) = common_plot_xyz(cmd0, mat2ds(arg1), "scatter", false, false; kw...)
scatter(arg; kw...) = common_plot_xyz("", cat_1_arg(arg, true), "scatter", true, false; kw...)
scatter!(arg; kw...) = common_plot_xyz("", cat_1_arg(arg, true), "scatter", false, false; kw...)
scatter(arg1, arg2; kw...) = common_plot_xyz("", cat_2_arg2(arg1, arg2, true), "scatter", true, false; kw...)
scatter!(arg1, arg2; kw...) = common_plot_xyz("", cat_2_arg2(arg1, arg2, true), "scatter", false, false; kw...)
bubblechart = scatter # Alias that supposedly only plots circles
bubblechart! = scatter!
function scatter(D::Vector{<:GMTdataset{Float64,2}}; first=true, kw...)
d = KW(kw)
labels = String[]
if ((s_val = hlp_desnany_str(d, [:labels], false)) !== "")
ts = fish_attrib_in_str(s_val)
labels = [D[k].attrib[ts] for k = 1:length(D)]
end
Dc = mat2ds(gmt_centroid_area(G_API[], D, Int(isgeog(D)), ca=2), geom=wkbPoint, text=labels)
(is_in_dict(d, [:marker, :Marker, :shape]) === nothing) && (d[:marker] = "circ")
(is_in_dict(d, [:ms :markersize :MarkerSize :size]) === nothing) && (d[:ms] = "12p")
common_plot_xyz("", Dc, "bubble", !first, false, d)
end
# ------------------------------------------------------------------------------------------------------
scatter3(cmd0::String="", arg1=nothing; kw...) = common_plot_xyz(cmd0, mat2ds(arg1), "scatter3", true, true; kw...)
scatter3!(cmd0::String="", arg1=nothing; kw...) = common_plot_xyz(cmd0, mat2ds(arg1), "scatter3", false, true; kw...)
scatter3(arg; kw...) = common_plot_xyz("", cat_1_arg(arg, true), "scatter3", true, true; kw...)
scatter3!(arg; kw...) = common_plot_xyz("", cat_1_arg(arg, true), "scatter3", false, true; kw...)
function scatter3(arg1::AbstractArray, arg2::AbstractArray, arg3::AbstractArray; kw...)
common_plot_xyz("", mat2ds(hcat(arg1, arg2, arg3)), "scatter3", true, true; kw...)
end
function scatter3!(arg1::AbstractArray, arg2::AbstractArray, arg3::AbstractArray; kw...)
common_plot_xyz("", mat2ds(hcat(arg1, arg2, arg3)), "scatter3", false, true; kw...)
end
function scatter3(f1::Function, f2::Function, range_t=nothing; first=true, kw...)
common_plot_xyz("", mat2ds(help_parametric_2f(f1, f2, range_t; kw...)), "scatter3", first, true; kw...)
end
scatter3!(f1::Function, f2::Function, range_t=nothing; kw...) = scatter3(f1, f2, range_t; first=false, kw...)
function scatter3(f1::Function, f2::Function, f3::Function, range_t=nothing; first=true, kw...)
common_plot_xyz("", mat2ds(help_parametric_3f(f1, f2, f3, range_t; kw...)), "scatter3", first, true; kw...)
end
scatter3!(f1::Function, f2::Function, f3::Function, range_t=nothing; kw...) = scatter3(f1, f2, f3, range_t; first=false, kw...)
const scatter3d = scatter3 # Alias
const scatter3d! = scatter3!
# ------------------------------------------------------------------------------------------------------
"""
bar(cmd0::String="", arg1=nothing; kwargs...)
Reads a file or (x,y) pairs and plots vertical bars extending from base to y.
- $(_opt_J)
- $(_opt_R)
- $(_opt_B)
- **fill** :: [Type => Str --
Select color or pattern for filling the bars
- **base** | **bottom** :: [Type => Str | Num] ``key=value``
By default, base = ymin. Use this option to change that value. If base is not appended then we read it.
from the last input data column.
- **size** | **width** :: [Type => Str | Num] ``key=value``
The size or width is the bar width. Append u if size is in x-units. When *width* is used the default is plot-distance units.
- $(opt_savefig)
Example:
bar(sort(randn(10)), fill=:black, axis=:auto, show=true)
"""
function bar(cmd0::String="", arg=nothing; first=true, kw...)
d = KW(kw)
(cmd0 != "" && arg === nothing) && (arg = gmtread(cmd0))
isa(arg, GMTdataset) && (arg::Matrix{<:Float64} = arg.data)
isa(arg, Vector{<:GMTdataset}) && (arg = arg[1].data; @warn("Multi-segments not allowed in 'bar'. Keeping only first segment."))
_bar(arg, first, d)
end
function _bar(arg, first::Bool, d::Dict{Symbol,Any})
do_cat = ((haskey(d, :stack) || haskey(d, :stacked)) && isvector(arg) && length(arg) > 2) ? false : true
is_waterfall = ((val = hlp_desnany_str(d, [:stack :stacked], false)) !== "" && startswith(val, "water"))
if (is_waterfall)
isa(arg, Vector) && (arg = reshape(arg, 1, length(arg))) # Waterfall stacks must be matrices
(arg[1] != 0) && (arg = hcat(repeat([1.0],size(arg,1)), arg)) # If first el != 0 assume coord is missing
do_cat = false
elseif (haskey(d, :xticks))
arg = hcat(1:size(arg,1), arg)
do_cat = false
end
if (do_cat) arg = Float64.(cat_1_arg(arg)) end # If ARG is a vector, prepend it with a 1:N x column
common_plot_xyz("", mat2ds(arg), "bar", first, false, d)
end
bar!(cmd0::String="", arg=nothing; kw...) = bar(cmd0, arg; first=false, kw...)
function bar(f::Function, range_x=nothing; first=true, kw...)
rang = gen_coords4funs(range_x, "x"; kw...)
bar("", cat_2_arg2(rang, [f(x) for x in rang]); first=first, kw...)
end
bar!(f::Function, rang=nothing; kw...) = bar(f, rang; first=false, kw...)
bar(arg1, arg2; first=true, kw...) = common_plot_xyz("", cat_2_arg2(arg1, Float64.(arg2), true), "bar", first, false; kw...)
bar!(arg1, arg2; kw...) = common_plot_xyz("", cat_2_arg2(arg1, Float64.(arg2), true), "bar", false, false; kw...)
bar(arg; kw...) = bar("", arg; kw...)
bar!(arg; kw...) = common_plot_xyz("", cat_1_arg(arg, true), "bar", false, false; kw...)
# ------------------------------------------------------------------------------------------------------
"""
bar3(cmd0::String="", arg1=nothing; kwargs...)
Read a grid file, a grid or a MxN matrix and plots vertical bars extending from base to z.
- $(_opt_J)
- $(_opt_R)
- $(_opt_B)
- **fill** :: [Type => Str] ``key=color``
Select color or pattern for filling the bars
- **base** :: [Type => Str | Num] ``key=value``
By default, base = ymin. Use this option to change that value. If base is not appended then we read it.
- $(_opt_p)
- $(opt_savefig)
Example:
G = gmt("grdmath -R-15/15/-15/15 -I0.5 X Y HYPOT DUP 2 MUL PI MUL 8 DIV COS EXCH NEG 10 DIV EXP MUL =");
bar3(G, lw=:thinnest, show=true)
"""
function bar3(cmd0::String="", arg=nothing; first=true, kwargs...)
# Contrary to "bar" this one has specific work to do here.
d = KW(kwargs)
opt_z::String = ""
if (isa(arg, Array{<:GMTdataset,1})) arg1 = arg[1] # It makes no sense accepting > 1 datasets
else arg1 = arg # Make a copy that may or not become a new thing
end
if (isa(arg1, Array))
ny, nx = size(arg1)
if ((nx >= 3 && ny > 3)) arg1 = mat2grid(arg1) end # Assume it is a 'bare grid'
elseif (cmd0 != "")
if ((val = find_in_dict(d, [:grd :grid])[1]) !== nothing)
arg1 = gmtread(cmd0, grd=true)
elseif ((val = find_in_dict(d, [:dataset :table])[1]) !== nothing)
arg1 = gmtread(cmd0, dataset=true)
else
error("BAR3: When first arg is a name, must also state its type. e.g. grd=true or dataset=true")
end
end
opt_base = add_opt(d, "", "", [:base]) # No need to purge because base is not a psxy option
opt_R = ""
if (isa(arg1, GMTgrid))
if (haskey(d, :bar))
opt_S::String = parse_bar_cmd(d, :bar, "", "So")[1]
else
# 0.85 is the % of inc width of bars
w1::Float64, w2::Float64 = arg1.inc[1]*0.85, arg1.inc[2]*0.85
opt_S = " -So$(w1)u/$(w2)u"
if (haskey(d, :nbands)) opt_z = string("+z", d[:nbands]); delete!(d, :nbands)
elseif (haskey(d, :Nbands)) opt_z = string("+Z", d[:Nbands]); delete!(d, :Nbands)
end
end
opt_R, = parse_R(d, "", O=!first)
if (opt_R == "" || opt_R == " -R") # OK, no R but we know it here so put it in 'd'
if (arg1.registration == 1) # Fine, grid is already pixel reg
push!(d, :R => arg1.range)
else # Need to get a pix reg R
range = deepcopy(arg1.range)
range[1] -= arg1.inc[1] / 2; range[2] += arg1.inc[1] / 2;
range[3] -= arg1.inc[2] / 2; range[4] += arg1.inc[2] / 2;
push!(d, :R => range)
end
z_min = arg1.range[5]
elseif (opt_base == "") # Shit, need to get zmin out of the opt_R string
t = split(opt_R, '/')
(length(t) == 6) ? z_min = t[5] : error("For 3D cases, region must have 6 selements")
end
(opt_base == "") ? push!(d, :base => 0) : push!(d, :base => opt_base)
arg1 = gmt("grd2xyz", arg1) # Now arg1 is a GMTdataset
else
opt_S = parse_I(d, "", [:S :width], "So", true)
if (opt_S == "")
opt_S = parse_bar_cmd(d, :bar, "", "So"; no_u=true)[1]
end
if (opt_S == "")
if ((isa(arg1, Array) && size(arg1,2) < 5) || (isa(arg1, GMTdataset) && size(arg1.data,2) < 5))
error("BAR3: When NOT providing *width* data must contain at least 5 columns.")
end
end
#if (opt_S != "" && !isletter(opt_S[end])) opt_S = opt_S * 'u' end
if (haskey(d, :nbands)) opt_z = string("+z", d[:nbands]); delete!(d, :nbands)
elseif (haskey(d, :Nbands)) opt_z = string("+Z", d[:Nbands]); delete!(d, :Nbands)
end
end
opt_base::String = add_opt(d, "", "", [:base]) # Do this again because :base may have been added above
if (opt_base == "")
_z_min::Float32 = (isa(arg1, Array)) ? minimum(view(arg1, :, 3)) : minimum(view(arg1.data, :, 3))
opt_S *= "+b$_z_min"
else
opt_S *= "+b" * opt_base
end
(opt_R != "") && (d[:R] = opt_R[4:end])
common_plot_xyz("", mat2ds(arg1), "bar3|" * opt_S * opt_z, first, true, d)
end
bar3(arg1; kw...) = bar3("", arg1; first=true, kw...)
bar3!(cmd0::String="", arg1=nothing; kw...) = bar3(cmd0, arg1; first=false, kw...)
bar3!(arg1; kw...) = bar3("", arg1; first=false, kw...)
# ------------------------------------------------------------------------------------------------------
"""
lines(cmd0::String="", arg1=nothing; decorated=(...), kwargs...)
Read a file or (x,y) pairs and plot a collection of different line with decorations
- $(_opt_B)
- $(_opt_J)
- $(_opt_R)
- **W** | **pen** | **line_attrib** :: [Type => Str]
Set pen attributes for lines or the outline of symbols
- $(opt_savefig)
Examples:
lines([0, 10]; [0, 20], limits=(-2,12,-2,22), proj="M2.5", pen=1, fill=:red,
decorated=(dist=(val=1,size=0.25), symbol=:box), show=true)
lines(x -> cos(x) * x, y -> sin(y) * y, linspace(0,2pi,100), region=(-4,7,-5.5,2.5), lw=2, lc=:sienna,
decorated=(quoted=true, const_label=" In Vino Veritas - In Aqua, Rãs & Toads", font=(25,"Times-Italic"),
curved=true, pen=(0.5,:red)), aspect=:equal, fmt=:png, show=true)
"""
lines(cmd0::String="", arg1=nothing; first=true, kwargs...) = common_plot_xyz(cmd0, mat2ds(arg1), "lines", first, false; kwargs...)
lines!(cmd0::String="", arg1=nothing; kw...) = lines(cmd0, arg1; first=false, kw...)
function lines(f::Function, rang=nothing; first=true, kw...)
rang = gen_coords4funs(rang, "x"; kw...)
lines("", cat_2_arg2(rang, [f(x) for x in rang]); first=first, kw...)
end
lines!(f::Function, rang=nothing; kw...) = lines(f, rang; first=false, kw...)
function lines(f1::Function, f2::Function, rang=nothing; first=true, kw...) # Parametric version
lines("", help_parametric_2f(f1, f2, rang; is3D=false, kw...); first=first, kw...)
end
lines!(f1::Function, f2::Function, rang=nothing; kw...) = lines(f1, f2, rang; first=false, kw...)
lines(arg1, arg2; kw...) = lines("", cat_2_arg2(arg1, arg2); first=true, kw...)
lines!(arg1, arg2; kw...) = lines("", cat_2_arg2(arg1, arg2); first=false, kw...)
lines(arg; kw...) = lines("", cat_1_arg(arg, true); first=true, kw...)
lines!(arg; kw...) = lines("", cat_1_arg(arg, true); first=false, kw...)
# ------------------------------------------------------------------------------------------------------
# fill_between(D, fill="blue@70,brown@80", lt=1, ls=:dot, show=1)
# fill_between(D, fill="blue@70,brown@80", lt=1, ls=:dot, show=1)
# fill_between(D, lt=1, ls=:dot, lc=:green, show=1)
# fill_between([theta y1], [theta y2], legend=(labels=(:Aa,:Vv), pos=:TL, box=:none), show=1)
# fill_between([theta y1], [theta y2], white=true, show=1)
"""
fill_between(D1 [,D2]; kwargs...)
Fill the area between two horizontal curves.
The curves are defined by the points (x, y1, y2) in matrix or GMTdataset `D1`. This creates one or
multiple polygons describing the filled area. Alternatively, give a second matrix, `D2` (or a scalar y=cte)
and the polygons are constructed from the intersections of curves `D1` and `D2`.
- `fill_between(..., fill=colors)`: Give a list with two colors to paint the 'up' and 'down' polygons.
- `fill_between(..., fillalpha=[alpha1,alpha2])`: Sets the transparency of the two sets of polygons (default 60%).
- `fill_between(..., pen=...)`: Sets pen specifications for the two curves. Easiest is to use the shortcuts
`lt`, `lc` and `ls` for the line thickness, color and style like it is used in the `plot()` module.
- `fill_between(..., stairs=true)`: Plot stairs curves instead.
- `fill_between(..., markers=true)`: Add marker points at the data locations.
- `fill_between(..., white=true)`: Draw a thin white border between the curves and the fills.
- `fill_between(..., labels=...)`: Pass labels to use in a legend.
- `labels=true` wil use the column names in `D1` and `D2`.
- `labels="Lab1,Lab2"` or `labels=["Lab1","Lab2"]` (this one can be a Tuple too) use the text in `Lab1`, `Lab2`.
- `fill_between(..., legend=...)`: If used as the above `labels` it behaves like wise, but its argument can
also be a named tuple with `legend=(labels="Lab1,Lab2", position=poscode, box=(...))`.
Example:
theta = linspace(-2π, 2π, 150);
y1 = sin.(theta) ./ theta;
y2 = sin.(2*theta) ./ theta;
fill_between([theta y1], [theta y2], white=true, legend="Sinc1,Sinc2", show=1)
"""
fill_between(fname::String; first::Bool=true, kw...) = fill_between(gmtread(fname); first=first, kw...)
fill_between!(arg1, arg2=nothing; kw...) = fill_between(arg1, arg2; first=false, kw...)
function fill_between(arg1, arg2=nothing; first=true, kw...)
d = init_module(false, kw...)[1]
fill_between(arg1, arg2, first, d)
end
function fill_between(arg1, arg2, first::Bool, d::Dict{Symbol, Any})
function find_the_pos(x, x_int)
len_x = length(x)
_ind = zeros(Int, length(x_int))
n = 0
for k = 1:numel(x_int)
while(n < len_x && x[n+=1] < x_int[k]) end
_ind[k] = n-1
end
return _ind
end
function fish_labels(bal, legs, one_array, D1, D2)
# See if we have labels to use in legend or asked to use column names.
if (isa(bal, Bool) && bal) legs = one_array ? [D1.colnames[2], D1.colnames[3]] : [D1.colnames[2], D2.colnames[2]]
elseif (isempty(legs) && isa(bal, String) && contains(bal,",")) legs = [string.(split(bal,","))...]
elseif (isempty(legs) && isa(bal, Tuple) || isa(bal, Array) && length(bal) > 1) legs = [string(bal[1]), string(bal[2])]
end
return legs
end
fc = helper_ds_fill(d) # Got fill colors?
if (!isempty(fc))
!any(contains.(fc, "@")) && (fc .*= "@60") # If no transparency provided default to 60%
(length(fc) == 1) && (append!(fc, ["white@100"])) # May need two fill colors, so generalize it.
fill_colors = fc
else
fill_colors = ["darkgreen@60", "darkred@60"]
end
# Deal with pen line specifications
if ((lc = add_opt_pen(d, [:W, :pen])) != "") # Actualy a lc, lt, ls but one cal only change lt and ls
if !contains(lc, ",") # Just a line thickness
l_colors = [string(lc,",",split(fill_colors[1], "@")[1]), string(lc,",",split(fill_colors[2], "@")[1])]
elseif (contains(lc ,",,")) # A ,,ls or lt,,ls
if (startswith(lc ,",,")) # ,,ls
l_colors = [string("0.5,",split(fill_colors[1], "@")[1], ",",lc[3:end]), string("0.5,",split(fill_colors[2], "@")[1], ",",lc[3:end])]
else # lt,,ls
l_colors = [string(split(lc, ",")[1], ",", split(fill_colors[1], "@")[1], ",", split(lc,",")[3]), string(split(lc, ",")[1], ",", split(fill_colors[2], "@")[1], ",", split(lc,",")[3])]
end
elseif ((nc = count_chars(lc, ',')) != 0)
if (nc == 1) # must be a ,lc or a lt,lc
if (lc[1] == ',') # a ,lc
l_colors = [lc, lc]
else
l_colors = [string(lc, ",", split(fill_colors[1], "@")[1]), string(lc, ",", split(fill_colors[2], "@")[1])]
end
else # must be nc == 2 => lt,lc,ls.
l_colors = [lc, lc]
end
end # There should be no ELSE branch
else
l_colors = [string("0.5,",split(fill_colors[1], "@")[1]), string("0.5,",split(fill_colors[2], "@")[1])]
end
one_array = (arg2 === nothing)
D1 = mat2ds(arg1)
if (isa(arg2, Real)) D2 = mat2ds([D1.data[1] arg2; D1.data[end,1] arg2]); set_dsBB!(D2) # 2nd line is y = arg2
else D2 = (!one_array) ? mat2ds(arg2) : GMTdataset()
end
if (one_array)
int = gmtspatial((D1[:,[1,2]], D1[:,[1,3]]), intersections=:e, sort=true)
if (isempty(int)) # Hmm so lines do not cross. Just create the polygon and we are done.
ff = (D1.ds_bbox[6] > D1.ds_bbox[4]) ? 2 : 1 # If second curve is above first, swapp fill color
Dsd = mat2ds([D1[:,[1,2]]; D1[end:-1:1, [1:3]]], fill=fill_colors[ff])
@goto no_crossings # What a delicious relict from past
end
ind = find_the_pos(view(D1, :, 1), view(int, :, 1)) # Indices of the points before the intersections
n_crossings = size(ind,1)
Dsd = Vector{GMTdataset{Float64,2}}(undef, n_crossings+1)
ff = (D1[1,2] < D1[1,3]) ? 2 : 1
Dsd[1] = mat2ds([D1[1:ind[1], [1,2]]; int[1:1,1:2]; D1[ind[1]:-1:1, [1,3]]], fill=fill_colors[ff])
for k = 2:n_crossings
s, e = ind[k-1], ind[k]
fillColor = (D1[ind[k]-1,2] >= D1[ind[k]-1,3]) ? fill_colors[1] : fill_colors[2]
Dsd[k] = mat2ds([int[k-1:k-1,1:2]; D1[s:e, [1,2]]; int[k:k,1:2]; D1[e:-1:s, [1,3]]], fill=fillColor)
end
k = n_crossings
s, e = ind[k], size(D1, 1)
fillColor = (D1[end,2] >= D1[end,3]) ? fill_colors[1] : fill_colors[2]
Dsd[n_crossings+1] = mat2ds([int[k:k,1:2]; D1[s:e, [1,2]]; D1[e:-1:s, [1,3]]], fill=fillColor)
else
int = gmtspatial((D1, D2), intersections=:e, sort=true)
if (isempty(int)) # Hmm so lines do not cross. Just create the polygon and we are done.
ff = (D2.ds_bbox[4] > D1.ds_bbox[4]) ? 2 : 1 # If second curve is above first, swapp fill color
Dsd = mat2ds([D1.data; D2.data[end:-1:1, :]], fill=fill_colors[ff])
@goto no_crossings # What a delicious relict from past
end
ind1 = find_the_pos(view(D1, :, 1), view(int, :, 1)) # Indices of the points before the intersections at line 1
ind2 = find_the_pos(view(D2, :, 1), view(int, :, 1)) # Indices of the points before the intersections at line 2
n_crossings = size(int,1)
Dsd = Vector{GMTdataset{Float64,2}}(undef, n_crossings+1)
ff = (D1[1,2] < D2[1,2]) ? 2 : 1
Dsd[1] = mat2ds([D1[1:ind1[1], [1,2]]; int[1:1,1:2]; D2[ind2[1]:-1:1, [1,2]]], fill=fill_colors[ff])
for k = 2:n_crossings
s1, e1, s2, e2 = ind1[k-1], ind1[k], ind2[k-1], ind2[k]
fillColor = (D1[ind1[k]-1,2] >= D2[max(1,ind2[k]-1),2]) ? fill_colors[1] : fill_colors[2]
Dsd[k] = mat2ds([int[k-1:k-1,1:2]; D1[s1:e1, [1,2]]; int[k:k,1:2]; D2[e2:-1:s2, [1,2]]], fill=fillColor)
end
k = n_crossings
s1, e1, s2, e2 = ind1[k], size(D1, 1), ind2[k], size(D2, 1)
fillColor = (D1[end,2] >= D2[end,2]) ? fill_colors[1] : fill_colors[2]
Dsd[n_crossings+1] = mat2ds([int[k:k,1:2]; D1[s1:e1, [1,2]]; D2[e2:-1:s2, [1,2]]], fill=fillColor)
end
@label no_crossings
set_dsBB!(Dsd)
(get(D1.attrib, "Timecol", "") == "1") && (Dsd[1].attrib["Timecol"] = "1") # Try to keep an eventual Timecol
Vd = haskey(d, :Vd) ? d[:Vd] : -1
do_show = ((val = find_in_dict(d, [:show])[1]) !== nothing && val != 0)
do_markers = ((val = find_in_dict(d, [:markers])[1]) !== nothing && val != 0)
do_stairs = ((val = find_in_dict(d, [:stairs])[1]) !== nothing && val != 0)
legs, lab_pos::String, lab_box = String[], "", nothing
((val = find_in_dict(d, [:labels])[1]) !== nothing) && (legs = fish_labels(val, legs, one_array, D1, D2))
if (isempty(legs) && (val = find_in_dict(d, [:leg :legend])[1]) !== nothing) # OK, so this likely means a legend location
legs = fish_labels(val, legs, one_array, D1, D2)
if (isempty(legs) && isa(val, NamedTuple)) # Must break & complicate because here a setting applies to 2 lines
dd = nt2dict(val)
lab_pos = ((val = find_in_dict(dd, [:pos :position])[1]) !== nothing) ? string(val) : "" # Legend position
((val = find_in_dict(dd, [:label :labels])[1]) !== nothing) && (legs = fish_labels(val, legs, one_array, D1, D2))
((val = find_in_dict(dd, [:box])[1]) !== nothing) && (lab_box = val)
end
end
border = 0.0
if (find_in_dict(d, [:white :witeborder])[1] !== nothing)
_lt = break_pen(scan_opt(l_colors[1], "-W"))[1]
border = (_lt == "") ? 1.5 : size_unit(_lt)+1.0
end
# ---------------------------------- Plot the patches ---------------------------------------
do_stairs && (d[:A] = "y")
common_plot_xyz("", Dsd, "", first, false, d) # The patches
do_stairs && (delete!(d, :A))
delete!(d, [[:theme], [:figsize], [:frame], [:xaxis], [:yaxis]]) # To not repeat -B -J
# -------------------------------------------------------------------------------------------
_D2 = one_array ? mat2ds(D1, (:,[1,3])) : D2 # Put second line in a unique var
if (border > 0) # Plot a white border
one_array ? common_plot_xyz("", [mat2ds(D1, (:,[1,2])), _D2], "lines", false, false, Dict{Symbol,Any}(:W => "$(border),white", :Vd => Vd)) :
common_plot_xyz("", [D1, _D2], "lines", false, false, Dict{Symbol,Any}(:W => "$(border),white", :Vd => Vd))
end
do_stairs && (d[:stairs_step] = :pre)
d[:W], d[:Vd] = l_colors[1], Vd
do_markers && (d[:marker] = :point; d[:mc] = string(split(fill_colors[1], "@")[1]))
!isempty(legs) && (d[:legend] = legs[1])
common_plot_xyz("", D1, "lines", false, false; d...) # This d... must be. D cant be consumed at this point
do_stairs && (d[:stairs_step] = :post)
d[:W], d[:Vd] = l_colors[2], Vd
do_markers && (d[:marker] = :point; d[:mc] = string(split(fill_colors[2], "@")[1]))
!isempty(legs) && (d[:legend] = (lab_pos == "") ? legs[2] : (label=legs[2], pos=lab_pos, box=lab_box))
d[:show] = do_show
common_plot_xyz("", _D2, "lines", false, false, d)
#=
d[:marker] = "c"
d[:ms] = "1p"
d[:mc] = :black
d[:Vd] = Vd
d[:show] = do_show
common_plot_xyz("", int, "", false, false, d)
=#
end
# ------------------------------------------------------------------------------------------------------
"""
stairs(cmd0::String="", arg1=nothing; step=:post, kwargs...)
Plot a stair function. The `step` parameter can take the following values:
`:post` - The default. Lines move first along x for cartesian plots or the parallels for geographic
and then along y or the meridians.
`:pre` - Lines move first along y for cartesian plots or the meridians for geographic
and then along x or the parallels.
Example:
x = linspace(0, 4*pi, 50);
stairs(x, sin.(x), show=true)
"""
function stairs(cmd0::String="", arg1=nothing; first=true, step=:post, kwargs...)
d = KW(kwargs)
d[:stairs_step] = step
lines(cmd0, arg1; first=first, d...)
end
stairs!(cmd0::String="", arg1=nothing; step=:post, kw...) = stairs(cmd0, mat2ds(arg1); first=false, step=step, kw...)
stairs(arg; step=:post, kw...) = stairs("", cat_1_arg(arg, true); step=step, kw...)
stairs!(arg; step=:post, kw...) = stairs("", cat_1_arg(arg, true); first=false, step=step, kw...)
stairs(arg1, arg2; step=:post, kw...) = stairs("", cat_2_arg2(arg1, arg2, true); step=step, kw...)
stairs!(arg1, arg2; step=:post, kw...) = stairs("", cat_2_arg2(arg1, arg2, true); first=false, step=step, kw...)
# ------------------------------------------------------------------------------------------------------
function helper_input_ds(d::Dict, cmd0::String="", arg1=nothing)
# Block common to some functions. Read the file if cmd0 != "" and takes care of "select by col"
# if arg1 is a GMTdataset (or a vector of them).
(cmd0 != "") && (arg1 = read_data(d, cmd0, "", nothing, "", false, true)[2])
isa(arg1, GMTdataset) && (arg1 = with_xyvar(d, arg1)) # It's not implemented for GMTdataset vectors
#(isa(arg1, Matrix) && size(arg1,2) > 2 && find_in_dict(d, [:multicol])[1] !== nothing) && (arg1 = mat2ds(arg1, multi=true, color="yes"))
(isa(arg1, GMTdataset) && size(arg1,2) > 2 && find_in_dict(d, [:multi :multicol])[1] !== nothing) && (arg1 = ds2ds(arg1, multi=true, fill=(haskey(d, :fill) ? d[:fill] : true)))
haveVarFill = (haskey(d, :fill) && d[:fill] == true) # Probably no longer true
haveR = (find_in_dict(d, [:R :region :limits :region_llur :limits_llur :limits_diag :region_diag :xlim :xlims :xlimits], false)[1] !== nothing)
return arg1, haveR, haveVarFill
end
# ------------------------------------------------------------------------------------------------------
"""
stem(cmd0::String="", arg1=nothing; kwargs...)
Example:
Y = linspace(-2*pi,2*pi,50);
stem([Y Y], show=true)
stem(Y,[Y -Y], multicol=true, fill=true, show=true)
"""
function stem(cmd0::String="", arg1=nothing; first=true, kwargs...)
d = KW(kwargs)
arg1, haveR, haveVarFill = helper_input_ds(d, cmd0, arg1)
if (isGMTdataset(arg1))
# OK, so now we have a GMTdataset or a vector of them. Must create new ones with extra columns.
if (isa(arg1, GMTdataset))
(!haveR) && (mimas = arg1.bbox[1:4])
arg1.data = [view(arg1,:,1) zeros(size(arg1,1)) view(arg1,:,1) view(arg1,:,2)]
add2ds!(arg1) # Fix arg1 meta after column insertion.
else
(!haveR) && (mimas = arg1[1].ds_bbox[1:4])
for a in arg1 # Loop to create the new arrays and assign fill color if needed.
a.data = [a[:,1] zeros(size(a,1)) a[:,1] a[:,2]]
(haveVarFill && (ind = findfirst(" -W,", a.header)) !== nothing) && (a.header *= " -G" * a.header[ind[end]+1:end])
end
add2ds!(arg1[1]) # Fix arg1 meta after column insertion.
end
else # Case of plain matrices
isempty(arg1) && error("stem: 'arg1' cannot be empty.")
if (!haveR)
mm = extrema(arg1, dims=1)
mimas = [mm[1][1], mm[1][2], mm[2][1], mm[2][2]]
end
arg1 = [arg1[:,1] zeros(size(arg1,1)) arg1[:,1] arg1[:,2]]
end
if (!haveR)
t = round_wesn(mimas) # Add a pad. Also sets the CTRL.limits plot values
(t[3] > 0) && (t[3] = 0) # Stems, when all positives, must start at zero
CTRL.limits[1:4] = mimas # These are the data limits
opt_R::String = @sprintf(" -R%.12g/%.12g/%.12g/%.12g", t[1], t[2], t[3], t[4])
_opt_R::String = merge_R_and_xyzlims(d, opt_R) # See if a x or ylim is used
if (_opt_R != opt_R) # Yes, it was so need to update the plot limits in CTRL.limits
CTRL.limits[7:10], opt_R = opt_R2num(_opt_R), _opt_R
end
d[:R] = opt_R[4:end]
end
len = ((val = find_in_dict(d, [:ms :markersize :MarkerSize :size])[1]) !== nothing) ? arg2str(val)::String : "8p"
d[:S] = "v$(len)+ec+s"
_show = false
if (!haskey(d, :nobaseline) && haskey(d, :show)) # If baseline we need to use the true show only at hline!()
_show = d[:show] != 0; d[:show]=false # Backup the :show val
end
MULTI_COL[] = false # Some cat_2_arg2 paths set it to true, wich cannot happen in this function
have_baseline = ((find_in_dict(d, [:nobaseline])[1]) === nothing)
out1, out2 = common_plot_xyz("", mat2ds(arg1), "stem", first, false, d), nothing
(have_baseline) && (out2 = hlines!(0.0, show=_show)) # See if we have a no-baseline request
(out1 !== nothing && out2 !== nothing) ? [out1;out2] : ((out1 !== nothing) ? out1 : out2)
end
stem!(cmd0::String="", arg1=nothing; kw...) = stem(cmd0, arg1; first=false, kw...)
stem(arg; kw...) = stem("", cat_1_arg(arg, true); kw...)
stem!(arg; kw...) = stem("", cat_1_arg(arg, true); first=false, kw...)
stem(arg1, arg2; kw...) = stem("", cat_2_arg2(arg1, arg2, true); kw...)
stem!(arg1, arg2; kw...) = stem("", cat_2_arg2(arg1, arg2, true); first=false, kw...)
# ------------------------------------------------------------------------------------------------------
"""
arrows(cmd0::String="", arg1=nothing; arrow=(...), kwargs...)
Plot an arrow field.
When the keyword `arrow=(...)` or `vector=(...)` is used, the direction (in degrees counter-clockwise
from horizontal) and length must be found in columns 3 and 4, and size, if not specified on the command-line,
should be present in column 5. The size is the length of the vector head. Vector stem width is set by
option `pen` or `line_attrib`.
The `vecmap=(...)` variation is similar to above except azimuth (in degrees east of north) should be
given instead of direction. The azimuth will be mapped into an angle based on the chosen map projection.
If length is not in plot units but in arbitrary user units (e.g., a rate in mm/yr) then you can use the
*input_col* option to scale the corresponding column via the +sscale modifier.
The `geovec=(...)` or `geovector=(...)` keywords plot geovectors. In geovectors, azimuth (in degrees east
from north) and geographical length must be found in columns 3 and 4. The size is the length of the vector
head. Vector width is set by `pen` or `line_attrib`. Note: Geovector stems are drawn as thin filled polygons
and hence pen attributes like dashed and dotted are not available. For allowable geographical units, see
the `units=()` option.