I am using google maps api v3, and i need to move many(hundreds) of markers at the same time. From server, i get array of objects, representing a marker with it´s coordinates as it moved over time(few months), ordered by time when it moved. Above my map, i have a progress bar representing time with "month:year" text. What my code does, is move the marker(and draw polyline) at the exact time so it fits the time currently displayed on the "time progress bar". At every position where the marker stops, i leave a new marker to show the history of the movement. The whole process repeats over and over(setInterval() function). My code is working, the problem is performance, so i am asking: is there any way i could improve the performance ? some better way than i am doing it? there must be something. Here is my code:
if (mapOverTime != null) {
trackables.forEach(function (trackable) {
var marker = new google.maps.Marker({
map: mapOverTime,
draggable: false,
clickable: false,
icon: geoIcon,
animation: google.maps.Animation.none,
zIndex: 2
});
var polyline = new google.maps.Polyline(polyOptions);
polyline.setMap(mapOverTime);
// adding these to arrays so i can access and remove them from the map later
markersArray.push(marker);
polylineArray.push(polyline);
trackable.years.forEach(function(year) {
$.each(year.months, function(index, month) {
setTimeout(function() {
if (month.coordinates.length > 0) {
var moveCounter = month.coordinates.length;
var timeToMove = Math.floor(pause_interval / moveCounter);
$.each(month.coordinates, function(i, position) {
var latTo = position.latitude;
var lngTo = position.longitude;
var destination = new google.maps.LatLng(latTo, lngTo);
setTimeout(function() {
moveToPosition(marker, destination, polyline);
}, (i) * timeToMove);
});
}
}, pause_interval * index);
});
});
});
}
This is the function making the moves happen:
function moveToPosition(marker, destination, polyline) {
// current path of the polyline
var path = polyline.getPath();
// add new coordinate
path.push(destination);
// rendering whole line would make the map even more chaotic, this makes the line dissapear after 8 moves of the marker
if (path.getLength() > 8) // comment out to render whole
th.removeAt(0); // path of polyline
// set marker position to the new one
marker.setPosition(destination);
// render new marker on this position so that it is well visible where the coins were
var historyPoint = new google.maps.Marker({
map: mapOverTime,
draggable: false,
icon: historyIcon,
animation: google.maps.Animation.none,
zIndex: 0
});
historyPoint.setPosition(destination);
// again add to global array so i can set access and remove this marker from the map later
historyPoints.push(historyPoint);
}
The only thing i do, is iterate through all the markers and it´s coordinates, move marker at given time, draw line behind it, create new marker at the new position, and go for another coordinates. When 1 iteration end, all the objects are removed from the map and the process starts again. Any idea how to improve performance? or the only solution is to decrease the number of markers i want to render? Thank you for your opinions!
Suggestions:
Why bother specifying animation: google.maps.Animation.none,? If you're not using it, just remove it. And according to the docs, there are two values, BOUNCE and DROP, so there isn't a none value anyway.
Use standard javascript array looping, instead of jquery's $.each() if you can; it's faster
You create two variables just for the benefit of specifying your destination coordinates. You use them once, and never again, so why not just use the original values? i.e.
var latTo = position.latitude;
var lngTo = position.longitude;
var destination = new google.maps.LatLng(latTo, lngTo);
just becomes
var destination = new google.maps.LatLng(position.latitude, position.longitude);
Another improvement you could make here. Use the shorthand inline struct notation for specifying your destination coordinates. It should reduce some overhead in creating hundreds of LatLng objects. i.e.
var destination = new google.maps.LatLng(position.latitude, position.longitude);
then becomes:
var destination = {lat: position.latitude, lng: position.longitude};
You create a polyline, then you set the map:
var polyline = new google.maps.Polyline(polyOptions);
polyline.setMap(mapOverTime);
I see this all the time and never understand it... just add map: mapOverTime to the polyOptions, saves having to do an additional function call.
Similarly with historyPoint... don't call setPosition() on it, just specify position: destination in the options you pass when you create the marker
All these are minor things which will likely make little different to trying to move hundreds of markers at once, but I'd still recommend you try them anyway.
Related
Currently attempting to track an asset on leaflet. The idea is to draw a polyline, animate it and animate a marker moving along with the polyline. The script for the polyline is the one from IvanSanchez
SnakeAnim
The script for the animated marker is atogle
AnimatedMaker
Issue is, I believe they're at two different speeds. Any help "merging" the two functions into one? Unfortunately one function uses pixels for distance and the other uses actual physical distance.
Thanks!
<div><p> <button id = "start" onclick='snake();'>Start Animation</button></p></div>
var polylines = [];
var markers = [];
for (var i = 0; i < route.length; i++) {
polylines.push(L.polyline(route[i]));
var marker = L.animatedMarker(L.polyline(route[i]).getLatLngs(), {
icon: Tanker,
autoStart: false,
onEnd: function() {
$(this._shadow).fadeOut();
$(this._icon).fadeOut(3000, function(){
map.removeLayer(this);
});
}
});
}
var pathmat = L.featureGroup (polylines);
function snake()
{
pathmat.snakeIn();
}
pathmat.on('snakestart snake snakeend', function(ev){
console.log(ev.type);
marker.start();
});
it's funny i had the same idea a few month ago.
I'm not using AnimatedMarker but MovingMarker because it behaves better when zooming and you can give it a duration array to have a variable speed.
After realizing the same speed issue you talk about, i submitted an issue to SnakeAnim project to ask if it was possible to set animation duration instead of animation speed. A dev answered me it was difficult to do. SnakeAnim is not designed that way.
Then i had an idea. It is quite easy to draw a polyline progressively with its addLatLng method. All i had to do is fire an event each time the MovingMarker moves, get this event in my code to add current MovingMarker position to the polyline coordinates. Here is the result.
My modifications to MovingMarker are here. Just add this if block.
Then i get the move event on my MovingMarker this way :
var completePolyline = L.polyline(coordinatesTable);
var snakePolyline = L.polyline([]);
// add coordinates to the current snake line when marker moves
function updateSnakeLine(e) {
var ll = e.target.getLatLng();
snakePolyline.addLatLng(ll);
}
var marker = L.Marker.movingMarker(completePolyline.getLatLngs(), 20000,{
autostart: false,
icon: theicon
});
marker.on('move', updateSnakeLine);
marker.start()
One downside of this technique : if the browser is slow during a few milliseconds, it will skip some markers positions so the snakePolyLine might not be strictly equal to the completePolyline. That's why, on animation end, i remove the snakePolyLine and draw the completePolyline.
I need to check every 5 seconds if a marker(user) is now contained in a circleMarker(loc)
function updateLocation(){
if(!isSet){clearInterval(start)}else{
user.setLatLng(new L.LatLng(lat, lng));
if(loc.getBounds().contains(new L.LatLng(lat, lng))){
document.getElementById('setButton').style.background = 'purple'
soundAlarm();
isSet = false;
}
}
}
Above is the current code I have, which is called by this:
var start = setInterval(updateLocation, 5000);
Thanks in advance, Ed.
It seems that you're trying to do geofencing, but in a weird way. Why rely on the drawn appearance of CircleMarkers when all you want is to know if the distance from the user to something is less than a threshold (the CircleMarker radius)?
Just use the distance method of L.Map:
var guardedLocation = L.latLng(...);
var thresholdDistance = 100; // In meters
var start;
function updateLocation(){
var userPosition = L.latLng(lat, lng);
user.setLatLng(userPosition);
if(map.distance(userPosition, guardedLocation) <= thresholdDistance) {
document.getElementById('setButton').style.background = 'purple'
soundAlarm();
isSet = false;
clearInterval(start);
}
}
start = setInterval(updateLocation, 5000);
If your loc variable is really an L.circleMarker, please realize that by definition a marker is a point / place, i.e. it does not have any area, hence no bounds. You probably meant to use an L.circle for loc in the first place.
In Leaflet 0.x (e.g. version 0.7.7), the L.circleMarker inherits from L.circle, therefore it does have a .getBounds() method, even though it is meaningless. Actually it returns a null area (southWest coordinates equal northEast coordinates equal the marker position).
Demo: https://jsfiddle.net/y63u5utf/2/
So your code would actually evaluate to true iif the user location is exactly the same as your loc.
In Leaflet 1.x (e.g. current version 1.0.1), this inconsistency has been corrected, and L.circleMarker no longer has the .getBounds() method.
Demo: https://jsfiddle.net/y63u5utf/1/
(zoom in and out to see the radius of the red circle marker adjusting to keep the same size in pixels, until at high zoom, the marker is out of it. Whereas the blue circle has a radius which scales according to the zoom, since it represents an actual length; therefore it always contains (or not) the marker)
I created a prototype for a project I'm working on, in which the user should create various markers in a map, be able to remove them, calculate a route with the markers, and go back to manage the markers.
I used some code found here in geocodezip.com to calculate the route, and wrote some for the markers, etc.
My problem is that once the user calculates the route, no matter how he edits the markers, when clicking the button to calculate the route, the map only returns the route with the markers that were there on the first time he clicked the button. And the strangest thing is that I checked the coordinates that are being passed to the script that generates the route and the function is sending the markers as it should, but no matter the coordinates sent, it only works correctly on the first time.
Js Fiddle: https://jsfiddle.net/1kmg2u65/2/
The code is really really long so it's all in the Fiddle, but this is what it does:
1. User clicks on map, generate marker, marker goes to an array
2. If user deletes marker, it becomes null in array, to maintain the indexes
3. 'Clean' markers array receives all the markers in order, without the items that are null
4. A function is called with all the markers, this function creates the route
5. To manage the markers, a function reload the map just like it was in the start, but render all the markers that already are in the markers array
it works fine if you remove the conditional if (!window.tour) in Tour_startUp function definition.
So here is what I believe is going on.
In the function markMap() you are instantiating new markers that belong to the google map object.
for (var i = 0; i < markerElements.length; i++){ //Loop para gerar os marcadores
if (markerElements[i] != null){
marker = new google.maps.Marker({
position: markerElements[i].position,
map: map,
title: markerElements[i].title
});
}
}
This if fine, but you are not storing that constructed object anywhere. You need to be able to reference that marker to UN-associate it from the map.
At the end of the this for loop you need to add THAT mark to a global array so you can manage it later in the script.
EXAMPLE
// defined at the top of the script
var markerGlobal = [];
for (var i = 0; i < markerElements.length; i++){ //Loop para gerar os marcadores
if (markerElements[i] != null){
marker = new google.maps.Marker({
position: markerElements[i].position,
map: map,
title: markerElements[i].title
});
// push marker onto global array
markerGlobal.push(marker);
}
}
Now we can loop through the array and setMap to null
// un-reference marker from map
markerGlobal[2].setMap(null);
I see you tried to do this with the removeMarker() function, but it doesn't have the handles to the markers already added to the map.
Some Suggestions
If I was you, I would think about refactoring the code to have one multi dimensional object that holds all the markers, their row info, variables etc.
You could take it one step further and create a constructor function that handles the map and its associated markers. It would be most efficient.
Good luck.
I wonder whether someone may be able to help me please.
I'm using this page to allow users to view markers saved in a MySQL database and to geocode/reverse geocode new marker positions.
I'm able to correctly center the map on the marker which is being loaded from the database, but the problem I'm having is that the map zooms in to it's maximum setting, whereas I would like to be able to set this manually.
I've tried adding another zoom control into my script here:
// add marker
var bounds = new google.maps.LatLngBounds();
var loc = new google.maps.LatLng(las[i],lgs[i]);
var marker = new google.maps.Marker({
position: loc,
map: window.map,
zoom: 8,
title: nms[i]
});
bounds.extend(loc);
map.fitBounds(bounds);
Unfortunately this has no affect. I appreciate that to some this may be a very minor fix, but I've been looking at this for sometime and I just can't seem to find a solution.
I just wondered whether someone may be able to look at this please and let me know where I've gone wrong?
POST UPDATE
I have worked out a solution using exisitng code and that suggested by #Andrew Leach. The working solution is:
// add marker
var bounds = new google.maps.LatLngBounds();
var loc = new google.maps.LatLng(las[i],lgs[i]);
var marker = new google.maps.Marker({
position: loc,
map: window.map,
title: nms[i]
});
bounds.extend(loc);
map.fitBounds(bounds);
map.setZoom(16);
Remove the references to the bounds.
You add a marker to the map, and create a LatLngBounds object which contains only that single point. You then ask the map to zoom in to fit the bounds into the map's viewport. Fitting a single location into the viewport will always zoom in as far as it can, and even then a single point can never fill the entire viewport.
However, adding zoom to the marker options won't work either, because zoom is not a valid option. Documentation
Instead, set the map's zoom explicitly, either with map.setZoom(8) or by including the zoom option in the map's options when you set it up.
I'm new to javascript and to programming itself, I'm trying to add markers in google maps api and load it's coords from mysql, I have everything done but now I got stuck into something, is it possible to create a number of variables based on the number of coords I have ? here is what I have:
function get_values(numero, array)
{
var i;
for(i=0;i<numero;i++)
{
//var i ( HERE: i want it to set variables based on i )= new google.maps.Marker({
position: array[2],
//map: map,
//title:"Hello World!"
});
}
}
It appears what you need to use is an array. This will allow you to store as many coordinates as you want and you'll be able to access them by index (number). For example, if you have 10 coordinates, they could be stored in an array like:
position[i] = array[2]
Your code looks, though, pretty broken, so I think you need more help getting started than what pointed questions on Stack Overflow will get you.
As Gordon says you need an array. If I understand correctly you want to create one marker for each iteration ?
Then I guess something like this would do the trick :
function get_values(numero, array)
{
var i;
var markers = new Array(numero); // create an array to store the markers
for(i=0;i<numero;i++)
{
markers[i] = new google.maps.Marker({
position: array[i],
map: map,
title: "Hello marker " + i // give a different title to each marker based on the number..
});
}
return markers;
}
This assumes that your get_values function takes the number of positions and an array of positions as parameters.