So, I am building a web app that uses google maps's API to display a map with some markers on it, every time you click on a marker, it is supposed to show 5 articles from that location, these provided by google news, the thing is, I am pretty new to javascript and web development and I've been trying for hours and hours to solve this problem on my own and I don't know how. I'll leave both my JS file and my python file that uses flask to handle server requests, but I'm really sure there's nothing wrong in the server side.
js:
// Google Map
var map;
// markers for map
var markers = [];
// info window
var info = new google.maps.InfoWindow();
// execute when the DOM is fully loaded
$(function() {
// styles for map
// https://developers.google.com/maps/documentation/javascript/styling
var styles = [
// hide Google's labels
{
featureType: "all",
elementType: "labels",
stylers: [
{visibility: "off"}
]
},
// hide roads
{
featureType: "road",
elementType: "geometry",
stylers: [
{visibility: "off"}
]
}
];
// options for map
// https://developers.google.com/maps/documentation/javascript/reference#MapOptions
var options = {
center: {lat: 42.3770, lng: -71.1256}, // Cambridge, MA
disableDefaultUI: true,
mapTypeId: google.maps.MapTypeId.ROADMAP,
maxZoom: 14,
panControl: true,
styles: styles,
zoom: 13,
zoomControl: true
};
// get DOM node in which map will be instantiated
var canvas = $("#map-canvas").get(0);
// instantiate map
map = new google.maps.Map(canvas, options);
// configure UI once Google Map is idle (i.e., loaded)
google.maps.event.addListenerOnce(map, "idle", configure);
});
/**
* Adds marker for place to map.
*/
function addMarker(place)
{
// initialize marker
var marker = new google.maps.Marker({
position: {lat: place.latitude, lng: place.longitude},
map: map,
title: place.place_name + ', ' + place.admin_name1,
icon: "http://maps.google.com/mapfiles/kml/pal2/icon31.png"
});
// add marker with its place to markers array
markers.push({marker, place});
index = markers.length - 1
// add event listener to the marker
google.maps.event.addListener(markers[index].marker, 'click', showArticles(markers[index]))
}
/**
* Gets the articles to be displayed
*/
function showArticles(local)
{
var parameters = {
geo: local.place.place_name
};
// get articles for the place
json = $.getJSON(Flask.url_for("articles"), parameters);
// store those articles in a string containing html
html = "<ul>"
json.done(function (){
if(json.responseJSON){
for (var i = 0; i < json.responseJSON.length; i++){
html += "<li><a src=\"" + json.responseJSON[i].link + "\">" + json.responseJSON[i].title + "</a></li>";
}
html += "</ul>"
console.log(json.responseJSON)
console.log(html)
showInfo(local.marker, html)
}}).fail(function (){
showInfo(local.marker, "")
})
}
/**
* Configures application.
*/
function configure()
{
// update UI after map has been dragged
google.maps.event.addListener(map, "dragend", function() {
// if info window isn't open
// http://stackoverflow.com/a/12410385
if (!info.getMap || !info.getMap())
{
update();
}
});
// update UI after zoom level changes
google.maps.event.addListener(map, "zoom_changed", function() {
update();
});
// configure typeahead
$("#q").typeahead({
highlight: false,
minLength: 1
},
{
display: function(suggestion) { return null; },
limit: 10,
source: search,
templates: {
suggestion: Handlebars.compile(
"<div>" +
"{{place_name}}, {{admin_name1}}, {{postal_code}}" +
"</div>"
)
}
});
// re-center map after place is selected from drop-down
$("#q").on("typeahead:selected", function(eventObject, suggestion, name) {
// set map's center
map.setCenter({lat: parseFloat(suggestion.latitude), lng: parseFloat(suggestion.longitude)});
// update UI
update();
});
// hide info window when text box has focus
$("#q").focus(function(eventData) {
info.close();
});
// re-enable ctrl- and right-clicking (and thus Inspect Element) on Google Map
// https://chrome.google.com/webstore/detail/allow-right-click/hompjdfbfmmmgflfjdlnkohcplmboaeo?hl=en
document.addEventListener("contextmenu", function(event) {
event.returnValue = true;
event.stopPropagation && event.stopPropagation();
event.cancelBubble && event.cancelBubble();
}, true);
// update UI
update();
// give focus to text box
$("#q").focus();
}
/**
* Removes markers from map.
*/
function removeMarkers()
{
for(var i = 0; i < markers.length; i++){
markers[i].marker.setMap(null)
}
markers = []
}
/**
* Searches database for typeahead's suggestions.
*/
function search(query, syncResults, asyncResults)
{
// get places matching query (asynchronously)
var parameters = {
q: query
};
$.getJSON(Flask.url_for("search"), parameters)
.done(function(data, textStatus, jqXHR) {
// call typeahead's callback with search results (i.e., places)
asyncResults(data);
})
.fail(function(jqXHR, textStatus, errorThrown) {
// log error to browser's console
console.log(errorThrown.toString());
// call typeahead's callback with no results
asyncResults([]);
});
}
/**
* Shows info window at marker with content.
*/
function showInfo(marker, content)
{
// start div
var div = "<div id='info'>";
if (typeof(content) == "undefined")
{
// http://www.ajaxload.info/
div += "<img alt='loading' src='/static/ajax-loader.gif'/>";
}
else
{
div += content
}
// end div
div += "</div>";
// set info window's content
info.setContent(div);
// open info window (if not already open)
info.open(map, marker);
}
/**
* Updates UI's markers.
*/
function update()
{
// get map's bounds
var bounds = map.getBounds();
var ne = bounds.getNorthEast();
var sw = bounds.getSouthWest();
// get places within bounds (asynchronously)
var parameters = {
ne: ne.lat() + "," + ne.lng(),
q: $("#q").val(),
sw: sw.lat() + "," + sw.lng()
};
$.getJSON(Flask.url_for("update"), parameters)
.done(function(data, textStatus, jqXHR) {
// remove old markers from map
removeMarkers();
// add new markers to map
for (var i = 0; i < data.length; i++)
{
addMarker(data[i]);
}
})
.fail(function(jqXHR, textStatus, errorThrown) {
// log error to browser's console
console.log(errorThrown.toString());
});
};
Python:
import os
import re
from flask import Flask, jsonify, render_template, request, url_for
from flask_jsglue import JSGlue
from cs50 import SQL
from helpers import lookup
# configure application
app = Flask(__name__)
JSGlue(app)
# ensure responses aren't cached
if app.config["DEBUG"]:
@app.after_request
def after_request(response):
response.headers["Cache-Control"] = "no-cache, no-store, must-revalidate"
response.headers["Expires"] = 0
response.headers["Pragma"] = "no-cache"
return response
# configure CS50 Library to use SQLite database
db = SQL("sqlite:///mashup.db")
@app.route("/")
def index():
"""Render map."""
if not os.environ.get("API_KEY"):
raise RuntimeError("API_KEY not set")
return render_template("index.html", key=os.environ.get("API_KEY"))
@app.route("/articles")
def articles():
"""Look up articles for geo."""
# get geo
geo = request.args.get("geo")
# if there is no geo, return error
if geo == '' or geo == None:
raise RuntimeError("missing geo")
# get articles for this geo
articles = lookup(geo)
if len(articles) > 5:
articles = articles[:5]
# return them as a json object
return jsonify(articles)
@app.route("/search")
def search():
"""Search for places that match query."""
place = request.args.get("q") + "%"
# get everything that mathes this query
places = db.execute("SELECT * FROM places WHERE postal_code LIKE :place OR place_name LIKE :place OR admin_name1 LIKE :place LIMIT 10", place=place)
# return them as objects
return jsonify(places)
@app.route("/update")
def update():
"""Find up to 10 places within view."""
# ensure parameters are present
if not request.args.get("sw"):
raise RuntimeError("missing sw")
if not request.args.get("ne"):
raise RuntimeError("missing ne")
# ensure parameters are in lat,lng format
if not re.search("^-?\d+(?:\.\d+)?,-?\d+(?:\.\d+)?$", request.args.get("sw")):
raise RuntimeError("invalid sw")
if not re.search("^-?\d+(?:\.\d+)?,-?\d+(?:\.\d+)?$", request.args.get("ne")):
raise RuntimeError("invalid ne")
# explode southwest corner into two variables
(sw_lat, sw_lng) = [float(s) for s in request.args.get("sw").split(",")]
# explode northeast corner into two variables
(ne_lat, ne_lng) = [float(s) for s in request.args.get("ne").split(",")]
# find 10 cities within view, pseudorandomly chosen if more within view
if (sw_lng <= ne_lng):
# doesn't cross the antimeridian
rows = db.execute("""SELECT * FROM places
WHERE :sw_lat <= latitude AND latitude <= :ne_lat AND (:sw_lng <= longitude AND longitude <= :ne_lng)
GROUP BY country_code, place_name, admin_code1
ORDER BY RANDOM()
LIMIT 10""",
sw_lat=sw_lat, ne_lat=ne_lat, sw_lng=sw_lng, ne_lng=ne_lng)
else:
# crosses the antimeridian
rows = db.execute("""SELECT * FROM places
WHERE :sw_lat <= latitude AND latitude <= :ne_lat AND (:sw_lng <= longitude OR longitude <= :ne_lng)
GROUP BY country_code, place_name, admin_code1
ORDER BY RANDOM()
LIMIT 10""",
sw_lat=sw_lat, ne_lat=ne_lat, sw_lng=sw_lng, ne_lng=ne_lng)
# output places as JSON
return jsonify(rows)
This is the error that appears on the console when I'm running the page, if needed, I can leave a link to the server where the app is running. enter image description here