Skip to content

Commit

Permalink
Merge pull request #51 from JuliaGeodynamics/bk-gmt-import
Browse files Browse the repository at this point in the history
Import GeoTIFF
  • Loading branch information
boriskaus authored Feb 21, 2024
2 parents bde2a1d + 91ed562 commit 83504cb
Show file tree
Hide file tree
Showing 11 changed files with 228 additions and 30 deletions.
2 changes: 1 addition & 1 deletion .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ updates:
- package-ecosystem: "github-actions"
directory: "/" # Location of package manifests
schedule:
interval: "weekly"
interval: "monthly"
2 changes: 1 addition & 1 deletion .github/workflows/blank.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ jobs:
fail-fast: false
matrix:
version:
- '1.8'
- '1.9'
- '1.10'
- 'nightly'
os:
- ubuntu-latest
- macOS-latest
Expand Down
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
86 changes: 81 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,65 @@ 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.
The file should either have `UTM` coordinates of `longlat` coordinates. If it doesn't, you can
use QGIS to convert it to `longlat` coordinates.
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")
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)\n
We recommend that you transfer your GeoTIFF to longlat by using QGIS \n
Open the GeoTIFF there and Export -> Save As , while selecting \"EPSG:4326 - WGS 84\" projection.")
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.

0 comments on commit 83504cb

Please sign in to comment.