The Survey object#
Most of airbornegeo’s functions take a dataframe plus the names of the columns they need (line_column, distance_column, time_column, …), which quickly gets repetitive. The airbornegeo.Survey class stores the dataframe together with those column names, a coordinate reference system, free-form metadata, and the survey’s intersection table, and exposes most package functions as methods which fill the column names in for you.
Two kinds of methods:
Methods which add or alter a few columns (e.g.
along_track_distance,filter_line, the crossover pipeline) operate in place onsurvey.dataand return the survey itself, so calls can be chained.Methods which replace the whole dataframe (
block_reduce,resample,resample_as) return a newSurvey, leaving the original untouched.
Summary statistics (bounding region, line counts, line lengths, spacings, and orientations) are computed lazily the first time you access them and cached afterwards.
The functional API is unchanged — every method simply wraps the function of the same name.
[249]:
%load_ext autoreload
%autoreload 2
import logging
import numpy as np
import pandas as pd
import airbornegeo
# setup logging to get some additional info from the airbornegeo functions
logging.getLogger("airbornegeo").setLevel("INFO")
logging.basicConfig()
The autoreload extension is already loaded. To reload it, use:
%reload_ext autoreload
Load data#
This is a subset of the BAS AGAP aeromagnetic survey over Antarctica’s Gamburtsev Subglacial Mountains, already processed and block-reduced (see the notebooks AGAP_magnetic_survey and processing_AGAP_magnetics). We mark the ~E-W survey lines with line_type=0 and the ~N-S tie lines with line_type=1.
[250]:
df = pd.read_csv("data/AGAP_magnetic_survey_processed_blocked.csv")
df = df[["easting", "northing", "height", "line", "unixtime", "mag"]]
df = df.dropna(subset="mag")
# define survey lines (0) vs tie lines (1)
df["line_type"] = np.where(df.line >= 142, 1, 0)
# for speed, limit the number of lines and points
df = df[~df.line.between(133, 142)]
df = df[~df.line.between(168, 176)]
lines_to_keep = np.concat(
[df[df.line_type == 0].line.unique()[::5], df[df.line_type == 1].line.unique()[::2]]
)
df = df[df.line.isin(lines_to_keep)]
df = df[::5]
df.head()
[250]:
| easting | northing | height | line | unixtime | mag | line_type | |
|---|---|---|---|---|---|---|---|
| 0 | 621152.853769 | 159064.167598 | 4112.55 | 1 | 1.229500e+09 | -34.245 | 0 |
| 5 | 622163.262927 | 159216.562790 | 4133.80 | 1 | 1.229500e+09 | -47.965 | 0 |
| 10 | 623146.956750 | 159388.532824 | 4134.15 | 1 | 1.229500e+09 | -58.360 | 0 |
| 15 | 624125.472066 | 159547.768230 | 4136.50 | 1 | 1.229500e+09 | -66.160 | 0 |
| 20 | 625101.022414 | 159720.791113 | 4143.60 | 1 | 1.229500e+09 | -70.900 | 0 |
Create a Survey#
We pass the dataframe once, along with the names of its structural columns, the coordinate reference system, and any metadata we want to keep with it. Only the projected coordinates default to a name (easting/northing, which the rest of the package requires); every other column must be named explicitly, since naming conventions vary too much between surveys to guess. Name them once here and every method will use them — a method needing a binding you did not set raises a ValueError
telling you which one.
The repr shows the column bindings and which statistics have been computed so far — none yet, since they are lazy.
[251]:
survey = airbornegeo.Survey(
df,
line_column="line",
line_type_column="line_type",
time_column="unixtime",
height_column="height",
crs="EPSG:3031",
metadata={"survey": "AGAP aeromagnetics", "source": "BAS", "units": "nT"},
)
survey
[251]:
Column attributes
| line | line |
| line_type | line_type |
| distance | not set |
| time | unixtime |
| height | height |
| latitude | not set |
| longitude | not set |
CRS & metadata3 metadata entries
| crs | EPSG:3031 |
| survey | AGAP aeromagnetics |
| source | BAS |
| units | nT |
Statistics0/10 computed — access an attribute or call .describe()
| region | not computed |
| line_counts | not computed |
| line_lengths | not computed |
| total_length | not computed |
| median_line_lengths | not computed |
| line_azimuths | not computed |
| mean_line_azimuths | not computed |
| median_line_spacings | not computed |
| line_point_spacings | not computed |
| median_point_spacings | not computed |
IntersectionsNone
None — call create_intersection_table()
Datafirst 5 rows
| easting | northing | height | line | unixtime | mag | line_type | |
|---|---|---|---|---|---|---|---|
| 0 | 621152.853769 | 159064.167598 | 4112.55 | 1 | 1.229500e+09 | -34.245 | 0 |
| 5 | 622163.262927 | 159216.562790 | 4133.80 | 1 | 1.229500e+09 | -47.965 | 0 |
| 10 | 623146.956750 | 159388.532824 | 4134.15 | 1 | 1.229500e+09 | -58.360 | 0 |
| 15 | 624125.472066 | 159547.768230 | 4136.50 | 1 | 1.229500e+09 | -66.160 | 0 |
| 20 | 625101.022414 | 159720.791113 | 4143.60 | 1 | 1.229500e+09 | -70.900 | 0 |
Summary statistics#
Each statistic is computed on first access and cached. Accessing them again is free, and the cache is cleared automatically whenever a method changes the survey’s geometry (e.g. re-splitting lines or block-reducing).
[252]:
survey.region
[252]:
(406015.150105372, 1636043.0769911269, -451144.7242889665, 607524.3407102434)
[253]:
survey.line_counts
[253]:
{'total': 55, 'flight': 27, 'tie': 28}
Line spacings are estimated per line type with airbornegeo.median_line_spacing, and line orientations are compass azimuths (degrees clockwise from north, in the axial 0–180° range) of each line’s minimum rotated bounding box.
[254]:
survey.median_line_spacings
[254]:
{'flight': 24370.825789257113, 'tie': 65923.78618791168}
[255]:
survey.mean_line_azimuths
[255]:
{'flight': 79.95588628453424, 'tie': 169.96912559755293}
describe() computes everything at once and returns a formatted summary.
[256]:
print(survey.describe())
airbornegeo.Survey (17,423 rows x 7 cols)
crs: EPSG:3031
metadata: {'survey': 'AGAP aeromagnetics', 'source': 'BAS', 'units': 'nT'}
region (W, E, S, N): 406,015.2, 1,636,043.1, -451,144.7, 607,524.3
line counts: total: 55, flight: 27, tie: 28
total length: 17,405,082.7
median line lengths: flight: 312,632.5, tie: 246,316.5
mean line azimuths: flight: 80.0, tie: 170.0
median line spacings: flight: 24,370.8, tie: 65,923.8
median point spacings: flight: 1,000.0, tie: 999.6
[257]:
survey
[257]:
Column attributes
| line | line |
| line_type | line_type |
| distance | not set |
| time | unixtime |
| height | height |
| latitude | not set |
| longitude | not set |
CRS & metadata3 metadata entries
| crs | EPSG:3031 |
| survey | AGAP aeromagnetics |
| source | BAS |
| units | nT |
Statisticsall computed
| region | 406,015.2, 1,636,043.1, -451,144.7, 607,524.3 |
| line_counts | total: 55, flight: 27, tie: 28 |
| line_lengths | 55 lines | min 27,901.8 | median 282,018.7 | max 744,780.9 |
| total_length | 17,405,082.7 |
| median_line_lengths | flight: 312,632.5, tie: 246,316.5 |
| line_azimuths | 55 lines | min 79.8 | median 169.6 | max 170.4 |
| mean_line_azimuths | flight: 80.0, tie: 170.0 |
| median_line_spacings | flight: 24,370.8, tie: 65,923.8 |
| line_point_spacings | 55 lines | min 993.1 | median 999.7 | max 1,004.5 |
| median_point_spacings | flight: 1,000.0, tie: 999.6 |
IntersectionsNone
None — call create_intersection_table()
Datafirst 5 rows
| easting | northing | height | line | unixtime | mag | line_type | |
|---|---|---|---|---|---|---|---|
| 0 | 621152.853769 | 159064.167598 | 4112.55 | 1 | 1.229500e+09 | -34.245 | 0 |
| 5 | 622163.262927 | 159216.562790 | 4133.80 | 1 | 1.229500e+09 | -47.965 | 0 |
| 10 | 623146.956750 | 159388.532824 | 4134.15 | 1 | 1.229500e+09 | -58.360 | 0 |
| 15 | 624125.472066 | 159547.768230 | 4136.50 | 1 | 1.229500e+09 | -66.160 | 0 |
| 20 | 625101.022414 | 159720.791113 | 4143.60 | 1 | 1.229500e+09 | -70.900 | 0 |
Calling a class method#
Functions such as along_track_distance require parameters such as groupby_column to inform the function which column of the dataframe denotes separate groups (lines). When we call these functions as class methods, those parameters which are stored as class column attributes,are automatically passed to the function. And in the below example, the resulting new column name along_track_dist is added as the distance column so other functions when required distances know when column to
use.
[258]:
survey.along_track_distance("along_track_dist")
survey
[258]:
Column attributes
| line | line |
| line_type | line_type |
| distance | along_track_dist |
| time | unixtime |
| height | height |
| latitude | not set |
| longitude | not set |
CRS & metadata3 metadata entries
| crs | EPSG:3031 |
| survey | AGAP aeromagnetics |
| source | BAS |
| units | nT |
Statisticsall computed
| region | 406,015.2, 1,636,043.1, -451,144.7, 607,524.3 |
| line_counts | total: 55, flight: 27, tie: 28 |
| line_lengths | 55 lines | min 27,901.8 | median 282,018.7 | max 744,780.9 |
| total_length | 17,405,082.7 |
| median_line_lengths | flight: 312,632.5, tie: 246,316.5 |
| line_azimuths | 55 lines | min 79.8 | median 169.6 | max 170.4 |
| mean_line_azimuths | flight: 80.0, tie: 170.0 |
| median_line_spacings | flight: 24,370.8, tie: 65,923.8 |
| line_point_spacings | 55 lines | min 993.1 | median 999.7 | max 1,004.5 |
| median_point_spacings | flight: 1,000.0, tie: 999.6 |
IntersectionsNone
None — call create_intersection_table()
Datafirst 5 rows
| easting | northing | height | line | unixtime | mag | line_type | along_track_dist | |
|---|---|---|---|---|---|---|---|---|
| 0 | 621152.853769 | 159064.167598 | 4112.55 | 1 | 1.229500e+09 | -34.245 | 0 | 0.000000 |
| 5 | 622163.262927 | 159216.562790 | 4133.80 | 1 | 1.229500e+09 | -47.965 | 0 | 1021.837051 |
| 10 | 623146.956750 | 159388.532824 | 4134.15 | 1 | 1.229500e+09 | -58.360 | 0 | 2020.449705 |
| 15 | 624125.472066 | 159547.768230 | 4136.50 | 1 | 1.229500e+09 | -66.160 | 0 | 3011.836682 |
| 20 | 625101.022414 | 159720.791113 | 4143.60 | 1 | 1.229500e+09 | -70.900 | 0 | 4002.611833 |
Chained processing#
Methods which add columns mutate survey.data in place and return the survey, so processing steps chain naturally. Here we compute the distance along each line (written to the survey’s distance_column) and low-pass filter the magnetic data along each line (written to mag_filtered by default).
[259]:
survey.ground_speed().filter_line(
data_column="ground_speed",
filter_width=5e3,
)
survey.data[["line", "ground_speed", "ground_speed", "ground_speed_filtered"]].head()
[259]:
| line | ground_speed | ground_speed | ground_speed_filtered | |
|---|---|---|---|---|
| 0 | 1 | 53.780897 | 53.780897 | 54.306611 |
| 5 | 1 | 54.652629 | 54.652629 | 54.868767 |
| 10 | 1 | 56.072838 | 56.072838 | 56.072502 |
| 15 | 1 | 57.477601 | 57.477601 | 57.302141 |
| 20 | 1 | 58.299015 | 58.299015 | 58.254057 |
Frame-replacing methods return a new Survey#
block_reduce, resample and resample_as produce an entirely new dataframe, so instead of overwriting the survey they return a new Survey carrying over the column bindings, CRS and metadata — the original survey is retained.
[260]:
blocked = survey.block_reduce(np.median, spacing=1e3)
print(f"original: {len(survey.data)} rows, blocked: {len(blocked.data)} rows")
blocked
original: 17423 rows, blocked: 14978 rows
[260]:
Column attributes
| line | line |
| line_type | line_type |
| distance | along_track_dist |
| time | unixtime |
| height | height |
| latitude | not set |
| longitude | not set |
CRS & metadata3 metadata entries
| crs | EPSG:3031 |
| survey | AGAP aeromagnetics |
| source | BAS |
| units | nT |
Statistics0/10 computed — access an attribute or call .describe()
| region | not computed |
| line_counts | not computed |
| line_lengths | not computed |
| total_length | not computed |
| median_line_lengths | not computed |
| line_azimuths | not computed |
| mean_line_azimuths | not computed |
| median_line_spacings | not computed |
| line_point_spacings | not computed |
| median_point_spacings | not computed |
IntersectionsNone
None — call create_intersection_table()
Datafirst 5 rows
| along_track_dist | easting | northing | height | line | unixtime | mag | line_type | ground_speed | ground_speed_filtered | |
|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 0.000000 | 621152.853769 | 159064.167598 | 4112.55 | 1 | 1.229500e+09 | -34.245 | 0.0 | 53.780897 | 54.306611 |
| 1 | 1021.837051 | 622163.262927 | 159216.562790 | 4133.80 | 1 | 1.229500e+09 | -47.965 | 0.0 | 54.652629 | 54.868767 |
| 2 | 2020.449705 | 623146.956750 | 159388.532824 | 4134.15 | 1 | 1.229500e+09 | -58.360 | 0.0 | 56.072838 | 56.072502 |
| 3 | 3011.836682 | 624125.472066 | 159547.768230 | 4136.50 | 1 | 1.229500e+09 | -66.160 | 0.0 | 57.477601 | 57.302141 |
| 4 | 4002.611833 | 625101.022414 | 159720.791113 | 4143.60 | 1 | 1.229500e+09 | -70.900 | 0.0 | 58.299015 | 58.254057 |
Plotting#
The plot() method plots a simple overview map of the survey data. You can choose which data column to color the points by.
[261]:
survey.plot(
color_by="line_type",
categorical=True,
label_categories=True,
)
[261]:
<Axes: title={'center': 'Survey: AGAP aeromagnetics'}, xlabel='easting', ylabel='northing'>
[262]:
survey.plot(color_by="mag")
[262]:
<Axes: title={'center': 'Survey: AGAP aeromagnetics'}, xlabel='easting', ylabel='northing'>
To plot a profile of a line, use plot_profiles and pass the line number and the column you want on the y axis.
[274]:
ax = survey.plot_profiles(
86,
y="mag",
)
Crossovers and levelling#
The crossover pipeline stores the intersection table on the survey as survey.intersections and updates it, together with survey.data, as each chained step runs. Here we find the intersections between the survey lines and the tie lines (the groups method, using the line_type column), add them to the dataframe, interpolate the magnetic data onto them, and calculate the crossover errors.
(The network method finds intersections between all line pairs instead; note that pairs of lines crossing more than once must be deduplicated before the rest of the pipeline — see the crossovers notebooks for details.)
[264]:
survey.create_intersection_table(
method="groups",
buffer_dist=500,
cutoff_dist=1e3,
)
survey.intersections.head()
INFO:airbornegeo:found 132 intersections
INFO:airbornegeo:removed 0 intersection point(s) with a max distance greater than 1.0 km
[264]:
| line1 | line2 | line1_along_dist | line2_along_dist | is_buffered | is_proximity | geometry | easting | northing | max_dist | |
|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 1 | 143 | 545635.514241 | 137755.398042 | False | False | POINT (1158154 254086) | 1158154.0 | 254086.0 | 396.438502 |
| 1 | 1 | 145 | 612039.124020 | 146343.411164 | False | False | POINT (1223519 265688) | 1223519.0 | 265688.0 | 393.967055 |
| 2 | 1 | 147 | 678454.809205 | 153987.235731 | False | False | POINT (1288902 277251) | 1288902.0 | 277251.0 | 312.700690 |
| 3 | 1 | 151 | 49724.770644 | 117174.975042 | False | False | POINT (670005 167672) | 670005.0 | 167672.0 | 283.606212 |
| 4 | 1 | 153 | 82657.056363 | 122714.949843 | True | False | POINT (702428 173420) | 702428.0 | 173420.0 | 331.990443 |
[265]:
survey.add_intersections().interpolate_intersections(
to_interp="mag"
).calculate_crossover_errors(data_col="mag")
survey.intersections.crossover_error_0.describe()
[265]:
count 131.000000
mean 10.590973
std 40.155999
min -78.528582
25% -23.436373
50% 14.900799
75% 41.849618
max 102.565149
Name: crossover_error_0, dtype: float64
[266]:
survey.plot_intersections(
"crossover_error_0",
)
[266]:
<Axes: title={'center': 'Crossover errors: crossover_error_0'}, xlabel='easting', ylabel='northing'>
Now we can level the survey using those crossover errors. The network levelling method updates both survey.data (adding the mag_levelled column) and survey.intersections (appending a new crossover_error_N column per iteration).
[267]:
survey.crossover_network_levelling(
data_col="mag",
levelled_col="mag_levelled",
degree=1,
max_iterations=5,
)
WARNING:airbornegeo:
Levelling terminated after 5 iterations with RMS of levelling correction of 7.63 because maximum number of iterations (5) reached.
[267]:
Column attributes
| line | line |
| line_type | line_type |
| distance | along_track_dist |
| time | unixtime |
| height | height |
| latitude | not set |
| longitude | not set |
CRS & metadata3 metadata entries
| crs | EPSG:3031 |
| survey | AGAP aeromagnetics |
| source | BAS |
| units | nT |
Statistics0/10 computed — access an attribute or call .describe()
| region | not computed |
| line_counts | not computed |
| line_lengths | not computed |
| total_length | not computed |
| median_line_lengths | not computed |
| line_azimuths | not computed |
| mean_line_azimuths | not computed |
| median_line_spacings | not computed |
| line_point_spacings | not computed |
| median_point_spacings | not computed |
Intersections131 rows
131 intersections with columns: ['line1', 'line2', 'line1_along_dist', 'line2_along_dist', 'is_buffered', 'is_proximity', 'geometry', 'easting', 'northing', 'max_dist', 'dist_along_line1', 'dist_along_line2', 'line1_interpolation_type', 'line2_interpolation_type', 'crossover_error_0', 'crossover_error_1', 'crossover_error_2', 'crossover_error_3', 'crossover_error_4', 'crossover_error_5']
Datafirst 5 rows
| easting | northing | height | line | unixtime | mag | line_type | along_track_dist | ground_speed | ground_speed_filtered | is_intersection | intersecting_line | mag_interpolation_type | mag_levelled | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 621152.853769 | 159064.167598 | 4112.55 | 1 | 1.229500e+09 | -34.245 | 0.0 | 0.000000 | 53.780897 | 54.306611 | False | NaN | none | -26.447144 |
| 1 | 622163.262927 | 159216.562790 | 4133.80 | 1 | 1.229500e+09 | -47.965 | 0.0 | 1021.837051 | 54.652629 | 54.868767 | False | NaN | none | -40.193800 |
| 2 | 623146.956750 | 159388.532824 | 4134.15 | 1 | 1.229500e+09 | -58.360 | 0.0 | 2020.449705 | 56.072838 | 56.072502 | False | NaN | none | -50.614850 |
| 3 | 624125.472066 | 159547.768230 | 4136.50 | 1 | 1.229500e+09 | -66.160 | 0.0 | 3011.836682 | 57.477601 | 57.302141 | False | NaN | none | -58.440711 |
| 4 | 625101.022414 | 159720.791113 | 4143.60 | 1 | 1.229500e+09 | -70.900 | 0.0 | 4002.611833 | 58.299015 | 58.254057 | False | NaN | none | -63.206556 |
[268]:
error_cols = [
c for c in survey.intersections.columns if c.startswith("crossover_error_")
]
rms_before = airbornegeo.rmse(survey.intersections[error_cols[0]])
rms_after = airbornegeo.rmse(survey.intersections[error_cols[-1]])
print(f"crossover error RMS: {rms_before:.2f} -> {rms_after:.2f} nT")
crossover error RMS: 41.38 -> 12.29 nT
The functional equivalent of this whole notebook would have passed line_column, distance_column and the intersection table around at every step — with the Survey object they are stored once and injected for you, while airbornegeo’s functions remain available for anything not wrapped as a method (e.g. the Eötvös corrections and plotting utilities).
Saving and reloading a Survey#
Survey has two built-in ways to save and reload its full state — the data, the intersection table (if any), the CRS, the metadata, and the column-name bindings:
``to_parquet``/``from_parquet`` (recommended): the data and intersections are saved as Parquet files, plus a small JSON sidecar for the CRS/metadata/bindings. Parquet preserves dtypes exactly and is fast and compact, so prefer it unless you need a plain-text format.
``to_csv``/``from_csv``: the same layout, but with CSV data files instead. Maximally portable and human-readable, at the cost of dtype fidelity (e.g. integer columns come back as floats) and file size.
Either way, the main data file is a plain Parquet/CSV file with no airbornegeo-specific encoding — anyone can read <path>.parquet/<path>.csv with bare pandas, no airbornegeo import required.
[269]:
import tempfile
from pathlib import Path
# replace this with your real local path you want to use
example_path = Path(tempfile.mkdtemp()) / "agap_survey"
example_path
[269]:
PosixPath('/tmp/tmpe0kdf8s2/agap_survey')
[270]:
# save the survey to the set path
survey.to_parquet(example_path)
# show the 3 file which were created
sorted(p.name for p in example_path.parent.iterdir())
[270]:
['agap_survey.json',
'agap_survey.parquet',
'agap_survey_intersections.parquet']
[271]:
reloaded = airbornegeo.Survey.from_parquet(example_path)
reloaded
[271]:
Column attributes
| line | line |
| line_type | line_type |
| distance | along_track_dist |
| time | unixtime |
| height | height |
| latitude | not set |
| longitude | not set |
CRS & metadata3 metadata entries
| crs | EPSG:3031 |
| survey | AGAP aeromagnetics |
| source | BAS |
| units | nT |
Statistics0/10 computed — access an attribute or call .describe()
| region | not computed |
| line_counts | not computed |
| line_lengths | not computed |
| total_length | not computed |
| median_line_lengths | not computed |
| line_azimuths | not computed |
| mean_line_azimuths | not computed |
| median_line_spacings | not computed |
| line_point_spacings | not computed |
| median_point_spacings | not computed |
Intersections131 rows
131 intersections with columns: ['line1', 'line2', 'line1_along_dist', 'line2_along_dist', 'is_buffered', 'is_proximity', 'geometry', 'easting', 'northing', 'max_dist', 'dist_along_line1', 'dist_along_line2', 'line1_interpolation_type', 'line2_interpolation_type', 'crossover_error_0', 'crossover_error_1', 'crossover_error_2', 'crossover_error_3', 'crossover_error_4', 'crossover_error_5']
Datafirst 5 rows
| easting | northing | height | line | unixtime | mag | line_type | along_track_dist | ground_speed | ground_speed_filtered | is_intersection | intersecting_line | mag_interpolation_type | mag_levelled | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 621152.853769 | 159064.167598 | 4112.55 | 1 | 1.229500e+09 | -34.245 | 0.0 | 0.000000 | 53.780897 | 54.306611 | False | NaN | none | -26.447144 |
| 1 | 622163.262927 | 159216.562790 | 4133.80 | 1 | 1.229500e+09 | -47.965 | 0.0 | 1021.837051 | 54.652629 | 54.868767 | False | NaN | none | -40.193800 |
| 2 | 623146.956750 | 159388.532824 | 4134.15 | 1 | 1.229500e+09 | -58.360 | 0.0 | 2020.449705 | 56.072838 | 56.072502 | False | NaN | none | -50.614850 |
| 3 | 624125.472066 | 159547.768230 | 4136.50 | 1 | 1.229500e+09 | -66.160 | 0.0 | 3011.836682 | 57.477601 | 57.302141 | False | NaN | none | -58.440711 |
| 4 | 625101.022414 | 159720.791113 | 4143.60 | 1 | 1.229500e+09 | -70.900 | 0.0 | 4002.611833 | 58.299015 | 58.254057 | False | NaN | none | -63.206556 |
The data file needs nothing but pandas to read — no airbornegeo import required:
[272]:
import pandas as pd
pd.read_parquet(example_path.with_suffix(".parquet")).head()
[272]:
| easting | northing | height | line | unixtime | mag | line_type | along_track_dist | ground_speed | ground_speed_filtered | is_intersection | intersecting_line | mag_interpolation_type | mag_levelled | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 621152.853769 | 159064.167598 | 4112.55 | 1 | 1.229500e+09 | -34.245 | 0.0 | 0.000000 | 53.780897 | 54.306611 | False | NaN | none | -26.447144 |
| 1 | 622163.262927 | 159216.562790 | 4133.80 | 1 | 1.229500e+09 | -47.965 | 0.0 | 1021.837051 | 54.652629 | 54.868767 | False | NaN | none | -40.193800 |
| 2 | 623146.956750 | 159388.532824 | 4134.15 | 1 | 1.229500e+09 | -58.360 | 0.0 | 2020.449705 | 56.072838 | 56.072502 | False | NaN | none | -50.614850 |
| 3 | 624125.472066 | 159547.768230 | 4136.50 | 1 | 1.229500e+09 | -66.160 | 0.0 | 3011.836682 | 57.477601 | 57.302141 | False | NaN | none | -58.440711 |
| 4 | 625101.022414 | 159720.791113 | 4143.60 | 1 | 1.229500e+09 | -70.900 | 0.0 | 4002.611833 | 58.299015 | 58.254057 | False | NaN | none | -63.206556 |