GenericMappingTools / GMT.jl

Generic Mapping Tools Library Wrapper for Julia
Other
191 stars 28 forks source link

Different outcomes on specifying legends #1406

Closed melodyjulia closed 3 months ago

melodyjulia commented 3 months ago

I tried three ways to specify legend, but got three different appearances.

First:

x = collect(1:10)
gmtbegin()
    basemap(limits=(1,10,-1,1),figsize=(6,4),frame=:a)
    lines(x,sin.(x),legend="sin(x)")
    lines(x,cos.(x),legend="cos(x)")
    legend(fontsize=6,position=(inside="RT",width=1.5))
gmtend(:show)

Second:

x = collect(1:10)
basemap(limits=(1,10,-1,1),figsize=(6,4),frame=:a)
lines!(x,sin.(x),legend="sin(x)")
lines!(x,cos.(x),legend="cos(x)")
legend!(fontsize=6,position=(inside="RT",width=1.5), show=true)

Third:

x = collect(1:10)
basemap(limits=(1,10,-1,1),figsize=(6,4),frame=:a)
lines!(x,sin.(x),legend="sin(x)")
lines!(x,cos.(x),legend=(label="cos(x)",fontsize=6,position=(inside="RT",width=1.5)), show=true)

The first way gives warning as

Warning: the following options were not consumed in legend => [:fontsize]

The second way gives warning as

pslegend [WARNING]: File <stdin> is empty!

Only the third method can set correct font size for the labels in legend.

BTW, the appearance of the figure produced by the first method is very different from that from the other two methods, it seems they use different settings.

first second third
joa-quim commented 3 months ago

The answer tho those goes back into GMT itself. In GMT (not the wrapper) we have the classic mode and the modern mode (man pages explain in depth the differences). In GMT.jl whenever we directly use gmtbegin or indirectly subplot and inset we are using GMT's modern mode and it has its own set of defaults. For example it annotates all 4 sides. The legend option inside the plotting modules of a modern mode (-l in plain GMT) is a wrapper of the legend module and intends to greatly simplify its usage.

On the other hand, in GMT.jl most of cases (those that do not use gmtbegin) use a kind of re-implementation of the modern mode but in plain Julia. The settings are not exactly equal (default annotates 2 sides and axes line thicknesses are a bit thinner. The legend option (not the module) is also less powerful and sometimes has difficulties in finding the true string length (a difficult problem that GMT only solves inside the PostScript language).

So, maybe a can fix that, but in your first example you should control the font size as mentioned in the legend manual. That is

legend(par=(FONT_ANNOT_PRIMARY=6,), position=(inside="RT",width=1.5))

The second case is more obscure for me now. For me on Windows that example (the legend command) actually hangs Julia. I need to find out what is happening in this case. It's weird because a much more complex example shown in its man page works fine.

Basically, I do not use much the gmtbegin/gmtend construct and the default have remain a bit different. I may be able to change that in a clean way, but it needs investigation.

melodyjulia commented 3 months ago

Thanks for your explanation. Although the third way works, but it seems the second way is more syntax friendly and clear.

joa-quim commented 3 months ago

If you try the master version case 1 is solved (now looks like 3). Case 2 is harder and I don't even understand how you could get a result. The point/confusion here is that legend is more than one thing. It is a module in itself, which is wrapped in modern mode by the legend option. This is the most versatile of all legend options. But legend is also an option for the plot family modules. In this case the number of possibilities is more reduced.

In you case 2 example you are calling legend in non-modern mode but passing no input data and that is why it complains that stdin is empty. For me on Windows that hangs Julia.

But with some convoluted gymnastics I made case 2 work too (also in master only for now) as long as it's called as legend! and previous commands had added entries to the legend fields (like your cases 2 & 3 do). Please try it.

melodyjulia commented 3 months ago

This is awesome! I can understand the complexity of legend now.

Using the master version, the figure of the first case has no labels for tick marks.

first

For the second case, the font size of legend labels cannot be set, neither of the following two ways work:

x = collect(1:10)
basemap(limits=(1,10,-1,1),figsize=(6,4),frame=:a,title="Second")
lines!(x,sin.(x),legend="sin(x)")
lines!(x,cos.(x),legend="cos(x)")
legend!(fontsize=3,position=(inside="RT",width=1.5),show=true)
x = collect(1:10)
basemap(limits=(1,10,-1,1),figsize=(6,4),frame=:a,title="Second")
lines!(x,sin.(x),legend="sin(x)")
lines!(x,cos.(x),legend="cos(x)")
legend!(par=(FONT_ANNOT_PRIMARY=3,),position=(inside="RT",width=1.5),show=true)

In addition, I noticed a minor issue on showing figure in the case 3, the following two methods showing figure produce different font sizes in the label of legend:

x = collect(1:10)
basemap(limits=(1,10,-1,1),figsize=(6,4),frame=:a,title="Third (show=true)")
lines!(x,sin.(x),legend="sin(x)")
lines!(x,cos.(x),legend=(label="cos(x)",fontsize=4,position=(inside="RT",width=1.5)),show=true)
x = collect(1:10)
basemap(limits=(1,10,-1,1),figsize=(6,4),frame=:a,title="Third (showfig)")
lines!(x,sin.(x),legend="sin(x)")
lines!(x,cos.(x),legend=(label="cos(x)",fontsize=4,position=(inside="RT",width=1.5)))
showfig()

Btw, the settings used by GMT.jl are more modern in my opinion, especially the thinner axes lines.

third1 third2
joa-quim commented 3 months ago

Hm, I can confirm the second issue but not the first. For me it plots the annotations. I don't see what it wouldn't as that is explicitly set in basemap(limits=(1,10,-1,1),figsize=(6,4),frame=:a)

melodyjulia commented 3 months ago

This is weird. It can be reproduced under both Linux and macOS with the master version. Is there any way I could output more information for diagnostic?

joa-quim commented 3 months ago

You can add V=true options in each command to have GMT print information, or even V=:d for debug messages but that is very verbose. When I add V=true in basemap I see:

basemap [INFORMATION]: Constructing the basemap
basemap [INFORMATION]: Linear projection implies y-axis distance exaggeration relative to the x-axis by a factor of 3
basemap [INFORMATION]: Auto-frame interval for x-axis (item 0): a2f2
basemap [INFORMATION]: Auto-frame interval for y-axis (item 0): a0.5f0.5
basemap [INFORMATION]: Map scale is 0.0015 km per cm or 1:150.

You can also add MAP_FRAME_TYPE="inside" like bellow to see if the annotations show up inside the frame

basemap(limits=(1,10,-1,1),figsize=(6,4),frame=:a, par=(MAP_FRAME_TYPE="inside",))
joa-quim commented 3 months ago

Indeed strange. It works for me on WSL too.

melodyjulia commented 3 months ago

I tried the following code snippet:

x = collect(1:10)
gmtbegin()
    basemap(limits=(1,10,-1,1),figsize=(6,4),frame=:a,title="First",V=true)
    lines(x,sin.(x),legend="sin(x)")
    lines(x,cos.(x),legend="cos(x)")
    legend(fontsize=6,position=(inside="RT",width=1.5))
gmtend(:show)

If running it in Julia REPL, it works and plots the annotations.

But if running it in Jupyter notebook, it doesn't work and outputs:

basemap [INFORMATION]: GMT_Parse_Options: Interval-setting -B options were reordered to appear before axis and frame -B options to ensure proper parsing.
basemap [INFORMATION]: GMT_Parse_Options: New option order: -R1[/10/-1/1](http://localhost:8888/10/-1/1) -JX6[/4](http://localhost:8888/4) -V -Ba -B+tFirst
basemap [INFORMATION]: Constructing the basemap
basemap [INFORMATION]: Linear projection implies y-axis distance exaggeration relative to the x-axis by a factor of 3
basemap [INFORMATION]: Auto-frame interval for x-axis (item 0): a2f2
basemap [INFORMATION]: Auto-frame interval for y-axis (item 0): a0.5f0.5
basemap [INFORMATION]: Map scale is 0.0015 km per cm or 1:150.
basemap [INFORMATION]: You should specify a paper size when requesting a PostScript file.
basemap [INFORMATION]: Changing paper size to A4, but we cannot know if this is adequate for your plot; better to use PS_MEDIA explicitly.
joa-quim commented 3 months ago

Yes, can confirm this too. The reason lies on what caused this messages to print

basemap [INFORMATION]: You should specify a paper size when requesting a PostScript file.
basemap [INFORMATION]: Changing paper size to A4, but we cannot know if this is adequate for your plot; better to use PS_MEDIA explicitly.

This makes no sense because we are not asking for a PS (PostScript) output, but somehow that request is sneaking in.

And, BTW, the showfig() issue is hopefully solved in master.

melodyjulia commented 3 months ago

Thanks very much! I tested with the master version and the showfig() issue is gone.

joa-quim commented 3 months ago

I hope I've solved the First issue too, but this guy proved to be much more tricky than anticipated so side effects may arise. In master too.

melodyjulia commented 3 months ago

It works in the master version now! Cheers!

joa-quim commented 3 months ago

Released that all in 1.12.2

melodyjulia commented 3 months ago

I've tested with 1.12.2, all above cases are handled charmingly except for setting font size for legend label via the following two ways:

x = collect(1:10)
basemap(limits=(1,10,-1,1),figsize=(6,4),frame=:a,title="Second")
lines!(x,sin.(x),legend="sin(x)")
lines!(x,cos.(x),legend="cos(x)")
legend!(fontsize=3,position=(inside="RT",width=1.5))
showfig()

and

x = collect(1:10)
basemap(limits=(1,10,-1,1),figsize=(6,4),frame=:a,title="Second")
lines!(x,sin.(x),legend="sin(x)")
lines!(x,cos.(x),legend="cos(x)")
legend!(par=(FONT_ANNOT_PRIMARY=3,),position=(inside="RT",width=1.5))
showfig()
joa-quim commented 3 months ago

It was not only the font size that could not be changed but all parameters set in the legend!(...) call. Again, tentatively fixed in master. But note that the par=(FONT_ANNOT_PRIMARY=3,) form is not supported here.

melodyjulia commented 3 months ago

Thanks. It works now! The legend!(...) call is really cool!

joa-quim commented 3 months ago

All solved, I think.