Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

User geolocation in Google Maps #784

Merged
merged 22 commits into from
Nov 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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