import math
import urllib

# Useful code for 22c80 HW11. 
# Jim Cremer, 5/1/09, extending/modifying/adapting code found
# on the web at http://wiki.forum.nokia.com/index.php/PyS60_Google_Maps_API

# To use this code, look at the showMap function at the bottom of the file.
# Then execute something like 
#   showMap("Varanasi, India", 12, 0, 0)

# To understand the code, study the key functions
#   geocodeAdress(...) and
#   retrieveStaticMap(...)

################################################################################

# apikey for running on localhost:8080
# if you use this Google Maps API key, you don't need to get your own right now.
# However, if you create Google Maps applications after this homework assignment
# you will need to get your own key and make sure you follow Google's legal Terms
# and Conditions for usage of the mapping APIs.
api_key = 'ABQIAAAA1XbMiDxx_BTCY2_FkPh06RR20YmIEbERyaW5EQEiVNF0mpNGfBSRb_rzgcy5bqzSaTV8cyi2Bgsx3g'

tmp_file = '/Users/cremer/Desktop/pic.jpg'

# Given a string representing a location, return 2-element 
# [latitude, longitude] list for that location (or print
# an error message when Google doesn't find the location)
#
def geocodeAddress(addressString):
  url = getGeocodeUrl(addressString)
  resultFromGoogle = urllib.urlopen(url).read()
  data = resultFromGoogle.split(',')
  # The URL asked Google to return information in "csv" form. 
  # data[0] contains: HTTP status code. 200 is good, others are errors.
  # data[1] contains: some "accuracy" information - ignore this for now.
  # data[2] contains: latitude 
  # data[3] contains: longitude
  if data[0] != "200":
    errorCode = int(data[0])
    printNow("Google Maps Exception: %s" % getGeocodeError(errorCode))
  else:
    # if no error return a [latitude, longitude] list
    return [float(data[2]),float(data[3])]
 
# Construct a URL that represents a query to google for the 
# location of the given addressString.
#
def getGeocodeUrl(addressString):
  urlbase = "http://maps.google.com/maps/geo"
  return "%(urlbase)s?%(params)s" % {'params': urllib.urlencode({'q':addressString,
                                      'output':'csv',
                                      'key':api_key
                                        }),
             'urlbase': urlbase}
 
def getGeocodeError(errorCode):
  errorDict = {400: "Bad request",
               500: "Server error",
               601: "Missing query",
               602: "Unknown address",
               603: "Unavailable address",
               604: "Unknown directions",
               610: "Bad API key",
               620: "Too many queries"}
 
  
  if errorCode in errorDict:
    return errorDict[errorCode]
  else:
    return "Unknown error"
 
# First construct a URL suitable for the Google Static Maps API
# Then use the URL to request a map from Google, storing the 
# resulting image in tmp_file
# 
def retrieveStaticMap(width, height, lat, long, zoom):
  url = getMapUrl(width, height, lat, long, zoom)
  urllib.urlretrieve(url, tmp_file)
  return tmp_file

# Contruct a Google Static Maps API URL for a map that:
#   has size width x height in pixels
#   is centered at latitude lat and longitude long
#   is "zoomed" to the give Google Maps zoom level (0 <= zoom <= 21)
#    
def getMapUrl(width, height, lat, lng, zoom):
  urlbase = "http://maps.google.com/staticmap"
  params = ["center=%(lat)s,%(lng)s" % {"lat":lat,"lng":lng}]
  params.append("format=%(format)s" % {"format":"jpeg"})
  params.append("zoom=%(zoom)s" % {"zoom":zoom})
  params.append("size=%(width)sx%(height)s" % {"width":width,"height":height})
  params.append("key=%(api_key)s" % {"api_key":api_key})
  return  "%(urlbase)s?%(params)s" % {"urlbase":urlbase,"params":"&".join(params)}
 

# some code useful for lat/long <--> x/y conversion
#
magic_number = 128<<21 # this is half the earth's circumference *in pixels*
                         # at Google zoom level 21
radius = magic_number / math.pi

# Return a new [latitude, longitude] pair that is 
# deltaX pixels to the right/left and 
# deltaY pixels above/below the input [lat, long] pair
# Since the shift/adjustment is in pixels you need
# to specify a Google zoom level since one-pixel shifts
# represent different lat/long shifts at different zoom
# levels.
# Example: adjust(0, 0, 100, 0, 0) yields
#                (0, 140.625) meaning that 
#          at zoom 0 (max farthest away view)
#          a 100 pixel shift moves from longitude
#          0 to 140.625 (with latitude at the equator)
#          
#          But adjust(0, 0, 100, 0, 18), a zoomed-way-in
#          view, yields just a tiny latitude change
#          0.00429 for the 100-pixel change.
#
def adjust(lat, lng, deltaX, deltaY, z):
  return (googleYToLat(latToGoogleY(lat) + (deltaY<<(21-z))),
          googleXToLong( longToGoogleX(lng) + (deltaX<<(21-z))))
         
# Return a list [winX, winY] of window coordinates corresponding to the 
# given lat/long location for a google map of winWidth-by-winHeight pixels
# centered at lat/long [winCenterLat, winCenterLng] and with the given zoom level
#
# This function should make it easy to place additional text on your maps - 
# in particular, it will make it pretty easy to annotate the graph edges with 
# their lengths/distances
#
def latLongToWindowXY(lat, lng, winCenterLat, winCenterLng, winWidth, winHeight, zoom):
  winCenterX = winWidth/2
  winCenterY = winHeight/2
  winX = winCenterX + ((longToGoogleX(lng) - longToGoogleX(winCenterLng))>>(21-zoom))
  winY = winCenterY + ((latToGoogleY(lat) - latToGoogleY(winCenterLat))>>(21-zoom))
  return [winX, winY]

def longToGoogleX(lng):
  return int(round(magic_number + (lng / 180.0) * magic_number))
 
def latToGoogleY(lat):
  return int(round(magic_number - 
                   radius * math.log( (1 + math.sin(lat * math.pi / 180)) /
                                      (1 - math.sin(lat * math.pi / 180)) ) / 2))
 
def googleXToLong(x):
  return 180.0 * ((round(x) - magic_number) / magic_number)
 
def googleYToLat(y):
  return ( (math.pi / 2) - (2 * math.atan(math.exp( (round(y)-magic_number)/radius ))) ) * 180 / math.pi

# Show a map, built via Google Static Maps API, based on the location
# specified by "addressString" and zoomed according to the 
# given zoom level (Google's zoom levels range from 0 to 21).
# The location will be in the center of the map is xShift and
# yShift are zero.  Otherwise the location will be offset 
# xShift pixels left/right of the center and yShift pixels up/down.
#
def showMap(addressString, zoom, xShift, yShift):
  latLong = geocodeAddress(addressString)
  adjustedLatLong= adjust(latLong[0], latLong[1], xShift, yShift, zoom)
  retrieveStaticMap(400,300, adjustedLatLong[0], adjustedLatLong[1], zoom)
  pic = makePicture(tmp_file)
  addText(pic, 10, 10, "hello")
  show(pic)

  