Greasy Fork is available in English.

Fake GPS Location

Falsify GPS location to protect privacy, or to provide a virtual location sensor device. This script should either be be configured for specific sites only, or for all sites but excludes some specific sites.

// ==UserScript==
// @name         Fake GPS Location
// @namespace    https://greasyfork.org/en/users/85671-jcunews
// @version      1.0.1
// @license      AGPLv3
// @author       jcunews
// @description  Falsify GPS location to protect privacy, or to provide a virtual location sensor device. This script should either be be configured for specific sites only, or for all sites but excludes some specific sites.
// @match        *://www.example-mockgps.net/*
// @exclude      *://www.report-real-gps.com/*
// @grant        none
// @run-at       document-start
// ==/UserScript==

(() => {

  //=== CONFIGURATION BEGIN ===

  //Default latitude in decimal degrees (-90 to 90). Negative = South of Equator. Positive = North of Equator.
  var defaultLatitude         = 0;
  //Default longitude in decimal degrees (-180 to 180). Negative = West. Positive = East.
  var defaultLongitude        = 0;
  //Default latitude & longitude accuracy in positive decimal degrees.
  var defaultAccuracy         = 0.000001;

  //Default altitude above sea level in meters.
  var defaultAltitude         = 0;
  //Default altitude accuracy in positive meters. `null` if not available.
  var defaultAltitudeAccuracy = 1;

  //Default heading in degrees. 0 to less than 360. 0 = North, 90 = East, 270 = West, etc. `null` if not available.
  var defaultHeading          = 0;
  //Default speed in meters per second. `null` if not available.
  var defaultSpeed            = 0;

  //Location profiles based on host name of websites.
  //It provides different location information between different websites.
  //All properties for each location profile are optional.
  //If a website host name if not in the list, the default values for all properties will be used.
  //If a property is not specified or has invalid value, the default value will be used.
  //If a property has invalid value range, it will be capped to its value range boundary.
  var locations = {
    "www.example-mockgps.net": {
      latitude: 50.7, longitude: 55, accuracy: 0.1, altitude: 100, altitudeAccuracy: 1, heading: 79.9, speed: 10.5
    },
    "www.another-example-mockgps.net": {
      latitude: -5.1, longitude: 123.4322, accuracy: 0.0002, altitude: 1234.5, altitudeAccuracy: 0.5, heading: 222.2, speed: 1.3
    }
  };

  //Time interval in milliseconds (1000ms is second) to report periodic location report.
  var watchInterval = 1000;

  //=== CONFIGURATION END ===

  Object.keys(locations).forEach(n => {
    n = locations[n];
    if ("number" === typeof n.latitude) {
      if (n.latitude > 90) {
        n.latitude = 90;
      } else if (n.latitude < -90) n.latitude = -90;
    } else n.latitude = defaultLatitude;
    if ("number" === typeof n.longitude) {
      if (n.longitude > 180) {
        n.longitude = 180;
      } else if (n.longitude < -180) n.longitude = -180;
    } else n.longitude = defaultLongitude;
    if ("number" === typeof n.accuracy) {
      if (n.accuracy <= 0) n.accuracy = defaultAccuracy;
    } else if (n.accuracy !== null) n.accuracy = defaultAccuracy;
    if ("number" !== typeof n.altitude) n.altitude = defaultAltitude;
    if ("number" === typeof n.altitudeAccuracy) {
      if (n.altitudeAccuracy <= 0) n.altitudeAccuracy = defaultAltitudeAccuracy;
    } else if (n.altitudeAccuracy !== null) n.altitudeAccuracy = defaultAltitudeAccuracy;
    if ("number" === typeof n.heading) {
      if (n.heading >= 360) {
        n.heading = 359.999999;
      } else if (n.heading < 0) n.heading = 0;
    } else if (n.heading !== null) n.heading = defaultHeading;
    if ("number" === typeof n.speed) {
      if (n.speed < 0) n.speed = 0;
    } else if (n.speed !== null) n.speed = defaultSpeed;
  });

  navigator.geolocation = navigator.geolocation || {};
  navigator.geolocation.getCurrentPosition = function(succ, err) {
    setTimeout((a, b) => {
      if (!(a = locations[location.hostname])) {
        a = {
          latitude: defaultLatitude, longitude: defaultLongitude, accuracy: defaultAccuracy,
          altitude: defaultAltitude, altitudeAccuracy: defaultAltitudeAccuracy, heading: defaultHeading, speed: defaultSpeed
        };
      }
      b = {coords: {}, timestamp: (new Date()).getTime()};
      Object.keys(a).forEach(k => b.coords[k] = a[k]);
      succ(b);
    }, 0);
  };
  navigator.geolocation.watchPosition = function(succ, err) {
    return setInterval((succ, err) => {
      navigator.geolocation.getCurrentPosition(succ, err);
    }, watchInterval, succ, err);
  };
  navigator.geolocation.clearWatch = function(id) {
    clearInterval(id);
  };

})();