#!/usr/bin/perl # Make a KML file of my nearby air routes. # Copyright : http://www.fsf.org/copyleft/gpl.html # Author : Dan Jacobson -- https://www.jidanni.org/comm/air/m750/programs/ # Created On : Thu Mar 7 07:14:40 2013 # Last Modified On: Wed Sep 18 19:37:34 2019 # Update Count : 271 use strict; use warnings FATAL => 'all'; use XML::LibXML; eval { require Carp::Always; Carp::Always->import(); }; ## http://eaip.caa.gov.tw/eaip/history/2019-08-29/html/eAIP/RC-ENR-3.3-en-TW.html#ID_938306 my $MetersPerFoot = 0.3048; my @dan = ( 120.865795, 24.181546 ); # My four corner junction my ( %route, %seen_names ); while (<>) { next if /^#/; my @l = split; $l[1] =~ s/^/0/; #DDDMMSS[.SS] my $r = $ARGV; $r =~ s!.*/!!; $r =~ s/\..*//; push @{ $route{$r}{name} }, shift @l; my $sign; my @cd; for (@l) { s/.$//; if ( $& eq q!S! || $& eq q!W! ) { $sign = -1 } elsif ( $& eq q!N! || $& eq q!E! ) { $sign = 1 } else { die "$& ugly" } my @t = unpack "A3A2A*"; push @cd, $sign * ( ( shift @t ) + ( shift @t ) / 60 + ( shift @t ) / 60 / 60 ); } push @{ $route{$r}{coord} }, [ reverse @cd ]; } for ( my $FL = 290 ; $FL <= 410 ; $FL += 20 ) { for ( keys %route ) { push @{ $route{$_}{FL} }, $FL; } ## Even though some go lower, and have southbound (even numbered) FLs too. } my $doc = XML::LibXML::Document->new( '1.0', "UTF-8" ); my $kml = $doc->createElementNS( "http://www.opengis.net/kml/2.2", "kml" ); $kml->setAttribute( "xmlns:gx", "http://www.google.com/kml/ext/2.2" ); my $comment = $doc->createComment("MADE BY $ENV{PWD}/$0, WILL GET OVERWRITTEN."); $kml->appendChild($comment); my $Document = $doc->createElement('Document'); $kml->appendChild($Document); $doc->setDocumentElement($kml); for ($Document) { my $name = $doc->createElement('name'); $name->appendText("近航線 Nearby air routes"); $_->appendChild($name); my $description = $doc->createElement('description'); $description->appendText("https://www.jidanni.org/comm/air/m750"); $_->appendChild($description); my $open = $doc->createElement('open'); $_->appendChild($open); $open->appendText(1); my $Style = $doc->createElement('Style'); $Style->setAttribute( 'id', 0 ); my $LineStyle = $doc->createElement('LineStyle'); $Style->appendChild($LineStyle); my $glv = $doc->createElement('gx:labelVisibility'); $glv->appendText(1); $LineStyle->appendChild($glv); $_->appendChild($Style); } for ( $doc->createElement('Folder') ) { my $name = $doc->createElement('name'); $_->appendChild($name); $name->appendText("景 Views"); my $open = $doc->createElement('open'); $_->appendChild($open); $open->appendText(1); $Document->appendChild($_); for my $azimuth ( 30, 230 ) { my $Placemark = $doc->createElement('Placemark'); $_->appendChild($Placemark); $name = $doc->createElement('name'); $name->appendText("$azimuth degrees"); $Placemark->appendChild($name); for ( $doc->createElement('Camera') ) { $Placemark->appendChild($_); my $longitude = $doc->createElement("longitude"); $_->appendChild($longitude); $longitude->appendText( $dan[0] ); my $latitude = $doc->createElement('latitude'); $_->appendChild($latitude); $latitude->appendText( $dan[1] ); my $altitude = $doc->createElement('altitude'); $_->appendChild($altitude); $altitude->appendText(2); my $heading = $doc->createElement('heading'); $_->appendChild($heading); $heading->appendText($azimuth); my $tilt = $doc->createElement('tilt'); $_->appendChild($tilt); $tilt->appendText(90); } my $Point = $doc->createElement('Point'); $Placemark->appendChild($Point); my $coordinates = $doc->createElement('coordinates'); $Point->appendChild($coordinates); $coordinates->appendText( join ",", @dan ); } } for my $r ( sort keys %route ) { for ( $doc->createElement('Folder') ) { my $name = $doc->createElement('name'); $_->appendChild($name); $name->appendText($r); $Document->appendChild($_); my $Folder = $doc->createElement('Folder'); $_->appendChild($Folder); $name = $doc->createElement('name'); $name->appendText('線 Lines'); $Folder->appendChild($name); for my $FL ( @{ $route{$r}{FL} } ) { my $Placemark = $doc->createElement('Placemark'); my $name = $doc->createElement('name'); $name->appendText("$r:FL$FL"); $Placemark->appendChild($name); my $LineString = $doc->createElement('LineString'); $Placemark->appendChild($LineString); my $styleUrl = $doc->createElement('styleUrl'); $styleUrl->appendText("#0"); $Placemark->appendChild($styleUrl); my $altitudeMode = $doc->createElement('altitudeMode'); $altitudeMode->appendText('absolute'); $LineString->appendChild($altitudeMode); my $coordinates = $doc->createElement('coordinates'); my @c; for ( @{ $route{$r}{coord} } ) { push @c, sprintf "%.5f,%.5f,%.0f", @$_, $FL * 100 * $MetersPerFoot; } $coordinates->appendText("@c"); $LineString->appendChild($coordinates); $Folder->appendChild($Placemark); } $Folder = $doc->createElement('Folder'); $_->appendChild($Folder); $name = $doc->createElement('name'); $name->appendText('點 Points'); $Folder->appendChild($name); for ( 0 .. $#{ $route{$r}{name} } ) { my $Placemark = $doc->createElement('Placemark'); my $name = $doc->createElement('name'); my $n = @{ $route{$r}{name} }[$_]; $name->appendText($n); $Placemark->appendChild($name); if ( exists $seen_names{$n} ) { my $visibility = $doc->createElement('visibility'); $Placemark->appendChild($visibility); $visibility->appendText(0); } $seen_names{$n}++; my $Point = $doc->createElement('Point'); $Placemark->appendChild($Point); my $extrude = $doc->createElement('extrude'); $extrude->appendText(1); $Point->appendChild($extrude); my $altitudeMode = $doc->createElement('altitudeMode'); $altitudeMode->appendText('absolute'); $Point->appendChild($altitudeMode); my $coordinates = $doc->createElement('coordinates'); $Point->appendChild($coordinates); $coordinates->appendText( sprintf "%.5f,%.5f,%.0f", @{ @{ $route{$r}{coord} }[$_] }, @{ $route{$r}{FL} }[-1] * 100 * $MetersPerFoot ); $Folder->appendChild($Placemark); } } } print $doc->toString(1); # Local Variables: # compile-command: "./3d a/M750.dms a/B1.dms a/W4.dms | tee /tmp/k.kml # ../m750_3d.kml" # End: