Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Import GeoTIFF #51

Merged
merged 13 commits into from
Feb 21, 2024
18 changes: 13 additions & 5 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "GeophysicalModelGenerator"
uuid = "3700c31b-fa53-48a6-808a-ef22d5a84742"
authors = ["Boris Kaus", "Marcel Thielmann"]
version = "0.5.8"
version = "0.5.9"

[deps]
Colors = "5ae59095-9a9b-59fe-a467-6f913c188581"
Expand All @@ -20,15 +20,24 @@ MeshIO = "7269a6da-0436-5bbc-96c2-40638cbb6118"
NearestNeighbors = "b8a86587-4115-5ab1-83bc-aa920d37bbce"
Parameters = "d96e819e-fc66-5662-9728-84c9c7592b0a"
Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7"
Requires = "ae029012-a4dd-5104-9daa-d747884805df"
SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b"
Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
WriteVTK = "64499a7a-5c06-52f2-abe2-ccb03c286192"

[weakdeps]
GLMakie = "e9467ef8-e4e7-5192-8a1a-b1aee30e663a"
GMT = "5752ebe1-31b9-557e-87aa-f909b540aa54"

[extensions]
GLMakie_Visualisation = "GLMakie"
GMT_utils = "GMT"

[compat]
Colors = "0.9 - 0.12"
Downloads = "1"
FileIO = "1"
GLMakie = "0.8, 0.9"
GMT = "1"
GeoParams = "0.2 - 0.5"
Geodesy = "1"
Expand All @@ -40,15 +49,14 @@ JLD2 = "0.4"
MeshIO = "0.1 - 0.4"
NearestNeighbors = "0.2 - 0.4"
Parameters = "0.9 - 0.12"
Requires = "1.0, 2"
SpecialFunctions = "1.0, 2"
WriteVTK = "1"
julia = "1.8"
julia = "1.9"

[extras]
GLMakie = "e9467ef8-e4e7-5192-8a1a-b1aee30e663a"
GMT = "5752ebe1-31b9-557e-87aa-f909b540aa54"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[targets]
test = ["Test"]
test = ["Test","GMT"]
19 changes: 18 additions & 1 deletion src/Visualisation.jl → ext/GLMakie_Visualisation.jl
Original file line number Diff line number Diff line change
@@ -1,8 +1,22 @@
module GLMakie_Visualisation
# This contains visualisation widgets which are optionally made available when GLMakie is loaded along with GMG
using .GLMakie, Statistics

using Statistics
using GeophysicalModelGenerator: LonLatDepthGrid, GeoData, CartData, km

# We do not check `isdefined(Base, :get_extension)` as recommended since
# Julia v1.9.0 does not load package extensions when their dependency is
# loaded from the main environment.
if VERSION >= v"1.9.1"
using GLMakie
else
using ..GLMakie
end

export Visualise

println("Loading GLMakie extensions for GMG")

"""
Visualise(DataSet; Topography=Topo_Data, Topo_range=nothing)

Expand Down Expand Up @@ -254,4 +268,7 @@ function Visualise(Data; Topography=nothing, Topo_range=nothing)
display(fig)

return nothing
end


end
84 changes: 79 additions & 5 deletions src/GMT_utils.jl → ext/GMT_utils.jl
Original file line number Diff line number Diff line change
@@ -1,12 +1,26 @@
# NOTE: these are useful routines that are only loaded when the GMT package is already loaded in the REPL
using .GMT
# NOTE: these are useful routines that are only made available when the GMT package is already loaded in the REPL
module GMT_utils

import GeophysicalModelGenerator: ImportTopo, ImportGeoTIFF

# We do not check `isdefined(Base, :get_extension)` as recommended since
# Julia v1.9.0 does not load package extensions when their dependency is
# loaded from the main environment.
if VERSION >= v"1.9.1"
using GMT
else
using ..GMT
end

using GeophysicalModelGenerator: LonLatDepthGrid, GeoData, UTMData, km

println("Loading GMT routines within GMG")

export ImportTopo

"""
Topo = ImportTopo(limits; file::String="@earth_relief_01m.grd")

Uses GMT to download the topography of a certain region, specified with limits=[lon_min, lon_max, lat_min, lat_max]
Uses `GMT` to download the topography of a certain region, specified with limits=[lon_min, lon_max, lat_min, lat_max]

Note:
====
Expand All @@ -31,7 +45,7 @@ Note:
| "@earth\\_relief\\_30m" | 30 arc min | ETOPO1 after Gaussian spherical filtering (55 km fullwidth) |
| "@earth\\_relief\\_60m" | 60 arc min | ETOPO1 after Gaussian spherical filtering (111 km fullwidth)|

*Note*: this routine is only available once the GMT.jl package is loaded on the REPL
*Note*: this routine is only available once the GMT.jl package is loaded in the REPL

# Example
```julia-repl
Expand Down Expand Up @@ -90,3 +104,63 @@ julia> Topo = ImportTopo(lon=(-50, -40), lat=(-10,-5), file="@earth_relief_30s.g

"""
ImportTopo(; lat=[37,49], lon=[4,20], file::String="@earth_relief_01m.grd") = ImportTopo([lon[1],lon[2], lat[1], lat[2]], file=file)


"""
data_GMT = ImportGeoTIFF(fname::String; fieldname=:layer1, negative=false, iskm=true)

This imports a GeoTIFF dataset (usually containing a surface of some sort) using GMT.
We try to determine if this is in `UTM` coordinates or

Optional keywords:
- `fieldname` : name of the field (default=:layer1)
- `negative` : if true, the depth is multiplied by -1 (default=false)
- `iskm` : if true, the depth is multiplied by 1e-3 (default=true)
- `NorthernHemisphere`: if true, the UTM zone is set to be in the northern hemisphere (default=true); only relevant if the data uses UTM projection
"""
function ImportGeoTIFF(fname::String; fieldname=:layer1, negative=false, iskm=true, NorthernHemisphere=true)
G = gmtread(fname);

# Transfer to GeoData
nx,ny = length(G.x)-1, length(G.y)-1
Lon,Lat,Depth = LonLatDepthGrid(G.x[1:nx],G.y[1:ny],0);
if hasfield(typeof(G),:z)
Depth[:,:,1] = G.z';
if negative
Depth[:,:,1] = -G.z';
end
if iskm
Depth *= 1e-3*km;
end
end

# Create GeoData structure
data = zero(Lon)
if hasfield(typeof(G),:z)
data = Depth

elseif hasfield(typeof(G),:image)
data[:,:,1] = G.image'

end
data_field = NamedTuple{(fieldname,)}((data,));

if contains(G.proj4,"utm")

zone = parse(Int64,split.(split(G.proj4,"zone=")[2]," ")[1]); # retrieve UTM zone
data_GMT = UTMData(Lon, Lat, Depth, zone, NorthernHemisphere, data_field)

elseif contains(G.proj4,"longlat") || contains(G.proj4,"somerc")
data_GMT = GeoData(Lon, Lat, Depth, data_field)

else
error("I'm sorry, I don't know how to handle this projection yet: $(G.proj4)")
end



return data_GMT
end


end
29 changes: 17 additions & 12 deletions src/GeophysicalModelGenerator.jl
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
module GeophysicalModelGenerator

using Base: String, show_index, Tuple, FieldDescStorage
using Requires


# Load & export some useful commands/functions from GeoParams:
import GeoParams
Expand Down Expand Up @@ -46,17 +44,24 @@ include("ProfileProcessing.jl")
include("IO.jl")

# Add optional routines (only activated when the packages are loaded)
function __init__()
@require GMT = "5752ebe1-31b9-557e-87aa-f909b540aa54" begin
println("Loading GMT routines within GMG")
@eval include("./GMT_utils.jl")
end
@require GLMakie = "e9467ef8-e4e7-5192-8a1a-b1aee30e663a" begin
println("Loading GLMakie plotting routines within GMG")
@eval include("./Visualisation.jl")
end
end

# GMT routines

"""
Optional routine that imports topography. It requires you to load `GMT`
"""
function ImportTopo end
function ImportGeoTIFF end
export ImportTopo, ImportGeoTIFF

# GLMakie routines

"""
Interactive widget that allows you to explore a 3D data set `DataSet` in an interactive manner.
It requires you to load `GLMakie`
"""
function Visualise end
export Visualise


end
85 changes: 80 additions & 5 deletions src/data_types.jl
Original file line number Diff line number Diff line change
Expand Up @@ -223,10 +223,26 @@ function Base.show(io::IO, d::GeoData)
println(io," size : $(size(d.lon))")
println(io," lon ϵ [ $(first(d.lon.val)) : $(last(d.lon.val))]")
println(io," lat ϵ [ $(first(d.lat.val)) : $(last(d.lat.val))]")
println(io," depth ϵ [ $(first(d.depth.val)) : $(last(d.depth.val))]")
if any(isnan.(NumValue(d.depth)))
z_vals = extrema(d.depth.val[isnan.(d.depth.val).==false])
println(io," depth ϵ [ $(z_vals[1]) : $(z_vals[2])]; has NaN's")
else
z_vals = extrema(d.depth.val)
println(io," depth ϵ [ $(z_vals[1]) : $(z_vals[2])]")
end
println(io," fields : $(keys(d.fields))")

# Only print attributes if we have non-default attributes
if any( propertynames(d) .== :atts)
println(io," attributes: $(keys(d.atts))")
show_atts = true
if haskey(d.atts,"note")
if d.atts["note"]=="No attributes were given to this dataset"
show_atts = false
end
end
if show_atts
println(io," attributes: $(keys(d.atts))")
end
end
end

Expand Down Expand Up @@ -256,8 +272,29 @@ function Base.show(io::IO, d::ParaviewData)
println(io," size : $(size(d.x))")
println(io," x ϵ [ $(first(d.x.val)) : $(last(d.x.val))]")
println(io," y ϵ [ $(first(d.y.val)) : $(last(d.y.val))]")
println(io," z ϵ [ $(first(d.z.val)) : $(last(d.z.val))]")
if any(isnan.(NumValue(d.z)))
z_vals = extrema(d.z.val[isnan.(d.z.val).==false])
println(io," z ϵ [ $(z_vals[1]) : $(z_vals[2])]; has NaN's")
else
z_vals = extrema(d.z.val)
println(io," z ϵ [ $(z_vals[1]) : $(z_vals[2])]")
end

println(io," fields: $(keys(d.fields))")

# Only print attributes if we have non-default attributes
if any( propertynames(d) .== :atts)
show_atts = true
if haskey(d.atts,"note")
if d.atts["note"]=="No attributes were given to this dataset"
show_atts = false
end
end
if show_atts
println(io," attributes: $(keys(d.atts))")
end
end

end

# conversion function from GeoData -> ParaviewData
Expand Down Expand Up @@ -439,10 +476,28 @@ function Base.show(io::IO, d::UTMData)
println(io," size : $(size(d.EW))")
println(io," EW ϵ [ $(first(d.EW.val)) : $(last(d.EW.val))]")
println(io," NS ϵ [ $(first(d.NS.val)) : $(last(d.NS.val))]")
println(io," depth ϵ [ $(first(d.depth.val)) : $(last(d.depth.val))]")

if any(isnan.(NumValue(d.depth)))
z_vals = extrema(d.depth.val[isnan.(d.depth.val).==false])
println(io," depth ϵ [ $(z_vals[1]) : $(z_vals[2])]; has NaNs")
else
z_vals = extrema(d.depth.val)
println(io," depth ϵ [ $(z_vals[1]) : $(z_vals[2])]")
end

println(io," fields : $(keys(d.fields))")

# Only print attributes if we have non-default attributes
if any( propertynames(d) .== :atts)
println(io," attributes: $(keys(d.atts))")
show_atts = true
if haskey(d.atts,"note")
if d.atts["note"]=="No attributes were given to this dataset"
show_atts = false
end
end
if show_atts
println(io," attributes: $(keys(d.atts))")
end
end
end

Expand Down Expand Up @@ -701,9 +756,29 @@ function Base.show(io::IO, d::CartData)
println(io," x ϵ [ $(minimum(d.x.val)) : $(maximum(d.x.val))]")
println(io," y ϵ [ $(minimum(d.y.val)) : $(maximum(d.y.val))]")
println(io," z ϵ [ $(minimum(d.z.val)) : $(maximum(d.z.val))]")

if any(isnan.(NumValue(d.z)))
z_vals = extrema(d.z.val[isnan.(d.z.val).==false])
println(io," z ϵ [ $(z_vals[1]) : $(z_vals[2])]; has NaN's")
else
z_vals = extrema(d.z.val)
println(io," z ϵ [ $(z_vals[1]) : $(z_vals[2])]")
end


println(io," fields : $(keys(d.fields))")

# Only print attributes if we have non-default attributes
if any( propertynames(d) .== :atts)
show_atts = true
if haskey(d.atts,"note")
if d.atts["note"]=="No attributes were given to this dataset"
show_atts = false
end
end
if show_atts
println(io," attributes: $(keys(d.atts))")
end
end
end

Expand Down
3 changes: 3 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ end
include("test_ProfileProcessing.jl")
end

@testset "GMT integration" begin
include("test_GMT.jl")
end


# Cleanup
Expand Down
14 changes: 14 additions & 0 deletions test/test_GMT.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using Test
using GeophysicalModelGenerator, GMT

Topo = ImportTopo(lat=[30,31], lon=[50, 51] )
@test sum(Topo.depth.val) ≈ 2773.3734999999997

Topo = ImportTopo([50,51, 30,31]);
@test sum(Topo.depth.val) ≈ 2773.3734999999997

test_fwd = ImportGeoTIFF("test_files/length_fwd.tif", fieldname=:forward)
@test maximum(test_fwd.fields.forward) ≈ 33.17775km

test2 = ImportGeoTIFF("test_files/UTM2GTIF.TIF")
@test test2.fields.layer1[20,20] == 105.0
Binary file added test/test_files/UTM2GTIF.TIF
Binary file not shown.
Binary file added test/test_files/length_fwd.tif
Binary file not shown.
Loading