// 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)
}
Example #2
0
// creates the NetCDF file following nc structure.
//func WriteNetcdf(any interface{}) error {
func WriteNetcdf(nc *lib.Nc, m *config.Map, ncType string) {

	// build filename
	filename := fmt.Sprintf("output/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
	//	for k, v := range roscop {
	//		fmt.Printf("%s: ", k)
	//		fmt.Println(v)
	//	}
	//	os.Exit(0)

	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 {
		v, err := ds.AddVar(key, netcdf.DOUBLE, dim_1D)
		if err != nil {
			log.Fatal(err)
		}
		map_1D[key] = v

		//initialise struct roscop with netcdf variable in reflect ways without knowing the form of the roscop struct
		Reflectroscop(roscop[key], map_1D[key])

	}

	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 m.Hdr {
		// remove PRFL from the key list
		if key == "PRFL" {
			continue
		}
		v, err := ds.AddVar(key, netcdf.DOUBLE, dim_2D)
		if err != nil {
			log.Fatal(err)
		}
		map_2D[key] = v

		//initialise struct roscop with netcdf variable in reflect ways without knowing the form of the roscop struct
		Reflectroscop(roscop[key], map_2D[key])

	}

	// defines global attributes
	for key, value := range nc.Attributes {
		a := ds.Attr(key)
		a.WriteBytes([]byte(value))
	}

	// leave define mode in NetCDF3
	ds.EndDef()

	// Create the data with the above dimensions and write it to the file.
	for key, value := range nc.Variables_1D {

		v := value.([]float64)
		fmt.Fprintf(echo, "writing %s: %d\n", key, len(v))
		err = map_1D[key].WriteFloat64s(v)
		if err != nil {
			log.Fatal(err)
		}
	}

	// write data 2D (value.data) to netcdf variables
	// for key, value := range nc.Variables_2D {
	for _, key := range m.Hdr {
		// 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++
			}
		}
		err = map_2D[key].WriteFloat64s(gopher)
		if err != nil {
			log.Fatal(err)
		}
	}
	fmt.Fprintf(echo, "writing %s done ...\n", filename)
	//return nil
}