Introduksjon¶
Sanntidsdata, som vi allerede har sett på, er et svært nyttig verktøy for å orientere seg om øyeblikksbildet – hva som skjer her og nå. På samme måte er det fint å kunne spole tilbake i tid for å se hvordan situasjonen så ut for fem, ti og femten minutter siden – eller i går, forrige uke og kanskje i fjor sommer.
Samtidig er det situasjoner hvor vi kanskje vet hva vi ser etter, eller hvor vi skal se, men ikke nødvendigvis vet når det riktige tidspunktet er. Vi har kanskje hex-koden til et fly vi vet har passert over Norge, men ikke tidspunktet. Hvor har politihelikopteret flydd den siste uken? Og hvilke helikoptre har blitt observert i et gitt geografisk område?
For å kunne jobbe effektivt med denne typen spørsmål og problemstillinger, trenger vi et verktøy som kan håndtere store mengder historiske data effektivt. Til dette skal vi bruke DuckDB.
Hva er DuckDB?¶
DuckDB er en lettvekts, relasjonell analytisk database optimalisert for analyse av store datamengder. Den er designet med fokus på ytelse, enkelhet og fleksibilitet, noe som gjør den svært velegnet for datajournalistisk analyse – for eksempel av store skips- og flytrafikkdatasett.
DuckDB kjører lokalt på datamaskinen din, noe som betyr at du ikke trenger å sette opp en kompleks server for å håndtere dataene. Hvis du allerede har gjort de tekniske forberedelsene tidligere i dette kompendiet, har du alt du trenger på datamaskinen din for å komme i gang.
DuckDB kan integreres direkte med verktøy som Python og R, noe som gir oss muligheten til å kombinere SQL-spørringer med andre dataanalyseverktøy og biblioteker. Dette er svært nyttig for å lage datadrevne historier som involverer statistisk analyse eller visualiseringer. Og hvis du trives i Excel, er det enkelt å eksportere et filtrert datasett direkte til dette verktøyet.
DuckDB, DuckLake og historiske data¶
Så hvordan får vi historiske data inn i DuckDB? I disse øvelsene tar vi utgangspunkt i Medieklyngens ADS-B-server, som tilrettelegger dataene i et format som enkelt lar seg importere inn i DuckDB uten ytterligere bearbeiding.
Medieklyngens ADS-B-server lagrer alle data fra sensorene som deler data med fellesskapet. Alle sensorene som henter ut rådata fra luften, sender disse dataene inn til en server som løpende vasker og bearbeider dem. Deretter lagres dataene i en datasjø noen ganger i minuttet. Datasjøen vi benytter i Medieklyngen, er DuckLake – en datasjø-standard som integrerer tett med DuckDB.
I DuckLake lagres dataene i formatet Parquet, eller parkett på godt norsk. Ja, som i tregulvet. I motsetning til radbaserte formater som CSV og Excel, lagrer Parquet-formatet dataene kolonnevis. Dette gir flere fordeler ved analyse av store datasett, siden det blir raskere å hente ut og komprimere spesifikke kolonner. For fly- og skipsdata, som ofte inneholder mange attributter som posisjon, hastighet og høyde, gjør Parquet det enkelt å trekke ut og behandle relevante felter. Dette gir betydelige ytelsesforbedringer sammenlignet med radbaserte formater – og er ofte 10-100 ganger raskere for analytiske spørringer.
Parquet-filene i DuckLake er hive-partisjonert, noe som betyr at de er delt inn i mapper basert på år, måned og dag. Dette gjør det enklere for DuckDB å effektivt filtrere og hente ut spesifikke data, og forbedrer spørringsytelsen ved at DuckDB kun leser de nødvendige partisjonene i stedet for hele datasettet.
Parquet-filer kan leses av mange ulike verktøy og programmeringsspråk, noe som gir stor fleksibilitet. For eksempel kan Parquet-filer behandles i verktøy som Apache Spark, Hadoop, Presto, Amazon Athena i tillegg til DuckDB. Dette gir en robust og skalerbar løsning som er klargjort for fremtidig vekst i datamengdene. Den gode nyheten er at du ikke trenger å forholde deg til denne kompleksiteten med mindre du ønsker å gå i dybden på et senere tidspunkt.
Spørrespråket SQL¶
DuckDB benytter det veletablerte spørrespråket SQL (Structured Query Language). SQL har røtter tilbake til 1970-tallet, da det ble utviklet av IBM for å håndtere data i relasjonsdatabaser. Språket er intuitivt bygget opp og relativt enkelt å lære seg på et grunnleggende nivå.
I DuckDBs SQL-dialekt bruker du enkle kommandoer for å hente, manipulere og analysere data fra tabeller. Den grunnleggende strukturen består av kommandoer som SELECT, som brukes for å hente spesifikke kolonner fra en tabell, og FROM, som angir hvilken tabell (datakilde) du henter data fra. Du kan også bruke WHERE for å filtrere dataene basert på bestemte kriterier.
Skjematisk forklart:
SELECT [kolonner]
FROM [tabell]
WHERE [betingelser];For eksempel kan en spørring se slik ut:
SELECT * FROM flights WHERE altitude > 10000;Her henter man ut alle kolonner fra tabellen flights, hvor høyde over havet er større enn 10.000 (fot).
Forberedelser¶
Nå skal vi sette opp DuckDB og koble oss til Medieklyngens datasjø. Under går vi gjennom stegene systematisk. De følgende instruksjonene forutsetter at du har installert DuckDB på maskinen din som beskrevet i kapitlet Tekniske forberedelser. Hvis du har lastet ned DuckDB som en fil i en mappe i stedet for å installere det, er det viktig at du navigerer til riktig mappe før du begynner å kjøre disse kommandoene.
Oppstart¶
Lag en tom mappe på valgfri plassering på datamaskinen din.
Start Terminal (Mac og Linux) eller PowerShell (Windows), og naviger til mappen du akkurat opprettet.
Start DuckDB med følgende kommando:
duckdb sensorkurs-andre-samling.dbDette starter DuckDB med en tom databasefil kalt sensorkurs-andre-samling.db. Filen lagres i mappen du kjører kommandoen fra.
DuckDB svarer med et prompt som ser omtrent slik ut:
DuckDB v1.4.1 (Andium) b390a7c376
Enter ".help" for usage hints.
D Installer og last inn utvidelser¶
Installer følgende utvidelser ved å skrive inn disse kommandoene i prompten (dette gjøres kun ved første gangs kjøring):
-- Installer utvidelser
INSTALL httpfs;
INSTALL postgres;
INSTALL ducklake;
INSTALL spatial;
INSTALL h3 FROM community;Last inn utvidelsene du akkurat installerte ved hjelp av følgende kommandoer (dette må gjøres hver gang, eller automatiseres med et script, se under):
-- Last inn utvidelser
LOAD httpfs;
LOAD postgres;
LOAD ducklake;
LOAD spatial;
LOAD h3;Legg inn tilgangsnøkler til serveren som lagrer de historiske dataene:
-- Legg inn tilgangsnøkkel
CREATE SECRET (
TYPE R2,
KEY_ID '9030e0f90a86af08b08b6e2a1222a778',
SECRET '2fe64ae1c22869400f577bb9421602f0f81a83a2f658cea6bdd556f4fc65064b',
ACCOUNT_ID 'bca3475a0f4afeb0640daafc17ec2b18'
);DuckDB bekrefter med:
┌─────────┐
│ Success │
│ boolean │
├─────────┤
│ true │
└─────────┘Logg på datasjø (DuckLake):
-- Logg på datasjø
ATTACH 'ducklake:postgres:dbname=mcn_ducklake host=ep-fragrant-unit-a20fur0h.eu-central-1.aws.neon.tech user=fra_signaler_til_skup_h2_2025 password=npg_TLH6IjXdAW9q sslmode=require' AS mcn
(DATA_PATH 'r2://mcn-ducklake', READ_ONLY);Gratulerer! Du er nå koblet til Medieklyngens datasjø og klar til å begynne å utforske data.
Last inn data i DuckDB¶
Nå begynner det spennende! La oss starte med å laste ned alle kjente flybevegelser for en spesifikk dag, for eksempel 1. november 2025. Kjør følgende kommando:
SELECT * FROM mcn.airtraces WHERE timestamp >= '2025-11-01 00:00:00' and timestamp < '2025-11-02 00:00:00';Når du trykker Enter for å kjøre kommandoen, tar det litt tid før du får respons. DuckDB laster ned dataene fra Medieklyngens datasjø, pakker dem ut og importerer dem til databasen. Når det er ferdig, ser det forhåpentligvis først slik ut:
100% ▕████████████████████████████████████████████████████████████▏Deretter listes det ut en tabell i terminalen som trolig minner om dette:
┌──────────────────────┬─────────┬──────────────┬─────────┬───┬─────────┬─────────┬───────┐
│ timestamp │ icao │ registration │ squawk │ … │ is_pia │ is_ladd │ flags │
│ timestamp with tim… │ varchar │ varchar │ varchar │ │ boolean │ boolean │ int32 │
├──────────────────────┼─────────┼──────────────┼─────────┼───┼─────────┼─────────┼───────┤
│ 2025-11-01 01:00:0… │ ADA699 │ N979RP │ 1405 │ … │ false │ false │ 0 │
│ 2025-11-01 01:00:0… │ 4BA9EF │ TC-JOO │ 2247 │ … │ false │ false │ 0 │
│ 2025-11-01 01:00:0… │ AB6FBF │ N836MH │ 2013 │ … │ false │ false │ 0 │
│ 2025-11-01 01:00:0… │ ABFDD1 │ N872AN │ 0551 │ … │ false │ false │ 0 │
│ 2025-11-01 01:00:0… │ 4CAD94 │ EI-SCD │ 0201 │ … │ false │ false │ 0 │
│ 2025-11-01 01:00:0… │ 48C1A8 │ SP-RNI │ 3567 │ … │ false │ false │ 0 │
│ 2025-11-01 01:00:0… │ 4BA9EF │ TC-JOO │ 2247 │ … │ false │ false │ 0 │
│ 2025-11-01 01:00:0… │ 478A54 │ NULL │ NULL │ … │ NULL │ NULL │ NULL │
│ 2025-11-01 01:00:0… │ 4080E2 │ G-JZDG │ 3257 │ … │ false │ false │ 0 │
│ 2025-11-01 01:00:0… │ AB6FBF │ N836MH │ 2013 │ … │ false │ false │ 0 │
│ 2025-11-01 01:00:0… │ ADA699 │ N979RP │ 1405 │ … │ false │ false │ 0 │
│ 2025-11-01 01:00:0… │ 48C1A8 │ SP-RNI │ 3567 │ … │ false │ false │ 0 │
│ 2025-11-01 01:00:0… │ 478A54 │ NULL │ NULL │ … │ NULL │ NULL │ NULL │
│ 2025-11-01 01:00:0… │ 4CAD94 │ EI-SCD │ 0201 │ … │ false │ false │ 0 │
│ 2025-11-01 01:00:0… │ 4BA9EF │ TC-JOO │ 2247 │ … │ false │ false │ 0 │
│ 2025-11-01 01:00:0… │ 4080E2 │ G-JZDG │ 3257 │ … │ false │ false │ 0 │
│ 2025-11-01 01:00:0… │ ADA699 │ N979RP │ 1405 │ … │ false │ false │ 0 │
│ 2025-11-01 01:00:0… │ 48C1A8 │ SP-RNI │ 3567 │ … │ false │ false │ 0 │
│ 2025-11-01 01:00:0… │ 478A6F │ NULL │ NULL │ … │ NULL │ NULL │ NULL │
│ 2025-11-01 01:00:0… │ 478A80 │ NULL │ NULL │ … │ NULL │ NULL │ NULL │
│ · │ · │ · │ · │ · │ · │ · │ · │
│ · │ · │ · │ · │ · │ · │ · │ · │
│ · │ · │ · │ · │ · │ · │ · │ · │
│ 2025-11-01 20:06:1… │ 4D2529 │ 9H-WNC │ 4321 │ … │ false │ false │ 0 │
│ 2025-11-01 20:06:1… │ 45D961 │ OY-VKA │ 2357 │ … │ false │ false │ 0 │
│ 2025-11-01 20:06:1… │ 406532 │ G-EUYM │ 6216 │ … │ false │ false │ 0 │
│ 2025-11-01 20:06:1… │ 4ACA87 │ SE-RTG │ 1161 │ … │ false │ false │ 0 │
│ 2025-11-01 20:06:1… │ 4ACAB2 │ SE-RUR │ 2354 │ … │ false │ false │ 0 │
│ 2025-11-01 20:06:1… │ 478A3E │ NULL │ NULL │ … │ NULL │ NULL │ NULL │
│ 2025-11-01 20:06:1… │ 478A45 │ NULL │ NULL │ … │ NULL │ NULL │ NULL │
│ 2025-11-01 20:06:1… │ AC72F0 │ N901AN │ 3451 │ … │ false │ false │ 0 │
│ 2025-11-01 20:06:1… │ 4ACA6D │ SE-RSM │ 6404 │ … │ false │ false │ 0 │
│ 2025-11-01 20:06:1… │ 4D2529 │ 9H-WNC │ 4321 │ … │ false │ false │ 0 │
│ 2025-11-01 20:06:1… │ 48C2A2 │ SP-RZC │ 2276 │ … │ false │ false │ 0 │
│ 2025-11-01 20:06:1… │ 4CA81D │ EI-DTM │ 1000 │ … │ false │ false │ 0 │
│ 2025-11-01 20:06:1… │ 702086 │ S2-AJS │ 5221 │ … │ false │ false │ 0 │
│ 2025-11-01 20:06:1… │ 4AC9F3 │ SE-ROS │ 0202 │ … │ false │ false │ 0 │
│ 2025-11-01 20:06:1… │ 406532 │ G-EUYM │ 6216 │ … │ false │ false │ 0 │
│ 2025-11-01 20:06:1… │ 45AC52 │ OY-KBR │ 0412 │ … │ false │ false │ 0 │
│ 2025-11-01 20:06:1… │ 407CB1 │ G-RUKE │ 6217 │ … │ false │ false │ 0 │
│ 2025-11-01 20:06:1… │ 4D24D6 │ 9H-WDX │ 4322 │ … │ false │ false │ 0 │
│ 2025-11-01 20:06:1… │ 4ACA87 │ SE-RTG │ 1161 │ … │ false │ false │ 0 │
│ 2025-11-01 20:06:1… │ 4D2529 │ 9H-WNC │ 4321 │ … │ false │ false │ 0 │
├──────────────────────┴─────────┴──────────────┴─────────┴───┴─────────┴─────────┴───────┤
│ 3839743 rows (3.84 million rows, 40 shown) 82 columns (7 shown) │
└─────────────────────────────────────────────────────────────────────────────────────────┘Du har gjennomført din første spørring i DuckDB – gratulerer!
La oss bryte den opp:
SELECT *– Velg alle kolonnerFROM mcn.airtraces– Fra tabellen mcn.airtracesWHERE timestamp >= '2025-11-01 00:00:00' and timestamp < '2025-11-02 00:00:00'– Som er lagret etter midnatt 1. november 2025, og før midnatt 2. november 2025;– Merk at spørringen avsluttes med et semikolon. Det er vårt signal til DuckDB om at spørringen kan kjøres
Deretter ble det skrevet ut en tabell til terminalen. Hvis vi ser på nederste linje i denne, ser vi at DuckDB forteller at den akkurat har returnert 3,84 millioner rader, hvorav 40 er synlige på skjermen (de tyve første, og de tyve siste i resultatet). Videre ser vi at det er 82 kolonner, med antallet som er synlig på skjermen i parentes.
Akkurat hvor mange kolonner som vises hos deg, avhenger av oppløsningen på skjermen din.
Vi har altså hentet ned 3,84 millioner rader, og som vi så tok det noe tid å fullføre spørringen. Det er fordi vi også har hentet hele 82 kolonner per rad, noe som tilsammen utgjør nærmere 314 millioner felt med data. Det er en betydelig mengde. Er alle kolonnene like relevante? Svaret er nei. Og DuckDB har tenkt på dette. Mens data i tekstfiler og regneark er lagret radvis, er dataene i DuckLake som sagt lagret kolonnevis. I tillegg er de komprimert på en måte som gjør det enkelt å hente ut bare de dataene vi trenger. Da går spørringen vår raskere, og vi blir mer effektive. Det skal vi se på straks. Men først, la oss se nærmere på kolonnene med data vi har til rådighet i datasettet vårt. Og til det trenger vi såkalte punktum-kommandoer.
Punktum-kommandoer i DuckDB¶
Nå har vi allerede sett at vi ikke har plass til å liste ut hele tabellen i et skjermbilde, men det finnes en løsning for dette. Hvis vi har behov for å se flere felter, kan vi endre hvordan dataene listes ut med en såkalt punktum-kommando. For å få oversikt over disse, skriver vi .help i DuckDBs prompt. Da lister DuckDB ut en meny med alternativer.
Her er det mye å velge i. Men vi vet heldigvis hva vi leter etter, og finner kommandoen .mode. Skriv .help mode for å vise de ulike alternativene:
.mode MODE ?TABLE? Set output mode
MODE is one of:
ascii Columns/rows delimited by 0x1F and 0x1E
box Tables using unicode box-drawing characters
csv Comma-separated values
column Output in columns. (See .width)
duckbox Tables with extensive features
html HTML code
insert SQL insert statements for TABLE
json Results in a JSON array
jsonlines Results in a NDJSON
latex LaTeX tabular environment code
line One value per line
list Values delimited by "|"
markdown Markdown table format
quote Escape answers as for SQL
table ASCII-art table
tabs Tab-separated values
tcl TCL list elements
trash No outputDuckDB viser oss nå ulike måter å visualisere dataene på. La oss først prøve alternativet line:
.mode lineEt dataeksempel¶
Deretter kjører vi spørringen på nytt, men denne gangen med en liten justering. Ser du den?
SELECT * FROM mcn.airtraces WHERE timestamp >= '2025-11-01 00:00:00' and timestamp < '2025-11-02 00:00:00' LIMIT 1;Den lille endringen vi har gjort, er å legge inn LIMIT 1 helt til slutt i spørringen. Da returnerer DuckDB kun én eneste rad. Og siden vi har aktivert .mode line, får vi nå se alle kolonnene denne ene raden med data inneholder – pent skrevet ut i en vertikal liste:
timestamp = 2025-11-01 01:00:01.021+01
icao = ADA699
registration = N979RP
squawk = 1405
flight = RPA4430
type = E170
category = A3
year = 2005
description = EMBRAER ERJ-170-100
owner = REPUBLIC AIRWAYS INC
latitude = 42.234696
longitude = -71.213752
altitude_barometric = 10600
altitude_geometric = 9875
on_ground = false
ground_speed = 290.6
indicated_air_speed = NULL
true_airspeed = NULL
mach = NULL
track = 264.07
calculated_track = NULL
track_rate = NULL
magnetic_heading = NULL
true_heading = NULL
roll = NULL
barometric_rate = 64
geometric_rate = NULL
navigation_integrity_category = 8
navigation_integrity_category_barometric_altitude = 1
radius_of_containment = 186
navigation_accuracy_for_position = 10
navigation_accuracy_for_velocity = 2
source_integrity_level = 3
source_integrity_level_type = perhour
geometric_vertical_accuracy = 2
system_design_assurance = 2
nav_qnh = 991.2
outside_air_temperature = NULL
total_air_temperature = NULL
wind_direction = NULL
wind_speed = NULL
nav_altitude_mcp = 10016
nav_altitude_fms = NULL
nav_heading = NULL
nav_mode_autopilot = true
nav_mode_vnav = true
nav_mode_alt_hold = false
nav_mode_approach = false
nav_mode_lnav = false
nav_mode_tcas = true
emergency = NULL
alert = 0
surveillance_identifier_code = 0
message_type = adsb_icao
mlat_derived_lat = false
mlat_derived_lon = false
mlat_derived_nic = false
mlat_derived_rc = false
tisb_derived_lat = false
tisb_derived_lon = false
tisb_derived_nic = false
tisb_derived_rc = false
flydenity_nation = United States
flydenity_description = general
flydenity_iso2 = US
flydenity_iso3 = USA
h3_1 = 812a3ffffffffff
h3_2 = 822a37fffffffff
h3_3 = 832a33fffffffff
h3_4 = 842a339ffffffff
h3_5 = 852a339bfffffff
h3_15 = 8f2a339a27152d0
rssi = -11.1
rough_receiver_latitude = NULL
rough_receiver_longitude = NULL
receiver_id = 6d23022f-adf4-4782
recent_receiver_ids = [6d23022f-adf4-4782]
is_military = false
is_interesting = false
is_pia = false
is_ladd = false
flags = 0Før vi glemmer det, endrer vi visningen tilbake til standardvisningen duckbox – den første tabellen vi så:
.mode duckboxKolonner i datasettet¶
Når vi nå har sett litt på feltene vi har tilgjengelig i datasettet, la oss dykke dypere i hva de gjør. Her er en fullstendig oversikt:
| Kolonnenavn | Beskrivelse |
|---|---|
| timestamp | Tidspunkt for når ADS-B-meldingen ble mottatt (med millisekundpresisjon) |
| icao | 24-biters ICAO flyadresse (heksadesimal) |
| registration | Flyets registreringsnummer (halenummer) |
| squawk | Transponder squawk-kode (4-sifret oktal) |
| flight | Flightnummer eller kallesignal |
| type | Flytypekode |
| category | Flykategori (A0-A7, B0-B7, C0-C7, etc.) |
| year | Flyets produksjonsår |
| description | Flybeskrivelse/modell |
| owner | Flyeier/operatør |
| latitude | Flyets breddegrad i desimalgrader |
| longitude | Flyets lengdegrad i desimalgrader |
| altitude_barometric | Barometrisk høyde i fot over havet |
| altitude_geometric | Geometrisk høyde i fot over havet |
| on_ground | Sann hvis flyet er på bakken, usann hvis luftbårent |
| ground_speed | Bakkefart i knop |
| indicated_air_speed | Indikert lufthastighet i knop |
| true_airspeed | Sann lufthastighet i knop |
| mach | Mach-tall (hastighet i forhold til lydhastighet) |
| track | Bane over bakken i grader (0-359) |
| calculated_track | Beregnet banevinkel i grader |
| track_rate | Endringshastighet for bane i grader per sekund |
| magnetic_heading | Magnetisk kurs i grader (0-359) |
| true_heading | Sann kurs i grader (0-359) |
| roll | Flyets rullevinkel i grader (positiv = høyrekrengning) |
| barometric_rate | Barometrisk vertikalhastighet i fot per minutt |
| geometric_rate | Geometrisk vertikalhastighet i fot per minutt |
| navigation_integrity_category | Navigation Integrity Category (NIC) - indikator for posisjonsnøyaktighet |
| navigation_integrity_category_barometric_altitude | NIC for barometrisk høyde |
| radius_of_containment | Inneslutningsradius i meter - posisjonsusikkerhet |
| navigation_accuracy_for_position | Navigation Accuracy Category for Position (NACp) |
| navigation_accuracy_for_velocity | Navigation Accuracy Category for Velocity (NACv) |
| source_integrity_level | Source Integrity Level (SIL) - indikator for dataintegritet |
| source_integrity_level_type | SIL-type (perhour, unknown, perflight) |
| geometric_vertical_accuracy | Geometrisk vertikal nøyaktighet (GVA) i fot |
| system_design_assurance | System Design Assurance (SDA) nivå |
| nav_qnh | Navigasjons-QNH trykk i hektopascal |
| outside_air_temperature | Utelufttemperatur i grader Celsius |
| total_air_temperature | Total lufttemperatur i grader Celsius |
| wind_direction | Vindretning i grader (0-359) |
| wind_speed | Vindhastighet i knop |
| nav_altitude_mcp | Navigasjonshøyde fra Mode Control Panel i fot |
| nav_altitude_fms | Navigasjonshøyde fra Flight Management System i fot |
| nav_heading | Navigasjonskurs i grader (0-359) |
| nav_mode_autopilot | Sann hvis autopilot-modus er aktiv |
| nav_mode_vnav | Sann hvis vertikal navigasjonsmodus er aktiv |
| nav_mode_alt_hold | Sann hvis høydeholdmodus er aktiv |
| nav_mode_approach | Sann hvis innflygingsmodus er aktiv |
| nav_mode_lnav | Sann hvis lateral navigasjonsmodus er aktiv |
| nav_mode_tcas | Sann hvis Traffic Collision Avoidance System-modus er aktiv |
| emergency | Nødsituasjonsstatus (none, general, lifeguard, minfuel, nordo, unlawful, downed, etc.) |
| alert | Varselflagg (0=ingen varsel, 1=varsel) |
| surveillance_identifier_code | Surveillance Identifier Code (SPI) |
| message_type | Type ADS-B-melding mottatt |
| mlat_derived_lat | Sann hvis breddegrad ble utledet fra multilaterasjon |
| mlat_derived_lon | Sann hvis lengdegrad ble utledet fra multilaterasjon |
| mlat_derived_nic | Sann hvis NIC ble utledet fra multilaterasjon |
| mlat_derived_rc | Sann hvis inneslutningsradius ble utledet fra multilaterasjon |
| tisb_derived_lat | Sann hvis breddegrad ble utledet fra TIS-B |
| tisb_derived_lon | Sann hvis lengdegrad ble utledet fra TIS-B |
| tisb_derived_nic | Sann hvis NIC ble utledet fra TIS-B |
| tisb_derived_rc | Sann hvis inneslutningsradius ble utledet fra TIS-B |
| flydenity_nation | Flyets nasjonalitet fra Flydenity-berikelse |
| flydenity_description | Flybeskrivelse fra Flydenity-berikelse |
| flydenity_iso2 | 2-bokstavers landskode fra Flydenity-berikelse |
| flydenity_iso3 | 3-bokstavers landskode fra Flydenity-berikelse |
| h3_1 | H3 geospatial indeks ved oppløsning 1 (grov) |
| h3_2 | H3 geospatial indeks ved oppløsning 2 |
| h3_3 | H3 geospatial indeks ved oppløsning 3 |
| h3_4 | H3 geospatial indeks ved oppløsning 4 |
| h3_5 | H3 geospatial indeks ved oppløsning 5 |
| h3_15 | H3 geospatial indeks ved oppløsning 15 (fin) |
| rssi | Mottatt signalstyrkeindikator i dBFS |
| rough_receiver_latitude | Omtrentlig mottaker-breddegrad i desimalgrader |
| rough_receiver_longitude | Omtrentlig mottaker-lengdegrad i desimalgrader |
| receiver_id | Unik identifikator for mottakeren som behandlet denne meldingen |
| recent_receiver_ids | Array av nylige mottaker-IDer som behandlet dette flyet |
| is_military | Sann hvis flyet er flagget som militært |
| is_interesting | Sann hvis flyet er flagget som interessant |
| is_pia | Sann hvis flyet er flagget som PIA (Position Information Available) |
| is_ladd | Sann hvis flyet er flagget som LADD (Low Altitude Detection and Display) |
| flags | Bitfelt som inneholder diverse flyflagg |
Dette er et betydelig antall kolonner å forholde seg til – 82 i alt, for å være helt nøyaktig. Men selv om mange av disse kan være relevante under gitte omstendigheter, er det et mye mindre antall vi typisk forholder oss til. La oss konsentere oss om de viktigste i første omgang.
De viktigste kolonnene¶
Noen kolonner er viktigere enn andre. La oss ta en titt på dem her:
| Kolonnenavn | Beskrivelse |
|---|---|
| timestamp | Tidspunkt for når ADS-B-meldingen ble mottatt (med millisekundpresisjon) |
| icao | 24-biters ICAO flyadresse (heksadesimal) |
| registration | Flyets registreringsnummer (halenummer) |
| squawk | Transponder squawk-kode (4-sifret oktal) |
| flight | Flightnummer eller kallesignal |
| latitude | Flyets breddegrad i desimalgrader |
| longitude | Flyets lengdegrad i desimalgrader |
| altitude_barometric | Barometrisk høyde i fot over havet |
| on_ground | Sann hvis flyet er på bakken, usann hvis luftbårent |
| ground_speed | Bakkefart i knop |
| track | Bane over bakken i grader (0-359) |
Her har vi en mer oversiktlig liste med kolonner. Og hvis vi bytter ut stjernen (*) i spørringen vår med de feltene vi ønsker å hente, ser det slik ut:
SELECT timestamp, icao, registration, squawk, flight, latitude, longitude, altitude_barometric, on_ground, ground_speed, track
FROM mcn.airtraces
WHERE timestamp >= '2025-11-01 00:00:00' and timestamp < '2025-11-02 00:00:00';Vi får et resultat som minner om dette:
┌────────────────────────────┬─────────┬──────────────┬─────────┬─────────┬──────────────┬───────────────┬─────────────────────┬───────────┬──────────────┬──────────────┐
│ timestamp │ icao │ registration │ squawk │ flight │ latitude │ longitude │ altitude_barometric │ on_ground │ ground_speed │ track │
│ timestamp with time zone │ varchar │ varchar │ varchar │ varchar │ decimal(9,6) │ decimal(10,6) │ int32 │ boolean │ decimal(6,1) │ decimal(5,2) │
├────────────────────────────┼─────────┼──────────────┼─────────┼─────────┼──────────────┼───────────────┼─────────────────────┼───────────┼──────────────┼──────────────┤
│ 2025-11-01 01:00:01.021+01 │ ADA699 │ N979RP │ 1405 │ RPA4430 │ 42.234696 │ -71.213752 │ 10600 │ false │ 290.6 │ 264.07 │
│ 2025-11-01 01:00:01.171+01 │ 4BA9EF │ TC-JOO │ 2247 │ THY6549 │ 51.968544 │ 4.425999 │ 35000 │ false │ 496.3 │ 118.53 │
│ 2025-11-01 01:00:01.188+01 │ AB6FBF │ N836MH │ 2013 │ DAL174 │ 42.226976 │ -71.137944 │ 33000 │ false │ 549.8 │ 52.46 │
│ 2025-11-01 01:00:01.348+01 │ ABFDD1 │ N872AN │ 0551 │ AAL70 │ 42.308576 │ -71.933608 │ 38975 │ false │ 570.2 │ 51.62 │
│ 2025-11-01 01:00:01.348+01 │ 4CAD94 │ EI-SCD │ 0201 │ SAS2679 │ 58.758820 │ 16.893340 │ 18125 │ false │ 386.4 │ 34.35 │
│ 2025-11-01 01:00:01.484+01 │ 48C1A8 │ SP-RNI │ 3567 │ RYR57YL │ 51.693740 │ 4.843783 │ 38000 │ false │ 386.7 │ 285.29 │
│ 2025-11-01 01:00:01.657+01 │ 4BA9EF │ TC-JOO │ 2247 │ THY6549 │ 51.967988 │ 4.427647 │ 35000 │ false │ 496.3 │ 118.53 │
│ 2025-11-01 01:00:01.771+01 │ 478A54 │ NULL │ NULL │ BR044 │ 60.299824 │ 5.226697 │ NULL │ true │ 0.0 │ NULL │
│ 2025-11-01 01:00:01.876+01 │ 4080E2 │ G-JZDG │ 3257 │ EXS8KN │ 52.167508 │ 6.337228 │ 38000 │ false │ 396.1 │ 304.09 │
│ 2025-11-01 01:00:02.088+01 │ AB6FBF │ N836MH │ 2013 │ DAL174 │ 42.228288 │ -71.135600 │ 33000 │ false │ 549.8 │ 52.46 │
│ 2025-11-01 01:00:02.088+01 │ ADA699 │ N979RP │ 1405 │ RPA4430 │ 42.234604 │ -71.215184 │ 10600 │ false │ 290.6 │ 264.07 │
│ 2025-11-01 01:00:02.161+01 │ 48C1A8 │ SP-RNI │ 3567 │ RYR57YL │ 51.694016 │ 4.842224 │ 38000 │ false │ 386.7 │ 285.29 │
│ 2025-11-01 01:00:02.292+01 │ 478A54 │ NULL │ NULL │ BR044 │ 60.299824 │ 5.226698 │ NULL │ true │ 0.0 │ NULL │
│ 2025-11-01 01:00:02.427+01 │ 4CAD94 │ EI-SCD │ 0201 │ SAS2679 │ 58.760424 │ 16.895466 │ 18100 │ false │ 386.4 │ 34.35 │
│ 2025-11-01 01:00:02.584+01 │ 4BA9EF │ TC-JOO │ 2247 │ THY6549 │ 51.966844 │ 4.431000 │ 35000 │ false │ 496.3 │ 118.53 │
│ 2025-11-01 01:00:02.93+01 │ 4080E2 │ G-JZDG │ 3257 │ EXS8KN │ 52.168440 │ 6.334991 │ 38000 │ false │ 396.1 │ 304.09 │
│ 2025-11-01 01:00:03.113+01 │ ADA699 │ N979RP │ 1405 │ RPA4430 │ 42.234420 │ -71.217376 │ 10600 │ false │ 290.6 │ 264.07 │
│ 2025-11-01 01:00:03.133+01 │ 48C1A8 │ SP-RNI │ 3567 │ RYR57YL │ 51.694520 │ 4.839032 │ 38000 │ false │ 386.7 │ 285.29 │
│ 2025-11-01 01:00:03.226+01 │ 478A6F │ NULL │ NULL │ BR071 │ 60.298976 │ 5.226556 │ NULL │ true │ 0.0 │ 2.81 │
│ 2025-11-01 01:00:03.26+01 │ 478A80 │ NULL │ NULL │ BR088 │ 60.299000 │ 5.226461 │ NULL │ true │ 0.0 │ NULL │
│ · │ · │ · │ · │ · │ · │ · │ · │ · │ · │ · │
│ · │ · │ · │ · │ · │ · │ · │ · │ · │ · │ · │
│ · │ · │ · │ · │ · │ · │ · │ · │ · │ · │ · │
│ 2025-11-01 20:06:15.369+01 │ 4D2529 │ 9H-WNC │ 4321 │ WZZ68WL │ 59.955736 │ 7.539557 │ 26250 │ false │ 435.8 │ 301.39 │
│ 2025-11-01 20:06:15.381+01 │ 45D961 │ OY-VKA │ 2357 │ VKG1523 │ 60.271088 │ 5.361423 │ 5625 │ false │ 282.3 │ 30.20 │
│ 2025-11-01 20:06:15.402+01 │ 406532 │ G-EUYM │ 6216 │ BAW773 │ 59.134416 │ 9.834503 │ 30450 │ false │ 326.3 │ 215.39 │
│ 2025-11-01 20:06:15.471+01 │ 4ACA87 │ SE-RTG │ 1161 │ NSZ5341 │ 62.409120 │ 6.190898 │ 13350 │ false │ 293.0 │ 195.03 │
│ 2025-11-01 20:06:15.471+01 │ 4ACAB2 │ SE-RUR │ 2354 │ SAS7470 │ 62.811400 │ 7.171122 │ 7625 │ false │ 299.7 │ 38.36 │
│ 2025-11-01 20:06:15.533+01 │ 478A3E │ NULL │ NULL │ BR022 │ 60.293620 │ 5.225988 │ NULL │ true │ 0.2 │ NULL │
│ 2025-11-01 20:06:15.533+01 │ 478A45 │ NULL │ NULL │ BR029 │ 60.294904 │ 5.230960 │ NULL │ true │ 0.2 │ NULL │
│ 2025-11-01 20:06:15.553+01 │ AC72F0 │ N901AN │ 3451 │ AAL1411 │ 42.782808 │ -71.602560 │ 36000 │ false │ 355.0 │ 224.09 │
│ 2025-11-01 20:06:15.56+01 │ 4ACA6D │ SE-RSM │ 6404 │ SAS74M │ 59.168264 │ 6.273667 │ 20100 │ false │ 384.0 │ 59.83 │
│ 2025-11-01 20:06:15.56+01 │ 4D2529 │ 9H-WNC │ 4321 │ WZZ68WL │ 59.956376 │ 7.537663 │ 26200 │ false │ 435.8 │ 301.39 │
│ 2025-11-01 20:06:15.591+01 │ 48C2A2 │ SP-RZC │ 2276 │ RYR80AM │ 52.223512 │ 4.854889 │ 39000 │ false │ 498.0 │ 93.57 │
│ 2025-11-01 20:06:15.591+01 │ 4CA81D │ EI-DTM │ 1000 │ EJU49DK │ NULL │ NULL │ 2525 │ false │ 198.0 │ 246.09 │
│ 2025-11-01 20:06:15.591+01 │ 702086 │ S2-AJS │ 5221 │ BBC202 │ 51.702988 │ 5.390279 │ 37000 │ false │ 512.8 │ 110.91 │
│ 2025-11-01 20:06:15.609+01 │ 4AC9F3 │ SE-ROS │ 0202 │ SAS131 │ 58.479904 │ 16.475239 │ 38000 │ false │ 354.5 │ 213.38 │
│ 2025-11-01 20:06:15.727+01 │ 406532 │ G-EUYM │ 6216 │ BAW773 │ 59.133912 │ 9.833862 │ 30450 │ false │ 326.3 │ 215.39 │
│ 2025-11-01 20:06:15.73+01 │ 45AC52 │ OY-KBR │ 0412 │ SAS33G │ 64.152756 │ 12.470032 │ 38000 │ false │ 385.1 │ 200.36 │
│ 2025-11-01 20:06:15.74+01 │ 407CB1 │ G-RUKE │ 6217 │ RUK3248 │ 57.924360 │ 5.222936 │ 39000 │ false │ 485.7 │ 59.16 │
│ 2025-11-01 20:06:15.855+01 │ 4D24D6 │ 9H-WDX │ 4322 │ WZZ99TX │ 58.488876 │ 8.600346 │ 34325 │ false │ 445.8 │ 298.98 │
│ 2025-11-01 20:06:15.856+01 │ 4ACA87 │ SE-RTG │ 1161 │ NSZ5341 │ 62.408568 │ 6.190592 │ 13375 │ false │ 293.0 │ 195.03 │
│ 2025-11-01 20:06:15.888+01 │ 4D2529 │ 9H-WNC │ 4321 │ WZZ68WL │ 59.956880 │ 7.535958 │ 26175 │ false │ 435.8 │ 301.39 │
├────────────────────────────┴─────────┴──────────────┴─────────┴─────────┴──────────────┴───────────────┴─────────────────────┴───────────┴──────────────┴──────────────┤
│ 3839743 rows (3.84 million rows, 40 shown) 11 columns │
└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘Antallet rader er identisk med tidligere, men vi ser at det nå er 11 kolonner i stedet for 82. Og hvis du har en litt stor skjerm, ser du kanskje alle på en gang.
En nærmere titt på kolonnene¶
La oss se på dataene vi nå har hentet frem. Vi legger merke til timestamp helt til venstre, som indikerer når dataene ble fanget opp av serveren med millisekundpresisjon. Vi har foreløpig ikke spesifisert sorteringen på dataene, men det kommer vi til. Kolonnen icao er selvforklarende. Det viser oss flyets ICAO-nummer, også kjent som hex-kode.
Når vi kommer til registration, er det en ting det er verdt å merke seg. Vi ser at noen av radene er markert med NULL. Det er DuckDBs måte å fortelle oss at kolonnen er tom i den aktuelle raden. Det er rett og slett ikke registrert noen verdi her, men det er helt normalt. Luftfartøyene sender nemlig ikke ut alle parameterne i samtlige sendinger.
Når vi beveger oss videre, ser vi kolonner for både squawk-koder og flight. Vi legger også merke til de essensielle feltene latitude (breddegrad), longitude (lengdegrad), samt altitude_barometric (barometrisk høyde i fot). Disse tre feltene sammen gir oss det vi trenger for å kunne fastslå fartøyets posisjon i tre dimensjoner.
Kolonnen on_ground er en såkalt boolean, eller en bolsk verdi på norsk. Det betyr at den enten er sann (true) eller usann (false). Og du har kanskje gjettet det allerede? Hvis on_ground er true, står fartøyet på bakken. Hvis verdien er false, er det i luften.
Til slutt legger vi merke til ground_speed, som indikerer fartøyets hastighet over bakken i knop, og track, som indikerer kursen.
Finn et spesifikt fartøy¶
Datapunktene vi har sett på til nå ble tilfeldig valgt fra en gitt dato. Vanligvis vil vi lete etter spesifikke fartøy, for eksempel et politihelikopter, eller se på trafikken i et bestemt geografisk område. I Norge har vi for tiden tre politihelikoptere, med registreringsnumrene LN-ORA, LN-ORB, og LN-ORC. La oss sjekke om vi finner et av dem i dataene fra den aktuelle dagen vår, 1. november 2025. Vi starter med en litt endret spørring:
SELECT timestamp, icao, registration
FROM mcn.airtraces
WHERE timestamp >= '2025-11-01 00:00:00'
AND timestamp < '2025-11-02 00:00:00'
AND registration = 'LN-ORC'
LIMIT 1;Dette filtrerer datasettet på registration (halenummer), slik at vi kun får data for LN-ORC, og begrenser treffet til ett datapunkt.
┌───────────────────────────┬─────────┬──────────────┐
│ timestamp │ icao │ registration │
│ timestamp with time zone │ varchar │ varchar │
├───────────────────────────┼─────────┼──────────────┤
│ 2025-11-01 15:09:47.93+01 │ 478058 │ LN-ORC │
└───────────────────────────┴─────────┴──────────────┘Her var det treff på første forsøk! Det ser ut til at det tredje politihelikopteret har vært ute og fløyet. Her ba vi kun om timestamp, icao og registration i spørringen vår (SELECT timestamp, icao, registration ), og det er også alt vi får i retur. Men som den observante leser kanskje har sett, har vi nå sikret oss ICAO-nummeret til dette helikopteret. Nå kan vi gjøre en ny spørring på den aktuelle datoen, med ICAO-nummeret som filter:
SELECT timestamp, icao, registration, squawk, flight, latitude, longitude, altitude_barometric, on_ground, ground_speed, track
FROM mcn.airtraces
WHERE timestamp >= '2025-11-01 00:00:00'
AND timestamp < '2025-11-02 00:00:00'
AND icao = '478058'
ORDER BY timestamp ASC;La du merke til noe nytt her? Vi har lagt til en ny kommando på slutten, nemlig ORDER BY timestamp ASC. Denne kommandoen signaliserer til DuckDB at vi ønsker å sortere resultatet stigende etter timestamp. La oss se på resultatet:
┌────────────────────────────┬─────────┬──────────────┬─────────┬─────────┬──────────────┬───────────────┬─────────────────────┬───────────┬──────────────┬──────────────┐
│ timestamp │ icao │ registration │ squawk │ flight │ latitude │ longitude │ altitude_barometric │ on_ground │ ground_speed │ track │
│ timestamp with time zone │ varchar │ varchar │ varchar │ varchar │ decimal(9,6) │ decimal(10,6) │ int32 │ boolean │ decimal(6,1) │ decimal(5,2) │
├────────────────────────────┼─────────┼──────────────┼─────────┼─────────┼──────────────┼───────────────┼─────────────────────┼───────────┼──────────────┼──────────────┤
│ 2025-11-01 15:09:47.93+01 │ 478058 │ LN-ORC │ NULL │ NULL │ 59.849184 │ 10.956116 │ 2800 │ false │ 134.4 │ 313.19 │
│ 2025-11-01 15:09:51.748+01 │ 478058 │ LN-ORC │ NULL │ NULL │ 59.850768 │ 10.952706 │ 2750 │ false │ 133.0 │ 313.17 │
│ 2025-11-01 15:09:54.859+01 │ 478058 │ LN-ORC │ 5151 │ NULL │ 59.852096 │ 10.949982 │ 2725 │ false │ 133.0 │ 313.17 │
│ 2025-11-01 15:09:55.788+01 │ 478058 │ LN-ORC │ 5151 │ NULL │ 59.852508 │ 10.949066 │ 2700 │ false │ 133.7 │ 312.88 │
│ 2025-11-01 15:09:56.771+01 │ 478058 │ LN-ORC │ 5151 │ NULL │ 59.852960 │ 10.948065 │ 2700 │ false │ 134.4 │ 313.19 │
│ 2025-11-01 15:10:01.855+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.855164 │ 10.943390 │ 2625 │ false │ 136.6 │ 312.33 │
│ 2025-11-01 15:10:02.888+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.855576 │ 10.942474 │ 2600 │ false │ 136.6 │ 312.33 │
│ 2025-11-01 15:10:05.946+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.856856 │ 10.939636 │ 2575 │ false │ 137.2 │ 313.23 │
│ 2025-11-01 15:10:06.492+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.857100 │ 10.939163 │ 2575 │ false │ 137.2 │ 313.23 │
│ 2025-11-01 15:10:07.094+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.857336 │ 10.938689 │ 2575 │ false │ 137.9 │ 313.53 │
│ 2025-11-01 15:10:08.023+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.857800 │ 10.937742 │ 2550 │ false │ 137.9 │ 313.53 │
│ 2025-11-01 15:10:08.623+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.858000 │ 10.937256 │ 2550 │ false │ 137.9 │ 313.53 │
│ 2025-11-01 15:10:10.152+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.858636 │ 10.935848 │ 2550 │ false │ 138.0 │ 312.94 │
│ 2025-11-01 15:10:21.951+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.863724 │ 10.925079 │ 2500 │ false │ 135.1 │ 312.90 │
│ 2025-11-01 15:10:24.025+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.864644 │ 10.923157 │ 2500 │ false │ 134.4 │ 313.79 │
│ 2025-11-01 15:10:41.393+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.864644 │ 10.923157 │ 2500 │ false │ 134.4 │ 313.79 │
│ 2025-11-01 15:10:43.469+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.864644 │ 10.923157 │ 2500 │ false │ 134.4 │ 313.79 │
│ 2025-11-01 15:10:45.216+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.875068 │ 10.907719 │ 2500 │ false │ 129.0 │ 327.67 │
│ 2025-11-01 15:10:45.598+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.875256 │ 10.907435 │ 2500 │ false │ 129.0 │ 327.67 │
│ 2025-11-01 15:10:46.307+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.875580 │ 10.907056 │ 2500 │ false │ 129.0 │ 327.67 │
│ · │ · │ · │ · │ · │ · │ · │ · │ · │ · │ · │
│ · │ · │ · │ · │ · │ · │ · │ · │ · │ · │ · │
│ · │ · │ · │ · │ · │ · │ · │ · │ · │ · │ · │
│ 2025-11-01 15:11:32.293+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.897184 │ 10.873260 │ 2500 │ false │ 134.1 │ 287.35 │
│ 2025-11-01 15:11:32.782+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.897228 │ 10.872676 │ 2500 │ false │ 133.2 │ 284.35 │
│ 2025-11-01 15:11:33.439+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.897320 │ 10.871919 │ 2500 │ false │ 133.2 │ 284.35 │
│ 2025-11-01 15:11:33.819+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.897368 │ 10.871445 │ 2500 │ false │ 131.6 │ 281.40 │
│ 2025-11-01 15:11:35.785+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.897508 │ 10.869232 │ 2500 │ false │ 127.5 │ 274.95 │
│ 2025-11-01 15:11:36.332+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.897552 │ 10.868500 │ 2500 │ false │ 127.5 │ 274.95 │
│ 2025-11-01 15:11:36.826+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.897552 │ 10.868042 │ 2500 │ false │ 126.1 │ 271.82 │
│ 2025-11-01 15:11:37.153+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.897552 │ 10.867467 │ 2500 │ false │ 126.1 │ 271.82 │
│ 2025-11-01 15:11:37.752+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.897508 │ 10.866943 │ 2500 │ false │ 125.0 │ 269.08 │
│ 2025-11-01 15:11:38.137+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.897508 │ 10.866486 │ 2500 │ false │ 125.0 │ 269.08 │
│ 2025-11-01 15:11:38.68+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.897508 │ 10.865753 │ 2500 │ false │ 124.3 │ 266.31 │
│ 2025-11-01 15:11:41.795+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.897232 │ 10.862457 │ 2500 │ false │ 121.4 │ 258.60 │
│ 2025-11-01 15:12:05.554+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.897232 │ 10.862457 │ 2500 │ false │ 109.7 │ 257.36 │
│ 2025-11-01 15:12:07.083+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.894108 │ 10.835645 │ 2500 │ false │ 108.7 │ 257.24 │
│ 2025-11-01 15:12:17.842+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.892928 │ 10.825653 │ 2475 │ false │ 104.1 │ 256.10 │
│ 2025-11-01 15:12:24.888+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.892016 │ 10.818878 │ 2475 │ false │ 104.4 │ 253.30 │
│ 2025-11-01 15:12:25.926+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.891876 │ 10.818124 │ 2475 │ false │ 103.1 │ 250.16 │
│ 2025-11-01 15:12:36.798+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.889408 │ 10.809979 │ 2475 │ false │ 93.4 │ 232.39 │
│ 2025-11-01 15:12:38.495+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.888848 │ 10.808937 │ 2500 │ false │ 90.5 │ 225.90 │
│ 2025-11-01 15:12:45.425+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.886616 │ 10.805717 │ 2500 │ false │ 88.4 │ 217.65 │
├────────────────────────────┴─────────┴──────────────┴─────────┴─────────┴──────────────┴───────────────┴─────────────────────┴───────────┴──────────────┴──────────────┤
│ 74 rows (40 shown) 11 columns │
└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘74 rader med posisjoner! Vi legger merke til at de første radene har nullverdier i kolonnene squawk og flight. Dette er ganske typisk når et fartøy kommer inn i dekningsområdet.
Vi ser at det fortsatt er 40 rader som vises, men det totale antallet 74 er i dette tilfellet så lavt at det burde gå greit å vise det på skjermen. Er det mulig? Ja, det er det. Her kommer punktumkommandoen .maxrows til nytte. La oss teste:
.maxrows 100Så kjører vi den samme spørringen på nytt, og plutselig ser vi alle datapunktene på skjermen!
SELECT timestamp, icao, registration, squawk, flight, latitude, longitude, altitude_barometric, on_ground, ground_speed, track
FROM mcn.airtraces
WHERE timestamp >= '2025-11-01 00:00:00'
AND timestamp < '2025-11-02 00:00:00'
AND icao = '478058'
ORDER BY timestamp ASC;┌────────────────────────────┬─────────┬──────────────┬─────────┬─────────┬──────────────┬───────────────┬─────────────────────┬───────────┬──────────────┬──────────────┐
│ timestamp │ icao │ registration │ squawk │ flight │ latitude │ longitude │ altitude_barometric │ on_ground │ ground_speed │ track │
│ timestamp with time zone │ varchar │ varchar │ varchar │ varchar │ decimal(9,6) │ decimal(10,6) │ int32 │ boolean │ decimal(6,1) │ decimal(5,2) │
├────────────────────────────┼─────────┼──────────────┼─────────┼─────────┼──────────────┼───────────────┼─────────────────────┼───────────┼──────────────┼──────────────┤
│ 2025-11-01 15:09:47.93+01 │ 478058 │ LN-ORC │ NULL │ NULL │ 59.849184 │ 10.956116 │ 2800 │ false │ 134.4 │ 313.19 │
│ 2025-11-01 15:09:51.748+01 │ 478058 │ LN-ORC │ NULL │ NULL │ 59.850768 │ 10.952706 │ 2750 │ false │ 133.0 │ 313.17 │
│ 2025-11-01 15:09:54.859+01 │ 478058 │ LN-ORC │ 5151 │ NULL │ 59.852096 │ 10.949982 │ 2725 │ false │ 133.0 │ 313.17 │
│ 2025-11-01 15:09:55.788+01 │ 478058 │ LN-ORC │ 5151 │ NULL │ 59.852508 │ 10.949066 │ 2700 │ false │ 133.7 │ 312.88 │
│ 2025-11-01 15:09:56.771+01 │ 478058 │ LN-ORC │ 5151 │ NULL │ 59.852960 │ 10.948065 │ 2700 │ false │ 134.4 │ 313.19 │
│ 2025-11-01 15:10:01.855+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.855164 │ 10.943390 │ 2625 │ false │ 136.6 │ 312.33 │
│ 2025-11-01 15:10:02.888+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.855576 │ 10.942474 │ 2600 │ false │ 136.6 │ 312.33 │
│ 2025-11-01 15:10:05.946+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.856856 │ 10.939636 │ 2575 │ false │ 137.2 │ 313.23 │
│ 2025-11-01 15:10:06.492+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.857100 │ 10.939163 │ 2575 │ false │ 137.2 │ 313.23 │
│ 2025-11-01 15:10:07.094+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.857336 │ 10.938689 │ 2575 │ false │ 137.9 │ 313.53 │
│ 2025-11-01 15:10:08.023+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.857800 │ 10.937742 │ 2550 │ false │ 137.9 │ 313.53 │
│ 2025-11-01 15:10:08.623+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.858000 │ 10.937256 │ 2550 │ false │ 137.9 │ 313.53 │
│ 2025-11-01 15:10:10.152+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.858636 │ 10.935848 │ 2550 │ false │ 138.0 │ 312.94 │
│ 2025-11-01 15:10:21.951+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.863724 │ 10.925079 │ 2500 │ false │ 135.1 │ 312.90 │
│ 2025-11-01 15:10:24.025+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.864644 │ 10.923157 │ 2500 │ false │ 134.4 │ 313.79 │
│ 2025-11-01 15:10:41.393+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.864644 │ 10.923157 │ 2500 │ false │ 134.4 │ 313.79 │
│ 2025-11-01 15:10:43.469+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.864644 │ 10.923157 │ 2500 │ false │ 134.4 │ 313.79 │
│ 2025-11-01 15:10:45.216+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.875068 │ 10.907719 │ 2500 │ false │ 129.0 │ 327.67 │
│ 2025-11-01 15:10:45.598+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.875256 │ 10.907435 │ 2500 │ false │ 129.0 │ 327.67 │
│ 2025-11-01 15:10:46.307+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.875580 │ 10.907056 │ 2500 │ false │ 129.0 │ 327.67 │
│ 2025-11-01 15:10:46.689+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.875808 │ 10.906769 │ 2500 │ false │ 129.0 │ 327.67 │
│ 2025-11-01 15:10:48.169+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.876496 │ 10.905853 │ 2500 │ false │ 128.5 │ 328.04 │
│ 2025-11-01 15:10:48.601+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.876792 │ 10.905541 │ 2500 │ false │ 128.5 │ 328.04 │
│ 2025-11-01 15:10:52.155+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.878468 │ 10.903457 │ 2500 │ false │ 128.5 │ 328.04 │
│ 2025-11-01 15:10:59.14+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.882080 │ 10.899078 │ 2500 │ false │ 129.3 │ 328.28 │
│ 2025-11-01 15:10:59.467+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.882264 │ 10.898804 │ 2500 │ false │ 129.3 │ 328.28 │
│ 2025-11-01 15:11:09.519+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.887312 │ 10.892565 │ 2500 │ false │ 130.7 │ 328.13 │
│ 2025-11-01 15:11:11.648+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.888432 │ 10.891145 │ 2500 │ false │ 131.2 │ 327.76 │
│ 2025-11-01 15:11:12.685+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.888988 │ 10.890482 │ 2475 │ false │ 131.2 │ 327.76 │
│ 2025-11-01 15:11:13.12+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.889220 │ 10.890198 │ 2500 │ false │ 131.6 │ 328.36 │
│ 2025-11-01 15:11:14.869+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.890104 │ 10.889061 │ 2500 │ false │ 131.6 │ 328.36 │
│ 2025-11-01 15:11:15.633+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.890528 │ 10.888588 │ 2475 │ false │ 132.4 │ 328.59 │
│ 2025-11-01 15:11:16.233+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.890848 │ 10.888209 │ 2500 │ false │ 132.4 │ 328.59 │
│ 2025-11-01 15:11:19.292+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.892388 │ 10.886315 │ 2500 │ false │ 133.1 │ 327.26 │
│ 2025-11-01 15:11:19.893+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.892712 │ 10.885841 │ 2500 │ false │ 134.0 │ 325.95 │
│ 2025-11-01 15:11:20.876+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.893176 │ 10.885083 │ 2500 │ false │ 135.0 │ 323.13 │
│ 2025-11-01 15:11:21.375+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.893432 │ 10.884705 │ 2475 │ false │ 135.0 │ 323.13 │
│ 2025-11-01 15:11:21.86+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.893688 │ 10.884326 │ 2475 │ false │ 135.6 │ 320.09 │
│ 2025-11-01 15:11:22.297+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.893876 │ 10.883947 │ 2475 │ false │ 135.6 │ 320.09 │
│ 2025-11-01 15:11:22.842+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.894156 │ 10.883473 │ 2475 │ false │ 137.2 │ 316.77 │
│ 2025-11-01 15:11:23.334+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.894340 │ 10.883095 │ 2475 │ false │ 137.2 │ 316.77 │
│ 2025-11-01 15:11:24.81+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.894944 │ 10.881683 │ 2475 │ false │ 137.2 │ 316.77 │
│ 2025-11-01 15:11:25.3+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.895216 │ 10.881134 │ 2475 │ false │ 138.4 │ 310.02 │
│ 2025-11-01 15:11:25.791+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.895356 │ 10.880676 │ 2475 │ false │ 138.4 │ 310.02 │
│ 2025-11-01 15:11:26.283+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.895584 │ 10.880035 │ 2500 │ false │ 138.4 │ 307.37 │
│ 2025-11-01 15:11:26.829+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.895740 │ 10.879496 │ 2500 │ false │ 138.7 │ 304.72 │
│ 2025-11-01 15:11:27.375+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.895924 │ 10.879117 │ 2475 │ false │ 138.7 │ 304.72 │
│ 2025-11-01 15:11:27.704+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.896040 │ 10.878571 │ 2500 │ false │ 138.2 │ 301.39 │
│ 2025-11-01 15:11:28.413+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.896272 │ 10.877930 │ 2500 │ false │ 138.2 │ 301.39 │
│ 2025-11-01 15:11:28.74+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.896360 │ 10.877472 │ 2500 │ false │ 138.2 │ 298.05 │
│ 2025-11-01 15:11:29.34+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.896528 │ 10.876844 │ 2500 │ false │ 138.2 │ 298.05 │
│ 2025-11-01 15:11:29.832+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.896636 │ 10.876282 │ 2500 │ false │ 137.0 │ 294.13 │
│ 2025-11-01 15:11:30.325+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.896764 │ 10.875707 │ 2500 │ false │ 137.0 │ 294.13 │
│ 2025-11-01 15:11:31.417+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.897004 │ 10.874451 │ 2500 │ false │ 135.4 │ 290.31 │
│ 2025-11-01 15:11:32.293+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.897184 │ 10.873260 │ 2500 │ false │ 134.1 │ 287.35 │
│ 2025-11-01 15:11:32.782+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.897228 │ 10.872676 │ 2500 │ false │ 133.2 │ 284.35 │
│ 2025-11-01 15:11:33.439+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.897320 │ 10.871919 │ 2500 │ false │ 133.2 │ 284.35 │
│ 2025-11-01 15:11:33.819+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.897368 │ 10.871445 │ 2500 │ false │ 131.6 │ 281.40 │
│ 2025-11-01 15:11:35.785+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.897508 │ 10.869232 │ 2500 │ false │ 127.5 │ 274.95 │
│ 2025-11-01 15:11:36.332+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.897552 │ 10.868500 │ 2500 │ false │ 127.5 │ 274.95 │
│ 2025-11-01 15:11:36.826+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.897552 │ 10.868042 │ 2500 │ false │ 126.1 │ 271.82 │
│ 2025-11-01 15:11:37.153+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.897552 │ 10.867467 │ 2500 │ false │ 126.1 │ 271.82 │
│ 2025-11-01 15:11:37.752+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.897508 │ 10.866943 │ 2500 │ false │ 125.0 │ 269.08 │
│ 2025-11-01 15:11:38.137+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.897508 │ 10.866486 │ 2500 │ false │ 125.0 │ 269.08 │
│ 2025-11-01 15:11:38.68+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.897508 │ 10.865753 │ 2500 │ false │ 124.3 │ 266.31 │
│ 2025-11-01 15:11:41.795+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.897232 │ 10.862457 │ 2500 │ false │ 121.4 │ 258.60 │
│ 2025-11-01 15:12:05.554+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.897232 │ 10.862457 │ 2500 │ false │ 109.7 │ 257.36 │
│ 2025-11-01 15:12:07.083+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.894108 │ 10.835645 │ 2500 │ false │ 108.7 │ 257.24 │
│ 2025-11-01 15:12:17.842+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.892928 │ 10.825653 │ 2475 │ false │ 104.1 │ 256.10 │
│ 2025-11-01 15:12:24.888+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.892016 │ 10.818878 │ 2475 │ false │ 104.4 │ 253.30 │
│ 2025-11-01 15:12:25.926+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.891876 │ 10.818124 │ 2475 │ false │ 103.1 │ 250.16 │
│ 2025-11-01 15:12:36.798+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.889408 │ 10.809979 │ 2475 │ false │ 93.4 │ 232.39 │
│ 2025-11-01 15:12:38.495+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.888848 │ 10.808937 │ 2500 │ false │ 90.5 │ 225.90 │
│ 2025-11-01 15:12:45.425+01 │ 478058 │ LN-ORC │ 5151 │ HEP03 │ 59.886616 │ 10.805717 │ 2500 │ false │ 88.4 │ 217.65 │
├────────────────────────────┴─────────┴──────────────┴─────────┴─────────┴──────────────┴───────────────┴─────────────────────┴───────────┴──────────────┴──────────────┤
│ 74 rows 11 columns │
└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘Spennende! Men hvordan kan vi verifisere at dette faktisk er politihelikopteret? Nå vet vi jo allerede hva halenummeret er, så i akkurat dette tilfellet er saken relativt grei. Men hva om vi ikke var helt sikker på hva vi hadde å gjøre med her? Da kunne vi ha lagt til et par kolonner til med praktisk informasjon. Se denne spørringen:
SELECT timestamp, icao, registration, type, description, flydenity_nation, flydenity_description
FROM mcn.airtraces
WHERE timestamp >= '2025-11-01 00:00:00'
AND timestamp < '2025-11-02 00:00:00'
AND icao = '478058'
LIMIT 1;┌───────────────────────────┬─────────┬──────────────┬─────────┬───────────────┬──────────────────┬───────────────────────┐
│ timestamp │ icao │ registration │ type │ description │ flydenity_nation │ flydenity_description │
│ timestamp with time zone │ varchar │ varchar │ varchar │ varchar │ varchar │ varchar │
├───────────────────────────┼─────────┼──────────────┼─────────┼───────────────┼──────────────────┼───────────────────────┤
│ 2025-11-01 15:09:47.93+01 │ 478058 │ LN-ORC │ A169 │ AGUSTA AW-169 │ Norway │ helicopters │
└───────────────────────────┴─────────┴──────────────┴─────────┴───────────────┴──────────────────┴───────────────────────┘Perfekt! Nå vet vi at dette er et norskregistrert helikopter av typen Agusta AW-169.
Analyser fartøy per registreringsland¶
La oss bygge videre på spørringene vi har sett på tidligere. Vi har allerede brukt denne strukturen:
SELECT timestamp, icao, registration, type, description, flydenity_nation, flydenity_description
FROM mcn.airtraces
WHERE timestamp >= '2025-11-01 00:00:00'
AND timestamp < '2025-11-02 00:00:00'
AND icao = '478058'
LIMIT 1;I datastrukturen legger vi merke til flydenity_nation, som er et felt som angir registreringslandet til de sporede fartøyene. La oss se hvilke land som er representert i datasettet på den aktuelle dagen vår, og hvor mange fly som er registrert per land:
SELECT flydenity_nation,
COUNT(DISTINCT icao)
FROM mcn.airtraces
WHERE timestamp >= '2025-11-01 00:00:00'
AND timestamp < '2025-11-02 00:00:00'
GROUP BY 1
ORDER BY 2 DESC;Vi får dette resultatet:
┌──────────────────────┬──────────────────────┐
│ flydenity_nation │ count(DISTINCT icao) │
│ varchar │ int64 │
├──────────────────────┼──────────────────────┤
│ NULL │ 929 │
│ United States │ 809 │
│ United Kingdom │ 342 │
│ Germany │ 218 │
│ Netherlands │ 214 │
│ Ireland │ 185 │
│ Sweden │ 136 │
│ Turkey │ 123 │
│ Malta │ 121 │
│ Norway │ 111 │
│ China │ 88 │
│ United Arab Emirates │ 68 │
│ Poland │ 62 │
│ France │ 60 │
│ Qatar │ 59 │
│ Canada │ 49 │
│ Austria │ 47 │
│ Finland │ 47 │
│ Denmark │ 38 │
│ Russia │ 37 │
│ · │ · │
│ · │ · │
│ · │ · │
│ Brazil │ 2 │
│ Mexico │ 2 │
│ Serbia │ 2 │
│ Bulgaria │ 2 │
│ Malaysia │ 2 │
│ San Marino │ 2 │
│ Isle of Man │ 2 │
│ Bangladesh │ 2 │
│ Kenya │ 2 │
│ Sri Lanka │ 2 │
│ Pakistan │ 2 │
│ Georgia │ 1 │
│ Algeria │ 1 │
│ Mauritius │ 1 │
│ Indonesia │ 1 │
│ Venezuela │ 1 │
│ Turkmenistan │ 1 │
│ Moldova │ 1 │
│ Israel │ 1 │
│ Dominican Republic │ 1 │
├──────────────────────┴──────────────────────┤
│ 72 rows (40 shown) 2 columns │
└─────────────────────────────────────────────┘Hvordan fungerer denne spørringen?¶
I spørringen over har vi introdusert et par nye konsepter. La oss bryte det ned:
SELECT flydenity_nation,– Vi velger ut kolonnenflydenity_nationpå samme måte som vi har valgt ut kolonner tidligereCOUNT(DISTINCT icao)– Men så gjør vi noe nytt: Vi både henter ut og teller unike ICAO-nummer i en og samme operasjonWHERE timestamp >= '2025-11-01 00:00:00' AND timestamp < '2025-11-02 00:00:00'– Deretter filtrerer vi på dag som tidligereGROUP BY 1– Vi grupperer resultatet etter kolonne 1 (den første kolonnen i SELECT-listen, altsåflydenity_nation)ORDER BY 2 DESC– Og sorterer etter kolonne 2 (antall unike fly per nasjon), synkende
Den mest kompliserte delen her er COUNT(DISTINCT icao). COUNT er en innebyggd funksjon i DuckDB, som lar oss gjøre en telleoperasjon direkte i spørringen. Nøkkelordet DISTINCT signaliserer at vi er ute etter unike ICAO-nummer. Vi henter altså ikke ut alle ICAO-numre i alle radene våre her, men vi teller hvor mange unike det finnes.
For å illustrere forskjellen:
COUNT(icao)ville talt alle forekomster av ICAO-nummer (samme fly flere ganger)COUNT(DISTINCT icao)teller kun unike ICAO-nummer (hvert fly kun én gang)
Etter hvert skal vi se på enda mer avanserte funksjoner vi kan skrive direkte inn i SQL-spørringer, men allerede nå aner vi konturene av noe kraftfullt.
Tolkning av resultatet¶
La oss se på resultatet. Vi ser at det er 72 rader, hvorav 71 er nasjoner. Hvordan kan dette ha seg? På toppen troner nemlig NULL. Dataene i kolonnen er hentet fra en ekstern database som heter Flydenity, og det er en del fly som ikke er registrert med nasjon her.
Hvis vi ønsker å kun vise registrerte land, kan vi filtrere bort NULL-verdiene ved å legge til betingelsen IS NOT NULL:
SELECT flydenity_nation,
COUNT(DISTINCT icao)
FROM mcn.airtraces
WHERE timestamp >= '2025-11-01 00:00:00'
AND timestamp < '2025-11-02 00:00:00'
AND flydenity_nation IS NOT NULL
GROUP BY 1
ORDER BY 2 DESC;┌──────────────────────┬──────────────────────┐
│ flydenity_nation │ count(DISTINCT icao) │
│ varchar │ int64 │
├──────────────────────┼──────────────────────┤
│ United States │ 809 │
│ United Kingdom │ 342 │
│ Germany │ 218 │
│ Netherlands │ 214 │
│ Ireland │ 185 │
│ Sweden │ 136 │
│ Turkey │ 123 │
│ Malta │ 121 │
│ Norway │ 111 │
│ China │ 88 │
│ United Arab Emirates │ 68 │
│ Poland │ 62 │
│ France │ 60 │
│ Qatar │ 59 │
│ Canada │ 49 │
│ Finland │ 47 │
│ Austria │ 47 │
│ Denmark │ 38 │
│ Russia │ 37 │
│ Portugal │ 35 │
│ · │ · │
│ · │ · │
│ · │ · │
│ Bulgaria │ 2 │
│ Sri Lanka │ 2 │
│ Pakistan │ 2 │
│ Kenya │ 2 │
│ Serbia │ 2 │
│ Isle of Man │ 2 │
│ San Marino │ 2 │
│ Brazil │ 2 │
│ Malaysia │ 2 │
│ Mexico │ 2 │
│ Bangladesh │ 2 │
│ Algeria │ 1 │
│ Indonesia │ 1 │
│ Georgia │ 1 │
│ Turkmenistan │ 1 │
│ Mauritius │ 1 │
│ Moldova │ 1 │
│ Venezuela │ 1 │
│ Dominican Republic │ 1 │
│ Israel │ 1 │
├──────────────────────┴──────────────────────┤
│ 71 rows (40 shown) 2 columns │
└─────────────────────────────────────────────┘IS NOT NULL er SQL-syntaks for å sjekke at en kolonne har en verdi (altså ikke er tom). Dette er nyttig når vi kun vil arbeide med komplette data.
Liste ut fartøy basert på nasjonalitet¶
La oss ta utgangspunkt i de flyene som har kjent nasjonalitet. Basert på dette resultatet kan vi nå endre spørringen til å få en oversikt over alle unike fartøy fra en spesifikk nasjon, for eksempel Russland:
SELECT DISTINCT icao,
registration,
flydenity_nation
FROM mcn.airtraces
WHERE timestamp >= '2025-11-01 00:00:00'
AND timestamp < '2025-11-02 00:00:00'
AND flydenity_nation = 'Russia'
ORDER BY 2;Denne spørringen ber om unike ICAO-nummer fra vår aktuelle dag hvor fartøyet er registrert i Russland, sortert etter registreringsnummer. Resultatet er som følger:
┌─────────┬──────────────┬──────────────────┐
│ icao │ registration │ flydenity_nation │
│ varchar │ varchar │ varchar │
├─────────┼──────────────┼──────────────────┤
│ 150657 │ RA-67159 │ Russia │
│ 151DF9 │ RA-73209 │ Russia │
│ 151E0C │ RA-73228 │ Russia │
│ 151E15 │ RA-73237 │ Russia │
│ 151E17 │ RA-73239 │ Russia │
│ 151E39 │ RA-73273 │ Russia │
│ 151E3B │ RA-73275 │ Russia │
│ 151E53 │ RA-73299 │ Russia │
│ 151E66 │ RA-73318 │ Russia │
│ 151E6B │ RA-73323 │ Russia │
│ 151E6D │ RA-73325 │ Russia │
│ 151EFA │ RA-73466 │ Russia │
│ 151F0C │ RA-73484 │ Russia │
│ 151F0D │ RA-73485 │ Russia │
│ 151FB8 │ RA-73656 │ Russia │
│ 151FB9 │ RA-73657 │ Russia │
│ 151FBD │ RA-73661 │ Russia │
│ 151FC0 │ RA-73664 │ Russia │
│ 151FF0 │ RA-73712 │ Russia │
│ 151FF3 │ RA-73715 │ Russia │
│ 151FF5 │ RA-73717 │ Russia │
│ 151FFF │ RA-73727 │ Russia │
│ 152001 │ RA-73729 │ Russia │
│ 15200A │ RA-73738 │ Russia │
│ 152029 │ RA-73769 │ Russia │
│ 152040 │ RA-73792 │ Russia │
│ 15204B │ RA-73803 │ Russia │
│ 15205F │ RA-73823 │ Russia │
│ 152064 │ RA-73828 │ Russia │
│ 152077 │ RA-73847 │ Russia │
│ 155BE3 │ RA-89059 │ Russia │
│ 155BE5 │ RA-89061 │ Russia │
│ 155C1A │ RA-89114 │ Russia │
│ 155C26 │ RA-89126 │ Russia │
│ 155C2C │ RA-89132 │ Russia │
│ 155C3C │ RA-89148 │ Russia │
│ 155C51 │ RA-89169 │ Russia │
├─────────┴──────────────┴──────────────────┤
│ 37 rows 3 columns │
└───────────────────────────────────────────┘Her ser vi 37 unike russiskregistrerte fly som ble observert 1. november 2025. Legg merke til at vi denne gangen bruker DISTINCT i SELECT-delen for å sikre at hvert fly kun vises én gang, selv om det kan ha blitt observert mange ganger i løpet av dagen.
Geografiske søk¶
Nå har vi sett på hvordan vi kan hente ut informasjon om et spesifikt luftfartøy, og fartøy registrert i ulike nasjoner. Men hva om vi ikke vet akkurat hva vi ser etter, og har behov for å jobbe i en bestemt region?
DuckDB har en utvidelse kalt spatial som lar oss gjøre geografiske spørringer. Kort fortalt gjør denne modulen DuckDB i stand til å håndtere koordinatsystemer. Vi begynner med å se på tradisjonelle former for geografiske søk, før vi utforsker det enkle og geniale H3-rammeverket for geografisk søk, utviklet av Uber.
Forberedelser: Lag en lokal tabell¶
Før vi gjør geografiske søk, må vi legge til et viktig steg. Til nå har vi jobbet direkte mot Medieklyngens datasjø, noe som fungerer fint for enkle operasjoner. Men når vi skal gjøre mer avanserte geografiske analyser med spatial-funksjoner, må vi laste dataene ned lokalt først. Siden DuckLake er en relativt ny løsning, er ikke alle DuckDBs funksjoner støttet direkte i datasjøen enda.
Vi laster ned dataene slik:
CREATE TABLE flights_02_11_25 AS
SELECT timestamp, icao, registration, squawk, flight, latitude, longitude,
altitude_barometric, on_ground, ground_speed, track
FROM mcn.airtraces
WHERE timestamp >= '2025-11-02 00:00:00'
AND timestamp < '2025-11-03 00:00:00'
ORDER BY timestamp ASC;Som vi ser, er spørringen tilnærmet identisk til dem vi kjenner godt fra tidligere. To ting er endret:
Vi starter spørringen med
CREATE TABLE flights_02_11_25 AS, som signaliserer til DuckDB at vi skal lage en ny, lokal tabell. NøkkelordetAS, helt på slutten av linjen, instruerer DuckDB om å populere denne tabellen med resultatet fra spørringen som følger. Navnet på tabellen velger vi fritt selv.Vi har også endret datoene i filtrene, slik at vi nå laster ned data fra 2. november. Forandring fryder!
Dataene lagres i den lokale tabellen flights_02_11_25, som bor i filen vi spesifiserte da vi startet opp DuckDB tidligere (sensorkurs-andre-samling.db) på din datamaskin. Nå kan vi spørre på dataene helt lokalt i fortsettelsen.
Administrere lokale tabeller¶
Etter hvert som vi jobber, kan den lokale databasefilen på maskinen din bli fylt opp med en rekke tabeller. For å se hvilke tabeller som finnes, kan vi kjøre følgende kommando:
SHOW TABLES;Da får vi opp en liste med de lokale tabellene våre:
┌──────────────────┐
│ name │
│ varchar │
├──────────────────┤
│ flights_02_11_25 │
└──────────────────┘Punkt og radius¶
Når man vurderer måter å avgrense et søk geografisk på, dukker kanskje passeren opp i tankene. I den analoge verdenen starter vi med et geografisk utgangspunkt på kartet, setter passernålen på ønsket posisjon og måler opp en radius. Denne metoden kan vi enkelt replikere i DuckDB.
La oss si at vi vil søke etter alle fly innenfor en radius på 1 kilometer fra Bergen lufthavn, Flesland. Funksjonen ST_Distance_Sphere bruker haversine-formelen som tar høyde for jordens kurvatur og gir oss storsirkelavstanden – den korteste avstanden mellom to punkter målt langs jordoverflaten. Se følgende spørring:
SELECT timestamp, icao, registration, longitude, latitude
FROM flights_02_11_25
WHERE ST_Distance_Sphere(
ST_Point(latitude, longitude),
ST_Point(60.293389, 5.218056) -- Fleslands koordinater
) <= 1000; -- meter┌────────────────────────────┬─────────┬──────────────┬───────────────┬──────────────┐
│ timestamp │ icao │ registration │ longitude │ latitude │
│ timestamp with time zone │ varchar │ varchar │ decimal(10,6) │ decimal(9,6) │
├────────────────────────────┼─────────┼──────────────┼───────────────┼──────────────┤
│ 2025-11-02 00:00:01.092+01 │ 478A3A │ NULL │ 5.231410 │ 60.294812 │
│ 2025-11-02 00:00:02.941+01 │ 478A3A │ NULL │ 5.231405 │ 60.294808 │
│ 2025-11-02 00:00:02.941+01 │ 478A88 │ NULL │ 5.231481 │ 60.295016 │
│ 2025-11-02 00:00:03.109+01 │ 478A3A │ NULL │ 5.231386 │ 60.294800 │
│ 2025-11-02 00:00:03.109+01 │ 478A88 │ NULL │ 5.231479 │ 60.295016 │
│ 2025-11-02 00:00:03.512+01 │ 478A3A │ NULL │ 5.231380 │ 60.294796 │
│ 2025-11-02 00:00:04.522+01 │ 478A44 │ NULL │ 5.231331 │ 60.294864 │
│ 2025-11-02 00:00:04.522+01 │ 478A3A │ NULL │ 5.231363 │ 60.294800 │
│ 2025-11-02 00:00:06.5+01 │ 478A88 │ NULL │ 5.231479 │ 60.295016 │
│ 2025-11-02 00:00:06.5+01 │ 478A3A │ NULL │ 5.231380 │ 60.294796 │
│ 2025-11-02 00:00:06.938+01 │ 478A3A │ NULL │ 5.231363 │ 60.294800 │
│ 2025-11-02 00:00:06.938+01 │ 478A88 │ NULL │ 5.231481 │ 60.295016 │
│ 2025-11-02 00:00:06.938+01 │ 478A44 │ NULL │ 5.231339 │ 60.294868 │
│ 2025-11-02 00:00:07.053+01 │ 478A3A │ NULL │ 5.231339 │ 60.294800 │
│ 2025-11-02 00:00:08.639+01 │ 478A44 │ NULL │ 5.231331 │ 60.294864 │
│ 2025-11-02 00:00:08.639+01 │ 478A3A │ NULL │ 5.231331 │ 60.294796 │
│ 2025-11-02 00:00:08.639+01 │ 478A88 │ NULL │ 5.231479 │ 60.295016 │
│ 2025-11-02 00:00:09.497+01 │ 478A88 │ NULL │ 5.231481 │ 60.295016 │
│ 2025-11-02 00:00:09.497+01 │ 478A3A │ NULL │ 5.231339 │ 60.294800 │
│ 2025-11-02 00:00:10.826+01 │ 478A3A │ NULL │ 5.231331 │ 60.294796 │
│ · │ · │ · │ · │ · │
│ · │ · │ · │ · │ · │
│ · │ · │ · │ · │ · │
│ 2025-11-02 23:59:51.827+01 │ 478A3A │ NULL │ 5.231363 │ 60.294744 │
│ 2025-11-02 23:59:51.827+01 │ 478A88 │ NULL │ 5.231386 │ 60.294984 │
│ 2025-11-02 23:59:53.083+01 │ 478A3A │ NULL │ 5.231356 │ 60.294740 │
│ 2025-11-02 23:59:53.083+01 │ 478A44 │ NULL │ 5.231062 │ 60.294796 │
│ 2025-11-02 23:59:53.083+01 │ 478A45 │ NULL │ 5.230747 │ 60.294928 │
│ 2025-11-02 23:59:53.748+01 │ 478A88 │ NULL │ 5.231405 │ 60.294984 │
│ 2025-11-02 23:59:54.284+01 │ 478A88 │ NULL │ 5.231386 │ 60.294984 │
│ 2025-11-02 23:59:56.156+01 │ 478A88 │ NULL │ 5.231405 │ 60.294984 │
│ 2025-11-02 23:59:56.156+01 │ 478A3A │ NULL │ 5.231380 │ 60.294752 │
│ 2025-11-02 23:59:56.252+01 │ 478A45 │ NULL │ 5.230747 │ 60.294928 │
│ 2025-11-02 23:59:57.081+01 │ 478A45 │ NULL │ 5.230747 │ 60.294912 │
│ 2025-11-02 23:59:57.081+01 │ 478A44 │ NULL │ 5.231102 │ 60.294788 │
│ 2025-11-02 23:59:57.191+01 │ 478A3A │ NULL │ 5.231380 │ 60.294760 │
│ 2025-11-02 23:59:57.275+01 │ 478A44 │ NULL │ 5.231086 │ 60.294784 │
│ 2025-11-02 23:59:57.756+01 │ 478A45 │ NULL │ 5.230743 │ 60.294912 │
│ 2025-11-02 23:59:58.896+01 │ 478A44 │ NULL │ 5.231111 │ 60.294784 │
│ 2025-11-02 23:59:59.039+01 │ 478A44 │ NULL │ 5.231102 │ 60.294788 │
│ 2025-11-02 23:59:59.505+01 │ 478A3A │ NULL │ 5.231386 │ 60.294752 │
│ 2025-11-02 23:59:59.505+01 │ 478A45 │ NULL │ 5.230747 │ 60.294912 │
│ 2025-11-02 23:59:59.876+01 │ 478A3A │ NULL │ 5.231380 │ 60.294752 │
├────────────────────────────┴─────────┴──────────────┴───────────────┴──────────────┤
│ 142136 rows (40 shown) 5 columns │
└────────────────────────────────────────────────────────────────────────────────────┘La oss bryte ned denne spørringen. Første delen er kjent stoff, men etter WHERE begynner det å balle på seg. Her bruker vi geo-funksjonen ST_Distance_Sphere, som filtrerer gjennom hvert datapunkt i tabellen og sjekker om det befinner seg innenfor vårt forhåndsdefinerte område.
Filteret vi har satt opp, er: «Alle datapunkter innenfor 1000 meter fra det geografiske punktet 5.218056, 60.293389 (Bergen lufthavn, Flesland)».
Avgrensningsboks¶
En annen måte å definere et geografisk område på er å tegne en avgrensningsboks ved hjelp av fire geografiske koordinater: to lengdegrader og to breddegrader. Ved å definere disse som punkter på et kart og trekke en tenkt linje mellom dem, får vi en sektor i form av et kvadrat eller et rektangel vi kan bruke som et filter.
La oss søke etter alle fly i et område rundt Bergen:
SELECT timestamp, icao, registration, longitude, latitude
FROM flights_02_11_25
WHERE (longitude BETWEEN 5.2 AND 5.4) AND (latitude BETWEEN 60.2 AND 60.4);┌────────────────────────────┬─────────┬──────────────┬───────────────┬──────────────┐
│ timestamp │ icao │ registration │ longitude │ latitude │
│ timestamp with time zone │ varchar │ varchar │ decimal(10,6) │ decimal(9,6) │
├────────────────────────────┼─────────┼──────────────┼───────────────┼──────────────┤
│ 2025-11-02 00:00:01.092+01 │ 478A3A │ NULL │ 5.231410 │ 60.294812 │
│ 2025-11-02 00:00:02.941+01 │ 478A3A │ NULL │ 5.231405 │ 60.294808 │
│ 2025-11-02 00:00:02.941+01 │ 478A88 │ NULL │ 5.231481 │ 60.295016 │
│ 2025-11-02 00:00:03.109+01 │ 478A3A │ NULL │ 5.231386 │ 60.294800 │
│ 2025-11-02 00:00:03.109+01 │ 478A88 │ NULL │ 5.231479 │ 60.295016 │
│ 2025-11-02 00:00:03.512+01 │ 478A3A │ NULL │ 5.231380 │ 60.294796 │
│ 2025-11-02 00:00:04.522+01 │ 478A44 │ NULL │ 5.231331 │ 60.294864 │
│ 2025-11-02 00:00:04.522+01 │ 478A3A │ NULL │ 5.231363 │ 60.294800 │
│ 2025-11-02 00:00:06.5+01 │ 478A88 │ NULL │ 5.231479 │ 60.295016 │
│ 2025-11-02 00:00:06.5+01 │ 478A3A │ NULL │ 5.231380 │ 60.294796 │
│ 2025-11-02 00:00:06.938+01 │ 478A3A │ NULL │ 5.231363 │ 60.294800 │
│ 2025-11-02 00:00:06.938+01 │ 478A88 │ NULL │ 5.231481 │ 60.295016 │
│ 2025-11-02 00:00:06.938+01 │ 478A44 │ NULL │ 5.231339 │ 60.294868 │
│ 2025-11-02 00:00:07.053+01 │ 478A3A │ NULL │ 5.231339 │ 60.294800 │
│ 2025-11-02 00:00:08.639+01 │ 478A44 │ NULL │ 5.231331 │ 60.294864 │
│ 2025-11-02 00:00:08.639+01 │ 478A3A │ NULL │ 5.231331 │ 60.294796 │
│ 2025-11-02 00:00:08.639+01 │ 478A88 │ NULL │ 5.231479 │ 60.295016 │
│ 2025-11-02 00:00:09.497+01 │ 478A88 │ NULL │ 5.231481 │ 60.295016 │
│ 2025-11-02 00:00:09.497+01 │ 478A3A │ NULL │ 5.231339 │ 60.294800 │
│ 2025-11-02 00:00:10.826+01 │ 478A3A │ NULL │ 5.231331 │ 60.294796 │
│ · │ · │ · │ · │ · │
│ · │ · │ · │ · │ · │
│ · │ · │ · │ · │ · │
│ 2025-11-02 23:59:51.827+01 │ 478A3A │ NULL │ 5.231363 │ 60.294744 │
│ 2025-11-02 23:59:51.827+01 │ 478A88 │ NULL │ 5.231386 │ 60.294984 │
│ 2025-11-02 23:59:53.083+01 │ 478A3A │ NULL │ 5.231356 │ 60.294740 │
│ 2025-11-02 23:59:53.083+01 │ 478A44 │ NULL │ 5.231062 │ 60.294796 │
│ 2025-11-02 23:59:53.083+01 │ 478A45 │ NULL │ 5.230747 │ 60.294928 │
│ 2025-11-02 23:59:53.748+01 │ 478A88 │ NULL │ 5.231405 │ 60.294984 │
│ 2025-11-02 23:59:54.284+01 │ 478A88 │ NULL │ 5.231386 │ 60.294984 │
│ 2025-11-02 23:59:56.156+01 │ 478A88 │ NULL │ 5.231405 │ 60.294984 │
│ 2025-11-02 23:59:56.156+01 │ 478A3A │ NULL │ 5.231380 │ 60.294752 │
│ 2025-11-02 23:59:56.252+01 │ 478A45 │ NULL │ 5.230747 │ 60.294928 │
│ 2025-11-02 23:59:57.081+01 │ 478A45 │ NULL │ 5.230747 │ 60.294912 │
│ 2025-11-02 23:59:57.081+01 │ 478A44 │ NULL │ 5.231102 │ 60.294788 │
│ 2025-11-02 23:59:57.191+01 │ 478A3A │ NULL │ 5.231380 │ 60.294760 │
│ 2025-11-02 23:59:57.275+01 │ 478A44 │ NULL │ 5.231086 │ 60.294784 │
│ 2025-11-02 23:59:57.756+01 │ 478A45 │ NULL │ 5.230743 │ 60.294912 │
│ 2025-11-02 23:59:58.896+01 │ 478A44 │ NULL │ 5.231111 │ 60.294784 │
│ 2025-11-02 23:59:59.039+01 │ 478A44 │ NULL │ 5.231102 │ 60.294788 │
│ 2025-11-02 23:59:59.505+01 │ 478A3A │ NULL │ 5.231386 │ 60.294752 │
│ 2025-11-02 23:59:59.505+01 │ 478A45 │ NULL │ 5.230747 │ 60.294912 │
│ 2025-11-02 23:59:59.876+01 │ 478A3A │ NULL │ 5.231380 │ 60.294752 │
├────────────────────────────┴─────────┴──────────────┴───────────────┴──────────────┤
│ 190905 rows (40 shown) 5 columns │
└────────────────────────────────────────────────────────────────────────────────────┘Denne metoden er litt mer lettlest enn den forrige. Her ber vi om alle datapunkter som ligger mellom lengdegradene 5.2 og 5.4, samt mellom breddegradene 60.2 og 60.4.
Sammenligning av geografiske søkemetoder¶
Vi har nå sett på to ulike måter å søke geografisk i flydata. Før vi går videre til den tredje metoden (H3), la oss sammenligne:
| Metode | Beste bruk | Fordeler | Ulemper |
|---|---|---|---|
| Punkt og radius | Søk rundt et spesifikt sted (f.eks. en flyplass eller landingsområde) | Enkel å forstå, presis sirkulær avgrensning | Kan være treg på store datasett |
| Avgrensningsboks | Søk i rektangulære områder | Rask, enkel SQL-syntaks | Rektangulær form passer ikke alle bruksområder |
| H3 (kommer nå) | Effektive søk og aggregeringer over store områder | Meget rask, hierarkisk, god for analyser | Krever forståelse av heksagonsystemet |
I tillegg til disse metodene er det mulig å gjøre komplekse søk i områder definert via polygoner. For eksempel kan man søke etter alle datapunkter innenfor et spesifikt fylke. Siden dette er mer spesialisert og mindre relevant for fly- og skipstrafikkdata, kommer vi ikke til å gå i dybden på dette her. I stedet skal vi nå utforske H3 – en svært effektiv metode for geografiske søk.
H3¶
H3 er et geografisk rutenett-system utviklet av Uber for å effektivisere og forenkle geografiske analyser. Systemet deler jordkloden inn i sekskantede celler, kjent som heksagoner, i motsetning til tradisjonelle rutemønstre basert på kvadrater. Hver heksagon har en unik identifikator (H3 ID) og kan representere geografiske områder med høy presisjon og i ulike oppløsninger.
Hvorfor heksagoner?¶
H3-rammeverket gir en mer naturlig tilnærming til romlige analyser sammenlignet med kvadratiske rutenett, som ofte introduserer skjevheter på grunn av jordens krumning. En annen fordel med heksagoner er at de overlapper i svært liten grad, i motsetning til for eksempel sirkler.
Oppløsninger og hierarki¶
H3 opererer med 15 ulike oppløsninger. Lavere oppløsninger dekker større områder, mens høyere oppløsninger gir mer detaljerte inndelinger. Dette gjør det mulig å skalere geografiske analyser etter behov – for eksempel kan man undersøke et helt kontinent med grov oppløsning eller et spesifikt nabolag med høy detaljgrad.
Et heksagon i H3 kan også grupperes med sine «foreldre» på høyere nivåer. Dette betyr at man enkelt kan aggregere data for større områder ved å slå sammen flere små heksagoner til større enheter. Slike hierarkiske strukturer er nyttige for analyser på tvers av forskjellige geografiske skalaer.
H3 i Medieklyngens datasjø¶
Det er mulig å oversette vanlige koordinater til H3-heksagoner direkte i DuckDB ved hjelp av innebygde funksjoner. I Medieklyngens datasjø har vi imidlertid allerede gjort denne jobben, for å sikre mest mulig effektive spørringer.
Med dette tillegget på plass, er det bare å gå i gang.
Bruk av H3 i praksis¶
Finn H3-sektoren du ønsker å søke i. Til dette finnes det flere åpne løsninger, for eksempel H3 Viewer.
Finn området du ønsker å undersøke, for eksempel Bergen. Etter hvert som du zoomer inn på kartet, vil du se at rutenettet av heksagoner endrer størrelse. Velg et passende utsnitt hvor ett heksagon dekker området du vil analysere. Noter Current H3 resolution øverst til høyre i skjermbildet.
Klikk på ønsket heksagon. Dennes ID kopieres da til utklippsboken din.
Kjør følgende spørring, tilpasset med din egen heksagon-ID og oppløsning. Oppløsningen spesifiserer du der det nå står
4(etterh3_cell_to_parent), mens heksagon-ID-en settes til slutt på samme linje.
SELECT registration, latitude, longitude, timestamp
FROM mcn.airtraces
WHERE timestamp >= '2025-11-02 00:00:00'
AND timestamp < '2025-11-03 00:00:00'
AND h3_cell_to_parent(h3_15, 4) = '84099e9ffffffff';┌──────────────┬──────────────┬───────────────┬────────────────────────────┐
│ registration │ latitude │ longitude │ timestamp │
│ varchar │ decimal(9,6) │ decimal(10,6) │ timestamp with time zone │
├──────────────┼──────────────┼───────────────┼────────────────────────────┤
│ B-303Z │ 58.303976 │ 8.286438 │ 2025-11-02 05:06:52.133+01 │
│ B-303Z │ 58.303344 │ 8.284843 │ 2025-11-02 05:06:52.625+01 │
│ B-303Z │ 58.301788 │ 8.280945 │ 2025-11-02 05:06:53.991+01 │
│ B-303Z │ 58.301148 │ 8.279350 │ 2025-11-02 05:06:54.537+01 │
│ B-303Z │ 58.300624 │ 8.278107 │ 2025-11-02 05:06:55.071+01 │
│ B-303Z │ 58.299928 │ 8.276276 │ 2025-11-02 05:06:55.63+01 │
│ B-303Z │ 58.297896 │ 8.271199 │ 2025-11-02 05:06:57.431+01 │
│ B-303Z │ 58.297304 │ 8.269781 │ 2025-11-02 05:06:57.871+01 │
│ B-303Z │ 58.295504 │ 8.265289 │ 2025-11-02 05:06:59.457+01 │
│ B-303Z │ 58.294968 │ 8.264022 │ 2025-11-02 05:06:59.837+01 │
│ B-303Z │ 58.293092 │ 8.259415 │ 2025-11-02 05:07:01.517+01 │
│ B-303Z │ 58.291176 │ 8.254669 │ 2025-11-02 05:07:03.059+01 │
│ B-303Z │ 58.290528 │ 8.253036 │ 2025-11-02 05:07:03.735+01 │
│ B-303Z │ 58.289016 │ 8.249315 │ 2025-11-02 05:07:05.074+01 │
│ B-303Z │ 58.288328 │ 8.247543 │ 2025-11-02 05:07:05.61+01 │
│ B-303Z │ 58.287644 │ 8.245771 │ 2025-11-02 05:07:06.21+01 │
│ B-303Z │ 58.287048 │ 8.244353 │ 2025-11-02 05:07:06.718+01 │
│ B-303Z │ 58.286452 │ 8.242936 │ 2025-11-02 05:07:07.155+01 │
│ B-303Z │ 58.285996 │ 8.241695 │ 2025-11-02 05:07:07.632+01 │
│ B-303Z │ 58.306368 │ 8.292374 │ 2025-11-02 05:06:50.114+01 │
│ · │ · │ · │ · │
│ · │ · │ · │ · │
│ · │ · │ · │ · │
│ LN-WDN │ 58.177356 │ 8.049774 │ 2025-11-02 23:29:50.621+01 │
│ LN-WDN │ 58.177912 │ 8.050507 │ 2025-11-02 23:29:51.769+01 │
│ LN-WDN │ 58.165392 │ 8.033936 │ 2025-11-02 23:29:28.067+01 │
│ LN-WDN │ 58.165672 │ 8.034302 │ 2025-11-02 23:29:28.667+01 │
│ LN-WDN │ 58.165952 │ 8.034668 │ 2025-11-02 23:29:29.049+01 │
│ LN-WDN │ 58.166200 │ 8.034993 │ 2025-11-02 23:29:29.596+01 │
│ LN-WDN │ 58.166508 │ 8.035400 │ 2025-11-02 23:29:30.142+01 │
│ LN-WDN │ 58.166784 │ 8.035767 │ 2025-11-02 23:29:30.634+01 │
│ LN-WDN │ 58.168464 │ 8.037872 │ 2025-11-02 23:29:33.747+01 │
│ LN-WDN │ 58.171072 │ 8.041351 │ 2025-11-02 23:29:38.662+01 │
│ LN-WDN │ 58.174256 │ 8.045625 │ 2025-11-02 23:29:44.67+01 │
│ LN-WDN │ 58.174760 │ 8.046334 │ 2025-11-02 23:29:45.707+01 │
│ LN-WDN │ 58.175264 │ 8.046936 │ 2025-11-02 23:29:46.527+01 │
│ LN-WDN │ 58.175536 │ 8.047308 │ 2025-11-02 23:29:47.185+01 │
│ LN-WDN │ 58.176848 │ 8.049042 │ 2025-11-02 23:29:49.641+01 │
│ LN-WDN │ 58.178472 │ 8.051239 │ 2025-11-02 23:29:52.805+01 │
│ LN-WDN │ 58.179016 │ 8.052004 │ 2025-11-02 23:29:53.897+01 │
│ LN-WDN │ 58.181580 │ 8.055459 │ 2025-11-02 23:29:58.976+01 │
│ LN-WDN │ 58.184060 │ 8.058746 │ 2025-11-02 23:30:03.837+01 │
│ LN-WDN │ 58.185132 │ 8.060120 │ 2025-11-02 23:30:05.914+01 │
├──────────────┴──────────────┴───────────────┴────────────────────────────┤
│ 10683 rows (40 shown) 4 columns │
└──────────────────────────────────────────────────────────────────────────┘Aggregering med H3¶
For å få en rask oversikt over hvor mange ganger hvert fartøy er observert i den valgte sektoren, kan vi bruke følgende spørring:
SELECT registration, COUNT(icao) as count
FROM mcn.airtraces
WHERE timestamp >= '2025-11-02 00:00:00'
AND timestamp < '2025-11-03 00:00:00'
AND h3_4 = '84099e9ffffffff'
GROUP BY registration
ORDER BY count DESC;┌──────────────┬───────┐
│ registration │ count │
│ varchar │ int64 │
├──────────────┼───────┤
│ LN-PFG │ 694 │
│ SE-RUP │ 530 │
│ LN-FGH │ 413 │
│ B-303Z │ 393 │
│ PH-HXE │ 380 │
│ SE-RST │ 322 │
│ 9H-MRT │ 312 │
│ G-DBCG │ 311 │
│ SE-RZE │ 309 │
│ SE-RZZ │ 302 │
│ M-ABGS │ 289 │
│ LN-WDN │ 280 │
│ SE-ROT │ 258 │
│ LN-NII │ 250 │
│ SE-RYC │ 245 │
│ C-FNOI │ 241 │
│ A7-BTB │ 238 │
│ OY-KBO │ 238 │
│ C-FRSO │ 237 │
│ SE-RPG │ 229 │
│ · │ · │
│ · │ · │
│ · │ · │
│ G-TTOB │ 113 │
│ EI-SII │ 113 │
│ LN-WDP │ 111 │
│ PH-EZM │ 110 │
│ TC-LHF │ 110 │
│ G-EUUI │ 102 │
│ 9H-WNV │ 99 │
│ EI-SIZ │ 92 │
│ LN-WDU │ 91 │
│ OY-KBR │ 90 │
│ PH-EZO │ 82 │
│ LN-ENN │ 82 │
│ PH-EXL │ 72 │
│ G-RUKJ │ 68 │
│ OH-LKN │ 56 │
│ SE-NAD │ 54 │
│ OY-SWO │ 52 │
│ A7-BAC │ 51 │
│ PH-EXU │ 42 │
│ LN-RKT │ 2 │
├──────────────┴───────┤
│ 55 rows 2 columns │
│ (40 shown) │
└──────────────────────┘Her får vi en oversikt over fartøyene som er observert i den aktuelle sektoren, sortert etter antall meldinger.
Oppsummering: Når bruker vi hvilken metode?¶
Nå har vi sett på tre ulike måter å søke geografisk i flydata. Her er en oppsummering av når du bør bruke hvilken:
| Metode | Bruk når... | Eksempel |
|---|---|---|
| Punkt og radius | Du søker rundt et spesifikt sted med kjent avstand | «Alle fly innenfor 5 km fra Bergen lufthavn» |
| Avgrensningsboks | Du har et rektangulært område definert av koordinater | «Alle fly mellom disse fire hjørnepunktene» |
| H3 | Du skal gjøre effektive søk eller aggregeringer over større områder | «Alle fly i Bergen-regionen gruppert etter antall observasjoner» |
I praksis vil du ofte bruke:
H3 for store analyser, aggregeringer og når du skal jobbe med mange områder
Punkt-og-radius for spesifikke undersøkelser av mindre områder rundt kjente punkter
Avgrensningsboks når du har et naturlig rektangulært område (f.eks. mellom to veier)
Oppgaver¶
Interessante fartøy og skjulte data¶
Mens mange av sporingsdataene vi har sett på så langt er offentlig tilgjengelig i tjenester som Flightradar24 og Radarbox, er det ekstra spennende med informasjon som er vanskeligere tilgjengelig. Noen fly ønsker ikke å bli sporet offentlig – men sender fortsatt ut ADS-B-signaler som vi kan fange opp. Andre fly er militære, eller klassifisert som «interessante» av ulike årsaker.
Her kommer kolonnen flags til unnsetning. Feltet inneholder informasjon om spesielle egenskaper ved fartøyet som ikke alltid er synlige i offentlige sporingstjenester. Dataene her stammer fra en database som oppdateres periodisk på serveren.
Hva forteller flags-kolonnen oss?¶
Kolonnen flags kan inneholde følgende informasjon:
flags = 1angir at fartøyet er militærtflags = 2angir at fartøyet er klassifisert som interessantflags = 4står for PIA: Privacy International Civil Aviation Organization (ICAO) Addressflags = 8står for LADD: Limiting Aircraft Data Displayed
LADD og PIA husker vi fra tidligere. LADD er flagget som skal signalisere til Flightradar24 og andre tjenester at det aktuelle fartøyet bør filtreres ut fra offentlig visning. PIA er et midlertidig ICAO-nummer som man kan søke om å få innvilget fra amerikanske luftfartsmyndigheter.
Disse flaggene kan gi oss svært verdifull journalistisk informasjon – for eksempel kan vi finne militære fly som opererer i et område, eller identifisere fly som aktivt prøver å skjule seg fra offentlig sporing.
Et praktisk eksempel først¶
La oss si at vi finner et fly med flags = 12. Hva betyr det?
Hvis vi ser i binærrepresentasjon:
12 = 8 + 4
= LADD + PIADette flyet har altså både LADD-beskyttelse OG et midlertidig PIA-nummer. Det prøver aktivt å skjule seg fra offentlig sporing på to måter samtidig. Spennende!
Men vent litt – hvordan kan én kolonne inneholde informasjon om flere egenskaper samtidig? Man kunne tenke at hvert fartøy kun kan ha ett av disse flaggene. Men sannheten er at hvert fartøy kan ha flere flagg satt samtidig, gjennom en smart teknisk løsning kjent som bitwise-operasjoner.
La oss se hvordan dette fungerer.
Hvordan fungerer bitwise?¶
Alt av data i en datamaskin lagres som biter – det vil si 0-er og 1-ere. Når vi snakker om en heltallsverdi som flags, består den egentlig bare av en sekvens av slike biter. Her er et forenklet eksempel:
Binærverdi: 1000 → Desimalverdi: 8Hver posisjon i dette binærtallet kan representere et «ja/nei»-flagg. Og siden vi kan slå sammen flere slike, kan ett enkelt tall representere flere egenskaper samtidig.
Oversikt over flaggverdier¶
| Flaggtype | Desimalverdi | Binærverdi | Betydning |
|---|---|---|---|
| Militært | 1 | 0001 | Bit 1 er satt |
| Interessant | 2 | 0010 | Bit 2 er satt |
| PIA | 4 | 0100 | Bit 3 er satt |
| LADD | 8 | 1000 | Bit 4 er satt |
Eksempel på kombinasjon¶
Hvis et fartøy både er markert som PIA og LADD, får det:
0100 (PIA = 4)
+ 1000 (LADD = 8)
-------
1100 (= 12)Med andre ord: flags = 12 forteller oss at begge disse to flaggene er satt samtidig.
Andre eksempler:
flags = 3betyr militært (1) + interessant (2)flags = 9betyr militært (1) + LADD (8)flags = 15betyr alle fire flagg er satt!
Hvordan finner vi ut hvilke flagg som er satt?¶
For å sjekke om et bestemt flagg er satt, bruker vi en bitvis AND-operasjon. Ampersand-tegnet (&) er operatoren vi bruker i SQL for dette. Den sammenligner to tall bit for bit og returnerer 1 bare hvis begge bitene er 1.
Slik fungerer &-operatoren¶
Eksempel:
flags = 12: 1100
LADD = 8: 1000
------
Resultat: 1000 (= 8, som er større enn 0)Derfor kan vi bruke flags & 8 != 0 for å sjekke om LADD-flagget er satt.
Et annet eksempel:
flags = 12: 1100
Militært=1: 0001
------
Resultat: 0000 (= 0, militært er IKKE satt)Her får vi 0, fordi det militære flagget ikke er aktivt i flags = 12.
Søke etter fly med spesifikke flagg¶
Nå som vi forstår teorien, la oss se hvordan vi bruker dette i praksis.
SELECT DISTINCT icao, registration, flags
FROM mcn.airtraces
WHERE timestamp >= '2025-11-01 00:00:00'
AND timestamp < '2025-11-02 00:00:00'
AND flags & 8 != 0
ORDER BY registration;Denne spørringen returnerer alle rader der LADD-flagget er satt. Vi sjekker altså om biten for tallet 8 (som tilsvarer 1000 binært) er skrudd på.
Dette gir oss:
┌─────────┬──────────────┬───────┐
│ icao │ registration │ flags │
│ varchar │ varchar │ int32 │
├─────────┼──────────────┼───────┤
│ AE10BB │ 01-0192 │ 10 │
│ 477FF2 │ 02 │ 10 │
│ 478125 │ 0265 │ 10 │
│ 47812A │ 0276 │ 10 │
│ AE119C │ 03-3119 │ 10 │
│ AE1458 │ 06-6154 │ 10 │
│ AE145E │ 06-6160 │ 10 │
│ AE146F │ 07-7177 │ 10 │
│ AE49C6 │ 09-9210 │ 10 │
│ 4A8199 │ 102004 │ 10 │
│ AE5BFD │ 16-03090 │ 10 │
│ AE03F1 │ 165379 │ 10 │
│ 478114 │ 5607 │ 10 │
│ ADFF0C │ 60-0335 │ 10 │
│ AE062B │ 60-0353 │ 10 │
│ AE0562 │ 85-0005 │ 10 │
│ AE057D │ 87-0032 │ 10 │
│ AE0CFC │ 87-24594 │ 10 │
│ AE07DA │ 93-0600 │ 10 │
│ AE07DE │ 93-0604 │ 10 │
│ AE07F3 │ 97-0041 │ 10 │
│ 06A01C │ A7-HJJ │ 10 │
│ 06A27A │ A7-MAM │ 10 │
│ 7101E3 │ HZ-MF2 │ 10 │
│ 485920 │ PH-GOV │ 10 │
│ 43C5EE │ ZM420 │ 10 │
│ 43C1C6 │ ZZ176 │ 10 │
│ 43C6F3 │ ZZ330 │ 10 │
│ A038E6 │ NULL │ 10 │
│ A12257 │ NULL │ 10 │
├─────────┴──────────────┴───────┤
│ 30 rows 3 columns │
└────────────────────────────────┘Her ser vi at alle luftfartøyene har flags = 10, som betyr at det har både LADD (8) og Interessant (2) satt.
Det samme prinsippet gjelder for de andre flaggene:
-- Alle fartøy klassifisert som militære
SELECT DISTINCT icao, registration, flags
FROM mcn.airtraces
WHERE timestamp >= '2025-11-01 00:00:00'
AND timestamp < '2025-11-02 00:00:00'
AND flags & 1 != 0
ORDER BY registration;
-- Alle fartøy klassifisert som interessante
SELECT DISTINCT icao, registration, flags
FROM mcn.airtraces
WHERE timestamp >= '2025-11-01 00:00:00'
AND timestamp < '2025-11-02 00:00:00'
AND flags & 2 != 0
ORDER BY registration;
-- Alle fartøy med PIA
SELECT DISTINCT icao, registration, flags
FROM mcn.airtraces
WHERE timestamp >= '2025-11-01 00:00:00'
AND timestamp < '2025-11-02 00:00:00'
AND flags & 4 != 0
ORDER BY registration;Hvis du vil hente ut fartøy som har enten LADD eller PIA (eller begge), gjør du slik:
SELECT DISTINCT icao, registration, flags
FROM mcn.airtraces
WHERE timestamp >= '2025-11-01 00:00:00'
AND timestamp < '2025-11-02 00:00:00'
AND flags & (8 + 4) != 0
ORDER BY registration;Her bruker vi (8 + 4) som tilsvarer 12 i binær: 1100. Dette sjekker om minst ett av de to flaggene er satt.
En enklere tilnærming: Boolske kolonner¶
Synes du bitwise-konseptet er i overkant avansert? Det er helt forståelig – det er tross alt journalistikk og ikke teknisk utvikling vi fokuserer på her. Derfor har det nylig kommet en utvidelse i Medieklyngens datasjø som lar deg søke frem interessante fartøy på en enklere måte.
I tillegg til flags-kolonnen finnes det nå også egne boolske kolonner for hvert flagg:
| Flaggtype | Kolonne | Mulige verdier |
|---|---|---|
| Militært | is_military | true eller false |
| Interessant | is_interesting | true eller false |
| PIA | is_pia | true eller false |
| LADD | is_ladd | true eller false |
Dette gjør spørringene mye mer lesbare og intuitive:
SELECT DISTINCT icao, registration, flags
FROM mcn.airtraces
WHERE timestamp >= '2025-11-01 00:00:00'
AND timestamp < '2025-11-02 00:00:00'
AND is_interesting
ORDER BY registration;Nødkoder og nødsituasjoner¶
Som vi så tidligere, kan piloter melde fra om ulike situasjoner ved å sette spesielle squawk-koder i transponderen. Disse kodene er standardiserte signaler som kommuniserer flyets status til lufttrafikkontrollen og andre fly. La oss se om vi finner noen slike koder i våre historiske data.
Hva er squawk-koder?¶
En squawk-kode er en firesifret kode som flyet sender ut via transponderen. De fleste squawk-koder er rutinemessige identifikatorer tildelt av lufttrafikkontrollen, men noen koder har spesielle betydninger:
| Squawk-kode | Betydning | Alvorlighetsgrad |
|---|---|---|
| 7700 | Generell nødsituasjon | Kritisk |
| 7600 | Radiokommunikasjon sviktet | Alvorlig |
| 7500 | Kapring (hijacking) | Kritisk |
I dette kapittelet skal vi se nærmere på kode 7700, som indikerer en generell nødsituasjon. Dette kan være alt fra tekniske problemer til medisinske nødssituasjoner om bord.
Søke etter nødkoder i dataene¶
La oss undersøke om det har vært noen nødsituasjoner i våre data. Vi velger en dag fra oktober 2024 hvor vi vet det skjedde noe interessant.
Steg 1: Finn fly med nødkode¶
Først søker vi etter alle fly som har sendt ut squawk-kode 7700:
SELECT DISTINCT registration, MIN(timestamp) as first_seen
FROM mcn.airtraces
WHERE timestamp >= '2024-10-15 00:00:00'
AND timestamp < '2024-10-16 00:00:00'
AND squawk = '7700'
GROUP BY registration
ORDER BY first_seen DESC;┌──────────────┬───────────────────────────┐
│ registration │ first_seen │
│ varchar │ timestamp with time zone │
├──────────────┼───────────────────────────┤
│ 9H-VISTA │ 2024-10-15 17:49:20.89+02 │
└──────────────┴───────────────────────────┘Interessant! Vi har funnet ett fly med aktivert nødkode denne dagen: 9H-VISTA.
Steg 2: Undersøk flyets bevegelser¶
La oss se nærmere på dette flyets posisjon og høyde mens nødkoden var aktiv:
SELECT registration, timestamp, latitude, longitude, altitude_barometric
FROM mcn.airtraces
WHERE timestamp >= '2024-10-15 00:00:00'
AND timestamp < '2024-10-16 00:00:00'
AND squawk = '7700'
AND registration = '9H-VISTA'
ORDER BY timestamp ASC;Dette gir oss en detaljert tidslinje:
┌──────────────┬───────────────────────────┬──────────────┬───────────────┬─────────────────────┐
│ registration │ timestamp │ latitude │ longitude │ altitude_barometric │
│ varchar │ timestamp with time zone │ decimal(9,6) │ decimal(10,6) │ int32 │
├──────────────┼───────────────────────────┼──────────────┼───────────────┼─────────────────────┤
│ 9H-VISTA │ 2024-10-15 17:49:20.89+02 │ 52.066315 │ 4.629288 │ 13675 │
│ 9H-VISTA │ 2024-10-15 17:49:37.06+02 │ 52.087326 │ 4.630280 │ 13400 │
│ 9H-VISTA │ 2024-10-15 17:49:47.49+02 │ 52.100473 │ 4.630973 │ 13225 │
│ 9H-VISTA │ 2024-10-15 17:50:00.16+02 │ 52.116577 │ 4.631882 │ 12975 │
│ 9H-VISTA │ 2024-10-15 17:50:15.5+02 │ 52.135803 │ 4.632874 │ 12725 │
│ 9H-VISTA │ 2024-10-15 17:50:21.35+02 │ 52.142899 │ 4.633179 │ 12625 │
│ 9H-VISTA │ 2024-10-15 17:50:27.08+02 │ 52.149819 │ 4.633562 │ 12525 │
│ 9H-VISTA │ 2024-10-15 17:50:29.1+02 │ 52.152328 │ 4.633636 │ 12450 │
│ 9H-VISTA │ 2024-10-15 17:50:35.88+02 │ 52.160294 │ 4.634018 │ 12350 │
│ 9H-VISTA │ 2024-10-15 17:50:42.86+02 │ 52.168671 │ 4.634476 │ 12225 │
│ 9H-VISTA │ 2024-10-15 17:50:49.86+02 │ 52.177002 │ 4.634857 │ 12100 │
│ 9H-VISTA │ 2024-10-15 17:50:52.75+02 │ 52.180573 │ 4.635010 │ 12050 │
│ 9H-VISTA │ 2024-10-15 17:50:57.83+02 │ 52.186478 │ 4.635239 │ 11975 │
│ 9H-VISTA │ 2024-10-15 17:51:00.72+02 │ 52.189853 │ 4.635446 │ 11900 │
│ 9H-VISTA │ 2024-10-15 17:51:08.42+02 │ 52.198883 │ 4.635849 │ 11775 │
│ 9H-VISTA │ 2024-10-15 17:51:13.89+02 │ 52.205262 │ 4.636152 │ 11675 │
│ 9H-VISTA │ 2024-10-15 17:51:17.25+02 │ 52.209091 │ 4.636383 │ 11625 │
│ 9H-VISTA │ 2024-10-15 17:51:20.93+02 │ 52.213257 │ 4.636612 │ 11575 │
│ 9H-VISTA │ 2024-10-15 17:51:23.22+02 │ 52.216049 │ 4.636688 │ 11525 │
│ 9H-VISTA │ 2024-10-15 17:51:28.36+02 │ 52.221771 │ 4.636993 │ 11450 │
│ · │ · │ · │ · │ · │
│ · │ · │ · │ · │ · │
│ · │ · │ · │ · │ · │
│ 9H-VISTA │ 2024-10-15 18:02:00.02+02 │ 52.454177 │ 4.751430 │ 2150 │
│ 9H-VISTA │ 2024-10-15 18:02:09.19+02 │ 52.447678 │ 4.751434 │ 2150 │
│ 9H-VISTA │ 2024-10-15 18:02:16.18+02 │ 52.442734 │ 4.751205 │ 2150 │
│ 9H-VISTA │ 2024-10-15 18:02:34.97+02 │ 52.429230 │ 4.749756 │ 2100 │
│ 9H-VISTA │ 2024-10-15 18:03:01.13+02 │ 52.411743 │ 4.746933 │ 1775 │
│ 9H-VISTA │ 2024-10-15 18:03:13.31+02 │ 52.404971 │ 4.746015 │ 1600 │
│ 9H-VISTA │ 2024-10-15 18:03:21.83+02 │ 52.400782 │ 4.745544 │ 1500 │
│ 9H-VISTA │ 2024-10-15 18:03:30.95+02 │ 52.396266 │ 4.745466 │ 1425 │
│ 9H-VISTA │ 2024-10-15 18:03:35.59+02 │ 52.393985 │ 4.745466 │ 1400 │
│ 9H-VISTA │ 2024-10-15 18:03:38.76+02 │ 52.392356 │ 4.745466 │ 1375 │
│ 9H-VISTA │ 2024-10-15 18:03:44.88+02 │ 52.389267 │ 4.745407 │ 1300 │
│ 9H-VISTA │ 2024-10-15 18:03:51.76+02 │ 52.385745 │ 4.745152 │ 1275 │
│ 9H-VISTA │ 2024-10-15 18:04:01.75+02 │ 52.380661 │ 4.744644 │ 1150 │
│ 9H-VISTA │ 2024-10-15 18:04:03.5+02 │ 52.379694 │ 4.744524 │ 1125 │
│ 9H-VISTA │ 2024-10-15 18:04:09.18+02 │ 52.376854 │ 4.744289 │ 1075 │
│ 9H-VISTA │ 2024-10-15 18:04:14.69+02 │ 52.374023 │ 4.743958 │ 1025 │
│ 9H-VISTA │ 2024-10-15 18:04:18.63+02 │ 52.372012 │ 4.743740 │ 975 │
│ 9H-VISTA │ 2024-10-15 18:04:26.27+02 │ 52.367935 │ 4.743423 │ 900 │
│ 9H-VISTA │ 2024-10-15 18:04:47.79+02 │ 52.356697 │ 4.742327 │ 675 │
│ 9H-VISTA │ 2024-10-15 18:05:03.25+02 │ 52.348597 │ 4.741699 │ 525 │
├──────────────┴───────────────────────────┴──────────────┴───────────────┴─────────────────────┤
│ 53 rows (40 shown) 5 columns │
└───────────────────────────────────────────────────────────────────────────────────────────────┘Steg 3: Analyser dataene¶
La oss se nærmere på hva disse dataene forteller oss:
Observasjon 1: Synkende høyde
Hvis vi følger kolonnen altitude_barometric, ser vi et tydelig mønster:
Starter på 13 675 fot (ca. 4 170 meter)
Synker gradvis gjennom hele perioden
Ender på kun 525 fot (ca. 160 meter)
Dette indikerer at flyet er i ferd med å lande.
Observasjon 2: Geografisk posisjon
La oss undersøke hvor flyet befinner seg. Kopier de siste koordinatene 52.348597, 4.741699 og lim dem inn i Google Maps.
Funn: Flyet er rett ved Schiphol lufthavn i Amsterdam! Den lave høyden og nødkoden sammen forteller oss at dette mest sannsynlig er en nødlanding.
Hva skjedde egentlig?¶
Med dataene vi nå har samlet, kan vi konkludere:
Fly: 9H-VISTA (registrert i Malta, kjennetegn på «9H»)
Tidspunkt: 15. oktober 2024, rundt kl. 17.49-18.05
Situasjon: Nødkode 7700 aktivert ved ca. 13 675 fot
Utfall: Flyet landet på Schiphol, Amsterdam (høyde sank til 525 fot)
Varighet: Nødkoden var aktiv i ca. 15 minutter
Et kjapt Google-søk forteller oss at dette er et privatfly operert av VistaJet. Flyet landet med nødkoden aktiv 15. oktober 2024 på Schiphol i Amsterdam.
Journalistisk oppfølging¶
Hadde dette vært et fly i Norge, kunne dette vært utgangspunkt for en journalistisk undersøkelse:
Hva var årsaken til nødkoden?
Var det passasjerer om bord?
Hvordan håndterte lufttrafikkontrollen situasjonen?
Er det lignende hendelser med samme operatør eller flytype?
Med ADS-B-data har vi dokumentasjon på eksakt tidspunkt, posisjon og høydeforløp – verdifullt underlagsmateriale for en sak.
Eksport av data¶
Når vi eksporterer data fra DuckDB for videre analyse, er det viktig å velge riktig format basert på hva vi ønsker å gjøre med informasjonen. De tre mest relevante formatene i denne sammenhengen er CSV, JSON og GeoJSON, som hver har sine styrker og svakheter avhengig av bruksområdet.
Oversikt over eksportformater¶
| Format | Beste bruk | Fordeler | Ulemper |
|---|---|---|---|
| CSV | Tabelldata, regneark, enkle grafer | Universelt støttet, lett å åpne i Excel | Mangler støtte for komplekse strukturer |
| JSON | Programmatisk behandling, API-er | Støtter hierarkisk data, fleksibelt | Vanskeligere å lese manuelt |
| GeoJSON | Kartvisualiseringer, geografisk analyse | Perfekt for kart, inneholder geometri | Større filstørrelse |
La oss se på hvert format i detalj.
CSV¶
CSV (Comma-Separated Values) er det mest tilgjengelige formatet og fungerer utmerket når vi ønsker å analysere data i regnearkprogrammer som Excel eller Numbers. Det er et enkelt, tekstbasert format der verdier skilles med komma, noe som gjør det lett å åpne i en rekke verktøy.
Når bruker vi CSV?¶
CSV er spesielt godt egnet til:
Numeriske analyser i regneark
Tabellbasert behandling av data
Grafer og visualiseringer i Everviz eller Datawrapper
Import til Excel eller Google Sheets
Begrensninger¶
CSV har begrensninger når det gjelder å håndtere komplekse datastrukturer eller geografiske koordinater utover enkle latitude/longitude-verdier.
JSON¶
Hvis vi trenger et format som støtter mer strukturert og hierarkisk data, er JSON (JavaScript Object Notation) et bedre valg. JSON er mye brukt i nettbaserte løsninger og API-er, fordi det er lett å tolke i programmeringsspråk som Python og JavaScript.
Når bruker vi JSON?¶
JSON egner seg godt for:
Videre databehandling i programmeringsmiljøer
API-integrasjoner og webutvikling
Komplekse datastrukturer med hierarkiske elementer
Store datasett som skal prosesseres programmatisk
Fordeler og ulemper¶
Til forskjell fra CSV kan JSON håndtere hierarkiske datastrukturer, men det kan være vanskeligere å lese manuelt uten et spesialisert verktøy.
GeoJSON¶
Når vi jobber med geografiske data, er GeoJSON et naturlig valg. Dette formatet er en utvidelse av JSON, spesialtilpasset geospatiale data, slik at det kan lagre informasjon om punkter, linjer, polygoner og andre geografiske objekter.
Når bruker vi GeoJSON?¶
GeoJSON er ideelt for:
Kartvisualiseringer i GeoJSON.io, Datawrapper, eller QGIS
Flytrafikk-visualisering med ruter og traséer
Geografisk analyse med punkter, linjer og områder
GIS-systemer som krever romlige data
Fordeler og ulemper¶
Fordi GeoJSON kan inneholde både attributter og geometriske data i samme fil, er det spesielt nyttig når vi ønsker å visualisere flytrafikk, for eksempel ved å plotte posisjonene til et spesifikt fly i løpet av en dag. Sammenlignet med CSV er GeoJSON mer detaljert, men dette fører også til større filstørrelser og mer kompleksitet.
Praktisk eksempel: Widerøe-nødlanding¶
For å demonstrere de ulike eksportformatene, tar vi utgangspunkt i en faktisk hendelse: En Widerøe-nødlanding på Bergen lufthavn Flesland i juli 2025. Vi skal eksportere dataene i alle tre formater og se hvordan de kan brukes videre.
Flyet vi skal se på:
Registrering: LN-WDI (Widerøe)
ICAO: 4785BF
Hendelse: Nødlanding 11. juli 2025
Tidsrom: 07.40 - 08.09
Eksport til CSV¶
CSV er det enkleste formatet, perfekt for å åpne dataene i Excel eller lage grafer i Everviz eller Datawrapper.
COPY (
SELECT timestamp, icao, registration, longitude, latitude,
altitude_barometric, ground_speed
FROM mcn.airtraces
WHERE timestamp >= '2025-07-11 07:40:00'
AND timestamp < '2025-07-11 08:09:00'
AND icao = '4785BF'
ORDER BY timestamp
) TO 'ln-wdi.csv' WITH (HEADER);Filen ln-wdi.csv dukker nå opp i mappen der du startet DuckDB, og kan åpnes i valgfritt program – for eksempel Excel eller Numbers.
Figur 1: CSV-data åpnet i Numbers på Mac.
Hva kan vi gjøre med CSV-filen?¶
Åpne i Excel/Numbers for manuell analyse
Lage grafer i Datawrapper eller Everviz
Visualisere høyde over tid for å se nødlandingsmønsteret
Beregne hastigheter og andre statistikker
Eksport til JSON¶
La oss prøve det samme med JSON. Det er bare en minimal endring som skal til – vi bytter ut filendelsen:
COPY (
SELECT timestamp, icao, registration, longitude, latitude,
altitude_barometric, ground_speed
FROM mcn.airtraces
WHERE timestamp >= '2025-07-11 07:40:00'
AND timestamp < '2025-07-11 08:09:00'
AND icao = '4785BF'
ORDER BY timestamp
) TO 'ln-wdi.json';
Figur 2: JSON-filen åpnet i teksteditoren Sublime.
JSON Lines-format¶
Når vi åpner JSON-filen i en teksteditor, ser vi at den ikke har standard JSON-format. Dette er JSONL (JSON Lines), et format velegnet for store datamengder i sekvens. Hver linje er et selvstendig JSON-objekt.
Eksempel på innhold:
{"timestamp":"2025-07-11 05:43:21.918+00","icao":"4785BF","registration":"LN-WDI","longitude":5.188293,"latitude":60.345808,"altitude_barometric":2700,"ground_speed":179.2}
{"timestamp":"2025-07-11 05:43:22.449+00","icao":"4785BF","registration":"LN-WDI","longitude":5.188097,"latitude":60.346224,"altitude_barometric":2725,"ground_speed":180.2}
{"timestamp":"2025-07-11 05:43:22.982+00","icao":"4785BF","registration":"LN-WDI","longitude":5.188009,"latitude":60.346712,"altitude_barometric":2725,"ground_speed":180.2}
{"timestamp":"2025-07-11 05:43:23.435+00","icao":"4785BF","registration":"LN-WDI","longitude":5.187901,"latitude":60.34706,"altitude_barometric":2750,"ground_speed":180.2}JSONL er like enkelt å tolke programmatisk som standard JSON, og er mer effektivt for store datasett. Du kan lese mer om formatet på jsonlines.org.
Hva kan vi gjøre med JSON-filen?¶
Importere i Python for videre analyse
Bruke i JavaScript for webvisualiseringer
Prosessere med jq (et kommandolinjeverktøy for JSON)
Sende til API-er for videre behandling
Eksport til GeoJSON¶
For eksport av geografiske data som skal visualiseres på kart, bruker vi GeoJSON. Dette er litt mer avansert fordi vi må konvertere våre geografiske punkter til en linje (LineString) som viser flyets rute.
Enkel GeoJSON-eksport (punkter)¶
La oss først se på den enkleste måten – eksportere hvert datapunkt individuelt:
COPY (
SELECT timestamp, registration, ST_POINT(longitude, latitude) AS geom
FROM mcn.airtraces
WHERE timestamp >= '2025-07-11 07:40:00'
AND timestamp < '2025-07-12 08:09:00'
AND icao = '4785BF'
GROUP BY ALL
ORDER BY timestamp
) TO 'ln-wdi-punkter.geojson' WITH (FORMAT gdal, DRIVER 'GeoJSON');Dette gir oss individuelle punkter på kartet.
Avansert GeoJSON-eksport (linje)¶
For å vise flyets rute som en sammenhengende linje, bruker vi en mer avansert spørring:
COPY (
SELECT registration, ST_MakeLine(ARRAY_AGG(ST_POINT(longitude, latitude) ORDER BY timestamp)) AS geom
FROM mcn.airtraces
WHERE timestamp >= '2025-07-11 07:40:00'
AND timestamp < '2025-07-12 08:09:00'
AND icao = '4785BF'
GROUP BY ALL
) TO 'ln-wdi-linje.geojson' WITH (FORMAT gdal, DRIVER 'GeoJSON');
Figur 3: GeoJSON-filen åpnet i visualiseringsverktøyet GeoJSON.io.
Hva kan vi gjøre med GeoJSON-filen?¶
Visualisere i GeoJSON.io for rask forhåndsvisning
Importere i Everviz eller Datawrapper for profesjonelle kart
Bruke i QGIS for avansert GIS-analyse
Legge i en nettside med Leaflet eller Mapbox
Valg av format: En beslutningstabell¶
Ikke sikker på hvilket format du skal velge? Bruk denne tabellen:
| Jeg vil... | Bruk format |
|---|---|
| Åpne i Excel og lage grafer | CSV |
| Analysere i Python eller JavaScript | JSON |
| Visualisere på kart | GeoJSON |
| Lage en tidsserie-graf (høyde, hastighet) | CSV |
| Vise flyets rute på et kart | GeoJSON (linje) |
| Importere i Datawrapper | CSV (for grafer) eller GeoJSON (for kart) |
| Videre prosessering i R eller Python | JSON eller CSV |
Visualisering av geodata¶
Når dataene er eksportert som CSV, JSON eller GeoJSON, er det enkelt å visualisere dem i grafer eller kart. For grafer anbefaler vi det norske verktøyet everviz, som bygger på teknologien fra Highsoft – et selskap fra Vik i Sogn. Datawrapper er også et godt alternativ. Når vi lager grafer, er CSV det beste utgangsformatet.
GeoJSON.io¶
Hvis du vil visualisere data på et kart, er gratisverktøyet GeoJSON.io et godt sted å starte. La oss teste det ut:
Gå til GeoJSON.io
Klikk på Open, bla til mappen der DuckDB er startet fra, og velg en fil i GeoJSON-format fra datamaskinen din.
Filen lastes inn i grensesnittet, og du kan utforske den nærmere på kartet.

Figur 4: Data visualisert i GeoJSON.io.
Datawrapper¶
Datawrapper er et mye brukt verktøy blant datajournalister for å visualisere data i grafer og kart. Med Datawrapper kan du enkelt importere data i GeoJSON-format og legge til punkter (features) og linjer (linestrings) som et lag over kartet.
Mange norske redaksjoner har tilgang til Datawrapper. Hvis du ikke jobber i en av dem, kan du likevel teste ut funksjonaliteten gratis.
Nå har du fått en grunnleggende innføring i noen av mulighetene som ligger i DuckDB. Ved å kombinere disse funksjonene kan du utføre avanserte søk – begrenset kun av fantasien. La oss prøve noen oppgaver:
Åpne Datawrappers veiviser for kart
Velg Locator Map. Du får nå opp en firestegs prosess.
Under Add markers huker du av for Import line and area markers nederst i skjermbildet.
Klikk Import markers from file, bla til GeoJSON-filen din, og trykk OK.

Figur 5: Last opp GeoJSON-filen i Datawrapper.
Klikk Fit map view to markers for å tilpasse kartet til dataene dine.
For å endre farge på dataene, trykk på den horisontale markøren Add region as area marker.

Figur 6: Endre farge på dataene.
Klikk More options, og kryss av for Show exact line (slower loading time).
Endre eventuelt Marker name.
Når du er fornøyd, trykk Proceed.
I steget Design maps velger du kartdesign og kan aktivere målestokk, utheve en region og mer.
Figur 7: Velg kartdesign.
Når du er fornøyd, trykk Proceed.
I steg tre legger du til tittel, beskrivelse, kilde og eventuelle kommentarer.
Når du er fornøyd, trykk Proceed.

Figur 8: Legg inn tittel, beskrivelse, kilde og eventuelle kommentarer.
Nå er du nesten i mål! På neste skjermbilde klikker du på Publish now. Deretter får du en embed-kode som du kan bruke til å legge kartet inn i en artikkel.
DuckDBs grafiske grensesnitt¶
Når vi nå har trent ferdighetene i kommandolinjearbeid, er det på tide å introdusere en ny og kanskje hakket mer intuitiv måte å jobbe med DuckDB på: DuckDBs grafiske grensesnitt. Dette kjører i nettleseren din, og lar deg organisere spørringene visuelt i celler. Hvorfor startet vi ikke i den enkle enden og introduserte dette med en gang, tenker du kanskje. Det er flere årsaker til det. For det første er DuckDBs grafiske grensesnitt relativt nyutviklet, og ikke helt stabilt. Visse store spørringer kjører og kjører uten å bli ferdig, og statusindikatorene er ikke helt på plass ennå. For det andre introduserer det grafiske grensesnittet noen funksjoner som er litt på siden av kjernen i DuckDB, og som kan virke forvirrende før man har den grunnleggende syntaksen på plass. Og for det tredje er det lettere å gå fra kommandolinjen, når man har konseptene her på plass, til det grafiske grensesnittet enn omvendt.

Figur 9: DuckDBs grafiske grensesnitt.
Oppstart¶
Hvis DuckDB allerede kjører, stopp den først ved å skrive .exit i DuckDBs kommandolinje. Deretter starter vi verktøyet opp igjen med følgende kommando som er nesten helt identisk til den vi har brukt til nå:
duckdb sensorkurs-andre-samling.db --uiDuckDB-kommandolinjen dukker opp i terminalen som tidligere, men nå lastes også nettleseren vår med DuckDBs grafiske grensesnitt. Her kan vi legge inn kommandoer og kjøre dem i sekvens.
Opprett ny notebook¶
Når DuckDBs grensesnitt er lastet, er neste steg å legge til en såkalt Notebook. Dette konseptet skal vi lære mer om senere i kurset, men i denne omgangen er det tilstrekkelig å se på notebooks som mapper hvor du kan lagre spørringer i en rekkefølge og kjøre dem gjentatte ganger.
Du oppretter en ny notebook ved å finne overskriften Notebooks oppe til venstre i grensesnittet, og deretter klikke +. Da åpnes en ny notebook med standardnavnet Untitled Notebook. Denne kan du endre navn på til det du ønsker ved å markere Untitled Notebook i den store kolonnen til høyre fra Notebooks-overskriften, og deretter endre teksten til det du selv ønsker. Lagre ved å trykke Enter.
Legg inn og kjør spørringer i celler¶
Neste steg er å legge inn spørringer i fornuftig store bolker. Dette gjør vi gjennom noe som kalles celler, eller Cells. La oss ta utgangspunkt i de første kommandoene vi kjørte i denne modulen.
Trykk Add Cell-knappen i den høyre kolonnen, og kopier og deretter lim inn følgende spørring i cellen du akkurat opprettet:
-- Installer utvidelser
INSTALL httpfs;
INSTALL postgres;
INSTALL ducklake;
INSTALL spatial;
INSTALL h3 FROM community;Deretter trykker vi Add Cell igjen, og legger inn neste spørring:
-- Last inn utvidelser
LOAD httpfs;
LOAD postgres;
LOAD ducklake;
LOAD spatial;
LOAD h3;Og så neste:
-- Legg inn tilgangsnøkkel
CREATE SECRET (
TYPE R2,
KEY_ID '9030e0f90a86af08b08b6e2a1222a778',
SECRET '2fe64ae1c22869400f577bb9421602f0f81a83a2f658cea6bdd556f4fc65064b',
ACCOUNT_ID 'bca3475a0f4afeb0640daafc17ec2b18'
);Og så neste:
-- Logg på datasjø
ATTACH 'ducklake:postgres:dbname=mcn_ducklake host=ep-fragrant-unit-a20fur0h.eu-central-1.aws.neon.tech user=fra_signaler_til_skup_h2_2025 password=npg_TLH6IjXdAW9q sslmode=require' AS mcn
(DATA_PATH 'r2://mcn-ducklake', READ_ONLY);Notebooks lar oss selv velge om vi vil kjøre cellene én for én mens vi jobber dem frem, eller når alle er skrevet inn. Oppe til venstre i hver celle finner du en play-knapp. Når du trykker på denne, kjører du spørringen i den aktuelle cellen. Prøv å gjøre dette med den øverste cellen i den valgte notebooken.
Du kan også kjøre alle cellene i sekvens, i rekkefølgen topp til bunn. Det gjør du ved å trykke Shift + Enter.
La oss prøve å legge inn og kjøre den aller første dataspørringen vi gjorde i dette kapittelet i en ny celle:
SELECT * FROM mcn.airtraces WHERE timestamp >= '2025-11-01 00:00:00' and timestamp < '2025-11-02 00:00:00';Vi ser en sirkel som spinner, og deretter lastes dataene inn i en grafisk tabell. Stilig! Vi kan nå scrolle nedover i tabellen, og nye data vil vises. Vi kan også scrolle horisontalt, slik at vi får oversikt over alle kolonnene.

Figur 10: Et resultat av en spørring vist i DuckDBs grafiske grensesnitt.
Statistikk om datasettet¶
Hvis du klikker i cellen der du kjørte spørringen over, dukker det opp en kolonne helt til høyre i skjermbildet. Denne inneholder informasjon og statistikk om denne spørringen. Helt øverst ser vi at det står 3,839,743 Rows, 82 Columns, som betyr at resultatet vårt inneholder 3,8 millioner rader og 82 kolonner. I tabellen under kan vi klikke oss inn på hver kolonne i datasettet vårt, og få opp informasjon om distribusjon av verdier, høyeste og laveste verdi (evt. tidligste og seneste verdi der dette er relevant), telling av de vanligste verdiene, med mer. Hvis vi holder musepekeren i ro over et felt, får vi en forklaring på hva vi ser i det aktuelle feltet. Kolonnen helt til høyre i tabellen viser antallet nullverdier i datasettet, som også kan være svært nyttig.
Praktisk bruk av det grafiske grensesnittet¶
DuckDBs grafiske grensesnitt er som nevnt relativt nytt, og flere funksjoner vil bli implementert etter hvert. Men du kan kjøre alle de samme spørringene i grensesnittet som på kommandolinjen, og du kan til og med bruke de to modusene om hverandre. Omfattende spørringer har en tendens til å bli stående og kverne lenge i det grafiske grensesnittet, mens kommandolinjen gjerne er mer effektiv. Dette vil trolig endre seg over tid, men det er uansett ikke noe i veien for å bruke begge modusene om hverandre.
Oppsummering¶
I dette kapittelet har vi sett på DuckDB som et kraftfullt verktøy for å analysere store mengder historiske flydata. La oss oppsummere de viktigste punktene:
Hva er DuckDB?¶
En lettvekts analytisk database som kjører lokalt på datamaskinen din
Optimalisert for raske analyser av store datasett uten behov for kompleks serveroppsett
Bruker SQL (Structured Query Language) som spørrespråk – intuitivt og relativt enkelt å lære
Integreres med Python, R og Excel for videre analyse
Medieklyngens datasjø¶
Data lagres i DuckLake – en datasjø som integrerer tett med DuckDB
Filer i Parquet-format (kolonnebasert lagring) gir 10-100 ganger raskere spørringer enn CSV
Hive-partisjonert etter år/måned/dag for effektiv filtrering
82 kolonner med data per observasjon – fra posisjon til tekniske parametere
Grunnleggende SQL-operasjoner¶
SELECTvelger hvilke kolonner du vil henteFROMangir hvilken tabell du henter data fraWHEREfiltrerer dataene basert på kriterierORDER BYsorterer resultatetLIMITbegrenser antall rader som returneresGROUP BYgrupperer data for aggregering og tellingCOUNT(DISTINCT)teller unike verdierIS NOT NULLfiltrerer bort tomme verdier
Geografiske søkemetoder¶
Punkt og radius:
Søk innenfor en bestemt avstand fra et spesifikt punkt
Bruker
ST_Distance_Sphere()-funksjonenLegg inn breddegrad, lengdegrad og radius i meter
Avgrensningsboks:
Definerer et rektangulært område med fire koordinater
Enkel syntaks med
BETWEENfor lengde- og breddegraderLettlest og intuitivt
H3 (heksagonsystemet):
Deler jordkloden i sekskantede celler med unike ID-er
15 oppløsninger fra region-nivå til meter-presisjon
Raskeste metode for store geografiske analyser
Hierarkisk struktur gjør aggregering enkel
Interessante fartøy¶
Flags-systemet (bitwise):
flags = 1: Militærtflags = 2: Interessantflags = 4: PIA (midlertidig ICAO-nummer)flags = 8: LADD (ønsker ikke offentlig sporing)Kan kombineres:
flags = 12= LADD + PIA
Boolske kolonner (enklere):
is_military,is_interesting,is_pia,is_laddVerdier:
trueellerfalseMer lesbar syntaks:
WHERE is_military
Nødkoder og nødsituasjoner¶
7700: Generell nødsituasjon (kritisk)
7600: Radiokommunikasjon sviktet (alvorlig)
7500: Kapring (kritisk)
Med SQL kan du analysere nødlandinger i detalj – høydefall, posisjon over tid, og varigheten av hendelsen.
Dataeksport og visualisering¶
Tre hovedformater:
CSV – Best for regneark, grafer i Excel/Datawrapper/Everviz
JSON – Best for programmatisk behandling og API-er
GeoJSON – Best for kartvisualiseringer
Verktøy for visualisering:
GeoJSON.io – Rask forhåndsvisning av geografiske data
Datawrapper.de – Profesjonelle kart og grafer for publisering
Everviz.com – Interaktive visualiseringer (norsk verktøy, utviklet i Medieklyngen)
DuckDBs grafiske grensesnitt¶
Kjøres i nettleseren med kommandoen
duckdb filnavn.db --uiOrganiserer spørringer i celler innenfor notebooks
Viser resultater i interaktive tabeller
Gir statistikk og distribusjon for hver kolonne
Kan brukes om hverandre med kommandolinjen
Journalistisk nytte¶
Med DuckDB kan du:
Spore spesifikke fartøy over tid (politihelikoptre, militære fly)
Identifisere nødsituasjoner i historiske data
Finne fly som aktivt prøver å skjule seg fra offentlig sporing
Analysere flytrafikk i bestemte geografiske områder
Dokumentere hendelser med millisekundpresisjon
Kombinere flere datakilder for dypere innsikt
Aggregere data for statistiske analyser
Viktige læringspunkter¶
Start enkelt: Filtrer på tid og ICAO-nummer før du går videre til komplekse spørringer
Bruk riktig metode: H3 for store analyser, punkt-og-radius for spesifikke undersøkelser
Velg riktig format: CSV for grafer, GeoJSON for kart
Dokumenter alt: SQL-spørringene dine er reproduserbare og transparente
Tenk journalistisk: Dataene er råmateriale – historien ligger i tolkningen
Kombiner verktøy: Bruk både kommandolinjen og det grafiske grensesnittet
Med denne kunnskapen i bagasjen er du klar til å utforske lufttrafikken og avdekke historier som ligger gjemt i millioner av datapunkter!