Mezzo Forte GeoXp Core is a an event based js library that manages all the background features of a multimedia geolocated tour.
It maps media control events to geographical locations based on configuration rules.
It’s meant to be used in any js environment, front or back end, regardless of the js framework or platform.
# install using npm
npm install @geoxp/core
# or using yarn
yarn add @geoxp/core
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.
Every geographical data, coming from the geolocation system, that does not fulfill accuracy requrements will be ignored.
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, the same media content can be linked to multiple positions, and multiple media can be linked to one position.
The event behavior for a GeoXp spot is designed as follows:
User enters the position circle (its inside area), spot becomes active, associated content is played. User leaves the position inside area, but he’s still inside the deadband, the content is still playing. User leaves the deadband, spot becomes outgoing, the audio content fades out and stops.
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.
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.
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.
As default behavior, when GeoXp instance is reloaded (eg: page refresh) exprience patterns memory of visited spots is cleared. This can be avoided enabling storage for patterns, by defining the getStoredVisitedSpots and setStoredVisitedSpots methods. Storage solution can be anything (eg: cookies/localstorage on browser, fs in node, etc.). GeoXp Core storage is managed automatically by our storage plugins.
When storage is enabled, an enitity for each pattern is updated every time a new spot is visited.
This storage can be deleted with different strategies:
completed event).last event).GeoXp Core is intended for usage as a singleton instance. It has to be created once the application starts, based on a configuration object.
// import module
import GeoXpCore from '@geoxp/core';
// create configuration object
const config = { /* your configuration here */ };
// create the GeoXp instance
const geoXpCore = new GeoXpCore(config);
GeoXpCore, 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 as a json object.
config: {
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
spots: [ // array of pattern’s spots
{
id: string // spot unique id
label: // spot name or descriptions
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”)
position: {
lat: number // latitude [degrees north]
lon: number // longitude [degrees east]
radius: number // activation radius [meters]
deadband: number // deadband past activation radius [meters]
fetch: number // prefetching distance [factor of radius]
}
}
]
}
],
options: {
visitedFilter: number // time after an already visited spot is notified [milliseconds] - default value = 5000 ms
accuracy: number // minimum acceptable accuracy [meters] - default value = 25 m
defaultRadius: number // default activation radius [meters] - default value = 20 m
defaultDeadband: number // default deadband past activation radius [meters] - default value = 10 m
defaultFetch: number // default prefetch distance [factor or radius] - default value = 1
}
}
NOTE - patterns are enabled by default. See API to know how to disable or re-enable them
// Creates a new geoXp instance
const geoXpCore = new GeoXpCore(config);
// Refreshes all geoXp state with the given configuration
geoXpCore.reload(config);
These methods offer a quick and convenient way to pause or resume GeoXp operations. They are especially useful when you need to enable or disable the core package globally, without having to manage individual patterns or spots manually.
For example, you might want to temporarily pause the core package while performing other tasks in your UI — such as allowing the user to make a selection — so that no content starts playing in the background.
// Temporarily stops all GeoXp activity globally.
geoXpCore.pause();
// Reactivates GeoXp after it has been paused.
geoXpCore.run();
GeoXpCore is meant to work automatically based on its configuration, so most of the interaction with it is based on events.
Its event dispatcher (geoXpCore.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:
geoXpCore.on(eventName, listener) - adds the listener function to the end of the listeners array for the event named eventNamegeoXpCore.once(eventName, listener) - adds a one-time listener function for the event named eventNamegeoXpCore.off(eventName, listener) - removes the specified listener from the listener array for the event named eventNameAvailable events are:
geoXpCore.on('incoming', spot => { /* ... */ })
Spot is nearby, start fetching content.
geoXpCore.on('active', spot => { /* ... */ })
Spot has been activated, media content should start.
geoXpCore.on('inactive', spot => { /* ... */ })
Spot has been deactivated, all related media content should stop.
geoXpCore.on('visited', spot => { /* ... */ })
User entered a spot which he already visited before. User should be able to choose whether to replay it or not.
geoXpCore.on('complete', patternId => { /* ... */ })
All spots in pattern have been visited.
geoXpCore.on('last', patternId => { /* ... */ })
Spot marked as last of pattern has been visited.
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.
Manual mode enables GeoXp spots to be activated without relying on the user's actual geolocation.
❗ Forcing a spot pauses all automatic experience management until the forced content finishes. Afterward, the experience logic resumes seamlessly.
Here are three key methods tied to this feature:
canForceSpot(spotId) - checks if manual mode is available for a specific spot. If manual mode isn’t allowed, it returns an error message explaining why. Otherwise, it returns undefined.forceSpot(spotId) - activates playback for the selected spot in manual mode.stopForcedSpot() - stops all forced spots and reverts to normal operations.When a spot is forced:
Manual mode is very useful in two scenarios:
Manual mode is perfect for unique content that doesn’t fit standard geographical placement (e.g., extra features, warnings, or custom messages). This usage turns GeoXp into a simpler media player.
Add a spot without a position (note that after/notAfter logics won’t apply because normal mechanisms are disabled) to your configuration file and call the forceSpot(spotId) method to trigger it directly.
spots: [
// manual spot
{
id: string,
label: string,
},
// normal spot
{
id: string,
label: string,
after: string,
notAfter: string,
position: {
lat: number,
lon: number,
radius: number,
deadband: number,
fetch: number,
}
}
]
In this scenario,
canForceSpot(id)will always allow forcing the spot.
Then GPS data becomes inaccurate due to environmental factors like nearby buildings, dense tree cover, satellite signal blockages, or electromagnetic interference from power lines, manual spot activation mode becomes accessible.
In this case geoXp enables manual mode 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).
Forcing a spot in this case is a plan B when something is not working properly (bad GPS or slow update time). In this case, forcing a spot ensures smooth playback but disrupts geolocation tracking temporarily.
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.
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.
user must hear the content every time is in the right location
config.experience.patterns[x].replay = true
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).
To contribute to this project, fork the repository, work on a development branch and open a MR. Remember to update the changelog (CHANGELOG.md)!
For repo admins To release a new version, follow these steps:
- verify and test changes
- verify changelog has been updated
- merge MR in
main- publish a new version
- create a new release in the release section