Search code examples
cswiftmigrate

Read binary (.hgt) file in Swift (migrate code from c++ to swift)


I need to read elevation data from a binary .hgt file in Swift. I have found this result for c, but I can not migrate it to Swift.

#include <stdio.h>

#define SIZE 1201
signed short int matrix[SIZE][SIZE] = {0};

int main(int argc, const char * argv[])
{
FILE *fp = fopen("N49E013.hgt", "rb");    

unsigned char buffer[2];
for (int i = 0; i < SIZE; ++i)
{
    for (int j = 0; j < SIZE; ++j) 
    {
        if (fread(buffer, sizeof(buffer), 1, fp) != 1)
        {
            printf("Error reading file!\n");
            system("PAUSE");
            return -1;
        }
        matrix[i][j] = (buffer[0] << 8) | buffer[1];       
    }
}

fclose(fp);
}

Solution

  • #define SIZE 1201

    This defines a constant named 'SIZE', so do that:

    let size = 1201

    next:

    FILE *fp = fopen("N49E013.hgt", "rb");

    This opens a file for reading. We can do that. Close the file in a 'defer' block, so that no matter what, the file gets closed when we're done.

    // change the path below to the correct path
    let handle = try FileHandle(forReadingFrom: URL(fileURLWithPath: "/path/to/N49E013.hgt"))
    defer { handle.closeFile() }
    

    Now, to construct the matrix. We want to create size number of arrays, each of which has size elements, read from the file. The original used two nested for loops, but Swift supports functional programming constructs, which we can use to do this a bit more elegantly:

    let matrix = try (0..<size).map { _ in
        try (0..<size).map { _ -> Int in
            // Unfortunately, FileHandle doesn't have any decent error-reporting mechanism
            // other than Objective-C exceptions.
            // If you need to catch errors, you can use fread as in the original,
            // or use an Objective-C wrapper to catch the exceptions.
    
            let data = handle.readData(ofLength: 2)
    
            if data.count < 2 { throw CocoaError(.fileReadCorruptFile) }
    
            return (Int(data[0]) << 8) | Int(data[1])
        }
    }
    

    Think that ought to do it.