@mezzo-forte/geoxp

1.3.12 • Public • Published

Mezzo Forte

Mezzo Forte GeoXp

2021

Description

Mezzo Forte GeoXp is a client side, event based js library that manages all the core background features of a geolocated audio tour.

It maps audio contents to geographical positions, and automatically reproduces them based on configuration rules.

It’s meant to be used inside any front-end interface, regardless of the js framework.

An API and methods documentation page is available at this link.

The example app (code is in /example-app directory) is published here.

It’s made of three modules.

  • Geo

    Manages all geolocation logic. It’s based on geolocation API, but it can be used with any external geolocation provider.

  • Audio

    Manages all audio playback logic. It relies on Howler JS library.

  • Experience

    It’s the real core of the package, defines automatic actions to be done as a consequence of geolocation updates.


Install

npm install @mezzo-forte/geoxp

Contents


Key concepts

Geo key concepts

Positions

A GeoXp position is a circular area defined by two geographical coordinates ({lat, lon}) a radius and a deadband. The inner radius defines the inside position status. (radius + deadband) defines an outer radius that serves hysteresis purposes to avoid abrupt status changes.

Geolocation providers

GeoXp default geolocation provider is the Web Geolocation API. Some framework or browser will not support Geolocation API, or have better ways to get the device location. It’s possible to use GeoXp with external geolocation providers (eg: Capacitor or native geolocation systems).

Minimum accuracy

Every geographical data, coming from the geolocation system, that does not fulfill accuracy requrements will be ignored.

Audio key concepts

Audio content

An audio content can be any audio file, reachable through an URL. GeoXp audio player is based on Howler.js (see here for suggested formats).

Experience key concepts

Spots

A GeoXp spot is the core entity of an experience and represents a relation between a position and an audio content. For example, if you want the user to listen the file audio_1.mp3 in the position position_A, it will be necessary to create a new spot that associate the audio_1 content to the position_A geographical coordinates (in the Usage section we describe in detail how to make this configuration).

This is to say, more than one spot can be linked to a certain position, the same audio content can be linked to multiple positions.

Behavior

The event behavior for a GeoXp spot is designed as follows:

GeoXp spot

User enters the position circle (its inside area), spot becomes active, associated audio content is played. User leaves the position inside area, but he’s still inside the deadband, the audio content is still playing. User leaves the deadband, spot becomes outgoing, the audio content fades out and stops.

Patterns

A list of spots is called a pattern. Patterns define the overall behavior of its spots. Multiple patterns could be active at any time, providing multiple simultaneous experiences (eg: one pattern defines what speeches to play in certain positions, one pattern defines background audio effects to play alongside the speeches, using the same positions). Patterns are separate entities that don’t talk to each other, spots order and content overlap management are independent between patterns.

Spots order

GeoXP provides limited content queue management. This can be achieved using the spot “after” and “notAfter” properties. If after is defined, GeoXp will not reproduce a certain spot content unless the after spot has already been played. If notAfter is defined, GeoXp will not reproduce a certain spot content if the notAfter spot has already been played.

Content replay

When content starts playing, a spot becomes visited. When the user reenters a visited spot, geoXp will not play its content. It will throw a notification instead, to let the user choose what to do. This behavior can be overridden using the pattern replay option. In this case, when the user reenters a visited spot, its content replays as usual. See Spot content replay for details.

Cookies

As default behavior, when GeoXp instance is reloaded (eg: page refresh) exprience patterns memory of visited spots is cleared. This can be avoided enabling cookies for patterns in configuration, by appending a cookies property to experience options.

When activated, a cookie for each pattern is updated every time a new spot is visited.

This cookie is deleted (to let the experience restart) when:

  • by default, when all spots in a pattern are visited (cookies.deleteOnCompletion).
  • when a specific spot, flagged with the “last” option is activated (cookies.deleteOnLastSpot).
  • manually, when geoXp.clearCookies() is called.
  • manually, when geoXp instance is destroyed (geoXp.destroy()).

Please note that deleteOnCompletion overrides deleteOnLastSpot.

The cookie property of a pattern can be set to true to use all the default options, or to an object with specific values See here for more details on configuration options

IMPORTANT Cookies have a standard lifespan of 5 minutes, which can be overridden with cookies.expiration. Please note that when cookies expire they are deleted, but no change is made runtime to the current geoXp visited spots. To let the experience to restart, a reload or page refresh is needed.

Content overlap

When the user is actually inside multiple spots at the same time (locations are overlapping, multiple spots are linked to the same location), as default behavior GeoXp will play one content at a time, with no overlapping. When the first audio finishes, the other starts. This can be overridden using the pattern “overlap” configuration option.

Manual mode

Sometimes geolocation data could be bad for unpredictable reasons, nerby buildings or trees could block part of the satellites communications, electromagnetic interference by power lines and so on, resulting in poor location accuracy. When accuracy is too low, manual spot activation mode becomes available. This mode overrides all experience playback rules, so geoXp enables it only for really low accuracy (greater than 100m), and only if user is not too far away from the intended spot playback position (in the case of slow location update time and the user has reached a new spot before an update). See Forcing spots activation for details.

IMPORTANT - forcing a spot is a plan B when something is not working properly (bad GPS or slow update time). It will interrupt all automatic experience management, until the forced content is finished. After that, all the experience logic will get back to work.

Usage

GeoXp is intended to be used as a singleton instance. It has to be created once the application starts, based on a configuration object.

Create a GeoXp instance

// import module
import GeoXp from '@mezzo-forte/geoxp';

// create configuration object
const config = { /* your configuration here */ };

// create the GeoXp instance
const geoXp = new GeoXp(config);

Configuration

GeoXp, once created, works without any external intervention. To provide this high level of automation, it has to be accurately configured according to the desired application. The configuration is provided with a json object made of three parts, each carrying the configuration information for one of the internal modules.

config: {
  geo: { /* ... */ },
  audio: { /* ... */ },
  experience: { /* ... */ }
}

Configuration for geolocation (geo) is a simple map of positions and parameters for geofencing, configuration for audio is a list of all the audio content available.

Configuration for experience is meant to set links between positions and related content.

Each configuration section has a .options child that stores some module working parameters. If no options object is provided, GeoXp will use its hardcoded default configuration.

Geo configuration

Provides information for geolocation module configuration. See here for more details on Geo options

geo: {
  positions: [
    {
      id: string // unique position id
      label: string // position name or description
      lat: number // latitude in degrees north
      lon: number // longitude in degrees east
      radius: number // fencing radius in meters
      deadband: number // fencing deadband in meters
      fetch: number // prefetching distance as ratio of the radius, from 1 to n
    }
  ],
  options: {
    enableHighAccuracy: boolean // @see https://developer.mozilla.org/en-US/docs/Web/API/PositionOptions/enableHighAccuracy
    accuracy: number // minimum acceptable accuracy in meters - default value = 25m
    defaultDeadband: number // default fencing deadband - default value = 10m
    defaultRadius: number // default fencing radius - default value = 20m
    defaultFetch: number // default prefetch distance ratio default value = 1 (integer)
  }
}

Audio configuration

Provides information for audio module configuration. See here for more details on Audio options

audio: {
  sounds: [ // array of audio contents
    {
      id: string // unique content id
      url: string // content url (local or remote)
    }
  ],
  options: {
    test: string // url for test sound
    silence: string // url for silence sound
    visited: string // url for spot already visited sound
    fadeInTime: number // fade in time [ms] - default value = 0 ms
    fadeOutTime: number // fade out time [ms] - default value = 1000 ms
  }

Experience configuration

Experience configuration provides relations between geolocation and content. See here for more details on Experience options

experience: {
  patterns: [ // array of patterns, each with its spots list and parameters
    {
      id: string // pattern unique id
      label: string // pattern name or description
      disabled: bool // pattern is disabled
      replay: bool // spots are automatically replayed
      overlap: bool // content playback can overlap
      spots: [ // array of pattern’s spots
        {
          id: string // spot unique id
          position: // position id as in geo position configuration
          audio: // audio id as in audio sound configuration
          after: // id of the previous mandatory spot (see “key concepts”, “Spots order”)
          notAfter: // id of the spot that prevents current spot playback (see “key concepts”, “Spots order”)
          label: // spot name or descriptions
        }
      ]
    }
  ],
  options: {
    visitedFilter: number // time after an already visited spot is notified [ms] - default value = 5000 ms
    cookies: { // enables pattern "visited spots" cookies
      deleteOnCompletion: boolean // default option, deletes cookie when all pattern spots are visited
      deleteOnLastSpot: boolean // deletes cookie when spot flagged with "last" is visited
      expiration: number // [minutes] overrides cookies default expiration time (5 minutes)
    }
  }
}

NOTE - patterns are enabled by default. See core methods to know how to disable or re-enable them

Reload and disposal

// Creates a new geoXp instance
const geoXp = new GeoXp(config);

// Refreshes all geoXp state with the given configuration
geoXp.reload(config);

// Disposes geoXp object
geoXp.destroy();

Events subscription

GeoXp is meant to work automatically based on its configuration, so most of the interaction with it is based on events. Its event dispatcher (geoXp.event) is based on Node.js EventEmitter and is responsible for events notification to outside subscribers.

Three main methods are wrappped by the GeoXp class:

  • geoXp.on(eventName, listener) - adds the listener function to the end of the listeners array for the event named eventName
  • geoXp.once(eventName, listener) - adds a one-time listener function for the event named eventName
  • geoXp.off(eventName, listener) - removes the specified listener from the listener array for the event named eventName

All other EventEmitter properties and methos are accessible through the event property of GeoXp class.

Available events are:

Position update

geoXp.on('position', position => { /* ... */ })

Position update occurs every time geolocation API receives a new location. The new location is provided to the callback as Geolocation API standard position object.

Spot incoming

geoXp.on('incoming', spot => { /* ... */ })

User entered the prefetch distance of a spot. GeoXp will start related audio pre loading. The object provided as callback argument carries all the spot info based on configuration.

spot: {
	id: string,
	position: string (position id as in geo configuration)
	audio: string (audio id as in audio configuration)
	after: string (spot id as in experience pattern configuration)
}

Spot active

geoXp.on('active', spot => { /* ... */ })

A spot is being activated. GeoXp will play the content associated. The object provided as callback argument carries all the spot info based on configuration.

Spot visited

geoXp.on('visited', spot => { /* ... */ })

User entered a spot which he already visited before. The object provided as callback argument carries all the spot info based on configuration.

Spot outgoing

geoXp.on('outgoing', spot => { /* ... */ })

User exited a spot. GeoXp will stop playing related content. The object provided as callback argument carries all the spot info based on configuration.

Content playing

geoXp.on('play', audio => { /* ... */ })

Some audio content just started playing. Callback argument is an object with the audio information.

audio: {
  id: string, // is the Howler instance audio id. Is composed as spotId-audioId
  overlap: boolean,
  playWhenReady: boolean,
  spot: { // spot that caused playback
    id: string, // spot id
    label: string, // spot label
    audio: string, // audio id
    postion: string, // position id
    after: string
  }
  audio: Howler.Howl // howler instance
}

Content ended

geoXp.on('stop', audio =>  { /* ... */ })

Some audio content just stopped (either for completion or because it has been stopped). Callback argument is an object with the audio information.

audio: {
  id: string, // is the Howler instance audio id. Is composed as spotId-audioId
  overlap: boolean,
  playWhenReady: boolean,
  spot: { // spot that caused playback
    id: string, // spot id
    label: string, // spot label
    audio: string, // audio id
    postion: string, // position id
    after: string
  }
  audio: Howler.Howl // howler instance
}

Audio interaction

GeoXp manages all audio content on its own. However, it’s possible to interact with it when needed (eg: showing audio current seek, playing / pausing audio, etc.). In facts, the audio property of the play and stop events is an Howl oject (the Howler.js instance for audio management) that exposes all methods for audio interaction (pause, play, seek).

// Sets the volume for all audio contents, 0 to 1 max volume
geoXp.audio.setVolume(volume: number);

// Immediately stops all audio currently playing
geoXp.audio.stopAll();

// Immediately mutes / unmutes all audio currently playing
geoXp.audio.muteAll(mute: boolean);

External geolocation providers

Internal geoXp geolocation provider is the Web Geolocation API. If needed, it could be overriden with an external one of choice (eg: Capacitor location for mobile integration, external GPS sensor, etc). Position updates can be provided using the updateGeolocation(position) method, passing position as Geolocation API standard position object. To avoid unwanted updates from the internal provider, disable it by calling the internalGeolocation(false) method. Internal geolocation provider can be reenabled by calling internalGeolocation(true).

Spots content replay

By default, when user reenters a spot he already visisted before, the spot isn't replayed; instead, a visited event for that spot is fired. The spot replay can be triggered calling the replaySpot(id) method, passing the id of the spot to replay. The spot is then marked as unvisited, and the playback starts immediately. Multiple spots could be linked to the same position, so multiple visited events could be fired at once. If you don't want to care about which spot is to be replayed, call the replaySpot() method with no argument, so all the spots linked to the current position are marked as unvisited, and replayed following the rules defined in configuration (eg: spot order). This behavior could be overridden using the pattern.replay option. If a pattern is set so, its spots will replay immediately, and no visited event is fired.

Forcing spots activation

If GPS accuracy is low and user isn't too far away from a spot location, the spot can be activated manually using the forceSpot(id) method, passing the id of the spot to force. GeoXp then enters manual mode (internal geolocation updates are stopped, all other audio content is stopped) and activates the desired spot. When the playback is finished (or stopped), geoXp returns to automatic mode and the experience goes on as usual. If you want to know if manual mode is available, just call the canForceSpot(id) passing the id of the deisired spot. If rules for manual mode are not fulfilled, it returns and error string explaing the reason why it can't be forced. Otherwise it will return undefined. Forcing contents is always allowed for spots that does not have a geographic position associated: in this case the only way to reproduce the audio file is to invoke forceSpot method, and the rules described above are not applied.

Example:

"spots": [
  {
    "id": "s02",
    "position": "p02",
    "audio": "a02",
    "label": "Spot 2!!"
  },
  {
    "id": "s03",
    "audio": "a03",
    "label": "Spot 3!!"
  },
]

In the above list, spot s02 can be forced only if all the geographic rules are respected. Spot s03 can always be forced since it does not have an associated position.

Core methods

All GeoXp core methods are available in the documentation page.


Best practices

Designing configuration for a specific use

Behavior of geoXp covers a wide variety of applications. This broad approach means that, to guarantee the user experience good flow and consistency, some effort needs to be spent on adopting an optimal configuration for the desired result.

For example, geoXp is used for an audio guide of a museum tour.

The content is made of speeches, in which a guide is explaining historical facts about different museum spots.

  • This means that content cannot overlap

    config.experience.patterns[x].overlap = false

  • that user must hear the content just once

    config.experience.patterns[x].replay = false

Now, let’s change application. GeoXp is used to play ambient sounds at certain locations.

  • This means that content can overlap

    config.experience.patterns[x].overlap = true

  • that user must hear the content every time is in the right location

    config.experience.patterns[x].replay = true

Positions overlap

Unless content overlapping is desired, it’s better to avoid positions overlap when possible.

If two pattern spots are actually near each other, try setting radiuses in a way that fencing doesn’t overlap (maybe by setting a small radius and a big deadband: user has to be close to the position for the content to start, but the content will not stop if he walks away).

If overlapping isn’t avoidable, make sure to apply filtering with experience.options.visitedFilter (usually 5000 or 10000 ms is enough).

Mobile integration

Most mobile browsers will block Howler and Geolocation API after some time with no user interaction, resulting in unpredictable geoXp behavior. To avoid this, force a periodic unlocking calling the unlock() method inside a user interaction (eg: click listerner, etc).

Contributing

To contribute to this project, fork the repository, work on a development branch and open a MR. Remember to update the changelog (CHANGELOG.md)!

Repo admins: to release a new version, follow these steps:

  • verify and test changes
  • verify changelog has been updated
  • merge MR in master
  • release a new version with npm run release --vers=X.Y.Z
  • create a new release in the release section

Examples

Some configuration examples, for different kind of patterns, can be found inside the example-patterns. A basic application, with event usage, can be found inside the example-app folder.


Credits

  • concept - Mezzo Forte
  • development - Francesco Cretti & Giuliano Buratti
  • music for example application - Bensound

Package Sidebar

Install

npm i @mezzo-forte/geoxp

Weekly Downloads

26

Version

1.3.12

License

Apache-2.0

Unpacked Size

20.8 MB

Total Files

81

Last publish

Collaborators

  • francescocretti
  • gbcode.it