Mapbox GL JS long tap/press - mapbox-gl-js

When using Mapbox GL JS in a browser, including using the emulation mode in Chrome, the event contextmenu responds to a long click/tap on the map. However, on a real device this event does not fire when the user taps and holds on the map.
What is the best way to listen for long taps on the map in Mapbox GL JS?

Similar issue is discussed here.
My derived solution looks like this:
if (Browser.isIos()) {
init_ios_context_menu();
} else {
map.on('contextmenu', (e) => {
show_context_menu_or_whatever(e);
});
} // end if
function init_ios_context_menu() {
let iosTimeout = null;
let clearIosTimeout = () => { clearTimeout(iosTimeout); };
map.on('touchstart', (e) => {
if (e.originalEvent.touches.length > 1) {
return;
}
iosTimeout = setTimeout(() => {
show_context_menu_or_whatever(e);
}, 500);
});
map.on('touchend', clearIosTimeout);
map.on('touchcancel', clearIosTimeout);
map.on('touchmove', clearIosTimeout);
map.on('pointerdrag', clearIosTimeout);
map.on('pointermove', clearIosTimeout);
map.on('moveend', clearIosTimeout);
map.on('gesturestart', clearIosTimeout);
map.on('gesturechange', clearIosTimeout);
map.on('gestureend', clearIosTimeout);
};

Related

Custom transform control for geoman

I am trying to add a custom transform control to geoman, to do certain transformations with polylines and polygons. I see that on edit, geoman draws hint lines above vertices etc. I would like my tool to highlight polylines/polygons with the same type of hints. Below is the skeleton of my action:
const ConvertAction = L.Toolbar2.Action.extend({
options: {
toolbarIcon: {
html:
'<div class="icon-maps icon-convert" title="Convert point"></div>',
tooltip: 'Convert point'
}
},
addHooks: () => {
// draw polygon
// map.pm.enableDraw();
changeConvert();
}
});
function changeConvert() {
convert = true;
map.eachLayer(function (layer) {
if (layer.feature && layer.feature.geometry.type === 'Point') {
layer._icon.style['pointer-events'] = 'auto';
}
});
}
Is there an internal function or something that I could use to outline shapes? When I enable Edit layers tool already built into the geoman, shapes are outlined for me. How could I achieve this from my code without having to reimplement the entire thing?
Thus far, after quickly reviewing geoman code, I was able to come up with:
const ConvertAction = L.Toolbar2.Action.extend({
options: {
toolbarIcon: {
html:
'<div class="icon-maps icon-convert" title="Convert point"></div>',
tooltip: 'Convert point'
}
},
addHooks: () => {
// draw polygon
// map.pm.enableDraw();
if (!convert) changeConvert();
else disableConvert();
}
});
function changeConvert() {
convert = true;
map.eachLayer(function (layer) {
if (
layer?.feature?.geometry.type === 'Polygon' ||
layer?.feature?.geometry.type === 'LineString'
) {
const coords = layer.getLatLngs();
const markerGroup = new L.LayerGroup();
markerGroup._pmTempLayer = true;
const createMarker = (latlng) => {
const marker = new L.Marker(latlng, {
draggable: true,
icon: L.divIcon({ className: 'marker-icon' })
});
layer.options.pane =
(map.pm.globalOptions.panes &&
map.pm.globalOptions.panes.vertexPane) ||
'markerPane';
marker._pmTempLayer = true;
markerGroup.addLayer(marker);
return marker;
};
const handleRing = (coordsArr) => {
// if there is another coords ring, go a level deep and do this again
if (Array.isArray(coordsArr[0])) {
return coordsArr.map(handleRing, this);
}
// the marker array, it includes only the markers of vertexes (no middle markers)
const ringArr = coordsArr.map(createMarker);
return ringArr;
};
const markers = handleRing(coords);
map.addLayer(markerGroup);
}
});
}
function disableConvert() {
convert = false;
map.eachLayer(function (layer) {
if (
layer.dragging &&
layer.options?.draggable === true &&
layer._pmTempLayer === true
) {
console.log('temp layer:', layer);
map.removeLayer(layer);
}
});
update();
}
Seems like an excessive amount of code, and reimplementation [and probably not as good the geoman version as I don't fully understand geoman code by far] of existing functionality.
How do I simplify/fix this?

Ionic Native Geolocation sample not understanding the description demo code

I am using Ionic Native Geolocation plugin from HERE and to start with the example provided so I've done this:
getLocation() {
this.geolocation.getCurrentPosition().then((resp) => {
// resp.coords.latitude
// resp.coords.longitude
}).catch((error) => {
console.log('Error getting location', error);
});
let watch = this.geolocation.watchPosition();
watch.subscribe((data) => {
// data.coords.latitude
// data.coords.longitude
});
}
I don't understand the code ... does it seem to be doing the same thing twice?
It's got the getCurrentPosition and the watchPosition sections and both get the saqme data?
Why? I'm I missing something?
In summery: this.geolocation.getCurrentPosition() is used to retrieve the device's current location once, while this.geolocation.watchPosition() Registers a handler function that will be called automatically each time the position of the device changes, returning the updated location.
References:
https://developer.mozilla.org/en-US/docs/Web/API/Geolocation_API
Code Examples:
//define the userPositionWatch
userPositionWatch: any;
//Subscriber to the userPositionWatch
this.userPositionWatch = this.geolocation.watchPosition()
.subscribe(
(position: any) => {
// This method will be triggered each time the position of the device changes
console.debug("geolocation.watchPosition => the callback function have been triggered");
let data: any = position;
if (data.coords !== undefined) {
this.doSomethingWithThePos(data.coords.latitude, data.coords.longitude);
} else {
console.error("geolocation.watchPosition() => the callback function have been triggered and the position is undefined", data);
}
}
, (error: any) => {
console.error("geolocation.watchPosition() => the callback function have been triggered and the there is an error:", error);
});
//To remove the subscription
this.userPositionWatch.unsubscribe();
//Another way to remove the subscription
navigator.geolocation.clearWatch(this.userPositionWatch);
this.geolocation.getCurrentPosition()
.then((position: any) => {
let data: any = position;
if (data.coords !== undefined) {
this.doSomethingWithThePos(data.coords.latitude, data.coords.longitude);
} else {
console.error("geolocation.getCurrentPosition() => the position is undefined", data);
}
}).catch(error => {
console.error("geolocation.getCurrentPosition() => the position has error:", error);
})
I hope it was clear...

How to stop functions when leaving the page in Ionic 4

I am working in my Ionic 4 app and I want to stop the functions when the page will leave.
This is my tab4.page.ts:
async getUserDetail(){
this.dataexists = false;
this.userActiveChallanges = [];
let me=this;
const loading = await this.loadingController.create({
message: '',
// duration: 2200,
translucent: true,
spinner: 'crescent',
showBackdrop: false,
cssClass: 'my-loading-class'
});
await loading.present();
this.userActiveChallanges=[];
this.storage.get('USERPROFILE').then(userObj => {
// console.log('User Profile :',userObj);
me.userprofile = userObj;
me.sendFitDatafunction(userObj);
me.myapi.apiCall('userActiveChallenges/'+userObj.id,'GET','').subscribe((data) => {
// console.log(data);
me.response=data;
loading.dismiss();
if(me.response.status === 'success'){
if(me.response && me.response.data && me.response.data.length>0){
this.userActiveChallanges=me.response.data;
this.flip(this.userActiveChallanges[0].challenge_id);
}
this.dataexists = true;
} else{
this.userActiveChallanges = '';
this.dataexists = true;
}
}, error => { loading.dismiss(); console.log(error); });
});
}
ionViewWillLeave() {
}
I want to stop this function when the page will leave because when I am not getting any response nor any error from the api the loader keeps running and when I move to the other page, it is showing there.
So, I want to stop the function when the page will leave.
Any help is much appreciated.
instead of local const loading, declare it as a property of your ts class (tab4).
now change your code and assign loader to it:
replace: const loading
with:
this.loading
Now inside ionViewWillLeave call:
ionViewWillLeave() {
if (this.loading) { this.loading.dismiss() }
}
Well, I don't know the function to stop your function, but to make something when you leave a page, you make it in IonViewDidLeave()

Is there any idle event for mapbox gl js?

I need some sort of google maps "idle" event for mapbox gl.
When every event fired and the map stop zoomin/out drag etc. and every layer has loaded, and the map is idle.
I have to use this code
map.on("render", function(e) {
if(map.loaded() && triggerOnce === true) {
//fires on zoomin runing
triggerOnce = false;
console.log("Render end")
setTimeout(somefunc(),1000)
}
})
Yes, as of mapbox-gl-js v0.52.0 there is now an idle event you can use. According to the docs:
Fired after the last frame rendered before the map enters an "idle"
state:
No camera transitions are in progress
All currently requested tiles have loaded
All fade/transition animations have completed
To use it:
map.once('idle', (e) => {
// do things the first time the map idles
});
map.on('idle', (e) => {
// do things every time the map idles
});
Demo: http://jsfiddle.net/godoshian/yrf0b9xt/
// Data from http://geojson.xyz/
const geojsonSource = 'https://d2ad6b4ur7yvpq.cloudfront.net/naturalearth-3.3.0/ne_50m_populated_places.geojson';
const outputContainer = document.getElementById('output-container');
mapboxgl.accessToken = 'pk.eyJ1IjoiY2NoYW5nc2EiLCJhIjoiY2lqeXU3dGo1MjY1ZXZibHp5cHF2a3Q1ZyJ9.8q-mw77HsgkdqrUHdi-XUg';
function createMap(container, layer = null) {
const map = new mapboxgl.Map({
container,
style: 'mapbox://styles/mapbox/light-v9',
});
map.on('idle', () => {
outputContainer.innerHTML += `${container} idle<br>`;
});
if (layer) {
map.on('load', () => {
map.addLayer(layer);
});
}
return map;
}
const map = createMap('map1');
setTimeout(() => {
fetch(geojsonSource)
.then(response => {
if (response.ok) return response.json();
throw Error(response);
})
.then(json => {
let layer = {
id: 'populated-places',
source: {
type: 'geojson',
data: json,
},
type: 'circle',
}
map.addLayer(layer);
createMap('map2', layer);
})
.catch(error => {
console.log(error);
})
}, 5000);
#demo {
display: flex;
flex-direction: row;
justify-content: space-evenly;
}
#demo #output-container {
flex: 1;
}
#demo .map {
height: 300px;
flex: 2;
}
<script src='https://api.tiles.mapbox.com/mapbox-gl-js/v0.52.0/mapbox-gl.js'></script>
<link href='https://api.tiles.mapbox.com/mapbox-gl-js/v0.52.0/mapbox-gl.css' rel='stylesheet' />
<div id="demo">
<div id="output-container">
</div>
<div id="map1" class="map">
</div>
<div id="map2" class="map">
</div>
</div>
Relevant PR: https://github.com/mapbox/mapbox-gl-js/pull/7625
Docs: https://www.mapbox.com/mapbox-gl-js/api/#map.event:idle
just listen to moveend event, it will be fired after any moves on the map like dragging, zooming, rotating and pitching.
idle, not working, because it fire event every 1 second, continues trigger event.
map.on('idle', (e) => {
use moveend event instead.
map.on('moveend', (e) => {
You can have similar event by using setTimeout to monitor the onViewPortChange event
var changingViewPortTimeout;
onViewportChange(viewport) {
if (changingViewPortTimeout) {
clearTimeout(changingViewPortTimeout);
}
changingViewPortTimeout = setTimeout(function () {
onIdle(viewport);
}, 200)
});
}

JSTree DND Event at Drag Starting Point

I have a external draggable object (draggable implemented through jstee's dnd) on which I need to perform a check before the object starts dragging.
I'm looking for a method much like "drag_finish" or a binding I can use, but at the start of the dragging event.
$(document).bind("drag_start.vakata", function (e, data) {
if(data.data.jstree) {
// add your code here
}
});
Binding to the document did the trick
Just as a more complete answer, here's some code for all 3 events (start, drag and stop):
$(document).bind("drag_start.vakata", function (e, data) {
if (data.data.jstree) {
//User started dragging
}
});
$(document).bind("drag.vakata", function (e, data) {
if (data.data.jstree) {
//User is dragging
}
});
$(document).bind("drag_stop.vakata", function (e, data) {
if (data.data.jstree) {
//User stopped dragging
}
});
drag_start.vakata has been changed to dnd_start.vakata Now the above event would be triggered on these Functions:
$(document).bind("drag_start.vakata", function (e, data) {
if (data.data.jstree) {
//User started dragging
}});
$(document).bind("drag.vakata", function (e, data) {
if (data.data.jstree) {
//User is dragging
}});
$(document).bind("drag_stop.vakata", function (e, data) {
if (data.data.jstree) {
//User stopped dragging
}});