I am using google maps API for calculating a route with waypoints
Sometimes route is correctly calculated but sometimes not
I take some values from Apex (this is a VisualForce Page Salesforce with Javascript)
var wayptsApresMidi = [];
var wayPointApresMidi = {!wayPointsLocationOfPCEApresMidi};
for(var i= 0; i < wayPointApresMidi.length; i++){
wayptsApresMidi.push({
location: new google.maps.LatLng(wayPointApresMidi[i][1], wayPointApresMidi[i][0]),
stopover: true
});
}
console.log('firstLocationOfPCELatitudeApresMidi '+'{!firstLocationOfPCELatitudeApresMidi}');
console.log('firstLocationOfPCELongitudeApresMidi '+'{!firstLocationOfPCELongitudeApresMidi}');
console.log('lastLocationOfPCELatitudeApresMidi '+'{!lastLocationOfPCELatitudeApresMidi}');
console.log('lastLocationOfPCELongitudeApresMidi '+'{!lastLocationOfPCELongitudeApresMidi}');
for(var i=0; i<wayptsApresMidi.length; i++){
console.debug('wayptsApresMidi Longitude and Latitude '+wayptsApresMidi[i].location);
}
directionsServiceApresMidi.route({
origin: new google.maps.LatLng('{!firstLocationOfPCELatitudeApresMidi}', '{!firstLocationOfPCELongitudeApresMidi}'),
destination: new google.maps.LatLng('{!lastLocationOfPCELatitudeApresMidi}','{!lastLocationOfPCELongitudeApresMidi}'),
waypoints: wayptsApresMidi,
optimizeWaypoints: true,
travelMode: 'DRIVING',
unitSystem: google.maps.UnitSystem.METRIC,
language: 'fr'
}, function(response, status) {
if (status === 'OK') {
console.log(response);
for (var i = 0; i < response.routes[0].legs.length; i++)
{
console.log(response.routes[0].legs[i].start_address);
console.log(response.routes[0].legs[i].end_address);
}
});
When not functions the values returned for waypoint_order can be [4294967295, 4294967295] (The number of wayPoints is correct but values are 4294967295)
for (var i = 0; i < response.routes[0].legs.length; i++)
{
console.log(response.routes[0].legs[i].start_address);
console.log(response.routes[0].legs[i].end_address);
}
ten times
38 Rue Verte, xxxx, France
firstPoint/ WAyPoints/ LastPoint have some good values
Furthermore, when the travelMode is WALKING to put travelMode DRIVING can function
(the contrary is true -> when DRIVING is not functionned WALKING can function)
The before weeks it worked always
i don't know if it functions only sometimes because of changes in my code or if Google Maps API has modified something in the process
It can have 16 digits after dot for latitude/ longitude (Is it a problem ?)
Do you have an idea ?
Thanks,
Looks like this is a known issue.
Please see the following google issue tracker.
https://issuetracker.google.com/issues/135627404
Related
I have been working on a end-to-end test using Webdriver I/O from Jasmine. One specific scenario has been giving me significant challenges.
I have a page with 5 links on it. The number of links actually challenges as the page is dynamic. I want to test the links to see if each links' title matches the title of the page that it links to. Due to the fact that the links are dynamically generated, I cannot just hard code tests for each link. So, I'm trying the following:
it('should match link titles to page titles', function(done) {
client = webdriverio.remote(settings.capabilities).init()
.url('http://www.example.com')
.elements('a').then(function(links) {
var mappings = [];
// For every link store the link title and corresponding page title
var results = [];
for (var i=0; i<links.value.length; i++) {
mappings.push({ linkTitle: links.value[0].title, pageTitle: '' });
results.push(client.click(links.value[i])
.getTitle().then(function(title, i) {
mappings[i].pageTitle = title;
});
);
}
// Once all promises have resolved, compared each link title to each corresponding page title
Promise.all(results).then(function() {
for (var i=0; i<mappings.length; i++) {
var mapping = mappings[i];
expect(mapping.linkTitle).toBe(mapping.pageTitle);
}
done();
});
});
;
});
I'm unable to even confirm if I'm getting the link title properly. I believe there is something I entirely misunderstand. I am not even getting each links title property. I'm definately not getting the corresponding page title. I think I'm lost in closure world here. Yet, I'm not sure.
UPDATE - NOV 24
I still have not figured this out. However, i believe it has something to do with the fact that Webdriver I/O uses the Q promise library. I came to this conclusion because the following test works:
it('should match link titles to page titles', function(done) {
var promise = new Promise(function(resolve, reject) {
setTimeout(function() { resolve(); }, 1000);
});
promise.then(function() {
var promises = [];
for (var i=0; i<3; i++) {
promises.push(
new Promise(function(resolve, reject) {
setTimeout(function() {
resolve();
}, 500);
})
);
}
Promise.all(promises).then(function() {
expect(true).toBe(true)
done();
});
});
However, the following does NOT work:
it('should match link titles to page titles', function(done) {
client = webdriverio.remote(settings.capabilities).init()
.url('http://www.example.com')
.elements('a').then(function(links) {
var mappings = [];
// For every link store the link title and corresponding page title
var results = [];
for (var i=0; i<links.value.length; i++) {
mappings.push({ linkTitle: links.value[0].title, pageTitle: '' });
results.push(client.click(links.value[i])
.getTitle().then(function(title, i) {
mappings[i].pageTitle = title;
});
);
}
// Once all promises have resolved, compared each link title to each corresponding page title
Q.all(results).then(function() {
for (var i=0; i<mappings.length; i++) {
var mapping = mappings[i];
expect(mapping.linkTitle).toBe(mapping.pageTitle);
}
done();
});
})
;
});
I'm not getting any exceptions. Yet, the code inside of Q.all does not seem to get executed. I'm not sure what to do here.
Reading the WebdriverIO manual, I feel like there are a few things wrong in your approach:
elements('a') returns WebElement JSON objects (https://code.google.com/p/selenium/wiki/JsonWireProtocol#WebElement_JSON_Object) NOT WebElements, so there is no title property thus linkTitle will always be undefined - http://webdriver.io/api/protocol/elements.html
Also, because it's a WebElement JSON object you cannot use it as client.click(..) input, which expects a selector string not an object - http://webdriver.io/api/action/click.html. To click a WebElement JSON Object client.elementIdClick(ID) instead which takes the ELEMENT property value of the WebElement JSON object.
When a client.elementIdClick is executed, the client will navigate to the page, trying to call client.elementIdClick in the next for loop cycle with next ID will fail, cause there is no such element as you moved away from the page. It will sound something like invalid element cache.....
So, I propose another solution for your task:
Find all elements as you did using elements('a')
Read href and title using client.elementIdAttribute(ID) for each of the elements and store in an object
Go through all of the objects, navigate to each of the href-s using client.url('href'), get the title of the page using .getTitle and compare it with the object.title.
The source I experimented with, not run by Jasmine, but should give an idea:
var client = webdriverio
.remote(options)
.init();
client
.url('https://www.google.com')
.elements('a')
.then(function (elements) {
var promises = [];
for (var i = 0; i < elements.value.length; i++) {
var elementId = elements.value[i].ELEMENT;
promises.push(
client
.elementIdAttribute(elementId, 'href')
.then(function (attributeRes) {
return client
.elementIdAttribute(elementId, 'title')
.then(function (titleRes) {
return {href: attributeRes.value, title: titleRes.value};
});
})
);
}
return Q
.all(promises)
.then(function (results) {
console.log(arguments);
var promises = [];
results.forEach(function (result) {
promises.push(
client
.url(result.href)
.getTitle()
.then(function (title) {
console.log('Title of ', result.href, 'is', title, 'but expected', result.title);
})
);
});
return Q.all(promises);
});
})
.then(function () {
client.end();
});
NOTE:
This fails to solve your problem, when the links trigger navigation with JavaScript event handlers not the href attributes.
Current Neo4J documentation states that to create a relationship, both nodes are locked.
Now consider one node be a master node and all other nodes being created are to be related to it. For example, all new "Animal" nodes need to be related to a master "Zoo" node.
So, when a lot of new "Animal" nodes are being created with relationship to existing "Zoo" node, wouldn't all Cypher requests just queue up waiting on the lock ? Because every request needs to lock the "Zoo" node.
I'm observing slow downs when a lot of data is being created on a graph in this manner.
Is there a way to tell Neo4J to not lock the nodes for a Cypher CREATE request ? Can some sort of parallel writes be enabled ?
Update :
Test Results:
Create Zoo nodes: time taken (ms): 2222
Create Animal nodes for separate Zoos: time taken (ms): 2206
Create Animal nodes for same Zoo: time taken (ms): 9015
There is a difference of about 7 seconds for just 200 simultaneous queries.
Test Script (using NodeJS Driver, neo4j version 2.2.4, ubuntu linux):
NodeJS Driver
Neo4j version 2.2.4, community edition
Ubuntu Linux
var seraph = require('seraph');
var moment = require('moment');
var async = require('async');
var neo4j = seraph({
"url": "http://localhost:7474",
"user": "neo4j",
"pass": "neo4j"
});
var num = 200;
async.series([
function (cb) {
// clean
neo4j.query('MATCH (n:Zoo)-[r:HAS]->(a:Animal) DELETE n,r,a', cb);
},
function (cb) {
// clean
neo4j.query('MATCH (n:Zoo) DELETE n', cb);
},
function (cb) {
// we create Zoo nodes, each with a num property
var start = moment();
var count = 0;
var abort = false;
for (var i = 0; i < num; i++) {
neo4j.query('CREATE (n:Zoo {obj})', { obj: { num: i } }, function (err, nodes) {
if (err) {
cb(err);
abort = true;
} else {
count++;
if (count >= num && !abort) {
console.log('Create Zoo nodes: time taken (ms): ' + moment().diff(start));
cb();
}
}
});
}
},
function (cb) {
// we create (Zoo)-[HAS]->(Animal) nodes, each Animal node related with a SEPARATE Zoo node
var start = moment();
var count = 0;
var abort = false;
for (var i = 0; i < num; i++) {
neo4j.query('MATCH (n:Zoo) WHERE n.num = {num} CREATE (n)-[r:HAS]->(a:Animal) RETURN a LIMIT 1', { num: i }, function (err, nodes) {
if (err) {
cb(err);
abort = true;
} else {
count++;
if (count >= num && !abort) {
console.log('Create Animal nodes for separate Zoos: time taken (ms): ' + moment().diff(start));
cb();
}
}
});
}
},
function (cb) {
// we create (Zoo)-[HAS]->(Animal) nodes, each Animal node related with SAME Zoo node
var start = moment();
var count = 0;
var abort = false;
for (var i = 0; i < num; i++) {
neo4j.query('MATCH (n:Zoo) WHERE n.num = 0 CREATE (n)-[r:HAS]->(a:Animal) RETURN a LIMIT 1', function (err, nodes) {
if (err) {
cb(err);
abort = true;
} else {
count++;
if (count >= num && !abort) {
console.log('Create Animal nodes for same Zoo: time taken (ms): ' + moment().diff(start));
cb();
}
}
});
}
}
], function (err) {
if (err) {
console.error(err);
}
process.exit(0);
});
As it's mentionned on the question Is it possible to override Neo4j lock behavior for relationships? :
It's not possible to override the locking behaviour. Neo4j used to
support multiple isolation levels, so it might be that the word
"default" is from that time and that the page needs an update.
But you can still have better performances using cypher Foreach statement if you need to create N relationships to one node. Also, upgrading to Neo4j 2.3 should be good for you, as an upgrade should ever be (don't forget to set allow_store_upgrade=true)
I'm a bit new to Meteor and something I'm having trouble with is reactive data -- particularly in instances where I need to change the data shown based on a mouse or keyboard event. Doing this kind of stuff the normal js way seems to give me trouble in meteor since everything I change gets re-rendered and reset constantly.
So, I thought I'd see if this would be a case in which I could use Meteor's Deps object, however I can't quite grasp it. Here's the code I'm using:
(function(){
var tenants = [];
var selectedTenant = 0;
var tenantsDep = new Deps.Dependency;
Template.tenantsBlock.tenantsList = function()
{
tenants = [];
var property = $properties.findOne({userId: Meteor.userId(), propertyId: Session.get('property')});
var tenancies = _Utils.resolveTenancies(property, true, null, true);
for(var i = 0; i < tenancies.length; i++)
{
if(tenancies[i].tenancyId == Session.get('tenancy'))
{
tenants = tenants.concat(tenancies[i].otherTenants, tenancies[i].primaryTenant);
}
}
tenants[selectedTenant].selected = 'Selected';
tenantsDep.changed();
return tenants;
};
Template.tenantsBlock.onlyOneTenant = function()
{
tenantsDep.depend();
return tenants.length > 1 ? '' : 'OneChild';
};
Template.tenantsBlock.phoneNumber = function()
{
tenantsDep.depend();
for(var i = 0; i < tenants[selectedTenant].details.length; i++)
if(_Utils.getDynamicContactIconClass(tenants[selectedTenant].details[i].key) == 'Phone')
return tenants[selectedTenant].details[i].value;
return null;
};
Template.tenantsBlock.emailAddress = function()
{
tenantsDep.depend();
for(var i = 0; i < tenants[selectedTenant].details.length; i++)
if(_Utils.getDynamicContactIconClass(tenants[selectedTenant].details[i].key) == 'Email')
return tenants[selectedTenant].details[i].value;
return null;
};
Template.tenantsBlock.addedDate = function()
{
tenantsDep.depend();
return _Utils.timeToDateString(tenants[selectedTenant].created);
};
Template.tenantsBlock.events({
'click .Name': function(e, template)
{
tenantsDep.depend();
var _this = e.currentTarget;
var tenantName = _this.innerHTML;
$(_this).addClass('Selected');
$(_this).siblings().removeClass('Selected');
for(var i = 0; i < tenants.length; i++)
{
if(tenants[i].name == tenantName)
tenants[i].selected = "Selected";
else
tenants[i].selected = '';
}
}
})
})();
^This seemed to be what they were getting at in the meteor documentation (http://docs.meteor.com/#deps_dependency) for dependency.changed() and dependency.depend(), but all this does is give me an infinite loop.
So can I modify the way I declare deps to get this to make data reactive? Is there a better way to do this all together?
UPDATE:
Although I was skeptical to do so, I've been inclined to try to use Session.set/Session.get in a localized way. So, the next time I have to do this, I'll just do
Session.set('tenantsBlock' {tenants: [], selectedTenant: 0});
and then just access this variable from within helpers and event maps related to Template.tenantsBlock. That way they all have real time access to the data and they all get re-run when the data changes. Here's what I converted this script into (sorry these are both so large):
(function()
{
Template.tenantsBlock.created = Template.tenantsBlock.destroyed =function()
{
_Utils.setSession('tenantsBlock', {
tenants: [],
selectedTenant: 0
})
};
Template.tenantsBlock.tenantsList = function()
{
var localContext = Session.get('tenantsBlock');
localContext.tenants = [];
var property = $properties.findOne({userId: Meteor.userId(), propertyId: Session.get('property')});
var tenancies = _Utils.resolveTenancies(property, true, null, true);
for(var i = 0; i < tenancies.length; i++)
{
if(tenancies[i].tenancyId == Session.get('tenancy'))
{
localContext.tenants = localContext.tenants.concat(tenancies[i].otherTenants, tenancies[i].primaryTenant);
break;
}
}
localContext.tenants[localContext.selectedTenant].selected = 'Selected';
Session.set('tenantsBlock', localContext);
return localContext.tenants;
};
Template.tenantsBlock.onlyOneTenant = function()
{
var localContext = Session.get('tenantsBlock');
return localContext.tenants.length > 1 ? '' : 'OneChild';
};
Template.tenantsBlock.phoneNumber = function()
{
var localContext = Session.get('tenantsBlock');
for(var i = 0; i < localContext.tenants[localContext.selectedTenant].details.length; i++)
if(_Utils.getDynamicContactIconClass(localContext.tenants[localContext.selectedTenant].details[i].key) == 'Phone')
return localContext.tenants[localContext.selectedTenant].details[i].value;
return null;
};
Template.tenantsBlock.emailAddress = function()
{
var localContext = Session.get('tenantsBlock');
var selectedTenantDetails = localContext.tenants[localContext.selectedTenant].details;
for(var i = 0; i < selectedTenantDetails.length; i++)
if(_Utils.getDynamicContactIconClass(selectedTenantDetails[i].key) == 'Mail')
return selectedTenantDetails[i].value;
return null;
};
Template.tenantsBlock.addedDate = function()
{
var localContext = Session.get('tenantsBlock');
return _Utils.timeToDateString(localContext.tenants[localContext.selectedTenant].created);
};
Template.tenantsBlock.events({
'click .Name': function(e, template)
{
var localContext = Session.get('tenantsBlock');
var _this = e.currentTarget;
var tenantName = _this.innerHTML;
for(var i = 0; i < localContext.tenants.length; i++)
{
if(localContext.tenants[i].name == tenantName)
{
localContext.tenants[i].selected = 'Selected';
localContext.selectedTenant = i;
}
else
{
localContext.tenants[i].selected = '';
}
}
Session.set('tenantsBlock', localContext);
}
})
})();
You'll have to overcome the old-school way of doing it :) Meteor is a lot simpler than you think. A good rule of thumb is that if you're using jQuery to manipulate any DOM elements, you're probably doing it wrong. Additionally, if you're accessing any data without using the collection API, you'd better have good reason to do so.
In your case, you don't need to code up any manual dependencies at all. Manual dependencies are rarely needed in most Meteor applications.
The first thing you need to do is put all your tenants inside a Meteor.Collection, which will make them easier to work with.
Tenants = new Meteor.Collection("tenants");
Your tenantsBlock template should look something like this (modulo some different html elements):
<template name="tenantsBlock">
<ol>
{{#each tenants}}
<li class="name {{selected}}">
<span>Primary Tenant: {{primaryTenant}}</span>
<span>Other Tenants: {{otherTenants}}</span>
<span>Phone Number: {{phoneNumber}}</span>
<span>Email Address: {{emailAddress}}</span>
<span>Added Date: {{addedDate}}</span>
</li>
{{/each}}
</ol>
</template>
Each document in Tenants should look something like the following:
{
primaryTenant: "Joe Blow",
otherTenants: "Mickey Mouse, Minnie Mouse",
phoneNumber: "555-234-5623",
emailAddress: "joe.blow#foo.com",
addedDate: "2005-10-30T10:45Z"
}
Then, all the code you would need is just for the selection/deselection, and you can delete everything else:
Template.tenantsBlock.tenants = function() {
return Tenants.find();
};
Template.tenantsBlock.selected = function() {
return Session.equals("selectedTenant", this._id);
};
Template.tenantsBlock.events({
'click .name': function(e) {
Session.set("selectedTenant", this._id);
}
});
Once again, I reiterate that you should never be doing DOM manipulations with Javascript when using Meteor. You just update your data and your templates will reactively update if everything is done correctly. Declare how you want your data to look, then change the data and watch the magic.
Meteor has really evolved since I posted this back in 2013. I thought
I should post a modern, superior method.
For a while now you've been able to create a ReactiveVar and now you can append those directly to templates. A ReactiveVar, similar to Session, is a reactive data store. ReactiveVar, however, holds only a single value (of any type).
You can add ReactiveVar to the client side of your project by running this in your terminal from your app's root directory:
$meteor add reactive-var
This javascript shows how you can pass the variable between the template's onCreated, onRendered, onDestroyed, events and helpers.
Template.myTemplate.onCreated = function() {
// Appends a reactive variable to the template instance
this.reactiveData = new ReactiveVar('Default Value');
};
Template.myTemplate.events({
'click .someButton': (e, template) => {
// Changes the value of the reactive variable for only this template instance
template.reactiveData.set('New Value');
},
});
Template.myTemplate.helpers({
theData: () => {
// Automatically updates view when reactive variable changes
return Template.instance().reactiveData.get();
},
});
This is superior for a few reasons:
It scopes the variable only to a single template instance. Particularly useful in cases where you might have a dozen instances of a template on a page, all requiring independent states.
It goes away when the template goes away. Using ReactiveVar or Session variables you will have to clear the variable when the template is destroyed (if it is even destroyed predictably).
It's just cleaner code.
Bonus Points: See ReactiveDict for cases in which you have many instances of a template on a page at once, but need to manage a handful of reactive variables and have those variables persist during the session.
I have a page at http://www.no1hastings.check.com.au/directions.html where visitors can get directions from anywhere to a fixed point. The destination that the Google geocoder recognises is a street address (1 Morwong Drive) but I would like it to display the name of the building there (No. 1 in Hastings Street) which Google doesn't recognise.
Is there way to set the street address as the destination but alias it to the building name when the result is displayed?
One option would be to modify the string in the response before sending it to the DirectionsRenderer:
function calcRoute() {
var request = {
origin: 'Brisbane QLD Australia Australia',
// origin: 'Brisbane Qld, Australia',
destination: '1 Morwong Drive, Noosa Heads Qld, Australia',
// waypoints:[{location: 'Bourke, NSW'}, {location: 'Broken Hill, NSW'}],
travelMode: google.maps.DirectionsTravelMode.DRIVING,
unitSystem: google.maps.UnitSystem.METRIC
};
directionsService.route(request, function(response, status) {
if (status == google.maps.DirectionsStatus.OK) {
var route = response.routes[0];
var lastleg = route.legs[route.legs.length-1];
response.routes[0].legs[route.legs.length-1].end_address = 'No. 1 in Hastings Street';
directionsDisplay.setDirections(response);
} else alert("Directions request failed: "+status);
});
}
To handle the case where the directions are changed by the directionsRenderer (i.e. for dragging the origin), you could do something like this (where changeEnd is a global and set to false), note that it will have undesireable results if the user drags the destination (you may want to prevent that: Google maps api v3 Two markers one fixed one dragged):
google.maps.event.addListener(directionsDisplay, 'directions_changed', function() {
computeTotalDistance(directionsDisplay.directions);
if (!changeEnd) {
var response = directionsDisplay.getDirections();
var route = response.routes[0];
var lastleg = route.legs[route.legs.length-1];
response.routes[0].legs[route.legs.length-1].end_address = 'No. 1 in Hastings Street';
changeEnd = true;
directionsDisplay.setDirections(response);
} else changeEnd = false;
});
I'm not sure this is the right group. If not, please let me know.
My dilemma:
I need to add Polylines to Google Earth from the results I get back from the Google Maps v3 DirectionsService. There seems to be nothing on the web to that extent. It has to be possible, because Roman does this in his driving simulator: http://earth-api-samples.googlecode.com/svn/trunk/demos/drive-simulator/index.html
Unfortunately, he is using Google Maps v2 there and I can't seem to figure out how to transfer this code into Google Maps v3.
If anyone is interested, here is how I managed to solve it:
function DrawLinesOnEarth() {
var sLat;
var sLon;
//var start = document.getElementById("start").value;
//var end = document.getElementById("end").value;
var request = {
origin: '40.306134,-74.05018',
destination: '40.313223,-74.043496',
travelMode: google.maps.TravelMode.WALKING
};
directionsService.route(request, function (result, status) {
if (status == google.maps.DirectionsStatus.OK) {
directionsDisplay.setDirections(result);
var steps = result.routes[0].legs[0].steps;
//Step through array of step legs and create polylines one by one
var lineStringPlacemark = IMC_ge.createPlacemark('');
var lineString = IMC_ge.createLineString('');
lineStringPlacemark.setGeometry(lineString);
// Add LineString points
for (var x in steps) {
for (var y in steps[x].path) {
sLat = steps[x].path[y].Na;
sLon = steps[x].path[y].Oa;
lineString.getCoordinates().pushLatLngAlt(sLat, sLon, 0);
}
}
// Add the feature to Earth
IMC_ge.getFeatures().appendChild(lineStringPlacemark);
}
});
}