Skip to content

Instantly share code, notes, and snippets.

@baylej
Last active August 29, 2015 14:21
Show Gist options
  • Save baylej/31cf3bd3ee49dc250756 to your computer and use it in GitHub Desktop.
Save baylej/31cf3bd3ee49dc250756 to your computer and use it in GitHub Desktop.
Python script to export paragliding sites in France to a KML file (for Google Earth)
# -*- coding: utf-8 -*-
code_to_name = dict({
'01': "Ain",
'02': "Aisne",
'03': "Allier",
'04': "Alpes-de-Haute-Provence",
'05': "Hautes-Alpes",
'06': "Alpes-Maritimes",
'07': "Ardèche",
'08': "Ardennes",
'09': "Ariège",
'10': "Aube",
'11': "Aude",
'12': "Aveyron",
'13': "Bouches-du-Rhône",
'14': "Calvados",
'15': "Cantal",
'16': "Charente",
'17': "Charente-Maritime",
'18': "Cher",
'19': "Corrèze",
'2A': "Corse-du-Sud",
'2B': "Haute-Corse",
'20': "Corse",
'21': "Côte-d'Or",
'22': "Côtes-d'Armor",
'23': "Creuse",
'24': "Dordogne",
'25': "Doubs",
'26': "Drôme",
'27': "Eure",
'28': "Eure-et-Loir",
'29': "Finistère",
'30': "Gard",
'31': "Haute-Garonne",
'32': "Gers",
'33': "Gironde",
'34': "Hérault",
'35': "Ille-et-Vilaine",
'36': "Indre",
'37': "Indre-et-Loire",
'38': "Isère",
'39': "Jura",
'40': "Landes",
'41': "Loir-et-Cher",
'42': "Loire",
'43': "Haute-Loire",
'44': "Loire-Atlantique",
'45': "Loiret",
'46': "Lot",
'47': "Lot-et-Garonne",
'48': "Lozère",
'49': "Maine-et-Loire",
'50': "Manche",
'51': "Marne",
'52': "Haute-Marne",
'53': "Mayenne",
'54': "Meurthe-et-Moselle",
'55': "Meuse",
'56': "Morbihan",
'57': "Moselle",
'58': "Nièvre",
'59': "Nord",
'60': "Oise",
'61': "Orne",
'62': "Pas-de-Calais",
'63': "Puy-de-Dôme",
'64': "Pyrénées-Atlantiques",
'65': "Hautes-Pyrénées",
'66': "Pyrénées-Orientales",
'67': "Bas-Rhin",
'68': "Haut-Rhin",
'69': "Rhône",
'70': "Haute-Saône",
'71': "Saône-et-Loire",
'72': "Sarthe",
'73': "Savoie",
'74': "Haute-Savoie",
'75': "Paris",
'76': "Seine-Maritime",
'77': "Seine-et-Marne",
'78': "Yvelines",
'79': "Deux-Sèvres",
'80': "Somme",
'81': "Tarn",
'82': "Tarn-et-Garonne",
'83': "Var",
'84': "Vaucluse",
'85': "Vendée",
'86': "Vienne",
'87': "Haute-Vienne",
'88': "Vosges",
'89': "Yonne",
'90': "Territoire de Belfort",
'91': "Essonne",
'92': "Hauts-de-Seine",
'93': "Seine-Saint-Denis",
'94': "Val-de-Marne",
'95': "Val-d'Oise",
'97': "Département d'Outre-Mer",
'971': "Guadeloupe",
'972': "Martinique",
'973': "Guyane",
'974': "La Réunion",
'975': "Saint-Pierre-et-Miquelon",
'976': "Mayotte",
'977': "Saint-Barthélemy",
'978': "Saint-Martin",
'98': "Collectivités d'Outre-Mer",
'986': "Wallis-et-Futuna",
'987': "Polynésie Française",
'988': "Nouvelle-Calédonie"
})
# -*- coding: utf-8 -*-
from urllib.request import urlopen
from xml.parsers import expat
import france_departements
class KMLBuilder:
""" Builds the KML document. """
__spc_c = 2
__spc_l = 2
__is_built = False
__document = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
def __init__(self, name):
self.__document += "<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\">\n"
self.__document += "<Document>\n"
self.__document += self.__spc_c*' ' + "<name>" + name +"</name>\n"
def enter_folder(self, name, desc=None):
self.__chk_built()
self.__document += self.__spc_c*' ' + "<Folder>\n"
self.__spc_c += self.__spc_l
self.__document += self.__spc_c*' ' + "<name>" + name + "</name>\n"
if desc is not None:
self.__document += self.__spc_c*' ' + " <description>" + desc + "</description>\n"
def close_folder(self):
self.__chk_built()
self.__spc_c -= self.__spc_l
self.__document += self.__spc_c*' ' + "</Folder>\n"
def mk_placemark(self, name, lon, lat, alt, desc=None):
self.__chk_built()
self.__document += self.__spc_c*' ' + "<Placemark>\n"
self.__document += self.__spc_c*' ' + " <name>" + name + "</name>\n"
self.__document += self.__spc_c*' ' + " <Point>\n"
self.__document += self.__spc_c*' ' + " <coordinates>" + str(lon) + ',' + str(lat) +',' + str(alt) + "</coordinates>\n"
self.__document += self.__spc_c*' ' + " </Point>\n"
if desc is not None:
self.__document += self.__spc_c*' ' + " <description>" + desc + "</description>\n"
self.__document += self.__spc_c*' ' + "</Placemark>\n"
def build(self):
if self.__spc_c != 2:
raise Exception("Unclosed folders")
self.__is_built = True
self.__document += "</Document>\n"
self.__document += "</kml>\n"
return self.__document
def __chk_built(self):
if self.__is_built == True:
raise Exception("this instance has already been built");
# DOWNLOAD TEH DATA
takeoffs_url = "http://carte.ffvl.fr/xml/sites/decollages.xml"
landings_url = "http://carte.ffvl.fr/xml/sites/atterrissages.xml"
viewsite_url = "http://federation.ffvl.fr/sites_pratique/voir/"
takeoffs = urlopen(takeoffs_url).read()
landings = urlopen(landings_url).read()
# PARSE THE DATA
class Spot:
ident = 0
dept = None
lat = 0
lon = 0
alt = 0
name = str()
spots_per_dept = dict()
is_name = False
current_spot = None
def start_element(name, attrs):
global current_spot
global is_name
if name == "site":
current_spot = Spot()
if name == "nom":
is_name = True
if name == "codepostal":
try:
current_spot.dept = attrs['value'][:2]
# handling DOM-TOMs names
if int(current_spot.dept) >= 97:
current_spot.dept = attrs['value'][:3]
except:
pass
if name == "altitude":
current_spot.alt = int(attrs['value'])
if name == "coord":
current_spot.lon = float(attrs['lon'])
current_spot.lat = float(attrs['lat'])
if name == "id":
current_spot.ident = int(attrs['value'])
def end_element(name):
global current_spot
global is_name
global spots
if name == "nom":
is_name = False
if name == "site":
if str(current_spot.dept) not in spots_per_dept:
spots_per_dept[str(current_spot.dept)] = dict()
spots = spots_per_dept[str(current_spot.dept)]
if str(current_spot.ident) not in spots:
spots[str(current_spot.ident)] = []
spots[str(current_spot.ident)].append(current_spot)
# CAUTION: expat splits on escaped characters (eg: &x#22;)
def char_data(data):
global current_spot
global is_name
if is_name == True:
current_spot.name += data.strip()
parser = expat.ParserCreate()
parser.StartElementHandler = start_element
parser.EndElementHandler = end_element
parser.CharacterDataHandler = char_data
parser.Parse(takeoffs)
parser = expat.ParserCreate()
parser.StartElementHandler = start_element
parser.EndElementHandler = end_element
parser.CharacterDataHandler = char_data
parser.Parse(landings)
# BUILD THE KML
doc = KMLBuilder("Paragliding Spots in France")
for dept in spots_per_dept:
spots = spots_per_dept[dept]
if dept == None or dept == '':
doc.enter_folder("Unknown")
else:
doc.enter_folder(france_departements.code_to_name[dept])
for site_id in spots.keys():
if spots[site_id][0].name != None and spots[site_id][0].name != '':
doc.enter_folder(spots[site_id][0].name)
else:
doc.enter_folder("Unnamed site")
for site in spots[site_id]:
doc.mk_placemark(site.name, site.lon, site.lat, site.alt, viewsite_url + str(site.ident))
doc.close_folder()
doc.close_folder()
# OUTPUT
file = open("France_paragliding_spots.kml", "w", encoding="utf-8")
file.write(doc.build())
file.flush()
file.close()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment