|
| 1 | +""" |
| 2 | + compass(; kwargs...) |
| 3 | +
|
| 4 | +Draw a map directional rose or magnetic compass on the map. |
| 5 | +
|
| 6 | +If `dec` (magnetic declination) is provided, draws a magnetic compass rose (`-Tm`). |
| 7 | +Otherwise, draws a directional rose (`-Td`). |
| 8 | +
|
| 9 | +Can be called standalone (creates its own plot) or as an overlay (`compass!`) on an existing plot. |
| 10 | +When called standalone without `region`/`proj`, a default canvas sized to fit the rose is created |
| 11 | +automatically. |
| 12 | +
|
| 13 | +### Compass-specific options |
| 14 | +
|
| 15 | +- **anchor** :: [Type => Tuple | Str] — Reference point on the map for the rose. |
| 16 | +- **width** :: [Type => Number | Str] — Width of the rose in cm. |
| 17 | +- **justify** :: [Type => Str] — Justification of the rose relative to anchor. |
| 18 | +- **labels** or **label** :: [Type => Str] — Comma-separated labels for the cardinal points. |
| 19 | +- **offset** :: [Type => Tuple | Str] — Offset from the anchor point. |
| 20 | +- **map** | **inside** | **outside** | **norm** | **paper** :: [Type => Str] — Coordinate system for the anchor. |
| 21 | +
|
| 22 | +### Directional rose only (`-Td`) |
| 23 | +
|
| 24 | +- **fancy** :: [Type => Bool | Int] — Draw a fancy rose. 1-3 for different levels. |
| 25 | +
|
| 26 | +### Magnetic compass only (`-Tm`) |
| 27 | +
|
| 28 | +- **dec** :: [Type => Number | Str] — Magnetic declination. |
| 29 | +- **rose_primary** :: [Type => Tuple | Str] — Pen for the primary rose circle. |
| 30 | +- **rose_secondary** :: [Type => Tuple | Str] — Pen for the secondary rose circle. |
| 31 | +- **annot** :: [Type => Tuple | Str] — Annotation info for the magnetic compass. |
| 32 | +
|
| 33 | +All other keyword arguments (e.g., `region`, `proj`, `par`, `show`, `savefig`, `Vd`, etc.) |
| 34 | +are passed through to `basemap`. |
| 35 | +
|
| 36 | +### Examples |
| 37 | +
|
| 38 | +Draw a standalone directional rose: |
| 39 | +
|
| 40 | +```julia |
| 41 | +compass(width=2.5, fancy=true, labels=",,,N", show=true) |
| 42 | +``` |
| 43 | +
|
| 44 | +Draw a directional rose as overlay: |
| 45 | +
|
| 46 | +```julia |
| 47 | +coast(region=(-10,10,-10,10), proj=:Mercator, frame=:auto, land=:lightgray) |
| 48 | +compass!(width=2.5, anchor=(0,0), justify=:CM, fancy=true, labels=",,,N", show=true) |
| 49 | +``` |
| 50 | +
|
| 51 | +Draw a magnetic compass: |
| 52 | +
|
| 53 | +```julia |
| 54 | +basemap(region=(-8,8,-6,6), proj=:Mercator, frame=:auto) |
| 55 | +compass!(anchor=(0,0), width=6, dec=-14.5, annot=(45,10,5,30,10,2), |
| 56 | + rose_primary=(0.25,:blue), rose_secondary=0.5, labels="", show=true) |
| 57 | +``` |
| 58 | +""" |
| 59 | +compass!(; kw...) = compass(; first=false, kw...) |
| 60 | +function compass(; first=true, kw...) |
| 61 | + d = init_module(false, kw...)[1] # Also checks if the user wants ONLY the HELP mode |
| 62 | + compass(first, d) |
| 63 | +end |
| 64 | +function compass(first::Bool, d::Dict{Symbol, Any}) |
| 65 | + |
| 66 | + compass_keys = (:anchor, :width, :justify, :labels, :label, :offset, |
| 67 | + :map, :inside, :outside, :norm, :paper, |
| 68 | + :fancy, :dec, :rose_primary, :rose_secondary, :annot) |
| 69 | + |
| 70 | + nt_pairs = Pair{Symbol,Any}[] |
| 71 | + for k in compass_keys |
| 72 | + haskey(d, k) && push!(nt_pairs, k => pop!(d, k)) |
| 73 | + end |
| 74 | + |
| 75 | + is_magnetic = any(p -> p.first === :dec, nt_pairs) |
| 76 | + |
| 77 | + # When standalone (first=true) and no region/proj given, create a paper-coordinates canvas. |
| 78 | + has_RJ = any(k -> haskey(d, k), (:R, :region, :limits, :J, :proj, :projection)) |
| 79 | + if (first && !has_RJ) |
| 80 | + # Get the width to size the canvas (default 5 cm) |
| 81 | + w = 5.0 |
| 82 | + for p in nt_pairs |
| 83 | + if p.first === :width |
| 84 | + w = isa(p.second, Real) ? Float64(p.second) : 5.0 |
| 85 | + break |
| 86 | + end |
| 87 | + end |
| 88 | + sz = ceil(w * 1.6; digits=1) # Canvas slightly larger than the rose |
| 89 | + d[:region] = (0, sz, 0, sz) |
| 90 | + d[:proj] = "X$(sz)c" |
| 91 | + # Default anchor to center of canvas in paper coordinates if not provided |
| 92 | + has_anchor = any(p -> p.first === :anchor, nt_pairs) |
| 93 | + has_coord = any(p -> p.first in (:map, :inside, :outside, :norm, :paper), nt_pairs) |
| 94 | + if !has_anchor && !has_coord |
| 95 | + # No positioning at all — center the rose on the canvas |
| 96 | + push!(nt_pairs, :paper => "$(sz/2)/$(sz/2)") |
| 97 | + push!(nt_pairs, :justify => :CM) |
| 98 | + elseif has_anchor && !has_coord |
| 99 | + # User gave anchor but no coordinate system — use paper coords |
| 100 | + anc = pop_anchor!(nt_pairs) |
| 101 | + push!(nt_pairs, :paper => isa(anc, Tuple) ? join(anc, '/') : string(anc)) |
| 102 | + end |
| 103 | + end |
| 104 | + |
| 105 | + nt = (; nt_pairs...) |
| 106 | + |
| 107 | + if is_magnetic |
| 108 | + d[:compass] = nt # -> parse_Tm -> -Tm |
| 109 | + else |
| 110 | + d[:rose] = nt # -> parse_Td -> -Td |
| 111 | + end |
| 112 | + |
| 113 | + haskey(d, :frame) || haskey(d, :B) || (d[:frame] = :none) |
| 114 | + helper_basemap(!first, true, d) |
| 115 | +end |
| 116 | + |
| 117 | +# Helper to pop :anchor from nt_pairs and return its value |
| 118 | +function pop_anchor!(pairs::Vector{Pair{Symbol,Any}}) |
| 119 | + for i in eachindex(pairs) |
| 120 | + if pairs[i].first === :anchor |
| 121 | + val = pairs[i].second |
| 122 | + deleteat!(pairs, i) |
| 123 | + return val |
| 124 | + end |
| 125 | + end |
| 126 | + return nothing |
| 127 | +end |
0 commit comments