MVF Overview
The Mappedin Venue Format (MVF) is a GeoJSON based format to describe indoor maps and their surroundings. It has a simple, flexible core of GeoJSON broken up by floor, with a powerful extension framework to add many different kinds of data.
TypeScript/JavaScript developers will primarily interact with MVFs though the main MVF package. It contains the parser to unzip an MVF bundle and convert it into a typed JavaScript object of the MVFv3 type. It also re-exports the Mappedin extensions, so developers only need to install the @mappedin/mvf
extension for most common cases.
Developers looking at the MVF directly can find specifics on each extension in their specific docs. The most common ones are:
- Core - The main extension almost every MVF will implement. Includes the manifest, floors, and geometry (one file per floor).
- Locations - Rich data for places of interest, linked to geometry.
Others may be used, but many, such as Connections, Nodes, and Navigation Flags, are mostly used by the Mappedin SDK.
Core
Almost every MVF will contain the Core extension. In particular, most extensions will contain references to Geometry and Floors.
Here is a very simple example, as an MVFv3
object:
import type { MVFv3 } from '@mappedin/mvf';
export const CORE_EXAMPLE: MVFv3 = {
manifest: {
type: 'FeatureCollection',
features: [
{
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [0, 0],
},
properties: {
name: 'My Map',
version: '3.0.0',
time: '2025-01-01T00:00:00.000Z',
contents: [
{
name: 'manifest.geojson',
type: 'file',
},
{
name: 'floors.geojson',
type: 'file',
},
{
name: 'geometry',
type: 'folder',
children: [
{
name: 'f_00000001.geojson',
type: 'file',
},
{
name: 'f_00000002.geojson',
type: 'file',
},
],
},
],
},
},
],
},
floors: {
type: 'FeatureCollection',
features: [
{
type: 'Feature',
geometry: {
type: 'Polygon',
coordinates: [
[
[0, 0],
[1, 0],
[1, 1],
[0, 1],
[0, 0],
],
],
},
properties: {
id: 'f_00000001',
elevation: 0,
details: {
name: 'Floor 1',
},
},
},
{
type: 'Feature',
geometry: {
type: 'Polygon',
coordinates: [
[
[0, 0],
[1, 0],
[1, 1],
[0, 1],
[0, 0],
],
],
},
properties: {
id: 'f_00000002',
elevation: 1,
details: {
name: 'Floor 2',
},
},
},
],
},
geometry: {
f_00000001: {
type: 'FeatureCollection',
features: [
{
type: 'Feature',
geometry: {
type: 'Polygon',
coordinates: [
[
[0, 0],
[1, 0],
[1, 1],
[0, 1],
[0, 0],
],
],
},
properties: {
id: 'g_00000001',
},
},
],
},
f_00000002: {
type: 'FeatureCollection',
features: [
{
type: 'Feature',
geometry: {
type: 'Polygon',
coordinates: [
[
[0, 0],
[1, 0],
[1, 1],
[0, 1],
[0, 0],
],
],
},
properties: {
id: 'g_00000002',
},
},
],
},
},
};
That would have come from a zip file with the following structure:
manifest.geojson
floors.geojson
geometry/
f_00000001.geojson
f_00000002.geojson
This describes a map, called "My Map", with two floors. Each has one geometry feature, which is a polygon. All of the files exposed by Core are GeoJSON feature collections. The manifest has a single point that is roughly the center of the map as a whole. Floors contains a outline of each floor, and geometry contains all of the actual geometry (walls, rooms, doors, desks, etc.) for each floor. There may be a few extra properties on these objects (see their types for details), but most other, more complex relationships, will be described in other extensions.
"Core" Concepts
There are a few concepts and patterns that Core provides that are important to understand.
Files vs Objects
MVF is a bundle format, but most developers will interact with it as a parsed JavaScript object. This means that when describing the structure of an MVF, this documentation will tend to prefer the object model over the file model. As can be seen from the example above, an MVF bundle is parsed into a single object, with top level properties for every file and folder in the root of the zip. For any file, they will be parsed as JSON and their extension removed. For any folder, their children (files and folders) will be parsed the same way and added to the object as a child property.
So in the example above, the contents of the "manifest.geojson" file is what is now in the manifest property of the MVFv3
object. The contents of the "floors.geojson" file is what is now in the floors property of the MVFv3
object. The contents of the "geometry" folder would be two files, one for each floor, with the name of the file being the floor id + ".geojson", and the contents of the file being what is now in the geometry property of the MVFv3
object, on the keys of the floor ids.
One File Per Floor
If at all possible, an extension will provide a top level property for itself, an object per floor, keyed by the floor id. Geometry in Core is a good example of this, but many extensions will do this as well. Floors are a fundamental concept that differential MVF from other geo-spatial formats, in particular outdoor oriented ones. Each floor can contain a lot of data, and a client may wish to only parse or consider data on a certain floor in many situations.
Utility Types
Core exports a number of utility types that are used by many extensions to have a common way of handling certain ideas.
WithGeometryId and WithGeometryAnchor
Nearly all extensions will involve relating some information to some Geometry from the Core extension. The Geometry object is kept deliberately minimal, with the idea being that one or more extensions will have references to geometries to add more data and context.
For example, for a given piece of Geometry, a Location such as a Restroom may reference it, along with several other geometries, to indicate where the restrooms are. A node may reference it to say a user can reach this place through the node graph by getting to that node. The Default Style extension may reference the geometry to say it should rendered blue.
All of this is done in a standard way, though two main utility types, exported from Core: WithGeometryId and WithGeometryAnchor, and their array variants (WithGeometryIds and WithGeometryAnchors).
WithGeometryId
adds a geometryId
property to a type, and WithGeometryIds
adds an array of geometryIds
properties.
export type WithGeometryId = {
geometryId: GeometryId;
}
export type WithGeometryIds = {
geometryIds: GeometryId[];
}
WithGeometryAnchor
adds a geometryAnchor
property to a type, containing a GeometryAnchor, and WithGeometryAnchors
adds geometryAnchors
, which is an array of GeometryAnchors. A GeometryAnchor
is an object with a geometryId and floorId.
export type GeometryAnchor = {
geometryId: GeometryId;
floorId: FloorId;
}
export type WithGeometryAnchor = {
geometryAnchor: GeometryAnchor;
}
export type WithGeometryAnchors = {
geometryAnchors: GeometryAnchor[];
}
An extension will use withGeometryId
if it is already known what floor it belongs to (ie, if it is following One File Per Floor practice and is inside of a object keyed by the floor id). If it is not known what floor it belongs to, it will use withGeometryAnchor
instead.
WithDetails
Core also exports a WithDetails utility type, which is used to add a standard details
property to an object. This is the way to add a small amount of additional (typically optional) user facing metadata to an object.
Extensions will tend to add a details
object rather than any of the properties inside of it directly. Developers can build default components to display information about objects that are WithDetails
if they don't have a more specific display.
export type Details = {
description?: string;
externalId?: string;
icon?: string;
name?: string;
shortName?: string;
};
export type WithDetails = {
details?: Details;
}
externalId
is worth noting specifically. This is a string that should be used to link the object to some other system external to Mappedin. For example, the core Geometry extension marks Geometry as withDetails
and a particular geometry might get an externalId
of UNIT-21
to represent the unit number in some leasing system.
Extra
Core also includes a WithExtras utility type, which is used to add the extra
property to an object, which is a grab back of string keys to any data. This should be used sparingly, preferring instead a more specific extension, since there is no validation or integrity checking on the data. Enterprise customers with custom data syncs and custom SDK applications may have some of their own data included in the extra property on certain objects, as per their agreements with Mappedin.
export type Extra = Record<string, unknown>;
export type WithExtras = {
extra?: Extra;
}
Example
The Locations extension uses all three of these utility types. Here is a simple example of a Location object:
const location: Location = {
id: 'loc_00000001',
geometryAnchors: [
{
geometryId: 'g_00000001',
floorId: 'f_00000001',
},
],
details: {
name: 'Main Conference Room',
},
extra: {
customData: 'some data',
},
categories: [],
social: [],
images: [],
links: [],
};
This location can be found on floor one. It is the Main Conference Room, and it has some custom data.