Google Map Integration with Dash web framework

In one of the requirement, I need to integrate Google Map with Dash framework and on map click - retrieve lat , long and address (on map click event).

I was able to retrieve the same using google API and flask framework using java script which gives Lat ,Long and Address based on map click event and renders Google Map

Here is the python code used in flask framework :

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import json
from flask import Flask, render_template, request

app = Flask(__name__)

@app.route('/map', methods=['GET', 'POST'])
def map():
location = {}
if request.method == "POST":
    location = request.get_json()
    # latitude = location['latitude']
    # longitude = location['longitude']
    print(location);
return render_template('map.html')

if __name__ == '__main__':
app.run(debug = True) # run app

Is there a way to achieve the same in Dash using similar method. Thank you.

You can use mapbox or dash-leaflet for drawing the map, but you will probably need another lib/api (such as google) for reverse geocoding to get addresses.

Thank you @Emil for your reply. I tried the dash leaflet library designed by you & your team as per below code. Must say great add on for Dash framework.
Two issues , if you can suggest the solution - it would be great. In below example Map Event Click is not working on zoom In and Out of the Map. And Is it possible to use mapbox tile here.

Below is complete code which I have tried -

import json
import dash
import dash_core_components as dcc
import dash_html_components as html
import dash_leaflet as dl
import json
from dash.dependencies import Output, Input

# region Example 5

GEOTIFF_ID = "geotiff-id"
GEOTIFF_MARKER_ID = "geotiff-marker-id"
COORDINATE_CLICK_ID = "coordinate-click-id"

def render_example5():
    # Example from https://plot.ly/python/scatter-plots-on-maps/#us-airports-map
    color_domain = dict(domainMin=20, domainMax=40, colorscale=['white', 'orange', 'red'])
    return [
        html.H1("Example 5: GeoTIFFOverlay"),
        html.P("US airports (most arrivals)"),
        dl.Map(style={'width': '1000px', 'height': '500px'},
               center=[-17.782769, -50.924872],
               zoom=3,
               children=[
                   # dl.TileLayer(url="https://cartodb-basemaps-{s}.global.ssl.fastly.net/dark_nolabels/{z}/{x}/{y}.png"),
                   dl.TileLayer(url="http://www.google.cn/maps/vt?lyrs=s@189&gl=cn&x={x}&y={y}&z={z}"),
                   dl.GeoTIFFOverlay(id=GEOTIFF_ID, interactive=True, style={'width': '1000px', 'height': '500px'}),
                   # dl.GeoTIFFOverlay(id=GEOTIFF_ID, interactive=True, url="/assets/tz850.tiff", band=1, opacity=0.9,
                   # **color_domain),
                   dl.Marker(
                       id="marker",
                       interactive=True,
                       position=[-17.782769, -50.924872],
                       draggable=False,
                   ),
                   html.Div(id=GEOTIFF_MARKER_ID)

               ]),

        html.P("Coordinate (click on map):"),
        html.Div(id=COORDINATE_CLICK_ID)
    ]


def register_example5(app):
    @app.callback(Output(GEOTIFF_MARKER_ID, 'children'),
                  [Input(GEOTIFF_ID, 'click_lat_lng_val')])
    def geotiff_marker(x):
        if x is not None:
            lat, lon, val = x
            return dl.Marker(position=[lat, lon],  children=[
                dl.Tooltip('Test')
            ])
        else:
            return None


def register_example1(app):
    @app.callback(Output(COORDINATE_CLICK_ID, 'children'),
                  [Input(GEOTIFF_ID, 'click_lat_lng_val')])
    def click_coord(e):
        if e is not None:
            return json.dumps(e)
        else:
            return "-"


# endregion

app = dash.Dash(__name__, external_scripts=['https://codepen.io/chriddyp/pen/bWLwgP.css'])

# Create layout.
app.layout = html.Div(

    render_example5()
)
# Bind callbacks.

register_example5(app)
register_example1(app)

if __name__ == '__main__':
    app.run_server(debug=False, port=8150)

@string - Thanks! The reason that click events are not working in the example is that you have added a geotiff overlay without an url. When the map zooms, leaflet tried to redraw the geotiff layer, but since the url is not set, this operation fails.

Here is a code piece, which does what i guess you were intending (without a geotiff overlay; it seems that you are not using it?),

import json
import dash
import dash_html_components as html
import dash_leaflet as dl
from dash.dependencies import Output, Input

MAP_ID = "map"
MARKER_GROUP_ID = "marker-group"
COORDINATE_CLICK_ID = "coordinate-click-id"

# Create app.
app = dash.Dash(__name__, external_scripts=['https://codepen.io/chriddyp/pen/bWLwgP.css'])
app.layout = html.Div([
    dl.Map(style={'width': '1000px', 'height': '500px'},
           center=[-17.782769, -50.924872],
           zoom=3,
           children=[
               dl.TileLayer(url="http://www.google.cn/maps/vt?lyrs=s@189&gl=cn&x={x}&y={y}&z={z}"),
               dl.LayerGroup(id=MARKER_GROUP_ID)
           ], id=MAP_ID),
    html.P("Coordinate (click on map):"),
    html.Div(id=COORDINATE_CLICK_ID)]
)


@app.callback(Output(MARKER_GROUP_ID, 'children'), [Input(MAP_ID, 'click_lat_lng')])
def set_marker(x):
    if not x:
        return None
    return dl.Marker(position=x, children=[dl.Tooltip('Test')])


@app.callback(Output(COORDINATE_CLICK_ID, 'children'), [Input(MAP_ID, 'click_lat_lng')])
def click_coord(e):
    if not e:
        return "-"
    return json.dumps(e)


if __name__ == '__main__':
    app.run_server(debug=False, port=8150)

You can use any tile provider that you like, mapbox included. Just set the url of the TileLayer accordingly.

2 Likes

How can I choose a different Google Map, ie. one that shows the streets in every country?

I don’t understand this: url="http://www.google.cn/maps/vt?lyrs=s@189&gl=cn&x={x}&y={y}&z={z}"

If you just set the url that you provided, the corresponding map is shown.

import dash_html_components as html
import dash_leaflet as dl
from dash import Dash

app = Dash(prevent_initial_callbacks=True)
app.layout = html.Div([
    dl.Map(dl.TileLayer(url="https://www.google.cn/maps/vt?lyrs=s@189&gl=cn&x={x}&y={y}&z={z}"),
           style={'width': '100%', 'height': '50vh', 'margin': "auto", "display": "block"}),
])

if __name__ == '__main__':
    app.run_server()

If you want a different map, you should use a different url.

Hi Emil, I mean where do I get the URL’s? I went on Google Map and tried the embed URL below but it does not work.
https://www.google.com/maps/embed?pb=!1m14!1m12!1m3!1d396925.16741000145!2d-" "122.98778408406982!3d49.18915414824289!2m3!1f0!2f0!3f0!3m2!" "1i1024!2i768!4f13.1!5e0!3m2!1sen!2sca!4v1624385730062!5m2!1sen!2sca

For example, I want a map of Vancouver, BC for which I can get a share URL like this: https://goo.gl/maps/9DT4uVRporXEQNKBA but when I try using this in the Plotly code, it does not show anything.

You can think of the map as a big picture. Then server cuts the picture into bits, each bit identified by (x,y,z). You can then request each bit via an url of the form,

https://www.google.cn/maps/vt?lyrs=s@189&gl=cn&x={x}&y={y}&z={z}

The url that you posted is not of this form, i.e. it wont work. There is not “one place” to find tile urls, but there is an overview of some providers here,

https://wiki.openstreetmap.org/wiki/Tile_servers

You could also make you own tile server if you wanted. Here is an example of hybrid tiles which I guess is what you are looking for,

http://mt0.google.com/vt/lyrs=y&hl=en&x={x}&y={y}&z={z}&s=Ga
1 Like