python-3.xbing-mapsgeocoder

Open a Bing Maps map of the world and put a pushpin on a location in Python 3


Given a Bing Maps API key, Python 3 and the location on a Bing Map of a city, as given by for example

import geocoder # pip install geocoder
g = geocoder.bing('Mountain View, CA', key=BING_KEY)
print(g.json['bbox'])

How do I, in Python 3 and using geocoder with or without another package,

Microsoft shows JavaScript examples, but it doesn't seem to have a native Python API, and I couldn't find a Bing Maps API for pushpins on GitHub, only a few APIs that extract information, but not any that decorates a map.


Solution

  • You can see in the Microsoft documentation that you cannot add pushpins to a Bing Maps URL. If you really want pushpins, you'll need to create your own web page that embeds a Bing Map using their JavaScript API (see below).

    However, instead of pushpins you can use shared places, which gives a similar effect.

    If that is all right with you, you can do the following:

    import geocoder # pip install geocoder
    import webbrowser
    from urllib.parse import quote
    
    BING_KEY = "..."
    searchTerm = "Mountain View, CA"
    g = geocoder.bing(searchTerm, key=BING_KEY)
    lat = g.json['lat']
    long = g.json['lng']
    zoomLevel = 1 # Show entire world
    url = f"https://www.bing.com/maps?cp={lat}~{long}&lvl={zoomLevel}&sp=point.{lat}_{long}_{quote(searchTerm)}"
    webbrowser.open_new(url)
    

    Using the JavaScript API

    If you do want to create your own page rather than use Bing.com, the easiest way is probably to create a separate HTML template and pull the parameters you need from a query string. This template can be opened as a static file, served locally from a server on your computer, or hosted on your own website.

    File bing.html, in the same directory as your Python script:

    <!DOCTYPE html>
    <html>
    <head>
        <title></title>
        <meta charset="utf-8" />
        <script type='text/javascript'>
        function GetMap() {
            // Note Internet Explorer does not support URLSearchParams. It would need to parse manually.
            var urlParams = new URLSearchParams(window.location.search);
    
            var map = new Microsoft.Maps.Map('#myMap', {
                credentials: urlParams.get("key"),
                center: new Microsoft.Maps.Location(+urlParams.get("lat"), +urlParams.get("long")),
                zoom: +urlParams.get("zoom")
            });
    
            var center = map.getCenter();
    
            // Create custom Pushpin
            var pin = new Microsoft.Maps.Pushpin(center, {
                title: urlParams.get("title"),
                subTitle: urlParams.get("subTitle"),
                text: urlParams.get("label")
            });
    
            // Add the pushpin to the map
            map.entities.push(pin);
        }
        </script>
        <script type='text/javascript' src='http://www.bing.com/api/maps/mapcontrol?callback=GetMap' async defer></script>
    </head>
    <body>
        <div id="myMap" style="position:relative;width:600px;height:400px;"></div>
    </body>
    </html>
    

    Then your Python script will be only slightly different from the other option above:

    import geocoder
    import webbrowser
    import pathlib
    from urllib.parse import quote, urlencode
    
    BING_KEY = "..."
    searchTerm = "Mountain View, CA"
    g = geocoder.bing(searchTerm, key=BING_KEY)
    lat = g.json['lat']
    long = g.json['lng']
    zoomLevel = 1
    
    queryString = urlencode({
        "key": BING_KEY,
        "lat": lat,
        "long": long,
        "title": searchTerm,
        "subTitle": "",
        "label": "1",
        "zoom": str(zoomLevel)
    })
    templateFile = "bing.html"
    templatePath = pathlib.Path(__file__).parent.absolute().joinpath(templateFile)
    url = f"file:///{templatePath}?{queryString}"
    
    # If serving locally with `http.server` module:
    # url = f"http://localhost:8000/{templateFile}?{queryString}"
    
    browser = "chrome" # 'firefox' and 'safari' are also options here
    
    # If opening as a static file, it MUST specify `browser`. The default will not work with a query string.
    webbrowser.get(browser).open_new(url)