Mapbox GL JS not showing user heading, always points north - mapbox

I have a Mapbox-GL-JS script that is working fine. However, the arrow next to the blue user location dot always points north. I thought the showUserHeading option is supposed to point in the direction (bearing) the user is moving. Anyone have a fix for this?
var geolocate = new mapboxgl.GeolocateControl({
positionOptions: {
enableHighAccuracy: true
},
trackUserLocation: true,
showUserHeading: true,
showUserLocation: true,
showAccuracyCircle: true
});

Related

Leaflet zoom control don't zoom in or out

I have an animation, which starts like this:
Window.map = new L.Map('map', {
zoomControl: false
});
L.control.zoom({position:'topleft'
}).addTo(Window.map);
//add tile layers
var basemapLayer = new
L.TileLayer('https://{s}.tiles.mapbox.com/v4/github.map-xgq2svrz/{z}/{x}/{y}.png?ac$
token: '.....'
});
And then I am adding feature layers set zoom min and max, and it did not work either. I may hide it, change the position, but when I click on the '+' or the '-' nothing happens.
I am using the default css from leaflet.

How to NOT change camera center upon GeolocateControl trigger?

I have a Mapbox web app map that I want to add the user's location to, but NOT have the map automatically recenter itself on the user's location. The example code here https://docs.mapbox.com/mapbox-gl-js/example/locate-user/ works great, I just don't want the map to recenter on the user.
This is specifically the code I'm using:
// GEO LOCATE USER!
const geolocate = new mapboxgl.GeolocateControl({
positionOptions: {
enableHighAccuracy: false
},
trackUserLocation: false
});
map.addControl(geolocate,"bottom-left");
geolocate.trigger();
Is there a way to have the geolocate.trigger() be a passive experience and not recenter the map, just add the user's location to it?
Mapbox-GL doesn't expose an official API to disable auto-tracking of a user's location with the camera, but you can monkey-patch it to accomplish what you want:
const locate = new mapboxgl.GeolocateControl({
positionOptions: { enableHighAccuracy: true },
trackUserLocation: true
})
// hacky workaround for the fact that mapbox doesn't let you disable camera auto-tracking
locate._updateCamera = () => {}
this.map.addControl(locate)
This works by replacing the internal _updateCamera method with a noop:
- https://github.com/mapbox/mapbox-gl-js/blob/2693518e8d042b3120c33f08433abbc3b114d25c/src/ui/control/geolocate_control.js#L187

Repeated tiles when switching from OpenLayers to Leaflet

I am trying to change an OpenLayers 2 call to Leaflet, but when I do the map is displayed fine at zoom level 0, but every time I zoom in, the map doubles from the previous number. Any suggestions as to why? Here is a picture to what its doing.
OpenLayers 2 map options
var options = {
projection: new OpenLayers.Projection("EPSG:3857"),
displayProjection: new OpenLayers.Projection("EPSG:4326"),
units: "m",
maxResolution: 156543.0339,
maxExtent: new OpenLayers.Bounds(-20037508.34, -20037508.34,
20037508.34, 20037508.34),
controls: [
new OpenLayers.Control.Navigation(
{dragPanOptions: {enableKinetic: true}}
)
]
};
OpenLayers 2 code
var bathyEsri = new OpenLayers.Layer.XYZ(
' ESRI Ocean'
,'http://services.arcgisonline.com/ArcGIS/rest/services/Ocean_Basemap/MapServer/tile/${z}/${y}/${x}.jpg'
,{
sphericalMercator : true
,isBaseLayer : true
,wrapDateLine : true
,opacity : 1
,visibility : true
}
);
Leaflet Options
var options = {
worldCopyJump: true,
maxBounds: L.LatLngBounds([20037508.34,20037508.34],[-20037508.34,-20037508.34]),
crs: L.CRS.EPSG4326,
center: [39.73, -104.99],
zoom: 0
};
Leaflet Code
var bathyEsri = L.tileLayer('http://services.arcgisonline.com/ArcGIS/rest/services/Ocean_Basemap/MapServer/tile/${z}/${y}/${x}.jpg');
Your problem is basically a typo.
Your analysis is also misleading: it's not that the map is being "duplicated", but rather every tile being requested is the 0/0/0 tile. If you use the network inspection tools of your browser, you'll see that the tile URL is something like https://services.arcgisonline.com/ArcGIS/rest/services/Ocean_Basemap/MapServer/tile/$3/$4/$5.jpg , but the tile image corresponds to the /0/0/0.jpg tile.
If you look at those URLs a bit more closely, you'll notice some "extra" $ signs. Why are those there? Well, consider that you wrote your tilelayer URL scheme as
var bathyEsri = L.tileLayer('http://...../tile/${z}/${y}/${x}.jpg');
But keep in mind that the Leaflet documentation clearly states:
var bathyEsri = L.tileLayer('http://...../tile/{z}/{y}/{x}.jpg');
Change this, and everything will magically start working.

Mapbox GL JS Bearing

Is it possible in Mapbox GL JS to get the users bearing?
I would like to show the direction in which the user is facing, to assist them in navigating to nearby POI.
I understand that it is possible to set the bearing of the map and also get the current bearing of it, but i need the actual real life bearing of the user.
Kind of the same thing as on Google Maps:
The service is intended to run as an Ionic app on iOS and Android, and the assistance in bearing is a key feature in helping them locate nearby POI on a well populated map.
You can get the user's bearing (if their device has such a sensor) by obtaining a Coordinates object from Gelocation#getCurrentPosition() and reading Coordinates#heading.
Mapbox GL JS has no built-in user interface for displaying a user's heading. Building your own user interface is easy. See this example which uses the symbol-rotation property.
So, after some time spend on this, i thought I'd show how i ended up doing this, in case someone else needs it or have a better solution.
It seems cordova has a built in "heading" property in the position object.
https://github.com/apache/cordova-plugin-geolocation
var heading = $rootScope.position.heading;
First, i make sure that the marker is always pointing in the heading direction, even when the user turns the map, by subtracting the mapBearing(degrees the map has turned from North), from the user heading.
map.on('rotate', function(){
map.setLayoutProperty('drone', 'icon-rotate', heading - map.getBearing())
});
I create an icon, at the users position, add the source and add the layer with the source.
map.on('load', function () {
var point = {"type": "Point", "coordinates": [$rootScope.position.long, $rootScope.position.lat]};
map.addSource('drone', {type: 'geojson', data: point });
map.addLayer({
"id": "drone",
"type": "symbol",
"source": "drone"
}
});
Next i check that heading is actually available, since it only appears to return a value, when the user is moving(only tested on Android so far), and if it is, update the heading of the point.
if($rootScope.position.heading){
var heading = $rootScope.position.heading;
map.setLayoutProperty('drone', 'icon-rotate', $rootScope.position.heading);
};
Finally i update the position of the point, in a "$watch" position.
map.getSource('drone').setData(point);
This way, i can watch the users heading, and the point keeps on track, even when the user rotates the map.
For the users coming here after 2020 (what an year lol), mapbox gl js now supports geolocation which not only provides user's heading but also a bunch of other helpful data:
const geolocate = map.addControl(
new mapboxgl.GeolocateControl({
positionOptions: {
enableHighAccuracy: true
},
trackUserLocation: true
})
)
then listen for geolocate event:
geolocate.on('geolocate', (e) => {
console.log(e);
});
this will give you following object:
{
coords: {
accuracy: number;
altitude: number;
altitudeAccuracy: number;
heading: number;
latitude: number;
longitude: number;
speed: number;
};
timestamp: number;
heading will give you direction of the user. As the geolocate control keeps triggering automatically so can get the user's direction as well as speed and altitude etc in real time and use that to display data driven symbols on map.

Leaflet: map.locate set maxZoom dynamically

It is easy to track a users position and show a position marker with Leaflet
_map.locate({
watch: true,
setView: true,
maxZoom: 13,
enableHighAccuracy: true
})
and some code in the locationfound callback.
However, the user might wish to zoom to a differed level, but when the position maker gets updated the map always zooms back to the value set in the locate maxZoom option.
Is there a way to change the maxZomm value dynamically depending on the zoom level the user has chosen?
Well, after digging a bit in the source it is just as easy as this:
Listen to the zoomend event
_map.on('zoomend', _changeLocateMaxZoom);
and then update the locateOptions maxZoom
function _changeLocateMaxZoom(e) {
if (_map._locateOptions) {
_map._locateOptions.maxZoom = _map.getZoom();
}
}
Leaflet is a well designed library.