# Thread: Retrieveing map tiles in python

1. Does it exist any python code snippet which shows how to download map tiles from googlemaps, virtualearth, yahoo or any other map provider?
I can only find perl scripts, but I don't understand perl!

2. Hi cassioli,

Try this example mobymaps.py (from book Mobile Python: Rapid Prototyping of Applications on the Mobile Platform)

http://www.mobilenin.com/mobilepytho...87-mopymaps.py

enjoy

eMO

3. thank you very much, that's just what I was looking for!

4. Try this. It's not the most sophisticated way, but it works with Google Maps.

Code:
```from math import pi, sin, cos, log, exp, atan, floor

# Constants used for degree to radian conversion, and vice-versa.
DTOR = pi / 180.
RTOD = 180. / pi
TILE_SIZE = 256

def __init__(self, num_zoom=18, tilesize=TILE_SIZE):
# Google's tilesize is 256x256, square tiles are assumed.
self.tilesize = tilesize

# Initializing arrays to hold the parameters for each
#  one of the zoom levels.
self.degpp = [] # Degrees per pixel
self.npix  = [] # 1/2 the number of pixels for a tile at the given zoom level

# Incrementing through the zoom levels and populating the
#  parameter arrays.
z = tilesize # The number of pixels per zoom level.
for i in xrange(num_zoom):
# Getting the degrees and radians per pixel, and the 1/2 the number of
#  for every zoom level.
self.degpp.append(z / 360.) # degrees per pixel
self.npix.append(z / 2) # number of pixels to center of tile

# Multiplying `z` by 2 for the next iteration.
z *= 2

def lonlat_to_pixel(self, lat, lon, zoom):
# Calculating the pixel x coordinate by multiplying the longitude
#  value with with the number of degrees/pixel at the given
#  zoom level.
px_x = round(self.npix[zoom] + (lon * self.degpp[zoom]))

# Creating the factor, and ensuring that 1 or -1 is not passed in as the
#  base to the logarithm.  Here's why:
#   if fac = -1, we'll get log(0) which is undefined;
#   if fac =  1, our logarithm base will be divided by 0, also undefined.
fac = min(max(sin(DTOR * lat), -0.9999), 0.9999)

# Calculating the pixel y coordinate.
px_y = round(self.npix[zoom] + (0.5 * log((1 + fac)/(1 - fac)) * (-1.0 * self.radpp[zoom])))

# Returning the pixel x, y to the caller of the function.
return (px_x, px_y)

def get_tile_url(self, px_x, px_y, zoom):
tile_x = floor(px_x / TILE_SIZE)
tile_y = floor(px_y / TILE_SIZE)

return "http://mt.google.com/mt?x=%d&y=%d&zoom=%d" % (tile_x, tile_y, 17-zoom)```
Instantiate a GoogleZoom object, get x,y with lon_lat_to_pixel and get the URL with get_tile_url.

NOTE: You are violating the Google T&C

Regards

5. Originally Posted by neil.young
Try this. [...]

NOTE: You are violating the Google T&C

Regards
Thanks for good and very commented code!
But how can avoid violating Google licence terms? Is their program for PDAs the only one officially authorized to use their maps?!? Are MGMaps and 8motions/j2memap violating T&C too?!?

6. You should follow the discussions in the MGMaps-Forum concerning this. MGMaps has been "asked" kindly by Google to not continue using their API the way they did before. MGMaps has dropped support for Google Maps and continues with Microsoft and Yahoo only.

Indeed, the T&C just talk about and allow the usage from a web based entity.

Have a look and decide by yourself. I doubt that Google will strike against everybody everywhere. I just wanted to note, that there is a potential pitfall.

Regards

7. Hi,
is it possible to display an exact geo position on the downloaded map to show e.g. my position?

8. If you are using the code above you'll always get just 1 tile for a geo code. You'll find your point there, but it's generally not centered. From the latSo you have

a) to do some math in order to find out, what tiles around you have to draw aditionally in order to build up a bitmap (of variable size) and cut an area out so that your coordinate is centered. The floor part of x,y is always the x,y coordinate to be queried at google. The ceil part multiplied with 256 is always the position of your geocode within the tile

b) To draw some flag or circle is up to you. You have a bitmap, draw on it.

Regards

9. Is it possible to give a geo coordinate to the map server instead of an adress?

Thx

10. It seems to be possible. You should just open up your browser, move to Google Maps, and move to a location. Have Wireshark running in the background and see what happens.

I haven't investigated in this API, but found some other probably usefull hints. Written down at

http://freenet-homepage.de/neilyoung/index.html

HTH

11. Thx neil.young,
I tried and it works with geo coordinates.

example:

12. No, I doubt that. You will have difficulities if using this URL outside your browser, e.g. in a python script. I bet you can't deal with the stuff returned by google, because you know nothing about the inner structure. This is up to the diverse GM JavaScript files, making the map visible to you in a browser.

The hardcore way is to get the tiles by x,y and build the map by yourself.

Regards

13. Originally Posted by neil.young
If you are using the code above you'll always get just 1 tile for a geo code. You'll find your point there, but it's generally not centered. From the latSo you have

a) to do some math in order to find out, what tiles around you have to draw aditionally in order to build up a bitmap (of variable size) and cut an area out so that your coordinate is centered. The floor part of x,y is always the x,y coordinate to be queried at google. The ceil part multiplied with 256 is always the position of your geocode within the tile

b) To draw some flag or circle is up to you. You have a bitmap, draw on it.

Regards
Hi Neil,

can you please demonstrate how to get x,y pixel coordinates inside a tile given latitude and longitude and zoom level?
eg. latitude = -36.856257 longitude = 174.765058 and zoom = 0

Cheers!

James

14. Hi James,

as I said above:

Code:
`Instantiate a GoogleZoom object, get x,y with lon_lat_to_pixel and get the URL with get_tile_url.`
BTW: Zoom level 0 is rather simple: There is just one tile available for that level, hence the tile coords are x=0, y=0. I think, you mean the largest zoom level 17, so the 3 liner is as follows:

(x, y) = gz.lonlat_to_pixel(-36.856257, 174.765058, 17)
print gz.get_tile_url(x, y, 17)

After that open your browser with the URL printed. And please let me know, if the result doesn't match your expectations.

EDIT: I'm currently reviewing your post and just seeing your real question (sorry, it's early in the morning here over...) I'm in a rush, but return to you ASAP.

Regards

15. So James,

your post gave me the opportunity to make some reworks and complete my series of articles at http://maps.alphadex.de (topic "Get a map") with a Python sample. As already stated above, the algorithm wasn't mine and wasn't sophisticated enough , because it does not explain anything. But the results compared with my algorithm are of course the same. Please visit my site in order to get a detailed explanation of all this.

So I rewrote a C# sample in Python, which now answers your initial question. And to be more specific: The sample coordinate you provided is somewhere in a triangle formed by Airedale St. and Symonds St.

Here is a "more sophisticated" approach, because it makes the Mercator roots more transparent:

Code:
```from math import pi, sin, log

# This script is published under GPL (included below)
#
# This program is free software; you can redistribute it and/or modify
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#

# Constants used for degree to radian conversion, and vice-versa.
DTOR = pi / 180.
RTOD = 180. / pi
TILE_SIZE = 256

def locationCoord(self, lat, lon, zoom):
# Per se earth coordinates are -90° <= lat <= +90° and -180° <= lon <= 180°
# Mercator is NOT applicable for abs(lat) > 85.0511287798066 degrees
if (abs(lat) > 85.0511287798066):
raise "Invalid latitude"
# latitude (deg) -> latitude (rad)
sin_phi = sin(float(lat) * DTOR)
# Use Mercator to calculate x and normalize this to -1 <= x <= +1 by division by 180.
# The center of the map is lambda_0 = 0, hence x = lat.
norm_x = lon / 180.
# Use Mercator to calculate y and normalize this to -1 <= y <= +1 by division by PI.
# According to Mercator y is defined in the range of -PI <= y <= PI only
norm_y = (0.5 * log((1 + sin_phi) / (1 - sin_phi))) / pi
# Apply the normalized point coordinations to any application (here the coordinates of
# a 256*256 image holding that point in a thought grid with origin 0,0 top/left)
col = pow(2, zoom) * ((norm_x + 1) / 2);
row = pow(2, zoom) * ((1 - norm_y) / 2);
return (col, row, zoom);```
Here is how to use it:

# Main
col, row, zoom = gz.locationCoord(-36.856257, 174.765058, 17)
print "X coordinate within the tile =", (col - long(col)) * TILE_SIZE
print "Y coordinate within the tile =", (row - long(row)) * 256

Enjoy and let me know

#### Posting Permissions

• You may not post new threads
• You may not post replies
• You may not post attachments
• You may not edit your posts
•
Nokia Developer aims to help you create apps and publish them so you can connect with users around the world.