Editorial Workflows

Lido2MapsmeWorkflow

public workflow

Install Workflow...

This workflow contains at least one Python script. Only use it if you trust the person who shared this with you, and if you know exactly what it does.

I understand, install the workflow!

This is a workflow for Editorial, a Markdown and plain text editor for iOS. To download it, you need to view this page on a device that has the app installed.

Description: workflow utilisés par l'app Workflow pour convertir le pdf Lido smartsuite en plotting pour MapsMe

https://workflow.is/workflows/85c227da1cd94357978bb974c4bb1407

Version 1.1 : mise a jour regex pour prendre en compte la syntaxe utilisée pour les waypoint random.

Version 1.2 :
- Tracé des tracks si présent dans OFP
- Exécute le workflow Lido2mPilot, le clipboard contiendra donc les données pour mPilot.

Version 1.3:
Ajout du tracé de l'ortho pour mieux visualiser à quoi correspond le gramet ogimet

Version 1.4:
Correction pour rlatsm

Version 1.5:
Correction rlatsm et nouveau msg tracks

Comments: Comment Feed (RSS)

There are no comments yet.

+ Add Comment

Workflow Preview
Set Variable ?
Variable Name
rawInput
Value
Input
Sub-Workflow ?
Workflow
Can Stop Main Workflow
OFF
Generate Text ?
Text
rawInput
Run Python Script ?
Source Code
#coding: utf-8
# version 1.5
import itertools
import math
import re
import sys
from decimal import Decimal
try:
	import workflow
	action_in = workflow.get_input()
except ImportError:
	# not running in Editorial
	action_in = """
retrieved: 27Mar/0429zAF  009  KJFK/LFPG  27Mar2015/0545z3OFP 9/0/1Main OFP (Long copy #1)LFPG/27R      206           ..../....                     44.8/7.3              206TSUITABLE DESTINATION ALTERNATE SUMMARY      ALTN      DIST  LVL   WC TIME   FUEL VIA      LFPO/26     82  100 T003 0020   2530 OLDGT INFO/LFQQ/26    128  140 H010 0029   3520 NURMO9A NURMO N874 CMB                                      P990 CMB1B INFO/EGKK/26L   177  200 H035 0038   4670 OPALE9A OPALE UT421 KUNAV                                     P2140 TIMBA3B INFO/EBBR/25L   195  260 H011 0038   4710 NURMO9A NURMO UN874 CMB                                     P2180 UZ373 ARVOL ARVOL6ADESTINATION ALTERNATE LFPO--------------------------------------------------------------------WAYPNT   RTE- MAG         M ETO /ATO   FL TP OAT WIND     CONS/EFOB   AWY   MORA TRU  DIS   GS ETE /TTE--------------------------------------------------------------------LFPG/27R      200           ..../.... CLB     -2 294/017       5.6           22 200T 0011     0003/0003                      .../...T-O-C         200       533 ..../.... 100 38  -6 307/029   1.3/4.3           22 200T 0027 349 0005/0008                      .../...T-O-D         200           ..../.... DSC 38  -5 305/027   1.8/3.8           22 200T 0004     0001/0008                      .../...OLDGT         018           ..../.... DSC     +0 288/014   1.8/3.7           22 019T 0041     0012/0020                      .../...LFPO/26       018           ..../....                      2.5/3.0              019TWPT COORDINATESKJFK  N4038.4W07346.7  GREKI N4128.8W07318.8  MARTN N4234.9W07316.2EBONY N4454.1W06709.4  ALLRY N5030.0W05200.0        N5100.0W05000.0      N5300.0W04000.0        N5500.0W03000.0        N5500.0W02000.0RESNO N5500.0W01500.0  NETKI N5500.0W01400.0  BAKUR N5214.5W00540.8STU   N5159.7W00502.4  NUMPO N5136.6W00317.0  OKESI N5126.6W00203.7BEDEK N5122.3W00133.5  NIGIT N5118.8W00110.3  VAPID N5115.2W00102.7MID   N5103.2W00037.5  SFD   N5045.6E00007.3  WAFFU N5035.0E00021.0HARDY N5028.3E00029.5  XIDIL N5021.1E00038.5  PETAX N5011.2E00050.9BIBAX N5003.5E00100.5  KOLIV N4918.0E00134.2  MOPAR N4917.5E00145.4PG534 N4915.7E00223.3  CRL   N4915.3E00230.9  PG536 N4913.9E00242.9LFPG  N4900.6E00232.9--------------------------------------------------------------------LFPG  N4900.6E00232.9  OLDGT N4825.8E00213.8  LFPO  N4843.4E00222.8ATC FLIGHT PLANFF KZNYZQZX KNYCZZZX CZQMZQZX CZQMZQZR CZQXZQZX EUCHZMFP EUCBZMFP270425 LFPGYEYX(FPL-AFR009-IS-B77W/H-SDE2E3FGHIJ3J5J6M1M2RWXY/LB1D1-KJFK0545-N0476F350 DCT GREKI DCT MARTN DCT EBONY/M084F350 N247A ALLRY/M084F370 DCT 51N050W 53N040W 55N030W 55N020W DCT RESNO DCT NETKI/N0479F350 DCT BAKUR/N0463F350 UN546 STU UP2 NIGIT UL18 SFD/N0414F250 UM605 BIBAX BIBAX7W-LFPG0554 LFPO-PBN/A1B1C1D1L1S1 DOF/150327 REG/FGSQB EET/KZBW0004 CZQM0047 CZQX0119 ALLRY0156 51N050W0205 53N040W0247 EGGX0328 55N020W0403 RESNO0420 NETKI0423 EISN0432 EGTT0457 LFFF0526 SEL/HPLM OPR/AFR RALT/CYQX EINN RVR/100 RMK/ACAS)Generated a
t 27Mar/0433zPage 10 of
retrieved: 27Mar/0429zAF  009  KJFK/LFPG  27Mar2015/0545z3OFP 9/0/1Main OFP (Long copy #1)TRACKSNAT WESTBND TRACKS FL 310/390 INCLUSIVE 27MAR1130-27MAR1900Z* * * * * * * * * * * * * * * * * * * * * * * * * * * * * *Generated at 27Mar/0433zPage 11 ofA   RESNO 56N020W 57N030W 58N040W 58N050W CUDDY   LVLS WB 310 320 330 340 350 360 370 380 390   LVLS EB NIL   EUR RTS WEST NIL   NAR NIL B   DOGAL 55N020W 56N030W 57N040W 57N050W HOIST   LVLS WB 310 320 330 340 350 360 370 380 390   LVLS EB NIL   EUR RTS WEST NIL   NAR NIL C   MALOT 54N020W 55N030W 56N040W 56N050W JANJO   LVLS WB 310 320 330 340 350 360 370 380 390   LVLS EB NIL   EUR RTS WEST NIL   NAR NIL D   SOMAX 50N020W 49N030W 48N040W 45N050W VODOR   LVLS WB 310 320 330 350 360 370 390   LVLS EB NIL   EUR RTS WEST NIL   NAR NIL E   BEDRA 49N020W 48N030W 47N040W 44N050W BOBTU JAROM   LVLS WB 310 320 330 350 360 370 390   LVLS EB NIL   EUR RTS WEST NIL   NAR NIL F   OMOKO 48N015W 48N020W 47N030W 46N040W 43N050W JEBBY CARAC   LVLS WB 310 320 330 350 360 370 390   LVLS EB NIL   EUR RTS WEST NIL   NAR NIL G   ETIKI 47N015W 47N020W 46N030W 45N040W 42N050W 42N060W DOVEY   LVLS WB 310 320 330 350 360 370 390   LVLS EB NIL   EUR RTS WEST VIA REGHI   NAR NIL H   43N040W 41N050W 41N060W JOBOC   LVLS WB 330 350 370 390   LVLS EB NIL   EUR RTS WEST NIL   NAR NIL J   42N040W 38N050W 33N060W NUMBR   LVLS WB 320 340 360 380   LVLS EB NIL   EUR RTS WEST NIL   NAR NIL NOTES:1. TMI IS 086 AND OPERATORS ARE REMINDED TO INCLUDE THETMI NUMBER AS PART OF THE OCEANIC CLEARANCE READ BACK.2. ADS-C AND CPDLC MANDATED OTS ARE AS FOLLOWSTRACK A 350 360 370 380 390TRACK B 350 360 370 380 390TRACK C 350 360 370 380 390TRACK D 350 360 370 390TRACK E 350 360 370 390TRACK F 350 360 370 390TRACK G 350 360 370 390END OF ADS-C AND CPDLC MANDATED OTS3. FOR STRATEGIC LATERAL OFFSET AND CONTINGENCY PROCEDURES RELATEDTO OPS IN NAT FLOW PLEASE REFER TO THE NAT PROGRAMME COORDINATIONWEB SITE AT WWW.NAT PCO.ORG. SLOP SHOULD BE USED AS A STANDARD
"""
# print action_in
# filter the main route part
try:
	main = action_in.split('WPT COORDINATES', 1)[1]
except IndexError:
	print "WPT COORDINATES not found"
	print "retry or send OFP to Yammer's group Maps.me"
	sys.exit()

try:
	main = main.split('----', 1)[0]
except IndexError:
	pass  # will process till the end of file

# all coordinates for main route regex
# returns a list of tuples (name, lat, long)
# lat and long are in text format NSEW prefixed, in degree/minute format
m = re.findall(r'(\S+|\s+)\s+([NS]\d{4}\.\d)([EW]\d{5}\.\d)', main)

def dm2dec(coord):
	"""convert lido coordinates to decimal"""
	sign = 1 if coord[0] in ('N', 'E') else -1
	offset = 3 if coord[0] in ('N', 'S') else 4
	degrees = Decimal(coord[1:offset])
	minutes = Decimal(coord[offset:])
	return sign * (degrees + minutes/60)

# coordinates suitable for kml (name, long, lat)
main_coordinates = [(t[0],  # name
					str(dm2dec(t[2])),  # long as str
					str(dm2dec(t[1])))  # lat as str
					for t in m]

# Optional tracks put as a list in natmarks variable
natmarks = []

def waypoint_todec(wpt):
	"""convert Arinc track waypoint to decimal point usable by maps.me"""
	def signed(letter, lon, lat):
		# W=S=- E=N=+
		if letter == 'N':  # NW
			return -lon, lat
		elif letter == 'E':  # NE
			return lon, lat
		elif letter == 'S':  # SE
			return lon, -lat
		elif letter == 'W':  # SW
			return -lon, -lat
		else:
			raise ValueError('unknow letter %s in %s' % (letter, wpt))

	if wpt[0] in 'NESW':
		# N5520  lon<100
		lat = Decimal(wpt[1:3]) + Decimal(0.5)
		lon = Decimal(wpt[3:5])
		return signed(wpt[0], lon, lat)
	elif wpt[1] in 'NESW':
		# 5N520  lon>=100
		lat = Decimal(wpt[0] + wpt[2]) + Decimal(0.5)
		lon = Decimal('1' + wpt[3:5])
		return signed(wpt[1], lon, lat)
	elif wpt[4] in 'NS':
		# 5530N020W => N5530.0W02000.0 => (55.5, -20)
		lat = dm2dec(wpt[4] + wpt[0:4] + '.0')
		lon = dm2dec((wpt[-1] + wpt[5:-1] + '00')[:5] + '.0')
		return lon, lat
	else:
		# 55N020W => N5500.0W02000.0 => (-20.0, 55.0)
		lat = dm2dec(wpt[2] + wpt[0:2] + '00.0')
		lon = dm2dec(wpt[6] + wpt[3:6] + '00.0')
		return lon, lat

def tracks_coordinates(text):
	"""extracts track geographic waypoint, returns a list of decimal (lon,lat)
	"""
	text = text.split('LVLS')[0]
	m = re.findall(r'(\d{2,4}[NS]\d{3,5}[EW]|[NESW]\d{4}|\d[NESW]\d{3}[^EW])\s', text)
	output = []
	for wpt in m:
		output.append(waypoint_todec(wpt))
	return output
nat_tpl ="""
			<Placemark>
				<name>NAT {track_id}</name>
				<styleUrl>{style}</styleUrl>
				<description>{description}</description>
				<LineString>
					<tessellate>1</tessellate>
					<coordinates>{coordinates}</coordinates>
				</LineString>
			</Placemark>
			{nat_label}
"""
nat_label_tpl = """
			<Placemark>
				<name>NAT {track_id}</name>
				<styleUrl>{style}</styleUrl>
				<description>{description}</description>
				<Point>
					<coordinates>{coordinates}</coordinates>
				</Point>
			</Placemark>
"""

# find the optional TRACKS part
try:
	s = action_in.split('TRACKSNAT', 1)[1]
	s = s.split('NOTES:', 1)[0]
	if 'REMARKS' in s:
		s = s.split('REMARKS:', 1)[0]
		s = s.split('Generated at')[0]
	if ' LVLS ' in s:
		it = iter(re.split(r'(?:\s|[^A-Z])([A-Z])\s{3}', s)[1:])
		tracks = zip(it, it)
	else:
		def updated_mar2016_generator():
			# Letter is lost in the middle
			# track route starts with something like ELSIR 50
			l = [m.start() for m in re.finditer('[A-Z]{5} \d\d', s)]
			for start, end in itertools.izip_longest(l, l[1:]):
				t = s[start:end]
				# letter is here
				parts = re.split('([A-Z])LVLS', t)
				# adds some missing spaces
				parts[2] = parts[2].replace('LVLS', ' LVLS').replace('NIL', 'NIL ')
				yield parts[1], "%s LVLS%s" % (parts[0], parts[2])
		tracks = list(updated_mar2016_generator())
except IndexError:
	tracks =[]
# render natmarks
# and keep track of all point having a nat_label
nat_labels = []
for t in tracks:
	try:
		coordinates = tracks_coordinates(t[1])
		coordinates_text = " ".join(["%s,%s" %c for c in coordinates])
		nat_label = ''
		if coordinates:
			lon, lat = coordinates[0]  # coordinates[-1] for exit point
			nat_labels.append((str(lon), str(lat)))
			nat_label = nat_label_tpl.format(
				style="#placemark-yellow",
				track_id=t[0],
				description=t[1],
				coordinates="%s,%s" % (lon, lat))
		# render the track line
		natmarks.append(nat_tpl.format(
			style="#rnat",
			track_id=t[0],
			description=t[1],
			nat_label=nat_label,  # or '' to remove bbthe label
			coordinates=coordinates_text))
	except ValueError:
		print "Error while building track %s: input was %s" % t
		print "Please send OFP to Yammer's group Maps.me"

# rendering of all route placemarks in a list
# we skip all points having already a nat_label set
main_tpl = """
			<Placemark>
				<name>{name}</name>
				<styleUrl>{style}</styleUrl>
				<description>{description}</description>	
				<Point>      
					<coordinates>{coordinates}</coordinates>
				</Point>
			</Placemark>
"""
main_placemarks = []
for c in main_coordinates:
	if c[1:] in nat_labels:
		continue
	main_placemarks.append(
		main_tpl.format(
			style="#placemark-purple",name=c[0],
			description=c[0],
			coordinates=",".join(c[1:])))

def splitgeodesics(longitude1,latitude1,longitude2,latitude2,num_of_segments='auto'):
	if num_of_segments == 'auto':
		num_of_segments = int(math.ceil(max(
			abs(longitude1 - longitude2), abs(latitude1 - latitude2))/5))
	ptlon1 = longitude1
	ptlat1 = latitude1
	ptlon2 = longitude2
	ptlat2 = latitude2

	numberofsegments = num_of_segments
	onelessthansegments = numberofsegments - 1
	fractionalincrement = (1.0/onelessthansegments)

	ptlon1_radians = math.radians(ptlon1)
	ptlat1_radians = math.radians(ptlat1)
	ptlon2_radians = math.radians(ptlon2)
	ptlat2_radians = math.radians(ptlat2)

	distance_radians=2*math.asin(math.sqrt(math.pow((math.sin((ptlat1_radians-ptlat2_radians)/2)),2) + math.cos(ptlat1_radians)*math.cos(ptlat2_radians)*math.pow((math.sin((ptlon1_radians-ptlon2_radians)/2)),2)))
	# 6371.009 represents the mean radius of the earth
	# shortest path distance
	distance_km = 6371.009 * distance_radians

	mylats = []
	mylons = []

	# write the starting coordinates
	mylats.append([])
	mylons.append([])
	mylats[0] = ptlat1
	mylons[0] = ptlon1 

	f = fractionalincrement
	icounter = 1
	while (icounter <  onelessthansegments):
	        icountmin1 = icounter - 1
	        mylats.append([])
	        mylons.append([])
	        # f is expressed as a fraction along the route from point 1 to point 2
	        A=math.sin((1-f)*distance_radians)/math.sin(distance_radians)
	        B=math.sin(f*distance_radians)/math.sin(distance_radians)
	        x = A*math.cos(ptlat1_radians)*math.cos(ptlon1_radians) + B*math.cos(ptlat2_radians)*math.cos(ptlon2_radians)
	        y = A*math.cos(ptlat1_radians)*math.sin(ptlon1_radians) +  B*math.cos(ptlat2_radians)*math.sin(ptlon2_radians)
	        z = A*math.sin(ptlat1_radians) + B*math.sin(ptlat2_radians)
	        newlat=math.atan2(z,math.sqrt(math.pow(x,2)+math.pow(y,2)))
	        newlon=math.atan2(y,x)
	        newlat_degrees = math.degrees(newlat)
	        newlon_degrees = math.degrees(newlon)
	        mylats[icounter] = newlat_degrees
	        mylons[icounter] = newlon_degrees
	        icounter += 1
	        f = f + fractionalincrement

	# write the ending coordinates
	mylats.append([])
	mylons.append([])
	mylats[onelessthansegments] = ptlat2
	mylons[onelessthansegments] = ptlon2
	return zip(mylons, mylats)

# kml file template
# copied from www.notreavion.net/convert/
# kml does not support inline base 64 image, may be we should create
# a kmz file instead with local copy of images 
xml_tpl = """<?xml version='1.0' encoding='UTF-8'?>
<kml xmlns='http://www.opengis.net/kml/2.2' xmlns:gx='http://www.google.com/kml/ext/2.2' xmlns:kml='http://www.opengis.net/kml/2.2' xmlns:atom='http://www.w3.org/2005/Atom'>
	<Document>
		<name>{route_name}</name>
		<Style id='rnat'>
			<LineStyle>
				<color>60DA25A8</color>
				<width>1</width>
			</LineStyle>
		</Style>
		<Style id='no_icone'>
			<IconStyle>
				<Icon>
					<href>http://www.notreavion.net/convert/images/no_icon.png</href>
				</Icon>
				<hotSpot x='0.5' y='0.0' xunits='fraction' yunits='fraction' />
			</IconStyle>
		</Style>
		<Style id='placemark-purple'>
			<IconStyle>
				<Icon>
					<href>http://www.notreavion.net/convert/images/icone_route.png</href>
				</Icon>
				<hotSpot x='0.5' y='0.5' xunits='fraction' yunits='fraction' />
			</IconStyle>
		</Style>
		<Style id='placemark-pink'>
			<IconStyle>
				<Icon>
					<href>http://www.notreavion.net/convert/images/icone_deg.png</href >
				</Icon>
				<hotSpot x='0.5' y='0.5' xunits='fraction' yunits='fraction' />
			</IconStyle>
		</Style>
		<Style id='rmain'>
			<LineStyle>
				<color>FFDA25A8</color>
				<width>3</width>
			</LineStyle>
			<PolyStyle>
				<color>FFDA25A8</color>
			</PolyStyle>
		</Style>
		<Style id='great_circle'>
			<LineStyle>
			<color>5000FFFF</color>
				<width>3</width>
			</LineStyle>
			<PolyStyle>
				<color>5000FFFF</color>
			</PolyStyle>
		</Style>

		<Folder>
			<name>Lignes</name>
			<open>1</open>
			{natmarks}	
			<Placemark>
				<name>Rmain</name>
				<styleUrl>#rmain</styleUrl>
				<LineString>
					<tessellate>1</tessellate>
					<coordinates>{main_coordinates}</coordinates>
				</LineString>
			</Placemark>
			<Placemark>
				<name>{great_circle_name}</name>
				<styleUrl>#great_circle</styleUrl>
				<LineString>
				<tessellate>1</tessellate>
				<coordinates>{great_circle_coordinates}</coordinates>
				</LineString>
			</Placemark>
		</Folder>

		<Folder>
			{placemarks}
		</Folder>
	</Document>
</kml>
"""
try:
	great_circle_coordinates = splitgeodesics(
    float(main_coordinates[0][1]), float(main_coordinates[0][2]),
    float(main_coordinates[-1][1]), float(main_coordinates[-1][2]))
except:
	great_circle_coordinates = []

action_out = xml_tpl.format(
	route_name="%s-%s" % (main_coordinates[0][0], main_coordinates[-1][0]),
	great_circle_name='Orthdromie %s-%s' % (main_coordinates[0][0], main_coordinates[-1][0]),
	great_circle_coordinates=" ".join(["%s,%s" % c for c in great_circle_coordinates]),
	main_coordinates=" ".join(["%s,%s" % c[1:] for c in main_coordinates]),
	natmarks="\n".join(natmarks),
	placemarks="\n".join(main_placemarks))
try:
	workflow.set_output(action_out)
except NameError:
	# not running in Editorial
	print action_out
Set File Contents ?
File Name
lido2mapsme.kml
In Dropbox
OFF
New Text
Input
If File Does Not Exist
  • Create
  • Stop Workflow
Open Document ?
File Name
lido2mapsme.kml
In Dropbox
OFF
Run Python Script ?
Source Code
#coding: utf-8
import editor
import console

# show the open with dialog

p = editor.get_path()
console.open_in(p)