In my application, I have a form on one side and google maps on the other side. To display google maps, I'm using their javascript api. The javascript is writing as a string and part of the html which gets gets called by QWebView. My objective is for the user to click and drag the pin. After the pin has stopped dragging, it would update 2 text boxes on the right side through qt which will populate with the latitude and longitude of the dropped pin. I am having trouble figuring out how to send the data between javascript and python. I am using python3 with pyside qt bindings.
Here is what I have so far.
webView = QWebView()
webView.setHtml(html)
self.central_widget_grid.addWidget(webView,1,0)
Html is a constant defined in another file
#!/usr/bin/python
jscode = """
var map;
var marker;
function initMap() {
map = new google.maps.Map(document.getElementById('map'), {
center: {lat: 40.793697, lng: -77.8586},
zoom: 10
});
map.addListener('click', function(e) {
placeMarkerAndPanTo(e.latLng, map);
});
}
function placeMarkerAndPanTo(latLng, map) {
if (marker === undefined) {
marker = new google.maps.Marker({
position: latLng,
map: map,
title: "Station Location",
draggable: true
});
map.panTo(latLng);
marker.addListener('dragend', function() { draggedMarker(); });
}
}
function draggedMarker() {
alert(marker.getPosition());
statLoc.updateLoc(marker.getPosition().lat(), marker.getPosition().lng());
}
"""
html = """<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
<style type="text/css">
html { height: 100% }
body { height: 100%; margin: 0px; padding: 0px }
#map_canvas { height: 100% }
</style>
<script type="text/javascript"
src="http://maps.google.com/maps/api/js?sensor=false&callback=initMap">
</script>
<script type="text/javascript">""" + jscode + """
</script>
</head>
<body onload="initMap();">
<div id="map" style="width:100%; height:100%"></div>
</body>
</html>"""
I have tried creating a class which holds the latitude and longitude and then passing that by calling addToJavaScriptWindowObject.
class StationLocation(QObject):
latitude = 0.0
longitude = 0.0
def __init__(self):
super(StationLocation, self).__init__()
def updateLoc(self,lat,long):
self.latitude = lat
self.longitude = long
print(self.latitude, self.longitude)
With the following changes to my webView
webView = QWebView()
webView.setHtml(html)
frame = webView.page().mainFrame()
frame.addToJavaScriptWindowObject('statLoc', self.station_location)
self.central_widget_grid.addWidget(webView, 1, 0)
With that added. With the print statement inside StationLocations, I expect to see the latitude and longitude printed in the console every time that function is called. I can't find out why this isn't the case.
There are two things you are doing wrong. Firstly, you need to wait until the page is loaded before adding the object. Secondly, the javascript must only call methods of the added object that are decorated as slots.
Below is a working demo. But one caveat: the PySide implementation of addToJavaScriptWindowObject is buggy. It should be possible to use self (i.e. the main window) as the added object, but when I try that with PySide, it hangs on exit for several seconds and then dumps core. For this reason, I used a proxy object in the demo - but with PyQt, the proxy wouldn't be needed.
import sys
from PySide import QtCore, QtGui, QtWebKit
html = '''
<html><head>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
<style type="text/css">
html { height: 100% }
body { height: 100%; margin: 0; padding: 0 }
#map { width: 100%; height: 100% }
</style>
<script src="https://maps.googleapis.com/maps/api/js"></script>
<script type="text/javascript">
var map, marker
function initialize() {
map = new google.maps.Map(document.getElementById("map"), {
center: {lat: 40.793697, lng: -77.8586},
zoom: 10
})
marker = new google.maps.Marker({
map: map,
position: map.getCenter(),
draggable: true
})
marker.addListener("dragend", function () {
var pos = marker.getPosition()
qt.showLocation(pos.lat(), pos.lng())
console.log("dragend: " + pos.toString())
})
}
google.maps.event.addDomListener(window, "load", initialize)
</script>
</head>
<body><div id="map"/></body>
</html>
'''
class WebPage(QtWebKit.QWebPage):
def javaScriptConsoleMessage(self, message, line, source):
if source:
print('line(%s) source(%s): %s' % (line, source, message))
else:
print(message)
class Proxy(QtCore.QObject):
#QtCore.Slot(float, float)
def showLocation(self, latitude, longitude):
self.parent().edit.setText('%s, %s' % (latitude, longitude))
class MainWindow(QtGui.QWidget):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.view = QtWebKit.QWebView(self)
self.view.setPage(WebPage(self))
self.edit = QtGui.QLineEdit(self)
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.view)
layout.addWidget(self.edit)
self.map = self.view.page().mainFrame()
self.map.loadFinished.connect(self.handleLoadFinished)
self.view.setHtml(html)
self._proxy = Proxy(self)
def handleLoadFinished(self, ok):
self.map.addToJavaScriptWindowObject('qt', self._proxy)
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
window = MainWindow()
window.setGeometry(500, 300, 800, 600)
window.show()
sys.exit(app.exec_())
Related
I have a KMZ file that i load into my google maps application via link using javascript. The file works perfectly in Google Earth. The problem is in my application when i click in one of the many elements (areas): the returned description data is always from only one of the elements, not displaying the actual clicked, correct, element. This is what i've tried:
Check if the click event in the map is correct by placing a marker in the clicked position, it is correct.
Convert the data into KML using Google Earth, place it into my google drive as public, and using a direct download link from google drive in my application. It displayed the data but the error continued.
Created the most basic/blank application using just the layer to make sure anything else in my other application is interfering. Also didn't work.
The file is in this website: https://www.voanaboa.pt/codigo-drone named as "Regulamento RPA_ver_5.0.kmz”
Here's the only file that creates a basic application using the kmz file, i've removed my API key for privacy.
<!DOCTYPE html>
<html>
<head>
<title>Simple Map</title>
<meta name="viewport" content="initial-scale=1.0">
<meta charset="utf-8">
<style>
/* Always set the map height explicitly to define the size of the div
* element that contains the map. */
#map {
height: 100%;
}
/* Optional: Makes the sample page fill the window. */
html, body {
height: 100%;
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<div id="map"></div>
<script>
var map;
function initMap() {
map = new google.maps.Map(document.getElementById('map'), {
center: {lat: -34.397, lng: 150.644},
zoom: 8
});
var kmlLayer = new google.maps.KmlLayer();
var src = 'https://www.voanaboa.pt/Files/downloads/Regulamento-RPA-ver-5.0.kmz';
var kmlLayer = new google.maps.KmlLayer(src, {
//suppressInfoWindows: true,
preserveViewport: false,
map: map
});
}
</script>
<script src="https://maps.googleapis.com/maps/api/js?key=MY_API_KEY&libraries=geometry&callback=initMap"
async defer></script>
Most (but not all) of your placemarks have the same ID "ID_00000"). If I change that to be unique, the polygon's descriptions become unique:
example with unique ids
Per the KML reference, that doesn't have to be unique (it is a "stanard XML ID", but I am guessing the rendering code is assuming it is.
code snippet with updated kmz file:
/* Always set the map height explicitly to define the size of the div
* element that contains the map. */
#map {
height: 100%;
}
/* Optional: Makes the sample page fill the window. */
html,
body {
height: 100%;
margin: 0;
padding: 0;
}
<div id="map"></div>
<script>
var map;
function initMap() {
map = new google.maps.Map(document.getElementById('map'), {
center: {
lat: -34.397,
lng: 150.644
},
zoom: 8
});
var kmlLayer = new google.maps.KmlLayer();
var src = 'http://www.geocodezip.com/geoxml3_test/kmz/Regulamento-RPA-ver-5.0a.kmz';
var kmlLayer = new google.maps.KmlLayer(src, {
//suppressInfoWindows: true,
preserveViewport: false,
map: map
});
}
</script>
<script src="https://maps.googleapis.com/maps/api/js?libraries=geometry&callback=initMap" async defer></script>
I have a challenge of trying to embed the Google StreetView for a specific location in on a web page. Normally, I would just use Google's iFrame embed but the platform on which the page is hosted doesn't allow iFrames.
So if I can't use an iFrame, I have to go the traditional way of using a pure HTML document and Google's true StreetView embed approach. That's fine except that I want to parameterize the URL so that the user can pass in a query string to tell the StreetView what location to display.
http://my.site.com/streetview.html&y=37.869260&x=-122.254811
Google's StreetView embed document gives me a great example of dropping StreetView right into an HTML Document.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Street View</title>
<style>
html, body {
height: 100%;
margin: 0;
padding: 0;
}
#street-view {
height: 100%;
}
</style>
</head>
<body>
<div id="street-view"></div>
<script>
var panorama;
function initialize() {
panorama = new google.maps.StreetViewPanorama(
document.getElementById('street-view'),
{
position: {lat: 37.869260, lng: -122.254811},
pov: {heading: 165, pitch: 0},
zoom: 1
});
}
</script>
<script async defer
src="https://maps.googleapis.com/maps/api/js?key=[YourAPIKey]&callback=initialize">
</script>
</body>
</html>
I'm trying to modify the Script section to write the values passed to the 'y' and 'x' query strings into the StreetView element's 'lat' and 'lng' variables so that the StreetView will always be at whatever location I pass in as the y and x query strings in the URL.
<script>
var panorama;
function initialize() {
panorama = new google.maps.StreetViewPanorama(
document.getElementById('street-view'),
{
position: {lat: 37.869260, lng: -122.254811},
pov: {heading: 165, pitch: 0},
zoom: 1
});
}
</script>
If I got your question right, you want to get the lattitude and longitude values from Query string.
First, you need to get query params, add this function-
function getParameterByName(name, url) {
if (!url) url = window.location.href;
name = name.replace(/[\[\]]/g, "\\$&");
var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)", "i"),
results = regex.exec(url);
if (!results) return null;
if (!results[2]) return '';
return decodeURIComponent(results[2].replace(/\+/g, " "));
}
Then modify your script as follows -
<script>
var panorama;
var latt,longg;
latt = parseFloat(getParameterByName('y'));
longg = parseFloat(getParameterByName('x'));
function initialize() {
panorama = new google.maps.StreetViewPanorama(
document.getElementById('street-view'),
{
position: {lat: latt, lng: longg},
pov: {heading: 165, pitch: 0},
zoom: 1
});
}
</script>
Let me know If I misunderstood your question.
I'm making a webapp that allows people to see the location of buses on Google maps. I'm having some problems with global variables in JavaScript. window.variable doesn't work for me. Neither does defining the variable outside the all the functions works. Here is my complete client side code:
<!DOCTYPE html>
<html>
<head>
<title>Geolocation</title>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no">
<meta charset="utf-8">
<style>
html, body, #map-canvas {
height: 100%;
margin: 0px;
padding: 0px
}
</style>
<script src="https://maps.googleapis.com/maps/api/js?v=3.exp"></script>
<script src="http://code.jquery.com/jquery-1.11.0.min.js"></script>
<script>
// Note: This example requires that you consent to location sharing when
// prompted by your browser. If you see a blank space instead of the map, this
// is probably because you have denied permission for location sharing.
var old = [];
function getLocation()
{
$.get( "http://54.86.161.214/EC_bus_app/get_location.php", function( data ) {
old=[];
var buses = data;
var number_of_buses = buses.slice(0,1);
buses = buses.slice(2);
buses = buses.slice(0,-1);
var bus_coordinates_and_numbers = buses.split(/[ ]+/);
var length_of_array = bus_coordinates_and_numbers.length;
// Turn a single dimensional array into a multi-dimensional array
for (var index = 0; index < bus_coordinates_and_numbers.length; index+= 3)
old.push( bus_coordinates_and_numbers.slice(index, index + 3) );
console.log(old);
//initialize(old);
});
}
setInterval(getLocation, 10000);
var map;
function initialize() {
var mapOptions = {
zoom: 18
};
map = new google.maps.Map(document.getElementById('map-canvas'),
mapOptions);
var image = "icon_97.png";
for (i=0;i<old.length; i = i + 1){
var x = old[i][0];
var y = old[i][1];
var myLatlng = new google.maps.LatLng(x,y);
var busMarker = new google.maps.Marker({
position: myLatlng,
map: map,
icon: image,
title: old[i][2]
});
}
// Try HTML5 geolocation
if(navigator.geolocation) {
navigator.geolocation.getCurrentPosition(function(position) {
var pos = new google.maps.LatLng(position.coords.latitude,
position.coords.longitude);
var infowindow = new google.maps.InfoWindow({
map: map,
position: pos,
content: 'Your location'
});
map.setCenter(pos);
}, function() {
handleNoGeolocation(true);
});
} else {
// Browser doesn't support Geolocation
handleNoGeolocation(false);
}
}
function handleNoGeolocation(errorFlag) {
if (errorFlag) {
var content = 'Error: The Geolocation service failed.';
} else {
var content = 'Error: Your browser doesn\'t support geolocation.';
}
var options = {
map: map,
position: new google.maps.LatLng(60, 105),
content: content
};
var infowindow = new google.maps.InfoWindow(options);
map.setCenter(options.position);
}
google.maps.event.addDomListener(window, 'load', initialize);
</script>
</head>
<body>
<div id="map-canvas"></div>
</body>
</html>
The variable in question is old. I can pass it to the initialize function from within the get location function using initialize(old);. However, as you can see, I'm using a timer, and initialize(old); causes the entire map to reload again and again, whereas I only want the location markers to load again and again.
Solved the problem by moving getLocation() inside initialize(). But what #geocodezip said in the comments also answers my question completely.
Your problem is not with global variables. It is with asynchronous functions. You need to initialize the map, then request data (asynchronous request), make markers on the map, then periodically update them.
I have some working code to display a KML layer on Google maps. When you click on the various parts of the layer their respective name pops up in an info window.
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
<style type="text/css">
html { height: 100% }
body { height: 100%; margin: 0; padding: 0 }
#map-canvas { height: 100% }
</style>
<script type="text/javascript"
// Load Google Maps API
src="https://maps.googleapis.com/maps/api/js?key=XXXXXXXXXXX&sensor=false">
</script>
<script type="text/javascript">
function initialize() {
var myLatlng = new google.maps.LatLng(51.875696, -0.624207);
var mapOptions = {
zoom: 6,
center: myLatlng
};
var map = new google.maps.Map(document.getElementById('map-canvas'),
mapOptions);
var kmlLayer = new google.maps.KmlLayer({
url: 'http://XXXXXXX.org/gliding/grid3.kml',
suppressInfoWindows: false,
map: map
});
google.maps.event.addListener(kmlLayer, 'click', function(kmlEvent) {
var text = kmlEvent.featureData.name;
showInContentWindow(text);
});
function showInContentWindow(text) {
var sidediv = document.getElementById('content-window');
}
}
google.maps.event.addDomListener(window, 'load', initialize);
</script>
</head>
<body>
<div id="map-canvas" style="width:100%; height:100%; float:left"></div>
</body>
</html>
I would like the info window to also contain a link to a page with the same name as object clicked on. For example if the user clicks on a shape in the KML layer called Tom the info window says Tom Click Here. If the user clicks the link they are taken to www.XYZ.com/Tom.
I'm sure this is quite simple, but I'm new to javascript and can't get it to work.
This is more a hack than a solution (which means google could change a property name and this would stop working.
However, here you go
google.maps.event.addListener(kmlLayer, 'click', function(kmlEvent) {
var text = kmlEvent.featureData.name;
kmlEvent.featureData.infoWindowHtml += 'Click Here';
showInContentWindow(text);
});
I am new to google maps api and am using it to put a map on my wordpress page and get the location of the user. So far, I have kept the following code in my header.php file with myapikey replaced with my actual api key.
<script type="text/javascript"
src="https://maps.googleapis.com/maps/api/js?libraries=places&key=myapikey"></script>
I also have a wordpress page with the following code. This code works when I keep it on a html file. However, when I put this code on my wordpress page, I don't even get the map to show. I am using Google Maps API v3 Geolocation. Could someone please help me.
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
<meta charset="utf-8">
<style type="text/css">
html { height: 100% }
body { height: 100%; margin: 0px; padding: 0 }
#map-canvas { height: 100%; width: 100%; top: -100px;}
</style>
<script type="text/javascript">
var map;
function initialize() {
var mapOptions = {
zoom: 12
};
map = new google.maps.Map(document.getElementById("map-canvas"),
mapOptions);
//Html five geolocation
if(navigator.geolocation) {
navigator.geolocation.getCurrentPosition(function(position) {
var pos = new google.maps.LatLng(position.coords.latitude,
position.coords.longitude);
var infowindow = new google.maps.InfoWindow({
map: map,
position: pos,
content: "HTML5 is used."
});
map.setCenter(pos);
}, function() {
handleNoGeolocation(true);
});
} else {
// Browser doesn't support Geolocation
handleNoGeolocation(false);
}
}
function handleNoGeolocation(errorFlag) {
if (errorFlag) {
var content = 'Error: The Geolocation service failed.';
} else {
var content = 'Error: Your browser doesn\'t support geolocation.';
}
var options = {
map: map,
position: new google.maps.LatLng(60, 105),
content: content
};
var infowindow = new google.maps.InfoWindow(options);
map.setCenter(options.position);
}
google.maps.event.addDomListener(window, 'load', initialize);
</script>
</head>
<body>
<div id="map-canvas"/>
</body>
</html>
You can't paste the complete document into the editor, what you put into the editor is expected to be te content of your page, it may be almost anything that you would put into the of a HTML-page(no html, head and body).
script and style always include either via the header/footer of the theme or via a plugin.