Bound popup removed when layer changed in control - leaflet

I have a map with a layer control that has overlays specified in the baselayer parameter:
var overlays = {
'Layer 1': mylayer1,
'Layer 2': mylayer2
};
L.control.layers( overlays, null, { collapsed: false } ).addTo( map );
I specify my layers as follows:
var mylayer1 = L.esri.featureLayer({
url: 'https://.../MapServer/5'
}).on( 'load', function ( e ) {
...
}).on( 'loading', function ( e ) {
...
}).bindPopup( function ( layer ) {
return L.Util.template( '<p>{_score}</p>', layer.feature.properties );
});
The issue is that when I change layers in the control the bindPopup event no longer gets called.
It's almost like the layer z-index is not updated. Would appreciate any insight on how I can address this.
See: https://codepen.io/jvanulde/pen/LYyOWZo

I see no one has given an answer.
A little around, but it works.
You add the id: x to each layer. Later in the loop you check which layer is active, and all the rest of the layers you add the style display: none.
window.addEventListener('DOMContentLoaded', () => {
let tiles = L.tileLayer('//{s}.tile.osm.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap contributors, Points &copy 2012 LINZ'
});
let l1 = L.esri.featureLayer({
url: 'https://maps-cartes.services.geo.ca/server_serveur/rest/services/NRCan/nhsl_en/MapServer/1',
id: 0, // required
simplifyFactor: 0.25,
precision: 5,
fields: ['OBJECTID'],
renderer: L.canvas()
}).bindPopup(function(layer) {
return L.Util.template('<p>Layer 1: <strong>{OBJECTID}</strong></p>', layer.feature.properties);
});
let l2 = L.esri.featureLayer({
url: 'https://maps-cartes.services.geo.ca/server_serveur/rest/services/NRCan/nhsl_en/MapServer/2',
id: 1, // required
simplifyFactor: 0.25,
precision: 5,
fields: ['OBJECTID'],
renderer: L.canvas()
}).bindPopup(function(layer) {
return L.Util.template('<p>Layer 2: <strong>{OBJECTID}</strong></p>', layer.feature.properties);
});
let map = L.map('map', {
center: [49.2827, -123.1207],
zoom: 12,
layers: [tiles]
});
let overlays = {
'Layer 1': l1,
'Layer 2': l2
};
L.control.layers(overlays, null, {
collapsed: false
}).addTo(map);
l1.addTo(map);
map.on('baselayerchange', function(e) {
const layersCanvas = document.querySelectorAll('.leaflet-overlay-pane > canvas');
layersCanvas.forEach((layer, index) => {
layer.style.display = '';
if (index !== e.layer.options.id) {
layer.style.display = 'none';
}
});
});
});
html {
height: 100%;
}
body {
min-height: 100%;
margin: 0;
padding: 0;
}
#map {
width: 100%;
height: 100vh;
}
<link rel="stylesheet" href="https://unpkg.com/leaflet#1.7.1/dist/leaflet.css" />
<script src="https://unpkg.com/leaflet#1.7.1/dist/leaflet.js"></script>
<script src="https://unpkg.com/esri-leaflet#3.0.2/dist/esri-leaflet.js"></script>
<div id="map"></div>

Related

Getting county from click on OSM

Is it possible to detect state and county based on coordinates from a click on OSM map (US only)?
I can get the coordinated using click event in Leaflet JS:
map.on('click', function(ev) {
console.log(ev.latlng);
});
I can also get location data by doing something l;like this:
map.on("click", function(ev) {
var ln = ev.latlng["lng"],
lt = ev.latlng["lat"];
pt = "https://nominatim.openstreetmap.org/reverse?format=jsonv2&lat="+lt+"&lon="+ln;
});
Which will return json data that looks like this:
https://nominatim.openstreetmap.org/reverse?format=jsonv2&lat=28.964162684075685&lon=-98.29776763916017
But I can't seem to be able to figure out what to do next... How do I get the county and state values from it?
You can just fetch, Ajax from jquery or use axios.
let config = {
minZoom: 2,
maxZoom: 18,
};
const zoom = 5;
const lat = 28.964162684075685;
const lng = -98.29776763916017;
const map = L.map("map", config).setView([lat, lng], zoom);
L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
attribution: '© OpenStreetMap contributors',
}).addTo(map);
map.on("click", function(e) {
const { lat, lng } = e.latlng;
const api = `https://nominatim.openstreetmap.org/reverse?format=json&lat=${lat}&lon=${lng}&zoom=18&addressdetails=1`;
fetchData(api).then((data) => {
const { county, state } = data.address;
console.log(county, state);
});
});
async function fetchData(url) {
try {
const response = await fetch(url);
const data = await response.json();
return data;
} catch (err) {
console.error(err);
}
}
*,
:after,
:before {
box-sizing: border-box;
padding: 0;
margin: 0;
}
html {
height: 100%;
}
body,
html,
#map {
width: 100%;
height: 100%;
}
body {
position: relative;
min-height: 100%;
margin: 0;
padding: 0;
background-color: #f1f1f1;
}
<script src="https://unpkg.com/leaflet#1.7.1/dist/leaflet.js"></script>
<link href="https://unpkg.com/leaflet#1.7.1/dist/leaflet.css" rel="stylesheet" />
<div id="map"></div>

why isn't leaflet demo working on jsfiddle?

https://terrafrost.com/leaflet/demo01.html works as expected when ran it's own but when ran on jsfiddle at https://jsfiddle.net/x2f05eov/ all I get is a blank page. There are no errors that I'm seeing in the console.
Any ideas?
The JS code is as follows:
function layerParams(id)
{
return {
maxZoom: 18,
attribution: 'Map data © OpenStreetMap contributors, ' +
'CC-BY-SA, ' +
'Imagery © Mapbox',
id: id,
tileSize: 512,
zoomOffset: -1
};
}
var mapboxUrl = 'https://api.mapbox.com/styles/v1/{id}/tiles/{z}/{x}/{y}?access_token=pk.eyJ1IjoibWFwYm94IiwiYSI6ImNpejY4NXVycTA2emYycXBndHRqcmZ3N3gifQ.rJcFIG214AriISLbB6B5aw';
var light = L.tileLayer(mapboxUrl, layerParams('mapbox/light-v10'));
var streets = L.tileLayer(mapboxUrl, layerParams('mapbox/streets-v11'));
var satellite = L.tileLayer(mapboxUrl, layerParams('mapbox/satellite-streets-v11'));
var mymap = L.map('mapid', {
center: [30.267222, -97.743056],
zoom: 13,
layers: [light]
});
var baseMaps = {
"Light": light,
"Streets" :streets,
"Satellite": satellite
};
lightLines = L.layerGroup();
streetsLines = L.layerGroup();
satelliteLines = L.layerGroup();
var params = {
color: 'blue',
opacity: 0.75,
smoothFactor: 1
};
var latlngs = [[30.267222, -97.743056],[31.267222, -96.743056]];
tempParams = Object.assign(params, {weight: 10});
satelliteLines.addLayer(L.polyline(latlngs, tempParams));
tempParams = Object.assign(params, {weight: 3});
lightLines.addLayer(L.polyline(latlngs, tempParams));
tempParams = Object.assign(params, {weight: 5});
streetsLines.addLayer(L.polyline(latlngs, tempParams));
var hasOverlay = false;
mymap.on('baselayerchange', function(e) {
switch (e.name) {
case 'Light':
if (hasOverlay) {
lightLines.addTo(mymap);
streetsLines.removeFrom(mymap);
satelliteLines.removeFrom(mymap);
}
break;
case 'Streets':
if (hasOverlay) {
lightLines.removeFrom(mymap);
streetsLines.addTo(mymap);
satelliteLines.removeFrom(mymap);
}
break;
case 'Satellite':
if (hasOverlay) {
lightLines.removeFrom(mymap);
streetsLines.removeFrom(mymap);
satelliteLines.addTo(mymap);
}
}
});
mymap.on('overlayadd', function(e) {
console.log('overlayadd called');
hasOverlay = true;
});
mymap.on('overlayremove', function(e) {
console.log('overlayremove called');
lightLines.removeFrom(mymap);
streetsLines.removeFrom(mymap);
satelliteLines.removeFrom(mymap);
});
var overlays = {
'Demo': lightLines
};
L.control.layers(baseMaps, overlays).addTo(mymap);
The HTML code is as follows:
<body style="margin: 0; padding: 0; height: 100%">
<div id="mapid" style="width: 100%; height: 100%"></div>
The leaflet CSS and JS are being included as well.
You just miss a closing body and html tag
<!DOCTYPE html>
<html style="height: 100%">
<head>
<link rel="stylesheet" href="https://unpkg.com/leaflet#1.7.1/dist/leaflet.css" integrity="sha512-xodZBNTC5n17Xt2atTPuE1HxjVMSvLVW9ocqUKLsCC5CXdbqCmblAshOMAS6/keqq/sMZMZ19scR4PsZChSR7A==" crossorigin="" />
<script src="https://unpkg.com/leaflet#1.7.1/dist/leaflet.js" integrity="sha512-XQoYMqMTK8LvdxXYG3nZ448hOEQiglfqkJs1NOQV44cWnUrBc8PkAOcXy20w0vlaXaVUearIOBhiXZ5V3ynxwA==" crossorigin=""></script>
</head>
<body style="margin: 0; padding: 0; height: 100%">
<div id="mapid" style="width: 100%; height: 100%"></div>
<script>
function layerParams(id) {
return {
maxZoom: 18,
attribution: 'Map data © OpenStreetMap contributors, ' +
'CC-BY-SA, ' +
'Imagery © Mapbox',
id: id,
tileSize: 512,
zoomOffset: -1
};
}
var mapboxUrl = 'https://api.mapbox.com/styles/v1/{id}/tiles/{z}/{x}/{y}?access_token=pk.eyJ1IjoibWFwYm94IiwiYSI6ImNpejY4NXVycTA2emYycXBndHRqcmZ3N3gifQ.rJcFIG214AriISLbB6B5aw';
var light = L.tileLayer(mapboxUrl, layerParams('mapbox/light-v10'));
var streets = L.tileLayer(mapboxUrl, layerParams('mapbox/streets-v11'));
var satellite = L.tileLayer(mapboxUrl, layerParams('mapbox/satellite-streets-v11'));
var mymap = L.map('mapid', {
center: [30.267222, -97.743056],
zoom: 13,
layers: [light]
});
var baseMaps = {
"Light": light,
"Streets": streets,
"Satellite": satellite
};
lightLines = L.layerGroup();
streetsLines = L.layerGroup();
satelliteLines = L.layerGroup();
var params = {
color: 'blue',
opacity: 0.75,
smoothFactor: 1
};
var latlngs = [
[30.267222, -97.743056],
[31.267222, -96.743056]
];
tempParams = Object.assign(params, {
weight: 10
});
satelliteLines.addLayer(L.polyline(latlngs, tempParams));
tempParams = Object.assign(params, {
weight: 3
});
lightLines.addLayer(L.polyline(latlngs, tempParams));
tempParams = Object.assign(params, {
weight: 5
});
streetsLines.addLayer(L.polyline(latlngs, tempParams));
var hasOverlay = false;
mymap.on('baselayerchange', function(e) {
switch (e.name) {
case 'Light':
if (hasOverlay) {
lightLines.addTo(mymap);
streetsLines.removeFrom(mymap);
satelliteLines.removeFrom(mymap);
}
break;
case 'Streets':
if (hasOverlay) {
lightLines.removeFrom(mymap);
streetsLines.addTo(mymap);
satelliteLines.removeFrom(mymap);
}
break;
case 'Satellite':
if (hasOverlay) {
lightLines.removeFrom(mymap);
streetsLines.removeFrom(mymap);
satelliteLines.addTo(mymap);
}
}
});
mymap.on('overlayadd', function(e) {
console.log('overlayadd called');
hasOverlay = true;
});
mymap.on('overlayremove', function(e) {
console.log('overlayremove called');
lightLines.removeFrom(mymap);
streetsLines.removeFrom(mymap);
satelliteLines.removeFrom(mymap);
});
var overlays = {
'Demo': lightLines
};
L.control.layers(baseMaps, overlays).addTo(mymap);
</script>
</body>
</html>
Edit:
you need to define explicit height (f.i 100vh) because setting 100% height means the height of the parent div which does not exist in this case
fiddle

OpenLayers 5: move marker smoothly

I use
https://openlayers.org/en/latest/examples/feature-move-animation.html
code snippet:
// 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 {module:ol/geom/LineString~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: 'https://openlayers.org/en/v4.6.5/examples/data/icon.png'
})
}),
'geoMarker': new ol.style.Style({
image: new ol.style.Circle({ // CircleStyle({
radius: 7,
snapToPixel: false,
fill: new ol.style.Fill({
color: 'black'
}),
stroke: new ol.style.Stroke({
color: 'white',
width: 2
})
})
})
};
var animating = false;
var speed, now;
var speedInput = document.getElementById('speed');
var startButton = document.getElementById('start-animation');
var vectorLayer = new ol.layer.Vector({ // VectorLayer({
source: new ol.source.Vector({ // VectorSource({
features: [routeFeature, geoMarker, startMarker, endMarker]
}),
style: function(feature) {
// hide geoMarker if animation is active
if (animating && feature.get('type') === 'geoMarker') {
return null;
}
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: 9,
minZoom: 2,
maxZoom: 19
}),
layers: [
new ol.layer.Tile({ // TileLayer({
source: new ol.source.OSM()
}),
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 OpenLayers 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 {module:ol/geom/Point~Point} */
(geoMarker.getGeometry())
.setCoordinates(coord);
//remove listener
map.un('postcompose', moveFeature);
}
startButton.addEventListener('click', startAnimation, false);
html,
body {
height: 100%;
width: 100%;
padding: 0px;
margin: 0px;
}
.map {
height: 90%;
width: 100%;
}
<script src="https://cdn.rawgit.com/openlayers/openlayers.github.io/master/en/v5.1.3/build/ol.js"></script>
<div id="map" class="map"></div>
<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>
but I need to move marker smoothly between two point (or many)
See ol-ext : featureAnimation
example from that library's documentation
code snippet (example from the OpenLayers documentation using ol-ext):
// use window.onload so can move encoded polyline definition to bottom
window.onload = function() {
var route = /** #type {module:ol/geom/LineString~LineString} */ (new ol.format.Polyline({
factor: 1e6
}).readGeometry(polyline, {
dataProjection: 'EPSG:4326',
featureProjection: 'EPSG:3857'
}));
var routeCoords = route.getCoordinates();
var routeLength = routeCoords.length;
var routePts = new ol.Feature({
type: 'route',
geometry: new ol.geom.MultiPoint(routeCoords)
});
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]
}),
image: new ol.style.Circle({
radius: 1,
snapToPixel: false,
fill: new ol.style.Fill({
color: 'red'
})
})
}),
'icon': new ol.style.Style({
image: new ol.style.Icon({
anchor: [0.5, 1],
src: 'https://openlayers.org/en/v4.6.5/examples/data/icon.png'
})
}),
'geoMarker': new ol.style.Style({
image: new ol.style.Circle({ // CircleStyle({
radius: 7,
snapToPixel: false,
fill: new ol.style.Fill({
color: 'black'
}),
stroke: new ol.style.Stroke({
color: 'white',
width: 2
})
})
})
};
var animating = false;
var speed, now;
var speedInput = document.getElementById('speed');
var startButton = document.getElementById('start-animation');
var vectorLayer = new ol.layer.Vector({ // VectorLayer({
source: new ol.source.Vector({ // VectorSource({
features: [routePts, routeFeature, geoMarker, startMarker, endMarker]
}),
style: function(feature) {
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: 9,
minZoom: 2,
maxZoom: 19
}),
layers: [
new ol.layer.Tile({ // TileLayer({
source: new ol.source.OSM()
}),
vectorLayer
]
});
console.log("init speed=" + speedInput.value / 100);
function startAnimation() {
if (animating) animating.stop();
console.log("update speed=" + Number(speedInput.value) / 100);
// anim.set("speed", Number(speedInput.value)/10);
var anim = new ol.featureAnimation.Path({
path: route,
easing: ol.easing.linear,
speed: Number(speedInput.value) / 100
});
animating = vectorLayer.animateFeature(geoMarker, anim);
}
geoMarker.on('change', function() {
map.getView().setCenter(geoMarker.getGeometry().getCoordinates());
});
startButton.addEventListener('click', startAnimation, false);
}
// 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('');
html,
body {
height: 100%;
width: 100%;
padding: 0px;
margin: 0px;
}
.map {
height: 90%;
width: 100%;
}
<script src="https://cdn.rawgit.com/openlayers/openlayers.github.io/master/en/v5.1.3/build/ol.js"></script>
<script src="https://cdn.rawgit.com/Viglino/ol-ext/master/dist/ol-ext.js"></script>
<button id="start-animation">Start Animation</button>
<label for="speed">
speed:
<input id="speed" type="range" min="10" max="4999" step="10" value="500">
</label>
<div id="map" class="map"></div>
The OpenLayers example doesn't move smoothly as the only coordinates it uses are the points where the linestring changes direction. Therefore long straights are traversed much faster than the curved sections. The ol-ext method breaks the linestring into small equal length segments, but it seems to be animating the view to keep up with the marker instead of simply animating the marker. By reusing the splitLineString function from the second part of the answer to this question https://gis.stackexchange.com/questions/306976/add-image-along-the-linestring to finely divide the linestring and setting only one icon style based on a timer based index it is possible to achieve a smooth movement independently of the view (best seen in a full screen window) and also allows the icon to stay aligned with the direction of travel.
// 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('');
function splitLineString(geometry, minSegmentLength, options) {
function calculatePointsDistance(coord1, coord2) {
var dx = coord1[0] - coord2[0];
var dy = coord1[1] - coord2[1];
return Math.sqrt(dx * dx + dy * dy);
};
function calculateSplitPointCoords(startNode, nextNode, distanceBetweenNodes, distanceToSplitPoint) {
var d = distanceToSplitPoint / distanceBetweenNodes;
var x = nextNode[0] + (startNode[0] - nextNode[0]) * d;
var y = nextNode[1] + (startNode[1] - nextNode[1]) * d;
return [x, y];
};
function calculateAngle(startNode, nextNode, alwaysUp) {
var x = (startNode[0] - nextNode[0]);
var y = (startNode[1] - nextNode[1]);
var angle = Math.atan(x/y);
if (!alwaysUp) {
angle = y > 0 ? angle + Math.PI : x < 0 ? angle + Math.PI*2 : angle;
}
return angle;
};
var splitPoints = [];
var coords = geometry.getCoordinates();
var coordIndex = 0;
var startPoint = coords[coordIndex];
var nextPoint = coords[coordIndex + 1];
var angle = calculateAngle(startPoint, nextPoint, options.alwaysUp);
var n = Math.ceil(geometry.getLength()/minSegmentLength);
var segmentLength = geometry.getLength() / n;
var currentSegmentLength = options.midPoints ? segmentLength/2 : segmentLength;
for (var i = 0; i <= n; i++) {
var distanceBetweenPoints = calculatePointsDistance(startPoint, nextPoint);
currentSegmentLength += distanceBetweenPoints;
if (currentSegmentLength < segmentLength) {
coordIndex++;
if(coordIndex < coords.length - 1) {
startPoint = coords[coordIndex];
nextPoint = coords[coordIndex + 1];
angle = calculateAngle(startPoint, nextPoint, options.alwaysUp);
i--;
continue;
} else {
if (!options.midPoints) {
var splitPointCoords = nextPoint;
splitPointCoords.push(angle);
splitPoints.push(splitPointCoords);
}
break;
}
} else {
var distanceToSplitPoint = currentSegmentLength - segmentLength;
var splitPointCoords = calculateSplitPointCoords(startPoint, nextPoint, distanceBetweenPoints, distanceToSplitPoint);
startPoint = splitPointCoords.slice();
splitPointCoords.push(angle);
splitPoints.push(splitPointCoords);
currentSegmentLength = 0;
}
}
return splitPoints;
};
var route = 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 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 raster = new ol.layer.Tile({
source: new ol.source.OSM()
});
var repeat = false;
var timer = -1;
var style = function(feature, resolution) {
if (feature.get('type') == 'route') {
var styles = [
new ol.style.Style({
stroke: new ol.style.Stroke({
width: 6,
color: [237, 212, 0, 0.8],
})
})
];
if (timer < 0) {
feature.unset('splitPoints', true);
} else {
var splitPoints = feature.get('splitPoints');
if (!splitPoints) {
splitPoints = splitLineString(feature.getGeometry(), 2 * resolution, {alwaysUp: false, midPoints: true});
feature.set('splitPoints', splitPoints, true);
}
if (!repeat && timer >= splitPoints.length) {
stopAnimation(true);
} else {
var index = timer % splitPoints.length;
var point = splitPoints[index];
styles.push(
new ol.style.Style({
geometry: new ol.geom.Point([point[0],point[1]]),
image: new ol.style.Icon({
src: 'https://cdn1.iconfinder.com/data/icons/basic-ui-elements-color-round/3/19-32.png',
rotation: point[2]
}),
zIndex: 1
})
);
}
}
return styles;
} else {
return [
new ol.style.Style({
image: new ol.style.Icon({
anchor: [0.5, 1],
src: 'https://openlayers.org/en/v4.6.5/examples/data/icon.png',
})
})
];
}
}
var vector = new ol.layer.Vector({
source: new ol.source.Vector({
features: [routeFeature, startMarker, endMarker]
}),
style: style
});
var map = new ol.Map({
layers: [raster, vector],
target: 'map',
view: new ol.View({
center: [-5639523.95, -3501274.52],
zoom: 10
})
});
var animating = false;
var token;
var speedInput = document.getElementById('speed');
var startButton = document.getElementById('start-animation');
function startAnimation() {
if (animating) {
stopAnimation(false);
} else {
animating = true;
startButton.textContent = 'Cancel Animation';
token = setInterval( function(){
timer++;
vector.setStyle(style);
}, 6000/speedInput.value);
}
}
function stopAnimation(ended) {
animating = false;
startButton.textContent = 'Start Animation';
clearInterval(token);
timer = -1;
if (!ended) {
vector.setStyle(style);
}
}
startButton.addEventListener('click', startAnimation, false);
html,
body {
height: 100%;
width: 100%;
padding: 0px;
margin: 0px;
}
.map {
height: 90%;
width: 100%;
}
<link rel="stylesheet" href="https://cdn.rawgit.com/openlayers/openlayers.github.io/master/en/v5.3.0/css/ol.css" type="text/css">
<script src="https://cdn.rawgit.com/openlayers/openlayers.github.io/master/en/v5.3.0/build/ol.js"></script>
<div id="map" class="map"></div>
<label for="speed">
speed:
<input id="speed" type="range" min="10" max="999" step="10" value="200">
</label>
<button id="start-animation">Start Animation</button>

Leaflet.js: click a polygon to remove the the layer and change it to new one

I've been making a Leaflet map for a while and trying to figure out the way to make it so if I click one of the polygon in GeoJSON layer, it will remove the current layer and replace it with another layer.
Likewise, if I click it again, it will remove the new layer and replace it with previous layer.
I've been trying to tinker with different stuff but nothing works. This is one of my recent attempt.
<script type="text/javascript" src="provinces.js"></script>
<script>
var map = L.map('map').setView([-2.5, 119], 5);
L.tileLayer('http://{s}.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap © CartoDB',
subdomains: 'abcd',
maxZoom: 19
}).addTo(map);
// get color depending on population density value
function getColor(d) {
return d > 5000 ? '#800026' :
d > 2500 ? '#BD0026' :
d > 1000 ? '#E31A1C' :
d > 500 ? '#FC4E2A' :
'#FFEDA0';
}
function style(feature) {
return {
weight: 2,
opacity: 1,
color: 'white',
dashArray: '',
fillOpacity: 0.7,
fillColor: getColor(feature.properties.kode)
};
}
function highlightFeature(e) {
var layer = e.target;
layer.setStyle({
weight: 5,
color: '#ccc',
dashArray: '',
fillOpacity: 0.7
});
if (!L.Browser.ie && !L.Browser.opera && !L.Browser.edge) {
layer.bringToFront();
}
info.update(layer.feature.properties);
}
var geojson;
function resetHighlight(e) {
geojson.resetStyle(e.target);
info.update();
}
function zoomToFeature(e) {
map.fitBounds(e.target.getBounds());
}
function addNewBoundary(e) { // this function doesn't do anything
var newLayerBoundary = new L.geoJson();
newLayerBoundary.addTo(map);
$.ajax({
dataType: "json",
url: "province-details.geojson",
success: function(data) {
$(data.features).each(function(key, data) {
newLayerBoundary.addData(data);
});
}
}).error(function() {});
}
function onEachFeature(feature, layer) {
layer.on({
mouseover: highlightFeature,
mouseout: resetHighlight,
click: clearLayers // with this it just clears the layers before being clicked
});
}
geojson = L.geoJson(provinces, {
style: style,
onEachFeature: onEachFeature
}).addTo(map);
</script>
var layers = [firstLayer,secondLayer]
function switchLayers(){
if(map.haslayer(layers[firstLayer])){
map.addLayer(layers[secondLayer]);
map.removeLayer(layers[firstLayer]);
}else{
if(map.haslayer(layers[secondLayer])){
map.addLayer(layers[firstLayer]);
map.removeLayer(layers[secondLayer]);
}
}

Famo.us drag & drop surfaces?

I've gone through the handy Famo.us University tutorials and am prototyping a drag & drop interface. It's the typical UI where the user can drag an icon and drop it onto a target to do something. I've gotten the drag part down, but detecting the drop is getting very hairy. Is there built-in collision detection in Famo.us?
Edit: I've looked at the Collision API but it's not clear whether this would work across views.
Here's how I've organized the project:
AppView (overall container)
|
|__ MenuView (sidebar) --> VizView (icons in MenuView)
|
|__ PageView (workspace where the drop targets live)
This may not be the best way to go about this. I'm not sure. Hooking up input events across the views seems to be painful.
VizView source:
/*** VizView.js ***/
define(function(require, exports, module) {
var View = require('famous/core/View');
var Surface = require('famous/core/Surface');
var Transform = require('famous/core/Transform');
var Modifier = require('famous/core/Modifier');
var ImageSurface = require('famous/surfaces/ImageSurface');
var Transitionable = require("famous/transitions/Transitionable");
var SnapTransition = require("famous/transitions/SnapTransition");
Transitionable.registerMethod("spring", SnapTransition);
var GenericSync = require('famous/inputs/GenericSync');
var MouseSync = require('famous/inputs/MouseSync');
var TouchSync = require('famous/inputs/TouchSync');
GenericSync.register({'mouse': MouseSync, 'touch': TouchSync});
function VizView() {
View.apply(this, arguments);
_createIcon.call(this);
}
VizView.prototype = Object.create(View.prototype);
VizView.prototype.constructor = VizView;
VizView.DEFAULT_OPTIONS = {
width: 200,
height: 100,
angle: -0.2,
iconSize: 98,
iconUrl: '',
title: 'Empty',
fontSize: 26
};
function _createIcon() {
this.zIndex = 0;
var me = this;
var iconSurface = new ImageSurface({
size: [this.options.iconSize, this.options.iconSize],
content : this.options.iconUrl,
properties: {
cursor: 'pointer'
}
});
var initModifier = new Modifier({
// places the icon in the proper location
transform: Transform.translate(24, 2, 0)
});
this.position = new Transitionable([0, 0]);
var positionModifier = new Modifier({
transform : function(){
var currentPosition = me.position.get();
return Transform.translate(currentPosition[0], currentPosition[1], me.zIndex);
},
});
var sync = new GenericSync(
['mouse', 'touch']
);
sync.on('start', function(data){
me.zIndex = 1;
});
sync.on('update', function(data){
me.updateIcon(data);
});
sync.on('end', function(data){
var velocity = data.velocity;
me.position.set([0, 0], {
method : 'spring',
period : 150,
velocity : velocity
});
me.zIndex = 0;
});
iconSurface.pipe(sync);
this.add(positionModifier).add(initModifier).add(iconSurface);
this.updateIcon = function (data) {
if (this.zIndex == 0) return;
var currentPosition = this.position.get();
this.position.set([
currentPosition[0] + data.delta[0],
currentPosition[1] + data.delta[1]
]);
}
}
module.exports = VizView;
});
A VizView is instantiated in MenuView as such:
var vizView = new VizView({
iconUrl: "path/to/iconUrl",
title: "Viz Title"
});
var vizModifier = new StateModifier({
transform: Transform.translate(0, yOffset, 0)
});
this.add(vizModifier).add(vizView);
A draggable Surface in Famo.us is not really a DOM draggable element although it can be setup to work in a browser using the mouse. I have not been able to get GenericSync and touch to work with this solution yet.
Reading the pitfalls on the Famo.us site, there are hints to drag and drop with surface draggables being an issue.
How do I find the absolute position of a Surface on the screen?
By design this is not possible. It is something the developer should
not care about. For the time being, this means that interactions such
as drag and drop are harder to implement, but this is intended and we
are working on an elegant solution for these use-cases.
Although: When not using GenericSync, you can use the DOM draggable events with a Famo.us Surface as you stated in the comments and link to the John Traver solution.
But: This solution will not work on mobile touch devices using Famo.us at the time of this answer. Getting this to work with touch may prove to be more difficult as stated in the pitfalls. Let's hope this gets solved in versions following 0.3.5 or in MixedMode (WebGL and DOM)
define('main', function(require, exports, module) {
var Engine = require('famous/core/Engine');
var Surface = require('famous/core/Surface');
var ImageSurface = require('famous/surfaces/ImageSurface');
var Transform = require('famous/core/Transform');
var Modifier = require('famous/core/Modifier');
var StateModifier = require('famous/modifiers/StateModifier');
var Draggable = require('famous/modifiers/Draggable');
var TransitionableTransform = require('famous/transitions/TransitionableTransform');
var mainContext = Engine.createContext();
var transTransform = new TransitionableTransform();
transTransform.set(Transform.translate(100, 0, 0));
var captureSurface = new Surface({
content: 'Drag to Here',
size: [300, 300],
properties: {
textAlign: 'center',
lineHeight: '300px',
backgroundColor: 'rgba(255,255,0,0.4)',
cursor: 'pointer'
},
attributes: {
dropzone: 'copy file:image/png file:image/gif file:image/jpeg'
}
});
captureSurface.on('dragenter', function(evt) {
console.log('dragenter', evt);
evt.preventDefault();
return false;
});
captureSurface.on('dragleave', function(evt) {
console.log('dragleave', evt);
captureSurface.setProperties({
border: 'none'
});
evt.preventDefault();
return false;
});
captureSurface.on('dragover', function(evt) {
console.log('dragover', evt);
captureSurface.setProperties({
border: '4px dashed black'
});
evt.preventDefault();
return false;
});
captureSurface.on('drop', function(evt) {
console.log('drop', evt);
evt.preventDefault();
evt.stopPropagation();
captureSurface.setProperties({
border: '4px solid red'
});
files = evt.dataTransfer.files;
console.log(files);
});
mainContext.add(new Modifier({
origin: [0.5, 0.5],
align: [0.5, 0.5]
})).add(captureSurface);
var surface = new Surface({
content: 'DOM Draggable',
size: [300, 100],
properties: {
backgroundColor: 'rgba(255,0,0,0.4)',
cursor: 'move'
},
attributes: {
draggable: 'true'
}
});
surface.on('drag', function(evt) {
console.log('surface drag', evt)
});
var imageSurface = new ImageSurface({
content: 'http://i.imgur.com/NGOwZeT.png',
size: [100, 100],
properties: {
cursor: 'copy'
},
attributes: {
draggable: 'true'
}
});
imageSurface.on('drag', function(evt) {
console.log('imageSurface drag', evt)
});
imageSurface.on('dragend', function(evt) {
console.log('imageSurface dragend', evt)
});
var dragSurface = new Surface({
content: 'Drag Me',
size: [100, 100],
properties: {
backgroundColor: 'rgba(0,0,0,0.1)',
cursor: 'move'
},
attributes: {
draggable: 'true'
}
});
dragSurface.on('dragstart', function(evt) {
console.log('dragSurface dragstart', event, evt);
});
dragSurface.on('drag', function(evt) {
console.log('dragSurface dragstart', event, evt);
});
var modifier = new Modifier({
origin: [0, 0],
align: [0, 0],
transform: transTransform
});
var imageModifier = new Modifier({
origin: [0, 0.5],
align: [0, 0.5]
});
var draggable = new Draggable();
draggable.subscribe(dragSurface);
mainContext.add(modifier).add(surface);
mainContext.add(imageModifier).add(imageSurface);
mainContext.add(draggable).add(dragSurface);
draggable.on('update', function(e) {
console.log('draggable update', e, event);
var pos = e.position;
surface.setContent('Draggable Position is ' + pos);
transTransform.set(Transform.translate(pos[0] + 100, pos[1], 0));
});
draggable.on('end', function(e) {
var pos = e.position;
surface.setContent('Draggable End Position is ' + pos);
transTransform.set(Transform.translate(pos[0] + 100, pos[1], 0));
});
//draggable.deactivate();
});
require(['main']);
<script src="http://requirejs.org/docs/release/2.1.16/minified/require.js"></script>
<script src="http://code.famo.us/lib/requestAnimationFrame.js"></script>
<script src="http://code.famo.us/lib/classList.js"></script>
<script src="http://code.famo.us/lib/functionPrototypeBind.js"></script>
<link rel="stylesheet" type="text/css" href="http://code.famo.us/famous/0.3.5/famous.css" />
<script src="http://code.famo.us/famous/0.3.5/famous.min.js"></script>