Skip to content

Commit ccf876f

Browse files
authored
Merge pull request #70 from GenericMappingTools/sattbx-extension
Make the SatelliteToolbox be an extension instead of a direct dependency.
2 parents 10d59b8 + 4d0a821 commit ccf876f

File tree

5 files changed

+135
-105
lines changed

5 files changed

+135
-105
lines changed

.github/workflows/ci.yml

+5-5
Original file line numberDiff line numberDiff line change
@@ -24,20 +24,20 @@ jobs:
2424
fail-fast: false
2525
matrix:
2626
version:
27-
- '1.8' # Replace this with the minimum Julia version that your package supports. E.g. if your package requires Julia 1.5 or higher, change this to '1.5'.
27+
- '1' # Replace this with the minimum Julia version that your package supports. E.g. if your package requires Julia 1.5 or higher, change this to '1.5'.
2828
#- '1' # Leave this line unchanged. '1' will automatically expand to the latest stable 1.x release of Julia.
2929
#- 'nightly'
3030
os:
31-
#- ubuntu-latest
32-
- ubuntu-20.04
31+
- ubuntu-latest
32+
#- ubuntu-20.04
3333
#- macos-latest
3434
arch:
3535
- x64
3636
include:
3737
# Linux
3838
- name: Linux - Compile only
39-
os: ubuntu-20.04
40-
#os: ubuntu-latest
39+
#os: ubuntu-20.04
40+
os: ubuntu-latest
4141
run_in_pr : true
4242

4343
steps:

Project.toml

+10-2
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,27 @@ GMT = "5752ebe1-31b9-557e-87aa-f909b540aa54"
88
Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7"
99
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
1010
Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
11+
PrecompileTools = "aea7be01-6a6a-4083-8856-8a6e6704d82a"
12+
13+
[weakdeps]
1114
SatelliteToolboxTle = "7ff27aeb-5fff-4337-a9ee-a9fe6b7ed35e"
1215
SatelliteToolboxPropagators = "c2b69894-ea78-4e2b-9ba6-cedbbc3d14d7"
1316
SatelliteToolboxTransformations = "6b019ec1-7a1e-4f04-96c7-a9db1ca5514d"
14-
PrecompileTools = "aea7be01-6a6a-4083-8856-8a6e6704d82a"
17+
18+
[extensions]
19+
RemoteSSatTbxExt = ["SatelliteToolboxTle", "SatelliteToolboxPropagators", "SatelliteToolboxTransformations"]
1520

1621
[extras]
1722
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
23+
SatelliteToolboxTle = "7ff27aeb-5fff-4337-a9ee-a9fe6b7ed35e"
24+
SatelliteToolboxPropagators = "c2b69894-ea78-4e2b-9ba6-cedbbc3d14d7"
25+
SatelliteToolboxTransformations = "6b019ec1-7a1e-4f04-96c7-a9db1ca5514d"
1826

1927
[targets]
2028
test = ["Test"]
2129

2230
[compat]
23-
julia = "1.6"
31+
julia = "1.9"
2432
GMT = "1.9"
2533
PrecompileTools = "1.0"
2634
SatelliteToolboxTle = "1"
+105
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
module RemoteSSatTbxExt
2+
using RemoteS, GMT, Dates
3+
using SatelliteToolboxTle, SatelliteToolboxPropagators, SatelliteToolboxTransformations
4+
using PrecompileTools
5+
6+
function RemoteS.sat_tracks_ext(; geocentric::Bool=false, tiles::Bool=false, position::Bool=false, kwargs...)
7+
(position && tiles) && error("Cannot require tiles and a single position. Makes no sense.")
8+
d = GMT.KW(kwargs)
9+
10+
start = ((val = find_in_dict(d, [:start])[1]) === nothing) ? now(Dates.UTC) : RemoteS.getitDTime(val)
11+
12+
(tiles) && (sat_name = get_sat_name(d))
13+
(tiles) && (start = round(start, Dates.Minute(5))) # This is for MODIS only
14+
15+
if ((val = find_in_dict(d, [:duration])[1]) !== nothing)
16+
if (isa(val, String)) # Accept duration in D(ays), h(ours), m(inutes) or s(econds)
17+
if (endswith(val,"D")) dur = Day(parse(Int, val[1:end-1]))
18+
elseif (endswith(val,"h")) dur = Hour(parse(Int, val[1:end-1]))
19+
elseif (endswith(val,"m")) dur = Minute(parse(Int, val[1:end-1]))
20+
elseif (endswith(val,"s")) dur = Second(parse(Int, val[1:end-1]))
21+
else error("Only 'D', 'h', 'm' or 's' are accepted in duration")
22+
end
23+
elseif (isa(val, Real)) # Assume duration was given in minutes
24+
dur = Minute(trunc(Int, val))
25+
end
26+
stop = start + dur
27+
else
28+
if ((val = find_in_dict(d, [:stop])[1]) !== nothing)
29+
stop = RemoteS.getitDTime(val)
30+
else
31+
stop = start + Minute(100) # Default is ~Terra period
32+
end
33+
end
34+
if ((val = find_in_dict(d, [:step :inc :dt])[1]) !== nothing) # Steps are in seconds
35+
if (isa(val, String))
36+
if (endswith(val,"m")) dt = parse(Int, val[1:end-1]) * 60
37+
elseif (endswith(val,"s")) dt = parse(Int, val[1:end-1])
38+
else error("Only 's' or 'm' are accepted in increment")
39+
end
40+
else
41+
dt = trunc(Int, val)
42+
end
43+
else
44+
dt = 30
45+
end
46+
47+
if ((val_tle = find_in_dict(d, [:tle_obj])[1]) !== nothing) tle = val_tle # Some other fun already got it
48+
else tle = RemoteS.loadTLE(d)
49+
end
50+
orbp = SatelliteToolboxPropagators.Propagators.init(Val(:SGP4), tle)
51+
52+
epoch_jd = orbp.sgp4d.epoch
53+
startmfe = (datetime2julian(DateTime(start)) - epoch_jd) * 24 * 3600
54+
stopmfe = (datetime2julian(DateTime(stop)) - epoch_jd) * 24 * 3600
55+
(tiles) && (dt = 60) # Arbitrary choice that works well for MODIS but may need revision for others
56+
t = startmfe:dt:stopmfe
57+
58+
(position) && (t = [t[1]]) # Single position. Doing it here wastes work above but code is way cleaner
59+
60+
out = Matrix{Float64}(undef, length(t), 4)
61+
#r = SatelliteToolboxPropagators.Propagators.propagate!.(orbp, t)[1] # Doesn't work. Why?
62+
63+
for n = 1:GMT.numel(t)
64+
r = SatelliteToolboxPropagators.Propagators.propagate!(orbp, t[n])[1]
65+
jd = epoch_jd + t[n] / (24 * 3600)
66+
tt = SatelliteToolboxTransformations.r_eci_to_ecef(SatelliteToolboxTransformations.TEME(), SatelliteToolboxTransformations.PEF(), jd) * r
67+
out[n,1], out[n,2], out[n,3], out[n, 4] = tt[1], tt[2], tt[3], jd
68+
end
69+
70+
if (tiles)
71+
out = mapproject(out, E=true, I=true)
72+
return make_sat_tiles(out[1].data, SCENE_HALFW[sat_name], sat_name)
73+
end
74+
75+
if (geocentric)
76+
D = mat2ds(out, geom=UInt32(2), colnames=["X", "Y", "Z", "JulianDay"])
77+
else
78+
D = mapproject(out, E=true, I=true)
79+
D.colnames, D.proj4, D.geom = ["lon", "lat", "alt", "JulianDay"], GMT.prj4WGS84, UInt32(2)
80+
end
81+
D
82+
end
83+
84+
# --------------------------------------------------------------------------------------------
85+
function RemoteS.loadTLE_ext(d::Dict)
86+
# Load a TLE or use a default one. In a function because it's used by two functions
87+
if ((val = find_in_dict(d, [:tle :TLE])[1]) !== nothing)
88+
if (isa(val, String)) tle = SatelliteToolboxTle.read_tle(val)
89+
elseif (isa(val, Vector{String}) && length(val) == 2)
90+
tle = SatelliteToolboxTle.read_tle(val[1], val[2])
91+
else
92+
error("BAD input TLE data")
93+
end
94+
else
95+
tle = SatelliteToolboxTle.read_tle("C:\\v\\AQUA.tle")
96+
end
97+
return tle
98+
end
99+
100+
@setup_workload begin
101+
RemoteS.sat_tracks_ext(tle=["1 27424U 02022A 21245.83760660 .00000135 00000-0 39999-4 0 9997"; "2 27424 98.2123 186.0654 0002229 67.6025 313.3829 14.57107527 28342"], duration=100, geocentric=true)
102+
RemoteS.sat_tracks_ext(position=true, tle=["1 27424U 02022A 21245.83760660 .00000135 00000-0 39999-4 0 9997"; "2 27424 98.2123 186.0654 0002229 67.6025 313.3829 14.57107527 28342"]);
103+
end
104+
105+
end

src/RemoteS.jl

-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
module RemoteS
22

33
using GMT, Printf, Statistics, Dates#, Requires
4-
using SatelliteToolboxTle, SatelliteToolboxPropagators, SatelliteToolboxTransformations
54
using PrecompileTools
65

76
const SCENE_HALFW = Dict("AQUA" => 1163479, "TERRA" => 1163479, "LANDSAT8" => 92500) # half widths
@@ -22,8 +21,6 @@ include("utils.jl")
2221
include("sat_tracks.jl")
2322

2423
@setup_workload begin
25-
sat_tracks(tle=["1 27424U 02022A 21245.83760660 .00000135 00000-0 39999-4 0 9997"; "2 27424 98.2123 186.0654 0002229 67.6025 313.3829 14.57107527 28342"], duration=100, geocentric=true)
26-
sat_tracks(position=true, tle=["1 27424U 02022A 21245.83760660 .00000135 00000-0 39999-4 0 9997"; "2 27424 98.2123 186.0654 0002229 67.6025 313.3829 14.57107527 28342"]);
2724
truecolor(mat2img(rand(UInt16,128,128)), mat2img(rand(UInt16,128,128)), mat2img(rand(UInt16,128,128)));
2825
get_MODIS_scene_name(datetime2julian(DateTime("2020-09-20")), "A");
2926
reportbands(mat2img(rand(UInt16, 4,4,3), names=["Band 1", "Band 2", "Band 3"]), 3)[1];

src/sat_tracks.jl

+15-95
Original file line numberDiff line numberDiff line change
@@ -37,84 +37,21 @@ and the orbit track can be visualized with
3737
imshow(orb, proj=:Robinson, region=:global, coast=true)
3838
"""
3939
function sat_tracks(; geocentric::Bool=false, tiles::Bool=false, position::Bool=false, kwargs...)
40-
# ...
41-
(position && tiles) && error("Cannot require tiles and a single position. Makes no sense.")
42-
d = KW(kwargs)
43-
44-
start = ((val = find_in_dict(d, [:start])[1]) === nothing) ? now(Dates.UTC) : getitDTime(val)
45-
46-
(tiles) && (sat_name = get_sat_name(d))
47-
(tiles) && (start = round(start, Dates.Minute(5))) # This is for MODIS only
48-
49-
if ((val = find_in_dict(d, [:duration])[1]) !== nothing)
50-
if (isa(val, String)) # Accept duration in D(ays), h(ours), m(inutes) or s(econds)
51-
if (endswith(val,"D")) dur = Day(parse(Int, val[1:end-1]))
52-
elseif (endswith(val,"h")) dur = Hour(parse(Int, val[1:end-1]))
53-
elseif (endswith(val,"m")) dur = Minute(parse(Int, val[1:end-1]))
54-
elseif (endswith(val,"s")) dur = Second(parse(Int, val[1:end-1]))
55-
else error("Only 'D', 'h', 'm' or 's' are accepted in duration")
56-
end
57-
elseif (isa(val, Real)) # Assume duration was given in minutes
58-
dur = Minute(trunc(Int, val))
59-
end
60-
stop = start + dur
61-
else
62-
if ((val = find_in_dict(d, [:stop])[1]) !== nothing)
63-
stop = getitDTime(val)
64-
else
65-
stop = start + Minute(100) # Default is ~Terra period
66-
end
67-
end
68-
if ((val = find_in_dict(d, [:step :inc :dt])[1]) !== nothing) # Steps are in seconds
69-
if (isa(val, String))
70-
if (endswith(val,"m")) dt = parse(Int, val[1:end-1]) * 60
71-
elseif (endswith(val,"s")) dt = parse(Int, val[1:end-1])
72-
else error("Only 's' or 'm' are accepted in increment")
73-
end
74-
else
75-
dt = trunc(Int, val)
76-
end
77-
else
78-
dt = 30
79-
end
80-
81-
if ((val_tle = find_in_dict(d, [:tle_obj])[1]) !== nothing) tle = val_tle # Some other fun already got it
82-
else tle = loadTLE(d)
83-
end
84-
orbp = SatelliteToolboxPropagators.Propagators.init(Val(:SGP4), tle)
85-
86-
epoch_jd = orbp.sgp4d.epoch
87-
startmfe = (datetime2julian(DateTime(start)) - epoch_jd) * 24 * 3600
88-
stopmfe = (datetime2julian(DateTime(stop)) - epoch_jd) * 24 * 3600
89-
(tiles) && (dt = 60) # Arbitrary choice that works well for MODIS but may need revision for others
90-
t = startmfe:dt:stopmfe
91-
92-
(position) && (t = [t[1]]) # Single position. Doing it here wastes work above but code is way cleaner
93-
94-
out = Matrix{Float64}(undef, length(t), 4)
95-
#r = SatelliteToolboxPropagators.Propagators.propagate!.(orbp, t)[1] # Doesn't work. Why?
96-
97-
for n = 1:length(t)
98-
r = SatelliteToolboxPropagators.Propagators.propagate!(orbp, t[n])[1]
99-
jd = epoch_jd + t[n] / (24 * 3600)
100-
tt = SatelliteToolboxTransformations.r_eci_to_ecef(SatelliteToolboxTransformations.TEME(), SatelliteToolboxTransformations.PEF(), jd) * r
101-
out[n,1], out[n,2], out[n,3], out[n, 4] = tt[1], tt[2], tt[3], jd
102-
end
40+
!(isdefined(Main, :SatelliteToolboxPropagators)) &&
41+
(printstyled("Satellite Toolbox Propagators not loaded. Load them with:\n\n"; color=:blue); printstyled("using SatelliteToolboxTle, SatelliteToolboxPropagators, SatelliteToolboxTransformations"; color=:yellow); return nothing)
42+
sat_tracks_ext(; geocentric=geocentric, tiles=tiles, position=position, kwargs...)
43+
end
10344

104-
if (tiles)
105-
out = mapproject(out, E=true, I=true)
106-
return make_sat_tiles(out[1].data, SCENE_HALFW[sat_name], sat_name)
107-
end
45+
function sat_tracks_ext end
10846

109-
if (geocentric)
110-
D = mat2ds(out, geom=UInt32(2), colnames=["X", "Y", "Z", "JulianDay"])
111-
else
112-
D = mapproject(out, E=true, I=true)
113-
D.colnames, D.proj4, D.geom = ["lon", "lat", "alt", "JulianDay"], GMT.prj4WGS84, UInt32(2)
114-
end
115-
D
47+
function loadTLE(d::Dict)
48+
!(isdefined(Main, :SatelliteToolboxTle)) &&
49+
(printstyled("Satellite Toolbox Propagators not loaded. Load them with:\n\n"; color=:blue); printstyled("using SatelliteToolboxTle, SatelliteToolboxPropagators, SatelliteToolboxTransformations"; color=:yellow); return nothing)
50+
loadTLE_ext(d)
11651
end
11752

53+
function loadTLE_ext end
54+
11855
# --------------------------------------------------------------------------------------------
11956
function getitDTime(val)
12057
if isa(val, String) || isa(val, Tuple) ret::DateTime = DateTime(val)
@@ -124,23 +61,6 @@ function getitDTime(val)
12461
return ret
12562
end
12663

127-
# --------------------------------------------------------------------------------------------
128-
function loadTLE(d::Dict)
129-
# Load a TLE or use a default one. In a function because it's used by two functions
130-
if ((val = find_in_dict(d, [:tle :TLE])[1]) !== nothing)
131-
if (isa(val, String)) tle = SatelliteToolboxTle.read_tle(val)
132-
elseif (isa(val, Vector{String}) && length(val) == 2)
133-
tle = SatelliteToolboxTle.read_tle(val[1], val[2])
134-
else
135-
error("BAD input TLE data")
136-
end
137-
else
138-
#tle = SatelliteToolbox.read_tle("C:\\v\\Landsat8.tle")
139-
tle = SatelliteToolboxTle.read_tle("C:\\v\\AQUA.tle")
140-
end
141-
tle
142-
end
143-
14464
# --------------------------------------------------------------------------------------------
14565
function get_sat_name(d::Dict)::String
14666
# Get the satellite name from kwargs (encoded in 'd'). Used by at least two functions
@@ -189,7 +109,7 @@ function sat_scenes(track, sat_name::String)
189109
ll14 = geod(track[k,1:2], [azim[k]+90, azim[k]-90], halfwidth)[1]
190110
ll23 = geod(track[k+5,1:2], [azim[k+5]+90, azim[k+5]-90], halfwidth)[1]
191111
sc = get_MODIS_scene_name(track[k,4], sat_name)
192-
D[n+=1] = GMTdataset([ll14[1:1,:]; ll23[1:1,:]; ll23[2:2,:]; ll14[2:2,:]; ll14[1:1,:]], Float64[], Float64[], Dict{String, String}(), ["lon","lat"], String[], sc, String[], "", "", 0, GMT.Gdal.wkbPolygon)
112+
D[n+=1] = GMTdataset([ll14[1:1,:]; ll23[1:1,:]; ll23[2:2,:]; ll14[2:2,:]; ll14[1:1,:]], Float64[], Float64[], GMT.DictSvS(), ["lon","lat"], String[], sc, String[], "", "", 0, GMT.Gdal.wkbPolygon)
193113
end
194114
D[1].proj4 = GMT.prj4WGS84
195115
D
@@ -237,7 +157,7 @@ function clip_orbits(track, bb::Vector{<:Real})
237157
end_seg = [inds[2:2:length(inds)]; length(azim)+1]
238158
D = Vector{GMTdataset}(undef, length(begin_seg))
239159
colnames, prj4 = isa(track, Vector) ? (track[1].colnames, track[1].proj4) : (track.colnames, track.proj4)
240-
for k = 1:length(begin_seg)
160+
for k = 1:GMT.numel(begin_seg)
241161
D[k] = GMTdataset(isa(segments, Vector) ? segments[1][begin_seg[k]:end_seg[k], :] : segments[begin_seg[k]:end_seg[k], :], Float64[], Float64[], GMT.DictSvS(), colnames, String[], "", String[], prj4, "", 0, GMT.Gdal.wkbLineString)
242162
end
243163
D[1].proj4 = GMT.prj4WGS84
@@ -312,7 +232,7 @@ function findscenes(lon::Real, lat::Real; kwargs...)
312232

313233
scenes = Vector{String}(undef, 0)
314234
dists = mapproject(D, G=(lon,lat))
315-
for k = 1:length(dists)
235+
for k = 1:GMT.numel(dists)
316236
d, ind = findmin(view(dists[k].data, :,5))
317237
if (d <= SCENE_HALFW[sat])
318238
jd = datetime2julian(floor(julian2datetime(dists[k][ind,4]), Dates.Minute(5)))
@@ -329,7 +249,7 @@ function day_night_orbits(D; day::Bool=false, night::Bool=false)
329249
(!day && !night) && return D # No selection requested
330250

331251
pass = zeros(Bool, length(D))
332-
for k = 1:length(D)
252+
for k = 1:GMT.numel(D)
333253
lon, lat = D[k][1,1:2]
334254
jd = D[k][1,4]
335255
raise, set = solar(I=@sprintf("%.4f/%.4f+d%s", lon, lat, string(julian2datetime(jd))), C=true)[5:6]

0 commit comments

Comments
 (0)