Animate all SnakeAnimations at once instead of one - Leaflet js - javascript

Currently I am using Leaflet JS and an addon called "SnakeAnim" which can be found
here
All of my map markers before using the snake animation were loading together and drawing an arc from the markers starting position to a certain location all at once.
All of the markers end at this certain location for their arc.
My problem is when I use the SnakeIn() function that it only animates one marker at a time, what I want it to do is animate them all together, simultaneously.
Inside my PopulateMap function I have:
lg.addLayer(L.marker(latlng, { icon: cssIcon }));
lg.addLayer(L.Polyline.Arc([latlng.lat, latlng.lng],
[netLat, netLong],
{
color: "red",
vertices: 50,
weight: 1,
opacity: 1,
snakeSpeed: 50
}));
return lg;
Note! netLat + netLong always have the same values.
Once returned I add it to the map and call the snakeIn() function.
.addTo(map).snakeIn();
Using this code it produces one marker, draws an arc from the marker start location to the end location, then loads the next marker and repeats the process.
I tried doing this also and removed the .snakeIn() part from the end of .addTo(map):
lg.addLayer(L.marker(latlng, { icon: cssIcon }));
lg.addLayer(L.Polyline.Arc([latlng.lat, latlng.lng],
[netLat, netLong],
{
color: "red",
vertices: 50,
weight: 1,
opacity: 1,
snakeSpeed: 0
}).snakeIn());
This way did, in-fact load all of the markers at once, but also it loaded the arcs with no animation except for 1. The very last marker was the only one that would animate not the others.
So I'm wondering if anybody else has experienced this same problem and worked around it or if anybody has any ideas on how I would accomplish this.
Thanks for your help :)

For anybody with a similar problem, mine was related to adding them to lg.addLayer method.
What I do now is I add them to the map rather than the lg.layer:
function drawArc(source, destination, leafletMap) {
for (var i = 0; i < source.length; i++) {
L.Polyline.Arc([source[i].geometry.coordinates[1], source[i].geometry.coordinates[0]],
destination,
{
color: "red",
vertices: 200,
snakingSpeed: 200
})
.addTo(leafletMap).snakeIn();
}
}
This way, all of my lines start animating at the same time.

Related

How to color LineString segments differently in Mapbox GL JS animations

I'm making an animation that shows where wolves go, based on some historical GPS-collar data I've got.
The code is based on this Mapbox example:
https://docs.mapbox.com/mapbox-gl-js/example/live-update-feature/
I would like to color the line segments based on whether it was daytime or nighttime, blue for night and red for daytime. Like this:
In time period 1, the wolf moves east-northeast; it's nighttime, so the line segment is blue. In time period 2, the wolf moves northeast; it's daytime, so the line segment is red. In time period 3, the wolf moves east-northeast again; it's nighttime, so the line segment is blue again.
But I can't seem to get the different coloring to work. I've got some toy/example data:
{ "type": "FeatureCollection", "features": [ { "type": "Feature", "geometry": {
"type": "LineString", "coordinates" : [[-112.191833, 57.073668],
[-112.181833, 57.083668],
[-112.181833, 57.073668],
[-112.201833, 57.075668]]} } ],
"properties": {"daytime" : [0, 1, 1, 0] }}
There are 4 time periods and the middle two are daytime (set to 1).
Here's my code. (You'll need to paste in your mapbox key for it to work):
mapboxgl.accessToken = 'INSERT YOUR MAPBOX KEY HERE';
var map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/satellite-v9',
zoom: 0
});
map.on('load', function() {
// We use D3 to fetch the JSON here to parse and use it separately
// from GL JS's use in the added source. You can use any request method
// that you want.
d3.json(
"https://raw.githubusercontent.com/pete-rodrigue/wolves_of_alberta/data_store/wolves_geojson_example.geojson?token=ACEMB42EH5NKZSF24MHPQSS6JFTMU",
function(err, data) {
if (err) throw err;
// save full coordinate list for later
var coordinates = data.features[0].geometry.coordinates;
// save 1's and 0's for later
var daynight = data.properties.daytime
// start by showing just the first coordinate
data.features[0].geometry.coordinates = [coordinates[0]];
// THIS NEXT LINE IS ONE PART OF MY FAILED APPROACH:
data.properties.daytime = ['blue']; // set initial color value to blue
// add it to the map
map.addSource('trace', { type: 'geojson', data: data });
map.addLayer({
'id': 'trace',
'type': 'line',
'source': 'trace',
'paint': {
// THIS WILL WORK FINE
'line-color': 'orange',
// 'line-color': ['to-string', ['get', 'daytime']], // DOES NOT WORK
'line-width': 8
},
layout: {'line-cap': 'round', 'line-join': 'round'}
});
// setup the viewport
map.jumpTo({ 'center': coordinates[0], 'zoom': 13 });
map.setPitch(30);
// on a regular basis, add more coords from the saved list to update map
var i = 0;
var timer = window.setInterval(function() {
if (i < coordinates.length) {
data.features[0].geometry.coordinates.push(
coordinates[i]
);
// if it's daytime, append blue; if it's nighttime, append red
if (daynight[i] == 0) {
data.properties.daytime.push(['blue']);
} else {data.properties.daytime.push(['red']);}
map.getSource('trace').setData(data);
map.panTo(coordinates[i]);
i++;
} else {
window.clearInterval(timer);
}
}, 150);
}
);
});
body { margin: 0; padding: 0; }
#map { position: absolute; top: 0; bottom: 0; width: 100%; }
<head>
<meta charset="utf-8" />
<title>Wolves GPS collar example data</title>
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" />
<script src="https://api.mapbox.com/mapbox-gl-js/v1.7.0/mapbox-gl.js"></script>
<script src="https://d3js.org/d3.v4.min.js" charset="utf-8"></script>
<link href="https://api.mapbox.com/mapbox-gl-js/v1.7.0/mapbox-gl.css" rel="stylesheet" />
<link rel="stylesheet" href="main_css.css">
</head>
<body>
<div id="map"></div>
<script type="text/javascript" src='main_js.js'></script>
</body>
Also here:
https://codepen.io/pete-rodrigue/pen/XWbJOpK
I've commented out the part that wasn't working and left a note.
Basically, I'm trying to do this:
'line-color': ['to-string', ['get', 'daytime']]
in the paint part of map.addLayer(), where the daytime property is an array of strings that say 'blue' or 'red', which I push new elements onto as the animation progresses--that's this part of the code:
if (daynight[i] == 0) {
data.properties.daytime.push(['blue']); // if it's daytime, append blue
} else {
data.properties.daytime.push(['red']); // if it's nighttime, append red
}
I'm sure there's an obvious reason why this doesn't work. But I'm new at this and can't fathom it.
Any help and explanation of the fundamentals would be much appreciated.
When you specify:
'line-color': ['to-string', ['get', 'daytime']]
in the 'trace' layer with Map#addLayer, the map renderer is being told to use '[0, 1, 1, 0]' (in the case of the example data you provided) as the color for the lines in the source with id 'trace'. It does not wait for you to set up the timer, and even if it did, you'd likely run into some problems with JavaScript's asynchronicity. As noted in Mapbox's style specification, 'line-color' expects a color in the form of HTML-style hex values, RGB, RGBA, HSL, or HSLA, so you are likely running into errors since a stringified array is none of those forms.
Instead, I'd recommend breaking up each time period into its own LineString where the 'line-color' is specified by its corresponding value in the original color array, and adding these LineStrings to the map as separate layers. You can then create the animation effect by setting up some intervals to specify the order in which the layers should be added and animated as done in the live update feature example.

How to change color of first vertex while drawing polygon using Leaflet.Draw?

I am using Leaflet and Leaflet.Draw, and I am letting the user from my code to draw polygon (NOT using the Leaflet Draw Controls).
While the user is drawing the polygon I need to change the color of its first vertex, for example: green, so that user knows that he needs to click on the first point in order to close the polygon and finish drawing.
How can I change color of first vertex while drawing polygon using Leaflet.Draw?
The following image for elaboration, meaning it's fixed with a Paint Software.
P.S. Here is my code
var map = L.map('mapid',
{
minZoom: -1,
maxZoom: 4,
center: [0, 0],
zoom: 1,
crs: L.CRS.Simple
});
var polygonDrawer = new L.Draw.Polygon(map);
map.on('draw:created', function (e) {
var type = e.layerType, layer = e.layer;
layer.editing.enable();
layer.addTo(map);
});
$(document)ready(function(){
polygonDrawer.enable();
});
While I was hacking with the Leaflet.Draw and on the creation of polygon I have come up with the following code:
map.on('draw:drawvertex',
function (e) {
$(".leaflet-marker-icon.leaflet-div-icon.leaflet-editing-icon.leaflet-touch-icon.leaflet-zoom-animated.leaflet-interactive:first").css({ 'background-color': 'green' });
});
So, there is a listener you can insert it in your code, draw:drawvertex which means whenever a vertex created I need to do something.
Then, using jQuery you're selecting the first element from this long selector, and set its background color to green or any other color.
This is a way to do it with CSS only:
#root
> main
> div
> div.col-sm-8.m-auto.p-0.flex-column.float-right
> div.leaflet-container.leaflet-touch.leaflet-fade-anim.leaflet-grab.leaflet-touch-drag.leaflet-touch-zoom
> div.leaflet-pane.leaflet-map-pane
> div.leaflet-pane.leaflet-marker-pane
> div:nth-child(2) {
background: green;
}
For me, worked this way (classes are a little bit different. leaflet 1.3.1 and draw 0.4.3)
map.on('draw:drawvertex', function (e) {
$(".leaflet-marker-icon.leaflet-div-icon.leaflet-editing-icon.leaflet-zoom-animated.leaflet-interactive:first").css({ 'background-color': 'green' });
});
This is worked for me:
map.on("editable:vertex:dragend", function (e) {
// Set GREEN color for Vertex START (First) Point
$(".leaflet-marker-icon.leaflet-div-icon.leaflet-vertex-icon.leaflet-zoom-animated.leaflet-interactive.leaflet-marker-draggable:nth-child(1)").css({ 'background-color': 'green' });
// Set RED color for Vertex END (Last) Point
$(".leaflet-marker-icon.leaflet-div-icon.leaflet-vertex-icon.leaflet-zoom-animated.leaflet-interactive.leaflet-marker-draggable:nth-child(2)").css({ 'background-color': 'red' });
});

How to zoom gradually in leaflet, javascript

I use leaflet and geojson-vt too displaing map, and some lines in vector tiles. I made some modifications in geojson-vt because i need to add some my functions when tiles are slicing.
Everything works fine, when i start my leafletMap from zoom 1, and then increasing zoom by mouse wheel, to for example zoom=15. But There is a problem when i start my Map with zoom= for example 7,
var leafletMap = L.map('map').setView([52.00, 19.64], 7);
because the vector tiles are not beeing calcuated from 0 to 7, but only at 7, so "my function" dont working well.
I think that the solution will be to start map on zoom 0, and then in loop increasing zoom to 7. But i dont know how.
I tried this but it isn't working with multiple zooms...
setTimeout(function() {
leafletMap.setZoom(2);
}, 300);
...
setTimeout(function() {
leafletMap.setZoom(7);
}, 300);
Here is an example that shows how to zoom in gradually. Part of the problem with your code is that you called sequential setTimeout methods with the same delay and so they will be executed one right after another. If you change the milliseconds so that they increase (300, 600, 900, ...) then you will actually see the animated zoom.
This was quick example using OSM tiles and not geojson-vt, so it looks a little clunky until your browser caches the tiles. However, with geojson-vt you are creating your own local vector tiles and so it should be a bit smoother.
However, I'm not sure this will solve your problem because you didn't show the code you changed in geojson-vt. It may be that setZoom() isn't triggering your functions, but until you show those custom functions it will be hard to get a proper answer to your question.
var zoomDelayMs = 600; // milliseconds for animation delay
var maxZoom = 18;
var initialZoom = 7;
// Create the map
var map = L.map('map').setView([39.5, -0.5], initialZoom);
// Set up the OSM layer
var baseLayer = L.tileLayer(
'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: maxZoom
}).addTo(map);
// function to zoom in gradually from initialZoom to maxZoom
slowZoom = function() {
// reset zoom to default
var zoom = initialZoom;
map.setZoom(zoom);
// if already in middle of slow zoom, stop it
if (map.zoomtimer) clearInterval(map.zoomtimer);
// zoom in one level every zoomDelayMs
map.zoomtimer = setInterval(function() {
if (zoom < maxZoom)
map.setZoom(++zoom);
else {
clearInterval(map.zoomtimer);
map.zoomtimer = 0;
}
}, zoomDelayMs);
}
#map {
height: 400px;
}
input {
font-size: 1.6em;
}
<link href="https://npmcdn.com/leaflet#0.7.7/dist/leaflet.css" rel="stylesheet" />
<script src="https://npmcdn.com/leaflet#0.7.7/dist/leaflet.js"></script>
<input type='button' onclick='slowZoom()' value='Start slow zoom' />
<div id="map"></div>

How to dynamically move Vector Features in OpenLayers 3

Based from an example given here: http://openlayers.org/en/vector-api/examples/dynamic-data.html?q=dynamic
Instead of using circle:
var imageStyle = new ol.style.Circle({
radius: 5,
fill: new ol.style.Fill({color: 'yellow'}),
stroke: new ol.style.Stroke({color: 'red', width: 1})
});
I want to use Vector Feature (Marker) as the object which is moving instead of using that yellow circle.
An example of using a feature vector is found here:
how to add markers with OpenLayers 3
Sorry, just a beginner in OpenLayers 3. Hope someone can help me. Thanks!
I've made you a basic example.
The idea is: You move an Overlay through a path using an interval to change its position like:
//fire the animation
map.once('postcompose', function(event) {
interval = setInterval(animation, 500);
});
var i = 0, interval;
var animation = function(){
if(i == path.length){
i = 0;
}
marker.setPosition(path[i]);
i++;
};

Delay between creation and display of polygons

To create polygons in my map, I am using jQuery.getJSON() to load geojson files containing polygons and multipolygons. Then I analyse the geojson with a github plugin (loadgeojson) and finally the polygons are created on the map.
I put a <div> with a loading gif that overlays the map that appears just before the jQuery.getJSON() is being called.
The problem is the timing to remove it. I make the loading animation disappears when all the polygons visible property is set to True.
I want the <div> to disappears when the polygons appears on the map. But for the moment, it disappear a little before this. So on slower browser there is a relatively big delay between the <div> disappearing and the polygons appearing.
I tried to put a listener on an event but I couldn't find an event that corresponded to what I want.
How can I remove the loading animation right on time when the polygons appears on my map?
Here's my code:
function readJSON(id){
showLoadingAnimation();
// If .json hasn't been read
if(stockArray[id].length == 0) {
$.getJSON(id + ".json", function(data){
showFeature(data, id)
})
}
}
function showFeature(geojson, elemtype){
currentFeature_or_Features = new GeoJSON(geojson, elemtype, options);
if (currentFeature_or_Features.type && currentFeature_or_Features.type == "Error"){
return;
}
// Display object
if (currentFeature_or_Features.length){
for (var i = 0; i < currentFeature_or_Features.length; i++){
if(currentFeature_or_Features[i].length){
for(var j = 0; j < currentFeature_or_Features[i].length; j++){
// Display multipolygon
currentFeature_or_Features[i][j].setMap(map);
// Mouse events for multipolygons
mouseEventsMulti(i,j,elemtype);
}
}
else{
// Display polygons, polylines and points
currentFeature_or_Features[i].setMap(map);
// Mouse events for polygons, polylines and points
mouseEventsSimple(i,elemtype)
}
}
} else {
currentFeature_or_Features.setMap(map)
}
// Stop loading animation
dontShowLoadingAnimation();
}
Finally, I modified my code to pan the map a tiny little bit after the polygons are created. Then it activate the idle listener that stops the loading animation.
It might not be the prettiest bit of code, but it works.
This is what I added to the showFeature function
center = map.getCenter();
latLngCenter = new google.maps.LatLng(center.lat() + 0.0000001,center.lng() + 0.0000001);
map.panTo(latLngCenter);
And this is the Listener
google.maps.event.addListener(map, 'idle', function() {
// Stop loading animation
dontShowLoadingAnimation();
});

Categories