I have a javascript function that creates a leaflet layer group, adds it to a map, removes it and then repeats. Each time, it goes through a json array that has the data which is placed in the layer group. The effect is an animation of data points on the map.
There are two data sets which are formatted to be animated with this function. Let's call them Nowcast and Forecast. In my app, if I run the animations in the following sequence:
Nowcast, Forecast, Nowcast, Forecast, Nowcast
On the third Nowcast run, it looks like some of the data from the Forecast shows up along with the Nowcast data. Not all the data, and only for a few iterations, but then it just sits there and does not disappear when the animation is over. The only way to get this weird residual data is to run the Forecast animation again. (running the Nowcast again does not remove it).
Anyway, here is the function:
function animateAshData(inputData,dataLayer) {
var currentTime = inputData.ashData[t];
var currentAshData = currentTime.concentrations;
window.Android.toLogCat("t = " + currentTime.date + " " + t);
window.Android.toLogCat("Current set size= " + currentAshData.length);
if (dataLayer != undefined) { //The map has a data layer already been created from previous time step, so remove it
removeDataLayer(dataLayer);
}
var currentAshLayerGroup = new L.LayerGroup();
for (j = 0; j<currentAshData.length; j++) {
L.marker([currentAshData[j].lat, currentAshData[j].lon],{icon: ashIcon}).addTo(currentAshLayerGroup);
map.addLayer(currentAshLayerGroup);
};
t = t+1;
// Queue up the animation to the next time step
tid = setTimeout(function(){
if (t > finalt) {
//end of animation
window.Android.toLogCat("Done with animation");
removeDataLayer(currentAshLayerGroup);
window.clearTimeout(tid);
} else {
// delete currentTime.concentrations;
clearMap();
animateAshData(inputData,currentAshLayerGroup);
}
}, 100);
My first thought was that the layer groups were somehow persisting, even though I had a map.removeLayer(layer) in there (although it is mysterious why it takes 3 iterations instead of just 2 for this to show up...). So, I made a removeDataLayer() function and added a layer.clearLayers(). This did not work either. I finally added the function from here Clear all polylines from leaflet map to just try clearing everything, but this still did not work. Here is my removeDataLayer function:
function removeDataLayer(layer){
map.removeLayer(layer);
layer.clearLayers();
clearMap();
};
function clearMap(){
for(i in map._layers){
if(map._layers[i]._path != undefined)
{
try{
map.removeLayer(map._layers[i]);
}
catch(e){
console.log("problem with " + e + map._layers[i]);
}
}
}
};
Anyway, I can't imagine I am the only one with problems with leaflet layers interfering with each other and/or problems removing them completely. Thanks in advance.
Related
I'm using angularjs as front-end framework. I get a data set via a rest get call.
That contains coordinates in an array. What needs to be done is that array with coordinates should be iterated and should be shown in a map.
Once the user hits play in the map those coordinates should be displayed one ofter another having at least 1 second of interval.
And when user hits play button, the button it self converts to a pause button it should do what the name intend to do. pause the process. I couldn't achive this kind of behavior using angularjs. Following is the closest that I could come.
var getReplayData = function () {
return $http.get('http://localhost:4000/replay/asiri/vin/' + from + '/' + to);
};
$scope.play = function () {
from = rangeProperties.getFrom() * 1000;
to = rangeProperties.getTo() * 1000;
getReplayData().success(function (data) {
console.log(data);
var waitingTime = 0;
var gap = 0;
for (var i = 0; i < data.length; i++) {
(function (i) {
var element = data[i];
var coordinates = new Object();
coordinates.latitude = element.LATITUDE;
coordinates.longitude = element.LONGITUDE;
setTimeout(function () {
broadcastData(coordinates);
}, waitingTime);
if (i + 1 < data.length) {
gap = data[i + 1].TIMESTAMP - element.TIMESTAMP;
console.log(gap);
} else {
gap = 0;
}
waitingTime = waitingTime + gap;
})(i);
}
});
$scope.play refers to the play action of the button. I can't figure out how to pause this process. Seems like I might have to keep the references to all timeouts and cancel them. Any idea how to implement this kinda of scenario? I don't exactly need a code segment just an idea how to approach to solve this kinda of problem would be really nice.
Seems like I might have to keep the references to all timeouts and cancel them.
That would be a good first step.
I don't exactly need a code segment just an idea how to approach to solve this kinda of problem would be really nice
I would do the following:
cache the response data as a variable, if possible.
separate the play and pause functionality, tying them into a cancelable callback that you get from using the $interval service.
during playback, treat your coordinates data as a FIFO (queue), dequeuing the item which gets passed to the $interval's promise
during pause, you simply cancel the $interval promise
playing again doesn't require any fancy work on the queue as you'll resume the same logic on the first item in the queue when you resume.
I have a problem with my code in Google maps API v3. Markers are updating every minute (or manually at any time). But I do not want to erase all markers and then again "re-upload" cause markers then "blinking".
I have many filters in our website that affect the viewing markers. So one time there may be 5000 markers and after 1 sec should be just a 1.
But how can I do it correctly? There is mine "addMarker" function.
this.addMarker = function(id, object)
{
if(typeof object == 'undefined'){
return;
}
if(!this.markers[id])
{
object.marker = new google.maps.Marker({
position: new google.maps.LatLng(object.lat, object.lng),
icon: object.image
});
if((object.approved == "1" && MapApprovedControl.enabled) || object.approved == "0" && MapUnapprovedControl.enabled){
object.marker.setMap(this.canvas);
}
this.markers[id] = object;
}
else
{
if(this.markers[id].marker.getIcon() != object.image) {
this.markers[id].markers.setIcon(object.image);
}
if(this.markers[id].marker.getMap() == null){
this.markers[id].marker.setMap(this.canvas);
}
}
this.updated.push(id);
};
In "else" block i'm adding the index into array, so I can find out which data are already there and which markers missing.
this.updated = [];
this.update = function()
{
if(this.updated.length > 0)
{
$.each(this.markers, function(index)
{
var exists = false;
$.each(map.updated, function(key, value){
if(index == value){
exists = true;
return;
}
});
if(!exists){
map.markers[index].marker.setMap(null);
}
});
this.updated = [];
}
};
and when map are updated this happening:
$.getScript('/Of1dPGvNutBUMJdfGSmJKeZwWjR4MGmUVoBDhb9I/pTTegD9WefgHfx7WzNhP', function() {
map.update();
});
In this URL is just "addMarker(..); addMarker(...);"
But this solution are not correct. Once it works, once nope - not sure why :D + it's slow.
So my questions is :
How to update markers correctly?
During the update, do I have two cycles, is there any way to use array filter?
Thanks a lot!
EDIT: I modified the code, deleted uselessness and moved "this.updated.push (id)" after conditions. This solved the problem with the fact that sometimes it worked and sometimes did not. Still, it remains a problem with two cycles. (I still think that this is not the right way)
EDIT1: OK, still does not working sometimes... Usually when I get a smaller number of markers than I had previously.
EDIT2: OK, the script (ajax snippet) is not loaded sometimes (probably cached, idk)
EDIT3: This is probably the best way to update markers without 2 cycles
this.update = function()
{
if(this.updated.length > 0)
{
$.each(map.markers, function(index){
if($.inArray(index, map.updated) == -1){
map.markers[index].marker.setMap(null);
}
});
this.updated = [];
}
};
But I still don't know how to load mine JS file (if there is a lot of markers, so the script just does not read, does not write any error) - pfff I don't get it.
EDIT4: Ok, it's probably fixed by adding this (in ajax snippet - update)
<script type="text/javascript">
function load_js()
{
var head= document.getElementsByTagName('head')[0];
var script= document.createElement('script');
script.type= 'text/javascript';
script.src= '/Of1dPGvNutBUMJdfGSmJKeZwWjR4MGmUVoBDhb9I/pTTegD9WefgHfx7WzNhP';
head.appendChild(script);
}
load_js();
</script>
and also adding "map.update();" line after last line in /Of1dPGvNutBUMJdfGSmJKeZwWjR4MGmUVoBDhb9I/pTTegD9WefgHfx7WzNhP file.
Next problem is: load just markers when they does not exists in our map (hey this topic is like private notepad, lel)
Ok, its probably "unsolvable" and I need power of god, to be continued...
BUT THANKS A LOT FOR HELP ANYWAY! :-))
Have you thought about hiding markers instead of redisplaying them for every filter? Display all of the markers, and then turn off all of the ones not in the filter set. That keeps your code clean because you only have to output the markers once.
I am trying to implement the new structure of here map(asp.net web app) , however, I cannot see the features of passing the location information and retrieving the certain address via javascript and stick that info with a bubble on the marker.
Appreciate for any advises.
cheers
I think you are looking to make multiple concurrent reverse geocode requests. There is no problem in doing this, since the calls are asynchronous, you just need to keep a count on which requests have completed, and call your "do-after-all-completed" stuff once at the end:
e.g:
function concurrentSearch(map){
// we will put our address markers into this container
addressContainer = new nokia.maps.map.Container();
managersFinished = 0;
//Locations to be displayed
latLngs= [[10,30], [-117.5, 15.3] ... etc];
var i = latLngs.length;
while(i--) {
nokia.places.search.manager.reverseGeocode({
latitude: latLngs[i][0],
longitude: latLngs[i][1],
onComplete: processResults
});
}
}
With a callback function as shown:
function processResults(data, requestStatus) {
if (requestStatus == "OK") {
// if we are finished, we add a marker for the mapped position
var marker = new nokia.maps.map.StandardMarker(data.location[0].position);
marker.content = data.location[0].address.text; // add your content here
addressContainer.objects.add(
marker);
//increment the counter to notify another manager has finished
managersFinished++;
} else if(requestStatus === "ERROR") {
// we'll also increment in case of an error
managersFinished++;
}
// if all managers are finished, we call the final function
if(managersFinished === latLngs.length) {
onAllManagersFinished();
}
}
And the final clean up:
function onAllManagersFinished() {
//we get the bounding box of the container
var bbox = addressContainer.getBoundingBox();
// if the bounding box is null then there are no objects inside
// meaning no markers have been added to it
if (bbox != null) {
// we have at least one address mapped
// so we add the container and zoomTo it
map.objects.add(addressContainer);
map.zoomTo(bbox);
} else {
// otherwise we'll pop up an error message
alert("There are no addresses to show :(");
}
}
Futhermore, you could add a single event handler on the container, which could open the infobubbles for all of your locations:
addressContainer.addListener(
"click",
function (evt) {
infoBubbles.openBubble(evt.target.content, // content is your own stuff...
evt.target.coordinate);
}
);
If you need to pass in more of your information when reverse-geocoding, you can do it in the manner described in the question here
The solution to a similar problem (multiple geocoding requests) can be found here.
I am trying to pause Google Maps markers from animating when elements pushed into an array reaches a certain number. I have tried to clearing the timer and resuming it however i am having some problems.
I have used console.log() to see where control is accessing however the only message i am getting from console.log() is '500 records saved'. The timer gets cleared however control does not access the else condition of the block to resume the timer.
I have also looked at the SO question Pause and play google map marker animation
However i am not using buttons to pause and play.
Here is the code any guidance will be greatly appreciated.
Console.log()
'500 records saved' and animation stops does not resume
var lastVertex = 1;
var stepnum=0;
var step = 90; //0.9 metres
var tick = 100; // milliseconds
var eol = [];
StartAnimation
function startAnimation(index) {
if (timerHandle[index]) clearTimeout(timerHandle[index]);
eol[index]=polyline[index].Distance();
map.setCenter(polyline[index].getPath().getAt(0));
poly2[index] = new google.maps.Polyline({path: [polyline[index].getPath().getAt(0)], strokeColor:"#FFFF00", strokeWeight:3});
timerHandle[index] = setTimeout("animate("+index+",50)",2000); // Allow time for the initial map display
}
Animate
function animate(index,d) {
if (d>eol[index]) {
marker[index].setPosition(endLocation[index].latlng);
if(marker[index].getPosition() == endLocation[index].latlng){
console.log('Completed'+' '+index);
}
return;
}
var p = polyline[index].GetPointAtDistance(d);
marker[index].setPosition(p);
updatePoly(index,d);
timerHandle[index] = setTimeout("animate("+index+","+(d+step)+")", tick);
citizens1.push({lat:marker[index].getPosition().lat(),lng:marker[index].getPosition().lng(),socialSecurityNumber:global_citizens[index].socialSecurityNumber});
if(citizens1.length = 500){
console.log('500 records saved');
window.clearTimeout( timerHandle[index]);
citizens1 = []; //clear the array
}
}
I have a marker on my map representing the current location of the International Space Station (pulled from http://open-notify-api.herokuapp.com/iss-now.json?callback=?). I'm also trying to get it to move every 1 second to follow along with the station's orbit.
This is the code I have now:
$.getJSON('http://open-notify-api.herokuapp.com/iss-now.json?callback=?', function(data) {
var latitude = data["data"]["iss_position"]["latitude"];
var longitude = data["data"]["iss_position"]["longitude"];
var iss = L.marker([latitude,longitude]).bindPopup("I am the ISS").addTo(map);
$(function() {
setInterval(function() {
iss.setLatLng([latitude,longitude]).update();
},1000);
});
});
Here's everything in a jsFiddle: http://jsfiddle.net/zmkqu/
It seems to place the marker in the correct position on load, but does not update every second as it should be. Any tips on what I'm doing wrong?
Thanks!
You should put the whole Ajax call inside the setInterval callback. Right now you are setting the marker to the same position very second, because data does not change. You have to make a new call every second:
var iss;
function update_position() {
$.getJSON('http://open-notify-api.herokuapp.com/iss-now.json?callback=?', function(data) {
var latitude = data["iss_position"]["latitude"];
var longitude = data["iss_position"]["longitude"];
if (!iss) {
iss = L.marker([latitude,longitude]).bindPopup("I am the ISS").addTo(map);
}
iss.setLatLng([latitude,longitude]).update();
setTimeout(update_position, 1000);
});
}
update_position();
DEMO
Note: It's better to use setTimeout together with Ajax calls, since you don't know how a long it takes to get the response.
If you need to guarantee that it updates every second you probably want to modify this a bit... Right now it fires 1 second after the server responds with the json, which is most likely not 1 second since the last update.
What you want is something more like this:
var iss
, timeElapsed = 0;
function update_position() {
elapsedTime = new Date().getMilliseconds();
$.getJSON('http://open-notify-api.herokuapp.com/iss-now.json?callback=?', function(data) {
var latitude = data["data"]["iss_position"]["latitude"];
var longitude = data["data"]["iss_position"]["longitude"];
elapsedTime = new Date().getMilliseconds() - elapsedTime;
if (!iss) {
iss = L.marker([latitude,longitude]).bindPopup("I am the ISS").addTo(map);
}
iss.setLatLng([latitude,longitude]).update();
setTimeout(update_position, 1000 - elapsedTime);
});
}
update_position();
You could probably go even farther with this... there will be some stuttering if a request takes more then a second for instance if your request takes longer then a second. But if that request is taking longer then a second you probably have other issues you need to deal with.