Siste oppdatering
Python-kode som les inn bin-filer
Under er eit døme på å lese inn filer av det binære .bin-formatet som er brukt for separasjonsmodellane mellom referansenivå i kystsona og til havs, publisert av Kartverket. Dette er same format som er brukt for høgdereferansemodellane for NN2000 og NN1954.
# -*- coding: utf-8 -*-
'''
Under er eit døme på å lese inn filer av det binære .bin-formatet som er brukt
for separasjonsmodellane mellom referansenivå i kystsona og til havs, publisert
av Kartverket. Dette er same format som er brukt for høgdereferansemodellane
for NN2000 og NN1954.
Det er definert ein funksjon for å lese inn filene til ein dictionary,
deretter vert funksjonen brukt for å lese inn fila
"ChartDatum_above_Ellipsoid_EUREF89_v2021a.bin", og resultatet plotta.
I koden under er det antatt at den .bin-fila ligg i same mappe som skriptet.
Lisensiert under Creative Commons BY-SA 3.0
https://creativecommons.org/licenses/by-sa/3.0/
'''
import struct
import numpy as np
import matplotlib.pyplot as plt
def readbinfile(filename):
'''Returnerer dictionary med data frå .bin-fil.
Parametre
---------
filename : string
Filnamn eller fullstendig sti til .bin-fil som skal lesast inn
Returnerer
----------
D : dictionary
Oppføringer i dict er
- lat : Nx1 numpy array med breiddegradsverdier
- lon : Mx1 numpy array med lengdegradsverdier
- data : NxM numpy masked array med modellverdiar
- dlon : avstand mellom gridpunkt i aust-vest-retning
- dlat : avstand mellom gridpunkt i nord-sør-retning
- ellipsoide : viss UTM-koordinatar, gjev aktuell ellipsoide
- UTM-sone : viss UTM-koordinatar, gjev UTM-sonen
Notat
-----
Formatet i .bin-filene som HREF og separasjonsmodellane kjem i er eit
Gravsoft-binærformat. Verdiane er på eit regulært grid, med radene langs
linjer av breiddegrad, og kolonnene langs linjer av lengdegrad.
Fila er delt i blokker på 64 byte, den fyrste blokken inneheld metadata.
Resten av fila er modellverdiar som "single precision" flyttal, det vil
seie 4 byte for kvart tal.
Alle tal i same 64 byte-blokk er på same breiddegrad, og verdiane går
frå vest mot aust. Fyrste rad er den nordlegaste. Det vil seie, den fyrste
verdien er i det nordvestre hjørnet, den siste i det søraustre hjørnet.
Innhald i metadata-blokken, fyrste 64 byte:
- Byte 1-4: Icode - 4 byte integer, variabel som skal ha verdi 777.
Viss ikkje er det feil med big/little endian.
- Byte 5-12: Rnmin - 8 byte float/double, gir minste breiddegrad for grid
- Byte 13-20: Rnmax - 8 byte float/double, gir største breiddegrad for grid
- Byte 21-28: Remin - 8 byte float/double, gir minste lengdegrad for grid
- Byte 29-36: Remin - 8 byte float/double, gir største lengdegrad for grid
- Byte 37-44: deltaN - 8 byte float/double, gir gridstørrelse i
breiddegradsretning
- Byte 45-52: deltaE - 8 byte float/double, gir gridstørrelse i
lengdegradsretning
- Byte 53-56: Iutm - 4 byte integer. Viss geografisk grid lik 0
- Byte 57-60: Iell - 4 byte integer. Lik 0 for geografisk grid.
For UTM-grid, kode for å definere ellipsoide.
- Byte 61-64: Izone - 4 byte integer. Angir UTM-sone,
viss geografisk grid er den lik 0.
Griddet i fila vil då ha N = (Rnmax - Rnmin)/deltaN + 1 rader og
M = (Remax - Remin)/deltaE + 1 kolonner.
OBS: viss M ikkje går opp i 16 i talet på verdiar i kvar blokk, vil slutten av
den siste blokken på ei rad vere fylt med null. T.d. om M = 2801, vil det
vere mod(2801, 16) = 1 reelle datapunkt i den siste blokken på ei rad,
og dermed får ein 16 - 1 = 15 nullverdiar på slutten av kvar rad.
Metode for lesing tatt frå https://stackoverflow.com/a/8711061
'''
# les inn heile fila gitt ved stien
with open(filename, mode='rb') as file:
fileContent = file.read()
# pakk ut fyrste fire byte til int, og sjekk om lik 777
Icode = struct.unpack('i', fileContent[:4])
if Icode[0] != 777:
raise('Icode er ikkje 777, feil endian')
# pakk ut neste 48 byte som double precision floats
Rnmin, Rnmax, Remin, Remax, dn, de = struct.unpack('dddddd',
fileContent[4:4+8*6])
# pakk ut siste del av metadatablokk som tre ints
Iutm, Iell, Izone = struct.unpack('iii', fileContent[4+8*6:64])
# dict med ellipsoider
ellipsoids = {
0: 'LatLon',
1: 'WGS84/NAD83',
2: 'Hayford/ED50',
3: 'Clarke/NAD27',
}
modEll = ellipsoids[Iell] if Iell in ellipsoids.keys() else 'Undefined'
# har ikkje testa med UTM-grid, so gi ut advarsel viss det er tilfelle
if Iutm != 0:
Warning('UTM-koordinatar i fil -- veit ikkje om dette fungerer')
# pakk ut resten av fila som single precision float, og legg i numpy array
data = struct.unpack('f' * ((len(fileContent) - 64) // 4),
fileContent[64:])
data = np.array(data)
# rekn ut storleik på grid
Nlon = round((Remax-Remin)/de) + 1
Nlat = round((Rnmax-Rnmin)/dn) + 1
# lag dict med lat/lon vektorar og anna metadata
# merk lat-vektoren går frå nord til sør for å samsvare med data-matrisa
D = {'lat': np.linspace(Rnmax, Rnmin, Nlat),
'lon': np.linspace(Remin, Remax, Nlon),
'dlat': dn,
'dlon': de,
'ellipsoide': modEll,
'UTM-sone': Izone}
N = data.size
if (N / Nlat) % 1 != 0:
raise('Talet på datapunkt går ikkje opp i talet med breiddegradpunkt.')
# kor mange datapunkt det er per rad, inkludert nullar
Nlon_with_zeros = N//Nlat
# (differansen mellom matrisestørrelse med og utan null-verdiar) delt på
# (differansen mellom radstørrelser med og utan null-verdiar)
# burde verte lik talet på rader
if (N - Nlon*Nlat) / (Nlon_with_zeros - Nlon) != Nlat:
raise('Feil tal på datapunkt i forhold til lat-lon-griddet.')
# reshape data til array, og fjern nullverdiane på slutten av kvar rad
data = data.reshape((int(Nlat), int(Nlon_with_zeros)))[:, :Nlon]
# lag masked_array og legg til i dict
# maske basert på størrelsen til verdiane
D['data'] = np.ma.masked_array(data, mask=np.abs(data) > 500)
return D
# definer namn på fila som skal lesast inn, og bruk funksjonen over
filnamn = 'ChartDatum_above_Ellipsoid_EUREF89_v2021a.bin'
modeldata = readbinfile(filnamn)
# lag matriser med lengde- og breiddegradsverdiar
lon, lat = np.meshgrid(modeldata['lon'], modeldata['lat'])
# plott resultatet
fig, ax = plt.subplots()
g = ax.pcolormesh(lon, lat, modeldata['data'])
ax.set_xlabel('Lengdegrad')
ax.set_ylabel('Breiddegrad')
ax.set_title('Sjøkartnull over ellipsoiden')
cb = fig.colorbar(g)
cb.set_label('Meter')
plt.show()
Separasjonsmodeller
Kartverket tilbyr ein rekke separasjonsmodellar som gjev deg skilnaden mellom to ulike referansenivå eller høgdesystem for eit geografisk område.
Desse kan ta deg frå GNSS-målingar til høgder over havet eller over sjøkartnull, eller frå djupnedata i sjøkart til høgder i NN2000.
E-posten er sendt