Skip to content

Instantly share code, notes, and snippets.

@schoenobates
Created October 5, 2019 11:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save schoenobates/02db89875ee737d6a4c0bc5cda0d1565 to your computer and use it in GitHub Desktop.
Save schoenobates/02db89875ee737d6a4c0bc5cda0d1565 to your computer and use it in GitHub Desktop.
Convert an ASCII DEM File to RAW or PNG for import into other apps (e.g. Unity)
package main
import (
"bufio"
"encoding/binary"
"flag"
"fmt"
"image"
"image/color"
"image/png"
"io"
"os"
"regexp"
"strconv"
"strings"
)
type meta struct {
ncols, nrows, nodata int32
xllcorner, yllcorner, cellsize float64
}
var ws = regexp.MustCompile("\\s+")
var min, max float64
var amin, amax float64
var delta float64
func main() {
var fname string
var format string
var filename string
min = 0
max = 0
flag.StringVar(&fname, "i", "", "The ASC file to process")
flag.StringVar(&format, "f", "PNG", "The output format type - one of PNG or RAW")
flag.StringVar(&filename, "o", "heightmap.png", "The filename to save the output as")
flag.Parse()
file, err := os.Open(fname)
if err != nil {
fmt.Printf("Failed to open file for reading: file = %s, err = %s\n", fname, err)
os.Exit(-1)
}
defer file.Close()
scanner := bufio.NewReader(file)
values := [][]float64{}
isBody := false
metadata := new(meta)
line, err := scanner.ReadString(byte('\n'))
count := 0
for err == nil {
if !isBody {
if strings.HasPrefix(line, "ncols") {
metadata.ncols = int32(metaValue(line))
line, err = scanner.ReadString(byte('\n'))
continue
}
if strings.HasPrefix(line, "nrows") {
metadata.nrows = int32(metaValue(line))
line, err = scanner.ReadString(byte('\n'))
continue
}
if strings.HasPrefix(line, "xllcorner") {
metadata.xllcorner = metaValue(line)
line, err = scanner.ReadString(byte('\n'))
continue
}
if strings.HasPrefix(line, "yllcorner") {
metadata.yllcorner = metaValue(line)
line, err = scanner.ReadString(byte('\n'))
continue
}
if strings.HasPrefix(line, "cellsize") {
metadata.cellsize = metaValue(line)
line, err = scanner.ReadString(byte('\n'))
continue
}
if strings.HasPrefix(line, "NODATA") {
metadata.nodata = int32(metaValue(line))
line, err = scanner.ReadString(byte('\n'))
continue
}
}
if !isBody {
fmt.Printf("Metadata: %+v\n", metadata)
}
isBody = true
heights := parse(line)
for _, val := range heights {
if val < min {
min = val
}
if val > max {
max = val
}
}
values = append(values, parse(line))
line, err = scanner.ReadString(byte('\n'))
count++
}
if err != io.EOF {
fmt.Fprintln(os.Stderr, "Error reading ASC file:", err)
os.Exit(-1)
}
delta = 0 - min
amin = min + delta
amax = max + delta
fmt.Printf("%#v\n", metadata)
fmt.Printf("Min = %.15f Max = %.15f\n", min, max)
fmt.Printf("Delta values: Delta = %.15f, Min = %.15f Max = %.15f\n", delta, amin, amax)
if format == "RAW" {
writeRAW(values, filename)
} else {
writePNG(values, filename)
}
}
func writeRAW(values [][]float64, filename string) {
outfile, err := os.Create(filename)
if err != nil {
fmt.Printf("Failed to open create file: filename = %s, error = %s", filename, err)
os.Exit(-1)
}
defer outfile.Close()
for _, row := range values {
for _, col := range row {
var c = uint16(0xFFFF * ((col + delta) / amax))
if c <= 0 {
fmt.Printf("%d, %.15f, %.15f, %.15f", c, col, amax, (col / amax))
}
err := binary.Write(outfile, binary.BigEndian, c)
if err != nil {
fmt.Println("Failed to save raw file: ", err)
os.Exit(-1)
}
}
}
}
func writePNG(values [][]float64, filename string) {
rows := len(values)
cols := len(values[0])
img := image.NewGray16(image.Rect(0, 0, cols, rows))
for j, row := range values {
for i, col := range row {
var c = uint16(0xFFFF * ((col + delta) / amax))
if c <= 0 {
fmt.Printf("%d, %.15f, %.15f, %.15f", c, col, amax, (col / amax))
}
img.SetGray16(i, j, color.Gray16{c})
}
}
outfile, err := os.Create(filename)
if err != nil {
fmt.Printf("Failed to create output file: filename = %s\n, error = %s", filename, err)
os.Exit(-1)
}
defer outfile.Close()
png.Encode(outfile, img)
}
func metaValue(line string) float64 {
parts := ws.Split(strings.TrimSpace(line), 2)
value, err := strconv.ParseFloat(parts[1], 64)
if err != nil {
fmt.Println("Failed to convert value: ", value)
fmt.Println(err)
os.Exit(1)
}
return value
}
func parse(line string) []float64 {
comps := ws.Split(strings.TrimSpace(line), -1)
heights := []float64{}
for _, comp := range comps {
height, _ := strconv.ParseFloat(comp, 64)
heights = append(heights, height)
}
return heights
}
@schoenobates
Copy link
Author

compile (e.g. go v1.13) ->

GOOS=windows GOARCH=amd64 go build -o demconv.exe main.go
GOOS=linux GOARCH=amd64 go build -o demconv main.go
GOOS=darwin GOARCH=amd64 go build -o demconv main.go

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment