1. Diese Seite verwendet Cookies. Wenn du dich weiterhin auf dieser Seite aufhältst, akzeptierst du unseren Einsatz von Cookies. Weitere Informationen

[QUIZ#10] OnlyFoo (Python)

Dieses Thema im Forum "Archiv" wurde erstellt von OnlyFoo, 10. Oktober 2009.

  1. OnlyFoo

    OnlyFoo Erfahrenes Mitglied

    Soo, etwas verspätet, hatte garnicht mitbekommen, dass das der Contest online war... danke an Matthias dafür nochmal... Und auch für die Google-Geocode-Funktion, die ich im Prinzip übernommen habe...

    Meine Berechnung der Route läuft im Wesentlichen so:
    Code (Python):
    1. def route( start, end, steps = 50 ):
    2.     # Punkte auf der Erd-Kugel bestimmen
    3.     v_start = Vector.from_location( start )
    4.     v_end = Vector.from_location( end )
    5.  
    6.     # Direkten Verbindungsvektor 'durch' die Erde finden
    7.     v_dir = v_end - v_start
    8.  
    9.     # Stückweise den Verbindungsvektor abgehen
    10.     points = []
    11.     for step in xrange( steps + 1 ):
    12.         frac = step / float(steps)
    13.  
    14.         # Einen Vector vom Mittelpunkt der Erde finden,
    15.         # der durch den direkten Verbindungsvektor geht
    16.         # und diesen bis zur Erdoberfläche verlängern.
    17.         v_current = (v_start + (v_dir * frac)).normalize()
    18.  
    19.         # Nun haben wir einen Punkt, den wir in
    20.         # unsere Zeichnung einfügen können.
    21.         points.append( (v_current.lo, v_current.la) )
    22.  
    23.     # Ferdisch!
    24.     return points
    Wenn ich das richtig sehe haben damit fast alle, die abgegeben haben, einen anderen Lösungsansatz gewählt...


    Komplettes Python Script:

    Code (Python):
    1. # -*- encoding: utf8 -*-
    2.  
    3. import re
    4. import sys
    5. import math
    6. import urllib
    7.  
    8. def __main__():
    9.     # Alle Zeilen von der Standard-Eingabe lesen
    10.     lines = sys.stdin.readlines()
    11.     if not lines:
    12.         return
    13.  
    14.     # Alle Zeilen parsen
    15.     entries = [parse_location_line(line) for line in lines if line.strip()]
    16.  
    17.     # SVG erzeugen
    18.     svg = [ svg_start() ]
    19.     svg_layer = []
    20.  
    21.     last_loc, last_name = None, None
    22.     for loc, name in entries:
    23.         # Kreuz und Name zeichnen
    24.         svg_layer.append( svg_cross( *loc ) )
    25.         svg_layer.append( svg_text( loc.lo - 5, loc.la + 5, name ) )
    26.  
    27.         # Wenn wir schon eine Position hatten, zeichne
    28.         # die Route ein
    29.         if last_loc:
    30.             for points in route( last_loc, loc ):
    31.                 svg.append( svg_polyline( points ) )
    32.  
    33.         # setzte "letzte" Position
    34.         last_loc, last_name = loc, name
    35.  
    36.     # SVG ausgeben
    37.     svg.append( "".join( svg_layer ) )
    38.     svg.append( svg_end() )
    39.     print "".join( svg )
    40.  
    41.  
    42. #
    43. # Berechnet eine Route zwischen 'start' und 'end'
    44. #
    45. def route( start, end, steps = 50 ):
    46.     # Punkte auf der Erd-Kugel bestimmen
    47.     v_start = Vector.from_location( start )
    48.     v_end = Vector.from_location( end )
    49.  
    50.     # Direkter Verbindungsvektor 'durch' die Erde finden
    51.     v_dir = v_end - v_start
    52.  
    53.     # Stückweise die Route abgehen
    54.     points = []
    55.     parts = []
    56.     last_sgn = 0
    57.     for step in xrange( steps + 1 ):
    58.         frac = step / float(steps)
    59.  
    60.         # Einen Vector vom Mittelpunkt der Erde finden,
    61.         # der durch den direkten Verbindungsvektor geht
    62.         # und diesen bis zur Erdoberfläche verlängern.
    63.         v_current = (v_start + (v_dir * frac)).normalize()
    64.  
    65.         # Schauen ob wir über einen Rand übertreten haben. Nämlich genau
    66.         # dann, wenn sich das Vorzeichen der Länge geändert hat,
    67.         # und der Betrag der länge > 90° ist
    68.         sgn = -1 if v_current.lo < 0 else 1
    69.         if last_sgn and abs( v_current.lo ) > 90 and sgn != last_sgn:
    70.             points.append( Location(v_current.lo - 360 * sgn, v_current.la) )
    71.             parts.append( points )
    72.  
    73.             # Neue Punktmenge beginnen
    74.             points = []
    75.             points.append( Location(v_current.lo + 360 * sgn, v_current.la) )
    76.  
    77.         # last_sgn updaten
    78.         last_sgn = sgn
    79.  
    80.         # Nun haben wir einen Punkt, den wir in
    81.         # unsere Zeichnung einfügen können.
    82.         points.append( (v_current.lo, v_current.la) )
    83.  
    84.     # Ferdisch!
    85.     parts.append( points )
    86.     return parts
    87.  
    88.  
    89. #
    90. # Zerlegt eine Zeile der Form "lat lon name" und gibt ein tuple
    91. # mit (Location, Name) zurück
    92. #
    93. def parse_location_line( line ):
    94.     line = line.strip()
    95.     if line.startswith( "!" ):
    96.         name = line[1:].strip()
    97.         return (ask_mother_google( name ), name)
    98.  
    99.     parts = filter( None, line.split( " " ) )
    100.     if len( parts ) < 3:
    101.         raise Exception( "Ungültige Zeile: " + line )
    102.  
    103.     return (Location( parts[1], parts[0] ), " ".join( parts[2:] ))
    104.  
    105.  
    106. #
    107. # Fragt Google nach den Koordinaten für einen gegeben Ort
    108. #
    109. def ask_mother_google( what ):
    110.     if what in ask_mother_google.cache:
    111.         return ask_mother_google.cache[what]
    112.  
    113.     # URL zusammensetzten
    114.     url = "http://maps.google.com/maps/geo?" + urllib.urlencode({
    115.         "output": "csv",
    116.         "q": what
    117.     })
    118.  
    119.     # Google fragen
    120.     parts = urllib.urlopen( url ).read().split(",")
    121.  
    122.     # Fehlerhaftes Ergebnis?
    123.     if len( parts ) != 4 or parts[0] != "200":
    124.         raise IOError( "Der Ort '%s' wurde nicht gefunden!" % what )
    125.  
    126.     # Breitengrad invertieren... Google ist da anderer Meinung als ich ;)
    127.     result = Location( float(parts[3]), -float(parts[2]) )
    128.     ask_mother_google.cache[what] = result
    129.     return result
    130.  
    131. # Mehrfache Anfragen cachen
    132. ask_mother_google.cache = {}
    133.  
    134.  
    135. #
    136. # Wandelt eine Angabe in Grad, Minuten und Sekunden in einen Winkel in
    137. # Grad um.
    138. #
    139. def parse_degree( input ):
    140.     # Mit regulärem Ausdruck aufteilen
    141.     match = re.findall( r'([0-9]+)°(?:([0-9]+)\')?(?:([0-9]+)")?([NOSWE])', input )
    142.     if not match:
    143.         raise Exception( "Ungültige Gradangabe: " + input )
    144.  
    145.     g, m, s, cardinal = match[0]
    146.  
    147.     # Gradwert berechnen
    148.     deg = float( g )
    149.     if m: deg += float(m) / 60
    150.     if s: deg += float(s) / 3600
    151.  
    152.     # Norden oder Westen invertieren
    153.     if cardinal in 'NW': deg *= -1
    154.  
    155.     return deg
    156.  
    157.  
    158. #
    159. # Ein Tupel welches das erste und zweite Element mittels .lo und .la
    160. # zurück gibt
    161. #
    162. class Location( tuple ):
    163.     def __new__( cls, lo, la ):
    164.         if isinstance( lo, str): lo = parse_degree( lo )
    165.         if isinstance( la, str): la = parse_degree( la )
    166.         return tuple.__new__( cls, (lo, la) )
    167.  
    168.     @property
    169.     def lo( self ):
    170.         return self[0]
    171.  
    172.     @property
    173.     def la( self ):
    174.         return self[1]
    175.  
    176.  
    177. #
    178. # Eine kleine Vektor Klasse, zum einfachen Rechnen mit Vektoren
    179. #
    180. class Vector( tuple ):
    181.     def __new__( cls, x, y, z ):
    182.         return tuple.__new__( cls, (x, y, z) )
    183.  
    184.     def __add__( self, other ):
    185.         return Vector( *map( lambda a, b: a + b, self, other ) )
    186.  
    187.     def __sub__( self, other ):
    188.         return Vector( *map( lambda a, b: a - b, self, other ) )
    189.  
    190.     def __mul__( self, scale ):
    191.         return Vector( *[a * scale for a in self] )
    192.  
    193.     def normalize( self ):
    194.         return self * (1 / self.length)
    195.  
    196.     @property
    197.     def length( self ):
    198.         return math.sqrt( sum( [a * a for a in self] ) )
    199.  
    200.     @property
    201.     def la( self ):
    202.         return math.degrees( math.asin( self.y ) )
    203.  
    204.     @property
    205.     def lo( self ):
    206.         return math.degrees( math.atan2( self.x, self.z ) )
    207.  
    208.     @property
    209.     def x( self ): return self[0]
    210.  
    211.     @property
    212.     def y( self ): return self[1]
    213.  
    214.     @property
    215.     def z( self ): return self[2]
    216.  
    217.     @staticmethod
    218.     def from_location( loc ):
    219.         lo = math.radians( loc.lo )
    220.         la = math.radians( loc.la )
    221.         return Vector(
    222.             math.sin( lo ) * math.cos( la ),
    223.             math.sin( la ),
    224.             math.cos( lo ) * math.cos( la ) )
    225.  
    226.  
    227. def svg_start():
    228.     return '''<?xml version="1.0" ?>
    229. <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
    230.    "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
    231. <svg version="1.1"
    232.    xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
    233.    width="1024" height="512" stroke="red" fill="none"
    234.    viewBox="-180 -90 360 180" stroke-width="0.3">
    235.  
    236.    <defs>
    237.        <g id='Kreuz'>
    238.            <line x1="-1" y1="-1" x2="1" y2="1" />
    239.            <line x1="-1" y1="1" x2="1" y2="-1" />
    240.        </g>
    241.    </defs>
    242.  
    243.  
    244.    <!-- <image x="-180" y="-90" width="360" height="180" xlink:href="http://veimages.gsfc.nasa.gov/2430/land_ocean_ice_2048.jpg" /> -->
    245.    <image x="-180" y="-90" width="360" height="180" xlink:href="world.jpg" />
    246.    '''
    247.  
    248. def svg_cross( x, y ):
    249.     return "<use xlink:href='#Kreuz' transform='translate(%f %f)' />" % (x, y)
    250.  
    251. def svg_polyline( points ):
    252.     return "<polyline points='%s' />" % " ".join( [str(c) for p in points for c in p] )
    253.  
    254. def svg_text( x, y, text ):
    255.     return "<text x='%f' y='%f' fill='#ff0' stroke='none' font-size='5'>%s</text>" % (x, y, text)
    256.  
    257. def svg_end():
    258.     return "</svg>"
    259.  
    260.  
    261.  
    262. if __name__ == '__main__':
    263.     __main__()
     
  2. Matthias Reitinger

    Matthias Reitinger ɐɯıǝɹ

    Sieht ganz so aus. Dein Ansatz ist aber ziemlich elegant, da hätte ich auch mal drauf kommen können :) Danke jedenfalls dass du noch abgegeben hast!

    Grüße, Matthias
     
Die Seite wird geladen...
Ähnliche Themen - QUIZ#10 OnlyFoo Python
  1. Yaslaw
    Antworten:
    1
    Aufrufe:
    8.250
  2. Matthias Reitinger
    Antworten:
    27
    Aufrufe:
    11.891
  3. OnlyFoo
    Antworten:
    21
    Aufrufe:
    7.267
  4. Jellysheep
    Antworten:
    2
    Aufrufe:
    3.415
  5. OnlyFoo
    Antworten:
    1
    Aufrufe:
    3.147