Multiple infowindows Jinja Google maps and flask - python

I have been fighting this all day
The code below takes a jinja array from flask and providing I dont want an info box it all works hunky dory.
Add the infobox code below and it presents a single marker and no info window. Its been driving me crazy. I also want to use clustering as I have about 500 pins but that is such a second order problem.
I have had a look at SO tried many of the answers to no avail, there is some sort of magic called closure that seems to be at the heart of the problem that I clearly don't get.
Any help would be appreciated
Thanks in anticipation
function initialize() {
var infowindow = new google.maps.InfoWindow();
var map = new google.maps.Map(
document.getElementById('map_canvas'), {
center: new google.maps.LatLng(10.455177, 12.584731),
zoom: 2,
mapTypeId: google.maps.MapTypeId.ROADMAP
});
function addMarker(lat,long,name,content){
var info = content;
var message = name;
var point = new google.maps.LatLng(lat,long);
var newmarker = new google.maps.Marker({position: point,
map: map,
title: name
});
\\take this block of code out and I get all the markers fine
google.maps.AddListener(point, 'click', function(map, newmarker){
infowindow.setContent(info)
infowindow.open(map,newmarker)
});
}
{% for marker in markers %}
addMarker({{marker.lat}},{{marker.long}},'{{marker.name}}', '{{marker.url}}');
{% endfor %}
}
google.maps.event.addDomListener(window, 'load', initialize);
</script>

Check the syntax for the event listener.
Should be google.maps.event.addListener(newmarker, 'click', function(evt){
fiddle

Related

On clicking the specific label in Canvasjs Pie chart, pass the clicked label to Flask for further processing

I am working with pie charts using Canvasjs and I want to make it clickable so that if the specific label is clicked it will send the clicked label value to the flask so that I can process that value in the flask also redirect me to the other page
My Chart Code on the index.html page
{%extends "master.html"%}
{%block chart%}
<script>
window.onload = function () {
var chart = new CanvasJS.Chart("chartContainer", {
animationEnabled: true,
exportEnabled: false,
theme: "light1", // "light1", "light2", "dark1", "dark2"
backgroundColor: "transparent",
**data: [{
click: function (e) {
var dataPoint = e.dataPoint.label;
window.open({{url_for('jazz_index')}}, "_blank");
},**
type: "doughnut",
startAngle: 20,
//innerRadius: 10,
indexLabelFontSize: 17,
indexLabelFontColor: "white",
indexLabel: "#percent%",
toolTipContent: "<b>{label}:</b> {y} (#percent%)",
dataPoints: [
{%for outgoing_number,outgoing_call_type in outgoing_calls%}
{ y: {{outgoing_call_type}}, label: "{{outgoing_number}}" },
{%endfor%}
]
}]
});
chart.render();
}
</script>
{%endblock%}
{%block body%}
<div class="card-body" id="chartContainer" style="min-height: 250px; height: 250px; max-height: 250px; max-width: 90%;">
</div>
{%endblock%}
In this code, I am making a pie chart and I want to pass variable dataPoint to the flask route jazz_index when the specific bar is clicked on the pie chart and I also want to know how to access that variable value in the route jazz_index so that I can process that dataPoint value and redirect the chart to the new page (jazz_index.html) which contains the information related to that dataPoint.
This is my jazz_index route
#app.route('/jazz', methods=['GET', 'POST'])
def jazz_index():
return render_template('jazz_index.html')
Please help me out with some code.
Thanks in advance
You are using an approach that is not ideal and there are many ways to improve it.
Having the browser open up a new page just to send data to the back end is not a great user experience.
Also by the time that your Javascript finally runs, the code for url_for will have already executed and rendered out a url. It's not possible for url_for to dynamically include a value from Javascript like you are attempting to do.
Without diving too deep into the front end changes that will be required to make this production ready, you can get it "working" by taking an approach similar to below and then improve from there.
{
data: [{
click: function(e) {
var dataPoint = e.dataPoint.label;
var url = `{{url_for('jazz_index')}}`;
fetch(url + `?dataPoint=${dataPoint}`)
.then(response => response.json())
.then(data => console.log(data));
}
}]
}
Then in flask you need to get the data from the url parameters.
#app.route('/jazz', methods=['GET', 'POST'])
def jazz_index():
# import request from Flask
datapoint = request.args.get('dataPoint')
return render_template('jazz_index.html')

bokeh django plot embed: large amount of white space in generated div tag

I have a Bokeh chart embed working well in my Django project but I'm having problems with the embed that it creates. I'm importing both the script and div in my template as {{ div | safe }} and {{ script | safe }} but the div is creating a large white space after itself on my template.
Any ideas?
Here's an image of how it looks in the browser
And here's the div which is really long for some reason
Script:
<script type="text/javascript">
(function() {
var fn = function() {
Bokeh.safely(function() {
(function(root) {
function embed_document(root) {
var docs_json = {"998040af-6ec7-4d0a-9512-892e668e543b":{"roots":{"references":[{"attributes":{},"id":"39af7b08-c2a0-4239-8140-5fb6715147c3","type":"ResetTool"},{"attributes":{},"id":"66fe871a-6ed6-485c-8785-2348adf6114e","type":"SaveTool"},{"attributes":{"active_drag":"auto","active_inspect":"auto","active_scroll":"auto","active_tap":"auto","tools":[{"id":"5974d4bb-2135-4a2a-ae1d-33193cf46b77","type":"PanTool"},{"id":"4d082f4c-d578-4258-ada6-3f36133784c8","type":"WheelZoomTool"},{"id":"9faa6e3c-28e7-497a-a09a-d8ca67e6dffd","type":"BoxZoomTool"},{"id":"66fe871a-6ed6-485c-8785-2348adf6114e","type":"SaveTool"},{"id":"39af7b08-c2a0-4239-8140-5fb6715147c3","type":"ResetTool"},{"id":"07186e88-4a07-43f0-8246-0e4dd3ac404e","type":"HelpTool"}]},"id":"66619cf2-604a-43ed-b139-0b376222a87e","type":"Toolbar"},{"attributes":{"callback":null},"id":"451449e3-c761-4c57-a674-8b1b5a37f378","type":"DataRange1d"},{"attributes":{"callback":null},"id":"e0c60293-c503-4885-af6b-132872d94c5c","type":"DataRange1d"},{"attributes":{"below":[{"id":"c419969e-5741-4736-90de-34259d29a726","type":"LinearAxis"}],"border_fill_color":{"value":"whitesmoke"},"left":[{"id":"5e62dbcd-09f9-410b-9b19-c8f420b90bfb","type":"LinearAxis"}],"plot_height":150,"plot_width":400,"renderers":[{"id":"c419969e-5741-4736-90de-34259d29a726","type":"LinearAxis"},{"id":"42ac392d-f455-4528-95e3-f940470c065b","type":"Grid"},{"id":"5e62dbcd-09f9-410b-9b19-c8f420b90bfb","type":"LinearAxis"},{"id":"120afe85-61ea-4894-8366-9d5b0887adde","type":"Grid"},{"id":"5ba3c58f-6a2a-4af8-bb1a-bb6add5cc76d","type":"BoxAnnotation"},{"id":"d5d3beda-b199-4e31-a82a-4ca59930767d","type":"GlyphRenderer"}],"sizing_mode":"scale_width","title":{"id":"01d880cf-b868-49b4-b91b-bec60d5de6d2","type":"Title"},"toolbar":{"id":"66619cf2-604a-43ed-b139-0b376222a87e","type":"Toolbar"},"x_range":{"id":"e0c60293-c503-4885-af6b-132872d94c5c","type":"DataRange1d"},"x_scale":{"id":"8a36f571-dd09-4aed-980c-e19037c9b24a","type":"LinearScale"},"y_range":{"id":"451449e3-c761-4c57-a674-8b1b5a37f378","type":"DataRange1d"},"y_scale":{"id":"9da9e3b4-e359-4786-bd7c-daf86e94c3bf","type":"LinearScale"}},"id":"77eaaafa-b380-4c45-832a-65faea06bb98","subtype":"Figure","type":"Plot"},{"attributes":{},"id":"07186e88-4a07-43f0-8246-0e4dd3ac404e","type":"HelpTool"},{"attributes":{},"id":"d5ced699-d662-4c31-9b6a-793d97c998c8","type":"BasicTickFormatter"},{"attributes":{"formatter":{"id":"0da65dd3-5702-4c74-91e1-71733066a74f","type":"BasicTickFormatter"},"plot":{"id":"77eaaafa-b380-4c45-832a-65faea06bb98","subtype":"Figure","type":"Plot"},"ticker":{"id":"41d2e0a6-2feb-4437-bb35-efcea6c1c813","type":"BasicTicker"}},"id":"c419969e-5741-4736-90de-34259d29a726","type":"LinearAxis"},{"attributes":{"data_source":{"id":"2e6d3a2c-3d54-4b8a-984d-9d620aa7febe","type":"ColumnDataSource"},"glyph":{"id":"e0d6dab9-8df6-4abb-b825-b273548f408a","type":"MultiLine"},"hover_glyph":null,"muted_glyph":null,"nonselection_glyph":{"id":"75db6381-cd4f-4161-9926-ec219d3bd761","type":"MultiLine"},"selection_glyph":null,"view":{"id":"f8571075-8034-434f-9579-5197405959a9","type":"CDSView"}},"id":"d5d3beda-b199-4e31-a82a-4ca59930767d","type":"GlyphRenderer"},{"attributes":{"source":{"id":"2e6d3a2c-3d54-4b8a-984d-9d620aa7febe","type":"ColumnDataSource"}},"id":"f8571075-8034-434f-9579-5197405959a9","type":"CDSView"},{"attributes":{"plot":{"id":"77eaaafa-b380-4c45-832a-65faea06bb98","subtype":"Figure","type":"Plot"},"ticker":{"id":"41d2e0a6-2feb-4437-bb35-efcea6c1c813","type":"BasicTicker"}},"id":"42ac392d-f455-4528-95e3-f940470c065b","type":"Grid"},{"attributes":{},"id":"0da65dd3-5702-4c74-91e1-71733066a74f","type":"BasicTickFormatter"},{"attributes":{"line_alpha":{"value":0.1},"line_color":{"value":"#1f77b4"},"line_width":{"value":4},"xs":{"field":"xs"},"ys":{"field":"ys"}},"id":"75db6381-cd4f-4161-9926-ec219d3bd761","type":"MultiLine"},{"attributes":{"callback":null,"column_names":["xs","ys","line_color","line_alpha"],"data":{"line_alpha":[0.8,0.3],"line_color":["firebrick","navy"],"xs":[[1,3,2],[3,4,6,6]],"ys":[[2,1,4],[4,7,8,5]]}},"id":"2e6d3a2c-3d54-4b8a-984d-9d620aa7febe","type":"ColumnDataSource"},{"attributes":{"formatter":{"id":"d5ced699-d662-4c31-9b6a-793d97c998c8","type":"BasicTickFormatter"},"plot":{"id":"77eaaafa-b380-4c45-832a-65faea06bb98","subtype":"Figure","type":"Plot"},"ticker":{"id":"132532f4-248b-4037-9069-7d27a093e89a","type":"BasicTicker"}},"id":"5e62dbcd-09f9-410b-9b19-c8f420b90bfb","type":"LinearAxis"},{"attributes":{"bottom_units":"screen","fill_alpha":{"value":0.5},"fill_color":{"value":"lightgrey"},"left_units":"screen","level":"overlay","line_alpha":{"value":1.0},"line_color":{"value":"black"},"line_dash":[4,4],"line_width":{"value":2},"plot":null,"render_mode":"css","right_units":"screen","top_units":"screen"},"id":"5ba3c58f-6a2a-4af8-bb1a-bb6add5cc76d","type":"BoxAnnotation"},{"attributes":{"dimension":1,"plot":{"id":"77eaaafa-b380-4c45-832a-65faea06bb98","subtype":"Figure","type":"Plot"},"ticker":{"id":"132532f4-248b-4037-9069-7d27a093e89a","type":"BasicTicker"}},"id":"120afe85-61ea-4894-8366-9d5b0887adde","type":"Grid"},{"attributes":{"line_alpha":{"field":"line_alpha"},"line_color":{"field":"line_color"},"line_width":{"value":4},"xs":{"field":"xs"},"ys":{"field":"ys"}},"id":"e0d6dab9-8df6-4abb-b825-b273548f408a","type":"MultiLine"},{"attributes":{},"id":"8a36f571-dd09-4aed-980c-e19037c9b24a","type":"LinearScale"},{"attributes":{},"id":"41d2e0a6-2feb-4437-bb35-efcea6c1c813","type":"BasicTicker"},{"attributes":{"plot":null,"text":""},"id":"01d880cf-b868-49b4-b91b-bec60d5de6d2","type":"Title"},{"attributes":{},"id":"132532f4-248b-4037-9069-7d27a093e89a","type":"BasicTicker"},{"attributes":{},"id":"9da9e3b4-e359-4786-bd7c-daf86e94c3bf","type":"LinearScale"},{"attributes":{},"id":"5974d4bb-2135-4a2a-ae1d-33193cf46b77","type":"PanTool"},{"attributes":{},"id":"4d082f4c-d578-4258-ada6-3f36133784c8","type":"WheelZoomTool"},{"attributes":{"overlay":{"id":"5ba3c58f-6a2a-4af8-bb1a-bb6add5cc76d","type":"BoxAnnotation"}},"id":"9faa6e3c-28e7-497a-a09a-d8ca67e6dffd","type":"BoxZoomTool"}],"root_ids":["77eaaafa-b380-4c45-832a-65faea06bb98"]},"title":"Bokeh Application","version":"0.12.10"}};
var render_items = [{"docid":"998040af-6ec7-4d0a-9512-892e668e543b","elementid":"46dc4d68-63cf-4288-aa48-347675c82cc3","modelid":"77eaaafa-b380-4c45-832a-65faea06bb98"}];
root.Bokeh.embed.embed_items(docs_json, render_items);
}
if (root.Bokeh !== undefined) {
embed_document(root);
} else {
var attempts = 0;
var timer = setInterval(function(root) {
if (root.Bokeh !== undefined) {
embed_document(root);
clearInterval(timer);
}
attempts++;
if (attempts > 100) {
console.log("Bokeh: ERROR: Unable to embed document because BokehJS library is missing")
clearInterval(timer);
}
}, 10, root)
}
})(window);
});
};
if (document.readyState != "loading") fn();
else document.addEventListener("DOMContentLoaded", fn);
})();
</script>
Div:
<div class="bk-root">
<div class="bk-plotdiv" id="46dc4d68-63cf-4288-aa48-347675c82cc3"></div>
</div>
You can add .bk-root { height: auto; } (or initial) to your CSS to fix this. Bokeh sets width and height of its root element to 100%, to allow scale_* and stretch_* sizing modes to fill maximum available space. However, aspect ratio preserving modes may give you results like what's observed here.
You have set the plot sizing_mode to be "scale_width" which is explicitly a direction to Bokeh to take as much horizontal space as possible. Perhaps you want the default sizing mode "fixed".
check the docs about border
from bokeh.plotting import figure, output_file, show
output_file("border.html")
p = figure(plot_width=400, plot_height=400)
p.border_fill_color = "whitesmoke"
p.min_border_left = 80
p.circle([1,2,3,4,5], [2,5,8,2,7], size=10)
show(p)
Figured it out. I inspected my HTML and noticed that I had the issue as described here. I had Unicode Character 'ZERO WIDTH NO-BREAK SPACE' (U+FEFF) at the top of my templates (&#65279).
Here's an image of the Inspect element: img
I had to open my html templates in Notepad++ and convert from UTF-8 BOM to UTF-8 (under the "Encoding" menu).

KO: Error when parsing JSON

Created a JSBin that demostrated the problem: http://jsbin.com/kukehoj/1/edit?html,js,console,output
I'm creating my first REST-powered website. The backend is in Python (Django REST Framework), and seems to be working fine. I'm trying to make the front-end get the comments for the posts, but its not working.
HTML Imports
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.1/knockout-min.js"></script>
scripts.js
function Comment(data) {
this.body = ko.observable(data.responseText)
}
function Post(data) {
this.title = ko.observable(data.title)
this.body = ko.observable(data.body)
var self = this;
self.comments = ko.observableArray([])
self.comments(($.map(data.comments, function(link) { // Map the data from
return $.getJSON(link, function(data) { return new Comment(data)}) //These requests
})))
}
function PostViewModel() {
// Data
var self = this;
self.posts = ko.observableArray([])
// Get the posts and map them to a mappedData array.
$.getJSON("/router/post/?format=json", function(allData) {
var mappedData = $.map(allData, function(data) { return new Post(data)})
self.posts(mappedData)
})
}
ko.applyBindings(new PostViewModel());
Server data:
[{ "title":"-->Title here<--",
"body":"-->Body here<--",
"comments":[
"http://127.0.0.1:8000/router/comment/6/?format=json",
"http://127.0.0.1:8000/router/comment/7/?format=json",
"http://127.0.0.1:8000/router/comment/8/?format=json",
"http://127.0.0.1:8000/router/comment/9/?format=json"]
}]
where each of the links leeds to:
{"body":"-->Body here<--"}
index.html
<div class="col-lg-7" data-bind="foreach: { data: posts, as: 'posts' }">
<h3 data-bind="text: title"></h3>
<p data-bind="text: body"> </p>
<span data-bind="foreach: { data: comments(), as: 'comments' }">
<p data-bind="text: comments.body"></p>
</span>
</div>
(There is a lot more HTML, but i removed the irrelevant parts)
Everything is working fine, except from that the comments seem to be in the wrong format.
The chrome console shows JSON "responseText" bound to each of the comment object values.
Wrong format
I'm sorry if this is a stupid question, but I have tried everything - but it doesn't work. (I'm a noob)
There is nothing wrong with your sample code you provided except the part you have this.body = ko.observable(data.responseText) while your data does not contain a responseText in your sample commentData object . if you replace commentData object with var commentData = {"responseText":"-->Body here<--"} it works.
Note: this part
<span data-bind="foreach: { data: comments(), as: 'comments' }">
<p data-bind="text: comments.body"></p> // comments.body => body
</span>
on your question is wrong but you have it correct on your sample code .It should be
<span data-bind="foreach: { data: comments(), as: 'comments' }">
<p data-bind="text: body"></p>
</span>
Here is a working version of your sample :https://jsfiddle.net/rnhkv840/26/
I assume you are using Django Rest Framework, so the JSON structure you get for your posts is done automatically by your serializer based on your model fields.
Back to the frontend, I have not used knockout js before, but what you require is to load the comments using another controller. Either you do it one by one using the links provided by your main resource (this can result in lots of queries sometimes), or you create a filter on your comments endpoint which will allow you to retrieve comments for a specific post.
Ever considered using the django REST framework? It can help you serialize all you models with a simple viewset. Check out the docs.
So found the actual problem. The way the JavaScript read the data from the server, ment that since there was only one value for the comments, the data property of a comment was the variable storing the body of the comment. Not the data.body.

Flask and Jinja2 url_for error - concatenating json object into url_for

I am using Flask with Jinja2 and MapBox on a project which involves plotting data on a map using GeoJSON derived from model data. Example of how this is loaded:
$.getJSON("{{ url_for(".geojson") }}", function(data) {
var geojson = L.geoJson(data, {
onEachFeature: function (feature, layer) {
//do stuff
}
});
markers.addLayer(geojson);
var map = L.map('map', {maxZoom: 9, minZoom: 3}).fitBounds(markers.getBounds());
baseLayer.addTo(map);
markers.addTo(map);
An example of using this JSON data within my JS:
var feature = e.layer.feature;
//print item name
console.log(feature.properties.name)
//print item latitude
console.log(feature.properties.latitude)
//print item category info
console.log(feature.properties.category.name)
This works great. My dataset has now extended to include image urls (example 09379_580_360.jpg), and the images themselves are hosted in a static/images/eol folder. I'd like to include these as an image within a DIV, of which I am setting dynamically via JS like so...
var commoncontent = '<div class="panel-heading"><h3>'+feature.properties.name+'</h3></div>'
$('#common').html(commoncontent)
However, when I attempt to concatenate my image data into jinja's url_for...
var commoncontent = '<div><img src="{{ url_for("static", filename="images/eol/thumbs/big/'+feature.properties.category.localimageurl.jpg+'") }}"></div>'
... I get this error in my console
GET http://127.0.0.1:5000/static/images/eol/thumbs/big/feature.properties.category.localimageurl.jpg 404 (NOT FOUND)
I know that feature.properties.category.localimageurl is correct as it prints to my console when I console.log() it. However, I have no idea why the interpreter is taking it directly as a string and not concatenating it?
feature is a JavaScript object. Jinja doesn't have access to those; it runs on the server, whereas your JavaScript runs in the client. feature doesn't exist when your template is rendered. You will need to handle the concatenation with JavaScript.
var commoncontent = '<div><img src="{{ url_for("static", filename="images/eol/thumbs/big/") }}' + feature.properties.category.localimageurl.jpg + '"></div>'

drop down with onchange events

I have a dropdown with onchange events. When ever user change the drop down box then ajax called and result displayed. All this working fine.
I have following list in template.
Apple
Pineapple
When ever user changed the drop down value. Then result merged
Orange
Graps
Apple
Pineapple
But output should be like this.
Orange
Graps
I have no idea what to do. Please help me. Here is my base. Here is my movie_list template. Here is my movie_sort template. And here is my view . Thanks :-)
UPDATE: Here is ajax code.
function sortMovie(str)
{
if (str=="")
{
document.getElementById("txtHint").innerHTML="";
return;
}
if (window.XMLHttpRequest)
{// code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp=new XMLHttpRequest();
}
else
{// code for IE6, IE5
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.onreadystatechange=function()
{
if (xmlhttp.readyState==4 && xmlhttp.status==200)
{
document.getElementById("txtHint").innerHTML=xmlhttp.responseText;
}
}
xmlhttp.open("GET","/moviesort/?q="+str,true);
xmlhttp.send();
}
DROP DOWN
<select name="category" onchange="sortMovie(this.value)">
<option value="">Choose</option>
{% for category in categories %}
<option value="{{ category.id}}">{{category.name}}</option>
{% endfor %}
</select>
if you are using jQuery to call ajax, I would recommend you use the .empty() straight after the .change() function.
I have no idea what yours looks like, although it should be something like this.
$('#id_category').change(function() {
/* Set your string to nothing */
var str = "";
/* once your dropdown is changed, it performs a look and gets the new data */
$("#id_category option:selected").each(function () {
/* Before inserting the new data, empty out all the old items */
$('#id_sub_category').empty();
str += $(this).val();
$.get('{% url gear_category %}', { category: str }, function(data){
$(data).appendTo("#id_sub_category");
});
});
});
Hope this helps.
I agree with ApPel on this.
However, since you aren't using jQuery...
You'll either have to loop through each option in the select and remove as you go, or I suppose if pure javascript supports you might be able to mimic the jQuery code, but with the javascript syntax.
Either way, you'll have to remove the old values before appending the new ones. That's the problem.

Categories