Skip to content

Commit

Permalink
User geolocation in Google Maps (#784)
Browse files Browse the repository at this point in the history
* Add checkbox for user geolocation location

* Move control into map, multiple map support

* Add mylocation parameter

* Draw circle for my location, replacing marker

* Update my location accuracy on every update

* Fix my location circle position update

* Fix map centering every time my location updates

* Use icon for my location toggle

* Add translation string

* Add marker to user location

* Follow user on map per default, stop following on drag

* Fix AJAX markers not updating on re-center

* Add translation string

* Store my location setting in localStorage, global for all maps

* Temp: Disabled locked location, disable zoom into location

* Center on location on button press

* Store follow my location option per map, add mylocationfollow parameter

* Cleanup

* Add my location zoom parameter

* Add to existing module

* Add to existing module

* Add translation string
  • Loading branch information
stevygee authored Nov 13, 2024
1 parent ec75e6a commit 543c5b2
Show file tree
Hide file tree
Showing 7 changed files with 298 additions and 2 deletions.
4 changes: 3 additions & 1 deletion extension.json
Original file line number Diff line number Diff line change
Expand Up @@ -248,14 +248,16 @@
"ext.maps.googlemaps3": {
"scripts": [
"GoogleMaps/jquery.googlemap.js",
"GoogleMaps/ext.maps.googlemaps3.js"
"GoogleMaps/ext.maps.googlemaps3.js",
"GoogleMaps/mylocation.js"
],
"messages": [
"maps-googlemaps3-incompatbrowser",
"maps-copycoords-prompt",
"maps-searchmarkers-text",
"maps-fullscreen-button",
"maps-fullscreen-button-tooltip",
"maps-mylocation-button-tooltip",
"maps-kml-parsing-failed"
],
"targets": [ "desktop", "mobile" ]
Expand Down
4 changes: 4 additions & 0 deletions i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@
"maps-displaymap-par-geojson": "URL of a file or name of the page containing GeoJSON data",
"maps-fullscreen-button": "Toggle fullscreen",
"maps-fullscreen-button-tooltip": "View the map as fullscreen or embedded.",
"maps-mylocation-button-tooltip": "Show my location on the map.",
"validation-error-invalid-location": "Parameter \"$1\" must be a valid location.",
"validation-error-invalid-locations": "Parameter \"$1\" must be one or more valid locations.",
"validation-error-invalid-width": "Parameter \"$1\" must be a valid width.",
Expand Down Expand Up @@ -125,6 +126,9 @@
"maps-par-height": "Allows setting the height of the map. By default pixels will be assumed as unit, but you can explicitly specify one of these units: px, ex, em.",
"maps-par-centre": "The location on which the map should be centered",
"maps-par-enable-fullscreen": "Enable fullscreen button",
"maps-par-enable-mylocation": "Enable the geolocation button",
"maps-par-mylocationzoom": "The zoom level to go to when user location is turned on",
"maps-par-enable-mylocationfollow": "Continously center map on user location",
"maps-par-kml": "KML files to load onto the map.",
"maps-par-markercluster": "Allows merging of multiple nearby markers into one marker",
"maps-googlemaps3-incompatbrowser": "Your browser is not compatible with Google Maps v3.",
Expand Down
2 changes: 1 addition & 1 deletion resources/GoogleMaps/googlemaps3ajax.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

(function( $, sm ) {
var ajaxRequest = null;
var mapEvents = ['dragend', 'zoom_changed'];
var mapEvents = ['dragend', 'bounds_changed'];

$( document ).ready( function() {
// todo: find a way to remove setTimeout.
Expand Down
Binary file added resources/GoogleMaps/img/mylocation-sprite-2x.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions resources/GoogleMaps/jquery.googlemap.js
Original file line number Diff line number Diff line change
Expand Up @@ -731,6 +731,11 @@
if(options.fullscreen){
this.map.controls[google.maps.ControlPosition.TOP_RIGHT].push(new FullscreenControl(this.map));
}

// - My Location
if(options.mylocation){
this.map.controls[google.maps.ControlPosition.RIGHT_BOTTOM].push(new window.MyLocationControl(this.map, options.mylocationfollow, options.mylocationzoom));
}
};

this.setup = function () {
Expand Down
267 changes: 267 additions & 0 deletions resources/GoogleMaps/mylocation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,267 @@
(function( $ ) {

/* Track user's location with marker on map */

function activateTrackMyLocation() {
localStorage.setItem( 'mapsTrackMyLocation', 'yes' );
}

function getTrackMyLocation() {
return localStorage.getItem( 'mapsTrackMyLocation' );
}

function isTrackMyLocationSet() {
return getTrackMyLocation() && getTrackMyLocation() === 'yes';
}

function clearTrackMyLocation() {
localStorage.removeItem( 'mapsTrackMyLocation' );
}

/* Follow: Center map on user's location */

function activateFollowMyLocation( mapDiv ) {
mapDiv.data( 'followMyLocation', 'locked' );
}

function getFollowMyLocation( mapDiv ) {
return mapDiv.data( 'followMyLocation' );
}

function isFollowMyLocationSet( mapDiv ) {
return getFollowMyLocation( mapDiv ) && getFollowMyLocation( mapDiv ) === 'locked';
}

function clearFollowMyLocation( mapDiv ) {
mapDiv.removeData( 'followMyLocation' );
}

function updateMapsTrackMyLocation( centerOnMyLocation = false ) {
$( window.mapsGoogleList ).each( function( index, map ) {
if ( ! map.options.mylocation ) {
return;
}

let mapDiv = $( map.map.getDiv() );

if( isTrackMyLocationSet() ) {
mapDiv.data( 'myLocationIconUI' ).style.backgroundPosition = '-144px 0';
activateMyLocation( map.map, centerOnMyLocation );
} else {
mapDiv.data( 'myLocationIconUI' ).style.backgroundPosition = '0 0';
deactivateMyLocation( map.map );
}
} );
}

$( document ).ready( function() {
// todo: find a way to remove setTimeout.
setTimeout( function() {
if( typeof google === 'undefined' ) {
return;
}

updateMapsTrackMyLocation( false );
}, 500 );
} );

// Control for toggling the user location function
function MyLocationControl( map, followMyLocation, zoom ) {
var controlDiv = document.createElement('div');
controlDiv.style.padding = '10px 10px 0px 10px';
controlDiv.index = 1;

var controlUI = document.createElement('div');
controlUI.style.padding = '6px 6px';
controlUI.style.backgroundColor = 'white';
controlUI.style.borderStyle = 'solid';
controlUI.style.borderColor = 'rgba(0, 0, 0, 0.14902)';
controlUI.style.borderWidth = '1px';
controlUI.style.borderRadius = '2px';
controlUI.style.cursor = 'pointer';
controlUI.style.textAlign = 'center';
controlUI.style.boxShadow = 'rgba(0, 0, 0, 0.298039) 0px 1px 4px -1px';
controlUI.style.backgroundClip = 'padding-box';
controlUI.title = mw.msg('maps-mylocation-button-tooltip');
controlDiv.appendChild(controlUI);

var controlText = document.createElement('div');
controlText.style.backgroundPosition = '0 0';
controlText.style.backgroundImage = 'url(' + mw.config.get( 'egMapsScriptPath' ) + '/resources/GoogleMaps/img/mylocation-sprite-2x.png)';
controlText.style.backgroundSize = '180px 18px';
controlText.style.display = 'block';
controlText.style.height = '18px';
controlText.style.left = '6px';
controlText.style.margin = '0';
controlText.style.padding = '0';
controlText.style.width = '18px';
controlUI.appendChild(controlText);

let mapDiv = $( map.getDiv() );

// Store for later access
mapDiv.data( 'myLocationIconUI', controlText );
mapDiv.data( 'myLocationZoom', zoom === -1 ? false : zoom );

// Handle toggle button click
google.maps.event.addDomListener( controlUI, 'click', function() {
if ( isTrackMyLocationSet() ) {
clearTrackMyLocation();
} else {
if ( followMyLocation ) {
activateFollowMyLocation( mapDiv );
}

activateTrackMyLocation();
}

updateMapsTrackMyLocation( true );
} );

// Handle dragged map
google.maps.event.addDomListener( map, 'dragend', function() {
// Stop centering on user's location
if ( isFollowMyLocationSet( mapDiv ) ) {
clearFollowMyLocation( mapDiv );
}
} );

return controlDiv;
}
window.MyLocationControl = MyLocationControl;

function handleLocationError( browserHasGeolocation, pos ) {
console.log( browserHasGeolocation ?
'Error: The Geolocation service failed.' :
'Error: Your browser doesn\'t support geolocation.' );
}

function drawMyLocation( position, map ) {
let pos = {
lat: position.coords.latitude,
lng: position.coords.longitude
};
let radius = position.coords.accuracy * 0.5;

let mapDiv = $( map.getDiv() );

if ( typeof mapDiv.data( 'myLocationMarker' ) === 'undefined' ) {
// Add a circle to visualize geolocation accuracy
let myLocationCircle = new google.maps.Circle( {
strokeWeight: 0,
fillColor: "#5383EC",
fillOpacity: 0.2,
map,
center: pos,
radius: radius,
} );

// Add a marker at the user's location
const svgMarker = {
path: "M 11, 11 m 10, 0 a 10,10 0 1,0 -20,0 a 10,10 0 1,0 20,0",
fillColor: "#5383EC",
fillOpacity: 1,
strokeWeight: 2,
strokeColor: "white",
anchor: new google.maps.Point( 11, 11 ),
scale: 0.75,
};

let myLocationMarker = new google.maps.Marker( {
position: pos,
icon: svgMarker,
map: map,
} );

// Store for later access
mapDiv.data( 'myLocationMarker', myLocationMarker );
mapDiv.data( 'myLocationCircle', myLocationCircle );
} else {
// Update position and radius
mapDiv.data( 'myLocationMarker' ).setPosition( pos );
mapDiv.data( 'myLocationCircle' ).setCenter( pos );
mapDiv.data( 'myLocationCircle' ).setRadius( radius );
}

if ( isFollowMyLocationSet( mapDiv ) ) {
// Center the map on the user's location
map.setCenter( pos );
}
}

function activateMyLocation( map, centerOnMyLocation ) {
let mapDiv = $( map.getDiv() );

let geolocationOptions = {
enableHighAccuracy: true,
timeout: 5000,
maximumAge: 0,
};

// Check if geolocation is supported
if ( navigator.geolocation ) {
if ( centerOnMyLocation ) {
// Center map only once
navigator.geolocation.getCurrentPosition(
function( position ) {
let pos = {
lat: position.coords.latitude,
lng: position.coords.longitude
};
map.setCenter( pos );

// Zoom into user's location
if ( mapDiv.data( 'myLocationZoom' ) !== false ) {
map.setZoom( mapDiv.data( 'myLocationZoom' ) );
}
},
// Error handling
function() {
handleLocationError( true, map.getCenter() );
},
geolocationOptions
);
}

// Continously track user's location
let myLocationWatchId = navigator.geolocation.watchPosition(
function( position ) {
drawMyLocation( position, map );
},
// Error handling
function() {
handleLocationError( true, map.getCenter() );
},
geolocationOptions
);
mapDiv.data( 'myLocationWatchId', myLocationWatchId );
} else {
// Browser doesn't support geolocation
handleLocationError( false, map.getCenter() );
}
}

function deactivateMyLocation( map ) {
let mapDiv = $( map.getDiv() );

// Check if geolocation is supported
if ( navigator.geolocation ) {
// Stop tracking location
navigator.geolocation.clearWatch( mapDiv.data( 'myLocationWatchId' ) );
mapDiv.removeData( 'myLocationWatchId' );
}

// Remove marker from the map
if ( typeof mapDiv.data( 'myLocationMarker' ) !== 'undefined' ) {
mapDiv.data( 'myLocationMarker' ).setMap( null );
mapDiv.removeData( 'myLocationMarker' );
}

// Remove circle from the map
if ( typeof mapDiv.data( 'myLocationCircle' ) !== 'undefined' ) {
mapDiv.data( 'myLocationCircle' ).setMap( null );
mapDiv.removeData( 'myLocationCircle' );
}
}

})( window.jQuery );
18 changes: 18 additions & 0 deletions src/GoogleMapsService.php
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,24 @@ function( string $fileName ) {
'message' => 'maps-par-enable-fullscreen',
];

$params['mylocation'] = [
'type' => 'boolean',
'default' => false,
'message' => 'maps-par-enable-mylocation',
];

$params['mylocationfollow'] = [
'type' => 'boolean',
'default' => false,
'message' => 'maps-par-enable-mylocationfollow',
];

$params['mylocationzoom'] = [
'type' => 'integer',
'default' => -1,
'message' => 'maps-par-mylocationzoom',
];

$params['scrollwheelzoom'] = [
'aliases' => [ 'scrollzoom' ],
'type' => 'boolean',
Expand Down

0 comments on commit 543c5b2

Please sign in to comment.