PDA

View Full Version : WebKit - accessing javascript results + googleMap



giusepped
15th May 2009, 06:32
I'am triying to get the results of a googlemap function, from the javascript code back to the c++ source code in order to process those results. But no success.
I have an html page which I load inside a class derived from QWebView. The html page I load inside the WebView is:


<script src="http://maps.google.com/maps?file=api&amp;v=2&amp;key=GOOGLE_MAPS_KEY"
type="text/javascript"></script>
<script type="text/javascript">

var map;
var geocoder;
var Lat,Lng;

function initialize()
{
if (GBrowserIsCompatible())
{
map = new GMap2(document.getElementById("map"));
//
map.addControl(new GSmallMapControl);
map.addControl(new GMapTypeControl);
geocoder = new GClientGeocoder();
map.setCenter( new GLatLng(0,0),1 );
GEvent.addListener(map, "click", getAddress);
}

}

function getAddress(overlay, latlng) {

if (latlng != null) {

address = latlng;

geocoder.getLocations(latlng, showAddress);

}
}
function showAddress(response) {
map.clearOverlays();
if (!response || response.Status.code != 200) {
alert("Status Code:" + response.Status.code);
} else {
place = response.Placemark[0];
point = new GLatLng(place.Point.coordinates[1],place.Point.coordinates[0]);
marker = new GMarker(point);
map.addOverlay(marker);
marker.openInfoWindowHtml(
'<b>orig latlng:</b>' + response.name + '<br/>' +
'<b>latlng:</b>' + place.Point.coordinates[1] + "," + place.Point.coordinates[0] + '<br>' +
'<b>Status Code:</b>' + response.Status.code + '<br>' +
'<b>Status Request:</b>' + response.Status.request + '<br>' +
'<b>Address:</b>' + place.address + '<br>' +
'<b>Accuracy:</b>' + place.AddressDetails.Accuracy + '<br>' +
'<b>Country code:</b> ' + place.AddressDetails.Country.CountryNameCode);
Lat = place.Point.coordinates[1];
Lng = place.Point.coordinates[2];
Name= plcae.AddressDetails.Country.AdmninistrativeArea.L ocality;
Map.setValues(Lat, Lng, Name);

}
}
function Open(x,y)
{
map.setCenter( new GLatLng(x,y),13 );
}
</script>

</head>
<body onload="initialize()" onunload="GUnload()" topmargin="0" leftmargin="0">
<div id="map" style="width: 341px; height: 271px"></div>
</body>
</html>


In my c++ code I have


this->page()->mainFrame()->addToJavaScriptWindowObject("Map", this);
connect(page()->mainFrame(), SIGNAL(javaScriptWindowObjectCleared()),
this, SLOT(populateJavaScriptWindowObject()));
.....
void Map::geoCode(QString local)
{
clearCoordinates();
// qDebug()<<"Map finding..."<<local;
QString requestStr( tr("http://maps.google.com/maps/geo?q=%1&output=%2&key=%3")
.arg(local)
.arg("csv")
.arg("GOOGLE_MAPS_KEY") );

manager->get( QNetworkRequest(requestStr) );
++pendingRequests;
}
...
void Map::populateJavaScriptWindowObject()
{
this->page()->mainFrame()->addToJavaScriptWindowObject("Map", this);

}
void Map::setValues(const QString &latitude, const QString &longitude, const QString &n)
{
lat = latitude.toDouble();
lon = longitude.toDouble();
name = n;
qDebug()<<"MAP new values available";
}

So, I am expecting that when I click on a point in the google map the javascript calls the object Map, which I exported with the addToJavaScriptWindowObject.
But, nothing: I double click on a point, the map change but it seems that the setValues faunction is not call at all.
:eek:

tpf80
16th May 2009, 07:59
I had a project doing this very same thing. I was not successful doing it in the way you describe, so I ended up doing it a different way. I found out that Google Maps is sometimes kindof laggy. What happens, is that your page will load, but without the calculated information, then as google catches up, it will then put the values in the page.

How I ended up getting it to work, was making a PHP page, which generated javascript according to variables I sent it:



webView->setUrl(QUrl("http://server/page.php?street=" + street->text() + "&street2=" + street2->text() + "&city=" + city->text() + "&state=" + state->currentText() + "&zip=" + zip->text()));


The javascript on the page was set to it generated a hidden <div> tag in the page, which contained the variables that I wanted to pass to my Qt program.

I created a slot to examine the page with a single shot timer in it.



connect(webView, SIGNAL(loadFinished(bool)), this, SLOT(examineWait(bool)));

void DialogDelivery::examineWait(bool ok) {
QTimer::singleShot(5000, this, SLOT(examinePage()));
}

void DialogDelivery::examinePage() {
//checks if the page has loaded the stuff from google yet
//if it has not found anything, it calls examineWait() again
//so it can see if the page has loaded yet
//also a good idea to keep count somewhere of how many
//times you tried to examine, and if too many times then
//give up because google could have timed out.
}


webView->page()->currentFrame()->toHtml() contents change as the map and geocoding from google loads.

I found that sometimes, google's server is bogged down and never returns results, or takes several seconds.

So even though the loadFinished(bool) signal is fired, google's map/geocoding may not have finished yet. This is why I use the single shot timer, to give google time to catch up, and do "tries" of seeing if google is done loading, so that I can give the user results as fast as possible when google finally does return the results.

After 10 or so tries, I make the program give up trying to examine the loaded page, as google probably will not return results at all if it hasn't within 50 seconds.

*edit*
Also, one other thing I thought of, was that the API key also is bound to a domain. Depending on where google thinks your page loads from, you may have trouble getting results.

giusepped
17th May 2009, 16:28
From what you are saying, I guess that it should be better to switch to another way to get coordinates/city data worldwide.
My application is very simple: I need to konw the coordinates of a city worldwide, and viceversa (from coordinates to city name).
Are you aware of other methods to perform the same task?
Bye

tpf80
17th May 2009, 21:03
How is your app going to use the coordinate/city name combo?

For example, are you expecting the user to click on the map, and then have something happen with the coordinate/city name? Or is the end result not using the map at all, and just doing something with the coordinate/city names?

tpf80
18th May 2009, 00:33
Actually I played around with your code for a bit, and I was able to get the values to come into the Qt Program what I did was in the html I created a function for example:



//javascript added to your web code
function getlat(){
return Lat;
}


Then in the Qt Program I made a function:


void someSlot() {
QString javascript = QString("getlat()");
QVariant latfromjavapage = webView->page()->currentFrame()->evaluateJavaScript(javascript);
}

the variable latfromjavapage did contain the latitude.

giusepped
18th May 2009, 00:35
Well, I need the coordinates of the city.
So for example, If I type "Paris" I need back paris' coordinates.
On the other hand, I wish to have a feature like that: if the user move a point on the map near Paris I would have back the point's coordinates.
Without this feature, the map would be not useful.

tpf80
18th May 2009, 03:03
you can for example do something like this:



// here you can enter an address and your web page evaluates it:
//put this in some slot connected to the button in the UI to find the city
QString javascript = QString("geocoder.getLocations(\"" + street->text() + " " + street2->text() + ", " + city->text() + ", " + state->currentText() + ", " + zip->text() + "\", showAddress);");
webView->page()->currentFrame()->evaluateJavaScript(javascript);
}

//now your coordinates are in Lat and Lng in the page;
// they also are there if you just click on the map

//so now all you have to do, is check them after they are loaded
// i use a 1 second single shot timer for this (call this from the click slot
// and from the button slot that inputs the address)
//and you can use them:

QTimer::singleShot(1000, this, SLOT(someSlot()));

void someSlot() {
QString javascript = QString("getlat()");
QVariant latfromjava = webView->page()->currentFrame()->evaluateJavaScript(javascript);
javascript = QString("getlng()");
QVariant lngfromjava = webView->page()->currentFrame()->evaluateJavaScript(javascript);
}


in your html you add these 2 pieces of code:


//javascript added to your web code
function getlat(){
return Lat;
}

function getlng(){
return Lng;
}


so what ends up happening, is:

If you click on the map, then Lat and Lng are filled with data (and the popup shows the info on the map), the single shot timer triggers, and the data is gathered into your Qt program.

If you enter an address in the GUI, then when you click the submit button, it forces the address into the getlocation function, which in turn shows the bubble of info on the map at that location, and triggers the single shot timer causing the returned lat/lng to be in your Qt program.

giusepped
18th May 2009, 04:06
Thank you.
I just did what you suggested, but do not work
But I do not understand where and how to call singleShot.


connect(this,SIGNAL(loadFinished(bool)),SLOT(waitP age(bool )));

void Map::waitPage(bool ok)
{
QTimer::singleShot(1000,this,SLOT(examinePage()));
}
void Map::examinePage()
{
QString javascript = QString("getlat()");
QVariant latfromjava = page()->currentFrame()->evaluateJavaScript(javascript);
javascript = QString("getlng()");
QVariant lngfromjava = page()->currentFrame()->evaluateJavaScript(javascript);
qDebug()<<latfromjava<<lngfromjava;

}

tpf80
18th May 2009, 07:31
class DialogMap: public QDialog, private Ui::DialogMap {

Q_OBJECT

public:
//functions:
DialogMap(QWidget *parent = 0);

//variables:

private slots:
void calcMap();
void examineWait();
void examinePage();

private:
bool examineSuccess;
int examineTries;
bool examinetimedout;

};

//setup interface of main window:
DialogMap::DialogMap(QWidget *parent) : QDialog(parent) {


examineSuccess = false;
examinetimedout = false;
examineTries = 10;


setupUi(this);


//if we click on the button in the GUI we call that slot:

connect(calcButton, SIGNAL(clicked()), this, SLOT(calcMap()));

//this is to make sure the program examines the script after the map is first loaded:

connect(webView, SIGNAL(loadFinished(bool)), this, SLOT(examineWait()));
///////////////////////////////////////////////////////////////////
//also can have other slots here, such as a "clicked" signal connecting to examinewait
//for when the map is clicked by the user
//or any other signal that might trigger you to examine the lat/lng in the map
//////////////////////////////////////////////////////////////////////



//load the view with the map page:
webView->setUrl(QUrl("http://domain/map.html"));



}


//make the map process the GUi vars we pass to the javascript:
void DialogMap::calcMap() {
//reset success to false because we have not examined it yet:
examineSuccess = false;
examinetimedout = false;
examineTries = 10;

//input the vars from our GUI into the google javascript
QString javascript = QString("geocoder.getLocations(\"" + street->text() + " " + street2->text() + ", " + city->text() + ", " + state->currentText() + ", " + zip->text() + "\", showAddress);");
webView->page()->currentFrame()->evaluateJavaScript(javascript);
}

//examine the map now that we sent an address to it
examineWait();

}



//single shot timer, to wait to examine the map (lets google catch up)
//tune the time variable based on your load times
void DialogMap::examineWait() {
QTimer::singleShot(1000, this, SLOT(examinePage()));
}


void DialogMap::examinePage() {

QString javascript = QString("getlat()");
QVariant lat = webView->page()->currentFrame()->evaluateJavaScript(javascript);

javascript = QString("getlng()");
QVariant lng = webView->page()->currentFrame()->evaluateJavaScript(javascript);

examineTries--;

//if we got results, then we can stop trying:
if ((examineSuccess == false)&&(lat.toString() != "")&&(lng.toString() != "")) {

//reset our tries for the next time the user clicks/requests an address

examineSuccess = true;
examineTries = 10;

//since we grabbed the lat and lng, reset them to "", so we
//don't pull them again out of the page until user selects a new
//point
javascript = QString("Lat = \"\";\nLng = \"\";");
webView->page()->currentFrame()->evaluateJavaScript(javascript);

////////////////////////////////////////////
//do something with your lat and lng vars here
////////////////////////////////////////////

//if we found nothing then try again until examinetries has 0 left
} else {
//if we have tries left, then we try again:
if ((examineSuccess == false)&&(examineTries > 0)) {
examineWait();
}
if ((examineSuccess == false)&&(examineTries == 0)) {
if (!examinetimedout) {
//qDebug()<< "Timed out!";
QMessageBox::critical(0, qApp->tr("Map Timed Out!"),

qApp->tr("Loading Google Map timed out!\n\nPlease click \"Calculate >>\" to try again."), QMessageBox::Ok,

QMessageBox::NoButton);
examinetimedout = true;
}
}
}

}

giusepped
19th May 2009, 02:01
Did you try your code?
G

tpf80
30th May 2009, 00:01
Yes and it works. The example I posted is a good start but not totally complete. You would need to make the UI file in designer, add the code in main to exec the dialog, and of course some code to do something with the data you gathered.