@@ -6,7 +6,7 @@ const NCDAllowedType = Union{Int8,UInt8,Int16,UInt16,Int32,UInt32,Int64,UInt64,F
66
77# CF standards don't enforce dimension names.
88# But these are common, and should take care of most dims.
9- const NCD_DIMMAP = Dict (
9+ const NCD_DIM_MAP = Dict (
1010 " lat" => Y,
1111 " latitude" => Y,
1212 " lon" => X,
@@ -23,6 +23,20 @@ const NCD_DIMMAP = Dict(
2323 " band" => Band,
2424)
2525
26+ const NCD_AXIS_MAP = Dict (
27+ " X" => X,
28+ " Y" => Y,
29+ " Z" => Z,
30+ " T" => Ti,
31+ )
32+
33+ const NCD_STANDARD_NAME_MAP = Dict (
34+ " longitude" => X,
35+ " latitude" => Y,
36+ " depth" => Z,
37+ " time" => Ti,
38+ )
39+
2640haslayers (:: Type{NCDfile} ) = true
2741defaultcrs (:: Type{NCDfile} ) = EPSG (4326 )
2842defaultmappedcrs (:: Type{NCDfile} ) = EPSG (4326 )
@@ -60,7 +74,7 @@ Write an NCDarray to a NetCDF file using NCDatasets.jl
6074
6175Returns `filename`.
6276"""
63- function Base. write (filename:: AbstractString , :: Type{NCDfile} , A:: AbstractRaster ; append = false , kw... )
77+ function Base. write (filename:: AbstractString , :: Type{NCDfile} , A:: AbstractRaster ; append= false , kw... )
6478 mode = ! isfile (filename) || ! append ? " c" : " a" ;
6579 ds = NCD. Dataset (filename, mode; attrib= _attribdict (metadata (A)))
6680 try
@@ -161,7 +175,7 @@ function DD.layerdims(ds::NCD.Dataset)
161175end
162176function DD. layerdims (var:: NCD.Variable )
163177 map (NCD. dimnames (var)) do dimname
164- _ncddimtype (dimname)()
178+ _ncddimtype (var . attrib, dimname)()
165179 end
166180end
167181
@@ -228,15 +242,17 @@ cleanreturn(A::NCD.CFVariable) = Array(A)
228242
229243function _ncddim (ds, dimname:: Key , crs= nothing , mappedcrs= nothing )
230244 if haskey (ds, dimname)
231- D = _ncddimtype (dimname)
245+ var = NCD. variable (ds, dimname)
246+ D = _ncddimtype (var. attrib, dimname)
232247 lookup = _ncdlookup (ds, dimname, D, crs, mappedcrs)
233248 return D (lookup)
234249 else
235250 # The var doesn't exist. Maybe its `complex` or some other marker,
236251 # so make it a custom `Dim` with `NoLookup`
237252 len = _ncfinddimlen (ds, dimname)
238253 len === nothing && _unuseddimerror ()
239- return Dim {Symbol(dimname)} (NoLookup (Base. OneTo (len)))
254+ lookup = NoLookup (Base. OneTo (len))
255+ return Dim {Symbol(dimname)} (lookup)
240256 end
241257end
242258
@@ -253,25 +269,42 @@ end
253269
254270# Find the matching dimension constructor. If its an unknown name
255271# use the generic Dim with the dim name as type parameter
256- _ncddimtype (dimname) = haskey (NCD_DIMMAP, dimname) ? NCD_DIMMAP[dimname] : DD. basetypeof (DD. key2dim (Symbol (dimname)))
272+ function _ncddimtype (attrib, dimname)
273+ if haskey (attrib, " axis" )
274+ k = attrib[" axis" ]
275+ if haskey (attrib, k)
276+ return NCD_AXIS_MAP[k]
277+ end
278+ end
279+ if haskey (attrib, " standard_name" )
280+ k = attrib[" standard_name" ]
281+ if haskey (NCD_STANDARD_NAME_MAP, k)
282+ return NCD_STANDARD_NAME_MAP[k]
283+ end
284+ end
285+ if haskey (NCD_DIM_MAP, dimname)
286+ return NCD_DIM_MAP[dimname]
287+ end
288+ return DD. basetypeof (DD. key2dim (Symbol (dimname)))
289+ end
257290
258291# _ncdlookup
259292# Generate a `LookupArray` from a netcdf dim.
260- function _ncdlookup (ds:: NCD.Dataset , dimname, D, crs, mappedcrs)
293+ function _ncdlookup (ds:: NCD.Dataset , dimname, D:: Type , crs, mappedcrs)
261294 dvar = ds[dimname]
262295 index = dvar[:]
263296 metadata = _metadatadict (NCDfile, dvar. attrib)
264297 return _ncdlookup (ds, dimname, D, index, metadata, crs, mappedcrs)
265298end
266299# For unknown types we just make a Categorical lookup
267- function _ncdlookup (ds:: NCD.Dataset , dimname, D, index:: AbstractArray , metadata, crs, mappedcrs)
300+ function _ncdlookup (ds:: NCD.Dataset , dimname, D:: Type , index:: AbstractArray , metadata, crs, mappedcrs)
268301 Categorical (index; order= Unordered (), metadata= metadata)
269302end
270303# For Number and AbstractTime we generate order/span/sampling
271304# We need to include `Missing` in unions in case `_FillValue` is used
272305# on coordinate variables in a file and propagates here.
273306function _ncdlookup (
274- ds:: NCD.Dataset , dimname, D, index:: AbstractArray{<:Union{Missing,Number,Dates.AbstractTime}} ,
307+ ds:: NCD.Dataset , dimname, D:: Type , index:: AbstractArray{<:Union{Missing,Number,Dates.AbstractTime}} ,
275308 metadata, crs, mappedcrs
276309)
277310 # Assume the locus is at the center of the cell if boundaries aren't provided.
@@ -426,7 +459,10 @@ function _def_dim_var!(ds::NCD.Dataset, dim::Dimension)
426459 if dim isa Y || dim isa X
427460 dim = convertlookup (Mapped, dim)
428461 end
462+ # Attributes
429463 attrib = _attribdict (metadata (dim))
464+ _ncd_set_axis_attrib! (attrib, dim)
465+ # Bounds variables
430466 if span (dim) isa Explicit
431467 bounds = val (span (dim))
432468 boundskey = get (metadata (dim), :bounds , string (dimkey, " _bnds" ))
@@ -437,6 +473,15 @@ function _def_dim_var!(ds::NCD.Dataset, dim::Dimension)
437473 return nothing
438474end
439475
476+ # Add axis and standard name attributes to dimension variabls
477+ # We need to get better at guaranteeing if X/Y is actually measured in `longitude/latitude`
478+ # CF standards requires that we specify "units" if we use these standard names
479+ _ncd_set_axis_attrib! (atr, dim:: X ) = atr[" axis" ] = " X" # at["standard_name"] = "longitude";
480+ _ncd_set_axis_attrib! (atr, dim:: Y ) = atr[" axis" ] = " Y" # at["standard_name"] = "latitude";
481+ _ncd_set_axis_attrib! (atr, dim:: Z ) = (atr[" axis" ] = " Z" ; atr[" standard_name" ] = " depth" )
482+ _ncd_set_axis_attrib! (atr, dim:: Ti ) = (atr[" axis" ] = " T" ; atr[" standard_name" ] = " time" )
483+ _ncd_set_axis_attrib! (atr, dim) = nothing
484+
440485_ncdshiftlocus (dim:: Dimension ) = _ncdshiftlocus (lookup (dim), dim)
441486_ncdshiftlocus (:: LookupArray , dim:: Dimension ) = dim
442487function _ncdshiftlocus (lookup:: AbstractSampled , dim:: Dimension )
0 commit comments