Merge pull request #4291 from jonataswalker/feature-move-example
Add 'Move a feature along a line' example.
This commit is contained in:
24
examples/feature-move-animation.html
Normal file
24
examples/feature-move-animation.html
Normal file
@@ -0,0 +1,24 @@
|
||||
---
|
||||
template: example.html
|
||||
title: Animate a feature movement
|
||||
shortdesc: Demonstrates how to move a feature along a line.
|
||||
docs: >
|
||||
This example shows how to use <b>postcompose</b> and <b>vectorContext</b> to
|
||||
animate a (marker) feature along a line. In this example an encoded polyline
|
||||
is being used.
|
||||
tags: "animation, feature, postcompose, polyline"
|
||||
---
|
||||
<div class="row-fluid">
|
||||
<div class="span12">
|
||||
<div id="map" class="map"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row-fluid">
|
||||
<div class="span12">
|
||||
<label for="speed">
|
||||
speed:
|
||||
<input id="speed" type="range" min="10" max="999" step="10" value="60">
|
||||
</label>
|
||||
<button id="start-animation">Start Animation</button>
|
||||
</div>
|
||||
</div>
|
||||
205
examples/feature-move-animation.js
Normal file
205
examples/feature-move-animation.js
Normal file
@@ -0,0 +1,205 @@
|
||||
goog.require('ol.Feature');
|
||||
goog.require('ol.Map');
|
||||
goog.require('ol.View');
|
||||
goog.require('ol.format.Polyline');
|
||||
goog.require('ol.geom.Point');
|
||||
goog.require('ol.layer.Tile');
|
||||
goog.require('ol.layer.Vector');
|
||||
goog.require('ol.source.BingMaps');
|
||||
goog.require('ol.source.Vector');
|
||||
goog.require('ol.style.Circle');
|
||||
goog.require('ol.style.Fill');
|
||||
goog.require('ol.style.Icon');
|
||||
goog.require('ol.style.Stroke');
|
||||
goog.require('ol.style.Style');
|
||||
|
||||
// This long string is placed here due to jsFiddle limitations.
|
||||
// It is usually loaded with AJAX.
|
||||
var polyline = [
|
||||
'hldhx@lnau`BCG_EaC??cFjAwDjF??uBlKMd@}@z@??aC^yk@z_@se@b[wFdE??wFfE}N',
|
||||
'fIoGxB_I\\gG}@eHoCyTmPqGaBaHOoD\\??yVrGotA|N??o[N_STiwAtEmHGeHcAkiA}^',
|
||||
'aMyBiHOkFNoI`CcVvM??gG^gF_@iJwC??eCcA]OoL}DwFyCaCgCcCwDcGwHsSoX??wI_E',
|
||||
'kUFmq@hBiOqBgTwS??iYse@gYq\\cp@ce@{vA}s@csJqaE}{@iRaqE{lBeRoIwd@_T{]_',
|
||||
'Ngn@{PmhEwaA{SeF_u@kQuyAw]wQeEgtAsZ}LiCarAkVwI}D??_}RcjEinPspDwSqCgs@',
|
||||
'sPua@_OkXaMeT_Nwk@ob@gV}TiYs[uTwXoNmT{Uyb@wNg]{Nqa@oDgNeJu_@_G}YsFw]k',
|
||||
'DuZyDmm@i_@uyIJe~@jCg|@nGiv@zUi_BfNqaAvIow@dEed@dCcf@r@qz@Egs@{Acu@mC',
|
||||
'um@yIey@gGig@cK_m@aSku@qRil@we@{mAeTej@}Tkz@cLgr@aHko@qOmcEaJw~C{w@ka',
|
||||
'i@qBchBq@kmBS{kDnBscBnFu_Dbc@_~QHeU`IuyDrC_}@bByp@fCyoA?qMbD}{AIkeAgB',
|
||||
'k_A_A{UsDke@gFej@qH{o@qGgb@qH{`@mMgm@uQus@kL{_@yOmd@ymBgwE}x@ouBwtA__',
|
||||
'DuhEgaKuWct@gp@cnBii@mlBa_@}|Asj@qrCg^eaC}L{dAaJ_aAiOyjByH{nAuYu`GsAw',
|
||||
'Xyn@ywMyOyqD{_@cfIcDe}@y@aeBJmwA`CkiAbFkhBlTgdDdPyiB`W}xDnSa}DbJyhCrX',
|
||||
'itAhT}x@bE}Z_@qW_Kwv@qKaaAiBgXvIm}A~JovAxCqW~WanB`XewBbK{_A`K}fBvAmi@',
|
||||
'xBycBeCauBoF}}@qJioAww@gjHaPopA_NurAyJku@uGmi@cDs[eRaiBkQstAsQkcByNma',
|
||||
'CsK_uBcJgbEw@gkB_@ypEqDoqSm@eZcDwjBoGw`BoMegBaU_`Ce_@_uBqb@ytBwkFqiT_',
|
||||
'fAqfEwe@mfCka@_eC_UmlB}MmaBeWkkDeHwqAoX}~DcBsZmLcxBqOwqE_DkyAuJmrJ\\o',
|
||||
'~CfIewG|YibQxBssB?es@qGciA}RorAoVajA_nAodD{[y`AgPqp@mKwr@ms@umEaW{dAm',
|
||||
'b@umAw|@ojBwzDaaJsmBwbEgdCsrFqhAihDquAi`Fux@}_Dui@_eB_u@guCuyAuiHukA_',
|
||||
'lKszAu|OmaA{wKm}@clHs_A_rEahCssKo\\sgBsSglAqk@yvDcS_wAyTwpBmPc|BwZknF',
|
||||
'oFscB_GsaDiZmyMyLgtHgQonHqT{hKaPg}Dqq@m~Hym@c`EuiBudIabB{hF{pWifx@snA',
|
||||
'w`GkFyVqf@y~BkoAi}Lel@wtc@}`@oaXi_C}pZsi@eqGsSuqJ|Lqeb@e]kgPcaAu}SkDw',
|
||||
'zGhn@gjYh\\qlNZovJieBqja@ed@siO{[ol\\kCmjMe\\isHorCmec@uLebB}EqiBaCg}',
|
||||
'@m@qwHrT_vFps@kkI`uAszIrpHuzYxx@e{Crw@kpDhN{wBtQarDy@knFgP_yCu\\wyCwy',
|
||||
'A{kHo~@omEoYmoDaEcPiuAosDagD}rO{{AsyEihCayFilLaiUqm@_bAumFo}DgqA_uByi',
|
||||
'@swC~AkzDlhA}xEvcBa}Cxk@ql@`rAo|@~bBq{@``Bye@djDww@z_C_cAtn@ye@nfC_eC',
|
||||
'|gGahH~s@w}@``Fi~FpnAooC|u@wlEaEedRlYkrPvKerBfYs}Arg@m}AtrCkzElw@gjBb',
|
||||
'h@woBhR{gCwGkgCc[wtCuOapAcFoh@uBy[yBgr@c@iq@o@wvEv@sp@`FajBfCaq@fIipA',
|
||||
'dy@ewJlUc`ExGuaBdEmbBpBssArAuqBBg}@s@g{AkB{bBif@_bYmC}r@kDgm@sPq_BuJ_',
|
||||
's@{X_{AsK_d@eM{d@wVgx@oWcu@??aDmOkNia@wFoSmDyMyCkPiBePwAob@XcQ|@oNdCo',
|
||||
'SfFwXhEmOnLi\\lbAulB`X_d@|k@au@bc@oc@bqC}{BhwDgcD`l@ed@??bL{G|a@eTje@',
|
||||
'oS~]cLr~Bgh@|b@}Jv}EieAlv@sPluD{z@nzA_]`|KchCtd@sPvb@wSb{@ko@f`RooQ~e',
|
||||
'[upZbuIolI|gFafFzu@iq@nMmJ|OeJn^{Qjh@yQhc@uJ~j@iGdd@kAp~BkBxO{@|QsAfY',
|
||||
'gEtYiGd]}Jpd@wRhVoNzNeK`j@ce@vgK}cJnSoSzQkVvUm^rSgc@`Uql@xIq\\vIgg@~k',
|
||||
'Dyq[nIir@jNoq@xNwc@fYik@tk@su@neB}uBhqEesFjoGeyHtCoD|D}Ed|@ctAbIuOzqB',
|
||||
'_}D~NgY`\\um@v[gm@v{Cw`G`w@o{AdjAwzBh{C}`Gpp@ypAxn@}mAfz@{bBbNia@??jI',
|
||||
'ab@`CuOlC}YnAcV`@_^m@aeB}@yk@YuTuBg^uCkZiGk\\yGeY}Lu_@oOsZiTe[uWi[sl@',
|
||||
'mo@soAauAsrBgzBqgAglAyd@ig@asAcyAklA}qAwHkGi{@s~@goAmsAyDeEirB_{B}IsJ',
|
||||
'uEeFymAssAkdAmhAyTcVkFeEoKiH}l@kp@wg@sj@ku@ey@uh@kj@}EsFmG}Jk^_r@_f@m',
|
||||
'~@ym@yjA??a@cFd@kBrCgDbAUnAcBhAyAdk@et@??kF}D??OL'
|
||||
].join('');
|
||||
|
||||
var route = /** @type {ol.geom.LineString} */ (new ol.format.Polyline({
|
||||
factor: 1e6
|
||||
}).readGeometry(polyline, {
|
||||
dataProjection: 'EPSG:4326',
|
||||
featureProjection: 'EPSG:3857'
|
||||
}));
|
||||
|
||||
var routeCoords = route.getCoordinates();
|
||||
var routeLength = routeCoords.length;
|
||||
|
||||
var routeFeature = new ol.Feature({
|
||||
type: 'route',
|
||||
geometry: route
|
||||
});
|
||||
var geoMarker = new ol.Feature({
|
||||
type: 'geoMarker',
|
||||
geometry: new ol.geom.Point(routeCoords[0])
|
||||
});
|
||||
var startMarker = new ol.Feature({
|
||||
type: 'icon',
|
||||
geometry: new ol.geom.Point(routeCoords[0])
|
||||
});
|
||||
var endMarker = new ol.Feature({
|
||||
type: 'icon',
|
||||
geometry: new ol.geom.Point(routeCoords[routeLength - 1])
|
||||
});
|
||||
|
||||
var styles = {
|
||||
'route': new ol.style.Style({
|
||||
stroke: new ol.style.Stroke({
|
||||
width: 6, color: [237, 212, 0, 0.8]
|
||||
})
|
||||
}),
|
||||
'icon': new ol.style.Style({
|
||||
image: new ol.style.Icon({
|
||||
anchor: [0.5, 1],
|
||||
src: 'data/icon.png'
|
||||
})
|
||||
}),
|
||||
'geoMarker': new ol.style.Style({
|
||||
image: new ol.style.Circle({
|
||||
radius: 7,
|
||||
snapToPixel: false,
|
||||
fill: new ol.style.Fill({color: 'black'}),
|
||||
stroke: new ol.style.Stroke({
|
||||
color: 'white', width: 2
|
||||
})
|
||||
})
|
||||
})
|
||||
};
|
||||
|
||||
var vectorLayer = new ol.layer.Vector({
|
||||
source: new ol.source.Vector({
|
||||
features: [routeFeature, geoMarker, startMarker, endMarker]
|
||||
}),
|
||||
style: function(feature, resolution) {
|
||||
// hide geoMarker if animation is active
|
||||
if (animating && feature.get('type') === 'geoMarker') {
|
||||
return [];
|
||||
}
|
||||
return [styles[feature.get('type')]];
|
||||
}
|
||||
});
|
||||
|
||||
var center = [-5639523.95, -3501274.52];
|
||||
var map = new ol.Map({
|
||||
target: document.getElementById('map'),
|
||||
loadTilesWhileAnimating: true,
|
||||
view: new ol.View({
|
||||
center: center,
|
||||
zoom: 10,
|
||||
minZoom: 2,
|
||||
maxZoom: 19
|
||||
}),
|
||||
layers: [
|
||||
new ol.layer.Tile({
|
||||
source: new ol.source.BingMaps({
|
||||
imagerySet: 'AerialWithLabels',
|
||||
key: 'Ak-dzM4wZjSqTlzveKz5u0d4IQ4bRzVI309GxmkgSVr1ewS6iPSrOvOKhA-CJlm3'
|
||||
})
|
||||
}),
|
||||
vectorLayer
|
||||
]
|
||||
});
|
||||
|
||||
var moveFeature = function(event) {
|
||||
var vectorContext = event.vectorContext;
|
||||
var frameState = event.frameState;
|
||||
|
||||
if (animating) {
|
||||
var elapsedTime = frameState.time - now;
|
||||
// here the trick to increase speed is to jump some indexes
|
||||
// on lineString coordinates
|
||||
var index = Math.round(speed * elapsedTime / 1000);
|
||||
|
||||
if (index >= routeLength) {
|
||||
stopAnimation(true);
|
||||
return;
|
||||
}
|
||||
|
||||
var currentPoint = new ol.geom.Point(routeCoords[index]);
|
||||
var feature = new ol.Feature(currentPoint);
|
||||
vectorContext.drawFeature(feature, styles.geoMarker);
|
||||
}
|
||||
// tell OL3 to continue the postcompose animation
|
||||
map.render();
|
||||
};
|
||||
|
||||
function startAnimation() {
|
||||
if (animating) {
|
||||
stopAnimation(false);
|
||||
} else {
|
||||
animating = true;
|
||||
now = new Date().getTime();
|
||||
speed = speedInput.value;
|
||||
startButton.textContent = 'Cancel Animation';
|
||||
// hide geoMarker
|
||||
geoMarker.setStyle(null);
|
||||
// just in case you pan somewhere else
|
||||
map.getView().setCenter(center);
|
||||
map.on('postcompose', moveFeature);
|
||||
map.render();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param {boolean} ended end of animation.
|
||||
*/
|
||||
function stopAnimation(ended) {
|
||||
animating = false;
|
||||
startButton.textContent = 'Start Animation';
|
||||
|
||||
// if animation cancelled set the marker at the beginning
|
||||
var coord = ended ? routeCoords[routeLength - 1] : routeCoords[0];
|
||||
/** @type {ol.geom.Point} */ (geoMarker.getGeometry())
|
||||
.setCoordinates(coord);
|
||||
//remove listener
|
||||
map.un('postcompose', moveFeature);
|
||||
}
|
||||
|
||||
var speed, now;
|
||||
var animating = false;
|
||||
var speedInput = document.getElementById('speed');
|
||||
var startButton = document.getElementById('start-animation');
|
||||
startButton.addEventListener('click', startAnimation, false);
|
||||
Reference in New Issue
Block a user