1+ import . ArchGDAL: RasterDataset, AbstractRasterBand,
2+ getgeotransform, width, height, getname, getcolorinterp,
3+ getband, nraster, getdataset, ArchGDAL
4+ using . ArchGDAL. DiskArrays: GridChunks, DiskArrays, eachchunk
5+ const AG = ArchGDAL
6+
7+ struct GDALBand{T} <: AG.DiskArrays.AbstractDiskArray{T,2}
8+ filename:: String
9+ band:: Int
10+ size:: Tuple{Int,Int}
11+ attrs:: Dict{String,Any}
12+ cs:: GridChunks{2}
13+ end
14+ function GDALBand (b, filename, i)
15+ s = size (b)
16+ atts = getbandattributes (b)
17+ GDALBand {AG.pixeltype(b)} (filename, i, s, atts, eachchunk (b))
18+ end
19+ Base. size (b:: GDALBand ) = b. size
20+ DiskArrays. eachchunk (b:: GDALBand ) = b. cs
21+ DiskArrays. haschunks (:: GDALBand ) = DiskArrays. Chunked ()
22+ function DiskArrays. readblock! (b:: GDALBand , aout, r:: AbstractUnitRange... )
23+ AG. read (b. filename) do ds
24+ AG. getband (ds, b. band) do bh
25+ DiskArrays. readblock! (bh, aout, r... )
26+ end
27+ end
28+ end
29+ function DiskArrays. writeblock! (b:: GDALBand , ain, r:: AbstractUnitRange... )
30+ AG. read (b. filename, flags= AG. OF_Update) do ds
31+ AG. getband (ds, b. band) do bh
32+ DiskArrays. writeblock! (bh, ain, r... )
33+ end
34+ end
35+ end
36+
37+ struct GDALDataset
38+ filename:: String
39+ bandsize:: Tuple{Int,Int}
40+ projection:: String
41+ trans:: Vector{Float64}
42+ bands:: OrderedDict{String}
43+ end
44+
45+ function GDALDataset (filename)
46+ AG. read (filename) do r
47+ nb = AG. nraster (r)
48+ allbands = map (1 : nb) do iband
49+ b = AG. getband (r, iband)
50+ gb = GDALBand (b, filename, iband)
51+ name = AG. GDAL. gdalgetdescription (b. ptr)
52+ if isempty (name)
53+ name = AG. getname (AG. getcolorinterp (b))
54+ end
55+ name => gb
56+ end
57+ proj = AG. getproj (r)
58+ trans = AG. getgeotransform (r)
59+ s = AG. _common_size (r)
60+ allnames = first .(allbands)
61+ if ! allunique (allnames)
62+ allbands = [" Band$i " => last (v) for (i,v) in enumerate (allbands)]
63+ end
64+ GDALDataset (filename, s[1 : end - 1 ], proj, trans, OrderedDict (allbands))
65+ end
66+ end
67+ Base. haskey (ds:: GDALDataset , k) = in (k, (" X" , " Y" )) || haskey (ds. bands, k)
68+ # Implement Dataset interface
69+ function get_var_handle (ds:: GDALDataset , name)
70+ if name == " X"
71+ range (ds. trans[1 ], length = ds. bandsize[1 ], step = ds. trans[2 ])
72+ elseif name == " Y"
73+ range (ds. trans[4 ], length = ds. bandsize[2 ], step = ds. trans[6 ])
74+ else
75+ ds. bands[name]
76+ end
77+ end
78+
79+
80+ get_varnames (ds:: GDALDataset ) = collect (keys (ds. bands))
81+
82+ function get_var_dims (ds:: GDALDataset , d)
83+ if d === " X"
84+ return (" X" ,)
85+ elseif d=== " Y"
86+ return (" Y" ,)
87+ else
88+ return (" X" , " Y" )
89+ end
90+ end
91+
92+ get_global_attrs (ds:: GDALDataset ) = Dict (" projection" => ds. projection)
93+
94+ function get_var_attrs (ds:: GDALDataset , name)
95+ if name in (" Y" , " X" )
96+ Dict {String,Any} ()
97+ else
98+ ds. bands[name]. attrs
99+ end
100+ end
101+
102+ const colornames = AG. getname .(AG. GDALColorInterp .(0 : 16 ))
103+
104+ islat (s) = startswith (uppercase (s), " LAT" )
105+ islon (s) = startswith (uppercase (s), " LON" )
106+ isx (s) = uppercase (s) == " X"
107+ isy (s) = uppercase (s) == " Y"
108+
109+ function totransform (x, y)
110+ xstep = diff (x)
111+ ystep = diff (y)
112+ if ! all (isapprox (first (xstep)), xstep) || ! all (isapprox (first (ystep)), ystep)
113+ throw (ArgumentError (" Grid must have regular spacing" ))
114+ end
115+ Float64[first (x), first (xstep), 0.0 , first (y), 0.0 , first (ystep)]
116+ end
117+ totransform (x:: AbstractRange , y:: AbstractRange ) =
118+ Float64[first (x), step (x), 0.0 , first (y), 0.0 , step (y)]
119+ getproj (userproj:: String , attrs) = AG. importPROJ4 (userproj)
120+ getproj (userproj:: AG.AbstractSpatialRef , attrs) = userproj
121+ function getproj (userproj:: Nothing , attrs)
122+ if haskey (attr, " projection_PROJ4" )
123+ return AG. importPROJ4 (attr[" projection_PROJ4" ])
124+ elseif haskey (attr, " projection_WKT" )
125+ return AG. importWKT (attr[" projection_WKT" ])
126+ else
127+ error (
128+ " Could not determine output projection from attributes, please specify userproj" ,
129+ )
130+ end
131+ end
132+
133+
134+ function create_dataset (
135+ :: Type{<:GDALDataset} ,
136+ outpath,
137+ gatts,
138+ dimnames,
139+ dimvals,
140+ dimattrs,
141+ vartypes,
142+ varnames,
143+ vardims,
144+ varattrs,
145+ varchunks;
146+ userproj = nothing ,
147+ kwargs... ,
148+ )
149+ @assert length (dimnames) == 2
150+ proj, trans = if islon (dimnames[1 ]) && islat (dimnames[2 ])
151+ # Lets set srs to EPSG:4326
152+ proj = AG. importEPSG (4326 )
153+ trans = totransform (dimvals[1 ], dimvals[2 ])
154+ proj, trans
155+ elseif isx (dimnames[1 ]) && isy (dimnames[2 ])
156+ # Try to find out srs
157+ proj = getproj (userproj, gatts)
158+ trans = totransform (dimvals[1 ], dimvals[2 ])
159+ proj, trans
160+ else
161+ error (" Did not find x, y or lon, lat dimensions in dataset" )
162+ end
163+ cs = first (varchunks)
164+ @assert all (isequal (varchunks[1 ]), varchunks)
165+
166+ driver = AG. getdriver (AG. extensiondriver (outpath))
167+
168+ nbands = length (varnames)
169+ dtype = promote_type (vartypes... )
170+ s = (length .(dimvals)... ,)
171+ bands = AG. create (
172+ outpath;
173+ driver = driver,
174+ width = length (dimvals[1 ]),
175+ height = length (dimvals[2 ]),
176+ nbands = nbands,
177+ dtype = dtype,
178+ options = [" BLOCKXSIZE=$(cs[1 ]) " , " BLOCKYSIZE=$(cs[2 ]) " ]
179+ ) do ds
180+ AG. setgeotransform! (ds, trans)
181+ bands = map (1 : length (varnames)) do i
182+ b = AG. getband (ds, i)
183+ icol = findfirst (isequal (varnames[i]), colornames)
184+ if isnothing (icol)
185+ AG. setcolorinterp! (b, AG. GDAL. GDALColorInterp (0 ))
186+ else
187+ AG. setcolorinterp! (b, AG. GDAL. GDALColorInterp (icol - 1 ))
188+ end
189+ AG. GDAL. gdalsetdescription (b. ptr, varnames[i])
190+ atts = varattrs[i]
191+ haskey (atts, " missing_value" ) && AG. setnodatavalue! (b, atts[" missing_value" ])
192+ if haskey (atts, " labels" )
193+ labeldict = atts[labels]
194+ maxlabel = maximum (keys (labeldict))
195+ kt = keytype (labeldict)
196+ labelvec = [haskey (labeldict, et (i)) ? labeldict[et (i)] : " " for i = 0 : maxlabel]
197+ AG. setcategorynames! (b, labelvec)
198+ end
199+ haskey (atts, " units" ) && AG. setunittype! (b, atts[" units" ])
200+ haskey (atts, " scale_factor" ) && AG. setscale! (b, atts[" scale_factor" ])
201+ haskey (atts, " add_offset" ) && AG. setoffset! (b, atts[" add_offset" ])
202+ GDALBand {dtype} (outpath, i, s, atts, AG. DiskArrays. GridChunks (s,cs))
203+ end
204+ end
205+ return GDALDataset (outpath, s, AG. toPROJ4 (proj), trans, OrderedDict (vn=> b for (vn,b) in zip (varnames, bands)))
206+ end
207+
208+ allow_parallel_write (:: Type{<:GDALDataset} ) = false
209+ allow_parallel_write (:: GDALDataset ) = false
210+
211+ allow_missings (:: Type{<:GDALDataset} ) = false
212+ allow_missings (:: GDALDataset ) = false
213+
214+ backendlist[:gdal ] = GDALDataset
215+ push! (backendregex,r" .tif$" => GDALDataset)
216+ push! (backendregex,r" .gtif$" => GDALDataset)
217+ push! (backendregex,r" .tiff$" => GDALDataset)
218+ push! (backendregex,r" .gtiff$" => GDALDataset)
0 commit comments