// creates the NetCDF file following nc structure. //func WriteNetcdf(any interface{}) error { func WriteNetcdf(nc *lib.Nc, m *config.Map, cfg toml.Configtoml, ncType string, prefixAll string) { // build filename filename := fmt.Sprintf(cfg.Outputfile+"OS_%s%s_%s.nc", strings.ToUpper(nc.Attributes["cycle_mesure"]), strings.ToUpper(prefixAll), ncType) //fmt.Println(filename) // get roscop definition file for variables attributes var roscop = nc.Roscop fmt.Fprintf(echo, "writing netCDF: %s\n", filename) // get variables_1D size len_1D := nc.Dimensions["TIME"] len_2D := nc.Dimensions["DEPTH"] // Create a new NetCDF 3 file. The dataset is returned. ds, err := netcdf.CreateFile(filename, netcdf.CLOBBER) if err != nil { log.Fatal(err) } defer ds.Close() // Add the dimensions for our data to the dataset dim_1D := make([]netcdf.Dim, 1) dim_2D := make([]netcdf.Dim, 2) // dimensions for ROSCOP paremeters as DEPTH, PRES, TEMP, PSAL, etc dim_2D[0], err = ds.AddDim("TIME", uint64(len_1D)) if err != nil { log.Fatal(err) } dim_2D[1], err = ds.AddDim("DEPTH", uint64(len_2D)) if err != nil { log.Fatal(err) } // dimension for PROFILE, LATITUDE, LONGITUDE and BATH dim_1D[0] = dim_2D[0] // Add the variable to the dataset that will store our data map_1D := make(map[string]netcdf.Var) for key, _ := range nc.Variables_1D { // convert types from code_roscop structure to native netcdf types var netcdfType netcdf.Type /*//remove LATITUDE and LONGITUDE from the key list if thermo file if ncType=="THERMO"{ if key == "LATITUDE" { continue } if key == "LONGITUDE" { continue } }*/ pa := roscop.GetAttributesStringValue(key, "types") switch pa { case "int32": netcdfType = netcdf.INT case "float32": netcdfType = netcdf.FLOAT case "float64": netcdfType = netcdf.DOUBLE default: log.Fatal(fmt.Sprintf("Error: key: %s, Value: [%s], check roscop file\n", key, pa)) // wrong type, check code_roscop file } // add variables v, err := ds.AddVar(key, netcdfType, dim_1D) if err != nil { log.Fatal(err) } map_1D[key] = v // define variable attributes with the right type // for an physical parameter, get a slice of attributes name for _, name := range roscop.GetAttributes(key) { // for each attribute, get the value value := roscop.GetAttributesValue(key, name) // add new attribute to the variable v a := v.Attr(name) // value is an interface{}, need type assertion switch value.(type) { case string: err = a.WriteBytes([]byte(value.(string))) case int32: err = a.WriteInt32s([]int32{value.(int32)}) case float32: err = a.WriteFloat32s([]float32{value.(float32)}) case float64: err = a.WriteFloat64s([]float64{value.(float64)}) default: log.Fatal(fmt.Sprintf("netcdf: create 1D attribute error, %v=%v:%v (%T)", key, name, value, value)) // wrong type, check code_roscop file } if err != nil { log.Fatal(fmt.Sprintf("%s, %v: %v (%T)", err, key, v, v)) } fmt.Fprintf(debug, "%s: %s=%v (%T)\n", key, name, value, value) } } // Add the variable to the dataset that will store our data map_2D := make(map[string]netcdf.Var) // use the order list gave by split or splitAll (config file) because // the iteration order is not specified and is not guaranteed to be // the same from one iteration to the next in golang // for key, _ := range nc.Variables_2D { for _, key := range config.GetPhysicalParametersList(m) { // remove PRFL from the key list if key == "PRFL" { continue } /*if key == "SSJT" { continue }*/ // convert types from code_roscop structure to native netcdf types var netcdfType netcdf.Type pa := roscop.GetAttributesStringValue(key, "types") switch pa { case "int32": netcdfType = netcdf.INT case "float32": netcdfType = netcdf.FLOAT case "float64": netcdfType = netcdf.DOUBLE default: log.Fatal(fmt.Sprintf("Error: key: %s, Value: [%s], check roscop file\n", key, pa)) // wrong type, check code_roscop file } v, err := ds.AddVar(key, netcdfType, dim_2D) if err != nil { log.Fatal(err) } map_2D[key] = v // define variable attributes with the right type // for an physical parameter, get a slice of attributes name for _, name := range roscop.GetAttributes(key) { // for each attribute, get the value value := roscop.GetAttributesValue(key, name) // add new attribute to the variable v a := v.Attr(name) // value is an interface{}, need type assertion switch value.(type) { case string: err = a.WriteBytes([]byte(value.(string))) case int32: err = a.WriteInt32s([]int32{value.(int32)}) case float32: err = a.WriteFloat32s([]float32{value.(float32)}) case float64: err = a.WriteFloat64s([]float64{value.(float64)}) default: log.Fatal(fmt.Sprintf("netcdf: create 1D attribute error: key: %s, Value: [%s], \n", key, value)) } if err != nil { log.Fatal(fmt.Sprintf("%s, %v: %v (%T)", err, key, v, v)) } fmt.Fprintf(debug, "%s: %s=%v (%T)\n", key, name, value, value) } } // defines global attributes for key, value := range nc.Attributes { a := ds.Attr(key) err = a.WriteBytes([]byte(value)) if err != nil { log.Fatal(fmt.Sprintf("%s, %v: %v (%T)", err, key, value, value)) } } // leave define mode in NetCDF3 ds.EndDef() // Create the data with the above dimensions and type, // write them to the file. for key, value := range nc.Variables_1D { // convert types from code_roscop structure to native netcdf types switch roscop.GetAttributesStringValue(key, "types") { case "int32": length := len(value.([]float64)) v := make([]int32, length) fmt.Fprintf(echo, "writing %s: %d\n", key, len(v)) fmt.Fprintf(debug, "writing %s: %d\n", key, len(v)) for i := 0; i < length; i++ { v[i] = int32(value.([]float64)[i]) } if err := map_1D[key].WriteInt32s(v); err != nil { log.Fatal(fmt.Sprintf("%s, %v: %v (%T)", err, key, v, v)) } case "float32": length := len(value.([]float64)) v := make([]float32, length) fmt.Fprintf(echo, "writing %s: %d\n", key, len(v)) fmt.Fprintf(debug, "writing %s: %d\n", key, len(v)) for i := 0; i < length; i++ { v[i] = float32(value.([]float64)[i]) } if err := map_1D[key].WriteFloat32s(v); err != nil { log.Fatal(fmt.Sprintf("%s, %v: %v (%T)", err, key, v, v)) } case "float64": length := len(value.([]float64)) v := make([]float64, length) fmt.Fprintf(echo, "writing %s: %d\n", key, len(v)) fmt.Fprintf(debug, "writing %s: %d\n", key, len(v)) for i := 0; i < length; i++ { v[i] = float64(value.([]float64)[i]) } if err := map_1D[key].WriteFloat64s(v); err != nil { log.Fatal(fmt.Sprintf("%s, %v: %v (%T)", err, key, v, v)) } default: log.Fatal(fmt.Sprintf("%s, %v", err, key)) } } // write data 2D (value.data) to netcdf variables // for key, value := range nc.Variables_2D { for _, key := range config.GetPhysicalParametersList(m) { // remove PRFL from the key list if key == "PRFL" { continue } value := nc.Variables_2D[key] i := 0 ht := len(lib.GetData(value)) wd := len(lib.GetData(value)[0]) fmt.Fprintf(echo, "writing %s: %d x %d\n", key, ht, wd) fmt.Fprintf(debug, "writing %s: %d x %d\n", key, ht, wd) // Write<type> netcdf methods need []<type>, [][]data will be flatten gopher := make([]float64, ht*wd) for x := 0; x < ht; x++ { for y := 0; y < wd; y++ { gopher[i] = lib.GetData(value)[x][y] i++ } } switch roscop.GetAttributesStringValue(key, "types") { case "int32": v := make([]int32, ht*wd) for i := 0; i < ht*wd; i++ { v[i] = int32(gopher[i]) } if err := map_2D[key].WriteInt32s(v); err != nil { log.Fatal(fmt.Sprintf("%s, %v: (%T)", err, key, v)) } case "float32": v := make([]float32, ht*wd) for i := 0; i < ht*wd; i++ { v[i] = float32(gopher[i]) } if err := map_2D[key].WriteFloat32s(v); err != nil { log.Fatal(fmt.Sprintf("%s, %v: (%T)", err, key, v)) } case "float64": if err := map_2D[key].WriteFloat64s(gopher); err != nil { log.Fatal(fmt.Sprintf("%s, %v: (%T)", err, key, gopher)) } default: log.Fatal(fmt.Sprintf("%s, %v", err, key)) } } fmt.Fprintf(echo, "writing %s done ...\n", filename) }
func WriteAscii(nc *lib.Nc, cfg toml.Configtoml, map_format map[string]string, hdr []string, inst string, prefixAll string) { // define 2 files, profiles header and data var asciiFilename string // build filenames str := nc.Attributes["cycle_mesure"] str = strings.Replace(str, "\r", "", -1) headerFilename := fmt.Sprintf(cfg.Outputfile+"%s."+inst, strings.ToLower(str)) asciiFilename = fmt.Sprintf(cfg.Outputfile+"%s%s_"+inst, strings.ToLower(str), prefixAll) //fmt.Println(headerFilename) //fmt.Println(asciiFilename) // open header file for writing result fid_hdr, err := os.Create(headerFilename) if err != nil { log.Fatal(err) } defer fid_hdr.Close() // use buffered mode for writing fbuf_hdr := bufio.NewWriter(fid_hdr) // open ASCII file for writing result fid_ascii, err := os.Create(asciiFilename) if err != nil { log.Fatal(err) } defer fid_ascii.Close() // use buffered mode for writing fbuf_ascii := bufio.NewWriter(fid_ascii) // write header to string str = fmt.Sprintf("%s %s %s %s %s %s\n", nc.Attributes["cycle_mesure"], nc.Attributes["plateforme"], nc.Attributes["institute"], nc.Attributes["type_instrument"], nc.Attributes["instrument_number"], nc.Attributes["pi"]) // write first line header on header file and ascii file fmt.Fprintf(fbuf_hdr, str) fmt.Fprintf(fbuf_ascii, str) // display on screen fmt.Printf("%s", str) // write physical parameters in second line str = "" for _, key := range hdr { fmt.Fprintf(fbuf_ascii, "%s ", key) fmt.Fprintf(lib.Debug, "%s ", key) } // append new line fmt.Fprintln(fbuf_ascii, "\n") // write second line header on ascii file fmt.Fprintln(fbuf_ascii, str) // display on screen fmt.Printf("%s", str) // get data (slices) from nc struct len_1D := nc.Dimensions["TIME"] len_2D := nc.Dimensions["DEPTH"] time := nc.Variables_1D["TIME"].([]float64) lat := nc.Variables_1D["LATITUDE"].([]float64) lon := nc.Variables_1D["LONGITUDE"].([]float64) profile := nc.Variables_1D["PROFILE"].([]float64) // loop over each profile for x := 0; x < len_1D; x++ { str = "" t1 := lib.NewTimeFromJulian(time[x]) // write profile informations to ASCII data file with DEPTH = -1 // TODOS: adapt profile format to stationPrefixLength fmt.Fprintf(fbuf_ascii, "%05.0f %f %f %f %s", profile[x], t1.JulianDayOfYear(), lat[x], lon[x], t1.Format("20060102150405")) // write profile informations to header file str = fmt.Sprintf("%05.0f %s %s %s %4.4g %4.4g %s %s\n", profile[x], t1.Format("02/01/2006 15:04:05"), lib.DecimalPosition2String(lat[x], 0), lib.DecimalPosition2String(lon[x], 0), nc.Extras_f[fmt.Sprintf("MINPRES:%d", int(profile[x]))], nc.Extras_f[fmt.Sprintf("PRES:%d", int(profile[x]))], nc.Extras_s[fmt.Sprintf("TYPECAST:%s", int(profile[x]))], cfg.Ladcp.CruisePrefix+nc.Extras_s[fmt.Sprintf("PRFL_NAME:%d", int(profile[x]))]) // write profile information to header file fmt.Fprintf(fbuf_hdr, str) // display on screen fmt.Printf("%s", str) // fill last header columns with 1e36 for i := 0; i < len(hdr)-6; i++ { fmt.Fprintf(fbuf_ascii, " %g", 1e36) } fmt.Fprintln(fbuf_ascii) // add newline // loop over each level for y := 0; y < len_2D; y++ { fmt.Fprintf(fbuf_ascii, "%05.0f ", profile[x]) // loop over each physical parameter (key) in the rigth order for _, key := range hdr { // if key not in map, goto next key if _, ok := nc.Variables_2D[key]; ok { // fill 2D slice data := lib.GetData(nc.Variables_2D[key])[x][y] // print data with it's format, change format for FillValue if data == 1e36 { fmt.Fprintf(fbuf_ascii, "%g ", data) } else { if strings.ContainsAny(map_format[key], "lf") { res := strings.Split(map_format[key], "l") map_format[key] = strings.Join(res, "") } fmt.Fprintf(fbuf_ascii, map_format[key]+" ", data) } } } fmt.Fprintf(fbuf_ascii, "\n") } fbuf_ascii.Flush() fbuf_hdr.Flush() } }