Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Easy OpenStreetMap tile displaying for Python

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:

  • Display on my own website
  • To upload my data to some Internet Server
  • interactive features like zooming, scrolling (in the first place)
  • manually process and render the .xml data from OSM
  • In the first place I don't want to define every detail of the rendering style. I hope/expect that there exists some default styles.

Do you have a good starting point for me? Or do I underestimate the complexity of this topic?

like image 847
BerndGit Avatar asked Feb 12 '15 11:02

BerndGit


2 Answers

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.

like image 173
Joining Dots Avatar answered Sep 20 '22 17:09

Joining Dots


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()
    
like image 36
BerndGit Avatar answered Sep 19 '22 17:09

BerndGit