I want to include the open street map (OSM) in my python code.
I have read through lots of webpages regarding to OSM. But unfortunately I'm a bit lost, regarding which package I use best.
I'm looking for an easy way to get an OSM image in my app. As I starting point I'm thinking of something like:
import matplotlib.pyplot as plt
# Pseudo - Code for required function 'GetOSMImage'
Map = GetOSMImage(lat,long,delta_lat,delta_long)
imgplot = plt.imshow(Map)
Later I want to add plot my additional data in this plt. (I'm aware that I'll need to deal with projections etc.)
What I don't need/want:
Do you have a good starting point for me? Or do I underestimate the complexity of this topic?
The following is also based on BerndGit's wonderful answer. I had to do some modifications to get it working with Python 3.6.7. Posting them here in case it helps others.
Set-up required Pillow, and replacing urllib with requests, and replacing io/StringIO with io/ByesIO
import requests
from io import BytesIO
And then just needed to modify how the image is downloaded in the getImageCluster() function:
imgstr = requests.get(imgurl)
tile = Image.open(BytesIO(imgstr.content))
Big thanks to BerndGit for going to the trouble of posting the original.
Haven't managed to get Etna's modified Basemap version working yet. Had to add in an export path for the PROJ_LIB error for Basemap:
export PROJ_LIB=/path/to/your/instalation/of/anaconda/share/proj/
(solution at Basemap import error in PyCharm —— KeyError: 'PROJ_LIB')
And getting a set attribute error when trying to plot. It occurs using the Basemap tutorial too (https://basemaptutorial.readthedocs.io/en/latest/plotting_data.html#plot) but with the difference that the scatter of data does still plot as a layer on top of the map despite the error. With the OSM tiles, cannot get the data layer to show on top of the map. Having to export each layer individually and then combine using image editing software.
Based on your input, I was able to achive my target. Here is my code for others, which are searching a starting point to OSM. (Of course there is still much room for improvements).
Update
Please respect the usage policy of Open Street Map!
OpenStreetMap data is free for everyone to use. Our tile servers are not.
Requirements
- Heavy use (e.g. distributing an app that uses tiles from openstreetmap.org) is forbidden without prior permission from the Operations Working Group. See below for alternatives.
- Clearly display license attribution.
- Do not actively or passively encourage copyright infringement.
- Calls to /cgi-bin/export may only be triggered by direct end-user action. (For example: “click here to export”.) The export call is an expensive (CPU+RAM) function to run and will frequently reject when server is under high load.
- Recommended: Do not hardcode any URL at tile.openstreetmap.org as doing so will limit your ability to react quickly if the service is disrupted or blocked.
- Recommended: add a link to https://www.openstreetmap.org/fixthemap to allow your users to report and fix problems in our data.
Technical Usage Requirements
- Valid HTTP User-Agent identifying application. Faking another app’s User-Agent WILL get you blocked.
- If known, a valid HTTP Referer.
- DO NOT send no-cache headers. (“Cache-Control: no-cache”, “Pragma: no-cache” etc.)
- Cache Tile downloads locally according to HTTP Expiry Header, alternatively a minimum of 7 days.
- Maximum of 2 download threads. (Unmodified web browsers’ download thread limits are acceptable.)
More details see: https://operations.osmfoundation.org/policies/tiles/
Here is the code:
import matplotlib.pyplot as plt
import numpy as np
import math
import urllib2
import StringIO
from PIL import Image
def deg2num(lat_deg, lon_deg, zoom):
lat_rad = math.radians(lat_deg)
n = 2.0 ** zoom
xtile = int((lon_deg + 180.0) / 360.0 * n)
ytile = int((1.0 - math.log(math.tan(lat_rad) + (1 / math.cos(lat_rad))) / math.pi) / 2.0 * n)
return (xtile, ytile)
def num2deg(xtile, ytile, zoom):
n = 2.0 ** zoom
lon_deg = xtile / n * 360.0 - 180.0
lat_rad = math.atan(math.sinh(math.pi * (1 - 2 * ytile / n)))
lat_deg = math.degrees(lat_rad)
return (lat_deg, lon_deg)
def getImageCluster(lat_deg, lon_deg, delta_lat, delta_long, zoom):
smurl = r"http://a.tile.openstreetmap.org/{0}/{1}/{2}.png"
xmin, ymax =deg2num(lat_deg, lon_deg, zoom)
xmax, ymin =deg2num(lat_deg + delta_lat, lon_deg + delta_long, zoom)
Cluster = Image.new('RGB',((xmax-xmin+1)*256-1,(ymax-ymin+1)*256-1) )
for xtile in range(xmin, xmax+1):
for ytile in range(ymin, ymax+1):
try:
imgurl=smurl.format(zoom, xtile, ytile)
print("Opening: " + imgurl)
imgstr = urllib2.urlopen(imgurl).read()
tile = Image.open(StringIO.StringIO(imgstr))
Cluster.paste(tile, box=((xtile-xmin)*256 , (ytile-ymin)*255))
except:
print("Couldn't download image")
tile = None
return Cluster
if __name__ == '__main__':
a = getImageCluster(38.5, -77.04, 0.02, 0.05, 13)
fig = plt.figure()
fig.patch.set_facecolor('white')
plt.imshow(np.asarray(a))
plt.show()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With