#!/usr/bin/python3.4
import re
import xml.dom.minidom as minidom
from xml.parsers.expat import ExpatError
from enum import IntEnum
from flask import request, redirect, url_for, flash, render_template
class MultiGeo(IntEnum):
"""Formats multiple coordinates, even in multiple lines to [(latitude, longitude, elevation), ...]
or [(latitude, longitude, None), ...] tuples."""
# Valid for input_format
FORMAT_GUESS = 0 # guesses the input format; default for input_format
FORMAT_NONE = -1 # indicates missing formats
# Valid for input_format and output_format
FORMAT_GEOCACHING = 1 # e.g. "N 47° 13.692 E 011° 25.535"
FORMAT_WINTERRODELN = 2 # e.g. "47.222134 N 11.467211 E"
FORMAT_GMAPPLUGIN = 3 # e.g. "47.232922, 11.452239"
FORMAT_GPX = 4 # e.g. "1090.57"
def multigeo_from_string(value, input_format):
assert input_format in [MultiGeo.FORMAT_GUESS, MultiGeo.FORMAT_GEOCACHING, MultiGeo.FORMAT_WINTERRODELN,
MultiGeo.FORMAT_GMAPPLUGIN, MultiGeo.FORMAT_GPX], "input_format %d is not recognized"
lines = [line.strip() for line in value.split("\n") if len(line.strip()) > 0]
result = []
for line in lines:
if input_format == MultiGeo.FORMAT_GUESS or input_format == MultiGeo.FORMAT_GEOCACHING:
r = re.match('N ?(\d+)° ?(\d+\.\d+) +E ?(\d+)° ?(\d+\.\d+)', line)
if r is not None:
g = r.groups()
result.append((float(g[0]) + float(g[1]) / 60, float(g[2]) + float(g[3]) / 60, None))
continue
if input_format == MultiGeo.FORMAT_GUESS or input_format == MultiGeo.FORMAT_WINTERRODELN:
r = re.match('(\d+\.\d+) N (\d+\.\d+) E', line)
if not r is None:
result.append((float(r.groups()[0]), float(r.groups()[1]), None))
continue
if input_format == MultiGeo.FORMAT_GUESS or input_format == MultiGeo.FORMAT_GMAPPLUGIN:
r = re.match('(\d+\.\d+), ?(\d+\.\d+)', line)
if r is not None:
result.append((float(r.groups()[0]), float(r.groups()[1]), None))
continue
if input_format == MultiGeo.FORMAT_GUESS or input_format == MultiGeo.FORMAT_GPX:
try:
xml = minidom.parseString(line)
coord = xml.documentElement
lat = float(coord.getAttribute('lat'))
lon = float(coord.getAttribute('lon'))
try:
ele = float(coord.childNodes[0].childNodes[0].nodeValue)
except (IndexError, ValueError):
ele = None
result.append((lat, lon, ele))
continue
except (ExpatError, IndexError, ValueError):
pass
raise ValueError("Coordinates '%s' have no known format" % line, value)
return result
def multigeo_to_string(value, output_format):
result = []
for latitude, longitude, height in value:
if output_format == MultiGeo.FORMAT_GEOCACHING:
result.append(
'N %02d° %02.3f E %03d° %02.3f' % (latitude, latitude % 1 * 60, longitude, longitude % 1 * 60))
elif output_format == MultiGeo.FORMAT_WINTERRODELN:
result.append('%.6f N %.6f E' % (latitude, longitude))
elif output_format == MultiGeo.FORMAT_GMAPPLUGIN:
result.append('%.6f, %.6f' % (latitude, longitude))
elif output_format == MultiGeo.FORMAT_GPX:
if not height is None:
result.append(
'%.2f' % (latitude, longitude, height))
else:
result.append('' % (latitude, longitude))
else:
raise ValueError("output_format %d is not recognized" % output_format, value)
return "\n".join(result)
class CoordtoolController:
def index(self):
return render_template('coordtool.html', result=None)
def convert(self):
assert request.method == 'POST'
input = request.form['input']
no_elevation = 'no_elevation' in request.form
simplify = 'simplify' in request.form
swap_latlon = 'swap_latlon' in request.form
no_geoformat = 'no_geoformat' in request.form
no_gpxformat = 'no_gpxformat' in request.form
no_gmapsformat = 'no_gmapsformat' in request.form
no_geocachingformat = 'no_geocachingformat' in request.form
if input is None or len(input.strip()) == 0:
return redirect(url_for('coordtool_index'))
try:
result = multigeo_from_string(input, MultiGeo.FORMAT_GUESS)
except ValueError as e:
flash(str(e))
return redirect(url_for('coordtool_index'))
if swap_latlon:
result = [(latitude, longitude, elevation) for (longitude, latitude, elevation) in result]
if no_elevation:
result = [(longitude, latitude, None) for (longitude, latitude, elevation) in result]
geo_winterrodeln = lambda value: multigeo_to_string(value, MultiGeo.FORMAT_WINTERRODELN)
geo_gmapplugin = lambda value: multigeo_to_string(value, MultiGeo.FORMAT_GMAPPLUGIN)
geo_gpx = lambda value: multigeo_to_string(value, MultiGeo.FORMAT_GPX)
geo_geocaching = lambda value: multigeo_to_string(value, MultiGeo.FORMAT_GEOCACHING)
return render_template('coordtool.html', result=result,
no_geoformat=no_geoformat, geo_winterrodeln=geo_winterrodeln,
no_gmapsformat=no_gmapsformat, geo_gmapplugin=geo_gmapplugin,
no_gpxformat=no_gpxformat, geo_gpx=geo_gpx,
no_geocachingformat=no_geocachingformat, geo_geocaching=geo_geocaching)