The following proof of concept exercise illustrates how to combine dual canvas features in a relatively complex visualization.
The visualization overlays state border polygons onto a map image and adds a mouse over event handler which shows the name of the state under the mouse in a floating dialog.
# https://stackoverflow.com/questions/1814169/geographical-boundaries-of-states-provinces-google-maps-polygon
# Parse State border polygons from XML
xml_file = "states.xml"
import xml.etree.ElementTree as ET
tree = ET.parse(xml_file)
root = tree.getroot()
name_to_boundary = {}
allpoints = []
for state in root:
name = state.attrib["name"]
#print name
boundary = []
for point in state:
lat = float(point.attrib["lat"])
lng = float(point.attrib["lng"])
#pt = [lat, lng]
pt = [lng, lat]
boundary.append(pt)
allpoints.append(pt)
name_to_boundary[name] = boundary
# Draw a color map image with state polygons and borders
from jp_doodle import dual_canvas
from IPython.display import display
states = dual_canvas.DualCanvasWidget(width=520, height=320)
states.check_jquery()
states.text(text="Longitude", y=-60, x=250, align="center", font="bold 20px Arial",)
states.text(text="Latitude", y=150, x=-50, align="center", degrees=90, font="bold 20px Arial",)
minlng = min(x[0] for x in allpoints)
maxlng = max(x[0] for x in allpoints)
minlat = min(x[1] for x in allpoints)
maxlat = max(x[1] for x in allpoints)
display(states)
# image underlay
earth_image = 'Earthmap1000x500.jpg'
# local link does not work (as written) in Jupyter Lab
earth_image = 'https://upload.wikimedia.org/wikipedia/commons/a/ac/Earthmap1000x500.jpg'
def latitude_pixel(lat):
"pixel from top edge = (90 - latitude) / 0,36"
return (90.0 - lat) / 0.36
def longitude_pixel(lng):
"pixel from left hand side = (180 + longitude) / 0,36"
return (180 + lng) / 0.36
sx = longitude_pixel(minlng)
sy = latitude_pixel(maxlat)
sWidth = (maxlng - minlng) / 0.36
sHeight = (maxlat - minlat) / 0.36
states.name_image_url("earth", earth_image)
states.named_image("earth", 0,0,500,300, degrees=0,
sx=sx, sy=sy, sWidth=sWidth, sHeight=sHeight)
# States polygons overlay.
frame = states.frame_region(0,0,500,300,minlng, minlat, maxlng, maxlat)
for name in name_to_boundary:
points = name_to_boundary[name]
# add semi-transparent filled polygon for state interior
frame.polygon(points=points, name=name, color="rgba(100,200,0,0.5)")
# add unnamed state border
frame.polygon(points=points, color="#38f", fill=False)
# add reference axes
frame.right_axis(
min_value= minlat,
max_value= maxlat,
max_tick_count= 6,
axis_origin= dict(x=maxlng+2, y=0),
tick_line_config= dict(color="#66f"),
tick_text_config= dict(color="#875"),
)
frame.bottom_axis(
min_value= minlng,
max_value= maxlng,
max_tick_count= 8,
axis_origin= dict(x=0, y= minlat-2),
tick_line_config= dict(color="#66f"),
tick_text_config= dict(color="#875", align="center", valign="center", degrees=0),
add_end_points= True,
)
#print minlng, minlat, maxlng, maxlat
states.fit()
# Use javascript to add mouse over event handling and a pop-up dialog.
states.js_init("""
// Text info area and JQueryUI dialog
var info = $("<div>info here</div>").appendTo(element);
var dialog = $("<div>dialog text</div>").appendTo(element);
dialog.dialog();
// https://stackoverflow.com/questions/17358622/dialog-box-hide-and-show-jquery
dialog.dialog("open");
// Using dialog.parent().hide() and *.show() avoids scrolling anomalies.
dialog.parent().hide()
element.text
var last_name = null
count = 0;
var mouse_move = function(event) {
count += 1;
var name = event.canvas_name;
var pos = { my: "left+10 top+10", at: "left bottom", of: event }
dialog.dialog("option", "position", pos);
if ((last_name) && ((last_name!=name) || event.type=="mouseout")) {
// obscure the interior)
element.change(last_name, {color: "rgba(100,200,0,0.5)"})
last_name = null;
dialog.parent().hide();
}
if (name) {
// make the interior transparent
element.change(name, {color: "rgba(0,0,0,0)"})
last_name = name;
dialog.html("<div>"+name+"</div>");
dialog.parent().show();
}
info.html("<div>name="+name+"; last_name="+last_name
+"; count="+count+"</div>");
}
element.on_canvas_event("mousemove", mouse_move);
""")
# https://commons.wikimedia.org/wiki/File:Earthmap1000x500.jpg
#pixel from top edge = (90 - latitude) / 0,36
#pixel from left hand side = (180 + longitude) / 0,36