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)