Animating markers on a map with a for loop - javascript

I am attempting to animate several markers on a map with a for loop, but instead of animating them as intended, the code below is adding the consecutive coordinates simultaneously (full code here):
for (j in jsonObj[i]) {
for (k in jsonObj[i][j]) {
if (jsonObj.hasOwnProperty(i)) {
subindex = Object.keys(jsonObj[i][j][k]);
sublenght = subindex.length;
for (cnt = 0; cnt < sublenght; cnt += 1) {
lat = [],
lon = [],
lat[cnt] = (jsonObj.flightPositions[j].positions[cnt].lat)
lon[cnt] = (jsonObj.flightPositions[j].positions[cnt].lon)
var marker = new L.Marker.movingMarker([[lat[cnt], lon[cnt]]], [2500], {autostart: true}).addTo(map);
};
}
}
}
I tried using closures, always getting the same result. My most recent attempt can be seen here. I guess there is something wrong with it, but what I am most afraid is that my whole approach may be wrong. I was hoping that anyone could throw me a hint. Any help appreciated!

According to Leaflet.MovingMarker plugin usage, you should create only 1 marker (per moving marker you need), and pass it directly an array of positions.
L.movingMarker(<LatLng[]> latlngs, <Number[]> durations [,<Object> options]);
In your code you create 1 marker per position, with only the current position.
I will not build on your JSFiddle as it looks far more complicated than probably necessary (do not know why you ever try some IIFE's), so building on the code you posted, you would have something like:
for (j in jsonObj[i]) { // i === "flightPositions"
for (k in jsonObj[i][j]) { // j === array index, k === "positions"
if (jsonObj.hasOwnProperty(i)) { // should have been checked before
subindex = Object.keys(jsonObj[i][j][k]); // indices of array
sublenght = subindex.length; // could have just done jsonObj[i][j][k].length
var coordinates = [];
for (cnt = 0; cnt < sublenght; cnt += 1) { // iterates in positions array
coordinates.push([
jsonObj.flightPositions[j].positions[cnt].lat,
jsonObj.flightPositions[j].positions[cnt].lon
]);
};
// outside the positions array for loop.
var marker = L.Marker.movingMarker(coordinates, 2500, {
autostart: true
}).addTo(map);
}
}
}

Related

Creating new array from unique elements found in array

I was given an assignment:
Finding unique elements in an array and creating a new array from these unique elements.
The professor gave us the pseudocode to code this assignment - it should be straightforward but my code is not working.
Here is my attempt:
// search for unique birthdays in the array
function find(birthdays) {
var uniqueBirthdays = [];
for (var i = 1; i <= birthdays.length; i = i + 2) {
var count = 0;
for (var j = 1; j <= birthdays.length; j = j + 2) {
if (birthdays[i] == birthdays[j]) {
count++;
}
}
if (count == 1) {
var n = uniqueBirthdays.length;
uniqueBirthdays[n] = birthdays[i - 1];
}
}
return uniqueBirthdays;
}
I have tried checking for indentation errors as well as a number of other things but can not figure out why as the array is traversed it is giving each element a count of only 1 (meaning there are no matching elements) - it does not seem to be traversing the array more than once so no elements have a count greater than 1 - even though I am using nested for loops.
I have increased the intervals by 2 because I need to compare every other element - there is a number assigned to each birthday so the array may look like:
['0001'][12/15]['0002'[03/12]...
I am brand new so I may be overlooking simple but ive tried so many things and i can not understand why this code isnt working - it is returning back all of the elements that are assigned to the birthdays instead of just the unique ones.
Any help that will point me in the right direction is very much appreciated.
You were very close, and there were just a couple mistakes. The only things that did not work were the way you wrote your for loops:
for (var i = 1; i <= birthdays.length; i = i + 2) {
Array indexes start at 0, so if you want to process the first element, use var i = 0;
Since these indexes start at 0, for an Array of 3 elements, the last index is 2. So you only want to run your loop while i is less than the array length: i < birthdays.length
You were skipping elements by doing i = i + 2. There seems to be no reason for it?
Something else worth mentionning: in JS, indentation does not matter - well, it does, but only to avoid making your eyes bleed. In fact, most websites use minified versions of their code, which fits on a single (often very long and ugly) line (example).
Here is your code, with only two lines fixed:
function find(birthdays) {
var uniqueBirthdays = [];
for (var i = 0; i < birthdays.length; i = i + 1) { // <-----
var count = 0;
for (var j = 0; j < birthdays.length; j = j + 1) { // <-----
if (birthdays[i] == birthdays[j]) {
count++;
}
}
if (count == 1) {
var n = uniqueBirthdays.length;
uniqueBirthdays[n] = birthdays[i];
}
}
return uniqueBirthdays;
}
// I used letters instead of birthdays for easier demo checking
var birthdays = ['a', 'b', 'a', 'c'];
console.log( find(birthdays) ); // ["b", "c"]
JS have direct methods tor that use Array.indexOf(), Array.lastIndexOf() and Array.filter()
uniques elements have same first position and last position
sample code:
const initailArray = [...'ldfkjlqklnmbnmykdshgmkudqjshmjfhmsdjhmjh']
const uniqueLetters = initailArray.filter((c,i,a)=>a.indexOf(c)===a.lastIndexOf(c)).sort()
console.log(JSON.stringify(uniqueLetters))

Manipulate more javascript array based on another array

I've a strange thing to do but I don't know how to start
I start with this vars
var base = [1,1,1,2,3,5,7,9,14,19,28,40,56,114,232,330];
var sky = [0,0,0,3,4,5,6,7,8,9,10,11,12,14,16,17];
var ite = [64,52,23,38,13,15,6,4,6,3,2,1,2,1,1,1];
So to start all the 3 array have the same length and the very first operation is to see if there is a duplicate value in sky array, in this case the 0 is duplicated and only in this case is at the end, but all of time the sky array is sorted. So I've to remove all the duplicate (in this case 0) from sky and remove the corresponding items from base and sum the corresponding items on ite. So if there's duplicate on position 4,5 I've to manipulate this conditions. But let see the new 3 array:
var new_base = [1,2,3,5,7,9,14,19,28,40,56,114,232,330];
var new_sky = [0,3,4,5,6,7,8,9,10,11,12,14,16,17];
var new_ite = [139,38,13,15,6,4,6,3,2,1,2,1,1,1];
If you see the new_ite have 139 instead the 64,52,23, that is the sum of 64+52+23, because the first 3 items on sky are the same (0) so I remove two corresponding value from base and sky too and I sum the corresponding value into the new_ite array.
There's a fast way to do that? I thought a for loops but I stuck at the very first for (i = 0; i < sky.length; i++) lol, cuz I've no idea on how to manipulate those 3 array in that way
J
When removing elements from an array during a loop, the trick is to start at the end and move to the front. It makes many things easier.
for( var i = sky.length-1; i>=0; i--) {
if (sky[i] == prev) {
// Remove previous index from base, sky
// See http://stackoverflow.com/questions/5767325/how-to-remove-a-particular-element-from-an-array-in-javascript
base.splice(i+1, 1);
sky.splice(i+1, 1);
// Do sum, then remove
ite[i] += ite[i+1];
ite.splice(i+1, 1);
}
prev = sky[i];
}
I won't speak to whether this is the "fastest", but it does work, and it's "fast" in terms of requiring little programmer time to write and understand. (Which is often the most important kind of fast.)
I would suggest this solution where j is used as index for the new arrays, and i for the original arrays:
var base = [1,1,1,2,3,5,7,9,14,19,28,40,56,114,232,330];
var sky = [0,0,0,3,4,5,6,7,8,9,10,11,12,14,16,17];
var ite = [64,52,23,38,13,15,6,4,6,3,2,1,2,1,1,1];
var new_base = [], new_sky = [], new_ite = [];
var j = -1;
sky.forEach(function (sk, i) {
if (!i || sk !== sky[i-1]) {
new_ite[++j] = 0;
new_base[j] = base[i];
new_sky[j] = sk;
}
new_ite[j] += ite[i];
});
console.log('new_base = ' + new_base);
console.log('new_sky = ' + new_sky);
console.log('new_ite = ' + new_ite);
You can use Array#reduce to create new arrays from the originals according to the rules:
var base = [1,1,1,2,3,5,7,9,14,19,28,40,56,114,232,330];
var sky = [0,0,0,3,4,5,6,7,8,9,10,11,12,14,16,17];
var ite = [64,52,23,38,13,15,6,4,6,3,2,1,2,1,1,1];
var result = sky.reduce(function(r, n, i) {
var last = r.sky.length - 1;
if(n === r.sky[last]) {
r.ite[last] += ite[i];
} else {
r.base.push(base[i]);
r.sky.push(n);
r.ite.push(ite[i]);
}
return r;
}, { base: [], sky: [], ite: [] });
console.log('new base:', result.base.join(','));
console.log('new sky:', result.sky.join(','));
console.log('new ite:', result.ite.join(','));
atltag's answer is fastest. Please see:
https://repl.it/FBpo/5
Just with a single .reduce() in O(n) time you can do as follows; (I have used array destructuring at the assignment part. One might choose to use three .push()s though)
var base = [1,1,1,2,3,5,7,9,14,19,28,40,56,114,232,330],
sky = [0,0,0,3,4,5,6,7,8,9,10,11,12,14,16,17],
ite = [64,52,23,38,13,15,6,4,6,3,2,1,2,1,1,1],
results = sky.reduce((r,c,i) => c === r[1][r[1].length-1] ? (r[2][r[2].length-1] += ite[i],r)
: ([r[0][r[0].length],r[1][r[1].length],r[2][r[2].length]] = [base[i],c,ite[i]],r),[[],[],[]]);
console.log(JSON.stringify(results));

Simulation of mouses moving, don't work

I'm trying to create a simulation of 150 mouses moving inside a 20x20 grid in p5.js (A processing like libary). First I'm spawning 150 mouses random places and everything goes fine. But after I have spawned the mouses I am trying to make them move to one of their neighbors. Instead of moving to one of the neighbors and make the current square empty it stays on the one that it already was one + it moves to the next one so instead of having 150 mouses i suddenly have 300... I have tried changing the code for hours but I can't find the proplem... Here is my code:
var w = 40;
var grid = [];
var mouses = 10;
var mouseAmount = [];
var Mouse;
var current;
function setup() {
createCanvas(800, 800);
cols = floor(width/w)
rows = floor(height/w)
// frameRate(60);
for (var j = 0; j < rows; j++) {
for ( var i = 0; i < cols; i++) {
var cell = new Cells(i,j);
grid.push(cell);
}
}
amount = new Amount;
}
function draw() {
background(51);
for ( var i = 0; i < grid.length; i++) {
grid[i].show();
}
amount.run();
}
function index(i, j) {
if (i < 0 || j < 0 || i > cols-1 || j > rows-1 ) {
return -1;
}
return i + j * cols;
}
function Cells(i, j) {
this.i = i;
this.j = j;
this.active = false;
this.moveCell = function() {
var neighbors = [];
var top = grid[index(i, j -1)];
var right = grid[index(i+1, j)];
var bottom = grid[index(i, j+1)];
var left = grid[index(i-1, j)];
if (top) {
neighbors.push(top)
}
if (right) {
neighbors.push(right)
}
if (bottom) {
neighbors.push(bottom)
}
if (left) {
neighbors.push(left)
}
if(neighbors.length > 0) {
var r = floor(random(0, neighbors.length));
return neighbors[r];
} else {
return undefined;
}
}
this.show = function() {
var x = this.i*w;
var y = this.j*w;
stroke(255);
noFill();
rect(x,y,w,w);
if(this.active == true) {
fill(155, 0, 255, 100)
rect(x, y, w, w)
}
}
}
function Amount() {
this.run = function() {
var r = floor(random(grid.length))
for (var i = 0; i < mouses; i++) {
var mouse = grid[r];
mouseAmount.push(mouse)
}
if (mouseAmount.length < 1499) {
for (var i = 0; i < mouseAmount.length; i++) {
mouseAmount[i].active = true;
}
}
if (mouseAmount.length > 1499) {
Next();
}
}
}
function Next(i,j) {
for (var i = 0; i < mouseAmount.length; i++) {
current = mouseAmount[i];
var nextCell = current.moveCell();
if (nextCell) {
nextCell.active = true;
current.active = false;
current = nextCell;
}
}
}
Thank you in advance :)
I don't really understand exactly what your code is supposed to do, but a few things stand out to me about your code:
Problem One: I don't understand how you're iterating through your grid array. You seem to be iterating over mouseAmount, which seems to hold random cells from the grid for some reason? That doesn't make a lot of sense to me. Why don't you just iterate over the grid array directly?
Problem Two: You then move the cells randomly to a neighbor, but you don't take into account whether the neighbor is already active or not. I'm not sure what you want to happen, but this seems a bit strange.
Problem Three: Usually with simulations like this, you have to copy the next generation into a new data structure instead of modifying the data structure as you step through it.
The biggest problem is that you haven't really explained what you want your code to do, or what this code does instead, or how those two things are different. But if I were you, I'd make the following changes:
Step One: Iterate over your grid array in a more reasonable way. Just iterate over every index and take the appropriate action for every cell. If I were you I would just use a 2D array and use a nested for loop to iterate over it.
Step Two: Make sure your logic for moving to a neighbor is correct. Do you want cells to move to already active cells?
Step Three: Make a copy of the grid before you modify it. Think about it this way: as you iterate over the grid, let's say you move a cell down one row. Then you continue iterating, you'll reach the newly active cell again. In other words, you'll touch the same active cell twice in one generation, which is definitely going to mess you up.
A word of advice: get this working for a single active cell first. It's really hard to tell what's going on since you have so many things going on at one time. Take a step back and make sure it works for one active cell before moving up to having a whole grid.

Finding if a point is in a polygon

I have this code here that is attempting to work out if a point latLng passes through a polygon Maps.area.
Maps.ui.contains = function(latLng){
//poly.getBounds gets the 'box' around the polygon
if(!Maps.ui.getBounds().contains(latLng))
return false;
//So we dont need to check t/f, we either set it or we dont
var inPolygon = false;
var count = 0;
Maps.area.getPaths().forEach(function(el0, index0){
var last = el0.getLength() - 1;
el0.forEach(function(el1, index1){
count += Maps.ui.ray_intersect_segment(latLng, el1, el0.getAt(last));
last = index1;
});
});
if(Maps.area.getPaths().getLength()%2 == 0)
return count%2==0;
else
return count%2!=0;
}
var eps = 0.0001;
var inf = 1e600;
Maps.ui.ray_intersect_segment = function(point, i1, i2){
var p = point;
var segment = (i1.lng() > i2.lng())?[i2, i1]:[i1, i2];
p = (p.lng() == segment[0].lng() || p.lng() == segment[1].lng())?new google.maps.LatLng(p.lng() + eps):p;
if(p.lng() < segment[0].lng() || p.lng() > segment[1].lng() || p.lat() > [segment[0].lat(), segment[1].lng()].max())
return 0;
if(p.lat() < [segment[0].lat(), segment[1].lat()].min())
return 1;
var a = (segment[0].lat() != segment[1].lat())?(segment[1].lng() - segment[0].lng())/(segment[1].lat() - segment[0].lat()):inf;
var b = (segment[0].lat() != p.lat()) ? (p.lng() - segment[0].lng())/(p.lat() - segment[0].lat()):inf;
return (b > a)?1:0;
}
Maps.ui.getBounds = function() {
//Lets make a box
var bounds = new google.maps.LatLngBounds();
//Get all the points lines of the polly
var paths = Maps.area.getPaths();
for (var p = 0; p < paths.getLength(); p++)
//To store each path
var path = paths.getAt(p);
//Now lets expand the box
for (var i = 0; i < path.getLength(); i++)
//Add each point of the line to the 'box' making it bigger each time
bounds.extend(path.getAt(i));
//Reaturn the bounds, this has a contains method so we can check if the latLng is in it.
return bounds;
}
Array.prototype.max = function() {
return Math.max.apply(null, this)
}
Array.prototype.min = function() {
return Math.min.apply(null, this)
}
But I can't seem to work it out. For a simple triangle or square it works perfectly, but when we get to something like this it doesn't work because we can't figure out whether count should be even or odd
The Google Maps API v3 spherical geometry library has the poly.contains. Takes a LatLng and a Polygon and tells you if the point is in the polygon.
containsLocation(point:LatLng, polygon:Polygon)
This is a pretty standard problem for geographical information systems. There are several "standard" algorithms for solving the problem. The link below notes a few of them and provides an example. Note that the algorithms tend to break down in edge cases, such as when the polygon spans the extreme lat/lon boundaries such as the poles and meridian.
Polygon Algorithms

Ordering z-indexes in an array

I have an array which looks something along the lines of
resourceData[0][0] = "pic1.jpg";
resourceData[0][1] = 5;
resourceData[1][0] = "pic2.jpg";
resourceData[1][1] = 2;
resourceData[2][0] = "pic3.jpg";
resourceData[2][1] = 900;
resourceData[3][0] = "pic4.jpg";
resourceData[3][1] = 1;
The numeric represents the z-index of the image. Minimum z-index value is 1. Maximum (not really important) is 2000.
I have all the rendering and setting z-indexes done fine. My question is, I want to have four functions:
// Brings image to z front
function bringToFront(resourceIndex) {
// Set z-index to max + 1
resourceData[resourceIndex][1] = getBiggestZindex() + 1;
// Change CSS property of image to bring to front
$('#imgD' + resourceIndex).css("z-index", resourceData[resourceIndex][1]);
}
function bringUpOne(resourceIndex) {
}
function bringDownOne(resourceIndex) {
}
// Send to back z
function sendToBack(resourceIndex) {
}
So given then index [3] (900 z):
If we send it to the back, it will take the value 1, and [3] will have to go to 2, but that conflicts with [1] who has a 2 z-index so they need to go to three etc.
Is there an easy programatical way of doing this because as soon as I start doing this it's going to get messy.
It's important that the indexes of the array don't change. We can't sort the array unfortunately due to design.
Update
Thanks for answers, I'll post the functions here once they are written incase anyone comes across this in the future (note this code has zindex listed in [6])
// Send to back z
function sendToBack(resourceIndex) {
resourceData[resourceIndex][6] = 1;
$('#imgD' + resourceIndex).css("z-index", 1);
for (i = 0; i < resourceData.length; i++) {
if (i != resourceIndex) {
resourceData[i][6]++;
$('#imgD' + i).css("z-index", resourceData[i][6]);
}
}
}
Loops! This function will reorder affected images around it. It will work with images that have widely separated z-index values. It also does not perform any changes unless it needs to.
EDIT: added function to do the CSS work
EDIT 2: Corrected problem with top/bottom functions - it wasn't moving all the images affected, now it is.
var resourceData = Array();
resourceData[0] = Array();
resourceData[0][0] = "pic1.jpg";
resourceData[0][1] = 5;
resourceData[1] = Array();
resourceData[1][0] = "pic2.jpg";
resourceData[1][1] = 2;
resourceData[2] = Array();
resourceData[2][0] = "pic3.jpg";
resourceData[2][1] = 900;
resourceData[3] = Array();
resourceData[3][0] = "pic4.jpg";
resourceData[3][1] = 1;
function _doMoveImage(ptr) {
// Change CSS property of image
$('#imgD' + ptr).css("z-index", resourceData[ptr][1]);
}
// Brings image to z front
function bringToFront(resourceIndex) {
var highest_idx = 0;
for (var i = 0; i < resourceData.length; i++) {
// for all images except the target
if (i != resourceIndex) {
// preserve the highest index we encounter
if (highest_idx < resourceData[i][1])
highest_idx = resourceData[i][1];
// move any images higher than the target down by one
if (resourceData[i][1] > resourceData[resourceIndex][1]) {
resourceData[i][1]--;
_doMoveImage(i);
}
}
}
// now move the target to the highest spot, only if needed
if (resourceData[resourceIndex][1] < highest_idx) {
resourceData[resourceIndex][1] = highest_idx;
_doMoveImage(resourceIndex);
}
return;
}
function bringUpOne(resourceIndex) {
var next_idx = 2000;
var next_ptr = false;
for (var i =0; i < resourceData.length; i++) {
// for all images except the target
if (
i != resourceIndex &&
next_idx > resourceData[i][1] &&
resourceData[i][1] > resourceData[resourceIndex][1]
){
next_idx = resourceData[i][1];
next_ptr = i;
}
}
// only move if needed
if (next_ptr) {
// target takes next's index
resourceData[resourceIndex][1] = resourceData[next_ptr][1];
// next's index decreases by one
resourceData[next_ptr][1]--;
_doMoveImage(resourceIndex);
_doMoveImage(next_ptr);
}
return;
}
function bringDownOne(resourceIndex) {
var next_idx = 0;
var next_ptr = false;
for (var i =0; i < resourceData.length; i++) {
// for all images except the target
if (
i != resourceIndex &&
next_idx < resourceData[i][1] &&
resourceData[i][1] < resourceData[resourceIndex][1]
){
next_idx = resourceData[i][1];
next_ptr = i;
}
}
// only move if needed
if (next_ptr) {
// target takes next's index
resourceData[resourceIndex][1] = resourceData[next_ptr][1];
// next's index decreases by one
resourceData[next_ptr][1]++;
_doMoveImage(resourceIndex);
_doMoveImage(next_ptr);
}
}
// Send to back z
function sendToBack(resourceIndex) {
var lowest_idx = 2000;
for (var i = 0; i < resourceData.length; i++) {
// for all images except the target
if (i != resourceIndex) {
// preserve the lowest index we encounter
if (lowest_idx > resourceData[i][1])
lowest_idx = resourceData[i][1];
// move any images lower than the target up by one
if (resourceData[i][1] < resourceData[resourceIndex][1]) {
resourceData[i][1]++;
_doMoveImage(i);
}
}
}
// now move the target to the lowest spot, only if needed
if (resourceData[resourceIndex][1] > lowest_idx) {
resourceData[resourceIndex][1] = lowest_idx;
_doMoveImage(resourceIndex);
}
return;
}
There it is: copy your structure and have it properly ordered, or if you prefer call it indexed. When you need the picture find the proper z-index and proceed w/ the rendering.
You can do that dynamically and use heap, if you don't wish to copy the entire structure.
Not tested, but how about this. For bringUpOne, bringDownOne one you can swap the z-indexes, e.g:
function bringUpOne(resourceIndex) {
var myZIndex = resourceData[resourceIndex][1];
var nextZIndex = resourceData[resourceIndex + 1][1];
resourceData[resourceIndex][1] = nextZIndex;
resourceData[resourceIndex + 1][1] = myZindex;
}
For bringing to the front:
function bringToFront(resourceIndex) {
var maxZIndex = resourceData[maxResourceIndex][1];
resourceData[resourceIndex][1] = maxZIndex + 1;
}
Now that's all fine. But what happens if you want to successively set images to the back? You can either set the zIndex to 0 each time (don't know how many you'll have actually visiable at any time) or you can start with the lowest zIndex set to something like 2000 or 10000 or whatever, which will acccomodate many calls to setToBack.
Edit: This assumes the list is ordered by zIndex to start with.

Categories

Resources